Fixed orifice with inertia, automatic R value
This commit is contained in:
@@ -25,7 +25,8 @@ namespace FluidSim.Core
|
||||
public float EffectiveLength;
|
||||
public float CurrentMdot; // kg/s, positive = volume → pipe
|
||||
|
||||
// --- Loss coefficient (linear resistance) ---
|
||||
// --- Loss coefficient (linear resistance) – inertance only ---
|
||||
// If 0 when UseInertance is true, a stable default is auto‑computed at runtime.
|
||||
public float LossCoefficient; // N·s/m⁵ or kg/(m⁴·s)
|
||||
}
|
||||
|
||||
@@ -57,9 +58,10 @@ namespace FluidSim.Core
|
||||
public int OpenEndCount { get; private set; }
|
||||
|
||||
// ---------- Add orifice (no inertance) ----------
|
||||
// Simple isentropic nozzle – no built‑in loss. For dissipation use pipe damping
|
||||
// or the inertance model if you need a damped resonator.
|
||||
public void AddOrifice(Port volumePort, int pipeIndex, bool isLeftEnd,
|
||||
int areaIndex, float dischargeCoeff = 1f,
|
||||
float lossCoefficient = 0f)
|
||||
int areaIndex, float dischargeCoeff = 1f)
|
||||
{
|
||||
_orifices[OrificeCount] = new OrificeDesc
|
||||
{
|
||||
@@ -71,22 +73,24 @@ namespace FluidSim.Core
|
||||
UseInertance = false,
|
||||
EffectiveLength = 0f,
|
||||
CurrentMdot = 0f,
|
||||
LossCoefficient = lossCoefficient
|
||||
LossCoefficient = 0f
|
||||
};
|
||||
OrificeCount++;
|
||||
}
|
||||
|
||||
// ---------- Add orifice with inertance ----------
|
||||
// effectiveLength – length of the inertial slug (m), typically the physical neck length.
|
||||
// lossCoefficient – linear resistance (N·s/m⁵). If 0 (or omitted) an automatic stable
|
||||
// value will be computed from the pipe's characteristic impedance.
|
||||
public void AddOrificeWithInertance(Port volumePort, int pipeIndex, bool isLeftEnd,
|
||||
int areaIndex, float dischargeCoeff,
|
||||
float effectiveLength, float lossCoefficient = 0f)
|
||||
{
|
||||
// Reuse the base AddOrifice and then override fields
|
||||
AddOrifice(volumePort, pipeIndex, isLeftEnd, areaIndex, dischargeCoeff, lossCoefficient);
|
||||
AddOrifice(volumePort, pipeIndex, isLeftEnd, areaIndex, dischargeCoeff);
|
||||
ref var d = ref _orifices[OrificeCount - 1];
|
||||
d.UseInertance = true;
|
||||
d.EffectiveLength = effectiveLength;
|
||||
d.LossCoefficient = lossCoefficient; // store the linear resistance
|
||||
d.LossCoefficient = lossCoefficient;
|
||||
}
|
||||
|
||||
public void AddOpenEnd(int pipeIndex, bool isLeftEnd,
|
||||
@@ -146,7 +150,7 @@ namespace FluidSim.Core
|
||||
? _pipeSystem.GetInteriorAirFractionLeft(d.PipeIndex)
|
||||
: _pipeSystem.GetInteriorAirFractionRight(d.PipeIndex);
|
||||
|
||||
// ---- Handle closed orifice (area ≈ 0) as a wall ----
|
||||
// ---- Handle closed orifice as a wall ----
|
||||
if (area < 1e-12f || d.VolumePort == null)
|
||||
{
|
||||
var (rInt, uInt, pInt) = d.IsLeftEnd
|
||||
@@ -165,7 +169,7 @@ namespace FluidSim.Core
|
||||
continue;
|
||||
}
|
||||
|
||||
// ---- Preliminary isentropic solution (used for face pressure if inertance is on) ----
|
||||
// ---- Preliminary isentropic solution (for reference) ----
|
||||
float mdotEst, rhoFaceEst, uFaceEst, pFaceEst;
|
||||
if (volP >= pipeP)
|
||||
{
|
||||
@@ -184,16 +188,26 @@ namespace FluidSim.Core
|
||||
|
||||
if (d.UseInertance)
|
||||
{
|
||||
// ---- Inertance ODE with (possibly automatic) linear loss ----
|
||||
float rhoUp = d.CurrentMdot >= 0 ? volRho : pipeRho;
|
||||
float inertance = rhoUp * d.EffectiveLength / MathF.Max(area, 1e-12f);
|
||||
float dp = volP - pipeP;
|
||||
float Rlin = d.LossCoefficient;
|
||||
|
||||
// Forward Euler
|
||||
// If loss coefficient not provided, use a tiny fraction of the pipe's characteristic impedance
|
||||
float Rlin = d.LossCoefficient;
|
||||
if (Rlin <= 0f)
|
||||
{
|
||||
// Auto‑sized linear drag: 0.5 % of Z_char
|
||||
float rhoRef = d.CurrentMdot >= 0 ? volRho : pipeRho;
|
||||
float cRef = d.CurrentMdot >= 0 ? MathF.Sqrt(Gamma * Rgas * volT) : MathF.Sqrt(Gamma * Rgas * pipeT);
|
||||
float Z_char = rhoRef * cRef / MathF.Max(area, 1e-12f);
|
||||
Rlin = 0.005f * Z_char;
|
||||
}
|
||||
|
||||
float dmdot_dt = (dp - Rlin * d.CurrentMdot) / MathF.Max(inertance, 1e-12f);
|
||||
float mdotNew = d.CurrentMdot + dmdot_dt * dt;
|
||||
|
||||
// ---------- Symmetric flow limiters (existing) ----------
|
||||
// Symmetric flow limiters
|
||||
if (d.VolumePort.Owner is Volume0D vol0)
|
||||
{
|
||||
float maxOut = vol0.Mass / dt;
|
||||
@@ -209,7 +223,7 @@ namespace FluidSim.Core
|
||||
float maxFromPipe = pipeCellMass / dt;
|
||||
if (mdotNew < -maxFromPipe) mdotNew = -maxFromPipe;
|
||||
|
||||
// ---------- Velocity clamp to Mach 0.9 ----------
|
||||
// Velocity clamp 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,20 +234,18 @@ 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 (purely isentropic) ----
|
||||
mdotFinal = mdotEst;
|
||||
rhoFace = rhoFaceEst;
|
||||
uFace = uFaceEst;
|
||||
@@ -245,6 +257,17 @@ namespace FluidSim.Core
|
||||
float maxOut = vol0.Mass / dt;
|
||||
if (mdotFinal > maxOut) mdotFinal = maxOut;
|
||||
}
|
||||
|
||||
// Safety velocity clamp (Mach 0.9)
|
||||
float cLocal = mdotFinal >= 0 ? MathF.Sqrt(Gamma * Rgas * volT) : MathF.Sqrt(Gamma * Rgas * pipeT);
|
||||
float maxULocal = 0.9f * cLocal;
|
||||
float uCheck = MathF.Abs(mdotFinal) / MathF.Max(rhoFace * area, 1e-12f);
|
||||
if (uCheck > maxULocal)
|
||||
{
|
||||
uFace = maxULocal;
|
||||
mdotFinal = rhoFace * uFace * area * (mdotFinal >= 0 ? 1f : -1f);
|
||||
}
|
||||
|
||||
d.CurrentMdot = mdotFinal;
|
||||
}
|
||||
|
||||
@@ -271,18 +294,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 flowing into the volume
|
||||
{
|
||||
float pipeH = GammaOverGm1 * pipeP / MathF.Max(pipeRho, 1e-12f);
|
||||
d.VolumePort.SpecificEnthalpy = pipeH;
|
||||
}
|
||||
else // mass flowing out of the volume (into pipe)
|
||||
else // mass flowing out of the volume
|
||||
{
|
||||
d.VolumePort.SpecificEnthalpy = volH;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user