structuralcodes 0.4.0__py3-none-any.whl → 0.6.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of structuralcodes might be problematic. Click here for more details.

Files changed (52) hide show
  1. structuralcodes/__init__.py +1 -1
  2. structuralcodes/codes/ec2_2004/__init__.py +2 -0
  3. structuralcodes/codes/ec2_2004/shear.py +44 -4
  4. structuralcodes/codes/mc2010/__init__.py +18 -0
  5. structuralcodes/codes/mc2010/_concrete_creep_and_shrinkage.py +2 -2
  6. structuralcodes/codes/mc2010/_concrete_punching.py +300 -388
  7. structuralcodes/core/base.py +116 -5
  8. structuralcodes/geometry/__init__.py +2 -8
  9. structuralcodes/geometry/_circular.py +3 -10
  10. structuralcodes/geometry/_geometry.py +58 -114
  11. structuralcodes/geometry/_rectangular.py +3 -10
  12. structuralcodes/geometry/_reinforcement.py +9 -14
  13. structuralcodes/geometry/profiles/__init__.py +19 -0
  14. structuralcodes/geometry/profiles/_base_profile.py +305 -0
  15. structuralcodes/geometry/profiles/_common_functions.py +194 -0
  16. structuralcodes/geometry/profiles/_he.py +192 -0
  17. structuralcodes/geometry/profiles/_ipe.py +130 -0
  18. structuralcodes/geometry/profiles/_ipn.py +329 -0
  19. structuralcodes/geometry/profiles/_ub.py +264 -0
  20. structuralcodes/geometry/profiles/_ubp.py +227 -0
  21. structuralcodes/geometry/profiles/_uc.py +276 -0
  22. structuralcodes/geometry/profiles/_upn.py +315 -0
  23. structuralcodes/materials/__init__.py +2 -1
  24. structuralcodes/materials/basic/__init__.py +11 -0
  25. structuralcodes/materials/basic/_elastic.py +69 -0
  26. structuralcodes/materials/basic/_elasticplastic.py +92 -0
  27. structuralcodes/materials/basic/_generic.py +43 -0
  28. structuralcodes/materials/concrete/__init__.py +3 -0
  29. structuralcodes/materials/concrete/_concrete.py +10 -1
  30. structuralcodes/materials/concrete/_concreteEC2_2004.py +14 -0
  31. structuralcodes/materials/concrete/_concreteEC2_2023.py +14 -0
  32. structuralcodes/materials/concrete/_concreteMC2010.py +19 -0
  33. structuralcodes/materials/constitutive_laws/__init__.py +3 -0
  34. structuralcodes/materials/constitutive_laws/_elasticplastic.py +2 -2
  35. structuralcodes/materials/constitutive_laws/_initial_strain.py +130 -0
  36. structuralcodes/materials/reinforcement/__init__.py +6 -0
  37. structuralcodes/materials/reinforcement/_reinforcement.py +10 -1
  38. structuralcodes/materials/reinforcement/_reinforcementEC2_2004.py +15 -1
  39. structuralcodes/materials/reinforcement/_reinforcementEC2_2023.py +15 -1
  40. structuralcodes/materials/reinforcement/_reinforcementMC2010.py +15 -1
  41. structuralcodes/sections/_generic.py +53 -14
  42. structuralcodes/sections/_rc_utils.py +15 -5
  43. structuralcodes/sections/section_integrators/__init__.py +3 -1
  44. structuralcodes/sections/section_integrators/_fiber_integrator.py +19 -11
  45. structuralcodes/sections/section_integrators/_marin_integrator.py +25 -20
  46. {structuralcodes-0.4.0.dist-info → structuralcodes-0.6.0.dist-info}/METADATA +2 -2
  47. structuralcodes-0.6.0.dist-info/RECORD +77 -0
  48. structuralcodes/geometry/_steel_sections.py +0 -2155
  49. structuralcodes-0.4.0.dist-info/RECORD +0 -63
  50. /structuralcodes/{sections/section_integrators → core}/_marin_integration.py +0 -0
  51. {structuralcodes-0.4.0.dist-info → structuralcodes-0.6.0.dist-info}/WHEEL +0 -0
  52. {structuralcodes-0.4.0.dist-info → structuralcodes-0.6.0.dist-info}/licenses/LICENSE +0 -0
