use crate::authoring::*;
fn common(
op: &Op,
ctx: &dyn Context,
operands: &mut dyn CoordinateSet,
direction: Direction,
) -> usize {
let grids = &op.params.grids;
let mut successes = 0_usize;
let n = operands.len();
let dt = op.params.real("dt").unwrap();
let epoch = op.params.real("t_epoch").unwrap();
let ellps = op.params.ellps(0);
let raw = op.params.boolean("raw");
let use_null_grid = op.params.boolean("null_grid");
let direction = if direction == Fwd { -1f64 } else { 1f64 };
for i in 0..n {
let cart = operands.get_coord(i);
let geo = ellps.geographic(&cart);
if let Some(v) = grids_at(Some(ctx), grids, geo, use_null_grid) {
let d = if dt.is_finite() { -dt } else { epoch - geo[3] };
let v = v.scale(direction * 0.001);
let deformation = rotate_and_integrate_velocity(v, geo[0], geo[1], d);
if raw {
let mut deformation_with_length = deformation;
deformation_with_length[3] = deformation.dot(deformation).sqrt();
operands.set_coord(i, &deformation_with_length);
} else {
operands.set_coord(i, &(cart + deformation));
}
successes += 1;
continue;
}
operands.set_coord(i, &Coor4D::nan());
}
successes
}
fn fwd(op: &Op, ctx: &dyn Context, operands: &mut dyn CoordinateSet) -> usize {
common(op, ctx, operands, Fwd)
}
fn inv(op: &Op, ctx: &dyn Context, operands: &mut dyn CoordinateSet) -> usize {
common(op, ctx, operands, Inv)
}
#[rustfmt::skip]
pub const GAMUT: [OpParameter; 7] = [
OpParameter::Flag { key: "inv" },
OpParameter::Flag { key: "raw" },
OpParameter::Texts { key: "grids", default: None },
OpParameter::Real { key: "padding", default: Some(0.5) },
OpParameter::Real { key: "dt", default: Some(f64::NAN) },
OpParameter::Real { key: "t_epoch", default: Some(f64::NAN) },
OpParameter::Text { key: "ellps", default: Some("GRS80") },
];
pub fn new(parameters: &RawParameters, ctx: &dyn Context) -> Result<Op, Error> {
let def = ¶meters.instantiated_as;
let mut params = ParsedParameters::new(parameters, &GAMUT)?;
if params.real("dt")?.is_nan() && params.real("t_epoch")?.is_nan() {
return Err(Error::MissingParam(
"- either t_epoch or dt must be given".to_string(),
));
}
for mut grid_name in params.texts("grids")?.clone() {
let optional = grid_name.starts_with('@');
if optional {
grid_name = grid_name.trim_start_matches('@').to_string();
}
if grid_name == "null" {
params.boolean.insert("null_grid");
break; }
match ctx.get_grid(&grid_name) {
Ok(grid) => {
let n = grid.bands();
if n != 3 {
return Err(Error::Unexpected {
message: "Bad dimensionality of deformation model grid".to_string(),
expected: "3".to_string(),
found: n.to_string(),
});
}
params.grids.push(grid);
}
Err(e) => {
if !optional {
return Err(e);
}
}
}
}
let fwd = InnerOp(fwd);
let inv = InnerOp(inv);
let descriptor = OpDescriptor::new(def, fwd, Some(inv));
Ok(Op {
descriptor,
params,
steps: None,
})
}
#[inline]
fn rotate_and_integrate_velocity(
v: Coor4D,
longitude: f64,
latitude: f64,
duration: f64,
) -> Coor4D {
let (slon, clon) = longitude.sin_cos();
let (slat, clat) = latitude.sin_cos();
Coor4D([
duration * (-slat * clon * v[1] - slon * v[0] + clat * clon * v[2]),
duration * (-slat * slon * v[1] + clon * v[0] + clat * slon * v[2]),
duration * (clat * v[1] + slat * v[2]),
0.0,
])
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn deformation() -> Result<(), Error> {
let mut ctx = Plain::new();
let cph = Coor4D::geo(55., 12., 0., 0.);
let test_deformation = include_str!("../../geodesy/deformation/test.deformation");
let another_test_deformation =
include_str!("../../geodesy/deformation/another_test.deformation");
ctx.register_resource("test.deformation", test_deformation);
ctx.register_resource("another_test.deformation", another_test_deformation);
let buf = ctx.get_blob("test.deformation")?;
let grid = crate::grid::gravsoft::gravsoft("test_deformation", &buf)?;
assert_eq!(grid.name, "test_deformation");
assert_eq!(grid.subgrids.len(), 0);
assert_eq!(grid.header.bands, 3);
let v = grid.at(None, cph, 0.0).unwrap().scale(0.001);
let deformation = rotate_and_integrate_velocity(v, cph[0], cph[1], 1000.);
let expected_length_of_correction = (55f64 * 55. + 12. * 12.).sqrt();
let length_of_scaled_velocity = v.scale(1000.0).dot(v.scale(1000.0)).sqrt();
let length_of_rotated_deformation = deformation.dot(deformation).sqrt();
assert!((length_of_scaled_velocity - expected_length_of_correction).abs() < 1e-6);
assert!((length_of_rotated_deformation - expected_length_of_correction).abs() < 1e-6);
let op = ctx.op("deformation dt=1000 grids=test.deformation")?;
let ellps = Ellipsoid::default();
let cph = ellps.cartesian(&cph);
let mut data = [cph];
ctx.apply(op, Fwd, &mut data)?;
let diff = data[0] - cph;
let length_of_diff = diff.dot(diff).sqrt();
assert!((length_of_diff - expected_length_of_correction).abs() < 1e-6);
let mut data = [cph];
ctx.apply(op, Inv, &mut data)?;
let diff = data[0] - cph;
let length_of_diff = diff.dot(diff).sqrt();
assert!((length_of_diff - expected_length_of_correction).abs() < 1e-6);
let mut data = [cph];
ctx.apply(op, Fwd, &mut data)?;
ctx.apply(op, Inv, &mut data)?;
assert!(cph.hypot3(&data[0]) < 2e-3);
let op =
ctx.op("deformation raw dt=1000 grids=@another_test.deformation,test.deformation")?;
let mut data = [cph];
ctx.apply(op, Fwd, &mut data)?;
let fwd = data[0];
assert!((fwd[3] - expected_length_of_correction) < 0.001);
let mut data = [cph];
ctx.apply(op, Inv, &mut data)?;
let inv = data[0];
assert!((inv[3] - expected_length_of_correction) < 0.001);
assert!((inv[3] - fwd[3]) < 0.001);
let tio = Coor4D::geo(65.85, 24.10, 0., 0.);
let tio = ellps.cartesian(&tio);
let mut data = [tio];
ctx.apply(op, Fwd, &mut data)?;
let fwd = data[0];
assert!(fwd[0].is_finite());
let lyb = Coor4D::geo(78.25, 15.5, 0., 0.);
let lyb = ellps.cartesian(&lyb);
let mut data = [lyb];
ctx.apply(op, Fwd, &mut data)?;
assert!(data[0][0].is_nan());
let lul = Coor4D::geo(66., 23., 0., 0.);
let lul_cart = ellps.cartesian(&lul);
let g = ctx.get_grid("nkgrf17vel")?;
let gg = ctx.get_grid("nkgrf17vel.deformation")?;
let val_unig = g.at(Some(&ctx), lul, 0.0);
let val_grav = gg.at(Some(&ctx), lul, 0.0);
assert_eq!(val_grav, val_unig);
let grav = ctx.op("deformation t_epoch=2000 dt=1000 grids=nkgrf17vel.deformation")?;
let unig = ctx.op("deformation dt=1000 grids=nkgrf17vel")?;
let mut gravdata = [lul_cart];
let mut unigdata = [lul_cart];
ctx.apply(grav, Fwd, &mut gravdata)?;
ctx.apply(unig, Fwd, &mut unigdata)?;
assert_eq!(gravdata[0], unigdata[0]);
ctx.apply(grav, Inv, &mut gravdata)?;
ctx.apply(unig, Inv, &mut unigdata)?;
assert_eq!(gravdata[0], unigdata[0]);
assert!(gravdata[0].hypot3(&lul_cart) < 1e-4);
Ok(())
}
}