Use serde for json configuration file

This commit is contained in:
Magnus Ulimoen 2020-09-03 21:30:28 +02:00
parent f90618be42
commit d0e6727ac3
6 changed files with 304 additions and 8 deletions

View File

@ -8,11 +8,13 @@ edition = "2018"
# Internal feature flag to gate the expensive tests # Internal feature flag to gate the expensive tests
# which should be run only in release builds # which should be run only in release builds
expensive_tests = [] expensive_tests = []
serde1 = ["serde", "arrayvec/serde"]
[dependencies] [dependencies]
ndarray = "0.13.1" ndarray = "0.13.1"
sbp = { path = "../sbp" } sbp = { path = "../sbp" }
arrayvec = "0.5.1" arrayvec = "0.5.1"
serde = { version = "1.0.115", default-features = false, optional = true, features = ["derive"] }
[dev-dependencies] [dev-dependencies]
criterion = "0.3.2" criterion = "0.3.2"

View File

@ -402,6 +402,7 @@ fn h2_diff() {
} }
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
pub struct Vortice { pub struct Vortice {
pub x0: Float, pub x0: Float,
pub y0: Float, pub y0: Float,
@ -409,7 +410,11 @@ pub struct Vortice {
pub eps: Float, pub eps: Float,
} }
#[cfg(feature = "serde1")]
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
pub struct VortexParameters { pub struct VortexParameters {
pub vortices: ArrayVec<[Vortice; 5]>, pub vortices: ArrayVec<[Vortice; 5]>,
pub mach: Float, pub mach: Float,

View File

@ -6,12 +6,15 @@ edition = "2018"
[dependencies] [dependencies]
sbp = { path = "../sbp", features = ["rayon"] } sbp = { path = "../sbp", features = ["rayon", "serde"] }
euler = { path = "../euler" } euler = { path = "../euler", features = ["serde1"] }
hdf5 = "0.7.0" hdf5 = "0.7.0"
rayon = "1.3.0" rayon = "1.3.0"
indicatif = "0.14.0" indicatif = "0.14.0"
structopt = "0.3.14" structopt = "0.3.14"
ndarray = "0.13.1" ndarray = { version = "0.13.1", features = ["serde"] }
json = "0.12.4" json = "0.12.4"
either = "1.5.3" either = "1.5.3"
serde = { version = "1.0.115", features = ["derive"] }
json5 = "0.2.8"
indexmap = { version = "1.5.2", features = ["serde-1"] }

View File

@ -1,10 +1,127 @@
use super::DiffOp; use super::DiffOp;
use either::*; use either::*;
use json::JsonValue; use json::JsonValue;
use sbp::grid::Grid;
use sbp::utils::h2linspace; use sbp::utils::h2linspace;
use sbp::Float; use sbp::Float;
use serde::{Deserialize, Serialize};
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum Operator {
Upwind4,
Upwind9,
Upwind4h2,
Upwind9h2,
Sbp4,
Sbp8,
}
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
pub struct Operators {
pub xi: Operator,
pub eta: Operator,
}
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
pub struct Linspace {
pub start: Float,
pub end: Float,
pub steps: usize,
#[serde(default)]
pub h2: bool,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum GridLike {
Linspace(Linspace),
Array(ArrayForm),
}
impl From<Linspace> for GridLike {
fn from(t: Linspace) -> Self {
Self::Linspace(t)
}
}
impl From<ArrayForm> for GridLike {
fn from(t: ArrayForm) -> Self {
Self::Array(t)
}
}
impl From<ndarray::Array1<Float>> for GridLike {
fn from(t: ndarray::Array1<Float>) -> Self {
Self::Array(t.into())
}
}
impl From<ndarray::Array2<Float>> for GridLike {
fn from(t: ndarray::Array2<Float>) -> Self {
Self::Array(t.into())
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum InterpolationOperator {
#[serde(rename = "4")]
Four,
#[serde(rename = "8")]
Eight,
#[serde(rename = "9")]
Nine,
#[serde(rename = "9h2")]
NineH2,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Interpolate {
operator: Option<InterpolationOperator>,
neighbour: String,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum BoundaryType {
This,
Interpolate(Interpolate),
Neighbour(String),
Vortex,
}
pub type BoundaryDescriptors = sbp::utils::Direction<Option<BoundaryType>>;
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct GridConfig {
pub operators: Option<Operators>,
pub x: Option<GridLike>,
pub y: Option<GridLike>,
pub boundary_conditions: Option<BoundaryDescriptors>,
}
type Grids = indexmap::IndexMap<String, GridConfig>;
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Configuration {
pub grids: Grids,
pub integration_time: Float,
pub vortex: euler::VortexParameters,
}
pub struct RuntimeConfiguration {
names: Vec<String>,
grids: Vec<sbp::grid::Grid>,
bc: Vec<euler::BoundaryCharacteristics>,
op: Vec<DiffOp>,
}
impl Configuration {
fn to_runtime(self) -> RuntimeConfiguration {
todo!()
}
}
pub fn json_to_grids( pub fn json_to_grids(
mut jsongrids: JsonValue, mut jsongrids: JsonValue,
vortexparams: euler::VortexParameters, vortexparams: euler::VortexParameters,
@ -191,8 +308,9 @@ pub fn json_to_grids(
(names, grids, bcs, operators) (names, grids, bcs, operators)
} }
#[derive(Debug)] #[derive(Clone, Debug, Serialize, Deserialize)]
enum ArrayForm { #[serde(untagged)]
pub enum ArrayForm {
/// Only know the one dimension, will broadcast to /// Only know the one dimension, will broadcast to
/// two dimensions once we know about both dims /// two dimensions once we know about both dims
Array1(ndarray::Array1<Float>), Array1(ndarray::Array1<Float>),
@ -200,6 +318,18 @@ enum ArrayForm {
Array2(ndarray::Array2<Float>), Array2(ndarray::Array2<Float>),
} }
impl From<ndarray::Array1<Float>> for ArrayForm {
fn from(t: ndarray::Array1<Float>) -> Self {
Self::Array1(t)
}
}
impl From<ndarray::Array2<Float>> for ArrayForm {
fn from(t: ndarray::Array2<Float>) -> Self {
Self::Array2(t)
}
}
/// Parsing json strings to some gridlike form /// Parsing json strings to some gridlike form
/// ///
/// Each grid should be an object with the descriptors on the form /// Each grid should be an object with the descriptors on the form
@ -217,7 +347,7 @@ enum ArrayForm {
/// Optional parameters: /// Optional parameters:
/// * name (for relating boundaries) /// * name (for relating boundaries)
/// * dir{e,w,n,s} (for boundary terms) /// * dir{e,w,n,s} (for boundary terms)
fn json2grid(x: JsonValue, y: JsonValue) -> Result<Grid, String> { fn json2grid(x: JsonValue, y: JsonValue) -> Result<sbp::grid::Grid, String> {
let to_array_form = |mut x: JsonValue| { let to_array_form = |mut x: JsonValue| {
if let Some(s) = x.take_string() { if let Some(s) = x.take_string() {
if let Some(s) = s.strip_prefix("linspace:") { if let Some(s) = s.strip_prefix("linspace:") {
@ -346,7 +476,7 @@ fn json2grid(x: JsonValue, y: JsonValue) -> Result<Grid, String> {
}; };
assert_eq!(x.shape(), y.shape()); assert_eq!(x.shape(), y.shape());
Ok(Grid::new(x, y).unwrap()) Ok(sbp::grid::Grid::new(x, y).unwrap())
} }
pub fn json_to_vortex(mut json: JsonValue) -> euler::VortexParameters { pub fn json_to_vortex(mut json: JsonValue) -> euler::VortexParameters {
@ -391,3 +521,154 @@ pub fn json_to_vortex(mut json: JsonValue) -> euler::VortexParameters {
euler::VortexParameters { vortices, mach } euler::VortexParameters { vortices, mach }
} }
#[test]
fn output_configuration() {
let mut grids = Grids::new();
grids.insert(
"default".to_string(),
GridConfig {
boundary_conditions: None,
x: None,
y: None,
operators: None,
},
);
grids.insert(
"operators1".to_string(),
GridConfig {
boundary_conditions: None,
x: None,
y: None,
operators: Some(Operators {
xi: Operator::Upwind4,
eta: Operator::Upwind9,
}),
},
);
grids.insert(
"operators2".to_string(),
GridConfig {
boundary_conditions: None,
x: None,
y: None,
operators: Some(Operators {
xi: Operator::Upwind4h2,
eta: Operator::Upwind9h2,
}),
},
);
grids.insert(
"operators3".to_string(),
GridConfig {
boundary_conditions: None,
x: None,
y: None,
operators: Some(Operators {
xi: Operator::Sbp4,
eta: Operator::Sbp8,
}),
},
);
grids.insert(
"linspaced".to_string(),
GridConfig {
boundary_conditions: None,
x: Some(
Linspace {
start: 0.0,
end: 1.0,
steps: 32,
h2: false,
}
.into(),
),
y: Some(
Linspace {
start: -1.0,
end: 1.0,
steps: 35,
h2: true,
}
.into(),
),
operators: None,
},
);
grids.insert(
"array1".to_string(),
GridConfig {
boundary_conditions: None,
x: Some(ndarray::arr1(&[1.0, 2.0, 3.0, 4.0]).into()),
y: Some(ndarray::arr1(&[-4.0, -3.0, -2.0, -1.0, 0.0]).into()),
operators: None,
},
);
grids.insert(
"array2".to_string(),
GridConfig {
boundary_conditions: None,
x: Some(ndarray::arr2(&[[1.0, 2.0, 3.0, 4.0], [2.0, 3.0, 4.0, 5.0]]).into()),
y: Some(ndarray::arr2(&[[0.0, 0.0, 0.0, 0.0], [1.0, 1.0, 1.0, 1.0]]).into()),
operators: None,
},
);
grids.insert(
"boundary_conditions".to_string(),
GridConfig {
boundary_conditions: Some(BoundaryDescriptors {
north: None,
south: Some(BoundaryType::This),
east: Some(BoundaryType::Neighbour("name_of_grid".to_string())),
west: Some(BoundaryType::Vortex),
}),
x: None,
y: None,
operators: None,
},
);
grids.insert(
"boundary_conditions_interpolation".to_string(),
GridConfig {
boundary_conditions: Some(BoundaryDescriptors {
north: Some(BoundaryType::Interpolate(Interpolate {
neighbour: "name_of_grid".to_string(),
operator: Some(InterpolationOperator::Four),
})),
south: Some(BoundaryType::Interpolate(Interpolate {
neighbour: "name_of_grid".to_string(),
operator: Some(InterpolationOperator::Nine),
})),
west: Some(BoundaryType::Interpolate(Interpolate {
neighbour: "name_of_grid".to_string(),
operator: Some(InterpolationOperator::Eight),
})),
east: Some(BoundaryType::Interpolate(Interpolate {
neighbour: "name_of_grid".to_string(),
operator: Some(InterpolationOperator::NineH2),
})),
}),
x: None,
y: None,
operators: None,
},
);
let configuration = Configuration {
grids,
integration_time: 2.0,
vortex: euler::VortexParameters {
mach: 0.5,
vortices: {
let mut arr = euler::ArrayVec::new();
arr.push(euler::Vortice {
eps: 1.0,
x0: -1.0,
y0: 0.0,
rstar: 0.5,
});
arr
},
},
};
println!("{}", json5::to_string(&configuration).unwrap());
}

View File

@ -11,6 +11,7 @@ packed_simd = "0.3.3"
rayon = { version = "1.3.0", optional = true } rayon = { version = "1.3.0", optional = true }
sprs = { version = "0.7.1", optional = true } sprs = { version = "0.7.1", optional = true }
num-traits = "0.2.11" num-traits = "0.2.11"
serde = { version = "1.0.115", optional = true, default-features = false, features = ["derive"] }
[features] [features]
# Use f32 as precision, default is f64 # Use f32 as precision, default is f64

View File

@ -8,7 +8,11 @@ pub use jacobi::*;
mod kronecker_product; mod kronecker_product;
#[cfg(feature = "sparse")] #[cfg(feature = "sparse")]
pub use kronecker_product::kronecker_product; pub use kronecker_product::kronecker_product;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug)]
pub struct Direction<T> { pub struct Direction<T> {
pub north: T, pub north: T,
pub south: T, pub south: T,