timoshenko-engine 2.0.2__tar.gz → 2.0.3__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 (51) hide show
  1. {timoshenko_engine-2.0.2 → timoshenko_engine-2.0.3}/PKG-INFO +39 -16
  2. {timoshenko_engine-2.0.2 → timoshenko_engine-2.0.3}/README.md +32 -14
  3. {timoshenko_engine-2.0.2 → timoshenko_engine-2.0.3}/pyproject.toml +28 -3
  4. {timoshenko_engine-2.0.2 → timoshenko_engine-2.0.3}/src/timoshenko/__init__.py +1 -1
  5. {timoshenko_engine-2.0.2 → timoshenko_engine-2.0.3}/src/timoshenko/adapters.py +2 -2
  6. {timoshenko_engine-2.0.2 → timoshenko_engine-2.0.3}/src/timoshenko/modal.py +3 -4
  7. {timoshenko_engine-2.0.2 → timoshenko_engine-2.0.3}/src/timoshenko/multichannel.py +3 -3
  8. {timoshenko_engine-2.0.2 → timoshenko_engine-2.0.3}/src/timoshenko/oma.py +1 -1
  9. {timoshenko_engine-2.0.2 → timoshenko_engine-2.0.3}/src/timoshenko/project.py +1 -0
  10. timoshenko_engine-2.0.3/src/timoshenko/py.typed +0 -0
  11. {timoshenko_engine-2.0.2 → timoshenko_engine-2.0.3}/src/timoshenko/sections.py +0 -1
  12. {timoshenko_engine-2.0.2 → timoshenko_engine-2.0.3}/src/timoshenko/sensors.py +1 -1
  13. {timoshenko_engine-2.0.2 → timoshenko_engine-2.0.3}/src/timoshenko/session.py +7 -5
  14. {timoshenko_engine-2.0.2 → timoshenko_engine-2.0.3}/src/timoshenko/storage.py +4 -3
  15. {timoshenko_engine-2.0.2 → timoshenko_engine-2.0.3}/src/timoshenko/uncertainty.py +4 -4
  16. {timoshenko_engine-2.0.2 → timoshenko_engine-2.0.3}/src/timoshenko_engine.egg-info/PKG-INFO +39 -16
  17. {timoshenko_engine-2.0.2 → timoshenko_engine-2.0.3}/src/timoshenko_engine.egg-info/SOURCES.txt +1 -0
  18. {timoshenko_engine-2.0.2 → timoshenko_engine-2.0.3}/src/timoshenko_engine.egg-info/requires.txt +5 -0
  19. {timoshenko_engine-2.0.2 → timoshenko_engine-2.0.3}/LICENSE +0 -0
  20. {timoshenko_engine-2.0.2 → timoshenko_engine-2.0.3}/setup.cfg +0 -0
  21. {timoshenko_engine-2.0.2 → timoshenko_engine-2.0.3}/src/timoshenko/_validation.py +0 -0
  22. {timoshenko_engine-2.0.2 → timoshenko_engine-2.0.3}/src/timoshenko/assets.py +0 -0
  23. {timoshenko_engine-2.0.2 → timoshenko_engine-2.0.3}/src/timoshenko/beams.py +0 -0
  24. {timoshenko_engine-2.0.2 → timoshenko_engine-2.0.3}/src/timoshenko/csv_source.py +0 -0
  25. {timoshenko_engine-2.0.2 → timoshenko_engine-2.0.3}/src/timoshenko/health.py +0 -0
  26. {timoshenko_engine-2.0.2 → timoshenko_engine-2.0.3}/src/timoshenko/mechanics.py +0 -0
  27. {timoshenko_engine-2.0.2 → timoshenko_engine-2.0.3}/src/timoshenko/monitor.py +0 -0
  28. {timoshenko_engine-2.0.2 → timoshenko_engine-2.0.3}/src/timoshenko/mqtt.py +0 -0
  29. {timoshenko_engine-2.0.2 → timoshenko_engine-2.0.3}/src/timoshenko/observations.py +0 -0
  30. {timoshenko_engine-2.0.2 → timoshenko_engine-2.0.3}/src/timoshenko/plugins.py +0 -0
  31. {timoshenko_engine-2.0.2 → timoshenko_engine-2.0.3}/src/timoshenko/polygon.py +0 -0
  32. {timoshenko_engine-2.0.2 → timoshenko_engine-2.0.3}/src/timoshenko/pressure.py +0 -0
  33. {timoshenko_engine-2.0.2 → timoshenko_engine-2.0.3}/src/timoshenko/report.py +0 -0
  34. {timoshenko_engine-2.0.2 → timoshenko_engine-2.0.3}/src/timoshenko/sensorthings.py +0 -0
  35. {timoshenko_engine-2.0.2 → timoshenko_engine-2.0.3}/src/timoshenko/shafts.py +0 -0
  36. {timoshenko_engine-2.0.2 → timoshenko_engine-2.0.3}/src/timoshenko/stability.py +0 -0
  37. {timoshenko_engine-2.0.2 → timoshenko_engine-2.0.3}/src/timoshenko/strength.py +0 -0
  38. {timoshenko_engine-2.0.2 → timoshenko_engine-2.0.3}/src/timoshenko/structure.py +0 -0
  39. {timoshenko_engine-2.0.2 → timoshenko_engine-2.0.3}/src/timoshenko/update.py +0 -0
  40. {timoshenko_engine-2.0.2 → timoshenko_engine-2.0.3}/src/timoshenko/vibration.py +0 -0
  41. {timoshenko_engine-2.0.2 → timoshenko_engine-2.0.3}/src/timoshenko_engine.egg-info/dependency_links.txt +0 -0
  42. {timoshenko_engine-2.0.2 → timoshenko_engine-2.0.3}/src/timoshenko_engine.egg-info/entry_points.txt +0 -0
  43. {timoshenko_engine-2.0.2 → timoshenko_engine-2.0.3}/src/timoshenko_engine.egg-info/top_level.txt +0 -0
  44. {timoshenko_engine-2.0.2 → timoshenko_engine-2.0.3}/tests/test_adapters.py +0 -0
  45. {timoshenko_engine-2.0.2 → timoshenko_engine-2.0.3}/tests/test_formulas.py +0 -0
  46. {timoshenko_engine-2.0.2 → timoshenko_engine-2.0.3}/tests/test_modal.py +0 -0
  47. {timoshenko_engine-2.0.2 → timoshenko_engine-2.0.3}/tests/test_package.py +0 -0
  48. {timoshenko_engine-2.0.2 → timoshenko_engine-2.0.3}/tests/test_polygon.py +0 -0
  49. {timoshenko_engine-2.0.2 → timoshenko_engine-2.0.3}/tests/test_project_report.py +0 -0
  50. {timoshenko_engine-2.0.2 → timoshenko_engine-2.0.3}/tests/test_storage_session.py +0 -0
  51. {timoshenko_engine-2.0.2 → timoshenko_engine-2.0.3}/tests/test_uncertainty.py +0 -0
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: timoshenko-engine
3
- Version: 2.0.2
3
+ Version: 2.0.3
4
4
  Summary: Composable structural engineering calculations and analysis primitives for Python applications.
