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 readonly List _orificeLinks = new(); private readonly List _junctions = new(); private readonly List _openEndLinks = new(); private double _dt; /// CFL target for sub‑stepping (0.3‑0.8). Lower values are safer for shocks. public double CflTarget { get; set; } = 0.8; // ---------- Timing accumulators (reset every LogInterval steps) ---------- private long _stepCount; private double _timeTotal; private double _timeCFL; private double _timeOrifice; private double _timeOpenEnd; private double _timeJunction; private double _timePipe; private double _timeClearGhosts; private double _timeUpdateState; private const int LogInterval = 5000; // print once per second (at 44.1 kHz) private const bool EnableLogging = false; 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); public void Step() { var pipes = _components.OfType().ToList(); if (pipes.Count == 0) return; var sw = Stopwatch.StartNew(); // CFL count int nSub = 1; foreach (var p in pipes) nSub = Math.Max(nSub, p.GetRequiredSubSteps(_dt, CflTarget)); double dtSub = _dt / nSub; _timeCFL += sw.Elapsed.TotalSeconds; const int maxSubSteps = 10000; if (nSub > maxSubSteps) { Console.WriteLine($"Warning: required sub‑steps {nSub} exceeds limit. Simulation stopped."); return; } for (int sub = 0; sub < nSub; sub++) { double t0; t0 = sw.Elapsed.TotalSeconds; foreach (var link in _orificeLinks) link.Resolve(dtSub); _timeOrifice += sw.Elapsed.TotalSeconds - t0; t0 = sw.Elapsed.TotalSeconds; foreach (var link in _openEndLinks) link.Resolve(dtSub); _timeOpenEnd += sw.Elapsed.TotalSeconds - t0; t0 = sw.Elapsed.TotalSeconds; foreach (var junc in _junctions) junc.Resolve(dtSub); _timeJunction += sw.Elapsed.TotalSeconds - t0; t0 = sw.Elapsed.TotalSeconds; foreach (var p in pipes) p.SimulateSingleStep(dtSub); _timePipe += sw.Elapsed.TotalSeconds - t0; } double tCG = sw.Elapsed.TotalSeconds; foreach (var p in pipes) p.ClearGhostFlags(); _timeClearGhosts += sw.Elapsed.TotalSeconds - tCG; double tUS = sw.Elapsed.TotalSeconds; foreach (var comp in _components) comp.UpdateState(_dt); _timeUpdateState += sw.Elapsed.TotalSeconds - tUS; // accumulate total step time (includes CFL, sub‑steps, clear ghosts, update state) _timeTotal += sw.Elapsed.TotalSeconds; // ---------- Periodic report ---------- _stepCount++; if (_stepCount % LogInterval == 0 && EnableLogging) { if (_timeTotal > 0) { double totalMs = _timeTotal * 1000.0; double avgUs = (_timeTotal / LogInterval) * 1e6; // µs per step double stepsPerSec = LogInterval / _timeTotal; // steps per second Console.WriteLine($"--- Solver timing ({LogInterval} steps) ---"); Console.WriteLine($" Steps per second: {stepsPerSec:F1}"); Console.WriteLine($" Avg step time: {avgUs:F1} µs (last nSub = {nSub})"); Console.WriteLine($" CFL calc: {_timeCFL / _timeTotal * 100:F1} % ({_timeCFL * 1e6 / LogInterval:F1} µs/step)"); Console.WriteLine($" Sub‑step loop:"); Console.WriteLine($" Orifice: {_timeOrifice / _timeTotal * 100:F1} % ({_timeOrifice * 1e6 / LogInterval:F1} µs/step)"); Console.WriteLine($" OpenEnd: {_timeOpenEnd / _timeTotal * 100:F1} % ({_timeOpenEnd * 1e6 / LogInterval:F1} µs/step)"); Console.WriteLine($" Junctions: {_timeJunction / _timeTotal * 100:F1} % ({_timeJunction * 1e6 / LogInterval:F1} µs/step)"); Console.WriteLine($" Pipe steps: {_timePipe / _timeTotal * 100:F1} % ({_timePipe * 1e6 / LogInterval:F1} µs/step)"); Console.WriteLine($" Clear ghosts: {_timeClearGhosts / _timeTotal * 100:F1} % ({_timeClearGhosts * 1e6 / LogInterval:F1} µs/step)"); Console.WriteLine($" Update state: {_timeUpdateState / _timeTotal * 100:F1} % ({_timeUpdateState * 1e6 / LogInterval:F1} µs/step)"); Console.WriteLine(); // ---------- Optional detailed pipe profiling ---------- if (Pipe1D.EnableDetailedProfiling) { foreach (var pipe in pipes) { Console.WriteLine(pipe.GetDetailProfileReport()); pipe.ResetDetailCounters(); } } } // Reset accumulators for next interval _timeTotal = 0; _timeCFL = 0; _timeOrifice = 0; _timeOpenEnd = 0; _timeJunction = 0; _timePipe = 0; _timeClearGhosts = 0; _timeUpdateState = 0; } } } }