embedded3 S2-2b-ii HELD: the device V-cycle's fine level refreshed with the operator (coarser levels kept K steps), the fine rhs zeroed before the gather — moving circle identity 1.7e-14 at K = 10, CG +9 %
CI / Python Bindings (maturin) (macos-latest) (push) Blocked by required conditions
CI / Build (macos-latest) (push) Waiting to run
CI / Test (macos-latest) (push) Blocked by required conditions
CI / Test (ubuntu-latest) (push) Blocked by required conditions
CI / Python Bindings (maturin) (ubuntu-latest) (push) Blocked by required conditions
CI / WASM Build + Size Check (push) Blocked by required conditions
CI / Distributed Training Tests (push) Blocked by required conditions
CI / CI Success (push) Blocked by required conditions
Documentation / Build API Documentation (push) Failing after 3s
CI / Clippy Check (push) Failing after 4s
CI / Build (ubuntu-latest) (push) Failing after 4s
Documentation / Build User Guide (push) Successful in 6s
CI / Format Check (push) Failing after 11s
CI / Build CPU-Only (Explicit) (push) Failing after 1m26s
Performance Benchmarks / Run Benchmarks (push) Successful in 2m3s

Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
Omar Sobh
2026-09-17 23:10:15 -05:00
co-authored by Claude Fable 5.1
parent c713bbbbb2
commit 6d2872e7d0
2 changed files with 47 additions and 0 deletions
@@ -171,6 +171,48 @@ impl DeviceVcycle {
self.levels.len()
}
/// Refresh the finest level (its cells, colours, neighbours, parents
/// and coefficients) for a changed operator, keeping the coarser
/// levels: the smoother is exact on the current operator, the coarse
/// correction approximate (a moving body's per-step refresh).
pub fn refresh_fine(&mut self, level0: &LevelExport) {
let rt = runtime();
let up_u = |v: &[u32]| -> CudaSlice<u32> {
rt.stream
.memcpy_stod(if v.is_empty() { &[0u32][..] } else { v })
.expect("upload")
};
let up_f = |v: &[f32]| -> CudaSlice<f32> { rt.stream.memcpy_stod(v).expect("upload") };
let lv = &mut self.levels[0];
lv.n_cells = level0.cells.len();
lv.n_red = level0.red.len();
lv.n_black = level0.black.len();
lv.cells = up_u(&level0.cells);
lv.red = up_u(&level0.red);
lv.black = up_u(&level0.black);
lv.top = up_u(&level0.top);
lv.bot = up_u(&level0.bot);
lv.coarse_of = up_u(&level0.coarse_of);
lv.ae = up_f(&level0.ae);
lv.aw = up_f(&level0.aw);
lv.an = up_f(&level0.an);
lv.as_ = up_f(&level0.as_);
lv.at = up_f(&level0.at);
lv.ab = up_f(&level0.ab);
lv.ap = up_f(&level0.ap);
self.fine_cells = level0.cells.clone();
}
/// Zero the finest level's right-hand side (a gather writes only the
/// current cells; stale entries elsewhere would restrict into the
/// coarse levels).
pub fn zero_fine_rhs(&mut self) {
let rt = runtime();
rt.stream
.memset_zeros(&mut self.levels[0].b)
.expect("b0 = 0");
}
fn half(&mut self, l: usize, colour: u8) {
let rt = runtime();
let k = kernels();
@@ -242,6 +242,10 @@ impl DeviceCg {
self.singular = singular_count > 0;
self.active_host = fine.active.clone();
self.key = OperatorKey::of(problem, params);
// The V-cycle's finest level follows the operator (its coarser
// levels stay).
let levels = export_hierarchy(problem, params);
self.vcycle.refresh_fine(&levels[0]);
}
pub fn n_cells(&self) -> usize {
@@ -435,6 +439,7 @@ impl DeviceCg {
let k = kernels();
let n_i = self.n_cells as i32;
rt.stream.memset_zeros(&mut self.z).expect("z = 0");
self.vcycle.zero_fine_rhs();
unsafe {
rt.stream
.launch_builder(&k.gather_f32)