structuralcodes 0.3.0__py3-none-any.whl → 0.4.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.

@@ -5,17 +5,15 @@ import warnings
5
5
 
6
6
  from structuralcodes.codes import ec2_2023
7
7
 
8
+ from ..constitutive_laws import ConstitutiveLaw, create_constitutive_law
8
9
  from ._concrete import Concrete
9
10
 
10
11
 
11
12
  class ConcreteEC2_2023(Concrete): # noqa: N801
12
13
  """Concrete implementation for EC2 2023 Concrete."""
13
14
 
14
- # Inherent concrete properties
15
15
  _kE: t.Optional[float] = None # noqa: N815
16
16
  _strength_dev_class: t.Optional[str] = None
17
-
18
- # Computed attributes
19
17
  _fcm: t.Optional[float] = None
20
18
  _fctm: t.Optional[float] = None
21
19
  _Ecm: t.Optional[float] = None
@@ -38,7 +36,29 @@ class ConcreteEC2_2023(Concrete): # noqa: N801
38
36
  'CS', 'CN', 'CR', 'slow', 'normal', 'rapid'
39
37
  ] = 'CN',
40
38
  gamma_c: t.Optional[float] = None,
41
- existing: bool = False,
39
+ constitutive_law: t.Optional[
40
+ t.Union[
41
+ t.Literal[
42
+ 'elastic',
43
+ 'parabolarectangle',
44
+ 'bilinearcompression',
45
+ 'sargin',
46
+ 'popovics',
47
+ ],
48
+ ConstitutiveLaw,
49
+ ]
50
+ ] = 'parabolarectangle',
51
+ fcm: t.Optional[float] = None,
52
+ fctm: t.Optional[float] = None,
53
+ fctk_5: t.Optional[float] = None,
54
+ fctk_95: t.Optional[float] = None,
55
+ Ecm: t.Optional[float] = None,
56
+ eps_c1: t.Optional[float] = None,
57
+ eps_cu1: t.Optional[float] = None,
58
+ k_sargin: t.Optional[float] = None,
59
+ eps_c2: t.Optional[float] = None,
60
+ eps_cu2: t.Optional[float] = None,
61
+ n_parabolic_rectangular: t.Optional[float] = None,
42
62
  **kwargs,
43
63
  ):
44
64
  """Initializes a new instance of Concrete for EC2 2023.
@@ -55,84 +75,185 @@ class ConcreteEC2_2023(Concrete): # noqa: N801
55
75
  CS, CN, CR, slow, normal or rapid.
56
76
  gamma_c (float, optional): Partial factor of concrete (default is
57
77
  1.5).
58
- existing (bool, optional): The material is of an existing structure
59
- (default: False).
78
+ consitutive_law (ConstitutiveLaw | str): A valid ConstitutiveLaw
79
+ object for concrete or a string defining a valid constitutive
80
+ law type for concrete. (valid options for string: 'elastic',
81
+ 'parabolarectangle', 'bilinearcompression', 'sargin',
82
+ 'popovics').
83
+ fcm (float, optional): The mean compressive strength.
84
+ fctm (float, optional): The mean tensile strength.
85
+ fctk_5 (float, optional): The 5% fractile for the tensile strength.
86
+ fctk_95 (float, optional): The 95% fractile for the tensile
87
+ strength.
88
+ Ecm (float, optional): The mean secant Young's modulus.
89
+ eps_c1 (float, optional): The strain at peak stress for the Sargin
90
+ constitutive law.
91
+ eps_cu1 (float, optional): The ultimate strain for the Sargin
92
+ constitutive law.
93
+ k_sargin (float, optional): The coefficient for the Sargin
94
+ constitutive law.
95
+ eps_c2 (float, optional): The strain at peak stress for the
96
+ parabolic rectangular constitutive law.
97
+ eps_cu2 (float, optional): The ultimate strain for the parabolic
98
+ rectangular constitutive law.
99
+ n_parabolic_rectangular (float, optional): The coefficient for the
100
+ parabolic rectangular constitutive law.
101
+
102
+ Raises:
103
+ ValueError: If the provided strength development class is invalid.
104
+ ValueError: If gamma_c is less than zero.
105
+ ValueError: If fcm is less than fck.
106
+ ValueError: If k_sargin is negative.
107
+ ValueError: If n_parabolic_rectangular is negative.
108
+ ValueError: If the constitutive law name is not available for the
109
+ material.
110
+ ValueError: If the provided constitutive law is not valid for
111
+ concrete.
112
+ Warning: If eps_c1 is larger than 0.1.
113
+ Warning: If eps_cu1 is larger than 0.1.
114
+ Warning: If eps_c2 is larger than 0.1.
115
+ Warning: If eps_cu2 is larger than 0.1.
60
116
  """
