structuralcodes 0.5.0__tar.gz → 0.6.1__tar.gz
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-0.5.0 → structuralcodes-0.6.1}/PKG-INFO +2 -2
- {structuralcodes-0.5.0 → structuralcodes-0.6.1}/pyproject.toml +1 -1
- {structuralcodes-0.5.0 → structuralcodes-0.6.1}/structuralcodes/__init__.py +1 -1
- {structuralcodes-0.5.0 → structuralcodes-0.6.1}/structuralcodes/codes/ec2_2004/shear.py +3 -2
- {structuralcodes-0.5.0 → structuralcodes-0.6.1}/structuralcodes/codes/mc2010/_concrete_creep_and_shrinkage.py +2 -2
- {structuralcodes-0.5.0 → structuralcodes-0.6.1}/structuralcodes/core/base.py +138 -12
- {structuralcodes-0.5.0 → structuralcodes-0.6.1}/structuralcodes/geometry/__init__.py +2 -8
- {structuralcodes-0.5.0 → structuralcodes-0.6.1}/structuralcodes/geometry/_geometry.py +103 -19
- {structuralcodes-0.5.0 → structuralcodes-0.6.1}/structuralcodes/geometry/_reinforcement.py +1 -3
- structuralcodes-0.6.1/structuralcodes/geometry/profiles/__init__.py +33 -0
- structuralcodes-0.6.1/structuralcodes/geometry/profiles/_base_profile.py +305 -0
- structuralcodes-0.6.1/structuralcodes/geometry/profiles/_common_functions.py +307 -0
- structuralcodes-0.6.1/structuralcodes/geometry/profiles/_hd.py +374 -0
- structuralcodes-0.6.1/structuralcodes/geometry/profiles/_he.py +192 -0
- structuralcodes-0.6.1/structuralcodes/geometry/profiles/_hp.py +319 -0
- structuralcodes-0.6.1/structuralcodes/geometry/profiles/_ipe.py +130 -0
- structuralcodes-0.6.1/structuralcodes/geometry/profiles/_ipn.py +329 -0
- structuralcodes-0.6.1/structuralcodes/geometry/profiles/_l.py +528 -0
- structuralcodes-0.6.1/structuralcodes/geometry/profiles/_li.py +217 -0
- structuralcodes-0.6.1/structuralcodes/geometry/profiles/_u.py +173 -0
- structuralcodes-0.6.1/structuralcodes/geometry/profiles/_ub.py +1356 -0
- structuralcodes-0.6.1/structuralcodes/geometry/profiles/_ubp.py +227 -0
- structuralcodes-0.6.1/structuralcodes/geometry/profiles/_uc.py +276 -0
- structuralcodes-0.6.1/structuralcodes/geometry/profiles/_upe.py +133 -0
- structuralcodes-0.6.1/structuralcodes/geometry/profiles/_upn.py +315 -0
- structuralcodes-0.6.1/structuralcodes/geometry/profiles/_w.py +2157 -0
- {structuralcodes-0.5.0 → structuralcodes-0.6.1}/structuralcodes/materials/basic/_elastic.py +18 -1
- {structuralcodes-0.5.0 → structuralcodes-0.6.1}/structuralcodes/materials/basic/_elasticplastic.py +18 -1
- structuralcodes-0.6.1/structuralcodes/materials/basic/_generic.py +43 -0
- {structuralcodes-0.5.0 → structuralcodes-0.6.1}/structuralcodes/materials/concrete/__init__.py +3 -0
- {structuralcodes-0.5.0 → structuralcodes-0.6.1}/structuralcodes/materials/concrete/_concrete.py +10 -1
- {structuralcodes-0.5.0 → structuralcodes-0.6.1}/structuralcodes/materials/concrete/_concreteEC2_2004.py +15 -1
- {structuralcodes-0.5.0 → structuralcodes-0.6.1}/structuralcodes/materials/concrete/_concreteEC2_2023.py +15 -1
- {structuralcodes-0.5.0 → structuralcodes-0.6.1}/structuralcodes/materials/concrete/_concreteMC2010.py +20 -1
- {structuralcodes-0.5.0 → structuralcodes-0.6.1}/structuralcodes/materials/constitutive_laws/__init__.py +3 -0
- {structuralcodes-0.5.0 → structuralcodes-0.6.1}/structuralcodes/materials/constitutive_laws/_elasticplastic.py +2 -2
- structuralcodes-0.6.1/structuralcodes/materials/constitutive_laws/_initial_strain.py +130 -0
- {structuralcodes-0.5.0 → structuralcodes-0.6.1}/structuralcodes/materials/reinforcement/__init__.py +6 -0
- {structuralcodes-0.5.0 → structuralcodes-0.6.1}/structuralcodes/materials/reinforcement/_reinforcement.py +10 -1
- {structuralcodes-0.5.0 → structuralcodes-0.6.1}/structuralcodes/materials/reinforcement/_reinforcementEC2_2004.py +14 -0
- {structuralcodes-0.5.0 → structuralcodes-0.6.1}/structuralcodes/materials/reinforcement/_reinforcementEC2_2023.py +14 -0
- {structuralcodes-0.5.0 → structuralcodes-0.6.1}/structuralcodes/materials/reinforcement/_reinforcementMC2010.py +14 -0
- {structuralcodes-0.5.0 → structuralcodes-0.6.1}/structuralcodes/sections/section_integrators/__init__.py +3 -1
- {structuralcodes-0.5.0 → structuralcodes-0.6.1}/structuralcodes/sections/section_integrators/_marin_integrator.py +1 -1
- structuralcodes-0.5.0/structuralcodes/geometry/_steel_sections.py +0 -2155
- structuralcodes-0.5.0/structuralcodes/materials/basic/_generic.py +0 -26
- {structuralcodes-0.5.0 → structuralcodes-0.6.1}/LICENSE +0 -0
- {structuralcodes-0.5.0 → structuralcodes-0.6.1}/README.md +0 -0
- {structuralcodes-0.5.0 → structuralcodes-0.6.1}/structuralcodes/codes/__init__.py +0 -0
- {structuralcodes-0.5.0 → structuralcodes-0.6.1}/structuralcodes/codes/ec2_2004/__init__.py +0 -0
- {structuralcodes-0.5.0 → structuralcodes-0.6.1}/structuralcodes/codes/ec2_2004/_concrete_creep_and_shrinkage.py +0 -0
- {structuralcodes-0.5.0 → structuralcodes-0.6.1}/structuralcodes/codes/ec2_2004/_concrete_material_properties.py +0 -0
- {structuralcodes-0.5.0 → structuralcodes-0.6.1}/structuralcodes/codes/ec2_2004/_reinforcement_material_properties.py +0 -0
- {structuralcodes-0.5.0 → structuralcodes-0.6.1}/structuralcodes/codes/ec2_2004/_section_7_3_crack_control.py +0 -0
- {structuralcodes-0.5.0 → structuralcodes-0.6.1}/structuralcodes/codes/ec2_2023/__init__.py +0 -0
- {structuralcodes-0.5.0 → structuralcodes-0.6.1}/structuralcodes/codes/ec2_2023/_annexB_time_dependent.py +0 -0
- {structuralcodes-0.5.0 → structuralcodes-0.6.1}/structuralcodes/codes/ec2_2023/_section5_materials.py +0 -0
- {structuralcodes-0.5.0 → structuralcodes-0.6.1}/structuralcodes/codes/ec2_2023/_section9_sls.py +0 -0
- {structuralcodes-0.5.0 → structuralcodes-0.6.1}/structuralcodes/codes/mc2010/__init__.py +0 -0
- {structuralcodes-0.5.0 → structuralcodes-0.6.1}/structuralcodes/codes/mc2010/_concrete_interface_different_casting_times.py +0 -0
- {structuralcodes-0.5.0 → structuralcodes-0.6.1}/structuralcodes/codes/mc2010/_concrete_material_properties.py +0 -0
- {structuralcodes-0.5.0 → structuralcodes-0.6.1}/structuralcodes/codes/mc2010/_concrete_punching.py +0 -0
- {structuralcodes-0.5.0 → structuralcodes-0.6.1}/structuralcodes/codes/mc2010/_concrete_shear.py +0 -0
- {structuralcodes-0.5.0 → structuralcodes-0.6.1}/structuralcodes/codes/mc2010/_concrete_torsion.py +0 -0
- {structuralcodes-0.5.0 → structuralcodes-0.6.1}/structuralcodes/codes/mc2010/_interface_concrete_steel_rebar.py +0 -0
- {structuralcodes-0.5.0 → structuralcodes-0.6.1}/structuralcodes/codes/mc2010/_reinforcement_material_properties.py +0 -0
- {structuralcodes-0.5.0 → structuralcodes-0.6.1}/structuralcodes/codes/mc2020/__init__.py +0 -0
- {structuralcodes-0.5.0 → structuralcodes-0.6.1}/structuralcodes/core/__init__.py +0 -0
- {structuralcodes-0.5.0/structuralcodes/sections/section_integrators → structuralcodes-0.6.1/structuralcodes/core}/_marin_integration.py +0 -0
- {structuralcodes-0.5.0 → structuralcodes-0.6.1}/structuralcodes/core/_section_results.py +0 -0
- {structuralcodes-0.5.0 → structuralcodes-0.6.1}/structuralcodes/geometry/_circular.py +0 -0
- {structuralcodes-0.5.0 → structuralcodes-0.6.1}/structuralcodes/geometry/_rectangular.py +0 -0
- {structuralcodes-0.5.0 → structuralcodes-0.6.1}/structuralcodes/materials/__init__.py +0 -0
- {structuralcodes-0.5.0 → structuralcodes-0.6.1}/structuralcodes/materials/basic/__init__.py +0 -0
- {structuralcodes-0.5.0 → structuralcodes-0.6.1}/structuralcodes/materials/constitutive_laws/_bilinearcompression.py +0 -0
- {structuralcodes-0.5.0 → structuralcodes-0.6.1}/structuralcodes/materials/constitutive_laws/_elastic.py +0 -0
- {structuralcodes-0.5.0 → structuralcodes-0.6.1}/structuralcodes/materials/constitutive_laws/_parabolarectangle.py +0 -0
- {structuralcodes-0.5.0 → structuralcodes-0.6.1}/structuralcodes/materials/constitutive_laws/_popovics.py +0 -0
- {structuralcodes-0.5.0 → structuralcodes-0.6.1}/structuralcodes/materials/constitutive_laws/_sargin.py +0 -0
- {structuralcodes-0.5.0 → structuralcodes-0.6.1}/structuralcodes/materials/constitutive_laws/_userdefined.py +0 -0
- {structuralcodes-0.5.0 → structuralcodes-0.6.1}/structuralcodes/sections/__init__.py +0 -0
- {structuralcodes-0.5.0 → structuralcodes-0.6.1}/structuralcodes/sections/_generic.py +0 -0
- {structuralcodes-0.5.0 → structuralcodes-0.6.1}/structuralcodes/sections/_rc_utils.py +0 -0
- {structuralcodes-0.5.0 → structuralcodes-0.6.1}/structuralcodes/sections/section_integrators/_factory.py +0 -0
- {structuralcodes-0.5.0 → structuralcodes-0.6.1}/structuralcodes/sections/section_integrators/_fiber_integrator.py +0 -0
- {structuralcodes-0.5.0 → structuralcodes-0.6.1}/structuralcodes/sections/section_integrators/_section_integrator.py +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: structuralcodes
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.1
|
|
4
4
|
Summary: A Python package that contains models from structural design codes.
|
|
5
5
|
Author-email: fib - International Federation for Structural Concrete <info@fib-international.org>
|
|
6
|
-
Requires-Python: >=3.
|
|
6
|
+
Requires-Python: >=3.9
|
|
7
7
|
Description-Content-Type: text/markdown
|
|
8
8
|
Classifier: Programming Language :: Python :: 3
|
|
9
9
|
Classifier: Operating System :: OS Independent
|
|
@@ -10,7 +10,7 @@ authors = [
|
|
|
10
10
|
{name = "fib - International Federation for Structural Concrete", email = "info@fib-international.org"}
|
|
11
11
|
]
|
|
12
12
|
readme = "README.md"
|
|
13
|
-
requires-python = ">=3.
|
|
13
|
+
requires-python = ">=3.9"
|
|
14
14
|
classifiers = [
|
|
15
15
|
"Programming Language :: Python :: 3",
|
|
16
16
|
"Operating System :: OS Independent"
|
|
@@ -203,14 +203,15 @@ def VRdc(
|
|
|
203
203
|
float: The concrete shear resistance in MPa.
|
|
204
204
|
"""
|
|
205
205
|
CRdc = CRdc or 0.18 / gamma_c
|
|
206
|
-
return (
|
|
206
|
+
return max(
|
|
207
207
|
max(
|
|
208
208
|
CRdc * _k(d) * (100 * _rho_L(Asl, bw, d) * fck) ** (1.0 / 3.0)
|
|
209
209
|
+ k1 * _sigma_cp(NEd, Ac, fcd), # VRdc
|
|
210
210
|
vmin(fck, d) + k1 * _sigma_cp(NEd, Ac, fcd), # VRdcmin
|
|
211
211
|
)
|
|
212
212
|
* bw
|
|
213
|
-
* d
|
|
213
|
+
* d,
|
|
214
|
+
0,
|
|
214
215
|
)
|
|
215
216
|
|
|
216
217
|
|
|
@@ -95,13 +95,13 @@ def _check_initial_stress(sigma: float, fcm: float) -> None:
|
|
|
95
95
|
raise ValueError(
|
|
96
96
|
'The stress level exceeds the range of application.'
|
|
97
97
|
'Maximum allowable stress is 0.6*fcm. Current stress level '
|
|
98
|
-
f'is {round(abs(sigma)/fcm, 3)}*fcm.'
|
|
98
|
+
f'is {round(abs(sigma) / fcm, 3)}*fcm.'
|
|
99
99
|
)
|
|
100
100
|
if abs(sigma) > 0.4 * fcm:
|
|
101
101
|
warnings.warn(
|
|
102
102
|
'Initial stress is too high to consider the '
|
|
103
103
|
'concrete as an aging linear visco-elastic material: '
|
|
104
|
-
f'sigma = {round(abs(sigma)/fcm,3)}*fcm > 0.4*fcm. Nonlinear'
|
|
104
|
+
f'sigma = {round(abs(sigma) / fcm, 3)}*fcm > 0.4*fcm. Nonlinear'
|
|
105
105
|
' creep calculations are performed according to subclause '
|
|
106
106
|
'5.1.9.4.3 (d) of the fib Model Code 2010 to account for '
|
|
107
107
|
'large compressive stresses.'
|
|
@@ -15,17 +15,44 @@ class Material(abc.ABC):
|
|
|
15
15
|
"""Abstract base class for materials."""
|
|
16
16
|
|
|
17
17
|
_constitutive_law = None
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
_initial_strain: t.Optional[float] = None
|
|
19
|
+
_initial_stress: t.Optional[float] = None
|
|
20
|
+
_strain_compatibility: t.Optional[bool] = None
|
|
21
|
+
|
|
22
|
+
def __init__(
|
|
23
|
+
self,
|
|
24
|
+
density: float,
|
|
25
|
+
initial_strain: t.Optional[float] = None,
|
|
26
|
+
initial_stress: t.Optional[float] = None,
|
|
27
|
+
strain_compatibility: t.Optional[bool] = None,
|
|
28
|
+
name: t.Optional[str] = None,
|
|
29
|
+
) -> None:
|
|
20
30
|
"""Initializes an instance of a new material.
|
|
21
31
|
|
|
22
32
|
Args:
|
|
23
|
-
density (float):
|
|
33
|
+
density (float): Density of the material in kg/m3.
|
|
24
34
|
|
|
25
35
|
Keyword Args:
|
|
36
|
+
initial_strain (Optional[float]): Initial strain of the material.
|
|
37
|
+
initial_stress (Optional[float]): Initial stress of the material.
|
|
38
|
+
strain_compatibility (Optional[bool]): Only relevant if
|
|
39
|
+
initial_strain or initial_stress are different from zero. If
|
|
40
|
+
True, the material deforms with the geometry. If False, the
|
|
41
|
+
stress in the material upon loading is kept constant
|
|
42
|
+
corresponding to the initial strain.
|
|
26
43
|
name (Optional[str]): descriptive name of the material
|
|
44
|
+
|
|
45
|
+
Raise:
|
|
46
|
+
ValueError: if both initial_strain and initial_stress are provided
|
|
27
47
|
"""
|
|
28
48
|
self._density = abs(density)
|
|
49
|
+
if initial_strain is not None and initial_stress is not None:
|
|
50
|
+
raise ValueError(
|
|
51
|
+
'Both initial_strain and initial_stress cannot be provided.'
|
|
52
|
+
)
|
|
53
|
+
self._initial_strain = initial_strain
|
|
54
|
+
self._initial_stress = initial_stress
|
|
55
|
+
self._strain_compatibility = strain_compatibility
|
|
29
56
|
self._name = name if name is not None else 'Material'
|
|
30
57
|
|
|
31
58
|
@property
|
|
@@ -43,6 +70,88 @@ class Material(abc.ABC):
|
|
|
43
70
|
"""Returns the density of the material in kg/m3."""
|
|
44
71
|
return self._density
|
|
45
72
|
|
|
73
|
+
@property
|
|
74
|
+
def initial_strain(self):
|
|
75
|
+
"""Returns the initial strain of the material."""
|
|
76
|
+
return self._initial_strain
|
|
77
|
+
|
|
78
|
+
@property
|
|
79
|
+
def initial_stress(self):
|
|
80
|
+
"""Returns the initial stress of the material."""
|
|
81
|
+
return self._initial_stress
|
|
82
|
+
|
|
83
|
+
@property
|
|
84
|
+
def strain_compatibility(self):
|
|
85
|
+
"""Returns the strain compatibility of the material.
|
|
86
|
+
|
|
87
|
+
If true (default), the strain compatibility is enforced
|
|
88
|
+
haveing the same strain as in all other materials of the
|
|
89
|
+
section at the same point. If false, the strain compatibility
|
|
90
|
+
is not enforced and the initial strain is applied to the section
|
|
91
|
+
independently.
|
|
92
|
+
"""
|
|
93
|
+
return self._strain_compatibility
|
|
94
|
+
|
|
95
|
+
def _apply_initial_strain(self):
|
|
96
|
+
"""Wraps the current constitutive law to apply initial strain."""
|
|
97
|
+
strain_compatibility = (
|
|
98
|
+
self._strain_compatibility
|
|
99
|
+
if self._strain_compatibility is not None
|
|
100
|
+
else True
|
|
101
|
+
)
|
|
102
|
+
if self._initial_stress is not None:
|
|
103
|
+
# Specified a stress, compute the strain from it
|
|
104
|
+
self._initial_strain_from_stress()
|
|
105
|
+
if self._initial_strain is not None:
|
|
106
|
+
# Lazy import to avoid circular dependency
|
|
107
|
+
from structuralcodes.materials.constitutive_laws import ( # noqa: PLC0415
|
|
108
|
+
InitialStrain,
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
if self._initial_stress is None:
|
|
112
|
+
# Compute the stress from the strain
|
|
113
|
+
self._initial_stress = self._constitutive_law.get_stress(
|
|
114
|
+
self._initial_strain
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
self._constitutive_law = InitialStrain(
|
|
118
|
+
self._constitutive_law,
|
|
119
|
+
self._initial_strain,
|
|
120
|
+
strain_compatibility,
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
def _initial_strain_from_stress(self):
|
|
124
|
+
"""Computes the initial strain from the initial stress.
|
|
125
|
+
|
|
126
|
+
This function is called internally so it assumes that the
|
|
127
|
+
initial stress is not None
|
|
128
|
+
"""
|
|
129
|
+
# Iteratively compute the initial strain that gives the desired
|
|
130
|
+
# initial stress. Note that the wrapped law can be nonlinear
|
|
131
|
+
tol = 1e-12
|
|
132
|
+
max_iter = 100
|
|
133
|
+
target_stress = self._initial_stress
|
|
134
|
+
strain = 0.0
|
|
135
|
+
stress = self._constitutive_law.get_stress(strain)
|
|
136
|
+
d_stress = target_stress - stress
|
|
137
|
+
num_iter = 0
|
|
138
|
+
while abs(d_stress) > tol and num_iter < max_iter:
|
|
139
|
+
tangent = self._constitutive_law.get_tangent(strain)
|
|
140
|
+
if tangent == 0:
|
|
141
|
+
raise ValueError(
|
|
142
|
+
'Tangent modulus = 0 during initial strain computation.'
|
|
143
|
+
)
|
|
144
|
+
d_strain = d_stress / tangent
|
|
145
|
+
strain += d_strain
|
|
146
|
+
stress = self._constitutive_law.get_stress(strain)
|
|
147
|
+
d_stress = target_stress - stress
|
|
148
|
+
num_iter += 1
|
|
149
|
+
|
|
150
|
+
if abs(d_stress) > tol:
|
|
151
|
+
raise RuntimeError('Failed to converge for given initial stress.')
|
|
152
|
+
|
|
153
|
+
self._initial_strain = strain
|
|
154
|
+
|
|
46
155
|
|
|
47
156
|
class ConstitutiveLaw(abc.ABC):
|
|
48
157
|
"""Abstract base class for constitutive laws."""
|
|
@@ -150,7 +259,9 @@ class ConstitutiveLaw(abc.ABC):
|
|
|
150
259
|
|
|
151
260
|
eps = np.concatenate((eps_neg, eps_pos))
|
|
152
261
|
sig = self.get_stress(eps)
|
|
153
|
-
from structuralcodes.materials.constitutive_laws import
|
|
262
|
+
from structuralcodes.materials.constitutive_laws import ( # noqa: PLC0415
|
|
263
|
+
UserDefined,
|
|
264
|
+
)
|
|
154
265
|
|
|
155
266
|
return UserDefined(eps, sig)
|
|
156
267
|
|
|
@@ -176,14 +287,29 @@ class ConstitutiveLaw(abc.ABC):
|
|
|
176
287
|
piecewise_law = self._discretize_law()
|
|
177
288
|
return piecewise_law.__marin_tangent__(**kwargs)
|
|
178
289
|
|
|
179
|
-
def get_secant(
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
"""
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
290
|
+
def get_secant(
|
|
291
|
+
self, eps: t.Union[float, ArrayLike]
|
|
292
|
+
) -> t.Union[float, ArrayLike]:
|
|
293
|
+
"""Method to return the secant at a given strain level."""
|
|
294
|
+
# Adjust eps if it is not scalar
|
|
295
|
+
eps = eps if np.isscalar(eps) else np.atleast_1d(eps)
|
|
296
|
+
|
|
297
|
+
# Calculate secant for scalar eps
|
|
298
|
+
if np.isscalar(eps):
|
|
299
|
+
if eps != 0:
|
|
300
|
+
sig = self.get_stress(eps)
|
|
301
|
+
return sig / eps
|
|
302
|
+
return self.get_tangent(eps)
|
|
303
|
+
|
|
304
|
+
# Calculate secant for array eps
|
|
305
|
+
secant = np.zeros_like(eps)
|
|
306
|
+
strain_is_zero = eps == 0
|
|
307
|
+
strain_is_nonzero = eps != 0
|
|
308
|
+
secant[strain_is_zero] = self.get_tangent(eps[strain_is_zero])
|
|
309
|
+
secant[strain_is_nonzero] = (
|
|
310
|
+
self.get_stress(eps[strain_is_nonzero]) / eps[strain_is_nonzero]
|
|
311
|
+
)
|
|
312
|
+
return secant
|
|
187
313
|
|
|
188
314
|
|
|
189
315
|
class Section(abc.ABC):
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"""Main entry point for geometry."""
|
|
2
2
|
|
|
3
|
+
from . import profiles
|
|
3
4
|
from ._circular import CircularGeometry
|
|
4
5
|
from ._geometry import (
|
|
5
6
|
CompoundGeometry,
|
|
@@ -14,7 +15,6 @@ from ._reinforcement import (
|
|
|
14
15
|
add_reinforcement_circle,
|
|
15
16
|
add_reinforcement_line,
|
|
16
17
|
)
|
|
17
|
-
from ._steel_sections import HE, IPE, IPN, UB, UBP, UC, UPN
|
|
18
18
|
|
|
19
19
|
__all__ = [
|
|
20
20
|
'Geometry',
|
|
@@ -22,13 +22,7 @@ __all__ = [
|
|
|
22
22
|
'SurfaceGeometry',
|
|
23
23
|
'CompoundGeometry',
|
|
24
24
|
'create_line_point_angle',
|
|
25
|
-
'
|
|
26
|
-
'HE',
|
|
27
|
-
'UB',
|
|
28
|
-
'UC',
|
|
29
|
-
'UBP',
|
|
30
|
-
'IPN',
|
|
31
|
-
'UPN',
|
|
25
|
+
'profiles',
|
|
32
26
|
'add_reinforcement',
|
|
33
27
|
'add_reinforcement_line',
|
|
34
28
|
'CircularGeometry',
|
|
@@ -4,6 +4,7 @@ from __future__ import annotations # To have clean hints of ArrayLike in docs
|
|
|
4
4
|
|
|
5
5
|
import typing as t
|
|
6
6
|
import warnings
|
|
7
|
+
from math import atan2
|
|
7
8
|
|
|
8
9
|
import numpy as np
|
|
9
10
|
from numpy.typing import ArrayLike
|
|
@@ -23,6 +24,39 @@ from structuralcodes.materials.basic import ElasticMaterial
|
|
|
23
24
|
from structuralcodes.materials.concrete import Concrete
|
|
24
25
|
|
|
25
26
|
|
|
27
|
+
def _mirror_about_axis_matrix(axis: LineString) -> np.ndarray:
|
|
28
|
+
if not isinstance(axis, LineString):
|
|
29
|
+
raise TypeError('axis should be a shapely LineString object')
|
|
30
|
+
|
|
31
|
+
(x1, y1), (x2, y2) = axis.coords
|
|
32
|
+
|
|
33
|
+
# angle of the line with respect to the horizontal axis
|
|
34
|
+
dx, dy = x2 - x1, y2 - y1
|
|
35
|
+
theta = atan2(dy, dx)
|
|
36
|
+
|
|
37
|
+
# Translation matrix T (move line start to origin)
|
|
38
|
+
T = np.array([[1, 0, -x1], [0, 1, -y1], [0, 0, 1]])
|
|
39
|
+
|
|
40
|
+
# Rotation matrix R (align line with x-axis)
|
|
41
|
+
R = np.array(
|
|
42
|
+
[
|
|
43
|
+
[np.cos(theta), np.sin(theta), 0],
|
|
44
|
+
[-np.sin(theta), np.cos(theta), 0],
|
|
45
|
+
[0, 0, 1],
|
|
46
|
+
]
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
# Mirror across x-axis
|
|
50
|
+
M = np.array([[1, 0, 0], [0, -1, 0], [0, 0, 1]])
|
|
51
|
+
|
|
52
|
+
# Inverses of T and R
|
|
53
|
+
T_inv = np.linalg.inv(T)
|
|
54
|
+
R_inv = np.linalg.inv(R)
|
|
55
|
+
|
|
56
|
+
# Final transformation matrix
|
|
57
|
+
return T_inv @ R_inv @ M @ R @ T
|
|
58
|
+
|
|
59
|
+
|
|
26
60
|
class Geometry:
|
|
27
61
|
"""Base class for a geometry object."""
|
|
28
62
|
|
|
@@ -79,6 +113,17 @@ class Geometry:
|
|
|
79
113
|
'This method should be implemented by subclasses'
|
|
80
114
|
)
|
|
81
115
|
|
|
116
|
+
def __add__(self, other: Geometry) -> CompoundGeometry:
|
|
117
|
+
"""Add operator "+" for geometries.
|
|
118
|
+
|
|
119
|
+
Arguments:
|
|
120
|
+
other (Geometry): The other geometry to add.
|
|
121
|
+
|
|
122
|
+
Returns:
|
|
123
|
+
CompoundGeometry: A new CompoundGeometry.
|
|
124
|
+
"""
|
|
125
|
+
return CompoundGeometry([self, other])
|
|
126
|
+
|
|
82
127
|
|
|
83
128
|
class PointGeometry(Geometry):
|
|
84
129
|
"""Class for a point geometry with material.
|
|
@@ -218,6 +263,31 @@ class PointGeometry(Geometry):
|
|
|
218
263
|
group_label=self._group_label,
|
|
219
264
|
)
|
|
220
265
|
|
|
266
|
+
def mirror(self, axis: LineString) -> PointGeometry:
|
|
267
|
+
"""Returns a new PointGeometry that is mirrored with respect to the
|
|
268
|
+
axis.
|
|
269
|
+
|
|
270
|
+
Arguments:
|
|
271
|
+
axis (LineString): The axis to mirror about.
|
|
272
|
+
|
|
273
|
+
Returns:
|
|
274
|
+
PointGeometry: The mirrored PointGeometry.
|
|
275
|
+
"""
|
|
276
|
+
if not isinstance(axis, LineString):
|
|
277
|
+
raise TypeError('axis should be a shapely LineString object')
|
|
278
|
+
|
|
279
|
+
# Build the transformation matrix
|
|
280
|
+
A = _mirror_about_axis_matrix(axis)
|
|
281
|
+
# Apply the transformation to the point
|
|
282
|
+
params = [A[0, 0], A[0, 1], A[1, 0], A[1, 1], A[0, 2], A[1, 2]]
|
|
283
|
+
return PointGeometry(
|
|
284
|
+
point=affinity.affine_transform(self._point, params),
|
|
285
|
+
diameter=self._diameter,
|
|
286
|
+
material=self._material,
|
|
287
|
+
name=self._name,
|
|
288
|
+
group_label=self._group_label,
|
|
289
|
+
)
|
|
290
|
+
|
|
221
291
|
@staticmethod
|
|
222
292
|
def from_geometry(
|
|
223
293
|
geo: PointGeometry,
|
|
@@ -470,17 +540,6 @@ class SurfaceGeometry(Geometry):
|
|
|
470
540
|
# get the intersection
|
|
471
541
|
return self.polygon.intersection(lines_polygon)
|
|
472
542
|
|
|
473
|
-
def __add__(self, other: Geometry) -> CompoundGeometry:
|
|
474
|
-
"""Add operator "+" for geometries.
|
|
475
|
-
|
|
476
|
-
Arguments:
|
|
477
|
-
other (Geometry): The other geometry to add.
|
|
478
|
-
|
|
479
|
-
Returns:
|
|
480
|
-
CompoundGeometry: A new CompoundGeometry.
|
|
481
|
-
"""
|
|
482
|
-
return CompoundGeometry([self, other])
|
|
483
|
-
|
|
484
543
|
def __sub__(self, other: Geometry) -> SurfaceGeometry:
|
|
485
544
|
"""Add operator "-" for geometries.
|
|
486
545
|
|
|
@@ -552,6 +611,28 @@ class SurfaceGeometry(Geometry):
|
|
|
552
611
|
concrete=self.concrete,
|
|
553
612
|
)
|
|
554
613
|
|
|
614
|
+
def mirror(self, axis: LineString) -> SurfaceGeometry:
|
|
615
|
+
"""Returns a new SurfaceGeometry that is mirrored about the given axis.
|
|
616
|
+
|
|
617
|
+
Arguments:
|
|
618
|
+
axis (LineString): The axis to mirror about.
|
|
619
|
+
|
|
620
|
+
Returns:
|
|
621
|
+
SurfaceGeometry: The mirrored SurfaceGeometry.
|
|
622
|
+
"""
|
|
623
|
+
if not isinstance(axis, LineString):
|
|
624
|
+
raise TypeError('axis should be a shapely LineString object')
|
|
625
|
+
# Build the transformation matrix
|
|
626
|
+
A = _mirror_about_axis_matrix(axis)
|
|
627
|
+
# Apply transformation matrix A
|
|
628
|
+
# Apply the transformation to the polygon
|
|
629
|
+
params = [A[0, 0], A[0, 1], A[1, 0], A[1, 1], A[0, 2], A[1, 2]]
|
|
630
|
+
return SurfaceGeometry(
|
|
631
|
+
poly=affinity.affine_transform(self.polygon, params),
|
|
632
|
+
material=self.material,
|
|
633
|
+
concrete=self.concrete,
|
|
634
|
+
)
|
|
635
|
+
|
|
555
636
|
@staticmethod
|
|
556
637
|
def from_geometry(
|
|
557
638
|
geo: SurfaceGeometry,
|
|
@@ -595,9 +676,6 @@ class SurfaceGeometry(Geometry):
|
|
|
595
676
|
# from_surface_geometry
|
|
596
677
|
# from_dxf
|
|
597
678
|
# from_ascii
|
|
598
|
-
# ...
|
|
599
|
-
# we could also add methods wrapping shapely function, like:
|
|
600
|
-
# mirror, translation, rotation, etc.
|
|
601
679
|
|
|
602
680
|
|
|
603
681
|
def _process_geometries_multipolygon(
|
|
@@ -789,16 +867,22 @@ class CompoundGeometry(Geometry):
|
|
|
789
867
|
processed_geoms.append(pg.rotate(angle, point, use_radians))
|
|
790
868
|
return CompoundGeometry(geometries=processed_geoms)
|
|
791
869
|
|
|
792
|
-
def
|
|
793
|
-
"""
|
|
870
|
+
def mirror(self, axis: LineString) -> CompoundGeometry:
|
|
871
|
+
"""Returns a new CompoundGeometry that is mirrored about the given
|
|
872
|
+
axis.
|
|
794
873
|
|
|
795
874
|
Arguments:
|
|
796
|
-
|
|
875
|
+
axis (LineString): The axis to mirror about.
|
|
797
876
|
|
|
798
877
|
Returns:
|
|
799
|
-
CompoundGeometry:
|
|
878
|
+
CompoundGeometry: The mirrored CompoundGeometry.
|
|
800
879
|
"""
|
|
801
|
-
|
|
880
|
+
processed_geoms = []
|
|
881
|
+
for g in self.geometries:
|
|
882
|
+
processed_geoms.append(g.mirror(axis))
|
|
883
|
+
for pg in self.point_geometries:
|
|
884
|
+
processed_geoms.append(pg.mirror(axis))
|
|
885
|
+
return CompoundGeometry(geometries=processed_geoms)
|
|
802
886
|
|
|
803
887
|
def __sub__(self, other: Geometry) -> CompoundGeometry:
|
|
804
888
|
"""Add operator "-" for geometries.
|
|
@@ -78,8 +78,6 @@ def add_reinforcement_line(
|
|
|
78
78
|
CompoundGeometry: A compound geometry with the original geometry and
|
|
79
79
|
the reinforcement.
|
|
80
80
|
"""
|
|
81
|
-
from math import floor
|
|
82
|
-
|
|
83
81
|
p1 = np.array(coords_i)
|
|
84
82
|
p2 = np.array(coords_j)
|
|
85
83
|
distance = np.linalg.norm(p2 - p1)
|
|
@@ -100,7 +98,7 @@ def add_reinforcement_line(
|
|
|
100
98
|
elif s > 0:
|
|
101
99
|
# Provided the spacing
|
|
102
100
|
# 1. Compute the number of bars
|
|
103
|
-
n = floor(distance / s) + 1
|
|
101
|
+
n = math.floor(distance / s) + 1
|
|
104
102
|
# 2. Distribute the bars centered in the segment
|
|
105
103
|
d = (n - 1) * s
|
|
106
104
|
p1 = p1 + v * (distance - d) / 2.0
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""Main entry point for profiles."""
|
|
2
|
+
|
|
3
|
+
from ._hd import HD
|
|
4
|
+
from ._he import HE
|
|
5
|
+
from ._hp import HP
|
|
6
|
+
from ._ipe import IPE
|
|
7
|
+
from ._ipn import IPN
|
|
8
|
+
from ._l import L
|
|
9
|
+
from ._li import LI
|
|
10
|
+
from ._u import U
|
|
11
|
+
from ._ub import UB
|
|
12
|
+
from ._ubp import UBP
|
|
13
|
+
from ._uc import UC
|
|
14
|
+
from ._upe import UPE
|
|
15
|
+
from ._upn import UPN
|
|
16
|
+
from ._w import W
|
|
17
|
+
|
|
18
|
+
__all__ = [
|
|
19
|
+
'HE',
|
|
20
|
+
'HP',
|
|
21
|
+
'HD',
|
|
22
|
+
'IPE',
|
|
23
|
+
'IPN',
|
|
24
|
+
'UB',
|
|
25
|
+
'UBP',
|
|
26
|
+
'UC',
|
|
27
|
+
'UPN',
|
|
28
|
+
'UPE',
|
|
29
|
+
'U',
|
|
30
|
+
'W',
|
|
31
|
+
'L',
|
|
32
|
+
'LI',
|
|
33
|
+
]
|