12#include "common/fixed.hlsli"
18 namespace fx = bicameral::fx;
29 constexpr uint64_t
MICRO = 1000000;
30 constexpr uint64_t
ONE_Q32 = (uint64_t)1 << 32;
67 std::array<uint64_t, GAS_MAX_SPECIES>
moved = {};
70 uint64_t
MulShiftU64(uint64_t a, uint64_t b, uint32_t shift) {
71 return fx::FxShiftRightU128(fx::FxMulU64Full(a, b), shift);
84 uint64_t capacity = 0;
95 chemical += (int64_t)cell.
amounts[s] * config.
species[s].formationEnergy;
112 if (heat <= 0 || capacity == 0)
115 const uint64_t temperature = fx::FxDivU128By64(fx::FxMulU64Full((uint64_t)heat,
MICRO), capacity).quotient;
116 assert(temperature <= std::numeric_limits<uint32_t>::max());
118 return (uint32_t)temperature;
123 const fx::FxU128 amountTemperature = fx::FxMulU64Full(
TotalAmount(config, cell), temperature);
124 assert(amountTemperature.hi == 0);
136 derived.
inverseMass = derived.
mass == 0 ? 0 : std::numeric_limits<uint64_t>::max() / derived.
mass;
137 derived.
inverseAmount = amount == 0 ? 0 : std::numeric_limits<uint64_t>::max() / amount;
147 return axis == 0 ? config.
sizeX : (axis == 1 ? config.
sizeY : config.
sizeZ);
161 return {index % sizeX, (index / sizeX) % sizeY, index / (sizeX * sizeY)};
168 if (coordinate < 0 || coordinate >= size) {
172 coordinate = ((coordinate % size) + size) % size;
175 cell[axis] = (uint32_t)coordinate;
181 const int64_t left = high ? (int64_t)cell[axis] : (int64_t)cell[axis] - 1;
188 face.
id = ((box.
Index(cell[0], cell[1], cell[2]) *
GAS_AXES) + axis) * 2 + (high ? 1 : 0);
196 std::vector<GasFace> faces;
199 for (uint32_t index = 0; index < box.
cells.size(); ++index) {
200 const std::array<uint32_t, GAS_AXES> cell =
CellCoordinate(box, index);
201 for (uint32_t axis = 0; axis <
GAS_AXES; ++axis) {
202 faces.push_back(
MakeFace(box, cell, axis,
false));
204 const bool lastCell = cell[axis] + 1 ==
AxisSize(config, axis);
206 faces.push_back(
MakeFace(box, cell, axis,
true));
241 const int64_t difference =
SideDerived(box, derived, face.
left).scaledPressure -
267 const int64_t quarter = fx::FxMulShiftS64(
283 int64_t pressure = derived[inside.
index].scaledPressure;
286 pressure += wallBelow ? hydrostatic : -hydrostatic;
293 const int64_t pressureSum =
SideDerived(box, derived, face.
left).scaledPressure +
300 const std::vector<GasCell>& forced,
GasFace& face) {
304 const int64_t momentumSum =
SideCell(box, forced, face.
left).momentum[face.
axis] +
309 const int64_t diffusion = fx::FxMulShiftS64(
DrivingPressure(box, derived, face),
311 face.
flow = central + diffusion;
320 const uint64_t inverseMass =
SideDerived(box, derived, donor).inverseMass;
327 const uint64_t magnitude = fx::FxAbsU64(a);
328 const fx::FxU128 product = {.hi = 0, .lo = magnitude};
329 const fx::FxDivResult result = fx::FxDivU128By64(product, b);
330 const uint64_t quotient = result.quotient + (result.remainder * 2 >= b ? 1 : 0);
332 return fx::FxApplySign(quotient, a < 0);
339 const uint64_t sourceCapacity =
HeatCapacity(config, source);
341 if (sourceCapacity == 0)
345 const fx::FxU128 numerator = fx::FxMulU64Full(fx::FxAbsU64(sourceHeat),
HeatCapacity(config, moved));
346 const fx::FxDivResult share = fx::FxDivU128By64(numerator, sourceCapacity);
347 const uint64_t heat = share.quotient + (share.remainder * 2 >= sourceCapacity ? 1 : 0);
348 const int64_t movedHeat = fx::FxApplySign(heat, sourceHeat < 0);
354 void Transfer(
GasBox& box,
const std::vector<GasCell>& forced, std::vector<GasCell>& next,
359 const bool forward = face.
flow > 0;
368 for (uint32_t axis = 0; axis <
GAS_AXES; ++axis)
374 for (uint32_t s = 0; s < speciesCount; ++s) {
380 for (uint32_t axis = 0; axis <
GAS_AXES; ++axis)
383 for (uint32_t s = 0; s < speciesCount; ++s)
387 for (uint32_t axis = 0; axis <
GAS_AXES; ++axis)
394 for (uint32_t s = 0; s < speciesCount; ++s)
398 for (uint32_t axis = 0; axis <
GAS_AXES; ++axis)
401 for (uint32_t s = 0; s < speciesCount; ++s)
405 for (uint32_t axis = 0; axis <
GAS_AXES; ++axis)
438 void LimitOutflow(
const GasBox& box,
const std::vector<GasDerived>& derived, std::vector<GasFace>& faces) {
439 std::vector<uint64_t> outflow(box.
cells.size(), 0);
442 const FaceSide donor = face.flow > 0 ? face.left : face.right;
444 outflow[donor.
index] += face.fraction;
448 const FaceSide donor = face.flow > 0 ? face.left : face.right;
452 face.fraction = fx::FxDivU128By64(fx::FxMulU64Full(face.fraction,
ONE_Q32), outflow[donor.
index])
462 if (a == 0 || b == 0 || (a > 0) != (b > 0))
465 const int64_t sign = a > 0 ? 1 : -1;
466 const int64_t smaller = std::min(a * sign, b * sign);
468 return smaller * sign;
470 const int64_t central = ((a * sign) + (b * sign)) / 2;
471 return std::min(2 * smaller, central) * sign;
476 const std::vector<GasDerived>& derived,
FaceSide side, uint32_t s) {
477 const uint64_t inverseAmount =
SideDerived(box, derived, side).inverseAmount;
485 int64_t
HalfSlope(
const GasBox& box,
const std::vector<GasCell>& cells,
const std::vector<GasDerived>& derived,
486 const GasFace& face, uint32_t s) {
487 const bool forward = face.
flow > 0;
494 const int64_t donorRatio =
SpeciesRatio(box, cells, derived, donor, s);
496 donorRatio -
SpeciesRatio(box, cells, derived, outer, s),
497 SpeciesRatio(box, cells, derived, receiver, s) - donorRatio);
499 return fx::FxMulShiftS64(slope, (int64_t)(
ONE_Q32 - face.
fraction), 33);
505 const std::vector<GasDerived>& derived,
const GasFace& face, uint32_t s) {
511 const int64_t halfSlope =
HalfSlope(box, cells, derived, face, s);
513 const uint64_t correction =
MulShiftU64(total, fx::FxAbsU64(halfSlope), 32);
515 return moved + correction;
517 return moved > correction ? moved - correction : 0;
523 const uint64_t whole = amountQ32 >> 32;
527 return whole + ((hash >> 32) < (amountQ32 &
LOW_32_BITS) ? 1 : 0);
532 const std::vector<GasDerived>& derived, uint64_t roundingTick,
533 std::vector<GasFace>& faces) {
535 if (face.fraction == 0)
538 const uint64_t faceHash = fx::FxHash64(box.
config.
randomSeed, roundingTick, face.id,
541 const uint64_t amountQ32 =
MovedAmountQ32(box, cells, derived, face, s);
542 face.moved[s] =
RoundAmount(box, amountQ32, fx::FxHashCombine(faceHash, s));
551 std::vector<std::array<uint64_t, GAS_MAX_SPECIES>> outgoing(box.
cells.size());
552 for (
const GasFace& face : faces) {
553 const FaceSide donor = face.flow > 0 ? face.left : face.right;
557 for (uint32_t s = 0; s < speciesCount; ++s)
558 outgoing[donor.
index][s] += face.moved[s];
562 const FaceSide donor = face.flow > 0 ? face.left : face.right;
567 for (uint32_t s = 0; s < speciesCount; ++s) {
568 const uint64_t total = outgoing[donor.
index][s];
569 if (total <= source.
amounts[s])
572 face.moved[s] = fx::FxDivU128By64(fx::FxMulU64Full(face.moved[s], source.
amounts[s]), total)
579 std::vector<GasDerived> derived(box.
cells.size());
580 for (uint32_t index = 0; index < box.
cells.size(); ++index)
586 void Substep(
GasBox& box,
const std::vector<GasFace>& layout, uint64_t roundingTick) {
588 const std::vector<GasDerived> derived =
DeriveAll(box);
591 std::vector<GasFace> faces = layout;
595 std::vector<GasCell> forced = box.
cells;
596 for (
const GasFace& face : faces)
612 std::vector<GasCell> next = forced;
613 for (
const GasFace& face : faces)
616 box.
cells = std::move(next);
623 const uint64_t substeps = config.
substeps;
631 coefficients.
centralFlow = ((uint64_t)1 << 31) / (30 * substeps);
640 coefficients.
gravityImpulse = (GRAVITY_MICROMETER_PER_SECOND2 << 36) / (60 * substeps * MICRO);
647 for (uint32_t s = 0; s < config.
speciesCount && s < amounts.size(); ++s)
651 const uint64_t heat = fx::FxDivU128By64(fx::FxMulU64Full(HeatCapacity(config, cell), temperatureMilliKelvin),
654 cell.
energy = fx::FxDivS64((int64_t)heat + ChemicalEnergy(config, cell), 1000);
660 uint32_t temperatureMilliKelvin,
661 uint64_t surfacePressureMicroPascal) {
663 std::vector<GasCell> layers;
664 uint64_t pressure = surfacePressureMicroPascal;
666 for (uint32_t z = 0; z < config.
sizeZ; ++z) {
668 const uint64_t divisor = PRESSURE_PER_AMOUNT_TEMPERATURE_Q32 * temperatureMilliKelvin;
669 const uint64_t total = fx::FxDivU128By64(fx::FxMulU64Full(pressure, ONE_Q32), divisor).quotient;
671 std::array<uint64_t, GAS_MAX_SPECIES> amounts = {};
672 for (uint32_t s = 0; s < config.
speciesCount && s < fractionsQ32.size(); ++s)
673 amounts[s] = MulShiftU64(total, fractionsQ32[s], 32);
676 layers.push_back(cell);
679 const uint64_t mass = CellMass(config, coefficients, cell);
680 pressure -= mass * HYDROSTATIC_MICROPASCAL_PER_MASS_X10000 / 10000;
687 assert(reference.size() == config.
sizeZ);
699 for (uint32_t axis = 0; axis <
GAS_AXES; ++axis)
702 box.
ghost.push_back(ghost);
709 const fx::FxU128 numerator = fx::FxMulU64Full(sound * sound * 8 * ground.
mass, ONE_Q32);
715 for (uint32_t z = 0; z < config.
sizeZ; ++z) {
716 for (uint32_t y = 0; y < config.
sizeY; ++y) {
717 for (uint32_t x = 0; x < config.
sizeX; ++x)
733 scaled = fx::FxDivS64(fx::FxMulShiftS64(scaled, soundTemperature, 0), derived.
temperature);
745 return fx::FxDivS64(cell.
momentum[axis], (int64_t)mass);
749 const std::vector<GasFace> layout = EnumerateFaces(box);
750 for (uint32_t substep = 0; substep < box.
config.
substeps; ++substep)
763 for (uint32_t axis = 0; axis <
GAS_AXES; ++axis)
771 uint64_t hash = fx::FxMix64(box.
tick);
774 hash = fx::FxHashCombine(hash, cell.
amounts[s]);
776 hash = fx::FxHashCombine(hash, (uint64_t)cell.
energy);
777 for (uint32_t axis = 0; axis <
GAS_AXES; ++axis)
778 hash = fx::FxHashCombine(hash, (uint64_t)cell.
momentum[axis]);
FaceSide BoundarySide(GasBoundary boundary, uint32_t z)
void ComputeFaceWeight(const GasBox &box, const std::vector< GasDerived > &derived, GasFace &face)
int64_t LimitedSlope(GasReconstruction reconstruction, int64_t a, int64_t b)
GasFace MakeFace(const GasBox &box, std::array< uint32_t, GAS_AXES > cell, uint32_t axis, bool high)
constexpr uint64_t MAX_CELL_AMOUNT
std::vector< GasFace > EnumerateFaces(const GasBox &box)
void ComputeFaceForce(const GasBox &box, const std::vector< GasDerived > &derived, GasFace &face)
int64_t Excess(const GasBox &box, const std::vector< GasDerived > &derived, FaceSide side)
uint32_t CellTemperature(const GasConfig &config, const GasCell &cell)
constexpr uint64_t HYDROSTATIC_MICROPASCAL_PER_MASS_X10000
int64_t ChemicalEnergy(const GasConfig &config, const GasCell &cell)
uint64_t TotalAmount(const GasConfig &config, const GasCell &cell)
void ApplyImpulse(GasBox &box, std::vector< GasCell > &next, const GasFace &face)
constexpr uint64_t GRAVITY_MICROMETER_PER_SECOND2
std::vector< GasDerived > DeriveAll(const GasBox &box)
uint64_t RoundAmount(const GasBox &box, uint64_t amountQ32, uint64_t hash)
const GasCell & SideCell(const GasBox &box, const std::vector< GasCell > &cells, FaceSide side)
constexpr uint64_t LOW_32_BITS
uint64_t MulShiftU64(uint64_t a, uint64_t b, uint32_t shift)
void ComputeFaceFlow(const GasBox &box, const std::vector< GasDerived > &derived, const std::vector< GasCell > &forced, GasFace &face)
int64_t DrivingPressure(const GasBox &box, const std::vector< GasDerived > &derived, const GasFace &face)
int64_t SpeciesRatio(const GasBox &box, const std::vector< GasCell > &cells, const std::vector< GasDerived > &derived, FaceSide side, uint32_t s)
int64_t HalfHydrostatic(int64_t excess)
constexpr uint64_t ONE_Q32
void LimitMovedAmounts(const GasBox &box, const std::vector< GasCell > &cells, std::vector< GasFace > &faces)
void Substep(GasBox &box, const std::vector< GasFace > &layout, uint64_t roundingTick)
int64_t DivideNearest(int64_t a, uint64_t b)
uint64_t HeatCapacity(const GasConfig &config, const GasCell &cell)
constexpr uint32_t GAS_ROUNDING_PURPOSE
void LimitOutflow(const GasBox &box, const std::vector< GasDerived > &derived, std::vector< GasFace > &faces)
uint64_t RawFraction(const GasBox &box, const std::vector< GasDerived > &derived, const GasFace &face)
uint32_t CellLayer(const GasBox &box, uint32_t index)
FaceSide SideAt(const GasBox &box, std::array< uint32_t, GAS_AXES > cell, uint32_t axis, int64_t coordinate)
int64_t CellPressure(const GasConfig &config, const GasCell &cell, uint32_t temperature)
void Transfer(GasBox &box, const std::vector< GasCell > &forced, std::vector< GasCell > &next, const GasFace &face)
int64_t MovedEnergy(const GasConfig &config, const GasCell &source, const GasCell &moved)
void ComputeMovedAmounts(const GasBox &box, const std::vector< GasCell > &cells, const std::vector< GasDerived > &derived, uint64_t roundingTick, std::vector< GasFace > &faces)
uint64_t CellMass(const GasConfig &config, const GasCoefficients &coefficients, const GasCell &cell)
GasDerived DeriveRaw(const GasConfig &config, const GasCoefficients &coefficients, const GasCell &cell)
uint32_t AxisSize(const GasConfig &config, uint32_t axis)
std::array< uint32_t, GAS_AXES > CellCoordinate(const GasBox &box, uint32_t index)
const GasDerived & SideDerived(const GasBox &box, const std::vector< GasDerived > &derived, FaceSide side)
constexpr uint64_t PRESSURE_PER_AMOUNT_TEMPERATURE_Q32
int64_t HalfSlope(const GasBox &box, const std::vector< GasCell > &cells, const std::vector< GasDerived > &derived, const GasFace &face, uint32_t s)
uint64_t MovedAmountQ32(const GasBox &box, const std::vector< GasCell > &cells, const std::vector< GasDerived > &derived, const GasFace &face, uint32_t s)
GasDerived DeriveGasCell(const GasBox &box, const GasCell &cell, uint32_t z)
GasCoefficients MakeGasCoefficients(const GasConfig &config)
uint64_t HashGas(const GasBox &box)
int64_t GasVelocity(const GasBox &box, const GasCell &cell, uint32_t axis)
constexpr uint32_t GAS_AXES
void StepGas(GasBox &box)
GasBox MakeGasBox(const GasConfig &config, std::vector< GasCell > reference)
GasTotals SumGas(const GasBox &box)
std::vector< GasCell > MakeHydrostaticReference(const GasConfig &config, std::span< const uint32_t > fractionsQ32, uint32_t temperatureMilliKelvin, uint64_t surfacePressureMicroPascal)
GasCell MakeGasCell(const GasConfig &config, std::span< const uint64_t > amounts, uint32_t temperatureMilliKelvin)
uint32_t Index(uint32_t x, uint32_t y, uint32_t z) const
std::vector< GasCell > reference
std::vector< GasCell > ghost
std::vector< GasCell > cells
std::vector< GasDerived > referenceDerived
GasCoefficients coefficients
std::array< uint64_t, GAS_MAX_SPECIES > amounts
std::array< int64_t, GAS_AXES > momentum
std::array< uint64_t, GAS_MAX_SPECIES > massPerAmount
uint32_t soundTemperature
std::array< GasBoundary, GAS_AXES > boundary
std::array< int32_t, GAS_AXES > windVelocity
uint32_t soundSpeedMmPerS
GasReconstruction reconstruction
std::array< GasSpecies, GAS_MAX_SPECIES > species
std::array< int64_t, GAS_MAX_SPECIES > amounts
std::array< int64_t, GAS_AXES > momentum
std::array< int64_t, GAS_AXES > momentum
std::array< uint64_t, GAS_MAX_SPECIES > amounts
std::array< uint64_t, GAS_MAX_SPECIES > moved