weac 3.0.1__tar.gz → 3.0.2__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.
Files changed (68) hide show
  1. {weac-3.0.1 → weac-3.0.2}/CITATION.cff +1 -1
  2. {weac-3.0.1/src/weac.egg-info → weac-3.0.2}/PKG-INFO +105 -8
  3. {weac-3.0.1 → weac-3.0.2}/README.md +101 -6
  4. {weac-3.0.1 → weac-3.0.2}/pyproject.toml +3 -5
  5. {weac-3.0.1 → weac-3.0.2}/src/weac/__init__.py +1 -1
  6. {weac-3.0.1 → weac-3.0.2}/src/weac/analysis/criteria_evaluator.py +28 -25
  7. {weac-3.0.1 → weac-3.0.2}/src/weac/analysis/plotter.py +26 -23
  8. {weac-3.0.1 → weac-3.0.2}/src/weac/components/model_input.py +3 -4
  9. {weac-3.0.1 → weac-3.0.2}/src/weac/core/eigensystem.py +21 -21
  10. {weac-3.0.1 → weac-3.0.2}/src/weac/core/scenario.py +7 -7
  11. {weac-3.0.1 → weac-3.0.2}/src/weac/core/slab.py +2 -4
  12. {weac-3.0.1 → weac-3.0.2}/src/weac/core/slab_touchdown.py +3 -3
  13. {weac-3.0.1 → weac-3.0.2}/src/weac/core/system_model.py +8 -9
  14. {weac-3.0.1 → weac-3.0.2}/src/weac/core/unknown_constants_solver.py +11 -14
  15. {weac-3.0.1 → weac-3.0.2}/src/weac/logging_config.py +1 -2
  16. {weac-3.0.1 → weac-3.0.2}/src/weac/utils/snowpilot_parser.py +12 -13
  17. {weac-3.0.1 → weac-3.0.2/src/weac.egg-info}/PKG-INFO +105 -8
  18. {weac-3.0.1 → weac-3.0.2}/src/weac.egg-info/requires.txt +4 -1
  19. {weac-3.0.1 → weac-3.0.2}/tests/analysis/test_criteria_evaluator.py +1 -1
  20. {weac-3.0.1 → weac-3.0.2}/tests/test_regression_simulation.py +3 -3
  21. {weac-3.0.1 → weac-3.0.2}/tests/utils/weac_reference_runner.py +5 -5
  22. {weac-3.0.1 → weac-3.0.2}/LICENSE +0 -0
  23. {weac-3.0.1 → weac-3.0.2}/MANIFEST.in +0 -0
  24. {weac-3.0.1 → weac-3.0.2}/img/bc.png +0 -0
  25. {weac-3.0.1 → weac-3.0.2}/img/layering.png +0 -0
  26. {weac-3.0.1 → weac-3.0.2}/img/logo.png +0 -0
  27. {weac-3.0.1 → weac-3.0.2}/img/model.png +0 -0
  28. {weac-3.0.1 → weac-3.0.2}/img/profiles.png +0 -0
  29. {weac-3.0.1 → weac-3.0.2}/img/systems.png +0 -0
  30. {weac-3.0.1 → weac-3.0.2}/setup.cfg +0 -0
  31. {weac-3.0.1 → weac-3.0.2}/src/weac/analysis/__init__.py +0 -0
  32. {weac-3.0.1 → weac-3.0.2}/src/weac/analysis/analyzer.py +0 -0
  33. {weac-3.0.1 → weac-3.0.2}/src/weac/components/__init__.py +0 -0
  34. {weac-3.0.1 → weac-3.0.2}/src/weac/components/config.py +0 -0
  35. {weac-3.0.1 → weac-3.0.2}/src/weac/components/criteria_config.py +0 -0
  36. {weac-3.0.1 → weac-3.0.2}/src/weac/components/layer.py +0 -0
  37. {weac-3.0.1 → weac-3.0.2}/src/weac/components/scenario_config.py +0 -0
  38. {weac-3.0.1 → weac-3.0.2}/src/weac/components/segment.py +0 -0
  39. {weac-3.0.1 → weac-3.0.2}/src/weac/constants.py +0 -0
  40. {weac-3.0.1 → weac-3.0.2}/src/weac/core/__init__.py +0 -0
  41. {weac-3.0.1 → weac-3.0.2}/src/weac/core/field_quantities.py +0 -0
  42. {weac-3.0.1 → weac-3.0.2}/src/weac/utils/__init__.py +0 -0
  43. {weac-3.0.1 → weac-3.0.2}/src/weac/utils/geldsetzer.py +0 -0
  44. {weac-3.0.1 → weac-3.0.2}/src/weac/utils/misc.py +0 -0
  45. {weac-3.0.1 → weac-3.0.2}/src/weac/utils/snow_types.py +0 -0
  46. {weac-3.0.1 → weac-3.0.2}/src/weac.egg-info/SOURCES.txt +0 -0
  47. {weac-3.0.1 → weac-3.0.2}/src/weac.egg-info/dependency_links.txt +0 -0
  48. {weac-3.0.1 → weac-3.0.2}/src/weac.egg-info/top_level.txt +0 -0
  49. {weac-3.0.1 → weac-3.0.2}/tests/__init__.py +0 -0
  50. {weac-3.0.1 → weac-3.0.2}/tests/analysis/__init__.py +0 -0
  51. {weac-3.0.1 → weac-3.0.2}/tests/analysis/test_analyzer.py +0 -0
  52. {weac-3.0.1 → weac-3.0.2}/tests/components/__init__.py +0 -0
  53. {weac-3.0.1 → weac-3.0.2}/tests/components/test_configs.py +0 -0
  54. {weac-3.0.1 → weac-3.0.2}/tests/components/test_layer.py +0 -0
  55. {weac-3.0.1 → weac-3.0.2}/tests/core/__init__.py +0 -0
  56. {weac-3.0.1 → weac-3.0.2}/tests/core/test_eigensystem.py +0 -0
  57. {weac-3.0.1 → weac-3.0.2}/tests/core/test_field_quantities.py +0 -0
  58. {weac-3.0.1 → weac-3.0.2}/tests/core/test_scenario.py +0 -0
  59. {weac-3.0.1 → weac-3.0.2}/tests/core/test_slab.py +0 -0
  60. {weac-3.0.1 → weac-3.0.2}/tests/core/test_slab_touchdown.py +0 -0
  61. {weac-3.0.1 → weac-3.0.2}/tests/core/test_system_model.py +0 -0
  62. {weac-3.0.1 → weac-3.0.2}/tests/run_tests.py +0 -0
  63. {weac-3.0.1 → weac-3.0.2}/tests/test_comparison_results.py +0 -0
  64. {weac-3.0.1 → weac-3.0.2}/tests/utils/__init__.py +0 -0
  65. {weac-3.0.1 → weac-3.0.2}/tests/utils/json_helpers.py +0 -0
  66. {weac-3.0.1 → weac-3.0.2}/tests/utils/test_json_helpers.py +0 -0
  67. {weac-3.0.1 → weac-3.0.2}/tests/utils/test_misc.py +0 -0
  68. {weac-3.0.1 → weac-3.0.2}/tests/utils/test_snowpilot_parser.py +0 -0
