structuralcodes 0.2.0__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/_concrete_creep_and_shrinkage.py +1 -1
- structuralcodes/codes/mc2010/_concrete_creep_and_shrinkage.py +105 -73
- structuralcodes/core/_section_results.py +5 -19
- structuralcodes/core/base.py +26 -11
- structuralcodes/geometry/__init__.py +10 -1
- structuralcodes/geometry/_circular.py +81 -0
- structuralcodes/geometry/_geometry.py +1 -1
- structuralcodes/geometry/_rectangular.py +83 -0
- structuralcodes/geometry/_reinforcement.py +132 -5
- structuralcodes/materials/constitutive_laws/_bilinearcompression.py +46 -1
- structuralcodes/materials/constitutive_laws/_elastic.py +19 -0
- structuralcodes/materials/constitutive_laws/_elasticplastic.py +55 -0
- structuralcodes/materials/constitutive_laws/_parabolarectangle.py +57 -0
- structuralcodes/materials/constitutive_laws/_userdefined.py +44 -0
- structuralcodes/sections/__init__.py +2 -0
- structuralcodes/sections/_generic.py +161 -24
- structuralcodes/sections/_rc_utils.py +114 -0
- structuralcodes/sections/section_integrators/_fiber_integrator.py +204 -108
- structuralcodes/sections/section_integrators/_marin_integrator.py +273 -102
- structuralcodes/sections/section_integrators/_section_integrator.py +28 -4
- {structuralcodes-0.2.0.dist-info → structuralcodes-0.3.0.dist-info}/METADATA +2 -2
- {structuralcodes-0.2.0.dist-info → structuralcodes-0.3.0.dist-info}/RECORD +24 -21
- {structuralcodes-0.2.0.dist-info → structuralcodes-0.3.0.dist-info}/WHEEL +1 -1
|
@@ -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,102 +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(tri, f'pq{30:.1f}Aa{max_area}o1')
|
|
107
|
-
mat = g.material
|
|
108
|
-
# Get x and y coordinates (centroid) and area for each fiber
|
|
109
|
-
x = []
|
|
110
|
-
y = []
|
|
111
|
-
area = []
|
|
112
|
-
for tr in mesh['triangles']:
|
|
113
|
-
# get centroid of triangle
|
|
114
|
-
xc = (
|
|
115
|
-
mesh['vertices'][tr[0]][0]
|
|
116
|
-
+ mesh['vertices'][tr[1]][0]
|
|
117
|
-
+ mesh['vertices'][tr[2]][0]
|
|
118
|
-
)
|
|
119
|
-
xc /= 3.0
|
|
120
|
-
x.append(xc)
|
|
121
|
-
yc = (
|
|
122
|
-
mesh['vertices'][tr[0]][1]
|
|
123
|
-
+ mesh['vertices'][tr[1]][1]
|
|
124
|
-
+ mesh['vertices'][tr[2]][1]
|
|
125
|
-
)
|
|
126
|
-
yc /= 3.0
|
|
127
|
-
y.append(yc)
|
|
128
|
-
# compute area
|
|
129
|
-
a = (
|
|
130
|
-
mesh['vertices'][tr[0]][0] * mesh['vertices'][tr[1]][1]
|
|
131
|
-
- mesh['vertices'][tr[0]][1]
|
|
132
|
-
* mesh['vertices'][tr[1]][0]
|
|
133
|
-
)
|
|
134
|
-
a += (
|
|
135
|
-
mesh['vertices'][tr[1]][0] * mesh['vertices'][tr[2]][1]
|
|
136
|
-
- mesh['vertices'][tr[1]][1]
|
|
137
|
-
* mesh['vertices'][tr[2]][0]
|
|
138
|
-
)
|
|
139
|
-
a += (
|
|
140
|
-
mesh['vertices'][tr[2]][0] * mesh['vertices'][tr[0]][1]
|
|
141
|
-
- mesh['vertices'][tr[2]][1]
|
|
142
|
-
* mesh['vertices'][tr[0]][0]
|
|
143
|
-
)
|
|
144
|
-
a = abs(a) * 0.5
|
|
145
|
-
area.append(a)
|
|
146
|
-
# pointer to the material
|
|
147
|
-
|
|
148
|
-
# return back the triangulation data
|
|
149
|
-
triangulated_data.append(
|
|
150
|
-
(np.array(x), np.array(y), np.array(area), mat)
|
|
151
|
-
)
|
|
152
|
-
# For the reinforcement
|
|
153
|
-
# Tentative proposal for managing reinforcement (PointGeometry)
|
|
154
|
-
reinf_data = {}
|
|
155
|
-
# Preprocess geometries having the same material
|
|
156
|
-
for pg in geo.point_geometries:
|
|
157
|
-
x, y = pg._point.coords.xy
|
|
158
|
-
x = x[0]
|
|
159
|
-
y = y[0]
|
|
160
|
-
area = pg.area
|
|
161
|
-
mat = pg.material
|
|
162
|
-
if reinf_data.get(mat) is None:
|
|
163
|
-
reinf_data[mat] = [
|
|
164
|
-
np.array(x),
|
|
165
|
-
np.array(y),
|
|
166
|
-
np.array(area),
|
|
167
|
-
]
|
|
168
|
-
else:
|
|
169
|
-
reinf_data[mat][0] = np.hstack((reinf_data[mat][0], x))
|
|
170
|
-
reinf_data[mat][1] = np.hstack((reinf_data[mat][1], y))
|
|
171
|
-
reinf_data[mat][2] = np.hstack((reinf_data[mat][2], area))
|
|
172
|
-
for mat, value in reinf_data.items():
|
|
173
|
-
triangulated_data.append((value[0], value[1], value[2], mat))
|
|
174
|
-
|
|
175
|
-
x = []
|
|
209
|
+
triangulated_data = self.triangulate(geo, mesh_size)
|
|
210
|
+
|
|
176
211
|
y = []
|
|
177
|
-
|
|
212
|
+
z = []
|
|
213
|
+
IA = [] # integrand (stress or tangent) * area
|
|
178
214
|
for tr in triangulated_data:
|
|
179
215
|
# All have the same material
|
|
180
216
|
strains = strain[0] - strain[2] * tr[0] + strain[1] * tr[1]
|
|
181
217
|
# compute stresses in all materials
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
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))]
|
|
187
228
|
|
|
188
229
|
return prepared_input, triangulated_data
|
|
189
230
|
|
|
190
|
-
def
|
|
231
|
+
def integrate_stress(
|
|
191
232
|
self,
|
|
192
233
|
prepared_input: t.List[
|
|
193
234
|
t.Tuple[int, np.ndarray, np.ndarray, np.ndarray]
|
|
@@ -202,35 +243,90 @@ class FiberIntegrator(SectionIntegrator):
|
|
|
202
243
|
Tuple(float, float, float): The stress resultants N, Mx and My.
|
|
203
244
|
"""
|
|
204
245
|
# Integration over all fibers
|
|
205
|
-
|
|
246
|
+
y, z, F = prepared_input[0]
|
|
206
247
|
|
|
207
248
|
N = np.sum(F)
|
|
208
|
-
|
|
209
|
-
|
|
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]
|
|
210
271
|
|
|
211
|
-
|
|
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
|
|
212
281
|
|
|
213
282
|
def integrate_strain_response_on_geometry(
|
|
214
|
-
self,
|
|
215
|
-
|
|
216
|
-
|
|
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.
|
|
217
291
|
|
|
218
292
|
Arguments:
|
|
219
293
|
geo (CompoundGeometry): The geometry of the section.
|
|
220
294
|
strain (ArrayLike): The strains and curvatures of the section,
|
|
221
295
|
given in the format (ea, ky, kz) which are i) strain at 0,0,
|
|
222
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').
|
|
223
302
|
mesh_size: Percentage of area (number from 0 to 1) max for triangle
|
|
224
303
|
elements.
|
|
225
304
|
|
|
226
305
|
Returns:
|
|
227
|
-
Tuple(Tuple(float, float, float),
|
|
228
|
-
|
|
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.
|
|
229
321
|
"""
|
|
230
322
|
# Prepare the general input based on the geometry and the input strains
|
|
231
323
|
prepared_input, triangulated_data = self.prepare_input(
|
|
232
|
-
geo, strain, **kwargs
|
|
324
|
+
geo, strain, integrate, **kwargs
|
|
233
325
|
)
|
|
234
326
|
|
|
235
327
|
# Return the calculated response
|
|
236
|
-
|
|
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}')
|