1use std::fs::File;
6use std::io::{self, Read, Write};
7
8#[allow(unused_imports)]
9use super::functions::*;
10use super::functions::{CHECKPOINT_MAGIC, CHECKPOINT_VERSION};
11
12#[derive(Debug, Clone)]
14pub struct AmrGrid {
15 pub level_meta: AmrLevel,
17 pub cells: Vec<AmrCell>,
19 pub field_names: Vec<String>,
21}
22impl AmrGrid {
23 pub fn new(level_meta: AmrLevel, field_names: Vec<String>) -> Self {
25 Self {
26 level_meta,
27 cells: Vec::new(),
28 field_names,
29 }
30 }
31 pub fn add_cell(&mut self, cell: AmrCell) {
33 self.cells.push(cell);
34 }
35 pub fn integer_bounding_box(&self) -> Option<[i64; 6]> {
37 if self.cells.is_empty() {
38 return None;
39 }
40 let mut mn = self.cells[0].coords;
41 let mut mx = self.cells[0].coords;
42 for c in &self.cells {
43 for d in 0..3 {
44 if c.coords[d] < mn[d] {
45 mn[d] = c.coords[d];
46 }
47 if c.coords[d] > mx[d] {
48 mx[d] = c.coords[d];
49 }
50 }
51 }
52 Some([mn[0], mn[1], mn[2], mx[0], mx[1], mx[2]])
53 }
54 pub fn physical_bounding_box(&self) -> Option<[f64; 6]> {
56 let ibb = self.integer_bounding_box()?;
57 let cs = self.level_meta.cell_size;
58 let o = &self.level_meta.domain_bounds;
59 Some([
60 o[0] + ibb[0] as f64 * cs,
61 o[1] + ibb[1] as f64 * cs,
62 o[2] + ibb[2] as f64 * cs,
63 o[0] + (ibb[3] + 1) as f64 * cs,
64 o[1] + (ibb[4] + 1) as f64 * cs,
65 o[2] + (ibb[5] + 1) as f64 * cs,
66 ])
67 }
68 pub fn cell_count(&self) -> usize {
70 self.cells.len()
71 }
72}
73pub struct AmrCoarsening;
76impl AmrCoarsening {
77 pub fn coarsen_grid(fine: &AmrGrid, coarse_level_meta: AmrLevel, ratio: i64) -> AmrGrid {
83 let mut coarse = AmrGrid::new(coarse_level_meta, fine.field_names.clone());
84 let num_fields = fine.field_names.len();
85 let mut sums: std::collections::HashMap<[i64; 3], (Vec<f64>, usize)> =
86 std::collections::HashMap::new();
87 for cell in &fine.cells {
88 let ck = [
89 cell.coords[0] / ratio,
90 cell.coords[1] / ratio,
91 cell.coords[2] / ratio,
92 ];
93 let entry = sums
94 .entry(ck)
95 .or_insert_with(|| (vec![0.0_f64; num_fields], 0));
96 for f in 0..num_fields {
97 entry.0[f] += cell.data.get(f).copied().unwrap_or(0.0_f64);
98 }
99 entry.1 += 1;
100 }
101 let level_val = fine.level_meta.level.saturating_sub(1);
102 for (ck, (sum, cnt)) in sums {
103 let avg: Vec<f64> = sum.iter().map(|&s| s / cnt as f64).collect();
104 coarse.add_cell(AmrCell::new(level_val, ck, avg));
105 }
106 coarse
107 }
108 pub fn remove_covered_cells(coarse_grid: &mut AmrGrid, fine_grid: &AmrGrid, ratio: i64) {
113 let fine_coords: std::collections::HashSet<[i64; 3]> =
114 fine_grid.cells.iter().map(|c| c.coords).collect();
115 coarse_grid.cells.retain(|cc| {
116 let mut all_covered = true;
117 for dz in 0..ratio {
118 for dy in 0..ratio {
119 for dx in 0..ratio {
120 let child = [
121 cc.coords[0] * ratio + dx,
122 cc.coords[1] * ratio + dy,
123 cc.coords[2] * ratio + dz,
124 ];
125 if !fine_coords.contains(&child) {
126 all_covered = false;
127 }
128 }
129 }
130 }
131 !all_covered
132 });
133 }
134}
135pub struct AmrMeshExport;
137impl AmrMeshExport {
138 pub fn write_vtu(path: &str, hierarchy: &AmrHierarchy, field_idx: usize) -> io::Result<()> {
142 let mut all_pts: Vec<[f64; 3]> = Vec::new();
143 let mut connectivity: Vec<usize> = Vec::new();
144 let mut offsets: Vec<usize> = Vec::new();
145 let mut types: Vec<u8> = Vec::new();
146 let mut field_vals: Vec<f64> = Vec::new();
147 let mut offset = 0usize;
148 for grid in &hierarchy.levels {
149 let cs = grid.level_meta.cell_size;
150 let o = &grid.level_meta.domain_bounds;
151 for cell in &grid.cells {
152 let x0 = o[0] + cell.coords[0] as f64 * cs;
153 let y0 = o[1] + cell.coords[1] as f64 * cs;
154 let z0 = o[2] + cell.coords[2] as f64 * cs;
155 let x1 = x0 + cs;
156 let y1 = y0 + cs;
157 let z1 = z0 + cs;
158 let base = all_pts.len();
159 all_pts.push([x0, y0, z0]);
160 all_pts.push([x1, y0, z0]);
161 all_pts.push([x1, y1, z0]);
162 all_pts.push([x0, y1, z0]);
163 all_pts.push([x0, y0, z1]);
164 all_pts.push([x1, y0, z1]);
165 all_pts.push([x1, y1, z1]);
166 all_pts.push([x0, y1, z1]);
167 for p in base..base + 8 {
168 connectivity.push(p);
169 }
170 offset += 8;
171 offsets.push(offset);
172 types.push(12);
173 field_vals.push(cell.data.get(field_idx).copied().unwrap_or(0.0_f64));
174 }
175 }
176 let num_pts = all_pts.len();
177 let num_cells = offsets.len();
178 let mut f = File::create(path)?;
179 writeln!(f, r#"<?xml version="1.0"?>"#)?;
180 writeln!(
181 f,
182 r#"<VTKFile type="UnstructuredGrid" version="0.1" byte_order="LittleEndian">"#
183 )?;
184 writeln!(f, r#" <UnstructuredGrid>"#)?;
185 writeln!(
186 f,
187 r#" <Piece NumberOfPoints="{num_pts}" NumberOfCells="{num_cells}">"#
188 )?;
189 writeln!(f, r#" <Points>"#)?;
190 writeln!(
191 f,
192 r#" <DataArray type="Float64" NumberOfComponents="3" format="ascii">"#
193 )?;
194 for pt in &all_pts {
195 writeln!(f, " {:.10} {:.10} {:.10}", pt[0], pt[1], pt[2])?;
196 }
197 writeln!(f, r#" </DataArray>"#)?;
198 writeln!(f, r#" </Points>"#)?;
199 writeln!(f, r#" <Cells>"#)?;
200 writeln!(
201 f,
202 r#" <DataArray type="Int64" Name="connectivity" format="ascii">"#
203 )?;
204 let conn_str: Vec<String> = connectivity.iter().map(|&c| c.to_string()).collect();
205 writeln!(f, " {}", conn_str.join(" "))?;
206 writeln!(f, r#" </DataArray>"#)?;
207 writeln!(
208 f,
209 r#" <DataArray type="Int64" Name="offsets" format="ascii">"#
210 )?;
211 let off_str: Vec<String> = offsets.iter().map(|&o| o.to_string()).collect();
212 writeln!(f, " {}", off_str.join(" "))?;
213 writeln!(f, r#" </DataArray>"#)?;
214 writeln!(
215 f,
216 r#" <DataArray type="UInt8" Name="types" format="ascii">"#
217 )?;
218 let typ_str: Vec<String> = types.iter().map(|&t| t.to_string()).collect();
219 writeln!(f, " {}", typ_str.join(" "))?;
220 writeln!(f, r#" </DataArray>"#)?;
221 writeln!(f, r#" </Cells>"#)?;
222 writeln!(f, r#" <CellData>"#)?;
223 writeln!(
224 f,
225 r#" <DataArray type="Float64" Name="field_{field_idx}" format="ascii">"#
226 )?;
227 let fv_str: Vec<String> = field_vals.iter().map(|&v| format!("{v:.10}")).collect();
228 writeln!(f, " {}", fv_str.join(" "))?;
229 writeln!(f, r#" </DataArray>"#)?;
230 writeln!(f, r#" </CellData>"#)?;
231 writeln!(f, r#" </Piece>"#)?;
232 writeln!(f, r#" </UnstructuredGrid>"#)?;
233 writeln!(f, r#"</VTKFile>"#)?;
234 Ok(())
235 }
236}
237#[derive(Debug, Clone)]
239pub struct AmrCell {
240 pub level: usize,
242 pub coords: [i64; 3],
244 pub data: Vec<f64>,
246}
247impl AmrCell {
248 pub fn new(level: usize, coords: [i64; 3], data: Vec<f64>) -> Self {
250 Self {
251 level,
252 coords,
253 data,
254 }
255 }
256 pub fn physical_center(&self, lvl: &AmrLevel) -> [f64; 3] {
258 let cs = lvl.cell_size;
259 let ox = lvl.domain_bounds[0];
260 let oy = lvl.domain_bounds[1];
261 let oz = lvl.domain_bounds[2];
262 [
263 ox + (self.coords[0] as f64 + 0.5) * cs,
264 oy + (self.coords[1] as f64 + 0.5) * cs,
265 oz + (self.coords[2] as f64 + 0.5) * cs,
266 ]
267 }
268}
269#[derive(Debug, Clone)]
271pub struct AmrLevel {
272 pub level: usize,
274 pub cell_size: f64,
276 pub domain_bounds: [f64; 6],
278}
279impl AmrLevel {
280 pub fn new(level: usize, cell_size: f64, domain_bounds: [f64; 6]) -> Self {
282 Self {
283 level,
284 cell_size,
285 domain_bounds,
286 }
287 }
288 pub fn refinement_ratio(&self) -> usize {
290 1 << self.level
291 }
292}
293pub struct AmrCheckpoint;
309impl AmrCheckpoint {
310 pub fn write(path: &str, hierarchy: &AmrHierarchy) -> io::Result<()> {
312 let mut f = File::create(path)?;
313 f.write_all(&CHECKPOINT_MAGIC.to_le_bytes())?;
314 f.write_all(&CHECKPOINT_VERSION.to_le_bytes())?;
315 f.write_all(&hierarchy.time.to_le_bytes())?;
316 f.write_all(&hierarchy.cycle.to_le_bytes())?;
317 f.write_all(&(hierarchy.levels.len() as u32).to_le_bytes())?;
318 for grid in &hierarchy.levels {
319 let lm = &grid.level_meta;
320 f.write_all(&(lm.level as u32).to_le_bytes())?;
321 f.write_all(&lm.cell_size.to_le_bytes())?;
322 for &b in &lm.domain_bounds {
323 f.write_all(&b.to_le_bytes())?;
324 }
325 f.write_all(&(grid.field_names.len() as u32).to_le_bytes())?;
326 for name in &grid.field_names {
327 let bytes = name.as_bytes();
328 f.write_all(&(bytes.len() as u32).to_le_bytes())?;
329 f.write_all(bytes)?;
330 }
331 f.write_all(&(grid.cells.len() as u32).to_le_bytes())?;
332 for cell in &grid.cells {
333 for &c in &cell.coords {
334 f.write_all(&c.to_le_bytes())?;
335 }
336 f.write_all(&(cell.data.len() as u32).to_le_bytes())?;
337 for &d in &cell.data {
338 f.write_all(&d.to_le_bytes())?;
339 }
340 }
341 }
342 Ok(())
343 }
344 pub fn read(path: &str) -> io::Result<AmrHierarchy> {
346 let mut raw = Vec::new();
347 File::open(path)?.read_to_end(&mut raw)?;
348 let mut cursor = 0usize;
349 macro_rules! read_u32 {
350 () => {{
351 let v =
352 u32::from_le_bytes(raw[cursor..cursor + 4].try_into().map_err(|_| {
353 io::Error::new(io::ErrorKind::InvalidData, "short read u32")
354 })?);
355 cursor += 4;
356 v
357 }};
358 }
359 macro_rules! read_u64 {
360 () => {{
361 let v =
362 u64::from_le_bytes(raw[cursor..cursor + 8].try_into().map_err(|_| {
363 io::Error::new(io::ErrorKind::InvalidData, "short read u64")
364 })?);
365 cursor += 8;
366 v
367 }};
368 }
369 macro_rules! read_i64 {
370 () => {{
371 let v =
372 i64::from_le_bytes(raw[cursor..cursor + 8].try_into().map_err(|_| {
373 io::Error::new(io::ErrorKind::InvalidData, "short read i64")
374 })?);
375 cursor += 8;
376 v
377 }};
378 }
379 macro_rules! read_f64 {
380 () => {{
381 let v =
382 f64::from_le_bytes(raw[cursor..cursor + 8].try_into().map_err(|_| {
383 io::Error::new(io::ErrorKind::InvalidData, "short read f64")
384 })?);
385 cursor += 8;
386 v
387 }};
388 }
389 let magic = read_u32!();
390 if magic != CHECKPOINT_MAGIC {
391 return Err(io::Error::new(io::ErrorKind::InvalidData, "bad magic"));
392 }
393 let _version = read_u32!();
394 let time = read_f64!();
395 let cycle = read_u64!();
396 let num_levels = read_u32!() as usize;
397 let mut hierarchy = AmrHierarchy::new();
398 hierarchy.time = time;
399 hierarchy.cycle = cycle;
400 for _ in 0..num_levels {
401 let level = read_u32!() as usize;
402 let cell_size = read_f64!();
403 let mut domain_bounds = [0.0f64; 6];
404 for b in &mut domain_bounds {
405 *b = read_f64!();
406 }
407 let lm = AmrLevel::new(level, cell_size, domain_bounds);
408 let num_fields = read_u32!() as usize;
409 let mut field_names = Vec::with_capacity(num_fields);
410 for _ in 0..num_fields {
411 let len = read_u32!() as usize;
412 let bytes = raw[cursor..cursor + len].to_vec();
413 cursor += len;
414 field_names.push(
415 String::from_utf8(bytes)
416 .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "bad utf8"))?,
417 );
418 }
419 let mut grid = AmrGrid::new(lm, field_names);
420 let num_cells = read_u32!() as usize;
421 for _ in 0..num_cells {
422 let i = read_i64!();
423 let j = read_i64!();
424 let k = read_i64!();
425 let nd = read_u32!() as usize;
426 let mut data = Vec::with_capacity(nd);
427 for _ in 0..nd {
428 data.push(read_f64!());
429 }
430 grid.add_cell(AmrCell::new(level, [i, j, k], data));
431 }
432 hierarchy.add_level(grid);
433 }
434 Ok(hierarchy)
435 }
436}
437#[derive(Debug, Clone)]
439pub struct AmrHierarchy {
440 pub levels: Vec<AmrGrid>,
442 pub time: f64,
444 pub cycle: u64,
446}
447impl AmrHierarchy {
448 pub fn new() -> Self {
450 Self {
451 levels: Vec::new(),
452 time: 0.0,
453 cycle: 0,
454 }
455 }
456 pub fn add_level(&mut self, grid: AmrGrid) {
458 self.levels.push(grid);
459 }
460 pub fn num_levels(&self) -> usize {
462 self.levels.len()
463 }
464 pub fn total_cells(&self) -> usize {
466 self.levels.iter().map(|g| g.cell_count()).sum()
467 }
468 pub fn coarsen_field(&self, fine_level: usize, field_idx: usize) -> Vec<f64> {
473 if fine_level == 0 || fine_level >= self.levels.len() {
474 return Vec::new();
475 }
476 let ratio = 2i64;
477 let coarse = &self.levels[fine_level - 1];
478 let fine = &self.levels[fine_level];
479 let mut sums: std::collections::HashMap<[i64; 3], (f64, usize)> =
480 std::collections::HashMap::new();
481 for fc in &fine.cells {
482 let ck = [
483 fc.coords[0] / ratio,
484 fc.coords[1] / ratio,
485 fc.coords[2] / ratio,
486 ];
487 let val = fc.data.get(field_idx).copied().unwrap_or(0.0);
488 let entry = sums.entry(ck).or_insert((0.0, 0));
489 entry.0 += val;
490 entry.1 += 1;
491 }
492 coarse
493 .cells
494 .iter()
495 .map(|cc| {
496 if let Some((sum, cnt)) = sums.get(&cc.coords) {
497 sum / *cnt as f64
498 } else {
499 cc.data.get(field_idx).copied().unwrap_or(0.0)
500 }
501 })
502 .collect()
503 }
504 pub fn refine_field(&self, coarse_level: usize, field_idx: usize) -> Vec<f64> {
508 if coarse_level + 1 >= self.levels.len() {
509 return Vec::new();
510 }
511 let ratio = 2i64;
512 let coarse = &self.levels[coarse_level];
513 let fine = &self.levels[coarse_level + 1];
514 let lookup: std::collections::HashMap<[i64; 3], f64> = coarse
515 .cells
516 .iter()
517 .map(|c| (c.coords, c.data.get(field_idx).copied().unwrap_or(0.0)))
518 .collect();
519 fine.cells
520 .iter()
521 .map(|fc| {
522 let ck = [
523 fc.coords[0] / ratio,
524 fc.coords[1] / ratio,
525 fc.coords[2] / ratio,
526 ];
527 lookup.get(&ck).copied().unwrap_or(0.0)
528 })
529 .collect()
530 }
531}
532pub struct AmrWriter;
534impl AmrWriter {
535 pub fn write_vtk(path: &str, hierarchy: &AmrHierarchy) -> io::Result<()> {
537 let mut f = File::create(path)?;
538 writeln!(f, r#"<?xml version="1.0"?>"#)?;
539 writeln!(
540 f,
541 r#"<VTKFile type="vtkHierarchicalBoxDataSet" version="1.1" byte_order="LittleEndian">"#
542 )?;
543 writeln!(f, r#" <vtkHierarchicalBoxDataSet>"#)?;
544 for (li, grid) in hierarchy.levels.iter().enumerate() {
545 writeln!(f, r#" <Block level="{li}">"#)?;
546 for cell in &grid.cells {
547 let cs = grid.level_meta.cell_size;
548 let o = &grid.level_meta.domain_bounds;
549 let x0 = o[0] + cell.coords[0] as f64 * cs;
550 let y0 = o[1] + cell.coords[1] as f64 * cs;
551 let z0 = o[2] + cell.coords[2] as f64 * cs;
552 let x1 = x0 + cs;
553 let y1 = y0 + cs;
554 let z1 = z0 + cs;
555 writeln!(
556 f,
557 r#" <DataSet index="0" amr_box="{} {} {} {} {} {}">"#,
558 cell.coords[0],
559 cell.coords[1],
560 cell.coords[2],
561 cell.coords[0],
562 cell.coords[1],
563 cell.coords[2]
564 )?;
565 writeln!(
566 f,
567 r#" <!-- bounds: {x0:.6} {y0:.6} {z0:.6} {x1:.6} {y1:.6} {z1:.6} -->"#
568 )?;
569 writeln!(f, r#" <PointData>"#)?;
570 for (fi, fname) in grid.field_names.iter().enumerate() {
571 let val = cell.data.get(fi).copied().unwrap_or(0.0);
572 writeln!(
573 f,
574 r#" <DataArray Name="{fname}" NumberOfTuples="1" format="ascii">{val:.10}</DataArray>"#
575 )?;
576 }
577 writeln!(f, r#" </PointData>"#)?;
578 writeln!(f, r#" </DataSet>"#)?;
579 }
580 writeln!(f, r#" </Block>"#)?;
581 }
582 writeln!(f, r#" </vtkHierarchicalBoxDataSet>"#)?;
583 writeln!(f, r#"</VTKFile>"#)?;
584 Ok(())
585 }
586}
587#[derive(Debug, Clone)]
591pub struct AmrBlock {
592 pub level: usize,
594 pub lo: [i64; 3],
596 pub hi: [i64; 3],
598 pub dims: [usize; 3],
600 pub refinement_flag: bool,
602 pub data: Vec<f64>,
606 pub num_fields: usize,
608}
609impl AmrBlock {
610 pub fn new(level: usize, lo: [i64; 3], hi: [i64; 3], num_fields: usize) -> Self {
612 let dims = [
613 (hi[0] - lo[0] + 1) as usize,
614 (hi[1] - lo[1] + 1) as usize,
615 (hi[2] - lo[2] + 1) as usize,
616 ];
617 let total = dims[0] * dims[1] * dims[2] * num_fields;
618 Self {
619 level,
620 lo,
621 hi,
622 dims,
623 refinement_flag: false,
624 data: vec![0.0_f64; total],
625 num_fields,
626 }
627 }
628 pub fn cell_count(&self) -> usize {
630 self.dims[0] * self.dims[1] * self.dims[2]
631 }
632 pub fn index(&self, i: usize, j: usize, k: usize, f: usize) -> usize {
634 ((k * self.dims[1] + j) * self.dims[0] + i) * self.num_fields + f
635 }
636 pub fn get(&self, i: usize, j: usize, k: usize, f: usize) -> f64 {
638 self.data[self.index(i, j, k, f)]
639 }
640 pub fn set(&mut self, i: usize, j: usize, k: usize, f: usize, val: f64) {
642 let idx = self.index(i, j, k, f);
643 self.data[idx] = val;
644 }
645 pub fn fill_field(&mut self, f: usize, val: f64) {
647 for k in 0..self.dims[2] {
648 for j in 0..self.dims[1] {
649 for i in 0..self.dims[0] {
650 self.set(i, j, k, f, val);
651 }
652 }
653 }
654 }
655 pub fn field_max(&self, f: usize) -> f64 {
657 let mut m = f64::NEG_INFINITY;
658 for k in 0..self.dims[2] {
659 for j in 0..self.dims[1] {
660 for i in 0..self.dims[0] {
661 let v = self.get(i, j, k, f);
662 if v > m {
663 m = v;
664 }
665 }
666 }
667 }
668 m
669 }
670 pub fn field_min(&self, f: usize) -> f64 {
672 let mut m = f64::INFINITY;
673 for k in 0..self.dims[2] {
674 for j in 0..self.dims[1] {
675 for i in 0..self.dims[0] {
676 let v = self.get(i, j, k, f);
677 if v < m {
678 m = v;
679 }
680 }
681 }
682 }
683 m
684 }
685}
686#[derive(Debug, Clone, PartialEq)]
688pub enum RefinementMethod {
689 GradientBased {
691 field_idx: usize,
693 threshold: f64,
695 },
696 CurvatureBased {
698 field_idx: usize,
700 threshold: f64,
702 },
703 UserDefined {
705 field_idx: usize,
707 value: f64,
709 },
710}
711#[derive(Debug, Clone)]
713pub struct AmrStats {
714 pub cells_per_level: Vec<usize>,
716 pub total_cells: usize,
718 pub memory_bytes: usize,
720}
721impl AmrStats {
722 pub fn compute(hierarchy: &AmrHierarchy) -> Self {
724 let cells_per_level: Vec<usize> = hierarchy.levels.iter().map(|g| g.cell_count()).collect();
725 let total_cells = cells_per_level.iter().sum();
726 let fields = hierarchy
727 .levels
728 .first()
729 .and_then(|g| g.field_names.len().into())
730 .unwrap_or(0);
731 let memory_bytes = total_cells * fields * std::mem::size_of::<f64>();
732 Self {
733 cells_per_level,
734 total_cells,
735 memory_bytes,
736 }
737 }
738}
739pub struct AmrReader;
741impl AmrReader {
742 pub fn read_vtk(path: &str) -> io::Result<AmrHierarchy> {
747 let mut raw = String::new();
748 File::open(path)?.read_to_string(&mut raw)?;
749 let mut hierarchy = AmrHierarchy::new();
750 let mut current_level: Option<usize> = None;
751 let mut current_coords: Option<[i64; 3]> = None;
752 let mut current_data: Vec<f64> = Vec::new();
753 let mut current_fields: Vec<String> = Vec::new();
754 let mut grid_map: std::collections::BTreeMap<usize, AmrGrid> =
755 std::collections::BTreeMap::new();
756 for line in raw.lines() {
757 let trimmed = line.trim();
758 if trimmed.starts_with(r#"<Block level=""#) {
759 if let Some(l) = parse_attr(trimmed, "level") {
760 current_level = l.parse::<usize>().ok();
761 if let Some(lv) = current_level {
762 grid_map.entry(lv).or_insert_with(|| {
763 AmrGrid::new(
764 AmrLevel::new(
765 lv,
766 1.0 / (1 << lv) as f64,
767 [0.0, 0.0, 0.0, 1.0, 1.0, 1.0],
768 ),
769 Vec::new(),
770 )
771 });
772 }
773 }
774 } else if trimmed.starts_with("<DataSet") {
775 if let Some(ab) = parse_attr(trimmed, "amr_box") {
776 let nums: Vec<i64> = ab
777 .split_whitespace()
778 .filter_map(|s| s.parse().ok())
779 .collect();
780 if nums.len() >= 3 {
781 current_coords = Some([nums[0], nums[1], nums[2]]);
782 current_data.clear();
783 current_fields.clear();
784 }
785 }
786 } else if trimmed.starts_with("<DataArray") {
787 if let (Some(name), Some(val_str)) =
788 (parse_attr(trimmed, "Name"), parse_inline_value(trimmed))
789 {
790 current_fields.push(name.to_string());
791 current_data.push(val_str.trim().parse::<f64>().unwrap_or(0.0));
792 }
793 } else if trimmed == "</DataSet>"
794 && let (Some(lv), Some(coords)) = (current_level, current_coords.take())
795 && let Some(grid) = grid_map.get_mut(&lv)
796 {
797 if grid.field_names.is_empty() {
798 grid.field_names = current_fields.clone();
799 }
800 grid.cells
801 .push(AmrCell::new(lv, coords, current_data.clone()));
802 }
803 }
804 for (_, grid) in grid_map {
805 hierarchy.add_level(grid);
806 }
807 Ok(hierarchy)
808 }
809}
810#[derive(Debug, Clone)]
812pub struct AmrtNode {
813 pub level: usize,
815 pub morton: u64,
817 pub coords: [u32; 3],
819 pub children: [Option<usize>; 8],
821 pub parent: Option<usize>,
823 pub is_leaf: bool,
825 pub neighbors: [Option<usize>; 6],
827}
828impl AmrtNode {
829 pub fn leaf(level: usize, coords: [u32; 3], parent: Option<usize>) -> Self {
831 let [i, j, k] = coords;
832 Self {
833 level,
834 morton: morton_encode(i, j, k),
835 coords,
836 children: [None; 8],
837 parent,
838 is_leaf: true,
839 neighbors: [None; 6],
840 }
841 }
842 pub fn octant_index(dx: usize, dy: usize, dz: usize) -> usize {
845 dx | (dy << 1) | (dz << 2)
846 }
847}
848pub struct AmrFieldInterp;
850impl AmrFieldInterp {
851 pub fn interpolate_at(
857 hierarchy: &AmrHierarchy,
858 point: [f64; 3],
859 field_idx: usize,
860 ) -> Option<f64> {
861 for grid in hierarchy.levels.iter().rev() {
862 let cs = grid.level_meta.cell_size;
863 let o = &grid.level_meta.domain_bounds;
864 let i = ((point[0] - o[0]) / cs).floor() as i64;
865 let j = ((point[1] - o[1]) / cs).floor() as i64;
866 let k = ((point[2] - o[2]) / cs).floor() as i64;
867 for cell in &grid.cells {
868 if cell.coords == [i, j, k] {
869 return cell.data.get(field_idx).copied();
870 }
871 }
872 }
873 None
874 }
875 pub fn bilinear_xy(grid: &AmrGrid, x: f64, y: f64, field_idx: usize) -> Option<f64> {
880 let cs = grid.level_meta.cell_size;
881 let ox = grid.level_meta.domain_bounds[0];
882 let oy = grid.level_meta.domain_bounds[1];
883 let fi = (x - ox) / cs - 0.5;
884 let fj = (y - oy) / cs - 0.5;
885 let i0 = fi.floor() as i64;
886 let j0 = fj.floor() as i64;
887 let tx = fi - fi.floor();
888 let ty = fj - fj.floor();
889 let lookup: std::collections::HashMap<[i64; 3], f64> = grid
890 .cells
891 .iter()
892 .map(|c| (c.coords, c.data.get(field_idx).copied().unwrap_or(0.0)))
893 .collect();
894 let k0 = 0i64;
895 let v00 = lookup.get(&[i0, j0, k0]).copied()?;
896 let v10 = lookup.get(&[i0 + 1, j0, k0]).copied().unwrap_or(v00);
897 let v01 = lookup.get(&[i0, j0 + 1, k0]).copied().unwrap_or(v00);
898 let v11 = lookup.get(&[i0 + 1, j0 + 1, k0]).copied().unwrap_or(v00);
899 let v = v00 * (1.0 - tx) * (1.0 - ty)
900 + v10 * tx * (1.0 - ty)
901 + v01 * (1.0 - tx) * ty
902 + v11 * tx * ty;
903 Some(v)
904 }
905}
906pub struct AmrRefinementCriteria {
910 pub method: RefinementMethod,
912}
913impl AmrRefinementCriteria {
914 pub fn new(method: RefinementMethod) -> Self {
916 Self { method }
917 }
918 pub fn flag_refinement(&self, grid: &AmrGrid) -> Vec<bool> {
923 match &self.method {
924 RefinementMethod::GradientBased {
925 field_idx,
926 threshold,
927 } => self.flag_gradient(grid, *field_idx, *threshold),
928 RefinementMethod::CurvatureBased {
929 field_idx,
930 threshold,
931 } => self.flag_curvature(grid, *field_idx, *threshold),
932 RefinementMethod::UserDefined { field_idx, value } => grid
933 .cells
934 .iter()
935 .map(|c| c.data.get(*field_idx).copied().unwrap_or(0.0_f64) > *value)
936 .collect(),
937 }
938 }
939 fn flag_gradient(&self, grid: &AmrGrid, field_idx: usize, threshold: f64) -> Vec<bool> {
940 let lookup: std::collections::HashMap<[i64; 3], f64> = grid
941 .cells
942 .iter()
943 .map(|c| (c.coords, c.data.get(field_idx).copied().unwrap_or(0.0_f64)))
944 .collect();
945 grid.cells
946 .iter()
947 .map(|cell| {
948 let v = cell.data.get(field_idx).copied().unwrap_or(0.0_f64);
949 let mut grad_sq = 0.0_f64;
950 for d in 0..3i64 {
951 let mut plus_c = cell.coords;
952 let mut minus_c = cell.coords;
953 plus_c[d as usize] += 1;
954 minus_c[d as usize] -= 1;
955 let vp = lookup.get(&plus_c).copied().unwrap_or(v);
956 let vm = lookup.get(&minus_c).copied().unwrap_or(v);
957 let g = (vp - vm) * 0.5_f64;
958 grad_sq += g * g;
959 }
960 grad_sq.sqrt() > threshold
961 })
962 .collect()
963 }
964 fn flag_curvature(&self, grid: &AmrGrid, field_idx: usize, threshold: f64) -> Vec<bool> {
965 let lookup: std::collections::HashMap<[i64; 3], f64> = grid
966 .cells
967 .iter()
968 .map(|c| (c.coords, c.data.get(field_idx).copied().unwrap_or(0.0_f64)))
969 .collect();
970 grid.cells
971 .iter()
972 .map(|cell| {
973 let v = cell.data.get(field_idx).copied().unwrap_or(0.0_f64);
974 let mut laplacian = 0.0_f64;
975 for d in 0..3i64 {
976 let mut plus_c = cell.coords;
977 let mut minus_c = cell.coords;
978 plus_c[d as usize] += 1;
979 minus_c[d as usize] -= 1;
980 let vp = lookup.get(&plus_c).copied().unwrap_or(v);
981 let vm = lookup.get(&minus_c).copied().unwrap_or(v);
982 laplacian += vp + vm - 2.0_f64 * v;
983 }
984 laplacian.abs() > threshold
985 })
986 .collect()
987 }
988}
989pub struct AmrSerializer;
1000impl AmrSerializer {
1001 pub fn to_json(hierarchy: &AmrHierarchy) -> String {
1003 let mut out = String::new();
1004 out.push_str(&format!(
1005 "{{\"time\":{:.15e},\"cycle\":{},\"levels\":[",
1006 hierarchy.time, hierarchy.cycle
1007 ));
1008 for (li, grid) in hierarchy.levels.iter().enumerate() {
1009 if li > 0 {
1010 out.push(',');
1011 }
1012 out.push_str(&format!(
1013 "{{\"level\":{},\"cell_size\":{:.15e},\"domain\":[{},{},{},{},{},{}],\"fields\":[",
1014 grid.level_meta.level,
1015 grid.level_meta.cell_size,
1016 grid.level_meta.domain_bounds[0],
1017 grid.level_meta.domain_bounds[1],
1018 grid.level_meta.domain_bounds[2],
1019 grid.level_meta.domain_bounds[3],
1020 grid.level_meta.domain_bounds[4],
1021 grid.level_meta.domain_bounds[5],
1022 ));
1023 for (fi, name) in grid.field_names.iter().enumerate() {
1024 if fi > 0 {
1025 out.push(',');
1026 }
1027 out.push('"');
1028 out.push_str(name);
1029 out.push('"');
1030 }
1031 out.push_str("],\"cells\":[");
1032 for (ci, cell) in grid.cells.iter().enumerate() {
1033 if ci > 0 {
1034 out.push(',');
1035 }
1036 out.push_str(&format!(
1037 "{{\"coords\":[{},{},{}],\"data\":[",
1038 cell.coords[0], cell.coords[1], cell.coords[2]
1039 ));
1040 for (di, &d) in cell.data.iter().enumerate() {
1041 if di > 0 {
1042 out.push(',');
1043 }
1044 out.push_str(&format!("{:.15e}", d));
1045 }
1046 out.push_str("]}");
1047 }
1048 out.push_str("]}}");
1049 }
1050 out.push_str("]}");
1051 out
1052 }
1053 pub fn from_json(s: &str) -> Option<AmrHierarchy> {
1058 let time = extract_f64_after(s, "\"time\":")?;
1059 let cycle = extract_u64_after(s, "\"cycle\":")?;
1060 let mut hierarchy = AmrHierarchy::new();
1061 hierarchy.time = time;
1062 hierarchy.cycle = cycle;
1063 let mut search_from = 0;
1064 loop {
1065 let level_pos = s[search_from..].find("\"level\":")?;
1066 let abs_pos = search_from + level_pos;
1067 let level_val = extract_usize_at(&s[abs_pos + 8..])?;
1068 let cs_pos = s[abs_pos..].find("\"cell_size\":")?;
1069 let cell_size = extract_f64_after(&s[abs_pos + cs_pos..], "\"cell_size\":")?;
1070 let dom_pos = s[abs_pos..].find("\"domain\":")?;
1071 let dom_start = abs_pos + dom_pos + 9;
1072 let domain = extract_f64_array_6(&s[dom_start..])?;
1073 let fields_pos = s[abs_pos..].find("\"fields\":")?;
1074 let fields_start = abs_pos + fields_pos + 9;
1075 let field_names = extract_string_array(&s[fields_start..]);
1076 let lm = AmrLevel::new(level_val, cell_size, domain);
1077 let mut grid = AmrGrid::new(lm, field_names);
1078 let cells_pos = s[abs_pos..].find("\"cells\":")?;
1079 let cells_start = abs_pos + cells_pos + 8;
1080 let arr_start = cells_start + s[cells_start..].find('[')?;
1081 let arr_end = find_matching_bracket(&s[arr_start..])? + arr_start;
1082 let cells_str = &s[arr_start + 1..arr_end];
1083 let mut cell_from = 0;
1084 while let Some(obj_start) = cells_str[cell_from..].find('{') {
1085 let abs_obj = cell_from + obj_start;
1086 let obj_end_rel = find_matching_brace(&cells_str[abs_obj..])?;
1087 let obj_str = &cells_str[abs_obj..abs_obj + obj_end_rel + 1];
1088 let coords = extract_i64_array_3(obj_str)?;
1089 let data = extract_f64_array_vec(obj_str);
1090 grid.add_cell(AmrCell::new(level_val, coords, data));
1091 cell_from = abs_obj + obj_end_rel + 1;
1092 }
1093 hierarchy.add_level(grid);
1094 search_from = abs_pos + 8;
1095 if s[search_from..].find("\"level\":").is_none() {
1096 break;
1097 }
1098 }
1099 Some(hierarchy)
1100 }
1101}
1102#[derive(Debug, Default)]
1107pub struct AmrtTree {
1108 pub nodes: Vec<AmrtNode>,
1110 pub max_level: usize,
1112 pub domain: [f64; 6],
1114}
1115impl AmrtTree {
1116 pub fn new(domain: [f64; 6], max_level: usize) -> Self {
1118 Self {
1119 nodes: Vec::new(),
1120 max_level,
1121 domain,
1122 }
1123 }
1124 pub fn init_root(&mut self) {
1126 self.nodes.clear();
1127 self.nodes.push(AmrtNode::leaf(0, [0, 0, 0], None));
1128 }
1129 pub fn refine(&mut self, node_idx: usize) -> bool {
1133 if node_idx >= self.nodes.len() {
1134 return false;
1135 }
1136 if !self.nodes[node_idx].is_leaf {
1137 return false;
1138 }
1139 let parent_level = self.nodes[node_idx].level;
1140 if parent_level >= self.max_level {
1141 return false;
1142 }
1143 let [pi, pj, pk] = self.nodes[node_idx].coords;
1144 let child_level = parent_level + 1;
1145 let mut child_indices = [usize::MAX; 8];
1146 for dz in 0..2usize {
1147 for dy in 0..2usize {
1148 for dx in 0..2usize {
1149 let ci = pi * 2 + dx as u32;
1150 let cj = pj * 2 + dy as u32;
1151 let ck = pk * 2 + dz as u32;
1152 let child_node = AmrtNode::leaf(child_level, [ci, cj, ck], Some(node_idx));
1153 let child_idx = self.nodes.len();
1154 child_indices[AmrtNode::octant_index(dx, dy, dz)] = child_idx;
1155 self.nodes.push(child_node);
1156 }
1157 }
1158 }
1159 let mut children_opts = [None; 8];
1160 for (k, &ci) in child_indices.iter().enumerate() {
1161 children_opts[k] = Some(ci);
1162 }
1163 self.nodes[node_idx].children = children_opts;
1164 self.nodes[node_idx].is_leaf = false;
1165 true
1166 }
1167 pub fn coarsen(&mut self, parent_idx: usize) -> bool {
1171 if parent_idx >= self.nodes.len() || self.nodes[parent_idx].is_leaf {
1172 return false;
1173 }
1174 let children: Vec<usize> = self.nodes[parent_idx]
1175 .children
1176 .iter()
1177 .filter_map(|&c| c)
1178 .collect();
1179 for &ci in &children {
1180 if !self.nodes[ci].is_leaf {
1181 return false;
1182 }
1183 }
1184 for &ci in &children {
1185 self.nodes[ci].is_leaf = false;
1186 }
1187 self.nodes[parent_idx].children = [None; 8];
1188 self.nodes[parent_idx].is_leaf = true;
1189 true
1190 }
1191 pub fn leaf_count(&self) -> usize {
1193 self.nodes.iter().filter(|n| n.is_leaf).count()
1194 }
1195 pub fn max_active_level(&self) -> usize {
1197 self.nodes
1198 .iter()
1199 .filter(|n| n.is_leaf)
1200 .map(|n| n.level)
1201 .max()
1202 .unwrap_or(0)
1203 }
1204 pub fn cell_size_at(&self, level: usize) -> [f64; 3] {
1206 let nx = (self.domain[3] - self.domain[0]) / (1u64 << level) as f64;
1207 let ny = (self.domain[4] - self.domain[1]) / (1u64 << level) as f64;
1208 let nz = (self.domain[5] - self.domain[2]) / (1u64 << level) as f64;
1209 [nx, ny, nz]
1210 }
1211 pub fn node_centre(&self, node_idx: usize) -> [f64; 3] {
1213 let n = &self.nodes[node_idx];
1214 let cs = self.cell_size_at(n.level);
1215 [
1216 self.domain[0] + (n.coords[0] as f64 + 0.5) * cs[0],
1217 self.domain[1] + (n.coords[1] as f64 + 0.5) * cs[1],
1218 self.domain[2] + (n.coords[2] as f64 + 0.5) * cs[2],
1219 ]
1220 }
1221}