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 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}