using System.Collections.Generic; using FluidSim.Components; using FluidSim.Interfaces; namespace FluidSim.Core { public class Solver { private readonly List _volumes = new(); private readonly List _pipes = new(); private readonly List _connections = new(); public void AddVolume(Volume0D v) => _volumes.Add(v); public void AddPipe(Pipe1D p) => _pipes.Add(p); public void AddConnection(Connection c) => _connections.Add(c); public void Step() { // 1. Volumes publish state foreach (var v in _volumes) v.PushStateToPort(); // 2. Compute boundary fluxes (orifice model) foreach (var conn in _connections) { if (IsPipePort(conn.PortA) && IsVolumePort(conn.PortB)) ApplyOrifice(conn, conn.PortA, conn.PortB); else if (IsVolumePort(conn.PortA) && IsPipePort(conn.PortB)) ApplyOrifice(conn, conn.PortB, conn.PortA); else if (IsVolumePort(conn.PortA) && IsVolumePort(conn.PortB)) VolumeToVolume(conn); } // 3. Pipe simulation step foreach (var p in _pipes) p.Simulate(); // 4. Transfer pipe‑port data to volumes foreach (var conn in _connections) { if (IsPipePort(conn.PortA) && IsVolumePort(conn.PortB)) TransferPipeToVolume(conn.PortA, conn.PortB); else if (IsVolumePort(conn.PortA) && IsPipePort(conn.PortB)) TransferPipeToVolume(conn.PortB, conn.PortA); } // 5. Integrate volumes foreach (var v in _volumes) v.Integrate(); } bool IsVolumePort(Port p) => _volumes.Exists(v => v.Port == p); bool IsPipePort(Port p) => _pipes.Exists(pp => pp.PortA == p || pp.PortB == p); Pipe1D GetPipe(Port p) => _pipes.Find(pp => pp.PortA == p || pp.PortB == p); void ApplyOrifice(Connection conn, Port pipePort, Port volPort) { Pipe1D pipe = GetPipe(pipePort); if (pipe == null) return; bool isLeft = pipe.PortA == pipePort; double pP = isLeft ? pipe.GetLeftPressure() : pipe.GetRightPressure(); double rhoP = isLeft ? pipe.GetLeftDensity() : pipe.GetRightDensity(); double uP = isLeft ? pipe.GetCellVelocity(0) : pipe.GetCellVelocity(pipe.GetCellCount() - 1); double pV = volPort.Pressure; double rhoV = volPort.Density; double uV = 0.0; // volume has zero organized velocity OrificeBoundary.PipeVolumeFlux( pP, rhoP, uP, pV, rhoV, uV, conn, pipe.Area, isLeft, out double massFlux, out double momFlux, out double energyFlux); if (isLeft) pipe.SetLeftBoundaryFlux(massFlux, momFlux, energyFlux); else pipe.SetRightBoundaryFlux(massFlux, momFlux, energyFlux); } void VolumeToVolume(Connection conn) { double mdot = OrificeBoundary.MassFlow( conn.PortA.Pressure, conn.PortA.Density, conn.PortB.Pressure, conn.PortB.Density, conn); conn.PortA.MassFlowRate = -mdot; conn.PortB.MassFlowRate = mdot; if (mdot > 0) conn.PortB.SpecificEnthalpy = conn.PortA.SpecificEnthalpy; else if (mdot < 0) conn.PortA.SpecificEnthalpy = conn.PortB.SpecificEnthalpy; } void TransferPipeToVolume(Port pipePort, Port volPort) { double mdot = pipePort.MassFlowRate; // mdot > 0 → fluid enters pipe from volume // mdot < 0 → fluid leaves pipe and enters volume // Volume mass flow sign is opposite (positive into volume) volPort.MassFlowRate = -mdot; if (mdot < 0) // pipe → volume { // ★ pipePort.SpecificEnthalpy now contains TOTAL enthalpy volPort.SpecificEnthalpy = pipePort.SpecificEnthalpy; } // else: fluid goes volume → pipe → volume owns its own (static) enthalpy, // which is already correct. } } }