Files
FluidSim/Core/Solver.cs
2026-05-08 13:16:51 +02:00

146 lines
5.6 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<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.9;
// ---------- 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<Pipe1D>().ToList();
if (pipes.Count == 0) return;
var sw = Stopwatch.StartNew();
// CFL count track which pipe demands the most substeps
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($" Substep 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;
}
}
}
}