Test curvature drift during position nudging
All checks were successful
/ test (pull_request) Successful in 2m25s

The test fails, as expected, if you disable sphere normalization by
making `Sphere::normalize_mut_rep` do nothing. Since the tests aren't
built for WebAssembly, we have to replace `console::log` with
`console_log!` in all of the functions they use. We'll eventually want
to do this replacement everywhere.
This commit is contained in:
Aaron Fenyes 2025-05-20 17:26:23 -07:00
parent c263d5b8a6
commit f1da5155a3
3 changed files with 76 additions and 38 deletions

View file

@ -46,6 +46,9 @@ features = [
dyna3 = { path = ".", default-features = false, features = ["dev"] }
wasm-bindgen-test = "0.3.34"
[lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = ["cfg(sycamore_force_ssr)"] }
[profile.release]
opt-level = "s" # optimize for small code size
debug = true # include debug symbols

View file

@ -610,9 +610,7 @@ impl Assembly {
create_effect(move || {
/* DEBUG */
// log the regulator update
console::log_1(&JsValue::from(
format!("Updated regulator with subjects {:?}", regulator.subjects())
));
console_log!("Updated regulator with subjects {:?}", regulator.subjects());
if regulator.try_activate() {
self_for_effect.realize();
@ -621,10 +619,10 @@ impl Assembly {
/* DEBUG */
// print an updated list of regulators
console::log_1(&JsValue::from("Regulators:"));
console_log!("Regulators:");
self.regulators.with_untracked(|regs| {
for reg in regs.into_iter() {
console::log_1(&JsValue::from(format!(
console_log!(
" {:?}: {}",
reg.subjects(),
reg.set_point().with_untracked(
@ -637,7 +635,7 @@ impl Assembly {
}
}
)
)));
);
}
});
}
@ -668,19 +666,11 @@ impl Assembly {
/* DEBUG */
// log the Gram matrix
console::log_1(&JsValue::from("Gram matrix:"));
problem.gram.log_to_console();
console_log!("Gram matrix:\n{}", problem.gram);
/* DEBUG */
// log the initial configuration matrix
console::log_1(&JsValue::from("Old configuration:"));
for j in 0..problem.guess.nrows() {
let mut row_str = String::new();
for k in 0..problem.guess.ncols() {
row_str.push_str(format!(" {:>8.3}", problem.guess[(j, k)]).as_str());
}
console::log_1(&JsValue::from(row_str));
}
console_log!("Old configuration:{:>8.3}", problem.guess);
// look for a configuration with the given Gram matrix
let (config, tangent, success, history) = realize_gram(
@ -689,16 +679,14 @@ impl Assembly {
/* DEBUG */
// report the outcome of the search
console::log_1(&JsValue::from(
if success {
"Target accuracy achieved!"
} else {
"Failed to reach target accuracy"
}
));
console::log_2(&JsValue::from("Steps:"), &JsValue::from(history.scaled_loss.len() - 1));
console::log_2(&JsValue::from("Loss:"), &JsValue::from(*history.scaled_loss.last().unwrap()));
console::log_2(&JsValue::from("Tangent dimension:"), &JsValue::from(tangent.dim()));
if success {
console_log!("Target accuracy achieved!")
} else {
console_log!("Failed to reach target accuracy")
}
console_log!("Steps: {}", history.scaled_loss.len() - 1);
console_log!("Loss: {}", *history.scaled_loss.last().unwrap());
console_log!("Tangent dimension: {}", tangent.dim());
if success {
// read out the solution
@ -791,9 +779,7 @@ impl Assembly {
elt.normalize_mut_rep(rep);
},
None => {
console::log_1(&JsValue::from(
format!("No velocity to unpack for fresh element \"{}\"", elt.id())
))
console_log!("No velocity to unpack for fresh element \"{}\"", elt.id())
}
};
});
@ -810,6 +796,8 @@ impl Assembly {
mod tests {
use super::*;
use crate::engine;
#[test]
#[should_panic(expected = "Sphere \"sphere\" should be indexed before writing problem data")]
fn unindexed_element_test() {
@ -835,4 +823,50 @@ mod tests {
}.pose(&mut ConstraintProblem::new(2));
});
}
#[test]
fn curvature_drift_test() {
const INITIAL_RADIUS: f64 = 0.25;
let _ = create_root(|| {
// set up an assembly containing a single sphere centered at the
// origin
let assembly = Assembly::new();
let sphere_id = "sphere0";
let _ = assembly.try_insert_element(
// we create the sphere by hand for two reasons: to choose the
// curvature (which can affect drift rate) and to make the test
// independent of `Sphere::default`
Sphere::new(
String::from(sphere_id),
String::from("Sphere 0"),
[0.75_f32, 0.75_f32, 0.75_f32],
engine::sphere(0.0, 0.0, 0.0, INITIAL_RADIUS)
)
);
// nudge the sphere repeatedly along the `z` axis
const STEP_SIZE: f64 = 0.0025;
const STEP_CNT: usize = 400;
let sphere = assembly.elements_by_id.with(|elts_by_id| elts_by_id[sphere_id].clone());
let velocity = DVector::from_column_slice(&[0.0, 0.0, STEP_SIZE, 0.0]);
for _ in 0..STEP_CNT {
assembly.deform(
vec![
ElementMotion {
element: sphere.clone(),
velocity: velocity.as_view()
}
]
);
}
// check how much the sphere's curvature has drifted
const INITIAL_HALF_CURV: f64 = 0.5 / INITIAL_RADIUS;
const DRIFT_TOL: f64 = 0.015;
let final_half_curv = sphere.representation().with_untracked(
|rep| rep[Sphere::CURVATURE_COMPONENT]
);
assert!((final_half_curv / INITIAL_HALF_CURV - 1.0).abs() < DRIFT_TOL);
});
}
}

View file

@ -1,5 +1,6 @@
use lazy_static::lazy_static;
use nalgebra::{Const, DMatrix, DVector, DVectorView, Dyn, SymmetricEigen};
use std::fmt::{Display, Error, Formatter};
use web_sys::{console, wasm_bindgen::JsValue}; /* DEBUG */
// --- elements ---
@ -109,15 +110,6 @@ impl PartialMatrix {
}
}
/* DEBUG */
pub fn log_to_console(&self) {
for &MatrixEntry { index: (row, col), value } in self {
console::log_1(&JsValue::from(
format!(" {} {} {}", row, col, value)
));
}
}
fn freeze(&self, a: &DMatrix<f64>) -> DMatrix<f64> {
let mut result = a.clone();
for &MatrixEntry { index, value } in self {
@ -143,6 +135,15 @@ impl PartialMatrix {
}
}
impl Display for PartialMatrix {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
for &MatrixEntry { index: (row, col), value } in self {
writeln!(f, " {row} {col} {value}")?;
}
Ok(())
}
}
impl IntoIterator for PartialMatrix {
type Item = MatrixEntry;
type IntoIter = std::vec::IntoIter<Self::Item>;