@@ -15,21 +15,48 @@ class Material(abc.ABC):
15
15
  """Abstract base class for materials."""
16
16
 
17
17
  _constitutive_law = None
18
-
19
- def __init__(self, density: float, name: t.Optional[str] = None) -> None:
18
+ _initial_strain: t.Optional[float] = None
19
+ _initial_stress: t.Optional[float] = None
20
+ _strain_compatibility: t.Optional[bool] = None
21
+
22
+ def __init__(
23
+ self,
24
+ density: float,
25
+ initial_strain: t.Optional[float] = None,
26
+ initial_stress: t.Optional[float] = None,
27
+ strain_compatibility: t.Optional[bool] = None,
28
+ name: t.Optional[str] = None,
29
+ ) -> None:
20
30
  """Initializes an instance of a new material.
21
31
 
22
32
  Args:
23
- density (float): density of the material in kg/m3
33
+ density (float): Density of the material in kg/m3.
24
34
 
25
35
  Keyword Args:
36
+ initial_strain (Optional[float]): Initial strain of the material.
37
+ initial_stress (Optional[float]): Initial stress of the material.
38
+ strain_compatibility (Optional[bool]): Only relevant if
39
+ initial_strain or initial_stress are different from zero. If
40
+ True, the material deforms with the geometry. If False, the
41
+ stress in the material upon loading is kept constant
42
+ corresponding to the initial strain.
26
43
  name (Optional[str]): descriptive name of the material
44
+
45
+ Raise:
46
+ ValueError: if both initial_strain and initial_stress are provided
27
47
  """
28
48
  self._density = abs(density)
49
+ if initial_strain is not None and initial_stress is not None:
50
+ raise ValueError(
51
+ 'Both initial_strain and initial_stress cannot be provided.'
52
+ )
53
+ self._initial_strain = initial_strain
54
+ self._initial_stress = initial_stress
55
+ self._strain_compatibility = strain_compatibility
29
56
  self._name = name if name is not None else 'Material'
30
57
 
31
58
  @property
32
- def constitutive_law(self):
59
+ def constitutive_law(self) -> ConstitutiveLaw:
33
60
  """Returns the ConstitutiveLaw of the object."""
34
61
  return self._constitutive_law
35
62
 
@@ -43,6 +70,88 @@ class Material(abc.ABC):
43
70
  """Returns the density of the material in kg/m3."""
44
71
  return self._density
45
72
 
73
+ @property
74
+ def initial_strain(self):
75
+ """Returns the initial strain of the material."""
76
+ return self._initial_strain
77
+
78
+ @property
79
+ def initial_stress(self):
80
+ """Returns the initial stress of the material."""
81
+ return self._initial_stress
82
+
83
+ @property
84
+ def strain_compatibility(self):
85
+ """Returns the strain compatibility of the material.
86
+
87
+ If true (default), the strain compatibility is enforced
88
+ haveing the same strain as in all other materials of the
89
+ section at the same point. If false, the strain compatibility
90
+ is not enforced and the initial strain is applied to the section
91
+ independently.
92
+ """
93
+ return self._strain_compatibility
94
+
95
+ def _apply_initial_strain(self):
96
+ """Wraps the current constitutive law to apply initial strain."""
97
+ strain_compatibility = (
98
+ self._strain_compatibility
99
+ if self._strain_compatibility is not None
100
+ else True
101
+ )
102
+ if self._initial_stress is not None:
103
+ # Specified a stress, compute the strain from it
104
+ self._initial_strain_from_stress()
105
+ if self._initial_strain is not None:
106
+ # Lazy import to avoid circular dependency
107
+ from structuralcodes.materials.constitutive_laws import ( # noqa: PLC0415
108
+ InitialStrain,
109
+ )
110
+
111
+ if self._initial_stress is None:
112
+ # Compute the stress from the strain
113
+ self._initial_stress = self._constitutive_law.get_stress(
114
+ self._initial_strain
115
+ )
116
+
117
+ self._constitutive_law = InitialStrain(
118
+ self._constitutive_law,
119
+ self._initial_strain,
120
+ strain_compatibility,
121
+ )
122
+
123
+ def _initial_strain_from_stress(self):
124
+ """Computes the initial strain from the initial stress.
125
+
126
+ This function is called internally so it assumes that the
127
+ initial stress is not None
128
+ """
129
+ # Iteratively compute the initial strain that gives the desired
130
+ # initial stress. Note that the wrapped law can be nonlinear
131
+ tol = 1e-12
132
+ max_iter = 100
133
+ target_stress = self._initial_stress
134
+ strain = 0.0
135
+ stress = self._constitutive_law.get_stress(strain)
136
+ d_stress = target_stress - stress
137
+ num_iter = 0
138
+ while abs(d_stress) > tol and num_iter < max_iter:
139
+ tangent = self._constitutive_law.get_tangent(strain)
140
+ if tangent == 0:
141
+ raise ValueError(
142
+ 'Tangent modulus = 0 during initial strain computation.'
143
+ )
144
+ d_strain = d_stress / tangent
145
+ strain += d_strain
146
+ stress = self._constitutive_law.get_stress(strain)
147
+ d_stress = target_stress - stress
148
+ num_iter += 1
149
+
150
+ if abs(d_stress) > tol:
151
+ raise RuntimeError('Failed to converge for given initial stress.')
152
+
153
+ self._initial_strain = strain
154
+
46
155
 
