shallow water equations solver
This commit is contained in:
		
							
								
								
									
										89
									
								
								shallow_water/src/characteristics.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								shallow_water/src/characteristics.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,89 @@
 | 
			
		||||
#![allow(non_snake_case)]
 | 
			
		||||
/*
 | 
			
		||||
 *                     Code generated with sympy 1.7.dev
 | 
			
		||||
 *
 | 
			
		||||
 *              See http://www.sympy.org/ for more information.
 | 
			
		||||
 *
 | 
			
		||||
 *                       This file is part of 'project'
 | 
			
		||||
 */
 | 
			
		||||
use super::Float;
 | 
			
		||||
 | 
			
		||||
pub fn Aplus(eta: Float, etau: Float, etav: Float, g: Float) -> [[Float; 3]; 3] {
 | 
			
		||||
    [
 | 
			
		||||
        [
 | 
			
		||||
            (eta.powf(3.0 / 2.0) * g.sqrt() + etau.abs()) / (2.0 * eta),
 | 
			
		||||
            1.0 / 2.0,
 | 
			
		||||
            0.0,
 | 
			
		||||
        ],
 | 
			
		||||
        [
 | 
			
		||||
            eta * g / 2.0 - etau.powi(2) / (2.0 * eta.powi(2)),
 | 
			
		||||
            (eta.powf(3.0 / 2.0) * g.sqrt() + 2.0 * etau + etau.abs()) / (2.0 * eta),
 | 
			
		||||
            0.0,
 | 
			
		||||
        ],
 | 
			
		||||
        [
 | 
			
		||||
            -etau * etav / (2.0 * eta.powi(2)),
 | 
			
		||||
            etav / (2.0 * eta),
 | 
			
		||||
            (eta.powf(3.0 / 2.0) * g.sqrt() + etau + etau.abs()) / (2.0 * eta),
 | 
			
		||||
        ],
 | 
			
		||||
    ]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn Aminus(eta: Float, etau: Float, etav: Float, g: Float) -> [[Float; 3]; 3] {
 | 
			
		||||
    [
 | 
			
		||||
        [
 | 
			
		||||
            -(eta.powf(3.0 / 2.0) * g.sqrt() + etau.abs()) / (2.0 * eta),
 | 
			
		||||
            1.0 / 2.0,
 | 
			
		||||
            0.0,
 | 
			
		||||
        ],
 | 
			
		||||
        [
 | 
			
		||||
            eta * g / 2.0 - etau.powi(2) / (2.0 * eta.powi(2)),
 | 
			
		||||
            (-eta.powf(3.0 / 2.0) * g.sqrt() + 2.0 * etau - etau.abs()) / (2.0 * eta),
 | 
			
		||||
            0.0,
 | 
			
		||||
        ],
 | 
			
		||||
        [
 | 
			
		||||
            -etau * etav / (2.0 * eta.powi(2)),
 | 
			
		||||
            etav / (2.0 * eta),
 | 
			
		||||
            (-eta.powf(3.0 / 2.0) * g.sqrt() + etau - etau.abs()) / (2.0 * eta),
 | 
			
		||||
        ],
 | 
			
		||||
    ]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn Bplus(eta: Float, etau: Float, etav: Float, g: Float) -> [[Float; 3]; 3] {
 | 
			
		||||
    [
 | 
			
		||||
        [
 | 
			
		||||
            (eta.powf(3.0 / 2.0) + etav.abs()) / (2.0 * eta),
 | 
			
		||||
            0.0,
 | 
			
		||||
            1.0 / 2.0,
 | 
			
		||||
        ],
 | 
			
		||||
        [
 | 
			
		||||
            -etau * etav / (2.0 * eta.powi(2)),
 | 
			
		||||
            (eta.powf(3.0 / 2.0) * g.sqrt() + etav + etav.abs()) / (2.0 * eta),
 | 
			
		||||
            etau / (2.0 * eta),
 | 
			
		||||
        ],
 | 
			
		||||
        [
 | 
			
		||||
            eta * g / 2.0 - etav.powi(2) / (2.0 * eta.powi(2)),
 | 
			
		||||
            0.0,
 | 
			
		||||
            (eta.powf(3.0 / 2.0) * g.sqrt() + 2.0 * etav + etav.abs()) / (2.0 * eta),
 | 
			
		||||
        ],
 | 
			
		||||
    ]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn Bminus(eta: Float, etau: Float, etav: Float, g: Float) -> [[Float; 3]; 3] {
 | 
			
		||||
    [
 | 
			
		||||
        [
 | 
			
		||||
            -(eta.powf(3.0 / 2.0) + etav.abs()) / (2.0 * eta),
 | 
			
		||||
            0.0,
 | 
			
		||||
            1.0 / 2.0,
 | 
			
		||||
        ],
 | 
			
		||||
        [
 | 
			
		||||
            -etau * etav / (2.0 * eta.powi(2)),
 | 
			
		||||
            (-eta.powf(3.0 / 2.0) * g.sqrt() + etav - etav.abs()) / (2.0 * eta),
 | 
			
		||||
            etau / (2.0 * eta),
 | 
			
		||||
        ],
 | 
			
		||||
        [
 | 
			
		||||
            eta * g / 2.0 - etav.powi(2) / (2.0 * eta.powi(2)),
 | 
			
		||||
            0.0,
 | 
			
		||||
            (-eta.powf(3.0 / 2.0) * g.sqrt() + 2.0 * etav - etav.abs()) / (2.0 * eta),
 | 
			
		||||
        ],
 | 
			
		||||
    ]
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										322
									
								
								shallow_water/src/lib.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										322
									
								
								shallow_water/src/lib.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,322 @@
 | 
			
		||||
use ndarray::prelude::*;
 | 
			
		||||
use sbp::*;
 | 
			
		||||
 | 
			
		||||
mod characteristics;
 | 
			
		||||
use characteristics::{Aminus, Aplus, Bminus, Bplus};
 | 
			
		||||
 | 
			
		||||
const G: Float = 1.0;
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug)]
 | 
			
		||||
pub struct Field(Array3<Float>);
 | 
			
		||||
 | 
			
		||||
impl<'a> Into<ArrayView3<'a, Float>> for &'a Field {
 | 
			
		||||
    fn into(self) -> ArrayView3<'a, Float> {
 | 
			
		||||
        self.0.view()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> Into<ArrayViewMut3<'a, Float>> for &'a mut Field {
 | 
			
		||||
    fn into(self) -> ArrayViewMut3<'a, Float> {
 | 
			
		||||
        self.0.view_mut()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Field {
 | 
			
		||||
    fn new(ny: usize, nx: usize) -> Self {
 | 
			
		||||
        Self(Array3::zeros((3, ny, nx)))
 | 
			
		||||
    }
 | 
			
		||||
    fn eta(&self) -> ArrayView2<Float> {
 | 
			
		||||
        self.0.slice(s![0, .., ..])
 | 
			
		||||
    }
 | 
			
		||||
    fn etau(&self) -> ArrayView2<Float> {
 | 
			
		||||
        self.0.slice(s![1, .., ..])
 | 
			
		||||
    }
 | 
			
		||||
    fn etav(&self) -> ArrayView2<Float> {
 | 
			
		||||
        self.0.slice(s![2, .., ..])
 | 
			
		||||
    }
 | 
			
		||||
    /*
 | 
			
		||||
    fn eta_mut(&mut self) -> ArrayViewMut2<Float> {
 | 
			
		||||
        self.0.slice_mut(s![0, .., ..])
 | 
			
		||||
    }
 | 
			
		||||
    fn etau_mut(&mut self) -> ArrayViewMut2<Float> {
 | 
			
		||||
        self.0.slice_mut(s![1, .., ..])
 | 
			
		||||
    }
 | 
			
		||||
    fn etav_mut(&mut self) -> ArrayViewMut2<Float> {
 | 
			
		||||
        self.0.slice_mut(s![2, .., ..])
 | 
			
		||||
    }
 | 
			
		||||
    */
 | 
			
		||||
    fn components_mut(
 | 
			
		||||
        &mut self,
 | 
			
		||||
    ) -> (
 | 
			
		||||
        ArrayViewMut2<Float>,
 | 
			
		||||
        ArrayViewMut2<Float>,
 | 
			
		||||
        ArrayViewMut2<Float>,
 | 
			
		||||
    ) {
 | 
			
		||||
        self.0
 | 
			
		||||
            .multi_slice_mut((s![0, .., ..], s![1, .., ..], s![2, .., ..]))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct System {
 | 
			
		||||
    fnow: Field,
 | 
			
		||||
    fnext: Field,
 | 
			
		||||
    x: (Float, Float, usize),
 | 
			
		||||
    y: (Float, Float, usize),
 | 
			
		||||
    op: Box<dyn operators::UpwindOperator2d>,
 | 
			
		||||
    k: [Field; 4],
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl System {
 | 
			
		||||
    pub fn new(x: (Float, Float, usize), y: (Float, Float, usize)) -> Self {
 | 
			
		||||
        let field = Field::new(y.2, x.2);
 | 
			
		||||
        Self {
 | 
			
		||||
            fnow: field.clone(),
 | 
			
		||||
            fnext: field.clone(),
 | 
			
		||||
            x,
 | 
			
		||||
            y,
 | 
			
		||||
            op: Box::new(operators::Upwind9),
 | 
			
		||||
            k: [field.clone(), field.clone(), field.clone(), field],
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    pub fn nx(&self) -> usize {
 | 
			
		||||
        self.x.2
 | 
			
		||||
    }
 | 
			
		||||
    pub fn ny(&self) -> usize {
 | 
			
		||||
        self.y.2
 | 
			
		||||
    }
 | 
			
		||||
    pub fn eta(&self) -> ArrayView2<Float> {
 | 
			
		||||
        self.fnow.eta()
 | 
			
		||||
    }
 | 
			
		||||
    pub fn etau(&self) -> ArrayView2<Float> {
 | 
			
		||||
        self.fnow.etau()
 | 
			
		||||
    }
 | 
			
		||||
    pub fn etav(&self) -> ArrayView2<Float> {
 | 
			
		||||
        self.fnow.etav()
 | 
			
		||||
    }
 | 
			
		||||
    pub fn components_mut(
 | 
			
		||||
        &mut self,
 | 
			
		||||
    ) -> (
 | 
			
		||||
        ArrayViewMut2<Float>,
 | 
			
		||||
        ArrayViewMut2<Float>,
 | 
			
		||||
        ArrayViewMut2<Float>,
 | 
			
		||||
    ) {
 | 
			
		||||
        self.fnow.components_mut()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn max_dt(&self) -> Float {
 | 
			
		||||
        0.1 * ((self.x.1 - self.x.0) / self.x.2 as Float)
 | 
			
		||||
            .min((self.y.1 - self.y.0) / self.y.2 as Float)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn advance(&mut self) {
 | 
			
		||||
        let max_dt = self.max_dt();
 | 
			
		||||
        let op = &self.op;
 | 
			
		||||
        let rhs = move |next: &mut Field, now: &Field, _t: Float| {
 | 
			
		||||
            let (mut next_eta, mut next_etau, mut next_etav) = next.components_mut();
 | 
			
		||||
            next_eta.fill(0.0);
 | 
			
		||||
            next_etau.fill(0.0);
 | 
			
		||||
            next_etav.fill(0.0);
 | 
			
		||||
 | 
			
		||||
            let nx = next_eta.shape()[1];
 | 
			
		||||
            let ny = next_eta.shape()[0];
 | 
			
		||||
 | 
			
		||||
            if false {
 | 
			
		||||
                let eta = now.eta();
 | 
			
		||||
                for j in 0..ny {
 | 
			
		||||
                    for i in 0..nx {
 | 
			
		||||
                        if eta[(j, i)] <= 0.0 {
 | 
			
		||||
                            panic!("{} {}", j, i);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            let mut temp = Array2::<Float>::zeros((ny, nx));
 | 
			
		||||
 | 
			
		||||
            // E flux
 | 
			
		||||
            let mut temp_dx = temp.clone();
 | 
			
		||||
            azip!((dest in &mut temp, etau in now.etau()) {
 | 
			
		||||
                *dest = *etau;
 | 
			
		||||
            });
 | 
			
		||||
            op.diffxi(temp.view(), temp_dx.view_mut());
 | 
			
		||||
            next_eta.scaled_add(-1.0, &temp_dx);
 | 
			
		||||
 | 
			
		||||
            azip!((dest in &mut temp, eta in now.eta(), etau in now.etau()) {
 | 
			
		||||
                *dest = etau.powi(2)/eta + G*eta.powi(2)/2.0
 | 
			
		||||
            });
 | 
			
		||||
            op.diffxi(temp.view(), temp_dx.view_mut());
 | 
			
		||||
            next_etau.scaled_add(-1.0, &temp_dx);
 | 
			
		||||
 | 
			
		||||
            azip!((dest in &mut temp, eta in now.eta(), etau in now.etau(), etav in now.etav()) {
 | 
			
		||||
                *dest = etau*etav/eta
 | 
			
		||||
            });
 | 
			
		||||
            op.diffxi(temp.view(), temp_dx.view_mut());
 | 
			
		||||
            next_etav.scaled_add(-1.0, &temp_dx);
 | 
			
		||||
 | 
			
		||||
            // F flux
 | 
			
		||||
            let mut temp_dy = temp_dx;
 | 
			
		||||
            azip!((dest in &mut temp, etav in now.etav()) {
 | 
			
		||||
                *dest = *etav;
 | 
			
		||||
            });
 | 
			
		||||
            op.diffeta(temp.view(), temp_dy.view_mut());
 | 
			
		||||
            next_eta.scaled_add(-1.0, &temp_dy);
 | 
			
		||||
 | 
			
		||||
            azip!((dest in &mut temp, eta in now.eta(), etau in now.etau(), etav in now.etav()) {
 | 
			
		||||
                *dest = etau*etav/eta;
 | 
			
		||||
            });
 | 
			
		||||
            op.diffeta(temp.view(), temp_dy.view_mut());
 | 
			
		||||
            next_etau.scaled_add(-1.0, &temp_dy);
 | 
			
		||||
 | 
			
		||||
            azip!((dest in &mut temp, eta in now.eta(), etav in now.etav()) {
 | 
			
		||||
                *dest = etav.powi(2)/eta + G*eta.powi(2)/2.0
 | 
			
		||||
            });
 | 
			
		||||
            op.diffeta(temp.view(), temp_dy.view_mut());
 | 
			
		||||
            next_etav.scaled_add(-1.0, &temp_dy);
 | 
			
		||||
 | 
			
		||||
            // Upwind dissipation
 | 
			
		||||
            if false {
 | 
			
		||||
                let mut temp_dx = temp_dy;
 | 
			
		||||
                azip!((dest in &mut temp, eta in now.eta(), etau in now.etau()) {
 | 
			
		||||
                    *dest = -(eta.powf(3.0/2.0)*G.sqrt() + etau.abs())/eta
 | 
			
		||||
                });
 | 
			
		||||
                op.dissxi(temp.view(), temp_dx.view_mut());
 | 
			
		||||
                azip!((dest in &mut next_eta, eta in now.eta(), diss in &temp_dx) {
 | 
			
		||||
                    *dest -= eta*diss;
 | 
			
		||||
                });
 | 
			
		||||
                azip!((dest in &mut next_etau, etau in now.etau(), diss in &temp_dx) {
 | 
			
		||||
                    *dest -= etau*diss;
 | 
			
		||||
                });
 | 
			
		||||
                azip!((dest in &mut next_etav, etav in now.etav(), diss in &temp_dx) {
 | 
			
		||||
                    *dest -= etav*diss;
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                let mut temp_dy = temp_dx;
 | 
			
		||||
                azip!((dest in &mut temp, eta in now.eta(), etav in now.etav()) {
 | 
			
		||||
                    *dest = -(eta.powf(3.0/2.0)*G.sqrt() + etav.abs())/eta
 | 
			
		||||
                });
 | 
			
		||||
                op.disseta(temp.view(), temp_dy.view_mut());
 | 
			
		||||
                azip!((dest in &mut next_eta, eta in now.eta(), diss in &temp_dy) {
 | 
			
		||||
                    *dest -= eta*diss;
 | 
			
		||||
                });
 | 
			
		||||
                azip!((dest in &mut next_etau, etau in now.etau(), diss in &temp_dy) {
 | 
			
		||||
                    *dest -= etau*diss;
 | 
			
		||||
                });
 | 
			
		||||
                azip!((dest in &mut next_etav, etav in now.etav(), diss in &temp_dy) {
 | 
			
		||||
                    *dest -= etav*diss;
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // SAT boundaries
 | 
			
		||||
            #[derive(Debug)]
 | 
			
		||||
            enum Direction {
 | 
			
		||||
                North,
 | 
			
		||||
                South,
 | 
			
		||||
                East,
 | 
			
		||||
                West,
 | 
			
		||||
            }
 | 
			
		||||
            for dir in &[
 | 
			
		||||
                Direction::North,
 | 
			
		||||
                Direction::South,
 | 
			
		||||
                Direction::East,
 | 
			
		||||
                Direction::West,
 | 
			
		||||
            ] {
 | 
			
		||||
                let mut dest;
 | 
			
		||||
                let this;
 | 
			
		||||
                let other;
 | 
			
		||||
                match dir {
 | 
			
		||||
                    Direction::North => {
 | 
			
		||||
                        dest = next.0.slice_mut(s![.., ny - 1, ..]);
 | 
			
		||||
                        this = now.0.slice(s![.., ny - 1, ..]);
 | 
			
		||||
                        other = now.0.slice(s![.., 0_usize, ..]);
 | 
			
		||||
                    }
 | 
			
		||||
                    Direction::South => {
 | 
			
		||||
                        dest = next.0.slice_mut(s![.., 0_usize, ..]);
 | 
			
		||||
                        this = now.0.slice(s![.., 0_usize, ..]);
 | 
			
		||||
                        other = now.0.slice(s![.., ny - 1, ..]);
 | 
			
		||||
                    }
 | 
			
		||||
                    Direction::East => {
 | 
			
		||||
                        dest = next.0.slice_mut(s![.., .., nx - 1]);
 | 
			
		||||
                        this = now.0.slice(s![.., .., nx - 1]);
 | 
			
		||||
                        other = now.0.slice(s![.., .., 0_usize]);
 | 
			
		||||
                    }
 | 
			
		||||
                    Direction::West => {
 | 
			
		||||
                        dest = next.0.slice_mut(s![.., .., 0_usize]);
 | 
			
		||||
                        this = now.0.slice(s![.., .., 0_usize]);
 | 
			
		||||
                        other = now.0.slice(s![.., .., nx - 1]);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                for ((mut dest, this), other) in dest
 | 
			
		||||
                    .axis_iter_mut(Axis(1))
 | 
			
		||||
                    .zip(this.axis_iter(Axis(1)))
 | 
			
		||||
                    .zip(other.axis_iter(Axis(1)))
 | 
			
		||||
                {
 | 
			
		||||
                    let tau = match dir {
 | 
			
		||||
                        Direction::North => 1.0,
 | 
			
		||||
                        Direction::South => -1.0,
 | 
			
		||||
                        Direction::East => 1.0,
 | 
			
		||||
                        Direction::West => -1.0,
 | 
			
		||||
                    };
 | 
			
		||||
                    let hinv = match dir {
 | 
			
		||||
                        Direction::North | Direction::South => {
 | 
			
		||||
                            if op.is_h2eta() {
 | 
			
		||||
                                (ny - 2) as Float / op.heta()[0]
 | 
			
		||||
                            } else {
 | 
			
		||||
                                (ny - 1) as Float / op.heta()[0]
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        Direction::East | Direction::West => {
 | 
			
		||||
                            if op.is_h2xi() {
 | 
			
		||||
                                (nx - 2) as Float / op.hxi()[0]
 | 
			
		||||
                            } else {
 | 
			
		||||
                                (nx - 1) as Float / op.hxi()[0]
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    };
 | 
			
		||||
 | 
			
		||||
                    let v = (this[0], this[1], this[2]);
 | 
			
		||||
                    let g = (other[0], other[1], other[2]);
 | 
			
		||||
                    let mat = match dir {
 | 
			
		||||
                        Direction::West => Aplus(v.0, v.1, v.2, G),
 | 
			
		||||
                        Direction::East => Aminus(v.0, v.1, v.2, G),
 | 
			
		||||
                        Direction::North => Bminus(v.0, v.1, v.2, G),
 | 
			
		||||
                        Direction::South => Bplus(v.0, v.1, v.2, G),
 | 
			
		||||
                    };
 | 
			
		||||
 | 
			
		||||
                    let q = [v.0 - g.0, v.1 - g.1, v.2 - g.2];
 | 
			
		||||
 | 
			
		||||
                    let mut res = [0.0; 3];
 | 
			
		||||
                    for j in 0..3 {
 | 
			
		||||
                        for i in 0..3 {
 | 
			
		||||
                            res[j] += mat[j][i] * q[i];
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    for i in 0..3 {
 | 
			
		||||
                        dest[i] += tau * hinv * res[i];
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            log::trace!("Iteration complete");
 | 
			
		||||
        };
 | 
			
		||||
        integrate::integrate::<integrate::Rk4, _, _>(
 | 
			
		||||
            rhs,
 | 
			
		||||
            &self.fnow,
 | 
			
		||||
            &mut self.fnext,
 | 
			
		||||
            &mut 0.0,
 | 
			
		||||
            max_dt,
 | 
			
		||||
            &mut self.k[..],
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        std::mem::swap(&mut self.fnow, &mut self.fnext)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[test]
 | 
			
		||||
fn test_advance() {
 | 
			
		||||
    let mut sys = System::new((0.0, 1.0, 50), (0.0, 1.0, 50));
 | 
			
		||||
    sys.fnow.components_mut().0.fill(1.0);
 | 
			
		||||
    for _ in 0..10 {
 | 
			
		||||
        sys.advance();
 | 
			
		||||
    }
 | 
			
		||||
    println!("{:?}", sys.fnow);
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user