structuralcodes 0.3.1__py3-none-any.whl → 0.5.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 (33) 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 +40 -0
  5. structuralcodes/codes/mc2010/_concrete_punching.py +300 -388
  6. structuralcodes/codes/mc2010/_interface_concrete_steel_rebar.py +348 -0
  7. structuralcodes/core/base.py +1 -22
  8. structuralcodes/geometry/_circular.py +3 -10
  9. structuralcodes/geometry/_geometry.py +47 -92
  10. structuralcodes/geometry/_rectangular.py +3 -10
  11. structuralcodes/geometry/_reinforcement.py +8 -11
  12. structuralcodes/materials/__init__.py +2 -1
  13. structuralcodes/materials/basic/__init__.py +11 -0
  14. structuralcodes/materials/basic/_elastic.py +52 -0
  15. structuralcodes/materials/basic/_elasticplastic.py +75 -0
  16. structuralcodes/materials/basic/_generic.py +26 -0
  17. structuralcodes/materials/concrete/_concrete.py +1 -62
  18. structuralcodes/materials/concrete/_concreteEC2_2004.py +270 -224
  19. structuralcodes/materials/concrete/_concreteEC2_2023.py +246 -188
  20. structuralcodes/materials/concrete/_concreteMC2010.py +280 -235
  21. structuralcodes/materials/constitutive_laws/__init__.py +16 -15
  22. structuralcodes/materials/reinforcement/_reinforcement.py +0 -71
  23. structuralcodes/materials/reinforcement/_reinforcementEC2_2004.py +37 -2
  24. structuralcodes/materials/reinforcement/_reinforcementEC2_2023.py +34 -1
  25. structuralcodes/materials/reinforcement/_reinforcementMC2010.py +38 -2
  26. structuralcodes/sections/_generic.py +76 -21
  27. structuralcodes/sections/_rc_utils.py +15 -5
  28. structuralcodes/sections/section_integrators/_fiber_integrator.py +19 -11
  29. structuralcodes/sections/section_integrators/_marin_integrator.py +24 -19
  30. {structuralcodes-0.3.1.dist-info → structuralcodes-0.5.0.dist-info}/METADATA +3 -2
  31. {structuralcodes-0.3.1.dist-info → structuralcodes-0.5.0.dist-info}/RECORD +33 -27
  32. {structuralcodes-0.3.1.dist-info → structuralcodes-0.5.0.dist-info}/WHEEL +1 -1
  33. structuralcodes-0.5.0.dist-info/licenses/LICENSE +201 -0
