weac 2.3.2__tar.gz → 2.4.1__tar.gz

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.
@@ -8,7 +8,7 @@ authors:
8
8
  - family-names: "Weissgraeber"
9
9
  given-names: "Philipp"
10
10
  orcid: "https://orcid.org/0000-0001-8320-8672"
11
- version: 2.3.2
11
+ version: 2.4.1
12
12
  date-released: 2021-12-30
13
13
  identifiers:
14
14
  - description: Collection of archived snapshots of all versions of WEAC
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: weac
3
- Version: 2.3.2
3
+ Version: 2.4.1
4
4
  Summary: Weak layer anticrack nucleation model
5
5
  Home-page: https://github.com/2phi/weac
6
6
  Author: 2phi GbR
@@ -15,8 +15,12 @@ Classifier: Operating System :: OS Independent
15
15
  Classifier: Topic :: Scientific/Engineering
16
16
  Requires-Python: >=3.10
17
17
  Description-Content-Type: text/markdown
18
- Provides-Extra: interactive
19
18
  License-File: LICENSE
19
+ Requires-Dist: matplotlib
20
+ Requires-Dist: numpy
21
+ Requires-Dist: scipy
22
+ Provides-Extra: interactive
23
+ Requires-Dist: jupyter; extra == "interactive"
20
24
 
21
25
  <!-- LOGO AND TITLE-->
22
26
  <!-- <p align="right"><img src="https://github.com/2phi/weac/raw/main/img/logo.png" alt="Logo" width="80" height="80"></p> -->
@@ -1,6 +1,6 @@
1
1
  [metadata]
2
2
  name = weac
3
- version = 2.3.2
3
+ version = 2.4.1
4
4
  author = 2phi GbR
5
5
  author_email = mail@2phi.de
6
6
  description = Weak layer anticrack nucleation model
@@ -11,7 +11,7 @@ from weac.inverse import Inverse
11
11
  from weac import plot
12
12
 
13
13
  # Version
14
- __version__ = '2.3.2'
14
+ __version__ = '2.4.1'
15
15
 
16
16
  # Public names