61
117
  del kwargs
62
118
  if name is None:
63
119
  name = f'C{round(fck):d}'
64
120
 
65
- # Check if strength_dev_class is valid
66
- strength_dev_class = strength_dev_class.strip()
67
- valid_dev_classes = ('cs', 'cn', 'cr', 'slow', 'normal', 'rapid')
68
- if strength_dev_class.lower() not in valid_dev_classes:
69
- raise ValueError(
70
- 'strength_dev_class not valid. '
71
- + f'{strength_dev_class} not {valid_dev_classes}'
72
- )
73
-
74
- # Check for valid security coeff
75
- if gamma_c is not None and gamma_c <= 0:
76
- raise ValueError(
77
- f'gamma_c cannot be less than zero. Current: {gamma_c}'
78
- )
79
-
80
121
  super().__init__(
81
122
  fck=fck,
82
123
  name=name,
83
124
  density=density,
84
- existing=existing,
125
+ existing=False,
85
126
  gamma_c=gamma_c,
86
127
  )
87
128
  self._kE = kE
88
- self._strength_dev_class = strength_dev_class
89
-
90
- def _reset_attributes(self):
91
- """Reset computed properties."""
92
- # We only need to reset computed properties
93
- self._fcm = None
94
- self._fctm = None
95
- self._Ecm = None
96
- self._fctk_5 = None
97
- self._fctk_95 = None
98
- self._eps_c1 = None
99
- self._eps_cu1 = None
100
- self._k_sargin = None
101
- self._eps_c2 = None
102
- self._eps_cu2 = None
103
- self._n_parabolic_rectangular = None
129
+ self._strength_dev_class = strength_dev_class.strip().lower()
130
+ self._fcm = abs(fcm) if fcm is not None else None
131
+ self._fctm = abs(fctm) if fctm is not None else None
132
+ self._fctk_5 = abs(fctk_5) if fctk_5 is not None else None
133
+ self._fctk_95 = abs(fctk_95) if fctk_95 is not None else None
134
+ self._Ecm = abs(Ecm) if Ecm is not None else None
135
+ self._eps_c1 = abs(eps_c1) if eps_c1 is not None else None
136
+ self._eps_cu1 = abs(eps_cu1) if eps_cu1 is not None else None
137
+ self._k_sargin = k_sargin if k_sargin is not None else None
138
+ self._eps_c2 = abs(eps_c2) if eps_c2 is not None else None
139
+ self._eps_cu2 = abs(eps_cu2) if eps_cu2 is not None else None
140
+ self._n_parabolic_rectangular = (
141
+ n_parabolic_rectangular
142
+ if n_parabolic_rectangular is not None
143
+ else None
144
+ )
104
145
 
105
- @property
106
- def fcm(self) -> float:
107
- """Returns the mean strength of concrete.
146
+ self.__post_init__()
108
147
 
109
- Returns:
110
- float: The mean compressive strength in MPa.
111
- """
112
- self._fcm = self._fcm or ec2_2023.fcm(self.fck)
113
- return self._fcm
148
+ # The constitutive law requires valid attributes, so it should be set
149
+ # after validation
150
+ self._constitutive_law = (
151
+ constitutive_law
152
+ if isinstance(constitutive_law, ConstitutiveLaw)
153
+ else create_constitutive_law(
154
+ constitutive_law_name=constitutive_law, material=self
155
+ )
156
+ )
157
+ if 'concrete' not in self._constitutive_law.__materials__:
158
+ raise ValueError(
159
+ 'The provided constitutive law is not valid for concrete.'
160
+ )
114
161
 
