Helmholtz testing (no decay bug)

This commit is contained in:
max
2026-05-09 01:44:35 +02:00
parent 9c9e23147a
commit 77ef4753a3
23 changed files with 1811 additions and 2118 deletions

View File

@@ -8,36 +8,40 @@ namespace FluidSim.Components
{
public List<Port> Ports { get; } = new List<Port>();
private double _airMass;
private double _exhaustMass;
public double InternalEnergy { get; set; }
public double Volume { get; set; }
public double Dvdt { get; set; }
public double Gamma { get; set; } = 1.4;
public double GasConstant { get; set; } = 287.0;
private float _airMass;
private float _exhaustMass;
public float InternalEnergy;
public float Volume;
public float Dvdt;
public float Gamma { get; set; } = 1.4f;
public float GasConstant { get; set; } = 287f;
public float AmbientPressure { get; set; } = 101325f;
public double AmbientPressure { get; set; } = 101325.0;
// ---------- Thermal relaxation to environment ----------
/// <summary>Rate of heat transfer to the surroundings (1/s). 0 = adiabatic.</summary>
public float EnergyRelaxationRate { get; set; } = 10f;
/// <summary>Temperature to relax toward (K). Default is room temperature.</summary>
public float AmbientTemperature { get; set; } = 300f;
// Derived quantities
public double Mass => _airMass + _exhaustMass;
public double AirFraction => _airMass / Math.Max(Mass, 1e-12);
public double Density => Mass / Math.Max(Volume, 1e-12);
public double Pressure => (Gamma - 1.0) * InternalEnergy / Math.Max(Volume, 1e-12);
public double Temperature => Pressure / Math.Max(Density * GasConstant, 1e-12);
public double SpecificEnthalpy => Gamma / (Gamma - 1.0) * Pressure / Math.Max(Density, 1e-12);
public float Mass => _airMass + _exhaustMass;
public float AirFraction => _airMass / MathF.Max(Mass, 1e-12f);
public float Density => Mass / MathF.Max(Volume, 1e-12f);
public float Pressure => (Gamma - 1f) * InternalEnergy / MathF.Max(Volume, 1e-12f);
public float Temperature => Pressure / MathF.Max(Density * GasConstant, 1e-12f);
public float SpecificEnthalpy => Gamma / (Gamma - 1f) * Pressure / MathF.Max(Density, 1e-12f);
public Volume0D(double initialVolume, double initialPressure,
double initialTemperature, double gasConstant = 287.0, double gamma = 1.4)
public Volume0D(float initialVolume, float initialPressure,
float initialTemperature, float gasConstant = 287f, float gamma = 1.4f)
{
GasConstant = gasConstant;
Gamma = gamma;
Volume = initialVolume;
Dvdt = 0.0;
Dvdt = 0f;
double rho0 = initialPressure / (GasConstant * initialTemperature);
_airMass = rho0 * Volume; // starts with all air
_exhaustMass = 0.0;
InternalEnergy = (initialPressure * Volume) / (Gamma - 1.0);
float rho0 = initialPressure / (GasConstant * initialTemperature);
_airMass = rho0 * Volume;
_exhaustMass = 0f;
InternalEnergy = (initialPressure * Volume) / (Gamma - 1f);
}
public Port CreatePort()
@@ -52,66 +56,75 @@ namespace FluidSim.Components
return port;
}
public void SetPressure(double pressure, double? temperature = null)
public void SetPressure(float pressure, float? temperature = null)
{
double V = Math.Max(Volume, 1e-12);
double T = temperature ?? Temperature;
double rho = pressure / (GasConstant * T);
double totalMass = rho * V;
// Keep current air fraction when setting pressure?
double af = AirFraction;
float V = MathF.Max(Volume, 1e-12f);
float T = temperature ?? Temperature;
float rho = pressure / (GasConstant * T);
float totalMass = rho * V;
float af = AirFraction;
_airMass = totalMass * af;
_exhaustMass = totalMass * (1.0 - af);
InternalEnergy = pressure * V / (Gamma - 1.0);
_exhaustMass = totalMass * (1f - af);
InternalEnergy = pressure * V / (Gamma - 1f);
}
public void UpdateState(double dt)
public void UpdateState(float dt)
{
double totalMdotAir = 0.0;
double totalMdotExhaust = 0.0;
double totalEdot = 0.0;
float totalMdotAir = 0f, totalMdotExhaust = 0f, totalEdot = 0f;
foreach (var port in Ports)
{
double mdot = port.MassFlowRate; // positive INTO volume
double af = mdot >= 0 ? port.AirFraction : AirFraction; // inflow: use port's fraction; outflow: well-mixed
float mdot = port.MassFlowRate;
float af = mdot >= 0f ? port.AirFraction : AirFraction;
totalMdotAir += mdot * af;
totalMdotExhaust += mdot * (1.0 - af);
totalMdotExhaust += mdot * (1f - af);
totalEdot += mdot * port.SpecificEnthalpy;
}
double dAir = totalMdotAir * dt;
double dExhaust = totalMdotExhaust * dt;
double dE = totalEdot * dt - Pressure * Dvdt * dt;
float dAir = totalMdotAir * dt;
float dExhaust = totalMdotExhaust * dt;
float dE = totalEdot * dt - Pressure * Dvdt * dt;
_airMass += dAir;
_exhaustMass += dExhaust;
InternalEnergy += dE;
double V = Math.Max(Volume, 1e-12);
double totalMass = _airMass + _exhaustMass;
if (totalMass < 1e-9)
// ---- Thermal relaxation ----
if (EnergyRelaxationRate > 0f)
{
_airMass = 1e-9;
_exhaustMass = 0.0;
InternalEnergy = AmbientPressure * V / (Gamma - 1.0);
}
else if (InternalEnergy < 0.0)
{
InternalEnergy = AmbientPressure * V / (Gamma - 1.0);
float currentMass = Mass;
if (currentMass > 1e-12f)
{
// Target internal energy: current mass at ambient temperature
float targetE = currentMass * GasConstant * AmbientTemperature / (Gamma - 1f);
float relaxFactor = MathF.Exp(-EnergyRelaxationRate * dt);
InternalEnergy = targetE + (InternalEnergy - targetE) * relaxFactor;
}
}
if (_airMass < 0.0) _airMass = 0.0;
if (_exhaustMass < 0.0) _exhaustMass = 0.0;
float V = MathF.Max(Volume, 1e-12f);
float totalMass = _airMass + _exhaustMass;
if (totalMass < 1e-9f)
{
_airMass = 1e-9f;
_exhaustMass = 0f;
InternalEnergy = AmbientPressure * V / (Gamma - 1f);
}
else if (InternalEnergy < 0f)
{
InternalEnergy = AmbientPressure * V / (Gamma - 1f);
}
double p = Pressure, rho = Density, T = Temperature, h = SpecificEnthalpy, afrac = AirFraction;
if (_airMass < 0f) _airMass = 0f;
if (_exhaustMass < 0f) _exhaustMass = 0f;
float p = Pressure, rho = Density, T = Temperature, h = SpecificEnthalpy, afr = AirFraction;
foreach (var port in Ports)
{
port.Pressure = p;
port.Density = rho;
port.Temperature = T;
port.SpecificEnthalpy = h;
port.AirFraction = afrac;
port.AirFraction = afr;
}
}