17
17
  __all__ = [
@@ -96,11 +96,12 @@ class Eigensystem:
96
96
 
97
97
  Arguments
98
98
  ---------
99
- system : {'pst-', '-pst', 'skier', 'skiers'}, optional
99
+ system : {'pst-', '-pst', 'vpst-', '-vpst', 'skier', 'skiers'}, optional
100
100
  Type of system to analyse: PST cut from the right (pst-),
101
- PST cut form the left (-pst), one skier on infinite
102
- slab (skier) or multiple skiers on infinite slab (skeirs).
103
- Default is 'pst-'.
101
+ PST cut form the left (-pst), PST with vertical faces cut
102
+ from the right (vpst-), PST with vertical faces cut from the
103
+ left (-vpst), one skier on infinite slab (skier) or multiple
104
+ skiers on infinite slab (skiers). Default is 'pst-'.
104
105
  layers : list, optional
105
106
  2D list of layer densities and thicknesses. Columns are
106
107
  density (kg/m^3) and thickness (mm). One row corresponds
@@ -110,7 +111,7 @@ class Eigensystem:
110
111
  self.g = 9810 # Gravitaiton (mm/s^2)
111
112
  self.lski = 1000 # Effective out-of-plane length of skis (mm)
112
113
  self.tol = 1e-3 # Relative Romberg integration tolerance
113
- self.system = system # 'pst-', '-pst', 'skier', 'skiers'
114
+ self.system = system # 'pst-', '-pst', 'vpst-', '-vpst', 'skier', 'skiers'
114
115
 
115
116
  # Initialize weak-layer attributes that will be filled later
116
117
  self.weak = False # Weak-layer properties dictionary
@@ -141,9 +142,9 @@ class Eigensystem:
141
142
  self.sR = False # Stability shift of real eigenvalues
142
143
 
143
144
  # Initialize touchdown attributes
145
+ self.touchdown = False # Flag whether touchdown is possible
144
146
  self.lC = False # Minimum length of substratum contact (mm)
145
- self.lS = False # Maximum length of span between
146
- # between bedded and touchdowned boundary (mm)
147
+ self.lS = False # Maximum span between bedded and touchdown (mm)
147
148
  self.ratio = False # Stiffness ratio of collalpsed to uncollapsed weak-layer
148
149
  self.beta = False # Ratio of slab to bedding stiffness
149
150
 
@@ -221,7 +222,7 @@ class Eigensystem:
221
222
 
222
223
  # Compute total slab thickness and center of gravity
223
224
  self.h, self.zs = calc_center_of_gravity(layers)
224
-
225
+
225
226
  # Assemble layering into matrix (top to bottom)
226
227
  # Columns are density (kg/m^3), layer thickness (mm)
227
228
  # Young's modulus (MPa), shear modulus (MPa), and
@@ -509,15 +510,19 @@ class Eigensystem:
509
510
  First coefficient for sixth order term,
510
511
  second coefficient for fith order term and so on.
511
512
  """
512
- a1 = self.kA55**2*kR1*kN1*q0
513
- a2 = 6*self.kA55*(self.D11*self.kA55 + kR1*kR2)*kN1*q0
514
- a3 = 30*self.D11*self.kA55*(kR1 + kR2)*kN1*q0
515
- a4 = 24*self.D11*(2*self.kA55**2*kR1 + 3*self.D11*self.kA55*kN1 + 3*kR1*kR2*kN1)*q0
516
- a5 = 72*self.D11*(self.D11*(self.kA55**2 + (kR1 + kR2)*kN1)*q0 \
517
- + self.kA55*kR1*(2*kR2*q0 - self.kA55*kN1*self.tc))
518
- a6 = 144*self.D11*self.kA55*(self.D11*(kR1 + kR2)*q0 \
519
- - (self.D11*self.kA55 + kR1*kR2)*kN1*self.tc)
520
- a7 = - 144*self.D11**2*self.kA55*(kR1 + kR2)*kN1*self.tc
513
+ kA55 = self.kA55
514
+ D11 = self.D11
515
+ tc = self.tc
516
+
517
+ a1 = kA55**2*kR1*kN1*q0
518
+ a2 = 6*kA55*(D11*kA55 + kR1*kR2)*kN1*q0
519
+ a3 = 30*D11*kA55*(kR1 + kR2)*kN1*q0
520
+ a4 = 24*D11*(2*kA55**2*kR1 + 3*D11*kA55*kN1 + 3*kR1*kR2*kN1)*q0
521
+ a5 = 72*D11*(D11*(kA55**2 + (kR1 + kR2)*kN1)*q0 \
522
+ + kA55*kR1*(2*kR2*q0 - kA55*kN1*tc))
523
+ a6 = 144*D11*kA55*(D11*(kR1 + kR2)*q0 \
524
+ - (D11*kA55 + kR1*kR2)*kN1*tc)
525
+ a7 = - 144*D11**2*kA55*(kR1 + kR2)*kN1*tc
521
526
  return [a1,a2,a3,a4,a5,a6,a7]
522
527
 
523
528
  # Get spring stiffnesses for adjacent segment with intact weak-layer
@@ -692,7 +697,7 @@ class Eigensystem:
692
697
  A11 = self.A11
693
698
  B11 = self.B11
694
699
  kA55 = self.kA55
695
- E0 = self.K0
700
+ K0 = self.K0
696
701
 
697
702
  # Unpack geometric properties
698
703
  h = self.h
@@ -711,13 +716,13 @@ class Eigensystem:
711
716
  [0]])
712
717
  else:
713
718
  zp = np.array([
714
- [(-3*(qt + pt)/A11 - B11*(qn + pn)*x/E0)/6*x**2],
715
- [(-2*(qt + pt)/A11 - B11*(qn + pn)*x/E0)/2*x],
716
- [-A11*(qn + pn)*x**4/(24*E0)],
717
- [-A11*(qn + pn)*x**3/(6*E0)],
718
- [A11*(qn + pn)*x**3/(6*E0)
719
+ [(-3*(qt + pt)/A11 - B11*(qn + pn)*x/K0)/6*x**2],
720
+ [(-2*(qt + pt)/A11 - B11*(qn + pn)*x/K0)/2*x],
721
+ [-A11*(qn + pn)*x**4/(24*K0)],
722
+ [-A11*(qn + pn)*x**3/(6*K0)],
723
+ [A11*(qn + pn)*x**3/(6*K0)
719
724
  + ((zs - B11/A11)*qt - h*pt/2 - (qn + pn)*x)/kA55],
720
- [(qn + pn)*(A11*x**2/(2*E0) - 1/kA55)]])
725
+ [(qn + pn)*(A11*x**2/(2*K0) - 1/kA55)]])
721
726
 
722
727
  return zp
723
728
 
@@ -26,11 +26,12 @@ class Layered(FieldQuantitiesMixin, SolutionMixin, AnalysisMixin,
26
26
 
27
27
  Arguments
28
28
  ---------
29
- system : {'pst-', '-pst', 'skier', 'skiers'}, optional
29
+ system : {'pst-', '-pst', 'vpst-', '-vpst', 'skier', 'skiers'}, optional
30
30
  Type of system to analyse: PST cut from the right (pst-),
31
- PST cut form the left (-pst), one skier on infinite
32
- slab (skier) or multiple skiers on infinite slab (skiers).
33
- Default is 'pst-'.
31
+ PST cut form the left (-pst), PST with vertical faces cut
32
+ from the right (vpst-), PST with vertical faces cut from the
33
+ left (-vpst), one skier on infinite slab (skier) or multiple
34
+ skiers on infinite slab (skiers). Default is 'pst-'.
34
35
  layers : list, optional
35
36
  2D list of layer densities and thicknesses. Columns are
36
37
  density(kg/m ^ 3) and thickness(mm). One row corresponds
@@ -4,13 +4,12 @@
4
4
  # Standard library imports
5
5
  from functools import partial
6
6
 
7
-
8
7
  # Third party imports
9
8
  import numpy as np
10
9
  from scipy.integrate import romberg, cumulative_trapezoid
11
10
 
12
11
  # Module imports
13
- from weac.tools import tensile_strength_slab
12
+ from weac.tools import tensile_strength_slab, calc_vertical_bc_center_of_gravity
14
13
 
15
14
 
16
15
  class FieldQuantitiesMixin:
@@ -510,15 +509,16 @@ class SolutionMixin:
510
509
  A - free end, B - intermediate touchdown,
511
510
  C - full touchdown (maximum clamped end).
512
511
  """
513
- # Classify boundary type by element length
514
- if l <= self.lC:
515
- mode = 'A'
516
- elif self.lC < l <= self.lS:
517
- mode = 'B'
518
- elif self.lS < l:
519
- mode = 'C'
520
-
521
- return mode
512
+ if self.touchdown:
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'
522
522
 
523
523
  def reduce_stiffness(self, l=0, mode='A'):
524
524
  """
@@ -586,28 +586,39 @@ class SolutionMixin:
586
586
  if not k:
587
587
  if mode in ['A']:
588
588
  # Free end
589
- bc = np.array([self.N(z),
590
- self.M(z),
591
- self.V(z)
592
- ])
589
+ bc = np.array([
590
+ self.N(z),
591
+ self.M(z),
592
+ self.V(z)
593
+ ])
593
594
  elif mode in ['B', 'C'] and pos in ['r', 'right']:
594
595
  # Touchdown right
595
- bc = np.array([self.N(z),
596
- self.M(z) + kf*kR*self.psi(z),
597
- self.w(z)
598
- ])
596
+ bc = np.array([
597
+ self.N(z),
598
+ self.M(z) + kf*kR*self.psi(z),
599
+ self.w(z)
600
+ ])
599
601
  elif mode in ['B', 'C'] and pos in ['l', 'left']:
600
602
  # Touchdown left
601
- bc = np.array([self.N(z),
602
- self.M(z) - kf*kR*self.psi(z),
603
- self.w(z)
604
- ])
603
+ bc = np.array([
604
+ self.N(z),
605
+ self.M(z) - kf*kR*self.psi(z),
606
+ self.w(z)
607
+ ])
605
608
  else:
606
609
  # Free end
607
- bc = np.array([self.N(z),
608
- self.M(z),
609
- self.V(z)
610
- ])
610
+ bc = np.array([
611
+ self.N(z),
612
+ self.M(z),
613
+ self.V(z)
614
+ ])
615
+ # Set boundary conditions for PST-systems with vertical faces
616
+ elif self.system in ['-vpst', 'vpst-']:
617
+ bc = np.array([
618
+ self.N(z),
619
+ self.M(z),
620
+ self.V(z)
621
+ ])
611
622
  # Set boundary conditions for SKIER-systems
612
623
  elif self.system in ['skier', 'skiers']:
613
624
  # Infinite end (vanishing complementary solution)
@@ -653,40 +664,40 @@ class SolutionMixin:
653
664
  """
654
665
  if pos in ('l', 'left'):
655
666
  eqs = np.array([
656
- self.bc(zl, l, k, pos)[0], # Left boundary condition
657
- self.bc(zl, l, k, pos)[1], # Left boundary condition
658
- self.bc(zl, l, k, pos)[2], # Left boundary condition
659
- self.u(zr, z0=0), # ui(xi = li)
660
- self.w(zr), # wi(xi = li)
661
- self.psi(zr), # psii(xi = li)
662
- self.N(zr), # Ni(xi = li)
663
- self.M(zr), # Mi(xi = li)
664
- self.V(zr)]) # Vi(xi = li)
667
+ self.bc(zl, l, k, pos)[0], # Left boundary condition
668
+ self.bc(zl, l, k, pos)[1], # Left boundary condition
669
+ self.bc(zl, l, k, pos)[2], # Left boundary condition
670
+ self.u(zr, z0=0), # ui(xi = li)
671
+ self.w(zr), # wi(xi = li)
672
+ self.psi(zr), # psii(xi = li)
673
+ self.N(zr), # Ni(xi = li)
674
+ self.M(zr), # Mi(xi = li)
675
+ self.V(zr)]) # Vi(xi = li)
665
676
  elif pos in ('m', 'mid'):
