Skip to content

Commit

Permalink
editoast: units: more qualifications
Browse files Browse the repository at this point in the history
  • Loading branch information
Tristramg committed Dec 17, 2024
1 parent f2d640e commit 8007e3b
Show file tree
Hide file tree
Showing 18 changed files with 187 additions and 94 deletions.
1 change: 1 addition & 0 deletions editoast/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

41 changes: 40 additions & 1 deletion editoast/editoast_common/src/hash_rounded_float.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
use std::hash::{Hash, Hasher};
use uom::si::{
acceleration::meter_per_second_squared,
f64::{Acceleration, Length, Velocity},
f64::{
Acceleration, Force, Frequency, Length, LinearMassDensity, LinearNumberDensity, Mass,
MassRate, Velocity,
},
force::newton,
frequency::hertz,
length::meter,
linear_mass_density::kilogram_per_meter,
linear_number_density::per_meter,
mass::kilogram,
mass_rate::kilogram_per_second,
velocity::meter_per_second,
};

Expand Down Expand Up @@ -30,6 +39,36 @@ pub fn hash_velocity<const T: u8, H: Hasher>(value: &Velocity, state: &mut H) {
hash_float::<T, H>(&value.get::<meter_per_second>(), state);
}

/// Hash a force through a rounded integer value
/// `hash_force<3,_>` means that the value is rounded to the nearest thousandth
pub fn hash_force<const T: u8, H: Hasher>(value: &Force, state: &mut H) {
hash_float::<T, H>(&value.get::<newton>(), state);
}

pub fn hash_mass<const T: u8, H: Hasher>(value: &Mass, state: &mut H) {
hash_float::<T, H>(&value.get::<kilogram>(), state);
}

/// Hash a mass rate through a rounded integer value
/// `hash_mass_rate<3,_>` means that the value is rounded to the nearest thousandth
pub fn hash_mass_rate<const T: u8, H: Hasher>(value: &MassRate, state: &mut H) {
hash_float::<T, H>(&value.get::<kilogram_per_second>(), state);
}

pub fn hash_frequency<const T: u8, H: Hasher>(value: &Frequency, state: &mut H) {
hash_float::<T, H>(&value.get::<hertz>(), state);
}

pub fn hash_linear_mass_density<const T: u8, H: Hasher>(value: &LinearMassDensity, state: &mut H) {
hash_float::<T, H>(&value.get::<kilogram_per_meter>(), state);
}

