structuralcodes 0.0.1__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 (50) hide show
  1. structuralcodes/__init__.py +17 -0
  2. structuralcodes/codes/__init__.py +79 -0
  3. structuralcodes/codes/ec2_2004/__init__.py +133 -0
  4. structuralcodes/codes/ec2_2004/_concrete_material_properties.py +239 -0
  5. structuralcodes/codes/ec2_2004/_reinforcement_material_properties.py +104 -0
  6. structuralcodes/codes/ec2_2004/_section_7_3_crack_control.py +941 -0
  7. structuralcodes/codes/ec2_2004/annex_b_shrink_and_creep.py +257 -0
  8. structuralcodes/codes/ec2_2004/shear.py +506 -0
  9. structuralcodes/codes/ec2_2023/__init__.py +104 -0
  10. structuralcodes/codes/ec2_2023/_annexB_time_dependent.py +17 -0
  11. structuralcodes/codes/ec2_2023/_section5_materials.py +1160 -0
  12. structuralcodes/codes/ec2_2023/_section9_sls.py +325 -0
  13. structuralcodes/codes/mc2010/__init__.py +169 -0
  14. structuralcodes/codes/mc2010/_concrete_creep_and_shrinkage.py +704 -0
  15. structuralcodes/codes/mc2010/_concrete_interface_different_casting_times.py +104 -0
  16. structuralcodes/codes/mc2010/_concrete_material_properties.py +463 -0
  17. structuralcodes/codes/mc2010/_concrete_punching.py +543 -0
  18. structuralcodes/codes/mc2010/_concrete_shear.py +749 -0
  19. structuralcodes/codes/mc2010/_concrete_torsion.py +164 -0
  20. structuralcodes/codes/mc2010/_reinforcement_material_properties.py +105 -0
  21. structuralcodes/core/__init__.py +1 -0
  22. structuralcodes/core/_section_results.py +211 -0
  23. structuralcodes/core/base.py +260 -0
  24. structuralcodes/geometry/__init__.py +25 -0
  25. structuralcodes/geometry/_geometry.py +875 -0
  26. structuralcodes/geometry/_steel_sections.py +2155 -0
  27. structuralcodes/materials/__init__.py +9 -0
  28. structuralcodes/materials/concrete/__init__.py +82 -0
  29. structuralcodes/materials/concrete/_concrete.py +114 -0
  30. structuralcodes/materials/concrete/_concreteEC2_2004.py +477 -0
  31. structuralcodes/materials/concrete/_concreteEC2_2023.py +435 -0
  32. structuralcodes/materials/concrete/_concreteMC2010.py +494 -0
  33. structuralcodes/materials/constitutive_laws.py +979 -0
  34. structuralcodes/materials/reinforcement/__init__.py +84 -0
  35. structuralcodes/materials/reinforcement/_reinforcement.py +172 -0
  36. structuralcodes/materials/reinforcement/_reinforcementEC2_2004.py +103 -0
  37. structuralcodes/materials/reinforcement/_reinforcementEC2_2023.py +93 -0
  38. structuralcodes/materials/reinforcement/_reinforcementMC2010.py +98 -0
  39. structuralcodes/sections/__init__.py +23 -0
  40. structuralcodes/sections/_generic.py +1249 -0
  41. structuralcodes/sections/_reinforcement.py +115 -0
  42. structuralcodes/sections/section_integrators/__init__.py +14 -0
  43. structuralcodes/sections/section_integrators/_factory.py +41 -0
  44. structuralcodes/sections/section_integrators/_fiber_integrator.py +238 -0
  45. structuralcodes/sections/section_integrators/_marin_integration.py +47 -0
  46. structuralcodes/sections/section_integrators/_marin_integrator.py +222 -0
  47. structuralcodes/sections/section_integrators/_section_integrator.py +49 -0
  48. structuralcodes-0.0.1.dist-info/METADATA +40 -0
  49. structuralcodes-0.0.1.dist-info/RECORD +50 -0
  50. structuralcodes-0.0.1.dist-info/WHEEL +4 -0