666
677
  eqs = np.array([
667
- -self.u(zl, z0=0), # -ui(xi = 0)
668
- -self.w(zl), # -wi(xi = 0)
669
- -self.psi(zl), # -psii(xi = 0)
670
- -self.N(zl), # -Ni(xi = 0)
671
- -self.M(zl), # -Mi(xi = 0)
672
- -self.V(zl), # -Vi(xi = 0)
673
- self.u(zr, z0=0), # ui(xi = li)
674
- self.w(zr), # wi(xi = li)
675
- self.psi(zr), # psii(xi = li)
676
- self.N(zr), # Ni(xi = li)
677
- self.M(zr), # Mi(xi = li)
678
- self.V(zr)]) # Vi(xi = li)
678
+ -self.u(zl, z0=0), # -ui(xi = 0)
679
+ -self.w(zl), # -wi(xi = 0)
680
+ -self.psi(zl), # -psii(xi = 0)
681
+ -self.N(zl), # -Ni(xi = 0)
682
+ -self.M(zl), # -Mi(xi = 0)
683
+ -self.V(zl), # -Vi(xi = 0)
684
+ self.u(zr, z0=0), # ui(xi = li)
685
+ self.w(zr), # wi(xi = li)
686
+ self.psi(zr), # psii(xi = li)
687
+ self.N(zr), # Ni(xi = li)
688
+ self.M(zr), # Mi(xi = li)
689
+ self.V(zr)]) # Vi(xi = li)
679
690
  elif pos in ('r', 'right'):
