forked from 1b-agent/fieldcraft-upstream
A D3Q7 passive scalar rides on the D3Q19 flow: it is advected by the cell velocity, diffuses with the fluid's thermal diffusivity (the relaxation time follows from k / (rho c)), and feeds back into the momentum through a per-cell Boussinesq force when the domain has an expansion coefficient and gravity. Walls and obstacle faces hold a temperature (anti-bounce-back), a heat flux (added to the bounced population) or are adiabatic (bounce-back); velocity inlets carry the held temperature in, pressure outlets copy their neighbour. The heat crossing each face group is accumulated during streaming and reported in physical units (W) in the result's heat histories, next to the force histories; frames gain a temperature field and the stats report the thermal relaxation time. Closed-form checks: linear conduction between held plates, a flux wall against q H / k with the wall's heat balance, an inlet temperature carried through a plug flow, and a periodic blob that conserves its total and returns after one lap. The de Vahl Davis cavity (Ra 1e3 and 1e4 against the published Nusselt numbers) is there as an ignored test because it takes minutes. The GPU backend declines domains with heat for now and asks for the CPU; its kernels follow. Part of #157.
113 lines
5.2 KiB
Rust
113 lines
5.2 KiB
Rust
//! FieldCraft's lattice Boltzmann flow solver (CPU).
|
||
//!
|
||
//! It solves a fluid step of the model ([`fieldcraft_model::Procedure::Fluid`]) in its fluid
|
||
//! domain: weakly compressible, isothermal, Newtonian flow on a uniform grid of cubic cells that
|
||
//! fills the domain's box, less the cells inside the obstacle bodies.
|
||
//!
|
||
//! - [`voxel`]: which cells the obstacles take, decided with exact predicates from the
|
||
//! face-tagged boundary surface, and which face each wall link meets.
|
||
//! - [`setup`]: the step in lattice units, the time step, and the stability limits.
|
||
//! - [`flow`]: the D3Q19 two-relaxation-time scheme with Guo forcing, halfway bounce-back
|
||
//! walls (moving walls after Ladd), slip faces, Zou–He inlets and outlets, and the
|
||
//! momentum-exchange force on every wall.
|
||
//! - [`solve`]: the time loop, output frames and force histories in model units.
|
||
//!
|
||
//! # Sources
|
||
//! - T. Krüger, H. Kusumaatmaja, A. Kuzmin, O. Shardt, G. Silva, E. M. Viggen, *The Lattice
|
||
//! Boltzmann Method: Principles and Practice*, Springer 2017 (velocity sets, equilibria, units,
|
||
//! stream–collide, bounce-back, forcing, boundary conditions).
|
||
//! - Y. H. Qian, D. d'Humières, P. Lallemand, *Lattice BGK models for Navier–Stokes equation*,
|
||
//! EPL 17(6):479, 1992 (D3Q19).
|
||
//! - I. Ginzburg, F. Verhaeghe, D. d'Humières, *Two-relaxation-time lattice Boltzmann scheme:
|
||
//! about parametrization, velocity, pressure and mixed boundary conditions*, Commun. Comput.
|
||
//! Phys. 3(2):427–478, 2008 (TRT and the parameter Λ).
|
||
//! - Z. Guo, C. Zheng, B. Shi, *Discrete lattice effects on the forcing term in the lattice
|
||
//! Boltzmann method*, PRE 65:046308, 2002 (body force).
|
||
//! - A. J. C. Ladd, *Numerical simulations of particulate suspensions via a discretized Boltzmann
|
||
//! equation. Part 1*, JFM 271:285–309, 1994 (bounce-back, moving walls, momentum exchange).
|
||
//! - R. Mei, D. Yu, W. Shyy, L.-S. Luo, *Force evaluation in the lattice Boltzmann method
|
||
//! involving curved geometry*, PRE 65:041203, 2002 (momentum exchange).
|
||
//! - Q. Zou, X. He, *On pressure and velocity boundary conditions for the lattice Boltzmann BGK
|
||
//! model*, Phys. Fluids 9(6):1591–1598, 1997; M. Hecht, J. Harting, J. Stat. Mech. 2010,
|
||
//! P01018 (the D3Q19 transverse correction).
|
||
//! - P. Lallemand, L.-S. Luo, *Theory of the lattice Boltzmann method: dispersion, dissipation,
|
||
//! isotropy, Galilean invariance, and stability*, PRE 61:6546, 2000 (stability limits).
|
||
//! - H. Edelsbrunner, E. P. Mücke, *Simulation of Simplicity*, ACM TOG 9(1):66–104, 1990;
|
||
//! J. R. Shewchuk 1997 (exact predicates, through `fieldcraft_mesh::predicates`).
|
||
//!
|
||
//! No code of any lattice Boltzmann package was read; everything is written from these papers and
|
||
//! the book.
|
||
//!
|
||
//! # Determinism
|
||
//! Double precision and fixed loop orders. Collision and streaming run on threads by fixed blocks
|
||
//! of cells ([`fieldcraft_parallel`]), each cell's update independent of the others; the wall
|
||
//! forces and every other sum run in a fixed order on one thread. The same model gives the same
|
||
//! result bit for bit on any number of threads and on every platform, wasm included.
|
||
#![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable)]
|
||
#![forbid(unsafe_code)]
|
||
// Population arrays are indexed by cell and direction counts fixed when they are allocated.
|
||
#![allow(clippy::needless_range_loop)]
|
||
|
||
pub mod backend;
|
||
pub mod flow;
|
||
pub mod lattice;
|
||
pub mod setup;
|
||
pub mod solve;
|
||
pub mod voxel;
|
||
|
||
pub use backend::{Backend, Choice, Fields, register_backend};
|
||
pub use flow::{Flow, Group, Link, OpenCell, Rule, Target, ThermalState};
|
||
pub use setup::ThermalSetup;
|
||
pub use setup::{Obstacle, Setup, fluid_step};
|
||
pub use solve::{FluidResult, FluidStats, ForceHistory, Frame, HeatHistory, run, solve_fluid, solve_fluid_on};
|
||
pub use voxel::Grid;
|
||
|
||
#[derive(Debug, Clone, PartialEq, thiserror::Error)]
|
||
pub enum Error {
|
||
#[error("invalid input: {0}")]
|
||
Invalid(String),
|
||
#[error("not found: {0}")]
|
||
NotFound(String),
|
||
#[error("limit reached: {0}")]
|
||
Limit(String),
|
||
/// The run diverged.
|
||
#[error("unstable: {0}")]
|
||
Unstable(String),
|
||
#[error(transparent)]
|
||
Model(#[from] fieldcraft_model::Error),
|
||
#[error(transparent)]
|
||
Mesh(#[from] fieldcraft_mesh::Error),
|
||
}
|
||
|
||
pub type Result<T> = std::result::Result<T, Error>;
|
||
|
||
/// Caps on a run's size and work; a run beyond any of them is refused before it starts.
|
||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||
pub struct Limits {
|
||
pub max_cells: u64,
|
||
pub max_steps: u64,
|
||
/// Fluid cells times time steps.
|
||
pub max_cell_updates: u64,
|
||
/// The memory the output frames may take.
|
||
pub max_frame_bytes: u64,
|
||
}
|
||
|
||
impl Limits {
|
||
/// 16 million cells: about 5 GB of populations in double precision.
|
||
pub const MAX_CELLS: u64 = 16_000_000;
|
||
pub const MAX_STEPS: u64 = 10_000_000;
|
||
pub const MAX_CELL_UPDATES: u64 = 1_000_000_000_000;
|
||
pub const MAX_FRAME_BYTES: u64 = 2_000_000_000;
|
||
}
|
||
|
||
impl Default for Limits {
|
||
fn default() -> Self {
|
||
Limits {
|
||
max_cells: Limits::MAX_CELLS,
|
||
max_steps: Limits::MAX_STEPS,
|
||
max_cell_updates: Limits::MAX_CELL_UPDATES,
|
||
max_frame_bytes: Limits::MAX_FRAME_BYTES,
|
||
}
|
||
}
|
||
}
|