5
5
  Author: Ayberk Korkmaz
6
6
  License-Expression: Apache-2.0
7
7
  Project-URL: Homepage, https://github.com/Ayberkrk/timoshenko
8
8
  Project-URL: Repository, https://github.com/Ayberkrk/timoshenko
9
9
  Project-URL: Issues, https://github.com/Ayberkrk/timoshenko/issues
10
- Project-URL: Documentation, https://github.com/Ayberkrk/timoshenko/tree/main/docs
10
+ Project-URL: Documentation, https://timoshenko.readthedocs.io/
11
11
  Project-URL: Changelog, https://github.com/Ayberkrk/timoshenko/blob/main/docs/changelog.md
12
12
  Keywords: civil engineering,structural engineering,structural health monitoring,modal analysis,digital twin
13
13
  Classifier: Development Status :: 3 - Alpha
@@ -19,6 +19,7 @@ Classifier: Programming Language :: Python :: 3.11
19
19
  Classifier: Programming Language :: Python :: 3.12
20
20
  Classifier: Programming Language :: Python :: 3.13
21
21
  Classifier: Topic :: Scientific/Engineering
22
+ Classifier: Typing :: Typed
22
23
  Requires-Python: >=3.10
23
24
  Description-Content-Type: text/markdown
24
25
  License-File: LICENSE
@@ -27,6 +28,10 @@ Provides-Extra: mqtt
27
28
  Requires-Dist: paho-mqtt<3,>=2.1; extra == "mqtt"
28
29
  Provides-Extra: test
29
30
  Requires-Dist: pytest>=7; extra == "test"
31
+ Requires-Dist: pytest-cov>=5; extra == "test"
32
+ Provides-Extra: lint
33
+ Requires-Dist: ruff>=0.6; extra == "lint"
34
+ Requires-Dist: mypy>=1.10; extra == "lint"
30
35
  Provides-Extra: docs
31
36
  Requires-Dist: mkdocs-material<10,>=9.5; extra == "docs"
32
37
  Dynamic: license-file
@@ -46,7 +51,10 @@ Dynamic: license-file
46
51
  <img alt="Python versions" src="https://img.shields.io/pypi/pyversions/timoshenko-engine?style=for-the-badge">
47
52
  <img alt="Alpha" src="https://img.shields.io/badge/stage-alpha-orange?style=for-the-badge">
48
53
  <img alt="Apache 2.0 license" src="https://img.shields.io/badge/license-Apache--2.0-green?style=for-the-badge">
54
+ <a href="https://doi.org/10.5281/zenodo.22968739"><img alt="DOI" src="https://img.shields.io/badge/DOI-10.5281%2Fzenodo.22968739-blue?style=for-the-badge"></a>
49
55
  <a href="https://github.com/Ayberkrk/timoshenko/actions/workflows/tests.yml"><img alt="Tests" src="https://github.com/Ayberkrk/timoshenko/actions/workflows/tests.yml/badge.svg"></a>
56
+ <a href="https://github.com/Ayberkrk/timoshenko/tree/python-coverage-comment-action-data"><img alt="Coverage" src="https://img.shields.io/endpoint?url=https%3A%2F%2Fraw.githubusercontent.com%2FAyberkrk%2Ftimoshenko%2Fpython-coverage-comment-action-data%2Fendpoint.json&style=for-the-badge"></a>
57
+ <a href="https://timoshenko.readthedocs.io/en/latest/"><img alt="Documentation" src="https://img.shields.io/readthedocs/timoshenko?style=for-the-badge"></a>
50
58
  </p>
51
59
 
52
60
  Timoshenko packages common structural calculations, modal analysis, sensor
@@ -56,7 +64,7 @@ Python library, not a hosted monitoring service or a general finite-element
56
64
  solver.
57
65
 
58
66
  > **Alpha:** the API may still change between releases. Every behavior change