@@ -61,24 +61,25 @@ def create_constitutive_law(
61
61
 
62
62
  If the consitutive law selected is not available for the specific
63
63
  material, an exception will be raised.
64
+
65
+ Raises:
66
+ ValueError: If the constitutive law is not available for the material.
67
+ ValueError: If the constitutive law name is unknown.
64
68
  """
65
- law = None
66
69
  const_law = CONSTITUTIVE_LAWS.get(constitutive_law_name.lower())
67
70
  if const_law is not None:
68
71
  method_name = f'__{constitutive_law_name}__'
69
72
  # check if the material object has the special method needed
70
- if hasattr(material, method_name):
73
+ if hasattr(material, method_name) and callable(
74
+ getattr(material, method_name)
75
+ ):
71
76
  method = getattr(material, method_name)
72
- if callable(method):
73
- # get the kwargs from the special dunder method
74
- kwargs = method()
75
- # create the constitutive law
76
- law = const_law(**kwargs)
77
- else:
78
- raise ValueError(
79
- f'Constitutive law {constitutive_law_name} not available for'
80
- f' material {material.__class__.__name__}'
81
- )
82
- else:
83
- raise ValueError(f'Unknown constitutive law: {constitutive_law_name}')
84
- return law
77
+ # get the kwargs from the special dunder method
78
+ kwargs = method()
79
+ # create and return the constitutive law
80
+ return const_law(**kwargs)
81
+ raise ValueError(
82
+ f'Constitutive law {constitutive_law_name} not available for'
83
+ f' material {material.__class__.__name__}'
84
+ )
85
+ raise ValueError(f'Unknown constitutive law: {constitutive_law_name}')
@@ -4,10 +4,6 @@ import abc
4
4
  import typing as t
5
5
 
6
6
  from structuralcodes.core.base import ConstitutiveLaw, Material
7
- from structuralcodes.materials.constitutive_laws import (
8
- ElasticPlastic,
9
- create_constitutive_law,
10
- )
11
7
 
12
8
 
13
9
  class Reinforcement(Material):
@@ -39,45 +35,21 @@ class Reinforcement(Material):
39
35
  self._epsuk = abs(epsuk)
40
36
  self._gamma_s = gamma_s
41
37
 
42
- # Calculate the plastic hardening modulus
43
- if self.epsuk - self.fyk / self.Es > 0.0 and self.ftk - self.fyk > 0:
44
- Eh = (self.ftk - self.fyk) / (self.epsuk - self.fyk / self.Es)
45
- else:
46
- Eh = 0.0
47
- self._constitutive_law = ElasticPlastic(
48
- E=self.Es, fy=self.fyd(), Eh=Eh, eps_su=self.epsud()
49
- )
50
-
51
38
  @property
52
39
  def fyk(self) -> float:
53
40
  """Returns fyk in MPa."""
54
41
  return self._fyk
55
42
 
56
- @fyk.setter
57
- def fyk(self, fyk: float) -> None:
58
- """Setter for fyk (in MPa)."""
59
- self._fyk = abs(fyk)
60
-
61
43
  @property
62
44
  def Es(self) -> float:
63
45
  """Returns Es in MPa."""
64
46
  return self._Es
65
47
 
66
- @Es.setter
67
- def Es(self, Es: float) -> None:
68
- """Setter for Es (in MPa)."""
69
- self._Es = abs(Es)
70
-
71
48
  @property
72
49
  def ftk(self) -> float:
73
50
  """Returns ftk in MPa."""
74
51
  return self._ftk
75
52
 
76
- @ftk.setter
77
- def ftk(self, ftk: float) -> None:
78
- """Setter for ftk (in MPa)."""
79
- self._ftk = abs(ftk)
80
-
81
53
  @property
82
54
  def epsuk(self) -> float:
83
55
  """Returns epsuk."""
@@ -98,49 +70,6 @@ class Reinforcement(Material):
98
70
  """Returns the constitutive law object."""
99
71
  return self._constitutive_law
100
72
 
101
- @constitutive_law.setter
102
- def constitutive_law(
103
- self,
104
- constitutive_law: t.Union[
105
- ConstitutiveLaw,
106
- t.Literal[
107
- 'elastic',
108
- 'elasticperfecltyplastic',
109
- 'elasticplastic',
110
- ],
111
- ],
112
- ) -> None:
113
- """Setter for constitutive law.
114
-
115
- Arguments:
116
- consitutive_law (ConstitutiveLaw | str): a valid ConstitutiveLaw
117
- object for reinforcement or a string defining a valid
118
- constitutive law type for reinforcement. (valid options:
119
- 'elastic', 'elasticperfectlyplastic', 'elasticplastic').
120
- """
121
- if constitutive_law is None:
122
- raise ValueError(
123
- 'At least a constitutive law or a string defining the '
124
- 'constitutive law must be provided.'
125
- )
126
- if isinstance(constitutive_law, str):
127
- constitutive_law = create_constitutive_law(
128
- constitutive_law_name=constitutive_law, material=self
129
- )
130
-
131
- if isinstance(constitutive_law, ConstitutiveLaw):
132
- if 'rebars' in constitutive_law.__materials__:
133
- self._constitutive_law = constitutive_law
134
- else:
135
- raise ValueError(
136
- 'The constitutive law selected is not suitable '
137
- 'for being used with a Reinforcement material.'
138
- )
139
- else:
140
- raise ValueError(
141
- f'The constitutive law {constitutive_law} could not be created'
142
- )
143
-
144
73
  @property
145
74
  @abc.abstractmethod
146
75
  def gamma_s(self) -> float:
@@ -4,6 +4,7 @@ import typing as t
4
4
 
5
5
  from structuralcodes.codes import ec2_2004
6
6
 
7
+ from ..constitutive_laws import ConstitutiveLaw, create_constitutive_law
7
8
  from ._reinforcement import Reinforcement
8
9
 
9
10
 
@@ -20,6 +21,16 @@ class ReinforcementEC2_2004(Reinforcement): # noqa: N801
20
21
  gamma_eps: t.Optional[float] = None,
21
22
  name: t.Optional[str] = None,
22
23
  density: float = 7850.0,
24
+ constitutive_law: t.Optional[
25
+ t.Union[
26
+ t.Literal[
27
+ 'elastic',
28
+ 'elasticperfectlyplastic',
29
+ 'elasticplastic',
30
+ ],
31
+ ConstitutiveLaw,
32
+ ]
33
+ ] = 'elasticplastic',
23
34
  ):
24
35
  """Initializes a new instance of Reinforcement for EC2 2004.
25
36
 
@@ -33,13 +44,25 @@ class ReinforcementEC2_2004(Reinforcement): # noqa: N801
33
44
  Default value is 1.15.
34
45
 
35
46
  Keyword Arguments:
47
+ gamma_eps (float): The partial factor for ultimate strain. Default
48
+ value is 0.9.
36
49
  name (str): A descriptive name for the reinforcement.
37
50
  density (float): Density of material in kg/m3 (default: 7850).
51
+ constitutive_law (ConstitutiveLaw | str): A valid ConstitutiveLaw
52
+ object for reinforcement or a string defining a valid
53
+ constitutive law type for reinforcement. (valid options for
54
+ string: 'elastic', 'elasticplastic', or
55
+ 'elasticperfectlyplastic').
56
+
57
+ Raises:
58
+ ValueError: If the constitutive law name is not available for the
59
+ material.
60
+ ValueError: If the provided constitutive law is not valid for
61
+ reinforcement.
38
62
  """
39
63
  if name is None:
40
64
  name = f'Reinforcement{round(fyk):d}'
41
65
 
42
- self._gamma_eps = gamma_eps
43
66
  super().__init__(
44
67
  fyk=fyk,
45
68
  Es=Es,
@@ -49,6 +72,18 @@ class ReinforcementEC2_2004(Reinforcement): # noqa: N801
49
72
  epsuk=epsuk,
50
73
  gamma_s=gamma_s,
51
74
  )
75
+ self._gamma_eps = gamma_eps
76
+ self._constitutive_law = (
77
+ constitutive_law
78
+ if isinstance(constitutive_law, ConstitutiveLaw)
79
+ else create_constitutive_law(
80
+ constitutive_law_name=constitutive_law, material=self
81
+ )
82
+ )
83
+ if 'steel' not in self._constitutive_law.__materials__:
84
+ raise ValueError(
85
+ 'The provided constitutive law is not valid for reinforcement.'
86
+ )
52
87
 
53
88
  def fyd(self) -> float:
54
89
  """The design yield strength."""
@@ -94,7 +129,7 @@ class ReinforcementEC2_2004(Reinforcement): # noqa: N801
94
129
  """Returns kwargs for ElasticPlastic constitutive law with strain
95
130
  hardening.
96
131
  """
97
- Eh = (self.ftd() - self.fyd()) / (self.epsuk - self.epsyd)
132
+ Eh = (self.ftd() - self.fyd()) / (self.epsud() - self.epsyd)
98
133
  return {
99
134
  'E': self.Es,
100
135
  'fy': self.fyd(),
@@ -4,6 +4,7 @@ import typing as t
4
4
 
5
5
  from structuralcodes.codes import ec2_2023
6
6
 
7
+ from ..constitutive_laws import ConstitutiveLaw, create_constitutive_law
7
8
  from ._reinforcement import Reinforcement
8
9
 
9
10
 
@@ -19,6 +20,16 @@ class ReinforcementEC2_2023(Reinforcement): # noqa: N801
19
20
  gamma_s: t.Optional[float] = None,
20
21
  name: t.Optional[str] = None,
21
22
  density: float = 7850.0,
23
+ constitutive_law: t.Optional[
24
+ t.Union[
25
+ t.Literal[
26
+ 'elastic',
27
+ 'elasticperfectlyplastic',
28
+ 'elasticplastic',
29
+ ],
30
+ ConstitutiveLaw,
31
+ ]
32
+ ] = 'elasticplastic',
22
33
  ):
23
34
  """Initializes a new instance of Reinforcement for EC2 2023.
24
35
 
@@ -34,6 +45,17 @@ class ReinforcementEC2_2023(Reinforcement): # noqa: N801
34
45
  Keyword Args:
35
46
  name (str): A descriptive name for the reinforcement.
36
47
  density (float): Density of material in kg/m3 (default: 7850).
48
+ constitutive_law (ConstitutiveLaw | str): A valid ConstitutiveLaw
49
+ object for reinforcement or a string defining a valid
50
+ constitutive law type for reinforcement. (valid options for
51
+ string: 'elastic', 'elasticplastic', or
52
+ 'elasticperfectlyplastic').
53
+
54
+ Raises:
55
+ ValueError: If the constitutive law name is not available for the
56
+ material.
57
+ ValueError: If the provided constitutive law is not valid for
58
+ reinforcement.
37
59
  """
38
60
  if name is None:
39
61
  name = f'Reinforcement{round(fyk):d}'
@@ -46,6 +68,17 @@ class ReinforcementEC2_2023(Reinforcement): # noqa: N801
46
68
  epsuk=epsuk,
47
69
  gamma_s=gamma_s,
48
70
  )
71
+ self._constitutive_law = (
72
+ constitutive_law
73
+ if isinstance(constitutive_law, ConstitutiveLaw)
74
+ else create_constitutive_law(
75
+ constitutive_law_name=constitutive_law, material=self
76
+ )
77
+ )
78
+ if 'steel' not in self._constitutive_law.__materials__:
79
+ raise ValueError(
80
+ 'The provided constitutive law is not valid for reinforcement.'
81
+ )
49
82
 
50
83
  def fyd(self) -> float:
51
84
  """The design yield strength."""
@@ -84,7 +117,7 @@ class ReinforcementEC2_2023(Reinforcement): # noqa: N801
84
117
  """Returns kwargs for ElasticPlastic constitutive law with strain
85
118
  hardening.
86
119
  """
87
- Eh = (self.ftd() - self.fyd()) / (self.epsuk - self.epsyd)
120
+ Eh = (self.ftd() - self.fyd()) / (self.epsud() - self.epsyd)
88
121
  return {
89
122
  'E': self.Es,
90
123
  'fy': self.fyd(),
@@ -4,6 +4,7 @@ import typing as t
4
4
 
5
5
  from structuralcodes.codes import mc2010
6
6
 
7
+ from ..constitutive_laws import ConstitutiveLaw, create_constitutive_law
7
8
  from ._reinforcement import Reinforcement
8
9
 
9
10
 
@@ -20,6 +21,16 @@ class ReinforcementMC2010(Reinforcement):
20
21
  gamma_eps: t.Optional[float] = None,
21
22
  name: t.Optional[str] = None,
22
23
  density: float = 7850.0,
24
+ constitutive_law: t.Optional[
25
+ t.Union[
26
+ t.Literal[
27
+ 'elastic',
28
+ 'elasticperfectlyplastic',
29
+ 'elasticplastic',
30
+ ],
31
+ ConstitutiveLaw,
32
+ ]
33
+ ] = 'elasticplastic',
23
34
  ):
24
35
  """Initializes a new instance of Reinforcement for MC2010.
25
36
 
@@ -33,12 +44,25 @@ class ReinforcementMC2010(Reinforcement):
33
44
  Default value is 1.15.
34
45
 
35
46
  Keyword Args:
47
+ gamma_eps (float): The partial factor for ultimate strain. Default
48
+ value is 0.9.
36
49
  name (str): A descriptive name for the reinforcement.
37
50
  density (float): Density of material in kg/m3 (default: 7850).
51
+ constitutive_law (ConstitutiveLaw | str): A valid ConstitutiveLaw
52
+ object for reinforcement or a string defining a valid
53
+ constitutive law type for reinforcement. (valid options for
54
+ string: 'elastic', 'elasticplastic', or
55
+ 'elasticperfectlyplastic').
56
+
57
+ Raises:
58
+ ValueError: If the constitutive law name is not available for the
59
+ material.
60
+ ValueError: If the provided constitutive law is not valid for
61
+ reinforcement.
38
62
  """
39
63
  if name is None:
40
64
  name = f'Reinforcement{round(fyk):d}'
41
- self._gamma_eps = gamma_eps
65
+
42
66
  super().__init__(
43
67
  fyk=fyk,
44
68
  Es=Es,
@@ -48,6 +72,18 @@ class ReinforcementMC2010(Reinforcement):
48
72
  epsuk=epsuk,
49
73
  gamma_s=gamma_s,
50
74
  )
75
+ self._gamma_eps = gamma_eps
76
+ self._constitutive_law = (
77
+ constitutive_law
78
+ if isinstance(constitutive_law, ConstitutiveLaw)
79
+ else create_constitutive_law(
80
+ constitutive_law_name=constitutive_law, material=self
81
+ )
82
+ )
83
+ if 'steel' not in self._constitutive_law.__materials__:
84
+ raise ValueError(
85
+ 'The provided constitutive law is not valid for reinforcement.'
86
+ )
51
87
 
52
88
  def fyd(self) -> float:
53
89
  """The design yield strength."""
@@ -89,7 +125,7 @@ class ReinforcementMC2010(Reinforcement):
89
125
  """Returns kwargs for ElasticPlastic constitutive law with strain
90
126
  hardening.
91
127
  """
92
- Eh = (self.ftd() - self.fyd()) / (self.epsuk - self.epsyd)
128
+ Eh = (self.ftd() - self.fyd()) / (self.epsud() - self.epsyd)
93
129
  return {
94
130
  'E': self.Es,
95
131
  'fy': self.fyd(),
@@ -19,7 +19,7 @@ from structuralcodes.geometry import (
19
19
  PointGeometry,
20
20
  SurfaceGeometry,
21
21
  )
22
- from structuralcodes.materials.constitutive_laws import Elastic
22
+ from structuralcodes.materials.basic import ElasticMaterial
23
23
 
24
24
  from .section_integrators import SectionIntegrator, integrator_factory
25
25
 
@@ -42,6 +42,8 @@ class GenericSection(Section):
42
42
  strength, moment curvature, etc.).
43
43
  """
44
44
 
45
+ geometry: CompoundGeometry
46
+
45
47
  def __init__(
46
48
  self,
47
49
  geometry: t.Union[SurfaceGeometry, CompoundGeometry],
@@ -96,6 +98,7 @@ class GenericSectionCalculator(SectionCalculator):
96
98
  """Calculator class implementing analysis algorithms for code checks."""
97
99
 
98
100
  integrator: SectionIntegrator
101
+ section: GenericSection
99
102
 
100
103
  def __init__(
101
104
  self,
@@ -155,13 +158,17 @@ class GenericSectionCalculator(SectionCalculator):
155
158
  # Computation of surface area, reinforcement area, EA (axial rigidity)
156
159
  # and mass: Morten -> problem with units! how do we deal with it?
157
160
  for geo in self.section.geometry.geometries:
158
- gp.ea += geo.area * geo.material.get_tangent(eps=0)
161
+ gp.ea += geo.area * geo.material.constitutive_law.get_tangent(
162
+ eps=0
163
+ )
159
164
  if geo.density is not None:
160
165
  # this assumes area in mm2 and density in kg/m3
161
166
  gp.mass += geo.area * geo.density * 1e-9
162
167
 
163
168
  for geo in self.section.geometry.point_geometries:
164
- gp.ea += geo.area * geo.material.get_tangent(eps=0)
169
+ gp.ea += geo.area * geo.material.constitutive_law.get_tangent(
170
+ eps=0
171
+ )
165
172
  gp.area_reinforcement += geo.area
166
173
  if geo.density is not None:
167
174
  # this assumes area in mm2 and density in kg/m3
@@ -237,7 +244,7 @@ class GenericSectionCalculator(SectionCalculator):
237
244
 
238
245
  # Create a dummy material for integration of area moments
239
246
  # This is used for J, S etc, not for E_J E_S etc
240
- dummy_mat = Elastic(E=1)
247
+ dummy_mat = ElasticMaterial(E=1, density=1)
241
248
  # Computation of moments of area (material-independet)
242
249
  # Note: this could be un-meaningfull when many materials
243
250
  # are combined
@@ -309,15 +316,40 @@ class GenericSectionCalculator(SectionCalculator):
309
316
  chi_min = 1e10
310
317
  for g in geom.geometries + geom.point_geometries:
311
318
  for other_g in geom.geometries + geom.point_geometries:
319
+ # This is left on purpose: even if tempted we should not do
320
+ # this check:
312
321
  # if g != other_g:
313
- eps_p = g.material.get_ultimate_strain(yielding=yielding)[1]
322
+ eps_p = g.material.constitutive_law.get_ultimate_strain(
323
+ yielding=yielding
324
+ )[1]
314
325
  if isinstance(g, SurfaceGeometry):
315
326
  y_p = g.polygon.bounds[1]
316
327
  elif isinstance(g, PointGeometry):
317
328
  y_p = g._point.coords[0][1]
318
- eps_n = other_g.material.get_ultimate_strain(
319
- yielding=yielding
329
+ # Check if the section is a reinforced concrete section:
330
+ # If it is, we need to obtain the "yield" strain of concrete
331
+ # (-0.002 for default parabola-rectangle concrete)
332
+ # If the geometry is not concrete, don't get the yield strain
333
+ # If it is not a reinforced concrete section, return
334
+ # the yield strain if asked.
335
+ is_rc_section = self.section.geometry.reinforced_concrete
336
+ is_concrete_geom = (
337
+ isinstance(other_g, SurfaceGeometry) and other_g.concrete
338
+ )
339
+
340
+ use_yielding = (
341
+ yielding
342
+ if (
343
+ (is_rc_section and is_concrete_geom)
344
+ or (not is_rc_section)
345
+ )
346
+ else False
347
+ )
348
+
349
+ eps_n = other_g.material.constitutive_law.get_ultimate_strain(
350
+ yielding=use_yielding
320
351
  )[0]
352
+
321
353
  if isinstance(other_g, SurfaceGeometry):
322
354
  y_n = other_g.polygon.bounds[3]
323
355
  elif isinstance(other_g, PointGeometry):
@@ -439,12 +471,18 @@ class GenericSectionCalculator(SectionCalculator):
439
471
  apply the bisection algorithm.
440
472
  """
441
473
  ITMAX = 20
474
+ MAXRESTATTEMPTS = 20
442
475
  sign = -1 if dn_a > 0 else 1
443
476
  found = False
444
477
  it = 0
478
+ restarts = 0
445
479
  delta = 1e-3
446
- while not found and it < ITMAX:
447
- eps_0_b = eps_0_a + sign * delta * (it + 1)
480
+ # Use a growth factor for an exponential finding
481
+ r = 2.0
482
+ diverging = False
483
+ diverging_steps = 0
484
+ while not found and it < ITMAX and restarts < MAXRESTATTEMPTS:
485
+ eps_0_b = eps_0_a + sign * delta * r ** (it)
448
486
  (
449
487
  n_int,
450
488
  _,
@@ -457,10 +495,20 @@ class GenericSectionCalculator(SectionCalculator):
457
495
  if dn_a * dn_b < 0:
458
496
  found = True
459
497
  elif abs(dn_b) > abs(dn_a):
460
- # we are driving aay from the solution, probably due
498
+ # we are driving away from the solution, probably due
461
499
  # to failure of a material
462
- delta /= 2
463
- it -= 1
500
+ diverging = True
501
+ if diverging:
502
+ # Count for how many steps we are diverging
503
+ diverging_steps += 1
504
+ # If we are consistently diverging for more than 10 steps,
505
+ # Restart the process with a small delta
506
+ if diverging_steps > 10:
507
+ delta /= 2
508
+ it = 0
509
+ restarts += 1
510
+ diverging = False
511
+ diverging_steps = 0
464
512
  it += 1
465
513
  if it >= ITMAX and not found:
466
514
  s = f'Last iteration reached a unbalance of: \
@@ -567,14 +615,20 @@ class GenericSectionCalculator(SectionCalculator):
567
615
 
568
616
  @property
569
617
  def n_min(self) -> float:
570
- """Return minimum axial load."""
618
+ """Return minimum axial load.
619
+
620
+ In most situations, this is the capacity in compression.
621
+ """
571
622
  if self._n_min is None:
572
623
  self._n_min, self._n_max = self.calculate_limit_axial_load()
573
624
  return self._n_min
574
625
 
575
626
  @property
576
627
  def n_max(self) -> float:
577
- """Return maximum axial load."""
628
+ """Return maximum axial load.
629
+
630
+ In most situations, this is the capacity in tension.
631
+ """
578
632
  if self._n_max is None:
579
633
  self._n_min, self._n_max = self.calculate_limit_axial_load()
580
634
  return self._n_max
@@ -631,7 +685,7 @@ class GenericSectionCalculator(SectionCalculator):
631
685
  matrix then `integrate='modulus'`.
632
686
 
633
687
  Examples:
634
- result = self.integrate_strain_profile(strain,integrate='tangent')
688
+ result = self.integrate_strain_profile(strain,integrate='modulus')
635
689
  # `result` will be the tangent stiffness matrix (a 3x3 numpy array)
636
690
 
637
691
  result = self.integrate_strain_profile(strain)
@@ -678,6 +732,9 @@ class GenericSectionCalculator(SectionCalculator):
678
732
  Returns:
679
733
  UltimateBendingMomentResults: The results from the calculation.
680
734
  """
735
+ # Check if the section can carry the axial load
736
+ self.check_axial_load(n=n)
737
+
681
738
  # Compute the bending strength with the bisection algorithm
682
739
  # Rotate the section of angle theta
683
740
  rotated_geom = self.section.geometry.rotate(-theta)
@@ -685,8 +742,6 @@ class GenericSectionCalculator(SectionCalculator):
685
742
  # Rotate also triangulated data!
686
743
  self._rotate_triangulated_data(-theta)
687
744
 
688
- # Check if the section can carry the axial load
689
- self.check_axial_load(n=n)
690
745
  # Find the strain distribution corresponding to failure and equilibrium
691
746
  # with external axial force
692
747
  strain = self.find_equilibrium_fixed_pivot(rotated_geom, n)
@@ -752,6 +807,9 @@ class GenericSectionCalculator(SectionCalculator):
752
807
  Returns:
753
808
  MomentCurvatureResults: The calculation results.
754
809
  """
810
+ # Check if the section can carry the axial load
811
+ self.check_axial_load(n=n)
812
+
755
813
  # Create an empty response object
756
814
  res = s_res.MomentCurvatureResults()
757
815
  res.n = n
@@ -761,9 +819,6 @@ class GenericSectionCalculator(SectionCalculator):
761
819
  # Rotate also triangulated data!
762
820
  self._rotate_triangulated_data(-theta)
763
821
 
764
- # Check if the section can carry the axial load
765
- self.check_axial_load(n=n)
766
-
767
822
  if chi is None:
768
823
  # Find ultimate curvature from the strain distribution
769
824
  # corresponding to failure and equilibrium with external axial
@@ -1342,7 +1397,7 @@ class GenericSectionCalculator(SectionCalculator):
1342
1397
  n (float): Axial load.
1343
1398
  my (float): Bending moment around y-axis.
1344
1399
  mz (float): Bending moment around z-axis.
1345
- initial (bool): If True the modified newton with initial tanget is
1400
+ initial (bool): If True the modified newton with initial tangent is
1346
1401
  used (default = False).
1347
1402
  max_iter (int): the maximum number of iterations in the iterative
1348
1403
  process (default = 10).
@@ -4,7 +4,8 @@ import typing as t
4
4
 
5
5
  import structuralcodes.core._section_results as s_res
6
6
  from structuralcodes.geometry import CompoundGeometry, SurfaceGeometry
7
- from structuralcodes.materials.constitutive_laws import Elastic, UserDefined
7
+ from structuralcodes.materials.basic import ElasticMaterial, GenericMaterial
8
+ from structuralcodes.materials.constitutive_laws import UserDefined
8
9
  from structuralcodes.sections import GenericSection
9
10
  from structuralcodes.sections.section_integrators import FiberIntegrator
10
11
 
@@ -57,13 +58,22 @@ def calculate_elastic_cracked_properties(
57
58
  rotated_geometry = section.geometry.rotate(-theta)
58
59
 
59
60
  for geo in rotated_geometry.geometries:
60
- Ec = geo.material.get_tangent(eps=0)
61
- elastic_concrete = UserDefined([-100, 0], [-100 * Ec, 0])
61
+ Ec = geo.material.constitutive_law.get_tangent(eps=0)
62
+ density = geo.material.density
63
+ elastic_concrete_law = UserDefined([-100, 0], [-100 * Ec, 0])
64
+ elastic_concrete = GenericMaterial(
65
+ density=density,
66
+ constitutive_law=elastic_concrete_law,
67
+ name='elastic concrete',
68
+ )
62
69
  geo._material = elastic_concrete
63
70
 
64
71
  for pg in rotated_geometry.point_geometries:
65
- Es = pg.material.get_tangent(eps=0)
66
- elastic_steel = Elastic(Es, 'elastic steel')
72
+ Es = pg.material.constitutive_law.get_tangent(eps=0)
73
+ density = pg.material.density
74
+ elastic_steel = ElasticMaterial(
75
+ E=Es, density=density, name='elastic steel'
76
+ )
67
77
  pg._material = elastic_steel
68
78
 
69
79
  curv = -1e-5 # Any curvature should return the same mechanical properties.