split euler and maxwell to separate crates

This commit is contained in:
Magnus Ulimoen
2020-05-02 00:22:59 +02:00
parent 52bd4f3f8f
commit a1acf00c5a
17 changed files with 202 additions and 113 deletions

View File

@@ -9,26 +9,14 @@ ndarray = { version = "0.13.1", features = ["approx"] }
approx = "0.3.2"
packed_simd = "0.3.3"
rayon = { version = "1.3.0", optional = true }
arrayvec = "0.5.1"
[features]
# Internal feature flag to gate the expensive tests
# which should be run only in release builds
expensive_tests = []
# Use f32 as precision, default is f64
f32 = []
[dev-dependencies]
criterion = "0.3.1"
[[bench]]
name = "maxwell"
harness = false
[[bench]]
name = "euler"
harness = false
[[bench]]
name = "sbpoperators"
harness = false

View File

@@ -1,86 +0,0 @@
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use sbp::euler::System;
use sbp::operators::{SbpOperator2d, Upwind4, UpwindOperator2d, SBP4};
use sbp::Float;
fn advance_system<SBP: SbpOperator2d>(universe: &mut System<SBP>, n: usize) {
for _ in 0..n {
universe.advance(1.0 / 40.0 * 0.2);
}
}
fn advance_system_upwind<UO: UpwindOperator2d>(universe: &mut System<UO>, n: usize) {
for _ in 0..n {
universe.advance_upwind(1.0 / 40.0 * 0.2);
}
}
fn advance_embedded<UO: UpwindOperator2d>(universe: &mut System<UO>, embedded: bool) {
let dt = 0.2 / std::cmp::max(universe.nx(), universe.ny()) as Float;
let t = 1.0;
if embedded {
let mut dt = dt;
universe.advance_adaptive(t, &mut dt, 1e-2);
} else {
for _ in 0..(t / dt).round() as isize {
universe.advance_upwind(dt);
}
}
}
fn performance_benchmark(c: &mut Criterion) {
let mut group = c.benchmark_group("EulerSystem");
group.sample_size(25);
let w = 40;
let h = 26;
let x = ndarray::Array1::linspace(-10.0, 10.0, w);
let x = x.broadcast((h, w)).unwrap();
let y = ndarray::Array1::linspace(-10.0, 10.0, h);
let y = y.broadcast((w, h)).unwrap().reversed_axes();
let mut universe = System::new(x.into_owned(), y.into_owned(), Upwind4);
group.bench_function("advance", |b| {
b.iter(|| {
universe.init_with_vortex(0.0, 0.0);
advance_system(&mut universe, black_box(20))
})
});
let mut universe = System::new(x.into_owned(), y.into_owned(), Upwind4);
group.bench_function("advance_upwind", |b| {
b.iter(|| {
universe.init_with_vortex(0.0, 0.0);
advance_system_upwind(&mut universe, black_box(20))
})
});
let mut universe = System::new(x.into_owned(), y.into_owned(), SBP4);
group.bench_function("advance_trad4", |b| {
b.iter(|| {
universe.init_with_vortex(0.0, 0.0);
advance_system(&mut universe, black_box(20))
})
});
group.finish();
let mut group = c.benchmark_group("adaptive integration");
group.sample_size(10);
let mut universe = System::new(x.into_owned(), y.into_owned(), Upwind4);
group.bench_function("static dt", |b| {
b.iter(|| {
universe.init_with_vortex(0.0, 0.0);
advance_embedded(&mut universe, false);
})
});
group.bench_function("adaptive dt", |b| {
b.iter(|| {
universe.init_with_vortex(0.0, 0.0);
advance_embedded(&mut universe, true);
})
});
group.finish();
}
criterion_group!(benches, performance_benchmark);
criterion_main!(benches);

View File