680
691
  eqs = np.array([
681
- -self.u(zl, z0=0), # -ui(xi = 0)
682
- -self.w(zl), # -wi(xi = 0)
683
- -self.psi(zl), # -psii(xi = 0)
684
- -self.N(zl), # -Ni(xi = 0)
685
- -self.M(zl), # -Mi(xi = 0)
686
- -self.V(zl), # -Vi(xi = 0)
687
- self.bc(zr, l, k, pos)[0], # Right boundary condition
688
- self.bc(zr, l, k, pos)[1], # Right boundary condition
689
- self.bc(zr, l, k, pos)[2]]) # Right boundary condition
692
+ -self.u(zl, z0=0), # -ui(xi = 0)
693
+ -self.w(zl), # -wi(xi = 0)
694
+ -self.psi(zl), # -psii(xi = 0)
695
+ -self.N(zl), # -Ni(xi = 0)
696
+ -self.M(zl), # -Mi(xi = 0)
697
+ -self.V(zl), # -Vi(xi = 0)
698
+ self.bc(zr, l, k, pos)[0], # Right boundary condition
699
+ self.bc(zr, l, k, pos)[1], # Right boundary condition
700
+ self.bc(zr, l, k, pos)[2]]) # Right boundary condition
690
701
  else:
691
702
  raise ValueError(
692
703
  (f'Invalid position argument {pos} given. '
@@ -719,10 +730,10 @@ class SolutionMixin:
719
730
  Used for system 'skiers'.
720
731
  L : float, optional
721
732
  Total length of model (mm). Used for systems 'pst-', '-pst',
722
- and 'skier'.
733
+ 'vpst-', '-vpst', and 'skier'.
723
734
  a : float, optional
724
- Crack length (mm). Used for systems 'pst-', '-pst', and
725
- 'skier'.
735
+ Crack length (mm). Used for systems 'pst-', '-pst', 'pst-',
736
+ '-pst', and 'skier'.
726
737
  phi : float, optional
727
738
  Inclination (degree).
728
739
  m : float, optional
@@ -752,12 +763,22 @@ class SolutionMixin:
752
763
  ki = np.array(ki) # Crack
753
764
  k0 = np.array(k0) # No crack
754
765
  elif self.system == 'pst-':
755
- li = np.array([L - a, lU]) # Segment lengths
766
+ li = np.array([L - a, lU]) # Segment lengths
756
767
  mi = np.array([0]) # Skier weights
757
768
  ki = np.array([True, False]) # Crack
758
769
  k0 = np.array([True, True]) # No crack
759
770
  elif self.system == '-pst':
760
- li = np.array([lU, L - a]) # Segment lengths
771
+ li = np.array([lU, L - a]) # Segment lengths
772
+ mi = np.array([0]) # Skier weights
773
+ ki = np.array([False, True]) # Crack
774
+ k0 = np.array([True, True]) # No crack
775
+ elif self.system == 'vpst-':
776
+ li = np.array([L - a, a]) # Segment lengths
777
+ mi = np.array([0]) # Skier weights
778
+ ki = np.array([True, False]) # Crack
779
+ k0 = np.array([True, True]) # No crack
780
+ elif self.system == '-vpst':
781
+ li = np.array([a, L - a]) # Segment lengths
761
782
  mi = np.array([0]) # Skier weights
762
783
  ki = np.array([False, True]) # Crack
763
784
  k0 = np.array([True, True]) # No crack
@@ -820,7 +841,7 @@ class SolutionMixin:
820
841
  raise ValueError('Make sure len(li)=N, len(ki)=N, and '
821
842
  'len(mi)=N-1 for a system of N segments.')
822
843
 
823
- if self.system not in ['pst-', '-pst']:
844
+ if self.system not in ['pst-', '-pst', 'vpst-', '-vpst']:
824
845
  # Boundary segments must be on foundation for infinite BCs
825
846
  if not all([ki[0], ki[-1]]):
826
847
  raise ValueError('Provide supported boundary segments in '
@@ -885,17 +906,34 @@ class SolutionMixin:
885
906
  if self.system not in ['pst-', '-pst']:
886
907
  rhs[:3] = self.bc(self.zp(x=0, phi=phi, bed=ki[0]))
887
908
  rhs[-3:] = self.bc(self.zp(x=li[-1], phi=phi, bed=ki[-1]))
888
-
889
- # Loop through segments to set touchdown at rhs
890
- for i in range(nS):
891
- # Length, foundation and position of segment i
892
- l, k, pos = li[i], ki[i], pi[i]
893
- mode = self.mode_td(l=l)
894
- if not k and bool(mode in ['B', 'C']):
895
- if i==0:
896
- rhs[:3] = np.vstack([0,0,self.tc])
897
- if i == (nS - 1):
898
- rhs[-3:] = np.vstack([0,0,self.tc])
909
+
910
+ # Set rhs for vertical faces
911
+ if self.system in ['vpst-', '-vpst']:
912
+ # Calculate center of gravity and mass of
913
+ # added or cut off slab segement
914
+ xs, zs, m = calc_vertical_bc_center_of_gravity(self.slab, phi)
915
+ # Convert slope angle to radians
916
+ phi = np.deg2rad(phi)
917
+ # Translate inbto section forces and moments
918
+ N = -self.g*m*np.sin(phi)
919
+ M = -self.g*m*(xs*np.cos(phi) + zs*np.sin(phi))
920
+ V = self.g*m*np.cos(phi)
921
+ # Add to right-hand side
922
+ rhs[:3] = np.vstack([N, M, V]) # left end
923
+ rhs[-3:] = np.vstack([N, M, V]) # right end
924
+
925
+ # Set touchdown boundary conditions
926
+ elif self.system in ['pst-', '-pst']:
927
+ # Loop through segments to set touchdown at rhs
928
+ for i in range(nS):
929
+ # Length, foundation and position of segment i
930
+ l, k, pos = li[i], ki[i], pi[i]
931
+ mode = self.mode_td(l=l)
932
+ if not k and bool(mode in ['B', 'C']):
933
+ if i==0:
934
+ rhs[:3] = np.vstack([0,0,self.tc])
935
+ if i == (nS - 1):
936
+ rhs[-3:] = np.vstack([0,0,self.tc])
899
937
 
900
938
  # --- SOLVE -----------------------------------------------------------
901
939
 
@@ -1091,7 +1129,7 @@ class AnalysisMixin:
1091
1129
  # Solution at crack tip
1092
1130
  z = self.z(li[idx], C[:, [idx]], li[idx], phi, bed=ki[idx])
1093
1131
  # Mode I and II differential energy release rates
1094
- Gdif[1:, j] = self.Gi(z, unit=unit), self.Gii(z, unit=unit)
1132
+ Gdif[1:, j] = np.concatenate((self.Gi(z, unit=unit), self.Gii(z, unit=unit)))
1095
1133
 
1096
1134
  # Sum mode I and II contributions
1097
1135
  Gdif[0, :] = Gdif[1, :] + Gdif[2, :]
@@ -349,7 +349,7 @@ def deformed(instance, xsl, xwl, z, phi, dz=2, scale=100,
349
349
  'k', linewidth=1)
350
350
 
351
351
  # Plot deformed weak-layer outline
352
- if instance.system in ['-pst', 'pst-']:
352
+ if instance.system in ['-pst', 'pst-', '-vpst', 'vpst-']:
353
353
  nanmask = np.isfinite(xwl)
354
354
  plt.plot(outline(Xwl[:, nanmask] + scale*Uwl[:, nanmask]),
355
355
  outline(Zwl[:, nanmask] + scale*Wwl[:, nanmask]),
@@ -49,6 +49,7 @@ def load_dummy_profile(profile_id):
49
49
  'e': [hard, soft, soft],
50
50
  'f': [soft, soft, hard],
51
51
  # Homogeneous
52
+ 'h': [medium, medium, medium],
52
53
  'soft': [soft, soft, soft],
53
54
  'medium': [medium, medium, medium],
54
55
  'hard': [hard, hard, hard],
@@ -75,7 +76,7 @@ def calc_center_of_gravity(layers):
75
76
 
76
77
  Arguments
77
78
  ---------
78
- layers : list
79
+ layers : ndarray
79
80
  2D list of layer densities and thicknesses. Columns are
80
81
  density (kg/m^3) and thickness (mm). One row corresponds
81
82
  to one layer.
@@ -88,10 +89,10 @@ def calc_center_of_gravity(layers):
88
89
  Z-coordinate of center of gravity (mm).
89
90
  """
90
91
  # Layering info for center of gravity calculation (bottom to top)
91
- n = layers.shape[0] # Number of layers
92
- rho = np.flipud(layers[:, 0]) # Layer densities
93
- h = np.flipud(layers[:, 1]) # Layer thicknesses
94
- H = sum(h) # Total slab thickness
92
+ n = layers.shape[0] # Number of layers
93
+ rho = 1e-12*np.flipud(layers[:, 0]) # Layer densities (kg/m^3 -> t/mm^3)
94
+ h = np.flipud(layers[:, 1]) # Layer thicknesses
95
+ H = sum(h) # Total slab thickness
95
96
  # Layer center coordinates (bottom to top)
96
97
  zi = [H/2 - sum(h[0:j]) - h[j]/2 for j in range(n)]
97
98
  # Z-coordinate of the center of gravity
@@ -100,6 +101,61 @@ def calc_center_of_gravity(layers):
100
101
  return H, zs
101
102
 
102
103
 
104
+ def calc_vertical_bc_center_of_gravity(slab, phi):
105
+ """
106
+ Calculate center of gravity of triangular slab segements for vertical PSTs.
107
+
108
+ Parameters
109
+ ----------
110
+ slab : ndarray
111
+ List of layer densities, thicknesses, and elastic properties.
112
+ Columns are density (kg/m^3), thickness (mm), Young's modulus
113
+ (MPa), shear modulus (MPa), and Poisson's ratio. One row corresponds
114
+ to one layer.
115
+ phi : fload
116
+ Slope angle (deg).
117
+
118
+ Returns
119
+ -------
120
+ xs : float
121
+ Horizontal coordinate of center of gravity (mm).
122
+ zs : float
123
+ Vertical coordinate of center of gravity (mm).
124
+ w : ndarray
125
+ Weight of the slab segment that is cut off or added (t).
126
+ """
127
+ # Convert slope angle to radians
128
+ phi = np.deg2rad(phi)
129
+
130
+ # Catch flat-field case
131
+ if phi == 0:
132
+ xs = 0
133
+ zs = 0
134
+ w = 0
135
+ else:
136
+ # Layering info for center of gravity calculation (top to bottom)
137
+ n = slab.shape[0] # Number of slab
138
+ rho = 1e-12*slab[:, 0] # Layer densities (kg/m^3 -> t/mm^3)
139
+ hi = slab[:, 1] # Layer thicknesses
140
+ H = sum(hi) # Total slab thickness
141
+ # Layer coordinates z_i (top to bottom)
142
+ z = np.array([-H/2 + sum(hi[0:j]) for j in range(n + 1)])
143
+ zi = z[:-1] # z_i
144
+ zii = z[1:] # z_{i+1}
145
+ # Center of gravity of all layers (top to bottom)
146
+ zsi = zi + hi/3*(3/2*H - zi - 2*zii)/(H - zi - zii)
147
+ # Surface area of all layers (top to bottom)
148
+ Ai = hi/2*(H - zi - zii)*np.tan(phi)
149
+ # Center of gravity in vertical direction
150
+ zs = sum(zsi*rho*Ai)/sum(rho*Ai)
151
+ # Center of gravity in horizontal direction
152
+ xs = (H/2 - zs)*np.tan(phi/2)
153
+ # Weight of added or cut off slab segments (t)
154
+ w = sum(Ai*rho)
155
+
156
+ # Return center of gravity and weight of slab segment
157
+ return xs, zs, w
158
+
103
159
  def scapozza(rho):
104
160
  """
105
161
  Compute Young's modulus (MPa) from density (kg/m^3).
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes