weac 2.4.1__py3-none-any.whl → 2.5.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/eigensystem.py +41 -183
- weac/layered.py +8 -6
- weac/mixins.py +574 -121
- weac/plot.py +25 -16
- weac/tools.py +60 -0
- {weac-2.4.1.dist-info → weac-2.5.1.dist-info}/METADATA +15 -6
- weac-2.5.1.dist-info/RECORD +12 -0
- {weac-2.4.1.dist-info → weac-2.5.1.dist-info}/WHEEL +1 -1
- weac-2.4.1.dist-info/RECORD +0 -12
- {weac-2.4.1.dist-info → weac-2.5.1.dist-info}/LICENSE +0 -0
- {weac-2.4.1.dist-info → weac-2.5.1.dist-info}/top_level.txt +0 -0
weac/mixins.py
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
"""Mixins for the elastic analysis of layered snow slabs."""
|
|
2
|
-
# pylint: disable=invalid-name,too-many-locals,too-many-arguments
|
|
2
|
+
# pylint: disable=invalid-name,too-many-locals,too-many-arguments,too-many-lines
|
|
3
3
|
|
|
4
4
|
# Standard library imports
|
|
5
5
|
from functools import partial
|
|
6
6
|
|
|
7
7
|
# Third party imports
|
|
8
8
|
import numpy as np
|
|
9
|
+
from scipy.optimize import brentq
|
|
9
10
|
from scipy.integrate import romberg, cumulative_trapezoid
|
|
10
11
|
|
|
11
12
|
# Module imports
|
|
@@ -485,72 +486,344 @@ class FieldQuantitiesMixin:
|
|
|
485
486
|
"""
|
|
486
487
|
return self.dz_dxdx(z, phi)[5, :]
|
|
487
488
|
|
|
488
|
-
|
|
489
|
+
|
|
490
|
+
class SlabContactMixin:
|
|
489
491
|
"""
|
|
490
|
-
Mixin for the
|
|
492
|
+
Mixin for handling the touchdown situation in a PST.
|
|
491
493
|
|
|
492
|
-
Provides
|
|
493
|
-
and
|
|
494
|
+
Provides Methods for the calculation of substitute spring stiffnesses,
|
|
495
|
+
cracklength-tresholds and element lengths.
|
|
494
496
|
"""
|
|
497
|
+
# pylint: disable=too-many-instance-attributes
|
|
495
498
|
|
|
496
|
-
def
|
|
499
|
+
def set_columnlength(self,L):
|
|
497
500
|
"""
|
|
498
|
-
|
|
501
|
+
Set cracklength.
|
|
499
502
|
|
|
500
503
|
Arguments
|
|
501
504
|
---------
|
|
502
|
-
|
|
503
|
-
|
|
505
|
+
L : float
|
|
506
|
+
Column length of a PST (mm).
|
|
507
|
+
"""
|
|
508
|
+
self.L = L
|
|
509
|
+
|
|
510
|
+
def set_cracklength(self,a):
|
|
511
|
+
"""
|
|
512
|
+
Set cracklength.
|
|
513
|
+
|
|
514
|
+
Arguments
|
|
515
|
+
---------
|
|
516
|
+
a : float
|
|
517
|
+
Cracklength in a PST (mm).
|
|
518
|
+
"""
|
|
519
|
+
self.a = a
|
|
520
|
+
|
|
521
|
+
def set_tc(self,cf):
|
|
522
|
+
"""
|
|
523
|
+
Set height of the crack.
|
|
524
|
+
|
|
525
|
+
Arguments
|
|
526
|
+
---------
|
|
527
|
+
cf : float
|
|
528
|
+
Collapse-factor. Ratio of the crack height to the
|
|
529
|
+
uncollapsed weak-layer height.
|
|
530
|
+
"""
|
|
531
|
+
# subtract displacement under constact load from collapsed wl height
|
|
532
|
+
qn = self.calc_qn()
|
|
533
|
+
self.tc = cf*self.t - qn/self.kn
|
|
534
|
+
|
|
535
|
+
def set_phi(self,phi):
|
|
536
|
+
"""
|
|
537
|
+
Set inclination of the slab.
|
|
538
|
+
|
|
539
|
+
Arguments
|
|
540
|
+
---------
|
|
541
|
+
phi : float
|
|
542
|
+
Inclination of the slab (°).
|
|
543
|
+
"""
|
|
544
|
+
self.phi = phi
|
|
545
|
+
|
|
546
|
+
def set_stiffness_ratio(self, ratio=1000):
|
|
547
|
+
"""
|
|
548
|
+
Set ratio between collapsed and uncollapsed weak-layer stiffness.
|
|
549
|
+
|
|
550
|
+
Parameters
|
|
551
|
+
----------
|
|
552
|
+
ratio : int, optional
|
|
553
|
+
Stiffness ratio between collapsed and uncollapsed weak layer.
|
|
554
|
+
Default is 1000.
|
|
555
|
+
"""
|
|
556
|
+
self.ratio = ratio
|
|
557
|
+
|
|
558
|
+
def calc_qn(self):
|
|
559
|
+
"""
|
|
560
|
+
Calc total surface normal load.
|
|
504
561
|
|
|
505
562
|
Returns
|
|
506
563
|
-------
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
A - free end, B - intermediate touchdown,
|
|
510
|
-
C - full touchdown (maximum clamped end).
|
|
564
|
+
float
|
|
565
|
+
Total surface normal load (N/mm).
|
|
511
566
|
"""
|
|
512
|
-
|
|
513
|
-
# Classify boundary type by element length
|
|
514
|
-
if l <= self.lC:
|
|
515
|
-
return 'A'
|
|
516
|
-
elif self.lC < l <= self.lS:
|
|
517
|
-
return 'B'
|
|
518
|
-
elif self.lS < l:
|
|
519
|
-
return 'C'
|
|
520
|
-
else:
|
|
521
|
-
return 'A'
|
|
567
|
+
return self.get_weight_load(self.phi)[0] + self.get_surface_load(self.phi)[0]
|
|
522
568
|
|
|
523
|
-
def
|
|
569
|
+
def calc_qt(self):
|
|
524
570
|
"""
|
|
525
|
-
|
|
571
|
+
Calc total surface normal load.
|
|
572
|
+
|
|
573
|
+
Returns
|
|
574
|
+
-------
|
|
575
|
+
float
|
|
576
|
+
Total surface normal load (N/mm).
|
|
577
|
+
"""
|
|
578
|
+
return self.get_weight_load(self.phi)[1] + self.get_surface_load(self.phi)[1]
|
|
579
|
+
|
|
580
|
+
def substitute_stiffness(self, L, support='rested', dof='rot'):
|
|
581
|
+
"""
|
|
582
|
+
Calc substitute stiffness for beam on elastic foundation.
|
|
526
583
|
|
|
527
584
|
Arguments
|
|
528
585
|
---------
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
586
|
+
L : float
|
|
587
|
+
Total length of the PST-column (mm).
|
|
588
|
+
support : string
|
|
589
|
+
Type of segment foundation. Defaults to 'rested'.
|
|
590
|
+
dof : string
|
|
591
|
+
Type of substitute spring, either 'rot' or 'trans'. Defaults to 'rot'.
|
|
535
592
|
|
|
536
593
|
Returns
|
|
537
594
|
-------
|
|
538
|
-
|
|
539
|
-
|
|
595
|
+
k : stiffness of substitute spring.
|
|
596
|
+
"""
|
|
597
|
+
# adjust system to substitute system
|
|
598
|
+
if dof in ['rot']:
|
|
599
|
+
tempsys = self.system
|
|
600
|
+
self.system = 'rot'
|
|
601
|
+
if dof in ['trans']:
|
|
602
|
+
tempsys = self.system
|
|
603
|
+
self.system = 'trans'
|
|
604
|
+
|
|
605
|
+
# Change eigensystem for rested segment
|
|
606
|
+
if support in ['rested']:
|
|
607
|
+
tempkn = self.kn
|
|
608
|
+
tempkt = self.kt
|
|
609
|
+
self.kn = self.ratio*self.kn
|
|
610
|
+
self.kt = self.ratio*self.kt
|
|
611
|
+
self.calc_system_matrix()
|
|
612
|
+
self.calc_eigensystem()
|
|
613
|
+
|
|
614
|
+
# prepare list of segment characteristics
|
|
615
|
+
segments = {'li': np.array([L, 0.]),
|
|
616
|
+
'mi': np.array([0]),
|
|
617
|
+
'ki': np.array([True, True])}
|
|
618
|
+
# solve system of equations
|
|
619
|
+
constants = self.assemble_and_solve(phi=0, **segments)
|
|
620
|
+
# calculate stiffness
|
|
621
|
+
_, z_pst, _ = self.rasterize_solution(
|
|
622
|
+
C=constants, phi=0, num=1, **segments)
|
|
623
|
+
if dof in ['rot']:
|
|
624
|
+
k = abs(1/self.psi(z_pst)[0])
|
|
625
|
+
if dof in ['trans']:
|
|
626
|
+
k = abs(1/self.w(z_pst)[0])
|
|
627
|
+
|
|
628
|
+
# Reset to previous system and eigensystem
|
|
629
|
+
self.system = tempsys
|
|
630
|
+
if support in ['rested']:
|
|
631
|
+
self.kn = tempkn
|
|
632
|
+
self.kt = tempkt
|
|
633
|
+
self.calc_system_matrix()
|
|
634
|
+
self.calc_eigensystem()
|
|
635
|
+
|
|
636
|
+
return k
|
|
637
|
+
|
|
638
|
+
def calc_a1(self):
|
|
639
|
+
"""
|
|
640
|
+
Calc transition lengths a1 (aAB).
|
|
641
|
+
|
|
642
|
+
Returns
|
|
643
|
+
-------
|
|
644
|
+
a1 : float
|
|
645
|
+
Length of the crack for transition of stage A to stage B (mm).
|
|
646
|
+
"""
|
|
647
|
+
# Unpack variables
|
|
648
|
+
bs = -(self.B11**2/self.A11 - self.D11)
|
|
649
|
+
ss = self.kA55
|
|
650
|
+
L = self.L
|
|
651
|
+
tc = self.tc
|
|
652
|
+
qn = self.calc_qn()
|
|
653
|
+
|
|
654
|
+
# Create polynomial expression
|
|
655
|
+
def polynomial(x):
|
|
656
|
+
# Spring stiffness supported segment
|
|
657
|
+
kRl = self.substitute_stiffness(L-x, 'supported', 'rot')
|
|
658
|
+
kNl = self.substitute_stiffness(L-x, 'supported', 'trans')
|
|
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
|
|
665
|
+
|
|
666
|
+
# Find root
|
|
667
|
+
a1 = brentq(polynomial, L/1000, 999/1000*L)
|
|
668
|
+
|
|
669
|
+
return a1
|
|
670
|
+
|
|
671
|
+
def calc_a2(self):
|
|
540
672
|
"""
|
|
541
|
-
|
|
542
|
-
if mode in ['A']:
|
|
543
|
-
kf = 0
|
|
544
|
-
# Reduction factor for touchdown
|
|
545
|
-
if mode in ['B', 'C']:
|
|
546
|
-
l = l - self.lC
|
|
547
|
-
# Beta needs to take into account different weak-layer spring stiffness
|
|
548
|
-
beta = self.beta*self.ratio**(1/4)
|
|
549
|
-
kf=(np.cos(2*beta*l)+np.cosh(2*beta*l)-2)/(np.sin(2*beta*l)+np.sinh(2*beta*l))
|
|
673
|
+
Calc transition lengths a2 (aBC).
|
|
550
674
|
|
|
551
|
-
|
|
675
|
+
Returns
|
|
676
|
+
-------
|
|
677
|
+
a2 : float
|
|
678
|
+
Length of the crack for transition of stage B to stage C (mm).
|
|
679
|
+
"""
|
|
680
|
+
# Unpack variables
|
|
681
|
+
bs = -(self.B11**2/self.A11 - self.D11)
|
|
682
|
+
ss = self.kA55
|
|
683
|
+
L = self.L
|
|
684
|
+
tc = self.tc
|
|
685
|
+
qn = self.calc_qn()
|
|
686
|
+
|
|
687
|
+
# Create polynomial function
|
|
688
|
+
def polynomial(x):
|
|
689
|
+
# Spring stiffness supported segment
|
|
690
|
+
kRl = self.substitute_stiffness(L-x, 'supported', 'rot')
|
|
691
|
+
kNl = self.substitute_stiffness(L-x, 'supported', 'trans')
|
|
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
|
+
2*ss**2*kRl \
|
|
697
|
+
+ 3*bs*ss*kNl)
|
|
698
|
+
c5 = 72*bs*(
|
|
699
|
+
bs*qn*(
|
|
700
|
+
ss**2 \
|
|
701
|
+
+ kRl*kNl) \
|
|
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
|
|
708
|
+
|
|
709
|
+
# Find root
|
|
710
|
+
a2 = brentq(polynomial, L/1000, 999/1000*L)
|
|
711
|
+
|
|
712
|
+
return a2
|
|
713
|
+
|
|
714
|
+
def calc_lA(self):
|
|
715
|
+
"""
|
|
716
|
+
Calculate the length of the touchdown element in mode A.
|
|
717
|
+
"""
|
|
718
|
+
lA = self.a
|
|
719
|
+
|
|
720
|
+
return lA
|
|
721
|
+
|
|
722
|
+
def calc_lB(self):
|
|
723
|
+
"""
|
|
724
|
+
Calculate the length of the touchdown element in mode B.
|
|
725
|
+
"""
|
|
726
|
+
lB = self.a
|
|
727
|
+
|
|
728
|
+
return lB
|
|
729
|
+
|
|
730
|
+
def calc_lC(self):
|
|
731
|
+
"""
|
|
732
|
+
Calculate the length of the touchdown element in mode C.
|
|
733
|
+
"""
|
|
734
|
+
# Unpack variables
|
|
735
|
+
bs = -(self.B11**2/self.A11 - self.D11)
|
|
736
|
+
ss = self.kA55
|
|
737
|
+
L = self.L
|
|
738
|
+
a = self.a
|
|
739
|
+
tc = self.tc
|
|
740
|
+
qn = self.calc_qn()
|
|
741
|
+
|
|
742
|
+
def polynomial(x):
|
|
743
|
+
# Spring stiffness supported segment
|
|
744
|
+
kRl = self.substitute_stiffness(L-a, 'supported', 'rot')
|
|
745
|
+
kNl = self.substitute_stiffness(L-a, 'supported', 'trans')
|
|
746
|
+
# Spring stiffness rested segment
|
|
747
|
+
kRr = self.substitute_stiffness(a-x, 'rested', 'rot')
|
|
748
|
+
# define constants
|
|
749
|
+
c1 = ss**2*kRl*kNl*qn
|
|
750
|
+
c2 = 6*ss*kNl*qn*(
|
|
751
|
+
bs*ss \
|
|
752
|
+
+ kRl*kRr)
|
|
753
|
+
c3 = 30*bs*ss*kNl*qn*(kRl + kRr)
|
|
754
|
+
c4 = 24*bs*qn*(
|
|
755
|
+
2*ss**2*kRl \
|
|
756
|
+
+ 3*bs*ss*kNl \
|
|
757
|
+
+ 3*kRl*kRr*kNl)
|
|
758
|
+
c5 = 72*bs*(
|
|
759
|
+
bs*qn*(
|
|
760
|
+
ss**2 \
|
|
761
|
+
+ kNl*(kRl + kRr)) \
|
|
762
|
+
+ ss*kRl*(
|
|
763
|
+
2*kRr*qn \
|
|
764
|
+
- ss*kNl*tc))
|
|
765
|
+
c6 = 144*bs*ss*(
|
|
766
|
+
bs*qn*(kRl + kRr) \
|
|
767
|
+
- kNl*tc*(
|
|
768
|
+
bs*ss \
|
|
769
|
+
+ kRl*kRr))
|
|
770
|
+
c7 = - 144*bs**2*ss*kNl*tc*(kRl + kRr)
|
|
771
|
+
return c1*x**6 + c2*x**5 + c3*x**4 + c4*x**3 + c5*x**2 + c6*x + c7
|
|
772
|
+
|
|
773
|
+
# Find root
|
|
774
|
+
lC = brentq(polynomial, a/1000, 999/1000*a)
|
|
775
|
+
|
|
776
|
+
return lC
|
|
777
|
+
|
|
778
|
+
def set_touchdown_attributes(self, L, a, cf, phi, ratio):
|
|
779
|
+
"""Set class attributes for touchdown consideration"""
|
|
780
|
+
self.set_columnlength(L)
|
|
781
|
+
self.set_cracklength(a)
|
|
782
|
+
self.set_tc(cf)
|
|
783
|
+
self.set_phi(phi)
|
|
784
|
+
self.set_stiffness_ratio(ratio)
|
|
785
|
+
|
|
786
|
+
def calc_touchdown_mode(self):
|
|
787
|
+
"""Calculate touchdown-mode from thresholds"""
|
|
788
|
+
if self.touchdown:
|
|
789
|
+
# Calculate stage transitions
|
|
790
|
+
a1 = self.calc_a1()
|
|
791
|
+
a2 = self.calc_a2()
|
|
792
|
+
# Assign stage
|
|
793
|
+
if self.a <= a1:
|
|
794
|
+
mode = 'A'
|
|
795
|
+
elif a1 < self.a <= a2:
|
|
796
|
+
mode = 'B'
|
|
797
|
+
elif a2 < self.a:
|
|
798
|
+
mode = 'C'
|
|
799
|
+
self.mode = mode
|
|
800
|
+
else:
|
|
801
|
+
self.mode = 'A'
|
|
802
|
+
|
|
803
|
+
def calc_touchdown_length(self):
|
|
804
|
+
"""Calculate touchdown length"""
|
|
805
|
+
if self.mode in ['A']:
|
|
806
|
+
self.td = self.calc_lA()
|
|
807
|
+
elif self.mode in ['B']:
|
|
808
|
+
self.td = self.calc_lB()
|
|
809
|
+
elif self.mode in ['C']:
|
|
810
|
+
self.td = self.calc_lC()
|
|
811
|
+
|
|
812
|
+
def calc_touchdown_system(self, L, a, cf, phi, ratio=1000):
|
|
813
|
+
"""Calculate touchdown"""
|
|
814
|
+
self.set_touchdown_attributes(L, a, cf, phi, ratio)
|
|
815
|
+
self.calc_touchdown_mode()
|
|
816
|
+
self.calc_touchdown_length()
|
|
817
|
+
|
|
818
|
+
class SolutionMixin:
|
|
819
|
+
"""
|
|
820
|
+
Mixin for the solution of boundary value problems.
|
|
821
|
+
|
|
822
|
+
Provides methods for the assembly of the system of equations
|
|
823
|
+
and for the computation of the free constants.
|
|
824
|
+
"""
|
|
552
825
|
|
|
553
|
-
def bc(self, z,
|
|
826
|
+
def bc(self, z, k=False, pos='mid'):
|
|
554
827
|
"""
|
|
555
828
|
Provide equations for free (pst) or infinite (skiers) ends.
|
|
556
829
|
|
|
@@ -574,37 +847,44 @@ class SolutionMixin:
|
|
|
574
847
|
bc : ndarray
|
|
575
848
|
Boundary condition vector (lenght 3) at position x.
|
|
576
849
|
"""
|
|
577
|
-
# Check mode for free end
|
|
578
|
-
mode = self.mode_td(l=l)
|
|
579
|
-
# Get spring stiffness reduction factor
|
|
580
|
-
kf = self.reduce_stiffness(l=l, mode=mode)
|
|
581
|
-
# Get spring stiffness for collapsed weak-layer
|
|
582
|
-
kR = self.calc_rot_spring(collapse=True)
|
|
583
850
|
|
|
584
851
|
# Set boundary conditions for PST-systems
|
|
585
852
|
if self.system in ['pst-', '-pst']:
|
|
586
853
|
if not k:
|
|
587
|
-
if mode in ['A']:
|
|
854
|
+
if self.mode in ['A']:
|
|
588
855
|
# Free end
|
|
589
|
-
bc = np.array([
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
elif mode in ['B', 'C'] and pos in ['r', 'right']:
|
|
856
|
+
bc = np.array([self.N(z),
|
|
857
|
+
self.M(z),
|
|
858
|
+
self.V(z)
|
|
859
|
+
])
|
|
860
|
+
elif self.mode in ['B'] and pos in ['r', 'right']:
|
|
595
861
|
# Touchdown right
|
|
596
|
-
bc = np.array([
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
862
|
+
bc = np.array([self.N(z),
|
|
863
|
+
self.M(z),
|
|
864
|
+
self.w(z)
|
|
865
|
+
])
|
|
866
|
+
elif self.mode in ['B'] and pos in ['l', 'left']: # Kann dieser Block
|
|
867
|
+
# Touchdown left # verschwinden? Analog zu 'A'
|
|
868
|
+
bc = np.array([self.N(z),
|
|
869
|
+
self.M(z),
|
|
870
|
+
self.w(z)
|
|
871
|
+
])
|
|
872
|
+
elif self.mode in ['C'] and pos in ['r', 'right']:
|
|
873
|
+
# Spring stiffness
|
|
874
|
+
kR = self.substitute_stiffness(self.a - self.td, 'rested', 'rot')
|
|
875
|
+
# Touchdown right
|
|
876
|
+
bc = np.array([self.N(z),
|
|
877
|
+
self.M(z) + kR*self.psi(z),
|
|
878
|
+
self.w(z)
|
|
879
|
+
])
|
|
880
|
+
elif self.mode in ['C'] and pos in ['l', 'left']:
|
|
881
|
+
# Spring stiffness
|
|
882
|
+
kR = self.substitute_stiffness(self.a - self.td, 'rested', 'rot')
|
|
602
883
|
# Touchdown left
|
|
603
|
-
bc = np.array([
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
])
|
|
884
|
+
bc = np.array([self.N(z),
|
|
885
|
+
self.M(z) - kR*self.psi(z),
|
|
886
|
+
self.w(z)
|
|
887
|
+
])
|
|
608
888
|
else:
|
|
609
889
|
# Free end
|
|
610
890
|
bc = np.array([
|
|
@@ -626,6 +906,12 @@ class SolutionMixin:
|
|
|
626
906
|
self.w(z),
|
|
627
907
|
self.psi(z)
|
|
628
908
|
])
|
|
909
|
+
# Set boundary conditions for substitute spring calculus
|
|
910
|
+
elif self.system in ['rot', 'trans']:
|
|
911
|
+
bc = np.array([self.N(z),
|
|
912
|
+
self.M(z),
|
|
913
|
+
self.V(z)
|
|
914
|
+
])
|
|
629
915
|
else:
|
|
630
916
|
raise ValueError(
|
|
631
917
|
'Boundary conditions not defined for'
|
|
@@ -633,7 +919,7 @@ class SolutionMixin:
|
|
|
633
919
|
|
|
634
920
|
return bc
|
|
635
921
|
|
|
636
|
-
def eqs(self, zl, zr,
|
|
922
|
+
def eqs(self, zl, zr, k=False, pos='mid'):
|
|
637
923
|
"""
|
|
638
924
|
Provide boundary or transmission conditions for beam segments.
|
|
639
925
|
|
|
@@ -643,8 +929,6 @@ class SolutionMixin:
|
|
|
643
929
|
Solution vector (6x1) at left end of beam segement.
|
|
644
930
|
zr : ndarray
|
|
645
931
|
Solution vector (6x1) at right end of beam segement.
|
|
646
|
-
l : float, optional
|
|
647
|
-
Length of the segment in consideration. Default is zero.
|
|
648
932
|
k : boolean
|
|
649
933
|
Indicates whether segment has foundation(True) or not (False).
|
|
650
934
|
Default is False.
|
|
@@ -664,15 +948,15 @@ class SolutionMixin:
|
|
|
664
948
|
"""
|
|
665
949
|
if pos in ('l', 'left'):
|
|
666
950
|
eqs = np.array([
|
|
667
|
-
self.bc(zl,
|
|
668
|
-
self.bc(zl,
|
|
669
|
-
self.bc(zl,
|
|
670
|
-
self.u(zr, z0=0),
|
|
671
|
-
self.w(zr),
|
|
672
|
-
self.psi(zr),
|
|
673
|
-
self.N(zr),
|
|
674
|
-
self.M(zr),
|
|
675
|
-
self.V(zr)])
|
|
951
|
+
self.bc(zl, k, pos)[0], # Left boundary condition
|
|
952
|
+
self.bc(zl, k, pos)[1], # Left boundary condition
|
|
953
|
+
self.bc(zl, k, pos)[2], # Left boundary condition
|
|
954
|
+
self.u(zr, z0=0), # ui(xi = li)
|
|
955
|
+
self.w(zr), # wi(xi = li)
|
|
956
|
+
self.psi(zr), # psii(xi = li)
|
|
957
|
+
self.N(zr), # Ni(xi = li)
|
|
958
|
+
self.M(zr), # Mi(xi = li)
|
|
959
|
+
self.V(zr)]) # Vi(xi = li)
|
|
676
960
|
elif pos in ('m', 'mid'):
|
|
677
961
|
eqs = np.array([
|
|
678
962
|
-self.u(zl, z0=0), # -ui(xi = 0)
|
|
@@ -689,15 +973,15 @@ class SolutionMixin:
|
|
|
689
973
|
self.V(zr)]) # Vi(xi = li)
|
|
690
974
|
elif pos in ('r', 'right'):
|
|
691
975
|
eqs = np.array([
|
|
692
|
-
-self.u(zl, z0=0),
|
|
693
|
-
-self.w(zl),
|
|
694
|
-
-self.psi(zl),
|
|
695
|
-
-self.N(zl),
|
|
696
|
-
-self.M(zl),
|
|
697
|
-
-self.V(zl),
|
|
698
|
-
self.bc(zr,
|
|
699
|
-
self.bc(zr,
|
|
700
|
-
self.bc(zr,
|
|
976
|
+
-self.u(zl, z0=0), # -ui(xi = 0)
|
|
977
|
+
-self.w(zl), # -wi(xi = 0)
|
|
978
|
+
-self.psi(zl), # -psii(xi = 0)
|
|
979
|
+
-self.N(zl), # -Ni(xi = 0)
|
|
980
|
+
-self.M(zl), # -Mi(xi = 0)
|
|
981
|
+
-self.V(zl), # -Vi(xi = 0)
|
|
982
|
+
self.bc(zr, k, pos)[0], # Right boundary condition
|
|
983
|
+
self.bc(zr, k, pos)[1], # Right boundary condition
|
|
984
|
+
self.bc(zr, k, pos)[2]]) # Right boundary condition
|
|
701
985
|
else:
|
|
702
986
|
raise ValueError(
|
|
703
987
|
(f'Invalid position argument {pos} given. '
|
|
@@ -705,8 +989,19 @@ class SolutionMixin:
|
|
|
705
989
|
'or left, mid and right.'))
|
|
706
990
|
return eqs
|
|
707
991
|
|
|
708
|
-
def calc_segments(
|
|
709
|
-
|
|
992
|
+
def calc_segments(
|
|
993
|
+
self,
|
|
994
|
+
li: list[float] | list[int] |bool = False,
|
|
995
|
+
mi: list[float] | list[int] |bool = False,
|
|
996
|
+
ki: list[bool] | bool = False,
|
|
997
|
+
k0: list[bool] | bool = False,
|
|
998
|
+
L: float = 1e4,
|
|
999
|
+
a: float = 0,
|
|
1000
|
+
m: float = 0,
|
|
1001
|
+
phi: float = 0,
|
|
1002
|
+
cf: float = 0.5,
|
|
1003
|
+
ratio: float = 1000,
|
|
1004
|
+
**kwargs):
|
|
710
1005
|
"""
|
|
711
1006
|
Assemble lists defining the segments.
|
|
712
1007
|
|
|
@@ -739,6 +1034,12 @@ class SolutionMixin:
|
|
|
739
1034
|
m : float, optional
|
|
740
1035
|
Weight of skier (kg) in the axial center of the model.
|
|
741
1036
|
Used for system 'skier'.
|
|
1037
|
+
cf : float, optional
|
|
1038
|
+
Collapse factor. Ratio of the crack height to the uncollapsed
|
|
1039
|
+
weak-layer height. Used for systems 'pst-', '-pst'. Default is 0.5.
|
|
1040
|
+
ratio : float, optional
|
|
1041
|
+
Stiffness ratio between collapsed and uncollapsed weak layer.
|
|
1042
|
+
Default is 1000.
|
|
742
1043
|
|
|
743
1044
|
Returns
|
|
744
1045
|
-------
|
|
@@ -749,12 +1050,9 @@ class SolutionMixin:
|
|
|
749
1050
|
"""
|
|
750
1051
|
|
|
751
1052
|
_ = kwargs # Unused arguments
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
lU = a
|
|
756
|
-
if mode in ['C']:
|
|
757
|
-
lU = self.lS
|
|
1053
|
+
|
|
1054
|
+
# Precompute touchdown properties
|
|
1055
|
+
self.calc_touchdown_system(L=L, a=a, cf=cf, phi=phi, ratio=ratio)
|
|
758
1056
|
|
|
759
1057
|
# Assemble list defining the segments
|
|
760
1058
|
if self.system == 'skiers':
|
|
@@ -763,12 +1061,12 @@ class SolutionMixin:
|
|
|
763
1061
|
ki = np.array(ki) # Crack
|
|
764
1062
|
k0 = np.array(k0) # No crack
|
|
765
1063
|
elif self.system == 'pst-':
|
|
766
|
-
li = np.array([L - a,
|
|
1064
|
+
li = np.array([L - self.a, self.td]) # Segment lengths
|
|
767
1065
|
mi = np.array([0]) # Skier weights
|
|
768
1066
|
ki = np.array([True, False]) # Crack
|
|
769
1067
|
k0 = np.array([True, True]) # No crack
|
|
770
1068
|
elif self.system == '-pst':
|
|
771
|
-
li = np.array([
|
|
1069
|
+
li = np.array([self.td, L - self.a]) # Segment lengths
|
|
772
1070
|
mi = np.array([0]) # Skier weights
|
|
773
1071
|
ki = np.array([False, True]) # Crack
|
|
774
1072
|
k0 = np.array([True, True]) # No crack
|
|
@@ -783,8 +1081,8 @@ class SolutionMixin:
|
|
|
783
1081
|
ki = np.array([False, True]) # Crack
|
|
784
1082
|
k0 = np.array([True, True]) # No crack
|
|
785
1083
|
elif self.system == 'skier':
|
|
786
|
-
lb = (L - a)/2
|
|
787
|
-
lf = a/2
|
|
1084
|
+
lb = (L - self.a)/2 # Half bedded length
|
|
1085
|
+
lf = self.a/2 # Half free length
|
|
788
1086
|
li = np.array([lb, lf, lf, lb]) # Segment lengths
|
|
789
1087
|
mi = np.array([0, m, 0]) # Skier weights
|
|
790
1088
|
ki = np.array([True, False, False, True]) # Crack
|
|
@@ -841,7 +1139,7 @@ class SolutionMixin:
|
|
|
841
1139
|
raise ValueError('Make sure len(li)=N, len(ki)=N, and '
|
|
842
1140
|
'len(mi)=N-1 for a system of N segments.')
|
|
843
1141
|
|
|
844
|
-
if self.system not in ['pst-', '-pst', 'vpst-', '-vpst']:
|
|
1142
|
+
if self.system not in ['pst-', '-pst', 'vpst-', '-vpst', 'rot', 'trans']:
|
|
845
1143
|
# Boundary segments must be on foundation for infinite BCs
|
|
846
1144
|
if not all([ki[0], ki[-1]]):
|
|
847
1145
|
raise ValueError('Provide supported boundary segments in '
|
|
@@ -856,6 +1154,7 @@ class SolutionMixin:
|
|
|
856
1154
|
|
|
857
1155
|
# Determine size of linear system of equations
|
|
858
1156
|
nS = len(li) # Number of beam segments
|
|
1157
|
+
|
|
859
1158
|
nDOF = 6 # Number of free constants per segment
|
|
860
1159
|
|
|
861
1160
|
# Add dummy segment if only one segment provided
|
|
@@ -884,11 +1183,11 @@ class SolutionMixin:
|
|
|
884
1183
|
zhi = self.eqs(
|
|
885
1184
|
zl=self.zh(x=0, l=l, bed=k),
|
|
886
1185
|
zr=self.zh(x=l, l=l, bed=k),
|
|
887
|
-
|
|
1186
|
+
k=k, pos=pos)
|
|
888
1187
|
zpi = self.eqs(
|
|
889
1188
|
zl=self.zp(x=0, phi=phi, bed=k),
|
|
890
1189
|
zr=self.zp(x=l, phi=phi, bed=k),
|
|
891
|
-
|
|
1190
|
+
k=k, pos=pos)
|
|
892
1191
|
# Rows for left-hand side assembly
|
|
893
1192
|
start = 0 if i == 0 else 3
|
|
894
1193
|
stop = 6 if i == nS - 1 else 9
|
|
@@ -903,10 +1202,10 @@ class SolutionMixin:
|
|
|
903
1202
|
# Right-hand side for transmission from segment i-1 to segment i
|
|
904
1203
|
rhs[6*i:6*i + 3] = np.vstack([Ft, -Ft*self.h/2, Fn])
|
|
905
1204
|
# Set rhs so that complementary integral vanishes at boundaries
|
|
906
|
-
if self.system not in ['pst-', '-pst']:
|
|
1205
|
+
if self.system not in ['pst-', '-pst', 'rested']:
|
|
907
1206
|
rhs[:3] = self.bc(self.zp(x=0, phi=phi, bed=ki[0]))
|
|
908
1207
|
rhs[-3:] = self.bc(self.zp(x=li[-1], phi=phi, bed=ki[-1]))
|
|
909
|
-
|
|
1208
|
+
|
|
910
1209
|
# Set rhs for vertical faces
|
|
911
1210
|
if self.system in ['vpst-', '-vpst']:
|
|
912
1211
|
# Calculate center of gravity and mass of
|
|
@@ -921,19 +1220,34 @@ class SolutionMixin:
|
|
|
921
1220
|
# Add to right-hand side
|
|
922
1221
|
rhs[:3] = np.vstack([N, M, V]) # left end
|
|
923
1222
|
rhs[-3:] = np.vstack([N, M, V]) # right end
|
|
924
|
-
|
|
925
|
-
#
|
|
926
|
-
|
|
927
|
-
#
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
1223
|
+
|
|
1224
|
+
# Loop through segments to set touchdown conditions at rhs
|
|
1225
|
+
for i in range(nS):
|
|
1226
|
+
# Length, foundation and position of segment i
|
|
1227
|
+
l, k, pos = li[i], ki[i], pi[i]
|
|
1228
|
+
# Set displacement BC in stage B
|
|
1229
|
+
if not k and bool(self.mode in ['B']):
|
|
1230
|
+
if i==0:
|
|
1231
|
+
rhs[:3] = np.vstack([0,0,self.tc])
|
|
1232
|
+
if i == (nS - 1):
|
|
1233
|
+
rhs[-3:] = np.vstack([0,0,self.tc])
|
|
1234
|
+
# Set normal force and displacement BC for stage C
|
|
1235
|
+
if not k and bool(self.mode in ['C']):
|
|
1236
|
+
N = self.calc_qt()*(self.a - self.td)
|
|
1237
|
+
if i==0:
|
|
1238
|
+
rhs[:3] = np.vstack([-N,0,self.tc])
|
|
1239
|
+
if i == (nS - 1):
|
|
1240
|
+
rhs[-3:] = np.vstack([N,0,self.tc])
|
|
1241
|
+
|
|
1242
|
+
# Rhs for substitute spring stiffness
|
|
1243
|
+
if self.system in ['rot']:
|
|
1244
|
+
# apply arbitrary moment of 1 at left boundary
|
|
1245
|
+
rhs = rhs*0
|
|
1246
|
+
rhs[1] = 1
|
|
1247
|
+
if self.system in ['trans']:
|
|
1248
|
+
# apply arbitrary force of 1 at left boundary
|
|
1249
|
+
rhs = rhs*0
|
|
1250
|
+
rhs[2] = 1
|
|
937
1251
|
|
|
938
1252
|
# --- SOLVE -----------------------------------------------------------
|
|
939
1253
|
|
|
@@ -951,7 +1265,14 @@ class AnalysisMixin:
|
|
|
951
1265
|
elastic foundations.
|
|
952
1266
|
"""
|
|
953
1267
|
|
|
954
|
-
def rasterize_solution(
|
|
1268
|
+
def rasterize_solution(
|
|
1269
|
+
self,
|
|
1270
|
+
C: np.ndarray,
|
|
1271
|
+
phi: float,
|
|
1272
|
+
li: list[float] | bool,
|
|
1273
|
+
ki: list[bool] | bool,
|
|
1274
|
+
num: int = 250,
|
|
1275
|
+
**kwargs):
|
|
955
1276
|
"""
|
|
956
1277
|
Compute rasterized solution vector.
|
|
957
1278
|
|
|
@@ -984,6 +1305,7 @@ class AnalysisMixin:
|
|
|
984
1305
|
_ = kwargs
|
|
985
1306
|
|
|
986
1307
|
# Drop zero-length segments
|
|
1308
|
+
li = abs(li)
|
|
987
1309
|
isnonzero = li > 0
|
|
988
1310
|
C, ki, li = C[:, isnonzero], ki[isnonzero], li[isnonzero]
|
|
989
1311
|
|
|
@@ -1003,7 +1325,7 @@ class AnalysisMixin:
|
|
|
1003
1325
|
# Loop through segments
|
|
1004
1326
|
for i, l in enumerate(li):
|
|
1005
1327
|
# Get local x-coordinates of segment i
|
|
1006
|
-
xi = np.linspace(0, l, num=nq[i], endpoint=(i == li.size - 1))
|
|
1328
|
+
xi = np.linspace(0, l, num=nq[i], endpoint=(i == li.size - 1)) # pylint: disable=superfluous-parens
|
|
1007
1329
|
# Compute start and end coordinates of segment i
|
|
1008
1330
|
x0 = lic[i]
|
|
1009
1331
|
# Assemble global coordinate vector
|
|
@@ -1466,6 +1788,137 @@ class OutputMixin:
|
|
|
1466
1788
|
Provides convenience methods for the assembly of output lists
|
|
1467
1789
|
such as rasterized displacements or rasterized stresses.
|
|
1468
1790
|
"""
|
|
1791
|
+
def external_potential(self, C, phi, L, **segments):
|
|
1792
|
+
"""
|
|
1793
|
+
Compute total external potential (pst only).
|
|
1794
|
+
|
|
1795
|
+
Arguments
|
|
1796
|
+
---------
|
|
1797
|
+
C : ndarray
|
|
1798
|
+
Matrix(6xN) of solution constants for a system of N
|
|
1799
|
+
segements. Columns contain the 6 constants of each segement.
|
|
1800
|
+
phi : float
|
|
1801
|
+
Inclination of the slab (°).
|
|
1802
|
+
L : float, optional
|
|
1803
|
+
Total length of model (mm).
|
|
1804
|
+
segments : dict
|
|
1805
|
+
Dictionary with lists of touchdown booleans (tdi), segement
|
|
1806
|
+
lengths (li), skier weights (mi), and foundation booleans
|
|
1807
|
+
in the cracked (ki) and uncracked (k0) configurations.
|
|
1808
|
+
|
|
1809
|
+
Returns
|
|
1810
|
+
-------
|
|
1811
|
+
Pi_ext : float
|
|
1812
|
+
Total external potential (Nmm).
|
|
1813
|
+
"""
|
|
1814
|
+
# Rasterize solution
|
|
1815
|
+
xq, zq, xb = self.rasterize_solution(C=C, phi=phi, **segments)
|
|
1816
|
+
_ = xq, xb
|
|
1817
|
+
# Compute displacements where weight loads are applied
|
|
1818
|
+
w0 = self.w(zq)
|
|
1819
|
+
us = self.u(zq, z0=self.zs)
|
|
1820
|
+
# Get weight loads
|
|
1821
|
+
qn = self.calc_qn()
|
|
1822
|
+
qt = self.calc_qt()
|
|
1823
|
+
# use +/- and us[0]/us[-1] according to system and phi
|
|
1824
|
+
# compute total external potential
|
|
1825
|
+
Pi_ext = - qn*(segments['li'][0] + segments['li'][1])*np.average(w0) \
|
|
1826
|
+
- qn*(L - (segments['li'][0] + segments['li'][1]))*self.tc
|
|
1827
|
+
# Ensure
|
|
1828
|
+
if self.system in ['pst-']:
|
|
1829
|
+
ub = us[-1]
|
|
1830
|
+
elif self.system in ['-pst']:
|
|
1831
|
+
ub = us[0]
|
|
1832
|
+
Pi_ext += - qt*(segments['li'][0] + segments['li'][1])*np.average(us) \
|
|
1833
|
+
- qt*(L - (segments['li'][0] + segments['li'][1]))*ub
|
|
1834
|
+
if self.system not in ['pst-', '-pst']:
|
|
1835
|
+
print('Input error: Only pst-setup implemented at the moment.')
|
|
1836
|
+
|
|
1837
|
+
return Pi_ext
|
|
1838
|
+
|
|
1839
|
+
def internal_potential(self, C, phi, L, **segments):
|
|
1840
|
+
"""
|
|
1841
|
+
Compute total internal potential (pst only).
|
|
1842
|
+
|
|
1843
|
+
Arguments
|
|
1844
|
+
---------
|
|
1845
|
+
C : ndarray
|
|
1846
|
+
Matrix(6xN) of solution constants for a system of N
|
|
1847
|
+
segements. Columns contain the 6 constants of each segement.
|
|
1848
|
+
phi : float
|
|
1849
|
+
Inclination of the slab (°).
|
|
1850
|
+
L : float, optional
|
|
1851
|
+
Total length of model (mm).
|
|
1852
|
+
segments : dict
|
|
1853
|
+
Dictionary with lists of touchdown booleans (tdi), segement
|
|
1854
|
+
lengths (li), skier weights (mi), and foundation booleans
|
|
1855
|
+
in the cracked (ki) and uncracked (k0) configurations.
|
|
1856
|
+
|
|
1857
|
+
Returns
|
|
1858
|
+
-------
|
|
1859
|
+
Pi_int : float
|
|
1860
|
+
Total internal potential (Nmm).
|
|
1861
|
+
"""
|
|
1862
|
+
# Rasterize solution
|
|
1863
|
+
xq, zq, xb = self.rasterize_solution(C=C, phi=phi, **segments)
|
|
1864
|
+
|
|
1865
|
+
# Compute section forces
|
|
1866
|
+
N, M, V = self.N(zq), self.M(zq), self.V(zq)
|
|
1867
|
+
|
|
1868
|
+
# Drop parts of the solution that are not a foundation
|
|
1869
|
+
zweak = zq[:, ~np.isnan(xb)]
|
|
1870
|
+
xweak = xb[~np.isnan(xb)]
|
|
1871
|
+
|
|
1872
|
+
# Compute weak layer displacements
|
|
1873
|
+
wweak = self.w(zweak)
|
|
1874
|
+
uweak = self.u(zweak, z0=self.h/2)
|
|
1875
|
+
|
|
1876
|
+
# Compute stored energy of the slab (monte-carlo integration)
|
|
1877
|
+
n = len(xq)
|
|
1878
|
+
nweak = len(xweak)
|
|
1879
|
+
# energy share from moment, shear force, wl normal and tangential springs
|
|
1880
|
+
Pi_int = L/2/n/self.A11*np.sum([Ni**2 for Ni in N]) \
|
|
1881
|
+
+ L/2/n/(self.D11-self.B11**2/self.A11)*np.sum([Mi**2 for Mi in M]) \
|
|
1882
|
+
+ L/2/n/self.kA55*np.sum([Vi**2 for Vi in V]) \
|
|
1883
|
+
+ L*self.kn/2/nweak*np.sum([wi**2 for wi in wweak]) \
|
|
1884
|
+
+ L*self.kt/2/nweak*np.sum([ui**2 for ui in uweak])
|
|
1885
|
+
# energy share from substitute rotation spring
|
|
1886
|
+
if self.system in ['pst-']:
|
|
1887
|
+
Pi_int += 1/2*M[-1]*(self.psi(zq)[-1])**2
|
|
1888
|
+
elif self.system in ['-pst']:
|
|
1889
|
+
Pi_int += 1/2*M[0]*(self.psi(zq)[0])**2
|
|
1890
|
+
else:
|
|
1891
|
+
print('Input error: Only pst-setup implemented at the moment.')
|
|
1892
|
+
|
|
1893
|
+
return Pi_int
|
|
1894
|
+
|
|
1895
|
+
def total_potential(self, C, phi, L, **segments):
|
|
1896
|
+
"""
|
|
1897
|
+
Returns total differential potential
|
|
1898
|
+
|
|
1899
|
+
Arguments
|
|
1900
|
+
---------
|
|
1901
|
+
C : ndarray
|
|
1902
|
+
Matrix(6xN) of solution constants for a system of N
|
|
1903
|
+
segements. Columns contain the 6 constants of each segement.
|
|
1904
|
+
phi : float
|
|
1905
|
+
Inclination of the slab (°).
|
|
1906
|
+
L : float, optional
|
|
1907
|
+
Total length of model (mm).
|
|
1908
|
+
segments : dict
|
|
1909
|
+
Dictionary with lists of touchdown booleans (tdi), segement
|
|
1910
|
+
lengths (li), skier weights (mi), and foundation booleans
|
|
1911
|
+
in the cracked (ki) and uncracked (k0) configurations.
|
|
1912
|
+
|
|
1913
|
+
Returns
|
|
1914
|
+
-------
|
|
1915
|
+
Pi : float
|
|
1916
|
+
Total differential potential (Nmm).
|
|
1917
|
+
"""
|
|
1918
|
+
Pi_int = self.internal_potential(C, phi, L, **segments)
|
|
1919
|
+
Pi_ext = self.external_potential(C, phi, L, **segments)
|
|
1920
|
+
|
|
1921
|
+
return Pi_int + Pi_ext
|
|
1469
1922
|
|
|
1470
1923
|
def get_weaklayer_shearstress(self, x, z, unit='MPa', removeNaNs=False):
|
|
1471
1924
|
"""
|