Files
FluidSim/Core/OrificeLink.cs

140 lines
5.3 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;
using FluidSim.Interfaces;
namespace FluidSim.Core
{
/// <summary>
/// Connects a port (volume or atmosphere) to one end of a pipe via an orifice.
/// The area can be dynamic (Func<double>).
/// </summary>
public class OrificeLink
{
public Port VolumePort { get; }
public Pipe1D Pipe { get; }
public bool IsPipeLeftEnd { get; }
public Func<double> AreaProvider { get; set; }
public double DischargeCoefficient { get; set; } = 0.62;
public double Gamma { get; set; } = 1.4;
public double GasConstant { get; set; } = 287.0;
// 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 OrificeLink(Port volumePort, Pipe1D pipe, bool isPipeLeftEnd, Func<double> areaProvider)
{
VolumePort = volumePort ?? throw new ArgumentNullException(nameof(volumePort));
Pipe = pipe ?? throw new ArgumentNullException(nameof(pipe));
IsPipeLeftEnd = isPipeLeftEnd;
AreaProvider = areaProvider ?? throw new ArgumentNullException(nameof(areaProvider));
}
/// <summary>
/// Resolve the coupling for one substep. Computes nozzle flow (isentropic)
/// and sets the pipe ghost cell and the port flow rates.
/// </summary>
public void Resolve(double dtSub)
{
double area = AreaProvider();
if (area < 1e-12)
{
SetClosedWall();
return;
}
// Retrieve volume state
double volP = VolumePort.Pressure;
double volRho = VolumePort.Density;
double volT = VolumePort.Temperature;
double volH = VolumePort.SpecificEnthalpy;
// Retrieve pipe interior state at the connected end
(double pipeRho, double pipeU, double pipeP) = IsPipeLeftEnd
? Pipe.GetInteriorStateLeft()
: Pipe.GetInteriorStateRight();
// Determine upstream/downstream: if volume pressure > pipe pressure, flow is out of volume (negative into volume).
bool flowOutOfVolume = volP > pipeP;
double pUp, rhoUp, TUp, pDown;
if (flowOutOfVolume)
{
pUp = volP; rhoUp = volRho; TUp = volT; pDown = pipeP;
}
else
{
// Pipe is upstream
pUp = pipeP; rhoUp = pipeRho; TUp = pipeP / (pipeRho * GasConstant); // temperature from pipe
pDown = volP;
}
// Compute isentropic nozzle flow
IsentropicOrifice.Compute(pUp, rhoUp, TUp, Gamma, GasConstant, pDown, area, DischargeCoefficient,
out double mdotUpstreamToDown, out double rhoFace, out double uFace, out double pFace);
// mdotUpstreamToDown is positive from upstream to downstream.
// Convert to mass flow into volume (positive mdot = into volume).
double mdotVolume;
if (flowOutOfVolume)
mdotVolume = -mdotUpstreamToDown; // out of volume is negative
else
mdotVolume = mdotUpstreamToDown; // into volume is positive
// Clamp mass flow to available mass in volume (if it is a Volume0D)
if (VolumePort.Owner is Volume0D vol)
{
double maxMdot = vol.Mass / dtSub;
if (mdotVolume > maxMdot) mdotVolume = maxMdot;
if (mdotVolume < -maxMdot) mdotVolume = -maxMdot;
}
// Apply ghost state to pipe
if (IsPipeLeftEnd)
Pipe.SetGhostLeft(rhoFace, uFace, pFace);
else
Pipe.SetGhostRight(rhoFace, uFace, pFace);
// Store results
LastMassFlowRate = mdotVolume;
LastFaceDensity = rhoFace;
LastFaceVelocity = uFace;
LastFacePressure = pFace;
// Set port flow rates for volume integration
VolumePort.MassFlowRate = mdotVolume;
if (mdotVolume >= 0)
{
// Inflow: enthalpy comes from upstream (pipe)
double pPipe = pipeP;
double rhoPipe = pipeRho;
VolumePort.SpecificEnthalpy = Gamma / (Gamma - 1.0) * pPipe / rhoPipe;
}
else
{
// Outflow: volume's own specific enthalpy
VolumePort.SpecificEnthalpy = volH;
}
}
private void SetClosedWall()
{
var (rInt, uInt, pInt) = IsPipeLeftEnd
? Pipe.GetInteriorStateLeft()
: Pipe.GetInteriorStateRight();
if (IsPipeLeftEnd)
Pipe.SetGhostLeft(rInt, -uInt, pInt);
else
Pipe.SetGhostRight(rInt, -uInt, pInt);
LastMassFlowRate = 0.0;
LastFaceDensity = rInt;
LastFaceVelocity = 0.0;
LastFacePressure = pInt;
VolumePort.MassFlowRate = 0.0;
// Keep specific enthalpy as is (not used)
}
}
}