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.
Files changed (147) hide show
  1. al_dvc-0.4.0/.gitignore +52 -0
  2. al_dvc-0.4.0/CHANGELOG.md +371 -0
  3. al_dvc-0.4.0/LICENSE +29 -0
  4. al_dvc-0.4.0/PKG-INFO +457 -0
  5. al_dvc-0.4.0/README.md +418 -0
  6. al_dvc-0.4.0/docs/design.md +644 -0
  7. al_dvc-0.4.0/pyproject.toml +113 -0
  8. al_dvc-0.4.0/src/al_dvc/__init__.py +46 -0
  9. al_dvc-0.4.0/src/al_dvc/__main__.py +6 -0
  10. al_dvc-0.4.0/src/al_dvc/_numba_compat.py +92 -0
  11. al_dvc-0.4.0/src/al_dvc/cli.py +341 -0
  12. al_dvc-0.4.0/src/al_dvc/core/__init__.py +49 -0
  13. al_dvc-0.4.0/src/al_dvc/core/checkpoint.py +209 -0
  14. al_dvc-0.4.0/src/al_dvc/core/config.py +317 -0
  15. al_dvc-0.4.0/src/al_dvc/core/data_structures.py +494 -0
  16. al_dvc-0.4.0/src/al_dvc/core/pipeline.py +454 -0
  17. al_dvc-0.4.0/src/al_dvc/export/__init__.py +32 -0
  18. al_dvc-0.4.0/src/al_dvc/export/export_csv.py +47 -0
  19. al_dvc-0.4.0/src/al_dvc/export/export_mat.py +93 -0
  20. al_dvc-0.4.0/src/al_dvc/export/export_npz.py +73 -0
  21. al_dvc-0.4.0/src/al_dvc/export/export_params.py +31 -0
  22. al_dvc-0.4.0/src/al_dvc/export/export_report.py +204 -0
  23. al_dvc-0.4.0/src/al_dvc/export/export_utils.py +116 -0
  24. al_dvc-0.4.0/src/al_dvc/export/export_vtk.py +94 -0
  25. al_dvc-0.4.0/src/al_dvc/export/slice_plots.py +321 -0
  26. al_dvc-0.4.0/src/al_dvc/gui/__init__.py +1 -0
  27. al_dvc-0.4.0/src/al_dvc/gui/app.py +666 -0
  28. al_dvc-0.4.0/src/al_dvc/gui/app_state.py +388 -0
  29. al_dvc-0.4.0/src/al_dvc/gui/arrows/spin_down.svg +3 -0
  30. al_dvc-0.4.0/src/al_dvc/gui/arrows/spin_down_hover.svg +3 -0
  31. al_dvc-0.4.0/src/al_dvc/gui/arrows/spin_up.svg +3 -0
  32. al_dvc-0.4.0/src/al_dvc/gui/arrows/spin_up_hover.svg +3 -0
  33. al_dvc-0.4.0/src/al_dvc/gui/assets/pyALDVC.png +0 -0
  34. al_dvc-0.4.0/src/al_dvc/gui/batch.py +231 -0
  35. al_dvc-0.4.0/src/al_dvc/gui/dialogs/__init__.py +1 -0
  36. al_dvc-0.4.0/src/al_dvc/gui/dialogs/batch_dialog.py +352 -0
  37. al_dvc-0.4.0/src/al_dvc/gui/dialogs/export_dialog.py +488 -0
  38. al_dvc-0.4.0/src/al_dvc/gui/field_canvas.py +216 -0
  39. al_dvc-0.4.0/src/al_dvc/gui/i18n.py +133 -0
  40. al_dvc-0.4.0/src/al_dvc/gui/i18n_tools.py +116 -0
  41. al_dvc-0.4.0/src/al_dvc/gui/icons.py +89 -0
  42. al_dvc-0.4.0/src/al_dvc/gui/kernel_warmup.py +46 -0
  43. al_dvc-0.4.0/src/al_dvc/gui/lattice_preview.py +181 -0
  44. al_dvc-0.4.0/src/al_dvc/gui/mask_editor.py +349 -0
  45. al_dvc-0.4.0/src/al_dvc/gui/names.py +162 -0
  46. al_dvc-0.4.0/src/al_dvc/gui/panels/__init__.py +1 -0
  47. al_dvc-0.4.0/src/al_dvc/gui/panels/mask_tools.py +326 -0
  48. al_dvc-0.4.0/src/al_dvc/gui/panels/param_panel.py +432 -0
  49. al_dvc-0.4.0/src/al_dvc/gui/panels/results_panel.py +313 -0
  50. al_dvc-0.4.0/src/al_dvc/gui/panels/run_panel.py +184 -0
  51. al_dvc-0.4.0/src/al_dvc/gui/panels/view3d.py +534 -0
  52. al_dvc-0.4.0/src/al_dvc/gui/panels/viewer.py +655 -0
  53. al_dvc-0.4.0/src/al_dvc/gui/panels/volume_panel.py +357 -0
  54. al_dvc-0.4.0/src/al_dvc/gui/pipeline_worker.py +73 -0
  55. al_dvc-0.4.0/src/al_dvc/gui/self_test.py +163 -0
  56. al_dvc-0.4.0/src/al_dvc/gui/session.py +199 -0
  57. al_dvc-0.4.0/src/al_dvc/gui/sticky_headers.py +102 -0
  58. al_dvc-0.4.0/src/al_dvc/gui/strain_window.py +541 -0
  59. al_dvc-0.4.0/src/al_dvc/gui/theme.py +726 -0
  60. al_dvc-0.4.0/src/al_dvc/gui/translations/de.json +394 -0
  61. al_dvc-0.4.0/src/al_dvc/gui/translations/es.json +394 -0
  62. al_dvc-0.4.0/src/al_dvc/gui/translations/fr.json +394 -0
  63. al_dvc-0.4.0/src/al_dvc/gui/translations/ja.json +394 -0
  64. al_dvc-0.4.0/src/al_dvc/gui/translations/zh_CN.json +394 -0
  65. al_dvc-0.4.0/src/al_dvc/gui/translations/zh_TW.json +394 -0
  66. al_dvc-0.4.0/src/al_dvc/gui/view3d_scene.py +351 -0
  67. al_dvc-0.4.0/src/al_dvc/gui/widgets.py +272 -0
  68. al_dvc-0.4.0/src/al_dvc/gui/window_chrome.py +50 -0
  69. al_dvc-0.4.0/src/al_dvc/gui/window_geometry.py +75 -0
  70. al_dvc-0.4.0/src/al_dvc/io/__init__.py +39 -0
  71. al_dvc-0.4.0/src/al_dvc/io/matlab_results.py +213 -0
  72. al_dvc-0.4.0/src/al_dvc/io/volume_io.py +473 -0
  73. al_dvc-0.4.0/src/al_dvc/io/volume_ops.py +375 -0
  74. al_dvc-0.4.0/src/al_dvc/mesh/__init__.py +26 -0
  75. al_dvc-0.4.0/src/al_dvc/mesh/grid_mesh.py +242 -0
  76. al_dvc-0.4.0/src/al_dvc/mesh/hex8.py +105 -0
  77. al_dvc-0.4.0/src/al_dvc/solver/__init__.py +44 -0
  78. al_dvc-0.4.0/src/al_dvc/solver/beta_tuning.py +90 -0
  79. al_dvc-0.4.0/src/al_dvc/solver/coarse_init.py +128 -0
  80. al_dvc-0.4.0/src/al_dvc/solver/cuda_kernels.py +1643 -0
  81. al_dvc-0.4.0/src/al_dvc/solver/global_operators.py +158 -0
  82. al_dvc-0.4.0/src/al_dvc/solver/init_disp.py +149 -0
  83. al_dvc-0.4.0/src/al_dvc/solver/integer_search.py +688 -0
  84. al_dvc-0.4.0/src/al_dvc/solver/interp_kernels.py +152 -0
  85. al_dvc-0.4.0/src/al_dvc/solver/local_icgn.py +372 -0
  86. al_dvc-0.4.0/src/al_dvc/solver/numba_kernels.py +1207 -0
  87. al_dvc-0.4.0/src/al_dvc/solver/reference_kernels.py +617 -0
  88. al_dvc-0.4.0/src/al_dvc/solver/subpb1_solver.py +183 -0
  89. al_dvc-0.4.0/src/al_dvc/solver/subpb2_solver.py +155 -0
  90. al_dvc-0.4.0/src/al_dvc/solver/uncertainty.py +133 -0
  91. al_dvc-0.4.0/src/al_dvc/solver/warmup.py +47 -0
  92. al_dvc-0.4.0/src/al_dvc/strain/__init__.py +30 -0
  93. al_dvc-0.4.0/src/al_dvc/strain/compute_strain.py +130 -0
  94. al_dvc-0.4.0/src/al_dvc/strain/gradient_methods.py +140 -0
  95. al_dvc-0.4.0/src/al_dvc/strain/strain_types.py +123 -0
  96. al_dvc-0.4.0/src/al_dvc/synthetic.py +145 -0
  97. al_dvc-0.4.0/src/al_dvc/utils/__init__.py +28 -0
  98. al_dvc-0.4.0/src/al_dvc/utils/grid_interp.py +79 -0
  99. al_dvc-0.4.0/src/al_dvc/utils/inpaint.py +123 -0
  100. al_dvc-0.4.0/src/al_dvc/utils/outlier_detection.py +95 -0
  101. al_dvc-0.4.0/src/al_dvc/utils/validation.py +55 -0
  102. al_dvc-0.4.0/src/al_dvc/viz/__init__.py +10 -0
  103. al_dvc-0.4.0/src/al_dvc/viz/slices.py +108 -0
  104. al_dvc-0.4.0/tests/__init__.py +0 -0
  105. al_dvc-0.4.0/tests/conftest.py +81 -0
  106. al_dvc-0.4.0/tests/test_anisotropic_subset.py +145 -0
  107. al_dvc-0.4.0/tests/test_batch.py +159 -0
  108. al_dvc-0.4.0/tests/test_beta_tuning.py +33 -0
  109. al_dvc-0.4.0/tests/test_checkpoint.py +103 -0
  110. al_dvc-0.4.0/tests/test_coarse_init.py +89 -0
  111. al_dvc-0.4.0/tests/test_config.py +115 -0
  112. al_dvc-0.4.0/tests/test_cuda_backend.py +313 -0
  113. al_dvc-0.4.0/tests/test_deformed_mask.py +219 -0
  114. al_dvc-0.4.0/tests/test_examples.py +39 -0
  115. al_dvc-0.4.0/tests/test_export_cli.py +153 -0
  116. al_dvc-0.4.0/tests/test_export_dialog.py +97 -0
  117. al_dvc-0.4.0/tests/test_frozen_bundle.py +69 -0
  118. al_dvc-0.4.0/tests/test_global_step.py +106 -0
  119. al_dvc-0.4.0/tests/test_gradient_mode.py +145 -0
  120. al_dvc-0.4.0/tests/test_gui.py +326 -0
  121. al_dvc-0.4.0/tests/test_i18n.py +63 -0
  122. al_dvc-0.4.0/tests/test_icgn_stall.py +184 -0
  123. al_dvc-0.4.0/tests/test_icgn_tolerance.py +95 -0
  124. al_dvc-0.4.0/tests/test_integer_search.py +90 -0
  125. al_dvc-0.4.0/tests/test_io.py +126 -0
  126. al_dvc-0.4.0/tests/test_kernels.py +292 -0
  127. al_dvc-0.4.0/tests/test_lattice_preview.py +190 -0
  128. al_dvc-0.4.0/tests/test_mask_editor.py +157 -0
  129. al_dvc-0.4.0/tests/test_mask_tools.py +236 -0
  130. al_dvc-0.4.0/tests/test_matlab_results.py +110 -0
  131. al_dvc-0.4.0/tests/test_mesh.py +97 -0
  132. al_dvc-0.4.0/tests/test_ncc_expansion.py +77 -0
  133. al_dvc-0.4.0/tests/test_noise_hessian.py +145 -0
  134. al_dvc-0.4.0/tests/test_pipeline.py +220 -0
  135. al_dvc-0.4.0/tests/test_predictive_stop.py +128 -0
  136. al_dvc-0.4.0/tests/test_provider_cache.py +43 -0
  137. al_dvc-0.4.0/tests/test_roi_tools.py +140 -0
  138. al_dvc-0.4.0/tests/test_strain.py +99 -0
  139. al_dvc-0.4.0/tests/test_strain_window.py +111 -0
  140. al_dvc-0.4.0/tests/test_subset_stride.py +139 -0
  141. al_dvc-0.4.0/tests/test_uncertainty.py +81 -0
  142. al_dvc-0.4.0/tests/test_utils.py +65 -0
  143. al_dvc-0.4.0/tests/test_view3d.py +214 -0
  144. al_dvc-0.4.0/tests/test_viewer_layout.py +200 -0
  145. al_dvc-0.4.0/tests/test_voi_from_mask.py +62 -0
  146. al_dvc-0.4.0/tests/test_volume_formats.py +100 -0
  147. al_dvc-0.4.0/tests/test_volume_ops_kernels.py +82 -0
@@ -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.