59
- > is listed in the [changelog](https://github.com/Ayberkrk/timoshenko/blob/main/docs/changelog.md).
67
+ > is listed in the [changelog](https://timoshenko.readthedocs.io/en/latest/changelog/).
60
68
 
61
69
  ## Install
62
70
 
@@ -100,6 +108,10 @@ for change in result.health.mode_changes:
100
108
  print(result.health.review_recommended) # True
101
109
  ```
102
110
 
111
+ <p align="center">
112
+ <img src="https://raw.githubusercontent.com/Ayberkrk/timoshenko/main/assets/modal-comparison.svg" alt="Both identified modes plot 5% below the reference model, past the 3% review threshold" width="800">
113
+ </p>
114
+
103
115
  Real records load from CSV or JSON with
104
116
  `tm.load_sensors("acceleration.csv", sampling_hz=100.0, column="acc")`. The
105
117
  steps inside `tm.monitor` are also available separately as `tm.modal.identify`,
@@ -188,13 +200,15 @@ The session does not open network connections or run in the background. Use
188
200
 
189
201
  ## Documentation
190
202
 
191
- - [Documentation home](https://github.com/Ayberkrk/timoshenko/blob/main/docs/index.md)
192
- - [API reference](https://github.com/Ayberkrk/timoshenko/blob/main/docs/api.md)
193
- - [Numerical methods and limits](https://github.com/Ayberkrk/timoshenko/blob/main/docs/numerical-methods.md)
194
- - [Architecture](https://github.com/Ayberkrk/timoshenko/blob/main/docs/architecture.md)
195
- - [Monitoring sessions](https://github.com/Ayberkrk/timoshenko/blob/main/docs/live-sessions.md) and [source adapters](https://github.com/Ayberkrk/timoshenko/blob/main/docs/adapters.md)
196
- - [Runnable examples](https://github.com/Ayberkrk/timoshenko/tree/main/examples), including a [cross-check of PyNite shear-deformable beams](https://github.com/Ayberkrk/timoshenko/blob/main/docs/pynite-verification.md)
197
- - [Changelog](https://github.com/Ayberkrk/timoshenko/blob/main/docs/changelog.md)
203
+ The full documentation is at
204
+ [timoshenko.readthedocs.io](https://timoshenko.readthedocs.io/en/latest/). Good places to start:
205
+
206
+ - [API reference](https://timoshenko.readthedocs.io/en/latest/api/)
207
+ - [Numerical methods and limits](https://timoshenko.readthedocs.io/en/latest/numerical-methods/)
208
+ - [Architecture](https://timoshenko.readthedocs.io/en/latest/architecture/)
209
+ - [Monitoring sessions](https://timoshenko.readthedocs.io/en/latest/live-sessions/) and [source adapters](https://timoshenko.readthedocs.io/en/latest/adapters/)
210
+ - [Runnable examples](https://github.com/Ayberkrk/timoshenko/tree/main/examples), including a [cross-check of PyNite shear-deformable beams](https://timoshenko.readthedocs.io/en/latest/pynite-verification/)
211
+ - [Changelog](https://timoshenko.readthedocs.io/en/latest/changelog/)
198
212
 
199
213
  ## Limits and engineering posture
200
214
 
@@ -209,8 +223,10 @@ The session does not open network connections or run in the background. Use
209
223
  ## Development
210
224
 
211
225
  ```bash
212
- python -m pip install -e ".[test]"
213
- python -m pytest
226
+ python -m pip install -e ".[test,lint]"
227
+ python -m pytest --cov
228
+ python -m ruff check .
229
+ python -m mypy
214
230
  ```
215
231
 
216
232
  To preview the documentation site:
@@ -221,13 +237,20 @@ python -m mkdocs serve
221
237
  ```
222
238
 
223
239
  Bug reports and pull requests are welcome in the
224
- [issue tracker](https://github.com/Ayberkrk/timoshenko/issues).
240
+ [issue tracker](https://github.com/Ayberkrk/timoshenko/issues). See
241
+ [`CONTRIBUTING.md`](https://github.com/Ayberkrk/timoshenko/blob/main/CONTRIBUTING.md)
242
+ for the checks a pull request needs to pass; issues labelled
243
+ [good first issue](https://github.com/Ayberkrk/timoshenko/labels/good%20first%20issue)
244
+ are small, self-contained starting points.
225
245
 
226
246
  ## Citation
227
247
 
228
- If you use Timoshenko in research, please cite it using the metadata in
229
- [`CITATION.cff`](https://github.com/Ayberkrk/timoshenko/blob/main/CITATION.cff)
230
- (GitHub's "Cite this repository" button reads the same file).
248
+ If you use Timoshenko in research, please cite it with its DOI,
249
+ [10.5281/zenodo.22968739](https://doi.org/10.5281/zenodo.22968739), which
250
+ always resolves to the latest release; each release also has its own DOI on
251
+ [Zenodo](https://zenodo.org/records/22968740). The metadata is in
252
+ [`CITATION.cff`](https://github.com/Ayberkrk/timoshenko/blob/main/CITATION.cff),
253
+ which GitHub's "Cite this repository" button reads.
231
254
 
232
255
  ## License
233
256
 
@@ -13,7 +13,10 @@
13
13
  <img alt="Python versions" src="https://img.shields.io/pypi/pyversions/timoshenko-engine?style=for-the-badge">
14
14
  <img alt="Alpha" src="https://img.shields.io/badge/stage-alpha-orange?style=for-the-badge">
15
15
  <img alt="Apache 2.0 license" src="https://img.shields.io/badge/license-Apache--2.0-green?style=for-the-badge">
16
+ <a href="https://doi.org/10.5281/zenodo.22968739"><img alt="DOI" src="https://img.shields.io/badge/DOI-10.5281%2Fzenodo.22968739-blue?style=for-the-badge"></a>
16
17
  <a href="https://github.com/Ayberkrk/timoshenko/actions/workflows/tests.yml"><img alt="Tests" src="https://github.com/Ayberkrk/timoshenko/actions/workflows/tests.yml/badge.svg"></a>
18
+ <a href="https://github.com/Ayberkrk/timoshenko/tree/python-coverage-comment-action-data"><img alt="Coverage" src="https://img.shields.io/endpoint?url=https%3A%2F%2Fraw.githubusercontent.com%2FAyberkrk%2Ftimoshenko%2Fpython-coverage-comment-action-data%2Fendpoint.json&style=for-the-badge"></a>
19
+ <a href="https://timoshenko.readthedocs.io/en/latest/"><img alt="Documentation" src="https://img.shields.io/readthedocs/timoshenko?style=for-the-badge"></a>
17
20
  </p>
18
21
 
19
22
  Timoshenko packages common structural calculations, modal analysis, sensor
@@ -23,7 +26,7 @@ Python library, not a hosted monitoring service or a general finite-element
23
26
  solver.
24
27
 
25
28
  > **Alpha:** the API may still change between releases. Every behavior change
26
- > is listed in the [changelog](https://github.com/Ayberkrk/timoshenko/blob/main/docs/changelog.md).
29
+ > is listed in the [changelog](https://timoshenko.readthedocs.io/en/latest/changelog/).
27
30
 
28
31
  ## Install
29
32
 
@@ -67,6 +70,10 @@ for change in result.health.mode_changes:
67
70
  print(result.health.review_recommended) # True
68
71
  ```
69
72
 
73
+ <p align="center">
74
+ <img src="https://raw.githubusercontent.com/Ayberkrk/timoshenko/main/assets/modal-comparison.svg" alt="Both identified modes plot 5% below the reference model, past the 3% review threshold" width="800">
75
+ </p>
76
+
70
77
  Real records load from CSV or JSON with
71
78
  `tm.load_sensors("acceleration.csv", sampling_hz=100.0, column="acc")`. The
72
79
  steps inside `tm.monitor` are also available separately as `tm.modal.identify`,
@@ -155,13 +162,15 @@ The session does not open network connections or run in the background. Use
155
162
 
156
163
  ## Documentation
157
164
 
158
- - [Documentation home](https://github.com/Ayberkrk/timoshenko/blob/main/docs/index.md)
159
- - [API reference](https://github.com/Ayberkrk/timoshenko/blob/main/docs/api.md)
160
- - [Numerical methods and limits](https://github.com/Ayberkrk/timoshenko/blob/main/docs/numerical-methods.md)
161
- - [Architecture](https://github.com/Ayberkrk/timoshenko/blob/main/docs/architecture.md)
162
- - [Monitoring sessions](https://github.com/Ayberkrk/timoshenko/blob/main/docs/live-sessions.md) and [source adapters](https://github.com/Ayberkrk/timoshenko/blob/main/docs/adapters.md)
163
- - [Runnable examples](https://github.com/Ayberkrk/timoshenko/tree/main/examples), including a [cross-check of PyNite shear-deformable beams](https://github.com/Ayberkrk/timoshenko/blob/main/docs/pynite-verification.md)
164
- - [Changelog](https://github.com/Ayberkrk/timoshenko/blob/main/docs/changelog.md)
165
+ The full documentation is at
166
+ [timoshenko.readthedocs.io](https://timoshenko.readthedocs.io/en/latest/). Good places to start:
167
+
168
+ - [API reference](https://timoshenko.readthedocs.io/en/latest/api/)
169
+ - [Numerical methods and limits](https://timoshenko.readthedocs.io/en/latest/numerical-methods/)
170
+ - [Architecture](https://timoshenko.readthedocs.io/en/latest/architecture/)
171
+ - [Monitoring sessions](https://timoshenko.readthedocs.io/en/latest/live-sessions/) and [source adapters](https://timoshenko.readthedocs.io/en/latest/adapters/)
172
+ - [Runnable examples](https://github.com/Ayberkrk/timoshenko/tree/main/examples), including a [cross-check of PyNite shear-deformable beams](https://timoshenko.readthedocs.io/en/latest/pynite-verification/)
173
+ - [Changelog](https://timoshenko.readthedocs.io/en/latest/changelog/)
165
174
 
166
175
  ## Limits and engineering posture
167
176
 
@@ -176,8 +185,10 @@ The session does not open network connections or run in the background. Use
176
185
  ## Development
177
186
 
178
187
  ```bash
179
- python -m pip install -e ".[test]"
180
- python -m pytest
188
+ python -m pip install -e ".[test,lint]"
189
+ python -m pytest --cov
190
+ python -m ruff check .
191
+ python -m mypy
181
192
  ```
182
193
 
183
194
  To preview the documentation site:
@@ -188,13 +199,20 @@ python -m mkdocs serve
188
199
  ```
189
200
 
190
201
  Bug reports and pull requests are welcome in the
191
- [issue tracker](https://github.com/Ayberkrk/timoshenko/issues).
202
+ [issue tracker](https://github.com/Ayberkrk/timoshenko/issues). See
203
+ [`CONTRIBUTING.md`](https://github.com/Ayberkrk/timoshenko/blob/main/CONTRIBUTING.md)
204
+ for the checks a pull request needs to pass; issues labelled
205
+ [good first issue](https://github.com/Ayberkrk/timoshenko/labels/good%20first%20issue)
206
+ are small, self-contained starting points.
192
207
 
193
208
  ## Citation
194
209
 
195
- If you use Timoshenko in research, please cite it using the metadata in
196
- [`CITATION.cff`](https://github.com/Ayberkrk/timoshenko/blob/main/CITATION.cff)
197
- (GitHub's "Cite this repository" button reads the same file).
210
+ If you use Timoshenko in research, please cite it with its DOI,
211
+ [10.5281/zenodo.22968739](https://doi.org/10.5281/zenodo.22968739), which
212
+ always resolves to the latest release; each release also has its own DOI on
213
+ [Zenodo](https://zenodo.org/records/22968740). The metadata is in
214
+ [`CITATION.cff`](https://github.com/Ayberkrk/timoshenko/blob/main/CITATION.cff),
215
+ which GitHub's "Cite this repository" button reads.
198
216
 
199
217
  ## License
200
218
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "timoshenko-engine"
7
- version = "2.0.2"
7
+ version = "2.0.3"
8
8
  description = "Composable structural engineering calculations and analysis primitives for Python applications."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -29,11 +29,13 @@ classifiers = [
29
29
  "Programming Language :: Python :: 3.12",
30
30
  "Programming Language :: Python :: 3.13",
31
31
  "Topic :: Scientific/Engineering",
32
+ "Typing :: Typed",
32
33
  ]
33
34
 
34
35
  [project.optional-dependencies]
35
36
  mqtt = ["paho-mqtt>=2.1,<3"]
36
- test = ["pytest>=7"]
37
+ test = ["pytest>=7", "pytest-cov>=5"]
38
+ lint = ["ruff>=0.6", "mypy>=1.10"]
37
39
  docs = ["mkdocs-material>=9.5,<10"]
38
40
 
39
41
  [project.entry-points."timoshenko.plugins"]
@@ -44,7 +46,7 @@ sensorthings = "timoshenko.sensorthings:SensorThingsPlugin"
44
46
  Homepage = "https://github.com/Ayberkrk/timoshenko"
45
47
  Repository = "https://github.com/Ayberkrk/timoshenko"
46
48
  Issues = "https://github.com/Ayberkrk/timoshenko/issues"
47
- Documentation = "https://github.com/Ayberkrk/timoshenko/tree/main/docs"
49
+ Documentation = "https://timoshenko.readthedocs.io/"
48
50
  Changelog = "https://github.com/Ayberkrk/timoshenko/blob/main/docs/changelog.md"
49
51
 
50
52
  [tool.setuptools]
@@ -53,6 +55,29 @@ package-dir = { "" = "src" }
53
55
  [tool.setuptools.packages.find]
54
56
  where = ["src"]
55
57
 
58
+ [tool.setuptools.package-data]
59
+ timoshenko = ["py.typed"]
60
+
56
61
  [tool.pytest.ini_options]
57
62
  testpaths = ["tests"]
58
63
  xfail_strict = true
64
+
65
+ [tool.coverage.run]
66
+ source = ["timoshenko"]
67
+ relative_files = true
68
+
69
+ [tool.coverage.report]
70
+ show_missing = true
71
+
72
+ [tool.ruff]
73
+ target-version = "py310"
74
+ src = ["src", "tests"]
75
+
76
+ [tool.ruff.lint]
77
+ select = ["E4", "E7", "E9", "F"]
78
+
79
+ [tool.mypy]
80
+ files = ["src/timoshenko"]
81
+ check_untyped_defs = true
82
+ warn_unused_ignores = true
83
+ warn_redundant_casts = true
@@ -59,7 +59,7 @@ from .update import update
59
59
  from .uncertainty import UncertaintyError, UncertaintyResult, propagate as propagate_uncertainty
60
60
  from .vibration import RayleighDampingResult, damping_ratio, harmonic_response, natural_frequency_hz, rayleigh_damping_coefficients
61
61
 
62
- __version__ = "2.0.2"
62
+ __version__ = "2.0.3"
63
63
 
64
64
  __all__ = [
65
65
  "HealthAssessment",
@@ -2,7 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from typing import Iterator, Protocol, runtime_checkable
5
+ from typing import Iterator, Literal, Protocol, runtime_checkable
6
6
 
7
7
  from .observations import ObservationBatch
8
8
  from .session import MonitoringSession, SessionIngestResult
@@ -73,7 +73,7 @@ class SessionRunner:
73
73
  self._consuming = False
74
74
  return self
75
75
 
76
- def __exit__(self, exc_type, exc, traceback) -> bool:
76
+ def __exit__(self, exc_type, exc, traceback) -> Literal[False]:
77
77
  self._active = False
78
78
  try:
79
79
  self.source.close()
@@ -8,7 +8,7 @@ import math
8
8
  import numpy as np
9
9
 
10
10
  from .sensors import SensorData
11
- from .oma import FDDMode, FDDResult, identify_fdd
11
+ from .oma import FDDMode as FDDMode, FDDResult as FDDResult, identify_fdd as identify_fdd
12
12
 
13
13
 
14
14
  @dataclass(frozen=True)
@@ -105,7 +105,7 @@ def identify(
105
105
  )
106
106
  for idx in selected
107
107
  )
108
- notes = ["Frequency spacing is limited by the record duration."] if resolution > 0.25 else []
108
+ notes: list[str] = ["Frequency spacing is limited by the record duration."] if resolution > 0.25 else []
109
109
  if any(mode.damping_ratio is not None for mode in modes):
110
110
  notes.append(
111
111
  "Damping ratios are coarse half-power screening estimates from an averaged spectrum; "
@@ -116,7 +116,6 @@ def identify(
116
116
  "Damping is withheld where the record is too short to resolve the half-power bandwidth "
117
117
  f"with {_MIN_DAMPING_AVERAGES} averages and at least {_MIN_DAMPING_BINS:g} frequency bins."
118
118
  )
119
- notes = tuple(notes)
120
119
  return ModalResult(
121
120
  modes=modes,
122
121
  sampling_hz=sensor_data.sampling_hz,
@@ -124,7 +123,7 @@ def identify(
124
123
  resolution_hz=resolution,
125
124
  channel=sensor_data.channel,
126
125
  status="ok" if modes else "no_peaks_found",
127
- notes=notes,
126
+ notes=tuple(notes),
128
127
  )
129
128
 
130
129
 
@@ -54,11 +54,11 @@ class MultiChannelData:
54
54
 
55
55
  @property
56
56
  def sample_count(self) -> int:
57
- return int(self.samples.shape[0])
57
+ return int(np.shape(self.samples)[0])
58
58
 
59
59
  @property
60
60
  def channel_count(self) -> int:
61
- return int(self.samples.shape[1])
61
+ return int(np.shape(self.samples)[1])
62
62
 
63
63
 
64
64
  def load_multichannel_csv(
@@ -93,7 +93,7 @@ def load_multichannel_csv(
93
93
  for row_number, row in enumerate(reader, start=2):
94
94
  record: list[float] = []
95
95
  for name in names:
96
- raw = row.get(name)
96
+ raw = row.get(name) or ""
97
97
  try:
98
98
  value = float(raw)
99
99
  except (TypeError, ValueError):
@@ -93,7 +93,7 @@ def identify_fdd(
93
93
  raise TypeError("observations must be MultiChannelData; use tm.load_multichannel_csv() or construct it explicitly")
94
94
  if len(set(observations.units)) != 1:
95
95
  raise ValueError("FDD requires channels with the same measurement unit; calibrate/transform mixed-unit channels first")
96
- sample_count, channel_count = observations.samples.shape
96
+ sample_count, channel_count = np.shape(observations.samples)
97
97
  nperseg_value, hop, segment_count, resolution, low, high = _plan(
98
98
  sample_count,
99
99
  observations.sampling_hz,
@@ -223,6 +223,7 @@ def run_project(project: LoadedProject | str | Path) -> ProjectRunResult:
223
223
  if not isinstance(loaded, LoadedProject):
224
224
  raise TypeError("project must be a manifest path or LoadedProject from tm.load_project()")
225
225
  manifest = loaded.manifest
226
+ modal: ModalResult | FDDResult
226
227
  if manifest.method == "peak_picking":
227
228
  if not isinstance(loaded.observations, SensorData):
228
229
  raise TypeError("peak_picking manifest must load one SensorData channel")
File without changes
@@ -94,7 +94,6 @@ def rectangular_tube(
94
94
  if 2.0 * thickness >= min(width, height):
95
95
  raise ValueError("twice wall_thickness_m must be smaller than both outer dimensions")
96
96
  inner_height = height - 2.0 * thickness
97
- inner_width = width - 2.0 * thickness
98
97
  horizontal_plate_offset = (height - thickness) / 2.0
99
98
  vertical_plate_offset = (width - thickness) / 2.0
100
99
  area = 2.0 * width * thickness + 2.0 * thickness * inner_height
@@ -16,7 +16,7 @@ import numpy as np
16
16
  class SensorData:
17
17
  """A single regularly sampled sensor channel."""
18
18
 
19
- samples: tuple[float, ...] | Sequence[float]
19
+ samples: Sequence[float] | np.ndarray
20
20
  sampling_hz: float
21
21
  unit: str = "m/s^2"
22
22
  channel: str = "sensor"
@@ -178,7 +178,7 @@ class MonitoringSession:
178
178
  self._review_threshold_pct = validate_review_threshold(review_threshold_pct)
179
179
  self._baseline_structure = structure
180
180
  self._store = store
181
- self._buffers = {sensor_id: deque(maxlen=window) for sensor_id in ids}
181
+ self._buffers: dict[str, deque[tuple[int, float]]] = {sensor_id: deque(maxlen=window) for sensor_id in ids}
182
182
  self._last_key: dict[str, int] = {}
183
183
  self._seen_by_key: dict[int, set[str]] = {}
184
184
  self._seen_key_heap: list[int] = []
@@ -350,23 +350,25 @@ class MonitoringSession:
350
350
  if snapshot is None:
351
351
  return None
352
352
  keys, matrix = snapshot
353
+ observations: SensorData | MultiChannelData
354
+ modal: ModalResult | FDDResult
353
355
  if len(self._sensor_ids) == 1:
354
- observations: SensorData | MultiChannelData = SensorData(
356
+ channel = SensorData(
355
357
  samples=matrix[:, 0],
356
358
  sampling_hz=self._sampling_hz,
357
359
  unit=self._units[0],
358
360
  channel=self._sensor_ids[0],
359
361
  )
360
- modal = identify(observations, **self._options)
362
+ observations, modal = channel, identify(channel, **self._options)
361
363
  method = "peak_picking"
362
364
  else:
363
- observations = MultiChannelData(
365
+ channels = MultiChannelData(
364
366
  samples=matrix,
365
367
  sampling_hz=self._sampling_hz,
366
368
  channel_ids=self._sensor_ids,
367
369
  units=self._units,
368
370
  )
369
- modal = identify_fdd(observations, **self._options)
371
+ observations, modal = channels, identify_fdd(channels, **self._options)
370
372
  method = "fdd"
371
373
  if modal.modes:
372
374
  self._structure = update(self._baseline_structure, modal)
@@ -75,7 +75,7 @@ class SQLiteStore:
75
75
  timeout = float(timeout_seconds)
76
76
  if not math.isfinite(timeout) or timeout <= 0.0:
77
77
  raise ValueError("timeout_seconds must be finite and positive")
78
- self._connection = sqlite3.connect(self.path, timeout=timeout)
78
+ self._connection: sqlite3.Connection | None = sqlite3.connect(self.path, timeout=timeout)
79
79
  self._connection.row_factory = sqlite3.Row
80
80
  self._db.execute("PRAGMA foreign_keys = ON")
81
81
  self._initialize()
@@ -154,8 +154,9 @@ class SQLiteStore:
154
154
  return connection
155
155
 
156
156
  def close(self) -> None:
157
- if getattr(self, "_connection", None) is not None:
158
- self._connection.close()
157
+ connection = getattr(self, "_connection", None)
158
+ if connection is not None:
159
+ connection.close()
159
160
  self._connection = None
160
161
 
161
162
  def __enter__(self) -> "SQLiteStore":
@@ -227,7 +227,7 @@ def _finite_scalar(value: Any, label: str) -> float:
227
227
  if isinstance(value, (bool, np.bool_)) or not np.isscalar(value):
228
228
  raise UncertaintyError(f"{label} must be a finite real number")
229
229
  try:
230
- result = float(value)
230
+ result = float(value) # type: ignore[arg-type]
231
231
  except (TypeError, ValueError) as error:
232
232
  raise UncertaintyError(f"{label} must be a finite real number") from error
233
233
  if not math.isfinite(result):
@@ -235,7 +235,7 @@ def _finite_scalar(value: Any, label: str) -> float:
235
235
  return result
236
236
 
237
237
 
238
- def _evaluate(function: Callable[..., float], names: tuple[str, ...], values: Sequence[float], label: str) -> float:
238
+ def _evaluate(function: Callable[..., float], names: tuple[str, ...], values: Sequence[float] | np.ndarray, label: str) -> float:
239
239
  try:
240
240
  result = function(**dict(zip(names, values)))
241
241
  except Exception as error:
@@ -243,7 +243,7 @@ def _evaluate(function: Callable[..., float], names: tuple[str, ...], values: Se
243
243
  if isinstance(result, (bool, np.bool_)) or not np.isscalar(result):
244
244
  raise UncertaintyError(f"equation output for {label} must be a finite scalar number")
245
245
  try:
246
- output = float(result)
246
+ output = float(result) # type: ignore[arg-type]
247
247
  except (TypeError, ValueError) as error:
248
248
  raise UncertaintyError(f"equation output for {label} must be a finite scalar number") from error
249
249
  if not math.isfinite(output):
@@ -251,7 +251,7 @@ def _evaluate(function: Callable[..., float], names: tuple[str, ...], values: Se
251
251
  return output
252
252
 
253
253
 
254
- def _try_evaluate(function: Callable[..., float], names: tuple[str, ...], values: Sequence[float]) -> float | None:
254
+ def _try_evaluate(function: Callable[..., float], names: tuple[str, ...], values: Sequence[float] | np.ndarray) -> float | None:
255
255
  try:
256
256
  return _evaluate(function, names, values, "finite difference input")
257
257
  except UncertaintyError:
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: timoshenko-engine
3
- Version: 2.0.2
3
+ Version: 2.0.3
4
4
  Summary: Composable structural engineering calculations and analysis primitives for Python applications.
5
5
  Author: Ayberk Korkmaz
6
6
  License-Expression: Apache-2.0
7
7
  Project-URL: Homepage, https://github.com/Ayberkrk/timoshenko
8
8
  Project-URL: Repository, https://github.com/Ayberkrk/timoshenko
9
9
  Project-URL: Issues, https://github.com/Ayberkrk/timoshenko/issues
10
- Project-URL: Documentation, https://github.com/Ayberkrk/timoshenko/tree/main/docs
10
+ Project-URL: Documentation, https://timoshenko.readthedocs.io/
11
11
  Project-URL: Changelog, https://github.com/Ayberkrk/timoshenko/blob/main/docs/changelog.md
12
12
  Keywords: civil engineering,structural engineering,structural health monitoring,modal analysis,digital twin
13
13
  Classifier: Development Status :: 3 - Alpha
@@ -19,6 +19,7 @@ Classifier: Programming Language :: Python :: 3.11
19
19
  Classifier: Programming Language :: Python :: 3.12
20
20
  Classifier: Programming Language :: Python :: 3.13
21
21
  Classifier: Topic :: Scientific/Engineering
22
+ Classifier: Typing :: Typed
22
23
  Requires-Python: >=3.10
23
24
  Description-Content-Type: text/markdown
24
25
  License-File: LICENSE
@@ -27,6 +28,10 @@ Provides-Extra: mqtt
27
28
  Requires-Dist: paho-mqtt<3,>=2.1; extra == "mqtt"
28
29
  Provides-Extra: test
29
30
  Requires-Dist: pytest>=7; extra == "test"
31
+ Requires-Dist: pytest-cov>=5; extra == "test"
32
+ Provides-Extra: lint
33
+ Requires-Dist: ruff>=0.6; extra == "lint"
34
+ Requires-Dist: mypy>=1.10; extra == "lint"
30
35
  Provides-Extra: docs
31
36
  Requires-Dist: mkdocs-material<10,>=9.5; extra == "docs"
32
37
  Dynamic: license-file
@@ -46,7 +51,10 @@ Dynamic: license-file
46
51
  <img alt="Python versions" src="https://img.shields.io/pypi/pyversions/timoshenko-engine?style=for-the-badge">
47
52
  <img alt="Alpha" src="https://img.shields.io/badge/stage-alpha-orange?style=for-the-badge">
48
53
  <img alt="Apache 2.0 license" src="https://img.shields.io/badge/license-Apache--2.0-green?style=for-the-badge">
54
+ <a href="https://doi.org/10.5281/zenodo.22968739"><img alt="DOI" src="https://img.shields.io/badge/DOI-10.5281%2Fzenodo.22968739-blue?style=for-the-badge"></a>
49
55
  <a href="https://github.com/Ayberkrk/timoshenko/actions/workflows/tests.yml"><img alt="Tests" src="https://github.com/Ayberkrk/timoshenko/actions/workflows/tests.yml/badge.svg"></a>
56
+ <a href="https://github.com/Ayberkrk/timoshenko/tree/python-coverage-comment-action-data"><img alt="Coverage" src="https://img.shields.io/endpoint?url=https%3A%2F%2Fraw.githubusercontent.com%2FAyberkrk%2Ftimoshenko%2Fpython-coverage-comment-action-data%2Fendpoint.json&style=for-the-badge"></a>
57
+ <a href="https://timoshenko.readthedocs.io/en/latest/"><img alt="Documentation" src="https://img.shields.io/readthedocs/timoshenko?style=for-the-badge"></a>
50
58
  </p>
51
59
 
52
60
  Timoshenko packages common structural calculations, modal analysis, sensor
@@ -56,7 +64,7 @@ Python library, not a hosted monitoring service or a general finite-element
56
64
  solver.
57
65
 
58
66
  > **Alpha:** the API may still change between releases. Every behavior change
59
- > is listed in the [changelog](https://github.com/Ayberkrk/timoshenko/blob/main/docs/changelog.md).
67
+ > is listed in the [changelog](https://timoshenko.readthedocs.io/en/latest/changelog/).
60
68
 
61
69
  ## Install
62
70
 
@@ -100,6 +108,10 @@ for change in result.health.mode_changes:
100
108
  print(result.health.review_recommended) # True
101
109
  ```
102
110
 
111
+ <p align="center">
112
+ <img src="https://raw.githubusercontent.com/Ayberkrk/timoshenko/main/assets/modal-comparison.svg" alt="Both identified modes plot 5% below the reference model, past the 3% review threshold" width="800">
113
+ </p>
114
+
103
115
  Real records load from CSV or JSON with
104
116
  `tm.load_sensors("acceleration.csv", sampling_hz=100.0, column="acc")`. The
105
117
  steps inside `tm.monitor` are also available separately as `tm.modal.identify`,
@@ -188,13 +200,15 @@ The session does not open network connections or run in the background. Use
188
200
 
189
201
  ## Documentation
190
202
 
191
- - [Documentation home](https://github.com/Ayberkrk/timoshenko/blob/main/docs/index.md)
192
- - [API reference](https://github.com/Ayberkrk/timoshenko/blob/main/docs/api.md)
193
- - [Numerical methods and limits](https://github.com/Ayberkrk/timoshenko/blob/main/docs/numerical-methods.md)
194
- - [Architecture](https://github.com/Ayberkrk/timoshenko/blob/main/docs/architecture.md)
195
- - [Monitoring sessions](https://github.com/Ayberkrk/timoshenko/blob/main/docs/live-sessions.md) and [source adapters](https://github.com/Ayberkrk/timoshenko/blob/main/docs/adapters.md)
196
- - [Runnable examples](https://github.com/Ayberkrk/timoshenko/tree/main/examples), including a [cross-check of PyNite shear-deformable beams](https://github.com/Ayberkrk/timoshenko/blob/main/docs/pynite-verification.md)
197
- - [Changelog](https://github.com/Ayberkrk/timoshenko/blob/main/docs/changelog.md)
203
+ The full documentation is at
204
+ [timoshenko.readthedocs.io](https://timoshenko.readthedocs.io/en/latest/). Good places to start:
205
+
206
+ - [API reference](https://timoshenko.readthedocs.io/en/latest/api/)
207
+ - [Numerical methods and limits](https://timoshenko.readthedocs.io/en/latest/numerical-methods/)
208
+ - [Architecture](https://timoshenko.readthedocs.io/en/latest/architecture/)
209
+ - [Monitoring sessions](https://timoshenko.readthedocs.io/en/latest/live-sessions/) and [source adapters](https://timoshenko.readthedocs.io/en/latest/adapters/)
210
+ - [Runnable examples](https://github.com/Ayberkrk/timoshenko/tree/main/examples), including a [cross-check of PyNite shear-deformable beams](https://timoshenko.readthedocs.io/en/latest/pynite-verification/)
211
+ - [Changelog](https://timoshenko.readthedocs.io/en/latest/changelog/)
198
212
 
199
213
  ## Limits and engineering posture
200
214
 
@@ -209,8 +223,10 @@ The session does not open network connections or run in the background. Use
209
223
  ## Development
210
224
 
211
225
  ```bash
212
- python -m pip install -e ".[test]"
213
- python -m pytest
226
+ python -m pip install -e ".[test,lint]"
227
+ python -m pytest --cov
228
+ python -m ruff check .
229
+ python -m mypy
214
230
  ```
215
231
 
216
232
  To preview the documentation site:
@@ -221,13 +237,20 @@ python -m mkdocs serve
221
237
  ```
222
238
 
223
239
  Bug reports and pull requests are welcome in the
224
- [issue tracker](https://github.com/Ayberkrk/timoshenko/issues).
240
+ [issue tracker](https://github.com/Ayberkrk/timoshenko/issues). See
241
+ [`CONTRIBUTING.md`](https://github.com/Ayberkrk/timoshenko/blob/main/CONTRIBUTING.md)
242
+ for the checks a pull request needs to pass; issues labelled
243
+ [good first issue](https://github.com/Ayberkrk/timoshenko/labels/good%20first%20issue)
244
+ are small, self-contained starting points.
225
245
 
226
246
  ## Citation
227
247
 
228
- If you use Timoshenko in research, please cite it using the metadata in
229
- [`CITATION.cff`](https://github.com/Ayberkrk/timoshenko/blob/main/CITATION.cff)
230
- (GitHub's "Cite this repository" button reads the same file).
248
+ If you use Timoshenko in research, please cite it with its DOI,
249
+ [10.5281/zenodo.22968739](https://doi.org/10.5281/zenodo.22968739), which
250
+ always resolves to the latest release; each release also has its own DOI on
251
+ [Zenodo](https://zenodo.org/records/22968740). The metadata is in
252
+ [`CITATION.cff`](https://github.com/Ayberkrk/timoshenko/blob/main/CITATION.cff),
253
+ which GitHub's "Cite this repository" button reads.
231
254
 
232
255
  ## License
233
256
 
@@ -19,6 +19,7 @@ src/timoshenko/plugins.py
19
19
  src/timoshenko/polygon.py
20
20
  src/timoshenko/pressure.py
21
21
  src/timoshenko/project.py
22
+ src/timoshenko/py.typed
22
23
  src/timoshenko/report.py
23
24
  src/timoshenko/sections.py
24
25
  src/timoshenko/sensors.py
@@ -3,8 +3,13 @@ numpy>=1.24
3
3
  [docs]
4
4
  mkdocs-material<10,>=9.5
5
5
 
6
+ [lint]
7
+ ruff>=0.6
8
+ mypy>=1.10
9
+
6
10
  [mqtt]
7
11
  paho-mqtt<3,>=2.1
8
12
 
9
13
  [test]
10
14
  pytest>=7
15
+ pytest-cov>=5