use crate::Kinetics::kinetics_lib_api::KineticData;
use crate::Kinetics::mechfinder_api::{ parse_kinetic_data_vec, ReactionData, Mechanism_search};
use crate::Kinetics::parsetask::{decipher_vector_of_shortcuts, decipher_vector_of_shortcuts_to_pairs};
use crate::Kinetics::stoichiometry_analyzer::StoichAnalyzer;
use std::fs::File;
use std::io::Write;
use serde_json::json;
use serde_json::Value;
use std::collections::HashMap;
use prettytable::{Table, Row, Cell};
#[derive(Debug, Clone)]
pub struct KinData {
pub shortcut_reactions: Option<Vec<String>>, pub map_of_reactions: Option<HashMap<String, Vec<String>>>, pub vec_of_pairs: Option<Vec<(String, String)>>, pub vec_of_reaction_Values: Option<Vec<Value>>, pub vec_of_reaction_data: Option<Vec<ReactionData>>, pub vec_of_equations: Vec<String>, pub substances: Vec<String>, pub groups: Option<HashMap<String, HashMap<String, usize>>>, pub stecheodata: StoichAnalyzer, }
impl KinData {
pub fn new() -> Self {
Self {
shortcut_reactions: None,
map_of_reactions: None,
vec_of_pairs: None,
vec_of_reaction_Values: None,
vec_of_reaction_data: None,
vec_of_equations: Vec::new(),
substances: Vec::new(),
groups: None,
stecheodata: StoichAnalyzer::new(),
}
}
pub fn set_reactions_directly(
&mut self,
reactions: Vec<String>,
groups: Option<HashMap<String, HashMap<String, usize>>>,
) -> () {
self.shortcut_reactions = Some(reactions);
self.groups = groups;
}
pub fn set_reactions_from_shortcut_range(&mut self, shortcut_range: String) -> Vec<String> {
let parts: Vec<&str> = shortcut_range.split("..").collect();
if parts.len() != 2 {
return vec![]; }
let prefix = parts[0]
.chars()
.take_while(|c| c.is_alphabetic())
.collect::<String>(); let start: usize = parts[0][prefix.len()..].parse().unwrap_or(0); let end: usize = parts[1].parse().unwrap_or(0);
(start..=end).map(|i| format!("{}{}", prefix, i)).collect() }
pub fn set_reactions_from_shortcuts(&mut self) -> () {
if let Some(shortcut_reactions) = &self.shortcut_reactions {
let vec: Vec<&str> = shortcut_reactions.iter().map(|s| s.as_str()).collect();
self.map_of_reactions = Some(decipher_vector_of_shortcuts(vec.clone()));
let vec_of_pairs = decipher_vector_of_shortcuts_to_pairs(vec);
self.vec_of_pairs = Some(vec_of_pairs.clone());
let mut vec_of_reaction_values = Vec::new();
let mut kin_instance = KineticData::new();
for (lib, reaction_id) in vec_of_pairs.iter() {
kin_instance.open_json_files(lib);
let reaction_data_Value =
kin_instance.search_reactdata_by_reaction_id(&reaction_id);
vec_of_reaction_values.push(reaction_data_Value);
}
self.vec_of_reaction_Values = Some(vec_of_reaction_values);
} else {
println!("KinData::create_map_of_reactions: shortcut_reactions is None");
}
}
pub fn construct_mechanism(&mut self, task_substances: Vec<String>, task_library: String,) {
let found_mech = Mechanism_search::new( task_substances,task_library.clone());
self.vec_of_reaction_data = Some(found_mech.reactdata);
let reactions = found_mech.mechanism;
let mut full_addres = Vec::new();
for reaction in reactions.iter() {
let addres = format!("{}_{}", task_library, reaction);
full_addres.push(addres);
}
self.shortcut_reactions = Some(full_addres);
}
pub fn reactdata_parsing(&mut self) -> () {
if let Some(vec_of_reaction_values ) = & self.vec_of_reaction_Values {
let (vec_ReactionData, vec_of_equations) =
parse_kinetic_data_vec( vec_of_reaction_values.clone());
self.vec_of_reaction_data = Some(vec_ReactionData);
self.vec_of_equations = vec_of_equations;
} else {
println!("KinData::reactdata_from_shortcuts: map_of_reactions is None");
}
}
pub fn analyze_reactions(&mut self) -> () {
let mut StoichAnalyzer_instance = StoichAnalyzer::new();
StoichAnalyzer_instance.reactions = self.vec_of_equations.clone();
StoichAnalyzer_instance.groups = self.groups.clone();
StoichAnalyzer_instance.search_substances();
self.substances = StoichAnalyzer_instance.substances.clone();
StoichAnalyzer_instance.analyse_reactions();
StoichAnalyzer_instance.create_matrix_of_elements();
self.stecheodata = StoichAnalyzer_instance;
}
pub fn print_raw_reactions(&self) -> Result<(), std::io::Error> {
if let Some(vec_of_reaction_Values) = & self.vec_of_reaction_Values {
let json_array = json!(vec_of_reaction_Values);
let mut file = File::create("raw_reactions.json")?;
file.write_all(serde_json::to_string_pretty(&json_array)?.as_bytes())?;
println!("Raw reactions have been written to raw_reactions.json");
Ok(())
} else {
println!("KinData::reactdata_from_shortcuts: map_of_reactions is None");
Ok(())
}
}
pub fn pretty_print_kindata(&self) -> Result<(), std::io::Error> {
if let Some(vec_of_reaction_Values) = &self.vec_of_reaction_Values {
let mut table = Table::new();
if let Some(Value::Object(first_obj)) = vec_of_reaction_Values.get(0) {
let header: Vec<Cell> = first_obj.keys().map(|k| Cell::new(k)).collect();
table.add_row(Row::new(header));
}
for value in vec_of_reaction_Values {
if let Value::Object(obj) = value {
let row: Vec<Cell> = obj.values().map(|v| Cell::new(&v.to_string())).collect();
table.add_row(Row::new(row));
}
}
table.printstd();
println!("Raw reactions have been written to raw_reactions.json");
Ok(())
} else {
println!("KinData::reactdata_from_shortcuts: map_of_reactions is None");
Ok(())
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_user_reactions_new() {
let shortcut_reactions = Some(vec![
"C_1".to_string(),
"C_2".to_string(),
"C_3".to_string(),
]);
let mut user_reactions = KinData::new();
user_reactions.shortcut_reactions = shortcut_reactions.clone();
assert_eq!(
user_reactions.shortcut_reactions,
Some(vec![
"C_1".to_string(),
"C_2".to_string(),
"C_3".to_string()
])
);
let mut map_of_reactions = HashMap::new();
map_of_reactions.insert(
"Cantera".to_string(),
vec!["1".to_string(), "2".to_string(), "3".to_string()]
.into_iter()
.collect(),
);
user_reactions.set_reactions_from_shortcuts();
println!(
"map_of_reactions: {:?} \n \n ",
&user_reactions.map_of_reactions
);
assert_eq!(user_reactions.map_of_reactions, Some(map_of_reactions));
user_reactions.reactdata_parsing();
println!(
"map_of_reactions data: {:?} \n \n ",
&user_reactions.clone().vec_of_reaction_data
);
assert_eq!(
user_reactions
.clone()
.vec_of_reaction_data
.unwrap()
.is_empty(),
false
);
assert_eq!(user_reactions.vec_of_equations.is_empty(), false);
user_reactions.analyze_reactions();
let subs = user_reactions.substances;
assert_eq!(subs.is_empty(), false);
let S = user_reactions.stecheodata.stecheo_matrx;
let nunber_of_reactions = S.len();
let number_of_substances = S[0].len();
assert_eq!(S.is_empty(), false);
assert_eq!(nunber_of_reactions, shortcut_reactions.unwrap().len());
assert_eq!(number_of_substances, subs.len());
}
#[test]
fn test_generate_strings_with_single_character_prefix() {
let mut user_reactions = KinData::new();
let result = user_reactions.set_reactions_from_shortcut_range("A1..5".to_string());
let expected = vec!["A1", "A2", "A3", "A4", "A5"];
assert_eq!(result, expected);
}
#[test]
fn test_generate_strings_with_multi_character_prefix() {
let mut user_reactions = KinData::new();
let result = user_reactions.set_reactions_from_shortcut_range("Cat5..8".to_string());
let expected = vec!["Cat5", "Cat6", "Cat7", "Cat8"];
assert_eq!(result, expected);
}
#[test]
fn test_generate_strings_with_large_numbers() {
let mut user_reactions = KinData::new();
let result = user_reactions.set_reactions_from_shortcut_range("Meow20..25".to_string());
let expected = vec!["Meow20", "Meow21", "Meow22", "Meow23", "Meow24", "Meow25"];
assert_eq!(result, expected);
}
}