weac 2.6.0__py3-none-any.whl → 2.6.2__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.
- weac/__init__.py +4 -8
- weac/eigensystem.py +180 -139
- weac/inverse.py +5 -8
- weac/layered.py +24 -9
- weac/mixins.py +423 -426
- weac/plot.py +244 -196
- weac/tools.py +84 -69
- {weac-2.6.0.dist-info → weac-2.6.2.dist-info}/METADATA +22 -8
- weac-2.6.2.dist-info/RECORD +12 -0
- {weac-2.6.0.dist-info → weac-2.6.2.dist-info}/WHEEL +1 -1
- weac-2.6.0.dist-info/RECORD +0 -12
- {weac-2.6.0.dist-info → weac-2.6.2.dist-info/licenses}/LICENSE +0 -0
- {weac-2.6.0.dist-info → weac-2.6.2.dist-info}/top_level.txt +0 -0
weac/mixins.py
CHANGED
|
@@ -6,11 +6,11 @@ from functools import partial
|
|
|
6
6
|
|
|
7
7
|
# Third party imports
|
|
8
8
|
import numpy as np
|
|
9
|
+
from scipy.integrate import cumulative_trapezoid, quad
|
|
9
10
|
from scipy.optimize import brentq
|
|
10
|
-
from scipy.integrate import romberg, cumulative_trapezoid
|
|
11
11
|
|
|
12
12
|
# Module imports
|
|
13
|
-
from weac.tools import
|
|
13
|
+
from weac.tools import calc_vertical_bc_center_of_gravity, tensile_strength_slab
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
class FieldQuantitiesMixin:
|
|
@@ -22,7 +22,7 @@ class FieldQuantitiesMixin:
|
|
|
22
22
|
"""
|
|
23
23
|
|
|
24
24
|
# pylint: disable=no-self-use
|
|
25
|
-
def w(self, Z, unit=
|
|
25
|
+
def w(self, Z, unit="mm"):
|
|
26
26
|
"""
|
|
27
27
|
Get centerline deflection w.
|
|
28
28
|
|
|
@@ -39,12 +39,12 @@ class FieldQuantitiesMixin:
|
|
|
39
39
|
Deflection w (in specified unit) of the slab.
|
|
40
40
|
"""
|
|
41
41
|
convert = {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
42
|
+
"m": 1e-3, # meters
|
|
43
|
+
"cm": 1e-1, # centimeters
|
|
44
|
+
"mm": 1, # millimeters
|
|
45
|
+
"um": 1e3, # micrometers
|
|
46
46
|
}
|
|
47
|
-
return convert[unit]*Z[2, :]
|
|
47
|
+
return convert[unit] * Z[2, :]
|
|
48
48
|
|
|
49
49
|
def dw_dx(self, Z):
|
|
50
50
|
"""
|
|
@@ -62,7 +62,7 @@ class FieldQuantitiesMixin:
|
|
|
62
62
|
"""
|
|
63
63
|
return Z[3, :]
|
|
64
64
|
|
|
65
|
-
def psi(self, Z, unit=
|
|
65
|
+
def psi(self, Z, unit="rad"):
|
|
66
66
|
"""
|
|
67
67
|
Get midplane rotation psi.
|
|
68
68
|
|
|
@@ -78,9 +78,9 @@ class FieldQuantitiesMixin:
|
|
|
78
78
|
psi : float
|
|
79
79
|
Cross-section rotation psi (radians) of the slab.
|
|
80
80
|
"""
|
|
81
|
-
if unit in [
|
|
81
|
+
if unit in ["deg", "degree", "degrees"]:
|
|
82
82
|
psi = np.rad2deg(Z[4, :])
|
|
83
|
-
elif unit in [
|
|
83
|
+
elif unit in ["rad", "radian", "radians"]:
|
|
84
84
|
psi = Z[4, :]
|
|
85
85
|
return psi
|
|
86
86
|
|
|
@@ -102,7 +102,7 @@ class FieldQuantitiesMixin:
|
|
|
102
102
|
return Z[5, :]
|
|
103
103
|
|
|
104
104
|
# pylint: enable=no-self-use
|
|
105
|
-
def u(self, Z, z0, unit=
|
|
105
|
+
def u(self, Z, z0, unit="mm"):
|
|
106
106
|
"""
|
|
107
107
|
Get horizontal displacement u = u0 + z0 psi.
|
|
108
108
|
|
|
@@ -121,12 +121,12 @@ class FieldQuantitiesMixin:
|
|
|
121
121
|
Horizontal displacement u (unit) of the slab.
|
|
122
122
|
"""
|
|
123
123
|
convert = {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
124
|
+
"m": 1e-3, # meters
|
|
125
|
+
"cm": 1e-1, # centimeters
|
|
126
|
+
"mm": 1, # millimeters
|
|
127
|
+
"um": 1e3, # micrometers
|
|
128
128
|
}
|
|
129
|
-
return convert[unit]*(Z[0, :] + z0*self.psi(Z))
|
|
129
|
+
return convert[unit] * (Z[0, :] + z0 * self.psi(Z))
|
|
130
130
|
|
|
131
131
|
def du_dx(self, Z, z0):
|
|
132
132
|
"""
|
|
@@ -145,7 +145,7 @@ class FieldQuantitiesMixin:
|
|
|
145
145
|
First derivative u' = u0' + z0 psi' of the horizontal
|
|
146
146
|
displacement of the slab.
|
|
147
147
|
"""
|
|
148
|
-
return Z[1, :] + z0*self.dpsi_dx(Z)
|
|
148
|
+
return Z[1, :] + z0 * self.dpsi_dx(Z)
|
|
149
149
|
|
|
150
150
|
def N(self, Z):
|
|
151
151
|
"""
|
|
@@ -161,7 +161,7 @@ class FieldQuantitiesMixin:
|
|
|
161
161
|
float
|
|
162
162
|
Axial normal force N (N) in the slab.
|
|
163
163
|
"""
|
|
164
|
-
return self.A11*Z[1, :] + self.B11*Z[5, :]
|
|
164
|
+
return self.A11 * Z[1, :] + self.B11 * Z[5, :]
|
|
165
165
|
|
|
166
166
|
def M(self, Z):
|
|
167
167
|
"""
|
|
@@ -177,7 +177,7 @@ class FieldQuantitiesMixin:
|
|
|
177
177
|
float
|
|
178
178
|
Bending moment M (Nmm) in the slab.
|
|
179
179
|
"""
|
|
180
|
-
return self.B11*Z[1, :] + self.D11*Z[5, :]
|
|
180
|
+
return self.B11 * Z[1, :] + self.D11 * Z[5, :]
|
|
181
181
|
|
|
182
182
|
def V(self, Z):
|
|
183
183
|
"""
|
|
@@ -193,9 +193,9 @@ class FieldQuantitiesMixin:
|
|
|
193
193
|
float
|
|
194
194
|
Vertical shear force V (N) in the slab.
|
|
195
195
|
"""
|
|
196
|
-
return self.kA55*(Z[3, :] + Z[4, :])
|
|
196
|
+
return self.kA55 * (Z[3, :] + Z[4, :])
|
|
197
197
|
|
|
198
|
-
def sig(self, Z, unit=
|
|
198
|
+
def sig(self, Z, unit="MPa"):
|
|
199
199
|
"""
|
|
200
200
|
Get weak-layer normal stress.
|
|
201
201
|
|
|
@@ -211,13 +211,10 @@ class FieldQuantitiesMixin:
|
|
|
211
211
|
float
|
|
212
212
|
Weak-layer normal stress sigma (in specified unit).
|
|
213
213
|
"""
|
|
214
|
-
convert = {
|
|
215
|
-
|
|
216
|
-
'MPa': 1
|
|
217
|
-
}
|
|
218
|
-
return -convert[unit]*self.kn*self.w(Z)
|
|
214
|
+
convert = {"kPa": 1e3, "MPa": 1}
|
|
215
|
+
return -convert[unit] * self.kn * self.w(Z)
|
|
219
216
|
|
|
220
|
-
def tau(self, Z, unit=
|
|
217
|
+
def tau(self, Z, unit="MPa"):
|
|
221
218
|
"""
|
|
222
219
|
Get weak-layer shear stress.
|
|
223
220
|
|
|
@@ -233,12 +230,12 @@ class FieldQuantitiesMixin:
|
|
|
233
230
|
float
|
|
234
231
|
Weak-layer shear stress tau (in specified unit).
|
|
235
232
|
"""
|
|
236
|
-
convert = {
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
233
|
+
convert = {"kPa": 1e3, "MPa": 1}
|
|
234
|
+
return (
|
|
235
|
+
-convert[unit]
|
|
236
|
+
* self.kt
|
|
237
|
+
* (self.dw_dx(Z) * self.t / 2 - self.u(Z, z0=self.h / 2))
|
|
238
|
+
)
|
|
242
239
|
|
|
243
240
|
def eps(self, Z):
|
|
244
241
|
"""
|
|
@@ -254,7 +251,7 @@ class FieldQuantitiesMixin:
|
|
|
254
251
|
float
|
|
255
252
|
Weak-layer normal strain epsilon.
|
|
256
253
|
"""
|
|
257
|
-
return -self.w(Z)/self.t
|
|
254
|
+
return -self.w(Z) / self.t
|
|
258
255
|
|
|
259
256
|
def gamma(self, Z):
|
|
260
257
|
"""
|
|
@@ -270,9 +267,9 @@ class FieldQuantitiesMixin:
|
|
|
270
267
|
float
|
|
271
268
|
Weak-layer shear strain gamma.
|
|
272
269
|
"""
|
|
273
|
-
return self.dw_dx(Z)/2 - self.u(Z, z0=self.h/2)/self.t
|
|
270
|
+
return self.dw_dx(Z) / 2 - self.u(Z, z0=self.h / 2) / self.t
|
|
274
271
|
|
|
275
|
-
def Gi(self, Ztip, unit=
|
|
272
|
+
def Gi(self, Ztip, unit="kJ/m^2"):
|
|
276
273
|
"""
|
|
277
274
|
Get mode I differential energy release rate at crack tip.
|
|
278
275
|
|
|
@@ -291,13 +288,13 @@ class FieldQuantitiesMixin:
|
|
|
291
288
|
or J/m^2) at the crack tip.
|
|
292
289
|
"""
|
|
293
290
|
convert = {
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
291
|
+
"J/m^2": 1e3, # joule per square meter
|
|
292
|
+
"kJ/m^2": 1, # kilojoule per square meter
|
|
293
|
+
"N/mm": 1, # newton per millimeter
|
|
297
294
|
}
|
|
298
|
-
return convert[unit]*self.sig(Ztip)**2/(2*self.kn)
|
|
295
|
+
return convert[unit] * self.sig(Ztip) ** 2 / (2 * self.kn)
|
|
299
296
|
|
|
300
|
-
def Gii(self, Ztip, unit=
|
|
297
|
+
def Gii(self, Ztip, unit="kJ/m^2"):
|
|
301
298
|
"""
|
|
302
299
|
Get mode II differential energy release rate at crack tip.
|
|
303
300
|
|
|
@@ -316,11 +313,11 @@ class FieldQuantitiesMixin:
|
|
|
316
313
|
or J/m^2) at the crack tip.
|
|
317
314
|
"""
|
|
318
315
|
convert = {
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
316
|
+
"J/m^2": 1e3, # joule per square meter
|
|
317
|
+
"kJ/m^2": 1, # kilojoule per square meter
|
|
318
|
+
"N/mm": 1, # newton per millimeter
|
|
322
319
|
}
|
|
323
|
-
return convert[unit]*self.tau(Ztip)**2/(2*self.kt)
|
|
320
|
+
return convert[unit] * self.tau(Ztip) ** 2 / (2 * self.kt)
|
|
324
321
|
|
|
325
322
|
def int1(self, x, z0, z1):
|
|
326
323
|
"""
|
|
@@ -342,7 +339,7 @@ class FieldQuantitiesMixin:
|
|
|
342
339
|
float or ndarray
|
|
343
340
|
Integrant of the mode I crack opening integral.
|
|
344
341
|
"""
|
|
345
|
-
return self.sig(z0(x))*self.eps(z1(x))*self.t
|
|
342
|
+
return self.sig(z0(x)) * self.eps(z1(x)) * self.t
|
|
346
343
|
|
|
347
344
|
def int2(self, x, z0, z1):
|
|
348
345
|
"""
|
|
@@ -364,7 +361,7 @@ class FieldQuantitiesMixin:
|
|
|
364
361
|
float or ndarray
|
|
365
362
|
Integrant of the mode II crack opening integral.
|
|
366
363
|
"""
|
|
367
|
-
return self.tau(z0(x))*self.gamma(z1(x))*self.t
|
|
364
|
+
return self.tau(z0(x)) * self.gamma(z1(x)) * self.t
|
|
368
365
|
|
|
369
366
|
def dz_dx(self, z, phi):
|
|
370
367
|
"""
|
|
@@ -494,9 +491,10 @@ class SlabContactMixin:
|
|
|
494
491
|
Provides Methods for the calculation of substitute spring stiffnesses,
|
|
495
492
|
cracklength-tresholds and element lengths.
|
|
496
493
|
"""
|
|
494
|
+
|
|
497
495
|
# pylint: disable=too-many-instance-attributes
|
|
498
496
|
|
|
499
|
-
def set_columnlength(self,L):
|
|
497
|
+
def set_columnlength(self, L):
|
|
500
498
|
"""
|
|
501
499
|
Set cracklength.
|
|
502
500
|
|
|
@@ -507,7 +505,7 @@ class SlabContactMixin:
|
|
|
507
505
|
"""
|
|
508
506
|
self.L = L
|
|
509
507
|
|
|
510
|
-
def set_cracklength(self,a):
|
|
508
|
+
def set_cracklength(self, a):
|
|
511
509
|
"""
|
|
512
510
|
Set cracklength.
|
|
513
511
|
|
|
@@ -518,7 +516,7 @@ class SlabContactMixin:
|
|
|
518
516
|
"""
|
|
519
517
|
self.a = a
|
|
520
518
|
|
|
521
|
-
def set_tc(self,cf):
|
|
519
|
+
def set_tc(self, cf):
|
|
522
520
|
"""
|
|
523
521
|
Set height of the crack.
|
|
524
522
|
|
|
@@ -528,11 +526,14 @@ class SlabContactMixin:
|
|
|
528
526
|
Collapse-factor. Ratio of the crack height to the
|
|
529
527
|
uncollapsed weak-layer height.
|
|
530
528
|
"""
|
|
529
|
+
# mark argument as intentionally unused (API compatibility)
|
|
530
|
+
_ = cf
|
|
531
531
|
# subtract displacement under constact load from collapsed wl height
|
|
532
532
|
qn = self.calc_qn()
|
|
533
|
-
|
|
533
|
+
collapse_height = 4.70 * (1 - np.exp(-self.t / 7.78))
|
|
534
|
+
self.tc = collapse_height - qn / self.kn
|
|
534
535
|
|
|
535
|
-
def set_phi(self,phi):
|
|
536
|
+
def set_phi(self, phi):
|
|
536
537
|
"""
|
|
537
538
|
Set inclination of the slab.
|
|
538
539
|
|
|
@@ -577,9 +578,9 @@ class SlabContactMixin:
|
|
|
577
578
|
"""
|
|
578
579
|
return self.get_weight_load(self.phi)[1] + self.get_surface_load(self.phi)[1]
|
|
579
580
|
|
|
580
|
-
def substitute_stiffness(self, L, support=
|
|
581
|
+
def substitute_stiffness(self, L, support="rested", dof="rot"):
|
|
581
582
|
"""
|
|
582
|
-
Calc substitute stiffness for beam on elastic foundation.
|
|
583
|
+
Calc substitute stiffness for beam on elastic foundation.
|
|
583
584
|
|
|
584
585
|
Arguments
|
|
585
586
|
---------
|
|
@@ -587,7 +588,7 @@ class SlabContactMixin:
|
|
|
587
588
|
Total length of the PST-column (mm).
|
|
588
589
|
support : string
|
|
589
590
|
Type of segment foundation. Defaults to 'rested'.
|
|
590
|
-
dof : string
|
|
591
|
+
dof : string
|
|
591
592
|
Type of substitute spring, either 'rot' or 'trans'. Defaults to 'rot'.
|
|
592
593
|
|
|
593
594
|
Returns
|
|
@@ -595,39 +596,40 @@ class SlabContactMixin:
|
|
|
595
596
|
k : stiffness of substitute spring.
|
|
596
597
|
"""
|
|
597
598
|
# adjust system to substitute system
|
|
598
|
-
if dof in [
|
|
599
|
+
if dof in ["rot"]:
|
|
599
600
|
tempsys = self.system
|
|
600
|
-
self.system =
|
|
601
|
-
if dof in [
|
|
601
|
+
self.system = "rot"
|
|
602
|
+
if dof in ["trans"]:
|
|
602
603
|
tempsys = self.system
|
|
603
|
-
self.system =
|
|
604
|
+
self.system = "trans"
|
|
604
605
|
|
|
605
606
|
# Change eigensystem for rested segment
|
|
606
|
-
if support in [
|
|
607
|
+
if support in ["rested"]:
|
|
607
608
|
tempkn = self.kn
|
|
608
609
|
tempkt = self.kt
|
|
609
|
-
self.kn = self.ratio*self.kn
|
|
610
|
-
self.kt = self.ratio*self.kt
|
|
610
|
+
self.kn = self.ratio * self.kn
|
|
611
|
+
self.kt = self.ratio * self.kt
|
|
611
612
|
self.calc_system_matrix()
|
|
612
613
|
self.calc_eigensystem()
|
|
613
614
|
|
|
614
615
|
# prepare list of segment characteristics
|
|
615
|
-
segments = {
|
|
616
|
-
|
|
617
|
-
|
|
616
|
+
segments = {
|
|
617
|
+
"li": np.array([L, 0.0]),
|
|
618
|
+
"mi": np.array([0]),
|
|
619
|
+
"ki": np.array([True, True]),
|
|
620
|
+
}
|
|
618
621
|
# solve system of equations
|
|
619
622
|
constants = self.assemble_and_solve(phi=0, **segments)
|
|
620
623
|
# calculate stiffness
|
|
621
|
-
_, z_pst, _ = self.rasterize_solution(
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
k = abs(1/self.w(z_pst)[0])
|
|
624
|
+
_, z_pst, _ = self.rasterize_solution(C=constants, phi=0, num=1, **segments)
|
|
625
|
+
if dof in ["rot"]:
|
|
626
|
+
k = abs(1 / self.psi(z_pst)[0])
|
|
627
|
+
if dof in ["trans"]:
|
|
628
|
+
k = abs(1 / self.w(z_pst)[0])
|
|
627
629
|
|
|
628
630
|
# Reset to previous system and eigensystem
|
|
629
631
|
self.system = tempsys
|
|
630
|
-
if support in [
|
|
632
|
+
if support in ["rested"]:
|
|
631
633
|
self.kn = tempkn
|
|
632
634
|
self.kt = tempkt
|
|
633
635
|
self.calc_system_matrix()
|
|
@@ -645,7 +647,7 @@ class SlabContactMixin:
|
|
|
645
647
|
Length of the crack for transition of stage A to stage B (mm).
|
|
646
648
|
"""
|
|
647
649
|
# Unpack variables
|
|
648
|
-
bs = -(self.B11**2/self.A11 - self.D11)
|
|
650
|
+
bs = -(self.B11**2 / self.A11 - self.D11)
|
|
649
651
|
ss = self.kA55
|
|
650
652
|
L = self.L
|
|
651
653
|
tc = self.tc
|
|
@@ -654,17 +656,17 @@ class SlabContactMixin:
|
|
|
654
656
|
# Create polynomial expression
|
|
655
657
|
def polynomial(x):
|
|
656
658
|
# Spring stiffness supported segment
|
|
657
|
-
kRl = self.substitute_stiffness(L-x,
|
|
658
|
-
kNl = self.substitute_stiffness(L-x,
|
|
659
|
-
c1 = 1/(8*bs)
|
|
660
|
-
c2 = 1/(2*kRl)
|
|
661
|
-
c3 = 1/(2*ss)
|
|
662
|
-
c4 = 1/kNl
|
|
663
|
-
c5 = -tc/qn
|
|
664
|
-
return c1*x**4 + c2*x**3 + c3*x**2 + c4*x + c5
|
|
659
|
+
kRl = self.substitute_stiffness(L - x, "supported", "rot")
|
|
660
|
+
kNl = self.substitute_stiffness(L - x, "supported", "trans")
|
|
661
|
+
c1 = 1 / (8 * bs)
|
|
662
|
+
c2 = 1 / (2 * kRl)
|
|
663
|
+
c3 = 1 / (2 * ss)
|
|
664
|
+
c4 = 1 / kNl
|
|
665
|
+
c5 = -tc / qn
|
|
666
|
+
return c1 * x**4 + c2 * x**3 + c3 * x**2 + c4 * x + c5
|
|
665
667
|
|
|
666
668
|
# Find root
|
|
667
|
-
a1 = brentq(polynomial, L/1000, 999/1000*L)
|
|
669
|
+
a1 = brentq(polynomial, L / 1000, 999 / 1000 * L)
|
|
668
670
|
|
|
669
671
|
return a1
|
|
670
672
|
|
|
@@ -678,7 +680,7 @@ class SlabContactMixin:
|
|
|
678
680
|
Length of the crack for transition of stage B to stage C (mm).
|
|
679
681
|
"""
|
|
680
682
|
# Unpack variables
|
|
681
|
-
bs = -(self.B11**2/self.A11 - self.D11)
|
|
683
|
+
bs = -(self.B11**2 / self.A11 - self.D11)
|
|
682
684
|
ss = self.kA55
|
|
683
685
|
L = self.L
|
|
684
686
|
tc = self.tc
|
|
@@ -687,27 +689,21 @@ class SlabContactMixin:
|
|
|
687
689
|
# Create polynomial function
|
|
688
690
|
def polynomial(x):
|
|
689
691
|
# Spring stiffness supported segment
|
|
690
|
-
kRl = self.substitute_stiffness(L-x,
|
|
691
|
-
kNl = self.substitute_stiffness(L-x,
|
|
692
|
-
c1 = ss**2*kRl*kNl*qn
|
|
693
|
-
c2 = 6*ss**2*bs*kNl*qn
|
|
694
|
-
c3 = 30*bs*ss*kRl*kNl*qn
|
|
695
|
-
c4 = 24*bs*qn*(
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
- ss**2*kRl*kNl*tc)
|
|
703
|
-
c6 = 144*bs*ss*(
|
|
704
|
-
bs*kRl*qn \
|
|
705
|
-
- bs*ss*kNl*tc)
|
|
706
|
-
c7 = - 144*bs**2*ss*kRl*kNl*tc
|
|
707
|
-
return c1*x**6 + c2*x**5 + c3*x**4 + c4*x**3 + c5*x**2 + c6*x + c7
|
|
692
|
+
kRl = self.substitute_stiffness(L - x, "supported", "rot")
|
|
693
|
+
kNl = self.substitute_stiffness(L - x, "supported", "trans")
|
|
694
|
+
c1 = ss**2 * kRl * kNl * qn
|
|
695
|
+
c2 = 6 * ss**2 * bs * kNl * qn
|
|
696
|
+
c3 = 30 * bs * ss * kRl * kNl * qn
|
|
697
|
+
c4 = 24 * bs * qn * (2 * ss**2 * kRl + 3 * bs * ss * kNl)
|
|
698
|
+
c5 = 72 * bs * (bs * qn * (ss**2 + kRl * kNl) - ss**2 * kRl * kNl * tc)
|
|
699
|
+
c6 = 144 * bs * ss * (bs * kRl * qn - bs * ss * kNl * tc)
|
|
700
|
+
c7 = -144 * bs**2 * ss * kRl * kNl * tc
|
|
701
|
+
return (
|
|
702
|
+
c1 * x**6 + c2 * x**5 + c3 * x**4 + c4 * x**3 + c5 * x**2 + c6 * x + c7
|
|
703
|
+
)
|
|
708
704
|
|
|
709
705
|
# Find root
|
|
710
|
-
a2 = brentq(polynomial, L/1000, 999/1000*L)
|
|
706
|
+
a2 = brentq(polynomial, L / 1000, 999 / 1000 * L)
|
|
711
707
|
|
|
712
708
|
return a2
|
|
713
709
|
|
|
@@ -732,7 +728,7 @@ class SlabContactMixin:
|
|
|
732
728
|
Calculate the length of the touchdown element in mode C.
|
|
733
729
|
"""
|
|
734
730
|
# Unpack variables
|
|
735
|
-
bs = -(self.B11**2/self.A11 - self.D11)
|
|
731
|
+
bs = -(self.B11**2 / self.A11 - self.D11)
|
|
736
732
|
ss = self.kA55
|
|
737
733
|
L = self.L
|
|
738
734
|
a = self.a
|
|
@@ -741,37 +737,41 @@ class SlabContactMixin:
|
|
|
741
737
|
|
|
742
738
|
def polynomial(x):
|
|
743
739
|
# Spring stiffness supported segment
|
|
744
|
-
kRl = self.substitute_stiffness(L-a,
|
|
745
|
-
kNl = self.substitute_stiffness(L-a,
|
|
740
|
+
kRl = self.substitute_stiffness(L - a, "supported", "rot")
|
|
741
|
+
kNl = self.substitute_stiffness(L - a, "supported", "trans")
|
|
746
742
|
# Spring stiffness rested segment
|
|
747
|
-
kRr = self.substitute_stiffness(a-x,
|
|
743
|
+
kRr = self.substitute_stiffness(a - x, "rested", "rot")
|
|
748
744
|
# define constants
|
|
749
|
-
c1 = ss**2*kRl*kNl*qn
|
|
750
|
-
c2 = 6*ss*kNl*qn*(
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
+ 3*bs*ss*kNl
|
|
757
|
-
|
|
758
|
-
c5 =
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
2*kRr*qn
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
745
|
+
c1 = ss**2 * kRl * kNl * qn
|
|
746
|
+
c2 = 6 * ss * kNl * qn * (bs * ss + kRl * kRr)
|
|
747
|
+
c3 = 30 * bs * ss * kNl * qn * (kRl + kRr)
|
|
748
|
+
c4 = (
|
|
749
|
+
24
|
|
750
|
+
* bs
|
|
751
|
+
* qn
|
|
752
|
+
* (2 * ss**2 * kRl + 3 * bs * ss * kNl + 3 * kRl * kRr * kNl)
|
|
753
|
+
)
|
|
754
|
+
c5 = (
|
|
755
|
+
72
|
|
756
|
+
* bs
|
|
757
|
+
* (
|
|
758
|
+
bs * qn * (ss**2 + kNl * (kRl + kRr))
|
|
759
|
+
+ ss * kRl * (2 * kRr * qn - ss * kNl * tc)
|
|
760
|
+
)
|
|
761
|
+
)
|
|
762
|
+
c6 = (
|
|
763
|
+
144
|
|
764
|
+
* bs
|
|
765
|
+
* ss
|
|
766
|
+
* (bs * qn * (kRl + kRr) - kNl * tc * (bs * ss + kRl * kRr))
|
|
767
|
+
)
|
|
768
|
+
c7 = -144 * bs**2 * ss * kNl * tc * (kRl + kRr)
|
|
769
|
+
return (
|
|
770
|
+
c1 * x**6 + c2 * x**5 + c3 * x**4 + c4 * x**3 + c5 * x**2 + c6 * x + c7
|
|
771
|
+
)
|
|
772
772
|
|
|
773
773
|
# Find root
|
|
774
|
-
lC = brentq(polynomial, a/1000, 999/1000*a)
|
|
774
|
+
lC = brentq(polynomial, a / 1000, 999 / 1000 * a)
|
|
775
775
|
|
|
776
776
|
return lC
|
|
777
777
|
|
|
@@ -791,22 +791,22 @@ class SlabContactMixin:
|
|
|
791
791
|
a2 = self.calc_a2()
|
|
792
792
|
# Assign stage
|
|
793
793
|
if self.a <= a1:
|
|
794
|
-
mode =
|
|
794
|
+
mode = "A"
|
|
795
795
|
elif a1 < self.a <= a2:
|
|
796
|
-
mode =
|
|
796
|
+
mode = "B"
|
|
797
797
|
elif a2 < self.a:
|
|
798
|
-
mode =
|
|
798
|
+
mode = "C"
|
|
799
799
|
self.mode = mode
|
|
800
800
|
else:
|
|
801
|
-
self.mode =
|
|
801
|
+
self.mode = "A"
|
|
802
802
|
|
|
803
803
|
def calc_touchdown_length(self):
|
|
804
804
|
"""Calculate touchdown length"""
|
|
805
|
-
if self.mode in [
|
|
805
|
+
if self.mode in ["A"]:
|
|
806
806
|
self.td = self.calc_lA()
|
|
807
|
-
elif self.mode in [
|
|
807
|
+
elif self.mode in ["B"]:
|
|
808
808
|
self.td = self.calc_lB()
|
|
809
|
-
elif self.mode in [
|
|
809
|
+
elif self.mode in ["C"]:
|
|
810
810
|
self.td = self.calc_lC()
|
|
811
811
|
|
|
812
812
|
def calc_touchdown_system(self, L, a, cf, phi, ratio=1000):
|
|
@@ -815,6 +815,7 @@ class SlabContactMixin:
|
|
|
815
815
|
self.calc_touchdown_mode()
|
|
816
816
|
self.calc_touchdown_length()
|
|
817
817
|
|
|
818
|
+
|
|
818
819
|
class SolutionMixin:
|
|
819
820
|
"""
|
|
820
821
|
Mixin for the solution of boundary value problems.
|
|
@@ -823,7 +824,7 @@ class SolutionMixin:
|
|
|
823
824
|
and for the computation of the free constants.
|
|
824
825
|
"""
|
|
825
826
|
|
|
826
|
-
def bc(self, z, k=False, pos=
|
|
827
|
+
def bc(self, z, k=False, pos="mid"):
|
|
827
828
|
"""
|
|
828
829
|
Provide equations for free (pst) or infinite (skiers) ends.
|
|
829
830
|
|
|
@@ -849,77 +850,48 @@ class SolutionMixin:
|
|
|
849
850
|
"""
|
|
850
851
|
|
|
851
852
|
# Set boundary conditions for PST-systems
|
|
852
|
-
if self.system in [
|
|
853
|
+
if self.system in ["pst-", "-pst"]:
|
|
853
854
|
if not k:
|
|
854
|
-
if self.mode in [
|
|
855
|
+
if self.mode in ["A"]:
|
|
855
856
|
# Free end
|
|
856
|
-
bc = np.array([self.N(z),
|
|
857
|
-
|
|
858
|
-
self.V(z)
|
|
859
|
-
])
|
|
860
|
-
elif self.mode in ['B'] and pos in ['r', 'right']:
|
|
857
|
+
bc = np.array([self.N(z), self.M(z), self.V(z)])
|
|
858
|
+
elif self.mode in ["B"] and pos in ["r", "right"]:
|
|
861
859
|
# Touchdown right
|
|
862
|
-
bc = np.array([self.N(z),
|
|
863
|
-
|
|
864
|
-
self.w(z)
|
|
865
|
-
])
|
|
866
|
-
elif self.mode in ['B'] and pos in ['l', 'left']: # Kann dieser Block
|
|
860
|
+
bc = np.array([self.N(z), self.M(z), self.w(z)])
|
|
861
|
+
elif self.mode in ["B"] and pos in ["l", "left"]: # Kann dieser Block
|
|
867
862
|
# Touchdown left # verschwinden? Analog zu 'A'
|
|
868
|
-
bc = np.array([self.N(z),
|
|
869
|
-
|
|
870
|
-
self.w(z)
|
|
871
|
-
])
|
|
872
|
-
elif self.mode in ['C'] and pos in ['r', 'right']:
|
|
863
|
+
bc = np.array([self.N(z), self.M(z), self.w(z)])
|
|
864
|
+
elif self.mode in ["C"] and pos in ["r", "right"]:
|
|
873
865
|
# Spring stiffness
|
|
874
|
-
kR = self.substitute_stiffness(self.a - self.td,
|
|
866
|
+
kR = self.substitute_stiffness(self.a - self.td, "rested", "rot")
|
|
875
867
|
# Touchdown right
|
|
876
|
-
bc = np.array([self.N(z),
|
|
877
|
-
|
|
878
|
-
self.w(z)
|
|
879
|
-
])
|
|
880
|
-
elif self.mode in ['C'] and pos in ['l', 'left']:
|
|
868
|
+
bc = np.array([self.N(z), self.M(z) + kR * self.psi(z), self.w(z)])
|
|
869
|
+
elif self.mode in ["C"] and pos in ["l", "left"]:
|
|
881
870
|
# Spring stiffness
|
|
882
|
-
kR = self.substitute_stiffness(self.a - self.td,
|
|
871
|
+
kR = self.substitute_stiffness(self.a - self.td, "rested", "rot")
|
|
883
872
|
# Touchdown left
|
|
884
|
-
bc = np.array([self.N(z),
|
|
885
|
-
self.M(z) - kR*self.psi(z),
|
|
886
|
-
self.w(z)
|
|
887
|
-
])
|
|
873
|
+
bc = np.array([self.N(z), self.M(z) - kR * self.psi(z), self.w(z)])
|
|
888
874
|
else:
|
|
889
875
|
# Free end
|
|
890
|
-
bc = np.array([
|
|
891
|
-
self.N(z),
|
|
892
|
-
self.M(z),
|
|
893
|
-
self.V(z)
|
|
894
|
-
])
|
|
876
|
+
bc = np.array([self.N(z), self.M(z), self.V(z)])
|
|
895
877
|
# Set boundary conditions for PST-systems with vertical faces
|
|
896
|
-
elif self.system in [
|
|
897
|
-
bc = np.array([
|
|
898
|
-
self.N(z),
|
|
899
|
-
self.M(z),
|
|
900
|
-
self.V(z)
|
|
901
|
-
])
|
|
878
|
+
elif self.system in ["-vpst", "vpst-"]:
|
|
879
|
+
bc = np.array([self.N(z), self.M(z), self.V(z)])
|
|
902
880
|
# Set boundary conditions for SKIER-systems
|
|
903
|
-
elif self.system in [
|
|
881
|
+
elif self.system in ["skier", "skiers"]:
|
|
904
882
|
# Infinite end (vanishing complementary solution)
|
|
905
|
-
bc = np.array([self.u(z, z0=0),
|
|
906
|
-
self.w(z),
|
|
907
|
-
self.psi(z)
|
|
908
|
-
])
|
|
883
|
+
bc = np.array([self.u(z, z0=0), self.w(z), self.psi(z)])
|
|
909
884
|
# Set boundary conditions for substitute spring calculus
|
|
910
|
-
elif self.system in [
|
|
911
|
-
bc = np.array([self.N(z),
|
|
912
|
-
self.M(z),
|
|
913
|
-
self.V(z)
|
|
914
|
-
])
|
|
885
|
+
elif self.system in ["rot", "trans"]:
|
|
886
|
+
bc = np.array([self.N(z), self.M(z), self.V(z)])
|
|
915
887
|
else:
|
|
916
888
|
raise ValueError(
|
|
917
|
-
|
|
918
|
-
|
|
889
|
+
f"Boundary conditions not defined for system of type {self.system}."
|
|
890
|
+
)
|
|
919
891
|
|
|
920
892
|
return bc
|
|
921
893
|
|
|
922
|
-
def eqs(self, zl, zr, k=False, pos=
|
|
894
|
+
def eqs(self, zl, zr, k=False, pos="mid"):
|
|
923
895
|
"""
|
|
924
896
|
Provide boundary or transmission conditions for beam segments.
|
|
925
897
|
|
|
@@ -946,62 +918,75 @@ class SolutionMixin:
|
|
|
946
918
|
or vector of transmission conditions (of length 6+6)
|
|
947
919
|
for center segments.
|
|
948
920
|
"""
|
|
949
|
-
if pos in (
|
|
950
|
-
eqs = np.array(
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
921
|
+
if pos in ("l", "left"):
|
|
922
|
+
eqs = np.array(
|
|
923
|
+
[
|
|
924
|
+
self.bc(zl, k, pos)[0], # Left boundary condition
|
|
925
|
+
self.bc(zl, k, pos)[1], # Left boundary condition
|
|
926
|
+
self.bc(zl, k, pos)[2], # Left boundary condition
|
|
927
|
+
self.u(zr, z0=0), # ui(xi = li)
|
|
928
|
+
self.w(zr), # wi(xi = li)
|
|
929
|
+
self.psi(zr), # psii(xi = li)
|
|
930
|
+
self.N(zr), # Ni(xi = li)
|
|
931
|
+
self.M(zr), # Mi(xi = li)
|
|
932
|
+
self.V(zr),
|
|
933
|
+
]
|
|
934
|
+
) # Vi(xi = li)
|
|
935
|
+
elif pos in ("m", "mid"):
|
|
936
|
+
eqs = np.array(
|
|
937
|
+
[
|
|
938
|
+
-self.u(zl, z0=0), # -ui(xi = 0)
|
|
939
|
+
-self.w(zl), # -wi(xi = 0)
|
|
940
|
+
-self.psi(zl), # -psii(xi = 0)
|
|
941
|
+
-self.N(zl), # -Ni(xi = 0)
|
|
942
|
+
-self.M(zl), # -Mi(xi = 0)
|
|
943
|
+
-self.V(zl), # -Vi(xi = 0)
|
|
944
|
+
self.u(zr, z0=0), # ui(xi = li)
|
|
945
|
+
self.w(zr), # wi(xi = li)
|
|
946
|
+
self.psi(zr), # psii(xi = li)
|
|
947
|
+
self.N(zr), # Ni(xi = li)
|
|
948
|
+
self.M(zr), # Mi(xi = li)
|
|
949
|
+
self.V(zr),
|
|
950
|
+
]
|
|
951
|
+
) # Vi(xi = li)
|
|
952
|
+
elif pos in ("r", "right"):
|
|
953
|
+
eqs = np.array(
|
|
954
|
+
[
|
|
955
|
+
-self.u(zl, z0=0), # -ui(xi = 0)
|
|
956
|
+
-self.w(zl), # -wi(xi = 0)
|
|
957
|
+
-self.psi(zl), # -psii(xi = 0)
|
|
958
|
+
-self.N(zl), # -Ni(xi = 0)
|
|
959
|
+
-self.M(zl), # -Mi(xi = 0)
|
|
960
|
+
-self.V(zl), # -Vi(xi = 0)
|
|
961
|
+
self.bc(zr, k, pos)[0], # Right boundary condition
|
|
962
|
+
self.bc(zr, k, pos)[1], # Right boundary condition
|
|
963
|
+
self.bc(zr, k, pos)[2],
|
|
964
|
+
]
|
|
965
|
+
) # Right boundary condition
|
|
985
966
|
else:
|
|
986
967
|
raise ValueError(
|
|
987
|
-
(
|
|
988
|
-
|
|
989
|
-
|
|
968
|
+
(
|
|
969
|
+
f"Invalid position argument {pos} given. "
|
|
970
|
+
"Valid segment positions are l, m, and r, "
|
|
971
|
+
"or left, mid and right."
|
|
972
|
+
)
|
|
973
|
+
)
|
|
990
974
|
return eqs
|
|
991
975
|
|
|
992
976
|
def calc_segments(
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
977
|
+
self,
|
|
978
|
+
li: list[float] | list[int] | bool = False,
|
|
979
|
+
mi: list[float] | list[int] | bool = False,
|
|
980
|
+
ki: list[bool] | bool = False,
|
|
981
|
+
k0: list[bool] | bool = False,
|
|
982
|
+
L: float = 1e4,
|
|
983
|
+
a: float = 0,
|
|
984
|
+
m: float = 0,
|
|
985
|
+
phi: float = 0,
|
|
986
|
+
cf: float = 0.5,
|
|
987
|
+
ratio: float = 1000,
|
|
988
|
+
**kwargs,
|
|
989
|
+
):
|
|
1005
990
|
"""
|
|
1006
991
|
Assemble lists defining the segments.
|
|
1007
992
|
|
|
@@ -1049,52 +1034,53 @@ class SolutionMixin:
|
|
|
1049
1034
|
in the cracked (ki) and uncracked (k0) configurations.
|
|
1050
1035
|
"""
|
|
1051
1036
|
|
|
1052
|
-
_ = kwargs
|
|
1037
|
+
_ = kwargs # Unused arguments
|
|
1053
1038
|
|
|
1054
1039
|
# Precompute touchdown properties
|
|
1055
1040
|
self.calc_touchdown_system(L=L, a=a, cf=cf, phi=phi, ratio=ratio)
|
|
1056
1041
|
|
|
1057
1042
|
# Assemble list defining the segments
|
|
1058
|
-
if self.system ==
|
|
1059
|
-
li = np.array(li)
|
|
1060
|
-
mi = np.array(mi)
|
|
1061
|
-
ki = np.array(ki)
|
|
1062
|
-
k0 = np.array(k0)
|
|
1063
|
-
elif self.system ==
|
|
1064
|
-
li = np.array([L - self.a, self.td])
|
|
1065
|
-
mi = np.array([0])
|
|
1066
|
-
ki = np.array([True, False])
|
|
1067
|
-
k0 = np.array([True, True])
|
|
1068
|
-
elif self.system ==
|
|
1069
|
-
li = np.array([self.td, L - self.a])
|
|
1070
|
-
mi = np.array([0])
|
|
1071
|
-
ki = np.array([False, True])
|
|
1072
|
-
k0 = np.array([True, True])
|
|
1073
|
-
elif self.system ==
|
|
1074
|
-
li = np.array([L - a, a])
|
|
1075
|
-
mi = np.array([0])
|
|
1076
|
-
ki = np.array([True, False])
|
|
1077
|
-
k0 = np.array([True, True])
|
|
1078
|
-
elif self.system ==
|
|
1079
|
-
li = np.array([a, L - a])
|
|
1080
|
-
mi = np.array([0])
|
|
1081
|
-
ki = np.array([False, True])
|
|
1082
|
-
k0 = np.array([True, True])
|
|
1083
|
-
elif self.system ==
|
|
1084
|
-
lb = (L - self.a)/2
|
|
1085
|
-
lf = self.a/2
|
|
1086
|
-
li = np.array([lb, lf, lf, lb])
|
|
1087
|
-
mi = np.array([0, m, 0])
|
|
1088
|
-
ki = np.array([True, False, False, True])
|
|
1089
|
-
k0 = np.array([True, True, True, True])
|
|
1043
|
+
if self.system == "skiers":
|
|
1044
|
+
li = np.array(li) # Segment lengths
|
|
1045
|
+
mi = np.array(mi) # Skier weights
|
|
1046
|
+
ki = np.array(ki) # Crack
|
|
1047
|
+
k0 = np.array(k0) # No crack
|
|
1048
|
+
elif self.system == "pst-":
|
|
1049
|
+
li = np.array([L - self.a, self.td]) # Segment lengths
|
|
1050
|
+
mi = np.array([0]) # Skier weights
|
|
1051
|
+
ki = np.array([True, False]) # Crack
|
|
1052
|
+
k0 = np.array([True, True]) # No crack
|
|
1053
|
+
elif self.system == "-pst":
|
|
1054
|
+
li = np.array([self.td, L - self.a]) # Segment lengths
|
|
1055
|
+
mi = np.array([0]) # Skier weights
|
|
1056
|
+
ki = np.array([False, True]) # Crack
|
|
1057
|
+
k0 = np.array([True, True]) # No crack
|
|
1058
|
+
elif self.system == "vpst-":
|
|
1059
|
+
li = np.array([L - a, a]) # Segment lengths
|
|
1060
|
+
mi = np.array([0]) # Skier weights
|
|
1061
|
+
ki = np.array([True, False]) # Crack
|
|
1062
|
+
k0 = np.array([True, True]) # No crack
|
|
1063
|
+
elif self.system == "-vpst":
|
|
1064
|
+
li = np.array([a, L - a]) # Segment lengths
|
|
1065
|
+
mi = np.array([0]) # Skier weights
|
|
1066
|
+
ki = np.array([False, True]) # Crack
|
|
1067
|
+
k0 = np.array([True, True]) # No crack
|
|
1068
|
+
elif self.system == "skier":
|
|
1069
|
+
lb = (L - self.a) / 2 # Half bedded length
|
|
1070
|
+
lf = self.a / 2 # Half free length
|
|
1071
|
+
li = np.array([lb, lf, lf, lb]) # Segment lengths
|
|
1072
|
+
mi = np.array([0, m, 0]) # Skier weights
|
|
1073
|
+
ki = np.array([True, False, False, True]) # Crack
|
|
1074
|
+
k0 = np.array([True, True, True, True]) # No crack
|
|
1090
1075
|
else:
|
|
1091
|
-
raise ValueError(f
|
|
1076
|
+
raise ValueError(f"System {self.system} is not implemented.")
|
|
1092
1077
|
|
|
1093
1078
|
# Fill dictionary
|
|
1094
1079
|
segments = {
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1080
|
+
"nocrack": {"li": li, "mi": mi, "ki": k0},
|
|
1081
|
+
"crack": {"li": li, "mi": mi, "ki": ki},
|
|
1082
|
+
"both": {"li": li, "mi": mi, "ki": ki, "k0": k0},
|
|
1083
|
+
}
|
|
1098
1084
|
return segments
|
|
1099
1085
|
|
|
1100
1086
|
def assemble_and_solve(self, phi, li, mi, ki):
|
|
@@ -1133,29 +1119,37 @@ class SolutionMixin:
|
|
|
1133
1119
|
|
|
1134
1120
|
# No foundation
|
|
1135
1121
|
if not any(ki):
|
|
1136
|
-
raise ValueError(
|
|
1122
|
+
raise ValueError("Provide at least one supported segment.")
|
|
1137
1123
|
# Mismatch of number of segements and transisions
|
|
1138
1124
|
if len(li) != len(ki) or len(li) - 1 != len(mi):
|
|
1139
|
-
raise ValueError(
|
|
1140
|
-
|
|
1125
|
+
raise ValueError(
|
|
1126
|
+
"Make sure len(li)=N, len(ki)=N, and "
|
|
1127
|
+
"len(mi)=N-1 for a system of N segments."
|
|
1128
|
+
)
|
|
1141
1129
|
|
|
1142
|
-
if self.system not in [
|
|
1130
|
+
if self.system not in ["pst-", "-pst", "vpst-", "-vpst", "rot", "trans"]:
|
|
1143
1131
|
# Boundary segments must be on foundation for infinite BCs
|
|
1144
1132
|
if not all([ki[0], ki[-1]]):
|
|
1145
|
-
raise ValueError(
|
|
1146
|
-
|
|
1133
|
+
raise ValueError(
|
|
1134
|
+
"Provide supported boundary segments in "
|
|
1135
|
+
"order to account for infinite extensions."
|
|
1136
|
+
)
|
|
1147
1137
|
# Make sure infinity boundary conditions are far enough from skiers
|
|
1148
1138
|
if li[0] < 5e3 or li[-1] < 5e3:
|
|
1149
|
-
print(
|
|
1150
|
-
|
|
1151
|
-
|
|
1139
|
+
print(
|
|
1140
|
+
(
|
|
1141
|
+
"WARNING: Boundary segments are short. Make sure "
|
|
1142
|
+
"the complementary solution has decayed to the "
|
|
1143
|
+
"boundaries."
|
|
1144
|
+
)
|
|
1145
|
+
)
|
|
1152
1146
|
|
|
1153
1147
|
# --- PREPROCESSING ---------------------------------------------------
|
|
1154
1148
|
|
|
1155
1149
|
# Determine size of linear system of equations
|
|
1156
|
-
nS = len(li)
|
|
1150
|
+
nS = len(li) # Number of beam segments
|
|
1157
1151
|
|
|
1158
|
-
nDOF = 6
|
|
1152
|
+
nDOF = 6 # Number of free constants per segment
|
|
1159
1153
|
|
|
1160
1154
|
# Add dummy segment if only one segment provided
|
|
1161
1155
|
if nS == 1:
|
|
@@ -1165,13 +1159,13 @@ class SolutionMixin:
|
|
|
1165
1159
|
nS = 2
|
|
1166
1160
|
|
|
1167
1161
|
# Assemble position vector
|
|
1168
|
-
pi = np.full(nS,
|
|
1169
|
-
pi[0], pi[-1] =
|
|
1162
|
+
pi = np.full(nS, "m")
|
|
1163
|
+
pi[0], pi[-1] = "l", "r"
|
|
1170
1164
|
|
|
1171
1165
|
# Initialize matrices
|
|
1172
|
-
zh0 = np.zeros([nS*6, nS*nDOF])
|
|
1173
|
-
zp0 = np.zeros([nS*6, 1])
|
|
1174
|
-
rhs = np.zeros([nS*6, 1])
|
|
1166
|
+
zh0 = np.zeros([nS * 6, nS * nDOF])
|
|
1167
|
+
zp0 = np.zeros([nS * 6, 1])
|
|
1168
|
+
rhs = np.zeros([nS * 6, 1])
|
|
1175
1169
|
|
|
1176
1170
|
# --- ASSEMBLE LINEAR SYSTEM OF EQUATIONS -----------------------------
|
|
1177
1171
|
|
|
@@ -1181,72 +1175,73 @@ class SolutionMixin:
|
|
|
1181
1175
|
l, k, pos = li[i], ki[i], pi[i]
|
|
1182
1176
|
# Transmission conditions at left and right segment ends
|
|
1183
1177
|
zhi = self.eqs(
|
|
1184
|
-
zl=self.zh(x=0, l=l, bed=k),
|
|
1185
|
-
|
|
1186
|
-
k=k, pos=pos)
|
|
1178
|
+
zl=self.zh(x=0, l=l, bed=k), zr=self.zh(x=l, l=l, bed=k), k=k, pos=pos
|
|
1179
|
+
)
|
|
1187
1180
|
zpi = self.eqs(
|
|
1188
1181
|
zl=self.zp(x=0, phi=phi, bed=k),
|
|
1189
1182
|
zr=self.zp(x=l, phi=phi, bed=k),
|
|
1190
|
-
k=k,
|
|
1183
|
+
k=k,
|
|
1184
|
+
pos=pos,
|
|
1185
|
+
)
|
|
1191
1186
|
# Rows for left-hand side assembly
|
|
1192
1187
|
start = 0 if i == 0 else 3
|
|
1193
1188
|
stop = 6 if i == nS - 1 else 9
|
|
1194
1189
|
# Assemble left-hand side
|
|
1195
|
-
zh0[(6*i - start):(6*i + stop), i*nDOF:(i + 1)*nDOF] = zhi
|
|
1196
|
-
zp0[(6*i - start):(6*i + stop)] += zpi
|
|
1190
|
+
zh0[(6 * i - start) : (6 * i + stop), i * nDOF : (i + 1) * nDOF] = zhi
|
|
1191
|
+
zp0[(6 * i - start) : (6 * i + stop)] += zpi
|
|
1197
1192
|
|
|
1198
1193
|
# Loop through loads to assemble right-hand side
|
|
1199
1194
|
for i, m in enumerate(mi, start=1):
|
|
1200
1195
|
# Get skier loads
|
|
1201
1196
|
Fn, Ft = self.get_skier_load(m, phi)
|
|
1202
1197
|
# Right-hand side for transmission from segment i-1 to segment i
|
|
1203
|
-
rhs[6*i:6*i + 3] = np.vstack([Ft, -Ft*self.h/2, Fn])
|
|
1198
|
+
rhs[6 * i : 6 * i + 3] = np.vstack([Ft, -Ft * self.h / 2, Fn])
|
|
1204
1199
|
# Set rhs so that complementary integral vanishes at boundaries
|
|
1205
|
-
if self.system not in [
|
|
1200
|
+
if self.system not in ["pst-", "-pst", "rested"]:
|
|
1206
1201
|
rhs[:3] = self.bc(self.zp(x=0, phi=phi, bed=ki[0]))
|
|
1207
1202
|
rhs[-3:] = self.bc(self.zp(x=li[-1], phi=phi, bed=ki[-1]))
|
|
1208
1203
|
|
|
1209
1204
|
# Set rhs for vertical faces
|
|
1210
|
-
if self.system in [
|
|
1205
|
+
if self.system in ["vpst-", "-vpst"]:
|
|
1211
1206
|
# Calculate center of gravity and mass of
|
|
1212
1207
|
# added or cut off slab segement
|
|
1213
1208
|
xs, zs, m = calc_vertical_bc_center_of_gravity(self.slab, phi)
|
|
1214
1209
|
# Convert slope angle to radians
|
|
1215
1210
|
phi = np.deg2rad(phi)
|
|
1216
1211
|
# Translate inbto section forces and moments
|
|
1217
|
-
N = -self.g*m*np.sin(phi)
|
|
1218
|
-
M = -self.g*m*(xs*np.cos(phi) + zs*np.sin(phi))
|
|
1219
|
-
V = self.g*m*np.cos(phi)
|
|
1212
|
+
N = -self.g * m * np.sin(phi)
|
|
1213
|
+
M = -self.g * m * (xs * np.cos(phi) + zs * np.sin(phi))
|
|
1214
|
+
V = self.g * m * np.cos(phi)
|
|
1220
1215
|
# Add to right-hand side
|
|
1221
|
-
rhs[:3] = np.vstack([N, M, V])
|
|
1222
|
-
rhs[-3:] = np.vstack([N, M, V])
|
|
1216
|
+
rhs[:3] = np.vstack([N, M, V]) # left end
|
|
1217
|
+
rhs[-3:] = np.vstack([N, M, V]) # right end
|
|
1223
1218
|
|
|
1224
1219
|
# Loop through segments to set touchdown conditions at rhs
|
|
1225
1220
|
for i in range(nS):
|
|
1226
1221
|
# Length, foundation and position of segment i
|
|
1227
1222
|
l, k, pos = li[i], ki[i], pi[i]
|
|
1228
1223
|
# Set displacement BC in stage B
|
|
1229
|
-
if not k and bool(self.mode in [
|
|
1230
|
-
if i==0:
|
|
1231
|
-
rhs[:3] = np.vstack([0,0,self.tc])
|
|
1224
|
+
if not k and bool(self.mode in ["B"]):
|
|
1225
|
+
if i == 0:
|
|
1226
|
+
rhs[:3] = np.vstack([0, 0, self.tc])
|
|
1232
1227
|
if i == (nS - 1):
|
|
1233
|
-
rhs[-3:] = np.vstack([0,0,self.tc])
|
|
1228
|
+
rhs[-3:] = np.vstack([0, 0, self.tc])
|
|
1234
1229
|
# Set normal force and displacement BC for stage C
|
|
1235
|
-
if not k and bool(self.mode in [
|
|
1236
|
-
N = self.calc_qt()*(self.a - self.td)
|
|
1237
|
-
if i==0:
|
|
1238
|
-
rhs[:3] = np.vstack([-N,0,self.tc])
|
|
1230
|
+
if not k and bool(self.mode in ["C"]):
|
|
1231
|
+
N = self.calc_qt() * (self.a - self.td)
|
|
1232
|
+
if i == 0:
|
|
1233
|
+
rhs[:3] = np.vstack([-N, 0, self.tc])
|
|
1239
1234
|
if i == (nS - 1):
|
|
1240
|
-
rhs[-3:] = np.vstack([N,0,self.tc])
|
|
1235
|
+
rhs[-3:] = np.vstack([N, 0, self.tc])
|
|
1241
1236
|
|
|
1242
1237
|
# Rhs for substitute spring stiffness
|
|
1243
|
-
if self.system in [
|
|
1238
|
+
if self.system in ["rot"]:
|
|
1244
1239
|
# apply arbitrary moment of 1 at left boundary
|
|
1245
|
-
rhs = rhs*0
|
|
1240
|
+
rhs = rhs * 0
|
|
1246
1241
|
rhs[1] = 1
|
|
1247
|
-
if self.system in [
|
|
1242
|
+
if self.system in ["trans"]:
|
|
1248
1243
|
# apply arbitrary force of 1 at left boundary
|
|
1249
|
-
rhs = rhs*0
|
|
1244
|
+
rhs = rhs * 0
|
|
1250
1245
|
rhs[2] = 1
|
|
1251
1246
|
|
|
1252
1247
|
# --- SOLVE -----------------------------------------------------------
|
|
@@ -1266,13 +1261,14 @@ class AnalysisMixin:
|
|
|
1266
1261
|
"""
|
|
1267
1262
|
|
|
1268
1263
|
def rasterize_solution(
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1264
|
+
self,
|
|
1265
|
+
C: np.ndarray,
|
|
1266
|
+
phi: float,
|
|
1267
|
+
li: list[float] | bool,
|
|
1268
|
+
ki: list[bool] | bool,
|
|
1269
|
+
num: int = 250,
|
|
1270
|
+
**kwargs,
|
|
1271
|
+
):
|
|
1276
1272
|
"""
|
|
1277
1273
|
Compute rasterized solution vector.
|
|
1278
1274
|
|
|
@@ -1310,7 +1306,7 @@ class AnalysisMixin:
|
|
|
1310
1306
|
C, ki, li = C[:, isnonzero], ki[isnonzero], li[isnonzero]
|
|
1311
1307
|
|
|
1312
1308
|
# Compute number of plot points per segment (+1 for last segment)
|
|
1313
|
-
nq = np.ceil(li/li.sum()*num).astype(
|
|
1309
|
+
nq = np.ceil(li / li.sum() * num).astype("int")
|
|
1314
1310
|
nq[-1] += 1
|
|
1315
1311
|
|
|
1316
1312
|
# Provide cumulated length and plot point lists
|
|
@@ -1329,14 +1325,14 @@ class AnalysisMixin:
|
|
|
1329
1325
|
# Compute start and end coordinates of segment i
|
|
1330
1326
|
x0 = lic[i]
|
|
1331
1327
|
# Assemble global coordinate vector
|
|
1332
|
-
xq[nqc[i]:nqc[i + 1]] = x0 + xi
|
|
1328
|
+
xq[nqc[i] : nqc[i + 1]] = x0 + xi
|
|
1333
1329
|
# Mask coordinates not on foundation (including endpoints)
|
|
1334
1330
|
if not ki[i]:
|
|
1335
|
-
issupported[nqc[i]:nqc[i + 1]] = False
|
|
1331
|
+
issupported[nqc[i] : nqc[i + 1]] = False
|
|
1336
1332
|
# Compute segment solution
|
|
1337
1333
|
zi = self.z(xi, C[:, [i]], l, phi, ki[i])
|
|
1338
1334
|
# Assemble global solution matrix
|
|
1339
|
-
zq[:, nqc[i]:nqc[i + 1]] = zi
|
|
1335
|
+
zq[:, nqc[i] : nqc[i + 1]] = zi
|
|
1340
1336
|
|
|
1341
1337
|
# Make sure cracktips are included
|
|
1342
1338
|
transmissionbool = [ki[j] or ki[j + 1] for j, _ in enumerate(ki[:-1])]
|
|
@@ -1391,7 +1387,6 @@ class AnalysisMixin:
|
|
|
1391
1387
|
|
|
1392
1388
|
# Loop through segments with crack advance
|
|
1393
1389
|
for j, l in enumerate(li):
|
|
1394
|
-
|
|
1395
1390
|
# Uncracked (0) and cracked (1) solutions at integration points
|
|
1396
1391
|
z0 = partial(self.z, C=C0[:, [j]], l=l, phi=phi, bed=True)
|
|
1397
1392
|
z1 = partial(self.z, C=C1[:, [j]], l=l, phi=phi, bed=False)
|
|
@@ -1401,14 +1396,12 @@ class AnalysisMixin:
|
|
|
1401
1396
|
int2 = partial(self.int2, z0=z0, z1=z1)
|
|
1402
1397
|
|
|
1403
1398
|
# Segement contributions to total crack opening integral
|
|
1404
|
-
Ginc1 +=
|
|
1405
|
-
|
|
1406
|
-
Ginc2 += romberg(int2, 0, l, rtol=self.tol,
|
|
1407
|
-
vec_func=True)/(2*da)
|
|
1399
|
+
Ginc1 += quad(int1, 0, l, epsabs=self.tol, epsrel=self.tol)[0] / (2 * da)
|
|
1400
|
+
Ginc2 += quad(int2, 0, l, epsabs=self.tol, epsrel=self.tol)[0] / (2 * da)
|
|
1408
1401
|
|
|
1409
1402
|
return np.array([Ginc1 + Ginc2, Ginc1, Ginc2]).flatten()
|
|
1410
1403
|
|
|
1411
|
-
def gdif(self, C, phi, li, ki, unit=
|
|
1404
|
+
def gdif(self, C, phi, li, ki, unit="kJ/m^2", **kwargs):
|
|
1412
1405
|
"""
|
|
1413
1406
|
Compute differential energy release rate of all crack tips.
|
|
1414
1407
|
|
|
@@ -1458,9 +1451,9 @@ class AnalysisMixin:
|
|
|
1458
1451
|
|
|
1459
1452
|
# Adjust contributions for center cracks
|
|
1460
1453
|
if nct > 1:
|
|
1461
|
-
avgmask = np.full(nct, True)
|
|
1454
|
+
avgmask = np.full(nct, True) # Initialize mask
|
|
1462
1455
|
avgmask[[0, -1]] = ki[[0, -1]] # Do not weight edge cracks
|
|
1463
|
-
Gdif[:, avgmask] *= 0.5
|
|
1456
|
+
Gdif[:, avgmask] *= 0.5 # Weigth with half crack length
|
|
1464
1457
|
|
|
1465
1458
|
# Return total differential energy release rate of all crack tips
|
|
1466
1459
|
return Gdif.sum(axis=1)
|
|
@@ -1486,18 +1479,17 @@ class AnalysisMixin:
|
|
|
1486
1479
|
# Get ply (layer) coordinates
|
|
1487
1480
|
z = self.get_ply_coordinates()
|
|
1488
1481
|
# Compute number of grid points per layer
|
|
1489
|
-
nlayer = np.ceil((z[1:] - z[:-1])/dz).astype(np.int32) + 1
|
|
1482
|
+
nlayer = np.ceil((z[1:] - z[:-1]) / dz).astype(np.int32) + 1
|
|
1490
1483
|
# Calculate grid points as list of z-coordinates (mm)
|
|
1491
|
-
zi = np.hstack(
|
|
1492
|
-
np.linspace(z[i], z[i + 1], n, endpoint=True)
|
|
1493
|
-
|
|
1494
|
-
])
|
|
1484
|
+
zi = np.hstack(
|
|
1485
|
+
[np.linspace(z[i], z[i + 1], n, endpoint=True) for i, n in enumerate(nlayer)]
|
|
1486
|
+
)
|
|
1495
1487
|
# Get lists of corresponding elastic properties (E, nu, rho)
|
|
1496
1488
|
si = np.repeat(self.slab[:, [2, 4, 0]], nlayer, axis=0)
|
|
1497
1489
|
# Assemble mesh with columns (z, E, G, nu)
|
|
1498
1490
|
return np.column_stack([zi, si])
|
|
1499
1491
|
|
|
1500
|
-
def Sxx(self, Z, phi, dz=2, unit=
|
|
1492
|
+
def Sxx(self, Z, phi, dz=2, unit="kPa"):
|
|
1501
1493
|
"""
|
|
1502
1494
|
Compute axial normal stress in slab layers.
|
|
1503
1495
|
|
|
@@ -1518,15 +1510,12 @@ class AnalysisMixin:
|
|
|
1518
1510
|
Axial slab normal stress in specified unit.
|
|
1519
1511
|
"""
|
|
1520
1512
|
# Unit conversion dict
|
|
1521
|
-
convert = {
|
|
1522
|
-
'kPa': 1e3,
|
|
1523
|
-
'MPa': 1
|
|
1524
|
-
}
|
|
1513
|
+
convert = {"kPa": 1e3, "MPa": 1}
|
|
1525
1514
|
|
|
1526
1515
|
# Get mesh along z-axis
|
|
1527
1516
|
zmesh = self.get_zmesh(dz=dz)
|
|
1528
1517
|
zi = zmesh[:, 0]
|
|
1529
|
-
rho = 1e-12*zmesh[:, 3]
|
|
1518
|
+
rho = 1e-12 * zmesh[:, 3]
|
|
1530
1519
|
|
|
1531
1520
|
# Get dimensions of stress field (n rows, m columns)
|
|
1532
1521
|
n = zmesh.shape[0]
|
|
@@ -1537,18 +1526,18 @@ class AnalysisMixin:
|
|
|
1537
1526
|
|
|
1538
1527
|
# Compute axial normal stress Sxx at grid points in MPa
|
|
1539
1528
|
for i, (z, E, nu, _) in enumerate(zmesh):
|
|
1540
|
-
Sxx[i, :] = E/(1-nu**2)*self.du_dx(Z, z)
|
|
1529
|
+
Sxx[i, :] = E / (1 - nu**2) * self.du_dx(Z, z)
|
|
1541
1530
|
|
|
1542
1531
|
# Calculate weight load at grid points and superimpose on stress field
|
|
1543
|
-
qt = -rho*self.g*np.sin(np.deg2rad(phi))
|
|
1532
|
+
qt = -rho * self.g * np.sin(np.deg2rad(phi))
|
|
1544
1533
|
for i, qi in enumerate(qt[:-1]):
|
|
1545
|
-
Sxx[i, :] += qi*(zi[i+1] - zi[i])
|
|
1546
|
-
Sxx[-1, :] += qt[-1]*(zi[-1] - zi[-2])
|
|
1534
|
+
Sxx[i, :] += qi * (zi[i + 1] - zi[i])
|
|
1535
|
+
Sxx[-1, :] += qt[-1] * (zi[-1] - zi[-2])
|
|
1547
1536
|
|
|
1548
1537
|
# Return axial normal stress in specified unit
|
|
1549
|
-
return convert[unit]*Sxx
|
|
1538
|
+
return convert[unit] * Sxx
|
|
1550
1539
|
|
|
1551
|
-
def Txz(self, Z, phi, dz=2, unit=
|
|
1540
|
+
def Txz(self, Z, phi, dz=2, unit="kPa"):
|
|
1552
1541
|
"""
|
|
1553
1542
|
Compute shear stress in slab layers.
|
|
1554
1543
|
|
|
@@ -1569,14 +1558,11 @@ class AnalysisMixin:
|
|
|
1569
1558
|
Shear stress at grid points in the slab in specified unit.
|
|
1570
1559
|
"""
|
|
1571
1560
|
# Unit conversion dict
|
|
1572
|
-
convert = {
|
|
1573
|
-
'kPa': 1e3,
|
|
1574
|
-
'MPa': 1
|
|
1575
|
-
}
|
|
1561
|
+
convert = {"kPa": 1e3, "MPa": 1}
|
|
1576
1562
|
# Get mesh along z-axis
|
|
1577
1563
|
zmesh = self.get_zmesh(dz=dz)
|
|
1578
1564
|
zi = zmesh[:, 0]
|
|
1579
|
-
rho = 1e-12*zmesh[:, 3]
|
|
1565
|
+
rho = 1e-12 * zmesh[:, 3]
|
|
1580
1566
|
|
|
1581
1567
|
# Get dimensions of stress field (n rows, m columns)
|
|
1582
1568
|
n = zmesh.shape[0]
|
|
@@ -1592,10 +1578,10 @@ class AnalysisMixin:
|
|
|
1592
1578
|
|
|
1593
1579
|
# Calculate first derivative of sxx at z-grid points
|
|
1594
1580
|
for i, (z, E, nu, _) in enumerate(zmesh):
|
|
1595
|
-
dsxx_dx[i, :] = E/(1-nu**2)*(du0_dxdx + z*dpsi_dxdx)
|
|
1581
|
+
dsxx_dx[i, :] = E / (1 - nu**2) * (du0_dxdx + z * dpsi_dxdx)
|
|
1596
1582
|
|
|
1597
1583
|
# Calculate weight load at grid points
|
|
1598
|
-
qt = -rho*self.g*np.sin(np.deg2rad(phi))
|
|
1584
|
+
qt = -rho * self.g * np.sin(np.deg2rad(phi))
|
|
1599
1585
|
|
|
1600
1586
|
# Integrate -dsxx_dx along z and add cumulative weight load
|
|
1601
1587
|
# to obtain shear stress Txz in MPa
|
|
@@ -1603,9 +1589,9 @@ class AnalysisMixin:
|
|
|
1603
1589
|
Txz += cumulative_trapezoid(qt, zi, initial=0)[:, None]
|
|
1604
1590
|
|
|
1605
1591
|
# Return shear stress Txz in specified unit
|
|
1606
|
-
return convert[unit]*Txz
|
|
1592
|
+
return convert[unit] * Txz
|
|
1607
1593
|
|
|
1608
|
-
def Szz(self, Z, phi, dz=2, unit=
|
|
1594
|
+
def Szz(self, Z, phi, dz=2, unit="kPa"):
|
|
1609
1595
|
"""
|
|
1610
1596
|
Compute transverse normal stress in slab layers.
|
|
1611
1597
|
|
|
@@ -1627,15 +1613,12 @@ class AnalysisMixin:
|
|
|
1627
1613
|
specified unit.
|
|
1628
1614
|
"""
|
|
1629
1615
|
# Unit conversion dict
|
|
1630
|
-
convert = {
|
|
1631
|
-
'kPa': 1e3,
|
|
1632
|
-
'MPa': 1
|
|
1633
|
-
}
|
|
1616
|
+
convert = {"kPa": 1e3, "MPa": 1}
|
|
1634
1617
|
|
|
1635
1618
|
# Get mesh along z-axis
|
|
1636
1619
|
zmesh = self.get_zmesh(dz=dz)
|
|
1637
1620
|
zi = zmesh[:, 0]
|
|
1638
|
-
rho = 1e-12*zmesh[:, 3]
|
|
1621
|
+
rho = 1e-12 * zmesh[:, 3]
|
|
1639
1622
|
|
|
1640
1623
|
# Get dimensions of stress field (n rows, m columns)
|
|
1641
1624
|
n = zmesh.shape[0]
|
|
@@ -1651,10 +1634,10 @@ class AnalysisMixin:
|
|
|
1651
1634
|
|
|
1652
1635
|
# Calculate second derivative of sxx at z-grid points
|
|
1653
1636
|
for i, (z, E, nu, _) in enumerate(zmesh):
|
|
1654
|
-
dsxx_dxdx[i, :] = E/(1-nu**2)*(du0_dxdxdx + z*dpsi_dxdxdx)
|
|
1637
|
+
dsxx_dxdx[i, :] = E / (1 - nu**2) * (du0_dxdxdx + z * dpsi_dxdxdx)
|
|
1655
1638
|
|
|
1656
1639
|
# Calculate weight load at grid points
|
|
1657
|
-
qn = rho*self.g*np.cos(np.deg2rad(phi))
|
|
1640
|
+
qn = rho * self.g * np.cos(np.deg2rad(phi))
|
|
1658
1641
|
|
|
1659
1642
|
# Integrate dsxx_dxdx twice along z to obtain transverse
|
|
1660
1643
|
# normal stress Szz in MPa
|
|
@@ -1663,10 +1646,11 @@ class AnalysisMixin:
|
|
|
1663
1646
|
Szz += cumulative_trapezoid(-qn, zi, initial=0)[:, None]
|
|
1664
1647
|
|
|
1665
1648
|
# Return shear stress txz in specified unit
|
|
1666
|
-
return convert[unit]*Szz
|
|
1649
|
+
return convert[unit] * Szz
|
|
1667
1650
|
|
|
1668
|
-
def principal_stress_slab(
|
|
1669
|
-
|
|
1651
|
+
def principal_stress_slab(
|
|
1652
|
+
self, Z, phi, dz=2, unit="kPa", val="max", normalize=False
|
|
1653
|
+
):
|
|
1670
1654
|
"""
|
|
1671
1655
|
Compute maxium or minimum principal stress in slab layers.
|
|
1672
1656
|
|
|
@@ -1698,11 +1682,11 @@ class AnalysisMixin:
|
|
|
1698
1682
|
is requested.
|
|
1699
1683
|
"""
|
|
1700
1684
|
# Raise error if specified component is not available
|
|
1701
|
-
if val not in [
|
|
1702
|
-
raise ValueError(f
|
|
1685
|
+
if val not in ["min", "max"]:
|
|
1686
|
+
raise ValueError(f"Component {val} not defined.")
|
|
1703
1687
|
|
|
1704
1688
|
# Multiplier selection dict
|
|
1705
|
-
m = {
|
|
1689
|
+
m = {"max": 1, "min": -1}
|
|
1706
1690
|
|
|
1707
1691
|
# Get axial normal stresses, shear stresses, transverse normal stresses
|
|
1708
1692
|
Sxx = self.Sxx(Z=Z, phi=phi, dz=dz, unit=unit)
|
|
@@ -1710,24 +1694,25 @@ class AnalysisMixin:
|
|
|
1710
1694
|
Szz = self.Szz(Z=Z, phi=phi, dz=dz, unit=unit)
|
|
1711
1695
|
|
|
1712
1696
|
# Calculate principal stress
|
|
1713
|
-
Ps = (Sxx + Szz)/2 + m[val]*np.sqrt((Sxx - Szz)**2 + 4*Txz**2)/2
|
|
1697
|
+
Ps = (Sxx + Szz) / 2 + m[val] * np.sqrt((Sxx - Szz) ** 2 + 4 * Txz**2) / 2
|
|
1714
1698
|
|
|
1715
1699
|
# Raise error if normalization of compressive stresses is attempted
|
|
1716
|
-
if normalize and val ==
|
|
1717
|
-
raise ValueError(
|
|
1700
|
+
if normalize and val == "min":
|
|
1701
|
+
raise ValueError("Can only normlize tensile stresses.")
|
|
1718
1702
|
|
|
1719
1703
|
# Normalize tensile stresses to tensile strength
|
|
1720
|
-
if normalize and val ==
|
|
1704
|
+
if normalize and val == "max":
|
|
1721
1705
|
# Get layer densities
|
|
1722
1706
|
rho = self.get_zmesh(dz=dz)[:, 3]
|
|
1723
1707
|
# Normlize maximum principal stress to layers' tensile strength
|
|
1724
|
-
return Ps/tensile_strength_slab(rho, unit=unit)[:, None]
|
|
1708
|
+
return Ps / tensile_strength_slab(rho, unit=unit)[:, None]
|
|
1725
1709
|
|
|
1726
1710
|
# Return absolute principal stresses
|
|
1727
1711
|
return Ps
|
|
1728
1712
|
|
|
1729
|
-
def principal_stress_weaklayer(
|
|
1730
|
-
|
|
1713
|
+
def principal_stress_weaklayer(
|
|
1714
|
+
self, Z, sc=2.6, unit="kPa", val="min", normalize=False
|
|
1715
|
+
):
|
|
1731
1716
|
"""
|
|
1732
1717
|
Compute maxium or minimum principal stress in the weak layer.
|
|
1733
1718
|
|
|
@@ -1757,30 +1742,31 @@ class AnalysisMixin:
|
|
|
1757
1742
|
is requested.
|
|
1758
1743
|
"""
|
|
1759
1744
|
# Raise error if specified component is not available
|
|
1760
|
-
if val not in [
|
|
1761
|
-
raise ValueError(f
|
|
1745
|
+
if val not in ["min", "max"]:
|
|
1746
|
+
raise ValueError(f"Component {val} not defined.")
|
|
1762
1747
|
|
|
1763
1748
|
# Multiplier selection dict
|
|
1764
|
-
m = {
|
|
1749
|
+
m = {"max": 1, "min": -1}
|
|
1765
1750
|
|
|
1766
1751
|
# Get weak-layer normal and shear stresses
|
|
1767
1752
|
sig = self.sig(Z, unit=unit)
|
|
1768
1753
|
tau = self.tau(Z, unit=unit)
|
|
1769
1754
|
|
|
1770
1755
|
# Calculate principal stress
|
|
1771
|
-
ps = sig/2 + m[val]*np.sqrt(sig**2 + 4*tau**2)/2
|
|
1756
|
+
ps = sig / 2 + m[val] * np.sqrt(sig**2 + 4 * tau**2) / 2
|
|
1772
1757
|
|
|
1773
1758
|
# Raise error if normalization of tensile stresses is attempted
|
|
1774
|
-
if normalize and val ==
|
|
1775
|
-
raise ValueError(
|
|
1759
|
+
if normalize and val == "max":
|
|
1760
|
+
raise ValueError("Can only normlize compressive stresses.")
|
|
1776
1761
|
|
|
1777
1762
|
# Normalize compressive stresses to compressive strength
|
|
1778
|
-
if normalize and val ==
|
|
1779
|
-
return ps/sc
|
|
1763
|
+
if normalize and val == "min":
|
|
1764
|
+
return ps / sc
|
|
1780
1765
|
|
|
1781
1766
|
# Return absolute principal stresses
|
|
1782
1767
|
return ps
|
|
1783
1768
|
|
|
1769
|
+
|
|
1784
1770
|
class OutputMixin:
|
|
1785
1771
|
"""
|
|
1786
1772
|
Mixin for outputs.
|
|
@@ -1788,6 +1774,7 @@ class OutputMixin:
|
|
|
1788
1774
|
Provides convenience methods for the assembly of output lists
|
|
1789
1775
|
such as rasterized displacements or rasterized stresses.
|
|
1790
1776
|
"""
|
|
1777
|
+
|
|
1791
1778
|
def external_potential(self, C, phi, L, **segments):
|
|
1792
1779
|
"""
|
|
1793
1780
|
Compute total external potential (pst only).
|
|
@@ -1822,17 +1809,21 @@ class OutputMixin:
|
|
|
1822
1809
|
qt = self.calc_qt()
|
|
1823
1810
|
# use +/- and us[0]/us[-1] according to system and phi
|
|
1824
1811
|
# compute total external potential
|
|
1825
|
-
Pi_ext =
|
|
1826
|
-
-
|
|
1812
|
+
Pi_ext = (
|
|
1813
|
+
-qn * (segments["li"][0] + segments["li"][1]) * np.average(w0)
|
|
1814
|
+
- qn * (L - (segments["li"][0] + segments["li"][1])) * self.tc
|
|
1815
|
+
)
|
|
1827
1816
|
# Ensure
|
|
1828
|
-
if self.system in [
|
|
1817
|
+
if self.system in ["pst-"]:
|
|
1829
1818
|
ub = us[-1]
|
|
1830
|
-
elif self.system in [
|
|
1819
|
+
elif self.system in ["-pst"]:
|
|
1831
1820
|
ub = us[0]
|
|
1832
|
-
Pi_ext +=
|
|
1833
|
-
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1821
|
+
Pi_ext += (
|
|
1822
|
+
-qt * (segments["li"][0] + segments["li"][1]) * np.average(us)
|
|
1823
|
+
- qt * (L - (segments["li"][0] + segments["li"][1])) * ub
|
|
1824
|
+
)
|
|
1825
|
+
if self.system not in ["pst-", "-pst"]:
|
|
1826
|
+
print("Input error: Only pst-setup implemented at the moment.")
|
|
1836
1827
|
|
|
1837
1828
|
return Pi_ext
|
|
1838
1829
|
|
|
@@ -1871,31 +1862,37 @@ class OutputMixin:
|
|
|
1871
1862
|
|
|
1872
1863
|
# Compute weak layer displacements
|
|
1873
1864
|
wweak = self.w(zweak)
|
|
1874
|
-
uweak = self.u(zweak, z0=self.h/2)
|
|
1865
|
+
uweak = self.u(zweak, z0=self.h / 2)
|
|
1875
1866
|
|
|
1876
1867
|
# Compute stored energy of the slab (monte-carlo integration)
|
|
1877
1868
|
n = len(xq)
|
|
1878
1869
|
nweak = len(xweak)
|
|
1879
1870
|
# energy share from moment, shear force, wl normal and tangential springs
|
|
1880
|
-
Pi_int =
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1871
|
+
Pi_int = (
|
|
1872
|
+
L / 2 / n / self.A11 * np.sum([Ni**2 for Ni in N])
|
|
1873
|
+
+ L
|
|
1874
|
+
/ 2
|
|
1875
|
+
/ n
|
|
1876
|
+
/ (self.D11 - self.B11**2 / self.A11)
|
|
1877
|
+
* np.sum([Mi**2 for Mi in M])
|
|
1878
|
+
+ L / 2 / n / self.kA55 * np.sum([Vi**2 for Vi in V])
|
|
1879
|
+
+ L * self.kn / 2 / nweak * np.sum([wi**2 for wi in wweak])
|
|
1880
|
+
+ L * self.kt / 2 / nweak * np.sum([ui**2 for ui in uweak])
|
|
1881
|
+
)
|
|
1885
1882
|
# energy share from substitute rotation spring
|
|
1886
|
-
if self.system in [
|
|
1887
|
-
Pi_int += 1/2*M[-1]*(self.psi(zq)[-1])**2
|
|
1888
|
-
elif self.system in [
|
|
1889
|
-
Pi_int += 1/2*M[0]*(self.psi(zq)[0])**2
|
|
1883
|
+
if self.system in ["pst-"]:
|
|
1884
|
+
Pi_int += 1 / 2 * M[-1] * (self.psi(zq)[-1]) ** 2
|
|
1885
|
+
elif self.system in ["-pst"]:
|
|
1886
|
+
Pi_int += 1 / 2 * M[0] * (self.psi(zq)[0]) ** 2
|
|
1890
1887
|
else:
|
|
1891
|
-
print(
|
|
1888
|
+
print("Input error: Only pst-setup implemented at the moment.")
|
|
1892
1889
|
|
|
1893
1890
|
return Pi_int
|
|
1894
1891
|
|
|
1895
1892
|
def total_potential(self, C, phi, L, **segments):
|
|
1896
1893
|
"""
|
|
1897
1894
|
Returns total differential potential
|
|
1898
|
-
|
|
1895
|
+
|
|
1899
1896
|
Arguments
|
|
1900
1897
|
---------
|
|
1901
1898
|
C : ndarray
|
|
@@ -1920,7 +1917,7 @@ class OutputMixin:
|
|
|
1920
1917
|
|
|
1921
1918
|
return Pi_int + Pi_ext
|
|
1922
1919
|
|
|
1923
|
-
def get_weaklayer_shearstress(self, x, z, unit=
|
|
1920
|
+
def get_weaklayer_shearstress(self, x, z, unit="MPa", removeNaNs=False):
|
|
1924
1921
|
"""
|
|
1925
1922
|
Compute weak-layer shear stress.
|
|
1926
1923
|
|
|
@@ -1944,7 +1941,7 @@ class OutputMixin:
|
|
|
1944
1941
|
Normal stress (stress unit input).
|
|
1945
1942
|
"""
|
|
1946
1943
|
# Convert coordinates from mm to cm and stresses from MPa to unit
|
|
1947
|
-
x = x/10
|
|
1944
|
+
x = x / 10
|
|
1948
1945
|
tau = self.tau(z, unit=unit)
|
|
1949
1946
|
# Filter stresses in unspupported segments
|
|
1950
1947
|
if removeNaNs:
|
|
@@ -1957,7 +1954,7 @@ class OutputMixin:
|
|
|
1957
1954
|
|
|
1958
1955
|
return x, tau
|
|
1959
1956
|
|
|
1960
|
-
def get_weaklayer_normalstress(self, x, z, unit=
|
|
1957
|
+
def get_weaklayer_normalstress(self, x, z, unit="MPa", removeNaNs=False):
|
|
1961
1958
|
"""
|
|
1962
1959
|
Compute weak-layer normal stress.
|
|
1963
1960
|
|
|
@@ -1981,7 +1978,7 @@ class OutputMixin:
|
|
|
1981
1978
|
Normal stress (stress unit input).
|
|
1982
1979
|
"""
|
|
1983
1980
|
# Convert coordinates from mm to cm and stresses from MPa to unit
|
|
1984
|
-
x = x/10
|
|
1981
|
+
x = x / 10
|
|
1985
1982
|
sig = self.sig(z, unit=unit)
|
|
1986
1983
|
# Filter stresses in unspupported segments
|
|
1987
1984
|
if removeNaNs:
|
|
@@ -1994,7 +1991,7 @@ class OutputMixin:
|
|
|
1994
1991
|
|
|
1995
1992
|
return x, sig
|
|
1996
1993
|
|
|
1997
|
-
def get_slab_displacement(self, x, z, loc=
|
|
1994
|
+
def get_slab_displacement(self, x, z, loc="mid", unit="mm"):
|
|
1998
1995
|
"""
|
|
1999
1996
|
Compute horizontal slab displacement.
|
|
2000
1997
|
|
|
@@ -2019,15 +2016,15 @@ class OutputMixin:
|
|
|
2019
2016
|
Horizontal displacements (unit input).
|
|
2020
2017
|
"""
|
|
2021
2018
|
# Coordinates (cm)
|
|
2022
|
-
x = x/10
|
|
2019
|
+
x = x / 10
|
|
2023
2020
|
# Locator
|
|
2024
|
-
z0 = {
|
|
2021
|
+
z0 = {"top": -self.h / 2, "mid": 0, "bot": self.h / 2}
|
|
2025
2022
|
# Displacement (unit)
|
|
2026
2023
|
u = self.u(z, z0=z0[loc], unit=unit)
|
|
2027
2024
|
# Output array
|
|
2028
2025
|
return x, u
|
|
2029
2026
|
|
|
2030
|
-
def get_slab_deflection(self, x, z, unit=
|
|
2027
|
+
def get_slab_deflection(self, x, z, unit="mm"):
|
|
2031
2028
|
"""
|
|
2032
2029
|
Compute vertical slab displacement.
|
|
2033
2030
|
|
|
@@ -2050,13 +2047,13 @@ class OutputMixin:
|
|
|
2050
2047
|
Vertical deflections (unit input).
|
|
2051
2048
|
"""
|
|
2052
2049
|
# Coordinates (cm)
|
|
2053
|
-
x = x/10
|
|
2050
|
+
x = x / 10
|
|
2054
2051
|
# Deflection (unit)
|
|
2055
2052
|
w = self.w(z, unit=unit)
|
|
2056
2053
|
# Output array
|
|
2057
2054
|
return x, w
|
|
2058
2055
|
|
|
2059
|
-
def get_slab_rotation(self, x, z, unit=
|
|
2056
|
+
def get_slab_rotation(self, x, z, unit="degrees"):
|
|
2060
2057
|
"""
|
|
2061
2058
|
Compute slab cross-section rotation angle.
|
|
2062
2059
|
|
|
@@ -2079,7 +2076,7 @@ class OutputMixin:
|
|
|
2079
2076
|
Cross section rotations (unit input).
|
|
2080
2077
|
"""
|
|
2081
2078
|
# Coordinates (cm)
|
|
2082
|
-
x = x/10
|
|
2079
|
+
x = x / 10
|
|
2083
2080
|
# Cross-section rotation angle (unit)
|
|
2084
2081
|
psi = self.psi(z, unit=unit)
|
|
2085
2082
|
# Output array
|