diff --git a/pyproject.toml b/pyproject.toml index b468c7e6..ace4846a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ build-backend = "scikit_build_core.build" [project] name = "pyvale" -version = "2026.10.0" +version = "2026.10.1" description = "Your virtual engineering lab: An all-in-one package for simulation-driven experimental design." authors = [ { name = "scepticalrabbit et al.", email = "thescepticalrabbit@gmail.com" }, diff --git a/src/pyvale/__init__.py b/src/pyvale/__init__.py index a56656d5..cd7bbe12 100644 --- a/src/pyvale/__init__.py +++ b/src/pyvale/__init__.py @@ -14,7 +14,7 @@ virtually testing digital shadows/twins. """ -__version__ = "2026.9.0" +__version__ = "2026.10.1" from . import data, dataio, dic, mooseherder, render, sensorsim, strain diff --git a/src/pyvale/dic/cpp/bindings.cpp b/src/pyvale/dic/cpp/bindings.cpp index 578cd95a..be6db4fe 100644 --- a/src/pyvale/dic/cpp/bindings.cpp +++ b/src/pyvale/dic/cpp/bindings.cpp @@ -15,6 +15,7 @@ #include #include #include +#include // commoncpp Header Files #include "../../commoncpp/util.hpp" @@ -174,6 +175,22 @@ PYBIND11_MODULE(diccpp, m) { engine_images(images_from_sequence(image_arrays, conf), img_roi_arr, calib, conf, multiwindowconf, saveconf); }, "Run DIC analysis on in-memory image arrays with config"); + m.def("_should_update_ref", [](int image_number, + const std::vector& iterations, + const std::vector& costs, + util::EIncrementalCond condition, + double threshold) { + const int num_results = static_cast(std::max(iterations.size(), costs.size())); + ResultArrays results(num_results, 0, false); + results.niter = iterations; + results.cost = costs; + + util::Config config{}; + config.incremental_update_cond = condition; + config.incremental_update_val = threshold; + return should_update_ref(image_number, results, config); + }, "Evaluate an incremental-reference update condition"); + // interpolator bindings py::class_(m, "InterpVals") .def_readonly("f", &InterpVals::f) diff --git a/src/pyvale/dic/cpp/dicmain.cpp b/src/pyvale/dic/cpp/dicmain.cpp index 044e8d3e..2d3d2fd9 100644 --- a/src/pyvale/dic/cpp/dicmain.cpp +++ b/src/pyvale/dic/cpp/dicmain.cpp @@ -573,7 +573,7 @@ bool should_update_ref(const int img_num_def_l, const ResultArrays& results, con if (results.cost.empty()) return false; double avg = std::accumulate(results.cost.begin(), results.cost.end(), 0.0) / results.cost.size(); - return avg > conf.incremental_update_val; + return avg < conf.incremental_update_val; } default: diff --git a/tests/dic/test_incremental.py b/tests/dic/test_incremental.py new file mode 100644 index 00000000..f66efeff --- /dev/null +++ b/tests/dic/test_incremental.py @@ -0,0 +1,40 @@ +"""Tests for incremental DIC reference-update conditions.""" + +import pytest + +import pyvale.dic.diccpp as diccpp + + +@pytest.mark.parametrize( + ("condition", "image_number", "iterations", "costs", "threshold", "expected"), + [ + # The first deformed image never updates the reference. + ("IMAGE", 1, [4], [0.5], 1, False), + ("IMAGE", 2, [4], [0.5], 1, True), + ("IMAGE", 3, [4], [0.5], 2, True), + # COST updates only when the mean ZNCC cost is strictly below the threshold. + ("COST", 2, [4, 4], [0.4, 0.6], 0.6, True), + ("COST", 2, [4, 4], [0.4, 0.6], 0.5, False), + ("COST", 2, [4, 4], [0.4, 0.6], 0.4, False), + # ITER updates only when the mean iteration count exceeds the threshold. + ("ITER", 2, [4, 6], [0.5, 0.5], 4.9, True), + ("ITER", 2, [4, 6], [0.5, 0.5], 5, False), + ], +) +def test_incremental_update_conditions( + condition: str, + image_number: int, + iterations: list[int], + costs: list[float], + threshold: float, + expected: bool, +) -> None: + result = diccpp._should_update_ref( + image_number, + iterations, + costs, + getattr(diccpp.IncrementalCond, condition), + threshold, + ) + + assert result is expected