symrs/
system.rs

1use std::{collections::HashSet, sync::LazyLock};
2
3use itertools::Itertools;
4use log::{debug, info};
5
6use super::*;
7use crate::symbol;
8
9#[derive(Debug, Clone)]
10pub struct System {
11    pub unknowns: Vec<Func>,
12    pub known_unknowns: Vec<Func>,
13    pub knowns: Vec<Func>,
14    pub equations: Vec<Equation>,
15}
16
17static SHAPE_MATRIXES: LazyLock<[Symbol; 4]> = LazyLock::new(|| {
18    [
19        Symbol::new("M^n"),
20        Symbol::new("A^n"),
21        Symbol::new("M^n,n-1"),
22        Symbol::new("A^n,n-1"),
23    ]
24});
25
26impl std::fmt::Display for System {
27    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
28        write!(
29            f,
30            "System ({}), ({}), ({})\n{}",
31            self.unknowns
32                .iter()
33                .map(|f| f.str())
34                .collect::<Vec<_>>()
35                .join(", "),
36            self.known_unknowns
37                .iter()
38                .map(|f| f.str())
39                .collect::<Vec<_>>()
40                .join(", "),
41            self.knowns
42                .iter()
43                .map(|f| f.str())
44                .collect::<Vec<_>>()
45                .join(", "),
46            self.equations
47                .iter()
48                .map(|e| format!("> {}", e.str()))
49                .collect::<Vec<_>>()
50                .join("\n")
51        )
52    }
53}
54
55#[derive(thiserror::Error, Debug)]
56pub enum SystemError {
57    #[error("failed to simplify system")]
58    SimplificationFailed(#[source] SolvingError),
59}
60
61impl System {
62    pub fn new<
63        'a,
64        T: IntoIterator<Item = &'a str>,
65        U: IntoIterator<Item = &'a str>,
66        V: IntoIterator<Item = &'a Equation>,
67    >(
68        unknowns: T,
69        knowns: U,
70        equations: V,
71    ) -> Self {
72        Self {
73            unknowns: unknowns.into_iter().map(|s| Func::new(s, [])).collect(),
74            knowns: knowns.into_iter().map(|s| Func::new(s, [])).collect(),
75            equations: equations.into_iter().cloned().collect(),
76            known_unknowns: vec![],
77        }
78    }
79
80    pub fn to_first_order_in_time(&self) -> Self {
81        let mut unknowns_with_snd_time_derivatives = HashSet::new();
82        for unknown in &self.unknowns {
83            let snd_time_derivative = unknown.diff("t", 2);
84            for equation in &self.equations {
85                if equation.has(snd_time_derivative.get_ref()) {
86                    unknowns_with_snd_time_derivatives.insert(unknown);
87                    info!(
88                        "Unknown `{}` has a second order time derivative",
89                        unknown.str()
90                    );
91                    break;
92                }
93            }
94        }
95        let mut unknowns = self.unknowns.clone();
96        let mut substitutions = vec![];
97        let mut equations =
98            Vec::with_capacity(self.equations.len() + unknowns_with_snd_time_derivatives.len());
99        if !unknowns_with_snd_time_derivatives.is_empty() {
100            info!("Converting system to first order in time");
101        }
102        for unknown in unknowns_with_snd_time_derivatives {
103            let fst_time_derivative = Func::new(&format!("dt_{}", unknown.name), []);
104
105            substitutions.push([unknown.diff("t", 2), fst_time_derivative.diff("t", 1)]);
106            equations.push(Equation {
107                lhs: fst_time_derivative.clone_box(),
108                rhs: unknown.diff("t", 1),
109            });
110            unknowns.push(fst_time_derivative);
111        }
112
113        equations.extend(
114            self.equations
115                .iter()
116                .map(|e| e.subs(&substitutions).as_eq().expect("equation")),
117        );
118
119        System {
120            unknowns,
121            knowns: self.knowns.clone(),
122            equations,
123            known_unknowns: vec![],
124        }
125    }
126
127    pub fn time_discretized(&self) -> Self {
128        let mut unknowns: Vec<Func> = Vec::with_capacity(self.unknowns.len());
129        let mut known_unknowns: Vec<Func> = Vec::with_capacity(self.unknowns.len());
130        let mut knowns: Vec<Func> = Vec::with_capacity(self.unknowns.len() + self.knowns.len());
131
132        // let substitutions: Vec<_> = iproduct!(self.unknowns.iter(), unknowns.iter()).chain(iproduct!(self.knowns.iter().zip,knowns.iter()).map(|(replaced, replacement)| [replaced, replacement]).collect();
133        let mut substitutions: Vec<[Box<dyn Expr>; 2]> =
134            Vec::with_capacity(self.unknowns.len() + self.knowns.len());
135
136        for f in &self.unknowns {
137            let [prev, curr] = f.time_discretize();
138            known_unknowns.push(prev);
139            unknowns.push(curr);
140        }
141
142        for f in &self.knowns {
143            let [prev, curr] = f.time_discretize();
144            knowns.push(prev);
145            knowns.push(curr);
146        }
147
148        for f in self.unknowns.iter().chain(self.knowns.iter()) {
149            let [prev, curr] = f.time_discretize();
150
151            let curr = &curr.clone_box();
152            let prev = &prev.clone_box();
153
154            let k = &Symbol::new_box("k").clone_box();
155            let theta = &Symbol::new_box("θ").clone_box();
156
157            substitutions.push([f.diff("t", 1).clone_box(), (curr - prev) / k]);
158            substitutions.push([
159                f.clone_box(),
160                theta * curr + (Integer::new_box(1) - theta) * prev,
161            ])
162        }
163
164        let equations: Vec<_> = self
165            .equations
166            .iter()
167            .map(|e| e.subs(&substitutions).as_eq().unwrap())
168            .collect();
169
170        System {
171            unknowns,
172            knowns,
173            equations,
174            known_unknowns,
175        }
176    }
177
178    pub fn simplified(&self) -> Result<Self, SystemError> {
179        info!("Simplifying system so that each equation has one unknown");
180        let mut equations = self.equations.clone();
181        let mut unsolved_unknowns: HashSet<&Func> = HashSet::from_iter(self.unknowns.iter());
182
183        for i in (0..self.equations.len()).rev() {
184            for unknown in self.unknowns.iter().rev() {
185                if !unsolved_unknowns.contains(unknown) {
186                    continue;
187                }
188                if equations[i].has(unknown.get_ref()) {
189                    info!("Solving equation {} for {}", i, unknown.str());
190                    let solved_equation = equations[i]
191                        .solve([unknown.get_ref()])
192                        .map_err(|e| SystemError::SimplificationFailed(e))?
193                        .expand()
194                        .as_eq()
195                        .unwrap();
196                    equations[i] = solved_equation;
197                    unsolved_unknowns.remove(unknown);
198                    if unsolved_unknowns.len() == 0 {
199                        break;
200                    }
201
202                    let substitution =
203                        vec![[equations[i].lhs.clone_box(), equations[i].rhs.clone_box()]];
204                    debug!("Equation {} is now {}", i, equations[i].str());
205                    debug!("substitution: {:?}", substitution);
206
207                    for j in 0..i {
208                        info!(
209                            "Substituting {} in equation {} using equation {}",
210                            equations[i].lhs.str(),
211                            j,
212                            i
213                        );
214                        equations[j] = equations[j].subs(&substitution).as_eq().expect("equation");
215                        equations[j] = equations[j]
216                            .solve(unsolved_unknowns.iter().map(|f| f.get_ref()))
217                            .map_err(|e| SystemError::SimplificationFailed(e))?;
218                    }
219                    break;
220                }
221            }
222        }
223
224        Ok(self.with_equations(equations))
225    }
226
227    pub fn factor(&self) -> Self {
228        let laplacian = symbol!("laplacian");
229        let matrixes = [Symbol::new_box("M^n"), Symbol::new_box("A^n")];
230        let symbols: Vec<Box<dyn Expr>> = self
231            .unknowns
232            .iter()
233            .map(|f| f.get_ref())
234            .chain(self.known_unknowns.iter().map(|f| f.get_ref()))
235            .chain(matrixes.iter().map(|m| m.get_ref()))
236            .chain(self.knowns.iter().map(|f| f.get_ref()))
237            .flat_map(|f| [laplacian * f, f.clone_box()])
238            .chain([Symbol::new_box("k"), Symbol::new_box("theta")])
239            .collect();
240        let symbols: Vec<_> = symbols.iter().map(|e| e.get_ref()).collect();
241        let equations: Vec<_> = self
242            .equations
243            .iter()
244            .map(|eq| eq.factor(&symbols).as_eq().unwrap())
245            .collect();
246
247        self.with_equations(equations)
248    }
249
250    pub fn matrixify(&self) -> Self {
251        let mut knowns: Vec<Func> = Vec::with_capacity(self.knowns.len());
252        let mut known_unknowns: Vec<Func> = Vec::with_capacity(self.known_unknowns.len());
253        let mut unknowns: Vec<Func> = Vec::with_capacity(self.unknowns.len());
254        let laplacian = symbol!("laplacian");
255
256        let mass_mat = symbol!("M^n");
257        let mass_mat_prev = symbol!("M^n,n-1");
258        let laplace_mat = symbol!("A^n");
259        let laplace_mat_prev = symbol!("A^n,n-1");
260
261        let mut substitutions =
262            Vec::with_capacity(2 * knowns.len() + 2 * known_unknowns.len() + unknowns.len());
263
264        for known in &self.knowns {
265            let known_vec = known.to_vector();
266            substitutions.push([known.clone_box(), known_vec.clone_box()]);
267            knowns.push(known_vec);
268        }
269
270        for unknown in &self.unknowns {
271            let unknown_vec = unknown.to_vector();
272            substitutions.push([
273                laplacian * unknown.get_ref(),
274                -laplace_mat * unknown_vec.get_ref(),
275            ]);
276            substitutions.push([unknown.clone_box(), mass_mat * unknown_vec.get_ref()]);
277            unknowns.push(unknown_vec);
278        }
279
280        for unknown in &self.known_unknowns {
281            let unknown_vec = unknown.to_vector();
282            substitutions.push([
283                laplacian * unknown.get_ref(),
284                -laplace_mat_prev * unknown_vec.get_ref(),
285            ]);
286            substitutions.push([unknown.clone_box(), mass_mat_prev * unknown_vec.get_ref()]);
287            known_unknowns.push(unknown_vec);
288        }
289
290        let equations = self
291            .equations
292            .iter()
293            .map(|e| {
294                e.subs(&substitutions)
295                    .factor(&unknowns.iter().map(|e| e.get_ref()).collect::<Vec<_>>())
296                    .as_eq()
297                    .unwrap()
298            })
299            .collect();
300
301        System {
302            unknowns,
303            knowns,
304            known_unknowns,
305            equations,
306        }
307    }
308
309    pub fn to_constant_mesh(&self) -> Self {
310        let mass_mat = Symbol::new_box("M^n");
311        let mass_mat_prev = Symbol::new_box("M^n,n-1");
312        let laplace_mat = Symbol::new_box("A^n");
313        let laplace_mat_prev = Symbol::new_box("A^n,n-1");
314
315        let subs = [[mass_mat_prev, mass_mat], [laplace_mat_prev, laplace_mat]];
316
317        self.with_equations(
318            self.equations
319                .iter()
320                .map(|eq| eq.subs(&subs).as_eq().unwrap())
321                .collect(),
322        )
323        .factor()
324    }
325
326    pub fn to_crank_nikolson(&self) -> Self {
327        info!("Applying a Crank-Nikolson time discretization");
328        let theta = Symbol::new_box("θ");
329        self.subs(&[[theta, Rational::new_box(1, 2)]]).simplify()
330    }
331
332    pub fn to_explicit_euler(&self) -> Self {
333        info!("Applying an explicit Euler time discretization");
334        let theta = Symbol::new_box("θ");
335        self.subs(&[[theta, Integer::new_box(0)]])
336    }
337
338    pub fn to_implicit_euler(&self) -> Self {
339        info!("Applying an implicit Euler time discretization");
340        let theta = Symbol::new_box("θ");
341        self.subs(&[[theta, Integer::new_box(1)]])
342    }
343
344    pub fn subs(&self, substitutions: &[[Box<dyn Expr>; 2]]) -> Self {
345        self.with_equations(
346            self.equations
347                .iter()
348                .map(|e| e.subs(substitutions).as_eq().unwrap())
349                .collect(),
350        )
351    }
352
353    pub fn expand(&self) -> Self {
354        self.with_equations(
355            self.equations
356                .iter()
357                .map(|e| e.expand().as_eq().unwrap())
358                .collect(),
359        )
360    }
361
362    pub fn simplify(&self) -> Self {
363        self.expand().factor().with_equations(
364            self.equations
365                .iter()
366                .map(|expr| expr.simplify().as_eq().unwrap())
367                .collect(),
368        )
369    }
370
371    pub fn with_equations(&self, equations: Vec<Equation>) -> Self {
372        System {
373            known_unknowns: self.known_unknowns.clone(),
374            unknowns: self.unknowns.clone(),
375            knowns: self.knowns.clone(),
376            equations,
377        }
378    }
379
380    pub fn vectors(&self) -> impl Iterator<Item = (&dyn Expr, bool)> {
381        self.unknowns
382            .iter()
383            .map(|u| (u, true))
384            .chain(self.known_unknowns.iter().map(|u| (u, true)))
385            .chain(self.knowns.iter().map(|f| (f, false)))
386            .map(|(f, is_unknown)| (f.get_ref(), is_unknown))
387    }
388
389    pub fn matrixes(&self) -> impl Iterator<Item = &dyn Expr> {
390        SHAPE_MATRIXES.iter().map(|m| m.get_ref())
391    }
392
393    pub fn num_vectors(&self) -> usize {
394        [&self.unknowns, &self.known_unknowns, &self.knowns]
395            .iter()
396            .map(|v| v.len())
397            .reduce(|acc, e| acc + e)
398            .unwrap()
399    }
400
401    pub fn eqs_in_solving_order(&self) -> impl Iterator<Item = &Equation> {
402        fn get_rhs_unknowns(system: &System, eq: &Equation) -> impl Iterator<Item = String> {
403            system.unknowns.iter().filter_map(|unknown| {
404                if eq.has(unknown) {
405                    Some(unknown.str())
406                } else {
407                    None
408                }
409            })
410        }
411
412        self.equations
413            .iter()
414            .map(|e| (e, get_rhs_unknowns(self, e).count()))
415            .sorted_by_key(|e| e.1)
416            .map(|(e, _)| e)
417    }
418
419    pub fn equation_lhs_unknowns(&self, equation: &Equation) -> impl Iterator<Item = &dyn Expr> {
420        self.unknowns.iter().filter_map(|unknown| {
421            if equation.has(unknown) {
422                Some(unknown.get_ref())
423            } else {
424                None
425            }
426        })
427    }
428}