@@ -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: 3.0.1
11
+ version: 3.0.2
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.4
2
2
  Name: weac
3
- Version: 3.0.1
3
+ Version: 3.0.2
4
4
  Summary: Weak layer anticrack nucleation model
5
5
  Author-email: 2phi GbR <mail@2phi.de>
6
6
  License-Expression: MIT
@@ -35,6 +35,9 @@ Requires-Dist: traitlets>=5.14.3; extra == "interactive"
35
35
  Provides-Extra: docs
36
36
  Requires-Dist: sphinx; extra == "docs"
37
37
  Requires-Dist: sphinxawesome-theme; extra == "docs"
38
+ Provides-Extra: build
39
+ Requires-Dist: build; extra == "build"
40
+ Requires-Dist: twine; extra == "build"
38
41
  Provides-Extra: dev
39
42
  Requires-Dist: nbclient>=0.10.0; extra == "dev"
40
43
  Requires-Dist: nbconvert>=7.16.4; extra == "dev"
@@ -55,7 +58,6 @@ Requires-Dist: pycodestyle>=2.11.1; extra == "dev"
55
58
  Requires-Dist: black>=24.4.0; extra == "dev"
56
59
  Requires-Dist: isort>=5.13.0; extra == "dev"
57
60
  Requires-Dist: bump-my-version; extra == "dev"
