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.
- structuralcodes/__init__.py +17 -0
- structuralcodes/codes/__init__.py +79 -0
- structuralcodes/codes/ec2_2004/__init__.py +133 -0
- structuralcodes/codes/ec2_2004/_concrete_material_properties.py +239 -0
- structuralcodes/codes/ec2_2004/_reinforcement_material_properties.py +104 -0
- structuralcodes/codes/ec2_2004/_section_7_3_crack_control.py +941 -0
- structuralcodes/codes/ec2_2004/annex_b_shrink_and_creep.py +257 -0
- structuralcodes/codes/ec2_2004/shear.py +506 -0
- structuralcodes/codes/ec2_2023/__init__.py +104 -0
- structuralcodes/codes/ec2_2023/_annexB_time_dependent.py +17 -0
- structuralcodes/codes/ec2_2023/_section5_materials.py +1160 -0
- structuralcodes/codes/ec2_2023/_section9_sls.py +325 -0
- structuralcodes/codes/mc2010/__init__.py +169 -0
- structuralcodes/codes/mc2010/_concrete_creep_and_shrinkage.py +704 -0
- structuralcodes/codes/mc2010/_concrete_interface_different_casting_times.py +104 -0
- structuralcodes/codes/mc2010/_concrete_material_properties.py +463 -0
- structuralcodes/codes/mc2010/_concrete_punching.py +543 -0
- structuralcodes/codes/mc2010/_concrete_shear.py +749 -0
- structuralcodes/codes/mc2010/_concrete_torsion.py +164 -0
- structuralcodes/codes/mc2010/_reinforcement_material_properties.py +105 -0
- structuralcodes/core/__init__.py +1 -0
- structuralcodes/core/_section_results.py +211 -0
- structuralcodes/core/base.py +260 -0
- structuralcodes/geometry/__init__.py +25 -0
- structuralcodes/geometry/_geometry.py +875 -0
- structuralcodes/geometry/_steel_sections.py +2155 -0
- structuralcodes/materials/__init__.py +9 -0
- structuralcodes/materials/concrete/__init__.py +82 -0
- structuralcodes/materials/concrete/_concrete.py +114 -0
- structuralcodes/materials/concrete/_concreteEC2_2004.py +477 -0
- structuralcodes/materials/concrete/_concreteEC2_2023.py +435 -0
- structuralcodes/materials/concrete/_concreteMC2010.py +494 -0
- structuralcodes/materials/constitutive_laws.py +979 -0
- structuralcodes/materials/reinforcement/__init__.py +84 -0
- structuralcodes/materials/reinforcement/_reinforcement.py +172 -0
- structuralcodes/materials/reinforcement/_reinforcementEC2_2004.py +103 -0
- structuralcodes/materials/reinforcement/_reinforcementEC2_2023.py +93 -0
- structuralcodes/materials/reinforcement/_reinforcementMC2010.py +98 -0
- structuralcodes/sections/__init__.py +23 -0
- structuralcodes/sections/_generic.py +1249 -0
- structuralcodes/sections/_reinforcement.py +115 -0
- structuralcodes/sections/section_integrators/__init__.py +14 -0
- structuralcodes/sections/section_integrators/_factory.py +41 -0
- structuralcodes/sections/section_integrators/_fiber_integrator.py +238 -0
- structuralcodes/sections/section_integrators/_marin_integration.py +47 -0
- structuralcodes/sections/section_integrators/_marin_integrator.py +222 -0
- structuralcodes/sections/section_integrators/_section_integrator.py +49 -0
- structuralcodes-0.0.1.dist-info/METADATA +40 -0
- structuralcodes-0.0.1.dist-info/RECORD +50 -0
- structuralcodes-0.0.1.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
"""Abstract base classes."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations # To have clean hints of ArrayLike in docs
|
|
4
|
+
|
|
5
|
+
import abc
|
|
6
|
+
import typing as t
|
|
7
|
+
import warnings
|
|
8
|
+
|
|
9
|
+
import numpy as np
|
|
10
|
+
from numpy.typing import ArrayLike
|
|
11
|
+
|
|
12
|
+
import structuralcodes.core._section_results as s_res
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Material(abc.ABC):
|
|
16
|
+
"""Abstract base class for materials."""
|
|
17
|
+
|
|
18
|
+
_constitutive_law = None
|
|
19
|
+
|
|
20
|
+
def __init__(self, density: float, name: t.Optional[str] = None) -> None:
|
|
21
|
+
"""Initializes an instance of a new material.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
density (float): density of the material in kg/m3
|
|
25
|
+
|
|
26
|
+
Keyword Args:
|
|
27
|
+
name (Optional[str]): descriptive name of the material
|
|
28
|
+
"""
|
|
29
|
+
self._density = abs(density)
|
|
30
|
+
self._name = name if name is not None else 'Material'
|
|
31
|
+
|
|
32
|
+
def update_attributes(self, updated_attributes: t.Dict) -> None:
|
|
33
|
+
"""Function for updating the attributes specified in the input
|
|
34
|
+
dictionary.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
updated_attributes (dict): the dictionary of parameters to be
|
|
38
|
+
updated (not found parameters are skipped with a warning)
|
|
39
|
+
"""
|
|
40
|
+
for key, value in updated_attributes.items():
|
|
41
|
+
if not hasattr(self, '_' + key):
|
|
42
|
+
str_list_keys = ', '.join(updated_attributes.keys())
|
|
43
|
+
str_warn = (
|
|
44
|
+
f"WARNING: attribute '{key}' not found."
|
|
45
|
+
" Ignoring the entry.\n"
|
|
46
|
+
f"Used keys in the call: {str_list_keys}"
|
|
47
|
+
)
|
|
48
|
+
warnings.warn(str_warn)
|
|
49
|
+
continue
|
|
50
|
+
setattr(self, '_' + key, value)
|
|
51
|
+
|
|
52
|
+
@property
|
|
53
|
+
def constitutive_law(self):
|
|
54
|
+
"""Returns the ConstitutiveLaw of the object."""
|
|
55
|
+
return self._constitutive_law
|
|
56
|
+
|
|
57
|
+
@property
|
|
58
|
+
def name(self):
|
|
59
|
+
"""Returns the name of the material."""
|
|
60
|
+
return self._name
|
|
61
|
+
|
|
62
|
+
@property
|
|
63
|
+
def density(self):
|
|
64
|
+
"""Returns the density of the material in kg/m3."""
|
|
65
|
+
return self._density
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class ConstitutiveLaw(abc.ABC):
|
|
69
|
+
"""Abstract base class for constitutive laws."""
|
|
70
|
+
|
|
71
|
+
__materials__: t.Tuple[str] = ()
|
|
72
|
+
constitutive_law_counter: t.ClassVar[int] = 0
|
|
73
|
+
|
|
74
|
+
def __init__(self, name: t.Optional[str] = None) -> None:
|
|
75
|
+
"""Initialize a ConstitutiveLaw object."""
|
|
76
|
+
self.id = self.constitutive_law_counter
|
|
77
|
+
self._name = name if name is not None else f'ConstitutiveLaw_{self.id}'
|
|
78
|
+
self._increase_global_counter()
|
|
79
|
+
|
|
80
|
+
@property
|
|
81
|
+
def name(self):
|
|
82
|
+
"""Returns the name of the constitutive law."""
|
|
83
|
+
return self._name
|
|
84
|
+
|
|
85
|
+
@classmethod
|
|
86
|
+
def _increase_global_counter(cls):
|
|
87
|
+
cls.constitutive_law_counter += 1
|
|
88
|
+
|
|
89
|
+
@abc.abstractmethod
|
|
90
|
+
def get_stress(self, eps: ArrayLike) -> ArrayLike:
|
|
91
|
+
"""Each constitutive law should provide a method to return the
|
|
92
|
+
stress given the strain level.
|
|
93
|
+
"""
|
|
94
|
+
|
|
95
|
+
@abc.abstractmethod
|
|
96
|
+
def get_tangent(self, eps: ArrayLike) -> ArrayLike:
|
|
97
|
+
"""Each constitutive law should provide a method to return the
|
|
98
|
+
tangent at a given strain level.
|
|
99
|
+
"""
|
|
100
|
+
|
|
101
|
+
@abc.abstractmethod
|
|
102
|
+
def get_ultimate_strain(self) -> t.Tuple[float, float]:
|
|
103
|
+
"""Each constitutive law should provide a method to return the
|
|
104
|
+
ultimate strain (positive and negative).
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
def preprocess_strains_with_limits(self, eps: ArrayLike) -> ArrayLike:
|
|
108
|
+
"""Preprocess strain arrays setting those strains sufficiently
|
|
109
|
+
near to ultimate strain limits to exactly ultimate strain limit.
|
|
110
|
+
"""
|
|
111
|
+
eps = np.atleast_1d(np.asarray(eps))
|
|
112
|
+
eps_max, eps_min = self.get_ultimate_strain()
|
|
113
|
+
|
|
114
|
+
idxs = np.isclose(eps, np.zeros_like(eps) + eps_max, atol=1e-6)
|
|
115
|
+
eps[idxs] = eps_max
|
|
116
|
+
idxs = np.isclose(eps, np.zeros_like(eps) + eps_min, atol=1e-6)
|
|
117
|
+
eps[idxs] = eps_min
|
|
118
|
+
|
|
119
|
+
return eps
|
|
120
|
+
|
|
121
|
+
def __marin__(self, **kwargs):
|
|
122
|
+
"""Function for getting the strain limits and coefficients
|
|
123
|
+
for marin integration.
|
|
124
|
+
|
|
125
|
+
By default the law is discretized as a piecewise linear
|
|
126
|
+
function. Then marin coefficients are computed based on this
|
|
127
|
+
discretization.
|
|
128
|
+
"""
|
|
129
|
+
|
|
130
|
+
# Discretize the constitutive law in a "smart way"
|
|
131
|
+
def find_x_lim(x, y):
|
|
132
|
+
# Check if there are non-zero values for x > 0
|
|
133
|
+
if np.any(y[0:] != 0):
|
|
134
|
+
# Find the last non-zero index for x > 0
|
|
135
|
+
non_zero_indices = np.nonzero(y[0:])[0]
|
|
136
|
+
x_lim_index = 0 + non_zero_indices[-1]
|
|
137
|
+
return x[x_lim_index]
|
|
138
|
+
# All values are zero for x > 0
|
|
139
|
+
return None
|
|
140
|
+
|
|
141
|
+
eps_max, eps_min = self.get_ultimate_strain()
|
|
142
|
+
eps_max = min(eps_max, 1)
|
|
143
|
+
# Analise positive branch
|
|
144
|
+
eps = np.linspace(0, eps_max, 10000)
|
|
145
|
+
sig = self.get_stress(eps)
|
|
146
|
+
sig[(sig < np.max(sig) * 1e-6)] = 0
|
|
147
|
+
eps_lim = find_x_lim(eps, sig)
|
|
148
|
+
# Now discretize the function in 10 steps for positive part
|
|
149
|
+
eps_pos = (
|
|
150
|
+
np.linspace(0, -eps_min, 1)
|
|
151
|
+
if eps_lim is None
|
|
152
|
+
else np.linspace(0, eps_lim, 10)
|
|
153
|
+
)
|
|
154
|
+
# Analise negative branch
|
|
155
|
+
eps = np.linspace(0, eps_min, 10000)
|
|
156
|
+
sig = -self.get_stress(eps)
|
|
157
|
+
sig[(sig < np.max(sig) * 1e-6)] = 0
|
|
158
|
+
eps_lim = find_x_lim(-eps, sig)
|
|
159
|
+
# Now discretize the function in 10 steps for negative part
|
|
160
|
+
eps_neg = (
|
|
161
|
+
np.linspace(eps_min, 0, 1, endpoint=False)
|
|
162
|
+
if eps_lim is None
|
|
163
|
+
else np.linspace(-eps_lim, 0, 10, endpoint=False)
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
eps = np.concatenate((eps_neg, eps_pos))
|
|
167
|
+
sig = self.get_stress(eps)
|
|
168
|
+
from structuralcodes.materials.constitutive_laws import UserDefined
|
|
169
|
+
|
|
170
|
+
# Return Marin coefficients for linearized version
|
|
171
|
+
return UserDefined(eps, sig).__marin__(**kwargs)
|
|
172
|
+
|
|
173
|
+
def get_secant(self, eps: float) -> float:
|
|
174
|
+
"""Method to return the
|
|
175
|
+
secant at a given strain level.
|
|
176
|
+
"""
|
|
177
|
+
if eps != 0:
|
|
178
|
+
sig = self.get_stress(eps)
|
|
179
|
+
return sig / eps
|
|
180
|
+
return self.get_tangent(eps)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
class Section(abc.ABC):
|
|
184
|
+
"""Abstract base class for a cross secion.
|
|
185
|
+
The section is defined by local axes y and z (mapped to x and y cartesian
|
|
186
|
+
plane respectively).
|
|
187
|
+
"""
|
|
188
|
+
|
|
189
|
+
section_counter: t.ClassVar[int] = 0
|
|
190
|
+
|
|
191
|
+
def __init__(self, name: t.Optional[str] = None) -> None:
|
|
192
|
+
"""Initialize a Section object."""
|
|
193
|
+
self.id = self.section_counter
|
|
194
|
+
self._name = name if name is not None else f'Section_{self.id}'
|
|
195
|
+
self._increase_global_counter()
|
|
196
|
+
|
|
197
|
+
@property
|
|
198
|
+
def name(self):
|
|
199
|
+
"""Returns the name of the section."""
|
|
200
|
+
return self._name
|
|
201
|
+
|
|
202
|
+
@classmethod
|
|
203
|
+
def _increase_global_counter(cls):
|
|
204
|
+
cls.section_counter += 1
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
class SectionCalculator(abc.ABC):
|
|
208
|
+
"""Abstract class for SectionCalculators
|
|
209
|
+
defining the interface.
|
|
210
|
+
"""
|
|
211
|
+
|
|
212
|
+
def __init__(self, section: Section) -> None:
|
|
213
|
+
"""Initialization of SectionCalculator object, providing
|
|
214
|
+
a Section object.
|
|
215
|
+
"""
|
|
216
|
+
self.section = section
|
|
217
|
+
|
|
218
|
+
@abc.abstractmethod
|
|
219
|
+
def _calculate_gross_section_properties(self) -> s_res.GrossProperties:
|
|
220
|
+
"""Calculates the gross section properties of the section
|
|
221
|
+
This function is private and called when the section is created
|
|
222
|
+
It stores the result into the result object.
|
|
223
|
+
|
|
224
|
+
Returns:
|
|
225
|
+
gross_section_properties (GrossSection)
|
|
226
|
+
"""
|
|
227
|
+
|
|
228
|
+
@abc.abstractmethod
|
|
229
|
+
def calculate_bending_strength(
|
|
230
|
+
self, theta=0, n=0
|
|
231
|
+
) -> s_res.UltimateBendingMomentResults:
|
|
232
|
+
"""Calculates the bending strength for given inclination of n.a.
|
|
233
|
+
and axial load.
|
|
234
|
+
|
|
235
|
+
Input:
|
|
236
|
+
theta (float, default = 0): inclination of n.a. respect to y axis
|
|
237
|
+
n (float, default = 0): axial load applied to the section (+: tension,
|
|
238
|
+
-: compression)
|
|
239
|
+
|
|
240
|
+
Return:
|
|
241
|
+
ultimate_bending_moment_result (UltimateBendingMomentResult)
|
|
242
|
+
"""
|
|
243
|
+
|
|
244
|
+
@abc.abstractmethod
|
|
245
|
+
def calculate_moment_curvature(
|
|
246
|
+
self, theta=0, n=0
|
|
247
|
+
) -> s_res.MomentCurvatureResults:
|
|
248
|
+
"""Calculates the moment-curvature relation for given inclination of
|
|
249
|
+
n.a. and axial load.
|
|
250
|
+
|
|
251
|
+
Input:
|
|
252
|
+
theta (float, default = 0): inclination of n.a. respect to y axis
|
|
253
|
+
n (float, default = 0): axial load applied to the section
|
|
254
|
+
(+: tension, -: compression)
|
|
255
|
+
chi_incr (float, default = 1e-8): the curvature increment for the
|
|
256
|
+
analysis
|
|
257
|
+
|
|
258
|
+
Return:
|
|
259
|
+
moment_curvature_result (MomentCurvatureResults)
|
|
260
|
+
"""
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""Main entry point for geometry."""
|
|
2
|
+
|
|
3
|
+
from ._geometry import (
|
|
4
|
+
CompoundGeometry,
|
|
5
|
+
Geometry,
|
|
6
|
+
PointGeometry,
|
|
7
|
+
SurfaceGeometry,
|
|
8
|
+
create_line_point_angle,
|
|
9
|
+
)
|
|
10
|
+
from ._steel_sections import HE, IPE, IPN, UB, UBP, UC, UPN
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
'Geometry',
|
|
14
|
+
'PointGeometry',
|
|
15
|
+
'SurfaceGeometry',
|
|
16
|
+
'CompoundGeometry',
|
|
17
|
+
'create_line_point_angle',
|
|
18
|
+
'IPE',
|
|
19
|
+
'HE',
|
|
20
|
+
'UB',
|
|
21
|
+
'UC',
|
|
22
|
+
'UBP',
|
|
23
|
+
'IPN',
|
|
24
|
+
'UPN',
|
|
25
|
+
]
|