Patch¶
Patch classes for specifying boundary conditions on structured blocks.
Limiting index rules¶
Patches are defined by specifying which block face or part of a face they are
on. Every Patch subclass is constructed as:
PatchType(i=..., j=..., k=..., label=...)
taking one argument for each of the three indexing directions i, j,
and k, subject to the following rules:
The first point in a direction is indexed 0; negative indices wrap around such that -1 is the last point.
Indices are inclusive, so
i=(0,-1)spans the entire range oficoordinate.Integer arguments are interpreted as a constant value of that index:
i=0means the patch spans the first i face;j=-1means the patch spans the last j face. Integer arguments are shorthand for, e.g.i=(0,0).Patches must be 2D subsets of an external face of the block. This implies that at least one constant dimension must be specified with a value 0 or -1.
Omitting a direction argument implies the patch should include every point in that direction, and is shorthand for e.g.
j=(0, -1).The elements of a direction tuple should be in ascending order after negative indices are wrapped.
k=(6,4)is not valid, and neither isk=(-1, -2).
Types of patches¶
The different types of patches (e.g. periodic, inlet, outlet) are represented
by subclasses of the abstract base class Patch,
but are all initialised by passing in the limiting indices and an optional
label for later debugging. Storing boundary condition information or matching
connections between patches is handled by methods on the subclasses.
Storage and attachment to blocks¶
A patch can be constructed in isolation without a Block.
However, any property that depends on block geometry – resolving negative
indices to absolute coordinates, computing patch size, or accessing block
coordinate views – will raise an error until the patch is attached to a block.
Patches are stored in a BlockPatchCollection accessible at block.patches.
Adding a patch to this collection automatically attaches it to the block,
validates its limits against the block shape, and checks that it does not
spatially overlap with any existing patch of the same type on the same face.
The collection supports retrieval by integer index, by string label, and by
patch type:
p = block.patches[0] # by index
p = block.patches["inlet_hub"] # by label
ps = block.patches.inlet # list of all InletPatch objects
The type-grouped properties (inlet, outlet, periodic,
rotating, etc.) each return a plain list.
Mutation is through the standard collection interface: append, extend,
insert, and del:
block.patches.append(InletPatch(i=0, label="inlet_hub"))
del block.patches["inlet_hub"]
len() returns the number of patches on the block.
A GridPatchCollection at grid.patches provides a corresponding
read-only aggregate view across all blocks in a
Grid. It supports integer indexing, slicing,
iteration, len(), and the same type-grouped properties (e.g.
grid.patches.periodic), but does not support string-key access or any
mutation methods.
Example usage¶
# example: patch_examples
import numpy as np
import ember.patch
from ember.block import Block
from ember.fluid import PerfectFluid
# Build a block with axial (x), radial (r), and circumferential (t) coordinates.
# The inlet and outlet are characteristic conditions, so the block has to be
# a whole blade passage: t spans exactly one pitch of the set blade count.
fluid = PerfectFluid(cp=1005.0, gamma=1.4, mu=1.8e-5, Pr=0.7)
block = Block(shape=(4, 5, 6))
block.set_fluid(fluid)
block.set_Nb(12)
pitch = 2.0 * np.pi / 12
block.set_x(np.linspace(0.0, 1.0, 4).reshape(-1, 1, 1) * np.ones((4, 5, 6)))
block.set_r(np.linspace(0.5, 1.0, 5).reshape(1, -1, 1) * np.ones((4, 5, 6)))
block.set_t(np.linspace(0.0, pitch, 6).reshape(1, 1, -1) * np.ones((4, 5, 6)))
# Add inlet, outlet, and periodic patches; appending attaches each to the block.
inlet = ember.patch.InletPatch(i=0, label="inflow")
outlet = ember.patch.OutletPatch(i=-1, label="outflow")
block.patches.extend([inlet, outlet])
block.patches.extend([
ember.patch.PeriodicPatch(k=0, label="lower"),
ember.patch.PeriodicPatch(k=-1, label="upper"),
])
print(len(block.patches)) # 4
print(len(block.patches.periodic)) # 2
# Patch shape and size are resolved against the block once attached.
# shape is (1, 5, 6): one i-plane, full j and k extent.
print(inlet.size) # 30
print(inlet.shape) # (1, 5, 6)
# const_dim is the index of the constant face (0=i, 1=j, 2=k).
print(inlet.const_dim) # 0
print(block.patches["lower"].const_dim) # 2
# Negative indices are stored as-is and resolved to absolute coordinates on demand.
# i=(1,-2), k=(1,-2) on shape (4,5,6): i=-2 resolves to 2, k=-2 resolves to 4.
partial = ember.patch.InviscidPatch(j=-1, i=(1, -2), k=(1, -2), label="tip_partial")
block.patches.append(partial)
print(partial.ijk_lim_abs[0, 1]) # 2
print(partial.ijk_lim_abs[2, 1]) # 4
print(partial.shape) # (2, 1, 4)
# Use the patch slice to index block coordinate arrays directly.
print(block[inlet.slice].x.shape) # (1, 5, 6)
print(block.xrt[inlet.slice].shape) # (1, 5, 6, 3)
# Set inlet stagnation conditions and flow angles. Each is imposed on the
# pitchwise mean at every span station, so values are a scalar or a
# spanwise profile.
inlet.set_Po_To(Po=2e5, To=1200.0)
inlet.set_Alpha(0.0)
inlet.set_Beta(0.0)
print(inlet.ho_nd.shape) # (1, 5, 1)
nj = inlet.shape[1]
Po_span = np.linspace(1.8e5, 2.2e5, nj).reshape(1, nj, 1)
inlet.set_Po_To(Po=Po_span, To=1200.0)
# Set static pressure on the outlet.
outlet.set_P(1e5)
print(outlet.P_nd.shape) # (1, 5, 1)
# Set angular velocity on a rotating wall patch.
rot_patch = ember.patch.RotatingPatch(j=0, label="hub")
block.patches.append(rot_patch)
rot_patch.set_Omega(500.0)
print(rot_patch.rpm) # 4774.648
Base classes¶
- class patch.Patch(i=(0, -1), j=(0, -1), k=(0, -1), label=None)[source]¶
Bases:
ABCInitialize with start and end indices for each dimension.
Indices are inclusive and a single integer sets a constant value in that dimension. See
ember.patchfor the full index rules.- Parameters:
- set_i_lim(i)[source]¶
Set the start and end indices on the i dimension.
Indices are inclusive and a single integer sets a constant value in that dimension. See
ember.patchfor the full index rules.
- set_j_lim(j)[source]¶
Set the start and end indices on the j dimension.
Indices are inclusive and a single integer is shorthand for a constant face, e.g.
j=0is equivalent toj=(0, 0). Seeember.patchfor the full index rules.
- set_k_lim(k)[source]¶
Set the start and end indices on the k dimension.
Indices are inclusive and a single integer is shorthand for a constant face, e.g.
k=0is equivalent tok=(0, 0). Seeember.patchfor the full index rules.
- get_ijk_face(perm=(0, 1, 2), flip=())[source]¶
Block indices for faces on the patch.
For example the constant k face bounded by (i -> i+1) and (j -> j+1) has indices (i, j, k).
- attach_to_block_resampled(block, src)[source]¶
Attach to a resampled
block, carryingsrc’s span-varying state.srcis this patch’s still-attached original on the gridblockwas resampled from, and is the only place the source span stations can be read from once the copy has been re-attached. The base implementation is a plainattach_to_block(): patch state that is one number, or none at all, follows a block onto any node count without help. Patch types holding a value per span station override this to interpolate it onto the new stations.Used by
resample(), which is what puts a configured patch on a coarser grid – the multigrid hierarchy ofember.solver.Solver.run_fmg(), among others.
- attach_to_block(block)[source]¶
Attach this patch to a block and validate limits against block shape.
Do not call directly; attachment is handled automatically when a patch is added to
patchesviaBlockPatchCollection.- Parameters:
block (
Block) – The block this patch belongs to. A weak reference is stored.
- check_match(other, rtol=1e-06)[source]¶
Check if this patch matches another patch for pairing purposes.
Base implementation always returns None. Subclasses should override this method to implement their specific matching criteria.
- copy()[source]¶
Return a new unattached patch of the same type with the same limits, label, and boundary condition state.
The returned patch is fully independent: it shares no mutable state with the original and is not attached to any block. Attach it to a block via
block.patches.append(copy)before using geometry-dependent properties.Boundary condition parameters (e.g. stagnation conditions on an inlet, static pressure on an outlet) are copied; any cached solver state that depends on block geometry is not.
- update_ref_scales()[source]¶
Re-derive anything this patch holds in nondimensional form.
Called by
Blockon every attached patch whenever the reference scales change –set_fluid()andset_L_ref()– after the block has swapped the scales and rescaled its own stored field, so an override reads the new scales straight offself.block.A patch that caches a nondimensional value must override this and either re-derive it from the raw dimensional quantity it came from, or discard it so it is rebuilt on next use. Leaving a stale nondimensional cache behind does not raise: it silently imposes the wrong physics, which is why this is the one hook a patch subclass has to know about.
What the base implementation handles is the sliced views of the face this class caches at
attach_to_block(). They share the block’s data, so they see the rescaled field, but they carry derived-property caches of their own that the block’s ownclear_cachedoes not reach – and a patch reads the face through them.
- property block¶
Access the parent block this patch is attached to.
- property block_view¶
Sliced view of the parent block at this patch location;
Blockwith shapeshape.Equivalent to
block[patch.slice]. Cached atattach_to_block()to avoid repeated sliced Block creation overhead.
- property block_view_offset_1¶
Sliced view one layer interior to the patch face;
Blockwith shapeshape.Used to read the outgoing characteristic state (e.g. entropy at a subsonic outlet) from the first interior layer. Equivalent to
block[patch.slice]offset by one along the constant dimension. Cached atattach_to_block()to avoid repeated sliced Block creation overhead.
- property const_dim¶
Axis of the constant dimension;
intin{0, 1, 2}.
- property ien¶
End index in the i dimension;
int.
- property ijk_lim_abs¶
Limits with negative indices resolved to positive;
ndarrayof shape(3, 2).
- property ist¶
Start index in the i dimension;
int.
- property jen¶
End index in the j dimension;
int.
- property jst¶
Start index in the j dimension;
int.
- property ken¶
End index in the k dimension;
int.
- property kst¶
Start index in the k dimension;
int.
- property label¶
String identifier for the patch;
strorNone.
- property shape¶
Extent of the patch in each dimension as
(ni, nj, nk); the constant dimension is always 1.
- property slice¶
tupleofsliceobjects for indexing the parent block array.
- property xrt_centre¶
Centre coordinates of the patch as
(x, r, t);ndarrayof shape(3,).
- class patch.RevolutionPatch(i=(0, -1), j=(0, -1), k=(0, -1), label=None)[source]¶
Bases:
PatchPatch on a surface of revolution.
Intermediate base class for patches that require surface-of-revolution geometry (inlet, outlet, mixing). A surface of revolution is an annular or axisymmetric surface where one patch axis is purely circumferential (the pitch direction, along which only theta varies) and the other runs meridionally from hub to tip (the span direction, along which both x and r vary).
When a patch is added to a block via
patches,attach_to_block()automatically identifies the span and pitch axes from the block geometry. It raisesValueErrorif the geometry is not a surface of revolution (i.e. a pitch axis with constant x and r cannot be found).Two operations are provided. The first is pitch-averaging: computing a circumferentially averaged flow state that subclasses (inlet, outlet, mixing) use to apply boundary conditions.
The second is the interface frame. A surface of revolution need not be a plane of constant \(x\), so a condition written against the velocity through the face cannot read that velocity off \(V_x\). The face normal is derived from the geometry, one direction per span node, and
resolve_to_interface()/resolve_from_interface()turn the meridional momentum into that frame and back, held for the duration of a calculation so a subclass can be written entirely in terms of a face-normal velocity and work at any orientation; seeNonReflectingPatchfor what that buys and what it costs. On a plane of constant \(x\) the rotation is the identity and is skipped, so a subclass pays nothing for the generality it does not use.- set_block_avg()[source]¶
Compute pitch-averaged conserved variables and store in block_avg.
Uses node-based pitch weights to compute a weighted sum of
block_view.conserved_ndover the pitch dimension, writing the result directly intoself.block_avg.conserved_nd.
- attach_to_block(block)[source]¶
Attach to block and detect surface-of-revolution geometry.
Calls the base Patch attach, then determines span/pitch dimensions and computes meridional properties. Raises ValueError if the patch is not a surface of revolution.
- resolve_from_interface()[source]¶
Rotate block_view momentum in-place from (norm, span) to (x, r) coordinates.
Inverse of
resolve_to_interface:rhoV_norm -> rhoVx = cosxi * rhoV_norm - sinxi * rhoV_span rhoV_span -> rhoVr = sinxi * rhoV_norm + cosxi * rhoV_span
A no-op on a face whose frame axis already is \(x\); see
chi_node.
- resolve_to_interface()[source]¶
Rotate block_view momentum in-place from (x, r) to (norm, span) coordinates.
Modifies
block_view.conservedso that the axial and radial momentum components become the interface-normal and interface-span components:rhoVx -> rhoV_norm = cosxi * rhoVx + sinxi * rhoVr rhoVr -> rhoV_span = -sinxi * rhoVx + cosxi * rhoVr
Uses the pre-computed to-interface rotation matrix, broadcast along
span_dimto match the full block shape. A no-op on a face whose frame axis already is \(x\); seechi_node.
- property chi_node¶
Angle of the frame axis from \(+x\), one value per span node.
The meridional-plane angle \(\chi\) that
resolve_to_interface()rotates through, so that \(V_n = \cos\chi\, V_x + \sin\chi\, V_r\) is the velocity along the frame axis and \(V_s = -\sin\chi\, V_x + \cos\chi\, V_r\) the one in the surface. Zero on a face whose frame axis is \(+x\).- Returns:
Angle [rad], shaped to broadcast over the patch along its span dimension.
- Return type:
array
- smooth_pitch_121(field, alpha)[source]¶
Apply a periodic 1-2-1 smoothing pass along the pitch axis.
Returns
alpha * smoothed + (1 - alpha) * fieldwheresmoothedis one pass of the discrete 1-2-1 filterf[i] = (f[i-1] + 2*f[i] + f[i+1]) / 4with periodic wrap alongpitch_dim. The pitch direction is circumferential, so periodic wrap is exact for an annular passage.The 1-2-1 filter has amplification \(\cos^2(k\Delta/2)\): it preserves the pitch mean and smooth variation, and annihilates the Nyquist (sawtooth) mode. Blending with the unsmoothed field by
alphatunes the strength:alpha=1is a full 1-2-1 pass,alpha=0leaves the field unchanged.
- update_ref_scales()[source]¶
Re-sync the pitch-averaged block to the parent’s reference scales.
block_avgis aBlockof its own, holding coordinates and a pitch-mean flow field nondimensionalised against the scales in force when the patch attached. Both have to follow the parent block, or the pitch average decodes this block’s state against stale scales – for the length scale that means angular momentum, the one conserved variable carrying it, and every quantity derived from the resulting tangential velocity.Each half is applied only when its scale actually moved, so the commoner call does not put the averaged field through a needless dimensional round trip and its float32 rounding.
- property block_avg¶
Pitch-averaged flow field;
Blockof shape(nspan,).Coordinates are the pitch-mean x, r, t at each span station. The conserved variables are populated by calling
set_block_avg(); before that call the flow-field arrays contain uninitialised values.
- property pitch_dim¶
Axis of the pitchwise (circumferential) dimension;
intin{0, 1, 2}.Detected automatically from block geometry on
attach_to_block(): the axis along which only theta varies while x and r remain constant.
- property span_dim¶
Axis of the spanwise (meridional) dimension;
intin{0, 1, 2}.Detected automatically from block geometry on
attach_to_block(): the axis along which x and r vary (hub to tip).
- property spf¶
Span fraction at each node, normalised to
[0, 1]by meridional arc-length;ndarrayof shape(nspan,).spf[0] == 0.0at the hub/start corner andspf[-1] == 1.0at the tip/end corner. Spacing reflects the actual meridional distances between nodes, not their indices.
- class patch.NonReflectingPatch(i=(0, -1), j=(0, -1), k=(0, -1), label=None)[source]¶
Bases:
RevolutionPatchThe steady non-reflecting boundary condition.
Subclasses declare a description for error messages, the inward face normal (or leave the geometry to decide it), the target space’s name and Jacobian, a mapping of each required target row to the setter that fills it, and which rows are seeded from the flow when nothing prescribes them. They add the setters themselves and nothing else: the characteristic treatment, both harmonic relations and the reversed-flow handling are all here.
update_soln()andadvance()are called once per timestep, the first refreshing the reference state to match Giles’ definition of the characteristic variables as perturbations about the time-level-\(n\) average, the second taking the condition’s one under-relaxed step on it.apply()is called once per Runge-Kutta stage and only imposes what those two settled, so the rate of the condition does not scale with the stage count.The face may be any surface of revolution; the condition works in the interface frame and the module docstring says how. What it is restricted to is a mean state subsonic both normal to the face and absolutely, which is checked and warned about. That restriction does not concern the direction of the flow through the face: a span station whose mean has reversed simply takes the other characteristic split, and drives the quantities that split prescribes toward rows of the same target.
- sigma = 0.05¶
Under-relaxation of the characteristic correction, Giles Eq. 5.25, needed for wellposedness. He suggests 1/N for N pitchwise nodes, applied once per timestep, and
advance()takes it exactly once per timestep, so the two are in the same units: set it to 1/N and it is 1/N. The bound is not about the transform amplifying – it cannot, its norm grows only logarithmically – but about how far the pitchwise-nonlocal harmonic relations may spread information in one application while the explicit interior march moves it one cell. Overridden byrf_inlet/rf_outletat the start of a run.
- ho_nd¶
Read-only view of one row of a patch’s prescribed target vector.
A descriptor rather than a plain attribute, so that the named rows stay views on
NonReflectingPatch._targetwith nothing to re-link when a patch is copied or unpickled, and so that a name the patch’s target space does not carry raises rather than quietly returning whatever that row holds: an inflow condition working in angles has noVr_nd, and one working in mix variables has notanAlpha.Resolution is by name against
NonReflectingPatch._target_namesof the instance, not by a fixed index, because the row order is a property of the target space and the classes do not share one.
- s_nd¶
Read-only view of one row of a patch’s prescribed target vector.
A descriptor rather than a plain attribute, so that the named rows stay views on
NonReflectingPatch._targetwith nothing to re-link when a patch is copied or unpickled, and so that a name the patch’s target space does not carry raises rather than quietly returning whatever that row holds: an inflow condition working in angles has noVr_nd, and one working in mix variables has notanAlpha.Resolution is by name against
NonReflectingPatch._target_namesof the instance, not by a fixed index, because the row order is a property of the target space and the classes do not share one.
- Vr_nd¶
Read-only view of one row of a patch’s prescribed target vector.
A descriptor rather than a plain attribute, so that the named rows stay views on
NonReflectingPatch._targetwith nothing to re-link when a patch is copied or unpickled, and so that a name the patch’s target space does not carry raises rather than quietly returning whatever that row holds: an inflow condition working in angles has noVr_nd, and one working in mix variables has notanAlpha.Resolution is by name against
NonReflectingPatch._target_namesof the instance, not by a fixed index, because the row order is a property of the target space and the classes do not share one.
- Vt_nd¶
Read-only view of one row of a patch’s prescribed target vector.
A descriptor rather than a plain attribute, so that the named rows stay views on
NonReflectingPatch._targetwith nothing to re-link when a patch is copied or unpickled, and so that a name the patch’s target space does not carry raises rather than quietly returning whatever that row holds: an inflow condition working in angles has noVr_nd, and one working in mix variables has notanAlpha.Resolution is by name against
NonReflectingPatch._target_namesof the instance, not by a fixed index, because the row order is a property of the target space and the classes do not share one.
- tanAlpha¶
Read-only view of one row of a patch’s prescribed target vector.
A descriptor rather than a plain attribute, so that the named rows stay views on
NonReflectingPatch._targetwith nothing to re-link when a patch is copied or unpickled, and so that a name the patch’s target space does not carry raises rather than quietly returning whatever that row holds: an inflow condition working in angles has noVr_nd, and one working in mix variables has notanAlpha.Resolution is by name against
NonReflectingPatch._target_namesof the instance, not by a fixed index, because the row order is a property of the target space and the classes do not share one.
- sinBeta¶
Read-only view of one row of a patch’s prescribed target vector.
A descriptor rather than a plain attribute, so that the named rows stay views on
NonReflectingPatch._targetwith nothing to re-link when a patch is copied or unpickled, and so that a name the patch’s target space does not carry raises rather than quietly returning whatever that row holds: an inflow condition working in angles has noVr_nd, and one working in mix variables has notanAlpha.Resolution is by name against
NonReflectingPatch._target_namesof the instance, not by a fixed index, because the row order is a property of the target space and the classes do not share one.
- P_nd¶
Read-only view of one row of a patch’s prescribed target vector.
A descriptor rather than a plain attribute, so that the named rows stay views on
NonReflectingPatch._targetwith nothing to re-link when a patch is copied or unpickled, and so that a name the patch’s target space does not carry raises rather than quietly returning whatever that row holds: an inflow condition working in angles has noVr_nd, and one working in mix variables has notanAlpha.Resolution is by name against
NonReflectingPatch._target_namesof the instance, not by a fixed index, because the row order is a property of the target space and the classes do not share one.
- advance()[source]¶
Take the boundary condition’s one step; call once per timestep.
The change in the incoming characteristics is scaled by
sigma, which is exactly Giles’ Eq. 5.25 correction. This is the whole of a timestep’s boundary-condition change:apply()only imposes the result, once per stage.Per timestep and not per stage because the harmonic relations couple every pitchwise node to every other through the Hilbert transform, so one application can spread information across the whole pitch while the explicit interior march moves it one cell. Giles’ \(1/N\) for \(N\) pitchwise nodes is the restriction that keeps the two in step, and it is a bound per timestep; taking the step once per stage multiplied the rate by the stage count and left
sigmadependent on the integrator.A no-op until something has been prescribed, so that a patch missing a setter still reports it from
apply()rather than from here.
- apply()[source]¶
Impose the non-reflecting condition on the patch.
Called once per Runge-Kutta stage, and imposes only: the outgoing characteristics are re-read from the marched face every stage so a wave reaching the boundary still passes through within the step, while the incoming ones are the state
update_soln()last authored. Thesigma-relaxed correction that advances that state is taken there, once per timestep, not here.A node-level override is then given the chance to change what actually reaches the block, and its result is deliberately not carried back into the state the solve is still working from, so a condition that has to depart from its own linear theory somewhere does not thereby corrupt the characteristic state it is still solving on.
- attach_to_block(block)[source]¶
Attach to a block, validate the boundary plane and build the transform.
Safe to call repeatedly; a target of the right shape survives re-attachment, and one of the wrong shape is rebuilt at the new shape rather than silently misread – every prescribed row by re-running the setter that filled it, the rest by re-seeding. Replay is at the original arguments, so a prescribed spanwise profile reaches the setter at the length it was set on and is refused if the span station count has moved. Going onto a coarser grid, as the multigrid hierarchy and
resample()do it, therefore goes throughattach_to_block_resampled(), which interpolates those profiles onto the new stations first.
- attach_to_block_resampled(block, src)[source]¶
Attach to a resampled
block, interpolating prescribed profiles.A prescribed row is recorded as the setter call that filled it (see
replayable()), arguments as the caller gave them – which for a spanwise profile is one number per span station of the grid the patch was configured on. Replaying that call against a block with a different number of stations, asattach_to_block()does on its own, hands the setter a profile of the wrong length and it refuses it. So the replay is deferred and every profile argument re-expressed on this patch’s own stations first, interpolated against span fraction (spf, meridional arc-length) rather than node index, so a prescription follows the geometry it was written against and not the mesh spacing.Scalar arguments pass through untouched, which is what makes this agree with plain re-attachment wherever plain re-attachment worked.
- update_ref_scales()[source]¶
Re-derive the prescribed target against the block’s current fluid.
Every
replayable()setter that filled a row is re-run with the dimensional arguments it was given, in the order it was given them, so a prescribed condition keeps meaning what it says:set_Po_To(4e5, 300)is four bar and three hundred kelvin whatever reference scales and datum come to be in force. That makesset_fluid()safe to call at any point, rather than only before the patches are configured.Rows nothing prescribed are cleared instead, to be taken afresh from the rescaled face – they are a frozen picture of the flow, and the only honest way to re-express one is to look again. They are cleared before the replay so that a row which is prescribed, and merely happens to be seedable, is refilled by its own setter.
The characteristic state is nondimensional with no dimensional original to return to, so it is dropped and rebuilt from the face. A fluid changed mid-march therefore restarts the condition from the marched state rather than continuing on the one it was solving: a small perturbation, and the alternative is carrying numbers that mean nothing under the new scales.
- update_soln()[source]¶
Refresh the frozen reference state; call once per timestep.
Re-derives the pitchwise-mean state and every Jacobian evaluated on it, which
apply()then holds fixed across the Runge-Kutta stages of the step. Snapshots the density first, so a reversed node’s density is relaxed from the start-of-step value rather than from whatever the last stage happened to leave.Pairs with
advance(), which takes the boundary condition’s own step on the reference this leaves behind.
Patch types¶
- class patch.PeriodicPatch(i=(0, -1), j=(0, -1), k=(0, -1), label=None)[source]¶
Bases:
PatchConnected boundary to another block.
This applies to patches which are exactly coincident, and patches which are periodic in the circumferential direction of an annular domain. The patches should come in pairs, and connectivity between the two patches is detected automatically. The conserved variables on the two patches are averaged at each time step to enforce periodicity.
- class patch.InletPatch(i=(0, -1), j=(0, -1), k=(0, -1), label=None)[source]¶
Bases:
NonReflectingPatchSubsonic inflow boundary condition.
Prescribes stagnation enthalpy \(h_0\), entropy \(s\), yaw angle \(\alpha\) and pitch angle \(\beta\) as pitchwise-mean quantities, while absorbing outgoing acoustic waves rather than reflecting them. All four must be set before
apply()is called, viaset_ho_s()orset_Po_To()together withset_Alpha()andset_Beta(). Each setter converts its target and stores it nondimensionally in the corresponding row of the prescribed target, published asho_nd,s_nd,tanAlphaandsinBeta, so the patch must already be attached to a block whose fluid is set.The angles are what makes this condition different from every other in the family. A physical inlet knows its flow angles and not its velocity magnitude, so \((\tan\alpha, \sin\beta)\) are the right variables here, and the target space’s Jacobian is
chic_to_bcond()rather than the base class’schic_to_mix(). Only rows 2 and 3 of the two Jacobians differ; rows 0, 1 and 4 are identical.A span station whose mean flow has reversed becomes an outflow, and the base class drives it to the static pressure of row 4 instead. Nothing need be configured for that:
set_backflow_P()prescribes the pressure and, left alone, it is seeded from the inflow plane at the first timestep. The angle rows are not solved there – row 4 is static pressure in both target spaces – so the factor of \(V_x\) those rows carry never takes the solve singular.- set_backflow_P(P)[source]¶
Prescribe the static pressure imposed where the inflow reverses.
A span station whose pitchwise-mean flow has turned round is an outflow: four of its five characteristics leave the domain and only one enters, so one quantity is prescribed and it is static pressure, not the inflow state. This is that pressure. The other four rows are not imposed at such a station – they are what the outgoing waves carry there.
Calling this is optional. Left alone, the row is seeded once from the pitchwise mean of the inflow plane at the first timestep and frozen there. If a large part of the span ends up reversed the inflow is no longer under control and the boundary wants moving upstream, rather than this value tuning.
- Parameters:
P (float or array) – Static pressure \(p\) [Pa]; must be positive and finite. A scalar or an array that broadcasts to
shape, of which only the pitchwise mean at each span station is imposed.
See also
ember.patch.OutletPatch.set_backflow_ho_sThe mirror of this, prescribing the inflow state an outflow face falls back on
- set_Beta(Beta)[source]¶
Prescribe the inflow pitch angle.
The angle is measured from the machine axis, \(\tan\beta = V_r / V_x\), whatever the orientation of the face it is prescribed on. The condition itself works in the interface frame, where the same flow makes the angle \(\beta - \chi\) with the frame axis, so what is stored is \(\sin(\beta - \chi)\) for the face angle
chi_node. On a face of constant \(x\) the frame axis is \(x\) and the two coincide.What it must satisfy is that flow actually comes in through the face: \(|\beta - \chi| \leq 90\). On a face of constant \(x\) that is the familiar \(|\beta| \leq 90\), and on a radial or reversed face it is the same condition said properly – a duct running along \(-x\) takes \(\beta = 180\), one flowing inward radially \(\beta = -90\).
- set_ho_s(ho, s)[source]¶
Prescribe the inflow stagnation enthalpy and entropy.
Both are measured from the fluid datum state where \(u = s = 0\) at \((p_\mathrm{dtm}, T_\mathrm{dtm})\), the same convention as
hoands; only differences are physically meaningful, so these are not \(c_p T_0\) and \(c_p \log(\ldots)\). Useset_Po_To()to prescribe a stagnation state instead.
- set_Po_To(Po, To)[source]¶
Prescribe the inflow stagnation pressure and temperature.
Converted to the stagnation enthalpy and entropy of
set_ho_s()using the fluid of the block this patch is attached to. The prescription is what survives, not the conversion: the pressure and temperature given here are kept, and a later change of fluid re-converts them against the new one, so this stays the stagnation state asked for rather than whatever number the old reference scales and datum made of it.
- property Alpha¶
Prescribed inflow yaw angle \(\alpha\) [deg]. Inverse of
set_Alpha().
- property Beta¶
Prescribed inflow pitch angle \(\beta\) [deg], measured from the machine axis.
Inverse of
set_Beta(): the stored sine is that of the face-frame angle \(\beta - \chi\), whichset_Beta()keeps within \(\pm 90\) degrees, exactlynumpy.arcsin’s range, so the recovery is exact up to that wrap.
- property Po¶
Prescribed inflow stagnation pressure [Pa].
Recovered from the currently stored
ho_ndands_ndthrough the block’s fluid, so it reads back the prescribed state however it was set – byset_Po_To()or byset_ho_s()– rather than undoing one setter in particular.See also
ToThe stagnation temperature this state also implies
- class patch.OutletPatch(i=(0, -1), j=(0, -1), k=(0, -1), label=None)[source]¶
Bases:
NonReflectingPatchSubsonic outflow boundary condition.
Prescribes the static pressure \(p\) as a pitchwise-mean quantity at each span station, while absorbing outgoing waves rather than reflecting them. It must be set before
apply()is called, viaset_P(), which stores its target nondimensionally inP_nd, so the patch must already be attached to a block whose fluid is set.Giles takes the mean-mode residual against the flux-averaged pressure; the mean here is the weighted pitch mean of
weight_pitch, the same average every other residual in the family is taken against.set_adjustment()adds a spanwise radial-equilibrium profile to the prescribed pressure, re-derived from the solution once per timestep byupdate_target(). Without it the prescribed pressure is imposed on every span station alike, which for a swirling exit flow fights the centrifugal pressure gradient the flow is trying to establish.set_throttle()turns the prescribed pressure into a starting point rather than the condition: a proportional-integral controller moves the level each timestep until the patch passes a target mass flow. The pressure is still what the boundary imposes – the throttle only chooses which pressure – so the characteristic treatment is untouched by it.set_backflow_ho_s()(orset_backflow_Po_To()) andset_backflow_Vt()prescribe the inflow state a reversed span station is driven to; see those methods and the module docstring. Its meridional direction is not prescribed and cannot be: backflow comes in normal to the exit surface, so the row that would carry it is pinned at zero. Unlike an angle, a meridional velocity cannot be resolved onto a face of arbitrary orientation without knowing the normal component, which is what the reversed-station solve derives from \(h_0\) – so there is nothing consistent for a setter to mean.- set_adjustment(radial_equilibrium=True, rf=0.1)[source]¶
Configure the spanwise adjustment to the prescribed pressure.
Swirling flow leaving a blade row carries a centrifugal radial pressure gradient. Prescribing one pressure at every span station fights it and induces unnatural streamline curvature, so the adjustment adds the profile satisfying \(dp/dr = \overline{\rho V_\theta}\, \overline{V_\theta}/r\), integrated from the hub, where the prescribed pressure is then the value enforced. It is re-derived from the solution by
update_target()once per timestep and relaxed toward the new value:\[\Delta p^\mathrm{new} = \mathit{rf}\,\Delta p + (1 - \mathit{rf})\,\Delta p^\mathrm{old}\]Off unless this method is called, and incompatible with a non-scalar
set_P(), which would prescribe a spanwise profile of its own and double count.There is no dynamic-head term. Such an offset has zero pitchwise mean at every span station by construction, and this patch imposes nothing but pitchwise means, so it would be annihilated exactly.
- set_backflow_ho_s(ho, s)[source]¶
Prescribe the stagnation enthalpy and entropy imposed where the exit flow reverses.
Reversal is carried at two levels, and both draw on the four backflow quantities this and its companion setters prescribe.
A span station whose mean has reversed is genuinely an inflow plane and is treated as one. Four of its five characteristics turn incoming, so four quantities have to be prescribed, and the four backflow rows are exactly they; the one wave still leaving, the downstream-running pressure wave, is carried through from the interior as always. The prescribed static pressure is not imposed at such a station: pressure is what the free wave carries there. If a large part of the span ends up reversed the exit level is no longer under control, and the boundary wants moving downstream rather than the condition made cleverer.
A node whose interior neighbour is pushing flow inward, at a station whose mean is still forward, is overwritten with the same four quantities and a density, the one quantity those four leave free, taken from the interior: relaxed from its start-of-step value toward the current one at a rate that falls away with the local axial Mach number, and capped to keep the axial velocity real. There is no characteristic split to change at that level – the split belongs to the station’s mean, and the Hilbert transform couples every node of a station to every other – so this one is a limiter on the linear theory, applied to what reaches the block and kept out of the state the solve carries forward.
The rows that can be prescribed are set independently, by this method or
set_backflow_Po_To()for the thermodynamic pair and byset_backflow_Vt()for the swirl, so a run can prescribe one and leave the rest seeded. The meridional direction is not among them: the backflow comes in normal to the exit surface, so the velocity in the surface is pinned at zero. See the class docstring.Both quantities here are measured from the fluid datum state where \(u = s = 0\) at \((p_\mathrm{dtm}, T_\mathrm{dtm})\), the same convention as
hoands.Calling any of the four is optional. Left alone, the rows are seeded once from the pitchwise mean of the exit plane at the first timestep and frozen there.
- Parameters:
See also
ember.patch.InletPatch.set_backflow_PThe mirror of this, prescribing the pressure an inflow face falls back on
- set_backflow_Po_To(Po, To)[source]¶
Prescribe the backflow stagnation state as pressure and temperature.
Converted to the stagnation enthalpy and entropy of
set_backflow_ho_s()using the fluid of the block this patch is attached to, and writing the same two target rows. The prescription is what survives, not the conversion: a later change of fluid re-converts the pressure and temperature given here against the new one.See
set_backflow_ho_s()for what the backflow rows do.
- set_backflow_Vt(Vt)[source]¶
Prescribe the tangential velocity imposed where the exit flow reverses.
See
set_backflow_ho_s()for what the backflow rows do.- Parameters:
Vt (float or array) – Tangential velocity \(V_\theta\) [m/s]. A scalar or a spanwise profile, of which only the pitchwise mean at each span station is imposed.
- set_P(P)[source]¶
Prescribe the outlet static pressure.
Imposed on the pitchwise mean at each span station, not node by node, so a value varying along the pitch is averaged before use. With
set_adjustment()configured this is the hub value and the spanwise profile follows from radial equilibrium; without it, a spanwise array prescribes the profile directly.
- set_throttle(mdot_target, Kp=0.5, Ki=0.002)[source]¶
Throttle the outlet to a target mass flow.
Turns
set_P()from the condition into a starting point. Each timestepupdate_target()measures the mass flow through the patch and a proportional-integral controller moves the prescribed level until the two agree:\[\varepsilon = \frac{\dot m - \dot m_\mathrm{target}} {\dot m_\mathrm{target}}, \qquad \frac{\Delta p_\mathrm{throttle}}{p_\mathrm{ref}} = K_p\, \varepsilon + K_i \sum \varepsilon \,\mathrm{cfl}\]the sum running over timesteps. Raising the back pressure reduces the flow, so the sign is as written: a mass flow above target pushes the pressure up. What the boundary imposes is still a pressure, and the characteristic treatment is untouched – the throttle only chooses which pressure.
The gains are dimensionless and should not need tuning. For a duct or blade row passing \(\dot m \sim A\sqrt{2\rho(p_0 - p)}\), the steady sensitivity of mass flow to exit pressure is
\[\frac{d\dot m}{\dot m} = -\frac{dp}{2q}, \qquad q = \tfrac{1}{2}\rho V_m^2\]so a correction of \(2q\,\varepsilon\) cancels the error outright: a Newton step whose natural scale is the exit dynamic head. That is exactly the scale the nondimensionalisation already works in, since \(p_\mathrm{ref} = \rho_\mathrm{ref} V_\mathrm{ref}^2\) with \(V_\mathrm{ref}\) a typical convection velocity. Hence the correction above is formed nondimensionally with no scale factor written anywhere, and \(K_p = 1\) is the notional Newton step.
The default is half that, because a pure Newton step overshoots and rings: the mass flow answers a change in exit pressure only after a wave has crossed the domain. Proportional action alone would then settle at a standing droop, since it can hold a correction only in proportion to an error, and the correction wanted at the target is not zero; the integral is what removes it. Because the scale is a fixed reference quantity rather than the dynamic head of the current solution, neither gain depends on the flow field or on how good the initial guess was.
The integral is weighted by the CFL, not by the step. The proportional term is memoryless and safe under any lag: it holds a fixed correction until the flow answers. The integral is not – over the steps the domain takes to respond it keeps piling on correction for an error it has already acted on – so its gain has to be paced against how much ground each step covers. Under local timestepping that is the Courant number, so a march at twice the CFL needs half as many steps and \(K_i \sum \varepsilon\,\mathrm{cfl}\) keeps one gain valid across a CFL sweep.
ember.grid.Grid.update_bconds()passes the march’s cfl down; nothing is held on the patch.The step count also scales with mesh density, and with whatever multigrid and residual smoothing are doing, and none of that is knowable from here: a patch can count the cells along its own normal but not along the flow path, which for a multi-block machine, or a patch that is not on a streamwise face, is not the same number. Refining the mesh may therefore want \(K_i\) revisited. Changing the CFL does not.
The price of the fixed pressure scale is a loop gain of \(p_\mathrm{ref} / 2q = (\rho_\mathrm{ref}/\rho) (V_\mathrm{ref}/V_m)^2\) rather than exactly one, so the gains do assume the reference scales are representative of the flow. A
V_reffar from the exit velocity moves the loop gain by its square, and is the one case where these want retuning.Only one outlet patch in a grid may be throttled; the solver refuses a grid carrying more. Two patches driving independent controllers at the same target would each apply the full correction for an error they share.
- Parameters:
mdot_target (float or None) –
Target mass flow \(\dot m_\mathrm{target}\) [kg/s], through one passage rather than the whole annulus, matching what
ember.average.flow_mass()returns for this patch. Must be positive and finite. Pass None to clear the throttle, which reverts the boundary to the pressureset_P()prescribed: the controller’s correction is derived from the gains and the error sum, so clearing those puts it back at zero on the nextupdate_target(). Clearing is therefore the inverse of setting, and does not depend on the order it is done in relative toset_P().To keep the operating point the controller found rather than revert to the one that was asked for, re-prescribe it first:
patch.set_P(patch.P_throttle) patch.set_throttle(None)
which is what a run does when it throttles to find an operating point and then holds it. See
P_throttle.Kp (float, optional) – Proportional gain, dimensionless. Default 0.5, half the Newton step of 1 above.
Ki (float, optional) – Integral gain, dimensionless. Default 0.002, from a sweep on a square duct at
cfl=5: ten times that rings, with six crossings of the target at a period of 360 steps, and a third of it never arrives. Since the integral only has to remove the droop, erring low costs settling time while erring high costs stability.
See also
ember.patch.OutletPatch.get_throttle_statsController state, as logged to the convergence history
- get_throttle_stats()[source]¶
Return the throttle state, for the convergence history.
All six values are zero when no throttle is set, which is what
ember.grid.Grid.get_convergence()records for a grid whose outlet holds a plain pressure.Only the measured mass flow and the running error sum are stored; the correction terms below are derived here from them and the gains, so there is no second copy of the controller state to keep in step. The values are those of the last
update_target(), so underGrid.update_bconds(freeze=True), which skips it, they stay at the last step the controller actually acted on.- Returns:
mdot_targetthe setpoint [kg/s];mdot_throttlethe mass flow last measured at the patch [kg/s];dP_throttlethe total correction \(\Delta p_\mathrm{throttle}\) [Pa];dP_PanddP_Iits proportional and integral parts [Pa];dP_Dalways zero, the controller being PI. The derivative column is retained so the.cnvrecord layout stays readable in both directions.- Return type:
- attach_to_block(block)[source]¶
Attach to a block and pin the in-surface backflow velocity at zero.
Done here rather than in a setter because it is not a prescription the user makes but a property of the condition; and after the base class, which is what allocates the target this writes into. Re-pinned on every attach, since a target rebuilt at a new shape comes back zeroed and unset.
- update_ref_scales()[source]¶
Re-derive the prescribed pressure and drop the spanwise adjustment.
The base class replays
set_P(), which rebuilds both the level and the target row it feeds. What is left is the adjustment relaxation state, an integral over a solution and a geometry expressed in the old scales: it is dropped, and the nextupdate_target()re-derives the profile from the rescaled solution.The throttle needs nothing done to it. Its error is a ratio of two dimensional mass flows and its correction is nondimensional by construction, so neither has a dimensional original to return to and the wound-out integral carries over intact. The pressure that correction comes to does follow the new \(p_\mathrm{ref}\), which is the same rule the gains obey in the first place; see
set_throttle().
- update_target(cfl=1.0)[source]¶
Recompute the pressure target for the current timestep.
Advances the throttle of
set_throttle()and applies the spanwise adjustment ofset_adjustment(), if either is configured. Should be called once per outer timestep before the Runge-Kutta stages;ember.grid.Grid.update_bconds()does so. The throttle’s integral advances once per call, so calling this per Runge-Kutta stage instead would scale the integral gain with the stage count.The two adjustments are orthogonal and simply add: the throttle moves the level of the pitchwise-mean pressure, and radial equilibrium shapes its spanwise profile about that level.
- Parameters:
cfl (float, optional) – CFL number of the march, weighting the throttle’s integral so that one \(K_i\) holds across a CFL sweep; see
set_throttle(). Passed down byember.grid.Grid.update_bconds()rather than held on the patch, so it is always the number the march is actually running at. Default 1, integrating per call, which is all a patch stepped by hand outside a march can mean. Unused without a throttle.
- property Kp¶
Proportional gain of the throttle, dimensionless.
Zero when no throttle is set, since
set_throttle()clears both gains along with the setpoint; testmdot_targetagainst None to tell an unthrottled patch from one deliberately given a zero gain. Set throughset_throttle(), which documents what the value means.
- property mdot_target¶
Throttle setpoint [kg/s], or None when the patch holds a pressure.
Read by
ember.grid.Grid.get_convergence(), and by the solver’s throttle validation, to find the throttled outlet; set throughset_throttle().
- property P¶
Outlet static pressure field as imposed [Pa], shaped like the patch.
The whole prescription, node by node, read back from
P_nd. Not the inverse ofset_P()and not a level: a scalar passed toset_P()comes back broadcast over the face,set_throttle()moves what is here away from what was passed, andset_adjustment()shapes it along the span about a level that is then the hub value rather than any average of this.For the single number the boundary is holding at, which is what a caller recording an operating point wants, see
P_throttle.
- property P_throttle¶
Static pressure level the throttle has arrived at [Pa].
The inverse of
set_P(), moved by whatever the controller has done to it:\[p_\mathrm{throttle} = p_\mathrm{out} + \Delta p_\mathrm{throttle}\]so with no throttle set this is simply the prescribed pressure, and with one it is the operating point the controller has reached. Scalar whenever a throttle is set,
set_throttle()having refused a non-scalar prescription; otherwise whatever shapeset_P()was given.This is the number to record when a run throttles to an operating point and something else has to reproduce it later, and the number to re-prescribe to hold that point:
patch.set_P(patch.P_throttle) # keep what the controller found patch.set_throttle(None)
Held apart from
Pbecause that is the imposed field rather than its level, and the two differ underset_adjustment()— seecalc_radial_equilibrium(), whose profile is anchored at the hub, so the level is the hub value and not the mean ofP.See also
ember.patch.OutletPatch.get_throttle_statsThe correction alone, with the rest of the controller state
- class patch.MixingPatch(i=(0, -1), j=(0, -1), k=(0, -1), label=None)[source]¶
Bases:
NonReflectingPatchOne side of a non-reflecting mixing plane.
Takes its whole prescribed target from the cross-plane exchange, so it needs no setter, and seeds every row from its own pitchwise mean before the first exchange has happened. Which side of the plane it is on, and so which rows it actually imposes, is settled from the flow rather than declared; see the module docstring.
A note on which average is which. The communicator evaluates its Jacobians on the symmetrised cross-plane average, so both sides linearise the interface jump about the same state; each patch’s own frozen reference state stays its local pitchwise mean, because the reference-state calculation calls
set_block_avg()itself and so re-derives it after the exchange has overwrittenblock_avg. The split is deliberate and follows Saxer: the interface jump belongs to the interface, the boundary condition to the boundary.- set_block_avg()[source]¶
Pitch-average the face, in interface coordinates unless the plane is reflective.
Overridden only to hold the rotation: the communicator calls this from outside any of the boundary condition’s own entry points, and the cross-plane average it builds has to be in the same frame on both sides. See
set_flux_avg().A reflective plane takes the average in
(x, r)instead, and wants to. Its conserved variables are the absolute-frame \([\rho, \rho V_x, \rho V_r, \rho r V_\theta, \rho e]\), which the two sides can compare directly: they share a meridional geometry and a radius, so nothing has to be resolved into a common frame first. The rotation would in fact be actively wrong before the frame has settled, since until then the two sides’ provisional axes are antiparallel and their normal components carry opposite signs; and the frame never does settle on a reflective plane, because settling happens inside the very window this skips.
- set_flux_avg()[source]¶
Compute pitch-averaged node fluxes and store in
flux_avg_nd.Called by
MixingCommunicatorbefore readingflux_avg_ndto form the cross-plane flux difference of Saxer Eq. 5.65.Taken in interface coordinates, so what the kernels below compute as the
x-direction flux is the flux through the face whatever the face’s orientation. The two sides of a plane share a meridional geometry and settle to opposite signs, so their frame axes coincide and the difference the communicator takes is between fluxes resolved the same way.
- set_target(target=None)[source]¶
Set the exchanged target, from an explicit array or this side’s own mean.
Called by
MixingCommunicatorafter each exchange. Omittingtargetre-seeds from the pitchwise mean of the current face state instead, which is how a patch that has not yet been exchanged gets a consistent starting point.- Parameters:
target (array of shape
(nspan, 5), optional) – Nondimensional[ho, s, Vr, Vt, P]target values.
- set_uniform(cons=None)[source]¶
Set the pitch-uniform conserved state a reflective plane imposes.
Called by
MixingCommunicatorafter each exchange, with the average of the two sides’ circumferential means. Omittingconsseeds from this side’s own circumferential mean instead, which is how a face that has not yet been exchanged has something physical to impose;apply()does that for itself rather than imposing zeros.- Parameters:
cons (array of shape
(nspan, 5), optional) – Nondimensional conserved variables, in(x, r)components and in this patch’s own span order.
- get_target()[source]¶
Return the exchanged target, a nondimensional
(nspan, 5)array.Rows are
[ho, s, Vr, Vt, P]. Read byMixingCommunicatorto form the symmetrised baseline the cross-plane mismatch is relaxed onto, which is why a patch that has never been exchanged is seeded here rather than left at zero.
- get_uniform()[source]¶
Return the reflective plane’s imposed state, nondimensional
(nspan, 5).Seeded from this side’s own circumferential mean if nothing has been exchanged onto it yet, for the same reason
get_target()seeds.
- advance()[source]¶
Take the boundary condition’s step; a no-op on a reflective plane.
A reflective plane has no state of its own between exchanges: what it imposes is settled entirely by the communicator, and
apply()imposes it outright.
- apply()[source]¶
Impose the condition on the face.
The plain plane runs the non-reflecting condition of
apply(). A reflective one overwrites the whole face with the pitch-uniform state the last exchange left, span station by span station and every stage, hub and casing nodes included – there is no characteristic content to preserve and no relaxation to take, so there is nothing here to be gradual about.
- attach_to_block(block)[source]¶
Attach to a block, validate the plane, and allocate the flux average.
Drops any settled frame: the base class has just rebuilt the provisional one from the new block’s geometry, so a stale
settledflag would pin the patch to that provisional frame for the rest of the run and never let the flow correct it.
- check_match(other, rtol=1e-05)[source]¶
Check whether this patch pairs with another across a mixing plane.
Pairs only with the opposite side of a mixing plane running the same treatment, which the inward face normal and the reflective flag identify between them: the two sides of one plane face each other, so their interiors lie on opposite sides of it. Matching is then on meridional geometry alone, so the two sides may differ in pitchwise resolution and blade count but not in spanwise resolution.
- Parameters:
- Returns:
None if the patches do not match. False if they match with no spanwise flip needed. True if they match but
other’s span must be reversed. Always test withis not None; do not use as a bare truthiness check since False is a valid match result.- Return type:
bool or None
- update_soln()[source]¶
Refresh the frozen reference state; a no-op on a reflective plane.
Nothing on a reflective plane is linearised about a mean state, so there is no reference to freeze.
- property flux_avg_nd¶
Pitch-averaged flux array, shape
(nspan, 5); populated byset_flux_avg()and read byMixingCommunicatorto form the cross-plane flux difference.
- class patch.NonMatchPatch(i=(0, -1), j=(0, -1), k=(0, -1), label=None)[source]¶
Bases:
PatchNon-matching interface between block faces with different node counts.
Connects two block faces that occupy the same physical space but have different nodal distributions. Conserved variables are transferred via bilinear parametric interpolation by
NonMatchCommunicator, which precomputes parametric coordinates at initialisation and interpolates each step.Unlike
PeriodicPatch, which requires identical node distributions, andMixingPatch, which exchanges only the pitch average,NonMatchPatchtransfers the full pointwise solution across the interface.Corner x-r coordinates must match between paired faces. Pitchwise and spanwise node counts may differ freely.
- check_match(other, rtol=1e-06)[source]¶
Check if this NonMatchPatch matches another for pairing purposes.
NonMatchPatch matching requires only x,r coordinates to match at corners, allowing for different nodal distributions in the varying dimensions. Theta coordinates are ignored to allow for circumferential mismatch.
- class patch.RotatingPatch(i=(0, -1), j=(0, -1), k=(0, -1), label=None)[source]¶
Bases:
PatchRotating wall boundary with a prescribed angular velocity.
Overrides the wall angular velocity on the patch face used by the Fortran flux routines to compute wall-relative velocities. Faces without a
RotatingPatchdefault to the block angular velocityOmega.Angular velocity must be set via
set_Omega()orset_rpm()before the solver runs.- set_Omega(Omega)[source]¶
Set the wall angular velocity.
- Parameters:
Omega (float) – Angular velocity [rad/s]. Must be a scalar.
- set_rpm(rpm)[source]¶
Set the wall angular velocity from revolutions per minute.
Converts via \(\Omega = \mathrm{rpm} \cdot 2\pi / 60\).
- Parameters:
rpm (float) – Rotational speed [rev/min].
- property Omega¶
Angular velocity [rad/s].
- property rpm¶
Angular velocity in revolutions per minute [rpm].
- class patch.CoolingPatch(i=(0, -1), j=(0, -1), k=(0, -1), label=None)[source]¶
Bases:
PatchCooling flow through a boundary.
- class patch.InviscidPatch(i=(0, -1), j=(0, -1), k=(0, -1), label=None)[source]¶
Bases:
PatchFrictionless slip-wall boundary.
Marks a face as impermeable but frictionless. The patch is included in the
slipcollection, which causes the Fortran flux routines to apply a slip (zero normal-velocity) condition without viscous stress. Use this instead of a no-slip wall when viscous effects on that face should be suppressed, for example on a symmetry plane or an inviscid endwall.
- class patch.CuspPatch(i=(0, -1), j=(0, -1), k=(0, -1), label=None)[source]¶
Bases:
PatchCusp boundary at a modelled trailing edge.
Marks the faces either side of a zero-thickness trailing edge where two block faces meet at a cusp. Each solver step the conserved variables at the cusp nodes (the axial range covered by the patch) are averaged across the two faces so that the solution remains continuous at the trailing edge.
Must be on a constant-k face and must span the full j extent of the block. Paired with the corresponding face on the other side of the trailing edge via
check_match().
- class patch.ProbePatch(i=(0, -1), j=(0, -1), k=(0, -1), label=None)[source]¶
Bases:
PatchPassive flow-sampling probe.
Records flow history at a point, face, or interior plane. Unlike boundary patches it does not affect wall/slip treatment of the sampled face – it is deliberately excluded from
PERMEABLE_TYPES/SLIP_TYPES. The two class flags below relax the generic patch rules so a probe can be placed on an interior constant plane and may coincide with another patch.
Collections¶
- class patch.BlockPatchCollection(block)[source]¶
Bases:
_LabelledListInitialise with a reference to the parent block.
- Parameters:
block (
Block) – The block whose boundary conditions this collection manages.
- extend(patches)[source]¶
Attach each patch to the block, validate limits and overlaps, then add all.
- insert(index, patch)[source]¶
Attach patch to the block, validate limits and overlaps, then insert at index.
- property cooling¶
All
CoolingPatchobjects.
- property inlet¶
All
InletPatchobjects.The inflow side of a mixing plane is not one of these: it shares the characteristic base class but is driven by a cross-plane exchange rather than a prescribed inflow state, and is a sibling type rather than a subclass; see
mixing.
- property inviscid¶
All
InviscidPatchobjects.
- property mixing¶
All
MixingPatchobjects.Both sides of a mixing plane, inflow and outflow.
- property outlet¶
All
OutletPatchobjects.The outflow side of a mixing plane is not one of these: it shares the characteristic base class but is driven by a cross-plane exchange rather than a prescribed exit pressure, and is a sibling type rather than a subclass; see
mixing.
- property periodic¶
All
PeriodicPatchobjects.
- property permeable¶
Patches through which flow passes (non-wall faces).
Includes
InletPatch,OutletPatch,PeriodicPatch,MixingPatch,NonMatchPatch, andCuspPatch. Used to identify which boundary faces are not solid walls when computing block boundary fluxes.
- property probe¶
All
ProbePatchobjects.
- property rotating¶
All
RotatingPatchobjects.
- property slip¶
Patches that impose no friction (permeable faces and inviscid walls).
Union of
permeableandInviscidPatch. Used when applying viscous wall functions: faces in this set are treated as frictionless so no friction is applied at those boundaries.
- clear()¶
Remove all items from collection.
- index(item, start=0, stop=None)¶
Return index of first occurrence of item.
- property labels¶
List of item labels, in order, including
Nonefor unlabelled items.- Returns:
The
labelof each item in the collection. Entries areNonewhere an item has no label.- Return type:
- pop(index=-1)¶
Remove and return item at index (default last).
- Parameters:
index (int, optional) – Index of item to remove and return. Default is -1 (last item).
- Returns:
The removed item.
- Return type:
Any
- remove(item)¶
Remove first occurrence of item.
- Parameters:
item (Any) – Item to remove.
- class patch.GridPatchCollection(grid)[source]¶
Bases:
objectInitialise with a reference to the parent grid.
- Parameters:
grid (
Grid) – The grid whose block patch collections this view aggregates.
- property cooling¶
Return all cooling patches from all blocks.
- property inlet¶
Return all inlet patches from all blocks.
- property mixing¶
Return both sides of every mixing plane, from all blocks.
- property outlet¶
Return all outlet patches from all blocks.
- property periodic¶
Return all periodic patches from all blocks.
- property probe¶
Return all probe patches from all blocks.
- property rotating¶
Return all rotating patches from all blocks.