turbo_moc.solvers.mln module#

Minimum-Length Nozzle (MLN) solver.

Inherits Stages 1, 2, and 4 from MOCSolver. Replaces Stage 3 (kernel) with a centred-expansion-fan construction:

  • There is no circular arc wall; the throat corner is a sharp point at (0, y_t).

  • All right-running (C+) characteristics originate from the corner.

  • Interior points and axis points are computed exactly as before.

  • Marching terminates when the axis Mach number reaches Noz_Mach.

Stage 4 (reflex / turning section) is inherited unchanged.

class turbo_moc.solvers.mln.MOCSolverMLN(config: dict, show_plot=False, fluid_manager: FluidManager = None)[source]#

Bases: object

Minimum-Length Nozzle solver. Stages 1, 2, 4 are identical to MOCSolver in turbo_moc/. Stage 3 uses a centred expansion fan from the throat corner.

Methods

build_result_data([extra_meta])

Returns the full result dict without touching the filesystem -- see turbo_moc.solvers.conventional.MOCSolver.build_result_data (same schema, reused as-is by turbo_moc.api).

plot_results()

run_stage_1_sauer([progress_callback])

run_stage_2_ivp_march([progress_callback])

The flashing throat front is flat (x=0 for every y), so it carries no geometric spread in x.

run_stage_2_sauer_direct([progress_callback])

For the MLN, the expansion fan originates entirely from the sharp throat corner: within a centred simple wave, the corner's own (theta, nu) at any fan angle follows directly from that angle alone (see InternalPoint.corner_point_prop), with no dependence on the rest of the throat line -- PROVIDED the corner's own reference state has theta=0 exactly (the nu(V) table's zero point).

run_stage_3_kernel_mln([progress_callback])

Minimum-length nozzle kernel.

export_full_result_data

run_stage_4_reflex

build_result_data(extra_meta=None)[source]#

Returns the full result dict without touching the filesystem – see turbo_moc.solvers.conventional.MOCSolver.build_result_data (same schema, reused as-is by turbo_moc.api).

export_full_result_data(filename='turbo_moc_result_data.json', extra_meta=None)[source]#
plot_results()[source]#
run_stage_1_sauer(progress_callback=None)[source]#
run_stage_2_ivp_march(progress_callback=None)[source]#

The flashing throat front is flat (x=0 for every y), so it carries no geometric spread in x. Stage 3’s Inv_Wall_pnt needs two IVP points with different x to define the line 3-1 slope — feeding the flat front to it directly divides by zero. Marching the front forward via interior-point compatibility (mixing C+/C- characteristics, always at different slopes since alpha != 0) builds a proper x-spread characteristics mesh before Stage 3 runs, exactly as moc_tool_flashing does for the arc-wall kernel.

NOTE: mutates self.Sauer in place; run_stage_1_sauer already cached the true throat-corner x in self.throat_corner_x before this runs.

run_stage_2_sauer_direct(progress_callback=None)[source]#

For the MLN, the expansion fan originates entirely from the sharp throat corner: within a centred simple wave, the corner’s own (theta, nu) at any fan angle follows directly from that angle alone (see InternalPoint.corner_point_prop), with no dependence on the rest of the throat line – PROVIDED the corner’s own reference state has theta=0 exactly (the nu(V) table’s zero point). This holds exactly for the flashing front (_stage1_flashing_front sets v=0, i.e. theta=0, at every point, including the corner), but NOT for the smooth Sauer sonic line (_stage1_sauer_smooth): the transonic curvature formula (SauerMain) gives a small but genuinely nonzero flow angle even at the wall point, so collapsing to a single point would silently drop that curvature. Only the flashing case is therefore simplified to a single-point IVP; the smooth case keeps the full multi-point front as before.

For the flashing case, dropping the other (redundant – all identical state, just spread over y) front points also makes the first fan angle collapse exactly to the reference algorithm’s ray 1 (corner -> axis directly, zero interior points, see Zebbiche & Youbi 2007 Fig. 3a) – previously ray 1 was seeded from the full multi-point front, adding “interior” points that carried no independent new information.

run_stage_3_kernel_mln(progress_callback=None)[source]#

Minimum-length nozzle kernel.

Identical logic to the standard Stage 3 kernel EXCEPT that the wall position is fixed at the throat corner (x_corner, y_t) for every fan angle, instead of moving along a circular arc.

For flashing cases, the corner point at each fan angle is computed DIRECTLY via InternalPoint.corner_point_prop (no iterative wall search), and self.IVP is seeded (in run_stage_2_sauer_direct) with just the single corner point rather than the full flat front – together these make ray 1 collapse exactly to corner -> axis directly (zero interior points), matching Zebbiche & Youbi (2007) Fig. 3a’s reference algorithm.

The corner position is taken from IVP[0] after Stage 2 (the wall-side point on the IVP line, which is already supersonic — avoiding the M=1 singularity that caused NaN in the previous implementation).

All C+ characteristics thus originate from this single corner point. The flow angle at the corner grows step-by-step through the wall compatibility equation (Inv_Wall_pnt), which is the standard procedure for any wall boundary.

FIXED TARGET, NOT A SEARCH. self.tau_mln is expected to already span [0, theta*] where theta* = 0.5*nu(M_f) is the classical SSL-MLN corner turn angle (Argrow & Emanuel 1991, eq. for the straight sonic line MLN), computed from the same real-gas Prandtl-Meyer integral used in NICFD_2026/python_codes/04_moc_flashing.py, referenced from the post-jump state (nu=0 there) rather than the classical M=1 point – exactly the origin of this corner’s fan. Sweeping a KNOWN, bounded angle range (rather than an open-ended Mach/pressure-crossing search) avoids accumulating turning far past where the design actually ends, which is what previously drove the axis-point solve into the near-M=1 singularity and the fluid table’s valid range.

run_stage_4_reflex(refine_near_corner=False, n_sub=8, n_segments=3, progress_callback=None)[source]#
turbo_moc.solvers.mln.march_line_jax_step(carry, _)[source]#

Vectorised MoC step with predictor-corrector (identical to turbo_moc/).