using System; using System.Collections.Generic; using System.Linq; using FluidSim.Components; using FluidSim.Interfaces; namespace FluidSim.Core { /// /// Top‑level solver that owns all components and couplings, /// orchestrates sub‑stepping, and exposes states for audio. /// public class Solver { private readonly List _components = new(); private readonly List _orificeLinks = new(); private readonly List _junctions = new(); private readonly List _openEndLinks = new(); private double _dt; public void SetTimeStep(double dt) => _dt = dt; public void AddComponent(IComponent component) => _components.Add(component); public void AddOrificeLink(OrificeLink link) => _orificeLinks.Add(link); public void AddJunction(Junction junction) => _junctions.Add(junction); public void AddOpenEndLink(OpenEndLink link) => _openEndLinks.Add(link); // Convenience: first pipe’s port B mass flow (often the exhaust) public double ExhaustMassFlow { get { var pipes = _components.OfType().ToList(); if (pipes.Count > 0) return Math.Abs(pipes[0].PortB.MassFlowRate); return 0.0; } } /// /// Advance the whole system by one global time step. /// public void Step() { var pipes = _components.OfType().ToList(); if (pipes.Count == 0) return; // 1. Determine sub‑step count (max CFL over all pipes) int nSub = 1; foreach (var p in pipes) nSub = Math.Max(nSub, p.GetRequiredSubSteps(_dt)); double dtSub = _dt / nSub; // 2. Sub‑step loop for (int sub = 0; sub < nSub; sub++) { // a) Resolve all orifice links (volume ↔ pipe) foreach (var link in _orificeLinks) link.Resolve(dtSub); // b) Resolve all open‑end links (pipe → atmosphere) foreach (var link in _openEndLinks) link.Resolve(dtSub); // c) Resolve all junctions (pipe ↔ pipe) foreach (var junc in _junctions) junc.Resolve(dtSub); // d) Advance all pipes foreach (var p in pipes) p.SimulateSingleStep(dtSub); } // 3. Clear ghost flags foreach (var p in pipes) p.ClearGhostFlags(); // 4. Integrate non‑pipe components (volumes, atmosphere, etc.) foreach (var comp in _components) comp.UpdateState(_dt); } } }