al-dvc 0.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- al_dvc-0.4.0/.gitignore +52 -0
- al_dvc-0.4.0/CHANGELOG.md +371 -0
- al_dvc-0.4.0/LICENSE +29 -0
- al_dvc-0.4.0/PKG-INFO +457 -0
- al_dvc-0.4.0/README.md +418 -0
- al_dvc-0.4.0/docs/design.md +644 -0
- al_dvc-0.4.0/pyproject.toml +113 -0
- al_dvc-0.4.0/src/al_dvc/__init__.py +46 -0
- al_dvc-0.4.0/src/al_dvc/__main__.py +6 -0
- al_dvc-0.4.0/src/al_dvc/_numba_compat.py +92 -0
- al_dvc-0.4.0/src/al_dvc/cli.py +341 -0
- al_dvc-0.4.0/src/al_dvc/core/__init__.py +49 -0
- al_dvc-0.4.0/src/al_dvc/core/checkpoint.py +209 -0
- al_dvc-0.4.0/src/al_dvc/core/config.py +317 -0
- al_dvc-0.4.0/src/al_dvc/core/data_structures.py +494 -0
- al_dvc-0.4.0/src/al_dvc/core/pipeline.py +454 -0
- al_dvc-0.4.0/src/al_dvc/export/__init__.py +32 -0
- al_dvc-0.4.0/src/al_dvc/export/export_csv.py +47 -0
- al_dvc-0.4.0/src/al_dvc/export/export_mat.py +93 -0
- al_dvc-0.4.0/src/al_dvc/export/export_npz.py +73 -0
- al_dvc-0.4.0/src/al_dvc/export/export_params.py +31 -0
- al_dvc-0.4.0/src/al_dvc/export/export_report.py +204 -0
- al_dvc-0.4.0/src/al_dvc/export/export_utils.py +116 -0
- al_dvc-0.4.0/src/al_dvc/export/export_vtk.py +94 -0
- al_dvc-0.4.0/src/al_dvc/export/slice_plots.py +321 -0
- al_dvc-0.4.0/src/al_dvc/gui/__init__.py +1 -0
- al_dvc-0.4.0/src/al_dvc/gui/app.py +666 -0
- al_dvc-0.4.0/src/al_dvc/gui/app_state.py +388 -0
- al_dvc-0.4.0/src/al_dvc/gui/arrows/spin_down.svg +3 -0
- al_dvc-0.4.0/src/al_dvc/gui/arrows/spin_down_hover.svg +3 -0
- al_dvc-0.4.0/src/al_dvc/gui/arrows/spin_up.svg +3 -0
- al_dvc-0.4.0/src/al_dvc/gui/arrows/spin_up_hover.svg +3 -0
- al_dvc-0.4.0/src/al_dvc/gui/assets/pyALDVC.png +0 -0
- al_dvc-0.4.0/src/al_dvc/gui/batch.py +231 -0
- al_dvc-0.4.0/src/al_dvc/gui/dialogs/__init__.py +1 -0
- al_dvc-0.4.0/src/al_dvc/gui/dialogs/batch_dialog.py +352 -0
- al_dvc-0.4.0/src/al_dvc/gui/dialogs/export_dialog.py +488 -0
- al_dvc-0.4.0/src/al_dvc/gui/field_canvas.py +216 -0
- al_dvc-0.4.0/src/al_dvc/gui/i18n.py +133 -0
- al_dvc-0.4.0/src/al_dvc/gui/i18n_tools.py +116 -0
- al_dvc-0.4.0/src/al_dvc/gui/icons.py +89 -0
- al_dvc-0.4.0/src/al_dvc/gui/kernel_warmup.py +46 -0
- al_dvc-0.4.0/src/al_dvc/gui/lattice_preview.py +181 -0
- al_dvc-0.4.0/src/al_dvc/gui/mask_editor.py +349 -0
- al_dvc-0.4.0/src/al_dvc/gui/names.py +162 -0
- al_dvc-0.4.0/src/al_dvc/gui/panels/__init__.py +1 -0
- al_dvc-0.4.0/src/al_dvc/gui/panels/mask_tools.py +326 -0
- al_dvc-0.4.0/src/al_dvc/gui/panels/param_panel.py +432 -0
- al_dvc-0.4.0/src/al_dvc/gui/panels/results_panel.py +313 -0
- al_dvc-0.4.0/src/al_dvc/gui/panels/run_panel.py +184 -0
- al_dvc-0.4.0/src/al_dvc/gui/panels/view3d.py +534 -0
- al_dvc-0.4.0/src/al_dvc/gui/panels/viewer.py +655 -0
- al_dvc-0.4.0/src/al_dvc/gui/panels/volume_panel.py +357 -0
- al_dvc-0.4.0/src/al_dvc/gui/pipeline_worker.py +73 -0
- al_dvc-0.4.0/src/al_dvc/gui/self_test.py +163 -0
- al_dvc-0.4.0/src/al_dvc/gui/session.py +199 -0
- al_dvc-0.4.0/src/al_dvc/gui/sticky_headers.py +102 -0
- al_dvc-0.4.0/src/al_dvc/gui/strain_window.py +541 -0
- al_dvc-0.4.0/src/al_dvc/gui/theme.py +726 -0
- al_dvc-0.4.0/src/al_dvc/gui/translations/de.json +394 -0
- al_dvc-0.4.0/src/al_dvc/gui/translations/es.json +394 -0
- al_dvc-0.4.0/src/al_dvc/gui/translations/fr.json +394 -0
- al_dvc-0.4.0/src/al_dvc/gui/translations/ja.json +394 -0
- al_dvc-0.4.0/src/al_dvc/gui/translations/zh_CN.json +394 -0
- al_dvc-0.4.0/src/al_dvc/gui/translations/zh_TW.json +394 -0
- al_dvc-0.4.0/src/al_dvc/gui/view3d_scene.py +351 -0
- al_dvc-0.4.0/src/al_dvc/gui/widgets.py +272 -0
- al_dvc-0.4.0/src/al_dvc/gui/window_chrome.py +50 -0
- al_dvc-0.4.0/src/al_dvc/gui/window_geometry.py +75 -0
- al_dvc-0.4.0/src/al_dvc/io/__init__.py +39 -0
- al_dvc-0.4.0/src/al_dvc/io/matlab_results.py +213 -0
- al_dvc-0.4.0/src/al_dvc/io/volume_io.py +473 -0
- al_dvc-0.4.0/src/al_dvc/io/volume_ops.py +375 -0
- al_dvc-0.4.0/src/al_dvc/mesh/__init__.py +26 -0
- al_dvc-0.4.0/src/al_dvc/mesh/grid_mesh.py +242 -0
- al_dvc-0.4.0/src/al_dvc/mesh/hex8.py +105 -0
- al_dvc-0.4.0/src/al_dvc/solver/__init__.py +44 -0
- al_dvc-0.4.0/src/al_dvc/solver/beta_tuning.py +90 -0
- al_dvc-0.4.0/src/al_dvc/solver/coarse_init.py +128 -0
- al_dvc-0.4.0/src/al_dvc/solver/cuda_kernels.py +1643 -0
- al_dvc-0.4.0/src/al_dvc/solver/global_operators.py +158 -0
- al_dvc-0.4.0/src/al_dvc/solver/init_disp.py +149 -0
- al_dvc-0.4.0/src/al_dvc/solver/integer_search.py +688 -0
- al_dvc-0.4.0/src/al_dvc/solver/interp_kernels.py +152 -0
- al_dvc-0.4.0/src/al_dvc/solver/local_icgn.py +372 -0
- al_dvc-0.4.0/src/al_dvc/solver/numba_kernels.py +1207 -0
- al_dvc-0.4.0/src/al_dvc/solver/reference_kernels.py +617 -0
- al_dvc-0.4.0/src/al_dvc/solver/subpb1_solver.py +183 -0
- al_dvc-0.4.0/src/al_dvc/solver/subpb2_solver.py +155 -0
- al_dvc-0.4.0/src/al_dvc/solver/uncertainty.py +133 -0
- al_dvc-0.4.0/src/al_dvc/solver/warmup.py +47 -0
- al_dvc-0.4.0/src/al_dvc/strain/__init__.py +30 -0
- al_dvc-0.4.0/src/al_dvc/strain/compute_strain.py +130 -0
- al_dvc-0.4.0/src/al_dvc/strain/gradient_methods.py +140 -0
- al_dvc-0.4.0/src/al_dvc/strain/strain_types.py +123 -0
- al_dvc-0.4.0/src/al_dvc/synthetic.py +145 -0
- al_dvc-0.4.0/src/al_dvc/utils/__init__.py +28 -0
- al_dvc-0.4.0/src/al_dvc/utils/grid_interp.py +79 -0
- al_dvc-0.4.0/src/al_dvc/utils/inpaint.py +123 -0
- al_dvc-0.4.0/src/al_dvc/utils/outlier_detection.py +95 -0
- al_dvc-0.4.0/src/al_dvc/utils/validation.py +55 -0
- al_dvc-0.4.0/src/al_dvc/viz/__init__.py +10 -0
- al_dvc-0.4.0/src/al_dvc/viz/slices.py +108 -0
- al_dvc-0.4.0/tests/__init__.py +0 -0
- al_dvc-0.4.0/tests/conftest.py +81 -0
- al_dvc-0.4.0/tests/test_anisotropic_subset.py +145 -0
- al_dvc-0.4.0/tests/test_batch.py +159 -0
- al_dvc-0.4.0/tests/test_beta_tuning.py +33 -0
- al_dvc-0.4.0/tests/test_checkpoint.py +103 -0
- al_dvc-0.4.0/tests/test_coarse_init.py +89 -0
- al_dvc-0.4.0/tests/test_config.py +115 -0
- al_dvc-0.4.0/tests/test_cuda_backend.py +313 -0
- al_dvc-0.4.0/tests/test_deformed_mask.py +219 -0
- al_dvc-0.4.0/tests/test_examples.py +39 -0
- al_dvc-0.4.0/tests/test_export_cli.py +153 -0
- al_dvc-0.4.0/tests/test_export_dialog.py +97 -0
- al_dvc-0.4.0/tests/test_frozen_bundle.py +69 -0
- al_dvc-0.4.0/tests/test_global_step.py +106 -0
- al_dvc-0.4.0/tests/test_gradient_mode.py +145 -0
- al_dvc-0.4.0/tests/test_gui.py +326 -0
- al_dvc-0.4.0/tests/test_i18n.py +63 -0
- al_dvc-0.4.0/tests/test_icgn_stall.py +184 -0
- al_dvc-0.4.0/tests/test_icgn_tolerance.py +95 -0
- al_dvc-0.4.0/tests/test_integer_search.py +90 -0
- al_dvc-0.4.0/tests/test_io.py +126 -0
- al_dvc-0.4.0/tests/test_kernels.py +292 -0
- al_dvc-0.4.0/tests/test_lattice_preview.py +190 -0
- al_dvc-0.4.0/tests/test_mask_editor.py +157 -0
- al_dvc-0.4.0/tests/test_mask_tools.py +236 -0
- al_dvc-0.4.0/tests/test_matlab_results.py +110 -0
- al_dvc-0.4.0/tests/test_mesh.py +97 -0
- al_dvc-0.4.0/tests/test_ncc_expansion.py +77 -0
- al_dvc-0.4.0/tests/test_noise_hessian.py +145 -0
- al_dvc-0.4.0/tests/test_pipeline.py +220 -0
- al_dvc-0.4.0/tests/test_predictive_stop.py +128 -0
- al_dvc-0.4.0/tests/test_provider_cache.py +43 -0
- al_dvc-0.4.0/tests/test_roi_tools.py +140 -0
- al_dvc-0.4.0/tests/test_strain.py +99 -0
- al_dvc-0.4.0/tests/test_strain_window.py +111 -0
- al_dvc-0.4.0/tests/test_subset_stride.py +139 -0
- al_dvc-0.4.0/tests/test_uncertainty.py +81 -0
- al_dvc-0.4.0/tests/test_utils.py +65 -0
- al_dvc-0.4.0/tests/test_view3d.py +214 -0
- al_dvc-0.4.0/tests/test_viewer_layout.py +200 -0
- al_dvc-0.4.0/tests/test_voi_from_mask.py +62 -0
- al_dvc-0.4.0/tests/test_volume_formats.py +100 -0
- al_dvc-0.4.0/tests/test_volume_ops_kernels.py +82 -0
al_dvc-0.4.0/.gitignore
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.pyc
|
|
4
|
+
*.pyo
|
|
5
|
+
*.egg-info/
|
|
6
|
+
dist/
|
|
7
|
+
build/
|
|
8
|
+
.pytest_cache/
|
|
9
|
+
*.egg
|
|
10
|
+
.eggs/
|
|
11
|
+
.coverage
|
|
12
|
+
.hypothesis/
|
|
13
|
+
|
|
14
|
+
# Numba JIT cache
|
|
15
|
+
*.nbi
|
|
16
|
+
*.nbc
|
|
17
|
+
|
|
18
|
+
# Generated reports (benchmarks, PDFs, plots) -- regenerate with scripts/
|
|
19
|
+
reports/*.pdf
|
|
20
|
+
reports/*.csv
|
|
21
|
+
reports/*.png
|
|
22
|
+
|
|
23
|
+
# Local scratch / datasets (large binary volumes)
|
|
24
|
+
datasets/
|
|
25
|
+
data/
|
|
26
|
+
examples/data/
|
|
27
|
+
*.tif
|
|
28
|
+
*.tiff
|
|
29
|
+
*.mat
|
|
30
|
+
*.npy
|
|
31
|
+
*.npz
|
|
32
|
+
*.vti
|
|
33
|
+
*.vtu
|
|
34
|
+
!tests/fixtures/**
|
|
35
|
+
|
|
36
|
+
# IDE / editor settings
|
|
37
|
+
.vscode/
|
|
38
|
+
.idea/
|
|
39
|
+
|
|
40
|
+
# Claude Code
|
|
41
|
+
CLAUDE.md
|
|
42
|
+
.claude/
|
|
43
|
+
|
|
44
|
+
# OS files
|
|
45
|
+
Thumbs.db
|
|
46
|
+
.DS_Store
|
|
47
|
+
desktop.ini
|
|
48
|
+
dist-exe/
|
|
49
|
+
build-exe/
|
|
50
|
+
packaging/version_info.txt
|
|
51
|
+
pyaldvc_self_test.txt
|
|
52
|
+
aldvc_results/
|
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to pyALDVC are documented here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/) and the project uses
|
|
5
|
+
[Semantic Versioning](https://semver.org/).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [0.4.0] - 2026-09-05
|
|
10
|
+
|
|
11
|
+
The application matured: readable parameters in seven languages, a node-lattice
|
|
12
|
+
preview and per-axis subsets, strain and export windows, the 3-D view, branding.
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
- Starting a second GUI run with checkpoints enabled failed with
|
|
16
|
+
`CheckpointMismatch`; the GUI now uses `resume="auto"`.
|
|
17
|
+
- Unchecking "Whole volume" left a 0..0 VOI and the run failed inside the
|
|
18
|
+
worker; parameters are validated before a run starts (readable message).
|
|
19
|
+
- numba-cuda driver messages (`cuMemFree` at INFO) and occupancy warnings no
|
|
20
|
+
longer flood the log; the CUDA probe is serialised across threads and the
|
|
21
|
+
run log names the compute backend (`Compute backend: cuda (...)`).
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
- Local IC-GN kernels are about 3x faster: the tricubic sampler allocated three
|
|
25
|
+
`np.empty(4)` weight arrays per sampled voxel (a heap allocation each in
|
|
26
|
+
Numba, 137 -> 52 ns per sample with scalar weights), the ZNCC numerator is
|
|
27
|
+
accumulated in the gradient pass, and per-voxel divisions became
|
|
28
|
+
multiplications. Results unchanged to 3e-14 with identical iteration counts;
|
|
29
|
+
256^3 / subset 32 / step 8: local step 14.9 -> 4.6 s, ADMM local steps 5.6 ->
|
|
30
|
+
1.6 s; the 1024x1024x306 micro-CT example (79,200 nodes) runs in 3.6 min
|
|
31
|
+
(3.2 min with `init_coarse_factor=2`) instead of 12.5 min with unchanged
|
|
32
|
+
agreement to the MATLAB code.
|
|
33
|
+
- The NCC pyramid refines the finer levels with radius 2 instead of 4
|
|
34
|
+
(`pyramid_fine_radius`, auto-expand still covers clipped peaks): initial
|
|
35
|
+
guess 4.7 -> 2.2 s at 19,683 nodes with the same error.
|
|
36
|
+
- `ListVolumeProvider` normalises frames on demand (LRU of three) instead of
|
|
37
|
+
holding a float32 copy of every frame of a sequence.
|
|
38
|
+
|
|
39
|
+
### Added
|
|
40
|
+
- Node-lattice preview on the slices: once a region of interest is drawn and
|
|
41
|
+
"Node lattice" is checked, the slice viewer draws the lattice the run will
|
|
42
|
+
place as a dark-yellow grid (the layer nearest to each slice, dimmed when
|
|
43
|
+
the layer is off the slice, stopping at the region's edge), outlines the
|
|
44
|
+
subset of the node at the crosshair with its
|
|
45
|
+
neighbour's subset dashed to show the overlap, and outlines the subset of
|
|
46
|
+
the node under the pointer. A line above the slices gives the grid size, the
|
|
47
|
+
node count, the subset edges and the overlap, or the pipeline's message when
|
|
48
|
+
the subset does not fit. The preview updates with every parameter, region or
|
|
49
|
+
slice change and is hidden while a result is overlaid
|
|
50
|
+
(`gui/lattice_preview.py`, `reports/subset.pdf`).
|
|
51
|
+
- Non-cubic subsets in the application: the subset size is three boxes
|
|
52
|
+
(x, y, z) with a "Cube" lock, on by default, so one value still sets a cubic
|
|
53
|
+
subset; unlocked, each axis is set on its own (flat or elongated subsets,
|
|
54
|
+
e.g. for anisotropic voxels). The solver kernels already took per-axis
|
|
55
|
+
half-widths; tests now cover them (Numba against the NumPy reference, the
|
|
56
|
+
pipeline on the affine field with 13 x 17 x 25 and 25 x 17 x 13 subsets).
|
|
57
|
+
- Branding: application icon (`assets/icon`, SVG / PNG / ICO, shown in the window
|
|
58
|
+
title bar and used by the Windows bundle), a README hero banner, screenshots
|
|
59
|
+
and a workflow GIF, all rendered offscreen from synthetic data by
|
|
60
|
+
`scripts/make_branding.py`; the README opens with the banner, badges, the
|
|
61
|
+
language list, "Why pyALDVC?" and the key features. The icon comes from the
|
|
62
|
+
hand-made master `assets/icon/pyALDVC-master.png` (the designed "A + cube"
|
|
63
|
+
mark, which is the official icon; the built-in SVG is only a fallback); the demo data is an open-cell foam with a
|
|
64
|
+
localised vortex under compression, whose displacement magnitude is a torus
|
|
65
|
+
(rings on the slices, a doughnut iso-surface in the 3-D view); the 3-D view's
|
|
66
|
+
colour bar is titled with the readable field name.
|
|
67
|
+
- Readable names everywhere (`gui/names.py`): every choice shows a translated
|
|
68
|
+
label and keeps the solver's key as item data (Cubic / B-spline / Linear,
|
|
69
|
+
Pyramid search / Single-level search / Zero displacement / Previous frame,
|
|
70
|
+
Accumulative / Incremental, Local DVC / AL-DVC, Precomputed / On the fly,
|
|
71
|
+
Automatic / GPU (CUDA) / CPU, Plane fitting / Finite elements / Finite
|
|
72
|
+
differences / Solver gradient, Infinitesimal / Green-Lagrange / Euler-Almansi /
|
|
73
|
+
Hencky); result fields are named in words (Displacement magnitude, Von Mises
|
|
74
|
+
strain...) and node statuses too. The parameter panel is regrouped: Subset &
|
|
75
|
+
search, Solver (tracking mode, solver Local DVC / AL-DVC), Units, Performance
|
|
76
|
+
(compute backend, CPU threads, gradient memory) and Advanced (global step
|
|
77
|
+
discretisation, sampling stride, coarse lattice, pre-smoothing, ADMM and
|
|
78
|
+
IC-GN settings), every row with a tooltip; strain settings live in the strain
|
|
79
|
+
window only. Combo items of every panel, the 3-D view included, follow a
|
|
80
|
+
language switch. `PYALDVC_LANGUAGE` pins the language; tests keep their
|
|
81
|
+
settings in a temporary folder.
|
|
82
|
+
- Drawing a shape may leave the image: the pointer is clamped to the slice
|
|
83
|
+
edge, so a rectangle that hugs the border no longer needs the last voxel
|
|
84
|
+
to be hit exactly.
|
|
85
|
+
- The 2 x 2 arrangement (XY / XZ left, YZ top-right) is the default layout of
|
|
86
|
+
the slice viewer, the strain window and the exported slice images.
|
|
87
|
+
- Region of interest: an icon toolbar (vector icons rendered from inline SVG in
|
|
88
|
+
the theme colours) replaces the combos and the four rows of text buttons;
|
|
89
|
+
shape tools are toggles, modes are Replace / Add / Cut, the edit actions are
|
|
90
|
+
one row of icons. "Automatic mask" segments the material in one click (Otsu
|
|
91
|
+
threshold, holes filled, largest connected component) as a replayable
|
|
92
|
+
`threshold` operation that sessions restore. `MaskOp` gained the `replace`
|
|
93
|
+
mode.
|
|
94
|
+
- 3-D view: three slice sliders under the view (shared with the Slices tab);
|
|
95
|
+
the "Warped grid" mode is now "Deformed lattice": only cells whose nodes are
|
|
96
|
+
valid are warped and they are drawn with their edges, so a region of interest
|
|
97
|
+
no longer produces an empty or two-faced picture.
|
|
98
|
+
### Added
|
|
99
|
+
- Languages: Traditional Chinese, Japanese, German, French and Spanish join
|
|
100
|
+
English and Simplified Chinese (`View > Language`; the system locale picks
|
|
101
|
+
the closest shipped language, e.g. `de_AT` -> German, `zh_HK` -> Traditional
|
|
102
|
+
Chinese). `al_dvc.gui.i18n_tools` extracts the `tr()` strings from the code
|
|
103
|
+
and audits every table; `tools/i18n_extract.py` reports coverage, lists
|
|
104
|
+
missing strings or adds them to a table; a test keeps every shipped language
|
|
105
|
+
complete.
|
|
106
|
+
- Strain post-processing window (`Analysis > Strain post-processing...`,
|
|
107
|
+
Ctrl+T, or the button of the results panel): strain method, measure,
|
|
108
|
+
plane-fit window and smoothing chosen after the run, computed on a worker
|
|
109
|
+
thread with cancel, shown on a private three-plane canvas with its own frame
|
|
110
|
+
navigation, colour range and layout; the result is written back so the main
|
|
111
|
+
viewer and the exports see it. The GUI run no longer computes strain inline
|
|
112
|
+
(`compute_strain=False`), like pyALDIC.
|
|
113
|
+
- Export dialog (`Analysis > Export results...`, Ctrl+E): destination and base
|
|
114
|
+
name, formats (npz, mat, CSV, ParaView, PDF report, slice images), field and
|
|
115
|
+
frame selection, image layout / colormap / DPI, progress on a worker thread,
|
|
116
|
+
"Open folder". `al_dvc.export.slice_plots` draws the three planes for the
|
|
117
|
+
canvases and the PNG export alike.
|
|
118
|
+
- Menu shortcuts: F5 run, Esc stop, Ctrl+N / Ctrl+O / Ctrl+S / Ctrl+Shift+S
|
|
119
|
+
sessions.
|
|
120
|
+
- "Same scale" option for the three planes (slice viewer, strain window,
|
|
121
|
+
image export): one voxels-per-pixel scale for XY, XZ and YZ, each pane shrunk
|
|
122
|
+
to its slice and centred in its cell (`slice_plots.apply_equal_scale`);
|
|
123
|
+
remembered in the session.
|
|
124
|
+
- Volume formats: HDF5 (`.h5` / `.hdf5`, first 3-D dataset or `mat_key`, also
|
|
125
|
+
written by `save_volume`), NIfTI (`.nii`, `.nii.gz`; needs `nibabel`), NRRD
|
|
126
|
+
(needs `pynrrd`), DICOM folders (needs `pydicom`, stacked by InstanceNumber)
|
|
127
|
+
with a clear message naming the missing optional package; colour slices are
|
|
128
|
+
converted to luminance instead of keeping the red channel; folder resolution
|
|
129
|
+
and the file dialog know the new extensions.
|
|
130
|
+
- Left column: section titles stay pinned at the top while scrolling (stacked,
|
|
131
|
+
click to jump back); the volume table shows a thumbnail of the middle slice;
|
|
132
|
+
the batch dialog uses the same groups, primary button and console style as
|
|
133
|
+
the main window.
|
|
134
|
+
- Volumes panel: a table (frame, name, shape, region) showing which frame
|
|
135
|
+
carries the region of interest or its own mask, frame reordering (Up / Down,
|
|
136
|
+
context menu), drag and drop of files or folders, a placeholder in the empty
|
|
137
|
+
list, a hint line telling whether a region of interest crops the analysis.
|
|
138
|
+
- The mask tools moved from the canvas toolbar into a "Region of interest"
|
|
139
|
+
section of the left column (pyALDIC's sidebar layout); the window fits
|
|
140
|
+
1200 x 700.
|
|
141
|
+
- Window layout is remembered between sessions (geometry and column widths,
|
|
142
|
+
QSettings); `View` menu toggles the data and results columns (Ctrl+1 / Ctrl+2)
|
|
143
|
+
and resets the layout; minimum window size 1100 x 680. Canvas fonts follow
|
|
144
|
+
the theme. Field lists show readable names (u, v, w, |u|, exx, von Mises...)
|
|
145
|
+
with frame previous / next buttons; the run status shows the elapsed time and
|
|
146
|
+
an estimate of the time left. Tooltips on tracking mode, global step,
|
|
147
|
+
gradient storage, initial guess, interpolation and threads.
|
|
148
|
+
- `reports/postprocessing.pdf` (`scripts/make_postprocessing_report.py`):
|
|
149
|
+
strain window and export dialog screenshots, strain timings per method,
|
|
150
|
+
export timings.
|
|
151
|
+
|
|
152
|
+
### Changed
|
|
153
|
+
- Initial guess: only nodes with a usable reference subset are correlated
|
|
154
|
+
(the others are inpainted), the coarsest pyramid level searches the
|
|
155
|
+
requested radius scaled to its voxels instead of the full radius in coarse
|
|
156
|
+
voxels, the sub-voxel peak neighbourhoods are gathered without a Python
|
|
157
|
+
loop, and on CUDA the direct kernel is used for any offset count when the
|
|
158
|
+
template fits in shared memory (no FFT fallback). Micro-CT example at step 8
|
|
159
|
+
with a partial mask on the RTX 5090: initial guess 95 s -> about 40 s.
|
|
160
|
+
- Slice viewer: the three slices can be arranged as a row, a column or a
|
|
161
|
+
2 x 2 grid (XY / XZ left, YZ top-right; remembered in the session); the
|
|
162
|
+
colorbar has its own axes, so changing a slice no longer shrinks the images.
|
|
163
|
+
Displacement fields are NaN outside the valid nodes like strain (the
|
|
164
|
+
inpainted values outside the region of interest are not shown or exported
|
|
165
|
+
with `trimmed=True`). Default colormap `turbo`. The Slices / 3-D view
|
|
166
|
+
switch is a prominent segmented control; the mask toolbar's target reads
|
|
167
|
+
"Mask for: This frame / All frames" with an explanation.
|
|
168
|
+
- 3-D view: controls follow the mode (slice positions shared with the Slices
|
|
169
|
+
tab, iso level, warp scale; arrow settings only with arrows), a background
|
|
170
|
+
selector (dark / black / grey / white) with contrast-aware text, and a slim
|
|
171
|
+
centred scalar bar in a plain sans-serif font.
|
|
172
|
+
- GUI layout after pyALDIC: run controls, results, exports and the console on
|
|
173
|
+
the right, folding parameter sections with fixed-width inputs on the left
|
|
174
|
+
(`Subset & search`, `Solver`, `Strain & units`, `Performance`, `Advanced`),
|
|
175
|
+
inputs that react to the mouse wheel only when focused, the subset size shown
|
|
176
|
+
as the odd voxel span. Results stay in memory and exports ask for their
|
|
177
|
+
destination; the output folder and the VOI spin boxes are gone from the
|
|
178
|
+
panel: the analysed box follows the region of interest drawn on the slices
|
|
179
|
+
(`voi_from_mask`). Checkpoints are an advanced option, off by default.
|
|
180
|
+
- Two install flavours only: `pip install al-dvc` is the complete CPU
|
|
181
|
+
application (PySide6, pyvista and pyvistaqt are regular dependencies now),
|
|
182
|
+
`pip install al-dvc[gpu]` adds the CUDA backend. The `gui`, `gui3d`, `viz`
|
|
183
|
+
and `dev` extras are gone.
|
|
184
|
+
|
|
185
|
+
### Added
|
|
186
|
+
- CUDA backend (`al_dvc.solver.cuda_kernels`, extra `gpu` = `numba-cuda[cu12]`):
|
|
187
|
+
the Hessian precompute, the 12-DOF IC-GN and the 3-DOF ADMM kernels as
|
|
188
|
+
numba-cuda kernels, one thread block per node, float32 sampling and
|
|
189
|
+
reductions with float64 solves, masks / NaN voxels / stride / noise
|
|
190
|
+
correction / look-ahead stop identical to the CPU kernels (same statuses and
|
|
191
|
+
iteration counts, displacements within ~1e-5 voxel). `backend="auto"` (new
|
|
192
|
+
default) uses the GPU when numba-cuda and a CUDA device are present and
|
|
193
|
+
falls back to the CPU kernels otherwise; `cuda` / `numba` / `numpy` force a
|
|
194
|
+
backend; the GUI has a backend selector with the detected device. RTX 5090
|
|
195
|
+
vs 24-core CPU: 12-DOF kernel 28x, 3-DOF 39x; the micro-CT example runs in
|
|
196
|
+
23 s instead of 190 s with the same agreement to MATLAB; `reports/gpu.pdf`
|
|
197
|
+
(`scripts/make_gpu_report.py`), `tests/test_cuda_backend.py` (skipped without
|
|
198
|
+
a GPU). The portable Windows bundle stays CPU-only.
|
|
199
|
+
- `icgn_predictive_stop` (default on): the IC-GN kernels apply the current step
|
|
200
|
+
and stop when the steps contract by at least 2x and the predicted next step
|
|
201
|
+
`dp_k^2 / dp_{k-1}` is below `icgn_dp_tol`, instead of spending one more
|
|
202
|
+
sampling pass to confirm convergence: 13 % (12-DOF) and 31 % (3-DOF) fewer
|
|
203
|
+
iterations on smooth synthetic fields, 3-DOF ADMM passes on the micro-CT
|
|
204
|
+
example 4.1 / 4.0 / 3.9 -> 3.6 / 3.4 / 3.3 iterations, same solution within
|
|
205
|
+
`icgn_dp_tol`. `tests/test_predictive_stop.py`.
|
|
206
|
+
- `init_coarse_factor` (`al_dvc.solver.coarse_init`): the NCC pyramid and a
|
|
207
|
+
12-DOF IC-GN run on every k-th node per axis; displacement and gradient are
|
|
208
|
+
interpolated trilinearly to all nodes as the initial guess of the full pass
|
|
209
|
+
(pyALDIC's seed-propagation idea without the sequential wave). Also a GUI
|
|
210
|
+
advanced parameter; `tests/test_coarse_init.py`.
|
|
211
|
+
- `icgn_noise_hessian` (default on): the IC-GN kernels subtract the expected
|
|
212
|
+
reference-gradient noise inflation `c s^2 (I3 (x) M)` from the stored Hessian
|
|
213
|
+
once a node's step is below half a voxel (`s^2` from the current ZNCC, the
|
|
214
|
+
model of `uncertainty.py`), capped at half of the translation diagonal. The
|
|
215
|
+
fixed point is unchanged; noisy synthetic data converge in 2x fewer iterations
|
|
216
|
+
(SNR ~ 5: 16 -> 8 iterations per node), clean data are untouched, and the
|
|
217
|
+
ADMM local passes on the micro-CT example need 4 instead of 7 iterations per
|
|
218
|
+
node with the same agreement to MATLAB. `tests/test_noise_hessian.py`.
|
|
219
|
+
- `subset_stride`: sample every k-th subset voxel per axis (k^3 fewer samples
|
|
220
|
+
per IC-GN iteration; the Hessian, the statistics and the uncertainty model
|
|
221
|
+
use the sampled set); 4.7x faster local steps at k = 2 with subset 32.
|
|
222
|
+
Also in the GUI's advanced parameters.
|
|
223
|
+
- `scripts/make_optimization_report.py` (`reports/optimization.pdf`): before /
|
|
224
|
+
after stage timings, thread scaling, stride trade-off, initial-guess variants
|
|
225
|
+
and the rejected experiments (fastmath on the search kernel, FFT correlation
|
|
226
|
+
at the fine pyramid level, a trilinear-start IC-GN, skipping the finest
|
|
227
|
+
pyramid level).
|
|
228
|
+
|
|
229
|
+
## [0.3.1] - 2026-09-03
|
|
230
|
+
|
|
231
|
+
GUI follow-ups: a 3-D view, mask drawing on the slices, batch runs.
|
|
232
|
+
|
|
233
|
+
### Added
|
|
234
|
+
- 3-D view tab in the GUI (`al_dvc.gui.view3d_scene`, `panels/view3d.py`,
|
|
235
|
+
pyvista + pyvistaqt): field slices, node points, iso-surface,
|
|
236
|
+
warped lattice, displacement arrows, volume slices, camera presets and PNG
|
|
237
|
+
screenshots; interactive pyvistaqt widget with an off-screen fallback;
|
|
238
|
+
`scripts/make_view3d_report.py` (`reports/view3d.pdf`).
|
|
239
|
+
- Mask drawing on the slice viewer (`al_dvc.gui.mask_editor`,
|
|
240
|
+
`panels/mask_tools.py`): rectangle, ellipse, polygon and brush on any of the
|
|
241
|
+
three slices, extruded through all slices / the current slice / a range, add
|
|
242
|
+
or cut, invert / fill / clear, undo / redo, apply to the current or all
|
|
243
|
+
frames, save as a mask volume; sessions store the drawing operations;
|
|
244
|
+
`scripts/make_mask_tools_report.py` (`reports/mask_tools.pdf`).
|
|
245
|
+
- Batch runs: `al_dvc.gui.batch` (`run_session_file`, `BatchRunner`), the
|
|
246
|
+
`File > Batch run...` dialog (job table, progress, log, stop, open a finished
|
|
247
|
+
session) and the CLI `al-dvc batch a.aldvc b.aldvc --export npz summary`;
|
|
248
|
+
`scripts/make_batch_report.py` (`reports/batch.pdf`).
|
|
249
|
+
|
|
250
|
+
### Fixed
|
|
251
|
+
- Windows bundle: the VTK modules pyvista loads lazily are collected by a
|
|
252
|
+
build-time probe (a static analysis found 19 of them and the frozen 3-D view
|
|
253
|
+
reported pyvista as missing); the self-test names the import failure.
|
|
254
|
+
|
|
255
|
+
## [0.3.0] - 2026-09-03
|
|
256
|
+
|
|
257
|
+
"Usable without code": a standalone graphical application and a portable
|
|
258
|
+
Windows bundle that needs no Python installation.
|
|
259
|
+
|
|
260
|
+
### Added
|
|
261
|
+
- Graphical application `al-dvc-gui` (`al-dvc gui`, `pip install al-dvc[gui]`):
|
|
262
|
+
PySide6 window with volume/mask list, parameter form (memory estimate,
|
|
263
|
+
VOI, advanced ADMM/IC-GN settings), background pipeline worker with progress,
|
|
264
|
+
stop and log, three-plane slice viewer with displacement / uncertainty /
|
|
265
|
+
strain overlays, result summary, exports (npz, mat, csv, vti, PDF), session
|
|
266
|
+
files (`.aldvc`), English / Simplified Chinese, background kernel warm-up,
|
|
267
|
+
self-test; offscreen tests and `scripts/make_gui_report.py` (`reports/gui.pdf`).
|
|
268
|
+
- Portable Windows bundle: `packaging/pyaldvc.spec` + `tools/build_exe.py`
|
|
269
|
+
(PyInstaller onedir, `pyALDVC.exe` and `pyALDVC-console.exe --self-test`),
|
|
270
|
+
`tests/test_frozen_bundle.py` driving the built executable, and
|
|
271
|
+
`.github/workflows/build-exe.yml` attaching `pyALDVC-<version>-win64.zip`
|
|
272
|
+
to every `v*` release.
|
|
273
|
+
|
|
274
|
+
## [0.2.0] - 2026-09-03
|
|
275
|
+
|
|
276
|
+
"Real-scan ready": validated against the MATLAB code on a micro-CT scan,
|
|
277
|
+
with masks on the deformed frame, per-node uncertainty, checkpoints and a
|
|
278
|
+
large-volume mode.
|
|
279
|
+
|
|
280
|
+
### Added
|
|
281
|
+
- Large-volume mode `gradient_mode="on_the_fly"`: the kernels evaluate the
|
|
282
|
+
7-point stencil on the reference at the subset voxels instead of reading three
|
|
283
|
+
stored gradient volumes; resident memory drops from 21 to 9 bytes per voxel
|
|
284
|
+
(a 1500^3 scan fits in 32 GB) for about 15-20 % more local-step time. The
|
|
285
|
+
pipeline logs the memory model (`memory_model`) at start;
|
|
286
|
+
`scripts/make_large_volume_report.py` measures both modes.
|
|
287
|
+
- Per-frame checkpoints: `run_aldvc(..., checkpoint_dir=DIR)` writes one
|
|
288
|
+
`frame_<k>.npz` per finished frame pair (plus `meta.json`) and reuses them on
|
|
289
|
+
a later call; a directory written with other parameters, volumes, schedule
|
|
290
|
+
or grid is rejected (`CheckpointMismatch`) unless `resume=False`. CLI:
|
|
291
|
+
`al-dvc run --checkpoint DIR [--restart]`. `scripts/make_checkpoint_report.py`.
|
|
292
|
+
- Deformed-frame masks: a frame's mask now also applies when the frame is the
|
|
293
|
+
deformed one. Masked voxels are NaN in the sampled volume, subset voxels whose
|
|
294
|
+
interpolation stencil touches them drop out of the node's correlation (the
|
|
295
|
+
subset statistics are recomputed on the remaining voxels), nodes that keep less
|
|
296
|
+
than half their voxels are reported `invalid_subset`, and the NCC search treats
|
|
297
|
+
masked voxels as featureless. `scripts/make_mask_report.py` shows the effect
|
|
298
|
+
(`reports/deformed_mask.pdf`).
|
|
299
|
+
- `FrameResult.U_std`: per-node standard deviation of u, v, w from the IC-GN
|
|
300
|
+
normal equations (noise-corrected Hessian, see `al_dvc.solver.uncertainty`),
|
|
301
|
+
exported as `disp_std_u/v/w`, `disp_std` (npz `U_std`, vti `displacement_std`,
|
|
302
|
+
mat `ResultDispStd`) and shown in the PDF report; `scripts/make_uncertainty_report.py`
|
|
303
|
+
calibrates it against synthetic noise (`reports/uncertainty.pdf`).
|
|
304
|
+
- `al_dvc.io.matlab_results`: reader for the MATLAB ALDVC `results_ws*_st*.mat`
|
|
305
|
+
files (0-based coordinates, `(N, 3)` / `(N, 3, 3)` layouts) and node matching.
|
|
306
|
+
- `scripts/compare_matlab.py`: node-wise cross-validation against the MATLAB
|
|
307
|
+
results shipped with the reference code, with a solver-equivalence check
|
|
308
|
+
(both codes' local solutions refined by the same kernel) and a ZNCC
|
|
309
|
+
objective comparison; writes `reports/matlab_crossval_<tag>.pdf`.
|
|
310
|
+
On the micro-CT example both codes' local solutions coincide to 0.001
|
|
311
|
+
voxel once refined by the same kernel and the final fields agree to
|
|
312
|
+
0.005 / 0.006 / 0.02 voxel (median, u / v / w); on the diverged `eyes`
|
|
313
|
+
example pyALDVC reports the failure through status codes instead of
|
|
314
|
+
returning an 86-voxel field.
|
|
315
|
+
- `icgn_dp_tol`: separate IC-GN parameter-increment tolerance (default 1e-3
|
|
316
|
+
voxel); `icgn_tol` keeps the MATLAB relative gradient-norm meaning.
|
|
317
|
+
- `icgn_patience` and status code `stalled` (7): IC-GN gives up on a node after
|
|
318
|
+
five iterations without objective improvement instead of running to the
|
|
319
|
+
100-iteration cap; textureless regions no longer dominate the run time.
|
|
320
|
+
- The IC-GN kernels walk the active nodes in a block-cyclic order, so spatial
|
|
321
|
+
clusters of skipped or hard nodes (masks, inpainted nodes, textureless
|
|
322
|
+
layers, node subsets) no longer leave most threads idle (79k-node scan:
|
|
323
|
+
local step 115 -> ~350 nodes/s together with the stall rule).
|
|
324
|
+
- Numba kernels for volume normalisation and the 7-point gradient;
|
|
325
|
+
`compute_gradients_np` and `voi_mean_std` expose the NumPy reference and
|
|
326
|
+
the VOI statistics.
|
|
327
|
+
|
|
328
|
+
### Changed
|
|
329
|
+
- The automatic `beta` selection uses the MATLAB L-curve score
|
|
330
|
+
`|u-u_hat| + h^2 |F-grad u_hat|` by default (`beta_criterion="matlab"`); the
|
|
331
|
+
previous z-normalised score remains available as `"normalized"`.
|
|
332
|
+
- IC-GN stops on the increment criterion at 1e-3 voxel instead of 1e-2. On
|
|
333
|
+
real CT data with weak z-texture the looser value left a 0.03-0.05 voxel
|
|
334
|
+
unconverged residual in `w`; the cost is about twice the local iterations.
|
|
335
|
+
- Pre-processing of a 1024x1024x306 scan (321 M voxels) drops from about
|
|
336
|
+
30 s to 1.1 s (parallel Numba normalisation 0.2 s and gradients 0.9 s; the
|
|
337
|
+
SciPy gradient alone took 9 s).
|
|
338
|
+
|
|
339
|
+
## [0.1.0] - 2026-09-02
|
|
340
|
+
|
|
341
|
+
Initial release: a complete Python port of the MATLAB ALDVC pipeline with
|
|
342
|
+
the pyALDIC architecture.
|
|
343
|
+
|
|
344
|
+
### Added
|
|
345
|
+
- `DVCPara` parameter set with validation, scalar-to-(x,y,z) broadcasting,
|
|
346
|
+
JSON/YAML round trip; no interactive prompts anywhere.
|
|
347
|
+
- Volume I/O: TIFF stacks, slice folders, MATLAB `.mat` (v5/v7.3 with axis
|
|
348
|
+
permutation), NumPy; streaming `FileVolumeProvider` with a bounded cache.
|
|
349
|
+
- Uniform hex8 node grid with VOI/mask trimming and subset-coverage tests.
|
|
350
|
+
- Numba kernels: tricubic (Keys), cubic B-spline and trilinear sampling;
|
|
351
|
+
12-DOF and 3-DOF IC-GN with in-place subset reads, per-node Cholesky
|
|
352
|
+
factors and status codes; NumPy reference implementations for testing.
|
|
353
|
+
- Initial guess: Hann-windowed phase-correlation global shift, texture-aware
|
|
354
|
+
coarse-to-fine NCC pyramid with a Numba spatial-domain ZNCC kernel (FFT
|
|
355
|
+
engine for large search windows), node-wise search-radius expansion,
|
|
356
|
+
sub-voxel quadratic peaks, PCE quality factor, universal median test and
|
|
357
|
+
harmonic (spring) inpainting.
|
|
358
|
+
- Global step: FEM (hex8, 2x2x2 Gauss) and finite-difference operator sets
|
|
359
|
+
assembled once per mesh; Jacobi-PCG multi-RHS solver (direct LU for small
|
|
360
|
+
meshes); lumped-mass nodal gradient; MATLAB-compatible L-curve `beta`
|
|
361
|
+
auto-tuning; scaled ADMM with `accumulate` or `reset` dual updates.
|
|
362
|
+
- Strain: masked 3D Savitzky-Golay plane fit, finite differences, FEM nodal
|
|
363
|
+
gradient, direct ADMM gradient; four strain measures and derived
|
|
364
|
+
quantities in physical units; edge-trim validity flags.
|
|
365
|
+
- Multi-frame tracking with `FrameSchedule` and cubic cumulative composition.
|
|
366
|
+
- Exports: `.npz`, `.mat` (Python and MATLAB layouts), CSV, VTK `.vti` +
|
|
367
|
+
`.pvd`, PDF report, parameter/summary JSON/YAML.
|
|
368
|
+
- CLI `al-dvc run|synth|info|plot`; synthetic data generator with exact
|
|
369
|
+
Lagrangian warps; validation and benchmark scripts producing PDF reports.
|
|
370
|
+
- 110 pytest tests (kernel-vs-reference, operators, search, strain, full
|
|
371
|
+
pipeline against analytic ground truth, exports, CLI).
|
al_dvc-0.4.0/LICENSE
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026, Zixiang (Zach) Tong, Jin Yang
|
|
4
|
+
All rights reserved.
|
|
5
|
+
|
|
6
|
+
Redistribution and use in source and binary forms, with or without
|
|
7
|
+
modification, are permitted provided that the following conditions are met:
|
|
8
|
+
|
|
9
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
10
|
+
list of conditions and the following disclaimer.
|
|
11
|
+
|
|
12
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
13
|
+
this list of conditions and the following disclaimer in the documentation
|
|
14
|
+
and/or other materials provided with the distribution.
|
|
15
|
+
|
|
16
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
17
|
+
contributors may be used to endorse or promote products derived from
|
|
18
|
+
this software without specific prior written permission.
|
|
19
|
+
|
|
20
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
21
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
22
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
23
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
24
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
25
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
26
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
27
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
28
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
29
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|