@@ -1,55 +0,0 @@
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use sbp::maxwell::System;
use sbp::operators::{SbpOperator2d, Upwind4, UpwindOperator2d, SBP4};
use sbp::Float;
fn advance_system<SBP: SbpOperator2d>(universe: &mut System<SBP>, n: usize) {
for _ in 0..n {
universe.advance(0.01);
}
}
fn advance_system_upwind<UO: UpwindOperator2d>(universe: &mut System<UO>, n: usize) {
for _ in 0..n {
universe.advance_upwind(0.01);
}
}
fn performance_benchmark(c: &mut Criterion) {
let mut group = c.benchmark_group("MaxwellSystem");
group.sample_size(25);
let w = 40;
let h = 26;
let x = ndarray::Array2::from_shape_fn((h, w), |(_, i)| i as Float / (w - 1) as Float);
let y = ndarray::Array2::from_shape_fn((h, w), |(j, _)| j as Float / (h - 1) as Float);
let mut universe = System::new(x.clone(), y.clone(), Upwind4);
group.bench_function("advance", |b| {
b.iter(|| {
universe.set_gaussian(0.5, 0.5);
advance_system(&mut universe, black_box(20))
})
});
let mut universe = System::new(x.clone(), y.clone(), Upwind4);
group.bench_function("advance_upwind", |b| {
b.iter(|| {
universe.set_gaussian(0.5, 0.5);
advance_system_upwind(&mut universe, black_box(20))
})
});
let mut universe = System::new(x, y, SBP4);
group.bench_function("advance_trad4", |b| {
b.iter(|| {
universe.set_gaussian(0.5, 0.5);
advance_system(&mut universe, black_box(20))
})
});
group.finish();
}
criterion_group!(benches, performance_benchmark);
criterion_main!(benches);

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
use super::operators::SbpOperator2d;
use crate::Float;
use ndarray::Array2;
use ndarray::{Array2, ArrayView2};
#[derive(Debug, Clone)]
pub struct Grid {
@@ -115,3 +115,21 @@ impl Metrics {
})
}
}
impl Metrics {
pub fn detj(&self) -> ArrayView2<Float> {
self.detj.view()
}
pub fn detj_dxi_dx(&self) -> ArrayView2<Float> {
self.detj_dxi_dx.view()
}
pub fn detj_dxi_dy(&self) -> ArrayView2<Float> {
self.detj_dxi_dy.view()
}
pub fn detj_deta_dx(&self) -> ArrayView2<Float> {
self.detj_deta_dx.view()
}
pub fn detj_deta_dy(&self) -> ArrayView2<Float> {
self.detj_deta_dy.view()
}
}

View File

