outram_foam_basic_lib/fv_options/selection.rs
1// SPDX-License-Identifier: GPL-3.0-only
2// Copyright (C) 2026 OUTRAM PARK contributors
3// Derived from OpenFOAM (www.openfoam.com)
4// Copyright (C) 2004-2023 OpenFOAM Foundation
5// Copyright (C) 2016-2023 OpenCFD Ltd.
6//
7// This file is part of OUTRAM PARK. See `src/lib.rs` for the full licence
8// notice.
9
10// Upstream source: src/finiteVolume/cfdTools/general/fvCellZone/ and the
11// `cellSetOption` base of src/fvModels/.
12
13//! Which cells a source term applies over.
14
15use std::sync::Arc;
16
17/// The set of cells a source term acts on.
18///
19/// Upstream's `fvCellZone` / `cellSetOption`. A source is almost never global:
20/// a heat source occupies one component, a porous drag one region, a melting
21/// model one material. This is how that restriction is expressed.
22///
23/// # Sharing
24///
25/// The cell list is held behind an `Arc`, per the workspace rule against
26/// lifetime parameters, so several models can share one selection without
27/// copying it.
28#[derive(Debug, Clone, PartialEq, Eq)]
29pub enum CellSelection {
30 /// Every cell in the mesh.
31 All,
32 /// An explicit list of cell indices.
33 ///
34 /// Indices are not validated against a mesh here, because a selection is
35 /// built before it meets one. An out-of-range index will panic on use,
36 /// which is the correct outcome for what is a case-setup error.
37 Zone(Arc<Vec<usize>>),
38}
39
40impl CellSelection {
41 /// A selection from an explicit list of cell indices.
42 #[must_use]
43 pub fn zone(cells: Vec<usize>) -> Self {
44 Self::Zone(Arc::new(cells))
45 }
46
47 /// The selected cells, given a mesh cell count.
48 ///
49 /// `n_cells` is used only to expand [`CellSelection::All`].
50 #[must_use]
51 pub fn cells(&self, n_cells: usize) -> Vec<usize> {
52 match self {
53 Self::All => (0..n_cells).collect(),
54 Self::Zone(c) => c.as_ref().clone(),
55 }
56 }
57
58 /// Number of selected cells, given a mesh cell count.
59 #[must_use]
60 pub fn len(&self, n_cells: usize) -> usize {
61 match self {
62 Self::All => n_cells,
63 Self::Zone(c) => c.len(),
64 }
65 }
66
67 /// Whether the selection is empty.
68 ///
69 /// An empty selection is legal — it contributes nothing — but is usually a
70 /// case-setup mistake worth reporting.
71 #[must_use]
72 pub fn is_empty(&self, n_cells: usize) -> bool {
73 self.len(n_cells) == 0
74 }
75}