123 lines
4.6 KiB
C#
123 lines
4.6 KiB
C#
using System;
|
||
using FluidSim.Components;
|
||
|
||
namespace FluidSim.Core
|
||
{
|
||
/// <summary>
|
||
/// Characteristic open‑end boundary condition.
|
||
/// For subsonic outflow the outgoing Riemann invariant is conserved,
|
||
/// and the ghost pressure is set to the prescribed ambient value.
|
||
/// </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;
|
||
|
||
// Last resolved state (for audio / monitoring)
|
||
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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Compute the ghost state and mass flow for one sub‑step.
|
||
/// </summary>
|
||
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;
|
||
|
||
double rhoGhost, uGhost, pGhost;
|
||
double mdot;
|
||
|
||
if (IsLeftEnd)
|
||
{
|
||
// Left end: outgoing invariant is J- = u - 2c/(γ-1)
|
||
double J_minus = uInt - 2.0 * cInt / gm1;
|
||
|
||
if (uInt <= -cInt) // supersonic inflow (all info from outside)
|
||
{
|
||
// Simple reservoir model – use ambient density and temperature 300 K
|
||
rhoGhost = pAmb / (287.0 * 300.0);
|
||
uGhost = uInt; // keep interior velocity (should be supersonic inward)
|
||
pGhost = pAmb;
|
||
}
|
||
else if (uInt < 0) // subsonic inflow
|
||
{
|
||
double rhoAmb = pAmb / (287.0 * 300.0);
|
||
double cAmb = Math.Sqrt(gamma * pAmb / rhoAmb);
|
||
uGhost = J_minus + 2.0 * cAmb / gm1;
|
||
rhoGhost = rhoAmb;
|
||
pGhost = pAmb;
|
||
}
|
||
else // subsonic outflow (uInt >= 0)
|
||
{
|
||
double s = pInt / Math.Pow(rhoInt, gamma);
|
||
rhoGhost = Math.Pow(pAmb / s, 1.0 / gamma);
|
||
double cGhost = Math.Sqrt(gamma * pAmb / rhoGhost);
|
||
uGhost = J_minus + 2.0 * cGhost / gm1;
|
||
if (uGhost < 0) uGhost = 0;
|
||
pGhost = pAmb;
|
||
}
|
||
}
|
||
else // Right end
|
||
{
|
||
// Right end: outgoing invariant is J+ = u + 2c/(γ-1)
|
||
double J_plus = uInt + 2.0 * cInt / gm1;
|
||
|
||
if (uInt >= cInt) // supersonic outflow
|
||
{
|
||
rhoGhost = rhoInt;
|
||
uGhost = uInt;
|
||
pGhost = pInt;
|
||
}
|
||
else if (uInt >= 0) // subsonic outflow
|
||
{
|
||
double s = pInt / Math.Pow(rhoInt, gamma);
|
||
rhoGhost = Math.Pow(pAmb / s, 1.0 / gamma);
|
||
double cGhost = Math.Sqrt(gamma * pAmb / rhoGhost);
|
||
uGhost = J_plus - 2.0 * cGhost / gm1;
|
||
if (uGhost < 0) uGhost = 0;
|
||
pGhost = pAmb;
|
||
}
|
||
else // subsonic inflow (uInt < 0)
|
||
{
|
||
double rhoAmb = pAmb / (287.0 * 300.0);
|
||
double cAmb = Math.Sqrt(gamma * pAmb / rhoAmb);
|
||
uGhost = J_plus - 2.0 * cAmb / gm1;
|
||
rhoGhost = rhoAmb;
|
||
pGhost = pAmb;
|
||
}
|
||
}
|
||
|
||
// Apply ghost to pipe
|
||
if (IsLeftEnd)
|
||
Pipe.SetGhostLeft(rhoGhost, uGhost, pGhost);
|
||
else
|
||
Pipe.SetGhostRight(rhoGhost, uGhost, pGhost);
|
||
|
||
// Mass flow (positive = out of pipe)
|
||
double area = Pipe.Area;
|
||
mdot = rhoGhost * uGhost * area;
|
||
if (IsLeftEnd) mdot = -mdot; // positive u into pipe, so out of pipe is negative u
|
||
LastMassFlowRate = mdot;
|
||
LastFaceDensity = rhoGhost;
|
||
LastFaceVelocity = uGhost;
|
||
LastFacePressure = pGhost;
|
||
}
|
||
}
|
||
} |