use crate::Thermodynamics::DBhandlers::Diffusion::MultiSubstanceDiffusion;
use crate::Thermodynamics::DBhandlers::thermo_api::{
ThermoCalculator, ThermoEnum, ThermoError, create_thermal_by_name,
};
use crate::Thermodynamics::DBhandlers::transport_api::{
TransportCalculator, TransportEnum, create_transport_calculator_by_name,
};
use crate::Thermodynamics::User_substances_error::SimpleExceptionLogger;
use crate::Thermodynamics::User_substances_error::{SubsDataError, SubsDataResult};
use crate::Thermodynamics::thermo_lib_api::ThermoData;
use std::fmt;
use RustedSciThe::symbolic::symbolic_engine::Expr;
use nalgebra::DMatrix;
use serde_json::Value;
use std::collections::HashMap;
#[derive(Debug, Eq, PartialEq, Hash, Clone, Copy)]
#[allow(non_camel_case_types)]
pub enum DataType {
Cp,
Cp_fun,
Cp_sym,
dH,
dH_fun,
dH_sym,
dS,
dS_fun,
dS_sym,
dmu,
dmu_fun,
dmu_sym,
Lambda,
Lambda_fun,
Lambda_sym,
Visc,
Visc_fun,
Visc_sym,
}
#[derive(Debug, Clone)]
pub enum CalculatorType {
Thermo(ThermoEnum),
Transport(TransportEnum),
}
#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)]
pub enum WhatIsFound {
Thermo,
Transport,
NotFound,
}
#[derive(Debug, Clone)]
pub struct SearchResult {
pub library: String,
pub priority_type: LibraryPriority,
pub data: Value,
pub calculator: Option<CalculatorType>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum LibraryPriority {
Priority,
Permitted,
Explicit,
}
#[derive(Debug, Clone, Copy)]
pub enum Phases {
Liquid,
Gas,
Solid,
Condensed,
}
pub struct SubsData {
pub P: Option<f64>,
pub P_unit: Option<String>,
pub T: Option<f64>,
pub Molar_mass_unit: Option<String>,
pub substances: Vec<String>,
pub library_priorities: HashMap<String, LibraryPriority>,
pub explicit_search_insructions: HashMap<String, String>,
pub ro_map: Option<HashMap<String, f64>>,
pub ro_map_sym: Option<HashMap<String, Box<Expr>>>,
pub map_of_phases: HashMap<String, Option<Phases>>,
pub search_results: HashMap<String, HashMap<WhatIsFound, Option<SearchResult>>>,
pub thermo_data: ThermoData,
pub therm_map_of_properties_values: HashMap<String, HashMap<DataType, Option<f64>>>,
pub therm_map_of_fun:
HashMap<String, HashMap<DataType, Option<Box<dyn Fn(f64) -> f64 + Send + Sync>>>>,
pub therm_map_of_sym: HashMap<String, HashMap<DataType, Option<Box<Expr>>>>,
pub transport_map_of_properties_values: HashMap<String, HashMap<DataType, Option<f64>>>,
pub transport_map_of_fun:
HashMap<String, HashMap<DataType, Option<Box<dyn Fn(f64) -> f64 + Send + Sync>>>>,
pub transport_map_of_sym: HashMap<String, HashMap<DataType, Option<Box<Expr>>>>,
pub elem_composition_matrix: Option<DMatrix<f64>>,
pub hasmap_of_molar_mass: HashMap<String, f64>,
pub diffusion_data: Option<MultiSubstanceDiffusion>,
pub unique_elements: Vec<String>,
pub logger: SimpleExceptionLogger,
}
impl fmt::Debug for SubsData {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("SubsData")
.field("substances", &self.substances)
.field("library_priorities", &self.library_priorities)
.field("search_results", &self.search_results)
.field(
"therm_map_of_properties_values",
&self.therm_map_of_properties_values,
)
.field("therm_map_of_sym", &self.therm_map_of_sym)
.finish()
}
}
impl Clone for SubsData {
fn clone(&self) -> Self {
let mut new_therm_map_of_fun = HashMap::new();
for (substance, type_map) in &self.therm_map_of_fun {
let mut new_type_map = HashMap::new();
for (data_type, _) in type_map {
new_type_map.insert(*data_type, None);
}
new_therm_map_of_fun.insert(substance.clone(), new_type_map);
}
let new_transport_map_of_fun = HashMap::new();
for (substance, type_map) in &self.therm_map_of_fun {
let mut new_type_map = HashMap::new();
for (data_type, _) in type_map {
new_type_map.insert(*data_type, None);
}
new_therm_map_of_fun.insert(substance.clone(), new_type_map);
}
let mut sd = SubsData {
P: self.P,
P_unit: self.P_unit.clone(),
T: self.T.clone(),
Molar_mass_unit: self.Molar_mass_unit.clone(),
substances: self.substances.clone(),
library_priorities: self.library_priorities.clone(),
explicit_search_insructions: self.explicit_search_insructions.clone(),
ro_map: self.ro_map.clone(),
ro_map_sym: self.ro_map_sym.clone(),
map_of_phases: self.map_of_phases.clone(),
search_results: self.search_results.clone(),
thermo_data: self.thermo_data.clone(),
therm_map_of_properties_values: self.therm_map_of_properties_values.clone(),
therm_map_of_fun: new_therm_map_of_fun,
therm_map_of_sym: self.therm_map_of_sym.clone(),
transport_map_of_properties_values: self.transport_map_of_properties_values.clone(),
transport_map_of_fun: new_transport_map_of_fun,
transport_map_of_sym: self.transport_map_of_sym.clone(),
elem_composition_matrix: self.elem_composition_matrix.clone(),
hasmap_of_molar_mass: self.hasmap_of_molar_mass.clone(),
diffusion_data: self.diffusion_data.clone(),
unique_elements: self.unique_elements.clone(),
logger: self.logger.clone(),
};
let _ = sd.calculate_transport_map_of_functions();
let _ = sd.calculate_therm_map_of_fun();
sd
}
}
impl SubsData {
pub fn new() -> Self {
Self {
P: None,
P_unit: None,
T: None,
Molar_mass_unit: None,
substances: Vec::new(),
library_priorities: HashMap::new(),
explicit_search_insructions: HashMap::new(),
search_results: HashMap::new(),
ro_map: None,
ro_map_sym: None,
map_of_phases: HashMap::new(),
thermo_data: ThermoData::new(),
therm_map_of_properties_values: HashMap::new(),
therm_map_of_fun: HashMap::new(),
therm_map_of_sym: HashMap::new(),
transport_map_of_properties_values: HashMap::new(),
transport_map_of_fun: HashMap::new(),
transport_map_of_sym: HashMap::new(),
elem_composition_matrix: None,
hasmap_of_molar_mass: HashMap::new(),
diffusion_data: None,
unique_elements: Vec::new(),
logger: SimpleExceptionLogger::new(),
}
}
pub fn empty() -> Self {
Self {
P: None,
P_unit: None,
T: None,
Molar_mass_unit: None,
substances: Vec::new(),
library_priorities: HashMap::new(),
explicit_search_insructions: HashMap::new(),
search_results: HashMap::new(),
ro_map: None,
ro_map_sym: None,
map_of_phases: HashMap::new(),
thermo_data: ThermoData::empty(),
therm_map_of_properties_values: HashMap::new(),
therm_map_of_fun: HashMap::new(),
therm_map_of_sym: HashMap::new(),
transport_map_of_properties_values: HashMap::new(),
transport_map_of_fun: HashMap::new(),
transport_map_of_sym: HashMap::new(),
elem_composition_matrix: None,
hasmap_of_molar_mass: HashMap::new(),
diffusion_data: None,
unique_elements: Vec::new(),
logger: SimpleExceptionLogger::new(),
}
}
pub fn set_substances(&mut self, substances: Vec<String>) {
self.substances = substances;
}
pub fn set_library_priority(&mut self, library: String, priority: LibraryPriority) {
self.library_priorities.insert(library, priority);
}
pub fn set_multiple_library_priorities(
&mut self,
libraries: Vec<String>,
priority: LibraryPriority,
) {
for library in libraries {
self.library_priorities.insert(library, priority.clone());
}
}
pub fn set_explicis_searh_instructions(&mut self, direct_search: HashMap<String, String>) {
self.explicit_search_insructions = direct_search;
}
pub fn create_calculator(&self, library: &str) -> Option<CalculatorType> {
match ThermoData::what_handler_to_use(library).as_str() {
"NASA"
| "NIST"
| "NASA7"
| "NUIG_thermo"
| "Cantera_nasa_base_gas"
| "Cantera_nasa_base_cond"
| "NASA_gas"
| "NASA_cond" => Some(CalculatorType::Thermo(create_thermal_by_name(library))),
"transport" | "CEA" | "Aramco_transport" => Some(CalculatorType::Transport(
create_transport_calculator_by_name(library),
)),
_ => {
panic!("not found calculator for library: {}", library);
}
}
}
pub fn search_substances(&mut self) -> SubsDataResult<()> {
println!("library priorities: {:?} \n", self.library_priorities);
let priority_libs: Vec<String> = self
.library_priorities
.iter()
.filter(move |&(_, &ref p)| *p == LibraryPriority::Priority)
.map(|(lib, _)| lib.clone())
.collect();
println!(
"\n search substances in Priority libraries: {} \n ",
priority_libs.join(", ")
);
let permitted_libs: Vec<String> = self
.library_priorities
.iter()
.filter(move |&(_, &ref p)| *p == LibraryPriority::Permitted)
.map(|(lib, _)| lib.clone())
.collect();
println!(
"\n search substances in Permitted libraries: {} \n ",
permitted_libs.join(", ")
);
let subs_to_search_explicit_instruction: Vec<String> =
self.explicit_search_insructions.keys().cloned().collect();
let subs_to_search = &mut self.substances.clone();
subs_to_search.retain(|subs_i| !subs_to_search_explicit_instruction.contains(subs_i));
for substance in subs_to_search {
println!("\n \n search substance {} \n \n ", substance);
let mut found = false;
for lib in &priority_libs {
println!(
"\n \n search substance {} in priority library: {} \n \n ",
substance, lib
);
if let Some(lib_data) = self.thermo_data.LibThermoData.get(lib) {
if let Some(substance_data) = lib_data.get(substance) {
let mut calculator = self.create_calculator(lib);
if let Some(calc_type) = calculator.as_mut() {
match calc_type {
CalculatorType::Thermo(thermo) => {
thermo.from_serde(substance_data.clone())?;
print!("\n \n instance of thermal props struct: ");
let _ = thermo.print_instance();
self.search_results
.entry(substance.clone()) .or_insert_with(HashMap::new)
.insert(
WhatIsFound::Thermo,
Some(SearchResult {
library: lib.clone(),
priority_type: LibraryPriority::Priority,
data: substance_data.clone(),
calculator,
}),
);
}
CalculatorType::Transport(transport) => {
transport.from_serde(substance_data.clone())?;
print!("\n \n instance of transport props struct: ");
let _ = transport.print_instance();
self.search_results
.entry(substance.clone()) .or_insert_with(HashMap::new)
.insert(
WhatIsFound::Transport,
Some(SearchResult {
library: lib.clone(),
priority_type: LibraryPriority::Priority,
data: substance_data.clone(),
calculator,
}),
);
}
}
}
println!(
"\n substance {} found in priority library {} \n {:?} \n",
substance,
lib,
self.search_results.get(substance).unwrap()
);
found = true;
}
}
}
if !found {
for lib in &permitted_libs {
println!(
"\n \n search substance {} in Permitted library: {} \n \n ",
substance, lib
);
if let Some(lib_data) = self.thermo_data.LibThermoData.get(lib) {
if let Some(substance_data) = lib_data.get(substance) {
let mut calculator = self.create_calculator(lib);
if let Some(calc_type) = calculator.as_mut() {
match calc_type {
CalculatorType::Thermo(thermo) => {
thermo.newinstance()?;
thermo.from_serde(substance_data.clone())?;
let _ = thermo.print_instance();
self.search_results
.entry(substance.clone()) .or_insert_with(HashMap::new)
.insert(
WhatIsFound::Thermo,
Some(SearchResult {
library: lib.clone(),
priority_type: LibraryPriority::Permitted,
data: substance_data.clone(),
calculator,
}),
);
assert!(
&self
.search_results
.get(substance)
.unwrap()
.get(&WhatIsFound::Thermo)
.unwrap()
.is_some()
);
}
CalculatorType::Transport(transport) => {
transport.from_serde(substance_data.clone())?;
let _ = transport.print_instance();
self.search_results
.entry(substance.clone()) .or_insert_with(HashMap::new)
.insert(
WhatIsFound::Transport,
Some(SearchResult {
library: lib.clone(),
priority_type: LibraryPriority::Permitted,
data: substance_data.clone(),
calculator,
}),
);
assert!(
&self
.search_results
.get(substance)
.unwrap()
.get(&WhatIsFound::Transport)
.unwrap()
.is_some()
);
}
}
}
println!(
"\n substance {} found in permitted library {} \n {:?} \n",
substance,
lib,
self.search_results.get(substance).unwrap()
);
found = true;
}
}
}
}
if !found {
println!("\n substance {} not found \n ", substance);
let data_to_insert = HashMap::from([(WhatIsFound::NotFound, None)]);
self.search_results
.insert(substance.clone(), data_to_insert);
}
} for (substance, library) in &self.explicit_search_insructions {
let mut found = false;
if let Some(lib_data) = self.thermo_data.LibThermoData.get(library) {
if let Some(substance_data) = lib_data.get(substance) {
let mut calculator = self.create_calculator(library);
if let Some(calc_type) = calculator.as_mut() {
match calc_type {
CalculatorType::Thermo(thermo) => {
thermo.from_serde(substance_data.clone())?;
print!("\n \n instance of thermal props struct: ");
let _ = thermo.print_instance();
self.search_results
.entry(substance.clone()) .or_insert_with(HashMap::new)
.insert(
WhatIsFound::Thermo,
Some(SearchResult {
library: library.clone(),
priority_type: LibraryPriority::Priority,
data: substance_data.clone(),
calculator,
}),
);
}
CalculatorType::Transport(transport) => {
transport.from_serde(substance_data.clone())?;
print!("\n \n instance of transport props struct: ");
let _ = transport.print_instance();
self.search_results
.entry(substance.clone()) .or_insert_with(HashMap::new)
.insert(
WhatIsFound::Transport,
Some(SearchResult {
library: library.clone(),
priority_type: LibraryPriority::Priority,
data: substance_data.clone(),
calculator,
}),
);
}
}
}
println!(
"\n substance {} found in priority library {} \n {:?} \n",
substance,
library,
self.search_results.get(substance).unwrap()
);
found = true;
} } if !found {
println!("\n substance {} not found \n ", substance);
let data_to_insert = HashMap::from([(WhatIsFound::NotFound, None)]);
self.search_results
.insert(substance.clone(), data_to_insert);
}
} Ok(())
}
pub fn get_substance_result(
&self,
substance: &str,
) -> Option<&HashMap<WhatIsFound, Option<SearchResult>>> {
self.search_results.get(substance)
}
pub fn get_substance_result_mut(
&mut self,
substance: &str,
) -> Option<&mut HashMap<WhatIsFound, Option<SearchResult>>> {
self.search_results.get_mut(substance)
}
pub fn get_all_results(&self) -> &HashMap<String, HashMap<WhatIsFound, Option<SearchResult>>> {
&self.search_results
}
pub fn get_not_found_substances(&self) -> Vec<String> {
self.search_results
.iter()
.filter(|(_, result)| result.get(&WhatIsFound::NotFound).is_some())
.map(|(substance, _)| substance.clone())
.collect()
}
pub fn get_priority_found_substances(&self) -> Vec<String> {
self.search_results
.iter()
.filter(|(_, result)| result.values().any(|result| {
if let Some(result) = result.as_ref() {
matches!(result, SearchResult { priority_type, .. } if *priority_type == LibraryPriority::Priority)
} else {
false
}
}))
.map(|(substance, _)| substance.clone())
.collect()
}
pub fn search_by_elements(&mut self, elements: Vec<String>) -> SubsDataResult<Vec<String>> {
let found_substances = self.thermo_data.search_by_elements(elements);
self.substances = found_substances.clone();
self.populate_element_search_results(found_substances)
}
pub fn search_by_elements_only(
&mut self,
elements: Vec<String>,
) -> SubsDataResult<Vec<String>> {
let found_substances = self.thermo_data.search_by_elements_only(elements);
self.substances = found_substances.clone();
self.populate_element_search_results(found_substances)
}
fn populate_element_search_results(
&mut self,
found_substances: Vec<String>,
) -> SubsDataResult<Vec<String>> {
for substance in &found_substances {
if let Some(thermo_data) = self.thermo_data.hashmap_of_thermo_data.get(substance) {
for (library, data) in thermo_data {
let mut calculator = self.create_calculator(library);
if let Some(CalculatorType::Thermo(thermo)) = calculator.as_mut() {
thermo.from_serde(data.clone())?;
self.search_results
.entry(substance.clone())
.or_insert_with(HashMap::new)
.insert(
WhatIsFound::Thermo,
Some(SearchResult {
library: library.clone(),
priority_type: LibraryPriority::Priority,
data: data.clone(),
calculator,
}),
);
}
}
}
}
Ok(found_substances)
}
pub fn get_permitted_found_substances(&self) -> Vec<String> {
self.search_results
.iter()
.filter(|(_, result)| result.values().any(|result| {
if let Some(result) = result.as_ref() {
matches!(result, SearchResult { priority_type, .. } if *priority_type == LibraryPriority::Permitted)
} else {
false
}
}))
.map(|(substance, _)| substance.clone())
.collect()
}
pub fn get_thermo_function(
&self,
substance: &str,
data_type: DataType,
) -> Option<&Box<dyn Fn(f64) -> f64 + Send + Sync>> {
self.therm_map_of_fun
.get(substance)
.and_then(|map| map.get(&data_type))
.and_then(|opt| opt.as_ref())
}
pub fn get_thermo_symbolic(&self, substance: &str, data_type: DataType) -> Option<&Box<Expr>> {
self.therm_map_of_sym
.get(substance)
.and_then(|map| map.get(&data_type))
.and_then(|opt| opt.as_ref())
}
fn with_thermo_calculator<F, R>(&mut self, substance: &str, f: F) -> SubsDataResult<R>
where
F: FnOnce(&mut dyn ThermoCalculator) -> Result<R, ThermoError>,
{
let datamap = self
.get_substance_result_mut(substance)
.ok_or_else(|| SubsDataError::SubstanceNotFound(substance.to_string()))?;
if datamap.contains_key(&WhatIsFound::NotFound) {
return Err(SubsDataError::SubstanceNotFound(substance.to_string()));
}
let search_result = datamap
.get_mut(&WhatIsFound::Thermo)
.ok_or_else(|| SubsDataError::CalculatorNotAvailable {
substance: substance.to_string(),
calc_type: "Thermo".to_string(),
})?
.as_mut()
.ok_or_else(|| SubsDataError::CalculatorNotAvailable {
substance: substance.to_string(),
calc_type: "Thermo".to_string(),
})?;
let calculator = search_result.calculator.as_mut().ok_or_else(|| {
SubsDataError::CalculatorNotAvailable {
substance: substance.to_string(),
calc_type: "Thermo".to_string(),
}
})?;
match calculator {
CalculatorType::Thermo(thermo) => {
let result: SubsDataResult<R> = f(thermo).map_err(Into::into);
if let Err(ref e) = result {
crate::log_error!(self.logger, e, substance, "with_thermo_calculator");
}
result
}
CalculatorType::Transport(_) => Err(SubsDataError::CalculatorNotAvailable {
substance: substance.to_string(),
calc_type: "Thermo (found Transport instead)".to_string(),
}),
}
}
pub fn extract_thermal_coeffs(
&mut self,
substance: &str,
temperature: f64,
) -> SubsDataResult<()> {
if temperature <= 0.0 {
let error = SubsDataError::InvalidTemperature(temperature);
return self.log_and_propagate(Err(error), substance, "extract_thermal_coeffs");
}
let result = self.with_thermo_calculator(substance, |thermo| {
thermo.extract_model_coefficients(temperature)
});
self.log_and_propagate(result, substance, "extract_thermal_coeffs")
}
pub fn extract_all_thermal_coeffs(&mut self, temperature: f64) -> SubsDataResult<()> {
if temperature <= 0.0 {
let error = SubsDataError::InvalidTemperature(temperature);
crate::log_error!(
self.logger,
&error,
"ALL_SUBSTANCES",
"extract_all_thermal_coeffs"
);
return Err(error);
}
for substance in self.substances.clone() {
let result = self.extract_thermal_coeffs(&substance, temperature);
self.log_and_propagate(result, &substance, "extract_all_thermal_coeffs")?;
}
Ok(())
}
pub fn is_coeffs_valid_for_T(
&mut self,
substance: &str,
temperature: f64,
) -> SubsDataResult<bool> {
if temperature <= 0.0 {
let error = SubsDataError::InvalidTemperature(temperature);
return self.log_and_propagate(Err(error), substance, "is_coeffs_valid_for_T");
}
let result = self.with_thermo_calculator(substance, |thermo| {
thermo.is_coeffs_valid_for_T(temperature)
});
self.log_and_propagate(result, substance, "is_coeffs_valid_for_T")
}
pub fn extract_coeffs_if_current_coeffs_not_valid(
&mut self,
substance: &str,
temperature: f64,
) -> SubsDataResult<(bool)> {
let check_flag = self.is_coeffs_valid_for_T(substance, temperature);
match check_flag {
Ok(flag) => match flag {
true => return Ok(true),
false => {
self.extract_thermal_coeffs(substance, temperature)?;
return self.log_and_propagate(
check_flag,
substance,
"extract_coeffs_if_current_coeffs_not_valid",
);
}
},
Err(error) => {
crate::log_error!(
self.logger,
&error,
"ALL_SUBSTANCES",
"extract_coeffs_if_current_coeffs_not_valid"
);
return Err(error);
}
}
}
pub fn extract_coeffs_if_current_coeffs_not_valid_for_all_subs(
&mut self,
temperature: f64,
) -> SubsDataResult<Vec<String>> {
let mut subs_with_changed_coeffs = Vec::new();
for substance in self.substances.clone() {
let result = self.extract_coeffs_if_current_coeffs_not_valid(&substance, temperature);
match result {
Ok(flag) => {
(if flag {
subs_with_changed_coeffs.push(substance.clone())
} else {
})
}
Err(_) => {}
}
self.log_and_propagate(
result,
&substance,
"extract_coeffs_if_current_coeffs_not_valid_for_all_subs",
)?;
}
Ok(subs_with_changed_coeffs)
}
pub fn set_T_range_for_thermo(
&mut self,
substance: &str,
T_min: f64,
T_max: f64,
) -> SubsDataResult<()> {
let result =
self.with_thermo_calculator(substance, |thermo| thermo.set_T_interval(T_min, T_max));
self.log_and_propagate(result, substance, "set_T_range_for_thermo")
}
pub fn set_T_range_for_all_thermo(&mut self, T_min: f64, T_max: f64) -> SubsDataResult<()> {
for substance in self.substances.clone() {
let result = self.set_T_range_for_thermo(&substance, T_min, T_max);
self.log_and_propagate(result, &substance, "set_T_range_for_all_thermo")?;
}
Ok(())
}
pub fn parse_thermal_coeffs(&mut self, substance: &str) -> SubsDataResult<()> {
let result = self.with_thermo_calculator(substance, |thermo| thermo.parse_coefficients());
self.log_and_propagate(result, substance, "parse_thermal_coeffs")
}
pub fn parse_all_thermal_coeffs(&mut self) -> SubsDataResult<()> {
for substance in self.substances.clone() {
let result = self.parse_thermal_coeffs(&substance);
self.log_and_propagate(result, &substance, "parse_all_thermal_coeffs")?;
}
Ok(())
}
pub fn fitting_thermal_coeffs_for_T_interval(&mut self, substance: &str) -> SubsDataResult<()> {
let result =
self.with_thermo_calculator(substance, |thermo| thermo.fitting_coeffs_for_T_interval());
self.log_and_propagate(result, substance, "fitting_thermal_coeffs_for_T_interval")
}
pub fn fitting_all_thermal_coeffs_for_T_interval(&mut self) -> SubsDataResult<()> {
for substance in self.substances.clone() {
let result = self.fitting_thermal_coeffs_for_T_interval(&substance);
self.log_and_propagate(
result,
&substance,
"fitting_all_thermal_coeffs_for_T_interval",
)?;
}
Ok(())
}
pub fn integr_mean(&mut self, substance: &str) -> SubsDataResult<()> {
let result = self.with_thermo_calculator(substance, |thermo| thermo.integr_mean());
self.log_and_propagate(result, substance, "integr_mean")
}
pub fn calculate_thermo_properties(
&mut self,
substance: &str,
temperature: f64,
) -> SubsDataResult<(f64, f64, f64)> {
let result = self._calculate_thermo_properties_internal(substance, temperature);
self.log_and_propagate(result, substance, "calculate_thermo_properties")
}
fn _calculate_thermo_properties_internal(
&mut self,
substance: &str,
temperature: f64,
) -> SubsDataResult<(f64, f64, f64)> {
if temperature <= 0.0 {
return Err(SubsDataError::InvalidTemperature(temperature));
}
let datamap = self
.search_results
.get(substance)
.ok_or_else(|| SubsDataError::SubstanceNotFound(substance.to_string()))?;
if datamap.contains_key(&WhatIsFound::NotFound) {
return Err(SubsDataError::SubstanceNotFound(substance.to_string()));
}
let search_result = datamap.get(&WhatIsFound::Thermo).ok_or_else(|| {
SubsDataError::CalculatorNotAvailable {
substance: substance.to_string(),
calc_type: "Thermo".to_string(),
}
})?;
match search_result {
Some(SearchResult {
calculator: Some(CalculatorType::Thermo(thermo)),
..
}) => {
let mut thermo = thermo.clone();
thermo.calculate_Cp_dH_dS(temperature)?;
let cp = thermo.get_Cp()?;
let dh = thermo.get_dh()?;
let ds = thermo.get_ds()?;
Ok((cp, dh, ds))
}
Some(SearchResult {
calculator: Some(CalculatorType::Transport(_)),
..
}) => Err(SubsDataError::CalculatorNotAvailable {
substance: substance.to_string(),
calc_type: "Thermo (found Transport instead)".to_string(),
}),
Some(SearchResult {
calculator: None, ..
}) => Err(SubsDataError::CalculatorNotAvailable {
substance: substance.to_string(),
calc_type: "Thermo".to_string(),
}),
None => Err(SubsDataError::SubstanceNotFound(substance.to_string())),
}
}
pub fn set_M(&mut self, M_map: HashMap<String, f64>, M_unit: Option<String>) {
self.hasmap_of_molar_mass = M_map;
self.Molar_mass_unit = M_unit;
}
pub fn set_P(&mut self, P: f64, P_unit: Option<String>) {
self.P = Some(P);
self.P_unit = P_unit;
}
pub fn set_T(&mut self, T: f64) {
self.T = Some(T);
}
pub fn build_property_map<T, F>(
&mut self,
calculator_fn: F,
target_map: &mut HashMap<String, HashMap<DataType, Option<T>>>,
property_types: &[DataType],
empty_value_fn: fn() -> Option<T>,
) -> SubsDataResult<()>
where
F: Fn(&mut Self, &str) -> SubsDataResult<HashMap<DataType, Option<T>>>,
{
for substance in self.substances.clone() {
match calculator_fn(self, &substance) {
Ok(properties) => {
target_map.insert(substance, properties);
}
Err(e) => {
let mut empty_map = HashMap::new();
for &prop_type in property_types {
empty_map.insert(prop_type, empty_value_fn());
}
target_map.insert(substance.clone(), empty_map);
println!(
"Warning: Failed to calculate properties for {}: {}",
substance, e
);
}
}
}
Ok(())
}
fn calculate_single_thermo_properties(
&mut self,
substance: &str,
temperature: f64,
) -> SubsDataResult<HashMap<DataType, Option<f64>>> {
let (cp, dh, ds) = self._calculate_thermo_properties_internal(substance, temperature)?;
let mut property_map = HashMap::new();
property_map.insert(DataType::Cp, Some(cp));
property_map.insert(DataType::dH, Some(dh));
property_map.insert(DataType::dS, Some(ds));
Ok(property_map)
}
pub fn calculate_therm_map_of_properties(&mut self, temperature: f64) -> SubsDataResult<()> {
let mut temp_map = HashMap::new();
for substance in self.substances.clone() {
let result = self.calculate_single_thermo_properties(&substance, temperature);
match self.log_and_propagate(result, &substance, "calculate_therm_map_of_properties") {
Ok(properties) => {
temp_map.insert(substance, properties);
}
Err(_) => {
let mut empty_map = HashMap::new();
empty_map.insert(DataType::Cp, None);
empty_map.insert(DataType::dH, None);
empty_map.insert(DataType::dS, None);
temp_map.insert(substance, empty_map);
}
}
}
self.therm_map_of_properties_values = temp_map;
Ok(())
}
fn calculate_single_thermo_functions(
&mut self,
substance: &str,
) -> SubsDataResult<HashMap<DataType, Option<Box<dyn Fn(f64) -> f64 + Send + Sync>>>> {
self.with_thermo_calculator(substance, |thermo| {
thermo.create_closures_Cp_dH_dS()?;
let mut function_map = HashMap::new();
function_map.insert(DataType::Cp_fun, thermo.get_C_fun().ok());
function_map.insert(DataType::dH_fun, thermo.get_dh_fun().ok());
function_map.insert(DataType::dS_fun, thermo.get_ds_fun().ok());
Ok(function_map)
})
}
pub fn calculate_therm_map_of_fun(&mut self) -> SubsDataResult<()> {
let mut temp_map = HashMap::new();
for substance in self.substances.clone() {
let result = self.calculate_single_thermo_functions(&substance);
match self.log_and_propagate(result, &substance, "calculate_therm_map_of_fun") {
Ok(properties) => {
temp_map.insert(substance, properties);
}
Err(_) => {
let mut empty_map = HashMap::new();
empty_map.insert(DataType::Cp_fun, None);
empty_map.insert(DataType::dH_fun, None);
empty_map.insert(DataType::dS_fun, None);
temp_map.insert(substance, empty_map);
}
}
}
self.therm_map_of_fun = temp_map;
Ok(())
}
fn calculate_single_thermo_symbolic(
&mut self,
substance: &str,
) -> SubsDataResult<HashMap<DataType, Option<Box<Expr>>>> {
self.with_thermo_calculator(substance, |thermo| {
thermo.create_sym_Cp_dH_dS()?;
let mut sym_map = HashMap::new();
sym_map.insert(DataType::Cp_sym, thermo.get_Cp_sym().ok().map(Box::new));
sym_map.insert(DataType::dH_sym, thermo.get_dh_sym().ok().map(Box::new));
sym_map.insert(DataType::dS_sym, thermo.get_ds_sym().ok().map(Box::new));
Ok(sym_map)
})
}
pub fn calculate_therm_map_of_sym(&mut self) -> SubsDataResult<()> {
let mut temp_map = HashMap::new();
for substance in self.substances.clone() {
let result = self.calculate_single_thermo_symbolic(&substance);
match self.log_and_propagate(result, &substance, "calculate_therm_map_of_sym") {
Ok(properties) => {
temp_map.insert(substance, properties);
}
Err(_) => {
let mut empty_map = HashMap::new();
empty_map.insert(DataType::Cp_sym, None);
empty_map.insert(DataType::dH_sym, None);
empty_map.insert(DataType::dS_sym, None);
temp_map.insert(substance, empty_map);
}
}
}
self.therm_map_of_sym = temp_map;
Ok(())
}
pub fn log_and_propagate<T>(
&mut self,
result: SubsDataResult<T>,
substance: &str,
function: &str,
) -> SubsDataResult<T> {
if let Err(ref e) = result {
crate::log_error!(self.logger, e, substance, function);
}
result
}
pub fn print_error_logs(&self) {
use crate::Thermodynamics::User_substances_error::ExceptionLogger;
self.logger.print_logs();
}
pub fn clear_error_logs(&mut self) {
use crate::Thermodynamics::User_substances_error::ExceptionLogger;
self.logger.clear_logs();
}
}