47
156
  class ConstitutiveLaw(abc.ABC):
48
157
  """Abstract base class for constitutive laws."""
@@ -150,7 +259,9 @@ class ConstitutiveLaw(abc.ABC):
150
259
 
151
260
  eps = np.concatenate((eps_neg, eps_pos))
152
261
  sig = self.get_stress(eps)
153
- from structuralcodes.materials.constitutive_laws import UserDefined
262
+ from structuralcodes.materials.constitutive_laws import ( # noqa: PLC0415
263
+ UserDefined,
264
+ )
154
265
 
155
266
  return UserDefined(eps, sig)
156
267
 
@@ -1,5 +1,6 @@
1
1
  """Main entry point for geometry."""
2
2
 
3
+ from . import profiles
3
4
  from ._circular import CircularGeometry
4
5
  from ._geometry import (
5
6
  CompoundGeometry,
@@ -14,7 +15,6 @@ from ._reinforcement import (
14
15
  add_reinforcement_circle,
15
16
  add_reinforcement_line,
16
17
  )
17
- from ._steel_sections import HE, IPE, IPN, UB, UBP, UC, UPN
18
18
 
19
19
  __all__ = [
20
20
  'Geometry',
@@ -22,13 +22,7 @@ __all__ = [
22
22
  'SurfaceGeometry',
23
23
  'CompoundGeometry',
24
24
  'create_line_point_angle',
25
- 'IPE',
26
- 'HE',
27
- 'UB',
28
- 'UC',
29
- 'UBP',
30
- 'IPN',
31
- 'UPN',
25
+ 'profiles',
32
26
  'add_reinforcement',
33
27
  'add_reinforcement_line',
34
28
  'CircularGeometry',
@@ -12,7 +12,7 @@ import numpy as np
12
12
  from numpy.typing import ArrayLike
13
13
  from shapely import Polygon
14
14
 
15
- from structuralcodes.core.base import ConstitutiveLaw, Material
15
+ from structuralcodes.core.base import Material
16
16
 
17
17
  from ._geometry import SurfaceGeometry
18
18
 
@@ -37,9 +37,8 @@ class CircularGeometry(SurfaceGeometry):
37
37
  def __init__(
38
38
  self,
39
39
  diameter: float,
40
- material: t.Union[Material, ConstitutiveLaw],
40
+ material: Material,
41
41
  n_points: int = 20,
42
- density: t.Optional[float] = None,
43
42
  concrete: bool = False,
44
43
  origin: t.Optional[ArrayLike] = None,
45
44
  name: t.Optional[str] = None,
@@ -49,14 +48,9 @@ class CircularGeometry(SurfaceGeometry):
49
48
 
50
49
  Arguments:
51
50
  diameter (float): The diameter of the geometry.
52
- material (Union(Material, ConstitutiveLaw)): A Material or
53
- ConsitutiveLaw class applied to the geometry.
51
+ material (Material): A Material class applied to the geometry.
54
52
  n_points (int): The number of points used to discretize the
55
53
  circle as a shapely `Polygon` (default = 20).
56
- density (Optional(float)): When a ConstitutiveLaw is passed as
57
- material, the density can be provided by this argument. When
58
- material is a Material object the density is taken from the
59
- material.
60
54
  concrete (bool): Flag to indicate if the geometry is concrete.
61
55
  origin (Optional(ArrayLike)): The center point of the circle.
62
56
  (0.0, 0.0) is used as default.
@@ -84,7 +78,6 @@ class CircularGeometry(SurfaceGeometry):
84
78
  super().__init__(
85
79
  poly=polygon,
86
80
  material=material,
87
- density=density,
88
81
  concrete=concrete,
89
82
  name=name,
90
83
  group_label=group_label,
@@ -18,9 +18,9 @@ from shapely.geometry import (
18
18
  )
19
19
  from shapely.ops import split
20
20
 
21
- from structuralcodes.core.base import ConstitutiveLaw, Material
21
+ from structuralcodes.core.base import Material
22
+ from structuralcodes.materials.basic import ElasticMaterial
22
23
  from structuralcodes.materials.concrete import Concrete
23
- from structuralcodes.materials.constitutive_laws import Elastic
24
24
 
25
25
 
26
26
  class Geometry:
@@ -72,13 +72,24 @@ class Geometry:
72
72
  @staticmethod
73
73
  def from_geometry(
74
74
  geo: Geometry,
75
- new_material: t.Optional[t.Union[Material, ConstitutiveLaw]] = None,
75
+ new_material: t.Optional[Material] = None,
76
76
  ) -> Geometry:
77
77
  """Create a new geometry with a different material."""
78
78
  raise NotImplementedError(
79
79
  'This method should be implemented by subclasses'
80
80
  )
81
81
 
82
+ def __add__(self, other: Geometry) -> CompoundGeometry:
83
+ """Add operator "+" for geometries.
84
+
85
+ Arguments:
86
+ other (Geometry): The other geometry to add.
87
+
88
+ Returns:
89
+ CompoundGeometry: A new CompoundGeometry.
90
+ """
91
+ return CompoundGeometry([self, other])
92
+
82
93
 
83
94
  class PointGeometry(Geometry):
84
95
  """Class for a point geometry with material.
@@ -91,8 +102,7 @@ class PointGeometry(Geometry):
91
102
  self,
92
103
  point: t.Union[Point, ArrayLike],
93
104
  diameter: float,
94
- material: t.Union[Material, ConstitutiveLaw],
95
- density: t.Optional[float] = None,
105
+ material: Material,
96
106
  name: t.Optional[str] = None,
97
107
  group_label: t.Optional[str] = None,
98
108
  ):
@@ -105,12 +115,7 @@ class PointGeometry(Geometry):
105
115
  point (Union(Point, ArrayLike)): A couple of coordinates or a
106
116
  shapely Point object.
107
117
  diameter (float): The diameter of the point.
108
- material (Union(Material, ConstitutiveLaw)): The material for the
109
- point (this can be a Material or a ConstitutiveLaw).
110
- density (Optional(float)): When a ConstitutiveLaw is passed as
111
- material, the density can be providen by this argument. When
112
- the material is a Material object the density is taken from the
113
- material.
118
+ material (Material): The material for the point.
114
119
  name (Optional(str)): The name to be given to the object.
115
120
  group_label (Optional(str)): A label for grouping several objects
116
121
  (default is None).
@@ -131,22 +136,12 @@ class PointGeometry(Geometry):
131
136
  warn_str += ' discarded'
132
137
  warnings.warn(warn_str)
133
138
  point = Point(coords)
134
- if not isinstance(material, Material) and not isinstance(
135
- material, ConstitutiveLaw
136
- ):
139
+ if not isinstance(material, Material):
137
140
  raise TypeError(
138
- f'mat should be a valid structuralcodes.base.Material \
139
- or structuralcodes.base.ConstitutiveLaw object. \
141
+ f'mat should be a valid structuralcodes.base.Material object. \
140
142
  {repr(material)}'
141
143
  )
142
- # Pass a constitutive law to the PointGeometry
143
- self._density = density
144
- if isinstance(material, Material):
145
- self._density = material.density
146
- self._material = material.constitutive_law
147
- elif isinstance(material, ConstitutiveLaw):
148
- self._material = material
149
-
144
+ self._material = material
150
145
  self._point = point
151
146
  self._diameter = diameter
152
147
  self._area = np.pi * diameter**2 / 4.0
@@ -162,14 +157,14 @@ class PointGeometry(Geometry):
162
157
  return self._area
163
158
 
164
159
  @property
165
- def material(self) -> ConstitutiveLaw:
160
+ def material(self) -> Material:
166
161
  """Returns the point material."""
167
162
  return self._material
168
163
 
169
164
  @property
170
165
  def density(self) -> float:
171
166
  """Returns the density."""
172
- return self._density
167
+ return self.material.density
173
168
 
174
169
  @property
175
170
  def x(self) -> float:
@@ -204,7 +199,6 @@ class PointGeometry(Geometry):
204
199
  point=affinity.translate(self._point, dx, dy),
205
200
  diameter=self._diameter,
206
201
  material=self._material,
207
- density=self._density,
208
202
  name=self._name,
209
203
  group_label=self._group_label,
210
204
  )
@@ -231,7 +225,6 @@ class PointGeometry(Geometry):
231
225
  ),
232
226
  diameter=self._diameter,
233
227
  material=self._material,
234
- density=self._density,
235
228
  name=self._name,
236
229
  group_label=self._group_label,
237
230
  )
@@ -239,16 +232,15 @@ class PointGeometry(Geometry):
239
232
  @staticmethod
240
233
  def from_geometry(
241
234
  geo: PointGeometry,
242
- new_material: t.Optional[t.Union[Material, ConstitutiveLaw]] = None,
235
+ new_material: t.Optional[Material] = None,
243
236
  ) -> PointGeometry:
244
237
  """Create a new PointGeometry with a different material.
245
238
 
246
239
  Arguments:
247
240
  geo (PointGeometry): The geometry.
248
- new_material (Optional(Union(Material, ConstitutiveLaw))): A new
249
- material to be applied to the geometry. If new_material is
250
- None an Elastic material with same stiffness as the original
251
- material is created.
241
+ new_material (Optional(Material)): A new material to be applied to
242
+ the geometry. If new_material is None an Elastic material with
243
+ same stiffness as the original material is created.
252
244
 
253
245
  Returns:
254
246
  PointGeometry: The new PointGeometry.
@@ -261,24 +253,21 @@ class PointGeometry(Geometry):
261
253
  raise TypeError('geo should be a PointGeometry')
262
254
  if new_material is not None:
263
255
  # provided a new_material
264
- if not isinstance(new_material, Material) and not isinstance(
265
- new_material, ConstitutiveLaw
266
- ):
256
+ if not isinstance(new_material, Material):
267
257
  raise TypeError(
268
258
  f'new_material should be a valid structuralcodes.base.\
269
- Material or structuralcodes.base.ConstitutiveLaw object. \
259
+ Material object. \
270
260
  {repr(new_material)}'
271
261
  )
272
262
  else:
273
263
  # new_material not provided, assume elastic material with same
274
264
  # elastic modulus
275
- new_material = Elastic(E=geo.material.get_tangent(eps=0))
265
+ new_material = ElasticMaterial.from_material(geo.material)
276
266
 
277
267
  return PointGeometry(
278
268
  point=geo._point,
279
269
  diameter=geo._diameter,
280
270
  material=new_material,
281
- density=geo._density,
282
271
  name=geo._name,
283
272
  group_label=geo._group_label,
284
273
  )
@@ -331,13 +320,12 @@ class SurfaceGeometry(Geometry):
331
320
  holes.
332
321
  """
333
322
 
334
- _material: ConstitutiveLaw
323
+ _material: Material
335
324
 
336
325
  def __init__(
337
326
  self,
338
327
  poly: Polygon,
339
- material: t.Union[Material, ConstitutiveLaw],
340
- density: t.Optional[float] = None,
328
+ material: Material,
341
329
  concrete: bool = False,
342
330
  name: t.Optional[str] = None,
343
331
  group_label: t.Optional[str] = None,
@@ -346,11 +334,7 @@ class SurfaceGeometry(Geometry):
346
334
 
347
335
  Arguments:
348
336
  poly (shapely.Polygon): A Shapely polygon.
349
- material (Union(Material, ConstitutiveLaw)): A Material or
350
- ConsitutiveLaw class applied to the geometry.
351
- density (Optional(float)): When a ConstitutiveLaw is passed as mat,
352
- the density can be provided by this argument. When mat is a
353
- Material object the density is taken from the material.
337
+ material (Material): A Material applied to the geometry.
354
338
  concrete (bool): Flag to indicate if the geometry is concrete.
355
339
  name (Optional(str)): The name to be given to the object.
356
340
  group_label (Optional(str)): A label for grouping several objects.
@@ -362,24 +346,15 @@ class SurfaceGeometry(Geometry):
362
346
  f'poly need to be a valid shapely.geometry.Polygon object. \
363
347
  {repr(poly)}'
364
348
  )
365
- if not isinstance(material, Material) and not isinstance(
366
- material, ConstitutiveLaw
367
- ):
349
+ if not isinstance(material, Material):
368
350
  raise TypeError(
369
- f'mat should be a valid structuralcodes.base.Material \
370
- or structuralcodes.base.ConstitutiveLaw object. \
351
+ f'mat should be a valid structuralcodes.base.Material object. \
371
352
  {repr(material)}'
372
353
  )
373
354
  self._polygon = poly
374
- # Pass a constitutive law to the SurfaceGeometry
375
- self._density = density
376
- if isinstance(material, Material):
377
- self._density = material.density
378
- if isinstance(material, Concrete):
379
- concrete = True
380
- material = material.constitutive_law
381
-
382
355
  self._material = material
356
+ if isinstance(material, Concrete):
357
+ concrete = True
383
358
  self._concrete = concrete
384
359
 
385
360
  @property
@@ -403,11 +378,11 @@ class SurfaceGeometry(Geometry):
403
378
  @property
404
379
  def density(self) -> float:
405
380
  """Returns the density."""
406
- return self._density
381
+ return self.material.density
407
382
 
408
383
  @property
409
- def material(self) -> ConstitutiveLaw:
410
- """Returns the Constitutive law."""
384
+ def material(self) -> Material:
385
+ """Returns the material."""
411
386
  return self._material
412
387
 
413
388
  @property
@@ -506,17 +481,6 @@ class SurfaceGeometry(Geometry):
506
481
  # get the intersection
507
482
  return self.polygon.intersection(lines_polygon)
508
483
 
509
- def __add__(self, other: Geometry) -> CompoundGeometry:
510
- """Add operator "+" for geometries.
511
-
512
- Arguments:
513
- other (Geometry): The other geometry to add.
514
-
515
- Returns:
516
- CompoundGeometry: A new CompoundGeometry.
517
- """
518
- return CompoundGeometry([self, other])
519
-
520
484
  def __sub__(self, other: Geometry) -> SurfaceGeometry:
521
485
  """Add operator "-" for geometries.
522
486
 
@@ -527,7 +491,6 @@ class SurfaceGeometry(Geometry):
527
491
  SurfaceGeometry: The resulting SurfaceGeometry.
528
492
  """
529
493
  material = self.material
530
- density = self._density
531
494
 
532
495
  # if we subtract a point from a surface we obtain the same surface
533
496
  sub_polygon = self.polygon
@@ -540,9 +503,7 @@ class SurfaceGeometry(Geometry):
540
503
  for g in other.geometries:
541
504
  sub_polygon = sub_polygon - g.polygon
542
505
 
543
- return SurfaceGeometry(
544
- poly=sub_polygon, material=material, density=density
545
- )
506
+ return SurfaceGeometry(poly=sub_polygon, material=material)
546
507
 
547
508
  def _repr_svg_(self) -> str:
548
509
  """Returns the svg representation."""
@@ -561,7 +522,6 @@ class SurfaceGeometry(Geometry):
561
522
  return SurfaceGeometry(
562
523
  poly=affinity.translate(self.polygon, dx, dy),
563
524
  material=self.material,
564
- density=self._density,
565
525
  concrete=self.concrete,
566
526
  )
567
527
 
@@ -589,23 +549,21 @@ class SurfaceGeometry(Geometry):
589
549
  self.polygon, angle, origin=point, use_radians=use_radians
590
550
  ),
591
551
  material=self.material,
592
- density=self._density,
593
552
  concrete=self.concrete,
594
553
  )
595
554
 
596
555
  @staticmethod
597
556
  def from_geometry(
598
557
  geo: SurfaceGeometry,
599
- new_material: t.Optional[t.Union[Material, ConstitutiveLaw]] = None,
558
+ new_material: t.Optional[Material] = None,
600
559
  ) -> SurfaceGeometry:
601
560
  """Create a new SurfaceGeometry with a different material.
602
561
 
603
562
  Arguments:
604
563
  geo (SurfaceGeometry): The geometry.
605
- new_material: (Optional(Union(Material, ConstitutiveLaw))): A new
606
- material to be applied to the geometry. If new_material is None
607
- an Elastic material with same stiffness of the original
608
- material is created.
564
+ new_material: (Optional(Material)): A new material to be applied to
565
+ the geometry. If new_material is None an Elastic material with
566
+ same stiffness of the original material is created.
609
567
 
610
568
  Returns:
611
569
  SurfaceGeometry: The new SurfaceGeometry.
@@ -618,22 +576,18 @@ class SurfaceGeometry(Geometry):
618
576
  raise TypeError('geo should be a SurfaceGeometry')
619
577
  if new_material is not None:
620
578
  # provided a new_material
621
- if not isinstance(new_material, Material) and not isinstance(
622
- new_material, ConstitutiveLaw
623
- ):
579
+ if not isinstance(new_material, Material):
624
580
  raise TypeError(
625
581
  f'new_material should be a valid structuralcodes.base.\
626
- Material or structuralcodes.base.ConstitutiveLaw object. \
582
+ Material object. \
627
583
  {repr(new_material)}'
628
584
  )
629
585
  else:
630
586
  # new_material not provided, assume elastic material with same
631
587
  # elastic modulus
632
- new_material = Elastic(E=geo.material.get_tangent(eps=0))
588
+ new_material = ElasticMaterial.from_material(geo.material)
633
589
 
634
- return SurfaceGeometry(
635
- poly=geo.polygon, material=new_material, density=geo._density
636
- )
590
+ return SurfaceGeometry(poly=geo.polygon, material=new_material)
637
591
 
638
592
  # here we can also add static methods like:
639
593
  # from_points
@@ -648,14 +602,12 @@ class SurfaceGeometry(Geometry):
648
602
 
649
603
  def _process_geometries_multipolygon(
650
604
  geometries: MultiPolygon,
651
- materials: t.Optional[
652
- t.Union[t.List[Material], Material, ConstitutiveLaw]
653
- ],
605
+ materials: t.Optional[t.Union[t.List[Material], Material]],
654
606
  ) -> list[Geometry]:
655
607
  """Process geometries for initialization."""
656
608
  checked_geometries = []
657
609
  # a MultiPolygon is provided
658
- if isinstance(materials, (ConstitutiveLaw, Material)):
610
+ if isinstance(materials, Material):
659
611
  for g in geometries.geoms:
660
612
  checked_geometries.append(
661
613
  SurfaceGeometry(poly=g, material=materials)
@@ -698,20 +650,23 @@ class CompoundGeometry(Geometry):
698
650
  properties.
699
651
  """
700
652
 
701
- geometries: t.List[Geometry]
653
+ geometries: t.List[t.Union[SurfaceGeometry, PointGeometry]]
702
654
 
703
655
  def __init__(
704
656
  self,
705
- geometries: t.Union[t.List[Geometry], MultiPolygon],
657
+ geometries: t.Union[
658
+ t.List[t.Union[SurfaceGeometry, PointGeometry, CompoundGeometry]],
659
+ MultiPolygon,
660
+ ],
706
661
  materials: t.Optional[t.Union[t.List[Material], Material]] = None,
707
662
  ) -> None:
708
663
  """Creates a compound geometry.
709
664
 
710
665
  Arguments:
711
666
  geometries (Union(List(Geometry), MultiPolygon)): A list of
712
- Geometry objects (i.e. PointGeometry or SurfaceGeometry) or a
713
- shapely MultiPolygon object (in this latter case also a list of
714
- materials should be given).
667
+ Geometry objects (i.e. PointGeometry, SurfaceGeometry or
668
+ CompoundGeometry) or a shapely MultiPolygon object (in this
669
+ latter case also a list of materials should be given).
715
670
  materials (Optional(List(Material), Material)): A material (applied
716
671
  to all polygons) or a list of materials. In this case the
717
672
  number of polygons should match the number of materials.
@@ -834,17 +789,6 @@ class CompoundGeometry(Geometry):
834
789
  processed_geoms.append(pg.rotate(angle, point, use_radians))
835
790
  return CompoundGeometry(geometries=processed_geoms)
836
791
 
837
- def __add__(self, other: Geometry) -> CompoundGeometry:
838
- """Add operator "+" for geometries.
839
-
840
- Arguments:
841
- other (Geometry): The other geometry to add.
842
-
843
- Returns:
844
- CompoundGeometry: A new CompoundGeometry.
845
- """
846
- return CompoundGeometry([self, other])
847
-
848
792
  def __sub__(self, other: Geometry) -> CompoundGeometry:
849
793
  """Add operator "-" for geometries.
850
794
 
@@ -868,7 +812,7 @@ class CompoundGeometry(Geometry):
868
812
  @staticmethod
869
813
  def from_geometry(
870
814
  geo: CompoundGeometry,
871
- new_material: t.Optional[t.Union[Material, ConstitutiveLaw]] = None,
815
+ new_material: t.Optional[Material] = None,
872
816
  ) -> CompoundGeometry:
873
817
  """Create a new CompoundGeometry with a different material.
874
818
 
@@ -11,7 +11,7 @@ import typing as t
11
11
  from numpy.typing import ArrayLike
12
12
  from shapely import Polygon
13
13
 
14
- from structuralcodes.core.base import ConstitutiveLaw, Material
14
+ from structuralcodes.core.base import Material
15
15
 
16
16
  from ._geometry import SurfaceGeometry
17
17
 
@@ -28,8 +28,7 @@ class RectangularGeometry(SurfaceGeometry):
28
28
  self,
29
29
  width: float,
30
30
  height: float,
31
- material: t.Union[Material, ConstitutiveLaw],
32
- density: t.Optional[float] = None,
31
+ material: Material,
33
32
  concrete: bool = False,
34
33
  origin: t.Optional[ArrayLike] = None,
35
34
  name: t.Optional[str] = None,
@@ -40,12 +39,7 @@ class RectangularGeometry(SurfaceGeometry):
40
39
  Arguments:
41
40
  width (float): The width of the geometry.
42
41
  height (float): The height of the geometry.
43
- material (Union(Material, ConstitutiveLaw)): A Material or
44
- ConsitutiveLaw class applied to the geometry.
45
- density (Optional(float)): When a ConstitutiveLaw is passed as
46
- material, the density can be provided by this argument. When
47
- material is a Material object the density is taken from the
48
- material.
42
+ material (Material): A Material class applied to the geometry.
49
43
  concrete (bool): Flag to indicate if the geometry is concrete. When
50
44
  passing a Material as material, this is automatically inferred.
51
45
  origin (Optional(ArrayLike)): The center point of the rectangle.
@@ -84,7 +78,6 @@ class RectangularGeometry(SurfaceGeometry):
84
78
  super().__init__(
85
79
  poly=polygon,
86
80
  material=material,
87
- density=density,
88
81
  concrete=concrete,
89
82
  name=name,
90
83
  group_label=group_label,