@@ -6,16 +6,14 @@ pub type Float = f32;
#[cfg(not(feature = "f32"))]
pub type Float = f64;
pub(crate) mod consts {
pub mod consts {
#[cfg(feature = "f32")]
pub(crate) use std::f32::consts::*;
pub use std::f32::consts::*;
#[cfg(not(feature = "f32"))]
pub(crate) use std::f64::consts::*;
pub use std::f64::consts::*;
}
pub mod euler;
pub mod grid;
pub mod integrate;
pub mod maxwell;
pub mod operators;
pub mod utils;

View File

@@ -1,628 +0,0 @@
use super::grid::{Grid, Metrics};
use super::integrate;
use super::operators::{SbpOperator2d, UpwindOperator2d};
use crate::Float;
use ndarray::azip;
use ndarray::prelude::*;
#[derive(Clone, Debug)]
pub struct Field(pub(crate) Array3<Float>);
impl std::ops::Deref for Field {
type Target = Array3<Float>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl std::ops::DerefMut for Field {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<'a> std::convert::From<&'a Field> for ArrayView3<'a, Float> {
fn from(f: &'a Field) -> Self {
f.0.view()
}
}
impl<'a> std::convert::From<&'a mut Field> for ArrayViewMut3<'a, Float> {
fn from(f: &'a mut Field) -> Self {
f.0.view_mut()
}
}
impl Field {
pub fn new(height: usize, width: usize) -> Self {
let field = Array3::zeros((3, height, width));
Self(field)
}
pub fn nx(&self) -> usize {
self.0.shape()[2]
}
pub fn ny(&self) -> usize {
self.0.shape()[1]
}
pub fn ex(&self) -> ArrayView2<Float> {
self.slice(s![0, .., ..])
}
pub fn hz(&self) -> ArrayView2<Float> {
self.slice(s![1, .., ..])
}
pub fn ey(&self) -> ArrayView2<Float> {
self.slice(s![2, .., ..])
}
pub fn ex_mut(&mut self) -> ArrayViewMut2<Float> {
self.slice_mut(s![0, .., ..])
}
pub fn hz_mut(&mut self) -> ArrayViewMut2<Float> {
self.slice_mut(s![1, .., ..])
}
pub fn ey_mut(&mut self) -> ArrayViewMut2<Float> {
self.slice_mut(s![2, .., ..])
}
pub fn components_mut(
&mut self,
) -> (
ArrayViewMut2<Float>,
ArrayViewMut2<Float>,
ArrayViewMut2<Float>,
) {
self.0
.multi_slice_mut((s![0, .., ..], s![1, .., ..], s![2, .., ..]))
}
}
#[derive(Debug, Clone)]
pub struct System<SBP: SbpOperator2d> {
sys: (Field, Field),
wb: WorkBuffers,
grid: Grid,
metrics: Metrics,
op: SBP,
}
impl<SBP: SbpOperator2d> System<SBP> {
pub fn new(x: Array2<Float>, y: Array2<Float>, op: SBP) -> Self {
assert_eq!(x.shape(), y.shape());
let ny = x.shape()[0];
let nx = x.shape()[1];
let grid = Grid::new(x, y).unwrap();
let metrics = grid.metrics(&op).unwrap();
Self {
op,
sys: (Field::new(ny, nx), Field::new(ny, nx)),
grid,
metrics,
wb: WorkBuffers::new(ny, nx),
}
}
pub fn field(&self) -> &Field {
&self.sys.0
}
pub fn set_gaussian(&mut self, x0: Float, y0: Float) {
let (ex, hz, ey) = self.sys.0.components_mut();
ndarray::azip!(
(ex in ex, hz in hz, ey in ey,
&x in &self.grid.x, &y in &self.grid.y)
{
*ex = 0.0;
*ey = 0.0;
*hz = gaussian(x, x0, y, y0)/32.0;
});
}
pub fn advance(&mut self, dt: Float) {
let op = &self.op;
let grid = &self.grid;
let metrics = &self.metrics;
let wb = &mut self.wb.tmp;
let rhs_adaptor = move |fut: &mut Field, prev: &Field, _time: Float| {
RHS(op, fut, prev, grid, metrics, wb);
};
let mut _time = 0.0;
integrate::integrate::<integrate::Rk4, _, _>(
rhs_adaptor,
&self.sys.0,
&mut self.sys.1,
&mut _time,
dt,
&mut self.wb.k,
);
std::mem::swap(&mut self.sys.0, &mut self.sys.1);
}
}
impl<UO: UpwindOperator2d> System<UO> {
/// Using artificial dissipation with the upwind operator
pub fn advance_upwind(&mut self, dt: Float) {
let op = &self.op;
let grid = &self.grid;
let metrics = &self.metrics;
let wb = &mut self.wb.tmp;
let rhs_adaptor = move |fut: &mut Field, prev: &Field, _time: Float| {
RHS_upwind(op, fut, prev, grid, metrics, wb);
};
let mut _time = 0.0;
integrate::integrate::<integrate::Rk4, _, _>(
rhs_adaptor,
&self.sys.0,
&mut self.sys.1,
&mut _time,
dt,
&mut self.wb.k,
);
std::mem::swap(&mut self.sys.0, &mut self.sys.1);
}
}
fn gaussian(x: Float, x0: Float, y: Float, y0: Float) -> Float {
use crate::consts::PI;
let x = x - x0;
let y = y - y0;
let sigma = 0.05;
1.0 / (2.0 * PI * sigma * sigma) * (-(x * x + y * y) / (2.0 * sigma * sigma)).exp()
}
#[allow(non_snake_case)]
/// Solving (Au)_x + (Bu)_y
/// with:
/// A B
/// [ 0, 0, 0] [ 0, 1, 0]
/// [ 0, 0, -1] [ 1, 0, 0]
/// [ 0, -1, 0] [ 0, 0, 0]
///
/// This flux is rotated by the grid metrics
/// (Au)_x + (Bu)_y = 1/J [
/// (J xi_x Au)_xi + (J eta_x Au)_eta
/// (J xi_y Bu)_xi + (J eta_y Bu)_eta
/// ]
/// where J is the grid determinant
///
/// This is used both in fluxes and SAT terms
fn RHS<SBP: SbpOperator2d>(
op: &SBP,
k: &mut Field,
y: &Field,
_grid: &Grid,
metrics: &Metrics,
tmp: &mut (Array2<Float>, Array2<Float>, Array2<Float>, Array2<Float>),
) {
fluxes(op, k, y, metrics, tmp);
let boundaries = BoundaryTerms {
north: Boundary::This,
south: Boundary::This,
west: Boundary::This,
east: Boundary::This,
};
SAT_characteristics(op, k, y, metrics, &boundaries);
azip!((k in &mut k.0,
&detj in &metrics.detj.broadcast((3, y.ny(), y.nx())).unwrap()) {
*k /= detj;
});
}
#[allow(non_snake_case)]
fn RHS_upwind<UO: UpwindOperator2d>(
op: &UO,
k: &mut Field,
y: &Field,
_grid: &Grid,
metrics: &Metrics,
tmp: &mut (Array2<Float>, Array2<Float>, Array2<Float>, Array2<Float>),
) {
fluxes(op, k, y, metrics, tmp);
dissipation(op, k, y, metrics, tmp);
let boundaries = BoundaryTerms {
north: Boundary::This,
south: Boundary::This,
west: Boundary::This,
east: Boundary::This,
};
SAT_characteristics(op, k, y, metrics, &boundaries);
azip!((k in &mut k.0,
&detj in &metrics.detj.broadcast((3, y.ny(), y.nx())).unwrap()) {
*k /= detj;
});
}
fn fluxes<SBP: super::operators::SbpOperator2d>(
op: &SBP,
k: &mut Field,
y: &Field,
metrics: &Metrics,
tmp: &mut (Array2<Float>, Array2<Float>, Array2<Float>, Array2<Float>),
) {
// ex = hz_y
{
ndarray::azip!((a in &mut tmp.0,
&dxi_dy in &metrics.detj_dxi_dy,
&hz in &y.hz())
*a = dxi_dy * hz
);
op.diffxi(tmp.0.view(), tmp.1.view_mut());
ndarray::azip!((b in &mut tmp.2,
&deta_dy in &metrics.detj_deta_dy,
&hz in &y.hz())
*b = deta_dy * hz
);
op.diffeta(tmp.2.view(), tmp.3.view_mut());
ndarray::azip!((flux in &mut k.ex_mut(), &ax in &tmp.1, &by in &tmp.3)
*flux = ax + by
);
}
{
// hz = -ey_x + ex_y
ndarray::azip!((a in &mut tmp.0,
&dxi_dx in &metrics.detj_dxi_dx,
&dxi_dy in &metrics.detj_dxi_dy,
&ex in &y.ex(),
&ey in &y.ey())
*a = dxi_dx * -ey + dxi_dy * ex
);
op.diffxi(tmp.0.view(), tmp.1.view_mut());
ndarray::azip!((b in &mut tmp.2,
&deta_dx in &metrics.detj_deta_dx,
&deta_dy in &metrics.detj_deta_dy,
&ex in &y.ex(),
&ey in &y.ey())
*b = deta_dx * -ey + deta_dy * ex
);
op.diffeta(tmp.2.view(), tmp.3.view_mut());
ndarray::azip!((flux in &mut k.hz_mut(), &ax in &tmp.1, &by in &tmp.3)
*flux = ax + by
);
}
// ey = -hz_x
{
ndarray::azip!((a in &mut tmp.0,
&dxi_dx in &metrics.detj_dxi_dx,
&hz in &y.hz())
*a = dxi_dx * -hz
);
op.diffxi(tmp.0.view(), tmp.1.view_mut());
azip!((b in &mut tmp.2,
&deta_dx in &metrics.detj_deta_dx,
&hz in &y.hz())
*b = deta_dx * -hz
);
op.diffeta(tmp.2.view(), tmp.3.view_mut());
azip!((flux in &mut k.ey_mut(), &ax in &tmp.1, &by in &tmp.3)
*flux = ax + by
);
}
}
fn dissipation<UO: UpwindOperator2d>(
op: &UO,
k: &mut Field,
y: &Field,
metrics: &Metrics,
tmp: &mut (Array2<Float>, Array2<Float>, Array2<Float>, Array2<Float>),
) {
// ex component
{
ndarray::azip!((a in &mut tmp.0,
&kx in &metrics.detj_dxi_dx,
&ky in &metrics.detj_dxi_dy,
&ex in &y.ex(),
&ey in &y.ey()) {
let r = Float::hypot(kx, ky);
*a = ky*ky/r * ex + -kx*ky/r*ey;
});
op.dissxi(tmp.0.view(), tmp.1.view_mut());
ndarray::azip!((b in &mut tmp.2,
&kx in &metrics.detj_deta_dx,
&ky in &metrics.detj_deta_dy,
&ex in &y.ex(),
&ey in &y.ey()) {
let r = Float::hypot(kx, ky);
*b = ky*ky/r * ex + -kx*ky/r*ey;
});
op.disseta(tmp.2.view(), tmp.3.view_mut());
ndarray::azip!((flux in &mut k.ex_mut(), &ax in &tmp.1, &by in &tmp.3)
*flux += ax + by
);
}
// hz component
{
ndarray::azip!((a in &mut tmp.0,
&kx in &metrics.detj_dxi_dx,
&ky in &metrics.detj_dxi_dy,
&hz in &y.hz()) {
let r = Float::hypot(kx, ky);
*a = r * hz;
});
op.dissxi(tmp.0.view(), tmp.1.view_mut());
ndarray::azip!((b in &mut tmp.2,
&kx in &metrics.detj_deta_dx,
&ky in &metrics.detj_deta_dy,
&hz in &y.hz()) {
let r = Float::hypot(kx, ky);
*b = r * hz;
});
op.disseta(tmp.2.view(), tmp.3.view_mut());
ndarray::azip!((flux in &mut k.hz_mut(), &ax in &tmp.1, &by in &tmp.3)
*flux += ax + by
);
}
// ey
{
ndarray::azip!((a in &mut tmp.0,
&kx in &metrics.detj_dxi_dx,
&ky in &metrics.detj_dxi_dy,
&ex in &y.ex(),
&ey in &y.ey()) {
let r = Float::hypot(kx, ky);
*a = -kx*ky/r * ex + kx*kx/r*ey;
});
op.dissxi(tmp.0.view(), tmp.1.view_mut());
ndarray::azip!((b in &mut tmp.2,
&kx in &metrics.detj_deta_dx,
&ky in &metrics.detj_deta_dy,
&ex in &y.ex(),
&ey in &y.ey()) {
let r = Float::hypot(kx, ky);
*b = -kx*ky/r * ex + kx*kx/r*ey;
});
op.disseta(tmp.2.view(), tmp.3.view_mut());
ndarray::azip!((flux in &mut k.hz_mut(), &ax in &tmp.1, &by in &tmp.3)
*flux += ax + by
);
}
}
#[derive(Clone, Debug)]
pub enum Boundary {
This,
}
#[derive(Clone, Debug)]
pub struct BoundaryTerms {
pub north: Boundary,
pub south: Boundary,
pub east: Boundary,
pub west: Boundary,
}
#[allow(non_snake_case)]
/// Boundary conditions (SAT)
fn SAT_characteristics<SBP: SbpOperator2d>(
op: &SBP,
k: &mut Field,
y: &Field,
metrics: &Metrics,
boundaries: &BoundaryTerms,
) {
let ny = y.ny();
let nx = y.nx();
fn positive_flux(kx: Float, ky: Float) -> [[Float; 3]; 3] {
let r = (kx * kx + ky * ky).sqrt();
[
[ky * ky / r / 2.0, ky / 2.0, -kx * ky / r / 2.0],
[ky / 2.0, r / 2.0, -kx / 2.0],
[-kx * ky / r / 2.0, -kx / 2.0, kx * kx / r / 2.0],
]
}
fn negative_flux(kx: Float, ky: Float) -> [[Float; 3]; 3] {
let r = (kx * kx + ky * ky).sqrt();
[
[-ky * ky / r / 2.0, ky / 2.0, kx * ky / r / 2.0],
[ky / 2.0, -r / 2.0, -kx / 2.0],
[kx * ky / r / 2.0, -kx / 2.0, -kx * kx / r / 2.0],
]
}
{
let g = match boundaries.east {
Boundary::This => y.slice(s![.., .., 0]),
};
// East boundary
let hinv = if op.is_h2xi() {
(nx - 2) as Float / op.hxi()[0]
} else {
(nx - 1) as Float / op.hxi()[0]
};
for ((((mut k, v), g), &kx), &ky) in k
.slice_mut(s![.., .., nx - 1])
.gencolumns_mut()
.into_iter()
.zip(y.slice(s![.., .., nx - 1]).gencolumns())
.zip(g.gencolumns())
.zip(metrics.detj_dxi_dx.slice(s![.., nx - 1]))
.zip(metrics.detj_dxi_dy.slice(s![.., nx - 1]))
{
// East boundary, positive flux
let tau = -1.0;
let v = (v[0], v[1], v[2]);
let g = (g[0], g[1], g[2]);
let plus = positive_flux(kx, ky);
k[0] += tau
* hinv
* (plus[0][0] * (v.0 - g.0) + plus[0][1] * (v.1 - g.1) + plus[0][2] * (v.2 - g.2));
k[1] += tau
* hinv
* (plus[1][0] * (v.0 - g.0) + plus[1][1] * (v.1 - g.1) + plus[1][2] * (v.2 - g.2));
k[2] += tau
* hinv
* (plus[2][0] * (v.0 - g.0) + plus[2][1] * (v.1 - g.1) + plus[2][2] * (v.2 - g.2));
}
}
{
// West boundary, negative flux
let g = match boundaries.east {
Boundary::This => y.slice(s![.., .., nx - 1]),
};
let hinv = if op.is_h2xi() {
(nx - 2) as Float / op.hxi()[0]
} else {
(nx - 1) as Float / op.hxi()[0]
};
for ((((mut k, v), g), &kx), &ky) in k
.slice_mut(s![.., .., 0])
.gencolumns_mut()
.into_iter()
.zip(y.slice(s![.., .., 0]).gencolumns())
.zip(g.gencolumns())
.zip(metrics.detj_dxi_dx.slice(s![.., 0]))
.zip(metrics.detj_dxi_dy.slice(s![.., 0]))
{
let tau = 1.0;
let v = (v[0], v[1], v[2]);
let g = (g[0], g[1], g[2]);
let minus = negative_flux(kx, ky);
k[0] += tau
* hinv
* (minus[0][0] * (v.0 - g.0)
+ minus[0][1] * (v.1 - g.1)
+ minus[0][2] * (v.2 - g.2));
k[1] += tau
* hinv
* (minus[1][0] * (v.0 - g.0)
+ minus[1][1] * (v.1 - g.1)
+ minus[1][2] * (v.2 - g.2));
k[2] += tau
* hinv
* (minus[2][0] * (v.0 - g.0)
+ minus[2][1] * (v.1 - g.1)
+ minus[2][2] * (v.2 - g.2));
}
}
{
let g = match boundaries.north {
Boundary::This => y.slice(s![.., 0, ..]),
};
let hinv = if op.is_h2eta() {
(ny - 2) as Float / op.heta()[0]
} else {
(ny - 1) as Float / op.heta()[0]
};
for ((((mut k, v), g), &kx), &ky) in k
.slice_mut(s![.., ny - 1, ..])
.gencolumns_mut()
.into_iter()
.zip(y.slice(s![.., ny - 1, ..]).gencolumns())
.zip(g.gencolumns())
.zip(metrics.detj_deta_dx.slice(s![ny - 1, ..]))
.zip(metrics.detj_deta_dy.slice(s![ny - 1, ..]))
{
// North boundary, positive flux
let tau = -1.0;
let v = (v[0], v[1], v[2]);
let g = (g[0], g[1], g[2]);
let plus = positive_flux(kx, ky);
k[0] += tau
* hinv
* (plus[0][0] * (v.0 - g.0) + plus[0][1] * (v.1 - g.1) + plus[0][2] * (v.2 - g.2));
k[1] += tau
* hinv
* (plus[1][0] * (v.0 - g.0) + plus[1][1] * (v.1 - g.1) + plus[1][2] * (v.2 - g.2));
k[2] += tau
* hinv
* (plus[2][0] * (v.0 - g.0) + plus[2][1] * (v.1 - g.1) + plus[2][2] * (v.2 - g.2));
}
}
{
let g = match boundaries.south {
Boundary::This => y.slice(s![.., ny - 1, ..]),
};
let hinv = if op.is_h2eta() {
(ny - 2) as Float / op.heta()[0]
} else {
(ny - 1) as Float / op.heta()[0]
};
for ((((mut k, v), g), &kx), &ky) in k
.slice_mut(s![.., 0, ..])
.gencolumns_mut()
.into_iter()
.zip(y.slice(s![.., 0, ..]).gencolumns())
.zip(g.gencolumns())
.zip(metrics.detj_deta_dx.slice(s![0, ..]))
.zip(metrics.detj_deta_dy.slice(s![0, ..]))
{
// South boundary, negative flux
let tau = 1.0;
let v = (v[0], v[1], v[2]);
let g = (g[0], g[1], g[2]);
let minus = negative_flux(kx, ky);
k[0] += tau
* hinv
* (minus[0][0] * (v.0 - g.0)
+ minus[0][1] * (v.1 - g.1)
+ minus[0][2] * (v.2 - g.2));
k[1] += tau
* hinv
* (minus[1][0] * (v.0 - g.0)
+ minus[1][1] * (v.1 - g.1)
+ minus[1][2] * (v.2 - g.2));
k[2] += tau
* hinv
* (minus[2][0] * (v.0 - g.0)
+ minus[2][1] * (v.1 - g.1)
+ minus[2][2] * (v.2 - g.2));
}
}
}
#[derive(Clone, Debug)]
pub struct WorkBuffers {
k: [Field; 4],
tmp: (Array2<Float>, Array2<Float>, Array2<Float>, Array2<Float>),
}
impl WorkBuffers {
pub fn new(ny: usize, nx: usize) -> Self {
let arr2 = Array2::zeros((ny, nx));
let arr3 = Field::new(ny, nx);
Self {
k: [arr3.clone(), arr3.clone(), arr3.clone(), arr3],
tmp: (arr2.clone(), arr2.clone(), arr2.clone(), arr2),
}
}
}

View File

@@ -7,6 +7,33 @@ pub struct Direction<T> {
pub east: T,
}
impl<T> Direction<T> {
pub fn north(&self) -> &T {
&self.north
}
pub fn north_mut(&mut self) -> &mut T {
&mut self.north
}
pub fn south(&self) -> &T {
&self.south
}
pub fn south_mut(&mut self) -> &mut T {
&mut self.south
}
pub fn east(&self) -> &T {
&self.east
}
pub fn east_mut(&mut self) -> &mut T {
&mut self.east
}
pub fn west(&self) -> &T {
&self.west
}
pub fn west_mut(&mut self) -> &mut T {
&mut self.west
}
}
pub fn h2linspace(start: Float, end: Float, n: usize) -> ndarray::Array1<Float> {
let h = (end - start) / (n - 2) as Float;
ndarray::Array1::from_shape_fn(n, |i| match i {

View File

@@ -1,83 +0,0 @@
#![cfg(feature = "expensive_tests")]
use ndarray::prelude::*;
use sbp::euler::*;
use sbp::Float;
fn run_with_size(size: usize, op: impl sbp::operators::UpwindOperator2d + Copy) -> Float {
let nx = size;
let ny = size;
let x = Array1::linspace(-5.0, 5.0, nx);
let y = Array1::linspace(-5.0, 5.0, ny);
let x = x.broadcast((ny, nx)).unwrap().to_owned();
let y = y
.reversed_axes()
.broadcast((nx, ny))
.unwrap()
.reversed_axes()
.to_owned();
let vortex_params = VortexParameters {
vortices: {
let mut v = ArrayVec::new();
v.push(Vortice {
x0: -1.0,
y0: 0.0,
rstar: 0.5,
eps: 1.0,
});
v
},
mach: 0.5,
};
let mut sys = System::new(x, y, op);
sys.vortex(0.0, vortex_params.clone());
let time = 0.2;
let dt = 0.2 * Float::min(1.0 / (nx - 1) as Float, 1.0 / (ny - 1) as Float);
let nsteps = (time / dt) as usize;
for _ in 0..nsteps {
sys.advance_upwind(dt);
}
let mut verifield = Field::new(ny, nx);
verifield.vortex(sys.x(), sys.y(), nsteps as Float * dt, &vortex_params);
verifield.h2_err(sys.field(), &op)
}
fn convergence(op: impl sbp::operators::UpwindOperator2d + Copy) {
let sizes = [25, 35, 50, 71, 100, 150, 200];
let mut prev: Option<(usize, Float)> = None;
println!("Size\tError(h2)\tq");
for size in &sizes {
print!("{:3}x{:3}", size, size);
let e = run_with_size(*size, op);
print!("\t{:.10}", e);
if let Some(prev) = prev.take() {
let m0 = size * size;
let e0 = e;
let (size1, e1) = prev;
let m1 = size1 * size1;
let q =
Float::log10(e0 / e1) / Float::log10((m0 as Float / m1 as Float).powf(1.0 / 2.0));
print!("\t{}", q);
}
println!();
prev = Some((*size, e));
}
}
#[test]
fn convergence_upwind4() {
convergence(sbp::operators::Upwind4);
}
#[test]
fn convergence_upwind9() {
convergence(sbp::operators::Upwind9);
}