using System; using System.Collections.Generic; using FluidSim.Interfaces; namespace FluidSim.Components { public class Volume0D : IComponent { public List Ports { get; } = new List(); 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; // ---------- Thermal relaxation to environment ---------- /// Rate of heat transfer to the surroundings (1/s). 0 = adiabatic. public float EnergyRelaxationRate { get; set; } = 10f; /// Temperature to relax toward (K). Default is room temperature. public float AmbientTemperature { get; set; } = 300f; 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(float initialVolume, float initialPressure, float initialTemperature, float gasConstant = 287f, float gamma = 1.4f) { GasConstant = gasConstant; Gamma = gamma; Volume = initialVolume; Dvdt = 0f; float rho0 = initialPressure / (GasConstant * initialTemperature); _airMass = rho0 * Volume; _exhaustMass = 0f; InternalEnergy = (initialPressure * Volume) / (Gamma - 1f); } public Port CreatePort() { var port = new Port { Owner = this }; port.Pressure = Pressure; port.Density = Density; port.Temperature = Temperature; port.SpecificEnthalpy = SpecificEnthalpy; port.AirFraction = AirFraction; Ports.Add(port); return port; } public void SetPressure(float pressure, float? temperature = null) { 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 * (1f - af); InternalEnergy = pressure * V / (Gamma - 1f); } public void UpdateState(float dt) { float totalMdotAir = 0f, totalMdotExhaust = 0f, totalEdot = 0f; foreach (var port in Ports) { float mdot = port.MassFlowRate; float af = mdot >= 0f ? port.AirFraction : AirFraction; totalMdotAir += mdot * af; totalMdotExhaust += mdot * (1f - af); totalEdot += mdot * port.SpecificEnthalpy; } float dAir = totalMdotAir * dt; float dExhaust = totalMdotExhaust * dt; float dE = totalEdot * dt - Pressure * Dvdt * dt; _airMass += dAir; _exhaustMass += dExhaust; InternalEnergy += dE; // ---- Thermal relaxation ---- if (EnergyRelaxationRate > 0f) { 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; } } 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); } 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 = afr; } } IReadOnlyList IComponent.Ports => Ports; } }