115
- @fcm.setter
116
- def fcm(self, value: float):
117
- """Sets a user defined value for the mean strength of concrete.
162
+ def __post_init__(self):
163
+ """Validator for the attributes that are set in the constructor."""
164
+ # Check if strength_dev_class is valid
165
+ valid_dev_classes = ('cs', 'cn', 'cr', 'slow', 'normal', 'rapid')
166
+ if self._strength_dev_class not in valid_dev_classes:
167
+ raise ValueError(
168
+ 'strength_dev_class not valid. '
169
+ + f'{self._strength_dev_class} not {valid_dev_classes}'
170
+ )
118
171
 
119
- Arguments:
120
- value (float): the value of the mean strength of concrete in MPa.
172
+ # Check for valid partial factor
173
+ if self._gamma_c is not None and self._gamma_c <= 0:
174
+ raise ValueError(
175
+ f'gamma_c cannot be less than zero. Current: {self._gamma_c}'
176
+ )
121
177
 
122
- Raises:
123
- ValueError: If value is less or equal than the value of fck.
124
- """
125
- if abs(value) <= self._fck:
178
+ # Check the mean compressive strength
179
+ if self._fcm is not None and self._fcm <= self._fck:
126
180
  raise ValueError(
127
181
  (
128
182
  'Mean compressive strength cannot be lower than',
129
183
  'characteristic strength.\n',
130
184
  'Current characteristing strength: ',
131
185
  f'fck = {self._fck}.',
132
- f'Current value: value = {value}',
186
+ f'Current value: value = {self._fcm}',
133
187
  )
134
188
  )
135
- self._fcm = value
189
+
190
+ # Check eps_c1
191
+ if self._eps_c1 is not None and self._eps_c1 >= 0.1:
192
+ warnings.warn(
193
+ 'A suspect value is input for eps_c1 that should be a pure'
194
+ f' number without units. Please check ({self._eps_c1} given).'
195
+ )
196
+
197
+ # Check eps_cu1
198
+ if self._eps_cu1 is not None and self._eps_cu1 >= 0.1:
199
+ warnings.warn(
200
+ 'A suspect value is input for eps_cu1 that should be a pure'
201
+ f' number without units. Please check ({self._eps_cu1} given).'
202
+ )
203
+
204
+ # Check k_sargin
205
+ if self._k_sargin is not None and self._k_sargin < 0:
206
+ raise ValueError(
207
+ f'k_sargin should be a positive value ({self._k_sargin} given)'
208
+ )
209
+
210
+ # Check eps_c2
211
+ if self._eps_c2 is not None and self._eps_c2 >= 0.1:
212
+ warnings.warn(
213
+ 'A suspect value is input for eps_c2 that should be a pure'
214
+ f' number without units. Please check ({self._eps_c2} given).'
215
+ )
216
+
217
+ # Check eps_cu2
218
+ if self._eps_cu2 is not None and self._eps_cu2 >= 0.1:
219
+ warnings.warn(
220
+ 'A suspect value is input for eps_cu2 that should be a pure'
221
+ f' number without units. Please check ({self._eps_cu2} given).'
222
+ )
223
+
224
+ # Check n_parabolic_rectangular
225
+ if (
226
+ self._n_parabolic_rectangular is not None
227
+ and self._n_parabolic_rectangular < 0
228
+ ):
229
+ raise ValueError(
230
+ 'n should be a positive value '
231
+ f'({self._n_parabolic_rectangular} given)'
232
+ )
233
+ if (
234
+ self._n_parabolic_rectangular is not None
235
+ and self._n_parabolic_rectangular >= 5
236
+ ):
237
+ warnings.warn(
238
+ 'A suspect value is input for eps_cu2 that should be a pure'
239
+ ' number without units. Please check '
240
+ f'({self._n_parabolic_rectangular} given).'
241
+ )
242
+
243
+ @property
244
+ def fcm(self) -> float:
245
+ """Returns the mean strength of concrete.
246
+
247
+ Returns:
248
+ float: The mean compressive strength in MPa.
249
+
250
+ Note:
251
+ The returned value is derived from fck if fcm is not manually
252
+ provided when initializing the object.
253
+ """
254
+ if self._fcm is None:
255
+ return ec2_2023.fcm(self.fck)
256
+ return self._fcm
136
257
 
