#[derive(Debug, Clone, Default)]
pub struct AmrOperators;
impl AmrOperators {
pub fn new() -> Self {
AmrOperators
}
pub fn prolongate_2d_values(&self, parent_vals: &[f64], children: &mut [Vec<f64>; 4]) {
for child in children.iter_mut() {
child.resize(parent_vals.len(), 0.0);
child.copy_from_slice(parent_vals);
}
}
pub fn prolongate_2d_with_gradient(
&self,
parent_vals: &[f64],
grad_x: &[f64],
grad_y: &[f64],
dx: f64,
dy: f64,
children: &mut [Vec<f64>; 4],
) {
let offsets: [(f64, f64); 4] = [
(-0.25 * dx, -0.25 * dy),
(0.25 * dx, -0.25 * dy),
(-0.25 * dx, 0.25 * dy),
(0.25 * dx, 0.25 * dy),
];
let n = parent_vals.len();
for (k, child) in children.iter_mut().enumerate() {
child.resize(n, 0.0);
let (ox, oy) = offsets[k];
for v in 0..n {
child[v] = parent_vals[v]
+ grad_x.get(v).copied().unwrap_or(0.0) * ox
+ grad_y.get(v).copied().unwrap_or(0.0) * oy;
}
}
}
pub fn restrict_2d_values(&self, children: &[Vec<f64>; 4]) -> Vec<f64> {
let n = children[0].len();
let mut parent = vec![0.0f64; n];
for child in children {
for (p, &c) in parent.iter_mut().zip(child.iter()) {
*p += c;
}
}
for p in &mut parent {
*p /= 4.0;
}
parent
}
pub fn check_conservation_2d(
&self,
parent_vals: &[f64],
children: &[Vec<f64>; 4],
tol: f64,
) -> bool {
let n = parent_vals.len();
for v in 0..n {
let sum: f64 = children.iter().map(|ch| ch[v]).sum::<f64>() / 4.0;
if (sum - parent_vals[v]).abs() > tol {
return false;
}
}
true
}
pub fn prolongate_3d_values(&self, parent_vals: &[f64], children: &mut [Vec<f64>; 8]) {
for child in children.iter_mut() {
child.resize(parent_vals.len(), 0.0);
child.copy_from_slice(parent_vals);
}
}
pub fn prolongate_3d_with_gradient(
&self,
parent_vals: &[f64],
grad_x: &[f64],
grad_y: &[f64],
grad_z: &[f64],
dx: f64,
dy: f64,
dz: f64,
children: &mut [Vec<f64>; 8],
) {
let n = parent_vals.len();
for (k, child) in children.iter_mut().enumerate() {
child.resize(n, 0.0);
let ox = if k & 1 != 0 { 0.25 * dx } else { -0.25 * dx };
let oy = if k & 2 != 0 { 0.25 * dy } else { -0.25 * dy };
let oz = if k & 4 != 0 { 0.25 * dz } else { -0.25 * dz };
for v in 0..n {
child[v] = parent_vals[v]
+ grad_x.get(v).copied().unwrap_or(0.0) * ox
+ grad_y.get(v).copied().unwrap_or(0.0) * oy
+ grad_z.get(v).copied().unwrap_or(0.0) * oz;
}
}
}
pub fn restrict_3d_values(&self, children: &[Vec<f64>; 8]) -> Vec<f64> {
let n = children[0].len();
let mut parent = vec![0.0f64; n];
for child in children {
for (p, &c) in parent.iter_mut().zip(child.iter()) {
*p += c;
}
}
for p in &mut parent {
*p /= 8.0;
}
parent
}
pub fn check_conservation_3d(
&self,
parent_vals: &[f64],
children: &[Vec<f64>; 8],
tol: f64,
) -> bool {
let n = parent_vals.len();
for v in 0..n {
let sum: f64 = children.iter().map(|ch| ch[v]).sum::<f64>() / 8.0;
if (sum - parent_vals[v]).abs() > tol {
return false;
}
}
true
}
}
pub fn prolongate_2d(parent_vals: &[f64], children: &mut [Vec<f64>; 4]) {
AmrOperators::new().prolongate_2d_values(parent_vals, children);
}
pub fn restrict_2d(children: &[Vec<f64>; 4]) -> Vec<f64> {
AmrOperators::new().restrict_2d_values(children)
}
pub fn prolongate_3d(parent_vals: &[f64], children: &mut [Vec<f64>; 8]) {
AmrOperators::new().prolongate_3d_values(parent_vals, children);
}
pub fn restrict_3d(children: &[Vec<f64>; 8]) -> Vec<f64> {
AmrOperators::new().restrict_3d_values(children)
}
#[cfg(test)]
mod tests {
use super::*;
fn make_children_2d(vals: Vec<f64>) -> [Vec<f64>; 4] {
[vals.clone(), vals.clone(), vals.clone(), vals.clone()]
}
fn make_children_3d(vals: Vec<f64>) -> [Vec<f64>; 8] {
[
vals.clone(),
vals.clone(),
vals.clone(),
vals.clone(),
vals.clone(),
vals.clone(),
vals.clone(),
vals.clone(),
]
}
#[test]
fn test_prolongate_2d_constant_field() {
let ops = AmrOperators::new();
let parent = vec![3.0, 7.0];
let mut children = make_children_2d(vec![0.0, 0.0]);
ops.prolongate_2d_values(&parent, &mut children);
for ch in &children {
assert!((ch[0] - 3.0).abs() < 1e-12);
assert!((ch[1] - 7.0).abs() < 1e-12);
}
}
#[test]
fn test_restrict_2d_conservation() {
let ops = AmrOperators::new();
let children: [Vec<f64>; 4] = [
vec![1.0, 4.0],
vec![2.0, 5.0],
vec![3.0, 6.0],
vec![4.0, 7.0],
];
let parent = ops.restrict_2d_values(&children);
assert!((parent[0] - 2.5).abs() < 1e-12);
assert!((parent[1] - 5.5).abs() < 1e-12);
}
#[test]
fn test_conservation_check_2d() {
let ops = AmrOperators::new();
let parent = vec![2.5, 5.5];
let children: [Vec<f64>; 4] = [
vec![1.0, 4.0],
vec![2.0, 5.0],
vec![3.0, 6.0],
vec![4.0, 7.0],
];
assert!(ops.check_conservation_2d(&parent, &children, 1e-10));
}
#[test]
fn test_prolong_restrict_roundtrip_2d() {
let ops = AmrOperators::new();
let parent_orig = vec![5.0, -3.0];
let mut children = make_children_2d(vec![0.0, 0.0]);
ops.prolongate_2d_values(&parent_orig, &mut children);
let parent_back = ops.restrict_2d_values(&children);
for (a, b) in parent_orig.iter().zip(parent_back.iter()) {
assert!((a - b).abs() < 1e-12, "roundtrip mismatch: {a} vs {b}");
}
}
#[test]
fn test_prolongate_3d_constant_field() {
let ops = AmrOperators::new();
let parent = vec![2.0, 4.0, 6.0];
let mut children = make_children_3d(vec![0.0; 3]);
ops.prolongate_3d_values(&parent, &mut children);
for ch in &children {
assert!((ch[0] - 2.0).abs() < 1e-12);
assert!((ch[1] - 4.0).abs() < 1e-12);
assert!((ch[2] - 6.0).abs() < 1e-12);
}
}
#[test]
fn test_restrict_3d_conservation() {
let ops = AmrOperators::new();
let children: [Vec<f64>; 8] = [
vec![1.0],
vec![2.0],
vec![3.0],
vec![4.0],
vec![5.0],
vec![6.0],
vec![7.0],
vec![8.0],
];
let parent = ops.restrict_3d_values(&children);
assert!((parent[0] - 4.5).abs() < 1e-12);
assert!(ops.check_conservation_3d(&parent, &children, 1e-10));
}
#[test]
fn test_prolong_restrict_roundtrip_3d() {
let ops = AmrOperators::new();
let parent_orig = vec![9.0, -1.0, 0.5];
let mut children = make_children_3d(vec![0.0; 3]);
ops.prolongate_3d_values(&parent_orig, &mut children);
let parent_back = ops.restrict_3d_values(&children);
for (a, b) in parent_orig.iter().zip(parent_back.iter()) {
assert!((a - b).abs() < 1e-12);
}
}
#[test]
fn test_prolongate_2d_with_gradient() {
let ops = AmrOperators::new();
let parent_val = vec![0.0]; let grad_x = vec![1.0];
let grad_y = vec![0.0];
let dx = 1.0;
let dy = 1.0;
let mut children = make_children_2d(vec![0.0]);
ops.prolongate_2d_with_gradient(&parent_val, &grad_x, &grad_y, dx, dy, &mut children);
assert!((children[0][0] - (-0.25)).abs() < 1e-12);
assert!((children[1][0] - 0.25).abs() < 1e-12);
}
#[test]
fn test_prolongate_3d_with_gradient() {
let ops = AmrOperators::new();
let parent_val = vec![0.0];
let grad_x = vec![1.0];
let grad_y = vec![1.0];
let grad_z = vec![1.0];
let dx = 1.0;
let dy = 1.0;
let dz = 1.0;
let mut children = make_children_3d(vec![0.0]);
ops.prolongate_3d_with_gradient(
&parent_val,
&grad_x,
&grad_y,
&grad_z,
dx,
dy,
dz,
&mut children,
);
assert!((children[7][0] - 0.75).abs() < 1e-12);
assert!((children[0][0] - (-0.75)).abs() < 1e-12);
}
}