turbo_moc.geometry.radial module#

Conformal mapping from an unrolled blade to an annular passage.

The log-spiral mapping wraps either a stator blade produced by parametrize_stator_blade or a rotor blade produced by turbo_moc.rotor.design_rotor_vortex_blade between two radii. Its reference point and axial-chord scale are derived from the blade’s own point cloud, so the mapping does not require geometry-specific constants.

The mapping is:

r = r1 * exp(log(r2/r1) * (chordwise - x1) / c_axial)
theta = theta0 + log(r2/r1) / c_axial * (pitchwise - y1)

The source axes used for the chordwise and pitchwise coordinates differ:

  • A stator’s chordwise extent runs mainly along its local y axis and its pitchwise extent along x after the final profile rotation.

  • A rotor’s chordwise direction runs along local x and its pitch runs along y.

Moving along the chord sweeps the radius from r1 to r2; a pitch offset between adjacent copies becomes the annular blade spacing. The mapping is angle-preserving by construction.

turbo_moc.geometry.radial.apply_conformal_mapping(chordwise, pitchwise, x1, y1, r1, r2, c_axial, theta0=0.0)[source]#

Log-spiral conformal map from the unrolled (chordwise, pitchwise) frame to Cartesian (X, Y) on an annulus: r=r1 at chordwise=x1, r=r2 at chordwise=x1+c_axial, theta=theta0 at pitchwise=y1.

turbo_moc.geometry.radial.conformal_length_scale(chord, x1, r1, r2, c_axial)[source]#

Exact local length-magnification factor of the log-spiral conformal map at a given chordwise coordinate.

w = X + iY, as a function of u = chordwise + i*pitchwise, is w = C * exp(a*u) with a = log(r2/r1)/c_axial and C a constant complex number – a holomorphic (complex-differentiable) function of u. For any holomorphic map, the local length-magnification factor is exactly |dw/du| = |a| * |w| = |a| * r (the map’s own r-formula, at this chordwise coordinate; the absolute value matters here – this module’s own _reference_point_and_scale anchors x1 at the chord MAXIMUM, which makes c_axial = min(chord)-max(chord), hence a, negative by convention, even though the magnification itself is always positive). A length measured in the 2D cascade plane at this chordwise location is scaled by exactly this factor once mapped onto the annulus – not an approximation, and no numerical search needed.

The conformal map preserves ANGLES (that’s what “conformal” means), not lengths – e.g. the 2D-plane throat_opening is not the actual radial-passage throat width; see mapped_throat_width.

turbo_moc.geometry.radial.consistent_radial_wrap(blade_data, r1, r2_target, source='stator')[source]#

Blade count and outer radius that keep the designed 2D passage.

Under the log-spiral map one 2D pitch p turns by |a| p, a = ln(r2/r1) / c_axial, at every radius; the copies are laid out 2*pi/N apart. Both agree only for N = 2*pi / (|a| p) (implied_n_blades_radial, which is exactly this, independent of where it is evaluated). So N is the nearest integer to that count at (r1, r2_target), and r2 is then moved to the radius where it is exact:

|ln(r2/r1)| = 2*pi |c_axial| / (N p),   same side of r1 as r2_target

Returns n_blades, r2, and the target’s non-integer implied count.

turbo_moc.geometry.radial.mapped_throat_width(blade_data, r1, r2, source='stator')[source]#

The 2D-plane throat_opening, rescaled by the EXACT local conformal length factor at the throat’s own (chordwise, pitchwise) location – see conformal_length_scale. Uses the SAME reference (x1, c_axial) the actual wrap (wrap_blade_radial/wrap_rotor_blade_radial) derives from the main blade contour, so the result is consistent with the geometry that actually gets rendered/exported.

blade_datadict

Stator: needs “blade_curve” ({“x”,”y”}) and “throat_point” ({“x”,”y”}, see parametrize_stator_blade/_semi) and “throat_opening”. Rotor: this tool’s rotor method has no throat_opening/throat_point – not supported here (its sizing is pitch-consistency only to begin with, see turbo_moc.geometry.sizing.size_rotor_from_pitch).

turbo_moc.geometry.radial.rotate_radial_points(x, y, angle_rad)[source]#

Rigid rotation by angle_rad about the origin – lays out the n_blades copies around the annulus after mapping.

turbo_moc.geometry.radial.wrap_blade_radial(blade_data, r1, r2, n_blades, theta0=0.0)[source]#

Wrap a Phase-2-style stator blade (parametrize_stator_blade/_semi’s output dict) onto an annular passage between radii r1 and r2.

Parameters:
blade_datadict

Needs “blade_curve” and “trailing_edge” ({“x”, “y”} each, mm).

r1, r2float

Inner/outer radii of the annular passage (same length units as blade_data, mm), reached at the blade’s own chordwise extremes.

n_bladesint

Number of blade copies laid out around the full annulus.

theta0float

Angular offset [rad] of the first (index-0) blade copy.

Returns:
dict

“blades”: list of n_blades {“x”, “y”} closed-contour dicts; “trailing_edges”: matching list for the trailing-edge arc; “r1”/”r2”/”n_blades”/”d_theta” echoed back; “units”.

turbo_moc.geometry.radial.wrap_curve_radial(x, y, r1, r2, n_blades, theta0=0.0, chordwise_axis='y')[source]#

Wrap ONE closed (or open) curve (x, y) onto an annular passage between radii r1/r2, laid out as n_blades copies spaced 2*pi/n_blades apart. chordwise_axis picks which of the curve’s own axes runs chordwise (the one that maps to radius) – “y” for a stator-convention curve, “x” for a rotor-convention one; see module docstring.

Returns a list of n_blades {“x”, “y”} dicts (the mapped curve, one per copy) plus the reference (x1, y1, c_axial, d_theta) used, for reuse when wrapping a second curve (e.g. a trailing edge) with the SAME reference frame as this one.

turbo_moc.geometry.radial.wrap_rotor_blade_radial(rotor_data, r1, r2, n_blades, theta0=0.0)[source]#

Wrap a Phase-3-style rotor blade (turbo_moc.rotor.design_rotor_vortex_blade’s output dict) onto an annular passage between radii r1 and r2.

Parameters:
rotor_datadict

Needs “blade” ({“x”, “y”}) – the closed LE/TE-rounded blade contour. Its trailing edge is already part of this closed curve (unlike the stator’s, which keeps a separate arc), so there is no separate trailing-edge overlay here. Its units don’t matter (it may well be nondimensional, r*): the map normalises the contour by its own chordwise extent, so a uniformly rescaled blade wraps to exactly the same result.

r1, r2float

Radii [mm] the blade’s chordwise extremes map to; the result is in the same units.

n_bladesint

Number of blade copies laid out around the full annulus.

theta0float

Angular offset [rad] of the first (index-0) blade copy.

Returns:
dict

Same shape as wrap_blade_radial’s return, with “trailing_edges” as a list of empty {“x”: [], “y”: []} placeholders (nothing extra to draw – kept for a uniform shape with the stator’s result, so plot_radial_cascade works unchanged for either source).