diff --git a/Cargo.toml b/Cargo.toml index 98f5478..dd18645 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ members = [ "euler", "maxwell", "shallow_water", + "gridgeneration", ] default-members = ["sbp", "euler", "maxwell", "shallow_water"] diff --git a/gridgeneration/Cargo.toml b/gridgeneration/Cargo.toml new file mode 100644 index 0000000..7d37d76 --- /dev/null +++ b/gridgeneration/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "gridgeneration" +version = "0.1.0" +authors = ["Magnus Ulimoen "] +edition = "2018" + +[dependencies] +ndarray = { version = "0.13.1", default-features = false } +plotters = { version = "0.3.0", default-features = false, features = ["svg_backend", "line_series", "point_series"] } +sbp = { path = "../sbp" } +json5 = { version = "0.2.8", optional = true } + +[features] +serde = ["sbp/serde1", "json5"] diff --git a/gridgeneration/src/lib.rs b/gridgeneration/src/lib.rs new file mode 100644 index 0000000..db01805 --- /dev/null +++ b/gridgeneration/src/lib.rs @@ -0,0 +1,125 @@ +use ndarray::Array2; + +pub use sbp::{consts::*, grid::Grid, Float}; + +pub fn unit(ny: usize, nx: usize) -> Grid { + let x = Array2::from_shape_fn((ny, nx), |(_j, i)| i as Float / (nx - 1) as Float); + let y = Array2::from_shape_fn((ny, nx), |(j, _i)| j as Float / (ny - 1) as Float); + Grid::new(x, y).unwrap() +} + +pub fn shell(ny: usize, nx: usize, mut inner: Float, mut outer: Float) -> Grid { + let mut unit = unit(ny, nx); + + #[allow(unused)] + enum Mode { + Log, + Exp, + Normal, + } + + let mode = Mode::Log; + match mode { + Mode::Exp => { + inner = inner.exp(); + outer = outer.exp(); + } + Mode::Log => { + inner = inner.ln(); + outer = outer.ln(); + } + _ => {} + } + + let (mut gx, mut gy) = unit.components(); + gx.iter_mut().zip(gy.iter_mut()).for_each(|(x, y)| { + let mut r = (*x) * (outer - inner) + inner; + match mode { + Mode::Exp => { + r = r.ln(); + } + Mode::Log => { + r = r.exp(); + } + _ => {} + } + let theta = *y * 2.0 * PI; + + *x = r * theta.cos(); + *y = r * theta.sin(); + }); + + unit +} + +pub fn vertical_interpolation(jlen: usize, north: &[Float], south: &[Float]) -> Grid { + let ilen = north.len(); + assert_eq!(north.len(), south.len()); + let mut grid = unit(jlen, ilen); + + for j in 0..jlen { + for i in 0..ilen { + let y = grid.y()[(j, i)]; + grid.y_mut()[(j, i)] = (north[i] - south[i]) * y + south[i]; + } + } + grid +} + +pub fn horizontal_interpolation(ilen: usize, east: &[Float], west: &[Float]) -> Grid { + let jlen = east.len(); + assert_eq!(east.len(), west.len()); + let mut grid = unit(jlen, ilen); + + for j in 0..jlen { + for i in 0..ilen { + let x = grid.x()[(j, i)]; + grid.x_mut()[(j, i)] = (east[j] - west[j]) * x + west[j]; + } + } + grid +} + +pub fn horizontal_interpolation_dual( + ilen: usize, + east: (&[Float], &[Float]), + west: (&[Float], &[Float]), +) -> Grid { + let jlen = east.0.len(); + assert_eq!(east.0.len(), east.1.len()); + assert_eq!(east.0.len(), west.0.len()); + assert_eq!(east.0.len(), west.1.len()); + let mut grid = unit(jlen, ilen); + + for j in 0..jlen { + for i in 0..ilen { + let x = grid.x()[(j, i)]; + grid.x_mut()[(j, i)] = (east.1[j] - west.1[j]) * x + west.1[j]; + grid.y_mut()[(j, i)] = (east.0[j] - west.0[j]) * x + west.0[j]; + } + } + grid +} + +/// [east] : fn([0, 1]) -> (y, x) +/// [west] : fn([0, 1]) -> (y, x) +pub fn horizontal_interpolation_dual2( + ilen: usize, + jlen: usize, + east: impl Fn(Float) -> (Float, Float), + west: impl Fn(Float) -> (Float, Float), +) -> Grid { + let mut grid = unit(jlen, ilen); + + for j in 0..jlen { + for i in 0..ilen { + let x = grid.x()[(j, i)]; + let y = grid.y()[(j, i)]; + let east = east(y); + let west = west(y); + grid.x_mut()[(j, i)] = (east.1 - west.1) * x + west.1; + grid.y_mut()[(j, i)] = (east.0 - west.0) * x + west.0; + } + } + grid +} diff --git a/gridgeneration/src/main.rs b/gridgeneration/src/main.rs new file mode 100644 index 0000000..888bdb8 --- /dev/null +++ b/gridgeneration/src/main.rs @@ -0,0 +1,145 @@ +#![allow(unused)] +use gridgeneration::*; +use plotters::prelude::*; + +fn main() { + // let grid = Grid::unit(10, 11); + // let grid = shell(30, 21, 0.1, 3.1); + let grid = { + let ybot = (0..100) + .map(|x| (x as Float / 30.0).sin()) + .collect::>(); + let ytop = (0..100) + .map(|x| 2.5 + (x as Float / 10.0).sin()) + .collect::>(); + // vertical_interpolation(10, &ytop, &ybot) + // horizontal_interpolation(10, &ytop, &ybot) + /* + horizontal_interpolation_dual( + 30, + (&[0.0, 0.0, 1.0, 3.0], &[0.0, 2.0, 4.0, 6.0]), + (&[3.0, 5.0, 6.0, 7.0], &[0.0, 2.0, 3.0, 10.0]), + ) + */ + /* + horizontal_interpolation_dual2( + 30, + 40, + move |t| ((t * 3.0).sin(), t + 0.2), + move |t| ((5.0 * t).sin() + 2.5, t), + ) + */ + horizontal_interpolation_dual2( + 30, + 40, + move |t| (2.0 * t * std::f64::consts::PI).sin_cos(), + move |t| { + let (y, x) = (2.0 * t * std::f64::consts::PI).sin_cos(); + (0.1 * y, 0.1 * x) + }, + ) + }; + draw_grid(&grid, "grid.svg"); + + let grid = horizontal_interpolation_dual2( + 200, + 200, + move |t| { + let ts = [0.05, 0.25, 0.75, 0.95]; + if t < ts[0] { + let t = t / (ts[0] - 0.0); + (t, 5.0) + } else if t < ts[1] { + let t = (t - ts[0]) / (ts[1] - ts[0]); + (1.0, 5.0 - 5.0 * t) + } else if t < ts[2] { + let t = (t - ts[1]) / (ts[2] - ts[1]); + (std::f64::consts::PI * (t + 0.5)).sin_cos() + } else if t < ts[3] { + let t = (t - ts[2]) / (ts[3] - ts[2]); + (-1.0, 5.0 * t) + } else { + let t = (t - ts[3]) / (1.0 - ts[3]); + (-1.0 + t, 5.0) + } + }, + move |t| { + let (y, x) = (2.0 * t * std::f64::consts::PI).sin_cos(); + (0.2 * y, 0.2 * x) + }, + ); + draw_grid(&grid, "ctype.svg"); + + #[cfg(feature = "serde")] + println!("{}", json5::to_string(&grid).unwrap()); +} + +fn linspace(range: std::ops::Range, steps: usize) -> Vec { + let start = range.start; + let end = range.end; + + let dx = (end - start) / (steps - 1) as Float; + + (0..steps).map(|i| i as Float * dx).collect() +} + +fn draw_grid(grid: &Grid, name: &str) { + let drawing_area = SVGBackend::new(name, (1000, 1000)).into_drawing_area(); + drawing_area.fill(&WHITE).unwrap(); + + let x_minmax = grid + .x() + .iter() + .fold((Float::INFINITY, -Float::INFINITY), |(min, max), &x| { + (min.min(x), max.max(x)) + }); + let y_minmax = grid + .y() + .iter() + .fold((Float::INFINITY, -Float::INFINITY), |(min, max), &y| { + (min.min(y), max.max(y)) + }); + + let mut chart = ChartBuilder::on(&drawing_area) + .x_label_area_size(40) + .y_label_area_size(40) + .build_cartesian_2d(x_minmax.0..x_minmax.1, y_minmax.0..y_minmax.1) + .unwrap(); + chart + .configure_mesh() + .x_desc("x") + .y_desc("y") + .disable_mesh() + .draw() + .unwrap(); + + let style = &BLACK.stroke_width(1); + for axes in &[ndarray::Axis(1), ndarray::Axis(0)] { + for (linex, liney) in grid.x().axis_iter(*axes).zip(grid.y().axis_iter(*axes)) { + chart + .draw_series(LineSeries::new( + linex.iter().zip(&liney).map(|(&x, &y)| (x, y)), + style.clone(), + )) + .unwrap(); + } + } + /* + let coord = |idx| (grid.x()[idx], grid.y()[idx]); + let zero = coord((0, 0)); + let up = coord((1, 0)); + let right = coord((0, 1)); + chart + .draw_series(PointSeries::of_element( + [zero].iter().copied(), + 5, + ShapeStyle::from(&RED).filled(), + &|coord, size, style| { + EmptyElement::at(coord) + + Circle::new((0, 0), size, style) + + Text::new("η", (1, 1), ("arial", 10)) + }, + )) + .unwrap(); + */ +}