Meridional Flatten
Axisymmetric body-of-revolution -> 2D meridional finite-volume geometry.
A body of revolution stores the same (x, r) mesh once per theta plane:
every theta plane of a true body of revolution is identical, so a 3D
Block built by revolving a wall curve about the x-axis
can be collapsed to a single 2D (x, r) node grid with no loss of
information, then turned into 2D axisymmetric finite-volume geometry (cell
volumes, face weights, face normals) suitable for a 2D solver that reproduces
the 3D body of revolution exactly.
Deliberately distinct from two other, differently-scoped “flatten” concepts in this package, to avoid confusion:
plot3d.flatmesh(flatten_mesh/FlatMesh) flattens a multi-block 3D structured mesh into an unstructured finite-volume graph (full 3D hex cells, owner/neighbor adjacency across welded block boundaries). It has no axisymmetric/2D-collapse logic and does not require a body of revolution.plot3d.glennht.plot3d_flatten_deckexports a GlennHT solver “flatten deck” file format; it is a boundary-condition/connectivity export, not a geometry module.
This module instead collapses a single, axisymmetric block’s redundant theta dimension and builds the resulting 2D mesh’s finite-volume metrics.
Everything here is axisymmetric: a cell’s true volume is
2*pi * integral(r dA) and a face’s true area is 2*pi * integral(r dL);
the common 2*pi divides out of a finite-volume balance, so
build_metrics() stores the per-radian quantities
(vol = area * r_centroid, face weight s = length * r_midpoint). The
axis is not a singularity: the j = 0 face weight is exactly zero because
r = 0 there.
- class plot3d.meridional_flatten.MeridionalMetrics(xc: ndarray, rc: ndarray, area: ndarray, vol: ndarray, si: ndarray, nix: ndarray, nir: ndarray, sj: ndarray, njx: ndarray, njr: ndarray)[source]
Finite-volume geometry of a flattened meridional mesh.
A
typing.NamedTupleof arrays is automatically a valid JAX pytree, so downstream code can pass this straight throughjax.jitwith no registration, without this module depending on JAX itself.Index convention for a node grid of shape
(NI+1, NJ+1): cells(NI, NJ);j = 0touches the axis,j = NJ-1touches the wall. i-faces(NI+1, NJ)are constant-i faces with normal toward +i. j-faces(NI, NJ+1)are constant-j faces with normal toward +j.- __getnewargs__()
Return self as a plain tuple. Used by copy and pickle.
- static __new__(_cls, xc: ndarray, rc: ndarray, area: ndarray, vol: ndarray, si: ndarray, nix: ndarray, nir: ndarray, sj: ndarray, njx: ndarray, njr: ndarray)
Create new instance of MeridionalMetrics(xc, rc, area, vol, si, nix, nir, sj, njx, njr)
- area: ndarray
Alias for field number 2
- nir: ndarray
Alias for field number 6
- nix: ndarray
Alias for field number 5
- njr: ndarray
Alias for field number 9
- njx: ndarray
Alias for field number 8
- rc: ndarray
Alias for field number 1
- si: ndarray
Alias for field number 4
- sj: ndarray
Alias for field number 7
- vol: ndarray
Alias for field number 3
- xc: ndarray
Alias for field number 0
- plot3d.meridional_flatten.analytic_volume(x: ndarray, r_wall: ndarray) float[source]
Volume of the body of revolution,
integral(pi R**2) dx.Trapezoidal rule written out by hand (
np.trapezoidis numpy >= 2.0 only andnp.trapzhas since been removed, so neither name is safe).- Parameters:
x (np.ndarray) – Axial stations.
r_wall (np.ndarray) – Wall radius at those stations.
- Returns:
The reference volume
enclosed_volume()should match.- Return type:
float
- plot3d.meridional_flatten.axisymmetry_error(block: Block) float[source]
How far the block is from being a true body of revolution.
Compares the radius at every theta plane against the first plane. For a mesh that is a genuine body of revolution this is round-off sized, which is the license to keep only one plane.
- Parameters:
block (Block) – Block to check.
- Returns:
max |r(i,j,k) - r(i,j,0)|.- Return type:
float
- plot3d.meridional_flatten.block_radius(block: Block) ndarray[source]
Radius about the x-axis at every node of a block.
- Parameters:
block (Block) – Body-of-revolution block about the x-axis.
- Returns:
rof shape(IMAX, JMAX, KMAX).- Return type:
np.ndarray
- plot3d.meridional_flatten.build_metrics(x2d: ndarray, r2d: ndarray) MeridionalMetrics[source]
Build cell volumes, face weights and face normals from a node grid.
- Parameters:
x2d (np.ndarray) – Node x coordinates, shape
(NI+1, NJ+1).r2d (np.ndarray) – Node r coordinates, shape
(NI+1, NJ+1).
- Returns:
Geometry arrays as plain numpy.
- Return type:
- plot3d.meridional_flatten.enclosed_volume(metrics: MeridionalMetrics) float[source]
Total duct volume implied by the metrics,
2*pi * sum(vol).- Parameters:
metrics (MeridionalMetrics) – Mesh metrics.
- Returns:
Volume of the full body of revolution.
- Return type:
float
- plot3d.meridional_flatten.flatten_to_meridional(block: Block, k_index: int = 0) Tuple[ndarray, ndarray][source]
Extract one constant-theta slice as a 2D
(x, r)node grid.- Parameters:
block (Block) – Body-of-revolution block about the x-axis.
k_index (int, optional) – Which theta plane to keep. Defaults to 0.
- Returns:
(x2d, r2d), each of shape(IMAX, JMAX). Indexj = 0sits on the axis,j = JMAX-1on the wall.- Return type:
Tuple[np.ndarray, np.ndarray]