Skip to main content

qualia_core_db/specialized_libs/chemistry_modeling/
kinetics.rs

1use super::*;
2
3/// Reaction analyzer for chemical reaction analysis
4pub struct ReactionAnalyzer {
5    reaction_network: ReactionNetwork,
6    kinetics_calculator: KineticsCalculator,
7    thermodynamics_calculator: ThermodynamicsCalculator,
8}
9
10/// Reaction network
11pub struct ReactionNetwork {
12    reactions: HashMap<String, Reaction>,
13    species: HashMap<String, Species>,
14    pathways: Vec<ReactionPathway>,
15}
16
17/// Reactions
18#[derive(Debug, Clone)]
19pub struct Reaction {
20    pub reaction_id: String,
21    pub reaction_name: String,
22    pub reactants: Vec<String>,
23    pub products: Vec<String>,
24    pub reaction_type: ReactionType,
25    pub mechanism: ReactionMechanism,
26}
27
28/// Reaction types
29#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
30pub enum ReactionType {
31    Elementary,
32    Complex,
33    Catalytic,
34    Chain,
35    Photochemical,
36    Electrochemical,
37}
38
39/// Reaction mechanisms
40#[derive(Debug, Clone)]
41pub struct ReactionMechanism {
42    pub mechanism_id: String,
43    pub steps: Vec<ReactionStep>,
44    pub intermediates: Vec<String>,
45}
46
47/// Reaction steps
48#[derive(Debug, Clone)]
49pub struct ReactionStep {
50    pub step_id: String,
51    pub reactants: Vec<String>,
52    pub products: Vec<String>,
53    pub rate_constant: f64,
54    pub activation_energy: f64,
55}
56
57/// Species
58#[derive(Debug, Clone)]
59pub struct Species {
60    pub species_id: String,
61    pub species_name: String,
62    pub formula: String,
63    pub molecular_weight: f64,
64    pub properties: SpeciesProperties,
65}
66
67/// Species properties
68#[derive(Debug, Clone)]
69pub struct SpeciesProperties {
70    pub enthalpy: f64,
71    pub entropy: f64,
72    pub gibbs_free_energy: f64,
73    pub heat_capacity: f64,
74}
75
76/// Reaction pathways
77#[derive(Debug, Clone)]
78pub struct ReactionPathway {
79    pub pathway_id: String,
80    pub pathway_name: String,
81    pub reactions: Vec<String>,
82    pub branching_ratios: Vec<f64>,
83}
84
85/// Kinetics calculator
86pub struct KineticsCalculator {
87    rate_laws: HashMap<String, RateLaw>,
88    rate_constants: HashMap<String, RateConstant>,
89    reaction_rates: HashMap<String, f64>,
90}
91
92/// Rate laws
93#[derive(Debug, Clone)]
94pub struct RateLaw {
95    pub law_id: String,
96    pub law_type: RateLawType,
97    pub rate_expression: String,
98    pub parameters: RateLawParameters,
99}
100
101/// Rate law types
102#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
103pub enum RateLawType {
104    Elementary,
105    MichaelisMenten,
106    Hill,
107    Custom,
108}
109
110/// Rate law parameters
111#[derive(Debug, Clone, Serialize, Deserialize)]
112pub struct RateLawParameters {
113    pub rate_constant: f64,
114    pub reaction_orders: Vec<f64>,
115    pub saturation_constants: Vec<f64>,
116}
117
118/// Rate constants
119#[derive(Debug, Clone)]
120pub struct RateConstant {
121    pub constant_id: String,
122    pub value: f64,
123    pub temperature_dependence: TemperatureDependence,
124    pub pressure_dependence: PressureDependence,
125}
126
127/// Temperature dependence
128#[derive(Debug, Clone)]
129pub struct TemperatureDependence {
130    pub arrhenius_parameters: ArrheniusParameters,
131    pub modified_arrhenius: Option<ModifiedArrheniusParameters>,
132}
133
134/// Arrhenius parameters
135#[derive(Debug, Clone, Serialize, Deserialize)]
136pub struct ArrheniusParameters {
137    pub pre_exponential: f64,
138    pub activation_energy: f64,
139}
140
141/// Modified Arrhenius parameters
142#[derive(Debug, Clone, Serialize, Deserialize)]
143pub struct ModifiedArrheniusParameters {
144    pub pre_exponential: f64,
145    pub activation_energy: f64,
146    pub temperature_exponent: f64,
147}
148
149/// Pressure dependence
150#[derive(Debug, Clone)]
151pub struct PressureDependence {
152    pub fall_off_parameters: FallOffParameters,
153    pub third_body_efficiency: f64,
154}
155
156/// Fall-off parameters
157#[derive(Debug, Clone, Serialize, Deserialize)]
158pub struct FallOffParameters {
159    pub low_pressure_limit: f64,
160    pub high_pressure_limit: f64,
161    pub fall_off_exponent: f64,
162}
163
164/// Thermodynamics calculator
165pub struct ThermodynamicsCalculator {
166    thermodynamic_data: HashMap<String, ThermodynamicData>,
167    equilibrium_calculator: EquilibriumCalculator,
168    phase_calculator: PhaseCalculator,
169}
170
171/// Thermodynamic data
172#[derive(Debug, Clone)]
173pub struct ThermodynamicData {
174    pub data_id: String,
175    pub temperature_range: (f64, f64),
176    pub enthalpy: f64,
177    pub entropy: f64,
178    pub gibbs_free_energy: f64,
179    pub heat_capacity: f64,
180}
181
182/// Equilibrium calculator
183pub struct EquilibriumCalculator {
184    equilibrium_constant: EquilibriumConstant,
185    reaction_quotient: ReactionQuotient,
186    gibbs_energy: GibbsEnergy,
187}
188
189/// Equilibrium constant
190#[derive(Debug, Clone)]
191pub struct EquilibriumConstant {
192    pub value: f64,
193    pub temperature: f64,
194    pub pressure: f64,
195}
196
197/// Reaction quotient
198#[derive(Debug, Clone)]
199pub struct ReactionQuotient {
200    pub value: f64,
201    pub concentrations: HashMap<String, f64>,
202}
203
204/// Gibbs energy
205#[derive(Debug, Clone)]
206pub struct GibbsEnergy {
207    pub standard_gibbs: f64,
208    pub actual_gibbs: f64,
209    pub delta_g: f64,
210}
211
212/// Phase calculator
213pub struct PhaseCalculator {
214    phase_diagrams: HashMap<String, PhaseDiagram>,
215    phase_transitions: HashMap<String, PhaseTransition>,
216    phase_equilibria: HashMap<String, PhaseEquilibrium>,
217}
218
219/// Phase diagrams
220#[derive(Debug, Clone)]
221pub struct PhaseDiagram {
222    pub diagram_id: String,
223    pub phases: Vec<Phase>,
224    pub boundaries: Vec<PhaseBoundary>,
225}
226
227/// Phases
228#[derive(Debug, Clone)]
229pub struct Phase {
230    pub phase_id: String,
231    pub phase_name: String,
232    pub phase_type: PhaseType,
233    pub composition: HashMap<String, f64>,
234}
235
236/// Phase types
237#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
238pub enum PhaseType {
239    Solid,
240    Liquid,
241    Gas,
242    Plasma,
243    Supercritical,
244}
245
246/// Phase boundaries
247#[derive(Debug, Clone)]
248pub struct PhaseBoundary {
249    pub boundary_id: String,
250    pub boundary_type: BoundaryType,
251    pub conditions: Vec<BoundaryCondition>,
252}
253
254/// Phase boundary types
255#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
256pub enum PhaseBoundaryType {
257    Melting,
258    Boiling,
259    Sublimation,
260    Triple,
261    Critical,
262}
263
264/// Boundary conditions
265#[derive(Debug, Clone)]
266pub struct BoundaryCondition {
267    pub temperature: f64,
268    pub pressure: f64,
269    pub composition: HashMap<String, f64>,
270}
271
272/// Phase transitions
273#[derive(Debug, Clone)]
274pub struct PhaseTransition {
275    pub transition_id: String,
276    pub transition_type: TransitionType,
277    pub enthalpy_change: f64,
278    pub entropy_change: f64,
279}
280
281/// Transition types
282#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
283pub enum TransitionType {
284    Fusion,
285    Vaporization,
286    Sublimation,
287    Deposition,
288    Ionization,
289}
290
291/// Phase equilibria
292#[derive(Debug, Clone)]
293pub struct PhaseEquilibrium {
294    pub equilibrium_id: String,
295    pub phases: Vec<String>,
296    pub equilibrium_conditions: EquilibriumConditions,
297}
298
299/// Equilibrium conditions
300#[derive(Debug, Clone)]
301pub struct EquilibriumConditions {
302    pub temperature: f64,
303    pub pressure: f64,
304    pub chemical_potentials: HashMap<String, f64>,
305}
306
307impl ReactionAnalyzer {
308    pub fn new() -> Self {
309        Self {
310            reaction_network: ReactionNetwork::new(),
311            kinetics_calculator: KineticsCalculator::new(),
312            thermodynamics_calculator: ThermodynamicsCalculator::new(),
313        }
314    }
315
316    /// Borrow the reaction network.
317    pub fn reaction_network(&self) -> &ReactionNetwork {
318        &self.reaction_network
319    }
320
321    /// Mutably borrow the reaction network.
322    pub fn reaction_network_mut(&mut self) -> &mut ReactionNetwork {
323        &mut self.reaction_network
324    }
325
326    pub fn initialize(&mut self) -> Result<(), ChemistryError> {
327        self.kinetics_calculator.initialize()?;
328        self.thermodynamics_calculator.initialize()?;
329        Ok(())
330    }
331
332    pub fn validate_reaction(&self, reaction: &Reaction) -> Result<(), ChemistryError> {
333        if reaction.reactants.is_empty() {
334            return Err(ChemistryError::ValidationError(
335                "Reaction must have at least one reactant".to_string(),
336            ));
337        }
338        if reaction.products.is_empty() {
339            return Err(ChemistryError::ValidationError(
340                "Reaction must have at least one product".to_string(),
341            ));
342        }
343        Ok(())
344    }
345
346    pub fn analyze_kinetics(
347        &mut self,
348        reaction: &Reaction,
349        conditions: &ReactionConditions,
350    ) -> Result<KineticsResults, ChemistryError> {
351        // REAL chemical kinetics via the Arrhenius equation:  k = A·exp(−Ea / (R·T)).
352        // The mechanism's rate-determining step (highest activation energy) governs the overall
353        // rate. `ReactionStep.rate_constant` is taken as the pre-exponential / frequency factor A
354        // and `activation_energy` as Ea in kJ/mol. Half-life follows from the reaction order.
355        const R: f64 = 8.314_462_618; // universal gas constant, J/(mol·K)
356
357        let t = conditions.temperature; // Kelvin
358        if !(t.is_finite() && t > 0.0) {
359            return Err(ChemistryError::ValidationError(
360                "temperature must be a positive value in Kelvin".to_string(),
361            ));
362        }
363
364        // Rate-determining elementary step = the one with the highest activation barrier.
365        let rds = reaction
366            .mechanism
367            .steps
368            .iter()
369            .max_by(|a, b| {
370                a.activation_energy
371                    .partial_cmp(&b.activation_energy)
372                    .unwrap_or(std::cmp::Ordering::Equal)
373            })
374            .ok_or_else(|| {
375                ChemistryError::InsufficientData(
376                    "reaction has no mechanism steps; cannot determine rate-determining step"
377                        .to_string(),
378                )
379            })?;
380
381        let a_factor = rds.rate_constant; // pre-exponential (frequency) factor A
382        let ea_kj = rds.activation_energy; // kJ/mol
383        if !(a_factor.is_finite() && ea_kj.is_finite()) {
384            return Err(ChemistryError::ValidationError(
385                "rate-determining step has non-finite A or Ea".to_string(),
386            ));
387        }
388        let ea_j = ea_kj * 1000.0; // kJ/mol → J/mol
389        let rate_constant = a_factor * (-ea_j / (R * t)).exp(); // k(T)
390
391        // Overall order ≈ number of distinct reactant species (elementary-rate approximation).
392        let reaction_order = reaction.reactants.len().max(1) as u32;
393
394        // Initial concentration of the first reactant (for order-dependent half-life).
395        let c0 = reaction
396            .reactants
397            .first()
398            .and_then(|name| conditions.concentration.get(name).copied())
399            .unwrap_or(1.0);
400
401        // Half-life by integrated rate law.
402        let half_life = if rate_constant <= 0.0 {
403            f64::INFINITY
404        } else {
405            match reaction_order {
406                0 => c0 / (2.0 * rate_constant), // t½ = [A]₀ / 2k
407                2 => {
408                    if c0 > 0.0 {
409                        1.0 / (rate_constant * c0)
410                    } else {
411                        f64::INFINITY
412                    }
413                } // 1 / (k[A]₀)
414                _ => std::f64::consts::LN_2 / rate_constant, // first order: ln2 / k
415            }
416        };
417
418        Ok(KineticsResults {
419            rate_constant,
420            activation_energy: ea_kj,
421            reaction_order,
422            half_life,
423        })
424    }
425}
426
427impl ReactionNetwork {
428    pub fn new() -> Self {
429        Self {
430            reactions: HashMap::new(),
431            species: HashMap::new(),
432            pathways: Vec::new(),
433        }
434    }
435
436    /// Register a reaction under its `reaction_id`, replacing any existing entry.
437    pub fn add_reaction(&mut self, reaction: Reaction) {
438        self.reactions
439            .insert(reaction.reaction_id.clone(), reaction);
440    }
441
442    /// Look up a reaction by id.
443    pub fn get_reaction(&self, reaction_id: &str) -> Option<&Reaction> {
444        self.reactions.get(reaction_id)
445    }
446
447    /// List the ids of all registered reactions.
448    pub fn list_reactions(&self) -> Vec<String> {
449        self.reactions.keys().cloned().collect()
450    }
451
452    /// Remove a reaction by id.
453    pub fn remove_reaction(&mut self, reaction_id: &str) -> Option<Reaction> {
454        self.reactions.remove(reaction_id)
455    }
456
457    /// Register a species under its `species_id`, replacing any existing entry.
458    pub fn add_species(&mut self, species: Species) {
459        self.species.insert(species.species_id.clone(), species);
460    }
461
462    /// Look up a species by id.
463    pub fn get_species(&self, species_id: &str) -> Option<&Species> {
464        self.species.get(species_id)
465    }
466
467    /// List the ids of all registered species.
468    pub fn list_species(&self) -> Vec<String> {
469        self.species.keys().cloned().collect()
470    }
471
472    /// Remove a species by id.
473    pub fn remove_species(&mut self, species_id: &str) -> Option<Species> {
474        self.species.remove(species_id)
475    }
476
477    /// Append a reaction pathway.
478    pub fn add_pathway(&mut self, pathway: ReactionPathway) {
479        self.pathways.push(pathway);
480    }
481
482    /// Borrow the reaction pathways.
483    pub fn pathways(&self) -> &Vec<ReactionPathway> {
484        &self.pathways
485    }
486
487    /// Mutably borrow the reaction pathways.
488    pub fn pathways_mut(&mut self) -> &mut Vec<ReactionPathway> {
489        &mut self.pathways
490    }
491}
492
493impl Reaction {
494    pub fn new() -> Self {
495        Self {
496            reaction_id: "rxn_1".to_string(),
497            reaction_name: "Test reaction".to_string(),
498            reactants: vec!["A".to_string()],
499            products: vec!["B".to_string()],
500            reaction_type: ReactionType::Elementary,
501            mechanism: ReactionMechanism::new(),
502        }
503    }
504}
505
506impl ReactionMechanism {
507    pub fn new() -> Self {
508        Self {
509            mechanism_id: "mech_1".to_string(),
510            steps: vec![ReactionStep::new()],
511            intermediates: Vec::new(),
512        }
513    }
514}
515
516impl ReactionStep {
517    pub fn new() -> Self {
518        Self {
519            step_id: "step_1".to_string(),
520            reactants: vec!["A".to_string()],
521            products: vec!["B".to_string()],
522            rate_constant: 1.0,
523            activation_energy: 10.0,
524        }
525    }
526}
527
528impl Species {
529    pub fn new() -> Self {
530        Self {
531            species_id: "species_1".to_string(),
532            species_name: "Test species".to_string(),
533            formula: "CH4".to_string(),
534            molecular_weight: 16.04,
535            properties: SpeciesProperties::new(),
536        }
537    }
538}
539
540impl SpeciesProperties {
541    pub fn new() -> Self {
542        Self {
543            enthalpy: -74.8,
544            entropy: 186.3,
545            gibbs_free_energy: -50.8,
546            heat_capacity: 35.7,
547        }
548    }
549}
550
551impl ReactionPathway {
552    pub fn new() -> Self {
553        Self {
554            pathway_id: "pathway_1".to_string(),
555            pathway_name: "Test pathway".to_string(),
556            reactions: vec!["rxn_1".to_string()],
557            branching_ratios: vec![1.0],
558        }
559    }
560}
561
562impl KineticsCalculator {
563    pub fn new() -> Self {
564        Self {
565            rate_laws: HashMap::new(),
566            rate_constants: HashMap::new(),
567            reaction_rates: HashMap::new(),
568        }
569    }
570
571    /// Register a rate law under its `law_id`, replacing any existing entry.
572    pub fn add_rate_law(&mut self, law: RateLaw) {
573        self.rate_laws.insert(law.law_id.clone(), law);
574    }
575
576    /// Look up a rate law by id.
577    pub fn get_rate_law(&self, law_id: &str) -> Option<&RateLaw> {
578        self.rate_laws.get(law_id)
579    }
580
581    /// List the ids of all registered rate laws.
582    pub fn list_rate_laws(&self) -> Vec<String> {
583        self.rate_laws.keys().cloned().collect()
584    }
585
586    /// Register a rate constant under its `constant_id`, replacing any existing entry.
587    pub fn add_rate_constant(&mut self, constant: RateConstant) {
588        self.rate_constants
589            .insert(constant.constant_id.clone(), constant);
590    }
591
592    /// Look up a rate constant by id.
593    pub fn get_rate_constant(&self, constant_id: &str) -> Option<&RateConstant> {
594        self.rate_constants.get(constant_id)
595    }
596
597    /// List the ids of all registered rate constants.
598    pub fn list_rate_constants(&self) -> Vec<String> {
599        self.rate_constants.keys().cloned().collect()
600    }
601
602    /// Record the instantaneous rate for a reaction id.
603    pub fn set_reaction_rate(&mut self, reaction_id: &str, rate: f64) {
604        self.reaction_rates.insert(reaction_id.to_string(), rate);
605    }
606
607    /// Look up the recorded rate for a reaction id.
608    pub fn get_reaction_rate(&self, reaction_id: &str) -> Option<&f64> {
609        self.reaction_rates.get(reaction_id)
610    }
611
612    /// List the reaction ids that have a recorded rate.
613    pub fn list_reaction_rates(&self) -> Vec<String> {
614        self.reaction_rates.keys().cloned().collect()
615    }
616
617    pub fn initialize(&mut self) -> Result<(), ChemistryError> {
618        Ok(())
619    }
620}
621
622impl RateLaw {
623    pub fn new() -> Self {
624        Self {
625            law_id: "law_1".to_string(),
626            law_type: RateLawType::Elementary,
627            rate_expression: "k * [A]".to_string(),
628            parameters: RateLawParameters::new(),
629        }
630    }
631}
632
633impl RateLawParameters {
634    pub fn new() -> Self {
635        Self {
636            rate_constant: 1.0,
637            reaction_orders: vec![1.0],
638            saturation_constants: Vec::new(),
639        }
640    }
641}
642
643impl RateConstant {
644    pub fn new() -> Self {
645        Self {
646            constant_id: "const_1".to_string(),
647            value: 1.0,
648            temperature_dependence: TemperatureDependence::new(),
649            pressure_dependence: PressureDependence::new(),
650        }
651    }
652}
653
654impl TemperatureDependence {
655    pub fn new() -> Self {
656        Self {
657            arrhenius_parameters: ArrheniusParameters::new(),
658            modified_arrhenius: None,
659        }
660    }
661}
662
663impl ArrheniusParameters {
664    pub fn new() -> Self {
665        Self {
666            pre_exponential: 1.0e13,
667            activation_energy: 10000.0,
668        }
669    }
670}
671
672impl ModifiedArrheniusParameters {
673    pub fn new() -> Self {
674        Self {
675            pre_exponential: 1.0e13,
676            activation_energy: 10000.0,
677            temperature_exponent: 0.0,
678        }
679    }
680}
681
682impl PressureDependence {
683    pub fn new() -> Self {
684        Self {
685            fall_off_parameters: FallOffParameters::new(),
686            third_body_efficiency: 1.0,
687        }
688    }
689}
690
691impl FallOffParameters {
692    pub fn new() -> Self {
693        Self {
694            low_pressure_limit: 1.0,
695            high_pressure_limit: 1.0,
696            fall_off_exponent: 1.0,
697        }
698    }
699}
700
701impl ThermodynamicsCalculator {
702    pub fn new() -> Self {
703        Self {
704            thermodynamic_data: HashMap::new(),
705            equilibrium_calculator: EquilibriumCalculator::new(),
706            phase_calculator: PhaseCalculator::new(),
707        }
708    }
709
710    /// Register thermodynamic data under its `data_id`, replacing any existing entry.
711    pub fn add_thermodynamic_data(&mut self, data: ThermodynamicData) {
712        self.thermodynamic_data.insert(data.data_id.clone(), data);
713    }
714
715    /// Look up thermodynamic data by id.
716    pub fn get_thermodynamic_data(&self, data_id: &str) -> Option<&ThermodynamicData> {
717        self.thermodynamic_data.get(data_id)
718    }
719
720    /// List the ids of all registered thermodynamic data entries.
721    pub fn list_thermodynamic_data(&self) -> Vec<String> {
722        self.thermodynamic_data.keys().cloned().collect()
723    }
724
725    /// Remove thermodynamic data by id.
726    pub fn remove_thermodynamic_data(&mut self, data_id: &str) -> Option<ThermodynamicData> {
727        self.thermodynamic_data.remove(data_id)
728    }
729
730    pub fn initialize(&mut self) -> Result<(), ChemistryError> {
731        self.equilibrium_calculator.initialize()?;
732        self.phase_calculator.initialize()?;
733        Ok(())
734    }
735}
736
737impl ThermodynamicData {
738    pub fn new() -> Self {
739        Self {
740            data_id: "data_1".to_string(),
741            temperature_range: (200.0, 400.0),
742            enthalpy: -74.8,
743            entropy: 186.3,
744            gibbs_free_energy: -50.8,
745            heat_capacity: 35.7,
746        }
747    }
748}
749
750impl EquilibriumCalculator {
751    pub fn new() -> Self {
752        Self {
753            equilibrium_constant: EquilibriumConstant::new(),
754            reaction_quotient: ReactionQuotient::new(),
755            gibbs_energy: GibbsEnergy::new(),
756        }
757    }
758
759    /// Borrow the equilibrium constant.
760    pub fn equilibrium_constant(&self) -> &EquilibriumConstant {
761        &self.equilibrium_constant
762    }
763
764    /// Mutably borrow the equilibrium constant.
765    pub fn equilibrium_constant_mut(&mut self) -> &mut EquilibriumConstant {
766        &mut self.equilibrium_constant
767    }
768
769    /// Borrow the reaction quotient.
770    pub fn reaction_quotient(&self) -> &ReactionQuotient {
771        &self.reaction_quotient
772    }
773
774    /// Mutably borrow the reaction quotient.
775    pub fn reaction_quotient_mut(&mut self) -> &mut ReactionQuotient {
776        &mut self.reaction_quotient
777    }
778
779    /// Borrow the Gibbs energy.
780    pub fn gibbs_energy(&self) -> &GibbsEnergy {
781        &self.gibbs_energy
782    }
783
784    /// Mutably borrow the Gibbs energy.
785    pub fn gibbs_energy_mut(&mut self) -> &mut GibbsEnergy {
786        &mut self.gibbs_energy
787    }
788
789    pub fn initialize(&mut self) -> Result<(), ChemistryError> {
790        Ok(())
791    }
792}
793
794impl EquilibriumConstant {
795    pub fn new() -> Self {
796        Self {
797            value: 1.0,
798            temperature: 298.15,
799            pressure: 1.0,
800        }
801    }
802}
803
804impl ReactionQuotient {
805    pub fn new() -> Self {
806        Self {
807            value: 1.0,
808            concentrations: HashMap::new(),
809        }
810    }
811}
812
813impl GibbsEnergy {
814    pub fn new() -> Self {
815        Self {
816            standard_gibbs: -50.8,
817            actual_gibbs: -50.8,
818            delta_g: 0.0,
819        }
820    }
821}
822
823impl PhaseCalculator {
824    pub fn new() -> Self {
825        Self {
826            phase_diagrams: HashMap::new(),
827            phase_transitions: HashMap::new(),
828            phase_equilibria: HashMap::new(),
829        }
830    }
831
832    /// Register a phase diagram under its `diagram_id`, replacing any existing entry.
833    pub fn add_phase_diagram(&mut self, diagram: PhaseDiagram) {
834        self.phase_diagrams
835            .insert(diagram.diagram_id.clone(), diagram);
836    }
837
838    /// Look up a phase diagram by id.
839    pub fn get_phase_diagram(&self, diagram_id: &str) -> Option<&PhaseDiagram> {
840        self.phase_diagrams.get(diagram_id)
841    }
842
843    /// List the ids of all registered phase diagrams.
844    pub fn list_phase_diagrams(&self) -> Vec<String> {
845        self.phase_diagrams.keys().cloned().collect()
846    }
847
848    /// Register a phase transition under its `transition_id`, replacing any existing entry.
849    pub fn add_phase_transition(&mut self, transition: PhaseTransition) {
850        self.phase_transitions
851            .insert(transition.transition_id.clone(), transition);
852    }
853
854    /// Look up a phase transition by id.
855    pub fn get_phase_transition(&self, transition_id: &str) -> Option<&PhaseTransition> {
856        self.phase_transitions.get(transition_id)
857    }
858
859    /// List the ids of all registered phase transitions.
860    pub fn list_phase_transitions(&self) -> Vec<String> {
861        self.phase_transitions.keys().cloned().collect()
862    }
863
864    /// Register a phase equilibrium under its `equilibrium_id`, replacing any existing entry.
865    pub fn add_phase_equilibrium(&mut self, equilibrium: PhaseEquilibrium) {
866        self.phase_equilibria
867            .insert(equilibrium.equilibrium_id.clone(), equilibrium);
868    }
869
870    /// Look up a phase equilibrium by id.
871    pub fn get_phase_equilibrium(&self, equilibrium_id: &str) -> Option<&PhaseEquilibrium> {
872        self.phase_equilibria.get(equilibrium_id)
873    }
874
875    /// List the ids of all registered phase equilibria.
876    pub fn list_phase_equilibria(&self) -> Vec<String> {
877        self.phase_equilibria.keys().cloned().collect()
878    }
879
880    pub fn initialize(&mut self) -> Result<(), ChemistryError> {
881        Ok(())
882    }
883}
884
885impl PhaseDiagram {
886    pub fn new() -> Self {
887        Self {
888            diagram_id: "diagram_1".to_string(),
889            phases: vec![Phase::new()],
890            boundaries: Vec::new(),
891        }
892    }
893}
894
895impl Phase {
896    pub fn new() -> Self {
897        Self {
898            phase_id: "phase_1".to_string(),
899            phase_name: "Liquid".to_string(),
900            phase_type: PhaseType::Liquid,
901            composition: HashMap::new(),
902        }
903    }
904}
905
906impl PhaseBoundary {
907    pub fn new() -> Self {
908        Self {
909            boundary_id: "boundary_1".to_string(),
910            boundary_type: BoundaryType::Boiling,
911            conditions: vec![BoundaryCondition::new()],
912        }
913    }
914}
915
916impl BoundaryCondition {
917    pub fn new() -> Self {
918        Self {
919            temperature: 373.15,
920            pressure: 1.0,
921            composition: HashMap::new(),
922        }
923    }
924}
925
926impl PhaseTransition {
927    pub fn new() -> Self {
928        Self {
929            transition_id: "transition_1".to_string(),
930            transition_type: TransitionType::Fusion,
931            enthalpy_change: 6.01,
932            entropy_change: 22.0,
933        }
934    }
935}
936
937impl PhaseEquilibrium {
938    pub fn new() -> Self {
939        Self {
940            equilibrium_id: "eq_1".to_string(),
941            phases: vec!["phase_1".to_string()],
942            equilibrium_conditions: EquilibriumConditions::new(),
943        }
944    }
945}
946
947impl EquilibriumConditions {
948    pub fn new() -> Self {
949        Self {
950            temperature: 273.15,
951            pressure: 1.0,
952            chemical_potentials: HashMap::new(),
953        }
954    }
955}