-
Notifications
You must be signed in to change notification settings - Fork 0
Implement validation tests for cfd-python bindings #272
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
cc86cbe
aae6c4a
9a515f3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -45,3 +45,4 @@ report/ | |
| *.dll | ||
| *.so | ||
| *.dylib | ||
| venv/ | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,73 @@ | ||
| use cfd_core::physics::cavitation::{CavitationRegimeClassifier, RayleighPlesset}; | ||
| use pyo3::prelude::*; | ||
|
|
||
| /// Rayleigh-Plesset bubble model | ||
| #[pyclass(name = "RayleighPlesset")] | ||
| #[derive(Clone)] | ||
| pub struct PyRayleighPlesset { | ||
| pub(crate) inner: RayleighPlesset<f64>, | ||
| } | ||
|
|
||
| #[pymethods] | ||
| impl PyRayleighPlesset { | ||
| /// Create a new Rayleigh-Plesset model | ||
| #[new] | ||
| #[pyo3(signature = (initial_radius, liquid_density, liquid_viscosity, surface_tension, vapor_pressure, polytropic_index))] | ||
| fn new( | ||
| initial_radius: f64, | ||
| liquid_density: f64, | ||
| liquid_viscosity: f64, | ||
| surface_tension: f64, | ||
| vapor_pressure: f64, | ||
| polytropic_index: f64, | ||
| ) -> Self { | ||
| PyRayleighPlesset { | ||
| inner: RayleighPlesset { | ||
| initial_radius, | ||
| liquid_density, | ||
| liquid_viscosity, | ||
| surface_tension, | ||
| vapor_pressure, | ||
| polytropic_index, | ||
| }, | ||
| } | ||
| } | ||
|
|
||
| /// Calculate critical Blake radius for unstable growth | ||
| fn blake_critical_radius(&self, ambient_pressure: f64) -> f64 { | ||
| self.inner.blake_critical_radius(ambient_pressure) | ||
| } | ||
| } | ||
|
|
||
| /// Cavitation regime classifier | ||
| #[pyclass(name = "CavitationRegimeClassifier")] | ||
| pub struct PyCavitationRegimeClassifier { | ||
| inner: CavitationRegimeClassifier<f64>, | ||
| } | ||
|
|
||
| #[pymethods] | ||
| impl PyCavitationRegimeClassifier { | ||
| /// Create new cavitation regime classifier | ||
| #[new] | ||
| #[pyo3(signature = (bubble_model, ambient_pressure, acoustic_pressure=None, acoustic_frequency=None))] | ||
| fn new( | ||
| bubble_model: PyRayleighPlesset, | ||
| ambient_pressure: f64, | ||
| acoustic_pressure: Option<f64>, | ||
| acoustic_frequency: Option<f64>, | ||
| ) -> Self { | ||
| PyCavitationRegimeClassifier { | ||
| inner: CavitationRegimeClassifier::new( | ||
| bubble_model.inner, | ||
| ambient_pressure, | ||
| acoustic_pressure, | ||
| acoustic_frequency, | ||
| ), | ||
| } | ||
| } | ||
|
|
||
| /// Calculate Blake threshold pressure | ||
| fn blake_threshold(&self) -> f64 { | ||
| self.inner.blake_threshold() | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| use cfd_core::physics::hemolysis::HemolysisModel; | ||
| use pyo3::prelude::*; | ||
|
|
||
| /// Hemolysis model types | ||
| #[pyclass(name = "HemolysisModel")] | ||
| #[derive(Clone)] | ||
| pub struct PyHemolysisModel { | ||
| pub(crate) inner: HemolysisModel, | ||
| } | ||
|
|
||
| #[pymethods] | ||
| impl PyHemolysisModel { | ||
| /// Create Giersiepen model with standard constants | ||
| #[staticmethod] | ||
| fn giersiepen_standard() -> Self { | ||
| PyHemolysisModel { | ||
| inner: HemolysisModel::giersiepen_standard(), | ||
| } | ||
| } | ||
|
|
||
| /// Calculate blood damage index from shear stress and exposure time | ||
| fn calculate_damage(&self, shear_stress: f64, exposure_time: f64) -> PyResult<f64> { | ||
| self.inner | ||
| .damage_index(shear_stress, exposure_time) | ||
| .map_err(|e| pyo3::exceptions::PyValueError::new_err(e.to_string())) | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -54,12 +54,34 @@ | |
| print(f" P_Blake = {P_Blake_python:.2f} Pa = {P_Blake_python/1000:.2f} kPa") | ||
|
|
||
| if has_cfd_python: | ||
| # TODO: Check if cfd_python exposes Blake threshold calculation | ||
| # For now, document that Rust implementation is in regimes.rs | ||
| print(f"\nRust implementation:") | ||
| print(f" Located in: crates/cfd-core/src/physics/cavitation/regimes.rs") | ||
| print(f" Method: blake_threshold() and blake_critical_radius()") | ||
| print(f" Formula matches Python implementation ✓") | ||
|
|
||
| # Instantiate Rust models | ||
| rp = cfd_python.RayleighPlesset( | ||
| initial_radius=R_0, | ||
| liquid_density=WATER_DENSITY, | ||
| liquid_viscosity=WATER_VISCOSITY, | ||
| surface_tension=sigma, | ||
| vapor_pressure=P_v, | ||
| polytropic_index=1.4 | ||
| ) | ||
|
|
||
| classifier = cfd_python.CavitationRegimeClassifier( | ||
| bubble_model=rp, | ||
| ambient_pressure=P_inf | ||
| ) | ||
|
|
||
| R_c_rust = rp.blake_critical_radius(P_inf) | ||
| P_Blake_rust = classifier.blake_threshold() | ||
|
|
||
| print(f" R_c = {R_c_rust*1e6:.4f} μm") | ||
| print(f" P_Blake = {P_Blake_rust:.2f} Pa = {P_Blake_rust/1000:.2f} kPa") | ||
|
|
||
| # Assert correctness | ||
| assert math.isclose(R_c_rust, R_c_python, rel_tol=1e-4), "R_c mismatch between Python and Rust" | ||
| assert math.isclose(P_Blake_rust, P_Blake_python, rel_tol=1e-4), "P_Blake mismatch between Python and Rust" | ||
| print(f" ✓ Rust values match Python calculations within tolerance") | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove extraneous Static analysis (Ruff F541) flags these f-strings that have no placeholders: Proposed fix- print(f" ✓ Rust values match Python calculations within tolerance")
+ print(" ✓ Rust values match Python calculations within tolerance")- print(f" ✓ Rust apparent_viscosity matches Python calculations for all tested shear rates")
+ print(" ✓ Rust apparent_viscosity matches Python calculations for all tested shear rates")- print(f" Located in: crates/cfd-core/src/physics/hemolysis/models.rs")
+ print(" Located in: crates/cfd-core/src/physics/hemolysis/models.rs")- print(f" ✓ Rust calculate_damage matches Python calculations for all tested cases")
+ print(" ✓ Rust calculate_damage matches Python calculations for all tested cases")Also applies to: 134-134, 173-173, 182-182 🧰 Tools🪛 Ruff (0.15.6)[error] 84-84: f-string without any placeholders Remove extraneous (F541) 🤖 Prompt for AI Agents |
||
| else: | ||
| print(f"\nRust verification skipped (cfd_python not available)") | ||
|
|
||
|
|
@@ -101,12 +123,15 @@ def carreau_yasuda_python(shear_rate): | |
| if has_cfd_python: | ||
| print(f"\nRust implementation:") | ||
| print(f" Located in: crates/cfd-core/src/physics/fluid/blood.rs") | ||
| print(f" Type: CarreauYasudaBlood") | ||
| print(f" Method: apparent_viscosity(shear_rate)") | ||
|
|
||
| # Try to test if we can create a blood model | ||
| # Note: This depends on cfd_python API structure | ||
| print(f"\n TODO: Add cfd_python API test if blood model is exposed") | ||
| blood_model = cfd_python.CarreauYasudaBlood() | ||
|
|
||
| for gamma_dot in test_shear_rates: | ||
| mu_python = carreau_yasuda_python(gamma_dot) | ||
| mu_rust = blood_model.apparent_viscosity(gamma_dot) | ||
| assert math.isclose(mu_python, mu_rust, rel_tol=1e-4), f"Mismatch for shear rate {gamma_dot}" | ||
|
|
||
| print(f" ✓ Rust apparent_viscosity matches Python calculations for all tested shear rates") | ||
| else: | ||
| print(f"\nRust verification skipped (cfd_python not available)") | ||
|
|
||
|
|
@@ -145,9 +170,16 @@ def giersiepen_python(shear_stress, exposure_time): | |
|
|
||
| if has_cfd_python: | ||
| print(f"\nRust implementation:") | ||
| print(f" Located in: crates/cfd-core/src/physics/hemolysis/giersiepen.rs") | ||
| print(f" Method: calculate_damage(shear_stress, exposure_time)") | ||
| print(f"\n TODO: Add cfd_python API test if hemolysis model is exposed") | ||
| print(f" Located in: crates/cfd-core/src/physics/hemolysis/models.rs") | ||
|
|
||
| hemolysis_model = cfd_python.HemolysisModel.giersiepen_standard() | ||
|
|
||
| for tau, t in test_cases: | ||
| damage_python = giersiepen_python(tau, t) | ||
| damage_rust = hemolysis_model.calculate_damage(tau, t) | ||
| assert math.isclose(damage_python, damage_rust, rel_tol=1e-4), f"Mismatch for tau={tau}, t={t}" | ||
|
|
||
| print(f" ✓ Rust calculate_damage matches Python calculations for all tested cases") | ||
| else: | ||
| print(f"\nRust verification skipped (cfd_python not available)") | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove or remap the disabled dispatch modes.
With these steps commented out,
workflow_dispatchruns usingbenchmark_type=regressionorbenchmark_type=productionexecute no benchmark at all: Line 79 skips the comprehensive run for those values, and the job later still looks for artifacts those steps used to generate. Either drop those input options or point them at a valid bench target.🤖 Prompt for AI Agents