1#[allow(unused_imports)]
6use super::functions::*;
7use super::functions::{foam_header, parse_dict_tokens, strip_foam_comments, tokenise_foam};
8#[allow(unused_imports)]
9use super::functions_2::*;
10
11pub struct FoamBc {
13 pub patch_name: String,
15 pub bc_type: String,
17 pub value: Option<String>,
19}
20#[allow(dead_code)]
22impl FoamBc {
23 pub fn zero_gradient(patch: &str) -> Self {
25 FoamBc {
26 patch_name: patch.to_string(),
27 bc_type: "zeroGradient".to_string(),
28 value: None,
29 }
30 }
31 pub fn fixed_scalar(patch: &str, val: f64) -> Self {
33 FoamBc {
34 patch_name: patch.to_string(),
35 bc_type: "fixedValue".to_string(),
36 value: Some(format!("uniform {}", val)),
37 }
38 }
39 pub fn fixed_vector(patch: &str, val: [f64; 3]) -> Self {
41 FoamBc {
42 patch_name: patch.to_string(),
43 bc_type: "fixedValue".to_string(),
44 value: Some(format!("uniform ({} {} {})", val[0], val[1], val[2])),
45 }
46 }
47 pub fn no_slip(patch: &str) -> Self {
49 FoamBc {
50 patch_name: patch.to_string(),
51 bc_type: "noSlip".to_string(),
52 value: None,
53 }
54 }
55 pub fn symmetry(patch: &str) -> Self {
57 FoamBc {
58 patch_name: patch.to_string(),
59 bc_type: "symmetry".to_string(),
60 value: None,
61 }
62 }
63 pub fn inlet_outlet_scalar(patch: &str, inlet_val: f64) -> Self {
65 FoamBc {
66 patch_name: patch.to_string(),
67 bc_type: "inletOutlet".to_string(),
68 value: Some(format!("uniform {}", inlet_val)),
69 }
70 }
71 pub fn pressure_inlet_outlet(patch: &str, val: f64) -> Self {
73 FoamBc {
74 patch_name: patch.to_string(),
75 bc_type: "totalPressure".to_string(),
76 value: Some(format!("uniform {}", val)),
77 }
78 }
79 pub fn empty(patch: &str) -> Self {
81 FoamBc {
82 patch_name: patch.to_string(),
83 bc_type: "empty".to_string(),
84 value: None,
85 }
86 }
87}
88pub struct FoamMesh {
90 pub points: Vec<[f64; 3]>,
92 pub faces: Vec<Vec<usize>>,
94 pub owner: Vec<usize>,
96 pub neighbour: Vec<i64>,
98 pub n_cells: usize,
100 pub boundary_patches: Vec<FoamPatch>,
102}
103impl FoamMesh {
104 pub fn box_mesh(lx: f64, ly: f64, lz: f64, nx: usize, ny: usize, nz: usize) -> Self {
110 let mut points = Vec::with_capacity((nx + 1) * (ny + 1) * (nz + 1));
111 for k in 0..=nz {
112 for j in 0..=ny {
113 for i in 0..=nx {
114 points.push([
115 lx * i as f64 / nx as f64,
116 ly * j as f64 / ny as f64,
117 lz * k as f64 / nz as f64,
118 ]);
119 }
120 }
121 }
122 let pid =
123 |i: usize, j: usize, k: usize| -> usize { k * (ny + 1) * (nx + 1) + j * (nx + 1) + i };
124 let cid = |i: usize, j: usize, k: usize| -> usize { k * ny * nx + j * nx + i };
125 let n_cells = nx * ny * nz;
126 let mut faces: Vec<Vec<usize>> = Vec::new();
127 let mut owner: Vec<usize> = Vec::new();
128 let mut neighbour: Vec<i64> = Vec::new();
129 for k in 0..nz {
130 for j in 0..ny {
131 for i in 1..nx {
132 faces.push(vec![
133 pid(i, j, k),
134 pid(i, j + 1, k),
135 pid(i, j + 1, k + 1),
136 pid(i, j, k + 1),
137 ]);
138 owner.push(cid(i - 1, j, k));
139 neighbour.push(cid(i, j, k) as i64);
140 }
141 }
142 }
143 for k in 0..nz {
144 for j in 1..ny {
145 for i in 0..nx {
146 faces.push(vec![
147 pid(i, j, k),
148 pid(i + 1, j, k),
149 pid(i + 1, j, k + 1),
150 pid(i, j, k + 1),
151 ]);
152 owner.push(cid(i, j - 1, k));
153 neighbour.push(cid(i, j, k) as i64);
154 }
155 }
156 }
157 for k in 1..nz {
158 for j in 0..ny {
159 for i in 0..nx {
160 faces.push(vec![
161 pid(i, j, k),
162 pid(i + 1, j, k),
163 pid(i + 1, j + 1, k),
164 pid(i, j + 1, k),
165 ]);
166 owner.push(cid(i, j, k - 1));
167 neighbour.push(cid(i, j, k) as i64);
168 }
169 }
170 }
171 let xmin_start = faces.len();
172 for k in 0..nz {
173 for j in 0..ny {
174 faces.push(vec![
175 pid(0, j, k),
176 pid(0, j, k + 1),
177 pid(0, j + 1, k + 1),
178 pid(0, j + 1, k),
179 ]);
180 owner.push(cid(0, j, k));
181 neighbour.push(-1);
182 }
183 }
184 let xmin_n = faces.len() - xmin_start;
185 let xmax_start = faces.len();
186 for k in 0..nz {
187 for j in 0..ny {
188 faces.push(vec![
189 pid(nx, j, k),
190 pid(nx, j + 1, k),
191 pid(nx, j + 1, k + 1),
192 pid(nx, j, k + 1),
193 ]);
194 owner.push(cid(nx - 1, j, k));
195 neighbour.push(-1);
196 }
197 }
198 let xmax_n = faces.len() - xmax_start;
199 let ymin_start = faces.len();
200 for k in 0..nz {
201 for i in 0..nx {
202 faces.push(vec![
203 pid(i, 0, k),
204 pid(i + 1, 0, k),
205 pid(i + 1, 0, k + 1),
206 pid(i, 0, k + 1),
207 ]);
208 owner.push(cid(i, 0, k));
209 neighbour.push(-1);
210 }
211 }
212 let ymin_n = faces.len() - ymin_start;
213 let ymax_start = faces.len();
214 for k in 0..nz {
215 for i in 0..nx {
216 faces.push(vec![
217 pid(i, ny, k),
218 pid(i, ny, k + 1),
219 pid(i + 1, ny, k + 1),
220 pid(i + 1, ny, k),
221 ]);
222 owner.push(cid(i, ny - 1, k));
223 neighbour.push(-1);
224 }
225 }
226 let ymax_n = faces.len() - ymax_start;
227 let zmin_start = faces.len();
228 for j in 0..ny {
229 for i in 0..nx {
230 faces.push(vec![
231 pid(i, j, 0),
232 pid(i, j + 1, 0),
233 pid(i + 1, j + 1, 0),
234 pid(i + 1, j, 0),
235 ]);
236 owner.push(cid(i, j, 0));
237 neighbour.push(-1);
238 }
239 }
240 let zmin_n = faces.len() - zmin_start;
241 let zmax_start = faces.len();
242 for j in 0..ny {
243 for i in 0..nx {
244 faces.push(vec![
245 pid(i, j, nz),
246 pid(i + 1, j, nz),
247 pid(i + 1, j + 1, nz),
248 pid(i, j + 1, nz),
249 ]);
250 owner.push(cid(i, j, nz - 1));
251 neighbour.push(-1);
252 }
253 }
254 let zmax_n = faces.len() - zmax_start;
255 let boundary_patches = vec![
256 FoamPatch {
257 name: "xmin".into(),
258 patch_type: "patch".into(),
259 start_face: xmin_start,
260 n_faces: xmin_n,
261 },
262 FoamPatch {
263 name: "xmax".into(),
264 patch_type: "patch".into(),
265 start_face: xmax_start,
266 n_faces: xmax_n,
267 },
268 FoamPatch {
269 name: "ymin".into(),
270 patch_type: "patch".into(),
271 start_face: ymin_start,
272 n_faces: ymin_n,
273 },
274 FoamPatch {
275 name: "ymax".into(),
276 patch_type: "patch".into(),
277 start_face: ymax_start,
278 n_faces: ymax_n,
279 },
280 FoamPatch {
281 name: "zmin".into(),
282 patch_type: "patch".into(),
283 start_face: zmin_start,
284 n_faces: zmin_n,
285 },
286 FoamPatch {
287 name: "zmax".into(),
288 patch_type: "patch".into(),
289 start_face: zmax_start,
290 n_faces: zmax_n,
291 },
292 ];
293 FoamMesh {
294 points,
295 faces,
296 owner,
297 neighbour,
298 n_cells,
299 boundary_patches,
300 }
301 }
302 pub fn write_points(&self) -> String {
304 let mut s = foam_header("vectorField", "points");
305 s.push('\n');
306 s.push_str(&format!("{}\n(\n", self.points.len()));
307 for p in &self.points {
308 s.push_str(&format!("({} {} {})\n", p[0], p[1], p[2]));
309 }
310 s.push_str(")\n");
311 s
312 }
313 pub fn write_faces(&self) -> String {
315 let mut s = foam_header("faceList", "faces");
316 s.push('\n');
317 s.push_str(&format!("{}\n(\n", self.faces.len()));
318 for face in &self.faces {
319 let verts: Vec<String> = face.iter().map(|v| v.to_string()).collect();
320 s.push_str(&format!("{}({})\n", face.len(), verts.join(" ")));
321 }
322 s.push_str(")\n");
323 s
324 }
325 pub fn write_owner(&self) -> String {
327 let mut s = foam_header("labelList", "owner");
328 s.push('\n');
329 s.push_str(&format!("{}\n(\n", self.owner.len()));
330 for &o in &self.owner {
331 s.push_str(&format!("{}\n", o));
332 }
333 s.push_str(")\n");
334 s
335 }
336 pub fn write_neighbour(&self) -> String {
338 let mut s = foam_header("labelList", "neighbour");
339 s.push('\n');
340 s.push_str(&format!("{}\n(\n", self.neighbour.len()));
341 for &n in &self.neighbour {
342 s.push_str(&format!("{}\n", n));
343 }
344 s.push_str(")\n");
345 s
346 }
347 pub fn write_boundary(&self) -> String {
349 let mut s = foam_header("polyBoundaryMesh", "boundary");
350 s.push('\n');
351 s.push_str(&format!("{}\n(\n", self.boundary_patches.len()));
352 for patch in &self.boundary_patches {
353 s.push_str(
354 &format!(
355 " {}\n {{\n type {};\n nFaces {};\n startFace {};\n }}\n",
356 patch.name, patch.patch_type, patch.n_faces, patch.start_face
357 ),
358 );
359 }
360 s.push_str(")\n");
361 s
362 }
363}
364#[allow(dead_code)]
365impl FoamMesh {
366 pub fn n_internal_faces(&self) -> usize {
368 self.neighbour.iter().filter(|&&n| n >= 0).count()
369 }
370 pub fn n_boundary_faces(&self) -> usize {
372 self.neighbour.iter().filter(|&&n| n < 0).count()
373 }
374 pub fn n_faces(&self) -> usize {
376 self.faces.len()
377 }
378 pub fn n_points(&self) -> usize {
380 self.points.len()
381 }
382 pub fn bounding_box(&self) -> ([f64; 3], [f64; 3]) {
384 if self.points.is_empty() {
385 return ([0.0; 3], [0.0; 3]);
386 }
387 let mut min = self.points[0];
388 let mut max = self.points[0];
389 for p in &self.points {
390 for i in 0..3 {
391 if p[i] < min[i] {
392 min[i] = p[i];
393 }
394 if p[i] > max[i] {
395 max[i] = p[i];
396 }
397 }
398 }
399 (min, max)
400 }
401 pub fn centre(&self) -> [f64; 3] {
403 let (min, max) = self.bounding_box();
404 [
405 (min[0] + max[0]) * 0.5,
406 (min[1] + max[1]) * 0.5,
407 (min[2] + max[2]) * 0.5,
408 ]
409 }
410 pub fn check_topology(&self) -> bool {
412 self.owner.len() == self.faces.len() && self.neighbour.len() == self.faces.len()
413 }
414 pub fn find_patch(&self, name: &str) -> Option<&FoamPatch> {
416 self.boundary_patches.iter().find(|p| p.name == name)
417 }
418 pub fn patch_names(&self) -> Vec<&str> {
420 self.boundary_patches
421 .iter()
422 .map(|p| p.name.as_str())
423 .collect()
424 }
425}
426#[allow(dead_code)]
428#[derive(Debug, Clone)]
429pub struct FoamTimeDir {
430 pub time: f64,
432 pub dir_name: String,
434 pub fields: Vec<String>,
436}
437pub struct ControlDict {
439 pub application: String,
441 pub start_time: f64,
443 pub end_time: f64,
445 pub delta_t: f64,
447 pub write_interval: f64,
449 pub write_format: String,
451 pub write_precision: usize,
453}
454impl ControlDict {
455 pub fn new(application: &str, end_time: f64, dt: f64) -> Self {
457 ControlDict {
458 application: application.to_string(),
459 start_time: 0.0,
460 end_time,
461 delta_t: dt,
462 write_interval: end_time / 10.0,
463 write_format: "ascii".to_string(),
464 write_precision: 6,
465 }
466 }
467 #[allow(clippy::inherent_to_string)]
469 pub fn to_string(&self) -> String {
470 let mut s = foam_header("dictionary", "controlDict");
471 s.push('\n');
472 s.push_str(&format!("application {};\n\n", self.application));
473 s.push_str("startFrom startTime;\n\n");
474 s.push_str(&format!("startTime {};\n\n", self.start_time));
475 s.push_str("stopAt endTime;\n\n");
476 s.push_str(&format!("endTime {};\n\n", self.end_time));
477 s.push_str(&format!("deltaT {};\n\n", self.delta_t));
478 s.push_str(&format!("writeFormat {};\n\n", self.write_format));
479 s.push_str(&format!("writePrecision {};\n\n", self.write_precision));
480 s.push_str("writeCompression off;\n\n");
481 s.push_str("timeFormat general;\n\n");
482 s.push_str("timePrecision 6;\n\n");
483 s.push_str("runTimeModifiable true;\n\n");
484 s.push_str(&format!("writeInterval {};\n", self.write_interval));
485 s
486 }
487}
488#[derive(Debug, Clone)]
490pub struct FoamPatch {
491 pub name: String,
493 pub patch_type: String,
495 pub start_face: usize,
497 pub n_faces: usize,
499}
500#[allow(dead_code)]
502#[derive(Debug, Clone, PartialEq)]
503pub struct FoamDict {
504 pub entries: Vec<(String, FoamValue)>,
506}
507#[allow(dead_code)]
508impl FoamDict {
509 pub fn new() -> Self {
511 FoamDict {
512 entries: Vec::new(),
513 }
514 }
515 pub fn insert(&mut self, key: impl Into<String>, value: FoamValue) {
517 self.entries.push((key.into(), value));
518 }
519 pub fn get(&self, key: &str) -> Option<&FoamValue> {
521 self.entries.iter().find(|(k, _)| k == key).map(|(_, v)| v)
522 }
523 pub fn get_scalar(&self, key: &str) -> Option<f64> {
525 match self.get(key) {
526 Some(FoamValue::Scalar(v)) => Some(*v),
527 _ => None,
528 }
529 }
530 pub fn get_word(&self, key: &str) -> Option<&str> {
532 match self.get(key) {
533 Some(FoamValue::Word(w)) => Some(w.as_str()),
534 _ => None,
535 }
536 }
537 pub fn get_dict(&self, key: &str) -> Option<&FoamDict> {
539 match self.get(key) {
540 Some(FoamValue::Dict(d)) => Some(d),
541 _ => None,
542 }
543 }
544 pub fn get_vector(&self, key: &str) -> Option<[f64; 3]> {
546 match self.get(key) {
547 Some(FoamValue::Vector(v)) => Some(*v),
548 _ => None,
549 }
550 }
551 pub fn len(&self) -> usize {
553 self.entries.len()
554 }
555 pub fn is_empty(&self) -> bool {
557 self.entries.is_empty()
558 }
559 pub fn keys(&self) -> Vec<&str> {
561 self.entries.iter().map(|(k, _)| k.as_str()).collect()
562 }
563 pub fn to_foam_string(&self, indent: usize) -> String {
565 let pad = " ".repeat(indent);
566 let mut s = String::new();
567 for (key, value) in &self.entries {
568 match value {
569 FoamValue::Scalar(v) => {
570 s.push_str(&format!("{}{:<16}{};\n", pad, key, v));
571 }
572 FoamValue::Word(w) => {
573 s.push_str(&format!("{}{:<16}{};\n", pad, key, w));
574 }
575 FoamValue::Vector(v) => {
576 s.push_str(&format!(
577 "{}{:<16}({} {} {});\n",
578 pad, key, v[0], v[1], v[2]
579 ));
580 }
581 FoamValue::Dict(d) => {
582 s.push_str(&format!("{}{}\n{}{{\n", pad, key, pad));
583 s.push_str(&d.to_foam_string(indent + 1));
584 s.push_str(&format!("{}}}\n", pad));
585 }
586 FoamValue::List(items) => {
587 s.push_str(&format!("{}{}\n{}(\n", pad, key, pad));
588 for item in items {
589 match item {
590 FoamValue::Scalar(v) => {
591 s.push_str(&format!("{} {}\n", pad, v));
592 }
593 FoamValue::Word(w) => {
594 s.push_str(&format!("{} {}\n", pad, w));
595 }
596 FoamValue::Vector(v) => {
597 s.push_str(&format!("{} ({} {} {})\n", pad, v[0], v[1], v[2]));
598 }
599 _ => {}
600 }
601 }
602 s.push_str(&format!("{});\n", pad));
603 }
604 }
605 }
606 s
607 }
608 pub fn parse(input: &str) -> Self {
613 let cleaned = strip_foam_comments(input);
614 let tokens = tokenise_foam(&cleaned);
615 let (dict, _) = parse_dict_tokens(&tokens, 0);
616 dict
617 }
618}
619impl Default for FoamDict {
620 fn default() -> Self {
621 Self::new()
622 }
623}
624#[allow(dead_code)]
626pub struct FvSchemes {
627 pub ddt_scheme: String,
629 pub grad_scheme: String,
631 pub div_schemes: Vec<(String, String)>,
633 pub laplacian_schemes: Vec<(String, String)>,
635 pub interpolation_scheme: String,
637 pub sn_grad_scheme: String,
639}
640#[allow(dead_code)]
641impl FvSchemes {
642 pub fn default_second_order() -> Self {
644 FvSchemes {
645 ddt_scheme: "Euler".to_string(),
646 grad_scheme: "Gauss linear".to_string(),
647 div_schemes: vec![
648 ("default".to_string(), "none".to_string()),
649 (
650 "div(phi,U)".to_string(),
651 "Gauss linearUpwind grad(U)".to_string(),
652 ),
653 ],
654 laplacian_schemes: vec![("default".to_string(), "Gauss linear corrected".to_string())],
655 interpolation_scheme: "linear".to_string(),
656 sn_grad_scheme: "corrected".to_string(),
657 }
658 }
659 #[allow(clippy::inherent_to_string)]
661 pub fn to_string(&self) -> String {
662 let mut s = foam_header("dictionary", "fvSchemes");
663 s.push('\n');
664 s.push_str("ddtSchemes\n{\n");
665 s.push_str(&format!(" default {};\n", self.ddt_scheme));
666 s.push_str("}\n\n");
667 s.push_str("gradSchemes\n{\n");
668 s.push_str(&format!(" default {};\n", self.grad_scheme));
669 s.push_str("}\n\n");
670 s.push_str("divSchemes\n{\n");
671 for (key, val) in &self.div_schemes {
672 s.push_str(&format!(" {} {};\n", key, val));
673 }
674 s.push_str("}\n\n");
675 s.push_str("laplacianSchemes\n{\n");
676 for (key, val) in &self.laplacian_schemes {
677 s.push_str(&format!(" {} {};\n", key, val));
678 }
679 s.push_str("}\n\n");
680 s.push_str("interpolationSchemes\n{\n");
681 s.push_str(&format!(
682 " default {};\n",
683 self.interpolation_scheme
684 ));
685 s.push_str("}\n\n");
686 s.push_str("snGradSchemes\n{\n");
687 s.push_str(&format!(" default {};\n", self.sn_grad_scheme));
688 s.push_str("}\n");
689 s
690 }
691}
692#[allow(dead_code)]
694#[derive(Debug, Clone)]
695pub struct FoamResidual {
696 pub time: f64,
698 pub field: String,
700 pub initial_residual: f64,
702 pub final_residual: f64,
704 pub n_iterations: usize,
706}
707#[allow(dead_code)]
709#[derive(Debug, Clone, PartialEq)]
710pub enum FoamValue {
711 Scalar(f64),
713 Word(String),
715 Vector([f64; 3]),
717 Dict(FoamDict),
719 List(Vec<FoamValue>),
721}
722#[allow(dead_code)]
724pub struct TransportProperties {
725 pub transport_model: String,
727 pub nu: f64,
729}
730#[allow(dead_code)]
731impl TransportProperties {
732 pub fn newtonian(nu: f64) -> Self {
734 TransportProperties {
735 transport_model: "Newtonian".to_string(),
736 nu,
737 }
738 }
739 #[allow(clippy::inherent_to_string)]
741 pub fn to_string(&self) -> String {
742 let mut s = foam_header("dictionary", "transportProperties");
743 s.push('\n');
744 s.push_str(&format!("transportModel {};\n\n", self.transport_model));
745 s.push_str(&format!("nu [0 2 -1 0 0 0 0] {};\n", self.nu));
746 s
747 }
748}
749pub struct FoamField {
751 pub n_cells: usize,
753 pub field_name: String,
755 pub field_class: String,
757 pub dimensions: String,
759 pub internal_values: FieldValues,
761 pub boundary_conditions: Vec<FoamBc>,
763}
764impl FoamField {
765 #[allow(clippy::inherent_to_string)]
767 pub fn to_string(&self) -> String {
768 let mut s = foam_header(&self.field_class, &self.field_name);
769 s.push('\n');
770 s.push_str(&format!("dimensions {};\n\n", self.dimensions));
771 match &self.internal_values {
772 FieldValues::Uniform(v) => {
773 s.push_str(&format!("internalField uniform {};\n\n", v));
774 }
775 FieldValues::UniformVec(v) => {
776 s.push_str(&format!(
777 "internalField uniform ({} {} {});\n\n",
778 v[0], v[1], v[2]
779 ));
780 }
781 FieldValues::NonUniform(vals) => {
782 s.push_str("internalField nonuniform List<scalar>\n");
783 s.push_str(&format!("{}\n(\n", vals.len()));
784 for v in vals {
785 s.push_str(&format!("{}\n", v));
786 }
787 s.push_str(");\n\n");
788 }
789 FieldValues::NonUniformVec(vals) => {
790 s.push_str("internalField nonuniform List<vector>\n");
791 s.push_str(&format!("{}\n(\n", vals.len()));
792 for v in vals {
793 s.push_str(&format!("({} {} {})\n", v[0], v[1], v[2]));
794 }
795 s.push_str(");\n\n");
796 }
797 }
798 s.push_str("boundaryField\n{\n");
799 for bc in &self.boundary_conditions {
800 s.push_str(&format!(" {}\n {{\n", bc.patch_name));
801 s.push_str(&format!(" type {};\n", bc.bc_type));
802 if let Some(val) = &bc.value {
803 s.push_str(&format!(" value {};\n", val));
804 }
805 s.push_str(" }\n");
806 }
807 s.push_str("}\n");
808 s
809 }
810}
811pub enum FieldValues {
813 Uniform(f64),
815 UniformVec([f64; 3]),
817 NonUniform(Vec<f64>),
819 NonUniformVec(Vec<[f64; 3]>),
821}
822#[allow(dead_code)]
824pub struct FvSolution {
825 pub solvers: Vec<FvSolverEntry>,
827 pub algorithm: String,
829 pub n_correctors: usize,
831 pub n_non_orthogonal_correctors: usize,
833 pub p_ref_cell: usize,
835 pub p_ref_value: f64,
837}
838#[allow(dead_code)]
839impl FvSolution {
840 pub fn default_piso() -> Self {
842 FvSolution {
843 solvers: vec![
844 FvSolverEntry {
845 field_name: "p".to_string(),
846 solver: "PCG".to_string(),
847 preconditioner: "DIC".to_string(),
848 tolerance: 1e-6,
849 rel_tol: 0.05,
850 },
851 FvSolverEntry {
852 field_name: "U".to_string(),
853 solver: "smoothSolver".to_string(),
854 preconditioner: "symGaussSeidel".to_string(),
855 tolerance: 1e-5,
856 rel_tol: 0.0,
857 },
858 ],
859 algorithm: "PISO".to_string(),
860 n_correctors: 2,
861 n_non_orthogonal_correctors: 0,
862 p_ref_cell: 0,
863 p_ref_value: 0.0,
864 }
865 }
866 #[allow(clippy::inherent_to_string)]
868 pub fn to_string(&self) -> String {
869 let mut s = foam_header("dictionary", "fvSolution");
870 s.push('\n');
871 s.push_str("solvers\n{\n");
872 for entry in &self.solvers {
873 s.push_str(&format!(" {}\n {{\n", entry.field_name));
874 s.push_str(&format!(" solver {};\n", entry.solver));
875 s.push_str(&format!(
876 " preconditioner {};\n",
877 entry.preconditioner
878 ));
879 s.push_str(&format!(" tolerance {};\n", entry.tolerance));
880 s.push_str(&format!(" relTol {};\n", entry.rel_tol));
881 s.push_str(" }\n");
882 }
883 s.push_str("}\n\n");
884 s.push_str(&format!("{}\n{{\n", self.algorithm));
885 s.push_str(&format!(" nCorrectors {};\n", self.n_correctors));
886 s.push_str(&format!(
887 " nNonOrthogonalCorrectors {};\n",
888 self.n_non_orthogonal_correctors
889 ));
890 s.push_str(&format!(" pRefCell {};\n", self.p_ref_cell));
891 s.push_str(&format!(" pRefValue {};\n", self.p_ref_value));
892 s.push_str("}\n");
893 s
894 }
895}
896#[allow(dead_code)]
898#[derive(Debug, Clone)]
899pub struct FvSolverEntry {
900 pub field_name: String,
902 pub solver: String,
904 pub preconditioner: String,
906 pub tolerance: f64,
908 pub rel_tol: f64,
910}
911#[allow(dead_code)]
913#[derive(Debug, Clone, PartialEq)]
914pub struct FoamFileHeader {
915 pub version: f64,
917 pub format: String,
919 pub class: String,
921 pub object: String,
923 pub note: Option<String>,
925 pub location: Option<String>,
927}
928#[allow(dead_code)]
929impl FoamFileHeader {
930 pub fn parse(input: &str) -> Option<Self> {
934 let cleaned = strip_foam_comments(input);
935 let start = cleaned.find("FoamFile")?;
936 let after = &cleaned[start + "FoamFile".len()..];
937 let brace_start = after.find('{')?;
938 let brace_content = &after[brace_start + 1..];
939 let brace_end = brace_content.find('}')?;
940 let block = &brace_content[..brace_end];
941 let mut version = 2.0_f64;
942 let mut format = "ascii".to_string();
943 let mut class = String::new();
944 let mut object = String::new();
945 let mut note = None;
946 let mut location = None;
947 for line in block.lines() {
948 let trimmed = line.trim();
949 if trimmed.is_empty() || trimmed.starts_with("//") {
950 continue;
951 }
952 let line_clean = trimmed.trim_end_matches(';').trim();
953 let parts: Vec<&str> = line_clean.splitn(2, char::is_whitespace).collect();
954 if parts.len() < 2 {
955 continue;
956 }
957 let key = parts[0].trim();
958 let val = parts[1].trim().trim_matches('"');
959 match key {
960 "version" => {
961 version = val.parse().unwrap_or(2.0);
962 }
963 "format" => {
964 format = val.to_string();
965 }
966 "class" => {
967 class = val.to_string();
968 }
969 "object" => {
970 object = val.to_string();
971 }
972 "note" => {
973 note = Some(val.to_string());
974 }
975 "location" => {
976 location = Some(val.to_string());
977 }
978 _ => {}
979 }
980 }
981 if class.is_empty() && object.is_empty() {
982 return None;
983 }
984 Some(FoamFileHeader {
985 version,
986 format,
987 class,
988 object,
989 note,
990 location,
991 })
992 }
993 #[allow(clippy::inherent_to_string)]
995 pub fn to_string(&self) -> String {
996 let mut s = String::new();
997 s.push_str("FoamFile\n{\n");
998 s.push_str(&format!(" version {};\n", self.version));
999 s.push_str(&format!(" format {};\n", self.format));
1000 s.push_str(&format!(" class {};\n", self.class));
1001 if let Some(ref loc) = self.location {
1002 s.push_str(&format!(" location \"{}\";\n", loc));
1003 }
1004 s.push_str(&format!(" object {};\n", self.object));
1005 if let Some(ref note) = self.note {
1006 s.push_str(&format!(" note \"{}\";\n", note));
1007 }
1008 s.push_str("}\n");
1009 s
1010 }
1011}