58
- Requires-Dist: build; extra == "dev"
59
61
  Dynamic: license-file
60
62
 
61
63
  <!-- LOGO AND TITLE-->
@@ -128,12 +130,13 @@ Dynamic: license-file
128
130
 
129
131
  1. [About the project](#about-the-project)
130
132
  2. [Installation](#installation)
131
- 3. [Usage](#usage)
132
- 4. [Roadmap](#roadmap)
133
- 5. [Release history](#release-history)
134
- 6. [How to contribute](#how-to-contribute)
135
- 7. [License](#license)
136
- 8. [Contact](#contact)
133
+ 3. [Development Setup](#development-setup)
134
+ 4. [Usage](#usage)
135
+ 5. [Roadmap](#roadmap)
136
+ 6. [Release history](#release-history)
137
+ 7. [How to contribute](#how-to-contribute)
138
+ 8. [License](#license)
139
+ 9. [Contact](#contact)
137
140
 
138
141
  <!-- ABOUT THE PROJECT -->
139
142
  ## About the project
@@ -189,6 +192,100 @@ Needs (runtime dependencies are declared in [pyproject.toml](https://github.com/
189
192
  - [Snowpylot](https://github.com/connellymk/snowpylot) ≥ 1.1.3
190
193
 
191
194
 
195
+ <!-- DEVELOPMENT SETUP -->
196
+ ## Development Setup
197
+
198
+ This project uses [uv](https://github.com/astral-sh/uv) for fast Python package management and project handling.
199
+
200
+ ### Installing uv
201
+
202
+ Install uv following the [official installation guide](https://github.com/astral-sh/uv#installation):
203
+
204
+ ```bash
205
+ # On macOS and Linux
206
+ curl -LsSf https://astral.sh/uv/install.sh | sh
207
+
208
+ # Using pip (alternative)
209
+ pip install uv
210
+ ```
211
+
212
+ ### Setting up the development environment
213
+
214
+ Clone the repository and set up the development environment:
215
+
216
+ ```bash
217
+ git clone https://github.com/2phi/weac
218
+ cd weac
219
+
220
+ # Install Python 3.12+ if not already available
221
+ # uv will automatically use the version specified in .python-version
222
+
223
+ # For basic setup (if only running the package):
224
+ uv sync
225
+
226
+ # For development (recommended for contributors):
227
+ uv sync --extra dev
228
+
229
+ # Activate the virtual environment
230
+ source .venv/bin/activate # On Windows: .venv\Scripts\activate
231
+ ```
232
+
233
+ ### Running tests
234
+
235
+ Run the test suite using uv:
236
+
237
+ ```bash
238
+ # Run all tests
239
+ uv run python tests/run_tests.py
240
+
241
+ # Or use pytest directly (if installed)
242
+ uv run pytest
243
+ ```
244
+
245
+ ### Code formatting and linting
246
+
247
+ This project uses [ruff](https://github.com/astral-sh/ruff) for fast Python linting and formatting:
248
+
249
+ ```bash
250
+ # Format code
251
+ uv run ruff format .
252
+
253
+ # Check for linting issues
254
+ uv run ruff check .
255
+
256
+ # Fix auto-fixable linting issues
257
+ uv run ruff check . --fix
258
+ ```
259
+
260
+ ### Building the package
261
+
262
+ Build the package for distribution:
263
+
264
+ ```bash
265
+ # Build wheel and source distribution
266
+ uv build
267
+
268
+ # Install in editable mode for development
269
+ uv pip install -e .
270
+ ```
271
+
272
+ ### Additional uv commands
273
+
274
+ ```bash
275
+ # Update dependencies
276
+ uv sync --upgrade
277
+
278
+ # Add a new dependency
279
+ uv add package-name
280
+
281
+ # Add a development dependency
282
+ uv add --dev package-name
283
+
284
+ # Show environment info
285
+ uv run python --version
286
+ uv run pip list
287
+ ```
288
+
192
289
  <!-- USAGE EXAMPLES -->
193
290
  ## Usage
194
291
 
@@ -68,12 +68,13 @@
68
68
 
69
69
  1. [About the project](#about-the-project)
70
70
  2. [Installation](#installation)
71
- 3. [Usage](#usage)
72
- 4. [Roadmap](#roadmap)
73
- 5. [Release history](#release-history)
74
- 6. [How to contribute](#how-to-contribute)
75
- 7. [License](#license)
76
- 8. [Contact](#contact)
71
+ 3. [Development Setup](#development-setup)
72
+ 4. [Usage](#usage)
73
+ 5. [Roadmap](#roadmap)
74
+ 6. [Release history](#release-history)
75
+ 7. [How to contribute](#how-to-contribute)
76
+ 8. [License](#license)
77
+ 9. [Contact](#contact)
77
78
 
78
79
  <!-- ABOUT THE PROJECT -->
79
80
  ## About the project
@@ -129,6 +130,100 @@ Needs (runtime dependencies are declared in [pyproject.toml](https://github.com/
129
130
  - [Snowpylot](https://github.com/connellymk/snowpylot) ≥ 1.1.3
130
131
 
131
132
 
133
+ <!-- DEVELOPMENT SETUP -->
134
+ ## Development Setup
135
+
136
+ This project uses [uv](https://github.com/astral-sh/uv) for fast Python package management and project handling.
137
+
138
+ ### Installing uv
139
+
140
+ Install uv following the [official installation guide](https://github.com/astral-sh/uv#installation):
141
+
142
+ ```bash
143
+ # On macOS and Linux
144
+ curl -LsSf https://astral.sh/uv/install.sh | sh
145
+
146
+ # Using pip (alternative)
147
+ pip install uv
148
+ ```
149
+
150
+ ### Setting up the development environment
151
+
152
+ Clone the repository and set up the development environment:
153
+
154
+ ```bash
155
+ git clone https://github.com/2phi/weac
156
+ cd weac
157
+
158
+ # Install Python 3.12+ if not already available
159
+ # uv will automatically use the version specified in .python-version
160
+
161
+ # For basic setup (if only running the package):
162
+ uv sync
163
+
164
+ # For development (recommended for contributors):
165
+ uv sync --extra dev
166
+
167
+ # Activate the virtual environment
168
+ source .venv/bin/activate # On Windows: .venv\Scripts\activate
169
+ ```
170
+
171
+ ### Running tests
172
+
173
+ Run the test suite using uv:
174
+
175
+ ```bash
176
+ # Run all tests
177
+ uv run python tests/run_tests.py
178
+
179
+ # Or use pytest directly (if installed)
180
+ uv run pytest
181
+ ```
182
+
183
+ ### Code formatting and linting
184
+
185
+ This project uses [ruff](https://github.com/astral-sh/ruff) for fast Python linting and formatting:
186
+
187
+ ```bash
188
+ # Format code
189
+ uv run ruff format .
190
+
191
+ # Check for linting issues
192
+ uv run ruff check .
193
+
194
+ # Fix auto-fixable linting issues
195
+ uv run ruff check . --fix
196
+ ```
197
+
198
+ ### Building the package
199
+
200
+ Build the package for distribution:
201
+
202
+ ```bash
203
+ # Build wheel and source distribution
204
+ uv build
205
+
206
+ # Install in editable mode for development
207
+ uv pip install -e .
208
+ ```
209
+
210
+ ### Additional uv commands
211
+
212
+ ```bash
213
+ # Update dependencies
214
+ uv sync --upgrade
215
+
216
+ # Add a new dependency
217
+ uv add package-name
218
+
219
+ # Add a development dependency
220
+ uv add --dev package-name
221
+
222
+ # Show environment info
223
+ uv run python --version
224
+ uv run pip list
225
+ ```
226
+
132
227
  <!-- USAGE EXAMPLES -->
133
228
  ## Usage
134
229
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "weac"
7
- version = "3.0.1"
7
+ version = "3.0.2"
8
8
  authors = [{ name = "2phi GbR", email = "mail@2phi.de" }]
9
9
  description = "Weak layer anticrack nucleation model"
10
10
  readme = "README.md"
@@ -46,6 +46,7 @@ interactive = [
46
46
  "traitlets>=5.14.3",
47
47
  ]
48
48
  docs = ["sphinx", "sphinxawesome-theme"]
49
+ build = ["build", "twine"]
49
50
  dev = [
50
51
  # Notebook execution (to run demo.ipynb non-interactively)
51
52
  "nbclient>=0.10.0",
@@ -73,9 +74,6 @@ dev = [
73
74
 
74
75
  # Versioning helper matching [tool.bumpversion]
75
76
  "bump-my-version", # e.g. bump-my-version bump patch
76
-
77
- # Building for Testing
78
- "build",
79
77
  ]
80
78
 
81
79
  [tool.setuptools]
@@ -125,7 +123,7 @@ ignore = [
125
123
  ]
126
124
 
127
125
  [tool.bumpversion]
128
- current_version = "3.0.1"
126
+ current_version = "3.0.2"
129
127
 
130
128
  [[tool.bumpversion.files]]
131
129
  filename = "pyproject.toml"
@@ -2,4 +2,4 @@
2
2
  WEAC - Weak Layer Anticrack Nucleation Model
3
3
  """
4
4
 
5
- __version__ = "3.0.1"
5
+ __version__ = "3.0.2"
@@ -9,7 +9,6 @@ import logging
9
9
  import time
10
10
  import warnings
11
11
  from dataclasses import dataclass
12
- from typing import List, Optional, Union
13
12
 
14
13
  # Third party imports
15
14
  import numpy as np
@@ -34,12 +33,12 @@ logger = logging.getLogger(__name__)
34
33
  class CoupledCriterionHistory:
35
34
  """Stores the history of the coupled criterion evaluation."""
36
35
 
37
- skier_weights: List[float]
38
- crack_lengths: List[float]
39
- incr_energies: List[np.ndarray]
40
- g_deltas: List[float]
41
- dist_maxs: List[float]
42
- dist_mins: List[float]
36
+ skier_weights: list[float]
37
+ crack_lengths: list[float]
38
+ incr_energies: list[np.ndarray]
39
+ g_deltas: list[float]
40
+ dist_maxs: list[float]
41
+ dist_mins: list[float]
43
42
 
44
43
 
45
44
  @dataclass
@@ -89,7 +88,7 @@ class CoupledCriterionResult:
89
88
  g_delta: float
90
89
  dist_ERR_envelope: float
91
90
  iterations: int
92
- history: Optional[CoupledCriterionHistory]
91
+ history: CoupledCriterionHistory | None
93
92
  final_system: SystemModel
94
93
  max_dist_stress: float
95
94
  min_dist_stress: float
@@ -130,9 +129,9 @@ class FindMinimumForceResult:
130
129
  Whether the algorithm converged.
131
130
  critical_skier_weight : float
132
131
  The critical skier weight.
133
- new_segments : List[Segment]
132
+ new_segments : list[Segment]
134
133
  The new segments.
135
- old_segments : List[Segment]
134
+ old_segments : list[Segment]
136
135
  The old segments.
137
136
  iterations : int
138
137
  The number of iterations.
@@ -144,9 +143,9 @@ class FindMinimumForceResult:
144
143
 
145
144
  success: bool
146
145
  critical_skier_weight: float
147
- new_segments: List[Segment]
148
- old_segments: List[Segment]
149
- iterations: Optional[int]
146
+ new_segments: list[Segment]
147
+ old_segments: list[Segment]
148
+ iterations: int | None
150
149
  max_dist_stress: float
151
150
  min_dist_stress: float
152
151
 
@@ -203,10 +202,10 @@ class CriteriaEvaluator:
203
202
 
204
203
  def stress_envelope(
205
204
  self,
206
- sigma: Union[float, np.ndarray],
207
- tau: Union[float, np.ndarray],
205
+ sigma: float | np.ndarray,
206
+ tau: float | np.ndarray,
208
207
  weak_layer: WeakLayer,
209
- method: Optional[str] = None,
208
+ method: str | None = None,
210
209
  ) -> np.ndarray:
211
210
  """
212
211
  Evaluate the stress envelope for given stress components.
@@ -672,16 +671,20 @@ class CriteriaEvaluator:
672
671
  UserWarning,
673
672
  )
674
673
  system_copy = copy.deepcopy(system)
674
+ system_copy.config.touchdown = True
675
+ system_copy.update_scenario(scenario_config=ScenarioConfig(phi=0.0))
676
+ l_BC = system.slab_touchdown.l_BC
677
+
675
678
  segments = [
676
679
  Segment(length=5e3, has_foundation=True, m=0.0),
677
- Segment(length=5e3, has_foundation=False, m=0.0),
680
+ Segment(length=2 * l_BC, has_foundation=False, m=0.0),
678
681
  ]
679
682
  scenario_config = ScenarioConfig(
680
683
  system_type="vpst-" if vertical else "pst-",
681
- phi=system.scenario.phi,
682
- cut_length=5e3,
684
+ phi=0.0, # Slab Touchdown works only for flat slab
685
+ cut_length=2 * l_BC,
683
686
  )
684
- system_copy.config.touchdown = True
687
+ # system_copy.config.touchdown = True
685
688
  system_copy.update_scenario(segments=segments, scenario_config=scenario_config)
686
689
  touchdown_distance = system_copy.slab_touchdown.touchdown_distance
687
690
  analyzer = Analyzer(system_copy, printing_enabled=print_call_stats)
@@ -825,7 +828,7 @@ class CriteriaEvaluator:
825
828
  system: SystemModel,
826
829
  search_interval: tuple[float, float] | None = None,
827
830
  target: float = 1,
828
- ) -> tuple[float, List[Segment]]:
831
+ ) -> tuple[float, list[Segment]]:
829
832
  """
830
833
  Finds the minimum crack length required to surpass the energy release rate envelope.
831
834
 
@@ -838,7 +841,7 @@ class CriteriaEvaluator:
838
841
  --------
839
842
  minimum_crack_length: float
840
843
  The minimum crack length required to surpass the energy release rate envelope [mm]
841
- new_segments: List[Segment]
844
+ new_segments: list[Segment]
842
845
  The updated list of segments
843
846
  """
844
847
  old_segments = copy.deepcopy(system.scenario.segments)
@@ -928,7 +931,7 @@ class CriteriaEvaluator:
928
931
  self,
929
932
  system: SystemModel,
930
933
  skier_weight: float,
931
- ) -> tuple[float, List[Segment]]:
934
+ ) -> tuple[float, list[Segment]]:
932
935
  """
933
936
  Finds the resulting anticrack length and updated segment configurations
934
937
  for a given skier weight.
@@ -944,7 +947,7 @@ class CriteriaEvaluator:
944
947
  -------
945
948
  new_crack_length: float
946
949
  The total length of the new cracked segments [mm]
947
- new_segments: List[Segment]
950
+ new_segments: list[Segment]
948
951
  The updated list of segments
949
952
  """
950
953
  logger.info(
@@ -1086,7 +1089,7 @@ class CriteriaEvaluator:
1086
1089
 
1087
1090
  def _find_stress_envelope_crossings(
1088
1091
  self, system: SystemModel, weak_layer: WeakLayer
1089
- ) -> List[float]:
1092
+ ) -> list[float]:
1090
1093
  """
1091
1094
  Finds the exact x-coordinates where the stress envelope is crossed.
1092
1095
  """
@@ -6,7 +6,7 @@ This module provides plotting functions for visualizing the results of the WEAC
6
6
  import colorsys
7
7
  import logging
8
8
  import os
9
- from typing import List, Literal, Optional
9
+ from typing import Literal
10
10
 
11
11
  # Third party imports
12
12
  import matplotlib.colors as mc
@@ -219,9 +219,9 @@ class Plotter:
219
219
 
220
220
  def _get_systems_to_plot(
221
221
  self,
222
- system_model: Optional[SystemModel] = None,
223
- system_models: Optional[List[SystemModel]] = None,
224
- ) -> List[SystemModel]:
222
+ system_model: SystemModel | None = None,
223
+ system_models: list[SystemModel] | None = None,
224
+ ) -> list[SystemModel]:
225
225
  """Determine which systems to plot based on override parameters."""
226
226
  if system_model is not None and system_models is not None:
227
227
  raise ValueError(
@@ -236,7 +236,7 @@ class Plotter:
236
236
  "SystemModel or list of SystemModels"
237
237
  )
238
238
 
239
- def _save_figure(self, filename: str, fig: Optional[Figure] = None):
239
+ def _save_figure(self, filename: str, fig: Figure | None = None):
240
240
  """Save figure with proper formatting."""
241
241
  if fig is None:
242
242
  fig = plt.gcf()
@@ -249,20 +249,20 @@ class Plotter:
249
249
 
250
250
  def plot_slab_profile(
251
251
  self,
252
- weak_layers: List[WeakLayer] | WeakLayer,
253
- slabs: List[Slab] | Slab,
252
+ weak_layers: list[WeakLayer] | WeakLayer,
253
+ slabs: list[Slab] | Slab,
254
254
  filename: str = "slab_profile",
255
- labels: Optional[List[str] | str] = None,
256
- colors: Optional[List[str]] = None,
255
+ labels: list[str] | str | None = None,
256
+ colors: list[str] | None = None,
257
257
  ):
258
258
  """
259
259
  Plot slab layer profiles for comparison.
260
260
 
261
261
  Parameters
262
262
  ----------
263
- weak_layers : List[WeakLayer] | WeakLayer
263
+ weak_layers : list[WeakLayer] | WeakLayer
264
264
  The weak layer or layers to plot.
265
- slabs : List[Slab] | Slab
265
+ slabs : list[Slab] | Slab
266
266
  The slab or slabs to plot.
267
267
  filename : str, optional
268
268
  Filename for saving plot
@@ -281,6 +281,9 @@ class Plotter:
281
281
  if isinstance(slabs, Slab):
282
282
  slabs = [slabs]
283
283
 
284
+ if len(weak_layers) != len(slabs):
285
+ raise ValueError("Number of weak layers must match number of slabs")
286
+
284
287
  if labels is None:
285
288
  labels = [f"System {i + 1}" for i in range(len(weak_layers))]
286
289
  elif isinstance(labels, str):
@@ -642,11 +645,11 @@ class Plotter:
642
645
 
643
646
  def plot_section_forces(
644
647
  self,
645
- system_model: Optional[SystemModel] = None,
646
- system_models: Optional[List[SystemModel]] = None,
648
+ system_model: SystemModel | None = None,
649
+ system_models: list[SystemModel] | None = None,
647
650
  filename: str = "section_forces",
648
- labels: Optional[List[str]] = None,
649
- colors: Optional[List[str]] = None,
651
+ labels: list[str] | None = None,
652
+ colors: list[str] | None = None,
650
653
  ):
651
654
  """
652
655
  Plot section forces (N, M, V) for comparison.
@@ -655,7 +658,7 @@ class Plotter:
655
658
  ----------
656
659
  system_model : SystemModel, optional
657
660
  Single system to plot (overrides default)
658
- system_models : List[SystemModel], optional
661
+ system_models : list[SystemModel], optional
659
662
  Multiple systems to plot (overrides default)
660
663
  filename : str, optional
661
664
  Filename for saving plot
@@ -721,11 +724,11 @@ class Plotter:
721
724
 
722
725
  def plot_energy_release_rates(
723
726
  self,
724
- system_model: Optional[SystemModel] = None,
725
- system_models: Optional[List[SystemModel]] = None,
727
+ system_model: SystemModel | None = None,
728
+ system_models: list[SystemModel] | None = None,
726
729
  filename: str = "ERR",
727
- labels: Optional[List[str]] = None,
728
- colors: Optional[List[str]] = None,
730
+ labels: list[str] | None = None,
731
+ colors: list[str] | None = None,
729
732
  ):
730
733
  """
731
734
  Plot energy release rates (G_I, G_II) for comparison.
@@ -734,7 +737,7 @@ class Plotter:
734
737
  ----------
735
738
  system_model : SystemModel, optional
736
739
  Single system to plot (overrides default)
737
- system_models : List[SystemModel], optional
740
+ system_models : list[SystemModel], optional
738
741
  Multiple systems to plot (overrides default)
739
742
  filename : str, optional
740
743
  Filename for saving plot
@@ -1035,7 +1038,7 @@ class Plotter:
1035
1038
  system_model: SystemModel,
1036
1039
  criteria_evaluator: CriteriaEvaluator,
1037
1040
  all_envelopes: bool = False,
1038
- filename: Optional[str] = None,
1041
+ filename: str | None = None,
1039
1042
  ):
1040
1043
  """
1041
1044
  Plot stress envelope in τ-σ space.
@@ -1074,7 +1077,7 @@ class Plotter:
1074
1077
  weak_layer = system_model.weak_layer
1075
1078
 
1076
1079
  # Define a function to find the root for a given tau
1077
- def find_sigma_for_tau(tau_val, sigma_c, method: Optional[str] = None):
1080
+ def find_sigma_for_tau(tau_val, sigma_c, method: str | None = None):
1078
1081
  # Target function to find the root of: envelope(sigma, tau) - 1 = 0
1079
1082
  def envelope_root_func(sigma_val):
1080
1083
  return (
@@ -14,7 +14,6 @@ field_name: type = Field(..., gt=0, description="Description")
14
14
 
15
15
  import json
16
16
  import logging
17
- from typing import List
18
17
 
19
18
  from pydantic import BaseModel, ConfigDict, Field, model_validator
20
19
 
@@ -49,13 +48,13 @@ class ModelInput(BaseModel):
49
48
  default_factory=lambda: WeakLayer(rho=125, h=20, E=1.0),
50
49
  description="Weak layer",
51
50
  )
52
- layers: List[Layer] = Field(
53
- default_factory=lambda: [Layer(rho=250, h=100)], description="List of layers"
51
+ layers: list[Layer] = Field(
52
+ default_factory=lambda: [Layer(rho=250, h=100)], description="list of layers"
54
53
  )
55
54
  scenario_config: ScenarioConfig = Field(
56
55
  default_factory=ScenarioConfig, description="Scenario configuration"
57
56
  )
58
- segments: List[Segment] = Field(
57
+ segments: list[Segment] = Field(
59
58
  default_factory=lambda: [
60
59
  Segment(length=5000, has_foundation=True, m=100),
61
60
  Segment(length=5000, has_foundation=True, m=0),