!> Allocation and management of primary flow variables and projection fluxes. !! !! This module is the central owner of hydrodynamic state used by the !! fractional-step solver. Arrays are globally sized because every MPI rank !! stores the replicated mesh, but numerical kernels update only owned cells or !! owner-owned faces before halo exchange. !! !! Important flux convention: `fields%face_flux(f)` is the volumetric face flux !! \(u_f \cdot n_f A_f\) in \( \mathrm{m^3/s} \), oriented from !! `mesh%faces(f)%owner` to `mesh%faces(f)%neighbor`. Cell-local loops !! reorient it to outward-from-cell by changing the sign when the current cell !! is the neighbor. `fields%mass_flux(f)` has the same owner-to-neighbor !! orientation and stores \( \rho_f u_f \cdot n_f A_f \) in !! \( \mathrm{kg/s} \). !! !! Constant-density mode targets \( \nabla \cdot u = 0 \). Guarded !! variable-density low-Mach mode targets \( \nabla \cdot u = S \), where !! `fields%projection_divergence_source` records the source actually consumed !! by the most recent projection and `fields%divergence_source` may later be !! advanced by energy/thermo density updates for the next projection. module mod_flow_fields use mod_precision, only : rk, zero use mod_mesh, only : mesh_t use mod_input, only : case_params_t use mod_bc, only : bc_set_t implicit none private !> Container for all primary hydrodynamic fields. type, public :: flow_fields_t real(rk), allocatable :: u(:,:) !< Current cell-centered velocity vector \((u, v, w)\) [m/s]. real(rk), allocatable :: u_old(:,:) !< Velocity vector from the previous time step \(n\) [m/s]. real(rk), allocatable :: u_star(:,:) !< Intermediate predicted velocity field [m/s]. real(rk), allocatable :: p(:) !< Projection/hydrodynamic pressure-like field [Pa], not Cantera thermodynamic pressure. real(rk), allocatable :: phi(:) !< Projection correction potential \(\phi\) used by the Poisson solve. real(rk), allocatable :: div(:) !< Recomputed \( \nabla \cdot u \) [1/s]; compare with `projection_divergence_source` in variable-density mode. !> Conservative face-centered volumetric flux \(U_f = \mathbf{u}_f \cdot \mathbf{n}_f A_f\). !! Oriented according to the face normal (owner \(\rightarrow\) neighbor). real(rk), allocatable :: face_flux(:) !< Volumetric flux across faces [m^3/s]. !> Conservative face-centered mass flux \(\dot{m}_f = \rho_f U_f\). !! Oriented according to the face normal (owner \(\rightarrow\) neighbor). real(rk), allocatable :: mass_flux(:) !< Mass flux across faces [kg/s]. !> Current low-Mach divergence source \(S\) [1/s] prepared for the next projection. !! !! Constant-density mode keeps this zero. Variable-density mode updates !! it after the energy/thermo density sync from a conservative density !! source; it can therefore differ from the source used by the latest !! projection. real(rk), allocatable :: divergence_source(:) !< Target divergence source [1/s]. real(rk), allocatable :: projection_rho(:) !< Active density snapshot at projection start [kg/m^3]. !> Copy of the low-Mach divergence source actually consumed by the latest projection. !! This distinguishes the projection residual `div(u)-S_projection` from !! post-energy source-evolution diagnostics using `divergence_source`. real(rk), allocatable :: projection_divergence_source(:) !< Projection-time target divergence source [1/s]. !> Storage for the previous explicit RHS (Advection + Diffusion). !! Required for 2nd-order Adams-Bashforth (AB2) time marching. real(rk), allocatable :: rhs_old(:,:) !< Momentum RHS from step \(n-1\). logical :: rhs_old_valid = .false. !< False on the first step (triggers Euler fallback). end type flow_fields_t public :: allocate_fields, finalize_fields, initialize_fields contains !> Dynamically allocates all arrays within the flow fields container. !! !! Sizing is determined by the number of cells and faces in the provided mesh. !! All fields are initialized to zero upon allocation. !! !! @param mesh The mesh structure defining the domain size. !! @param fields The container to be allocated. subroutine allocate_fields(mesh, fields) type(mesh_t), intent(in) :: mesh type(flow_fields_t), intent(inout) :: fields call finalize_fields(fields) allocate(fields%u(3, mesh%ncells)) allocate(fields%u_old(3, mesh%ncells)) allocate(fields%u_star(3, mesh%ncells)) allocate(fields%p(mesh%ncells)) allocate(fields%phi(mesh%ncells)) allocate(fields%div(mesh%ncells)) allocate(fields%face_flux(mesh%nfaces)) allocate(fields%divergence_source(mesh%ncells)) allocate(fields%projection_rho(mesh%ncells)) allocate(fields%projection_divergence_source(mesh%ncells)) allocate(fields%mass_flux(mesh%nfaces)) allocate(fields%rhs_old(3, mesh%ncells)) fields%u = zero fields%u_old = zero fields%u_star = zero fields%p = zero fields%phi = zero fields%div = zero fields%face_flux = zero fields%divergence_source = zero fields%projection_rho = zero fields%projection_divergence_source = zero fields%mass_flux = zero fields%rhs_old = zero fields%rhs_old_valid = .false. end subroutine allocate_fields !> Initializes flow fields and sets simulation initial conditions. !! !! Currently implements a "Start from Rest" condition where all !! velocities and pressures are zero. IC overrides for restarts or !! analytical solutions will be implemented here. !! !! @param mesh The computational mesh. !! @param fields The fields container to initialize. subroutine initialize_fields(mesh, fields) type(mesh_t), intent(in) :: mesh type(flow_fields_t), intent(inout) :: fields ! Initial condition: Fluids starts from rest. ! Boundary motion and body forces drive the flow from t=0. call allocate_fields(mesh, fields) end subroutine initialize_fields !> Deallocates all arrays and resets validity flags. !! !! @param fields The container to be cleared. subroutine finalize_fields(fields) type(flow_fields_t), intent(inout) :: fields if (allocated(fields%u)) deallocate(fields%u) if (allocated(fields%u_old)) deallocate(fields%u_old) if (allocated(fields%u_star)) deallocate(fields%u_star) if (allocated(fields%p)) deallocate(fields%p) if (allocated(fields%phi)) deallocate(fields%phi) if (allocated(fields%div)) deallocate(fields%div) if (allocated(fields%face_flux)) deallocate(fields%face_flux) if (allocated(fields%divergence_source)) deallocate(fields%divergence_source) if (allocated(fields%projection_rho)) deallocate(fields%projection_rho) if (allocated(fields%projection_divergence_source)) deallocate(fields%projection_divergence_source) if (allocated(fields%mass_flux)) deallocate(fields%mass_flux) if (allocated(fields%rhs_old)) deallocate(fields%rhs_old) fields%rhs_old_valid = .false. end subroutine finalize_fields end module mod_flow_fields