1use std::collections::{BTreeMap, BTreeSet, HashSet};
11
12use crate::adapt::{
13 AdaptationProvenanceMap, BoundaryRemeshingPolicy, FvStabilityThresholds,
14 MetricAdaptationAction, MetricAdaptationOptions, MetricAdaptationResult,
15 MetricRemeshingBackend, MetricSplitHint, MetricTensor, MetricThresholds,
16 adapt_with_metric_policy, select_cells_from_fvm_diagnostics, transfer_cell_types_refinement,
17 transfer_labels_refinement, transfer_ownership_refinement,
18};
19use crate::algs::communicator::Communicator;
20use crate::algs::distribute::{
21 CellPartitioner, DistributedMeshData, DistributionConfig, distribute_with_overlap,
22};
23use crate::algs::dual_graph::{DualGraph, build_dual};
24use crate::algs::point_sf::PointSF;
25use crate::algs::point_sf::{PointSfLeaf, RemotePoint};
26use crate::algs::renumber::{StratifiedOrdering, stratified_permutation};
27use crate::algs::submesh::{SubmeshMaps, SubmeshSelection, extract_by_label};
28use crate::data::atlas::Atlas;
29use crate::data::closure::{ClosureIndex, ClosureOrder, IdentitySectionSym, build_closure_index};
30use crate::data::coordinates::Coordinates;
31use crate::data::discretization::Discretization;
32use crate::data::global_map::{LocalToGlobalMap, global_vector_for_map};
33use crate::data::hanging_node_constraints::{
34 HangingNodeConstraints, apply_hanging_constraints_to_section,
35};
36use crate::data::multi_section::constrained_section_from_label_specs;
37use crate::data::section::Section;
38use crate::data::storage::{Storage, VecStorage};
39use crate::data::{ConstrainedSection, LabelConstraintSpec};
40use crate::diagnostics::{
41 FvmQualityDiagnostics, MeshCheckOptions, PrepareForSolveDiagnostics, PrepareForSolveOptions,
42 PrepareForSolvePreallocationDiagnostic, PrepareForSolveStepDiagnostic, fvm_cell_diagnostics,
43 fvm_quality_diagnostics, prepare_for_solve_prerequisites, run_mesh_checks,
44};
45use crate::geometry::fvm::build_fvm_face_metrics;
46use crate::io::MeshData;
47use crate::mesh_error::MeshSieveError;
48use crate::mesh_graph::{AdjacencyWeighting, MeshGraph, cell_adjacency_graph_with_cells};
49use crate::overlap::overlap::Overlap;
50use crate::physics::fe::{ElementClosureData, extract_oriented_element_closure};
51use crate::topology::anchors::TopologicalAnchors;
52use crate::topology::cache::InvalidateCache;
53use crate::topology::cell_type::CellType;
54use crate::topology::labels::LabelSet;
55use crate::topology::ownership::PointOwnership;
56use crate::topology::point::PointId;
57use crate::topology::refine::refine_mesh;
58use crate::topology::sieve::strata::compute_strata;
59use crate::topology::sieve::{MeshSieve, OrientedSieve, Sieve};
60
61#[derive(Clone, Debug)]
68pub struct MeshDMOptions {
69 pub pre_refine: usize,
71 pub distribute: bool,
73 pub distribute_overlap: usize,
75 pub post_refine: usize,
77 pub localize_coordinates: bool,
79 pub check_symmetry: bool,
81 pub check_skeleton: bool,
83 pub check_faces: bool,
85 pub check_geometry: bool,
87 pub check_all: bool,
89 pub reorder_section: Option<StratifiedOrdering>,
91 pub balance_boundary_ownership: bool,
93 pub synchronize_sections: bool,
95}
96
97impl Default for MeshDMOptions {
98 fn default() -> Self {
99 Self {
100 pre_refine: 0,
101 distribute: false,
102 distribute_overlap: 1,
103 post_refine: 0,
104 localize_coordinates: false,
105 check_symmetry: false,
106 check_skeleton: false,
107 check_faces: false,
108 check_geometry: false,
109 check_all: false,
110 reorder_section: None,
111 balance_boundary_ownership: false,
112 synchronize_sections: true,
113 }
114 }
115}
116
117impl MeshDMOptions {
118 pub fn distribution_config(&self) -> DistributionConfig {
120 DistributionConfig {
121 overlap_depth: self.distribute_overlap,
122 synchronize_sections: self.synchronize_sections || self.localize_coordinates,
123 balance_boundary_ownership: self.balance_boundary_ownership,
124 }
125 }
126}
127
128#[derive(Debug)]
130pub struct MeshDMBuilder<V, St = VecStorage<V>, CtSt = VecStorage<CellType>>
131where
132 St: Storage<V>,
133 CtSt: Storage<CellType>,
134{
135 mesh_data: MeshData<MeshSieve, V, St, CtSt>,
136 options: MeshDMOptions,
137}
138
139impl<V, St, CtSt> MeshDMBuilder<V, St, CtSt>
140where
141 V: Clone + Default,
142 St: Storage<V> + Clone,
143 CtSt: Storage<CellType> + Clone,
144{
145 pub fn new(topology: MeshSieve) -> Self {
147 Self {
148 mesh_data: MeshData::new(topology),
149 options: MeshDMOptions::default(),
150 }
151 }
152
153 pub fn from_mesh_data(mesh_data: MeshData<MeshSieve, V, St, CtSt>) -> Self {
155 Self {
156 mesh_data,
157 options: MeshDMOptions::default(),
158 }
159 }
160
161 pub fn options(mut self, options: MeshDMOptions) -> Self {
163 self.options = options;
164 self
165 }
166
167 pub fn configure(mut self, f: impl FnOnce(&mut MeshDMOptions)) -> Self {
169 f(&mut self.options);
170 self
171 }
172
173 pub fn coordinates(mut self, coordinates: Coordinates<V, St>) -> Self {
175 self.mesh_data.coordinates = Some(coordinates);
176 self
177 }
178
179 pub fn labels(mut self, labels: LabelSet) -> Self {
181 self.mesh_data.labels = Some(labels);
182 self
183 }
184
185 pub fn cell_types(mut self, cell_types: Section<CellType, CtSt>) -> Self {
187 self.mesh_data.cell_types = Some(cell_types);
188 self
189 }
190
191 pub fn discretization(mut self, discretization: Discretization) -> Self {
193 self.mesh_data.discretization = Some(discretization);
194 self
195 }
196
197 pub fn section(mut self, name: impl Into<String>, section: Section<V, St>) -> Self {
199 self.mesh_data.sections.insert(name.into(), section);
200 self
201 }
202
203 pub fn build(self) -> Result<MeshDM<V, St, CtSt>, MeshSieveError> {
205 let mut dm = MeshDM::from_mesh_data_with_options(self.mesh_data, self.options);
206 dm.setup_serial()?;
207 Ok(dm)
208 }
209}
210
211#[derive(Clone, Debug, PartialEq)]
213pub struct MeshVector<V> {
214 pub section: Option<String>,
216 pub values: Vec<V>,
218}
219
220#[derive(Clone, Copy, Debug, PartialEq, Eq)]
222pub enum MeshVectorInsertMode {
223 InsertValues,
225 AddValues,
227}
228
229#[derive(Clone, Copy, Debug, PartialEq, Eq)]
235pub struct MeshPointChart {
236 pub start: u64,
238 pub end_inclusive: u64,
240 pub point_count: usize,
242 pub is_dense: bool,
244}
245
246#[derive(Clone, Debug, PartialEq, Eq)]
248pub struct PreallocationGraph {
249 pub xadj: Vec<usize>,
251 pub adjncy: Vec<usize>,
253 pub order: Vec<PointId>,
255 pub row_nnz: Vec<usize>,
257}
258
259#[derive(Clone, Debug, PartialEq, Eq)]
261pub struct MeshDMLabelSelection {
262 pub label_name: String,
264 pub label_value: i32,
266 pub topology: SubmeshSelection,
268 pub section: Option<String>,
270}
271
272impl MeshDMLabelSelection {
273 pub fn new(label_name: impl Into<String>, label_value: i32) -> Self {
275 Self {
276 label_name: label_name.into(),
277 label_value,
278 topology: SubmeshSelection::FullClosure,
279 section: None,
280 }
281 }
282
283 pub fn topology(mut self, topology: SubmeshSelection) -> Self {
285 self.topology = topology;
286 self
287 }
288
289 pub fn section(mut self, section: impl Into<String>) -> Self {
291 self.section = Some(section.into());
292 self
293 }
294}
295
296#[derive(Debug)]
298pub struct MeshDMSubmesh<V, St = VecStorage<V>, CtSt = VecStorage<CellType>>
299where
300 St: Storage<V>,
301 CtSt: Storage<CellType>,
302{
303 pub dm: MeshDM<V, St, CtSt>,
305 pub maps: SubmeshMaps,
307}
308
309#[derive(Clone, Debug, Default, PartialEq, Eq)]
311pub struct MeshDMDistribution {
312 pub point_owners: Vec<usize>,
314 pub cell_parts: Vec<usize>,
316 pub rank: usize,
318 pub size: usize,
320}
321
322#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
324pub enum MeshDMTransferStrategy {
325 #[default]
327 TopologyOnly,
328 PreserveLabels,
330 PreserveCoordinatesAndLabels,
332 PreserveAll,
334}
335
336#[derive(Clone, Debug, Default)]
338pub struct MeshDMAdaptLabelPolicy {
339 pub fixed_boundary_labels: Vec<(String, i32)>,
340 pub protected_region_labels: Vec<(String, i32)>,
341 pub relax_region_labels: Vec<(String, i32)>,
342}
343
344#[derive(Clone, Debug)]
346pub struct MeshDMMetricAdaptOptions {
347 pub thresholds: MetricThresholds,
348 pub labels: MeshDMAdaptLabelPolicy,
349 pub transfer: MeshDMTransferStrategy,
350 pub normalization: crate::adapt::MetricNormalizationControls,
351 pub backend: MetricRemeshingBackend,
352}
353
354impl Default for MeshDMMetricAdaptOptions {
355 fn default() -> Self {
356 Self {
357 thresholds: MetricThresholds::default(),
358 labels: MeshDMAdaptLabelPolicy::default(),
359 transfer: MeshDMTransferStrategy::TopologyOnly,
360 normalization: crate::adapt::MetricNormalizationControls::default(),
361 backend: MetricRemeshingBackend::Internal,
362 }
363 }
364}
365
366#[derive(Clone, Debug, Default)]
368pub struct MeshDMAdaptDiagnostics {
369 pub changed_cells: usize,
370 pub preserved_labels: usize,
371 pub failed_transfer_points: Vec<PointId>,
372 pub split_hint_cells: usize,
373 pub fvm_face_metrics_cached: usize,
374 pub boundary_faces: usize,
375 pub provenance_edges: usize,
376 pub transferred_sections: usize,
377 pub transferred_constraints: usize,
378 pub transferred_overlap_points: usize,
379}
380
381#[derive(Clone, Debug)]
383pub struct MeshDMMetricAdaptResult {
384 pub action: MetricAdaptationAction,
385 pub metrics: Vec<(PointId, crate::adapt::MetricCellMetrics)>,
386 pub diagnostics: MeshDMAdaptDiagnostics,
387 pub fv_selection: Option<(Vec<PointId>, Vec<PointId>)>,
388 pub provenance: AdaptationProvenanceMap,
389}
390
391#[derive(Clone, Debug, Default)]
392pub struct MeshDMAdaptIterationOptions {
393 pub max_iterations: usize,
394 pub stop_when_below_threshold: bool,
395}
396
397#[derive(Clone, Debug, Default)]
398pub struct MeshDMFvIterationReport {
399 pub pass_index: usize,
400 pub fvm_quality: Option<FvmQualityDiagnostics>,
401 pub refine_candidates: usize,
402 pub coarsen_candidates: usize,
403}
404
405#[derive(Clone, Debug, Default)]
407pub struct MeshDMProvenance<C: Communicator + Sync + 'static> {
408 pub load_map: Option<PointSF<'static, C>>,
409 pub redistribute_map: Option<PointSF<'static, C>>,
410 pub section_map: Option<PointSF<'static, C>>,
411 pub storage_version: Option<i32>,
412 pub permutation_source: Option<String>,
413 pub redistribution_map_id: Option<String>,
414 pub saved_rank_count: Option<usize>,
415 pub loaded_rank_count: Option<usize>,
416}
417
418impl<C> MeshDMProvenance<C>
419where
420 C: Communicator + Sync + 'static,
421{
422 pub fn validate_consistency(&self) -> Result<(), MeshSieveError> {
424 if matches!(
425 (self.saved_rank_count, self.loaded_rank_count),
426 (Some(0), _) | (_, Some(0))
427 ) {
428 return Err(MeshSieveError::MeshIoParse(
429 "DMPlex provenance rank counts must be non-zero".to_string(),
430 ));
431 }
432 if let (Some(saved), Some(loaded)) = (self.saved_rank_count, self.loaded_rank_count)
433 && saved != loaded
434 && self.redistribute_map.is_none()
435 {
436 return Err(MeshSieveError::MeshIoParse(format!(
437 "DMPlex provenance records save/load rank change {saved}->{loaded} without a redistribution SF"
438 )));
439 }
440
441 let loaded = self.loaded_rank_count;
442 if let Some(sf) = &self.load_map {
443 sf.validate_provenance_stage("load", loaded)?;
444 }
445 if let Some(sf) = &self.redistribute_map {
446 sf.validate_provenance_stage("redistribute", loaded)?;
447 }
448 if let Some(sf) = &self.section_map {
449 sf.validate_provenance_stage("section", loaded)?;
450 }
451 if self.section_map.is_some() && self.load_map.is_none() {
452 return Err(MeshSieveError::MeshIoParse(
453 "DMPlex provenance has a section SF but no load SF".to_string(),
454 ));
455 }
456 if let (Some(load), Some(section)) = (&self.load_map, &self.section_map) {
457 let load_range: BTreeSet<_> = load.leaves().map(|leaf| leaf.remote.point).collect();
458 let missing = section.missing_leaf_points(load_range);
459 if !missing.is_empty() {
460 return Err(MeshSieveError::MeshIoParse(format!(
461 "section SF does not cover loaded points {:?}",
462 missing
463 )));
464 }
465 }
466 Ok(())
467 }
468}
469
470impl<St, CtSt> MeshDM<f64, St, CtSt>
471where
472 St: Storage<f64> + Clone,
473 CtSt: Storage<CellType> + Clone,
474{
475 pub fn adapt_with_attached_metric<C>(
477 &mut self,
478 metric_section_name: &str,
479 metric: &Section<MetricTensor, VecStorage<MetricTensor>>,
480 coarsen_plan: C,
481 options: MeshDMMetricAdaptOptions,
482 ) -> Result<MeshDMMetricAdaptResult, MeshSieveError>
483 where
484 C: Fn(&[MetricSplitHint]) -> Vec<crate::topology::coarsen::CoarsenEntity>,
485 {
486 let coords = self.mesh_data.coordinates.as_ref().ok_or_else(|| {
487 MeshSieveError::MissingSectionName {
488 name: "coordinates".into(),
489 }
490 })?;
491 let cell_types = self.mesh_data.cell_types.as_ref().ok_or_else(|| {
492 MeshSieveError::MissingSectionName {
493 name: "cell_types".into(),
494 }
495 })?;
496 let _ = metric_section_name;
497
498 let boundary_policy = self.compose_boundary_policy(&options.labels);
499 let result: MetricAdaptationResult<()> = adapt_with_metric_policy(
500 &mut self.mesh_data.sieve,
501 cell_types,
502 coords,
503 metric,
504 self.mesh_data.labels.as_ref(),
505 coarsen_plan,
506 options.thresholds,
507 MetricAdaptationOptions {
508 boundary_policy,
509 normalization: options.normalization,
510 backend: options.backend,
511 },
512 )?;
513
514 let provenance = result.provenance.clone();
515 let diagnostics =
516 self.transfer_after_adaptation(&result.action, &provenance, options.transfer)?;
517 self.enforce_post_adaptation_fvm_contract()?;
518 Ok(MeshDMMetricAdaptResult {
519 action: result.action,
520 metrics: result.metrics,
521 diagnostics,
522 fv_selection: None,
523 provenance,
524 })
525 }
526
527 pub fn adapt_until_fv_thresholds<C>(
528 &mut self,
529 metric_section_name: &str,
530 metric: &Section<MetricTensor, VecStorage<MetricTensor>>,
531 coarsen_plan: C,
532 options: MeshDMMetricAdaptOptions,
533 fv_thresholds: FvStabilityThresholds,
534 iter: MeshDMAdaptIterationOptions,
535 ) -> Result<Vec<MeshDMMetricAdaptResult>, MeshSieveError>
536 where
537 C: Fn(&[MetricSplitHint]) -> Vec<crate::topology::coarsen::CoarsenEntity> + Copy,
538 {
539 let mut history = Vec::new();
540 for _ in 0..iter.max_iterations.max(1) {
541 let mut pass = self.adapt_with_attached_metric(
542 metric_section_name,
543 metric,
544 coarsen_plan,
545 options.clone(),
546 )?;
547 let coords = self.mesh_data.coordinates.as_ref().ok_or_else(|| {
548 MeshSieveError::MissingSectionName {
549 name: "coordinates".into(),
550 }
551 })?;
552 let cell_types = self.mesh_data.cell_types.as_ref().ok_or_else(|| {
553 MeshSieveError::MissingSectionName {
554 name: "cell_types".into(),
555 }
556 })?;
557 let cell_diags = fvm_cell_diagnostics(&self.mesh_data.sieve, cell_types, coords)?;
558 let selection = select_cells_from_fvm_diagnostics(&cell_diags, fv_thresholds);
559 pass.fv_selection = Some((
560 selection.refine_cells.clone(),
561 selection.coarsen_cells.clone(),
562 ));
563 history.push(pass.clone());
564 let stop_by_action = matches!(pass.action, MetricAdaptationAction::NoChange);
565 let stop_by_threshold = iter.stop_when_below_threshold
566 && selection.refine_cells.is_empty()
567 && selection.coarsen_cells.is_empty();
568 if stop_by_action || stop_by_threshold {
569 break;
570 }
571 }
572 Ok(history)
573 }
574
575 pub fn adapt_until_fv_thresholds_with_history<C>(
576 &mut self,
577 metric_section_name: &str,
578 metric: &Section<MetricTensor, VecStorage<MetricTensor>>,
579 coarsen_plan: C,
580 options: MeshDMMetricAdaptOptions,
581 fv_thresholds: FvStabilityThresholds,
582 iter: MeshDMAdaptIterationOptions,
583 ) -> Result<(Vec<MeshDMMetricAdaptResult>, Vec<MeshDMFvIterationReport>), MeshSieveError>
584 where
585 C: Fn(&[MetricSplitHint]) -> Vec<crate::topology::coarsen::CoarsenEntity> + Copy,
586 {
587 let mut history = Vec::new();
588 let mut reports = Vec::new();
589 for pass_idx in 0..iter.max_iterations.max(1) {
590 let mut pass = self.adapt_with_attached_metric(
591 metric_section_name,
592 metric,
593 coarsen_plan,
594 options.clone(),
595 )?;
596 self.refresh_fv_geometry_caches()?;
597 let coords = self.mesh_data.coordinates.as_ref().ok_or_else(|| {
598 MeshSieveError::MissingSectionName {
599 name: "coordinates".into(),
600 }
601 })?;
602 let cell_types = self.mesh_data.cell_types.as_ref().ok_or_else(|| {
603 MeshSieveError::MissingSectionName {
604 name: "cell_types".into(),
605 }
606 })?;
607 let quality = fvm_quality_diagnostics(&self.mesh_data.sieve, cell_types, coords).ok();
608 let cell_diags = fvm_cell_diagnostics(&self.mesh_data.sieve, cell_types, coords)?;
609 let selection = select_cells_from_fvm_diagnostics(&cell_diags, fv_thresholds);
610 pass.fv_selection = Some((
611 selection.refine_cells.clone(),
612 selection.coarsen_cells.clone(),
613 ));
614 reports.push(MeshDMFvIterationReport {
615 pass_index: pass_idx,
616 fvm_quality: quality,
617 refine_candidates: selection.refine_cells.len(),
618 coarsen_candidates: selection.coarsen_cells.len(),
619 });
620 history.push(pass.clone());
621 let stop_by_action = matches!(pass.action, MetricAdaptationAction::NoChange);
622 let stop_by_threshold = iter.stop_when_below_threshold
623 && selection.refine_cells.is_empty()
624 && selection.coarsen_cells.is_empty();
625 if stop_by_action || stop_by_threshold {
626 break;
627 }
628 }
629 Ok((history, reports))
630 }
631
632 pub fn adapt_with_attached_metric_iterative<C, F>(
634 &mut self,
635 metric_section_name: &str,
636 metric: &Section<MetricTensor, VecStorage<MetricTensor>>,
637 coarsen_plan: C,
638 options: MeshDMMetricAdaptOptions,
639 max_iterations: usize,
640 stop: F,
641 ) -> Result<Vec<MeshDMMetricAdaptResult>, MeshSieveError>
642 where
643 C: Fn(&[MetricSplitHint]) -> Vec<crate::topology::coarsen::CoarsenEntity> + Copy,
644 F: Fn(&MeshDMMetricAdaptResult) -> bool,
645 {
646 let mut history = Vec::new();
647 for _ in 0..max_iterations {
648 let pass = self.adapt_with_attached_metric(
649 metric_section_name,
650 metric,
651 coarsen_plan,
652 options.clone(),
653 )?;
654 let done = matches!(pass.action, MetricAdaptationAction::NoChange) || stop(&pass);
655 history.push(pass);
656 if done {
657 break;
658 }
659 }
660 Ok(history)
661 }
662
663 fn compose_boundary_policy(&self, labels: &MeshDMAdaptLabelPolicy) -> BoundaryRemeshingPolicy {
664 let mut strata = labels.fixed_boundary_labels.clone();
665 strata.extend(labels.protected_region_labels.clone());
666 if strata.is_empty() {
667 BoundaryRemeshingPolicy::PreserveBoundary
668 } else {
669 BoundaryRemeshingPolicy::PreserveLabeled(strata)
670 }
671 }
672
673 fn transfer_after_adaptation(
674 &mut self,
675 action: &MetricAdaptationAction,
676 provenance: &AdaptationProvenanceMap,
677 strategy: MeshDMTransferStrategy,
678 ) -> Result<MeshDMAdaptDiagnostics, MeshSieveError> {
679 let changed_cells = match action {
680 MetricAdaptationAction::Refined { mesh } => mesh.cell_refinement.len(),
681 MetricAdaptationAction::Coarsened { mesh } => mesh.transfer_map.len(),
682 MetricAdaptationAction::NoChange => 0,
683 };
684 let preserved_labels = self
685 .mesh_data
686 .labels
687 .as_ref()
688 .map_or(0, |l| l.iter().count());
689 let coords = self.mesh_data.coordinates.as_ref();
690 let cell_types = self.mesh_data.cell_types.as_ref();
691 let (fvm_face_metrics_cached, boundary_faces) = if let (Some(coords), Some(cell_types)) =
692 (coords, cell_types)
693 {
694 let face_metrics = build_fvm_face_metrics(&self.mesh_data.sieve, cell_types, coords)?;
695 let bfaces = face_metrics.iter().filter(|m| m.neighbor.is_none()).count();
696 (face_metrics.len(), bfaces)
697 } else {
698 (0, 0)
699 };
700 let mut transferred_sections = 0;
701 let mut transferred_constraints = 0;
702 let mut transferred_overlap_points = 0;
703 let failed_transfer_points = Vec::new();
704
705 match action {
706 MetricAdaptationAction::Refined { mesh } => {
707 if matches!(
708 strategy,
709 MeshDMTransferStrategy::PreserveLabels
710 | MeshDMTransferStrategy::PreserveCoordinatesAndLabels
711 | MeshDMTransferStrategy::PreserveAll
712 ) && let Some(labels) = self.mesh_data.labels.as_ref()
713 {
714 self.mesh_data.labels =
715 Some(transfer_labels_refinement(labels, &mesh.cell_refinement));
716 }
717 if matches!(
718 strategy,
719 MeshDMTransferStrategy::PreserveCoordinatesAndLabels
720 | MeshDMTransferStrategy::PreserveAll
721 ) && let Some(coords) = mesh.coordinates.clone()
722 {
723 self.mesh_data.coordinates = Some(convert_coordinates_storage(coords)?);
724 }
725 if matches!(strategy, MeshDMTransferStrategy::PreserveAll) {
726 if let Some(cell_types) = self.mesh_data.cell_types.as_ref() {
727 self.mesh_data.cell_types = Some(convert_cell_type_section_storage(
728 transfer_cell_types_refinement(cell_types, &mesh.cell_refinement)?,
729 )?);
730 }
731 self.mesh_data.sections = transfer_dm_sections_refinement(
732 &self.mesh_data.sections,
733 &mesh.cell_refinement,
734 )?;
735 transferred_sections = self.mesh_data.sections.len();
736 transferred_constraints = mesh.hanging_constraints.constraints().len();
737 if let Some(ownership) = self.ownership.as_ref() {
738 let rank = self.distribution.as_ref().map_or(0, |d| d.rank);
739 self.ownership =
740 Some(transfer_ownership_refinement(ownership, mesh, rank)?);
741 }
742 if let Some(overlap) = self.overlap.as_ref() {
743 let (mapped, count) =
744 transfer_overlap_refinement(overlap, &mesh.cell_refinement)?;
745 self.overlap = Some(mapped);
746 transferred_overlap_points = count;
747 }
748 }
749 self.mesh_data.sieve = oriented_from_refined_sieve(&mesh.sieve);
750 }
751 MetricAdaptationAction::Coarsened { mesh } => {
752 if matches!(strategy, MeshDMTransferStrategy::PreserveAll) {
753 self.mesh_data.sections = transfer_dm_sections_coarsening(
754 &self.mesh_data.sections,
755 &mesh.transfer_map,
756 )?;
757 transferred_sections = self.mesh_data.sections.len();
758 }
759 self.mesh_data.sieve = oriented_from_refined_sieve(&mesh.sieve);
760 }
761 MetricAdaptationAction::NoChange => {}
762 }
763
764 self.refresh_fv_geometry_caches()?;
765 let diagnostics = MeshDMAdaptDiagnostics {
766 changed_cells,
767 preserved_labels,
768 failed_transfer_points,
769 split_hint_cells: changed_cells,
770 fvm_face_metrics_cached,
771 boundary_faces,
772 provenance_edges: provenance
773 .old_to_new
774 .iter()
775 .map(|(_, points)| points.len())
776 .sum(),
777 transferred_sections,
778 transferred_constraints,
779 transferred_overlap_points,
780 };
781 Ok(diagnostics)
782 }
783
784 fn enforce_post_adaptation_fvm_contract(&mut self) -> Result<(), MeshSieveError> {
791 self.mesh_data.sieve.invalidate_cache();
792 let _ = compute_strata(&self.mesh_data.sieve)?;
793 if let (Some(coords), Some(cell_types)) = (
794 self.mesh_data.coordinates.as_ref(),
795 self.mesh_data.cell_types.as_ref(),
796 ) {
797 let face_metrics = build_fvm_face_metrics(&self.mesh_data.sieve, cell_types, coords)?;
798 let faces = face_metrics.iter().map(|m| m.face);
799 let loops = crate::physics::fvm::classify_face_loops(&self.mesh_data.sieve, faces)?;
800 let mut packed = crate::physics::fvm::FvmInputs::new(
801 loops.internal.into_iter().chain(loops.boundary),
802 Vec::new(),
803 Vec::new(),
804 );
805 packed.build_packed_cache();
806 }
807 Ok(())
808 }
809
810 fn refresh_fv_geometry_caches(&mut self) -> Result<(), MeshSieveError> {
811 let _ = compute_strata(&self.mesh_data.sieve)?;
812 if let (Some(coords), Some(cell_types)) = (
813 self.mesh_data.coordinates.as_ref(),
814 self.mesh_data.cell_types.as_ref(),
815 ) {
816 let _ = build_fvm_face_metrics(&self.mesh_data.sieve, cell_types, coords)?;
817 }
818 Ok(())
819 }
820}
821
822fn convert_section_storage<V, Src, Dst>(
823 section: Section<V, Src>,
824) -> Result<Section<V, Dst>, MeshSieveError>
825where
826 V: Clone + Default,
827 Src: Storage<V>,
828 Dst: Storage<V> + Clone,
829{
830 let atlas = section.atlas().clone();
831 let mut out = Section::<V, Dst>::new(atlas);
832 for (point, slice) in section.iter() {
833 out.try_set(point, slice)?;
834 }
835 Ok(out)
836}
837
838fn convert_coordinates_storage<St>(
839 coords: Coordinates<f64, VecStorage<f64>>,
840) -> Result<Coordinates<f64, St>, MeshSieveError>
841where
842 St: Storage<f64> + Clone,
843{
844 let section = convert_section_storage::<f64, _, St>(coords.section().clone())?;
845 Coordinates::from_section(
846 coords.topological_dimension(),
847 coords.embedding_dimension(),
848 section,
849 )
850}
851
852fn convert_cell_type_section_storage<CtSt>(
853 section: Section<CellType, VecStorage<CellType>>,
854) -> Result<Section<CellType, CtSt>, MeshSieveError>
855where
856 CtSt: Storage<CellType> + Clone,
857{
858 convert_section_storage::<CellType, _, CtSt>(section)
859}
860
861fn transfer_section_refinement_as_section<St>(
862 section: &Section<f64, St>,
863 refinement: &[(PointId, Vec<(PointId, crate::topology::arrow::Polarity)>)],
864) -> Result<Section<f64, St>, MeshSieveError>
865where
866 St: Storage<f64> + Clone,
867{
868 let mut atlas = Atlas::default();
869 let mut values: Vec<(PointId, Vec<f64>)> = Vec::new();
870 for (old, new_points) in refinement {
871 if let Ok(slice) = section.try_restrict(*old) {
872 for (new_point, _) in new_points {
873 atlas.try_insert(*new_point, slice.len())?;
874 values.push((*new_point, slice.to_vec()));
875 }
876 }
877 }
878 let mut out = Section::<f64, St>::new(atlas);
879 for (point, slice) in values {
880 out.try_set(point, &slice)?;
881 }
882 Ok(out)
883}
884
885fn transfer_dm_sections_refinement<St>(
886 sections: &BTreeMap<String, Section<f64, St>>,
887 refinement: &[(PointId, Vec<(PointId, crate::topology::arrow::Polarity)>)],
888) -> Result<BTreeMap<String, Section<f64, St>>, MeshSieveError>
889where
890 St: Storage<f64> + Clone,
891{
892 let mut out = BTreeMap::new();
893 for (name, section) in sections {
894 out.insert(
895 name.clone(),
896 transfer_section_refinement_as_section(section, refinement)?,
897 );
898 }
899 Ok(out)
900}
901
902fn transfer_section_coarsening_as_section<St>(
903 section: &Section<f64, St>,
904 transfer: &[(PointId, Vec<(PointId, crate::topology::arrow::Polarity)>)],
905) -> Result<Section<f64, St>, MeshSieveError>
906where
907 St: Storage<f64> + Clone,
908{
909 let mut atlas = Atlas::default();
910 let mut values: Vec<(PointId, Vec<f64>)> = Vec::new();
911 for (new_point, old_points) in transfer {
912 let Some((first_old, _)) = old_points.first() else {
913 continue;
914 };
915 if let Ok(first) = section.try_restrict(*first_old) {
916 let mut accum = vec![0.0; first.len()];
917 let mut count = 0.0;
918 for (old, _) in old_points {
919 if let Ok(slice) = section.try_restrict(*old)
920 && slice.len() == accum.len()
921 {
922 for (a, v) in accum.iter_mut().zip(slice) {
923 *a += *v;
924 }
925 count += 1.0;
926 }
927 }
928 if count > 0.0 {
929 for a in &mut accum {
930 *a /= count;
931 }
932 atlas.try_insert(*new_point, accum.len())?;
933 values.push((*new_point, accum));
934 }
935 }
936 }
937 let mut out = Section::<f64, St>::new(atlas);
938 for (point, slice) in values {
939 out.try_set(point, &slice)?;
940 }
941 Ok(out)
942}
943
944fn transfer_dm_sections_coarsening<St>(
945 sections: &BTreeMap<String, Section<f64, St>>,
946 transfer: &[(PointId, Vec<(PointId, crate::topology::arrow::Polarity)>)],
947) -> Result<BTreeMap<String, Section<f64, St>>, MeshSieveError>
948where
949 St: Storage<f64> + Clone,
950{
951 let mut out = BTreeMap::new();
952 for (name, section) in sections {
953 out.insert(
954 name.clone(),
955 transfer_section_coarsening_as_section(section, transfer)?,
956 );
957 }
958 Ok(out)
959}
960
961fn oriented_from_refined_sieve(refined: &impl Sieve<Point = PointId>) -> MeshSieve {
962 let mut out = MeshSieve::default();
963 for src in refined.base_points() {
964 for dst in refined.cone_points(src) {
965 out.add_arrow(src, dst, ());
966 }
967 }
968 out
969}
970
971fn transfer_overlap_refinement(
972 overlap: &Overlap,
973 refinement: &[(PointId, Vec<(PointId, crate::topology::arrow::Polarity)>)],
974) -> Result<(Overlap, usize), MeshSieveError> {
975 let mut out = overlap.clone();
976 let mut count = 0;
977 for (old, new_points) in refinement {
978 for rank in overlap.neighbor_ranks() {
979 for (local, remote) in overlap.links_to(rank) {
980 if local == *old {
981 for (new_point, _) in new_points {
982 match remote {
983 Some(remote_point) => {
984 out.try_add_link(*new_point, rank, remote_point)?
985 }
986 None => {
987 out.try_add_link_structural_one(*new_point, rank)?;
988 }
989 }
990 count += 1;
991 }
992 }
993 }
994 }
995 }
996 Ok((out, count))
997}
998
999#[derive(Debug)]
1002pub struct MeshDM<V, St = VecStorage<V>, CtSt = VecStorage<CellType>>
1003where
1004 St: Storage<V>,
1005 CtSt: Storage<CellType>,
1006{
1007 mesh_data: MeshData<MeshSieve, V, St, CtSt>,
1008 options: MeshDMOptions,
1009 ownership: Option<PointOwnership>,
1010 overlap: Option<Overlap>,
1011 global_sections: BTreeMap<String, LocalToGlobalMap>,
1012 anchors: TopologicalAnchors,
1013 hanging_constraints: BTreeMap<String, HangingNodeConstraints<V>>,
1014 distribution: Option<MeshDMDistribution>,
1015 provenance_maps: MeshDMProvenance<crate::algs::communicator::NoComm>,
1016}
1017
1018impl<V, St, CtSt> MeshDM<V, St, CtSt>
1019where
1020 V: Clone + Default,
1021 St: Storage<V> + Clone,
1022 CtSt: Storage<CellType> + Clone,
1023{
1024 pub fn builder(topology: MeshSieve) -> MeshDMBuilder<V, St, CtSt> {
1026 MeshDMBuilder::new(topology)
1027 }
1028
1029 pub fn from_mesh_data(mesh_data: MeshData<MeshSieve, V, St, CtSt>) -> Self {
1031 Self::from_mesh_data_with_options(mesh_data, MeshDMOptions::default())
1032 }
1033
1034 pub fn from_mesh_data_with_options(
1036 mesh_data: MeshData<MeshSieve, V, St, CtSt>,
1037 options: MeshDMOptions,
1038 ) -> Self {
1039 Self {
1040 mesh_data,
1041 options,
1042 ownership: None,
1043 overlap: None,
1044 global_sections: BTreeMap::new(),
1045 anchors: TopologicalAnchors::default(),
1046 hanging_constraints: BTreeMap::new(),
1047 distribution: None,
1048 provenance_maps: MeshDMProvenance::default(),
1049 }
1050 }
1051
1052 pub fn into_mesh_data(self) -> MeshData<MeshSieve, V, St, CtSt> {
1054 self.mesh_data
1055 }
1056
1057 pub fn mesh_data(&self) -> &MeshData<MeshSieve, V, St, CtSt> {
1059 &self.mesh_data
1060 }
1061
1062 pub fn topology(&self) -> &MeshSieve {
1064 &self.mesh_data.sieve
1065 }
1066
1067 pub fn topology_mut(&mut self) -> &mut MeshSieve {
1069 self.invalidate_numbering();
1070 &mut self.mesh_data.sieve
1071 }
1072
1073 pub fn labels(&self) -> Option<&LabelSet> {
1075 self.mesh_data.labels.as_ref()
1076 }
1077
1078 pub fn coordinates(&self) -> Option<&Coordinates<V, St>> {
1080 self.mesh_data.coordinates.as_ref()
1081 }
1082
1083 pub fn coordinates_mut(&mut self) -> Option<&mut Coordinates<V, St>> {
1085 self.mesh_data.coordinates.as_mut()
1086 }
1087
1088 pub fn set_coordinates(
1090 &mut self,
1091 coordinates: Coordinates<V, St>,
1092 ) -> Option<Coordinates<V, St>> {
1093 self.mesh_data.coordinates.replace(coordinates)
1094 }
1095
1096 pub fn section(&self, name: &str) -> Option<&Section<V, St>> {
1098 self.mesh_data.sections.get(name)
1099 }
1100
1101 pub fn section_mut(&mut self, name: &str) -> Option<&mut Section<V, St>> {
1103 self.global_sections.remove(name);
1104 self.mesh_data.sections.get_mut(name)
1105 }
1106
1107 pub fn labels_mut_or_insert(&mut self) -> &mut LabelSet {
1109 self.mesh_data.labels.get_or_insert_with(LabelSet::new)
1110 }
1111
1112 pub fn insert_section(
1114 &mut self,
1115 name: impl Into<String>,
1116 section: Section<V, St>,
1117 ) -> Option<Section<V, St>> {
1118 let name = name.into();
1119 self.global_sections.remove(&name);
1120 self.mesh_data.sections.insert(name, section)
1121 }
1122
1123 pub fn remove_section(&mut self, name: &str) -> Option<Section<V, St>> {
1125 self.global_sections.remove(name);
1126 self.hanging_constraints.remove(name);
1127 self.mesh_data.sections.remove(name)
1128 }
1129
1130 pub fn cell_types(&self) -> Option<&Section<CellType, CtSt>> {
1132 self.mesh_data.cell_types.as_ref()
1133 }
1134
1135 pub fn cell_types_mut(&mut self) -> Option<&mut Section<CellType, CtSt>> {
1137 self.mesh_data.cell_types.as_mut()
1138 }
1139
1140 pub fn set_cell_types(
1142 &mut self,
1143 cell_types: Section<CellType, CtSt>,
1144 ) -> Option<Section<CellType, CtSt>> {
1145 self.mesh_data.cell_types.replace(cell_types)
1146 }
1147
1148 pub fn discretization(&self) -> Option<&Discretization> {
1150 self.mesh_data.discretization.as_ref()
1151 }
1152
1153 pub fn discretization_mut(&mut self) -> Option<&mut Discretization> {
1155 self.mesh_data.discretization.as_mut()
1156 }
1157
1158 pub fn set_discretization(&mut self, discretization: Discretization) -> Option<Discretization> {
1160 self.mesh_data.discretization.replace(discretization)
1161 }
1162
1163 pub fn ownership(&self) -> Option<&PointOwnership> {
1165 self.ownership.as_ref()
1166 }
1167
1168 pub fn overlap(&self) -> Option<&Overlap> {
1170 self.overlap.as_ref()
1171 }
1172
1173 pub fn distribution(&self) -> Option<&MeshDMDistribution> {
1175 self.distribution.as_ref()
1176 }
1177
1178 pub fn provenance_maps(&self) -> &MeshDMProvenance<crate::algs::communicator::NoComm> {
1179 &self.provenance_maps
1180 }
1181
1182 pub fn options(&self) -> &MeshDMOptions {
1184 &self.options
1185 }
1186
1187 pub fn set_topological_anchors(&mut self, anchors: TopologicalAnchors) {
1189 self.anchors = anchors;
1190 }
1191
1192 pub fn topological_anchors(&self) -> &TopologicalAnchors {
1194 &self.anchors
1195 }
1196
1197 pub fn set_hanging_constraints_for_section(
1199 &mut self,
1200 section_name: impl Into<String>,
1201 constraints: HangingNodeConstraints<V>,
1202 ) {
1203 self.hanging_constraints
1204 .insert(section_name.into(), constraints);
1205 }
1206
1207 pub fn hanging_constraints_for_section(
1209 &self,
1210 section_name: &str,
1211 ) -> Option<&HangingNodeConstraints<V>> {
1212 self.hanging_constraints.get(section_name)
1213 }
1214
1215 fn apply_hanging_constraints_to_sections(&mut self) -> Result<(), MeshSieveError>
1216 where
1217 V: core::ops::AddAssign + core::ops::Mul<Output = V>,
1218 {
1219 let constraints = self.hanging_constraints.clone();
1220 for (name, hanging) in constraints {
1221 let section = self
1222 .mesh_data
1223 .sections
1224 .get_mut(&name)
1225 .ok_or_else(|| MeshSieveError::MissingSectionName { name: name.clone() })?;
1226 apply_hanging_constraints_to_section(section, &hanging)?;
1227 }
1228 Ok(())
1229 }
1230
1231 pub fn setup_serial(&mut self) -> Result<(), MeshSieveError> {
1233 self.run_refinement_passes(
1234 self.options.pre_refine,
1235 MeshDMTransferStrategy::PreserveCoordinatesAndLabels,
1236 )?;
1237 if let Some(ordering) = self.options.reorder_section {
1238 self.reorder_sections(ordering)?;
1239 }
1240 self.enforce_phase_invariants()?;
1241 self.run_requested_checks()?;
1242 self.run_refinement_passes(
1243 self.options.post_refine,
1244 MeshDMTransferStrategy::PreserveCoordinatesAndLabels,
1245 )?;
1246 self.enforce_phase_invariants()?;
1247 self.run_requested_checks()?;
1248 Ok(())
1249 }
1250
1251 fn run_refinement_passes(
1252 &mut self,
1253 passes: usize,
1254 strategy: MeshDMTransferStrategy,
1255 ) -> Result<(), MeshSieveError> {
1256 for _ in 0..passes {
1257 self.run_single_refinement_pass(strategy)?;
1258 self.enforce_phase_invariants()?;
1259 self.run_requested_checks()?;
1260 }
1261 Ok(())
1262 }
1263
1264 fn run_single_refinement_pass(
1265 &mut self,
1266 strategy: MeshDMTransferStrategy,
1267 ) -> Result<(), MeshSieveError> {
1268 let Some(cell_types) = self.mesh_data.cell_types.as_ref() else {
1269 return Ok(());
1270 };
1271 let refined = refine_mesh(&mut self.mesh_data.sieve, cell_types)?;
1272 self.mesh_data.sieve = refined.sieve;
1273 if matches!(
1274 strategy,
1275 MeshDMTransferStrategy::PreserveCoordinatesAndLabels
1276 ) {
1277 let _ = refined.coordinates;
1278 }
1279 if self.mesh_data.labels.is_some()
1280 && matches!(
1281 strategy,
1282 MeshDMTransferStrategy::PreserveLabels
1283 | MeshDMTransferStrategy::PreserveCoordinatesAndLabels
1284 )
1285 {
1286 }
1288 self.ensure_serial_ownership(0)?;
1289 if let Some(ownership) = &self.ownership {
1290 self.provenance_maps.section_map = Some(crate::algs::point_sf::create_process_sf::<
1291 crate::algs::communicator::NoComm,
1292 >(ownership, 0));
1293 }
1294 Ok(())
1295 }
1296
1297 fn enforce_phase_invariants(&mut self) -> Result<(), MeshSieveError> {
1298 let _ = compute_strata(&self.mesh_data.sieve)?;
1299 self.mesh_data.sieve.invalidate_cache();
1300 if self.options.reorder_section.is_none() {
1301 self.reorder_sections(StratifiedOrdering::CellFirst)?;
1302 }
1303 Ok(())
1304 }
1305
1306 fn closure_index_for_points(
1307 &self,
1308 point: PointId,
1309 order: &ClosureOrder,
1310 ) -> Result<ClosureIndex<i32>, MeshSieveError> {
1311 let mut atlas = Atlas::default();
1312 let mut closure_points: Vec<_> =
1313 self.mesh_data.sieve.closure_iter_sorted([point]).collect();
1314 closure_points.sort_unstable();
1315 closure_points.dedup();
1316 for point in closure_points {
1317 atlas.try_insert(point, 1)?;
1318 }
1319 let section = Section::<(), VecStorage<()>>::new(atlas);
1320 build_closure_index(
1321 &self.mesh_data.sieve,
1322 §ion,
1323 point,
1324 0,
1325 order,
1326 &IdentitySectionSym,
1327 )
1328 }
1329
1330 pub fn run_requested_checks(&mut self) -> Result<(), MeshSieveError> {
1332 let check_all = self.options.check_all;
1333 let check_options = MeshCheckOptions {
1334 check_symmetry: check_all || self.options.check_symmetry,
1335 check_skeleton: check_all || self.options.check_skeleton,
1336 check_faces: check_all || self.options.check_faces,
1337 check_geometry: check_all || self.options.check_geometry,
1338 check_overlap: check_all,
1339 check_ownership: check_all,
1340 check_sections: check_all,
1341 };
1342
1343 if check_options.check_faces {
1344 let cells = self.height_stratum(0)?;
1345 let _ = self.cell_adjacency_graph(cells, Default::default(), AdjacencyWeighting::None);
1346 }
1347
1348 run_mesh_checks(
1349 &mut self.mesh_data.sieve,
1350 self.mesh_data.cell_types.as_ref(),
1351 self.mesh_data.coordinates.as_ref(),
1352 self.ownership.as_ref(),
1353 self.overlap.as_ref(),
1354 self.mesh_data.sections.values(),
1355 check_options,
1356 )
1357 }
1358
1359 pub fn point_chart(&self) -> Option<MeshPointChart> {
1361 let points = self.mesh_data.sieve.points_sorted();
1362 let start = points.first()?.get();
1363 let end_inclusive = points.last()?.get();
1364 let point_count = points.len();
1365 let interval_len = end_inclusive
1366 .checked_sub(start)
1367 .and_then(|width| width.checked_add(1));
1368 Some(MeshPointChart {
1369 start,
1370 end_inclusive,
1371 point_count,
1372 is_dense: interval_len == u64::try_from(point_count).ok(),
1373 })
1374 }
1375
1376 pub fn cone_points(&self, point: PointId) -> Vec<PointId> {
1378 let mut points: Vec<_> = self.mesh_data.sieve.cone_points(point).collect();
1379 points.sort_unstable();
1380 points
1381 }
1382
1383 pub fn oriented_cone(&self, point: PointId) -> Vec<(PointId, i32)> {
1385 let mut points: Vec<_> = self.mesh_data.sieve.cone_o(point).collect();
1386 points.sort_unstable_by_key(|(point, _)| *point);
1387 points
1388 }
1389
1390 pub fn support_points(&self, point: PointId) -> Vec<PointId> {
1392 let mut points: Vec<_> = self.mesh_data.sieve.support_points(point).collect();
1393 points.sort_unstable();
1394 points
1395 }
1396
1397 pub fn oriented_support(&self, point: PointId) -> Vec<(PointId, i32)> {
1399 let mut points: Vec<_> = self.mesh_data.sieve.support_o(point).collect();
1400 points.sort_unstable_by_key(|(point, _)| *point);
1401 points
1402 }
1403
1404 pub fn cone_size(&self, point: PointId) -> usize {
1406 self.mesh_data.sieve.cone_points(point).count()
1407 }
1408
1409 pub fn support_size(&self, point: PointId) -> usize {
1411 self.mesh_data.sieve.support_points(point).count()
1412 }
1413
1414 pub fn height_stratum(&self, height: u32) -> Result<Vec<PointId>, MeshSieveError> {
1416 let strata = compute_strata(&self.mesh_data.sieve)?;
1417 Ok(strata
1418 .strata
1419 .get(height as usize)
1420 .cloned()
1421 .unwrap_or_default())
1422 }
1423
1424 pub fn depth_stratum(&self, depth: u32) -> Result<Vec<PointId>, MeshSieveError> {
1426 let strata = compute_strata(&self.mesh_data.sieve)?;
1427 let mut points: Vec<_> = strata
1428 .depth
1429 .iter()
1430 .filter_map(|(&point, &d)| (d == depth).then_some(point))
1431 .collect();
1432 points.sort_unstable();
1433 Ok(points)
1434 }
1435
1436 pub fn create_section_from_depth(
1441 &mut self,
1442 name: impl Into<String>,
1443 dofs_by_depth: &[usize],
1444 ) -> Result<Option<Section<V, St>>, MeshSieveError> {
1445 self.create_section_from_depth_impl(name.into(), dofs_by_depth, None)
1446 }
1447
1448 pub fn create_section_from_depth_on_label(
1450 &mut self,
1451 name: impl Into<String>,
1452 dofs_by_depth: &[usize],
1453 label_name: &str,
1454 label_value: i32,
1455 ) -> Result<Option<Section<V, St>>, MeshSieveError> {
1456 let labels = self
1457 .labels()
1458 .ok_or_else(|| MeshSieveError::MissingSectionName {
1459 name: "labels".to_string(),
1460 })?;
1461 let support: HashSet<_> = labels.points_with_label(label_name, label_value).collect();
1462 self.create_section_from_depth_impl(name.into(), dofs_by_depth, Some(&support))
1463 }
1464
1465 fn create_section_from_depth_impl(
1466 &mut self,
1467 name: String,
1468 dofs_by_depth: &[usize],
1469 support: Option<&HashSet<PointId>>,
1470 ) -> Result<Option<Section<V, St>>, MeshSieveError> {
1471 let strata = compute_strata(&self.mesh_data.sieve)?;
1472 let mut entries: Vec<_> = strata.depth.into_iter().collect();
1473 entries.sort_unstable_by_key(|(point, _)| *point);
1474 let mut atlas = Atlas::default();
1475 for (point, depth) in entries {
1476 if support.is_some_and(|points| !points.contains(&point)) {
1477 continue;
1478 }
1479 let dofs = dofs_by_depth.get(depth as usize).copied().unwrap_or(0);
1480 if dofs > 0 {
1481 atlas.try_insert(point, dofs)?;
1482 }
1483 }
1484 self.global_sections.remove(&name);
1485 Ok(self.mesh_data.sections.insert(name, Section::new(atlas)))
1486 }
1487
1488 pub fn closure_points(
1490 &self,
1491 point: PointId,
1492 order: &ClosureOrder,
1493 ) -> Result<Vec<PointId>, MeshSieveError> {
1494 Ok(self
1495 .closure_index_for_points(point, order)?
1496 .point_order()
1497 .collect())
1498 }
1499
1500 pub fn transitive_closure_points(
1502 &self,
1503 point: PointId,
1504 order: &ClosureOrder,
1505 ) -> Result<Vec<PointId>, MeshSieveError> {
1506 self.closure_points(point, order)
1507 }
1508
1509 pub fn closure_points_at_depth(
1511 &self,
1512 point: PointId,
1513 depth: u32,
1514 order: &ClosureOrder,
1515 ) -> Result<Vec<PointId>, MeshSieveError> {
1516 let strata = compute_strata(&self.mesh_data.sieve)?;
1517 Ok(self
1518 .closure_index_for_points(point, order)?
1519 .point_order()
1520 .filter(|closure_point| strata.depth.get(closure_point).copied() == Some(depth))
1521 .collect())
1522 }
1523
1524 pub fn star_points(&self, point: PointId) -> Vec<PointId> {
1526 let mut points: Vec<_> = self.mesh_data.sieve.star_iter_sorted([point]).collect();
1527 points.sort_unstable();
1528 points.dedup();
1529 points
1530 }
1531
1532 pub fn closure_both_points(&self, point: PointId) -> Vec<PointId> {
1534 let mut points: Vec<_> = self
1535 .mesh_data
1536 .sieve
1537 .closure_both_iter_sorted([point])
1538 .collect();
1539 points.sort_unstable();
1540 points.dedup();
1541 points
1542 }
1543
1544 pub fn get_closure_values(
1546 &self,
1547 section_name: &str,
1548 cell: PointId,
1549 order: &ClosureOrder,
1550 ) -> Result<ElementClosureData<V>, MeshSieveError> {
1551 let section = self.mesh_data.sections.get(section_name).ok_or_else(|| {
1552 MeshSieveError::MissingSectionName {
1553 name: section_name.to_string(),
1554 }
1555 })?;
1556 let global_map = self.global_sections.get(section_name);
1557 extract_oriented_element_closure(
1558 &self.mesh_data.sieve,
1559 section,
1560 global_map,
1561 cell,
1562 0,
1563 order,
1564 &IdentitySectionSym,
1565 )
1566 }
1567
1568 pub fn points_by_label_selection(
1570 &self,
1571 selection: &MeshDMLabelSelection,
1572 ) -> Result<Vec<PointId>, MeshSieveError> {
1573 let labels = self
1574 .labels()
1575 .ok_or_else(|| MeshSieveError::MissingSectionName {
1576 name: "labels".to_string(),
1577 })?;
1578 let seeds: Vec<PointId> = labels
1579 .points_with_label(&selection.label_name, selection.label_value)
1580 .collect();
1581 let mut points = match selection.topology {
1582 SubmeshSelection::FullClosure => {
1583 self.mesh_data.sieve.closure_iter_sorted(seeds).collect()
1584 }
1585 SubmeshSelection::ClosureDepth(depth) => {
1586 crate::algs::traversal::TraversalBuilder::new(&self.mesh_data.sieve)
1587 .seeds(seeds)
1588 .dir(crate::algs::traversal::Dir::Down)
1589 .max_depth(Some(depth))
1590 .run()
1591 }
1592 SubmeshSelection::TargetStratum { axis, index } => {
1593 let closure: Vec<PointId> =
1594 self.mesh_data.sieve.closure_iter_sorted(seeds).collect();
1595 let strata = compute_strata(&self.mesh_data.sieve)?;
1596 let stratum_map = match axis {
1597 crate::topology::sieve::strata::StratumAxis::Height => &strata.height,
1598 crate::topology::sieve::strata::StratumAxis::Depth => &strata.depth,
1599 };
1600 let target = closure
1601 .into_iter()
1602 .filter(|p| stratum_map.get(p).copied() == Some(index));
1603 self.mesh_data.sieve.closure_iter_sorted(target).collect()
1604 }
1605 };
1606 if let Some(section_name) = &selection.section {
1607 let section = self.mesh_data.sections.get(section_name).ok_or_else(|| {
1608 MeshSieveError::MissingSectionName {
1609 name: section_name.clone(),
1610 }
1611 })?;
1612 points.retain(|point| section.atlas().contains(*point));
1613 }
1614 points.sort_unstable();
1615 points.dedup();
1616 Ok(points)
1617 }
1618
1619 pub fn points_by_label_in_section(
1621 &self,
1622 label_name: &str,
1623 label_value: i32,
1624 section_name: &str,
1625 topology: SubmeshSelection,
1626 ) -> Result<Vec<PointId>, MeshSieveError> {
1627 self.points_by_label_selection(
1628 &MeshDMLabelSelection::new(label_name, label_value)
1629 .topology(topology)
1630 .section(section_name),
1631 )
1632 }
1633
1634 pub fn create_constrained_view_from_labels(
1636 &self,
1637 section_name: &str,
1638 constraints: &[LabelConstraintSpec],
1639 ) -> Result<ConstrainedSection<V, St>, MeshSieveError> {
1640 let section = self.mesh_data.sections.get(section_name).ok_or_else(|| {
1641 MeshSieveError::MissingSectionName {
1642 name: section_name.to_string(),
1643 }
1644 })?;
1645 let point_dofs: Vec<_> = section
1646 .atlas()
1647 .points()
1648 .filter_map(|point| section.atlas().get(point).map(|(_, len)| (point, len)))
1649 .collect();
1650 self.create_constrained_section_from_labels(section_name, &point_dofs, constraints)
1651 }
1652
1653 pub fn sub_dm_by_label(
1655 &self,
1656 label_name: &str,
1657 label_value: i32,
1658 topology: SubmeshSelection,
1659 ) -> Result<MeshDMSubmesh<V, St, CtSt>, MeshSieveError> {
1660 self.sub_dm_by_label_with_sections(label_name, label_value, topology, None)
1661 }
1662
1663 pub fn sub_dm_by_label_with_sections(
1665 &self,
1666 label_name: &str,
1667 label_value: i32,
1668 topology: SubmeshSelection,
1669 section_names: Option<&[&str]>,
1670 ) -> Result<MeshDMSubmesh<V, St, CtSt>, MeshSieveError> {
1671 let labels = self
1672 .labels()
1673 .ok_or_else(|| MeshSieveError::MissingSectionName {
1674 name: "labels".to_string(),
1675 })?;
1676 let (mut mesh_data, maps) =
1677 extract_by_label(&self.mesh_data, labels, label_name, label_value, topology)?;
1678 if let Some(names) = section_names {
1679 let keep: HashSet<&str> = names.iter().copied().collect();
1680 mesh_data
1681 .sections
1682 .retain(|name, _| keep.contains(name.as_str()));
1683 }
1684 let mut dm = MeshDM::from_mesh_data_with_options(mesh_data, self.options.clone());
1685 dm.ownership = remap_ownership_to_submesh(self.ownership.as_ref(), &maps)?;
1686 dm.global_sections = remap_global_sections_to_submesh(&self.global_sections, &maps)?;
1687 dm.anchors = self.anchors.clone();
1688 dm.hanging_constraints = self.hanging_constraints.clone();
1689 dm.provenance_maps = MeshDMProvenance {
1690 load_map: None,
1691 redistribute_map: None,
1692 section_map: Some(point_sf_for_submesh_maps(&maps)),
1693 storage_version: self.provenance_maps.storage_version,
1694 permutation_source: Some("sub_dm_by_label".to_string()),
1695 redistribution_map_id: self.provenance_maps.redistribution_map_id.clone(),
1696 saved_rank_count: self.provenance_maps.saved_rank_count,
1697 loaded_rank_count: self.provenance_maps.loaded_rank_count,
1698 };
1699 Ok(MeshDMSubmesh { dm, maps })
1700 }
1701
1702 pub fn dual_graph(&self, cells: impl IntoIterator<Item = PointId>) -> DualGraph {
1704 build_dual(&self.mesh_data.sieve, cells)
1705 }
1706
1707 pub fn cell_adjacency_graph(
1709 &self,
1710 cells: impl IntoIterator<Item = PointId>,
1711 opts: crate::algs::adjacency_graph::CellAdjacencyOpts,
1712 weighting: AdjacencyWeighting,
1713 ) -> MeshGraph {
1714 cell_adjacency_graph_with_cells(&self.mesh_data.sieve, cells, opts, weighting)
1715 }
1716
1717 pub fn matrix_preallocation_graph(
1719 &self,
1720 cells: impl IntoIterator<Item = PointId>,
1721 opts: crate::algs::adjacency_graph::CellAdjacencyOpts,
1722 ) -> PreallocationGraph {
1723 let graph = self.cell_adjacency_graph(cells, opts, AdjacencyWeighting::None);
1724 let row_nnz = graph
1725 .xadj
1726 .windows(2)
1727 .map(|w| w[1].saturating_sub(w[0]))
1728 .collect();
1729 PreallocationGraph {
1730 xadj: graph.xadj,
1731 adjncy: graph.adjncy,
1732 order: graph.order,
1733 row_nnz,
1734 }
1735 }
1736
1737 pub fn prepare_for_solve<C>(
1746 &mut self,
1747 comm: &C,
1748 options: PrepareForSolveOptions,
1749 ) -> Result<PrepareForSolveDiagnostics, MeshSieveError>
1750 where
1751 C: Communicator + Sync,
1752 V: Send
1753 + PartialEq
1754 + bytemuck::Pod
1755 + 'static
1756 + core::ops::AddAssign
1757 + core::ops::Mul<Output = V>,
1758 {
1759 self.apply_hanging_constraints_to_sections()?;
1760
1761 if options.create_serial_ownership && comm.size() == 1 {
1762 self.ensure_serial_ownership(comm.rank())?;
1763 }
1764
1765 let prerequisites = prepare_for_solve_prerequisites(
1766 &self.mesh_data.sieve,
1767 self.mesh_data.coordinates.as_ref(),
1768 self.mesh_data.cell_types.as_ref(),
1769 self.ownership.as_ref(),
1770 self.overlap.as_ref(),
1771 options,
1772 )?;
1773 let mut diagnostics = PrepareForSolveDiagnostics {
1774 ready: false,
1775 prerequisites,
1776 ..PrepareForSolveDiagnostics::default()
1777 };
1778
1779 if diagnostics.has_missing_required_prerequisites() {
1780 diagnostics.steps.push(PrepareForSolveStepDiagnostic {
1781 name: "section_global_numbering",
1782 status: "skipped",
1783 detail: "required prerequisites are missing".to_string(),
1784 });
1785 diagnostics.steps.push(PrepareForSolveStepDiagnostic {
1786 name: "matrix_preallocation_graph",
1787 status: "skipped",
1788 detail: "required prerequisites are missing".to_string(),
1789 });
1790 diagnostics.steps.push(PrepareForSolveStepDiagnostic {
1791 name: "ownership_overlap_checks",
1792 status: "skipped",
1793 detail: "required prerequisites are missing".to_string(),
1794 });
1795 diagnostics.steps.push(PrepareForSolveStepDiagnostic {
1796 name: "section_synchronization",
1797 status: "skipped",
1798 detail: "required prerequisites are missing".to_string(),
1799 });
1800 return Ok(diagnostics);
1801 }
1802
1803 self.build_global_sections(comm)?;
1804 diagnostics.global_sections = self.global_sections.keys().cloned().collect();
1805 diagnostics.steps.push(PrepareForSolveStepDiagnostic {
1806 name: "section_global_numbering",
1807 status: "completed",
1808 detail: format!("numbered_sections={}", diagnostics.global_sections.len()),
1809 });
1810
1811 let cells = self.height_stratum(0)?;
1812 let preallocation = self.matrix_preallocation_graph(cells, Default::default());
1813 diagnostics.preallocation = Some(PrepareForSolvePreallocationDiagnostic {
1814 rows: preallocation.order.len(),
1815 edges: preallocation.adjncy.len(),
1816 order: preallocation.order,
1817 row_nnz: preallocation.row_nnz,
1818 });
1819 diagnostics.steps.push(PrepareForSolveStepDiagnostic {
1820 name: "matrix_preallocation_graph",
1821 status: "completed",
1822 detail: diagnostics
1823 .preallocation
1824 .as_ref()
1825 .map(|p| format!("rows={}, edges={}", p.rows, p.edges))
1826 .unwrap_or_else(|| "rows=0, edges=0".to_string()),
1827 });
1828
1829 run_mesh_checks(
1830 &mut self.mesh_data.sieve,
1831 self.mesh_data.cell_types.as_ref(),
1832 self.mesh_data.coordinates.as_ref(),
1833 self.ownership.as_ref(),
1834 self.overlap.as_ref(),
1835 self.mesh_data.sections.values(),
1836 MeshCheckOptions {
1837 check_symmetry: true,
1838 check_skeleton: true,
1839 check_faces: true,
1840 check_geometry: true,
1841 check_overlap: true,
1842 check_ownership: true,
1843 check_sections: true,
1844 },
1845 )?;
1846 diagnostics.steps.push(PrepareForSolveStepDiagnostic {
1847 name: "ownership_overlap_checks",
1848 status: "completed",
1849 detail: "ownership and overlap topology are consistent".to_string(),
1850 });
1851
1852 if options.synchronize_ghost_sections {
1853 let has_overlap = self.overlap.is_some();
1854 let has_ownership = self.ownership.is_some();
1855 if has_overlap && has_ownership {
1856 let mut synchronized = Vec::new();
1857 if self.mesh_data.coordinates.is_some() {
1858 synchronized.push("coordinates".to_string());
1859 }
1860 synchronized.extend(self.mesh_data.sections.keys().cloned());
1861 self.distribute_fields(comm)?;
1862 diagnostics.synchronized_sections = synchronized;
1863 diagnostics.steps.push(PrepareForSolveStepDiagnostic {
1864 name: "section_synchronization",
1865 status: "completed",
1866 detail: format!(
1867 "synchronized_sections={}",
1868 diagnostics.synchronized_sections.len()
1869 ),
1870 });
1871 } else {
1872 diagnostics.steps.push(PrepareForSolveStepDiagnostic {
1873 name: "section_synchronization",
1874 status: "skipped",
1875 detail: "no overlap/ownership state available for ghost synchronization"
1876 .to_string(),
1877 });
1878 }
1879 } else {
1880 diagnostics.steps.push(PrepareForSolveStepDiagnostic {
1881 name: "section_synchronization",
1882 status: "skipped",
1883 detail: "ghost section synchronization disabled".to_string(),
1884 });
1885 }
1886
1887 diagnostics.ready = true;
1888 Ok(diagnostics)
1889 }
1890
1891 pub fn create_local_vector(&self, section_name: &str) -> Result<MeshVector<V>, MeshSieveError> {
1893 let section = self.mesh_data.sections.get(section_name).ok_or_else(|| {
1894 MeshSieveError::MissingSectionName {
1895 name: section_name.to_string(),
1896 }
1897 })?;
1898 Ok(MeshVector {
1899 section: Some(section_name.to_string()),
1900 values: vec![V::default(); section.atlas().total_len()],
1901 })
1902 }
1903
1904 pub fn get_local_vector_closure(
1906 &self,
1907 section_name: &str,
1908 vector: &MeshVector<V>,
1909 point: PointId,
1910 order: &ClosureOrder,
1911 ) -> Result<Vec<V>, MeshSieveError> {
1912 let (section, index) =
1913 self.local_vector_closure_index(section_name, vector, point, order)?;
1914 let mut values = Vec::with_capacity(index.len);
1915 for entry in &index.points {
1916 let (offset, len) = section
1917 .atlas()
1918 .get(entry.point)
1919 .ok_or(MeshSieveError::PointNotInAtlas(entry.point))?;
1920 let point_values = vector.values.get(offset..offset + len).ok_or(
1921 MeshSieveError::ScatterLengthMismatch {
1922 expected: section.atlas().total_len(),
1923 found: vector.values.len(),
1924 },
1925 )?;
1926 for &slot in &entry.permutation {
1927 values.push(point_values[slot].clone());
1928 }
1929 }
1930 Ok(values)
1931 }
1932
1933 pub fn get_local_vector_closure_at_depth(
1935 &self,
1936 section_name: &str,
1937 vector: &MeshVector<V>,
1938 point: PointId,
1939 depth: u32,
1940 order: &ClosureOrder,
1941 ) -> Result<Vec<V>, MeshSieveError> {
1942 let strata = compute_strata(&self.mesh_data.sieve)?;
1943 let (section, index) =
1944 self.local_vector_closure_index(section_name, vector, point, order)?;
1945 let mut values = Vec::new();
1946 for entry in &index.points {
1947 if strata.depth.get(&entry.point).copied() != Some(depth) {
1948 continue;
1949 }
1950 let (offset, len) = section
1951 .atlas()
1952 .get(entry.point)
1953 .ok_or(MeshSieveError::PointNotInAtlas(entry.point))?;
1954 let point_values = &vector.values[offset..offset + len];
1955 values.extend(
1956 entry
1957 .permutation
1958 .iter()
1959 .map(|&slot| point_values[slot].clone()),
1960 );
1961 }
1962 Ok(values)
1963 }
1964
1965 pub fn set_local_vector_closure(
1967 &self,
1968 section_name: &str,
1969 vector: &mut MeshVector<V>,
1970 point: PointId,
1971 order: &ClosureOrder,
1972 values: &[V],
1973 mode: MeshVectorInsertMode,
1974 ) -> Result<(), MeshSieveError>
1975 where
1976 V: core::ops::AddAssign<V>,
1977 {
1978 let (section, index) =
1979 self.local_vector_closure_index(section_name, vector, point, order)?;
1980 if values.len() != index.len {
1981 return Err(MeshSieveError::ScatterLengthMismatch {
1982 expected: index.len,
1983 found: values.len(),
1984 });
1985 }
1986 for entry in &index.points {
1987 let (offset, len) = section
1988 .atlas()
1989 .get(entry.point)
1990 .ok_or(MeshSieveError::PointNotInAtlas(entry.point))?;
1991 let expected = section.atlas().total_len();
1992 let found = vector.values.len();
1993 let point_values = vector
1994 .values
1995 .get_mut(offset..offset + len)
1996 .ok_or(MeshSieveError::ScatterLengthMismatch { expected, found })?;
1997 for (local_slot, §ion_slot) in entry.permutation.iter().enumerate() {
1998 let value = values[entry.offset + local_slot].clone();
1999 match mode {
2000 MeshVectorInsertMode::InsertValues => point_values[section_slot] = value,
2001 MeshVectorInsertMode::AddValues => point_values[section_slot] += value,
2002 }
2003 }
2004 }
2005 Ok(())
2006 }
2007
2008 fn local_vector_closure_index<'a>(
2009 &'a self,
2010 section_name: &str,
2011 vector: &MeshVector<V>,
2012 point: PointId,
2013 order: &ClosureOrder,
2014 ) -> Result<(&'a Section<V, St>, ClosureIndex<i32>), MeshSieveError> {
2015 if vector.section.as_deref() != Some(section_name) {
2016 return Err(MeshSieveError::VectorSectionMismatch {
2017 expected: section_name.to_string(),
2018 found: vector.section.clone(),
2019 });
2020 }
2021 let section = self.mesh_data.sections.get(section_name).ok_or_else(|| {
2022 MeshSieveError::MissingSectionName {
2023 name: section_name.to_string(),
2024 }
2025 })?;
2026 if vector.values.len() != section.atlas().total_len() {
2027 return Err(MeshSieveError::ScatterLengthMismatch {
2028 expected: section.atlas().total_len(),
2029 found: vector.values.len(),
2030 });
2031 }
2032 let index = build_closure_index(
2033 &self.mesh_data.sieve,
2034 section,
2035 point,
2036 0,
2037 order,
2038 &IdentitySectionSym,
2039 )?;
2040 Ok((section, index))
2041 }
2042
2043 pub fn build_global_sections<C>(&mut self, comm: &C) -> Result<(), MeshSieveError>
2045 where
2046 C: Communicator + Sync,
2047 {
2048 self.ensure_serial_ownership(comm.rank())?;
2049 let ownership = self.ownership.as_ref().expect("ownership inserted");
2050 let empty_overlap = Overlap::default();
2051 let overlap = self.overlap.as_ref().unwrap_or(&empty_overlap);
2052 let mut maps = BTreeMap::new();
2053 for (name, section) in &self.mesh_data.sections {
2054 let map = if let Some(hanging) = self.hanging_constraints.get(name) {
2055 let mask = hanging.to_dof_constraint_mask(V::default());
2056 LocalToGlobalMap::from_section_with_constraints_and_ownership(
2057 section,
2058 &mask,
2059 overlap,
2060 ownership,
2061 comm,
2062 comm.rank(),
2063 crate::algs::communicator::SectionCommTags::from_base(
2064 crate::algs::communicator::CommTag::new(0xBEEF),
2065 ),
2066 )?
2067 } else {
2068 LocalToGlobalMap::from_section_with_ownership(
2069 section,
2070 overlap,
2071 ownership,
2072 comm,
2073 comm.rank(),
2074 )?
2075 };
2076 maps.insert(name.clone(), map);
2077 }
2078 self.global_sections = maps;
2079 Ok(())
2080 }
2081
2082 pub fn global_section(&self, name: &str) -> Option<&LocalToGlobalMap> {
2084 self.global_sections.get(name)
2085 }
2086
2087 pub fn create_global_vector(
2089 &self,
2090 section_name: &str,
2091 ) -> Result<MeshVector<V>, MeshSieveError> {
2092 let map = self.global_sections.get(section_name).ok_or_else(|| {
2093 MeshSieveError::MissingSectionName {
2094 name: section_name.to_string(),
2095 }
2096 })?;
2097 Ok(MeshVector {
2098 section: Some(section_name.to_string()),
2099 values: global_vector_for_map(map),
2100 })
2101 }
2102
2103 pub fn create_constrained_section_from_labels(
2105 &self,
2106 field_name: &str,
2107 point_dofs: &[(PointId, usize)],
2108 constraints: &[LabelConstraintSpec],
2109 ) -> Result<ConstrainedSection<V, St>, MeshSieveError> {
2110 if let Some(discretization) = self.discretization()
2111 && discretization.field(field_name).is_none()
2112 {
2113 return Err(MeshSieveError::MissingSectionName {
2114 name: field_name.to_string(),
2115 });
2116 }
2117 let labels = self
2118 .labels()
2119 .ok_or_else(|| MeshSieveError::MissingSectionName {
2120 name: "labels".to_string(),
2121 })?;
2122 constrained_section_from_label_specs(point_dofs, labels, constraints)
2123 }
2124
2125 pub fn distribute_with<P, C>(
2128 &self,
2129 cells: &[PointId],
2130 partitioner: &P,
2131 comm: &C,
2132 ) -> Result<Self, MeshSieveError>
2133 where
2134 P: CellPartitioner<MeshSieve>,
2135 C: Communicator + Sync,
2136 V: Send + PartialEq + bytemuck::Pod + 'static,
2137 {
2138 let distributed = distribute_with_overlap(
2139 &self.mesh_data,
2140 cells,
2141 partitioner,
2142 self.options.distribution_config(),
2143 comm,
2144 )?;
2145 let mut dm =
2146 Self::from_distributed(distributed, self.options.clone(), comm.rank(), comm.size());
2147 dm.run_refinement_passes(
2148 dm.options.post_refine,
2149 MeshDMTransferStrategy::PreserveCoordinatesAndLabels,
2150 )?;
2151 dm.enforce_phase_invariants()?;
2152 dm.run_requested_checks()?;
2153 Ok(dm)
2154 }
2155
2156 pub fn distribute_fields<C>(&mut self, comm: &C) -> Result<(), MeshSieveError>
2158 where
2159 C: Communicator + Sync,
2160 V: Send + PartialEq + bytemuck::Pod + 'static,
2161 {
2162 let Some(ownership) = &self.ownership else {
2163 return Ok(());
2164 };
2165 let Some(overlap) = &self.overlap else {
2166 return Ok(());
2167 };
2168 let sf =
2169 crate::algs::point_sf::PointSF::with_ownership(overlap, ownership, comm, comm.rank());
2170 sf.validate()?;
2171 self.provenance_maps.section_map = Some(crate::algs::point_sf::create_process_sf::<
2172 crate::algs::communicator::NoComm,
2173 >(ownership, comm.rank()));
2174 if let Some(coords) = &mut self.mesh_data.coordinates {
2175 sf.complete_section(coords.section_mut())?;
2176 if let Some(high_order) = coords.high_order_mut() {
2177 sf.complete_section(high_order.section_mut())?;
2178 }
2179 }
2180 for section in self.mesh_data.sections.values_mut() {
2181 sf.complete_section(section)?;
2182 }
2183 Ok(())
2184 }
2185
2186 fn from_distributed(
2187 data: DistributedMeshData<V, St, CtSt>,
2188 options: MeshDMOptions,
2189 rank: usize,
2190 size: usize,
2191 ) -> Self {
2192 let redistribute_map = crate::algs::point_sf::create_process_sf::<
2193 crate::algs::communicator::NoComm,
2194 >(&data.ownership, rank);
2195 let mesh_data = MeshData {
2196 sieve: data.sieve,
2197 coordinates: data.coordinates,
2198 sections: data.sections,
2199 mixed_sections: data.mixed_sections,
2200 labels: data.labels,
2201 cell_types: data.cell_types,
2202 discretization: data.discretization,
2203 };
2204 Self {
2205 mesh_data,
2206 options,
2207 ownership: Some(data.ownership),
2208 overlap: Some(data.overlap),
2209 global_sections: BTreeMap::new(),
2210 anchors: TopologicalAnchors::default(),
2211 hanging_constraints: BTreeMap::new(),
2212 distribution: Some(MeshDMDistribution {
2213 point_owners: data.point_owners,
2214 cell_parts: data.cell_parts,
2215 rank,
2216 size,
2217 }),
2218 provenance_maps: MeshDMProvenance {
2219 load_map: None,
2220 redistribute_map: Some(redistribute_map),
2221 section_map: None,
2222 storage_version: None,
2223 permutation_source: None,
2224 redistribution_map_id: None,
2225 saved_rank_count: Some(size),
2226 loaded_rank_count: Some(size),
2227 },
2228 }
2229 }
2230
2231 fn ensure_serial_ownership(&mut self, rank: usize) -> Result<(), MeshSieveError> {
2232 if self.ownership.is_some() {
2233 return Ok(());
2234 }
2235 let mut ownership = PointOwnership::default();
2236 for point in self.mesh_data.sieve.points() {
2237 ownership.set(point, rank, false)?;
2238 }
2239 self.ownership = Some(ownership);
2240 Ok(())
2241 }
2242
2243 fn invalidate_numbering(&mut self) {
2244 self.global_sections.clear();
2245 }
2246
2247 fn reorder_sections(&mut self, ordering: StratifiedOrdering) -> Result<(), MeshSieveError> {
2248 let permutation = stratified_permutation(&self.mesh_data.sieve, ordering)?;
2249 for section in self.mesh_data.sections.values_mut() {
2250 *section = reorder_section_by_points(section, &permutation)?;
2251 }
2252 if let Some(coords) = &mut self.mesh_data.coordinates {
2253 let topological_dimension = coords.topological_dimension();
2254 let embedding_dimension = coords.embedding_dimension();
2255 let section = reorder_section_by_points(coords.section(), &permutation)?;
2256 *coords =
2257 Coordinates::from_section(topological_dimension, embedding_dimension, section)?;
2258 }
2259 if let Some(cell_types) = &mut self.mesh_data.cell_types {
2260 *cell_types = reorder_section_by_points(cell_types, &permutation)?;
2261 }
2262 Ok(())
2263 }
2264}
2265
2266fn reorder_section_by_points<V, St>(
2267 section: &Section<V, St>,
2268 order: &[PointId],
2269) -> Result<Section<V, St>, MeshSieveError>
2270where
2271 V: Clone + Default,
2272 St: Storage<V> + Clone,
2273{
2274 let mut atlas = Atlas::default();
2275 for &point in order {
2276 if let Some((_offset, len)) = section.atlas().get(point) {
2277 atlas.try_insert(point, len)?;
2278 }
2279 }
2280 for point in section.atlas().points() {
2281 if !atlas.contains(point) {
2282 let (_offset, len) = section
2283 .atlas()
2284 .get(point)
2285 .ok_or(MeshSieveError::PointNotInAtlas(point))?;
2286 atlas.try_insert(point, len)?;
2287 }
2288 }
2289 let mut reordered = Section::new(atlas);
2290 for point in section.atlas().points() {
2291 let values = section.try_restrict(point)?.to_vec();
2292 reordered.try_set(point, &values)?;
2293 }
2294 Ok(reordered)
2295}
2296
2297fn remap_ownership_to_submesh(
2298 ownership: Option<&PointOwnership>,
2299 maps: &SubmeshMaps,
2300) -> Result<Option<PointOwnership>, MeshSieveError> {
2301 let Some(ownership) = ownership else {
2302 return Ok(None);
2303 };
2304 let mut remapped = PointOwnership::default();
2305 for (&parent, &sub) in &maps.parent_to_sub {
2306 if let Some(entry) = ownership.entry(parent) {
2307 remapped.set(sub, entry.owner, entry.is_ghost)?;
2308 }
2309 }
2310 Ok(Some(remapped))
2311}
2312
2313fn remap_global_sections_to_submesh(
2314 global_sections: &BTreeMap<String, LocalToGlobalMap>,
2315 maps: &SubmeshMaps,
2316) -> Result<BTreeMap<String, LocalToGlobalMap>, MeshSieveError> {
2317 let new_to_old = maps
2318 .sub_to_parent
2319 .iter()
2320 .enumerate()
2321 .map(|(idx, &parent)| PointId::new((idx + 1) as u64).map(|sub| (sub, parent)))
2322 .collect::<Result<Vec<_>, _>>()?;
2323 let mut remapped = BTreeMap::new();
2324 for (name, map) in global_sections {
2325 remapped.insert(name.clone(), map.remap_points(new_to_old.iter().copied())?);
2326 }
2327 Ok(remapped)
2328}
2329
2330fn point_sf_for_submesh_maps(
2331 maps: &SubmeshMaps,
2332) -> PointSF<'static, crate::algs::communicator::NoComm> {
2333 let leaves = maps
2334 .sub_to_parent
2335 .iter()
2336 .enumerate()
2337 .filter_map(|(idx, &parent)| {
2338 PointId::new((idx + 1) as u64).ok().map(|sub| PointSfLeaf {
2339 local: sub,
2340 remote: RemotePoint {
2341 rank: 0,
2342 point: parent,
2343 },
2344 owner_rank: 0,
2345 is_ghost: false,
2346 })
2347 })
2348 .collect::<Vec<_>>();
2349 PointSF::from_leaves(0, maps.sub_to_parent.iter().copied(), leaves)
2350}