dynamic-systems-testing-framework 1.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dynamic_systems_testing_framework-1.0.1/.gitignore +89 -0
- dynamic_systems_testing_framework-1.0.1/LICENSE +21 -0
- dynamic_systems_testing_framework-1.0.1/PKG-INFO +266 -0
- dynamic_systems_testing_framework-1.0.1/README.md +226 -0
- dynamic_systems_testing_framework-1.0.1/pyproject.toml +188 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/__init__.py +41 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/__main__.py +10 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/authoring/__init__.py +13 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/authoring/builtin/__init__.py +32 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/authoring/builtin/accept.py +205 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/authoring/builtin/edit_criteria.py +479 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/authoring/builtin/edit_family.py +228 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/authoring/builtin/edit_on_plot.py +1354 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/authoring/builtin/edit_tree.py +1399 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/authoring/builtin/enqueue.py +144 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/authoring/capabilities.py +463 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/authoring/errors.py +49 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/authoring/family_rows.py +526 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/authoring/forms.py +631 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/authoring/layout.py +104 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/authoring/live_rows.py +337 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/authoring/live_runs.py +385 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/authoring/needle.py +127 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/authoring/picker_rows.py +92 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/authoring/queue_rows.py +354 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/authoring/session.py +3016 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/authoring/shell/__init__.py +9 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/authoring/shell/actions.py +702 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/authoring/shell/app.py +53 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/authoring/shell/drawer_controls.py +190 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/authoring/shell/family_fold.py +468 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/authoring/shell/figure_hook.py +249 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/authoring/shell/grips.js +1000 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/authoring/shell/header.py +783 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/authoring/shell/live_view.py +93 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/authoring/shell/picker.py +267 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/authoring/shell/placing.py +122 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/authoring/shell/plot_card.py +1171 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/authoring/shell/queue_panel.py +433 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/authoring/shell/rail.py +593 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/authoring/shell/shell.py +528 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/authoring/shell/stats_card.py +501 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/authoring/shell/strip.py +204 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/authoring/shell/style.py +721 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/authoring/shell/suite.py +277 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/authoring/shell/suite_grid.js +414 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/authoring/shell/suite_grid.py +373 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/authoring/shell/tab_theme.py +110 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/authoring/shell/tabstate.py +146 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/authoring/shell/test_list.py +179 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/authoring/shell/test_page.py +1404 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/authoring/shell/tree_card.py +1171 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/authoring/shell/variables_drawer.py +589 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/authoring/shell/widgets.py +188 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/authoring/situation.py +287 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/authoring/spec_document.py +215 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/authoring/spec_preview.py +83 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/authoring/suite_rows.py +99 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/authoring/tree_move.py +516 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/authoring/tree_pointer.py +33 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/authoring/tree_text.py +61 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/authoring/typed_number.js +64 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/authoring/typed_number.py +76 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/authoring/unscorable.py +141 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/authoring/variable_rows.py +216 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/backends/__init__.py +283 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/backends/base.py +1117 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/backends/common/__init__.py +1 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/backends/common/json_driver.py +484 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/backends/common/live_mat.py +369 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/backends/common/mat_reader.py +632 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/backends/common/proc_kill.py +238 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/backends/common/proc_output.py +20 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/backends/common/result_files.py +109 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/backends/common/result_variables.py +329 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/backends/common/statistics.py +39 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/backends/cross_backend.py +193 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/backends/dymola/__init__.py +5 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/backends/dymola/backend.py +776 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/backends/dymola/interface_loader.py +241 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/backends/dymola/log_parser.py +396 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/backends/dymola/persistent_backend.py +949 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/backends/fmpy/__init__.py +11 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/backends/fmpy/backend.py +443 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/backends/julia/__init__.py +27 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/backends/julia/backend.py +156 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/backends/julia/persistent_backend.py +541 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/backends/julia/run_persistent.jl +167 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/backends/julia/run_test.jl +146 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/backends/openmodelica/__init__.py +21 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/backends/openmodelica/backend.py +368 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/backends/openmodelica/init_xml.py +76 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/backends/openmodelica/log_parser.py +119 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/backends/openmodelica/mos_generator.py +228 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/backends/openmodelica/persistent_backend.py +1045 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/backends/openmodelica/session_loader.py +63 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/backends/persistent.py +1811 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/backends/pool.py +602 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/backends/progress.py +395 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/backends/python/__init__.py +11 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/backends/python/backend.py +141 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/backends/python/run_test.py +141 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/backends/run_metadata.py +103 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/backends/types.py +310 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/cli/__init__.py +12 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/cli/author_cmd.py +61 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/cli/baseline_cmds.py +341 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/cli/common.py +231 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/cli/diag_cmds.py +350 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/cli/main.py +561 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/cli/review.py +398 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/cli/run_cmds.py +589 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/cli/spec_cmds.py +123 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/comparison/__init__.py +13 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/comparison/algorithms.py +2127 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/comparison/builtins.py +291 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/comparison/comparator.py +1189 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/comparison/geometry.py +164 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/comparison/handles.py +1194 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/comparison/metric_tree.py +332 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/comparison/mode_schema.py +176 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/comparison/modes.py +788 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/comparison/registry.py +230 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/comparison/seeds.py +281 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/comparison/sides.py +158 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/comparison/snap.py +62 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/comparison/strict_keys.py +246 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/comparison/summaries.py +354 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/comparison/tree_eval.py +868 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/comparison/tree_spec.py +678 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/comparison/types.py +194 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/comparison/validator.py +232 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/config.py +815 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/discovery/__init__.py +0 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/discovery/json_recognizer.py +702 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/discovery/library_state.py +248 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/discovery/mo_parser.py +794 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/discovery/patch_apply.py +308 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/discovery/patch_envelope.py +63 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/discovery/plots.py +318 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/discovery/recognizer.py +103 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/discovery/scan_cache.py +154 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/discovery/spec_parser.py +538 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/discovery/spec_shape.py +122 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/discovery/spec_update.py +493 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/discovery/test_registry.py +488 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/discovery/unit_tests.py +95 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/figures/__init__.py +14 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/figures/assemble.py +48 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/figures/decimate.py +150 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/figures/families.py +289 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/figures/palette.py +170 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/figures/plotly.py +1413 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/figures/view.py +233 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/pipeline.py +435 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/plugins.py +104 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/reporting/__init__.py +0 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/reporting/console_report.py +187 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/reporting/context_builder.py +979 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/reporting/dashboard_render.py +534 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/reporting/junit_report.py +192 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/reporting/overlay_loader.py +708 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/reporting/report_suite.py +1059 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/reporting/rows.py +458 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/reporting/schema_export.py +413 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/reporting/stats_rows.py +321 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/reporting/status.py +388 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/reporting/templates/axes.js +61 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/reporting/templates/dashboard.html +284 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/reporting/templates/dashboard.js +604 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/reporting/templates/filters.js +100 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/reporting/templates/interactive.html +420 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/reporting/templates/report.js +116 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/reporting/templates/vendor/plotly.min.js +8 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/reporting/theme.py +324 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/reporting/tree_rows.py +73 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/reporting/verdicts.py +124 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/storage/__init__.py +0 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/storage/accept.py +216 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/storage/baseline.py +43 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/storage/batch_manifest.py +204 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/storage/ref_index.py +295 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/storage/reference_store.py +661 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/storage/roles.py +576 -0
- dynamic_systems_testing_framework-1.0.1/src/dstf/storage/run_stats.py +85 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[codz]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
dist/
|
|
6
|
+
build/
|
|
7
|
+
*.egg
|
|
8
|
+
|
|
9
|
+
# Virtual environments
|
|
10
|
+
.venv/
|
|
11
|
+
venv/
|
|
12
|
+
.env
|
|
13
|
+
|
|
14
|
+
# uv
|
|
15
|
+
# uv.lock is committed for reproducibility
|
|
16
|
+
|
|
17
|
+
# Dymola artifacts
|
|
18
|
+
*.mat
|
|
19
|
+
dslog.txt
|
|
20
|
+
dsin.txt
|
|
21
|
+
dsfinal.txt
|
|
22
|
+
buildlog.txt
|
|
23
|
+
dymosim
|
|
24
|
+
dymosim.exe
|
|
25
|
+
dymosim.exp
|
|
26
|
+
dymosim.lib
|
|
27
|
+
dymosim.dll
|
|
28
|
+
dsmodel.c
|
|
29
|
+
dsmodel_att.h
|
|
30
|
+
dymola.log
|
|
31
|
+
status
|
|
32
|
+
success
|
|
33
|
+
failure
|
|
34
|
+
*.bif-*
|
|
35
|
+
|
|
36
|
+
# Keep test fixture results (override above exclusions)
|
|
37
|
+
!tests/fixtures/results/**/*.mat
|
|
38
|
+
!tests/fixtures/results/**/dslog.txt
|
|
39
|
+
!tests/fixtures/results/**/dsin.txt
|
|
40
|
+
!tests/fixtures/results/**/dsfinal.txt
|
|
41
|
+
!tests/fixtures/results_openmodelica/**/*.mat
|
|
42
|
+
|
|
43
|
+
# Simulation output (generated by the tool)
|
|
44
|
+
testing_output/
|
|
45
|
+
|
|
46
|
+
# Generated reports (dashboard.html, interactive plots, comparison_data.json)
|
|
47
|
+
reports/
|
|
48
|
+
|
|
49
|
+
# Reference-FMUs binaries (fetched by scripts/fetch_reference_fmus.py)
|
|
50
|
+
examples/fmu/reference-fmus-binaries/
|
|
51
|
+
|
|
52
|
+
# IDE
|
|
53
|
+
.idea/
|
|
54
|
+
.vscode/
|
|
55
|
+
*.swp
|
|
56
|
+
*.swo
|
|
57
|
+
|
|
58
|
+
# OS
|
|
59
|
+
.DS_Store
|
|
60
|
+
Thumbs.db
|
|
61
|
+
|
|
62
|
+
# Coverage / testing
|
|
63
|
+
.coverage
|
|
64
|
+
.coverage.*
|
|
65
|
+
htmlcov/
|
|
66
|
+
.pytest_cache/
|
|
67
|
+
.tox/
|
|
68
|
+
.nox/
|
|
69
|
+
|
|
70
|
+
# Translations (gettext .mo — NOT Modelica .mo files)
|
|
71
|
+
*.pot
|
|
72
|
+
|
|
73
|
+
# Cursor
|
|
74
|
+
.cursorignore
|
|
75
|
+
.cursorindexingignore
|
|
76
|
+
# Local agent configuration and working notes. Machine-local: the paths
|
|
77
|
+
# inside resolve only on the machine that wrote them.
|
|
78
|
+
.claude/
|
|
79
|
+
|
|
80
|
+
# Test fixtures named like Dymola artifacts are real, committed test data —
|
|
81
|
+
# the artifact patterns above (dslog.txt, *.mat, ...) must not swallow them.
|
|
82
|
+
# (results/Dymola/dslog.txt + dsres.mat were already force-added; this keeps
|
|
83
|
+
# the results_additional/ dslog fixture tracked too. Big generated fixtures —
|
|
84
|
+
# dsfinal.txt, dsin.txt, dsmodel.c — stay ignored: no test references them.)
|
|
85
|
+
!tests/fixtures/**/dslog.txt
|
|
86
|
+
|
|
87
|
+
# Local working-session notes and agent state — never published.
|
|
88
|
+
# The project's durable record is docs/decisions.md and docs/ideas.md.
|
|
89
|
+
.soul/
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ORNL-Modelica
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: dynamic-systems-testing-framework
|
|
3
|
+
Version: 1.0.1
|
|
4
|
+
Summary: Dynamic Systems Testing Framework — regression & unit testing for time-dependent system behavior across Modelica (Dymola, OpenModelica), FMU (FMPy), Julia (ModelingToolkit), and Python backends
|
|
5
|
+
Project-URL: Homepage, https://github.com/ORNL-Modelica/DynamicSystemsTestingFramework
|
|
6
|
+
Project-URL: Repository, https://github.com/ORNL-Modelica/DynamicSystemsTestingFramework
|
|
7
|
+
Project-URL: Documentation, https://github.com/ORNL-Modelica/DynamicSystemsTestingFramework/blob/main/docs/usage.md
|
|
8
|
+
Project-URL: Issues, https://github.com/ORNL-Modelica/DynamicSystemsTestingFramework/issues
|
|
9
|
+
Author: Scott Greenwood
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: dymola,fmi,fmu,julia,modelica,modelingtoolkit,openmodelica,regression-testing,simulation,testing
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Intended Audience :: Science/Research
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering
|
|
19
|
+
Classifier: Topic :: Software Development :: Testing
|
|
20
|
+
Requires-Python: >=3.10
|
|
21
|
+
Requires-Dist: jinja2>=3.1
|
|
22
|
+
Requires-Dist: numpy>=1.20
|
|
23
|
+
Requires-Dist: psutil>=5.9
|
|
24
|
+
Provides-Extra: author
|
|
25
|
+
Requires-Dist: nicegui>=3.16; extra == 'author'
|
|
26
|
+
Requires-Dist: regex>=2026.9.10; extra == 'author'
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: mypy>=1.10; extra == 'dev'
|
|
29
|
+
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
|
|
30
|
+
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
|
|
31
|
+
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
32
|
+
Requires-Dist: ruff>=0.16; extra == 'dev'
|
|
33
|
+
Requires-Dist: scipy>=1.10; extra == 'dev'
|
|
34
|
+
Requires-Dist: types-regex>=2026.9.10; extra == 'dev'
|
|
35
|
+
Provides-Extra: fmpy
|
|
36
|
+
Requires-Dist: fmpy>=0.3; extra == 'fmpy'
|
|
37
|
+
Provides-Extra: om
|
|
38
|
+
Requires-Dist: ompython>=3.5; extra == 'om'
|
|
39
|
+
Description-Content-Type: text/markdown
|
|
40
|
+
|
|
41
|
+
# Dynamic Systems Testing Framework (DSTF)
|
|
42
|
+
|
|
43
|
+
[](https://github.com/ORNL-Modelica/DynamicSystemsTestingFramework/actions/workflows/quality.yml)
|
|
44
|
+
|
|
45
|
+
Regression and unit testing for time-dependent system behavior. Discovers tests, runs simulations, compares against versioned baselines, and reports — across **Modelica (Dymola, OpenModelica), FMU (FMPy), Julia/ModelingToolkit, and arbitrary Python**.
|
|
46
|
+
|
|
47
|
+
Library-agnostic: tests can be discovered from in-model annotations (`UnitTests` components), declarative `test_spec.json`, or both. Reference results are partitioned per backend / OS so the same library can be regression-tested across backends without ref drift.
|
|
48
|
+
|
|
49
|
+
Criteria are written in **`dstf author`**, a local browser tool over the same figures the report draws: edit a criterion beside the plot it scores — drag a handle, or Ctrl+click to add a row that settles on the data — give a tolerance a lower and an upper side, put one criterion on many elements of an array, queue tests on simulator workers that stay alive, and watch a running test's trace as Dymola or OpenModelica writes it. A default and a dark theme cover the tool, the dashboard and the report pages.
|
|
50
|
+
|
|
51
|
+

|
|
52
|
+
|
|
53
|
+
*The unified dashboard — live progress during a run, a static report after. Each report is stamped with the backend, tool + MSL version, and OS that produced it; the table gives per-test verdicts, worst-NRMSE, and phase timing.*
|
|
54
|
+
|
|
55
|
+
## What's in the box
|
|
56
|
+
|
|
57
|
+
- **5 backends**, each with persistent-worker support where it pays off:
|
|
58
|
+
|
|
59
|
+
| Backend | Source kind | Persistent | Notes |
|
|
60
|
+
|---|---|---|---|
|
|
61
|
+
| Dymola | Modelica `.mo` | ✓ default | Python interface; `--batch` selects the `.mos` path. Records translation statistics, so a change in a model's state count is a structural warning |
|
|
62
|
+
| OpenModelica | Modelica `.mo` | ✓ default | OMPython ZMQ; `--batch` selects the `omc -s` path. Records timing statistics and the model's state count (from the init file omc generates), so a change in it is the structural warning it is on Dymola — against a baseline that records the count, which one accepted before this does at its next accept; system sizes and Jacobian counts are Dymola's alone |
|
|
63
|
+
| FMPy | Pre-built FMU | per-test thread | `fmpy.simulate_fmu` in-process |
|
|
64
|
+
| Julia / MTK | `.jl` ModelingToolkit | ✓ default | stdin-JSON pipe; Dyad untested |
|
|
65
|
+
| Python | Any `simulate(stop_time, tol) -> dict` | subprocess-per-test | scipy, pandas, CSV loader, HTTP — anything |
|
|
66
|
+
|
|
67
|
+
- **7 comparison modes**, composable via a `MetricTree`:
|
|
68
|
+
|
|
69
|
+
| Mode | What it scores |
|
|
70
|
+
|---|---|
|
|
71
|
+
| `nrmse` | RMSE over signal range — the default |
|
|
72
|
+
| `tube` | Pass if signal stays inside a (constant or time-varying) envelope around ref |
|
|
73
|
+
| `points` | Per-point checks at declared times with `time_tolerance` x-box and abs/rel y-tolerance |
|
|
74
|
+
| `range` | Scalar `min_value` / `max_value` bounds — baseline-free |
|
|
75
|
+
| `event-timing` | Match Modelica solver events against a declared list within per-event tolerance |
|
|
76
|
+
| `dominant-frequency` | FFT peak matching against a declared peaks table — windowable |
|
|
77
|
+
| `scalar` | One number — a reduction of the signal (`max`/`min`/`mean`/`sample_mean`/`final`/`integral`) or a stored statistic by dotted path — vs a baseline scalar or declared bounds |
|
|
78
|
+
|
|
79
|
+
Combinators: `and`, `or`, `k-of-n`, `warn` (relax to advisory), `weighted`. Leaves can scope to a time window and target named baselines (`primary`, soft-checks, companions).
|
|
80
|
+
|
|
81
|
+
- **Read-only reports** — per-test Plotly HTML drawn from figures Python built: the trajectory with its criterion geometry (tube band, range bounds, point boxes, event windows, FFT spectrum), overlay picker, error overlays, and the metric tree with the CLI's verdict and score text per leaf. Plus a live dashboard during runs and JUnit XML for CI. The report shows, the CLI decides.
|
|
82
|
+
|
|
83
|
+
- **An authoring tool** — `dstf author` (the `[author]` extra) serves a Suite page and a Test page on `127.0.0.1`. Criteria are edited inline under the plot they score, staged, and written to `test_spec.json` on Save:
|
|
84
|
+
|
|
85
|
+
| Feature | What it does |
|
|
86
|
+
|---|---|
|
|
87
|
+
| Two-sided tolerance | A tolerance is one number or `{"lower": a, "upper": b}`; on the plot, an arrow outside each side sets it |
|
|
88
|
+
| On-plot editing | Drag a ring, an arrow or a bound line; Ctrl+click adds a row that settles on the data; a field's ⌖ arms it for the next click; a placed value is snapped to a readable step |
|
|
89
|
+
| Simulation queue | Queue the selected tests: first in, first out, on workers that stay alive between runs; Remove a waiting test, Cancel a running one |
|
|
90
|
+
| Live results | A running test's trace is drawn as Dymola or OpenModelica writes its result file; a cancelled or timed-out run is shown, never scored or accepted |
|
|
91
|
+
| Criteria over an array | One criterion on the elements `1:5:20` of an array; identical criteria fold into one row, edited as one |
|
|
92
|
+
| Themes | `"theme": "dark"` in `testing.json` for the dashboard, the report pages and the tool; the tool's ⋮ menu switches a browser tab live |
|
|
93
|
+
|
|
94
|
+
- **Three baseline roles** per test, partitioned on disk: **primary** (regression anchor, hard-fail), **soft_checks** (warn-wrapped cross-regression imports), **companions** (plot-only overlays, including cross-library).
|
|
95
|
+
|
|
96
|
+
## Install
|
|
97
|
+
|
|
98
|
+
End users:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
# Not on PyPI yet — these work from the first published release.
|
|
102
|
+
uv tool install dynamic-systems-testing-framework # then run plain: dstf ...
|
|
103
|
+
# or: pipx install dynamic-systems-testing-framework
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Developers:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
uv sync --extra dev --locked # core + pytest, from uv.lock
|
|
110
|
+
uv sync --extra dev --extra author --extra fmpy --extra om --locked # plus the authoring tool, FMPy + OpenModelica extras
|
|
111
|
+
uv run dstf --help
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
`uv sync` installs the project editable and pins every dependency to `uv.lock`,
|
|
115
|
+
the same lock CI uses; `--locked` fails instead of silently re-locking when the
|
|
116
|
+
lock has drifted from `pyproject.toml`. It prunes anything you did not ask for
|
|
117
|
+
on that command line — being in the lock does not save it — so name every extra
|
|
118
|
+
at once.
|
|
119
|
+
|
|
120
|
+
For the Julia backend, install Julia 1.11+ and instantiate the demo project once:
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
curl -fsSL https://install.julialang.org | sh -s -- -y --default-channel 1.11
|
|
124
|
+
cd examples/julia/JuliaMtkTestingLib && julia --project=. -e 'using Pkg; Pkg.instantiate()'
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
The Python examples (`SimpleRamp`, `PointsCheck`) need scipy, which the `dev`
|
|
128
|
+
extra installs. Reach them through `uv run dstf ...`: the Python backend takes
|
|
129
|
+
the interpreter from `testing.json`'s `backends: {"Python": [...]}` entry and
|
|
130
|
+
falls back to `PATH` — and the shipped example names `python`, so either route
|
|
131
|
+
ends at the first `python` on `PATH`, which `uv run` points at `.venv`. Run the
|
|
132
|
+
same examples from a tool-installed `dstf` and that is whatever comes first on
|
|
133
|
+
your PATH — a conda env, often — which needs its own scipy.
|
|
134
|
+
|
|
135
|
+
## First 5 minutes
|
|
136
|
+
|
|
137
|
+
The repo ships demo libraries for every backend. Easiest first run is the Modelica demo:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
# From the repo root:
|
|
141
|
+
uv run dstf --config examples/modelica/ModelicaTestingLib/Resources/ReferenceResults/testing.json run
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
What this does:
|
|
145
|
+
|
|
146
|
+
1. Reads `testing.json` to find the demo library, the backend (Dymola or OpenModelica — first one whose binary exists wins), the reference root, and any per-test recognizers.
|
|
147
|
+
2. Discovers the demo tests via the bundled `UnitTests` recognizer plus declarative recognizers in the JSON.
|
|
148
|
+
3. Runs each in a persistent simulator worker, partitioning output under `testing_output/` by backend + OS.
|
|
149
|
+
4. Compares against `Resources/ReferenceResults/<Backend>/<os>/ref_NNNN.json` baselines.
|
|
150
|
+
5. Prints a console report with pass/fail per test.
|
|
151
|
+
|
|
152
|
+
To see the interactive HTML reporter:
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
uv run dstf --config examples/modelica/ModelicaTestingLib/Resources/ReferenceResults/testing.json run --report
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
The dashboard URL is printed as a run starts (`Live progress: file://…`) and the page is opened in a browser tab; it reloads itself every 5s during the run. `--report` writes the per-test pages under `<work_dir>/reports/` and opens the dashboard again when they are written. `--no-browser` (on `run` and `compare`) opens no tab and still prints the link.
|
|
159
|
+
|
|
160
|
+
The examples ship baselines for these `<Backend>/<os>` partitions: Modelica — `Dymola/linux`, `Dymola/windows` and `OpenModelica/linux` (its array model, `HeatRodTest`, on the two Linux ones); FMU — `FMPy/linux` and `FMPy/windows`; Julia — `Julia/linux`; Python — `Python/linux`. A first run on an OS a backend has no partition for (OpenModelica, Julia or Python on Windows, say) reports every test `NO_REF` until `run --accept` stores its baselines there. A companion in the examples is another source's curve of the SAME model, drawn over the test's plot when its box is ticked on the report page, and each ships in the partitions named here and no other: in `OpenModelica/linux`, the Modelica `FrequencyTest`, `RangeCheckTest` and `MultiFrequencyTest` carry the Julia example's baseline of that model; in `Julia/linux`, the Julia `Frequency`, `RangeCheck` and `MultiFrequency` carry the OpenModelica one; and `MetricTreeTest` carries its closed-form solution in `Dymola/linux` and `OpenModelica/linux`. A Dymola run of the example therefore shows no Julia companion, and `Dymola/windows` ships no companion at all.
|
|
161
|
+
|
|
162
|
+
To repeat with another backend's demo library, swap the config path:
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
# Julia/MTK, Python, and prebuilt FMUs through FMPy
|
|
166
|
+
uv run dstf --config examples/julia/JuliaMtkTestingLib/Resources/ReferenceResults/testing.json run
|
|
167
|
+
uv run dstf --config examples/python/PythonTestingLib/Resources/ReferenceResults/testing.json run
|
|
168
|
+
uv run dstf --config examples/fmu/testing.json run # needs reference-fmus-binaries/
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## Configuration cheat-sheet
|
|
172
|
+
|
|
173
|
+
`testing.json` is the single entry point. The full field reference lives in `docs/usage.md`; the headline shape:
|
|
174
|
+
|
|
175
|
+
```json
|
|
176
|
+
{
|
|
177
|
+
"source_path": "../../MyLib",
|
|
178
|
+
"backends": {
|
|
179
|
+
"Dymola": ["C:\\Program Files\\Dymola 2026x\\bin64\\Dymola.exe"],
|
|
180
|
+
"OpenModelica": ["/usr/bin/omc"]
|
|
181
|
+
},
|
|
182
|
+
"test_spec": "test_spec.json",
|
|
183
|
+
"dependencies": ["/path/to/SomeDep"],
|
|
184
|
+
"tolerance": 1e-4,
|
|
185
|
+
"diagnostic_variables": ["CPUtime", "EventCounter"]
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
The first `backends` entry whose binary exists wins (no need for per-OS config files). `backend: "Dymola"` (or any named entry) forces a specific one.
|
|
190
|
+
|
|
191
|
+
## Common commands
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
# Discover tests without running
|
|
195
|
+
uv run dstf --config testing.json discover
|
|
196
|
+
|
|
197
|
+
# Run, compare, accept all current results as new baselines
|
|
198
|
+
uv run dstf --config testing.json run --accept
|
|
199
|
+
|
|
200
|
+
# Interactive review by category
|
|
201
|
+
uv run dstf --config testing.json run -i failed
|
|
202
|
+
|
|
203
|
+
# Filter (glob, comma list, or @file)
|
|
204
|
+
uv run dstf --config testing.json run --filter "Foo.A,Foo.B"
|
|
205
|
+
uv run dstf --config testing.json run --filter @rerun.txt
|
|
206
|
+
|
|
207
|
+
# Parallel + interactive HTML report (--batch-size N splits a Dymola --batch run into small batches)
|
|
208
|
+
uv run dstf --config testing.json run --parallel 4 --report
|
|
209
|
+
|
|
210
|
+
# Rerun only previously-failed tests, merge with last full run, regen report
|
|
211
|
+
uv run dstf --config testing.json run --rerun failed,sim-failed --merge --report
|
|
212
|
+
|
|
213
|
+
# Compare without re-simulating (uses last results)
|
|
214
|
+
uv run dstf --config testing.json compare
|
|
215
|
+
|
|
216
|
+
# Open the authoring tool in a browser (needs the [author] extra)
|
|
217
|
+
uv run dstf --config testing.json author
|
|
218
|
+
|
|
219
|
+
# Apply an RFC 6902 JSON-Patch envelope against test_spec.json — single- or
|
|
220
|
+
# multi-model form, from any source
|
|
221
|
+
uv run dstf --config testing.json spec-update spec_patch.json
|
|
222
|
+
|
|
223
|
+
# Manage the other baseline roles
|
|
224
|
+
uv run dstf --config testing.json companion add <model> <name> <path>
|
|
225
|
+
uv run dstf --config testing.json soft-check list <model>
|
|
226
|
+
uv run dstf --config testing.json import-baseline <model> <name> <source> # soft_checks only
|
|
227
|
+
|
|
228
|
+
# Manifest / schema
|
|
229
|
+
uv run dstf --config testing.json manifest dump
|
|
230
|
+
uv run dstf --config testing.json manifest cleanup [--orphans] [--apply] # dry-run by default
|
|
231
|
+
uv run dstf --config testing.json export-schema --output schema.json
|
|
232
|
+
|
|
233
|
+
# Diagnose Dymola Python interface discovery
|
|
234
|
+
uv run dstf check-dymola
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
`uv run dstf <command> --help` for each subcommand's full flag set.
|
|
238
|
+
|
|
239
|
+
## Where things live
|
|
240
|
+
|
|
241
|
+
| Doc | What's in it |
|
|
242
|
+
|---|---|
|
|
243
|
+
| `docs/usage.md` | The deep user guide — every command, every config field, every comparison mode |
|
|
244
|
+
| `docs/architecture.md` | Six-layer plug-in pipeline + current-state map to code |
|
|
245
|
+
| `docs/extensibility.md` | Plug-in contracts for Source / Discovery / Backend / Dataset / Metric / Combinator |
|
|
246
|
+
| `docs/vision.md` | Where the framework is going (target user base, non-Modelica futures) |
|
|
247
|
+
| `docs/decisions.md` | Authoritative decision log |
|
|
248
|
+
| `docs/ideas.md` | Backlog (unimplemented features, with explicit deferred-by-design entries) |
|
|
249
|
+
| `examples/{modelica,fmu,julia,python}/` | Working demo libraries — one per backend |
|
|
250
|
+
|
|
251
|
+
## CI integration
|
|
252
|
+
|
|
253
|
+
```yaml
|
|
254
|
+
- run: |
|
|
255
|
+
uv run dstf --config ./testing.json run --report-format junit --output test-results.xml
|
|
256
|
+
- uses: actions/upload-artifact@v4
|
|
257
|
+
with:
|
|
258
|
+
name: test-results
|
|
259
|
+
path: test-results.xml
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
For HTML reports as build artifacts, also pass `--report` and upload `dashboard.html` and the `reports/` directory from the work dir (`--work-dir` sets it).
|
|
263
|
+
|
|
264
|
+
## Status
|
|
265
|
+
|
|
266
|
+
v1.0. 5 backends in production, 4 demo libraries, and a comprehensive pytest suite (a Playwright subset drives the report pages and the authoring tool in a real browser). The report is read-only and mutates nothing: every number it draws comes from a figure Python built or a string the server rendered. Criteria are authored in `dstf author`, driven by the same figures and the same scorer as the CLI. Nothing is published to PyPI yet.
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
# Dynamic Systems Testing Framework (DSTF)
|
|
2
|
+
|
|
3
|
+
[](https://github.com/ORNL-Modelica/DynamicSystemsTestingFramework/actions/workflows/quality.yml)
|
|
4
|
+
|
|
5
|
+
Regression and unit testing for time-dependent system behavior. Discovers tests, runs simulations, compares against versioned baselines, and reports — across **Modelica (Dymola, OpenModelica), FMU (FMPy), Julia/ModelingToolkit, and arbitrary Python**.
|
|
6
|
+
|
|
7
|
+
Library-agnostic: tests can be discovered from in-model annotations (`UnitTests` components), declarative `test_spec.json`, or both. Reference results are partitioned per backend / OS so the same library can be regression-tested across backends without ref drift.
|
|
8
|
+
|
|
9
|
+
Criteria are written in **`dstf author`**, a local browser tool over the same figures the report draws: edit a criterion beside the plot it scores — drag a handle, or Ctrl+click to add a row that settles on the data — give a tolerance a lower and an upper side, put one criterion on many elements of an array, queue tests on simulator workers that stay alive, and watch a running test's trace as Dymola or OpenModelica writes it. A default and a dark theme cover the tool, the dashboard and the report pages.
|
|
10
|
+
|
|
11
|
+

|
|
12
|
+
|
|
13
|
+
*The unified dashboard — live progress during a run, a static report after. Each report is stamped with the backend, tool + MSL version, and OS that produced it; the table gives per-test verdicts, worst-NRMSE, and phase timing.*
|
|
14
|
+
|
|
15
|
+
## What's in the box
|
|
16
|
+
|
|
17
|
+
- **5 backends**, each with persistent-worker support where it pays off:
|
|
18
|
+
|
|
19
|
+
| Backend | Source kind | Persistent | Notes |
|
|
20
|
+
|---|---|---|---|
|
|
21
|
+
| Dymola | Modelica `.mo` | ✓ default | Python interface; `--batch` selects the `.mos` path. Records translation statistics, so a change in a model's state count is a structural warning |
|
|
22
|
+
| OpenModelica | Modelica `.mo` | ✓ default | OMPython ZMQ; `--batch` selects the `omc -s` path. Records timing statistics and the model's state count (from the init file omc generates), so a change in it is the structural warning it is on Dymola — against a baseline that records the count, which one accepted before this does at its next accept; system sizes and Jacobian counts are Dymola's alone |
|
|
23
|
+
| FMPy | Pre-built FMU | per-test thread | `fmpy.simulate_fmu` in-process |
|
|
24
|
+
| Julia / MTK | `.jl` ModelingToolkit | ✓ default | stdin-JSON pipe; Dyad untested |
|
|
25
|
+
| Python | Any `simulate(stop_time, tol) -> dict` | subprocess-per-test | scipy, pandas, CSV loader, HTTP — anything |
|
|
26
|
+
|
|
27
|
+
- **7 comparison modes**, composable via a `MetricTree`:
|
|
28
|
+
|
|
29
|
+
| Mode | What it scores |
|
|
30
|
+
|---|---|
|
|
31
|
+
| `nrmse` | RMSE over signal range — the default |
|
|
32
|
+
| `tube` | Pass if signal stays inside a (constant or time-varying) envelope around ref |
|
|
33
|
+
| `points` | Per-point checks at declared times with `time_tolerance` x-box and abs/rel y-tolerance |
|
|
34
|
+
| `range` | Scalar `min_value` / `max_value` bounds — baseline-free |
|
|
35
|
+
| `event-timing` | Match Modelica solver events against a declared list within per-event tolerance |
|
|
36
|
+
| `dominant-frequency` | FFT peak matching against a declared peaks table — windowable |
|
|
37
|
+
| `scalar` | One number — a reduction of the signal (`max`/`min`/`mean`/`sample_mean`/`final`/`integral`) or a stored statistic by dotted path — vs a baseline scalar or declared bounds |
|
|
38
|
+
|
|
39
|
+
Combinators: `and`, `or`, `k-of-n`, `warn` (relax to advisory), `weighted`. Leaves can scope to a time window and target named baselines (`primary`, soft-checks, companions).
|
|
40
|
+
|
|
41
|
+
- **Read-only reports** — per-test Plotly HTML drawn from figures Python built: the trajectory with its criterion geometry (tube band, range bounds, point boxes, event windows, FFT spectrum), overlay picker, error overlays, and the metric tree with the CLI's verdict and score text per leaf. Plus a live dashboard during runs and JUnit XML for CI. The report shows, the CLI decides.
|
|
42
|
+
|
|
43
|
+
- **An authoring tool** — `dstf author` (the `[author]` extra) serves a Suite page and a Test page on `127.0.0.1`. Criteria are edited inline under the plot they score, staged, and written to `test_spec.json` on Save:
|
|
44
|
+
|
|
45
|
+
| Feature | What it does |
|
|
46
|
+
|---|---|
|
|
47
|
+
| Two-sided tolerance | A tolerance is one number or `{"lower": a, "upper": b}`; on the plot, an arrow outside each side sets it |
|
|
48
|
+
| On-plot editing | Drag a ring, an arrow or a bound line; Ctrl+click adds a row that settles on the data; a field's ⌖ arms it for the next click; a placed value is snapped to a readable step |
|
|
49
|
+
| Simulation queue | Queue the selected tests: first in, first out, on workers that stay alive between runs; Remove a waiting test, Cancel a running one |
|
|
50
|
+
| Live results | A running test's trace is drawn as Dymola or OpenModelica writes its result file; a cancelled or timed-out run is shown, never scored or accepted |
|
|
51
|
+
| Criteria over an array | One criterion on the elements `1:5:20` of an array; identical criteria fold into one row, edited as one |
|
|
52
|
+
| Themes | `"theme": "dark"` in `testing.json` for the dashboard, the report pages and the tool; the tool's ⋮ menu switches a browser tab live |
|
|
53
|
+
|
|
54
|
+
- **Three baseline roles** per test, partitioned on disk: **primary** (regression anchor, hard-fail), **soft_checks** (warn-wrapped cross-regression imports), **companions** (plot-only overlays, including cross-library).
|
|
55
|
+
|
|
56
|
+
## Install
|
|
57
|
+
|
|
58
|
+
End users:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
# Not on PyPI yet — these work from the first published release.
|
|
62
|
+
uv tool install dynamic-systems-testing-framework # then run plain: dstf ...
|
|
63
|
+
# or: pipx install dynamic-systems-testing-framework
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Developers:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
uv sync --extra dev --locked # core + pytest, from uv.lock
|
|
70
|
+
uv sync --extra dev --extra author --extra fmpy --extra om --locked # plus the authoring tool, FMPy + OpenModelica extras
|
|
71
|
+
uv run dstf --help
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
`uv sync` installs the project editable and pins every dependency to `uv.lock`,
|
|
75
|
+
the same lock CI uses; `--locked` fails instead of silently re-locking when the
|
|
76
|
+
lock has drifted from `pyproject.toml`. It prunes anything you did not ask for
|
|
77
|
+
on that command line — being in the lock does not save it — so name every extra
|
|
78
|
+
at once.
|
|
79
|
+
|
|
80
|
+
For the Julia backend, install Julia 1.11+ and instantiate the demo project once:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
curl -fsSL https://install.julialang.org | sh -s -- -y --default-channel 1.11
|
|
84
|
+
cd examples/julia/JuliaMtkTestingLib && julia --project=. -e 'using Pkg; Pkg.instantiate()'
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
The Python examples (`SimpleRamp`, `PointsCheck`) need scipy, which the `dev`
|
|
88
|
+
extra installs. Reach them through `uv run dstf ...`: the Python backend takes
|
|
89
|
+
the interpreter from `testing.json`'s `backends: {"Python": [...]}` entry and
|
|
90
|
+
falls back to `PATH` — and the shipped example names `python`, so either route
|
|
91
|
+
ends at the first `python` on `PATH`, which `uv run` points at `.venv`. Run the
|
|
92
|
+
same examples from a tool-installed `dstf` and that is whatever comes first on
|
|
93
|
+
your PATH — a conda env, often — which needs its own scipy.
|
|
94
|
+
|
|
95
|
+
## First 5 minutes
|
|
96
|
+
|
|
97
|
+
The repo ships demo libraries for every backend. Easiest first run is the Modelica demo:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
# From the repo root:
|
|
101
|
+
uv run dstf --config examples/modelica/ModelicaTestingLib/Resources/ReferenceResults/testing.json run
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
What this does:
|
|
105
|
+
|
|
106
|
+
1. Reads `testing.json` to find the demo library, the backend (Dymola or OpenModelica — first one whose binary exists wins), the reference root, and any per-test recognizers.
|
|
107
|
+
2. Discovers the demo tests via the bundled `UnitTests` recognizer plus declarative recognizers in the JSON.
|
|
108
|
+
3. Runs each in a persistent simulator worker, partitioning output under `testing_output/` by backend + OS.
|
|
109
|
+
4. Compares against `Resources/ReferenceResults/<Backend>/<os>/ref_NNNN.json` baselines.
|
|
110
|
+
5. Prints a console report with pass/fail per test.
|
|
111
|
+
|
|
112
|
+
To see the interactive HTML reporter:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
uv run dstf --config examples/modelica/ModelicaTestingLib/Resources/ReferenceResults/testing.json run --report
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
The dashboard URL is printed as a run starts (`Live progress: file://…`) and the page is opened in a browser tab; it reloads itself every 5s during the run. `--report` writes the per-test pages under `<work_dir>/reports/` and opens the dashboard again when they are written. `--no-browser` (on `run` and `compare`) opens no tab and still prints the link.
|
|
119
|
+
|
|
120
|
+
The examples ship baselines for these `<Backend>/<os>` partitions: Modelica — `Dymola/linux`, `Dymola/windows` and `OpenModelica/linux` (its array model, `HeatRodTest`, on the two Linux ones); FMU — `FMPy/linux` and `FMPy/windows`; Julia — `Julia/linux`; Python — `Python/linux`. A first run on an OS a backend has no partition for (OpenModelica, Julia or Python on Windows, say) reports every test `NO_REF` until `run --accept` stores its baselines there. A companion in the examples is another source's curve of the SAME model, drawn over the test's plot when its box is ticked on the report page, and each ships in the partitions named here and no other: in `OpenModelica/linux`, the Modelica `FrequencyTest`, `RangeCheckTest` and `MultiFrequencyTest` carry the Julia example's baseline of that model; in `Julia/linux`, the Julia `Frequency`, `RangeCheck` and `MultiFrequency` carry the OpenModelica one; and `MetricTreeTest` carries its closed-form solution in `Dymola/linux` and `OpenModelica/linux`. A Dymola run of the example therefore shows no Julia companion, and `Dymola/windows` ships no companion at all.
|
|
121
|
+
|
|
122
|
+
To repeat with another backend's demo library, swap the config path:
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
# Julia/MTK, Python, and prebuilt FMUs through FMPy
|
|
126
|
+
uv run dstf --config examples/julia/JuliaMtkTestingLib/Resources/ReferenceResults/testing.json run
|
|
127
|
+
uv run dstf --config examples/python/PythonTestingLib/Resources/ReferenceResults/testing.json run
|
|
128
|
+
uv run dstf --config examples/fmu/testing.json run # needs reference-fmus-binaries/
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Configuration cheat-sheet
|
|
132
|
+
|
|
133
|
+
`testing.json` is the single entry point. The full field reference lives in `docs/usage.md`; the headline shape:
|
|
134
|
+
|
|
135
|
+
```json
|
|
136
|
+
{
|
|
137
|
+
"source_path": "../../MyLib",
|
|
138
|
+
"backends": {
|
|
139
|
+
"Dymola": ["C:\\Program Files\\Dymola 2026x\\bin64\\Dymola.exe"],
|
|
140
|
+
"OpenModelica": ["/usr/bin/omc"]
|
|
141
|
+
},
|
|
142
|
+
"test_spec": "test_spec.json",
|
|
143
|
+
"dependencies": ["/path/to/SomeDep"],
|
|
144
|
+
"tolerance": 1e-4,
|
|
145
|
+
"diagnostic_variables": ["CPUtime", "EventCounter"]
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
The first `backends` entry whose binary exists wins (no need for per-OS config files). `backend: "Dymola"` (or any named entry) forces a specific one.
|
|
150
|
+
|
|
151
|
+
## Common commands
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
# Discover tests without running
|
|
155
|
+
uv run dstf --config testing.json discover
|
|
156
|
+
|
|
157
|
+
# Run, compare, accept all current results as new baselines
|
|
158
|
+
uv run dstf --config testing.json run --accept
|
|
159
|
+
|
|
160
|
+
# Interactive review by category
|
|
161
|
+
uv run dstf --config testing.json run -i failed
|
|
162
|
+
|
|
163
|
+
# Filter (glob, comma list, or @file)
|
|
164
|
+
uv run dstf --config testing.json run --filter "Foo.A,Foo.B"
|
|
165
|
+
uv run dstf --config testing.json run --filter @rerun.txt
|
|
166
|
+
|
|
167
|
+
# Parallel + interactive HTML report (--batch-size N splits a Dymola --batch run into small batches)
|
|
168
|
+
uv run dstf --config testing.json run --parallel 4 --report
|
|
169
|
+
|
|
170
|
+
# Rerun only previously-failed tests, merge with last full run, regen report
|
|
171
|
+
uv run dstf --config testing.json run --rerun failed,sim-failed --merge --report
|
|
172
|
+
|
|
173
|
+
# Compare without re-simulating (uses last results)
|
|
174
|
+
uv run dstf --config testing.json compare
|
|
175
|
+
|
|
176
|
+
# Open the authoring tool in a browser (needs the [author] extra)
|
|
177
|
+
uv run dstf --config testing.json author
|
|
178
|
+
|
|
179
|
+
# Apply an RFC 6902 JSON-Patch envelope against test_spec.json — single- or
|
|
180
|
+
# multi-model form, from any source
|
|
181
|
+
uv run dstf --config testing.json spec-update spec_patch.json
|
|
182
|
+
|
|
183
|
+
# Manage the other baseline roles
|
|
184
|
+
uv run dstf --config testing.json companion add <model> <name> <path>
|
|
185
|
+
uv run dstf --config testing.json soft-check list <model>
|
|
186
|
+
uv run dstf --config testing.json import-baseline <model> <name> <source> # soft_checks only
|
|
187
|
+
|
|
188
|
+
# Manifest / schema
|
|
189
|
+
uv run dstf --config testing.json manifest dump
|
|
190
|
+
uv run dstf --config testing.json manifest cleanup [--orphans] [--apply] # dry-run by default
|
|
191
|
+
uv run dstf --config testing.json export-schema --output schema.json
|
|
192
|
+
|
|
193
|
+
# Diagnose Dymola Python interface discovery
|
|
194
|
+
uv run dstf check-dymola
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
`uv run dstf <command> --help` for each subcommand's full flag set.
|
|
198
|
+
|
|
199
|
+
## Where things live
|
|
200
|
+
|
|
201
|
+
| Doc | What's in it |
|
|
202
|
+
|---|---|
|
|
203
|
+
| `docs/usage.md` | The deep user guide — every command, every config field, every comparison mode |
|
|
204
|
+
| `docs/architecture.md` | Six-layer plug-in pipeline + current-state map to code |
|
|
205
|
+
| `docs/extensibility.md` | Plug-in contracts for Source / Discovery / Backend / Dataset / Metric / Combinator |
|
|
206
|
+
| `docs/vision.md` | Where the framework is going (target user base, non-Modelica futures) |
|
|
207
|
+
| `docs/decisions.md` | Authoritative decision log |
|
|
208
|
+
| `docs/ideas.md` | Backlog (unimplemented features, with explicit deferred-by-design entries) |
|
|
209
|
+
| `examples/{modelica,fmu,julia,python}/` | Working demo libraries — one per backend |
|
|
210
|
+
|
|
211
|
+
## CI integration
|
|
212
|
+
|
|
213
|
+
```yaml
|
|
214
|
+
- run: |
|
|
215
|
+
uv run dstf --config ./testing.json run --report-format junit --output test-results.xml
|
|
216
|
+
- uses: actions/upload-artifact@v4
|
|
217
|
+
with:
|
|
218
|
+
name: test-results
|
|
219
|
+
path: test-results.xml
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
For HTML reports as build artifacts, also pass `--report` and upload `dashboard.html` and the `reports/` directory from the work dir (`--work-dir` sets it).
|
|
223
|
+
|
|
224
|
+
## Status
|
|
225
|
+
|
|
226
|
+
v1.0. 5 backends in production, 4 demo libraries, and a comprehensive pytest suite (a Playwright subset drives the report pages and the authoring tool in a real browser). The report is read-only and mutates nothing: every number it draws comes from a figure Python built or a string the server rendered. Criteria are authored in `dstf author`, driven by the same figures and the same scorer as the CLI. Nothing is published to PyPI yet.
|