///|
/// Return the number of completed time steps.
pub fn Simulation::steps(self : Simulation) -> Int {
self.step_count
}
///|
/// Return the number of stored distribution populations.
pub fn Simulation::distribution_count(self : Simulation) -> Int {
self.f.length()
}
///|
/// Copy the distribution payload for diagnostics or persistence adapters.
pub fn Simulation::distribution_values(self : Simulation) -> Array[Double] {
self.f.copy()
}
///|
/// Copy the solid-cell mask in row-major order.
pub fn Simulation::solid_values(self : Simulation) -> Array[Bool] {
self.solid.copy()
}
///|
/// Compute a deterministic checksum over geometry and populations.
pub fn Simulation::state_checksum(self : Simulation) -> Double {
let mut checksum = self.size.width.to_double() * 0.000001 +
self.size.height.to_double() * 0.000000001
for i, value in self.f {
checksum += (i + 1).to_double() * value
}
for i, solid in self.solid {
if solid {
checksum += (i + 1).to_double() * 0.0000001
}
}
checksum + self.step_count.to_double() * 0.0000000001
}
///|
/// Produce a compact, deterministic identity for a simulation state.
pub fn Simulation::state_fingerprint(self : Simulation) -> String {
let builder = StringBuilder(size_hint=96)
builder.write_string("MLBM:")
builder.write_string("\{self.size.width}x\{self.size.height}:")
builder.write_string("\{self.step_count}:")
builder.write_string("\{self.mass()}:")
builder.write_string("\{self.state_checksum()}")
builder.to_string()
}
///|
/// Return the serialized checkpoint size in bytes.
pub fn Simulation::checkpoint_size(self : Simulation) -> Int {
self.to_checkpoint().length()
}
///|
/// Estimate the payload bytes without allocating a checkpoint string.
pub fn Simulation::payload_bytes(self : Simulation) -> Int {
self.f.length() * 8 + self.solid.length()
}
///|
/// Return whether the distribution is within tolerance of local equilibrium.
pub fn Simulation::is_equilibrium(
self : Simulation,
tolerance? : Double = 0.000000001,
) -> Bool {
let limit = tolerance.max(0.0)
let mut result = true
for y in 0.. Point {
let mut ux = 0.0
let mut uy = 0.0
let mut count = 0
for y in 0.. Double {
self.density_field().statistics_masked(mask=self.current_mask()).maximum
}
///|
/// Find the minimum fluid density.
pub fn Simulation::minimum_density(self : Simulation) -> Double {
let stats = self.density_field().statistics_masked(mask=self.current_mask())
stats.minimum
}
///|
/// Construct a field of local kinetic energy densities.
pub fn Simulation::kinetic_energy_field(self : Simulation) -> Field2D {
let result = Field2D::new(size=self.size)
for y in 0.. Field2D {
self.density_field().map((x, y, rho) => rho * self.cell(x~, y~).ux)
}
///|
/// Construct the y component of local momentum density.
pub fn Simulation::momentum_y_field(self : Simulation) -> Field2D {
let result = Field2D::new(size=self.size)
for y in 0.. String {
self.density_field().to_csv(name="density")
}
///|
/// Export velocity magnitude as a CSV grid.
pub fn Simulation::speed_csv(self : Simulation) -> String {
self.speed_field().to_csv(name="speed")
}
///|
/// Export velocity components as a CSV point table.
pub fn Simulation::velocity_csv(self : Simulation) -> String {
self.velocity_field().to_csv()
}
///|
/// Compare two states using a normalized distribution L2 error.
pub fn Simulation::state_delta(self : Simulation, other : Simulation) -> Double {
if self.size != other.size {
1.0
} else {
relative_l2_error(self.f[:], other.f[:])
}
}
///|
/// Return whether two simulations share dimensions and solid geometry.
pub fn Simulation::same_geometry(self : Simulation, other : Simulation) -> Bool {
self.size == other.size && self.solid[:] == other.solid[:]
}
///|
/// Return a human-readable state summary for logs and notebooks.
pub fn Simulation::state_summary(self : Simulation) -> String {
let stability = self.stability()
let mean = self.mean_velocity()
let builder = StringBuilder(size_hint=256)
builder.write_string("steps=\{self.step_count}")
builder.write_string(" mass=\{stability.mass}")
builder.write_string(" rho=[\{stability.min_rho},\{stability.max_rho}]")
builder.write_string(" mean_velocity=(\{mean.x},\{mean.y})")
builder.write_string(" max_speed=\{stability.max_speed}")
builder.write_string(" checksum=\{self.state_checksum()}")
builder.to_string()
}