weac 2.5.2__tar.gz → 2.6.0__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.
- {weac-2.5.2 → weac-2.6.0}/CITATION.cff +4 -1
- weac-2.6.0/LICENSE +24 -0
- {weac-2.5.2 → weac-2.6.0}/PKG-INFO +22 -10
- {weac-2.5.2 → weac-2.6.0}/README.md +20 -8
- {weac-2.5.2 → weac-2.6.0}/build/weac.egg-info/SOURCES.txt +5 -0
- {weac-2.5.2 → weac-2.6.0}/setup.cfg +1 -1
- weac-2.6.0/tests/test_eigensystem.py +104 -0
- weac-2.6.0/tests/test_layered.py +191 -0
- weac-2.6.0/tests/test_mixins.py +121 -0
- weac-2.6.0/tests/test_plot.py +121 -0
- weac-2.6.0/tests/test_tools.py +41 -0
- {weac-2.5.2 → weac-2.6.0}/weac/__init__.py +1 -1
- weac-2.5.2/LICENSE +0 -3
- {weac-2.5.2 → weac-2.6.0}/MANIFEST.in +0 -0
- {weac-2.5.2 → weac-2.6.0}/img/bc.png +0 -0
- {weac-2.5.2 → weac-2.6.0}/img/layering.png +0 -0
- {weac-2.5.2 → weac-2.6.0}/img/logo.png +0 -0
- {weac-2.5.2 → weac-2.6.0}/img/model.png +0 -0
- {weac-2.5.2 → weac-2.6.0}/img/profiles.png +0 -0
- {weac-2.5.2 → weac-2.6.0}/img/systems.png +0 -0
- {weac-2.5.2 → weac-2.6.0}/pyproject.toml +0 -0
- {weac-2.5.2 → weac-2.6.0}/weac/eigensystem.py +0 -0
- {weac-2.5.2 → weac-2.6.0}/weac/inverse.py +0 -0
- {weac-2.5.2 → weac-2.6.0}/weac/layered.py +0 -0
- {weac-2.5.2 → weac-2.6.0}/weac/mixins.py +0 -0
- {weac-2.5.2 → weac-2.6.0}/weac/plot.py +0 -0
- {weac-2.5.2 → weac-2.6.0}/weac/tools.py +0 -0
|
@@ -8,12 +8,15 @@ authors:
|
|
|
8
8
|
- family-names: "Weissgraeber"
|
|
9
9
|
given-names: "Philipp"
|
|
10
10
|
orcid: "https://orcid.org/0000-0001-8320-8672"
|
|
11
|
-
version: 2.
|
|
11
|
+
version: 2.6.0
|
|
12
12
|
date-released: 2021-12-30
|
|
13
13
|
identifiers:
|
|
14
14
|
- description: Collection of archived snapshots of all versions of WEAC
|
|
15
15
|
type: doi
|
|
16
16
|
value: 10.5281/zenodo.5773113
|
|
17
|
+
- description: Release v2.5 with the implementation of slab touchdown in PST experiments
|
|
18
|
+
type: doi
|
|
19
|
+
value: 10.5281/zenodo.11121171
|
|
17
20
|
- description: Release v2.4 for the analysis of slope-normal and vertical PST boundary conditions
|
|
18
21
|
type: doi
|
|
19
22
|
value: 10.5281/zenodo.10555144
|
weac-2.6.0/LICENSE
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International
|
|
2
|
+
|
|
3
|
+
WEAC (c) 2024 is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/4.0/.
|
|
4
|
+
|
|
5
|
+
You are free to:
|
|
6
|
+
|
|
7
|
+
- Share — copy and redistribute the material in any medium or format
|
|
8
|
+
- Adapt — remix, transform, and build upon the material.
|
|
9
|
+
|
|
10
|
+
Under the following terms:
|
|
11
|
+
|
|
12
|
+
- Attribution — You must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use.
|
|
13
|
+
|
|
14
|
+
- NonCommercial — You may not use the material for commercial purposes.
|
|
15
|
+
|
|
16
|
+
- ShareAlike — If you remix, transform, or build upon the material, you must distribute your contributions under the same license as the original.
|
|
17
|
+
|
|
18
|
+
No additional restrictions — You may not apply legal terms or technological measures that legally restrict others from doing anything the license permits.
|
|
19
|
+
|
|
20
|
+
Notices:
|
|
21
|
+
|
|
22
|
+
You do not have to comply with the license for elements of the material in the public domain or where your use is permitted by an applicable exception or limitation.
|
|
23
|
+
|
|
24
|
+
No warranties are given. The license may not give you all of the permissions necessary for your intended use. For example, other rights such as publicity, privacy, or moral rights may limit how you use the material.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: weac
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.6.0
|
|
4
4
|
Summary: Weak layer anticrack nucleation model
|
|
5
5
|
Home-page: https://github.com/2phi/weac
|
|
6
6
|
Author: 2phi GbR
|
|
@@ -144,12 +144,11 @@ git clone https://github.com/2phi/weac
|
|
|
144
144
|
```
|
|
145
145
|
for local use.
|
|
146
146
|
|
|
147
|
-
Needs
|
|
147
|
+
Needs (see also [requirements.txt](https://github.com/2phi/weac/blob/main/weac/requirements.txt)):
|
|
148
148
|
- [Python](https://www.python.org/downloads/release/python-3100/) ≥ 3.10
|
|
149
|
-
- [Numpy](https://numpy.org/)
|
|
150
|
-
- [Scipy](https://www.scipy.org/)
|
|
151
|
-
- [
|
|
152
|
-
- [Matplotlib](https://matplotlib.org/) for plotting
|
|
149
|
+
- [Numpy](https://numpy.org/) ≥ 2.0.1
|
|
150
|
+
- [Scipy](https://www.scipy.org/) ≥ 1.14.0
|
|
151
|
+
- [Matplotlib](https://matplotlib.org/) ≥ 3.9.1
|
|
153
152
|
|
|
154
153
|
<!-- USAGE EXAMPLES -->
|
|
155
154
|
## Usage
|
|
@@ -284,18 +283,31 @@ See the [open issues](https://github.com/2phi/weac/issues) for a list of propose
|
|
|
284
283
|
5. Open a pull request
|
|
285
284
|
|
|
286
285
|
|
|
287
|
-
<!--
|
|
286
|
+
<!-- WORKFLOWS -->
|
|
288
287
|
## Workflows
|
|
289
288
|
[](https://github.com/2phi/weac/actions/workflows/release.yml)<br>
|
|
290
289
|
[](https://github.com/2phi/weac/actions/workflows/docs.yml)
|
|
291
290
|
|
|
292
291
|
|
|
292
|
+
|
|
293
293
|
<!-- LICENSE -->
|
|
294
294
|
## License
|
|
295
295
|
|
|
296
|
-
|
|
296
|
+
<p xmlns:cc="http://creativecommons.org/ns#" xmlns:dct="http://purl.org/dc/terms/">WEAC is licensed under <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank" rel="license noopener noreferrer" style="display:inline-block;">CC BY-NC-SA 4.0 <img style="height:22px!important;margin-left:3px;vertical-align:text-bottom;" src="https://mirrors.creativecommons.org/presskit/icons/cc.svg?ref=chooser-v1" alt=""><img style="height:22px!important;margin-left:3px;vertical-align:text-bottom;" src="https://mirrors.creativecommons.org/presskit/icons/by.svg?ref=chooser-v1" alt=""><img style="height:22px!important;margin-left:3px;vertical-align:text-bottom;" src="https://mirrors.creativecommons.org/presskit/icons/nc.svg?ref=chooser-v1" alt=""><img style="height:22px!important;margin-left:3px;vertical-align:text-bottom;" src="https://mirrors.creativecommons.org/presskit/icons/sa.svg?ref=chooser-v1" alt=""></a></p>
|
|
297
|
+
|
|
298
|
+
You are free to:
|
|
299
|
+
|
|
300
|
+
- **Share** — copy and redistribute the material in any medium or format
|
|
301
|
+
- **Adapt** — remix, transform, and build upon the material for any purpose, even commercially.
|
|
302
|
+
|
|
303
|
+
Under the following terms:
|
|
304
|
+
|
|
305
|
+
- **Attribution** — You must give [appropriate credit](https://creativecommons.org/licenses/by-nc-sa/4.0/?ref=chooser-v1#ref-appropriate-credit), provide a link to the license, and [indicate if changes were made](https://creativecommons.org/licenses/by-nc-sa/4.0/?ref=chooser-v1#ref-indicate-changes). You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use.
|
|
306
|
+
|
|
307
|
+
- **NonCommercial** — You may not use the material for [commercial purposes](https://creativecommons.org/licenses/by-nc-sa/4.0/?ref=chooser-v1#ref-commercial-purposes).
|
|
308
|
+
|
|
309
|
+
- **ShareAlike** — If you remix, transform, or build upon the material, you must distribute your contributions under the [same license](https://creativecommons.org/licenses/by-nc-sa/4.0/?ref=chooser-v1#ref-same-license) as the original.
|
|
297
310
|
|
|
298
|
-
We currently do not offer an open-source license. Please contact us for private licensing options.
|
|
299
311
|
|
|
300
312
|
|
|
301
313
|
<!-- CONTACT -->
|
|
@@ -120,12 +120,11 @@ git clone https://github.com/2phi/weac
|
|
|
120
120
|
```
|
|
121
121
|
for local use.
|
|
122
122
|
|
|
123
|
-
Needs
|
|
123
|
+
Needs (see also [requirements.txt](https://github.com/2phi/weac/blob/main/weac/requirements.txt)):
|
|
124
124
|
- [Python](https://www.python.org/downloads/release/python-3100/) ≥ 3.10
|
|
125
|
-
- [Numpy](https://numpy.org/)
|
|
126
|
-
- [Scipy](https://www.scipy.org/)
|
|
127
|
-
- [
|
|
128
|
-
- [Matplotlib](https://matplotlib.org/) for plotting
|
|
125
|
+
- [Numpy](https://numpy.org/) ≥ 2.0.1
|
|
126
|
+
- [Scipy](https://www.scipy.org/) ≥ 1.14.0
|
|
127
|
+
- [Matplotlib](https://matplotlib.org/) ≥ 3.9.1
|
|
129
128
|
|
|
130
129
|
<!-- USAGE EXAMPLES -->
|
|
131
130
|
## Usage
|
|
@@ -260,18 +259,31 @@ See the [open issues](https://github.com/2phi/weac/issues) for a list of propose
|
|
|
260
259
|
5. Open a pull request
|
|
261
260
|
|
|
262
261
|
|
|
263
|
-
<!--
|
|
262
|
+
<!-- WORKFLOWS -->
|
|
264
263
|
## Workflows
|
|
265
264
|
[](https://github.com/2phi/weac/actions/workflows/release.yml)<br>
|
|
266
265
|
[](https://github.com/2phi/weac/actions/workflows/docs.yml)
|
|
267
266
|
|
|
268
267
|
|
|
268
|
+
|
|
269
269
|
<!-- LICENSE -->
|
|
270
270
|
## License
|
|
271
271
|
|
|
272
|
-
|
|
272
|
+
<p xmlns:cc="http://creativecommons.org/ns#" xmlns:dct="http://purl.org/dc/terms/">WEAC is licensed under <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank" rel="license noopener noreferrer" style="display:inline-block;">CC BY-NC-SA 4.0 <img style="height:22px!important;margin-left:3px;vertical-align:text-bottom;" src="https://mirrors.creativecommons.org/presskit/icons/cc.svg?ref=chooser-v1" alt=""><img style="height:22px!important;margin-left:3px;vertical-align:text-bottom;" src="https://mirrors.creativecommons.org/presskit/icons/by.svg?ref=chooser-v1" alt=""><img style="height:22px!important;margin-left:3px;vertical-align:text-bottom;" src="https://mirrors.creativecommons.org/presskit/icons/nc.svg?ref=chooser-v1" alt=""><img style="height:22px!important;margin-left:3px;vertical-align:text-bottom;" src="https://mirrors.creativecommons.org/presskit/icons/sa.svg?ref=chooser-v1" alt=""></a></p>
|
|
273
|
+
|
|
274
|
+
You are free to:
|
|
275
|
+
|
|
276
|
+
- **Share** — copy and redistribute the material in any medium or format
|
|
277
|
+
- **Adapt** — remix, transform, and build upon the material for any purpose, even commercially.
|
|
278
|
+
|
|
279
|
+
Under the following terms:
|
|
280
|
+
|
|
281
|
+
- **Attribution** — You must give [appropriate credit](https://creativecommons.org/licenses/by-nc-sa/4.0/?ref=chooser-v1#ref-appropriate-credit), provide a link to the license, and [indicate if changes were made](https://creativecommons.org/licenses/by-nc-sa/4.0/?ref=chooser-v1#ref-indicate-changes). You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use.
|
|
282
|
+
|
|
283
|
+
- **NonCommercial** — You may not use the material for [commercial purposes](https://creativecommons.org/licenses/by-nc-sa/4.0/?ref=chooser-v1#ref-commercial-purposes).
|
|
284
|
+
|
|
285
|
+
- **ShareAlike** — If you remix, transform, or build upon the material, you must distribute your contributions under the [same license](https://creativecommons.org/licenses/by-nc-sa/4.0/?ref=chooser-v1#ref-same-license) as the original.
|
|
273
286
|
|
|
274
|
-
We currently do not offer an open-source license. Please contact us for private licensing options.
|
|
275
287
|
|
|
276
288
|
|
|
277
289
|
<!-- CONTACT -->
|
|
@@ -10,6 +10,11 @@ img/logo.png
|
|
|
10
10
|
img/model.png
|
|
11
11
|
img/profiles.png
|
|
12
12
|
img/systems.png
|
|
13
|
+
tests/test_eigensystem.py
|
|
14
|
+
tests/test_layered.py
|
|
15
|
+
tests/test_mixins.py
|
|
16
|
+
tests/test_plot.py
|
|
17
|
+
tests/test_tools.py
|
|
13
18
|
weac/__init__.py
|
|
14
19
|
weac/eigensystem.py
|
|
15
20
|
weac/inverse.py
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Unit tests for the Eigensystem class in the WEAC package.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import unittest
|
|
6
|
+
|
|
7
|
+
from weac.eigensystem import Eigensystem
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class TestEigensystem(unittest.TestCase):
|
|
11
|
+
"""Test cases for the Eigensystem class."""
|
|
12
|
+
|
|
13
|
+
def setUp(self):
|
|
14
|
+
"""Set up test fixtures."""
|
|
15
|
+
# Create an Eigensystem instance for testing
|
|
16
|
+
self.eigen = Eigensystem(system="pst-")
|
|
17
|
+
|
|
18
|
+
# Set up properties needed for tests
|
|
19
|
+
self.eigen.set_beam_properties([[300, 200]])
|
|
20
|
+
self.eigen.set_foundation_properties()
|
|
21
|
+
|
|
22
|
+
def test_initialization(self):
|
|
23
|
+
"""Test that Eigensystem initializes with correct default values."""
|
|
24
|
+
# Test default initialization
|
|
25
|
+
self.assertEqual(self.eigen.system, "pst-")
|
|
26
|
+
self.assertFalse(self.eigen.touchdown)
|
|
27
|
+
self.assertAlmostEqual(self.eigen.g, 9810.0) # Gravitational constant
|
|
28
|
+
|
|
29
|
+
def test_set_beam_properties(self):
|
|
30
|
+
"""Test setting beam properties with different layer configurations."""
|
|
31
|
+
# Create a new instance to test from scratch
|
|
32
|
+
eigen = Eigensystem(system="pst-")
|
|
33
|
+
|
|
34
|
+
# Test with a single layer
|
|
35
|
+
eigen.set_beam_properties([[300, 200]]) # [density (kg/m^3), thickness (mm)]
|
|
36
|
+
|
|
37
|
+
# Check that slab property is set
|
|
38
|
+
self.assertIsNotNone(eigen.slab)
|
|
39
|
+
# The actual shape might be different from what we expected
|
|
40
|
+
# Let's just check that it's a 2D array with at least one row
|
|
41
|
+
self.assertGreaterEqual(eigen.slab.shape[0], 1)
|
|
42
|
+
|
|
43
|
+
# Test with multiple layers
|
|
44
|
+
eigen.set_beam_properties(
|
|
45
|
+
[
|
|
46
|
+
[200, 100], # [density (kg/m^3), thickness (mm)]
|
|
47
|
+
[300, 150],
|
|
48
|
+
[400, 50],
|
|
49
|
+
]
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
# Check that slab property is updated
|
|
53
|
+
self.assertIsNotNone(eigen.slab)
|
|
54
|
+
# Check that we have the right number of layers
|
|
55
|
+
self.assertEqual(eigen.slab.shape[0], 3)
|
|
56
|
+
|
|
57
|
+
def test_set_foundation_properties(self):
|
|
58
|
+
"""Test setting foundation properties."""
|
|
59
|
+
# Create a new instance to test from scratch
|
|
60
|
+
eigen = Eigensystem(system="pst-")
|
|
61
|
+
|
|
62
|
+
# Test with default parameters
|
|
63
|
+
eigen.set_foundation_properties()
|
|
64
|
+
|
|
65
|
+
# Check that weak layer properties are set
|
|
66
|
+
self.assertIsNotNone(eigen.weak)
|
|
67
|
+
self.assertIn("E", eigen.weak)
|
|
68
|
+
self.assertIn("nu", eigen.weak)
|
|
69
|
+
|
|
70
|
+
# Test with custom parameters
|
|
71
|
+
eigen.set_foundation_properties(
|
|
72
|
+
t=50.0, # Weak layer thickness (mm)
|
|
73
|
+
E=0.5, # Young's modulus (MPa)
|
|
74
|
+
nu=0.3, # Poisson's ratio
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
# Check that weak layer properties are updated
|
|
78
|
+
self.assertIsNotNone(eigen.weak)
|
|
79
|
+
self.assertEqual(eigen.weak["E"], 0.5)
|
|
80
|
+
self.assertEqual(eigen.weak["nu"], 0.3)
|
|
81
|
+
self.assertEqual(eigen.t, 50.0)
|
|
82
|
+
|
|
83
|
+
def test_calc_fundamental_system(self):
|
|
84
|
+
"""Test calculation of the fundamental system."""
|
|
85
|
+
# Calculate the fundamental system
|
|
86
|
+
self.eigen.calc_fundamental_system()
|
|
87
|
+
|
|
88
|
+
# Check that the system has been initialized
|
|
89
|
+
self.assertIsNotNone(
|
|
90
|
+
getattr(self.eigen, "kn", None)
|
|
91
|
+
) # Foundation normal stiffness
|
|
92
|
+
self.assertIsNotNone(
|
|
93
|
+
getattr(self.eigen, "kt", None)
|
|
94
|
+
) # Foundation shear stiffness
|
|
95
|
+
self.assertIsNotNone(getattr(self.eigen, "A11", None)) # Extensional stiffness
|
|
96
|
+
self.assertIsNotNone(
|
|
97
|
+
getattr(self.eigen, "B11", None)
|
|
98
|
+
) # Bending-extension coupling stiffness
|
|
99
|
+
self.assertIsNotNone(getattr(self.eigen, "D11", None)) # Bending stiffness
|
|
100
|
+
self.assertIsNotNone(getattr(self.eigen, "kA55", None)) # Shear stiffness
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
if __name__ == "__main__":
|
|
104
|
+
unittest.main()
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Unit tests for the Layered class in the WEAC package.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import unittest
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
|
|
9
|
+
from weac.layered import Layered
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class TestLayered(unittest.TestCase):
|
|
13
|
+
"""Test cases for the Layered class."""
|
|
14
|
+
|
|
15
|
+
def setUp(self):
|
|
16
|
+
"""Set up test fixtures."""
|
|
17
|
+
# Create a default Layered instance for testing
|
|
18
|
+
self.layered = Layered(system="pst-")
|
|
19
|
+
|
|
20
|
+
# Create a Layered instance with custom parameters
|
|
21
|
+
self.custom_layered = Layered(
|
|
22
|
+
system="skier",
|
|
23
|
+
layers=[[240, 200]], # [density (kg/m^3), thickness (mm)]
|
|
24
|
+
touchdown=True,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
def test_initialization(self):
|
|
28
|
+
"""Test that Layered initializes with correct default values."""
|
|
29
|
+
# Test default initialization
|
|
30
|
+
self.assertEqual(self.layered.system, "pst-")
|
|
31
|
+
self.assertFalse(self.layered.touchdown)
|
|
32
|
+
|
|
33
|
+
# Test custom initialization
|
|
34
|
+
self.assertEqual(self.custom_layered.system, "skier")
|
|
35
|
+
self.assertTrue(self.custom_layered.touchdown)
|
|
36
|
+
self.assertEqual(len(self.custom_layered.slab), 1)
|
|
37
|
+
self.assertAlmostEqual(self.custom_layered.slab[0, 0], 240.0) # Density
|
|
38
|
+
self.assertAlmostEqual(self.custom_layered.slab[0, 1], 200.0) # Thickness
|
|
39
|
+
|
|
40
|
+
def test_calc_segments(self):
|
|
41
|
+
"""Test calculation of segments for different systems."""
|
|
42
|
+
# Test for PST cut from right
|
|
43
|
+
self.layered.system = "pst-"
|
|
44
|
+
segments = self.layered.calc_segments(L=1000, a=300)
|
|
45
|
+
|
|
46
|
+
# Check that segments dictionary contains expected keys
|
|
47
|
+
self.assertIn("crack", segments)
|
|
48
|
+
self.assertIn("nocrack", segments)
|
|
49
|
+
self.assertIn("both", segments)
|
|
50
|
+
|
|
51
|
+
# Check segment lengths for crack configuration
|
|
52
|
+
crack_segments = segments["crack"]
|
|
53
|
+
self.assertIn("li", crack_segments)
|
|
54
|
+
self.assertEqual(len(crack_segments["li"]), 2) # Two segments for PST-
|
|
55
|
+
self.assertAlmostEqual(crack_segments["li"][0], 700.0) # First segment length
|
|
56
|
+
self.assertAlmostEqual(crack_segments["li"][1], 300.0) # Second segment length
|
|
57
|
+
|
|
58
|
+
# Test for skier system
|
|
59
|
+
self.layered.system = "skier"
|
|
60
|
+
segments = self.layered.calc_segments()
|
|
61
|
+
|
|
62
|
+
# Check that segments dictionary contains expected keys
|
|
63
|
+
self.assertIn("crack", segments)
|
|
64
|
+
|
|
65
|
+
# Check segment lengths for skier configuration
|
|
66
|
+
skier_segments = segments["crack"]
|
|
67
|
+
self.assertIn("li", skier_segments)
|
|
68
|
+
# Note: The actual implementation returns 4 segments for skier, not 2
|
|
69
|
+
self.assertEqual(len(skier_segments["li"]), 4) # Four segments for skier
|
|
70
|
+
|
|
71
|
+
# Test for multiple skiers
|
|
72
|
+
self.layered.system = "skiers"
|
|
73
|
+
segments = self.layered.calc_segments(
|
|
74
|
+
li=[500, 100, 250, 30, 30, 500],
|
|
75
|
+
ki=[True, True, True, False, False, True],
|
|
76
|
+
mi=[80, 80, 0, 0, 0],
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
# Check that segments dictionary contains expected keys
|
|
80
|
+
self.assertIn("crack", segments)
|
|
81
|
+
|
|
82
|
+
# Check segment lengths for multiple skiers configuration
|
|
83
|
+
skiers_segments = segments["crack"]
|
|
84
|
+
self.assertIn("li", skiers_segments)
|
|
85
|
+
self.assertEqual(len(skiers_segments["li"]), 6) # Six segments as specified
|
|
86
|
+
self.assertAlmostEqual(skiers_segments["li"][0], 500.0)
|
|
87
|
+
self.assertAlmostEqual(skiers_segments["li"][1], 100.0)
|
|
88
|
+
self.assertAlmostEqual(skiers_segments["li"][2], 250.0)
|
|
89
|
+
self.assertAlmostEqual(skiers_segments["li"][3], 30.0)
|
|
90
|
+
self.assertAlmostEqual(skiers_segments["li"][4], 30.0)
|
|
91
|
+
self.assertAlmostEqual(skiers_segments["li"][5], 500.0)
|
|
92
|
+
|
|
93
|
+
def test_assemble_and_solve(self):
|
|
94
|
+
"""Test assembly and solution of the system."""
|
|
95
|
+
# Set up a simple configuration
|
|
96
|
+
self.layered.set_beam_properties([[240, 200]])
|
|
97
|
+
self.layered.set_foundation_properties()
|
|
98
|
+
self.layered.calc_fundamental_system()
|
|
99
|
+
|
|
100
|
+
# Calculate segments
|
|
101
|
+
segments = self.layered.calc_segments(L=1000, a=300)
|
|
102
|
+
|
|
103
|
+
# Assemble and solve the system
|
|
104
|
+
C = self.layered.assemble_and_solve(phi=0, **segments["crack"])
|
|
105
|
+
|
|
106
|
+
# Check that solution vector has correct shape
|
|
107
|
+
self.assertIsNotNone(C)
|
|
108
|
+
self.assertEqual(C.shape, (6, 2)) # 6 state variables, 2 segments
|
|
109
|
+
|
|
110
|
+
# Test with non-zero slope angle
|
|
111
|
+
C_slope = self.layered.assemble_and_solve(phi=30, **segments["crack"])
|
|
112
|
+
self.assertIsNotNone(C_slope)
|
|
113
|
+
self.assertEqual(C_slope.shape, (6, 2))
|
|
114
|
+
|
|
115
|
+
def test_rasterize_solution(self):
|
|
116
|
+
"""Test rasterization of the solution."""
|
|
117
|
+
# Set up a simple configuration
|
|
118
|
+
self.layered.set_beam_properties([[240, 200]])
|
|
119
|
+
self.layered.set_foundation_properties()
|
|
120
|
+
self.layered.calc_fundamental_system()
|
|
121
|
+
|
|
122
|
+
# Calculate segments
|
|
123
|
+
segments = self.layered.calc_segments(L=1000, a=300)
|
|
124
|
+
|
|
125
|
+
# Assemble and solve the system
|
|
126
|
+
C = self.layered.assemble_and_solve(phi=0, **segments["crack"])
|
|
127
|
+
|
|
128
|
+
# Rasterize the solution
|
|
129
|
+
xsl, z, xwl = self.layered.rasterize_solution(C=C, phi=0, **segments["crack"])
|
|
130
|
+
|
|
131
|
+
# Check that output arrays have correct shapes
|
|
132
|
+
self.assertIsNotNone(xsl)
|
|
133
|
+
self.assertIsNotNone(z)
|
|
134
|
+
self.assertIsNotNone(xwl)
|
|
135
|
+
self.assertEqual(z.shape[0], 6) # 6 state variables
|
|
136
|
+
self.assertEqual(xsl.shape, z.shape[1:]) # Same length as state variables
|
|
137
|
+
|
|
138
|
+
# Check that x coordinates are within expected range
|
|
139
|
+
self.assertGreaterEqual(np.min(xsl), 0)
|
|
140
|
+
self.assertLessEqual(np.max(xsl), 1000)
|
|
141
|
+
|
|
142
|
+
def test_gdif(self):
|
|
143
|
+
"""Test calculation of differential energy release rate."""
|
|
144
|
+
# Set up a simple configuration
|
|
145
|
+
self.layered.set_beam_properties([[240, 200]])
|
|
146
|
+
self.layered.set_foundation_properties()
|
|
147
|
+
self.layered.calc_fundamental_system()
|
|
148
|
+
|
|
149
|
+
# Calculate segments
|
|
150
|
+
segments = self.layered.calc_segments(L=1000, a=300)
|
|
151
|
+
|
|
152
|
+
# Assemble and solve the system
|
|
153
|
+
C = self.layered.assemble_and_solve(phi=0, **segments["crack"])
|
|
154
|
+
|
|
155
|
+
# Calculate differential energy release rate
|
|
156
|
+
G = self.layered.gdif(C, phi=0, **segments["crack"])
|
|
157
|
+
|
|
158
|
+
# Check that energy release rate is non-negative
|
|
159
|
+
self.assertIsNotNone(G)
|
|
160
|
+
self.assertEqual(len(G), 3) # Three components: mode I, mode II, and total
|
|
161
|
+
self.assertGreaterEqual(
|
|
162
|
+
G[2], 0
|
|
163
|
+
) # Total energy release rate should be non-negative
|
|
164
|
+
|
|
165
|
+
def test_ginc(self):
|
|
166
|
+
"""Test calculation of incremental energy release rate."""
|
|
167
|
+
# Set up a simple configuration
|
|
168
|
+
self.layered.set_beam_properties([[240, 200]])
|
|
169
|
+
self.layered.set_foundation_properties()
|
|
170
|
+
self.layered.calc_fundamental_system()
|
|
171
|
+
|
|
172
|
+
# Calculate segments for both configurations
|
|
173
|
+
segments = self.layered.calc_segments(L=1000, a=300)
|
|
174
|
+
|
|
175
|
+
# Assemble and solve the system for both configurations
|
|
176
|
+
C0 = self.layered.assemble_and_solve(phi=0, **segments["nocrack"])
|
|
177
|
+
C1 = self.layered.assemble_and_solve(phi=0, **segments["crack"])
|
|
178
|
+
|
|
179
|
+
# Calculate incremental energy release rate
|
|
180
|
+
G = self.layered.ginc(C0, C1, phi=0, **segments["both"])
|
|
181
|
+
|
|
182
|
+
# Check that energy release rate is non-negative
|
|
183
|
+
self.assertIsNotNone(G)
|
|
184
|
+
self.assertEqual(len(G), 3) # Three components: mode I, mode II, and total
|
|
185
|
+
self.assertGreaterEqual(
|
|
186
|
+
G[2], 0
|
|
187
|
+
) # Total energy release rate should be non-negative
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
if __name__ == "__main__":
|
|
191
|
+
unittest.main()
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Unit tests for the mixins module in the WEAC package.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import unittest
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
|
|
9
|
+
from weac.eigensystem import Eigensystem
|
|
10
|
+
from weac.mixins import FieldQuantitiesMixin, SlabContactMixin, SolutionMixin
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class TestClass(FieldQuantitiesMixin, SolutionMixin, SlabContactMixin, Eigensystem):
|
|
14
|
+
"""Test class for mixin testing."""
|
|
15
|
+
|
|
16
|
+
def __init__(self):
|
|
17
|
+
"""Initialize test class."""
|
|
18
|
+
# Initialize parent class
|
|
19
|
+
super().__init__(system="pst-", touchdown=False)
|
|
20
|
+
|
|
21
|
+
# Create a 2D array for Z where the first index is the state variable
|
|
22
|
+
# and the second index is the position
|
|
23
|
+
self.Z = np.zeros((6, 5)) # 6 state variables, 5 positions
|
|
24
|
+
for i in range(6):
|
|
25
|
+
self.Z[i, :] = i + 1 # Each row has values [1,1,1,1,1], [2,2,2,2,2], etc.
|
|
26
|
+
|
|
27
|
+
# Set required attributes for the mixins
|
|
28
|
+
self.h = 200 # slab thickness in mm
|
|
29
|
+
self.td = 0 # touchdown length
|
|
30
|
+
self.t = 1 # weak layer thickness
|
|
31
|
+
self.A11 = 1e6 # axial stiffness
|
|
32
|
+
self.B11 = 1e4 # coupling stiffness
|
|
33
|
+
self.D11 = 1e2 # bending stiffness
|
|
34
|
+
self.kA55 = 1e5 # shear stiffness
|
|
35
|
+
self.kn = 1e3 # normal foundation stiffness
|
|
36
|
+
self.kt = 1e3 # tangential foundation stiffness
|
|
37
|
+
self.system = "pst-" # system type
|
|
38
|
+
self.touchdown = False # touchdown flag
|
|
39
|
+
self.g = 9810 # gravity constant
|
|
40
|
+
self.mode = "A" # touchdown mode
|
|
41
|
+
|
|
42
|
+
# Create slab properties array with columns:
|
|
43
|
+
# density (kg/m^3), thickness (mm), Young's modulus (MPa), shear modulus (MPa), Poisson's ratio
|
|
44
|
+
self.slab = np.array([[300, 200, 1e3, 4e2, 0.25]])
|
|
45
|
+
|
|
46
|
+
self.p = 0 # surface line load
|
|
47
|
+
self.phi = 0 # inclination angle
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class TestFieldQuantitiesMixin(unittest.TestCase):
|
|
51
|
+
"""Test cases for FieldQuantitiesMixin."""
|
|
52
|
+
|
|
53
|
+
def setUp(self):
|
|
54
|
+
"""Set up test fixtures."""
|
|
55
|
+
self.test_obj = TestClass()
|
|
56
|
+
|
|
57
|
+
def test_w(self):
|
|
58
|
+
"""Test calculation of deflection."""
|
|
59
|
+
# Test with default parameters
|
|
60
|
+
result = self.test_obj.w(self.test_obj.Z)
|
|
61
|
+
self.assertIsInstance(result, np.ndarray)
|
|
62
|
+
self.assertEqual(result.shape, (5,)) # Should match number of positions
|
|
63
|
+
self.assertTrue(np.allclose(result, 3)) # Third row of Z
|
|
64
|
+
|
|
65
|
+
# Test with different units
|
|
66
|
+
result_mm = self.test_obj.w(self.test_obj.Z, unit="mm")
|
|
67
|
+
result_cm = self.test_obj.w(self.test_obj.Z, unit="cm")
|
|
68
|
+
self.assertTrue(np.allclose(result_mm, result_cm * 10))
|
|
69
|
+
|
|
70
|
+
def test_dw_dx(self):
|
|
71
|
+
"""Test calculation of deflection derivative."""
|
|
72
|
+
# Test with default parameters
|
|
73
|
+
result = self.test_obj.dw_dx(self.test_obj.Z)
|
|
74
|
+
self.assertIsInstance(result, np.ndarray)
|
|
75
|
+
self.assertEqual(result.shape, (5,)) # Should match number of positions
|
|
76
|
+
self.assertTrue(np.allclose(result, 4)) # Fourth row of Z
|
|
77
|
+
|
|
78
|
+
def test_psi(self):
|
|
79
|
+
"""Test calculation of rotation."""
|
|
80
|
+
# Test with default parameters
|
|
81
|
+
result = self.test_obj.psi(self.test_obj.Z)
|
|
82
|
+
self.assertIsInstance(result, np.ndarray)
|
|
83
|
+
self.assertEqual(result.shape, (5,)) # Should match number of positions
|
|
84
|
+
self.assertTrue(np.allclose(result, 5)) # Fifth row of Z
|
|
85
|
+
|
|
86
|
+
# Test with different units
|
|
87
|
+
result_rad = self.test_obj.psi(self.test_obj.Z, unit="rad")
|
|
88
|
+
result_deg = self.test_obj.psi(self.test_obj.Z, unit="degrees")
|
|
89
|
+
self.assertTrue(np.allclose(result_rad, np.deg2rad(result_deg)))
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class TestSolutionMixin(unittest.TestCase):
|
|
93
|
+
"""Test cases for SolutionMixin."""
|
|
94
|
+
|
|
95
|
+
def setUp(self):
|
|
96
|
+
"""Set up test fixtures."""
|
|
97
|
+
self.test_obj = TestClass()
|
|
98
|
+
|
|
99
|
+
def test_calc_segments(self):
|
|
100
|
+
"""Test calculation of segments."""
|
|
101
|
+
# Test with default parameters
|
|
102
|
+
crack_segments = self.test_obj.calc_segments(L=1000, a=300)
|
|
103
|
+
|
|
104
|
+
# Check that the segments dictionary contains expected keys
|
|
105
|
+
self.assertIn("crack", crack_segments)
|
|
106
|
+
self.assertIn("li", crack_segments["crack"])
|
|
107
|
+
self.assertIn("ki", crack_segments["crack"])
|
|
108
|
+
self.assertIn("mi", crack_segments["crack"])
|
|
109
|
+
|
|
110
|
+
# Check segment lengths
|
|
111
|
+
self.assertEqual(
|
|
112
|
+
len(crack_segments["crack"]["li"]), 2
|
|
113
|
+
) # Should have 2 segments for pst-
|
|
114
|
+
self.assertEqual(crack_segments["crack"]["li"][0], 700) # First segment length
|
|
115
|
+
self.assertEqual(
|
|
116
|
+
crack_segments["crack"]["li"][1], 300
|
|
117
|
+
) # Second segment length (crack length)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
if __name__ == "__main__":
|
|
121
|
+
unittest.main()
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Unit tests for the plot module in the WEAC package.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import unittest
|
|
7
|
+
|
|
8
|
+
import matplotlib.pyplot as plt
|
|
9
|
+
|
|
10
|
+
import weac.plot
|
|
11
|
+
from weac.layered import Layered
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class TestPlot(unittest.TestCase):
|
|
15
|
+
"""Test cases for visualization functions in the plot module."""
|
|
16
|
+
|
|
17
|
+
def setUp(self):
|
|
18
|
+
"""Set up test fixtures."""
|
|
19
|
+
# Create a Layered instance for testing
|
|
20
|
+
self.layered = Layered(system="pst-")
|
|
21
|
+
self.layered.set_beam_properties(
|
|
22
|
+
[[300, 200]]
|
|
23
|
+
) # [density (kg/m^3), thickness (mm)]
|
|
24
|
+
self.layered.set_foundation_properties()
|
|
25
|
+
self.layered.calc_fundamental_system()
|
|
26
|
+
|
|
27
|
+
# Calculate segments
|
|
28
|
+
self.segments = self.layered.calc_segments(L=1000, a=300)
|
|
29
|
+
|
|
30
|
+
# Assemble and solve the system
|
|
31
|
+
self.C = self.layered.assemble_and_solve(phi=0, **self.segments["crack"])
|
|
32
|
+
|
|
33
|
+
# Rasterize the solution
|
|
34
|
+
self.xsl, self.z, self.xwl = self.layered.rasterize_solution(
|
|
35
|
+
C=self.C, phi=0, **self.segments["crack"]
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
# Create plots directory if it doesn't exist
|
|
39
|
+
if not os.path.exists("plots"):
|
|
40
|
+
os.makedirs("plots")
|
|
41
|
+
|
|
42
|
+
def tearDown(self):
|
|
43
|
+
"""Clean up after tests."""
|
|
44
|
+
# Close all matplotlib figures to avoid memory leaks
|
|
45
|
+
plt.close("all")
|
|
46
|
+
|
|
47
|
+
# Clean up plot files
|
|
48
|
+
plot_files = [
|
|
49
|
+
"plots/profile.png",
|
|
50
|
+
"plots/cont.png",
|
|
51
|
+
"plots/disp.png",
|
|
52
|
+
"plots/stress.png",
|
|
53
|
+
]
|
|
54
|
+
for file in plot_files:
|
|
55
|
+
if os.path.exists(file):
|
|
56
|
+
os.remove(file)
|
|
57
|
+
|
|
58
|
+
def test_slab_profile(self):
|
|
59
|
+
"""Test plotting of slab profile."""
|
|
60
|
+
# Test with default parameters
|
|
61
|
+
weac.plot.slab_profile(self.layered)
|
|
62
|
+
|
|
63
|
+
# Check that the plot file was created
|
|
64
|
+
self.assertTrue(os.path.exists("plots/profile.png"))
|
|
65
|
+
|
|
66
|
+
def test_deformed(self):
|
|
67
|
+
"""Test plotting of deformed slab."""
|
|
68
|
+
# Test with default parameters
|
|
69
|
+
weac.plot.deformed(self.layered, xsl=self.xsl, xwl=self.xwl, z=self.z, phi=0)
|
|
70
|
+
|
|
71
|
+
# Check that the plot file was created
|
|
72
|
+
self.assertTrue(os.path.exists("plots/cont.png"))
|
|
73
|
+
|
|
74
|
+
# Test with custom parameters
|
|
75
|
+
weac.plot.deformed(
|
|
76
|
+
self.layered,
|
|
77
|
+
xsl=self.xsl,
|
|
78
|
+
xwl=self.xwl,
|
|
79
|
+
z=self.z,
|
|
80
|
+
phi=0,
|
|
81
|
+
scale=2.0,
|
|
82
|
+
field="w",
|
|
83
|
+
normalize=False,
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
# Check that the plot file was created
|
|
87
|
+
self.assertTrue(os.path.exists("plots/cont.png"))
|
|
88
|
+
|
|
89
|
+
def test_displacements(self):
|
|
90
|
+
"""Test plotting of displacements."""
|
|
91
|
+
# Test with default parameters
|
|
92
|
+
weac.plot.displacements(
|
|
93
|
+
self.layered,
|
|
94
|
+
x=self.xsl,
|
|
95
|
+
z=self.z,
|
|
96
|
+
li=self.segments["crack"]["li"],
|
|
97
|
+
ki=self.segments["crack"]["ki"],
|
|
98
|
+
mi=self.segments["crack"]["mi"], # Add mi parameter
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
# Check that the plot file was created
|
|
102
|
+
self.assertTrue(os.path.exists("plots/disp.png"))
|
|
103
|
+
|
|
104
|
+
def test_stresses(self):
|
|
105
|
+
"""Test plotting of stresses."""
|
|
106
|
+
# Test with default parameters
|
|
107
|
+
weac.plot.stresses(
|
|
108
|
+
self.layered,
|
|
109
|
+
x=self.xwl,
|
|
110
|
+
z=self.z,
|
|
111
|
+
li=self.segments["crack"]["li"],
|
|
112
|
+
ki=self.segments["crack"]["ki"],
|
|
113
|
+
mi=self.segments["crack"]["mi"], # Add mi parameter
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
# Check that the plot file was created
|
|
117
|
+
self.assertTrue(os.path.exists("plots/stress.png"))
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
if __name__ == "__main__":
|
|
121
|
+
unittest.main()
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Unit tests for the tools module in the WEAC package.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import unittest
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
|
|
9
|
+
from weac.tools import bergfeld
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class TestTools(unittest.TestCase):
|
|
13
|
+
"""Test cases for utility functions in the tools module."""
|
|
14
|
+
|
|
15
|
+
def test_bergfeld(self):
|
|
16
|
+
"""Test the Bergfeld function for calculating Young's modulus from density."""
|
|
17
|
+
# Test with a typical snow density
|
|
18
|
+
density = 300 # kg/m^3
|
|
19
|
+
young = bergfeld(density)
|
|
20
|
+
|
|
21
|
+
# Check that the result is positive and within expected range
|
|
22
|
+
self.assertGreater(young, 0)
|
|
23
|
+
|
|
24
|
+
# Test with an array of densities
|
|
25
|
+
densities = np.array([200, 300, 400])
|
|
26
|
+
youngs = bergfeld(densities)
|
|
27
|
+
|
|
28
|
+
# Check that the result is an array of the same shape
|
|
29
|
+
self.assertEqual(youngs.shape, densities.shape)
|
|
30
|
+
|
|
31
|
+
# Check that all values are positive and increasing with density
|
|
32
|
+
self.assertTrue(np.all(youngs > 0))
|
|
33
|
+
self.assertTrue(np.all(np.diff(youngs) > 0))
|
|
34
|
+
|
|
35
|
+
# Test with zero density (should handle gracefully)
|
|
36
|
+
zero_young = bergfeld(0)
|
|
37
|
+
self.assertGreaterEqual(zero_young, 0)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
if __name__ == "__main__":
|
|
41
|
+
unittest.main()
|
weac-2.5.2/LICENSE
DELETED
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|