Files
FluidSim/Core/Solver.cs

149 lines
6.3 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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<IComponent> _components = new();
private readonly List<OrificeLink> _orificeLinks = new();
private readonly List<Junction> _junctions = new();
private readonly List<OpenEndLink> _openEndLinks = new();
private double _dt;
/// <summary>CFL target for substepping (0.30.8). Lower values are safer for shocks.</summary>
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<Pipe1D>().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 substeps {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, substeps, 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($" Substep 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;
}
}
}
}