stable
This commit is contained in:
@@ -165,7 +165,7 @@ namespace FluidSim.Core
|
||||
continue;
|
||||
}
|
||||
|
||||
// ---- Preliminary isentropic solution (used for face pressure if inertance is on) ----
|
||||
// ---- Preliminary isentropic solution ----
|
||||
float mdotEst, rhoFaceEst, uFaceEst, pFaceEst;
|
||||
if (volP >= pipeP)
|
||||
{
|
||||
@@ -179,7 +179,7 @@ namespace FluidSim.Core
|
||||
mdotEst = -mdotEst;
|
||||
}
|
||||
|
||||
// ---- Compute ghost state ----
|
||||
// ---- Compute final mass flow with limiters ----
|
||||
float mdotFinal, rhoFace, uFace, pFace, airFracGhost;
|
||||
|
||||
if (d.UseInertance)
|
||||
@@ -189,11 +189,10 @@ namespace FluidSim.Core
|
||||
float dp = volP - pipeP;
|
||||
float Rlin = d.LossCoefficient;
|
||||
|
||||
// Forward Euler
|
||||
float dmdot_dt = (dp - Rlin * d.CurrentMdot) / MathF.Max(inertance, 1e-12f);
|
||||
float mdotNew = d.CurrentMdot + dmdot_dt * dt;
|
||||
|
||||
// ---------- Symmetric flow limiters (existing) ----------
|
||||
// Limit outflow from volume (if volume owner is Volume0D)
|
||||
if (d.VolumePort.Owner is Volume0D vol0)
|
||||
{
|
||||
float maxOut = vol0.Mass / dt;
|
||||
@@ -201,15 +200,19 @@ namespace FluidSim.Core
|
||||
if (mdotNew < -maxOut) mdotNew = -maxOut;
|
||||
}
|
||||
|
||||
int adjCell = d.IsLeftEnd ? _pipeSystem.GetPipeStart(d.PipeIndex)
|
||||
: _pipeSystem.GetPipeEnd(d.PipeIndex) - 1;
|
||||
float pipeRhoAdj = _pipeSystem.GetCellDensity(adjCell);
|
||||
float pipeDxAdj = _pipeSystem.GetCellDx(adjCell);
|
||||
float pipeCellMass = pipeRhoAdj * area * pipeDxAdj;
|
||||
float maxFromPipe = pipeCellMass / dt;
|
||||
if (mdotNew < -maxFromPipe) mdotNew = -maxFromPipe;
|
||||
// Limit inflow from pipe – pipe cell cannot be emptied in one step
|
||||
{
|
||||
int adjCell = d.IsLeftEnd ? _pipeSystem.GetPipeStart(d.PipeIndex)
|
||||
: _pipeSystem.GetPipeEnd(d.PipeIndex) - 1;
|
||||
float pipeRhoAdj = _pipeSystem.GetCellDensity(adjCell);
|
||||
float pipeAreaCell = _pipeSystem.GetCellArea(adjCell); // true cell area, not orifice
|
||||
float pipeDxAdj = _pipeSystem.GetCellDx(adjCell);
|
||||
float pipeCellMass = pipeRhoAdj * pipeAreaCell * pipeDxAdj;
|
||||
float maxFromPipe = pipeCellMass / dt;
|
||||
if (mdotNew < -maxFromPipe) mdotNew = -maxFromPipe;
|
||||
}
|
||||
|
||||
// ---------- Velocity clamp to Mach 0.9 ----------
|
||||
// Velocity clamp to Mach 0.9
|
||||
float rhoFacePrelim = mdotNew >= 0 ? volRho : pipeRho;
|
||||
float uFacePrelim = MathF.Abs(mdotNew) / MathF.Max(rhoFacePrelim * area, 1e-12f);
|
||||
float cUp = mdotNew >= 0 ? MathF.Sqrt(Gamma * Rgas * volT) : MathF.Sqrt(Gamma * Rgas * pipeT);
|
||||
@@ -220,46 +223,64 @@ namespace FluidSim.Core
|
||||
mdotNew = rhoFacePrelim * uFacePrelim * area * (mdotNew >= 0 ? 1f : -1f);
|
||||
}
|
||||
|
||||
// NaN safety
|
||||
if (float.IsNaN(mdotNew)) mdotNew = 0f;
|
||||
|
||||
d.CurrentMdot = mdotNew;
|
||||
mdotFinal = mdotNew;
|
||||
|
||||
// Ghost state
|
||||
rhoFace = mdotFinal >= 0 ? volRho : pipeRho;
|
||||
pFace = pFaceEst;
|
||||
uFace = MathF.Abs(mdotFinal) / MathF.Max(rhoFace * area, 1e-12f);
|
||||
}
|
||||
else
|
||||
{
|
||||
// ---- Standard quasi‑steady orifice ----
|
||||
// Standard quasi‑steady orifice
|
||||
mdotFinal = mdotEst;
|
||||
rhoFace = rhoFaceEst;
|
||||
uFace = uFaceEst;
|
||||
pFace = pFaceEst;
|
||||
|
||||
// Limit outflow from cavity
|
||||
// Limit outflow from volume (if Volume0D)
|
||||
if (d.VolumePort.Owner is Volume0D vol0)
|
||||
{
|
||||
float maxOut = vol0.Mass / dt;
|
||||
if (mdotFinal > maxOut) mdotFinal = maxOut;
|
||||
}
|
||||
|
||||
// ***** CRITICAL: Limit inflow from pipe – pipe cell cannot be drained *****
|
||||
if (mdotFinal < 0)
|
||||
{
|
||||
int adjCell = d.IsLeftEnd ? _pipeSystem.GetPipeStart(d.PipeIndex)
|
||||
: _pipeSystem.GetPipeEnd(d.PipeIndex) - 1;
|
||||
float pipeRhoAdj = _pipeSystem.GetCellDensity(adjCell);
|
||||
float pipeAreaCell = _pipeSystem.GetCellArea(adjCell);
|
||||
float pipeDxAdj = _pipeSystem.GetCellDx(adjCell);
|
||||
float pipeCellMass = pipeRhoAdj * pipeAreaCell * pipeDxAdj;
|
||||
float maxFromPipe = pipeCellMass / dt;
|
||||
if (mdotFinal < -maxFromPipe)
|
||||
mdotFinal = -maxFromPipe;
|
||||
}
|
||||
|
||||
d.CurrentMdot = mdotFinal;
|
||||
|
||||
// Limit outflow from cylinder into pipe (positive mdot = volume → pipe)
|
||||
if (mdotFinal > 0f && d.VolumePort?.Owner is Cylinder cyl)
|
||||
{
|
||||
float maxOut = cyl.Mass / dt;
|
||||
if (mdotFinal > maxOut)
|
||||
mdotFinal = maxOut;
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Determine air fraction for ghost ----
|
||||
// ---- Air fraction for ghost ----
|
||||
if (mdotFinal >= 0)
|
||||
{
|
||||
airFracGhost = volAF;
|
||||
}
|
||||
else
|
||||
{
|
||||
airFracGhost = pipeAF;
|
||||
if (d.VolumePort != null) d.VolumePort.AirFraction = pipeAF;
|
||||
}
|
||||
|
||||
// ---- Apply sign convention for velocity ----
|
||||
// ---- Sign convention for velocity ----
|
||||
if (mdotFinal >= 0 && d.IsLeftEnd) uFace = +uFace;
|
||||
else if (mdotFinal >= 0 && !d.IsLeftEnd) uFace = -uFace;
|
||||
else if (mdotFinal < 0 && d.IsLeftEnd) uFace = -uFace;
|
||||
@@ -271,18 +292,17 @@ namespace FluidSim.Core
|
||||
else
|
||||
_pipeSystem.SetGhostRight(d.PipeIndex, rhoFace, uFace, pFace, airFracGhost);
|
||||
|
||||
// ---- Update volume port (mass flow: positive into volume) ----
|
||||
// ---- Update volume port ----
|
||||
if (d.VolumePort != null)
|
||||
{
|
||||
d.VolumePort.MassFlowRate = -mdotFinal;
|
||||
|
||||
// Set enthalpy of the stream entering the volume
|
||||
if (-mdotFinal >= 0) // mass flowing into the volume (out of pipe)
|
||||
if (-mdotFinal >= 0) // mass entering volume (out of pipe)
|
||||
{
|
||||
float pipeH = GammaOverGm1 * pipeP / MathF.Max(pipeRho, 1e-12f);
|
||||
d.VolumePort.SpecificEnthalpy = pipeH;
|
||||
}
|
||||
else // mass flowing out of the volume (into pipe)
|
||||
else // mass leaving volume (into pipe)
|
||||
{
|
||||
d.VolumePort.SpecificEnthalpy = volH;
|
||||
}
|
||||
@@ -309,6 +329,7 @@ namespace FluidSim.Core
|
||||
float cInt = MathF.Sqrt(gamma * pInt / MathF.Max(rhoInt, 1e-12f));
|
||||
float pAmb = d.AmbientPressure;
|
||||
|
||||
// Characteristic solution (isentropic expansion to ambient)
|
||||
float Jplus = uInt + 2f * cInt / gm1;
|
||||
float Jminus = uInt - 2f * cInt / gm1;
|
||||
float s = pInt / MathF.Pow(rhoInt, gamma);
|
||||
@@ -318,9 +339,14 @@ namespace FluidSim.Core
|
||||
? (Jminus + 2f * cIso / gm1)
|
||||
: (Jplus - 2f * cIso / gm1);
|
||||
|
||||
// Supersonic check
|
||||
bool supersonic = d.IsLeftEnd ? (uInt <= -cInt) : (uInt >= cInt);
|
||||
float rhoGhost, uGhost, pGhost, afGhost;
|
||||
if (!supersonic)
|
||||
{
|
||||
supersonic = d.IsLeftEnd ? (uIso <= -cIso) : (uIso >= cIso);
|
||||
}
|
||||
|
||||
float rhoGhost, uGhost, pGhost, afGhost;
|
||||
if (supersonic)
|
||||
{
|
||||
rhoGhost = rhoInt; uGhost = uInt; pGhost = pInt; afGhost = afInt;
|
||||
@@ -332,15 +358,45 @@ namespace FluidSim.Core
|
||||
afGhost = inflow ? 1f : afInt;
|
||||
}
|
||||
|
||||
// ------- Mass flow limiter -------
|
||||
int adjCell = d.IsLeftEnd
|
||||
? _pipeSystem.GetPipeStart(d.PipeIndex)
|
||||
: _pipeSystem.GetPipeEnd(d.PipeIndex) - 1;
|
||||
float pipeRhoAdj = _pipeSystem.GetCellDensity(adjCell);
|
||||
float pipeAreaCell = _pipeSystem.GetCellArea(adjCell);
|
||||
float pipeDxAdj = _pipeSystem.GetCellDx(adjCell);
|
||||
float cellMass = pipeRhoAdj * pipeAreaCell * pipeDxAdj;
|
||||
|
||||
float area = d.PipeArea;
|
||||
float mdotRaw = rhoGhost * uGhost * area; // positive out of pipe
|
||||
if (d.IsLeftEnd) mdotRaw = -mdotRaw; // now positive = out of pipe
|
||||
|
||||
// Outflow limit
|
||||
if (mdotRaw > 0 && mdotRaw * dt > cellMass)
|
||||
{
|
||||
mdotRaw = cellMass / dt;
|
||||
}
|
||||
// Inflow limit (allow up to 10× cell mass to avoid starving the pipe)
|
||||
else if (mdotRaw < 0 && -mdotRaw * dt > 10f * cellMass)
|
||||
{
|
||||
mdotRaw = -10f * cellMass / dt;
|
||||
}
|
||||
|
||||
// Recompute uGhost from the limited mdot, keeping rhoGhost, pGhost
|
||||
float mdotMag = MathF.Abs(mdotRaw);
|
||||
uGhost = mdotMag / MathF.Max(rhoGhost * area, 1e-12f);
|
||||
if (d.IsLeftEnd)
|
||||
uGhost = (mdotRaw >= 0f) ? -uGhost : uGhost;
|
||||
else
|
||||
uGhost = (mdotRaw >= 0f) ? uGhost : -uGhost;
|
||||
|
||||
// Apply ghost
|
||||
if (d.IsLeftEnd)
|
||||
_pipeSystem.SetGhostLeft(d.PipeIndex, rhoGhost, uGhost, pGhost, afGhost);
|
||||
else
|
||||
_pipeSystem.SetGhostRight(d.PipeIndex, rhoGhost, uGhost, pGhost, afGhost);
|
||||
|
||||
float area = d.PipeArea;
|
||||
float mdot = rhoGhost * uGhost * area;
|
||||
if (d.IsLeftEnd) mdot = -mdot;
|
||||
d.LastMassFlowRate = mdot;
|
||||
d.LastMassFlowRate = mdotRaw;
|
||||
d.LastFacePressure = pGhost;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user