137
258
  @property
138
259
  def fctm(self) -> None:
@@ -140,38 +261,43 @@ class ConcreteEC2_2023(Concrete): # noqa: N801
140
261
 
141
262
  Returns:
142
263
  float: The mean concrete tensile strength.
143
- """
144
- self._fctm = self._fctm or ec2_2023.fctm(self.fck)
145
- return self._fctm
146
264
 
147
- @fctm.setter
148
- def fctm(self, value: float):
149
- """Sets a custom user defined value for the concrete tensile strength
150
- for the concrete.
151
-
152
- Arguments:
153
- value (float): The new value for fctm in MPa.
265
+ Note:
266
+ The returned value is derived from fck if fctm is not manually
267
+ provided when initializing the object.
154
268
  """
155
- self._fctm = value
269
+ if self._fctm is None:
270
+ return ec2_2023.fctm(self.fck)
271
+ return self._fctm
156
272
 
157
273
  @property
158
274
  def fctk_5(self) -> float:
159
- """Returns the 5% mean concrete tensile strength fractile.
275
+ """Returns the 5% fractile for the concrete tensile strength.
160
276
 
161
277
  Returns:
162
- float: The 5% mean concrete tensile strength fractile in MPa.
278
+ float: The 5% fractile for the concrete tensile strength in MPa.
279
+
280
+ Note:
281
+ The returned value is derived from fctm if fctk_5 is not manually
282
+ provided when initializing the object.
163
283
  """
164
- self._fctk_5 = self._fctk_5 or ec2_2023.fctk_5(self.fctm)
284
+ if self._fctk_5 is None:
285
+ return ec2_2023.fctk_5(self.fctm)
165
286
  return self._fctk_5
166
287
 
167
288
  @property
168
289
  def fctk_95(self) -> float:
169
- """Returns the 95% mean concrete tensile strength fractile.
290
+ """Returns the 95% fractile for the concrete tensile strength.
170
291
 
171
292
  Returns:
172
- float: The 5% mean concrete tensile strength fractile in MPa.
293
+ float: The 5% fractile for the concrete tensile strength in MPa.
294
+
295
+ Note:
296
+ The returned value is derived from fctm if fctk_95 is not manually
297
+ provided when initializing the object.
173
298
  """
174
- self._fctk_95 = self._fctk_95 or ec2_2023.fctk_95(self.fctm)
299
+ if self._fctk_95 is None:
300
+ return ec2_2023.fctk_95(self.fctm)
175
301
  return self._fctk_95
176
302
 
177
303
  @property
@@ -180,18 +306,14 @@ class ConcreteEC2_2023(Concrete): # noqa: N801
180
306
 
181
307
  Returns:
182
308
  float: The secant concrete modulus in MPa.
183
- """
184
- self._Ecm = self._Ecm or ec2_2023.Ecm(self.fcm, self._kE)
185
- return self._Ecm
186
-
187
- @Ecm.setter
188
- def Ecm(self, value: float) -> None:
189
- """Sets the secant modulus.
190
309
 
191
- Arguments:
192
- float: The secand modulus value in MPa.
310
+ Note:
311
+ The returned value is derived from fcm and kE if Ecm is not
312
+ manually provided when initializing the object.
193
313
  """