@@ -0,0 +1,1160 @@
1
+ """Functions from Section 5 of EN 1992-1-1:2023."""
2
+
3
+ import math
4
+ import typing as t
5
+
6
+ import numpy as np
7
+ import scipy.interpolate
8
+
9
+ from structuralcodes.codes import mc2010
10
+
11
+ VALID_STRENGTH_DEV_CLASSES = ('CS', 'CN', 'CR', 'SLOW', 'NORMAL', 'RAPID')
12
+
13
+ DUCTILITY_CLASSES = {
14
+ 'A': {
15
+ 'epsuk': 2.5e-2,
16
+ 'k': 1.05,
17
+ },
18
+ 'B': {
19
+ 'epsuk': 5.0e-2,
20
+ 'k': 1.08,
21
+ },
22
+ 'C': {
23
+ 'epsuk': 7.5e-2,
24
+ 'k': 1.15,
25
+ },
26
+ }
27
+
28
+
29
+ def fcm(fck: float, delta_f: float = 8.0) -> float:
30
+ """Determines the mean strength of concrete from its characteristic value.
31
+
32
+ EN 1992-1-1:2023, Table 5.1.
33
+
34
+ Args:
35
+ fck (float): Is the characteristic compressive strength in MPa.
36
+
37
+ Keyword Args:
38
+ delta_s (float): The increment in MPa to compute the mean compressive
39
+ strength.
40
+
41
+ Returns:
42
+ float: The mean compressive strength in MPa
43
+ """
44
+ return mc2010.fcm(fck, delta_f)
45
+
46
+
47
+ def fctm(fck: float) -> float:
48
+ """Compute the mean concrete tensile strength from the characteristic
49
+ compressive strength.
50
+
51
+ EN 1992-1-1:2023, Table 5.1.
52
+
53
+ Args:
54
+ fck (float): The characteristic compressive strength in MPa.
55
+
56
+ Returns:
57
+ float: The mean tensile strength in MPa.
58
+ """
59
+ if abs(fck) <= 50:
60
+ return 0.3 * math.pow(abs(fck), 2 / 3)
61
+ return 1.1 * math.pow(abs(fck), 1 / 3)
62
+
63
+
64
+ def fctk_5(fctm: float) -> float:
65
+ """Compute the 5% mean concrete tensile strength fractile.
66
+
67
+ EN 1992-1-1:2023, Table 5.1.
68
+
69
+ Args:
70
+ fctm (float): The mean concrete tensile strength in MPa.
71
+
72
+ Returns:
73
+ float: The 5% mean concrete tensile strength fractile in MPa.
74
+ """
75
+ return abs(fctm) * 0.7
76
+
77
+
78
+ def fctk_95(fctm: float) -> float:
79
+ """Compute the 95% mean concrete tensile strength fractile.
80
+
81
+ EN 1992-1-1:2023, Table 5.1.
82
+
83
+ Args:
84
+ fctm (float): The mean concrete tensile strength in MPa.
85
+
86
+ Returns:
87
+ float: The 5% mean concrete tensile strength fractile in MPa.
88
+ """
89
+ return abs(fctm) * 1.3
90
+
91
+
92
+ def Ecm(fcm: float, kE: float = 9500) -> float:
93
+ """Computes the secant modulus between sigma_c=0 and sigma_c=0.4*fcm.
94
+
95
+ EN 1992-1-1:2023, Eq. (5.1).
96
+
97
+ Args:
98
+ fcm (float): The mean compressive strength in MPa.
99
+
100
+ Keyword Args:
101
+ kE (float): Coefficient relating the aggregates used in concrete.
102
+ Default value is 9500, but it can vary from 5000 to 13000.
103
+
104
+ Returns:
105
+ float: The secant concrete modulus in MPa.
106
+
107
+ Raises:
108
+ ValueError: If fcm is less than 0.
109
+ ValueError: If kE is not between 5000 and 13000.
110
+ """
111
+ if fcm < 0:
112
+ raise ValueError(f'fcm={fcm} cannot be less than 0')
113
+ if kE < 5000:
114
+ raise ValueError(f'kE={kE} cannot be less than 5000')
115
+ if kE > 13000:
116
+ raise ValueError(f'kE={kE} cannot be larger than 13000')
117
+
118
+ return kE * math.pow(fcm, 1 / 3)
119
+
120
+
121
+ def hn(Ac: float, u: float) -> float:
122
+ """Computes the notional size of a given concrete cross-section.
123
+
124
+ EN 1992-1-1:2023, Table 5.2.
125
+
126
+ Args:
127
+ Ac (float): The concrete cross-sectional area in (mm2).
128
+ u (float): The perimeter exposed to drying (mm).
129
+
130
+ Returns:
131
+ float: The notional size (mm).
132
+
133
+ Raises:
134
+ ValueError: If Ac is less than 0.
135
+ ValueError: If u is less than 0.
136
+ """
137
+ if Ac < 0:
138
+ raise ValueError(f'Ac={Ac} cannot be less than 0')
139
+ if u < 0:
140
+ raise ValueError(f'u={u} cannot be less than 0')
141
+
142
+ return 2 * Ac / u
143
+
144
+
145
+ def A_phi_correction_exp(
146
+ hn: float, atm_conditions: t.Literal['dry', 'humid']
147
+ ) -> float:
148
+ """Computes the correction exponent for the modification for the phi_50y_t0
149
+ with respect the fck value.
150
+
151
+ EN 1992-1-1:2023, Table 5.2.
152
+
153
+ Args:
154
+ hn (float): The notional size in mm.
155
+ atm_conditions (str): 'dry' or 'humid'.
156
+
157
+ Returns:
158
+ float: The correction exponent value.
159
+
160
+ Raises:
161
+ ValueError: If hn is less than 100 or greater than 1000.
162
+ ValueError: If atm_conditions is not 'dry' or 'humid'.
163
+ """
164
+ if hn < 100:
165
+ raise ValueError(f'hn={hn} cannot be less than 100')
166
+ if hn > 1000:
167
+ raise ValueError(f'hn={hn} cannot be larger than 1000')
168
+
169
+ atm_conditions = atm_conditions.lower().strip()
170
+ x = (100, 200, 500, 1000)
171
+ if atm_conditions == 'dry':
172
+ y = (0.82, 0.79, 0.75, 0.72)
173
+ elif atm_conditions == 'humid':
174
+ y = (0.71, 0.68, 0.66, 0.64)
175
+ else:
176
+ raise ValueError(
177
+ f'atm_conditions={atm_conditions} can only take '
178
+ + '"dry" and "humid" as values'
179
+ )
180
+
181
+ interpol = scipy.interpolate.interp1d(x, y)
182
+ return interpol(hn)
183
+
184
+
185
+ def phi_correction_factor(fck: float, A_exponent: float) -> float:
186
+ """Computes the correction factor for the computation of the phi_50y_t0.
187
+
188
+ EN 1992-1-1:2023, Table 5.2.
189
+
190
+ Args:
191
+ fck (float): Characteristic strength of concrete in MPa.
192
+ A_exponent (float): The A correction exponent value.
193
+
194
+ Returns:
195
+ float: The correction factor value.
196
+
197
+ Raises:
198
+ ValueError: If fck is not between 12 and 100 MPa.
199
+ ValueError: If A_exponent is not between 0.64 and 0.82.
200
+ """
201
+ if fck < 12:
202
+ raise ValueError(f'fck={fck} cannot be less than 12')
203
+ if fck > 100:
204
+ raise ValueError(f'fck={fck} cannot be larger than 100')
205
+ if A_exponent < 0.64:
206
+ raise ValueError(f'A_exponent={A_exponent} cannot be less than 0.64')
207
+ if A_exponent > 0.82:
208
+ raise ValueError(f'A_exponent={A_exponent} cannot be less than 0.82')
209
+
210
+ return math.pow(35 / fck, A_exponent)
211
+
212
+
213
+ def phi_50y_t0(
214
+ t0: float,
215
+ atm_conditions: t.Literal['dry', 'humid'],
216
+ hn: float,
217
+ strength_dev_class: t.Literal['CS', 'CN', 'CR', 'slow', 'normal', 'rapid'],
218
+ ) -> float:
219
+ """Computes the creep coefficient of plain concrete at 50 years of loading.
220
+ Interpolation is linear between values.
221
+
222
+ EN 1992-1-1:2023, Table 5.2.
223
+
224
+ Args:
225
+ t0 (float): Age at loading [days].
226
+ atm_conditions (str): 'dry' or 'humid'.
227
+ hn (float): The notional size in mm.
228
+ strength_dev_class (str): Strength development class 'CS', 'CN', 'CR',
229
+ 'slow', 'normal' or 'rapid'.
230
+
231
+ Returns:
232
+ float: The creep coefficient.
233
+
234
+ Raises:
235
+ ValueError: If t0 is less than 1.
236
+ ValueError: If atm_conditions is not 'dry' or 'humid'.
237
+ ValueError: If hn is less than 100 or larger than 1000.
238
+ ValueError: If strength_dev_class is not 'CS', 'CN',
239
+ 'CR', 'slow', 'normal' or 'rapid'.
240
+ ValueError: If combination of t0 and hn is out of scope.
241
+ """
242
+ if t0 < 1:
243
+ raise ValueError(f't0={t0} cannot be less than 1')
244
+
245
+ atm_conditions = atm_conditions.lower().strip()
246
+ if atm_conditions not in ('dry', 'humid'):
247
+ raise ValueError(
248
+ f'atm_conditions={atm_conditions} must be "dry" or "humid"'
249
+ )
250
+
251
+ if hn < 100:
252
+ raise ValueError(f'hn={hn} must be larger or equal than 100')
253
+ if hn > 1000:
254
+ raise ValueError(f'hn={hn} must be less or equal than 1000')
255
+
256
+ strength_dev_class = strength_dev_class.upper().strip()
257
+ if strength_dev_class not in VALID_STRENGTH_DEV_CLASSES:
258
+ raise ValueError(
259
+ f'strength_dev_class={strength_dev_class} must be'
260
+ + '"CS", "CN", "CR", "slow", "normal" or "rapid"'
261
+ )
262
+
263
+ if strength_dev_class in ('CS', 'SLOW'):
264
+ _t = (3, 10, 32, 91, 365)
265
+ elif strength_dev_class in ('CN', 'NORMAL'):
266
+ _t = (1, 7, 28, 91, 365)
267
+ elif strength_dev_class in ('CR', 'RAPID'):
268
+ _t = (1, 3, 23, 91, 365)
269
+
270
+ h_v = (100, 200, 500, 1000)
271
+
272
+ if atm_conditions == 'dry':
273
+ values = (
274
+ 4.2,
275
+ 3.8,
276
+ 3.4,
277
+ 3.1,
278
+ 3.1,
279
+ 2.8,
280
+ 2.5,
281
+ 2.3,
282
+ 2.4,
283
+ 2.2,
284
+ 1.9,
285
+ 1.8,
286
+ 1.9,
287
+ 1.7,
288
+ 1.5,
289
+ 1.4,
290
+ 1.4,
291
+ 1.3,
292
+ 1.1,
293
+ 1.0,
294
+ )
295
+ elif atm_conditions == 'humid':
296
+ values = (
297
+ 3.0,
298
+ 2.8,
299
+ 2.6,
300
+ 2.5,
301
+ 2.2,
302
+ 2.1,
303
+ 2.0,
304
+ 1.9,
305
+ 1.7,
306
+ 1.6,
307
+ 1.6,
308
+ 1.5,
309
+ 1.4,
310
+ 1.3,
311
+ 1.2,
312
+ 1.2,
313
+ 1.0,
314
+ 0.9,
315
+ 0.9,
316
+ 0.8,
317
+ )
318
+
319
+ grid = np.array(np.meshgrid(_t, h_v)).T.reshape(-1, 2)
320
+ p = (t0, hn)
321
+
322
+ interp = scipy.interpolate.griddata(grid, values, p, method='linear')
323
+ _phi_50y_t0 = float(interp)
324
+
325
+ if math.isnan(_phi_50y_t0) or math.isnan(_phi_50y_t0):
326
+ raise ValueError('Combination of t0, hn out of scope')
327
+
328
+ return _phi_50y_t0
329
+
330
+
331
+ def eps_cs_50y( # noqa: PLR0912
332
+ fck_28: float,
333
+ atm_conditions: t.Literal['dry', 'humid'],
334
+ hn: float,
335
+ strength_dev_class: t.Literal['CS', 'CN', 'CR', 'slow', 'normal', 'rapid'],
336
+ ) -> float:
337
+ """Computes the nominal total shrinkage in ‰ for concrete after a duration
338
+ of drying of 50 years.
339
+
340
+ EN 1992-1-1:2023, Table 5.3.
341
+
342
+ Args:
343
+ fck_28 (float): Characteristic strength at 28 days in MPa
344
+ atm_conditions (str): 'dry' or 'humid'.
345
+ hn (float): The notional size in mm.
346
+ strength_dev_class (str): Strength development class 'CS', 'CN', 'CR',
347
+ 'slow', 'normal' or 'rapid'.
348
+
349
+ Returns:
350
+ float: The nominal shrinkage value in percent.
351
+
352
+ Raises:
353
+ ValueError: If fck_28 is less than 20 MPa or larger than 80 MPa.
354
+ ValueError: If atm_conditions is not 'dry' or 'humid'.
355
+ ValueError: If hn is less than 100 or larger than 1000.
356
+ ValueError: If strength_dev_class is not CS', 'CN', 'CR',
357
+ 'slow', 'normal' or 'rapid'.
358
+ ValueError: If combination of fck_28 and hn is out of scope.
359
+ """
360
+ if fck_28 < 20:
361
+ raise ValueError(f'fck_28={fck_28} cannot be less than 20')
362
+ if fck_28 > 80:
363
+ raise ValueError(f'fck_28={fck_28} cannot be larger than 80')
364
+
365
+ atm_conditions = atm_conditions.lower().strip()
366
+ if atm_conditions not in ('dry', 'humid'):
367
+ raise ValueError(
368
+ f'atm_conditions={atm_conditions} must be "dry" or "humid"'
369
+ )
370
+
371
+ if hn < 100:
372
+ raise ValueError(f'hn={hn} must be larger or equal than 100')
373
+ if hn > 1000:
374
+ raise ValueError(f'hn={hn} must be less or equal than 1000')
375
+
376
+ strength_dev_class = strength_dev_class.upper().strip()
377
+ if strength_dev_class == 'SLOW':
378
+ strength_dev_class = 'CS'
379
+ elif strength_dev_class == 'NORMAL':
380
+ strength_dev_class = 'CN'
381
+ elif strength_dev_class == 'RAPID':
382
+ strength_dev_class = 'CR'
383
+
384
+ if strength_dev_class == 'CS':
385
+ fck_v = (20, 35, 50)
386
+ elif strength_dev_class == 'CN':
387
+ fck_v = (20, 35, 50, 80)
388
+ elif strength_dev_class == 'CR':
389
+ fck_v = (35, 50, 80)
390
+ else:
391
+ raise ValueError(
392
+ f'strength_dev_class={strength_dev_class} '
393
+ + 'must be "CS", "CN", "CR", "slow", "normal" or "rapid"'
394
+ )
395
+
396
+ h_v = (100, 200, 500, 1000)
397
+
398
+ data = {
399
+ 'dry': {
400
+ 'CS': (
401
+ 0.57,
402
+ 0.56,
403
+ 0.48,
404
+ 0.36,
405
+ 0.53,
406
+ 0.51,
407
+ 0.45,
408
+ 0.35,
409
+ 0.49,
410
+ 0.48,
411
+ 0.43,
412
+ 0.35,
413
+ ),
414
+ 'CN': (
415
+ 0.67,
416
+ 0.65,
417
+ 0.56,
418
+ 0.41,
419
+ 0.60,
420
+ 0.59,
421
+ 0.51,
422
+ 0.39,
423
+ 0.55,
424
+ 0.54,
425
+ 0.48,
426
+ 0.37,
427
+ 0.48,
428
+ 0.48,
429
+ 0.43,
430
+ 0.36,
431
+ ),
432
+ 'CR': (
433
+ 0.76,
434
+ 0.74,
435
+ 0.65,
436
+ 0.48,
437
+ 0.67,
438
+ 0.66,
439
+ 0.58,
440
+ 0.44,
441
+ 0.55,
442
+ 0.54,
443
+ 0.49,
444
+ 0.39,
445
+ ),
446
+ },
447
+ 'humid': {
448
+ 'CS': (
449
+ 0.33,
450
+ 0.32,
451
+ 0.28,
452
+ 0.21,
453
+ 0.31,
454
+ 0.31,
455
+ 0.27,
456
+ 0.22,
457
+ 0.30,
458
+ 0.29,
459
+ 0.27,
460
+ 0.23,
461
+ ),
462
+ 'CN': (
463
+ 0.38,
464
+ 0.37,
465
+ 0.32,
466
+ 0.24,
467
+ 0.34,
468
+ 0.34,
469
+ 0.30,
470
+ 0.24,
471
+ 0.31,
472
+ 0.31,
473
+ 0.28,
474
+ 0.23,
475
+ 0.30,
476
+ 0.30,
477
+ 0.28,
478
+ 0.25,
479
+ ),
480
+ 'CR': (
481
+ 0.42,
482
+ 0.41,
483
+ 0.36,
484
+ 0.28,
485
+ 0.36,
486
+ 0.35,
487
+ 0.32,
488
+ 0.26,
489
+ 0.31,
490
+ 0.30,
491
+ 0.28,
492
+ 0.25,
493
+ ),
494
+ },
495
+ }
496
+ values = data.get(atm_conditions).get(strength_dev_class)
497
+
498
+ grid = np.array(np.meshgrid(fck_v, h_v)).T.reshape(-1, 2)
499
+ p = (fck_28, hn)
500
+
501
+ interp = scipy.interpolate.griddata(grid, values, p, method='linear')
502
+ _eps_cs_50y = float(interp)
503
+
504
+ if math.isnan(_eps_cs_50y):
505
+ raise ValueError('Combination of fck_28, hn out of scope')
506
+
507
+ return _eps_cs_50y
508
+
509
+
510
+ def eta_cc(fck: float, fck_ref: float = 40) -> float:
511
+ """Computes the factor to measure the difference between the undistributed
512
+ compressive strength of a cylinder and the effective compressive strength
513
+ in a structural member.
514
+
515
+ EN 1992-1-1:2023, Eq. (5.4).
516
+
517
+ Args:
518
+ fck (float): The characterisitic compressive strength in MPa.
519
+
520
+ Keyword Args:
521
+ fck_ref (float, optional): The reference compressive strength MPa.
522
+
523
+ Returns:
524
+ float: The value of the factor eta_cc.
525
+
526
+ Raises:
527
+ ValueError: If fck is less than 12 MPa.
528
+ ValueError: If fkc_ref is less or equal to 0.
529
+ """
530
+ if fck < 12:
531
+ raise ValueError(f'fck={fck} must be larger or equal than 12 MPa')
532
+ if fck_ref <= 0:
533
+ raise ValueError(f'fck_ref={fck_ref} must be larger than 0')
534
+
535
+ return min(math.pow(fck_ref / fck, 1 / 3), 1)
536
+
537
+
538
+ def k_tc(
539
+ t_ref: float,
540
+ t0: float,
541
+ strength_dev_class: t.Literal['CS', 'CN', 'CR', 'slow', 'normal', 'rapid'],
542
+ ) -> float:
543
+ """Computes the factor for considering the effect of high sustained loads
544
+ and of time of loading on concrete compressive strength.
545
+
546
+ EN 1992-1-1:2023, Eq. (5.3).
547
+
548
+ Args:
549
+ t_ref (float): The reference time in days.
550
+ t0 (float): Age at loading in days.
551
+ strength_dev_class (str): Strength development class 'CS', 'CN', 'CR',
552
+ 'slow', 'normal', 'rapid'.
553
+
554
+ Returns:
555
+ float: The factor value.
556
+
557
+ Raises:
558
+ ValueError: If t_ref is less than 0.
559
+ ValueError: If t0 is less than 0.
560
+ ValueError: If strength_dev_class is not 'CS', 'CN', 'CR', 'slow',
561
+ 'normal', 'rapid'.
562
+ """
563
+ if t_ref < 0:
564
+ raise ValueError(f't_ref={t_ref} must be larger than 0')
565
+ if t0 < 0:
566
+ raise ValueError(f't0={t0} must be larger than 0')
567
+
568
+ strength_dev_class = strength_dev_class.upper().strip()
569
+
570
+ if strength_dev_class not in VALID_STRENGTH_DEV_CLASSES:
571
+ raise ValueError(
572
+ f'strength_dev_class={strength_dev_class}'
573
+ + f'should can only take {VALID_STRENGTH_DEV_CLASSES}'
574
+ )
575
+
576
+ if (
577
+ strength_dev_class in ('CR', 'CN', 'RAPID', 'NORMAL')
578
+ and t_ref <= 28
579
+ and t0 > 90
580
+ ):
581
+ return 1
582
+
583
+ if strength_dev_class in ('CS', 'SLOW') and t_ref <= 56 and t0 > 90:
584
+ return 1
585
+
586
+ return 0.85
587
+
588
+
589
+ def fcd(fck: float, eta_cc: float, k_tc: float, gamma_c: float) -> float:
590
+ """Computes the value of the design compressive strength of concrete.
591
+
592
+ EN 1992-1-1:2023, Eq. (5.3).
593
+
594
+ Args:
595
+ fck (float): Characteristic compressive strength in MPa.
596
+ eta_cc (float): Factor for measuring the difference between the
597
+ undistributed compressive strength of a cylinder and the effective
598
+ compressive strength in the real structural member.
599
+ k_tc (float): Factor for taking into consideration high sustained
600
+ loads and of time of loading.
601
+ gamma_c (float): Partial factor of concrete.
602
+
603
+ Returns:
604
+ float: The design compressive strength of concrete in MPa.
605
+
606
+ Raises:
607
+ ValueError: If fck is less than 12 MPa.
608
+ ValueError: If _etc_cc is not between 0 and 1.
609
+ ValueError: If gamma_c is less than 1.
610
+ """
611
+ if fck < 12:
612
+ raise ValueError(f'fck={fck} must be larger or equal than 12 MPa')
613
+ if eta_cc < 0 or eta_cc > 1:
614
+ raise ValueError(f'eta_cc={eta_cc} must be between 0 and 1')
615
+ if gamma_c < 1:
616
+ raise ValueError(f'gamma_c={gamma_c} must be larger or equal to 1')
617
+
618
+ return eta_cc * k_tc * fck / gamma_c
619
+
620
+
621
+ def k_tt(
622
+ t_ref: float,
623
+ strength_dev_class: t.Literal['CS', 'CN', 'CR', 'slow', 'normal', 'rapid'],
624
+ ) -> float:
625
+ """Computes the factor for considering the effect of high sustained loads
626
+ and of time of loading on concrete tensile strength.
627
+
628
+ EN 1992-1-1:2023, Eq. (5.5).
629
+
630
+ Args:
631
+ t_ref (float): The reference time in days.
632
+ strength_dev_class (str): Strength development class 'CS', 'CN', 'CR',
633
+ 'slow', 'normal' or 'rapid'.
634
+
635
+ Returns:
636
+ float: The factor value.
637
+
638
+ Raises:
639
+ ValueError: If t_ref is less than 0.
640
+ ValueError: If strength_dev_class is not 'CS', 'CN', 'CR', 'slow',
641
+ 'normal' or 'rapid'.
642
+ """
643
+ if t_ref < 0:
644
+ raise ValueError(f't_ref={t_ref} must be larger than 0')
645
+
646
+ strength_dev_class = strength_dev_class.upper().strip()
647
+
648
+ if strength_dev_class not in VALID_STRENGTH_DEV_CLASSES:
649
+ raise ValueError(
650
+ f'strength_dev_class={strength_dev_class}'
651
+ + 'should can only take "CS", "CN", "CR", "slow"'
652
+ + '"normal", "rapid" as values',
653
+ )
654
+
655
+ if strength_dev_class in ('CR', 'CN', 'RAPID', 'NORMAL') and t_ref <= 28:
656
+ return 0.8
657
+
658
+ if strength_dev_class in ('CS', 'SLOW') and t_ref <= 56:
659
+ return 0.8
660
+
661
+ return 0.7
662
+
663
+
664
+ def fctd(fctk_5: float, k_tt: float, gamma_c: float) -> float:
665
+ """Computes the value of the design tensile strength of concrete.
666
+
667
+ EN 1992-1-1:2023, Eq. (5.5).
668
+
669
+ Args:
670
+ fctk_5 (float): The 5% mean concrete tensile strength fractile in MPa.
671
+ k_tt (float): The factor for considering the effect of high sustained
672
+ loads and of time of loading on concrete tensile strength.
673
+ gamma_c (float): Partial factor of concrete.
674
+
675
+ Returns:
676
+ float: The design tensile strength of concrete in MPa.
677
+
678
+ Raises:
679
+ ValueError: If fctk_5 is less than 0.
680
+ ValueError: If gamma_c is less than 1.
681
+ """
682
+ if fctk_5 < 0:
683
+ raise ValueError(f'fctk_5={fctk_5} must be larger or equal to 0')
684
+ if gamma_c < 1:
685
+ raise ValueError(f'gamma_c={gamma_c} must be larger or equal to 1')
686
+
687
+ return k_tt * fctk_5 / gamma_c
688
+
689
+
690
+ def eps_c1(fcm: float) -> float:
691
+ """Computes the strain at maximum compressive strength of concrete (fcm)
692
+ for the Sargin constitutive law.
693
+
694
+ EN 1992-1-1:2023, Eq. (5.9).
695
+
696
+ Args:
697
+ fcm (float): The mean strength of concrete in MPa.
698
+
699
+ Returns:
700
+ float: The strain at maximum compressive strength of concrete.
701
+
702
+ Raises:
703
+ ValueError: If fcm is less than 12+8MPa.
704
+ """
705
+ if fcm < 20:
706
+ raise ValueError(f'fcm={fcm} must be larger or equal to 12+8MPa')
707
+
708
+ return min(0.7 * math.pow(fcm, 1 / 3), 2.8) / 1000
709
+
710
+
711
+ def eps_cu1(fcm: float) -> float:
712
+ """Computes the strain at concrete failure of concrete.
713
+
714
+ EN 1992-1-1:2023, Eq. (5.10).
715
+
716
+ Args:
717
+ fcm (float): The mean strength of concrete in MPa.
718
+
719
+ Returns:
720
+ float: The maximum strength at failure of concrete.
721
+
722
+ Raises:
723
+ ValueError: If fcm is less than 12+8MPa.
724
+ """
725
+ if fcm < 20:
726
+ raise ValueError(f'fcm={fcm} must be larger or equal to 12+8MPa')
727
+
728
+ return min(2.8 + 14 * (1 - fcm / 108) ** 4, 3.5) / 1000
729
+
730
+
731
+ def k_sargin(
732
+ Ecm: float,
733
+ fcm: float,
734
+ eps_c1: float,
735
+ ) -> float:
736
+ """Computes the coefficient k for Sargin constitutive law in compression.
737
+
738
+ EN 1992-1-1:2003, eq. (5.7)
739
+
740
+ Args:
741
+ Ecm (float): the secant modulus between sigma_c=0 and sigma_c=0.4*fcm
742
+ in MPa
743
+ fcm (float): the mean compressive strength of concrete in MPa
744
+ eps_c1 (float): the strain of concrete at stress fcm
745
+
746
+ Returns:
747
+ float: the coefficient k for Sargin constitutive law
748
+
749
+ Raises:
750
+ ValueError: if Ecm is less or equal to 0
751
+ ValueError: if fcm is less than 12+8MPa
752
+ ValueError: if eps_c1 is less or equal to 0
753
+ """
754
+ if Ecm <= 0:
755
+ raise ValueError(f'Ecm={Ecm} must be larger than 0')
756
+ if fcm < 20:
757
+ raise ValueError(f'fcm={fcm} must be larger or equal to 12+8MPa')
758
+ if eps_c1 <= 0:
759
+ raise ValueError(f'eps_c1={eps_c1} must be larger than 0')
760
+ return 1.05 * Ecm * eps_c1 / fcm
761
+
762
+
763
+ def eps_c2() -> float:
764
+ """The strain at maximum compressive stress of concrete for the
765
+ parabolic-rectangular law.
766
+
767
+ EN 1992-1-1:2023, Eq. 8.4
768
+
769
+ Returns:
770
+ float: The strain at maximum compressive stress, absolute value, no
771
+ unit.
772
+ """
773
+ return 2.0e-3
774
+
775
+
776
+ def eps_cu2() -> float:
777
+ """The ultimate strain of the parabolic-rectangular law.
778
+
779
+ EN 1992-1-1:2023, Eq. 8.4
780
+
781
+ Returns:
782
+ float: The ultimate strain, absolute value, no unit.
783
+ """
784
+ return 3.5e-3
785
+
786
+
787
+ def n_parabolic_rectangular() -> float:
788
+ """The exponent in the parabolic-rectangular law.
789
+
790
+ EN 1992-1-1:2023, Eq. 8.4
791
+ Returns:
792
+ float: The exponent n, absolute value, no unit.
793
+ """
794
+ return 2.0
795
+
796
+
797
+ def weight_c(concrete_type: t.Literal['nc', 'npc']) -> float:
798
+ """Returns the mean unit weight of concrete in kN/m3.
799
+
800
+ EN 1992-1-1:2023, 5.1.6-5.
801
+
802
+ Args:
803
+ concrete_type (str): 'nc' for normal concrete, or 'npc' for normal
804
+ plain concrete.
805
+
806
+ Returns:
807
+ float: Mean unit weight in kN/m3.
808
+
809
+ Raises:
810
+ ValueError: If concrete_type is not 'nc' or 'npc'.
811
+ """
812
+ concrete_type = concrete_type.lower().strip()
813
+
814
+ if concrete_type == 'nc':
815
+ return 25
816
+ if concrete_type == 'npc':
817
+ return 24
818
+
819
+ raise ValueError(
820
+ f'concrete_type={concrete_type} can only take'
821
+ + '"nc" or "npc" as values'
822
+ )
823
+
824
+
825
+ def alpha_c_th() -> float:
826
+ """Returns the linear coefficient of thermal expansion in 1/Cº for
827
+ concrete.
828
+
829
+ EN 1992-1-1:2023, 5.1.6-6.
830
+
831
+ Returns:
832
+ float: The linear coefficient of thermal expansion in 1/Cº for
833
+ concrete.
834
+ """
835
+ return 10 * 10e-6
836
+
837
+
838
+ def Es() -> float:
839
+ """Returns the value of the modulus of elasticity for weldable reinforcing
840
+ steel.
841
+
842
+ EN 1992-1-1:2023, 5.2.4-3.
843
+
844
+ Returns:
845
+ float: Modulus of elasticity in MPa.
846
+ """
847
+ return 200000
848
+
849
+
850
+ def alpha_s_th() -> float:
851
+ """Returns the linear coefficient of thermal expansion in 1/Cº for weldable
852
+ reinforced steel.
853
+
854
+ EN 1992-1-1:2023, 5.2.4-5
855
+
856
+ Returns:
857
+ float: The linear coefficient of thermal expansion in 1/Cº for weldable
858
+ reinforcement steel.
859
+ """
860
+ return 10 * 10e-6
861
+
862
+
863
+ def weight_s() -> float:
864
+ """Returns the mean unit weight of reinforced steel for the purposes of
865
+ design in kN/m3.
866
+
867
+ EN 1992-1-1:2023.2.4-4.
868
+
869
+ Returns:
870
+ float: The mean unit weight in kN/m3.
871
+ """
872
+ return 78.5
873
+
874
+
875
+ def fyd(fyk: float, gamma_s: float) -> float:
876
+ """Design value for the yielding stress for welding reinforcing steel.
877
+
878
+ EN 1992-1-1:2023, Eq (5.11).
879
+
880
+ Args:
881
+ fyk (float): Characteristic yield stress for the steel in MPa.
882
+ gamma_s (float): Safety coefficient.
883
+
884
+ Returns:
885
+ float: Design yielding stress for steel in MPa.
886
+
887
+ Raises:
888
+ ValueError: If fyk is less than 0.
889
+ ValueError: If gamma_s is less than 1.
890
+ """
891
+ if fyk < 0:
892
+ raise ValueError(f'fyk={fyk} cannot be less than 0')
893
+ if gamma_s < 1:
894
+ raise ValueError(f'gamma_s={gamma_s} must be larger or equal to 1')
895
+
896
+ return fyk / gamma_s
897
+
898
+
899
+ def eps_ud(eps_uk: float, gamma_s: float) -> float:
900
+ """Design value for the ultimate limit strain welding reinforcing steel.
901
+
902
+ EN 1992-1-1:2023, 5.2.4-2.
903
+
904
+ Args:
905
+ eps_uk (float): Characteristic ultimate limit strain.
906
+ gamma_s (float): Safety coefficient.
907
+
908
+ Returns:
909
+ float: Design ultimate strain limit.
910
+
911
+ Raises:
912
+ ValueError: If eps_uk is less than 0.
913
+ ValueError: If gamma_s is less than 1.
914
+ """
915
+ if eps_uk < 0:
916
+ raise ValueError(f'eps_uk={eps_uk} must be equal or larger to 0')
917
+ if gamma_s < 1:
918
+ raise ValueError(f'gamma_s={gamma_s} must be larger or equal to 1')
919
+
920
+ return eps_uk / gamma_s
921
+
922
+
923
+ def sigma_s(
924
+ eps: float, fy: float, k: float, eps_u: float, Es: float = 200000
925
+ ) -> float:
926
+ """Compute the stress for welded reinforcing steel in MPa for a given
927
+ strain.
928
+
929
+ EN 1992-1-1:2023, 5.2.4.
930
+
931
+ Args:
932
+ eps (float): The strain value.
933
+ fy (float): The yielding stress in MPa. Use fyd for the design
934
+ strength, and fyk for the characteristic strength.
935
+ k (float): Curve parameter. Ratio between the ultimate stress and the
936
+ yielding stress. k = 1 for horizontal post-elastic branch without
937
+ strain limit.
938
+ eps_u (float): Ultimate strain at failure. Use eps_ud for the design
939
+ ultimate strain, and eps_uk for the characteristic ultimate strain.
940
+
941
+ Keyword Args:
942
+ Es (float): The modulus of elasticity for reinforcing steel.
943
+
944
+ Returns:
945
+ float: The nominal stress in MPa.
946
+
947
+ Raises:
948
+ ValueError: If eps is less than 0 or larger than eps_uk.
949
+ ValueError: If Es is less or equal to 0.
950
+ ValueError: If fy is less or equal to 0.
951
+ ValueError: If k is less than 1.
952
+ ValueError: If eps_u is less or equal to 0 and k > 1.
953
+ """
954
+ if eps < 0:
955
+ raise ValueError(f'eps={eps} must be larger or equal to 0')
956
+ if eps_u <= 0:
957
+ raise ValueError(f'eps_u={eps_u} must be larger than 0')
958
+ if eps > eps_u and k > 1:
959
+ raise ValueError(
960
+ f'eps={eps} must be equal o less than'
961
+ + 'eps_uk={eps_u} when k is larger than 1'
962
+ )
963
+ if Es <= 0:
964
+ raise ValueError(f'Es={Es} must be larger than 0')
965
+ if fy <= 0:
966
+ raise ValueError(f'fyk={fy} must be larger than 0')
967
+ if k < 0:
968
+ raise ValueError(f'k={k} must be larger than 1')
969
+
970
+ eps_y = fy / Es
971
+
972
+ # If in elastic area
973
+ if eps <= eps_y:
974
+ return eps * Es
975
+
976
+ # If in plastic area
977
+ m = fy * (k - 1) / (eps_u - eps_y)
978
+ return fy + m * (eps - eps_y)
979
+
980
+
981
+ def p_steel_stress_params(
982
+ prestress_class: t.Literal[
983
+ 'Y1560',
984
+ 'Y1670',
985
+ 'Y1770',
986
+ 'Y1860',
987
+ 'Y1770',
988
+ 'Y1860',
989
+ 'Y1960',
990
+ 'Y2060',
991
+ 'Y1030',
992
+ 'Y1050',
993
+ 'Y1100',
994
+ 'Y1230',
995
+ ],
996
+ element: t.Literal['W', 'S', 'B'],
997
+ ) -> t.Tuple[float, float]:
998
+ """Computes the stress-diagram parameters fp01k and fpk.
999
+
1000
+ EN 1992-1-1:2023, 5.3.3.
1001
+
1002
+ Args:
1003
+ prestress_class (str): Possible values: Y1560, Y1670,
1004
+ Y1770, Y1860, Y1770, Y1860, Y1960, Y2060,
1005
+ Y1030, Y1050, Y1100 and Y1230
1006
+ element (str): Element type, 'W' for Wires, 'S' for Strands, and 'B'
1007
+ for Bars.
1008
+
1009
+ Returns:
1010
+ Tuple(float, float): With the value of fp01k and fpk in MPa.
1011
+
1012
+ Raises:
1013
+ ValueError: If combination of prestress_class and element is not a
1014
+ possible value from the range.
1015
+ """
1016
+ prestress_class = prestress_class.upper().strip()
1017
+ element = element.upper().strip()
1018
+ data = {
1019
+ 'W': {
1020
+ 'Y1570': (1380, 1570),
1021
+ 'Y1670': (1470, 1670),
1022
+ 'Y1770': (1550, 1770),
1023
+ 'Y1860': (1650, 1860),
1024
+ },
1025
+ 'S': {
1026
+ 'Y1770': (1560, 1770),
1027
+ 'Y1860': (1640, 1860),
1028
+ 'Y1960': (1740, 1960),
1029
+ 'Y2060': (1830, 2060),
1030
+ },
1031
+ 'B': {
1032
+ 'Y1030': (835, 1030),
1033
+ 'Y1050': (950, 1050),
1034
+ 'Y1100': (900, 1100),
1035
+ 'Y1230': (1080, 1230),
1036
+ },
1037
+ }
1038
+
1039
+ if element not in data:
1040
+ raise ValueError(f'element={element} has not a valid value')
1041
+
1042
+ material = data[element]
1043
+
1044
+ if prestress_class not in material:
1045
+ raise ValueError(
1046
+ f'prestress_class={prestress_class} has not a valid value'
1047
+ )
1048
+
1049
+ return material[prestress_class]
1050
+
1051
+
1052
+ def fpd(fp01k: float, gamma_P: float) -> float:
1053
+ """Computes the design value for the prestressing steel stress.
1054
+
1055
+ EN 1992-1-1:2023, 5.3.3.
1056
+
1057
+ Args:
1058
+ fp01k (float): The 0.1% proof stress in MPa.
1059
+ gamma_P (float): The safety coefficient.
1060
+
1061
+ Returns:
1062
+ float: The design value for the design prestressing steel stress in
1063
+ MPa.
1064
+
1065
+ Raises:
1066
+ ValueError: If fp01k is less than 0.
1067
+ ValueError: If gamma_P is less than 1.
1068
+ """
1069
+ if fp01k < 0:
1070
+ raise ValueError(f'fp01k={fp01k} must be larger or equal to 0')
1071
+ if gamma_P < 1:
1072
+ raise ValueError(f'gamma_P={gamma_P} must be larger or equal than 1')
1073
+
1074
+ return fp01k / gamma_P
1075
+
1076
+
1077
+ def sigma_p(
1078
+ eps: float,
1079
+ fpy: float,
1080
+ fpu: float,
1081
+ eps_u: float = 0.035,
1082
+ Ep: float = 190000,
1083
+ ) -> float:
1084
+ """Computes the stress for prestressing steel as a function of the strain.
1085
+
1086
+ EN 1992-1-1:2023, 5.3.3.
1087
+
1088
+ Args:
1089
+ eps (float): Strain value.
1090
+ fpy (float): Yielding stress of the steel in MPa. Use fd for design
1091
+ stress values, and fp01k for nominal stress values.
1092
+ fpu (float): The maximum stress at eps_u in MPa. Use fpd for design
1093
+ stress values, fpk for nominal stress values, and fpu == fpy for
1094
+ horizontal post-elastic branch without strain limit.
1095
+
1096
+ Keyword Args:
1097
+ eps_u (float): Ultimate strain. Use eps_uk = 0.035 for nominal ultimate
1098
+ strain, and eps_ud for design ultimate strain.
1099
+ Ep (float): Modulus of elasticity of prestressing steel in MPa.
1100
+
1101
+ Raises:
1102
+ ValueError: If eps is less than 0 or larger than eps_u.
1103
+ ValueError: If fpy is less or equal to 0.
1104
+ ValueError: If fpu is less than fpy.
1105
+ ValueError: If eps_u is lower or equal to 0.
1106
+ ValueError: If _Ep is less or equal to 0.
1107
+ """
1108
+ if eps < 0:
1109
+ raise ValueError(f'eps={eps} must be larger or equal to 0')
1110
+ if eps_u <= 0:
1111
+ raise ValueError(f'eps_u={eps_u} must be larger than 0')
1112
+ if eps_u < eps and fpy != fpu:
1113
+ raise ValueError(f'eps_u={eps_u} must be larger than eps={eps}')
1114
+ if fpy <= 0:
1115
+ raise ValueError(f'fpy={fpy} must be larger than 0')
1116
+ if fpu < fpy:
1117
+ raise ValueError(f'fpu={fpy} must be larger or equal to fpy={fpy}')
1118
+ if Ep <= 0:
1119
+ raise ValueError(f'Ep={Ep} must be larger than 0')
1120
+
1121
+ eps_y = fpy / Ep
1122
+
1123
+ # If elastic
1124
+ if eps <= eps_y:
1125
+ return eps * Ep
1126
+
1127
+ # If plastic
1128
+ m = (fpu - fpy) / (eps_u - eps_y)
1129
+ return fpy + m * (eps - eps_y)
1130
+
1131
+
1132
+ def reinforcement_duct_props(
1133
+ fyk: float,
1134
+ ductility_class: t.Literal['A', 'B', 'C'],
1135
+ ) -> t.Dict[str, float]:
1136
+ """Return a dict with the minimum characteristic ductility properties for
1137
+ reinforcement ductility class.
1138
+
1139
+ EUROCODE 2 1992-1-1:2023, Tab. 5.5.
1140
+
1141
+ Args:
1142
+ fyk (float): The characteristic yield strength.
1143
+ ductility_class (Literal['A', 'B', 'C']): The reinforcement ductility
1144
+ class designation.
1145
+
1146
+ Returns:
1147
+ Dict[str, float]: A dict with the characteristik strain value at the
1148
+ ultimate stress level (epsuk), and the characteristic ultimate stress
1149
+ (ftk).
1150
+ """
1151
+ duct_props = DUCTILITY_CLASSES.get(ductility_class.upper(), None)
1152
+ if duct_props is None:
1153
+ raise ValueError(
1154
+ 'The no properties was found for the provided ductility class '
1155
+ f'({ductility_class}).'
1156
+ )
1157
+ return {
1158
+ 'epsuk': duct_props['epsuk'],
1159
+ 'ftk': duct_props['k'] * fyk,
1160
+ }