diff --git a/src/properties/mod.rs b/src/properties/mod.rs index fee4f45..5f3105a 100644 --- a/src/properties/mod.rs +++ b/src/properties/mod.rs @@ -1,6 +1,9 @@ mod data; -mod thermo_db; mod error; -mod polynomials; +mod thermo_db; +mod thermo_fit; +mod transport_db; +mod transport_fit; +mod utils; pub use error::PropertiesError; diff --git a/src/properties/test_thermo.inp b/src/properties/test_thermo.inp new file mode 100644 index 0000000..6afb0b9 --- /dev/null +++ b/src/properties/test_thermo.inp @@ -0,0 +1,29 @@ +thermo + 200.00 1000.00 6000.00 20000. 1/1/2024 +! Simple test thermo file with H2, O2, and H2O +H2 Hydrogen gas + 2 test H 2.00 0.00 0.00 0.00 0.00 0 2.01588000 0.000 + 200.000 1000.0007 -2.0 -1.0 0.0 1.0 2.0 3.0 4.0 0.0 8468.102 + 4.078323210D+04-8.009186040D+02 8.214701670D+00-1.269714457D-02 1.753605076D-05 +-1.202860270D-08 3.368093490D-12 2.682484665D+03-3.043788844D+01 + 1000.000 6000.0007 -2.0 -1.0 0.0 1.0 2.0 3.0 4.0 0.0 8468.102 + 5.608128010D+05-8.371504740D+02 2.975364532D+00 1.252249124D-03-3.740716190D-07 + 5.936625200D-11-3.606994100D-15 5.339824410D+03-2.202774769D+00 +O2 Oxygen gas + 2 test O 2.00 0.00 0.00 0.00 0.00 0 31.99880000 0.000 + 200.000 1000.0007 -2.0 -1.0 0.0 1.0 2.0 3.0 4.0 0.0 8680.104 +-3.425563420D+04 4.847000970D+02 1.119010961D+00 4.293889240D-03-6.836300520D-07 +-2.023372700D-09 1.039040018D-12 -3.391454870D+03 1.849699470D+01 + 1000.000 6000.0007 -2.0 -1.0 0.0 1.0 2.0 3.0 4.0 0.0 8680.104 +-1.037939022D+06 2.344830282D+03 1.819732036D+00 1.267847582D-03-2.188067988D-07 + 2.053719572D-11-8.193467050D-16 -1.689010929D+04 1.738716506D+01 +H2O Water vapor + 2 test H 2.00 O 1.00 0.00 0.00 0.00 0 18.01528000 -241826.000 + 200.000 1000.0007 -2.0 -1.0 0.0 1.0 2.0 3.0 4.0 0.0 9904.092 +-3.947960830D+04 5.755731020D+02 9.317826530D-01 7.222712860D-03-7.342557370D-06 + 4.955043490D-09-1.336933246D-12 -3.303974310D+04 1.724205775D+01 + 1000.000 6000.0007 -2.0 -1.0 0.0 1.0 2.0 3.0 4.0 0.0 9904.092 + 1.034972096D+06-2.412698562D+03 4.646110780D+00 2.291998307D-03-6.836830480D-07 + 9.426468930D-11-4.822380530D-15 -1.384286509D+04-7.978148510D+00 +END PRODUCTS +END REACTANTS diff --git a/src/properties/thermo_db.rs b/src/properties/thermo_db.rs index 587ead0..09e66b8 100644 --- a/src/properties/thermo_db.rs +++ b/src/properties/thermo_db.rs @@ -1,27 +1,14 @@ use crate::properties::{ PropertiesError, error::make_parse_error, - polynomials::{Phase, Polynomial, SpeciesElement, SpeciesPolynomial}, + thermo_fit::{Phase, SpeciesElement, SpeciesThermoData, ThermoPolynomial}, + utils::parse_fields, }; -fn parse_fields(line: &str, widths: &[usize]) -> Vec { - let mut fields = Vec::new(); - let mut pos = 0; - - for &width in widths { - if let Some(field) = line.get(pos..pos + width) { - // The replace chnages the fortran formatted D exponential for the normal E exponential - fields.push(field.trim().replace("D", "E")); - } - pos += width; - } - - fields -} pub struct ThermoDB { - pub products: Vec, - pub reactants: Vec, + pub products: Vec, + pub reactants: Vec, } /// Parse a thermo formatted db @@ -59,7 +46,7 @@ impl ThermoDB { fn parse_species<'a>( line: &str, lines: &mut impl Iterator, -) -> Result { +) -> Result { // Parsing a fortran generated file which means we used fixed column width parsing. Define the // fixed column widths used const SPECIES_LINE_2_WIDTHS: &[usize] = &[3, 7, 2, 6, 2, 6, 2, 6, 2, 6, 2, 6, 2, 13, 15]; @@ -112,7 +99,7 @@ fn parse_species<'a>( lines.next().ok_or(PropertiesError::InvalidFile)?; } - Ok(SpeciesPolynomial { + Ok(SpeciesThermoData { name, polynomials, elements, @@ -125,7 +112,7 @@ fn parse_species<'a>( fn parse_polynomials_block<'a>( lines: &mut impl Iterator, intervals: usize, -) -> Result, PropertiesError> { +) -> Result, PropertiesError> { // Now parse the actual polynomial intervals (0..intervals) .map(|_| parse_polynomial_block(lines)) @@ -134,7 +121,7 @@ fn parse_polynomials_block<'a>( fn parse_polynomial_block<'a>( lines: &mut impl Iterator, -) -> Result { +) -> Result { // Ignore the coefficients since they are the same const SPECIES_INTERVAL_1_WIDTHS: &[usize] = &[11, 11]; const SPECIES_INTERVAL_2_WIDTHS: &[usize] = &[16; 5]; @@ -169,7 +156,7 @@ fn parse_polynomial_block<'a>( ); } - Ok(Polynomial { + Ok(ThermoPolynomial { a, temp_range: (temp_lo, temp_hi), }) @@ -180,8 +167,8 @@ mod test { use crate::{ assert_delta, assert_vec_delta, properties::{ - polynomials::Phase, thermo_db::{ThermoDB, parse_polynomial_block, parse_polynomials_block, parse_species}, + thermo_fit::Phase, }, }; diff --git a/src/properties/polynomials.rs b/src/properties/thermo_fit.rs similarity index 75% rename from src/properties/polynomials.rs rename to src/properties/thermo_fit.rs index 0bfdccd..324762b 100644 --- a/src/properties/polynomials.rs +++ b/src/properties/thermo_fit.rs @@ -3,15 +3,15 @@ pub enum Phase { Condensed, } -pub struct SpeciesPolynomial { +pub struct SpeciesThermoData { pub name: String, pub elements: Vec, pub phase: Phase, - pub polynomials: Vec, + pub polynomials: Vec, pub molecular_weight: f64, pub h_formation: f64, } -pub struct Polynomial { +pub struct ThermoPolynomial { pub a: Vec, pub temp_range: (f64, f64), } diff --git a/src/properties/transport_db.rs b/src/properties/transport_db.rs new file mode 100644 index 0000000..d9f47d8 --- /dev/null +++ b/src/properties/transport_db.rs @@ -0,0 +1,207 @@ +use crate::properties::{ + PropertiesError, + error::make_parse_error, + transport_fit::{SpeciesTransportData, TransportFit}, + utils::parse_fields, +}; + +enum ViscosityOrConductivity { + Viscosity, + Conductivity, +} + +pub struct TransportDB { + data: Vec, +} + +impl TransportDB { + pub fn parse() {} +} + +fn parse_species_transport_block<'a>( + mut lines: impl Iterator, +) -> Result { + let line = lines.next().ok_or(PropertiesError::InvalidFile)?; + let mut viscosities = vec![]; + let mut conductivities = vec![]; + let (name, viscosity_count, conductivity_count) = parse_species_header_line(line)?; + for _ in 0..viscosity_count + conductivity_count { + let line = lines.next().ok_or(PropertiesError::InvalidFile)?; + match parse_coefficients(line)? { + (ViscosityOrConductivity::Viscosity, fit) => viscosities.push(fit), + (ViscosityOrConductivity::Conductivity, fit) => conductivities.push(fit), + } + } + Ok(SpeciesTransportData { + name, + viscosities, + conductivities, + }) +} + +fn parse_species_header_line(line: &str) -> Result<(String, usize, usize), PropertiesError> { + const HEADER_WIDTHS: &[usize] = &[34, 1, 1, 1, 1]; + + let splits = parse_fields(line, HEADER_WIDTHS); + + let name = splits[0].to_string(); + let viscosity_count: usize = splits[2] + .parse() + .map_err(|_| make_parse_error("V", "usize", &splits[2]))?; + + let conductivity_count: usize = splits[4] + .parse() + .map_err(|_| make_parse_error("C", "usize", &splits[2]))?; + Ok((name, viscosity_count, conductivity_count)) +} + +fn parse_coefficients( + line: &str, +) -> Result<(ViscosityOrConductivity, TransportFit), PropertiesError> { + const COEFFICIENTS_WIDTH: &[usize] = &[2, 7, 11, 15, 15, 15, 15]; + let splits = parse_fields(line, COEFFICIENTS_WIDTH); + let transport_type = if splits[0].contains("V") { + ViscosityOrConductivity::Viscosity + } else { + ViscosityOrConductivity::Conductivity + }; + Ok(( + transport_type, + TransportFit { + temp_range: ( + splits[1] + .parse() + .map_err(|_| make_parse_error("tlo", "f64", &splits[1]))?, + splits[2] + .parse() + .map_err(|_| make_parse_error("tlo", "f64", &splits[2]))?, + ), + a: splits[3] + .parse() + .map_err(|_| make_parse_error("a", "f64", &splits[3]))?, + b: splits[4] + .parse() + .map_err(|_| make_parse_error("a", "f64", &splits[4]))?, + c: splits[5] + .parse() + .map_err(|_| make_parse_error("a", "f64", &splits[5]))?, + + d: splits[6] + .parse() + .map_err(|_| make_parse_error("a", "f64", &splits[6]))?, + }, + )) +} +#[cfg(test)] +pub mod test { + use crate::{ + assert_delta, + properties::transport_db::{ + ViscosityOrConductivity, parse_coefficients, parse_species_header_line, + parse_species_transport_block, + }, + }; + + #[test] + fn test_parse_species_transport_header() { + let line = "Ar V3C1 BICH ET AL (1990)"; + + let (name, viscosity_count, conductivity_count) = parse_species_header_line(line).unwrap(); + + assert_eq!(name, "Ar"); + assert_eq!(viscosity_count, 3); + assert_eq!(conductivity_count, 1); + } + + #[test] + fn test_parse_coefficients() { + let line = + " V 200.0 1000.0 0.61205763E 00-0.67714354E 02 0.19040660E 03 0.21588272E 01"; + let (transport_type, fit) = parse_coefficients(line).unwrap(); + + assert!(matches!(transport_type, ViscosityOrConductivity::Viscosity)); + assert_delta!(fit.temp_range.0, 200.0, 1e-1); + assert_delta!(fit.temp_range.1, 1000.0, 1e-1); + assert_delta!(fit.a, 0.61205763e00, 1e-8); + assert_delta!(fit.b, -0.67714354E02, 1e-8); + assert_delta!(fit.c, 0.19040660e03, 1e-8); + assert_delta!(fit.d, 0.21588272E01, 1e-8); + + let line = + " C 5000.0 15000.0 0.76269502E+00 0.62341752E+03-0.71899552E+06 0.56927918E+00"; + + let (transport_type, fit) = parse_coefficients(line).unwrap(); + assert!(matches!( + transport_type, + ViscosityOrConductivity::Conductivity + )); + assert_delta!(fit.temp_range.0, 5000.0, 1e-1); + assert_delta!(fit.temp_range.1, 15000.0, 1e-1); + assert_delta!(fit.a, 0.76269502E+00, 1e-8); + assert_delta!(fit.b, 0.62341752E+03, 1e-8); + assert_delta!(fit.c, -0.71899552E+06, 1e-8); + assert_delta!(fit.d, 0.56927918E+00, 1e-8); + } + + #[test] + fn test_parse_transport_species_block() { + let species_block = r#"Ar V3C3 BICH ET AL (1990) + V 200.0 1000.0 0.61205763E 00-0.67714354E 02 0.19040660E 03 0.21588272E 01 + V 1000.0 5000.0 0.69357334E 00 0.70953943E 02-0.28386007E 05 0.14856447E 01 + V 5000.0 15000.0 0.76608935E+00 0.67867215E+03-0.84991417E+06 0.77935167E+00 + C 200.0 1000.0 0.60968928E 00-0.70892249E 02 0.58420624E 03 0.19337152E 01 + C 1000.0 5000.0 0.69075463E 00 0.62676058E 02-0.25667413E 05 0.12664189E 01 + C 5000.0 15000.0 0.76269502E+00 0.62341752E+03-0.71899552E+06 0.56927918E+00"#; + + let lines = species_block.lines(); + let species_data = parse_species_transport_block(lines).unwrap(); + + assert_eq!(species_data.name, "Ar"); + assert_eq!(species_data.viscosities.len(), 3); + assert_eq!(species_data.conductivities.len(), 3); + + // Viscosity fits + assert_delta!(species_data.viscosities[0].temp_range.0, 200.0, 1e-1); + assert_delta!(species_data.viscosities[0].temp_range.1, 1000.0, 1e-1); + assert_delta!(species_data.viscosities[0].a, 0.61205763e00, 1e-8); + assert_delta!(species_data.viscosities[0].b, -0.67714354e02, 1e-8); + assert_delta!(species_data.viscosities[0].c, 0.19040660e03, 1e-8); + assert_delta!(species_data.viscosities[0].d, 0.21588272e01, 1e-8); + + assert_delta!(species_data.viscosities[1].temp_range.0, 1000.0, 1e-1); + assert_delta!(species_data.viscosities[1].temp_range.1, 5000.0, 1e-1); + assert_delta!(species_data.viscosities[1].a, 0.69357334e00, 1e-8); + assert_delta!(species_data.viscosities[1].b, 0.70953943e02, 1e-8); + assert_delta!(species_data.viscosities[1].c, -0.28386007e05, 1e-8); + assert_delta!(species_data.viscosities[1].d, 0.14856447e01, 1e-8); + + assert_delta!(species_data.viscosities[2].temp_range.0, 5000.0, 1e-1); + assert_delta!(species_data.viscosities[2].temp_range.1, 15000.0, 1e-1); + assert_delta!(species_data.viscosities[2].a, 0.76608935e00, 1e-8); + assert_delta!(species_data.viscosities[2].b, 0.67867215e03, 1e-8); + assert_delta!(species_data.viscosities[2].c, -0.84991417e06, 1e-8); + assert_delta!(species_data.viscosities[2].d, 0.77935167e00, 1e-8); + + // Conductivity fits + assert_delta!(species_data.conductivities[0].temp_range.0, 200.0, 1e-1); + assert_delta!(species_data.conductivities[0].temp_range.1, 1000.0, 1e-1); + assert_delta!(species_data.conductivities[0].a, 0.60968928e00, 1e-8); + assert_delta!(species_data.conductivities[0].b, -0.70892249e02, 1e-8); + assert_delta!(species_data.conductivities[0].c, 0.58420624e03, 1e-8); + assert_delta!(species_data.conductivities[0].d, 0.19337152e01, 1e-8); + + assert_delta!(species_data.conductivities[1].temp_range.0, 1000.0, 1e-1); + assert_delta!(species_data.conductivities[1].temp_range.1, 5000.0, 1e-1); + assert_delta!(species_data.conductivities[1].a, 0.69075463e00, 1e-8); + assert_delta!(species_data.conductivities[1].b, 0.62676058e02, 1e-8); + assert_delta!(species_data.conductivities[1].c, -0.25667413e05, 1e-8); + assert_delta!(species_data.conductivities[1].d, 0.12664189e01, 1e-8); + + assert_delta!(species_data.conductivities[2].temp_range.0, 5000.0, 1e-1); + assert_delta!(species_data.conductivities[2].temp_range.1, 15000.0, 1e-1); + assert_delta!(species_data.conductivities[2].a, 0.76269502e00, 1e-8); + assert_delta!(species_data.conductivities[2].b, 0.62341752e03, 1e-8); + assert_delta!(species_data.conductivities[2].c, -0.71899552e06, 1e-8); + assert_delta!(species_data.conductivities[2].d, 0.56927918e00, 1e-8); + } +} diff --git a/src/properties/transport_fit.rs b/src/properties/transport_fit.rs new file mode 100644 index 0000000..8489d3c --- /dev/null +++ b/src/properties/transport_fit.rs @@ -0,0 +1,13 @@ +pub struct SpeciesTransportData { + pub name: String, + pub viscosities: Vec, + pub conductivities: Vec, +} + +pub struct TransportFit { + pub temp_range: (f64, f64), + pub a: f64, + pub b: f64, + pub c: f64, + pub d: f64, +} diff --git a/src/properties/utils.rs b/src/properties/utils.rs new file mode 100644 index 0000000..7d02a05 --- /dev/null +++ b/src/properties/utils.rs @@ -0,0 +1,14 @@ +pub fn parse_fields(line: &str, widths: &[usize]) -> Vec { + let mut fields = Vec::new(); + let mut pos = 0; + + for &width in widths { + if let Some(field) = line.get(pos..pos + width) { + // The replace changes the fortran formatted D exponential for the normal E exponential + fields.push(field.trim().replace("D", "E").replace("E ", "E")); + } + pos += width; + } + + fields +}