Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion structuralcodes/core/base/__init__.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
"""Abstract base classes for the class hierarchy."""

from ._constitutive_law import ConstitutiveLaw
from ._geometry import Geometry
from ._geometry import Geometry, _GroupMixin
from ._material import Material
from ._section import Section, SectionCalculator

__all__ = [
'ConstitutiveLaw',
'Geometry',
'_GroupMixin',
'Material',
'Section',
'SectionCalculator',
Expand Down
21 changes: 15 additions & 6 deletions structuralcodes/core/base/_constitutive_law.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@ class ConstitutiveLaw(abc.ABC):
"""Abstract base class for constitutive laws."""

__materials__: t.Tuple[str] = ()
constitutive_law_counter: t.ClassVar[int] = 0
_constitutive_law_counter: t.ClassVar[int] = 0
id: int
Comment thread
talledodiego marked this conversation as resolved.

def __init__(self, name: t.Optional[str] = None) -> None:
def __init__(
self, name: t.Optional[str] = None, base_name: str = 'ConstitutiveLaw'
Comment thread
mortenengen marked this conversation as resolved.
) -> None:
"""Initialize a ConstitutiveLaw object."""
self.id = self.constitutive_law_counter
self._name = name if name is not None else f'ConstitutiveLaw_{self.id}'
self._increase_global_counter()
self.id = ConstitutiveLaw.return_global_counter_and_increase()
self._name = name if name is not None else f'{base_name}_{self.id}'

@property
def name(self):
Expand All @@ -28,7 +30,14 @@ def name(self):

@classmethod
def _increase_global_counter(cls):
cls.constitutive_law_counter += 1
cls._constitutive_law_counter += 1

@classmethod
def return_global_counter_and_increase(cls):
"""Returns the current counter and increases it by one."""
counter = cls._constitutive_law_counter
cls._increase_global_counter()
return counter

@abc.abstractmethod
def get_stress(
Expand Down
100 changes: 52 additions & 48 deletions structuralcodes/core/base/_geometry.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,57 @@
from ._material import Material


class _GroupMixin:
"""A mixin class for handling functionality related to group labels."""

_group_label: t.Optional[str] = None

@property
def group_label(self):
"""Returns the group_label."""
return self._group_label

def _group_matches(
self, pattern: str, *, case_sensitive: bool = True
) -> bool:
"""Checks if the group_label matches a pattern.

Arguments:
pattern (str): the string pattern to be checked

Keyword Arguments:
case_sensitive (bool, optional): if True (default) the check is
case sensitive.

Returns:
(bool): Returns True if the group_label matches the pattern.

Note:
The matching permits to use:
- "*" any chars
- "?" single char
- "[abc]" character set

Examples:
>>> geo.group_matches("nametos*")
>>> geo.group_matches("*pier*")
>>> geo.group_matches("Abutment??", case_senstive=False)
"""
if self.group_label is None:
return False
if not case_sensitive:
return fnmatchcase(self.group_label.casefold(), pattern.casefold())
return fnmatchcase(self.group_label, pattern)


class Geometry:
"""Base class for a geometry object."""

section_counter: t.ClassVar[int] = 0
_geometry_counter: t.ClassVar[int] = 0
id: int
Comment thread
mortenengen marked this conversation as resolved.

def __init__(
self, name: t.Optional[str] = None, group_label: t.Optional[str] = None
self, name: t.Optional[str] = None, base_name: str = 'Geometry'
) -> None:
"""Initializes a geometry object.

Expand All @@ -23,34 +67,26 @@ def __init__(

Arguments:
name (Optional(str)): The name to be given to the object.
group_label (Optional(str)): A label for grouping several objects.
base_name (str): If name is not given, use this argument together
with a global counter to create a unique name for the geometry.
"""
if name is not None:
self._name = name
else:
counter = Geometry.return_global_counter_and_increase()
self._name = f'Geometry_{counter}'
self._group_label = group_label
self.id = Geometry.return_global_counter_and_increase()
self._name = name if name is not None else f'{base_name}_{self.id}'

@property
def name(self):
"""Returns the name of the Geometry."""
return self._name

@property
def group_label(self):
"""Returns the group_label fo the Geometry."""
return self._group_label

@classmethod
def _increase_global_counter(cls):
"""Increases the global counter by one."""
cls.section_counter += 1
cls._geometry_counter += 1

@classmethod
def return_global_counter_and_increase(cls):
"""Returns the current counter and increases it by one."""
counter = cls.section_counter
counter = cls._geometry_counter
cls._increase_global_counter()
return counter

Expand Down Expand Up @@ -93,35 +129,3 @@ def _name_matches(
if not case_sensitive:
return fnmatchcase(self.name.casefold(), pattern.casefold())
return fnmatchcase(self.name, pattern)

def _group_matches(
self, pattern: str, *, case_sensitive: bool = True
) -> bool:
"""Checks if the group_label matches a pattern.

Arguments:
pattern (str): the string pattern to be checked

Keyword Arguments:
case_sensitive (bool, optional): if True (default) the check is
case sensitive.

Returns:
(bool): Returns True if the group_label matches the pattern.

Note:
The matching permits to use:
- "*" any chars
- "?" single char
- "[abc]" character set

Examples:
>>> geo.group_matches("nametos*")
>>> geo.group_matches("*pier*")
>>> geo.group_matches("Abutment??", case_senstive=False)
"""
if self.group_label is None:
return False
if not case_sensitive:
return fnmatchcase(self.group_label.casefold(), pattern.casefold())
return fnmatchcase(self.group_label, pattern)
21 changes: 19 additions & 2 deletions structuralcodes/core/base/_material.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ class Material(abc.ABC):
_initial_strain: t.Optional[float] = None
_initial_stress: t.Optional[float] = None
_strain_compatibility: t.Optional[bool] = None
_material_counter: t.ClassVar[int] = 0
id: int

def __init__(
self,
Expand All @@ -23,6 +25,7 @@ def __init__(
initial_stress: t.Optional[float] = None,
strain_compatibility: t.Optional[bool] = None,
name: t.Optional[str] = None,
base_name: str = 'Material',
) -> None:
"""Initializes an instance of a new material.

Expand All @@ -37,7 +40,9 @@ def __init__(
True, the material deforms with the geometry. If False, the
stress in the material upon loading is kept constant
corresponding to the initial strain.
name (Optional[str]): descriptive name of the material
name (Optional[str]): Descriptive name of the material
base_name (str): If name is not given, use this argument together
with a global counter to create a unique name for the material.

Raise:
ValueError: if both initial_strain and initial_stress are provided
Expand All @@ -50,7 +55,8 @@ def __init__(
self._initial_strain = initial_strain
self._initial_stress = initial_stress
self._strain_compatibility = strain_compatibility
self._name = name if name is not None else 'Material'
self.id = Material.return_global_counter_and_increase()
self._name = name if name is not None else f'{base_name}_{self.id}'

@property
def constitutive_law(self) -> ConstitutiveLaw:
Expand All @@ -62,6 +68,17 @@ def name(self):
"""Returns the name of the material."""
return self._name

@classmethod
def _increase_global_counter(cls):
cls._material_counter += 1

@classmethod
def return_global_counter_and_increase(cls):
"""Returns the current counter and increases it by one."""
counter = cls._material_counter
cls._increase_global_counter()
return counter

@property
def density(self):
"""Returns the density of the material in kg/m3."""
Expand Down
21 changes: 15 additions & 6 deletions structuralcodes/core/base/_section.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,16 @@ class Section(abc.ABC):
"""

geometry: Geometry
section_counter: t.ClassVar[int] = 0
_section_counter: t.ClassVar[int] = 0
id: int
section_calculator: SectionCalculator

def __init__(self, name: t.Optional[str] = None) -> None:
def __init__(
self, name: t.Optional[str] = None, base_name: str = 'Section'
) -> None:
"""Initialize a Section object."""
self.id = self.section_counter
self._name = name if name is not None else f'Section_{self.id}'
self._increase_global_counter()
self.id = Section.return_global_counter_and_increase()
self._name = name if name is not None else f'{base_name}_{self.id}'

@property
def name(self):
Expand All @@ -31,7 +33,14 @@ def name(self):

@classmethod
def _increase_global_counter(cls):
cls.section_counter += 1
cls._section_counter += 1

@classmethod
def return_global_counter_and_increase(cls):
"""Returns the current counter and increases it by one."""
counter = cls._section_counter
cls._increase_global_counter()
return counter


class SectionCalculator(abc.ABC):
Expand Down
12 changes: 7 additions & 5 deletions structuralcodes/geometry/_geometry.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
from structuralcodes.materials.basic import ElasticMaterial
from structuralcodes.materials.concrete import Concrete

from ..core.base import Geometry, Material
from ..core.base import Geometry, Material, _GroupMixin


def _mirror_about_axis_matrix(axis: LineString) -> np.ndarray:
Expand Down Expand Up @@ -59,7 +59,7 @@ def _mirror_about_axis_matrix(axis: LineString) -> np.ndarray:
return T_inv @ R_inv @ M @ R @ T


class PointGeometry(Geometry):
class PointGeometry(_GroupMixin, Geometry):
"""Class for a point geometry with material.

Basically it is a wrapper for shapely Point including the material (and
Expand Down Expand Up @@ -88,7 +88,8 @@ def __init__(
group_label (Optional(str)): A label for grouping several objects
(default is None).
"""
super().__init__(name, group_label)
super().__init__(name)
self._group_label = group_label
# I check if point is a shapely Point or an ArrayLike object
if not isinstance(point, Point):
# It is an ArrayLike object -> create the Point given the
Expand Down Expand Up @@ -316,7 +317,7 @@ def create_line_point_angle(
return LineString([(x1, y1), (x2, y2)])


class SurfaceGeometry(Geometry):
class SurfaceGeometry(_GroupMixin, Geometry):
"""Class for a surface geometry with material.

Basically it is a wrapper for shapely polygon including the material (and
Expand All @@ -343,7 +344,8 @@ def __init__(
name (Optional(str)): The name to be given to the object.
group_label (Optional(str)): A label for grouping several objects.
"""
super().__init__(name=name, group_label=group_label)
super().__init__(name=name)
self._group_label = group_label
# Check if inputs are of the correct type, otherwise return error
if not isinstance(poly, Polygon):
raise TypeError(
Expand Down
3 changes: 2 additions & 1 deletion structuralcodes/materials/basic/_elastic.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ def __init__(
initial_strain=initial_strain,
initial_stress=initial_stress,
strain_compatibility=strain_compatibility,
name=name if name else 'ElasticMaterial',
name=name,
base_name='ElasticMaterial',
)
self._E = E
self._ultimate_strain = ultimate_strain
Expand Down
3 changes: 2 additions & 1 deletion structuralcodes/materials/basic/_elasticplastic.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ def __init__(
initial_strain=initial_strain,
initial_stress=initial_stress,
strain_compatibility=strain_compatibility,
name=name if name else 'ElasticPlasticMaterial',
name=name,
base_name='ElasticPlasticMaterial',
)
self._E = E
self._fy = fy
Expand Down
3 changes: 2 additions & 1 deletion structuralcodes/materials/basic/_generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ def __init__(
initial_strain=initial_strain,
initial_stress=initial_stress,
strain_compatibility=strain_compatibility,
name=name if name else 'GenericMaterial',
name=name,
base_name='GenericMaterial',
)
self._constitutive_law = constitutive_law
self._apply_initial_strain()
2 changes: 1 addition & 1 deletion structuralcodes/materials/concrete/_concrete.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@ def __init__(
strain_compatibility: t.Optional[bool] = None,
) -> None:
"""Initializes an abstract concrete material."""
name = name if name is not None else 'Concrete'
super().__init__(
density=density,
initial_strain=initial_strain,
initial_stress=initial_stress,
strain_compatibility=strain_compatibility,
name=name,
base_name='Concrete',
)

self._fck = abs(fck)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,7 @@ def __init__(
eps_cu (float): Ultimate strain (pure number).
name (str): A descriptive name for the constitutive law.
"""
name = name if name is not None else 'BilinearCompressionLaw'
super().__init__(name=name)
super().__init__(name=name, base_name='BilinearCompressionLaw')
self._fc = -abs(fc)
self._eps_c = -abs(eps_c)
self._eps_cu = -abs(eps_cu)
Expand Down
3 changes: 1 addition & 2 deletions structuralcodes/materials/constitutive_laws/_elastic.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,7 @@ def __init__(
and positive strains. If a tuple is provided, it should be
given as (negative, positive). Default value = None.
"""
name = name if name is not None else 'ElasticLaw'
super().__init__(name=name)
super().__init__(name=name, base_name='ElasticLaw')
self._E = E
if E <= 0:
raise ValueError('Elastic modulus should be greater than 0')
Expand Down
Loading
Loading