using System; using System.Collections.Generic; using FluidSim.Interfaces; namespace FluidSim.Components { public class Volume0D : IComponent { public List Ports { get; } = new List(); 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; public double AmbientPressure { get; set; } = 101325.0; // 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 Volume0D(double initialVolume, double initialPressure, double initialTemperature, double gasConstant = 287.0, double gamma = 1.4) { GasConstant = gasConstant; Gamma = gamma; Volume = initialVolume; Dvdt = 0.0; double rho0 = initialPressure / (GasConstant * initialTemperature); _airMass = rho0 * Volume; // starts with all air _exhaustMass = 0.0; InternalEnergy = (initialPressure * Volume) / (Gamma - 1.0); } 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(double pressure, double? 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; _airMass = totalMass * af; _exhaustMass = totalMass * (1.0 - af); InternalEnergy = pressure * V / (Gamma - 1.0); } public void UpdateState(double dt) { double totalMdotAir = 0.0; double totalMdotExhaust = 0.0; double totalEdot = 0.0; 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 totalMdotAir += mdot * af; totalMdotExhaust += mdot * (1.0 - af); totalEdot += mdot * port.SpecificEnthalpy; } double dAir = totalMdotAir * dt; double dExhaust = totalMdotExhaust * dt; double 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) { _airMass = 1e-9; _exhaustMass = 0.0; InternalEnergy = AmbientPressure * V / (Gamma - 1.0); } else if (InternalEnergy < 0.0) { InternalEnergy = AmbientPressure * V / (Gamma - 1.0); } if (_airMass < 0.0) _airMass = 0.0; if (_exhaustMass < 0.0) _exhaustMass = 0.0; double p = Pressure, rho = Density, T = Temperature, h = SpecificEnthalpy, afrac = AirFraction; foreach (var port in Ports) { port.Pressure = p; port.Density = rho; port.Temperature = T; port.SpecificEnthalpy = h; port.AirFraction = afrac; } } IReadOnlyList IComponent.Ports => Ports; } }