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
|
@@ -6,9 +6,10 @@ import typing as t
|
|
|
6
6
|
|
|
7
7
|
import numpy as np
|
|
8
8
|
import triangle
|
|
9
|
-
from numpy.typing import ArrayLike
|
|
9
|
+
from numpy.typing import ArrayLike, NDArray
|
|
10
10
|
from shapely import Polygon
|
|
11
11
|
|
|
12
|
+
from structuralcodes.core.base import ConstitutiveLaw
|
|
12
13
|
from structuralcodes.geometry import CompoundGeometry, SurfaceGeometry
|
|
13
14
|
|
|
14
15
|
from ._section_integrator import SectionIntegrator
|
|
@@ -18,7 +19,7 @@ class FiberIntegrator(SectionIntegrator):
|
|
|
18
19
|
"""Section integrator based on the Marin algorithm."""
|
|
19
20
|
|
|
20
21
|
def prepare_triangulation(self, geo: SurfaceGeometry) -> t.Dict:
|
|
21
|
-
"""
|
|
22
|
+
"""Prepare data for triangulating it with triangle.
|
|
22
23
|
|
|
23
24
|
Arguments:
|
|
24
25
|
geo (SurfaceGeometry): The geometry to triangulate.
|
|
@@ -60,26 +61,137 @@ class FiberIntegrator(SectionIntegrator):
|
|
|
60
61
|
tri['holes'] = holes
|
|
61
62
|
return tri
|
|
62
63
|
|
|
64
|
+
def triangulate(
|
|
65
|
+
self, geo: CompoundGeometry, mesh_size: float
|
|
66
|
+
) -> t.List[t.Tuple[np.ndarray, np.ndarray, np.ndarray, ConstitutiveLaw]]:
|
|
67
|
+
"""Triangulate the geometry discretizing it into fibers.
|
|
68
|
+
|
|
69
|
+
Arguments:
|
|
70
|
+
geo (CompoundGeometry): The geometry of the section.
|
|
71
|
+
mesh_size (float): Percentage of area (number from 0 to 1) max for
|
|
72
|
+
triangle elements.
|
|
73
|
+
|
|
74
|
+
Raises:
|
|
75
|
+
ValueError if mesh_size is <= 0.0 or if mesh_size is > 1.0.
|
|
76
|
+
"""
|
|
77
|
+
# Check mesh_size being between 0 and 1.
|
|
78
|
+
if mesh_size <= 0 or mesh_size > 1:
|
|
79
|
+
raise ValueError('mesh_size is a number from 0 to 1')
|
|
80
|
+
|
|
81
|
+
triangulated_data = []
|
|
82
|
+
|
|
83
|
+
# For the surface geometries
|
|
84
|
+
for g in geo.geometries:
|
|
85
|
+
# prepare data structure for triangle module
|
|
86
|
+
tri = self.prepare_triangulation(g)
|
|
87
|
+
# define the maximum area of the triangles
|
|
88
|
+
max_area = g.area * mesh_size
|
|
89
|
+
# triangulate the geometry getting back the mesh
|
|
90
|
+
mesh = triangle.triangulate(tri, f'pq{30:.1f}Aa{max_area}o1')
|
|
91
|
+
mat = g.material
|
|
92
|
+
# Get x and y coordinates (centroid) and area for each fiber
|
|
93
|
+
x = []
|
|
94
|
+
y = []
|
|
95
|
+
area = []
|
|
96
|
+
for tr in mesh['triangles']:
|
|
97
|
+
# get centroid of triangle
|
|
98
|
+
xc = (
|
|
99
|
+
mesh['vertices'][tr[0]][0]
|
|
100
|
+
+ mesh['vertices'][tr[1]][0]
|
|
101
|
+
+ mesh['vertices'][tr[2]][0]
|
|
102
|
+
)
|
|
103
|
+
xc /= 3.0
|
|
104
|
+
x.append(xc)
|
|
105
|
+
yc = (
|
|
106
|
+
mesh['vertices'][tr[0]][1]
|
|
107
|
+
+ mesh['vertices'][tr[1]][1]
|
|
108
|
+
+ mesh['vertices'][tr[2]][1]
|
|
109
|
+
)
|
|
110
|
+
yc /= 3.0
|
|
111
|
+
y.append(yc)
|
|
112
|
+
# compute area
|
|
113
|
+
a = (
|
|
114
|
+
mesh['vertices'][tr[0]][0] * mesh['vertices'][tr[1]][1]
|
|
115
|
+
- mesh['vertices'][tr[0]][1] * mesh['vertices'][tr[1]][0]
|
|
116
|
+
)
|
|
117
|
+
a += (
|
|
118
|
+
mesh['vertices'][tr[1]][0] * mesh['vertices'][tr[2]][1]
|
|
119
|
+
- mesh['vertices'][tr[1]][1] * mesh['vertices'][tr[2]][0]
|
|
120
|
+
)
|
|
121
|
+
a += (
|
|
122
|
+
mesh['vertices'][tr[2]][0] * mesh['vertices'][tr[0]][1]
|
|
123
|
+
- mesh['vertices'][tr[2]][1] * mesh['vertices'][tr[0]][0]
|
|
124
|
+
)
|
|
125
|
+
a = abs(a) * 0.5
|
|
126
|
+
area.append(a)
|
|
127
|
+
# pointer to the material
|
|
128
|
+
|
|
129
|
+
# return back the triangulation data
|
|
130
|
+
triangulated_data.append(
|
|
131
|
+
(np.array(x), np.array(y), np.array(area), mat)
|
|
132
|
+
)
|
|
133
|
+
# For the reinforcement
|
|
134
|
+
# Tentative proposal for managing reinforcement (PointGeometry)
|
|
135
|
+
reinf_data = {}
|
|
136
|
+
# Preprocess geometries having the same material
|
|
137
|
+
for pg in geo.point_geometries:
|
|
138
|
+
x, y = pg._point.coords.xy
|
|
139
|
+
x = x[0]
|
|
140
|
+
y = y[0]
|
|
141
|
+
area = pg.area
|
|
142
|
+
mat = pg.material
|
|
143
|
+
if reinf_data.get(mat) is None:
|
|
144
|
+
reinf_data[mat] = [
|
|
145
|
+
np.array(x),
|
|
146
|
+
np.array(y),
|
|
147
|
+
np.array(area),
|
|
148
|
+
]
|
|
149
|
+
else:
|
|
150
|
+
reinf_data[mat][0] = np.hstack((reinf_data[mat][0], x))
|
|
151
|
+
reinf_data[mat][1] = np.hstack((reinf_data[mat][1], y))
|
|
152
|
+
reinf_data[mat][2] = np.hstack((reinf_data[mat][2], area))
|
|
153
|
+
for mat, value in reinf_data.items():
|
|
154
|
+
triangulated_data.append((value[0], value[1], value[2], mat))
|
|
155
|
+
|
|
156
|
+
return triangulated_data
|
|
157
|
+
|
|
63
158
|
def prepare_input(
|
|
64
|
-
self,
|
|
159
|
+
self,
|
|
160
|
+
geo: CompoundGeometry,
|
|
161
|
+
strain: ArrayLike,
|
|
162
|
+
integrate: t.Literal['stress', 'modulus'] = 'stress',
|
|
163
|
+
**kwargs,
|
|
65
164
|
) -> t.Tuple[t.Tuple[np.ndarray, np.ndarray, np.ndarray]]:
|
|
66
|
-
"""Prepare general input to the integration
|
|
165
|
+
"""Prepare general input to the integration of stress or material
|
|
166
|
+
modulus in the section.
|
|
67
167
|
|
|
68
|
-
Calculate the
|
|
168
|
+
Calculate the stress resultants or tangent section stiffness based on
|
|
169
|
+
strains in a set of points.
|
|
69
170
|
|
|
70
|
-
|
|
171
|
+
Arguments:
|
|
71
172
|
geo (CompoundGeometry): The geometry of the section.
|
|
72
173
|
strain (ArrayLike): The strains and curvatures of the section,
|
|
73
174
|
given in the format (ea, ky, kz) which are i) strain at 0,0,
|
|
74
175
|
ii) curvature y axis, iii) curvature z axis.
|
|
75
|
-
|
|
76
|
-
|
|
176
|
+
integrate (str): a string indicating the quantity to integrate over
|
|
177
|
+
the section. It can be 'stress' or 'modulus'. When 'stress'
|
|
178
|
+
is selected, the return value will be the stress resultants N,
|
|
179
|
+
My, Mz, while if 'modulus' is selected, the return will be the
|
|
180
|
+
tangent section stiffness matrix (default is 'stress').
|
|
181
|
+
|
|
182
|
+
Keyword Arguments:
|
|
183
|
+
mesh_size (float): Percentage of area (number from 0 to 1) max for
|
|
184
|
+
triangle elements.
|
|
77
185
|
|
|
78
186
|
Returns:
|
|
79
|
-
Tuple(List
|
|
187
|
+
Tuple(List): The prepared input representing a list with
|
|
80
188
|
x-coordinates, y-coordinates and force for each fiber and a
|
|
81
|
-
|
|
189
|
+
list containing the triangulation data that can be stored and
|
|
82
190
|
used later to avoid repetition of triangulation.
|
|
191
|
+
|
|
192
|
+
Raises:
|
|
193
|
+
ValueError: If a unkown value is passed to the `integrate`
|
|
194
|
+
parameter.
|
|
83
195
|
"""
|
|
84
196
|
# This method should:
|
|
85
197
|
# - discretize the section in a number of fibers (mesh_size)
|
|
@@ -92,104 +204,31 @@ class FiberIntegrator(SectionIntegrator):
|
|
|
92
204
|
# No triangulation is provided, triangulate the section
|
|
93
205
|
# Fiber integrator for generic section uses delaunay triangulation
|
|
94
206
|
# for discretizing in fibers
|
|
95
|
-
|
|
207
|
+
|
|
96
208
|
mesh_size = kwargs.get('mesh_size', 0.01)
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
# For the surface geometries
|
|
100
|
-
for g in geo.geometries:
|
|
101
|
-
# prepare data structure for triangle module
|
|
102
|
-
tri = self.prepare_triangulation(g)
|
|
103
|
-
# define the maximum area of the triangles
|
|
104
|
-
max_area = g.area * mesh_size
|
|
105
|
-
# triangulate the geometry getting back the mesh
|
|
106
|
-
mesh = triangle.triangulate(
|
|
107
|
-
tri, f'pq{30:.1f}Aa{max_area:.1f}o1'
|
|
108
|
-
)
|
|
109
|
-
mat = g.material
|
|
110
|
-
# Get x and y coordinates (centroid) and area for each fiber
|
|
111
|
-
x = []
|
|
112
|
-
y = []
|
|
113
|
-
area = []
|
|
114
|
-
for tr in mesh['triangles']:
|
|
115
|
-
# get centroid of triangle
|
|
116
|
-
xc = (
|
|
117
|
-
mesh['vertices'][tr[0]][0]
|
|
118
|
-
+ mesh['vertices'][tr[1]][0]
|
|
119
|
-
+ mesh['vertices'][tr[2]][0]
|
|
120
|
-
)
|
|
121
|
-
xc /= 3.0
|
|
122
|
-
x.append(xc)
|
|
123
|
-
yc = (
|
|
124
|
-
mesh['vertices'][tr[0]][1]
|
|
125
|
-
+ mesh['vertices'][tr[1]][1]
|
|
126
|
-
+ mesh['vertices'][tr[2]][1]
|
|
127
|
-
)
|
|
128
|
-
yc /= 3.0
|
|
129
|
-
y.append(yc)
|
|
130
|
-
# compute area
|
|
131
|
-
a = (
|
|
132
|
-
mesh['vertices'][tr[0]][0] * mesh['vertices'][tr[1]][1]
|
|
133
|
-
- mesh['vertices'][tr[0]][1]
|
|
134
|
-
* mesh['vertices'][tr[1]][0]
|
|
135
|
-
)
|
|
136
|
-
a += (
|
|
137
|
-
mesh['vertices'][tr[1]][0] * mesh['vertices'][tr[2]][1]
|
|
138
|
-
- mesh['vertices'][tr[1]][1]
|
|
139
|
-
* mesh['vertices'][tr[2]][0]
|
|
140
|
-
)
|
|
141
|
-
a += (
|
|
142
|
-
mesh['vertices'][tr[2]][0] * mesh['vertices'][tr[0]][1]
|
|
143
|
-
- mesh['vertices'][tr[2]][1]
|
|
144
|
-
* mesh['vertices'][tr[0]][0]
|
|
145
|
-
)
|
|
146
|
-
a = abs(a) * 0.5
|
|
147
|
-
area.append(a)
|
|
148
|
-
# pointer to the material
|
|
149
|
-
|
|
150
|
-
# return back the triangulation data
|
|
151
|
-
triangulated_data.append(
|
|
152
|
-
(np.array(x), np.array(y), np.array(area), mat)
|
|
153
|
-
)
|
|
154
|
-
# For the reinforcement
|
|
155
|
-
# Tentative proposal for managing reinforcement (PointGeometry)
|
|
156
|
-
reinf_data = {}
|
|
157
|
-
# Preprocess geometries having the same material
|
|
158
|
-
for pg in geo.point_geometries:
|
|
159
|
-
x, y = pg._point.coords.xy
|
|
160
|
-
x = x[0]
|
|
161
|
-
y = y[0]
|
|
162
|
-
area = pg.area
|
|
163
|
-
mat = pg.material
|
|
164
|
-
if reinf_data.get(mat) is None:
|
|
165
|
-
reinf_data[mat] = [
|
|
166
|
-
np.array(x),
|
|
167
|
-
np.array(y),
|
|
168
|
-
np.array(area),
|
|
169
|
-
]
|
|
170
|
-
else:
|
|
171
|
-
reinf_data[mat][0] = np.hstack((reinf_data[mat][0], x))
|
|
172
|
-
reinf_data[mat][1] = np.hstack((reinf_data[mat][1], y))
|
|
173
|
-
reinf_data[mat][2] = np.hstack((reinf_data[mat][2], area))
|
|
174
|
-
for mat, value in reinf_data.items():
|
|
175
|
-
triangulated_data.append((value[0], value[1], value[2], mat))
|
|
176
|
-
|
|
177
|
-
x = []
|
|
209
|
+
triangulated_data = self.triangulate(geo, mesh_size)
|
|
210
|
+
|
|
178
211
|
y = []
|
|
179
|
-
|
|
212
|
+
z = []
|
|
213
|
+
IA = [] # integrand (stress or tangent) * area
|
|
180
214
|
for tr in triangulated_data:
|
|
181
215
|
# All have the same material
|
|
182
216
|
strains = strain[0] - strain[2] * tr[0] + strain[1] * tr[1]
|
|
183
217
|
# compute stresses in all materials
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
218
|
+
if integrate == 'stress':
|
|
219
|
+
integrand = tr[3].get_stress(strains)
|
|
220
|
+
elif integrate == 'modulus':
|
|
221
|
+
integrand = tr[3].get_tangent(strains)
|
|
222
|
+
else:
|
|
223
|
+
raise ValueError(f'Unknown integrate type: {integrate}')
|
|
224
|
+
y.append(tr[0])
|
|
225
|
+
z.append(tr[1])
|
|
226
|
+
IA.append(integrand * tr[2])
|
|
227
|
+
prepared_input = [(np.hstack(y), np.hstack(z), np.hstack(IA))]
|
|
189
228
|
|
|
190
229
|
return prepared_input, triangulated_data
|
|
191
230
|
|
|
192
|
-
def
|
|
231
|
+
def integrate_stress(
|
|
193
232
|
self,
|
|
194
233
|
prepared_input: t.List[
|
|
195
234
|
t.Tuple[int, np.ndarray, np.ndarray, np.ndarray]
|
|
@@ -204,35 +243,90 @@ class FiberIntegrator(SectionIntegrator):
|
|
|
204
243
|
Tuple(float, float, float): The stress resultants N, Mx and My.
|
|
205
244
|
"""
|
|
206
245
|
# Integration over all fibers
|
|
207
|
-
|
|
246
|
+
y, z, F = prepared_input[0]
|
|
208
247
|
|
|
209
248
|
N = np.sum(F)
|
|
210
|
-
|
|
211
|
-
|
|
249
|
+
My = np.sum(F * z)
|
|
250
|
+
Mz = np.sum(-F * y)
|
|
251
|
+
|
|
252
|
+
return N, My, Mz
|
|
253
|
+
|
|
254
|
+
def integrate_modulus(
|
|
255
|
+
self,
|
|
256
|
+
prepared_input: t.List[
|
|
257
|
+
t.Tuple[int, np.ndarray, np.ndarray, np.ndarray]
|
|
258
|
+
],
|
|
259
|
+
) -> NDArray:
|
|
260
|
+
"""Integrate material modulus over the geometry to obtain section
|
|
261
|
+
stiffness.
|
|
262
|
+
|
|
263
|
+
Arguments:
|
|
264
|
+
prepared_input (List): The prepared input from .prepare_input().
|
|
265
|
+
|
|
266
|
+
Returns:
|
|
267
|
+
NDArray[float]: The stiffness matrix with shape (3,3).
|
|
268
|
+
"""
|
|
269
|
+
# Integration over all fibers
|
|
270
|
+
y, z, MA = prepared_input[0]
|
|
212
271
|
|
|
213
|
-
|
|
272
|
+
stiffness = np.zeros((3, 3))
|
|
273
|
+
stiffness[0, 0] = np.sum(MA)
|
|
274
|
+
stiffness[0, 1] = stiffness[1, 0] = np.sum(MA * z)
|
|
275
|
+
stiffness[0, 2] = stiffness[2, 0] = np.sum(-MA * y)
|
|
276
|
+
stiffness[1, 1] = np.sum(z * z * MA)
|
|
277
|
+
stiffness[1, 2] = stiffness[2, 1] = np.sum(-y * z * MA)
|
|
278
|
+
stiffness[2, 2] = np.sum(y * y * MA)
|
|
279
|
+
|
|
280
|
+
return stiffness
|
|
214
281
|
|
|
215
282
|
def integrate_strain_response_on_geometry(
|
|
216
|
-
self,
|
|
217
|
-
|
|
218
|
-
|
|
283
|
+
self,
|
|
284
|
+
geo: CompoundGeometry,
|
|
285
|
+
strain: ArrayLike,
|
|
286
|
+
integrate: t.Literal['stress', 'modulus'] = 'stress',
|
|
287
|
+
**kwargs,
|
|
288
|
+
) -> t.Tuple[t.Union[t.Tuple[float, float, float], np.ndarray], t.List]:
|
|
289
|
+
"""Integrate stress or material modulus in the section with the fiber
|
|
290
|
+
algorithm.
|
|
219
291
|
|
|
220
292
|
Arguments:
|
|
221
293
|
geo (CompoundGeometry): The geometry of the section.
|
|
222
294
|
strain (ArrayLike): The strains and curvatures of the section,
|
|
223
295
|
given in the format (ea, ky, kz) which are i) strain at 0,0,
|
|
224
296
|
ii) curvature y axis, iii) curvature z axis.
|
|
297
|
+
integrate (str): a string indicating the quantity to integrate over
|
|
298
|
+
the section. It can be 'stress' or 'modulus'. When 'stress'
|
|
299
|
+
is selected, the return value will be the stress resultants N,
|
|
300
|
+
My, Mz, while if 'modulus' is selected, the return will be the
|
|
301
|
+
section stiffness matrix (default is 'stress').
|
|
225
302
|
mesh_size: Percentage of area (number from 0 to 1) max for triangle
|
|
226
303
|
elements.
|
|
227
304
|
|
|
228
305
|
Returns:
|
|
229
|
-
Tuple(Tuple(float, float, float),
|
|
230
|
-
|
|
306
|
+
Tuple(Union(Tuple(float, float, float), np.ndarray), List): The
|
|
307
|
+
first element is either a tuple of floats (for the stress
|
|
308
|
+
resultants (N, My, Mz) when `integrate='stress'`, or a numpy
|
|
309
|
+
array representing the stiffness matrix then `integrate='modulus'`.
|
|
310
|
+
The second element is a list with data from triangulation.
|
|
311
|
+
|
|
312
|
+
Example:
|
|
313
|
+
result, tri = integrate_strain_response_on_geometry(geo, strain,
|
|
314
|
+
integrate='tangent')
|
|
315
|
+
`result` will be the stiffness matrix (a 3x3 numpy array) if
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
Raises:
|
|
319
|
+
ValueError: If a unkown value is passed to the `integrate`
|
|
320
|
+
parameter.
|
|
231
321
|
"""
|
|
232
322
|
# Prepare the general input based on the geometry and the input strains
|
|
233
323
|
prepared_input, triangulated_data = self.prepare_input(
|
|
234
|
-
geo, strain, **kwargs
|
|
324
|
+
geo, strain, integrate, **kwargs
|
|
235
325
|
)
|
|
236
326
|
|
|
237
327
|
# Return the calculated response
|
|
238
|
-
|
|
328
|
+
if integrate == 'stress':
|
|
329
|
+
return *self.integrate_stress(prepared_input), triangulated_data
|
|
330
|
+
if integrate == 'modulus':
|
|
331
|
+
return self.integrate_modulus(prepared_input), triangulated_data
|
|
332
|
+
raise ValueError(f'Unknown integrate type: {integrate}')
|