diff --git a/Components/EngineCylinder.cs b/Components/EngineCylinder.cs
index 645fa29..348aecd 100644
--- a/Components/EngineCylinder.cs
+++ b/Components/EngineCylinder.cs
@@ -17,17 +17,19 @@ namespace FluidSim.Core
// ---- Exhaust valve ----
private double exhMaxOrificeArea;
- private double exhValveOpenStart = 120.0 * Math.PI / 180.0; // 120° (EVO)
- private double exhValveOpenEnd = 480.0 * Math.PI / 180.0; // 480° (EVC)
+ private double exhValveOpenStart = 130.0 * Math.PI / 180.0;
+ private double exhValveOpenEnd = 390.0 * Math.PI / 180.0;
private double exhValveRampWidth = 30.0 * Math.PI / 180.0;
public double ExhaustOrificeArea => ExhaustValveLift() * exhMaxOrificeArea;
+ public double ExhaustValveLiftCurrent => ExhaustValveLift();
// ---- Intake valve ----
private double intMaxOrificeArea;
- private double intValveOpenStart = 380.0 * Math.PI / 180.0; // 380° (IVO)
- private double intValveOpenEnd = 560.0 * Math.PI / 180.0; // 560° (IVC)
+ private double intValveOpenStart = 340.0 * Math.PI / 180.0;
+ private double intValveOpenEnd = 600.0 * Math.PI / 180.0;
private double intValveRampWidth = 30.0 * Math.PI / 180.0;
public double IntakeOrificeArea => IntakeValveLift() * intMaxOrificeArea;
+ public double IntakeValveLiftCurrent => IntakeValveLift();
// ---- Combustion ----
public double TargetPeakPressure { get; set; } = 50.0 * 101325.0;
@@ -70,8 +72,8 @@ namespace FluidSim.Core
this.stroke = stroke;
conRodLength = 2.0 * stroke;
this.compressionRatio = compressionRatio;
- exhMaxOrificeArea = exhPipeArea;
- intMaxOrificeArea = intPipeArea;
+ exhMaxOrificeArea = exhPipeArea * 0.5;
+ intMaxOrificeArea = intPipeArea * 0.5;
pistonArea = Math.PI / 4.0 * bore * bore;
V_disp = pistonArea * stroke;
diff --git a/Components/Nozzleflow.cs b/Components/Nozzleflow.cs
new file mode 100644
index 0000000..a365f71
--- /dev/null
+++ b/Components/Nozzleflow.cs
@@ -0,0 +1,46 @@
+using System;
+
+namespace FluidSim.Components
+{
+ public static class NozzleFlow
+ {
+ ///
+ /// Computes the nozzle‑exit primitive state and mass flow rate from a
+ /// volume to a pipe, using isentropic relations. Follows ensim4's flow() logic.
+ ///
+ public static void Compute(double Pt_high, double Tt_high,
+ double P_low, double gamma, double R, double area,
+ out double rhoExit, out double uExit,
+ out double pExit, out double mdot)
+ {
+ double gm1 = gamma - 1.0;
+ double Pt_over_Ps = Pt_high / P_low;
+
+ // Mach number (subsonic, clamped to 1)
+ double M = Math.Sqrt(Math.Max(0.0,
+ (2.0 / gm1) * (Math.Pow(Pt_over_Ps, gm1 / gamma) - 1.0)));
+ if (M > 1.0) M = 1.0;
+
+ double T_star = Tt_high / (1.0 + 0.5 * gm1 * M * M);
+ double a_star = Math.Sqrt(gamma * R * T_star);
+ double u_star = M * a_star;
+ pExit = Pt_high * Math.Pow(1.0 + 0.5 * gm1 * M * M, -gamma / gm1);
+ rhoExit = pExit / (R * T_star);
+ uExit = u_star; // positive away from high‑pressure side
+ mdot = rhoExit * uExit * area;
+ }
+
+ ///
+ /// Ambient cell for non‑reflecting open end (ensim4 calc_ambient_cell).
+ ///
+ public static void ComputeAmbientCell(double rhoInt, double uInt, double pInt,
+ double pAmbient, double gamma,
+ out double rhoAmb, out double uAmb,
+ out double pAmb)
+ {
+ pAmb = pAmbient;
+ uAmb = uInt;
+ rhoAmb = rhoInt * Math.Pow(pAmb / pInt, 1.0 / gamma);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Program.cs b/Program.cs
index 278e544..94ffa98 100644
--- a/Program.cs
+++ b/Program.cs
@@ -25,7 +25,7 @@ public class Program
// Throttle smoothing
private static double targetThrottle = 0.0; // 1.0 when W is pressed, 0.0 otherwise
private static double currentThrottle = 0.0;
- private const double ThrottleSmoothing = 40.0; // rate of change
+ private const double ThrottleSmoothing = 20.0; // rate of change
private static volatile bool running = true;
diff --git a/Scenarios/EngineScenario.cs b/Scenarios/EngineScenario.cs
index c0f513f..57f43d3 100644
--- a/Scenarios/EngineScenario.cs
+++ b/Scenarios/EngineScenario.cs
@@ -31,7 +31,7 @@ namespace FluidSim.Core
private const int LogInterval = 1000;
public double Throttle { get; set; } = 0.15;
- private const double FullLoadPeakPressure = 60.0 * Units.bar;
+ private const double FullLoadPeakPressure = 140.0 * Units.bar;
public override void Initialize(int sampleRate)
{
@@ -41,29 +41,29 @@ namespace FluidSim.Core
crankshaft = new Crankshaft(initialRPM: 2000.0)
{
Inertia = 0.05,
- FrictionConstant = 0.5,
- FrictionViscous = 0.01
+ FrictionConstant = 0.2,
+ FrictionViscous = 0.025
};
// Exhaust pipe (longer, larger)
- double exhLength = 1;
- double exhRadius = 0.02;
+ double exhLength = 0.5;
+ double exhRadius = 1.5 * Units.cm;
exhPipeArea = Math.PI * exhRadius * exhRadius;
- exhaustPipe = new Pipe1D(exhLength, exhPipeArea, sampleRate, forcedCellCount: 100);
+ exhaustPipe = new Pipe1D(exhLength, exhPipeArea, sampleRate, forcedCellCount: 30);
exhaustPipe.SetUniformState(1.225, 0.0, AmbientPressure);
exhaustPipe.DampingMultiplier = 0.0;
exhaustPipe.EnergyRelaxationRate = 100.0f;
// Intake pipe (shorter, narrower)
- double intLength = 1;
- double intRadius = 0.01;
+ double intLength = 0.1;
+ double intRadius = 1 * Units.cm;
intPipeArea = Math.PI * intRadius * intRadius;
- intakePipe = new Pipe1D(intLength, intPipeArea, sampleRate, forcedCellCount: 50);
+ intakePipe = new Pipe1D(intLength, intPipeArea, sampleRate, forcedCellCount: 10);
intakePipe.SetUniformState(1.225, 0.0, AmbientPressure);
// Cylinder (starts at BDC, fresh charge)
engineCyl = new EngineCylinder(crankshaft,
- bore: 0.065, stroke: 0.0565, compressionRatio: 8.0,
+ bore: 56 * Units.mm, stroke: 57 * Units.mm, compressionRatio: 9.5,
exhPipeArea: exhPipeArea, intPipeArea: intPipeArea, sampleRate: sampleRate);
engineCyl.ignition = true;
@@ -107,7 +107,9 @@ namespace FluidSim.Core
public override float Process()
{
- double throttle = Math.Clamp(Throttle, 0.2, 1.0);
+ double idleThrottle = 0.1;
+ if (crankshaft.AngularVelocity < 80) idleThrottle = 0.2;
+ double throttle = Math.Clamp(Throttle, idleThrottle, 1.0);
double targetPressure = throttle * FullLoadPeakPressure;
engineCyl.TargetPeakPressure = targetPressure;
@@ -211,10 +213,10 @@ namespace FluidSim.Core
float centerY = winH / 2f;
const float T_ambient = 293.15f;
- const float T_hot = 1500f;
- const float T_cold = 0f;
- const float R = 287.05f;
- float deltaHot = T_hot - T_ambient;
+ const float T_hot = 1500f;
+ const float T_cold = 0f;
+ const float R = 287.05f;
+ float deltaHot = T_hot - T_ambient;
float deltaCold = T_ambient - T_cold;
float NormaliseTemperature(double T)
@@ -240,7 +242,7 @@ namespace FluidSim.Core
target.Draw(cylRect);
// ---- Piston ----
- float pistonWidth = cylW - 12f;
+ float pistonWidth = cylW - 12f;
float pistonHeight = 16f;
float pistonFraction = (float)engineCyl.PistonPositionFraction;
float pistonTopY = cylRect.Position.Y + pistonFraction * (cylH - pistonHeight);
@@ -251,12 +253,36 @@ namespace FluidSim.Core
};
target.Draw(pistonRect);
+ // ---------- NEW: Valve lift indicators ----------
+ float barWidth = 30f;
+ float barHeight = 10f;
+ float exhLift = (float)engineCyl.ExhaustValveLiftCurrent;
+ float intLift = (float)engineCyl.IntakeValveLiftCurrent;
+
+ // Exhaust valve indicator (right side of cylinder)
+ var exhBar = new RectangleShape(new Vector2f(barWidth, barHeight))
+ {
+ Position = new Vector2f(cylRect.Position.X + cylW - 10,
+ cylRect.Position.Y - 20 - exhLift * 20),
+ FillColor = new Color(200, 200, 200)
+ };
+ target.Draw(exhBar);
+
+ // Intake valve indicator (left side of cylinder)
+ var intBar = new RectangleShape(new Vector2f(barWidth, barHeight))
+ {
+ Position = new Vector2f(cylRect.Position.X - 20,
+ cylRect.Position.Y - 20 - intLift * 20),
+ FillColor = new Color(200, 200, 200)
+ };
+ target.Draw(intBar);
+
// ---- Exhaust pipe (rightwards) ----
- DrawPipe(target, exhaustPipe, startX: 280f, endX: winW - 60f, centerY,
+ DrawPipe(target, exhaustPipe, startX: 280f, endX: winW - 60f, centerY + 10 - cylRect.Size.Y / 2,
T_ambient, T_hot, T_cold, R, NormaliseTemperature, true);
// ---- Intake pipe (leftwards) ----
- DrawPipe(target, intakePipe, startX: 200f, endX: 20f, centerY,
+ DrawPipe(target, intakePipe, startX: 200f, endX: 20f, centerY + 10 - cylRect.Size.Y / 2,
T_ambient, T_hot, T_cold, R, NormaliseTemperature, false);
}
@@ -276,11 +302,11 @@ namespace FluidSim.Core
for (int i = 0; i < n; i++)
{
float x = startX + i * dx;
- double p = pipe.GetCellPressure(i);
+ double p = pipe.GetCellPressure(i);
double rho = pipe.GetCellDensity(i);
- double T = p / (rho * R);
+ double T = p / (rho * R);
- float r = baseRadius * 0.3f * (float)(1.0 + (p - ambPress) / ambPress);
+ float r = baseRadius * 0.2f * (float)(1.0 + (p - ambPress) / ambPress);
if (r < 2f) r = 2f;
float tn = normaliseTemp(T);
@@ -289,7 +315,7 @@ namespace FluidSim.Core
byte gC = (byte)(255 * (1 - Math.Abs(tn)));
var col = new Color(rC, gC, bC);
- vertices[i * 2] = new Vertex(new Vector2f(x, centerY - r), col);
+ vertices[i * 2] = new Vertex(new Vector2f(x, centerY - r), col);
vertices[i * 2 + 1] = new Vertex(new Vector2f(x, centerY + r), col);
}