Files
FluidSim/Core/OpenEndLink.cs
2026-05-07 13:28:41 +02:00

104 lines
4.0 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using FluidSim.Components;
namespace FluidSim.Core
{
/// <summary>
/// Characteristic openend 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.
/// </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 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;
// ---- Subsonic branch (used for both outflow and inflow) ----
// Isentropic expansion to ambient pressure using pipe's entropy
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: if the isentropic velocity exceeds the speed of sound,
// the flow is supersonic and we extrapolate the interior state.
bool supersonic = IsLeftEnd
? (uInt <= -cInt) // left end: outflow is when u < -c
: (uInt >= cInt); // right end: outflow is when u > c
// Extra check: if the isentropic velocity is supersonic in the outflow direction,
// also treat as supersonic (this can happen when the interior pressure is very high).
if (!supersonic)
{
if (IsLeftEnd)
supersonic = uIso <= -cIso;
else
supersonic = uIso >= cIso;
}
if (supersonic)
{
// Supersonic outflow extrapolate interior
rhoGhost = rhoInt;
uGhost = uInt;
pGhost = pInt;
}
else
{
// Subsonic flow use the isentropic state
rhoGhost = rhoIso;
uGhost = uIso;
pGhost = pAmb;
}
// Apply ghost to pipe
if (IsLeftEnd)
Pipe.SetGhostLeft(rhoGhost, uGhost, pGhost);
else
Pipe.SetGhostRight(rhoGhost, uGhost, pGhost);
// 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, so out is -u
LastMassFlowRate = mdot;
LastFaceDensity = rhoGhost;
LastFaceVelocity = uGhost;
LastFacePressure = pGhost;
}
}
}