Skip to content

51 analytical inlet module#110

Open
nlaing613 wants to merge 10 commits into
developfrom
51-analytical-inlet-module
Open

51 analytical inlet module#110
nlaing613 wants to merge 10 commits into
developfrom
51-analytical-inlet-module

Conversation

@nlaing613

Copy link
Copy Markdown
Contributor

This PR adds the first fully operational version of the inlet_moc module and an associated test case (emami_inlet.py). It is not ready to directly integrate in the same manner as simple_inlet_model.py, but it works nicely for this test case and the code is large. I reason it'd be better to start this sooner than later.

Sources: rotational_solvers.py is adapted from Zucrow and Hoffman, Volume II, Chapter 17. The irrotational shock-net algorithm is documented in SUPIN and Anderson NASA TN D-4960. I extended the irrotational shock algorithm for rotational solvers, where irrotational flow is a special case of rotational flow. This should make the solver more accurate and robust, as shocks violate the irrotationality assumption (Crocco's theorem).

Ruff formatting/checks were also run before opening this PR.

Note: throughout the code, the np.ndarray variable pt is used. This always contains x, y, V, theta, p, and rho. I am open to changing this to a dataclass, but since points are constantly called from the net, repackaging them seems wasteful.

Module Structure

  • moc_solution.py

    • Main bookkeeping object.
    • Processes the initial condition and input geometry.
    • Calls for creation of the first characteristic net.
  • initial_domain.py

    • Contains helper functions that identify the inlet leading edge.
    • Constructs FlowCells containing the freestream, post-shock state, and initial data line (IDL).
  • solver_events.py

    • Identifies incident shock locations in nets.
    • Handles shock reflection.
  • planar_inlet.py

    • Geometry class using math module interpolation to return the y location of the two walls, centerbody and cowl, and their slopes at a given x.
  • char_net.py

    • Represents a characteristic net consisting of x, y, V, theta, p, and rho.
    • Contains functionality to self-solve after being fed inlet information and an IDL, only for the initial net.
  • flow_physics.py

    • Contains a variety of helper functions for perfect-gas and isentropic-flow relations.
  • rotational_solvers.py

    • Contains field_point_rot, the solver for the intersection of C+ and C- characteristics, reconstructing the C0 streamline.
    • Contains wall_point_rot, the solver for the intersection of the wall (C0) and two characteristic points.
  • utils_moc.py

    • Holds small helper functions that minimize NumPy usage for speed.
    • Includes clipped interpolation, vectorized linear algebra implementations, and a piecewise-linear plus tangent-line intersection function.

Shock Solvers

  • shock_solvers/char_shock.py

    • Holds the main shock functions that determine the position and state of upstream net points (net_L) and downstream net points (net_R) that satisfy oblique shock relations and characteristic/compatibility equations.
    • Also contains oblique-shock helper functions and the ShockPoint class.
  • shock_solvers/net_logic_helpers.py

    • Assortment of helper functions used to identify valid net points under indexing constraints for interpolation or the next solve.
    • Responsible for clipping coalescing same-family characteristics to retain a valid solution.
    • Constructs valid net boundaries and removes spurious points after resampling or net-shock solves.
    • Returns active point segments for the requested characteristic type, which are fed into net_shock.py.
  • shock_solvers/net_shock.py

    • Main marching algorithm that propagates a shock through the upstream net_L.
    • Constructs and resamples the downstream net_R to retain user-specified resolution.

Processing

  • processing/processing.py

    • Master plotting and averaging script after the solution terminates.
    • Allows direct and analytical modes, with user-specified sampling resolution for analytical mode.
  • processing/triangulated_solution.py

    • Consolidates nets and cells into a triangulated vertex-centered point mesh.
    • Creates an interpolation object that returns primitive variables along a line in y.
  • processing/get_streamtube_bounds.py

    • Uses the processing mode and triangulated solution to find external flow-region bounds used in cross-sectional averaging.
  • processing/cross_sectional_average.py

    • Uses the triangulated solution, input Cantera gas object, and streamtube bounds to compute stream-thrust averaged quantities.

Plotting

  • plot/net_shock.py

    • If plot_during_solve is specified, generates a real-time plot of the net-shock marching process.
    • Mainly used for debugging; open to removal.
  • plot/solution.py

    • Master plotting script.
    • Uses the triangulated solution and inlet to generate 2D contour plots of requested variables in the solution domain.
    • Also generates plots of stream-thrust averaged quantities.
  • plot/helpers.py

    • Stores axis labels, names, y-ranges, and other plotting/formatting utilities.

Example Case

  • examples/moc_testing/emami_inlet.py
    • Runs the NASA Mach 4 case with user-specified x_stop and IDL resolution.
    • Plots two-dimensional primitives plus Mach number and temperature.
    • Plots stream-thrust averaged comparison with refined SU2 Euler solve results.
    • Plots capture bounds computed in get_streamtube_bounds.py versus the analytical function provided in the original paper.

Next Steps

  • Wire the inlet solver more directly into StanShock’s geometry infrastructure.

    • In particular, the wall-boundary characteristic solves need access to local wall slope, not just wall y values; thus, the StanShock geometry module could require extension (to return dy_dx).
  • Isolate the thermodynamic model.

    • The perfect-gas assumption is currently baked into several characteristic, shock, and helper relations; I'd rather use the StanShock fluid state.

@nlaing613
nlaing613 requested a review from TimothyEDawson July 16, 2026 07:23
@nlaing613 nlaing613 self-assigned this Jul 16, 2026
@nlaing613 nlaing613 added the enhancement New feature or request label Jul 16, 2026
@nlaing613 nlaing613 linked an issue Jul 16, 2026 that may be closed by this pull request
@TimothyEDawson

Copy link
Copy Markdown
Collaborator

Awesome, taking a look at it now! Would you mind if I make minor changes as I review? I'll avoid modifying the functionality and focus on code style, variable names, and type annotations.

@nlaing613

Copy link
Copy Markdown
Contributor Author

@TimothyEDawson I don't mind at all, thanks for helping clean up the admittedly lengthy code!

@TimothyEDawson

Copy link
Copy Markdown
Collaborator

@nlaing613 Alrighty, I've got a few things to clean up but overall it's looking pretty good. I did notice the hyshot_inlet example case crashes, so if you want to look into that in the mean time please feel free. If it's going to take a while to fix no worries, we'll merge this in and work on that in a subsequent pull request.

Added some type annotations, but still much work to be done. Started using
match-case in place of chained if clauses. Dropping use of `math` in favor
of NumPy-only calls. Pull in Matplotlib style sheet instead of hardcoding it.

@TimothyEDawson TimothyEDawson left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm good with merging this in as-is and fixing the rest later, but some misc. comments on things to improve going forward:

  • I don't typically use the math module when we already use NumPy so extensively. I'd rather just use NumPy exclusively.
  • I recommend avoiding unpacking NumPy arrays, like when you unpack x, y, V, theta, p, rho = pt. There are several options for rewriting this sort of logic, but it's hard to recommend one without fully understanding the overall flow of data.
  • The logic and type annotations can be substantially simplified by avoiding unnecessary uses of None. Examples:
    • For invalid cases, raise an error instead of returning None. E.g. in char_net.point_mask, is there any valid path through the calling program where a returned None would be valid?
    • In NetShockSolver, are self.net_R or self.net_R_test ever intended to be None? Many type errors disappeared by simply not initializing them with None values, i.e. assuming they will always be of type CharNet.
  • I see invalid calls to wall_point_rot in CharNet - looks like you added the wall and family input arguments to the signature but didn't update the calls here?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Analytical Inlet Module

2 participants