Reorganize some code and add the start of the transport file parsing

This commit is contained in:
Alex Selimov 2026-03-28 22:56:36 -04:00
parent d5ed4c7599
commit 3548d8b449
7 changed files with 281 additions and 28 deletions

View file

@ -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;

View file

@ -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

View file

@ -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<String> {
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<SpeciesPolynomial>,
pub reactants: Vec<SpeciesPolynomial>,
pub products: Vec<SpeciesThermoData>,
pub reactants: Vec<SpeciesThermoData>,
}
/// Parse a thermo formatted db
@ -59,7 +46,7 @@ impl ThermoDB {
fn parse_species<'a>(
line: &str,
lines: &mut impl Iterator<Item = &'a str>,
) -> Result<SpeciesPolynomial, PropertiesError> {
) -> Result<SpeciesThermoData, PropertiesError> {
// 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<Item = &'a str>,
intervals: usize,
) -> Result<Vec<Polynomial>, PropertiesError> {
) -> Result<Vec<ThermoPolynomial>, 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<Item = &'a str>,
) -> Result<Polynomial, PropertiesError> {
) -> Result<ThermoPolynomial, PropertiesError> {
// 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,
},
};

View file

@ -3,15 +3,15 @@ pub enum Phase {
Condensed,
}
pub struct SpeciesPolynomial {
pub struct SpeciesThermoData {
pub name: String,
pub elements: Vec<SpeciesElement>,
pub phase: Phase,
pub polynomials: Vec<Polynomial>,
pub polynomials: Vec<ThermoPolynomial>,
pub molecular_weight: f64,
pub h_formation: f64,
}
pub struct Polynomial {
pub struct ThermoPolynomial {
pub a: Vec<f64>,
pub temp_range: (f64, f64),
}

View file

@ -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<SpeciesTransportData>,
}
impl TransportDB {
pub fn parse() {}
}
fn parse_species_transport_block<'a>(
mut lines: impl Iterator<Item = &'a str>,
) -> Result<SpeciesTransportData, PropertiesError> {
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);
}
}

View file

@ -0,0 +1,13 @@
pub struct SpeciesTransportData {
pub name: String,
pub viscosities: Vec<TransportFit>,
pub conductivities: Vec<TransportFit>,
}
pub struct TransportFit {
pub temp_range: (f64, f64),
pub a: f64,
pub b: f64,
pub c: f64,
pub d: f64,
}

14
src/properties/utils.rs Normal file
View file

@ -0,0 +1,14 @@
pub fn parse_fields(line: &str, widths: &[usize]) -> Vec<String> {
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
}