refactoring (broken right now)
This commit is contained in:
228
Core/Junction.cs
Normal file
228
Core/Junction.cs
Normal file
@@ -0,0 +1,228 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using FluidSim.Components;
|
||||
|
||||
namespace FluidSim.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Zero‑dimensional junction connecting multiple pipe ends.
|
||||
/// The coupling conditions are mass conservation and equality of
|
||||
/// stagnation enthalpy (Bernoulli invariant) for all branches,
|
||||
/// following Reigstad (2014, 2015). A root‑finding method (Brent)
|
||||
/// solves for the common junction pressure.
|
||||
/// </summary>
|
||||
public class Junction
|
||||
{
|
||||
public struct Branch
|
||||
{
|
||||
public Pipe1D Pipe;
|
||||
public bool IsLeftEnd;
|
||||
}
|
||||
|
||||
private readonly List<Branch> _branches = new List<Branch>();
|
||||
public IReadOnlyList<Branch> Branches => _branches;
|
||||
|
||||
// Last resolved state (for audio / monitoring)
|
||||
public double LastJunctionPressure { get; private set; }
|
||||
public double[] LastBranchMassFlows { get; private set; } = Array.Empty<double>();
|
||||
|
||||
public Junction() { }
|
||||
|
||||
public void AddBranch(Pipe1D pipe, bool isLeftEnd)
|
||||
{
|
||||
_branches.Add(new Branch { Pipe = pipe, IsLeftEnd = isLeftEnd });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Solve the junction for one sub‑step. Uses Brent's method to find
|
||||
/// the pressure p* that satisfies sum(mdot) = 0 with stagnation enthalpy equality.
|
||||
/// </summary>
|
||||
public void Resolve(double dtSub)
|
||||
{
|
||||
int nb = _branches.Count;
|
||||
if (nb < 2)
|
||||
throw new InvalidOperationException("Junction requires at least 2 branches.");
|
||||
|
||||
// Gather interior states and areas
|
||||
var rho = new double[nb];
|
||||
var u = new double[nb];
|
||||
var p = new double[nb];
|
||||
var area = new double[nb];
|
||||
var isLeft = new bool[nb];
|
||||
double gamma = 1.4;
|
||||
|
||||
double pMin = double.MaxValue, pMax = double.MinValue;
|
||||
for (int i = 0; i < nb; i++)
|
||||
{
|
||||
var branch = _branches[i];
|
||||
(double ri, double ui, double pi) = branch.IsLeftEnd
|
||||
? branch.Pipe.GetInteriorStateLeft()
|
||||
: branch.Pipe.GetInteriorStateRight();
|
||||
rho[i] = ri; u[i] = ui; p[i] = pi;
|
||||
area[i] = branch.Pipe.Area;
|
||||
isLeft[i] = branch.IsLeftEnd;
|
||||
|
||||
if (pi < pMin) pMin = pi;
|
||||
if (pi > pMax) pMax = pi;
|
||||
}
|
||||
|
||||
// We solve for pStar that makes totalMassFlow(pStar) = 0.
|
||||
// The function: totalMassFlow = sum( sign_i * rhoStar_i * uStar_i * A_i )
|
||||
// where for each branch:
|
||||
// - Riemann invariant: J = u + 2c/(γ-1) for right end, J = u - 2c/(γ-1) for left end.
|
||||
// - uStar = J ∓ 2cStar/(γ-1) (depending on direction)
|
||||
// - Isentropic relation: rhoStar = rho_i * (pStar / p_i)^{1/γ}
|
||||
// - cStar = sqrt(γ pStar / rhoStar)
|
||||
// We require stagnation enthalpy equality: h0 = h + u^2/2 = constant across junction.
|
||||
// Hence for each branch we compute the specific total enthalpy:
|
||||
// hStar = (γ/(γ-1)) * pStar/rhoStar, h0_star = hStar + 0.5 uStar^2.
|
||||
// We enforce that all h0_star are equal. Mass conservation then determines pStar.
|
||||
// This is a scalar root‑finding problem.
|
||||
|
||||
// Bracket the solution: pressure must lie between min and max of branch pressures (expanded a bit)
|
||||
double a = Math.Max(100.0, pMin * 0.1);
|
||||
double b = Math.Min(1e7, pMax * 10.0);
|
||||
if (a >= b) { a = 100.0; b = 1e7; }
|
||||
|
||||
Func<double, double> f = pStar =>
|
||||
{
|
||||
double totalMdot = 0.0;
|
||||
double h0Ref = 0.0;
|
||||
bool first = true;
|
||||
for (int i = 0; i < nb; i++)
|
||||
{
|
||||
double g = gamma;
|
||||
double gm1 = g - 1.0;
|
||||
double rhoI = rho[i], uI = u[i], pI = p[i];
|
||||
double cI = Math.Sqrt(g * pI / rhoI);
|
||||
double J = isLeft[i] ? uI - 2.0 * cI / gm1 : uI + 2.0 * cI / gm1;
|
||||
|
||||
double pratio = Math.Max(pStar / pI, 1e-6);
|
||||
double rhoStar = rhoI * Math.Pow(pratio, 1.0 / g);
|
||||
double cStar = Math.Sqrt(g * pStar / rhoStar);
|
||||
double uStar = isLeft[i] ? J + 2.0 * cStar / gm1 : J - 2.0 * cStar / gm1;
|
||||
|
||||
double hStar = (g / gm1) * pStar / rhoStar;
|
||||
double h0 = hStar + 0.5 * uStar * uStar;
|
||||
|
||||
if (first)
|
||||
{
|
||||
h0Ref = h0;
|
||||
first = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Equality of stagnation enthalpy: ideally h0 == h0Ref.
|
||||
// We incorporate a penalty to enforce this.
|
||||
}
|
||||
|
||||
// Mass flow into junction: sign convention = positive if fluid leaves pipe into junction.
|
||||
double sign = isLeft[i] ? -1.0 : 1.0; // left end: positive u is into pipe, so into junction is -u
|
||||
double mdot_i = sign * rhoStar * uStar * area[i];
|
||||
totalMdot += mdot_i;
|
||||
}
|
||||
|
||||
// Additional term to enforce equal stagnation enthalpies? For simplicity, we only enforce mass conservation here,
|
||||
// because with the Riemann invariants and a common pressure, the stagnation enthalpies are automatically equal
|
||||
// if the junction is isentropic? Actually, with a common pressure and isentropic relations from each branch,
|
||||
// each branch has its own entropy (p/ρ^γ = const), so h0 may differ. The correct condition is mass conservation + equality of h0.
|
||||
// To solve both, we would need to vary pStar and a common h0? In Reigstad's formulation, the system yields
|
||||
// mass conservation as the determinant, and pStar is found from that equation, with the assumption that the junction
|
||||
// itself does not introduce entropy. The typical implementation uses the Riemann invariants and mass conservation only.
|
||||
// We'll stick to mass conservation for now.
|
||||
return totalMdot;
|
||||
};
|
||||
|
||||
double pStar = BrentsMethod(f, a, b, 1e-6, 100);
|
||||
LastJunctionPressure = pStar;
|
||||
LastBranchMassFlows = new double[nb];
|
||||
|
||||
// Apply ghost states and record mass flows
|
||||
for (int i = 0; i < nb; i++)
|
||||
{
|
||||
double g = gamma, gm1 = g - 1.0;
|
||||
double rhoI = rho[i], uI = u[i], pI = p[i];
|
||||
double cI = Math.Sqrt(g * pI / rhoI);
|
||||
double J = isLeft[i] ? uI - 2.0 * cI / gm1 : uI + 2.0 * cI / gm1;
|
||||
|
||||
double pratio = Math.Max(pStar / pI, 1e-6);
|
||||
double rhoStar = rhoI * Math.Pow(pratio, 1.0 / g);
|
||||
double cStar = Math.Sqrt(g * pStar / rhoStar);
|
||||
double uStar = isLeft[i] ? J + 2.0 * cStar / gm1 : J - 2.0 * cStar / gm1;
|
||||
|
||||
double sign = isLeft[i] ? -1.0 : 1.0;
|
||||
double mdot = sign * rhoStar * uStar * area[i];
|
||||
LastBranchMassFlows[i] = mdot;
|
||||
|
||||
if (isLeft[i])
|
||||
_branches[i].Pipe.SetGhostLeft(rhoStar, uStar, pStar);
|
||||
else
|
||||
_branches[i].Pipe.SetGhostRight(rhoStar, uStar, pStar);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Simple Brent's method root finder.</summary>
|
||||
private static double BrentsMethod(Func<double, double> f, double a, double b, double tol, int maxIter)
|
||||
{
|
||||
double fa = f(a), fb = f(b);
|
||||
if (fa * fb >= 0)
|
||||
return (a + b) / 2.0; // fallback
|
||||
|
||||
double c = a, fc = fa;
|
||||
double d = b - a, e = d;
|
||||
|
||||
for (int iter = 0; iter < maxIter; iter++)
|
||||
{
|
||||
if (Math.Abs(fc) < Math.Abs(fb))
|
||||
{
|
||||
a = b; b = c; c = a;
|
||||
fa = fb; fb = fc; fc = fa;
|
||||
}
|
||||
double tol1 = 2 * double.Epsilon * Math.Abs(b) + 0.5 * tol;
|
||||
double xm = 0.5 * (c - b);
|
||||
if (Math.Abs(xm) <= tol1 || fb == 0.0)
|
||||
return b;
|
||||
|
||||
if (Math.Abs(e) >= tol1 && Math.Abs(fa) > Math.Abs(fb))
|
||||
{
|
||||
double s = fb / fa;
|
||||
double p, q;
|
||||
if (a == c)
|
||||
{
|
||||
p = 2.0 * xm * s;
|
||||
q = 1.0 - s;
|
||||
}
|
||||
else
|
||||
{
|
||||
q = fa / fc;
|
||||
double r = fb / fc;
|
||||
p = s * (2.0 * xm * q * (q - r) - (b - a) * (r - 1.0));
|
||||
q = (q - 1.0) * (r - 1.0) * (s - 1.0);
|
||||
}
|
||||
if (p > 0) q = -q; else p = -p;
|
||||
s = e; e = d;
|
||||
if (2.0 * p < 3.0 * xm * q - Math.Abs(tol1 * q) && p < Math.Abs(0.5 * s * q))
|
||||
{
|
||||
d = p / q;
|
||||
}
|
||||
else
|
||||
{
|
||||
d = xm; e = d;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
d = xm; e = d;
|
||||
}
|
||||
|
||||
a = b; fa = fb;
|
||||
if (Math.Abs(d) > tol1)
|
||||
b += d;
|
||||
else
|
||||
b += Math.Sign(xm) * tol1;
|
||||
fb = f(b);
|
||||
}
|
||||
return b;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user