pub fn hash_linear_number_density<const T: u8, H: Hasher>(
value: &LinearNumberDensity,
state: &mut H,
) {
hash_float::<T, H>(&value.get::<per_meter>(), state);
}
/// Hash a list of floats through a list of rounded integer value
/// `hash_float_slice<3,_>` means that the values are rounded to the nearest thousandth
pub fn hash_float_slice<const T: u8, H: Hasher>(value: &[f64], state: &mut H) {
Expand Down
6 changes: 6 additions & 0 deletions editoast/editoast_common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@ pub mod units;
pub use hash_rounded_float::hash_acceleration;
pub use hash_rounded_float::hash_float;
pub use hash_rounded_float::hash_float_slice;
pub use hash_rounded_float::hash_force;
pub use hash_rounded_float::hash_frequency;
pub use hash_rounded_float::hash_length;
pub use hash_rounded_float::hash_linear_mass_density;
pub use hash_rounded_float::hash_linear_number_density;
pub use hash_rounded_float::hash_mass;
pub use hash_rounded_float::hash_mass_rate;
pub use hash_rounded_float::hash_velocity;

schemas! {
Expand Down
30 changes: 29 additions & 1 deletion editoast/editoast_common/src/units.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,10 @@
//! ```
/// Re-export the Quantities that are used in OSRD
pub use uom::si::f64::{Acceleration, Length, Velocity};
pub use uom::si::f64::{
Acceleration, Force, Frequency, Length, LinearMassDensity, LinearNumberDensity, Mass, MassRate,
Velocity,
};

macro_rules! quantity_to_path {
(Length, $unit:ident) => {
Expand All @@ -47,6 +50,24 @@ macro_rules! quantity_to_path {
(Acceleration, $unit:ident) => {
uom::si::acceleration::$unit
};
(Mass, $unit:ident) => {
uom::si::mass::$unit
};
(Force, $unit:ident) => {
uom::si::force::$unit
};
(MassRate, $unit:ident) => {
uom::si::mass_rate::$unit
};
(Frequency, $unit:ident) => {
uom::si::frequency::$unit
};
(LinearMassDensity, $unit:ident) => {
uom::si::linear_mass_density::$unit
};
(LinearNumberDensity, $unit:ident) => {
uom::si::linear_number_density::$unit
};
}

macro_rules! define_unit {
Expand Down Expand Up @@ -104,8 +125,15 @@ macro_rules! define_unit {
};
}

// Any new value here must also be added in editoast_derive/src/annotate_units.rs
define_unit!(meter, Length);
define_unit!(millimeter, Length);
define_unit!(meter_per_second, Velocity);
define_unit!(kilometer_per_hour, Velocity);
define_unit!(meter_per_second_squared, Acceleration);
define_unit!(kilogram, Mass);
define_unit!(newton, Force);
define_unit!(kilogram_per_second, MassRate);
define_unit!(hertz, Frequency);
define_unit!(kilogram_per_meter, LinearMassDensity);
define_unit!(per_meter, LinearNumberDensity);
6 changes: 6 additions & 0 deletions editoast/editoast_derive/src/annotate_units.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,18 @@ use quote::quote;
use syn::{parse_quote, DeriveInput, LitStr};

pub fn get_abbreviation(value: String) -> Option<&'static str> {
// Any new value here must also be added in editoast_common/src/units.rs
match value.replace("::option", "").as_str() {
"meter" => Some("Length in m"),
"millimeter" => Some("Length in mm"),
"meter_per_second" => Some("Velocity in m·s⁻¹"),
"kilometer_per_hour" => Some("Velocity in km·h⁻¹"),
"meter_per_second_squared" => Some("Acceleration in m·s⁻²"),
"kilogram" => Some("Mass in kg"),
"newton" => Some("Force in N"),
"hertz" => Some("Viscosity friction in N/(m·s⁻1) = s⁻¹"),
"kilogram_per_meter" => Some("Aerodynamic drag in N/(m·s⁻1)² = kg·m⁻¹"),
"per_meter" => Some("Aerodynamic drag per kg in (N/kg)/(m/s)² = m⁻¹"),
_ => None,
}
}
Expand Down
1 change: 1 addition & 0 deletions editoast/editoast_schemas/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ edition.workspace = true
chrono.workspace = true
derivative.workspace = true
editoast_common.workspace = true
editoast_derive.workspace = true
enum-map.workspace = true
geojson.workspace = true
iso8601 = "0.6.1"
Expand Down
4 changes: 2 additions & 2 deletions editoast/editoast_schemas/src/rolling_stock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ pub struct RollingStock {
// under ETCS/ERTMS signaling system in m/s^2
pub const_gamma: f64,
pub inertia_coefficient: f64,
/// In kg
pub mass: f64,
#[serde(with = "kilogram")]
pub mass: Mass,
pub rolling_resistance: RollingResistance,
pub loading_gauge: LoadingGaugeType,
/// Mapping of power restriction code to power class
Expand Down
43 changes: 26 additions & 17 deletions editoast/editoast_schemas/src/rolling_stock/rolling_resistance.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use derivative::Derivative;
use editoast_common::units::*;
use serde::Deserialize;
use serde::Serialize;
use utoipa::ToSchema;
Expand All @@ -8,6 +9,7 @@ editoast_common::schemas! {
RollingResistancePerWeight,
}

#[editoast_derive::annotate_units]
#[derive(Clone, Debug, Default, PartialEq, Deserialize, Serialize, ToSchema, Derivative)]
#[derivative(Hash)]
#[serde(deny_unknown_fields)]
Expand All @@ -16,30 +18,37 @@ pub struct RollingResistance {
#[serde(rename = "type")]
pub rolling_resistance_type: String,
/// Solid friction in N
#[derivative(Hash(hash_with = "editoast_common::hash_float::<5,_>"))]
pub A: f64,
/// Viscosity friction in N/(m/s)
#[derivative(Hash(hash_with = "editoast_common::hash_float::<5,_>"))]
pub B: f64,
/// Aerodynamic drag in N/(m/s)²
#[derivative(Hash(hash_with = "editoast_common::hash_float::<5,_>"))]
pub C: f64,
#[derivative(Hash(hash_with = "editoast_common::hash_force::<5,_>"))]
#[serde(with = "newton")]
pub A: Force,
/// Viscosity friction in N/(m/s) - N = kg⋅m⋅s−2 => viscosity friction kg/s
#[derivative(Hash(hash_with = "editoast_common::hash_mass_rate::<5,_>"))]
#[serde(with = "kilogram_per_second")]
pub B: MassRate,
/// Aerodynamic drag in N/(m/s)² => kg·m⁻¹
#[derivative(Hash(hash_with = "editoast_common::hash_linear_mass_density::<5,_>"))]
#[serde(with = "kilogram_per_meter")]
pub C: LinearMassDensity,
}

#[editoast_derive::annotate_units]
#[derive(Clone, Debug, Default, PartialEq, Deserialize, Serialize, ToSchema, Derivative)]
#[derivative(Hash)]
#[serde(deny_unknown_fields)]
#[allow(non_snake_case)]
pub struct RollingResistancePerWeight {
#[serde(rename = "type")]
pub rolling_resistance_type: String,
/// Solid friction in N/kg
#[derivative(Hash(hash_with = "editoast_common::hash_float::<5,_>"))]
pub A: f64,
/// Viscosity friction in (N/kg)/(m/s)
#[derivative(Hash(hash_with = "editoast_common::hash_float::<5,_>"))]
pub B: f64,
/// Aerodynamic drag in (N/kg)/(m/s)²
#[derivative(Hash(hash_with = "editoast_common::hash_float::<5,_>"))]
pub C: f64,
/// Solid friction in N/kg -- N = kg⋅m⋅s⁻²
#[derivative(Hash(hash_with = "editoast_common::hash_acceleration::<5,_>"))]
#[serde(with = "meter_per_second_squared")]
pub A: Acceleration,
/// Viscosity friction in (N/kg)/(m/s) — N = kg⋅m⋅s−2 => 1/s
#[derivative(Hash(hash_with = "editoast_common::hash_frequency::<5,_>"))]
#[serde(with = "hertz")]
pub B: Frequency,
/// Aerodynamic drag per kg in (N/kg)/(m/s)² => m⁻¹
#[derivative(Hash(hash_with = "editoast_common::hash_linear_number_density::<5,_>"))]
#[serde(with = "per_meter")]
pub C: LinearNumberDensity,
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ pub struct TowedRollingStock {
pub name: String,
pub label: String,
pub railjson_version: String,
/// In kg
pub mass: f64,
#[serde(with = "kilogram")]
pub mass: Mass,
#[serde(with = "meter")]
pub length: Length,
#[serde(with = "meter_per_second_squared")]
Expand All @@ -19,6 +19,6 @@ pub struct TowedRollingStock {
/// The constant gamma braking coefficient used when NOT circulating
/// under ETCS/ERTMS signaling system in m/s^2
pub const_gamma: f64,
#[serde(with = "meter_per_second::option")]
#[serde(default, with = "meter_per_second::option")]
pub max_speed: Option<Velocity>,
}
49 changes: 23 additions & 26 deletions editoast/src/core/simulation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ pub struct PhysicsConsist {
#[serde(with = "meter_per_second")]
pub max_speed: Velocity,
// Time in ms
pub startup_time: u64,
pub startup_time: u64, //TODOUOM
#[derivative(Hash(hash_with = "editoast_common::hash_acceleration::<5,_>"))]
#[serde(with = "meter_per_second_squared")]
pub startup_acceleration: Acceleration,
Expand All @@ -61,8 +61,10 @@ pub struct PhysicsConsist {
pub const_gamma: f64,
#[derivative(Hash(hash_with = "editoast_common::hash_float::<5,_>"))]
pub inertia_coefficient: f64,
/// Mass of the rolling stock in kg
pub mass: u64,
/// Mass of the rolling stock
#[derivative(Hash(hash_with = "editoast_common::hash_mass::<5,_>"))]
#[serde(with = "kilogram")]
pub mass: Mass,
pub rolling_resistance: RollingResistance,
/// Mapping of power restriction code to power class
#[serde(default)]
Expand All @@ -77,11 +79,8 @@ pub struct PhysicsConsist {

#[derive(Debug, Clone)]
pub struct PhysicsConsistParameters {
/// In kg
pub total_mass: Option<f64>,
/// In m
pub total_mass: Option<Mass>,
pub total_length: Option<Length>,
/// In m/s
pub max_speed: Option<Velocity>,
pub towed_rolling_stock: Option<TowedRollingStock>,
pub traction_engine: RollingStock,
Expand Down Expand Up @@ -156,23 +155,21 @@ impl PhysicsConsistParameters {
let traction_engine_inertia =
self.traction_engine.mass * self.traction_engine.inertia_coefficient;
let towed_inertia = towed_mass * towed_rolling_stock.inertia_coefficient;
(traction_engine_inertia + towed_inertia) / total_mass
((traction_engine_inertia + towed_inertia) / total_mass).get::<uom::si::ratio::ratio>()
} else {
self.traction_engine.inertia_coefficient
}
}

pub fn compute_mass(&self) -> u64 {
pub fn compute_mass(&self) -> Mass {
let traction_engine_mass = self.traction_engine.mass;
let towed_rolling_stock_mass = self
.towed_rolling_stock
.as_ref()
.map(|trs| trs.mass)
.unwrap_or(0.0);
let mass = self
.total_mass
.unwrap_or(traction_engine_mass + towed_rolling_stock_mass);
mass.round() as u64
.unwrap_or_default();
self.total_mass
.unwrap_or(traction_engine_mass + towed_rolling_stock_mass)
}

pub fn compute_rolling_resistance(&self) -> RollingResistance {
Expand Down Expand Up @@ -520,7 +517,7 @@ mod tests {
fn create_physics_consist() -> PhysicsConsistParameters {
PhysicsConsistParameters {
total_length: Some(meter::new(100.0)),
total_mass: Some(50000.0),
total_mass: Some(kilogram::new(50000.0)),
max_speed: Some(meter_per_second::new(22.0)),
towed_rolling_stock: Some(create_towed_rolling_stock()),
traction_engine: create_simple_rolling_stock(),
Expand Down Expand Up @@ -549,20 +546,20 @@ mod tests {
#[test]
fn physics_consist_compute_mass() {
let mut physics_consist = create_physics_consist();
physics_consist.total_mass = Some(50000.0); // kg
physics_consist.traction_engine.mass = 15000.0; // kg
physics_consist.total_mass = Some(kilogram::new(50000.0));
physics_consist.traction_engine.mass = kilogram::new(15000.0);

// We always take total_mass
assert_eq!(physics_consist.compute_mass(), 50000);
assert_eq!(physics_consist.compute_mass(), kilogram::new(50000.));

physics_consist.total_mass = None;
// When no total_mass we take towed mass + traction_engine mass
assert_eq!(physics_consist.compute_mass(), 65000);
assert_eq!(physics_consist.compute_mass(), kilogram::new(65000.));

physics_consist.total_mass = None;
physics_consist.towed_rolling_stock = None;
// When no user specified mass and towed rolling stock, we take traction_engine mass
assert_eq!(physics_consist.compute_mass(), 15000);
assert_eq!(physics_consist.compute_mass(), kilogram::new(15000.));
}

#[test]
Expand Down Expand Up @@ -592,16 +589,16 @@ mod tests {
meter_per_second::new(24.0)
);

physics_consist.traction_engine.max_speed = Velocity::new::<meter_per_second>(40.0); // m/s
physics_consist.traction_engine.max_speed = meter_per_second::new(40.0); // m/s
assert_eq!(
physics_consist.compute_max_speed(),
Velocity::new::<meter_per_second>(35.0)
meter_per_second::new(35.0)
);

physics_consist.towed_rolling_stock = None;
assert_eq!(
physics_consist.compute_max_speed(),
Velocity::new::<meter_per_second>(40.0)
meter_per_second::new(40.0)
);
}

Expand Down Expand Up @@ -657,9 +654,9 @@ mod tests {
physics_consist.compute_rolling_resistance(),
RollingResistance {
rolling_resistance_type: "davis".to_string(),
A: 35001.0,
B: 350.01,
C: 7.0005,
A: newton::new(35001.0),
B: kilogram_per_second::new(350.01),
C: kilogram_per_meter::new(7.0005),
}
);

Expand Down
Loading

0 comments on commit 8007e3b

Please sign in to comment.