weac 2.6.0__py3-none-any.whl → 2.6.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- weac/__init__.py +1 -1
- weac/mixins.py +426 -427
- weac/tools.py +80 -66
- {weac-2.6.0.dist-info → weac-2.6.1.dist-info}/METADATA +10 -7
- weac-2.6.1.dist-info/RECORD +12 -0
- weac-2.6.0.dist-info/RECORD +0 -12
- {weac-2.6.0.dist-info → weac-2.6.1.dist-info}/LICENSE +0 -0
- {weac-2.6.0.dist-info → weac-2.6.1.dist-info}/WHEEL +0 -0
- {weac-2.6.0.dist-info → weac-2.6.1.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
|
|
|
@@ -530,9 +528,9 @@ class SlabContactMixin:
|
|
|
530
528
|
"""
|
|
531
529
|
# subtract displacement under constact load from collapsed wl height
|
|
532
530
|
qn = self.calc_qn()
|
|
533
|
-
self.tc = cf*self.t - qn/self.kn
|
|
531
|
+
self.tc = cf * self.t - qn / self.kn
|
|
534
532
|
|
|
535
|
-
def set_phi(self,phi):
|
|
533
|
+
def set_phi(self, phi):
|
|
536
534
|
"""
|
|
537
535
|
Set inclination of the slab.
|
|
538
536
|
|
|
@@ -577,9 +575,9 @@ class SlabContactMixin:
|
|
|
577
575
|
"""
|
|
578
576
|
return self.get_weight_load(self.phi)[1] + self.get_surface_load(self.phi)[1]
|
|
579
577
|
|
|
580
|
-
def substitute_stiffness(self, L, support=
|
|
578
|
+
def substitute_stiffness(self, L, support="rested", dof="rot"):
|
|
581
579
|
"""
|
|
582
|
-
Calc substitute stiffness for beam on elastic foundation.
|
|
580
|
+
Calc substitute stiffness for beam on elastic foundation.
|
|
583
581
|
|
|
584
582
|
Arguments
|
|
585
583
|
---------
|
|
@@ -587,7 +585,7 @@ class SlabContactMixin:
|
|
|
587
585
|
Total length of the PST-column (mm).
|
|
588
586
|
support : string
|
|
589
587
|
Type of segment foundation. Defaults to 'rested'.
|
|
590
|
-
dof : string
|
|
588
|
+
dof : string
|
|
591
589
|
Type of substitute spring, either 'rot' or 'trans'. Defaults to 'rot'.
|
|
592
590
|
|
|
593
591
|
Returns
|
|
@@ -595,39 +593,40 @@ class SlabContactMixin:
|
|
|
595
593
|
k : stiffness of substitute spring.
|
|
596
594
|
"""
|
|
597
595
|
# adjust system to substitute system
|
|
598
|
-
if dof in [
|
|
596
|
+
if dof in ["rot"]:
|
|
599
597
|
tempsys = self.system
|
|
600
|
-
self.system =
|
|
601
|
-
if dof in [
|
|
598
|
+
self.system = "rot"
|
|
599
|
+
if dof in ["trans"]:
|
|
602
600
|
tempsys = self.system
|
|
603
|
-
self.system =
|
|
601
|
+
self.system = "trans"
|
|
604
602
|
|
|
605
603
|
# Change eigensystem for rested segment
|
|
606
|
-
if support in [
|
|
604
|
+
if support in ["rested"]:
|
|
607
605
|
tempkn = self.kn
|
|
608
606
|
tempkt = self.kt
|
|
609
|
-
self.kn = self.ratio*self.kn
|
|
610
|
-
self.kt = self.ratio*self.kt
|
|
607
|
+
self.kn = self.ratio * self.kn
|
|
608
|
+
self.kt = self.ratio * self.kt
|
|
611
609
|
self.calc_system_matrix()
|
|
612
610
|
self.calc_eigensystem()
|
|
613
611
|
|
|
614
612
|
# prepare list of segment characteristics
|
|
615
|
-
segments = {
|
|
616
|
-
|
|
617
|
-
|
|
613
|
+
segments = {
|
|
614
|
+
"li": np.array([L, 0.0]),
|
|
615
|
+
"mi": np.array([0]),
|
|
616
|
+
"ki": np.array([True, True]),
|
|
617
|
+
}
|
|
618
618
|
# solve system of equations
|
|
619
619
|
constants = self.assemble_and_solve(phi=0, **segments)
|
|
620
620
|
# calculate stiffness
|
|
621
|
-
_, z_pst, _ = self.rasterize_solution(
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
k = abs(1/self.w(z_pst)[0])
|
|
621
|
+
_, z_pst, _ = self.rasterize_solution(C=constants, phi=0, num=1, **segments)
|
|
622
|
+
if dof in ["rot"]:
|
|
623
|
+
k = abs(1 / self.psi(z_pst)[0])
|
|
624
|
+
if dof in ["trans"]:
|
|
625
|
+
k = abs(1 / self.w(z_pst)[0])
|
|
627
626
|
|
|
628
627
|
# Reset to previous system and eigensystem
|
|
629
628
|
self.system = tempsys
|
|
630
|
-
if support in [
|
|
629
|
+
if support in ["rested"]:
|
|
631
630
|
self.kn = tempkn
|
|
632
631
|
self.kt = tempkt
|
|
633
632
|
self.calc_system_matrix()
|
|
@@ -645,7 +644,7 @@ class SlabContactMixin:
|
|
|
645
644
|
Length of the crack for transition of stage A to stage B (mm).
|
|
646
645
|
"""
|
|
647
646
|
# Unpack variables
|
|
648
|
-
bs = -(self.B11**2/self.A11 - self.D11)
|
|
647
|
+
bs = -(self.B11**2 / self.A11 - self.D11)
|
|
649
648
|
ss = self.kA55
|
|
650
649
|
L = self.L
|
|
651
650
|
tc = self.tc
|
|
@@ -654,17 +653,17 @@ class SlabContactMixin:
|
|
|
654
653
|
# Create polynomial expression
|
|
655
654
|
def polynomial(x):
|
|
656
655
|
# 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
|
|
656
|
+
kRl = self.substitute_stiffness(L - x, "supported", "rot")
|
|
657
|
+
kNl = self.substitute_stiffness(L - x, "supported", "trans")
|
|
658
|
+
c1 = 1 / (8 * bs)
|
|
659
|
+
c2 = 1 / (2 * kRl)
|
|
660
|
+
c3 = 1 / (2 * ss)
|
|
661
|
+
c4 = 1 / kNl
|
|
662
|
+
c5 = -tc / qn
|
|
663
|
+
return c1 * x**4 + c2 * x**3 + c3 * x**2 + c4 * x + c5
|
|
665
664
|
|
|
666
665
|
# Find root
|
|
667
|
-
a1 = brentq(polynomial, L/1000, 999/1000*L)
|
|
666
|
+
a1 = brentq(polynomial, L / 1000, 999 / 1000 * L)
|
|
668
667
|
|
|
669
668
|
return a1
|
|
670
669
|
|
|
@@ -678,7 +677,7 @@ class SlabContactMixin:
|
|
|
678
677
|
Length of the crack for transition of stage B to stage C (mm).
|
|
679
678
|
"""
|
|
680
679
|
# Unpack variables
|
|
681
|
-
bs = -(self.B11**2/self.A11 - self.D11)
|
|
680
|
+
bs = -(self.B11**2 / self.A11 - self.D11)
|
|
682
681
|
ss = self.kA55
|
|
683
682
|
L = self.L
|
|
684
683
|
tc = self.tc
|
|
@@ -687,27 +686,21 @@ class SlabContactMixin:
|
|
|
687
686
|
# Create polynomial function
|
|
688
687
|
def polynomial(x):
|
|
689
688
|
# 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
|
|
689
|
+
kRl = self.substitute_stiffness(L - x, "supported", "rot")
|
|
690
|
+
kNl = self.substitute_stiffness(L - x, "supported", "trans")
|
|
691
|
+
c1 = ss**2 * kRl * kNl * qn
|
|
692
|
+
c2 = 6 * ss**2 * bs * kNl * qn
|
|
693
|
+
c3 = 30 * bs * ss * kRl * kNl * qn
|
|
694
|
+
c4 = 24 * bs * qn * (2 * ss**2 * kRl + 3 * bs * ss * kNl)
|
|
695
|
+
c5 = 72 * bs * (bs * qn * (ss**2 + kRl * kNl) - ss**2 * kRl * kNl * tc)
|
|
696
|
+
c6 = 144 * bs * ss * (bs * kRl * qn - bs * ss * kNl * tc)
|
|
697
|
+
c7 = -144 * bs**2 * ss * kRl * kNl * tc
|
|
698
|
+
return (
|
|
699
|
+
c1 * x**6 + c2 * x**5 + c3 * x**4 + c4 * x**3 + c5 * x**2 + c6 * x + c7
|
|
700
|
+
)
|
|
708
701
|
|
|
709
702
|
# Find root
|
|
710
|
-
a2 = brentq(polynomial, L/1000, 999/1000*L)
|
|
703
|
+
a2 = brentq(polynomial, L / 1000, 999 / 1000 * L)
|
|
711
704
|
|
|
712
705
|
return a2
|
|
713
706
|
|
|
@@ -732,7 +725,7 @@ class SlabContactMixin:
|
|
|
732
725
|
Calculate the length of the touchdown element in mode C.
|
|
733
726
|
"""
|
|
734
727
|
# Unpack variables
|
|
735
|
-
bs = -(self.B11**2/self.A11 - self.D11)
|
|
728
|
+
bs = -(self.B11**2 / self.A11 - self.D11)
|
|
736
729
|
ss = self.kA55
|
|
737
730
|
L = self.L
|
|
738
731
|
a = self.a
|
|
@@ -741,37 +734,41 @@ class SlabContactMixin:
|
|
|
741
734
|
|
|
742
735
|
def polynomial(x):
|
|
743
736
|
# Spring stiffness supported segment
|
|
744
|
-
kRl = self.substitute_stiffness(L-a,
|
|
745
|
-
kNl = self.substitute_stiffness(L-a,
|
|
737
|
+
kRl = self.substitute_stiffness(L - a, "supported", "rot")
|
|
738
|
+
kNl = self.substitute_stiffness(L - a, "supported", "trans")
|
|
746
739
|
# Spring stiffness rested segment
|
|
747
|
-
kRr = self.substitute_stiffness(a-x,
|
|
740
|
+
kRr = self.substitute_stiffness(a - x, "rested", "rot")
|
|
748
741
|
# 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
|
-
|
|
742
|
+
c1 = ss**2 * kRl * kNl * qn
|
|
743
|
+
c2 = 6 * ss * kNl * qn * (bs * ss + kRl * kRr)
|
|
744
|
+
c3 = 30 * bs * ss * kNl * qn * (kRl + kRr)
|
|
745
|
+
c4 = (
|
|
746
|
+
24
|
|
747
|
+
* bs
|
|
748
|
+
* qn
|
|
749
|
+
* (2 * ss**2 * kRl + 3 * bs * ss * kNl + 3 * kRl * kRr * kNl)
|
|
750
|
+
)
|
|
751
|
+
c5 = (
|
|
752
|
+
72
|
|
753
|
+
* bs
|
|
754
|
+
* (
|
|
755
|
+
bs * qn * (ss**2 + kNl * (kRl + kRr))
|
|
756
|
+
+ ss * kRl * (2 * kRr * qn - ss * kNl * tc)
|
|
757
|
+
)
|
|
758
|
+
)
|
|
759
|
+
c6 = (
|
|
760
|
+
144
|
|
761
|
+
* bs
|
|
762
|
+
* ss
|
|
763
|
+
* (bs * qn * (kRl + kRr) - kNl * tc * (bs * ss + kRl * kRr))
|
|
764
|
+
)
|
|
765
|
+
c7 = -144 * bs**2 * ss * kNl * tc * (kRl + kRr)
|
|
766
|
+
return (
|
|
767
|
+
c1 * x**6 + c2 * x**5 + c3 * x**4 + c4 * x**3 + c5 * x**2 + c6 * x + c7
|
|
768
|
+
)
|
|
772
769
|
|
|
773
770
|
# Find root
|
|
774
|
-
lC = brentq(polynomial, a/1000, 999/1000*a)
|
|
771
|
+
lC = brentq(polynomial, a / 1000, 999 / 1000 * a)
|
|
775
772
|
|
|
776
773
|
return lC
|
|
777
774
|
|
|
@@ -791,22 +788,22 @@ class SlabContactMixin:
|
|
|
791
788
|
a2 = self.calc_a2()
|
|
792
789
|
# Assign stage
|
|
793
790
|
if self.a <= a1:
|
|
794
|
-
mode =
|
|
791
|
+
mode = "A"
|
|
795
792
|
elif a1 < self.a <= a2:
|
|
796
|
-
mode =
|
|
793
|
+
mode = "B"
|
|
797
794
|
elif a2 < self.a:
|
|
798
|
-
mode =
|
|
795
|
+
mode = "C"
|
|
799
796
|
self.mode = mode
|
|
800
797
|
else:
|
|
801
|
-
self.mode =
|
|
798
|
+
self.mode = "A"
|
|
802
799
|
|
|
803
800
|
def calc_touchdown_length(self):
|
|
804
801
|
"""Calculate touchdown length"""
|
|
805
|
-
if self.mode in [
|
|
802
|
+
if self.mode in ["A"]:
|
|
806
803
|
self.td = self.calc_lA()
|
|
807
|
-
elif self.mode in [
|
|
804
|
+
elif self.mode in ["B"]:
|
|
808
805
|
self.td = self.calc_lB()
|
|
809
|
-
elif self.mode in [
|
|
806
|
+
elif self.mode in ["C"]:
|
|
810
807
|
self.td = self.calc_lC()
|
|
811
808
|
|
|
812
809
|
def calc_touchdown_system(self, L, a, cf, phi, ratio=1000):
|
|
@@ -815,6 +812,7 @@ class SlabContactMixin:
|
|
|
815
812
|
self.calc_touchdown_mode()
|
|
816
813
|
self.calc_touchdown_length()
|
|
817
814
|
|
|
815
|
+
|
|
818
816
|
class SolutionMixin:
|
|
819
817
|
"""
|
|
820
818
|
Mixin for the solution of boundary value problems.
|
|
@@ -823,7 +821,7 @@ class SolutionMixin:
|
|
|
823
821
|
and for the computation of the free constants.
|
|
824
822
|
"""
|
|
825
823
|
|
|
826
|
-
def bc(self, z, k=False, pos=
|
|
824
|
+
def bc(self, z, k=False, pos="mid"):
|
|
827
825
|
"""
|
|
828
826
|
Provide equations for free (pst) or infinite (skiers) ends.
|
|
829
827
|
|
|
@@ -849,77 +847,48 @@ class SolutionMixin:
|
|
|
849
847
|
"""
|
|
850
848
|
|
|
851
849
|
# Set boundary conditions for PST-systems
|
|
852
|
-
if self.system in [
|
|
850
|
+
if self.system in ["pst-", "-pst"]:
|
|
853
851
|
if not k:
|
|
854
|
-
if self.mode in [
|
|
852
|
+
if self.mode in ["A"]:
|
|
855
853
|
# 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']:
|
|
854
|
+
bc = np.array([self.N(z), self.M(z), self.V(z)])
|
|
855
|
+
elif self.mode in ["B"] and pos in ["r", "right"]:
|
|
861
856
|
# 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
|
|
857
|
+
bc = np.array([self.N(z), self.M(z), self.w(z)])
|
|
858
|
+
elif self.mode in ["B"] and pos in ["l", "left"]: # Kann dieser Block
|
|
867
859
|
# 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']:
|
|
860
|
+
bc = np.array([self.N(z), self.M(z), self.w(z)])
|
|
861
|
+
elif self.mode in ["C"] and pos in ["r", "right"]:
|
|
873
862
|
# Spring stiffness
|
|
874
|
-
kR = self.substitute_stiffness(self.a - self.td,
|
|
863
|
+
kR = self.substitute_stiffness(self.a - self.td, "rested", "rot")
|
|
875
864
|
# 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']:
|
|
865
|
+
bc = np.array([self.N(z), self.M(z) + kR * self.psi(z), self.w(z)])
|
|
866
|
+
elif self.mode in ["C"] and pos in ["l", "left"]:
|
|
881
867
|
# Spring stiffness
|
|
882
|
-
kR = self.substitute_stiffness(self.a - self.td,
|
|
868
|
+
kR = self.substitute_stiffness(self.a - self.td, "rested", "rot")
|
|
883
869
|
# Touchdown left
|
|
884
|
-
bc = np.array([self.N(z),
|
|
885
|
-
self.M(z) - kR*self.psi(z),
|
|
886
|
-
self.w(z)
|
|
887
|
-
])
|
|
870
|
+
bc = np.array([self.N(z), self.M(z) - kR * self.psi(z), self.w(z)])
|
|
888
871
|
else:
|
|
889
872
|
# Free end
|
|
890
|
-
bc = np.array([
|
|
891
|
-
self.N(z),
|
|
892
|
-
self.M(z),
|
|
893
|
-
self.V(z)
|
|
894
|
-
])
|
|
873
|
+
bc = np.array([self.N(z), self.M(z), self.V(z)])
|
|
895
874
|
# 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
|
-
])
|
|
875
|
+
elif self.system in ["-vpst", "vpst-"]:
|
|
876
|
+
bc = np.array([self.N(z), self.M(z), self.V(z)])
|
|
902
877
|
# Set boundary conditions for SKIER-systems
|
|
903
|
-
elif self.system in [
|
|
878
|
+
elif self.system in ["skier", "skiers"]:
|
|
904
879
|
# Infinite end (vanishing complementary solution)
|
|
905
|
-
bc = np.array([self.u(z, z0=0),
|
|
906
|
-
self.w(z),
|
|
907
|
-
self.psi(z)
|
|
908
|
-
])
|
|
880
|
+
bc = np.array([self.u(z, z0=0), self.w(z), self.psi(z)])
|
|
909
881
|
# 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
|
-
])
|
|
882
|
+
elif self.system in ["rot", "trans"]:
|
|
883
|
+
bc = np.array([self.N(z), self.M(z), self.V(z)])
|
|
915
884
|
else:
|
|
916
885
|
raise ValueError(
|
|
917
|
-
|
|
918
|
-
|
|
886
|
+
"Boundary conditions not defined for" f"system of type {self.system}."
|
|
887
|
+
)
|
|
919
888
|
|
|
920
889
|
return bc
|
|
921
890
|
|
|
922
|
-
def eqs(self, zl, zr, k=False, pos=
|
|
891
|
+
def eqs(self, zl, zr, k=False, pos="mid"):
|
|
923
892
|
"""
|
|
924
893
|
Provide boundary or transmission conditions for beam segments.
|
|
925
894
|
|
|
@@ -946,62 +915,75 @@ class SolutionMixin:
|
|
|
946
915
|
or vector of transmission conditions (of length 6+6)
|
|
947
916
|
for center segments.
|
|
948
917
|
"""
|
|
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
|
-
|
|
918
|
+
if pos in ("l", "left"):
|
|
919
|
+
eqs = np.array(
|
|
920
|
+
[
|
|
921
|
+
self.bc(zl, k, pos)[0], # Left boundary condition
|
|
922
|
+
self.bc(zl, k, pos)[1], # Left boundary condition
|
|
923
|
+
self.bc(zl, k, pos)[2], # Left boundary condition
|
|
924
|
+
self.u(zr, z0=0), # ui(xi = li)
|
|
925
|
+
self.w(zr), # wi(xi = li)
|
|
926
|
+
self.psi(zr), # psii(xi = li)
|
|
927
|
+
self.N(zr), # Ni(xi = li)
|
|
928
|
+
self.M(zr), # Mi(xi = li)
|
|
929
|
+
self.V(zr),
|
|
930
|
+
]
|
|
931
|
+
) # Vi(xi = li)
|
|
932
|
+
elif pos in ("m", "mid"):
|
|
933
|
+
eqs = np.array(
|
|
934
|
+
[
|
|
935
|
+
-self.u(zl, z0=0), # -ui(xi = 0)
|
|
936
|
+
-self.w(zl), # -wi(xi = 0)
|
|
937
|
+
-self.psi(zl), # -psii(xi = 0)
|
|
938
|
+
-self.N(zl), # -Ni(xi = 0)
|
|
939
|
+
-self.M(zl), # -Mi(xi = 0)
|
|
940
|
+
-self.V(zl), # -Vi(xi = 0)
|
|
941
|
+
self.u(zr, z0=0), # ui(xi = li)
|
|
942
|
+
self.w(zr), # wi(xi = li)
|
|
943
|
+
self.psi(zr), # psii(xi = li)
|
|
944
|
+
self.N(zr), # Ni(xi = li)
|
|
945
|
+
self.M(zr), # Mi(xi = li)
|
|
946
|
+
self.V(zr),
|
|
947
|
+
]
|
|
948
|
+
) # Vi(xi = li)
|
|
949
|
+
elif pos in ("r", "right"):
|
|
950
|
+
eqs = np.array(
|
|
951
|
+
[
|
|
952
|
+
-self.u(zl, z0=0), # -ui(xi = 0)
|
|
953
|
+
-self.w(zl), # -wi(xi = 0)
|
|
954
|
+
-self.psi(zl), # -psii(xi = 0)
|
|
955
|
+
-self.N(zl), # -Ni(xi = 0)
|
|
956
|
+
-self.M(zl), # -Mi(xi = 0)
|
|
957
|
+
-self.V(zl), # -Vi(xi = 0)
|
|
958
|
+
self.bc(zr, k, pos)[0], # Right boundary condition
|
|
959
|
+
self.bc(zr, k, pos)[1], # Right boundary condition
|
|
960
|
+
self.bc(zr, k, pos)[2],
|
|
961
|
+
]
|
|
962
|
+
) # Right boundary condition
|
|
985
963
|
else:
|
|
986
964
|
raise ValueError(
|
|
987
|
-
(
|
|
988
|
-
|
|
989
|
-
|
|
965
|
+
(
|
|
966
|
+
f"Invalid position argument {pos} given. "
|
|
967
|
+
"Valid segment positions are l, m, and r, "
|
|
968
|
+
"or left, mid and right."
|
|
969
|
+
)
|
|
970
|
+
)
|
|
990
971
|
return eqs
|
|
991
972
|
|
|
992
973
|
def calc_segments(
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
974
|
+
self,
|
|
975
|
+
li: list[float] | list[int] | bool = False,
|
|
976
|
+
mi: list[float] | list[int] | bool = False,
|
|
977
|
+
ki: list[bool] | bool = False,
|
|
978
|
+
k0: list[bool] | bool = False,
|
|
979
|
+
L: float = 1e4,
|
|
980
|
+
a: float = 0,
|
|
981
|
+
m: float = 0,
|
|
982
|
+
phi: float = 0,
|
|
983
|
+
cf: float = 0.5,
|
|
984
|
+
ratio: float = 1000,
|
|
985
|
+
**kwargs,
|
|
986
|
+
):
|
|
1005
987
|
"""
|
|
1006
988
|
Assemble lists defining the segments.
|
|
1007
989
|
|
|
@@ -1049,52 +1031,53 @@ class SolutionMixin:
|
|
|
1049
1031
|
in the cracked (ki) and uncracked (k0) configurations.
|
|
1050
1032
|
"""
|
|
1051
1033
|
|
|
1052
|
-
_ = kwargs
|
|
1034
|
+
_ = kwargs # Unused arguments
|
|
1053
1035
|
|
|
1054
1036
|
# Precompute touchdown properties
|
|
1055
1037
|
self.calc_touchdown_system(L=L, a=a, cf=cf, phi=phi, ratio=ratio)
|
|
1056
1038
|
|
|
1057
1039
|
# 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])
|
|
1040
|
+
if self.system == "skiers":
|
|
1041
|
+
li = np.array(li) # Segment lengths
|
|
1042
|
+
mi = np.array(mi) # Skier weights
|
|
1043
|
+
ki = np.array(ki) # Crack
|
|
1044
|
+
k0 = np.array(k0) # No crack
|
|
1045
|
+
elif self.system == "pst-":
|
|
1046
|
+
li = np.array([L - self.a, self.td]) # Segment lengths
|
|
1047
|
+
mi = np.array([0]) # Skier weights
|
|
1048
|
+
ki = np.array([True, False]) # Crack
|
|
1049
|
+
k0 = np.array([True, True]) # No crack
|
|
1050
|
+
elif self.system == "-pst":
|
|
1051
|
+
li = np.array([self.td, L - self.a]) # Segment lengths
|
|
1052
|
+
mi = np.array([0]) # Skier weights
|
|
1053
|
+
ki = np.array([False, True]) # Crack
|
|
1054
|
+
k0 = np.array([True, True]) # No crack
|
|
1055
|
+
elif self.system == "vpst-":
|
|
1056
|
+
li = np.array([L - a, a]) # Segment lengths
|
|
1057
|
+
mi = np.array([0]) # Skier weights
|
|
1058
|
+
ki = np.array([True, False]) # Crack
|
|
1059
|
+
k0 = np.array([True, True]) # No crack
|
|
1060
|
+
elif self.system == "-vpst":
|
|
1061
|
+
li = np.array([a, L - a]) # Segment lengths
|
|
1062
|
+
mi = np.array([0]) # Skier weights
|
|
1063
|
+
ki = np.array([False, True]) # Crack
|
|
1064
|
+
k0 = np.array([True, True]) # No crack
|
|
1065
|
+
elif self.system == "skier":
|
|
1066
|
+
lb = (L - self.a) / 2 # Half bedded length
|
|
1067
|
+
lf = self.a / 2 # Half free length
|
|
1068
|
+
li = np.array([lb, lf, lf, lb]) # Segment lengths
|
|
1069
|
+
mi = np.array([0, m, 0]) # Skier weights
|
|
1070
|
+
ki = np.array([True, False, False, True]) # Crack
|
|
1071
|
+
k0 = np.array([True, True, True, True]) # No crack
|
|
1090
1072
|
else:
|
|
1091
|
-
raise ValueError(f
|
|
1073
|
+
raise ValueError(f"System {self.system} is not implemented.")
|
|
1092
1074
|
|
|
1093
1075
|
# Fill dictionary
|
|
1094
1076
|
segments = {
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1077
|
+
"nocrack": {"li": li, "mi": mi, "ki": k0},
|
|
1078
|
+
"crack": {"li": li, "mi": mi, "ki": ki},
|
|
1079
|
+
"both": {"li": li, "mi": mi, "ki": ki, "k0": k0},
|
|
1080
|
+
}
|
|
1098
1081
|
return segments
|
|
1099
1082
|
|
|
1100
1083
|
def assemble_and_solve(self, phi, li, mi, ki):
|
|
@@ -1133,29 +1116,37 @@ class SolutionMixin:
|
|
|
1133
1116
|
|
|
1134
1117
|
# No foundation
|
|
1135
1118
|
if not any(ki):
|
|
1136
|
-
raise ValueError(
|
|
1119
|
+
raise ValueError("Provide at least one supported segment.")
|
|
1137
1120
|
# Mismatch of number of segements and transisions
|
|
1138
1121
|
if len(li) != len(ki) or len(li) - 1 != len(mi):
|
|
1139
|
-
raise ValueError(
|
|
1140
|
-
|
|
1122
|
+
raise ValueError(
|
|
1123
|
+
"Make sure len(li)=N, len(ki)=N, and "
|
|
1124
|
+
"len(mi)=N-1 for a system of N segments."
|
|
1125
|
+
)
|
|
1141
1126
|
|
|
1142
|
-
if self.system not in [
|
|
1127
|
+
if self.system not in ["pst-", "-pst", "vpst-", "-vpst", "rot", "trans"]:
|
|
1143
1128
|
# Boundary segments must be on foundation for infinite BCs
|
|
1144
1129
|
if not all([ki[0], ki[-1]]):
|
|
1145
|
-
raise ValueError(
|
|
1146
|
-
|
|
1130
|
+
raise ValueError(
|
|
1131
|
+
"Provide supported boundary segments in "
|
|
1132
|
+
"order to account for infinite extensions."
|
|
1133
|
+
)
|
|
1147
1134
|
# Make sure infinity boundary conditions are far enough from skiers
|
|
1148
1135
|
if li[0] < 5e3 or li[-1] < 5e3:
|
|
1149
|
-
print(
|
|
1150
|
-
|
|
1151
|
-
|
|
1136
|
+
print(
|
|
1137
|
+
(
|
|
1138
|
+
"WARNING: Boundary segments are short. Make sure "
|
|
1139
|
+
"the complementary solution has decayed to the "
|
|
1140
|
+
"boundaries."
|
|
1141
|
+
)
|
|
1142
|
+
)
|
|
1152
1143
|
|
|
1153
1144
|
# --- PREPROCESSING ---------------------------------------------------
|
|
1154
1145
|
|
|
1155
1146
|
# Determine size of linear system of equations
|
|
1156
|
-
nS = len(li)
|
|
1147
|
+
nS = len(li) # Number of beam segments
|
|
1157
1148
|
|
|
1158
|
-
nDOF = 6
|
|
1149
|
+
nDOF = 6 # Number of free constants per segment
|
|
1159
1150
|
|
|
1160
1151
|
# Add dummy segment if only one segment provided
|
|
1161
1152
|
if nS == 1:
|
|
@@ -1165,13 +1156,13 @@ class SolutionMixin:
|
|
|
1165
1156
|
nS = 2
|
|
1166
1157
|
|
|
1167
1158
|
# Assemble position vector
|
|
1168
|
-
pi = np.full(nS,
|
|
1169
|
-
pi[0], pi[-1] =
|
|
1159
|
+
pi = np.full(nS, "m")
|
|
1160
|
+
pi[0], pi[-1] = "l", "r"
|
|
1170
1161
|
|
|
1171
1162
|
# Initialize matrices
|
|
1172
|
-
zh0 = np.zeros([nS*6, nS*nDOF])
|
|
1173
|
-
zp0 = np.zeros([nS*6, 1])
|
|
1174
|
-
rhs = np.zeros([nS*6, 1])
|
|
1163
|
+
zh0 = np.zeros([nS * 6, nS * nDOF])
|
|
1164
|
+
zp0 = np.zeros([nS * 6, 1])
|
|
1165
|
+
rhs = np.zeros([nS * 6, 1])
|
|
1175
1166
|
|
|
1176
1167
|
# --- ASSEMBLE LINEAR SYSTEM OF EQUATIONS -----------------------------
|
|
1177
1168
|
|
|
@@ -1181,72 +1172,73 @@ class SolutionMixin:
|
|
|
1181
1172
|
l, k, pos = li[i], ki[i], pi[i]
|
|
1182
1173
|
# Transmission conditions at left and right segment ends
|
|
1183
1174
|
zhi = self.eqs(
|
|
1184
|
-
zl=self.zh(x=0, l=l, bed=k),
|
|
1185
|
-
|
|
1186
|
-
k=k, pos=pos)
|
|
1175
|
+
zl=self.zh(x=0, l=l, bed=k), zr=self.zh(x=l, l=l, bed=k), k=k, pos=pos
|
|
1176
|
+
)
|
|
1187
1177
|
zpi = self.eqs(
|
|
1188
1178
|
zl=self.zp(x=0, phi=phi, bed=k),
|
|
1189
1179
|
zr=self.zp(x=l, phi=phi, bed=k),
|
|
1190
|
-
k=k,
|
|
1180
|
+
k=k,
|
|
1181
|
+
pos=pos,
|
|
1182
|
+
)
|
|
1191
1183
|
# Rows for left-hand side assembly
|
|
1192
1184
|
start = 0 if i == 0 else 3
|
|
1193
1185
|
stop = 6 if i == nS - 1 else 9
|
|
1194
1186
|
# 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
|
|
1187
|
+
zh0[(6 * i - start) : (6 * i + stop), i * nDOF : (i + 1) * nDOF] = zhi
|
|
1188
|
+
zp0[(6 * i - start) : (6 * i + stop)] += zpi
|
|
1197
1189
|
|
|
1198
1190
|
# Loop through loads to assemble right-hand side
|
|
1199
1191
|
for i, m in enumerate(mi, start=1):
|
|
1200
1192
|
# Get skier loads
|
|
1201
1193
|
Fn, Ft = self.get_skier_load(m, phi)
|
|
1202
1194
|
# 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])
|
|
1195
|
+
rhs[6 * i : 6 * i + 3] = np.vstack([Ft, -Ft * self.h / 2, Fn])
|
|
1204
1196
|
# Set rhs so that complementary integral vanishes at boundaries
|
|
1205
|
-
if self.system not in [
|
|
1197
|
+
if self.system not in ["pst-", "-pst", "rested"]:
|
|
1206
1198
|
rhs[:3] = self.bc(self.zp(x=0, phi=phi, bed=ki[0]))
|
|
1207
1199
|
rhs[-3:] = self.bc(self.zp(x=li[-1], phi=phi, bed=ki[-1]))
|
|
1208
1200
|
|
|
1209
1201
|
# Set rhs for vertical faces
|
|
1210
|
-
if self.system in [
|
|
1202
|
+
if self.system in ["vpst-", "-vpst"]:
|
|
1211
1203
|
# Calculate center of gravity and mass of
|
|
1212
1204
|
# added or cut off slab segement
|
|
1213
1205
|
xs, zs, m = calc_vertical_bc_center_of_gravity(self.slab, phi)
|
|
1214
1206
|
# Convert slope angle to radians
|
|
1215
1207
|
phi = np.deg2rad(phi)
|
|
1216
1208
|
# 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)
|
|
1209
|
+
N = -self.g * m * np.sin(phi)
|
|
1210
|
+
M = -self.g * m * (xs * np.cos(phi) + zs * np.sin(phi))
|
|
1211
|
+
V = self.g * m * np.cos(phi)
|
|
1220
1212
|
# Add to right-hand side
|
|
1221
|
-
rhs[:3] = np.vstack([N, M, V])
|
|
1222
|
-
rhs[-3:] = np.vstack([N, M, V])
|
|
1213
|
+
rhs[:3] = np.vstack([N, M, V]) # left end
|
|
1214
|
+
rhs[-3:] = np.vstack([N, M, V]) # right end
|
|
1223
1215
|
|
|
1224
1216
|
# Loop through segments to set touchdown conditions at rhs
|
|
1225
1217
|
for i in range(nS):
|
|
1226
1218
|
# Length, foundation and position of segment i
|
|
1227
1219
|
l, k, pos = li[i], ki[i], pi[i]
|
|
1228
1220
|
# 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])
|
|
1221
|
+
if not k and bool(self.mode in ["B"]):
|
|
1222
|
+
if i == 0:
|
|
1223
|
+
rhs[:3] = np.vstack([0, 0, self.tc])
|
|
1232
1224
|
if i == (nS - 1):
|
|
1233
|
-
rhs[-3:] = np.vstack([0,0,self.tc])
|
|
1225
|
+
rhs[-3:] = np.vstack([0, 0, self.tc])
|
|
1234
1226
|
# 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])
|
|
1227
|
+
if not k and bool(self.mode in ["C"]):
|
|
1228
|
+
N = self.calc_qt() * (self.a - self.td)
|
|
1229
|
+
if i == 0:
|
|
1230
|
+
rhs[:3] = np.vstack([-N, 0, self.tc])
|
|
1239
1231
|
if i == (nS - 1):
|
|
1240
|
-
rhs[-3:] = np.vstack([N,0,self.tc])
|
|
1232
|
+
rhs[-3:] = np.vstack([N, 0, self.tc])
|
|
1241
1233
|
|
|
1242
1234
|
# Rhs for substitute spring stiffness
|
|
1243
|
-
if self.system in [
|
|
1235
|
+
if self.system in ["rot"]:
|
|
1244
1236
|
# apply arbitrary moment of 1 at left boundary
|
|
1245
|
-
rhs = rhs*0
|
|
1237
|
+
rhs = rhs * 0
|
|
1246
1238
|
rhs[1] = 1
|
|
1247
|
-
if self.system in [
|
|
1239
|
+
if self.system in ["trans"]:
|
|
1248
1240
|
# apply arbitrary force of 1 at left boundary
|
|
1249
|
-
rhs = rhs*0
|
|
1241
|
+
rhs = rhs * 0
|
|
1250
1242
|
rhs[2] = 1
|
|
1251
1243
|
|
|
1252
1244
|
# --- SOLVE -----------------------------------------------------------
|
|
@@ -1266,13 +1258,14 @@ class AnalysisMixin:
|
|
|
1266
1258
|
"""
|
|
1267
1259
|
|
|
1268
1260
|
def rasterize_solution(
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1261
|
+
self,
|
|
1262
|
+
C: np.ndarray,
|
|
1263
|
+
phi: float,
|
|
1264
|
+
li: list[float] | bool,
|
|
1265
|
+
ki: list[bool] | bool,
|
|
1266
|
+
num: int = 250,
|
|
1267
|
+
**kwargs,
|
|
1268
|
+
):
|
|
1276
1269
|
"""
|
|
1277
1270
|
Compute rasterized solution vector.
|
|
1278
1271
|
|
|
@@ -1310,7 +1303,7 @@ class AnalysisMixin:
|
|
|
1310
1303
|
C, ki, li = C[:, isnonzero], ki[isnonzero], li[isnonzero]
|
|
1311
1304
|
|
|
1312
1305
|
# Compute number of plot points per segment (+1 for last segment)
|
|
1313
|
-
nq = np.ceil(li/li.sum()*num).astype(
|
|
1306
|
+
nq = np.ceil(li / li.sum() * num).astype("int")
|
|
1314
1307
|
nq[-1] += 1
|
|
1315
1308
|
|
|
1316
1309
|
# Provide cumulated length and plot point lists
|
|
@@ -1329,14 +1322,14 @@ class AnalysisMixin:
|
|
|
1329
1322
|
# Compute start and end coordinates of segment i
|
|
1330
1323
|
x0 = lic[i]
|
|
1331
1324
|
# Assemble global coordinate vector
|
|
1332
|
-
xq[nqc[i]:nqc[i + 1]] = x0 + xi
|
|
1325
|
+
xq[nqc[i] : nqc[i + 1]] = x0 + xi
|
|
1333
1326
|
# Mask coordinates not on foundation (including endpoints)
|
|
1334
1327
|
if not ki[i]:
|
|
1335
|
-
issupported[nqc[i]:nqc[i + 1]] = False
|
|
1328
|
+
issupported[nqc[i] : nqc[i + 1]] = False
|
|
1336
1329
|
# Compute segment solution
|
|
1337
1330
|
zi = self.z(xi, C[:, [i]], l, phi, ki[i])
|
|
1338
1331
|
# Assemble global solution matrix
|
|
1339
|
-
zq[:, nqc[i]:nqc[i + 1]] = zi
|
|
1332
|
+
zq[:, nqc[i] : nqc[i + 1]] = zi
|
|
1340
1333
|
|
|
1341
1334
|
# Make sure cracktips are included
|
|
1342
1335
|
transmissionbool = [ki[j] or ki[j + 1] for j, _ in enumerate(ki[:-1])]
|
|
@@ -1391,7 +1384,6 @@ class AnalysisMixin:
|
|
|
1391
1384
|
|
|
1392
1385
|
# Loop through segments with crack advance
|
|
1393
1386
|
for j, l in enumerate(li):
|
|
1394
|
-
|
|
1395
1387
|
# Uncracked (0) and cracked (1) solutions at integration points
|
|
1396
1388
|
z0 = partial(self.z, C=C0[:, [j]], l=l, phi=phi, bed=True)
|
|
1397
1389
|
z1 = partial(self.z, C=C1[:, [j]], l=l, phi=phi, bed=False)
|
|
@@ -1401,14 +1393,12 @@ class AnalysisMixin:
|
|
|
1401
1393
|
int2 = partial(self.int2, z0=z0, z1=z1)
|
|
1402
1394
|
|
|
1403
1395
|
# 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)
|
|
1396
|
+
Ginc1 += quad(int1, 0, l, epsabs=self.tol, epsrel=self.tol)[0] / (2 * da)
|
|
1397
|
+
Ginc2 += quad(int2, 0, l, epsabs=self.tol, epsrel=self.tol)[0] / (2 * da)
|
|
1408
1398
|
|
|
1409
1399
|
return np.array([Ginc1 + Ginc2, Ginc1, Ginc2]).flatten()
|
|
1410
1400
|
|
|
1411
|
-
def gdif(self, C, phi, li, ki, unit=
|
|
1401
|
+
def gdif(self, C, phi, li, ki, unit="kJ/m^2", **kwargs):
|
|
1412
1402
|
"""
|
|
1413
1403
|
Compute differential energy release rate of all crack tips.
|
|
1414
1404
|
|
|
@@ -1451,16 +1441,18 @@ class AnalysisMixin:
|
|
|
1451
1441
|
# Solution at crack tip
|
|
1452
1442
|
z = self.z(li[idx], C[:, [idx]], li[idx], phi, bed=ki[idx])
|
|
1453
1443
|
# Mode I and II differential energy release rates
|
|
1454
|
-
Gdif[1:, j] = np.concatenate(
|
|
1444
|
+
Gdif[1:, j] = np.concatenate(
|
|
1445
|
+
(self.Gi(z, unit=unit), self.Gii(z, unit=unit))
|
|
1446
|
+
)
|
|
1455
1447
|
|
|
1456
1448
|
# Sum mode I and II contributions
|
|
1457
1449
|
Gdif[0, :] = Gdif[1, :] + Gdif[2, :]
|
|
1458
1450
|
|
|
1459
1451
|
# Adjust contributions for center cracks
|
|
1460
1452
|
if nct > 1:
|
|
1461
|
-
avgmask = np.full(nct, True)
|
|
1453
|
+
avgmask = np.full(nct, True) # Initialize mask
|
|
1462
1454
|
avgmask[[0, -1]] = ki[[0, -1]] # Do not weight edge cracks
|
|
1463
|
-
Gdif[:, avgmask] *= 0.5
|
|
1455
|
+
Gdif[:, avgmask] *= 0.5 # Weigth with half crack length
|
|
1464
1456
|
|
|
1465
1457
|
# Return total differential energy release rate of all crack tips
|
|
1466
1458
|
return Gdif.sum(axis=1)
|
|
@@ -1486,18 +1478,20 @@ class AnalysisMixin:
|
|
|
1486
1478
|
# Get ply (layer) coordinates
|
|
1487
1479
|
z = self.get_ply_coordinates()
|
|
1488
1480
|
# Compute number of grid points per layer
|
|
1489
|
-
nlayer = np.ceil((z[1:] - z[:-1])/dz).astype(np.int32) + 1
|
|
1481
|
+
nlayer = np.ceil((z[1:] - z[:-1]) / dz).astype(np.int32) + 1
|
|
1490
1482
|
# Calculate grid points as list of z-coordinates (mm)
|
|
1491
|
-
zi = np.hstack(
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1483
|
+
zi = np.hstack(
|
|
1484
|
+
[
|
|
1485
|
+
np.linspace(z[i], z[i + 1], n, endpoint=True)
|
|
1486
|
+
for i, n in enumerate(nlayer)
|
|
1487
|
+
]
|
|
1488
|
+
)
|
|
1495
1489
|
# Get lists of corresponding elastic properties (E, nu, rho)
|
|
1496
1490
|
si = np.repeat(self.slab[:, [2, 4, 0]], nlayer, axis=0)
|
|
1497
1491
|
# Assemble mesh with columns (z, E, G, nu)
|
|
1498
1492
|
return np.column_stack([zi, si])
|
|
1499
1493
|
|
|
1500
|
-
def Sxx(self, Z, phi, dz=2, unit=
|
|
1494
|
+
def Sxx(self, Z, phi, dz=2, unit="kPa"):
|
|
1501
1495
|
"""
|
|
1502
1496
|
Compute axial normal stress in slab layers.
|
|
1503
1497
|
|
|
@@ -1518,15 +1512,12 @@ class AnalysisMixin:
|
|
|
1518
1512
|
Axial slab normal stress in specified unit.
|
|
1519
1513
|
"""
|
|
1520
1514
|
# Unit conversion dict
|
|
1521
|
-
convert = {
|
|
1522
|
-
'kPa': 1e3,
|
|
1523
|
-
'MPa': 1
|
|
1524
|
-
}
|
|
1515
|
+
convert = {"kPa": 1e3, "MPa": 1}
|
|
1525
1516
|
|
|
1526
1517
|
# Get mesh along z-axis
|
|
1527
1518
|
zmesh = self.get_zmesh(dz=dz)
|
|
1528
1519
|
zi = zmesh[:, 0]
|
|
1529
|
-
rho = 1e-12*zmesh[:, 3]
|
|
1520
|
+
rho = 1e-12 * zmesh[:, 3]
|
|
1530
1521
|
|
|
1531
1522
|
# Get dimensions of stress field (n rows, m columns)
|
|
1532
1523
|
n = zmesh.shape[0]
|
|
@@ -1537,18 +1528,18 @@ class AnalysisMixin:
|
|
|
1537
1528
|
|
|
1538
1529
|
# Compute axial normal stress Sxx at grid points in MPa
|
|
1539
1530
|
for i, (z, E, nu, _) in enumerate(zmesh):
|
|
1540
|
-
Sxx[i, :] = E/(1-nu**2)*self.du_dx(Z, z)
|
|
1531
|
+
Sxx[i, :] = E / (1 - nu**2) * self.du_dx(Z, z)
|
|
1541
1532
|
|
|
1542
1533
|
# Calculate weight load at grid points and superimpose on stress field
|
|
1543
|
-
qt = -rho*self.g*np.sin(np.deg2rad(phi))
|
|
1534
|
+
qt = -rho * self.g * np.sin(np.deg2rad(phi))
|
|
1544
1535
|
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])
|
|
1536
|
+
Sxx[i, :] += qi * (zi[i + 1] - zi[i])
|
|
1537
|
+
Sxx[-1, :] += qt[-1] * (zi[-1] - zi[-2])
|
|
1547
1538
|
|
|
1548
1539
|
# Return axial normal stress in specified unit
|
|
1549
|
-
return convert[unit]*Sxx
|
|
1540
|
+
return convert[unit] * Sxx
|
|
1550
1541
|
|
|
1551
|
-
def Txz(self, Z, phi, dz=2, unit=
|
|
1542
|
+
def Txz(self, Z, phi, dz=2, unit="kPa"):
|
|
1552
1543
|
"""
|
|
1553
1544
|
Compute shear stress in slab layers.
|
|
1554
1545
|
|
|
@@ -1569,14 +1560,11 @@ class AnalysisMixin:
|
|
|
1569
1560
|
Shear stress at grid points in the slab in specified unit.
|
|
1570
1561
|
"""
|
|
1571
1562
|
# Unit conversion dict
|
|
1572
|
-
convert = {
|
|
1573
|
-
'kPa': 1e3,
|
|
1574
|
-
'MPa': 1
|
|
1575
|
-
}
|
|
1563
|
+
convert = {"kPa": 1e3, "MPa": 1}
|
|
1576
1564
|
# Get mesh along z-axis
|
|
1577
1565
|
zmesh = self.get_zmesh(dz=dz)
|
|
1578
1566
|
zi = zmesh[:, 0]
|
|
1579
|
-
rho = 1e-12*zmesh[:, 3]
|
|
1567
|
+
rho = 1e-12 * zmesh[:, 3]
|
|
1580
1568
|
|
|
1581
1569
|
# Get dimensions of stress field (n rows, m columns)
|
|
1582
1570
|
n = zmesh.shape[0]
|
|
@@ -1592,10 +1580,10 @@ class AnalysisMixin:
|
|
|
1592
1580
|
|
|
1593
1581
|
# Calculate first derivative of sxx at z-grid points
|
|
1594
1582
|
for i, (z, E, nu, _) in enumerate(zmesh):
|
|
1595
|
-
dsxx_dx[i, :] = E/(1-nu**2)*(du0_dxdx + z*dpsi_dxdx)
|
|
1583
|
+
dsxx_dx[i, :] = E / (1 - nu**2) * (du0_dxdx + z * dpsi_dxdx)
|
|
1596
1584
|
|
|
1597
1585
|
# Calculate weight load at grid points
|
|
1598
|
-
qt = -rho*self.g*np.sin(np.deg2rad(phi))
|
|
1586
|
+
qt = -rho * self.g * np.sin(np.deg2rad(phi))
|
|
1599
1587
|
|
|
1600
1588
|
# Integrate -dsxx_dx along z and add cumulative weight load
|
|
1601
1589
|
# to obtain shear stress Txz in MPa
|
|
@@ -1603,9 +1591,9 @@ class AnalysisMixin:
|
|
|
1603
1591
|
Txz += cumulative_trapezoid(qt, zi, initial=0)[:, None]
|
|
1604
1592
|
|
|
1605
1593
|
# Return shear stress Txz in specified unit
|
|
1606
|
-
return convert[unit]*Txz
|
|
1594
|
+
return convert[unit] * Txz
|
|
1607
1595
|
|
|
1608
|
-
def Szz(self, Z, phi, dz=2, unit=
|
|
1596
|
+
def Szz(self, Z, phi, dz=2, unit="kPa"):
|
|
1609
1597
|
"""
|
|
1610
1598
|
Compute transverse normal stress in slab layers.
|
|
1611
1599
|
|
|
@@ -1627,15 +1615,12 @@ class AnalysisMixin:
|
|
|
1627
1615
|
specified unit.
|
|
1628
1616
|
"""
|
|
1629
1617
|
# Unit conversion dict
|
|
1630
|
-
convert = {
|
|
1631
|
-
'kPa': 1e3,
|
|
1632
|
-
'MPa': 1
|
|
1633
|
-
}
|
|
1618
|
+
convert = {"kPa": 1e3, "MPa": 1}
|
|
1634
1619
|
|
|
1635
1620
|
# Get mesh along z-axis
|
|
1636
1621
|
zmesh = self.get_zmesh(dz=dz)
|
|
1637
1622
|
zi = zmesh[:, 0]
|
|
1638
|
-
rho = 1e-12*zmesh[:, 3]
|
|
1623
|
+
rho = 1e-12 * zmesh[:, 3]
|
|
1639
1624
|
|
|
1640
1625
|
# Get dimensions of stress field (n rows, m columns)
|
|
1641
1626
|
n = zmesh.shape[0]
|
|
@@ -1651,10 +1636,10 @@ class AnalysisMixin:
|
|
|
1651
1636
|
|
|
1652
1637
|
# Calculate second derivative of sxx at z-grid points
|
|
1653
1638
|
for i, (z, E, nu, _) in enumerate(zmesh):
|
|
1654
|
-
dsxx_dxdx[i, :] = E/(1-nu**2)*(du0_dxdxdx + z*dpsi_dxdxdx)
|
|
1639
|
+
dsxx_dxdx[i, :] = E / (1 - nu**2) * (du0_dxdxdx + z * dpsi_dxdxdx)
|
|
1655
1640
|
|
|
1656
1641
|
# Calculate weight load at grid points
|
|
1657
|
-
qn = rho*self.g*np.cos(np.deg2rad(phi))
|
|
1642
|
+
qn = rho * self.g * np.cos(np.deg2rad(phi))
|
|
1658
1643
|
|
|
1659
1644
|
# Integrate dsxx_dxdx twice along z to obtain transverse
|
|
1660
1645
|
# normal stress Szz in MPa
|
|
@@ -1663,10 +1648,11 @@ class AnalysisMixin:
|
|
|
1663
1648
|
Szz += cumulative_trapezoid(-qn, zi, initial=0)[:, None]
|
|
1664
1649
|
|
|
1665
1650
|
# Return shear stress txz in specified unit
|
|
1666
|
-
return convert[unit]*Szz
|
|
1651
|
+
return convert[unit] * Szz
|
|
1667
1652
|
|
|
1668
|
-
def principal_stress_slab(
|
|
1669
|
-
|
|
1653
|
+
def principal_stress_slab(
|
|
1654
|
+
self, Z, phi, dz=2, unit="kPa", val="max", normalize=False
|
|
1655
|
+
):
|
|
1670
1656
|
"""
|
|
1671
1657
|
Compute maxium or minimum principal stress in slab layers.
|
|
1672
1658
|
|
|
@@ -1698,11 +1684,11 @@ class AnalysisMixin:
|
|
|
1698
1684
|
is requested.
|
|
1699
1685
|
"""
|
|
1700
1686
|
# Raise error if specified component is not available
|
|
1701
|
-
if val not in [
|
|
1702
|
-
raise ValueError(f
|
|
1687
|
+
if val not in ["min", "max"]:
|
|
1688
|
+
raise ValueError(f"Component {val} not defined.")
|
|
1703
1689
|
|
|
1704
1690
|
# Multiplier selection dict
|
|
1705
|
-
m = {
|
|
1691
|
+
m = {"max": 1, "min": -1}
|
|
1706
1692
|
|
|
1707
1693
|
# Get axial normal stresses, shear stresses, transverse normal stresses
|
|
1708
1694
|
Sxx = self.Sxx(Z=Z, phi=phi, dz=dz, unit=unit)
|
|
@@ -1710,24 +1696,25 @@ class AnalysisMixin:
|
|
|
1710
1696
|
Szz = self.Szz(Z=Z, phi=phi, dz=dz, unit=unit)
|
|
1711
1697
|
|
|
1712
1698
|
# Calculate principal stress
|
|
1713
|
-
Ps = (Sxx + Szz)/2 + m[val]*np.sqrt((Sxx - Szz)**2 + 4*Txz**2)/2
|
|
1699
|
+
Ps = (Sxx + Szz) / 2 + m[val] * np.sqrt((Sxx - Szz) ** 2 + 4 * Txz**2) / 2
|
|
1714
1700
|
|
|
1715
1701
|
# Raise error if normalization of compressive stresses is attempted
|
|
1716
|
-
if normalize and val ==
|
|
1717
|
-
raise ValueError(
|
|
1702
|
+
if normalize and val == "min":
|
|
1703
|
+
raise ValueError("Can only normlize tensile stresses.")
|
|
1718
1704
|
|
|
1719
1705
|
# Normalize tensile stresses to tensile strength
|
|
1720
|
-
if normalize and val ==
|
|
1706
|
+
if normalize and val == "max":
|
|
1721
1707
|
# Get layer densities
|
|
1722
1708
|
rho = self.get_zmesh(dz=dz)[:, 3]
|
|
1723
1709
|
# Normlize maximum principal stress to layers' tensile strength
|
|
1724
|
-
return Ps/tensile_strength_slab(rho, unit=unit)[:, None]
|
|
1710
|
+
return Ps / tensile_strength_slab(rho, unit=unit)[:, None]
|
|
1725
1711
|
|
|
1726
1712
|
# Return absolute principal stresses
|
|
1727
1713
|
return Ps
|
|
1728
1714
|
|
|
1729
|
-
def principal_stress_weaklayer(
|
|
1730
|
-
|
|
1715
|
+
def principal_stress_weaklayer(
|
|
1716
|
+
self, Z, sc=2.6, unit="kPa", val="min", normalize=False
|
|
1717
|
+
):
|
|
1731
1718
|
"""
|
|
1732
1719
|
Compute maxium or minimum principal stress in the weak layer.
|
|
1733
1720
|
|
|
@@ -1757,30 +1744,31 @@ class AnalysisMixin:
|
|
|
1757
1744
|
is requested.
|
|
1758
1745
|
"""
|
|
1759
1746
|
# Raise error if specified component is not available
|
|
1760
|
-
if val not in [
|
|
1761
|
-
raise ValueError(f
|
|
1747
|
+
if val not in ["min", "max"]:
|
|
1748
|
+
raise ValueError(f"Component {val} not defined.")
|
|
1762
1749
|
|
|
1763
1750
|
# Multiplier selection dict
|
|
1764
|
-
m = {
|
|
1751
|
+
m = {"max": 1, "min": -1}
|
|
1765
1752
|
|
|
1766
1753
|
# Get weak-layer normal and shear stresses
|
|
1767
1754
|
sig = self.sig(Z, unit=unit)
|
|
1768
1755
|
tau = self.tau(Z, unit=unit)
|
|
1769
1756
|
|
|
1770
1757
|
# Calculate principal stress
|
|
1771
|
-
ps = sig/2 + m[val]*np.sqrt(sig**2 + 4*tau**2)/2
|
|
1758
|
+
ps = sig / 2 + m[val] * np.sqrt(sig**2 + 4 * tau**2) / 2
|
|
1772
1759
|
|
|
1773
1760
|
# Raise error if normalization of tensile stresses is attempted
|
|
1774
|
-
if normalize and val ==
|
|
1775
|
-
raise ValueError(
|
|
1761
|
+
if normalize and val == "max":
|
|
1762
|
+
raise ValueError("Can only normlize compressive stresses.")
|
|
1776
1763
|
|
|
1777
1764
|
# Normalize compressive stresses to compressive strength
|
|
1778
|
-
if normalize and val ==
|
|
1779
|
-
return ps/sc
|
|
1765
|
+
if normalize and val == "min":
|
|
1766
|
+
return ps / sc
|
|
1780
1767
|
|
|
1781
1768
|
# Return absolute principal stresses
|
|
1782
1769
|
return ps
|
|
1783
1770
|
|
|
1771
|
+
|
|
1784
1772
|
class OutputMixin:
|
|
1785
1773
|
"""
|
|
1786
1774
|
Mixin for outputs.
|
|
@@ -1788,6 +1776,7 @@ class OutputMixin:
|
|
|
1788
1776
|
Provides convenience methods for the assembly of output lists
|
|
1789
1777
|
such as rasterized displacements or rasterized stresses.
|
|
1790
1778
|
"""
|
|
1779
|
+
|
|
1791
1780
|
def external_potential(self, C, phi, L, **segments):
|
|
1792
1781
|
"""
|
|
1793
1782
|
Compute total external potential (pst only).
|
|
@@ -1822,17 +1811,21 @@ class OutputMixin:
|
|
|
1822
1811
|
qt = self.calc_qt()
|
|
1823
1812
|
# use +/- and us[0]/us[-1] according to system and phi
|
|
1824
1813
|
# compute total external potential
|
|
1825
|
-
Pi_ext =
|
|
1826
|
-
-
|
|
1814
|
+
Pi_ext = (
|
|
1815
|
+
-qn * (segments["li"][0] + segments["li"][1]) * np.average(w0)
|
|
1816
|
+
- qn * (L - (segments["li"][0] + segments["li"][1])) * self.tc
|
|
1817
|
+
)
|
|
1827
1818
|
# Ensure
|
|
1828
|
-
if self.system in [
|
|
1819
|
+
if self.system in ["pst-"]:
|
|
1829
1820
|
ub = us[-1]
|
|
1830
|
-
elif self.system in [
|
|
1821
|
+
elif self.system in ["-pst"]:
|
|
1831
1822
|
ub = us[0]
|
|
1832
|
-
Pi_ext +=
|
|
1833
|
-
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1823
|
+
Pi_ext += (
|
|
1824
|
+
-qt * (segments["li"][0] + segments["li"][1]) * np.average(us)
|
|
1825
|
+
- qt * (L - (segments["li"][0] + segments["li"][1])) * ub
|
|
1826
|
+
)
|
|
1827
|
+
if self.system not in ["pst-", "-pst"]:
|
|
1828
|
+
print("Input error: Only pst-setup implemented at the moment.")
|
|
1836
1829
|
|
|
1837
1830
|
return Pi_ext
|
|
1838
1831
|
|
|
@@ -1871,31 +1864,37 @@ class OutputMixin:
|
|
|
1871
1864
|
|
|
1872
1865
|
# Compute weak layer displacements
|
|
1873
1866
|
wweak = self.w(zweak)
|
|
1874
|
-
uweak = self.u(zweak, z0=self.h/2)
|
|
1867
|
+
uweak = self.u(zweak, z0=self.h / 2)
|
|
1875
1868
|
|
|
1876
1869
|
# Compute stored energy of the slab (monte-carlo integration)
|
|
1877
1870
|
n = len(xq)
|
|
1878
1871
|
nweak = len(xweak)
|
|
1879
1872
|
# energy share from moment, shear force, wl normal and tangential springs
|
|
1880
|
-
Pi_int =
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1873
|
+
Pi_int = (
|
|
1874
|
+
L / 2 / n / self.A11 * np.sum([Ni**2 for Ni in N])
|
|
1875
|
+
+ L
|
|
1876
|
+
/ 2
|
|
1877
|
+
/ n
|
|
1878
|
+
/ (self.D11 - self.B11**2 / self.A11)
|
|
1879
|
+
* np.sum([Mi**2 for Mi in M])
|
|
1880
|
+
+ L / 2 / n / self.kA55 * np.sum([Vi**2 for Vi in V])
|
|
1881
|
+
+ L * self.kn / 2 / nweak * np.sum([wi**2 for wi in wweak])
|
|
1882
|
+
+ L * self.kt / 2 / nweak * np.sum([ui**2 for ui in uweak])
|
|
1883
|
+
)
|
|
1885
1884
|
# 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
|
|
1885
|
+
if self.system in ["pst-"]:
|
|
1886
|
+
Pi_int += 1 / 2 * M[-1] * (self.psi(zq)[-1]) ** 2
|
|
1887
|
+
elif self.system in ["-pst"]:
|
|
1888
|
+
Pi_int += 1 / 2 * M[0] * (self.psi(zq)[0]) ** 2
|
|
1890
1889
|
else:
|
|
1891
|
-
print(
|
|
1890
|
+
print("Input error: Only pst-setup implemented at the moment.")
|
|
1892
1891
|
|
|
1893
1892
|
return Pi_int
|
|
1894
1893
|
|
|
1895
1894
|
def total_potential(self, C, phi, L, **segments):
|
|
1896
1895
|
"""
|
|
1897
1896
|
Returns total differential potential
|
|
1898
|
-
|
|
1897
|
+
|
|
1899
1898
|
Arguments
|
|
1900
1899
|
---------
|
|
1901
1900
|
C : ndarray
|
|
@@ -1920,7 +1919,7 @@ class OutputMixin:
|
|
|
1920
1919
|
|
|
1921
1920
|
return Pi_int + Pi_ext
|
|
1922
1921
|
|
|
1923
|
-
def get_weaklayer_shearstress(self, x, z, unit=
|
|
1922
|
+
def get_weaklayer_shearstress(self, x, z, unit="MPa", removeNaNs=False):
|
|
1924
1923
|
"""
|
|
1925
1924
|
Compute weak-layer shear stress.
|
|
1926
1925
|
|
|
@@ -1944,7 +1943,7 @@ class OutputMixin:
|
|
|
1944
1943
|
Normal stress (stress unit input).
|
|
1945
1944
|
"""
|
|
1946
1945
|
# Convert coordinates from mm to cm and stresses from MPa to unit
|
|
1947
|
-
x = x/10
|
|
1946
|
+
x = x / 10
|
|
1948
1947
|
tau = self.tau(z, unit=unit)
|
|
1949
1948
|
# Filter stresses in unspupported segments
|
|
1950
1949
|
if removeNaNs:
|
|
@@ -1957,7 +1956,7 @@ class OutputMixin:
|
|
|
1957
1956
|
|
|
1958
1957
|
return x, tau
|
|
1959
1958
|
|
|
1960
|
-
def get_weaklayer_normalstress(self, x, z, unit=
|
|
1959
|
+
def get_weaklayer_normalstress(self, x, z, unit="MPa", removeNaNs=False):
|
|
1961
1960
|
"""
|
|
1962
1961
|
Compute weak-layer normal stress.
|
|
1963
1962
|
|
|
@@ -1981,7 +1980,7 @@ class OutputMixin:
|
|
|
1981
1980
|
Normal stress (stress unit input).
|
|
1982
1981
|
"""
|
|
1983
1982
|
# Convert coordinates from mm to cm and stresses from MPa to unit
|
|
1984
|
-
x = x/10
|
|
1983
|
+
x = x / 10
|
|
1985
1984
|
sig = self.sig(z, unit=unit)
|
|
1986
1985
|
# Filter stresses in unspupported segments
|
|
1987
1986
|
if removeNaNs:
|
|
@@ -1994,7 +1993,7 @@ class OutputMixin:
|
|
|
1994
1993
|
|
|
1995
1994
|
return x, sig
|
|
1996
1995
|
|
|
1997
|
-
def get_slab_displacement(self, x, z, loc=
|
|
1996
|
+
def get_slab_displacement(self, x, z, loc="mid", unit="mm"):
|
|
1998
1997
|
"""
|
|
1999
1998
|
Compute horizontal slab displacement.
|
|
2000
1999
|
|
|
@@ -2019,15 +2018,15 @@ class OutputMixin:
|
|
|
2019
2018
|
Horizontal displacements (unit input).
|
|
2020
2019
|
"""
|
|
2021
2020
|
# Coordinates (cm)
|
|
2022
|
-
x = x/10
|
|
2021
|
+
x = x / 10
|
|
2023
2022
|
# Locator
|
|
2024
|
-
z0 = {
|
|
2023
|
+
z0 = {"top": -self.h / 2, "mid": 0, "bot": self.h / 2}
|
|
2025
2024
|
# Displacement (unit)
|
|
2026
2025
|
u = self.u(z, z0=z0[loc], unit=unit)
|
|
2027
2026
|
# Output array
|
|
2028
2027
|
return x, u
|
|
2029
2028
|
|
|
2030
|
-
def get_slab_deflection(self, x, z, unit=
|
|
2029
|
+
def get_slab_deflection(self, x, z, unit="mm"):
|
|
2031
2030
|
"""
|
|
2032
2031
|
Compute vertical slab displacement.
|
|
2033
2032
|
|
|
@@ -2050,13 +2049,13 @@ class OutputMixin:
|
|
|
2050
2049
|
Vertical deflections (unit input).
|
|
2051
2050
|
"""
|
|
2052
2051
|
# Coordinates (cm)
|
|
2053
|
-
x = x/10
|
|
2052
|
+
x = x / 10
|
|
2054
2053
|
# Deflection (unit)
|
|
2055
2054
|
w = self.w(z, unit=unit)
|
|
2056
2055
|
# Output array
|
|
2057
2056
|
return x, w
|
|
2058
2057
|
|
|
2059
|
-
def get_slab_rotation(self, x, z, unit=
|
|
2058
|
+
def get_slab_rotation(self, x, z, unit="degrees"):
|
|
2060
2059
|
"""
|
|
2061
2060
|
Compute slab cross-section rotation angle.
|
|
2062
2061
|
|
|
@@ -2079,7 +2078,7 @@ class OutputMixin:
|
|
|
2079
2078
|
Cross section rotations (unit input).
|
|
2080
2079
|
"""
|
|
2081
2080
|
# Coordinates (cm)
|
|
2082
|
-
x = x/10
|
|
2081
|
+
x = x / 10
|
|
2083
2082
|
# Cross-section rotation angle (unit)
|
|
2084
2083
|
psi = self.psi(z, unit=unit)
|
|
2085
2084
|
# Output array
|