outram-foam-basic-lib 0.1.1

OpenFOAM primitive functions translated to Rust -- an independent OUTRAM PARK fork, not the official OpenFOAM software
Documentation
// SPDX-License-Identifier: GPL-3.0-only
// Copyright (C) 2026 OUTRAM PARK contributors
// Derived from OpenFOAM (www.openfoam.com)
// Copyright (C) 2004-2023 OpenFOAM Foundation
// Copyright (C) 2016-2023 OpenCFD Ltd.
//
// This file is part of OUTRAM PARK.
//
// OUTRAM PARK is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the
// Free Software Foundation, either version 3 of the License, or (at your
// option) any later version.
//
// OUTRAM PARK is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with OUTRAM PARK.  If not, see <https://www.gnu.org/licenses/>.

use crate::fields::field::Field;
use crate::primitives::{SymmTensor, Tensor, Vector3};

/// Boundary condition variant for a single patch.
///
/// Covers the BC types required by the target solvers.  More exotic types
/// (inlet-outlet, total pressure, etc.) will be added when Layer 3 is
/// implemented.
#[derive(Debug, Clone)]
pub enum BoundaryCondition<T: Clone> {
    /// Dirichlet: fixed uniform value.
    FixedValue(T),
    /// Dirichlet: fixed per-face values.
    FixedField(Field<T>),
    /// Neumann: zero normal gradient — boundary face value = internal adjacent value.
    ZeroGradient,
    /// Symmetry plane — normal component zeroed.
    Symmetry,
    /// 2-D / wedge — zero-area faces; value has no physical meaning.
    Empty,
    /// Value computed by the solver and stored here (read-only from BC side).
    Calculated(Field<T>),
}

impl<T: Clone + Default> BoundaryCondition<T> {
    /// True if the BC imposes a value (Dirichlet-like).
    pub fn is_fixed_value(&self) -> bool {
        matches!(self, Self::FixedValue(_) | Self::FixedField(_))
    }
}

/// Boundary field for one patch: the BC type plus the current face values.
///
/// The `values` field always holds the latest face values (updated by
/// `update_coeffs` in Layer 3 operators).  For `FixedValue`/`FixedField` the
/// values are set at construction and never change.  For `ZeroGradient` and
/// `Calculated` they are written by the operator code.
#[derive(Debug, Clone)]
pub struct PatchField<T: Clone> {
    /// The boundary condition applied on this patch.
    pub bc: BoundaryCondition<T>,
    /// Current face values for this patch (length == patch.size).
    pub values: Field<T>,
}

impl PatchField<f64> {
    /// Dirichlet patch holding a uniform scalar `v` on all `size` faces.
    pub fn fixed_value(size: usize, v: f64) -> Self {
        Self {
            bc: BoundaryCondition::FixedValue(v),
            values: Field::uniform(size, v),
        }
    }

    /// Zero-gradient (Neumann) scalar patch of `size` faces; values default to
    /// `0.0` and are overwritten by the operator that owns them.
    pub fn zero_gradient(size: usize) -> Self {
        Self {
            bc: BoundaryCondition::ZeroGradient,
            values: Field::zeros(size),
        }
    }

    /// Empty (zero-area) scalar patch — no faces, no physical value.
    pub fn empty() -> Self {
        Self {
            bc: BoundaryCondition::Empty,
            values: Field::new(vec![]),
        }
    }
}

impl PatchField<Vector3> {
    /// Dirichlet patch holding a uniform `Vector3` value `v` on all `size` faces.
    pub fn fixed_value_vec(size: usize, v: Vector3) -> Self {
        Self {
            bc: BoundaryCondition::FixedValue(v),
            values: Field::uniform(size, v),
        }
    }

    /// Zero-gradient (Neumann) vector patch of `size` faces; values default to
    /// `Vector3::ZERO` and are overwritten by the operator that owns them.
    pub fn zero_gradient_vec(size: usize) -> Self {
        Self {
            bc: BoundaryCondition::ZeroGradient,
            values: Field::zero_vec(size),
        }
    }

    /// Empty (zero-area) vector patch — no faces, no physical value.
    pub fn empty_vec() -> Self {
        Self {
            bc: BoundaryCondition::Empty,
            values: Field::new(vec![]),
        }
    }
}

impl PatchField<Tensor> {
    /// Dirichlet patch holding a uniform `Tensor` value.
    pub fn fixed_value_tensor(size: usize, v: Tensor) -> Self {
        Self {
            bc: BoundaryCondition::FixedValue(v),
            values: Field::uniform(size, v),
        }
    }

    /// Zero-gradient (Neumann) patch for a `Tensor` field; values default to
    /// `Tensor::ZERO` and are overwritten by the operator that owns them.
    pub fn zero_gradient_tensor(size: usize) -> Self {
        Self {
            bc: BoundaryCondition::ZeroGradient,
            values: Field::uniform(size, Tensor::ZERO),
        }
    }
}

impl PatchField<SymmTensor> {
    /// Dirichlet patch holding a uniform `SymmTensor` value.
    pub fn fixed_value_symm_tensor(size: usize, v: SymmTensor) -> Self {
        Self {
            bc: BoundaryCondition::FixedValue(v),
            values: Field::uniform(size, v),
        }
    }

    /// Zero-gradient (Neumann) patch for a `SymmTensor` field; values default to
    /// `SymmTensor::ZERO` and are overwritten by the operator that owns them.
    pub fn zero_gradient_symm_tensor(size: usize) -> Self {
        Self {
            bc: BoundaryCondition::ZeroGradient,
            values: Field::uniform(size, SymmTensor::ZERO),
        }
    }
}