using System; 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(); private double _dt; 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 SetTimeStep(double dt) => _dt = dt; /// /// Set boundary type for a pipe end. isA = true for port A (left), false for port B (right). /// public void SetPipeBoundary(Pipe1D pipe, bool isA, BoundaryType type, double ambientPressure = 101325.0) { if (isA) { pipe.SetABoundaryType(type); if (type == BoundaryType.OpenEnd) pipe.SetAAmbientPressure(ambientPressure); } else { pipe.SetBBoundaryType(type); if (type == BoundaryType.OpenEnd) pipe.SetBAmbientPressure(ambientPressure); } } public float Step() { // 1. Volumes publish state foreach (var v in _volumes) v.PushStateToPort(); // 2. Set volume BCs for volume‑coupled ends foreach (var conn in _connections) { if (IsPipePort(conn.PortA) && IsVolumePort(conn.PortB)) { var pipe = GetPipe(conn.PortA); bool isA = pipe.PortA == conn.PortA; if ((isA && pipe.ABCType == BoundaryType.VolumeCoupling) || (!isA && pipe.BBCType == BoundaryType.VolumeCoupling)) SetVolumeBC(conn.PortA, conn.PortB); } else if (IsVolumePort(conn.PortA) && IsPipePort(conn.PortB)) { var pipe = GetPipe(conn.PortB); bool isA = pipe.PortB == conn.PortB; if ((isA && pipe.ABCType == BoundaryType.VolumeCoupling) || (!isA && pipe.BBCType == BoundaryType.VolumeCoupling)) SetVolumeBC(conn.PortB, conn.PortA); } } // 3. Sub‑steps int nSub = 1; foreach (var p in _pipes) nSub = Math.Max(nSub, p.GetRequiredSubSteps(_dt)); double dtSub = _dt / nSub; for (int sub = 0; sub < nSub; sub++) { foreach (var p in _pipes) p.SimulateSingleStep(dtSub); foreach (var conn in _connections) { if (IsPipePort(conn.PortA) && IsVolumePort(conn.PortB)) { var pipe = GetPipe(conn.PortA); bool isA = pipe.PortA == conn.PortA; if ((isA && pipe.ABCType == BoundaryType.VolumeCoupling) || (!isA && pipe.BBCType == BoundaryType.VolumeCoupling)) TransferAndIntegrate(conn.PortA, conn.PortB, dtSub); } else if (IsVolumePort(conn.PortA) && IsPipePort(conn.PortB)) { var pipe = GetPipe(conn.PortB); bool isA = pipe.PortB == conn.PortB; if ((isA && pipe.ABCType == BoundaryType.VolumeCoupling) || (!isA && pipe.BBCType == BoundaryType.VolumeCoupling)) TransferAndIntegrate(conn.PortB, conn.PortA, dtSub); } } if (sub < nSub - 1) { foreach (var v in _volumes) v.PushStateToPort(); foreach (var conn in _connections) { if (IsPipePort(conn.PortA) && IsVolumePort(conn.PortB)) { var pipe = GetPipe(conn.PortA); bool isA = pipe.PortA == conn.PortA; if ((isA && pipe.ABCType == BoundaryType.VolumeCoupling) || (!isA && pipe.BBCType == BoundaryType.VolumeCoupling)) SetVolumeBC(conn.PortA, conn.PortB); } else if (IsVolumePort(conn.PortA) && IsPipePort(conn.PortB)) { var pipe = GetPipe(conn.PortB); bool isA = pipe.PortB == conn.PortB; if ((isA && pipe.ABCType == BoundaryType.VolumeCoupling) || (!isA && pipe.BBCType == BoundaryType.VolumeCoupling)) SetVolumeBC(conn.PortB, conn.PortA); } } } } // 5. Audio samples (none for now, but placeholder) var audioSamples = new List(); foreach (var conn in _connections) { if (conn is SoundConnection sc) audioSamples.Add(sc.GetAudioSample()); } // 6. Clear BC flags foreach (var p in _pipes) p.ClearBC(); return SoundProcessor.MixAndClip(audioSamples.ToArray()); } private bool IsVolumePort(Port p) => _volumes.Exists(v => v.Port == p); private bool IsPipePort(Port p) => _pipes.Exists(pp => pp.PortA == p || pp.PortB == p); private Pipe1D GetPipe(Port p) => _pipes.Find(pp => pp.PortA == p || pp.PortB == p); private Volume0D GetVolume(Port p) => _volumes.Find(v => v.Port == p); private void SetVolumeBC(Port pipePort, Port volPort) { var pipe = GetPipe(pipePort); if (pipe == null) return; bool isA = pipe.PortA == pipePort; if (isA) pipe.SetAVolumeState(volPort.Density, volPort.Pressure); else pipe.SetBVolumeState(volPort.Density, volPort.Pressure); } private void TransferAndIntegrate(Port pipePort, Port volPort, double dtSub) { double mdot = pipePort.MassFlowRate; volPort.MassFlowRate = -mdot; if (mdot < 0) // pipe → volume { volPort.SpecificEnthalpy = pipePort.SpecificEnthalpy; } // else volume’s own enthalpy (from PushStateToPort) is used GetVolume(volPort)?.Integrate(dtSub); } } }