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