General testing

This commit is contained in:
2026-05-05 10:32:30 +02:00
parent ff4c4aef23
commit d963032e74
11 changed files with 794 additions and 448 deletions

View File

@@ -5,9 +5,10 @@ namespace FluidSim.Components
{
public enum BoundaryType
{
VolumeCoupling,
OpenEnd,
ClosedEnd
ZeroPressureOpen, // pressurerelease boundary (strong reflection)
ClosedEnd,
GhostCell
}
public class Pipe1D
@@ -21,27 +22,20 @@ namespace FluidSim.Components
private double _dx, _dt, _gamma, _area, _diameter;
private double[] _rho, _rhou, _E;
// Volumecoupling ghost states for boundaries A and B
private double _rhoA, _pA;
private double _rhoB, _pB;
private bool _aBCSet, _bBCSet;
// Ghost cell states
private double _rhoGhostL, _uGhostL, _pGhostL;
private double _rhoGhostR, _uGhostR, _pGhostR;
private bool _ghostLSet, _ghostRSet;
private BoundaryType _aBCType = BoundaryType.VolumeCoupling;
private BoundaryType _bBCType = BoundaryType.VolumeCoupling;
private BoundaryType _aBCType = BoundaryType.GhostCell;
private BoundaryType _bBCType = BoundaryType.GhostCell;
private double _aAmbientPressure = 101325.0;
private double _bAmbientPressure = 101325.0;
private const double CflTarget = 0.8;
private const double ReferenceSoundSpeed = 340.0;
public int GetCellCount() => _n;
public double GetCellDensity(int i) => _rho[i];
public double GetCellPressure(int i) => Pressure(i);
public double GetCellVelocity(int i) => _rhou[i] / Math.Max(_rho[i], 1e-12);
public BoundaryType ABCType => _aBCType;
public BoundaryType BBCType => _bBCType;
private double _lastMassFlow = 0.0;
public Pipe1D(double length, double area, int sampleRate, int forcedCellCount = 0)
{
@@ -49,9 +43,7 @@ namespace FluidSim.Components
int nCells;
if (forcedCellCount > 1)
{
nCells = forcedCellCount;
}
else
{
double dxTarget = ReferenceSoundSpeed * dtGlobal * CflTarget;
@@ -65,8 +57,6 @@ namespace FluidSim.Components
_dt = dtGlobal;
_area = area;
_gamma = 1.4;
// Hydraulic diameter for a circular pipe
_diameter = 2.0 * Math.Sqrt(area / Math.PI);
_rho = new double[_n];
@@ -82,6 +72,26 @@ namespace FluidSim.Components
public void SetAAmbientPressure(double p) => _aAmbientPressure = p;
public void SetBAmbientPressure(double p) => _bAmbientPressure = p;
public void SetGhostLeft(double rho, double u, double p)
{
_rhoGhostL = rho;
_uGhostL = u;
_pGhostL = p;
_ghostLSet = true;
}
public void SetGhostRight(double rho, double u, double p)
{
_rhoGhostR = rho;
_uGhostR = u;
_pGhostR = p;
_ghostRSet = true;
}
public void ClearGhostFlag()
{
_ghostLSet = false;
_ghostRSet = false;
}
public void SetUniformState(double rho, double u, double p)
{
double e = p / ((_gamma - 1) * rho);
@@ -94,6 +104,46 @@ namespace FluidSim.Components
}
}
public double GetOpenEndMassFlow()
{
if (_bBCType != BoundaryType.OpenEnd && _bBCType != BoundaryType.ZeroPressureOpen)
return 0.0;
int lastCell = _n - 1;
double rho = _rho[lastCell];
double u = _rhou[lastCell] / Math.Max(rho, 1e-12);
double p = Pressure(lastCell);
double c = Math.Sqrt(_gamma * p / rho);
double uFace = u;
double rhoFace = rho;
double pFace = p;
// Subsonic outflow: impose ambient pressure, adjust velocity and density
if (uFace > 0 && uFace < c)
{
double s = p / Math.Pow(rho, _gamma);
double rhoAmb = Math.Pow(_bAmbientPressure / s, 1.0 / _gamma);
double cAmb = Math.Sqrt(_gamma * _bAmbientPressure / rhoAmb);
double J_plus = u + 2.0 * c / (_gamma - 1.0);
double uFaceNew = J_plus - 2.0 * cAmb / (_gamma - 1.0);
if (uFaceNew > 0) uFace = uFaceNew;
if (uFace < 0) uFace = 0;
rhoFace = rhoAmb;
pFace = _bAmbientPressure;
}
return rhoFace * uFace * _area;
}
public double GetAndStoreMassFlowForDerivative()
{
double current = GetOpenEndMassFlow();
double derivative = (current - _lastMassFlow) / _dt;
_lastMassFlow = current;
return derivative;
}
public void SetCellState(int i, double rho, double u, double p)
{
if (i < 0 || i >= _n) return;
@@ -103,22 +153,18 @@ namespace FluidSim.Components
_E[i] = rho * e + 0.5 * rho * u * u;
}
public void SetAVolumeState(double rhoVol, double pVol)
public int GetCellCount() => _n;
public double GetCellDensity(int i) => _rho[i];
public double GetCellPressure(int i) => Pressure(i);
public double GetCellVelocity(int i) => _rhou[i] / Math.Max(_rho[i], 1e-12);
private double Pressure(int i) => (_gamma - 1.0) * (_E[i] - 0.5 * _rhou[i] * _rhou[i] / Math.Max(_rho[i], 1e-12));
public double GetPressureAtFraction(double fraction)
{
_rhoA = rhoVol;
_pA = pVol;
_aBCSet = true;
int i = (int)(fraction * (_n - 1));
i = Math.Clamp(i, 0, _n - 1);
return Pressure(i);
}
public void SetBVolumeState(double rhoVol, double pVol)
{
_rhoB = rhoVol;
_pB = pVol;
_bBCSet = true;
}
public void ClearBC() => _aBCSet = _bBCSet = false;
public int GetRequiredSubSteps(double dtGlobal, double cflTarget = 0.8)
{
double maxW = 0.0;
@@ -141,101 +187,72 @@ namespace FluidSim.Components
double[] Fp = new double[n + 1];
double[] Fe = new double[n + 1];
// ---------- Boundary A (face 0, left) ----------
double rhoIntA = _rho[0];
double uIntA = _rhou[0] / Math.Max(rhoIntA, 1e-12);
double pIntA = Pressure(0);
switch (_aBCType)
// Left boundary (face 0)
double rhoL = _rho[0];
double uL = _rhou[0] / Math.Max(rhoL, 1e-12);
double pL = Pressure(0);
if (_aBCType == BoundaryType.GhostCell && _ghostLSet)
HLLCFlux(_rhoGhostL, _uGhostL, _pGhostL, rhoL, uL, pL, out Fm[0], out Fp[0], out Fe[0]);
else if (_aBCType == BoundaryType.OpenEnd)
OpenEndFluxLeft(rhoL, uL, pL, _aAmbientPressure, out Fm[0], out Fp[0], out Fe[0]);
else if (_aBCType == BoundaryType.ZeroPressureOpen)
{
case BoundaryType.VolumeCoupling:
if (_aBCSet)
{
HLLCFlux(_rhoA, 0.0, _pA,
rhoIntA, uIntA, pIntA,
out Fm[0], out Fp[0], out Fe[0]);
}
else
{
Fm[0] = 0; Fp[0] = pIntA; Fe[0] = 0;
}
break;
case BoundaryType.OpenEnd:
OpenEndFluxA(rhoIntA, uIntA, pIntA, _aAmbientPressure,
out Fm[0], out Fp[0], out Fe[0]);
break;
case BoundaryType.ClosedEnd:
ClosedEndFlux(rhoIntA, uIntA, pIntA, isRightBoundary: false,
out Fm[0], out Fp[0], out Fe[0]);
break;
// Strong reflection: force pressure to ambient, extrapolate density and velocity
double rhoFace = rhoL;
double uFace = uL;
double pFace = _aAmbientPressure;
HLLCFlux(rhoFace, uFace, pFace, rhoL, uL, pL, out Fm[0], out Fp[0], out Fe[0]);
}
else if (_aBCType == BoundaryType.ClosedEnd)
ClosedEndFlux(rhoL, uL, pL, false, out Fm[0], out Fp[0], out Fe[0]);
else
{ Fm[0] = 0; Fp[0] = pL; Fe[0] = 0; }
// ---------- Internal faces ----------
// Internal faces
for (int i = 0; i < n - 1; i++)
{
double rhoL = _rho[i];
double uL = _rhou[i] / Math.Max(rhoL, 1e-12);
double pL = Pressure(i);
double rhoR = _rho[i + 1];
double uR = _rhou[i + 1] / Math.Max(rhoR, 1e-12);
double pR = Pressure(i + 1);
HLLCFlux(rhoL, uL, pL, rhoR, uR, pR,
out Fm[i + 1], out Fp[i + 1], out Fe[i + 1]);
double rhoLi = _rho[i];
double uLi = _rhou[i] / Math.Max(rhoLi, 1e-12);
double pLi = Pressure(i);
double rhoRi = _rho[i + 1];
double uRi = _rhou[i + 1] / Math.Max(rhoRi, 1e-12);
double pRi = Pressure(i + 1);
HLLCFlux(rhoLi, uLi, pLi, rhoRi, uRi, pRi, out Fm[i + 1], out Fp[i + 1], out Fe[i + 1]);
}
// ---------- Boundary B (face n, right) ----------
double rhoIntB = _rho[n - 1];
double uIntB = _rhou[n - 1] / Math.Max(rhoIntB, 1e-12);
double pIntB = Pressure(n - 1);
switch (_bBCType)
// Right boundary (face n)
double rhoR = _rho[n - 1];
double uR = _rhou[n - 1] / Math.Max(rhoR, 1e-12);
double pR = Pressure(n - 1);
if (_bBCType == BoundaryType.GhostCell && _ghostRSet)
HLLCFlux(rhoR, uR, pR, _rhoGhostR, _uGhostR, _pGhostR, out Fm[n], out Fp[n], out Fe[n]);
else if (_bBCType == BoundaryType.OpenEnd)
OpenEndFluxRight(rhoR, uR, pR, _bAmbientPressure, out Fm[n], out Fp[n], out Fe[n]);
else if (_bBCType == BoundaryType.ZeroPressureOpen)
{
case BoundaryType.VolumeCoupling:
if (_bBCSet)
{
HLLCFlux(rhoIntB, uIntB, pIntB,
_rhoB, 0.0, _pB,
out Fm[n], out Fp[n], out Fe[n]);
}
else
{
Fm[n] = 0; Fp[n] = pIntB; Fe[n] = 0;
}
break;
case BoundaryType.OpenEnd:
OpenEndFluxB(rhoIntB, uIntB, pIntB, _bAmbientPressure,
out Fm[n], out Fp[n], out Fe[n]);
break;
case BoundaryType.ClosedEnd:
ClosedEndFlux(rhoIntB, uIntB, pIntB, isRightBoundary: true,
out Fm[n], out Fp[n], out Fe[n]);
break;
double rhoFace = rhoR;
double uFace = uR;
double pFace = _bAmbientPressure;
HLLCFlux(rhoR, uR, pR, rhoFace, uFace, pFace, out Fm[n], out Fp[n], out Fe[n]);
}
else if (_bBCType == BoundaryType.ClosedEnd)
ClosedEndFlux(rhoR, uR, pR, true, out Fm[n], out Fp[n], out Fe[n]);
else
{ Fm[n] = 0; Fp[n] = pR; Fe[n] = 0; }
// ---- Cell update with linear laminar damping ----
// Cell update (linear damping)
double radius = _diameter / 2.0;
double mu_air = 1.8e-5;
double laminarCoeff = DampingMultiplier * 8.0 * mu_air / (radius * radius);
for (int i = 0; i < n; i++)
{
double dM = (Fm[i + 1] - Fm[i]) / _dx;
double dP = (Fp[i + 1] - Fp[i]) / _dx;
double dE = (Fe[i + 1] - Fe[i]) / _dx;
_rho[i] -= dtSub * dM;
_rhou[i] -= dtSub * dP;
_E[i] -= dtSub * dE;
_rho[i] -= dtSub * (Fm[i + 1] - Fm[i]) / _dx;
_rhou[i] -= dtSub * (Fp[i + 1] - Fp[i]) / _dx;
_E[i] -= dtSub * (Fe[i + 1] - Fe[i]) / _dx;
double rho = Math.Max(_rho[i], 1e-12);
double dampingRate = laminarCoeff / rho;
double dampingFactor = Math.Exp(-dampingRate * dtSub);
double dampingFactor = Math.Exp(-(laminarCoeff / rho) * dtSub);
_rhou[i] *= dampingFactor;
if (_rho[i] < 1e-12) _rho[i] = 1e-12;
@@ -244,139 +261,10 @@ namespace FluidSim.Components
double eMin = pMin / ((_gamma - 1) * _rho[i]) + kinetic;
if (_E[i] < eMin) _E[i] = eMin;
}
// ---------- Port quantities ----------
double mdotA_sub = _aBCType == BoundaryType.VolumeCoupling && _aBCSet ? Fm[0] * _area : 0.0;
double mdotB_sub = _bBCType == BoundaryType.VolumeCoupling && _bBCSet ? -Fm[n] * _area : 0.0;
PortA.MassFlowRate = mdotA_sub;
PortB.MassFlowRate = mdotB_sub;
PortA.Pressure = pIntA;
PortB.Pressure = pIntB;
PortA.Density = _rho[0];
PortB.Density = _rho[n - 1];
// Corrected enthalpy for both directions
if (_aBCType == BoundaryType.VolumeCoupling && _aBCSet)
{
PortA.SpecificEnthalpy = mdotA_sub < 0
? GetCellTotalSpecificEnthalpy(0)
: (_gamma / (_gamma - 1.0)) * _pA / Math.Max(_rhoA, 1e-12);
}
if (_bBCType == BoundaryType.VolumeCoupling && _bBCSet)
{
PortB.SpecificEnthalpy = mdotB_sub < 0
? GetCellTotalSpecificEnthalpy(_n - 1)
: (_gamma / (_gamma - 1.0)) * _pB / Math.Max(_rhoB, 1e-12);
}
}
private double GetCellTotalSpecificEnthalpy(int i)
{
double rho = Math.Max(_rho[i], 1e-12);
double u = _rhou[i] / rho;
double p = Pressure(i);
double h = _gamma / (_gamma - 1.0) * p / rho;
return h + 0.5 * u * u;
}
private double Pressure(int i) =>
(_gamma - 1.0) * (_E[i] - 0.5 * _rhou[i] * _rhou[i] / Math.Max(_rho[i], 1e-12));
// ========== Characteristicbased Open End ==========
private void OpenEndFluxA(double rhoInt, double uInt, double pInt, double pAmb,
out double fm, out double fp, out double fe)
{
double cInt = Math.Sqrt(_gamma * pInt / Math.Max(rhoInt, 1e-12));
// Subsonic inflow (uInt ≤ 0, so flow inside pipe ←)
if (uInt <= -cInt) // supersonic inflow use interior state as ghost
{
fm = rhoInt * uInt;
fp = rhoInt * uInt * uInt + pInt;
fe = (rhoInt * (pInt / ((_gamma - 1) * rhoInt) + 0.5 * uInt * uInt) + pInt) * uInt;
return;
}
else if (uInt <= 0) // subsonic inflow
{
// Reservoir condition: p = pAmb, T = 300K, u = 0
double T0 = 300.0;
double R = 287.0;
double rhoGhost = pAmb / (R * T0);
HLLCFlux(rhoGhost, 0.0, pAmb, rhoInt, uInt, pInt, out fm, out fp, out fe);
return;
}
else // subsonic outflow (uInt > 0)
{
// Ghost pressure forced to pAmb
double s = pInt / Math.Pow(rhoInt, _gamma);
double rhoGhost = Math.Pow(pAmb / s, 1.0 / _gamma);
double cGhost = Math.Sqrt(_gamma * pAmb / rhoGhost);
// Outgoing Riemann invariant J⁻ = uInt - 2*cInt/(γ-1) (for left boundary)
double J_minus = uInt - 2.0 * cInt / (_gamma - 1.0);
double uGhost = J_minus + 2.0 * cGhost / (_gamma - 1.0);
// Prevent spurious inflow by clipping to zero
if (uGhost < 0) uGhost = 0;
HLLCFlux(rhoGhost, uGhost, pAmb, rhoInt, uInt, pInt, out fm, out fp, out fe);
}
}
private void OpenEndFluxB(double rhoInt, double uInt, double pInt, double pAmb,
out double fm, out double fp, out double fe)
{
double cInt = Math.Sqrt(_gamma * pInt / Math.Max(rhoInt, 1e-12));
if (uInt >= cInt) // supersonic outflow (extrapolation)
{
fm = rhoInt * uInt;
fp = rhoInt * uInt * uInt + pInt;
fe = (rhoInt * (pInt / ((_gamma - 1) * rhoInt) + 0.5 * uInt * uInt) + pInt) * uInt;
return;
}
else if (uInt >= 0) // subsonic outflow
{
double s = pInt / Math.Pow(rhoInt, _gamma);
double rhoGhost = Math.Pow(pAmb / s, 1.0 / _gamma);
double cGhost = Math.Sqrt(_gamma * pAmb / rhoGhost);
// Outgoing Riemann invariant J⁺ = uInt + 2*cInt/(γ-1) (for right boundary)
double J_plus = uInt + 2.0 * cInt / (_gamma - 1.0);
double uGhost = J_plus - 2.0 * cGhost / (_gamma - 1.0);
// Clip to zero to prevent inflow
if (uGhost > 0) uGhost = 0;
HLLCFlux(rhoInt, uInt, pInt, rhoGhost, uGhost, pAmb, out fm, out fp, out fe);
}
else // subsonic inflow
{
double T0 = 300.0;
double R = 287.0;
double rhoGhost = pAmb / (R * T0);
HLLCFlux(rhoInt, uInt, pInt, rhoGhost, 0.0, pAmb, out fm, out fp, out fe);
}
}
// ========== Closed end (mirror) ==========
private void ClosedEndFlux(double rhoInt, double uInt, double pInt, bool isRightBoundary,
out double fm, out double fp, out double fe)
{
double rhoGhost = rhoInt;
double pGhost = pInt;
double uGhost = -uInt; // mirror velocity
if (isRightBoundary)
HLLCFlux(rhoInt, uInt, pInt, rhoGhost, uGhost, pGhost, out fm, out fp, out fe);
else
HLLCFlux(rhoGhost, uGhost, pGhost, rhoInt, uInt, pInt, out fm, out fp, out fe);
}
// ========== Standard HLLC flux ==========
private void HLLCFlux(double rL, double uL, double pL,
double rR, double uR, double pR,
// ---------- HLLC Riemann solver ----------
private void HLLCFlux(double rL, double uL, double pL, double rR, double uR, double pR,
out double fm, out double fp, out double fe)
{
double cL = Math.Sqrt(_gamma * pL / Math.Max(rL, 1e-12));
@@ -385,7 +273,6 @@ namespace FluidSim.Components
double ER = pR / ((_gamma - 1) * rR) + 0.5 * uR * uR;
double SL = Math.Min(uL - cL, uR - cR);
double SR = Math.Max(uL + cL, uR + cR);
double Ss = (pR - pL + rL * uL * (SL - uL) - rR * uR * (SR - uR))
/ (rL * (SL - uL) - rR * (SR - uR));
@@ -404,17 +291,76 @@ namespace FluidSim.Components
else
{
double rsR = rR * (SR - uR) / (SR - Ss);
double ps = pL + rL * (SL - uL) * (Ss - uL);
double ps = pR + rR * (SR - uR) * (Ss - uR);
double EsR = ER + (Ss - uR) * (Ss + pR / (rR * (SR - uR)));
fm = rsR * Ss; fp = rsR * Ss * Ss + ps; fe = (rsR * EsR + ps) * Ss;
}
}
public double GetPressureAtFraction(double fraction)
// ---------- Characteristic openend boundaries ----------
private void OpenEndFluxLeft(double rhoInt, double uInt, double pInt, double pAmb,
out double fm, out double fp, out double fe)
{
int i = (int)(fraction * (_n - 1));
i = Math.Clamp(i, 0, _n - 1);
return Pressure(i);
double cInt = Math.Sqrt(_gamma * pInt / Math.Max(rhoInt, 1e-12));
if (uInt <= -cInt) // supersonic inflow
{
fm = rhoInt * uInt;
fp = rhoInt * uInt * uInt + pInt;
fe = (rhoInt * (pInt / ((_gamma - 1) * rhoInt) + 0.5 * uInt * uInt) + pInt) * uInt;
return;
}
if (uInt <= 0) // subsonic inflow
{
double T0 = 300.0, R = 287.0;
double ghost_Rho = pAmb / (R * T0);
HLLCFlux(ghost_Rho, 0.0, pAmb, rhoInt, uInt, pInt, out fm, out fp, out fe);
return;
}
// subsonic outflow
double s = pInt / Math.Pow(rhoInt, _gamma);
double ghostRho = Math.Pow(pAmb / s, 1.0 / _gamma);
double cGhost = Math.Sqrt(_gamma * pAmb / ghostRho);
double J_minus = uInt - 2.0 * cInt / (_gamma - 1.0);
double uGhost = J_minus + 2.0 * cGhost / (_gamma - 1.0);
if (uGhost < 0) uGhost = 0;
HLLCFlux(ghostRho, uGhost, pAmb, rhoInt, uInt, pInt, out fm, out fp, out fe);
}
private void OpenEndFluxRight(double rhoInt, double uInt, double pInt, double pAmb,
out double fm, out double fp, out double fe)
{
double cInt = Math.Sqrt(_gamma * pInt / Math.Max(rhoInt, 1e-12));
if (uInt >= cInt) // supersonic outflow
{
fm = rhoInt * uInt;
fp = rhoInt * uInt * uInt + pInt;
fe = (rhoInt * (pInt / ((_gamma - 1) * rhoInt) + 0.5 * uInt * uInt) + pInt) * uInt;
return;
}
if (uInt >= 0) // subsonic outflow
{
double s = pInt / Math.Pow(rhoInt, _gamma);
double ghost_Rho = Math.Pow(pAmb / s, 1.0 / _gamma);
double cGhost = Math.Sqrt(_gamma * pAmb / ghost_Rho);
double J_plus = uInt + 2.0 * cInt / (_gamma - 1.0);
double uGhost = J_plus - 2.0 * cGhost / (_gamma - 1.0);
if (uGhost > 0) uGhost = 0;
HLLCFlux(rhoInt, uInt, pInt, ghost_Rho, uGhost, pAmb, out fm, out fp, out fe);
}
// subsonic inflow
double T0 = 300.0, R = 287.0;
double ghostRho = pAmb / (R * T0);
HLLCFlux(rhoInt, uInt, pInt, ghostRho, 0.0, pAmb, out fm, out fp, out fe);
}
private void ClosedEndFlux(double rhoInt, double uInt, double pInt, bool isRight,
out double fm, out double fp, out double fe)
{
double rhoGhost = rhoInt, pGhost = pInt, uGhost = -uInt;
if (isRight)
HLLCFlux(rhoInt, uInt, pInt, rhoGhost, uGhost, pGhost, out fm, out fp, out fe);
else
HLLCFlux(rhoGhost, uGhost, pGhost, rhoInt, uInt, pInt, out fm, out fp, out fe);
}
}
}

View File

@@ -1,21 +1,17 @@
using System;
using FluidSim.Interfaces;
using FluidSim.Utils;
namespace FluidSim.Components
{
public class Volume0D
{
public Port Port { get; private set; }
public double Mass { get; private set; }
public double InternalEnergy { get; private set; }
public double Mass { get; set; }
public double InternalEnergy { get; set; }
public double Gamma { get; set; } = 1.4;
public double GasConstant { get; set; } = 287.0;
public double Volume { get; set; }
public double dVdt { get; set; }
public double Dvdt { get; set; }
private double _dt;
@@ -24,6 +20,9 @@ namespace FluidSim.Components
public double Temperature => Pressure / (Density * GasConstant);
public double SpecificEnthalpy => Gamma / (Gamma - 1.0) * Pressure / Density;
public double MassFlowRateIn { get; set; }
public double SpecificEnthalpyIn { get; set; }
public Volume0D(double initialVolume, double initialPressure,
double initialTemperature, int sampleRate,
double gasConstant = 287.0, double gamma = 1.4)
@@ -31,54 +30,38 @@ namespace FluidSim.Components
GasConstant = gasConstant;
Gamma = gamma;
Volume = initialVolume;
dVdt = 0.0;
Dvdt = 0.0;
_dt = 1.0 / sampleRate;
double rho0 = initialPressure / (GasConstant * initialTemperature);
Mass = rho0 * Volume;
InternalEnergy = (initialPressure * Volume) / (Gamma - 1.0);
Port = new Port();
PushStateToPort();
}
public void PushStateToPort()
{
Port.Pressure = Pressure;
Port.Density = Density;
Port.Temperature = Temperature;
Port.SpecificEnthalpy = SpecificEnthalpy;
}
// Original integrate (uses the constructors sample rate)
public void Integrate()
{
Integrate(_dt);
}
public void SetPressure(double newPressure)
{
InternalEnergy = newPressure * Volume / (Gamma - 1.0);
// Mass stays the same, so density is unchanged
}
// New overload: integrate with a custom time step (for substeps)
public void Integrate(double dtOverride)
{
double mdot = Port.MassFlowRate;
double h_in = Port.SpecificEnthalpy;
double dm = mdot * dtOverride;
double dE = (mdot * h_in) * dtOverride - Pressure * dVdt * dtOverride;
double dm = MassFlowRateIn * dtOverride;
double dE = (MassFlowRateIn * SpecificEnthalpyIn) * dtOverride - Pressure * Dvdt * dtOverride;
Mass += dm;
InternalEnergy += dE;
// Hard physical bounds prevent NaN and unphysical states
if (Mass < 1e-12) Mass = 1e-12;
if (InternalEnergy < 1e-12) InternalEnergy = 1e-12;
// Safety: if mass becomes extremely small, reset internal energy to zero
if (Mass < 1e-12)
{
Mass = 0.0;
InternalEnergy = 0.0;
}
else if (InternalEnergy < 1e-12)
{
InternalEnergy = 0.0;
}
PushStateToPort();
// Avoid negative mass/energy
if (Mass < 0.0) Mass = 0.0;
if (InternalEnergy < 0.0) InternalEnergy = 0.0;
}
public void Integrate() => Integrate(_dt);
}
}