cmtool_assemble/generators/
descriptors.rs1use crate::CMError;
4use cmtool_data::PhaseCM;
5
6pub struct Reactor0DDescriptor {
7 liquid_volume: f64,
8 gas_volume: f64,
9}
10
11impl Reactor0DDescriptor {
12 pub fn liquid_volume(&self) -> f64 {
13 self.liquid_volume
14 }
15
16 pub fn gas_volume(&self) -> f64 {
17 self.gas_volume
18 }
19
20 pub fn is_valid(&self) -> Result<(), CMError> {
21 if !self.liquid_volume.is_finite() || self.liquid_volume <= 0.0 {
22 return Err(CMError::Descriptor(
23 "Liquid volume must be a finite positive number".to_owned(),
24 ));
25 }
26 if self.gas_volume < 0. {
28 return Err(CMError::Descriptor(
29 "Gas volume must be a finite positive number".to_owned(),
30 ));
31 }
32
33 Ok(())
34 }
35
36 pub fn new<L, G>(liquid_volume: L, gas_volume: G) -> Result<Self, CMError>
37 where
38 L: Into<f64>,
39 G: Into<f64>,
40 {
41 let reactor = Self {
42 liquid_volume: liquid_volume.into(),
43 gas_volume: gas_volume.into(),
44 };
45
46 reactor.is_valid()?;
47 Ok(reactor)
48 }
49
50 pub fn from_fraction(total_volume: f64, gas_fraction: f64) -> Result<Self, CMError> {
51 if !(0. ..=1.).contains(&gas_fraction) {
52 return Err(CMError::Descriptor(format!(
53 "Gas fraction must be between 0 and 1, got {}",
54 gas_fraction
55 )));
56 }
57 let gas_volume = gas_fraction * total_volume;
58 let liquid_volume = total_volume - gas_volume;
59
60 Self::new(liquid_volume, gas_volume)
61 }
62}
63
64pub struct PFRDescription {
65 pub(super) n_compartment: usize,
66 pub(super) length: f64,
67 pub(super) diameter: f64,
68 pub(super) liquid_flow: f64,
69 pub(super) gas_flow: f64,
70 pub(super) gas_fraction: f64,
71 pub(super) axial_dispersion: f64,
72}
73
74impl PFRDescription {
75 pub fn new(
76 n_compartment: usize,
77 length: impl Into<f64>,
78 diameter: impl Into<f64>,
79 liquid_flow: impl Into<f64>,
80 gas_flow: impl Into<f64>,
81 gas_fraction: impl Into<f64>,
82 axial_dispersion: impl Into<f64>,
83 ) -> Result<PFRDescription, CMError> {
84 let pfr = Self {
85 n_compartment,
86 length: length.into(),
87 diameter: diameter.into(),
88 liquid_flow: liquid_flow.into(),
89 gas_flow: gas_flow.into(),
90 gas_fraction: gas_fraction.into(),
91 axial_dispersion: axial_dispersion.into(),
92 };
93
94 pfr.is_valid()?;
95 Ok(pfr)
96 }
97
98 pub fn get_liquid_flow(&self) -> f64 {
99 self.liquid_flow
100 }
101
102 pub fn get_gas_flow(&self) -> f64 {
103 self.gas_flow
104 }
105 pub fn get_gas_fraction(&self) -> f64 {
106 self.gas_fraction
107 }
108 pub fn get_liquid_fraction(&self) -> f64 {
109 1. - self.gas_fraction
110 }
111 #[allow(unused)]
113 pub fn geometrical_volume(&self) -> f64 {
114 self.length * (self.diameter * self.diameter) * std::f64::consts::PI / 4.
115 }
116
117 pub fn extract_volume_flow(&self, phase: PhaseCM) -> (f64, f64) {
118 match phase {
119 PhaseCM::Liquid => (self.get_liquid_fraction(), self.get_liquid_flow()),
120 PhaseCM::Gas => (self.get_gas_fraction(), self.get_gas_flow()),
121 }
122 }
123
124 pub fn is_valid(&self) -> Result<(), CMError> {
125 if self.n_compartment == 0 {
126 return Err(CMError::Descriptor("n_compartment must be >= 1".to_owned()));
127 }
128 if !self.length.is_finite() || self.length <= 0.0 {
129 return Err(CMError::Descriptor(
130 "length must be a finite positive number".to_owned(),
131 ));
132 }
133 if !self.diameter.is_finite() || self.diameter <= 0.0 {
134 return Err(CMError::Descriptor(
135 "diameter must be a finite positive number".to_owned(),
136 ));
137 }
138 if !self.liquid_flow.is_finite() || self.liquid_flow < 0.0 {
139 return Err(CMError::Descriptor(
140 "liquid_flow must be a finite non-negative number".to_owned(),
141 ));
142 }
143 if !self.gas_flow.is_finite() || self.gas_flow < 0.0 {
144 return Err(CMError::Descriptor(
145 "gas_flow must be a finite non-negative number".to_owned(),
146 ));
147 }
148 if !self.gas_fraction.is_finite() || self.gas_fraction < 0.0 || self.gas_fraction > 1.0 {
149 return Err(CMError::Descriptor(
150 "gas_fraction must be between 0.0 and 1.0".to_owned(),
151 ));
152 }
153 if !self.axial_dispersion.is_finite() || self.axial_dispersion < 0.0 {
154 return Err(CMError::Descriptor(
155 "axial_dispersion must be a finite non-negative number".to_owned(),
156 ));
157 }
158
159 if self.liquid_flow == 0.0 && self.gas_flow == 0.0 {
160 return Err(CMError::Descriptor(
161 "At least one of liquid_flow or gas_flow must be positive".to_owned(),
162 ));
163 }
164 if (self.gas_flow > 0.0 && self.liquid_flow == 0.0)
165 && (self.gas_fraction < 0.0 || self.gas_fraction > 1.0)
166 {
167 return Err(CMError::Descriptor(
168 "Invalid gas_fraction given flows".to_owned(),
169 ));
170 }
171
172 if self.length < self.diameter {
174 return Err(CMError::Descriptor(
175 "length should be greater than or equal to diameter".to_owned(),
176 ));
177 }
178
179 Ok(())
180 }
181}