Files
FluidSim/Core/Solver.cs
2026-05-05 19:39:11 +02:00

120 lines
4.9 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 FluidSim.Components;
namespace FluidSim.Core
{
public class Solver
{
private readonly List<Volume0D> _volumes = new();
private readonly List<Pipe1D> _pipes = new();
private readonly List<PipeVolumeConnection> _connections = new();
private double _dt;
private double _ambientPressure = 101325.0;
public void SetAmbientPressure(double p) => _ambientPressure = p;
public void AddVolume(Volume0D v) => _volumes.Add(v);
public void AddPipe(Pipe1D p) => _pipes.Add(p);
public void AddConnection(PipeVolumeConnection c) => _connections.Add(c);
public void SetTimeStep(double dt) => _dt = dt;
public void SetPipeBoundary(Pipe1D pipe, bool isA, BoundaryType type, double ambientPressure = 101325.0)
{
if (isA)
{
pipe.SetABoundaryType(type);
if (type == BoundaryType.OpenEnd) pipe.SetAAmbientPressure(ambientPressure);
}
else
{
pipe.SetBBoundaryType(type);
if (type == BoundaryType.OpenEnd) pipe.SetBAmbientPressure(ambientPressure);
}
}
public float Step()
{
// 1. For each connection, handle flow or closed wall
foreach (var conn in _connections)
{
double area = conn.OrificeArea;
if (area < 1e-12) // valve closed → treat as solid wall
{
conn.Volume.MassFlowRateIn = 0.0;
conn.Volume.SpecificEnthalpyIn = conn.Volume.SpecificEnthalpy; // not used
// Set ghost to a reflective wall (u = -u_pipe, same p, ρ)
int cellIdx = conn.IsPipeLeftEnd ? 0 : conn.Pipe.GetCellCount() - 1;
double rho = Math.Max(conn.Pipe.GetCellDensity(cellIdx), 1e-6);
double p = Math.Max(conn.Pipe.GetCellPressure(cellIdx), 100.0);
double u = conn.Pipe.GetCellVelocity(cellIdx);
if (conn.IsPipeLeftEnd)
conn.Pipe.SetGhostLeft(rho, -u, p);
else
conn.Pipe.SetGhostRight(rho, -u, p);
continue;
}
// Valve open → use the nozzle model
double downstreamPressure = conn.IsPipeLeftEnd
? conn.Pipe.GetCellPressure(0)
: conn.Pipe.GetCellPressure(conn.Pipe.GetCellCount() - 1);
NozzleFlow.Compute(conn.Volume, area, downstreamPressure,
out double mdot, out double rhoFace, out double uFace, out double pFace,
gamma: conn.Volume.Gamma);
// Clamp mdot to available mass
double maxMdot = conn.Volume.Mass / _dt;
conn.LastMassFlowIntoVolume = mdot;
if (mdot > maxMdot) mdot = maxMdot;
if (mdot < -maxMdot) mdot = -maxMdot;
conn.Volume.MassFlowRateIn = mdot;
// enthalpy: if inflow, use pipe enthalpy; if outflow, use cylinder enthalpy
if (mdot >= 0)
{
int cellIdx = conn.IsPipeLeftEnd ? 0 : conn.Pipe.GetCellCount() - 1;
double pPipe = Math.Max(conn.Pipe.GetCellPressure(cellIdx), 100.0);
double rhoPipe = Math.Max(conn.Pipe.GetCellDensity(cellIdx), 1e-6);
conn.Volume.SpecificEnthalpyIn = (conn.Volume.Gamma / (conn.Volume.Gamma - 1.0)) * pPipe / rhoPipe;
}
else
{
conn.Volume.SpecificEnthalpyIn = conn.Volume.SpecificEnthalpy;
}
// Integrate the volume
conn.Volume.Integrate(_dt);
// Set ghost from nozzle face state (but don't allow zero density)
if (rhoFace < 1e-6) rhoFace = Constants.Rho_amb;
if (pFace < 100.0) pFace = Constants.P_amb;
if (conn.IsPipeLeftEnd)
conn.Pipe.SetGhostLeft(rhoFace, uFace, pFace);
else
conn.Pipe.SetGhostRight(rhoFace, uFace, pFace);
}
// 2. Substep pipes
int nSub = 1;
foreach (var p in _pipes)
nSub = Math.Max(nSub, p.GetRequiredSubSteps(_dt));
double dtSub = _dt / nSub;
for (int sub = 0; sub < nSub; sub++)
foreach (var p in _pipes)
p.SimulateSingleStep(dtSub);
// 3. Clear ghost flags
foreach (var p in _pipes)
p.ClearGhostFlag();
// 4. Return exhaust tailpipe mass flow
if (_pipes.Count > 0)
return (float)_pipes[0].GetOpenEndMassFlow();
return 0f;
}
}
}