structuralcodes 0.1.1__py3-none-any.whl → 0.3.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.
- structuralcodes/__init__.py +1 -1
- structuralcodes/codes/ec2_2004/__init__.py +43 -11
- structuralcodes/codes/ec2_2004/_concrete_creep_and_shrinkage.py +529 -0
- structuralcodes/codes/mc2010/_concrete_creep_and_shrinkage.py +105 -73
- structuralcodes/core/_section_results.py +5 -19
- structuralcodes/core/base.py +42 -15
- structuralcodes/geometry/__init__.py +10 -1
- structuralcodes/geometry/_circular.py +81 -0
- structuralcodes/geometry/_geometry.py +4 -2
- structuralcodes/geometry/_rectangular.py +83 -0
- structuralcodes/geometry/_reinforcement.py +132 -5
- structuralcodes/materials/constitutive_laws/__init__.py +84 -0
- structuralcodes/materials/constitutive_laws/_bilinearcompression.py +183 -0
- structuralcodes/materials/constitutive_laws/_elastic.py +133 -0
- structuralcodes/materials/constitutive_laws/_elasticplastic.py +227 -0
- structuralcodes/materials/constitutive_laws/_parabolarectangle.py +255 -0
- structuralcodes/materials/constitutive_laws/_popovics.py +133 -0
- structuralcodes/materials/constitutive_laws/_sargin.py +115 -0
- structuralcodes/materials/constitutive_laws/_userdefined.py +262 -0
- structuralcodes/sections/__init__.py +2 -0
- structuralcodes/sections/_generic.py +174 -27
- structuralcodes/sections/_rc_utils.py +114 -0
- structuralcodes/sections/section_integrators/_fiber_integrator.py +204 -110
- structuralcodes/sections/section_integrators/_marin_integrator.py +273 -102
- structuralcodes/sections/section_integrators/_section_integrator.py +28 -4
- {structuralcodes-0.1.1.dist-info → structuralcodes-0.3.0.dist-info}/METADATA +2 -2
- {structuralcodes-0.1.1.dist-info → structuralcodes-0.3.0.dist-info}/RECORD +28 -18
- {structuralcodes-0.1.1.dist-info → structuralcodes-0.3.0.dist-info}/WHEEL +1 -1
- structuralcodes/codes/ec2_2004/annex_b_shrink_and_creep.py +0 -257
- structuralcodes/materials/constitutive_laws.py +0 -981
|
@@ -7,7 +7,8 @@ import typing as t
|
|
|
7
7
|
import warnings
|
|
8
8
|
|
|
9
9
|
import numpy as np
|
|
10
|
-
from numpy.typing import ArrayLike
|
|
10
|
+
from numpy.typing import ArrayLike, NDArray
|
|
11
|
+
from scipy.linalg import lu_factor, lu_solve
|
|
11
12
|
from shapely import MultiPolygon
|
|
12
13
|
from shapely.ops import unary_union
|
|
13
14
|
|
|
@@ -20,7 +21,7 @@ from structuralcodes.geometry import (
|
|
|
20
21
|
)
|
|
21
22
|
from structuralcodes.materials.constitutive_laws import Elastic
|
|
22
23
|
|
|
23
|
-
from .section_integrators import integrator_factory
|
|
24
|
+
from .section_integrators import SectionIntegrator, integrator_factory
|
|
24
25
|
|
|
25
26
|
|
|
26
27
|
class GenericSection(Section):
|
|
@@ -55,6 +56,16 @@ class GenericSection(Section):
|
|
|
55
56
|
of the section.
|
|
56
57
|
name (str): The name of the section.
|
|
57
58
|
integrator (str): The name of the SectionIntegrator to use.
|
|
59
|
+
kwargs (dict): A collection of keyword arguments to pass on to the
|
|
60
|
+
section calculator.
|
|
61
|
+
|
|
62
|
+
Note:
|
|
63
|
+
The GenericSection uses a GenericSectionCalculator for all
|
|
64
|
+
calculations. The GenericSectionCalculator uses a SectionIntegrator
|
|
65
|
+
for integrating over the section. Any additional keyword arguments
|
|
66
|
+
used when creating the GenericSection are passed on to the
|
|
67
|
+
SectionCalculator to customize the behaviour. See
|
|
68
|
+
GenericSectionCalculator for available keyword arguments.
|
|
58
69
|
"""
|
|
59
70
|
if name is None:
|
|
60
71
|
name = 'GenericSection'
|
|
@@ -72,7 +83,7 @@ class GenericSection(Section):
|
|
|
72
83
|
self._gross_properties = None
|
|
73
84
|
|
|
74
85
|
@property
|
|
75
|
-
def gross_properties(self) -> s_res.
|
|
86
|
+
def gross_properties(self) -> s_res.SectionProperties:
|
|
76
87
|
"""Return the gross properties of the section."""
|
|
77
88
|
if self._gross_properties is None:
|
|
78
89
|
self._gross_properties = (
|
|
@@ -84,6 +95,8 @@ class GenericSection(Section):
|
|
|
84
95
|
class GenericSectionCalculator(SectionCalculator):
|
|
85
96
|
"""Calculator class implementing analysis algorithms for code checks."""
|
|
86
97
|
|
|
98
|
+
integrator: SectionIntegrator
|
|
99
|
+
|
|
87
100
|
def __init__(
|
|
88
101
|
self,
|
|
89
102
|
sec: GenericSection,
|
|
@@ -98,7 +111,7 @@ class GenericSectionCalculator(SectionCalculator):
|
|
|
98
111
|
(default = 'marin').
|
|
99
112
|
|
|
100
113
|
Note:
|
|
101
|
-
When using
|
|
114
|
+
When using `fiber` integrator the kwarg `mesh_size` can be used to
|
|
102
115
|
specify a dimensionless number (between 0 and 1) specifying the
|
|
103
116
|
size of the resulting mesh.
|
|
104
117
|
"""
|
|
@@ -113,17 +126,17 @@ class GenericSectionCalculator(SectionCalculator):
|
|
|
113
126
|
self._n_max = None
|
|
114
127
|
self._n_min = None
|
|
115
128
|
|
|
116
|
-
def _calculate_gross_section_properties(self) -> s_res.
|
|
129
|
+
def _calculate_gross_section_properties(self) -> s_res.SectionProperties:
|
|
117
130
|
"""Calculates the gross section properties of the GenericSection.
|
|
118
131
|
|
|
119
132
|
This function is private and called when the section is created.
|
|
120
133
|
It stores the result into the result object.
|
|
121
134
|
|
|
122
135
|
Returns:
|
|
123
|
-
|
|
136
|
+
SectionProperties: The gross properties of the section.
|
|
124
137
|
"""
|
|
125
138
|
# It will use the algorithms for generic sections
|
|
126
|
-
gp = s_res.
|
|
139
|
+
gp = s_res.SectionProperties()
|
|
127
140
|
|
|
128
141
|
# Computation of perimeter using shapely
|
|
129
142
|
polygon = unary_union(
|
|
@@ -142,13 +155,13 @@ class GenericSectionCalculator(SectionCalculator):
|
|
|
142
155
|
# Computation of surface area, reinforcement area, EA (axial rigidity)
|
|
143
156
|
# and mass: Morten -> problem with units! how do we deal with it?
|
|
144
157
|
for geo in self.section.geometry.geometries:
|
|
145
|
-
gp.ea += geo.area * geo.material.get_tangent(eps=0)
|
|
158
|
+
gp.ea += geo.area * geo.material.get_tangent(eps=0)
|
|
146
159
|
if geo.density is not None:
|
|
147
160
|
# this assumes area in mm2 and density in kg/m3
|
|
148
161
|
gp.mass += geo.area * geo.density * 1e-9
|
|
149
162
|
|
|
150
163
|
for geo in self.section.geometry.point_geometries:
|
|
151
|
-
gp.ea += geo.area * geo.material.get_tangent(eps=0)
|
|
164
|
+
gp.ea += geo.area * geo.material.get_tangent(eps=0)
|
|
152
165
|
gp.area_reinforcement += geo.area
|
|
153
166
|
if geo.density is not None:
|
|
154
167
|
# this assumes area in mm2 and density in kg/m3
|
|
@@ -325,7 +338,7 @@ class GenericSectionCalculator(SectionCalculator):
|
|
|
325
338
|
|
|
326
339
|
def find_equilibrium_fixed_pivot(
|
|
327
340
|
self, geom: CompoundGeometry, n: float, yielding: bool = False
|
|
328
|
-
) -> t.
|
|
341
|
+
) -> t.List[float]:
|
|
329
342
|
"""Find the equilibrium changing curvature fixed a pivot.
|
|
330
343
|
The algorithm uses bisection algorithm between curvature
|
|
331
344
|
of balanced failure and 0. Selected the pivot point as
|
|
@@ -339,8 +352,8 @@ class GenericSectionCalculator(SectionCalculator):
|
|
|
339
352
|
yielding (bool): ...
|
|
340
353
|
|
|
341
354
|
Returns:
|
|
342
|
-
|
|
343
|
-
|
|
355
|
+
List(float): 3 floats: Axial strain at (0,0), and curvatures of y*
|
|
356
|
+
and z* axes. Note that being uniaxial bending,
|
|
344
357
|
curvature along z* is 0.0.
|
|
345
358
|
"""
|
|
346
359
|
# Number of maximum iteration for the bisection algorithm
|
|
@@ -359,6 +372,10 @@ class GenericSectionCalculator(SectionCalculator):
|
|
|
359
372
|
) = self.integrator.integrate_strain_response_on_geometry(
|
|
360
373
|
geom, strain, tri=self.triangulated_data, mesh_size=self.mesh_size
|
|
361
374
|
)
|
|
375
|
+
# save the triangulation data
|
|
376
|
+
if self.triangulated_data is None and tri is not None:
|
|
377
|
+
self.triangulated_data = tri
|
|
378
|
+
|
|
362
379
|
# Check if there is equilibrium with this strain distribution
|
|
363
380
|
chi_a = strain[1]
|
|
364
381
|
dn_a = n_int - n
|
|
@@ -404,9 +421,6 @@ class GenericSectionCalculator(SectionCalculator):
|
|
|
404
421
|
s = f'Last iteration reached a unbalance of {dn_c}'
|
|
405
422
|
raise ValueError(f'Maximum number of iterations reached.\n{s}')
|
|
406
423
|
# Found equilibrium
|
|
407
|
-
# save the triangulation data
|
|
408
|
-
if self.triangulated_data is None:
|
|
409
|
-
self.triangulated_data = tri
|
|
410
424
|
# Return the strain distribution
|
|
411
425
|
return [eps_0, chi_c, 0]
|
|
412
426
|
|
|
@@ -486,7 +500,7 @@ class GenericSectionCalculator(SectionCalculator):
|
|
|
486
500
|
) = self.integrator.integrate_strain_response_on_geometry(
|
|
487
501
|
geom, [eps_0, curv, 0], tri=self.triangulated_data
|
|
488
502
|
)
|
|
489
|
-
if self.triangulated_data is None:
|
|
503
|
+
if self.triangulated_data is None and tri is not None:
|
|
490
504
|
self.triangulated_data = tri
|
|
491
505
|
dn_a = n_int - n
|
|
492
506
|
# It may occur that dn_a is already almost zero (in eqiulibrium)
|
|
@@ -547,7 +561,7 @@ class GenericSectionCalculator(SectionCalculator):
|
|
|
547
561
|
self.section.geometry, [eps_p, 0, 0], tri=tri
|
|
548
562
|
)
|
|
549
563
|
|
|
550
|
-
if self.triangulated_data is None:
|
|
564
|
+
if self.triangulated_data is None and tri is not None:
|
|
551
565
|
self.triangulated_data = tri
|
|
552
566
|
return n_min, n_max
|
|
553
567
|
|
|
@@ -594,25 +608,60 @@ class GenericSectionCalculator(SectionCalculator):
|
|
|
594
608
|
self.triangulated_data = rotated_triangulated_data
|
|
595
609
|
|
|
596
610
|
def integrate_strain_profile(
|
|
597
|
-
self,
|
|
598
|
-
|
|
599
|
-
|
|
611
|
+
self,
|
|
612
|
+
strain: ArrayLike,
|
|
613
|
+
integrate: t.Literal['stress', 'modulus'] = 'stress',
|
|
614
|
+
) -> t.Union[t.Tuple[float, float, float], NDArray]:
|
|
615
|
+
"""Integrate a strain profile returning stress resultants or tangent
|
|
616
|
+
section stiffness matrix.
|
|
600
617
|
|
|
601
618
|
Arguments:
|
|
602
619
|
strain (ArrayLike): Represents the deformation plane. The strain
|
|
603
620
|
should have three entries representing respectively: axial
|
|
604
621
|
strain (At 0,0 coordinates), curv_y, curv_z.
|
|
622
|
+
integrate (str): a string indicating the quantity to integrate over
|
|
623
|
+
the section. It can be 'stress' or 'modulus'. When 'stress'
|
|
624
|
+
is selected, the return value will be the stress resultants N,
|
|
625
|
+
My, Mz, while if 'modulus' is selected, the return will be the
|
|
626
|
+
tangent section stiffness matrix (default is 'stress').
|
|
605
627
|
|
|
606
628
|
Returns:
|
|
607
|
-
Tuple(float, float, float): N, My and Mz
|
|
629
|
+
Union(Tuple(float, float, float),NDArray): N, My and Mz when
|
|
630
|
+
`integrate='stress'`, or a numpy array representing the stiffness
|
|
631
|
+
matrix then `integrate='modulus'`.
|
|
632
|
+
|
|
633
|
+
Examples:
|
|
634
|
+
result = self.integrate_strain_profile(strain,integrate='tangent')
|
|
635
|
+
# `result` will be the tangent stiffness matrix (a 3x3 numpy array)
|
|
636
|
+
|
|
637
|
+
result = self.integrate_strain_profile(strain)
|
|
638
|
+
# `result` will be a tuple containing section forces (N, My, Mz)
|
|
639
|
+
|
|
640
|
+
Raises:
|
|
641
|
+
ValueError: If a unkown value is passed to the `integrate`
|
|
642
|
+
parameter.
|
|
608
643
|
"""
|
|
609
|
-
|
|
644
|
+
result = self.integrator.integrate_strain_response_on_geometry(
|
|
610
645
|
geo=self.section.geometry,
|
|
611
646
|
strain=strain,
|
|
647
|
+
integrate=integrate,
|
|
612
648
|
tri=self.triangulated_data,
|
|
613
649
|
mesh_size=self.mesh_size,
|
|
614
650
|
)
|
|
615
|
-
|
|
651
|
+
|
|
652
|
+
# Save triangulation data for future use
|
|
653
|
+
if self.triangulated_data is None and result[-1] is not None:
|
|
654
|
+
self.triangulated_data = result[-1]
|
|
655
|
+
|
|
656
|
+
# manage the returning from integrate_strain_response_on_geometry:
|
|
657
|
+
# this function returns one of the two:
|
|
658
|
+
# a. float, float, float, tri (i.e. N, My, Mz, tri)
|
|
659
|
+
# b. (NDArray, tri) (i.e. section stiff matrix, tri)
|
|
660
|
+
# We need to return only forces or stifness
|
|
661
|
+
# (without triangultion data)
|
|
662
|
+
if len(result) == 2:
|
|
663
|
+
return result[0]
|
|
664
|
+
return result[:-1]
|
|
616
665
|
|
|
617
666
|
def calculate_bending_strength(
|
|
618
667
|
self, theta=0, n=0
|
|
@@ -654,13 +703,15 @@ class GenericSectionCalculator(SectionCalculator):
|
|
|
654
703
|
if self.triangulated_data is not None:
|
|
655
704
|
# Rotate back also triangulated data!
|
|
656
705
|
self._rotate_triangulated_data(theta)
|
|
706
|
+
# Rotate also curvature!
|
|
707
|
+
strain_rotated = T @ np.array([[strain[1]], [strain[2]]])
|
|
657
708
|
|
|
658
709
|
# Create result object
|
|
659
710
|
res = s_res.UltimateBendingMomentResults()
|
|
660
711
|
res.theta = theta
|
|
661
712
|
res.n = N
|
|
662
|
-
res.chi_y =
|
|
663
|
-
res.chi_z =
|
|
713
|
+
res.chi_y = strain_rotated[0, 0]
|
|
714
|
+
res.chi_z = strain_rotated[1, 0]
|
|
664
715
|
res.eps_a = strain[0]
|
|
665
716
|
res.m_y = M[0, 0]
|
|
666
717
|
res.m_z = M[1, 0]
|
|
@@ -959,7 +1010,7 @@ class GenericSectionCalculator(SectionCalculator):
|
|
|
959
1010
|
mesh_size=self.mesh_size,
|
|
960
1011
|
)
|
|
961
1012
|
)
|
|
962
|
-
if self.triangulated_data is None:
|
|
1013
|
+
if self.triangulated_data is None and tri is not None:
|
|
963
1014
|
self.triangulated_data = tri
|
|
964
1015
|
forces[i, 0] = N
|
|
965
1016
|
forces[i, 1] = My
|
|
@@ -1229,7 +1280,7 @@ class GenericSectionCalculator(SectionCalculator):
|
|
|
1229
1280
|
tri=self.triangulated_data,
|
|
1230
1281
|
)
|
|
1231
1282
|
)
|
|
1232
|
-
if self.triangulated_data is None:
|
|
1283
|
+
if self.triangulated_data is None and tri is not None:
|
|
1233
1284
|
self.triangulated_data = tri
|
|
1234
1285
|
forces[i, 0] = N
|
|
1235
1286
|
forces[i, 1] = My
|
|
@@ -1275,3 +1326,99 @@ class GenericSectionCalculator(SectionCalculator):
|
|
|
1275
1326
|
res.strains[i, 2] = res_bend_strength.chi_z
|
|
1276
1327
|
|
|
1277
1328
|
return res
|
|
1329
|
+
|
|
1330
|
+
def calculate_strain_profile(
|
|
1331
|
+
self,
|
|
1332
|
+
n,
|
|
1333
|
+
my,
|
|
1334
|
+
mz,
|
|
1335
|
+
initial: bool = False,
|
|
1336
|
+
max_iter: int = 10,
|
|
1337
|
+
tol: float = 1e-6,
|
|
1338
|
+
) -> t.List[float]:
|
|
1339
|
+
"""Get the strain plane for a given axial force and biaxial bending.
|
|
1340
|
+
|
|
1341
|
+
Args:
|
|
1342
|
+
n (float): Axial load.
|
|
1343
|
+
my (float): Bending moment around y-axis.
|
|
1344
|
+
mz (float): Bending moment around z-axis.
|
|
1345
|
+
initial (bool): If True the modified newton with initial tanget is
|
|
1346
|
+
used (default = False).
|
|
1347
|
+
max_iter (int): the maximum number of iterations in the iterative
|
|
1348
|
+
process (default = 10).
|
|
1349
|
+
tol (float): the tolerance for convergence test in terms of strain
|
|
1350
|
+
increment.
|
|
1351
|
+
|
|
1352
|
+
Returns:
|
|
1353
|
+
List(float): 3 floats: Axial strain at (0,0), and curvatures of the
|
|
1354
|
+
section around y and z axes.
|
|
1355
|
+
"""
|
|
1356
|
+
# Get the gometry
|
|
1357
|
+
geom = self.section.geometry
|
|
1358
|
+
|
|
1359
|
+
# Collect loads in a numpy array
|
|
1360
|
+
loads = np.array([n, my, mz])
|
|
1361
|
+
|
|
1362
|
+
# Compute initial tangent stiffness matrix
|
|
1363
|
+
stiffness_tangent, tri = (
|
|
1364
|
+
self.integrator.integrate_strain_response_on_geometry(
|
|
1365
|
+
geom,
|
|
1366
|
+
[0, 0, 0],
|
|
1367
|
+
integrate='modulus',
|
|
1368
|
+
tri=self.triangulated_data,
|
|
1369
|
+
)
|
|
1370
|
+
)
|
|
1371
|
+
# eventually save the triangulation data
|
|
1372
|
+
if self.triangulated_data is None and tri is not None:
|
|
1373
|
+
self.triangulated_data = tri
|
|
1374
|
+
|
|
1375
|
+
# Calculate strain plane with Newton Rhapson Iterative method
|
|
1376
|
+
num_iter = 0
|
|
1377
|
+
strain = np.zeros(3)
|
|
1378
|
+
|
|
1379
|
+
# Factorize once the stiffness matrix if using initial
|
|
1380
|
+
if initial:
|
|
1381
|
+
# LU factorization
|
|
1382
|
+
# (maybe also Choelesky could work if matrix always SPD?)
|
|
1383
|
+
lu, piv = lu_factor(stiffness_tangent)
|
|
1384
|
+
|
|
1385
|
+
# Do Newton loops
|
|
1386
|
+
while True:
|
|
1387
|
+
# Check if number of iterations exceeds the maximum
|
|
1388
|
+
if num_iter > max_iter:
|
|
1389
|
+
break
|
|
1390
|
+
|
|
1391
|
+
# Calculate response and residuals
|
|
1392
|
+
response = np.array(self.integrate_strain_profile(strain=strain))
|
|
1393
|
+
residual = loads - response
|
|
1394
|
+
|
|
1395
|
+
if initial:
|
|
1396
|
+
# Solve using the decomposed matrix
|
|
1397
|
+
delta_strain = lu_solve((lu, piv), residual)
|
|
1398
|
+
else:
|
|
1399
|
+
# Calculate the current tangent stiffness
|
|
1400
|
+
stiffness_tangent, _ = (
|
|
1401
|
+
self.integrator.integrate_strain_response_on_geometry(
|
|
1402
|
+
geom,
|
|
1403
|
+
strain,
|
|
1404
|
+
integrate='modulus',
|
|
1405
|
+
tri=self.triangulated_data,
|
|
1406
|
+
)
|
|
1407
|
+
)
|
|
1408
|
+
|
|
1409
|
+
# Solve using the current tangent stiffness
|
|
1410
|
+
delta_strain = np.linalg.solve(stiffness_tangent, residual)
|
|
1411
|
+
|
|
1412
|
+
# Update the strain
|
|
1413
|
+
strain += delta_strain
|
|
1414
|
+
|
|
1415
|
+
num_iter += 1
|
|
1416
|
+
|
|
1417
|
+
# Check for convergence:
|
|
1418
|
+
if np.linalg.norm(delta_strain) < tol:
|
|
1419
|
+
break
|
|
1420
|
+
|
|
1421
|
+
if num_iter >= max_iter:
|
|
1422
|
+
raise StopIteration('Maximum number of iterations reached.')
|
|
1423
|
+
|
|
1424
|
+
return strain.tolist()
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"""A collection of functions related to reinforced concrete sections."""
|
|
2
|
+
|
|
3
|
+
import typing as t
|
|
4
|
+
|
|
5
|
+
import structuralcodes.core._section_results as s_res
|
|
6
|
+
from structuralcodes.geometry import CompoundGeometry, SurfaceGeometry
|
|
7
|
+
from structuralcodes.materials.constitutive_laws import Elastic, UserDefined
|
|
8
|
+
from structuralcodes.sections import GenericSection
|
|
9
|
+
from structuralcodes.sections.section_integrators import FiberIntegrator
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def calculate_elastic_cracked_properties(
|
|
13
|
+
section: GenericSection,
|
|
14
|
+
theta: float = 0,
|
|
15
|
+
return_cracked_section: bool = False,
|
|
16
|
+
) -> t.Union[
|
|
17
|
+
s_res.SectionProperties, t.Tuple[s_res.SectionProperties, CompoundGeometry]
|
|
18
|
+
]:
|
|
19
|
+
"""Calculates the cracked section properties of a reinforced concrete
|
|
20
|
+
section. (GenericSection). Materials in surface geometries and point
|
|
21
|
+
geometries are elastic-linear in order to make the cracking properties
|
|
22
|
+
independent of the stress state. Tension in all surface geometries is
|
|
23
|
+
neglected.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
section (GenericSection): The section to use as basis for the
|
|
27
|
+
calculation.
|
|
28
|
+
theta (float): Angle of the neutral axis to the horizontal in radians.
|
|
29
|
+
theta=0 implies upper compression block.
|
|
30
|
+
return_cracked_section (bool): If true, returns also the cracked
|
|
31
|
+
section.
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
t.Union[SectionProperties, t.Tuple[SectionProperties, GenericSection]]:
|
|
35
|
+
SectionProperties data of a cracked section (i.e cracked section
|
|
36
|
+
properties) and (if return_cracked_section is True) the cracked
|
|
37
|
+
section.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
def create_surface_geometries(polygons_list, material):
|
|
41
|
+
"""Process shapely polygons to SurfaceGeometries."""
|
|
42
|
+
# Create an empty list to store SurfaceGeometry objects
|
|
43
|
+
surface_geometries = []
|
|
44
|
+
|
|
45
|
+
# Iterate over the list of polygons and create SurfaceGeometry for each
|
|
46
|
+
for polygon in polygons_list:
|
|
47
|
+
# Create a new SurfaceGeometry for the current polygon
|
|
48
|
+
surface_geometry = SurfaceGeometry(polygon, material)
|
|
49
|
+
# Add the new SurfaceGeometry to the list
|
|
50
|
+
surface_geometries.append(surface_geometry)
|
|
51
|
+
|
|
52
|
+
return CompoundGeometry(surface_geometries)
|
|
53
|
+
|
|
54
|
+
if not section.geometry.reinforced_concrete:
|
|
55
|
+
return None
|
|
56
|
+
|
|
57
|
+
rotated_geometry = section.geometry.rotate(-theta)
|
|
58
|
+
|
|
59
|
+
for geo in rotated_geometry.geometries:
|
|
60
|
+
Ec = geo.material.get_tangent(eps=0)
|
|
61
|
+
elastic_concrete = UserDefined([-100, 0], [-100 * Ec, 0])
|
|
62
|
+
geo._material = elastic_concrete
|
|
63
|
+
|
|
64
|
+
for pg in rotated_geometry.point_geometries:
|
|
65
|
+
Es = pg.material.get_tangent(eps=0)
|
|
66
|
+
elastic_steel = Elastic(Es, 'elastic steel')
|
|
67
|
+
pg._material = elastic_steel
|
|
68
|
+
|
|
69
|
+
curv = -1e-5 # Any curvature should return the same mechanical properties.
|
|
70
|
+
# Find the equilibrium with fixed curvature
|
|
71
|
+
eps = section.section_calculator.find_equilibrium_fixed_curvature(
|
|
72
|
+
rotated_geometry, 0, curv, 0
|
|
73
|
+
)[0]
|
|
74
|
+
z_na = -eps / curv # distance to neutral fibre
|
|
75
|
+
|
|
76
|
+
# Cutting concrete geometries and retaining the compressed block
|
|
77
|
+
cut_geom = None
|
|
78
|
+
for part in rotated_geometry.geometries:
|
|
79
|
+
upper_div, lower_div = part.split(((0, z_na), 0))
|
|
80
|
+
# Convert to SurfaceGeometry
|
|
81
|
+
subpart = create_surface_geometries(upper_div, part.material)
|
|
82
|
+
if cut_geom is None:
|
|
83
|
+
cut_geom = subpart
|
|
84
|
+
else:
|
|
85
|
+
cut_geom += subpart
|
|
86
|
+
|
|
87
|
+
# Add reinforcement geometries
|
|
88
|
+
for reinf in rotated_geometry.point_geometries:
|
|
89
|
+
cut_geom += reinf
|
|
90
|
+
|
|
91
|
+
# return geoemtry to original rotation
|
|
92
|
+
cracked_geom = cut_geom.rotate(theta)
|
|
93
|
+
|
|
94
|
+
# Define the cracked section
|
|
95
|
+
if isinstance(section.section_calculator.integrator, FiberIntegrator):
|
|
96
|
+
mesh_size = getattr(
|
|
97
|
+
section.section_calculator.integrator, 'mesh_size', 0.01
|
|
98
|
+
)
|
|
99
|
+
cracked_sec = GenericSection(
|
|
100
|
+
cracked_geom,
|
|
101
|
+
integrator='fiber',
|
|
102
|
+
mesh_size=mesh_size,
|
|
103
|
+
)
|
|
104
|
+
else:
|
|
105
|
+
cracked_sec = GenericSection(cracked_geom, integrator='marin')
|
|
106
|
+
|
|
107
|
+
cracked_prop = cracked_sec.gross_properties
|
|
108
|
+
|
|
109
|
+
if return_cracked_section:
|
|
110
|
+
result = (cracked_prop, cracked_geom)
|
|
111
|
+
else:
|
|
112
|
+
result = cracked_prop
|
|
113
|
+
|
|
114
|
+
return result
|