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 _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, _timeCFL, _timeOrifice, _timeOpenEnd, _timePipe, _timeClearGhosts, _timeUpdateState; private const int LogInterval = 5000; private const bool EnableLogging = false; // temporarily ON for debugging 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 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 – track which pipe demands the most sub‑steps int nSub = 1; Pipe1D worstPipe = pipes[0]; foreach (var p in pipes) { int n = p.GetRequiredSubSteps(_dt, CflTarget); if (n > nSub) { nSub = n; worstPipe = p; } } double dtSub = _dt / nSub; // ----- Diagnostic: warn if nSub is high ----- if (nSub > 50) { double maxW = 0; for (int i = 0; i < worstPipe.CellCount; i++) { double rho = worstPipe.GetCellDensity(i); double u = Math.Abs(worstPipe.GetCellVelocity(i)); double p = worstPipe.GetCellPressure(i); double c = Math.Sqrt(1.4 * p / Math.Max(rho, 1e-12)); if (u + c > maxW) maxW = u + c; } Console.WriteLine($"nSub = {nSub} (worst pipe: {worstPipe.Name}, maxW = {maxW:F0} m/s)"); } _timeCFL += sw.Elapsed.TotalSeconds; // ----- Safety cap – prevent the solver from hanging ----- const int maxSubSteps = 10000; const int hardLimit = 500; // temporary low cap for debugging if (nSub > hardLimit) { Console.WriteLine($"nSub ({nSub}) exceeds hard limit {hardLimit}. Simulation step skipped."); 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 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; _timeTotal += sw.Elapsed.TotalSeconds; _stepCount++; if (_stepCount % LogInterval == 0 && EnableLogging) { if (_timeTotal > 0) { double stepsPerSec = LogInterval / _timeTotal; double avgUs = (_timeTotal / LogInterval) * 1e6; 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} %"); Console.WriteLine($" Sub‑step loop:"); Console.WriteLine($" Orifice: {_timeOrifice / _timeTotal * 100:F1} %"); Console.WriteLine($" OpenEnd: {_timeOpenEnd / _timeTotal * 100:F1} %"); Console.WriteLine($" Pipe steps: {_timePipe / _timeTotal * 100:F1} %"); Console.WriteLine($" Clear ghosts: {_timeClearGhosts / _timeTotal * 100:F1} %"); Console.WriteLine($" Update state: {_timeUpdateState / _timeTotal * 100:F1} %"); Console.WriteLine(); } _timeTotal = 0; _timeCFL = 0; _timeOrifice = 0; _timeOpenEnd = 0; _timePipe = 0; _timeClearGhosts = 0; _timeUpdateState = 0; } } } }