120 lines
4.7 KiB
C#
120 lines
4.7 KiB
C#
using System;
|
||
using FluidSim.Components;
|
||
|
||
namespace FluidSim.Core
|
||
{
|
||
/// <summary>
|
||
/// Characteristic open‑end boundary condition after Jones (1978).
|
||
/// For all subsonic flow (outflow and inflow), the ghost state is derived
|
||
/// from the isentropic expansion to ambient pressure, using the pipe's entropy,
|
||
/// and the outgoing Riemann invariant. This avoids a density jump at flow reversal.
|
||
/// Supersonic outflow extrapolates the interior state.
|
||
/// Now includes air fraction tracking: incoming air is fresh (AF=1), outgoing uses interior pipe AF.
|
||
/// </summary>
|
||
public class OpenEndLink
|
||
{
|
||
public Pipe1D Pipe { get; }
|
||
public bool IsLeftEnd { get; }
|
||
public double AmbientPressure { get; set; } = 101325.0;
|
||
public double Gamma { get; set; } = 1.4;
|
||
|
||
public double LastMassFlowRate { get; private set; }
|
||
public double LastFaceDensity { get; private set; }
|
||
public double LastFaceVelocity { get; private set; }
|
||
public double LastFacePressure { get; private set; }
|
||
|
||
public OpenEndLink(Pipe1D pipe, bool isLeftEnd)
|
||
{
|
||
Pipe = pipe ?? throw new ArgumentNullException(nameof(pipe));
|
||
IsLeftEnd = isLeftEnd;
|
||
}
|
||
|
||
public void Resolve(double dtSub)
|
||
{
|
||
(double rhoInt, double uInt, double pInt) = IsLeftEnd
|
||
? Pipe.GetInteriorStateLeft()
|
||
: Pipe.GetInteriorStateRight();
|
||
|
||
double airFracInt = IsLeftEnd
|
||
? Pipe.GetInteriorAirFractionLeft()
|
||
: Pipe.GetInteriorAirFractionRight();
|
||
|
||
double gamma = Gamma;
|
||
double gm1 = gamma - 1.0;
|
||
double cInt = Math.Sqrt(gamma * pInt / Math.Max(rhoInt, 1e-12));
|
||
double pAmb = AmbientPressure;
|
||
|
||
// Riemann invariants
|
||
double J_plus = uInt + 2.0 * cInt / gm1;
|
||
double J_minus = uInt - 2.0 * cInt / gm1;
|
||
|
||
double rhoGhost, uGhost, pGhost, airFracGhost;
|
||
|
||
// ---- Subsonic branch (used for both outflow and inflow) ----
|
||
double s = pInt / Math.Pow(rhoInt, gamma); // entropy constant
|
||
double rhoIso = Math.Pow(pAmb / s, 1.0 / gamma);
|
||
double cIso = Math.Sqrt(gamma * pAmb / Math.Max(rhoIso, 1e-12));
|
||
double uIso = IsLeftEnd
|
||
? (J_minus + 2.0 * cIso / gm1)
|
||
: (J_plus - 2.0 * cIso / gm1);
|
||
|
||
// Check for supersonic outflow
|
||
bool supersonic = IsLeftEnd
|
||
? (uInt <= -cInt)
|
||
: (uInt >= cInt);
|
||
|
||
if (!supersonic)
|
||
{
|
||
if (IsLeftEnd)
|
||
supersonic = uIso <= -cIso;
|
||
else
|
||
supersonic = uIso >= cIso;
|
||
}
|
||
|
||
if (supersonic)
|
||
{
|
||
// Supersonic outflow – extrapolate interior
|
||
rhoGhost = rhoInt;
|
||
uGhost = uInt;
|
||
pGhost = pInt;
|
||
airFracGhost = airFracInt; // whatever is leaving
|
||
}
|
||
else
|
||
{
|
||
// Subsonic flow – use isentropic state to ambient
|
||
rhoGhost = rhoIso;
|
||
uGhost = uIso;
|
||
pGhost = pAmb;
|
||
|
||
// Determine if inflow or outflow
|
||
bool isInflow = IsLeftEnd ? (uIso >= 0) : (uIso <= 0); // positive u means into pipe from left end? Wait: left end: u>0 means flow to the right, into pipe. Right end: u>0 means flow to the right, out of pipe. Let's use mass flow sign later.
|
||
// More straightforward: if using the isentropic state, the ghost velocity direction indicates flow. For inflow (ambient to pipe), airFraction = 1.0; for outflow, airFraction = interior's AF.
|
||
if ((IsLeftEnd && uIso >= 0) || (!IsLeftEnd && uIso <= 0))
|
||
{
|
||
// Inflow (ambient enters pipe)
|
||
airFracGhost = 1.0;
|
||
}
|
||
else
|
||
{
|
||
// Outflow (pipe exits to ambient)
|
||
airFracGhost = airFracInt;
|
||
}
|
||
}
|
||
|
||
// Apply ghost to pipe
|
||
if (IsLeftEnd)
|
||
Pipe.SetGhostLeft(rhoGhost, uGhost, pGhost, airFracGhost);
|
||
else
|
||
Pipe.SetGhostRight(rhoGhost, uGhost, pGhost, airFracGhost);
|
||
|
||
// Mass flow out of the pipe (positive = leaving)
|
||
double area = Pipe.Area;
|
||
double mdot = rhoGhost * uGhost * area;
|
||
if (IsLeftEnd) mdot = -mdot; // left end: positive u is into pipe, outward flow is -u
|
||
LastMassFlowRate = mdot;
|
||
LastFaceDensity = rhoGhost;
|
||
LastFaceVelocity = uGhost;
|
||
LastFacePressure = pGhost;
|
||
}
|
||
}
|
||
} |