194
- self._Ecm = value
314
+ if self._Ecm is None:
315
+ return ec2_2023.Ecm(self.fcm, self._kE)
316
+ return self._Ecm
195
317
 
196
318
  def fcd(
197
319
  self, t_ref: float = 28, t0: float = 91, fck_ref: float = 40
@@ -242,24 +364,14 @@ class ConcreteEC2_2023(Concrete): # noqa: N801
242
364
 
243
365
  Returns:
244
366
  float: The strain at maximum compressive strength of concrete.
245
- """
246
- self._eps_c1 = self._eps_c1 or ec2_2023.eps_c1(self.fcm)
247
- return self._eps_c1
248
367
 
249
- @eps_c1.setter
250
- def eps_c1(self, value: float):
251
- """Sets a user defined value for strain at peak strength for Sargin
252
- constitutive law.
253
-
254
- Arguments:
255
- value (float): The new value for eps_c1, no units.
368
+ Note:
369
+ The returned value is derived from fcm if eps_c1 is not manually
370
+ provided when initializing the object.
256
371
  """
257
- if abs(value) >= 0.1:
258
- warnings.warn(
259
- 'A suspect value is input for eps_c1 that should be a pure'
260
- ' number without units. Plase check ({value} given).'
261
- )
262
- self._eps_c1 = value
372
+ if self._eps_c1 is None:
373
+ return ec2_2023.eps_c1(self.fcm)
374
+ return self._eps_c1
263
375
 
264
376
  @property
265
377
  def eps_cu1(self) -> float:
@@ -267,23 +379,14 @@ class ConcreteEC2_2023(Concrete): # noqa: N801
267
379
 
268
380
  Returns:
269
381
  float: The maximum strength at failure of concrete.
270
- """
271
- self._eps_cu1 = self._eps_cu1 or ec2_2023.eps_cu1(self.fcm)
272
- return self._eps_cu1
273
-
274
- @eps_cu1.setter
275
- def eps_cu1(self, value: float):
276
- """Sets the nominal ultimate strain for Sargin constitutive law.
277
382
 
278
- Arguments:
279
- value (float): The new value for eps_cu1, no units.
383
+ Note:
384
+ The returned value is derived from fcm if eps_cu1 is not manually
385
+ provided when initializing the object.
280
386
  """
281
- if abs(value) >= 0.1:
282
- warnings.warn(
283
- 'A suspect value is input for eps_cu1 that should be a pure'
284
- ' number without units. Plase check ({value} given).'
285
- )
286
- self._eps_cu1 = value
387
+ if self._eps_cu1 is None:
388
+ return ec2_2023.eps_cu1(self.fcm)
389
+ return self._eps_cu1
287
390
 
288
391
  @property
289
392
  def k_sargin(self) -> float:
@@ -291,27 +394,18 @@ class ConcreteEC2_2023(Concrete): # noqa: N801
291
394
 
292
395
  Returns:
293
396
  float: k coefficient for Sargin constitutive law.
294
- """
295
- self._k_sargin = self._k_sargin or ec2_2023.k_sargin(
296
- Ecm=self.Ecm,
297
- fcm=self.fcm,
298
- eps_c1=self.eps_c1,
299
- )
300
- return self._k_sargin
301
-
302
- @k_sargin.setter
303
- def k_sargin(self, value: float):
304
- """Sets the the coefficient for Sargin constitutive law.
305
-
306
- Arguments:
307
- value (float): The new value for k, no units.
308
397
 
309
- Raises:
310
- ValueError: If value < 0.
398
+ Note:
399
+ The returned value is derived from Ecm, fcm, and eps_c1 if k_sargin
400
+ is not manually provided when initializing the object.
311
401
  """
312
- if value < 0:
313
- raise ValueError(f'n should be a positive value ({value} given)')
314
- self._k_sargin = value
402
+ if self._k_sargin is None:
403
+ return ec2_2023.k_sargin(
404
+ Ecm=self.Ecm,
405
+ fcm=self.fcm,
406
+ eps_c1=self.eps_c1,
407
+ )
408
+ return self._k_sargin
315
409
 
316
410
  @property
317
411
  def eps_c2(self) -> float:
@@ -320,24 +414,14 @@ class ConcreteEC2_2023(Concrete): # noqa: N801
320
414
 
321
415
  Returns:
322
416
  float: The strain at maximum compressive strength of concrete.
323
- """
324
- self._eps_c2 = self._eps_c2 or ec2_2023.eps_c2()
325
- return self._eps_c2
326
-
327
- @eps_c2.setter
328
- def eps_c2(self, value: float):
329
- """Sets the strain at maximum compressive strength of concrete (fcd)
330
- for the Parabola-rectangle constitutive law.
331
417
 
332
- Arguments:
333
- value (float): The new value for eps_c2, no units.
418
+ Note:
419
+ The returned value is the default value in the design code if not
420
+ manually provided when initializing the object.
334
421
  """
335
- if abs(value) >= 0.1:
336
- warnings.warn(
337
- 'A suspect value is input for eps_c2 that should be a pure'
338
- ' number without units. Plase check ({value} given).'
339
- )
340
- self._eps_c2 = value
422
+ if self._eps_c2 is None:
423
+ return ec2_2023.eps_c2()
424
+ return self._eps_c2
341
425
 
342
426
  @property
343
427
  def eps_cu2(self) -> float:
@@ -346,24 +430,14 @@ class ConcreteEC2_2023(Concrete): # noqa: N801
346
430
 
347
431
  Returns:
348
432
  float: The maximum strain at failure of concrete.
349
- """
350
- self._eps_cu2 = self._eps_cu2 or ec2_2023.eps_cu2()
351
- return self._eps_cu2
352
-
353
- @eps_cu2.setter
354
- def eps_cu2(self, value: float):
355
- """Sets the strain at concrete failure of concrete for the
356
- Parabola-rectangle constitutive law.
357
433
 
358
- Arguments:
359
- value (float): The new value for eps_cu2, no units.
434
+ Note:
435
+ The returned value is the default value in the design code if not
436
+ manually provided when initializing the object.
360
437
  """
361
- if abs(value) >= 0.1:
362
- warnings.warn(
363
- 'A suspect value is input for eps_cu2 that should be a pure'
364
- ' number without units. Plase check ({value} given).'
365
- )
366
- self._eps_cu2 = value
438
+ if self._eps_cu2 is None:
439
+ return ec2_2023.eps_cu2()
440
+ return self._eps_cu2
367
441
 
368
442
  @property
369
443
  def n_parabolic_rectangular(self) -> float:
@@ -371,30 +445,14 @@ class ConcreteEC2_2023(Concrete): # noqa: N801
371
445
 
372
446
  Returns:
373
447
  float: The exponent for Parabola-recangle law.
374
- """
375
- self._n_parabolic_rectangular = (
376
- self._n_parabolic_rectangular or ec2_2023.n_parabolic_rectangular()
377
- )
378
- return self._n_parabolic_rectangular
379
448
 
380
- @n_parabolic_rectangular.setter
381
- def n_parabolic_rectangular(self, value: float):
382
- """Sets the coefficient for Parabola-rectangle constitutive law.
383
-
384
- Arguments:
385
- value (float): The new value for n, no units.
386
-
387
- Raises:
388
- ValueError: If value < 0.
449
+ Note:
450
+ The returned value is the default value in the design code if not
451
+ manually provided when initializing the object.
389
452
  """
390
- if value < 0:
391
- raise ValueError(f'n should be a positive value ({value} given)')
392
- if value >= 5:
393
- warnings.warn(
394
- 'A suspect value is input for eps_cu2 that should be a pure'
395
- ' number without units. Plase check ({value} given).'
396
- )
397
- self._n_parabolic_rectangular = value
453
+ if self._n_parabolic_rectangular is None:
454
+ return ec2_2023.n_parabolic_rectangular()
455
+ return self._n_parabolic_rectangular
398
456
 
399
457
  @property
400
458
  def gamma_c(self) -> float: