using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using FluidSim.Components; using FluidSim.Interfaces; namespace FluidSim.Core { public class Solver { private readonly List _components = new(); private PipeSystem _pipeSystem; private BoundarySystem _boundarySystem; private double _dt; public int SubStepCount { get; set; } = 4; public bool EnableProfiling { get; set; } = false; private long _stepCount; private long _ticksOrifice, _ticksOpenEnd, _ticksPipe, _ticksUpdate; public void SetTimeStep(double dt) => _dt = dt; public void AddComponent(IComponent component) => _components.Add(component); public void SetPipeSystem(PipeSystem pipeSystem) { _pipeSystem = pipeSystem; } public void SetBoundarySystem(BoundarySystem boundarySystem) { _boundarySystem = boundarySystem; } public void Step() { if (_pipeSystem == null || _boundarySystem == null) return; int nSub = _pipeSystem.GetRequiredSubSteps((float)_dt, 0.8f); nSub = Math.Max(nSub, SubStepCount); // never go below fixed minimum float dtSub = (float)(_dt / nSub); for (int sub = 0; sub < nSub; sub++) { long t0; t0 = Stopwatch.GetTimestamp(); _boundarySystem.ResolveOrifices(dtSub); _ticksOrifice += Stopwatch.GetTimestamp() - t0; t0 = Stopwatch.GetTimestamp(); _boundarySystem.ResolveOpenEnds(dtSub); _ticksOpenEnd += Stopwatch.GetTimestamp() - t0; t0 = Stopwatch.GetTimestamp(); _pipeSystem.SimulateStep(dtSub); _ticksPipe += Stopwatch.GetTimestamp() - t0; } long tUS = Stopwatch.GetTimestamp(); foreach (var comp in _components) comp.UpdateState((float)_dt); _ticksUpdate += Stopwatch.GetTimestamp() - tUS; _stepCount++; if (_stepCount % 5000 == 0 && EnableProfiling) { double freq = Stopwatch.Frequency; double total = _ticksOrifice + _ticksOpenEnd + _ticksPipe + _ticksUpdate; double avgStepUs = (total / freq) * 1e6 / 5000.0; int orificeCalls = 5000 * nSub; int updateCalls = 5000; double orificeMs = _ticksOrifice * 1000.0 / freq; double openEndMs = _ticksOpenEnd * 1000.0 / freq; double pipeMs = _ticksPipe * 1000.0 / freq; double updateMs = _ticksUpdate * 1000.0 / freq; double orificeAvgUs = orificeMs * 1000.0 / orificeCalls; double openEndAvgUs = openEndMs * 1000.0 / orificeCalls; double pipeAvgUs = pipeMs * 1000.0 / orificeCalls; double updateAvgUs = updateMs * 1000.0 / updateCalls; Console.WriteLine($"--- Solver ({5000} steps, nSub={nSub}) ---"); Console.WriteLine($" Average step: {avgStepUs:F2} µs"); Console.WriteLine($" Orifice: {orificeMs:F2} ms ({(double)_ticksOrifice / total * 100:F1}%), avg {orificeAvgUs:F2} µs/call"); Console.WriteLine($" OpenEnd: {openEndMs:F2} ms ({(double)_ticksOpenEnd / total * 100:F1}%), avg {openEndAvgUs:F2} µs/call"); Console.WriteLine($" Pipe: {pipeMs:F2} ms ({(double)_ticksPipe / total * 100:F1}%), avg {pipeAvgUs:F2} µs/call"); Console.WriteLine($" Update: {updateMs:F2} ms ({(double)_ticksUpdate / total * 100:F1}%), avg {updateAvgUs:F2} µs/call"); // Pipe internal breakdown (with per-phase averages) if (_pipeSystem.EnableProfiling) { Console.WriteLine(_pipeSystem.GetProfileReport()); } _ticksOrifice = _ticksOpenEnd = _ticksPipe = _ticksUpdate = 0; } } } }