Grid¶
Collection of connected blocks forming a complete flow domain.
This module defines the Grid, the top-level container for a
multi-block structured simulation. A grid is an ordered collection of
Block objects together with the topology that connects
them. A Grid stores no flow field of its own: every coordinate and
conserved quantity lives on the constituent blocks.
Therefore the solution is read from one block at a time as for example grid[0].P to access pressure on the first block.
Construction and file formats¶
Like a plain Python list, blocks can be added to a grid at construction or a later time:
from ember.block import Block
from ember.grid import Grid
grid = Grid([Block(shape=(10, 11, 10))])
rotor = Block(shape=(20, 20, 20))
rotor.set_label("rotor")
grid.append(rotor)
The grid then behaves as a standard Python collection: it supports iteration,
len(), membership testing with in, and the usual mutating operations
(Grid.append(), Grid.extend(), Grid.insert(),
Grid.remove(), Grid.pop(), Grid.clear()). Indexing accepts
either an integer position or a label string, and membership testing accepts
either a block or a label. Grid.labels lists the labels in order, with
None for any unlabelled block:
len(grid) # 2
grid[1] is grid["rotor"] # True -- refers to same block
"rotor" in grid # True -- membership by label
rotor in grid # True -- membership by block
grid.labels # [None, 'rotor'] -- None if unlabelled
Grid.copy() duplicates a grid, and Grid.resample() returns a copy
regridded onto a new node count.
|
Create a deep copy of the grid with copied blocks. |
|
Resample all blocks, returning a new Grid at the new resolution. |
A grid can also be read from and written to two file formats. The reading
methods are constructors, returning a new Grid.
EMB –
Grid.read_emb(),Grid.write_emb(). Our native format: a pickle of the grid with its blocks, patches, and labels, optionally gzip-compressed. Being a pickle of the objects themselves, it is the format that preserves a grid most completely.Plot3D –
Grid.read_plot3d(),Grid.write_plot3d(). The standard multi-block structured interchange format, carrying coordinates only. Boundary patches are stored alongside it in a separate FieldView boundary file, which may be read and written with the Plot3D file or on its own viaGrid.write_fvbnd().
|
Read grid from EMB pickle file. |
|
Read grid from Plot3D format file with optional boundary patches. |
|
Write grid to EMB binary format file. |
|
Write boundary conditions in FieldView boundary (.fvbnd) format. |
|
Write grid to Plot3D format with optional FVBND boundary file. |
Connectivity¶
What distinguishes a Grid from a plain list of blocks is the topology it
derives from the blocks’ boundary patches, as found in patches. See also ember.patch for the patch types and their semantics.
Grid.patches presents every patch from every block as one flat, read-only
sequence, filterable by patch type (grid.patches.inlet,
grid.patches.periodic, and so on). It is a view: patches are still owned by
the block they sit on, and are added and removed there.
Grid.connectivity manages communicators that exchange data across
the seams between blocks, one per patch type, reached as
grid.connectivity.periodic and likewise mixing, nonmatch, cusp.
Pairing each patch to its partner on a neighbouring block, and the exchange
itself, are described in Communicators.
Changing grid topology – adding or removing a block or a patch – may break the indexing describing pairing, and unfortunately the cache does not detect this. In these situations, the cache must be flushed by hand and the next communicator exchange will then pair the new topology from scratch.:
grid.apply_bconds() # pairs the periodic patches
# ...
grid.append(another_block)
grid.connectivity.clear() # drop stale pairs
grid.apply_bconds() # will pair the new topology
Blocks joined to one another by periodic patches make up a single blade row.
Rows are separated from one another by mixing patches, and
Grid.rows groups the blocks accordingly, ordering the rows from inlet to
outlet; Grid.n_row is their count. The first row’s upstream face is the
domain inlet and the last row’s downstream face is the domain outlet. Both
properties will pair the periodic patches for themselves.
Get the cached connectivity manager for this grid. |
|
Get the number of blade rows in the grid. |
|
Get read-only view of all patches across all blocks. |
|
Per-row upstream/downstream measurement-station patch identifiers. |
|
Get blade rows by grouping blocks connected by periodic patches. |
Global flow field setting¶
Rather than setting up each block’s flow field individually, a handful of
Grid methods populate every block at once from a source outside the
grid: an initial guess constructed from meridional or quasi-3D data, the
solution on another grid, or an unstructured cloud of points, mapped
onto the grid’s structured topology via Grid.align_cart_unstr().
Map an unstructured Cartesian point cloud onto this grid's nodes. |
|
|
Apply a circumferentially uniform flow field guess. |
|
Apply quasi-3D flow field guess by interpolating between two meridional planes. |
|
Interpolate a flow field onto this grid from plain arrays. |
Interpolate the solution from another grid onto this one. |
|
|
Set conserved variables from Cartesian unstructured data. |
|
Set primitive variables from Cartesian unstructured data. |
Time marching¶
Many of the grid methods, such as Grid.update_residual() and
Grid.apply_bconds(), form the inner loop of a time-marching solver. They
are documented in ember.solver, and should be used with care.
During time marching,
Grid.get_convergence() returns a ConvergenceStep of
residual and station monitors for the current step, which
ConvergenceHistory accumulates into a time
series. Grid.check_nan() raises DivergenceError if the flow field
has blown up.
|
Add the current conserved field into the running time-average. |
Apply all boundary conditions across the grid once. |
|
|
This method creates a pitchwise-repeated grid to include neighboring passages, extracts all wall nodes from the repeated blocks, builds a KDTree for efficient nearest neighbor search, and calculates the distance from each node to the nearest wall surface. |
Scan every block's density field for NaN; report the first bad block. |
|
Commit the accumulated time-average as the solution. |
|
Grid-representative convergence monitors at one step, non-dimensional. |
|
|
Apply constant-coefficient artificial dissipation to every block. |
|
Refresh boundary-condition targets across the grid once. |
Refresh conserved-dependent caches on every block. |
|
|
Evolve the SFD low-pass filter one step on every block. |
|
Rebuild the unintegrated net-flow residual on every block. |
|
Zero and rebuild the body force on every block of this grid level. |
|
Recompute the volumetric time step on every block. |
Metadata¶
Scalar properties of the grid as a whole, rather than per-node data: the working fluid, reference length, mean radius, and total node count.
Calculate reference radial coordinates for each row of blocks. |
|
|
Set equation of state on all blocks, preserving any existing flow field. |
|
Set reference length scale on all blocks, preserving dimensional geometry and flow field. |
Total number of grid points across all blocks. |
- class grid.Grid(blocks=None)[source]¶
Bases:
_LabelledListInitialize grid with optional list of blocks.
- Parameters:
blocks (list, optional) – Initial list of blocks to add to the grid.
- classmethod read_emb(filename)[source]¶
Read grid from EMB pickle file.
Automatically detects and handles both uncompressed and gzip-compressed EMB files.
- Parameters:
filename (str) – Input EMB file to read
- Returns:
New grid containing all blocks, patches, flow data, and metadata from EMB file
- Return type:
- Raises:
FileNotFoundError – If file does not exist
- classmethod read_plot3d(p3d_file, fvbnd_file=None, flip_k=True)[source]¶
Read grid from Plot3D format file with optional boundary patches.
- Parameters:
- Returns:
New grid containing blocks with coordinates and optional patches from files
- Return type:
- set_conserved_cart_unstr(xyz, conserved_cart)[source]¶
Set conserved variables from Cartesian unstructured data.
Useful for importing flow solutions from unstructured CFD solvers that store data on arbitrary point clouds in Cartesian coordinates.
Automatically detects the coordinate permutation and sign mapping that aligns the Cartesian data with the structured grid, converts coordinates to polar, and transforms the momentum vector accordingly.
The input conserved state vector is:
\[\mathcal{U}_{\mathrm{cart}} = \begin{pmatrix} \rho,\ \rho V_x,\ \rho V_y,\ \rho V_z,\ \rho e \end{pmatrix}\]The Cartesian momentum components are first converted to velocities, rotated into the polar frame \((x, r, \theta)\), then reassembled as polar conserved variables:
\[\mathcal{U} = \begin{pmatrix} \rho,\ \rho V_x,\ \rho V_r,\ \rho r V_\theta,\ \rho e \end{pmatrix}\]where the polar velocity components are:
\[\begin{split}V_r &= V_y \cos\theta - V_z \sin\theta \\ V_\theta &= -V_y \sin\theta - V_z \cos\theta\end{split}\]with \(\theta = \mathrm{atan2}(-z,\, y)\).
Warning
The total energy \(\rho e\) is assumed to be measured from this grid’s thermodynamic datum. External solvers generally use their own, in which case passing their conserved variables here stores the energy on the wrong datum and silently corrupts temperature. Convert the foreign internal energy to pressure first and use
set_primitive_cart_unstr(), whose variables are all datum-free. See Datum state.
- set_fluid(fluid_obj)[source]¶
Set equation of state on all blocks, preserving any existing flow field.
The fluid object specifies the reference scales used to normalise stored thermodynamic quantities. This method rescales the stored nondimensional flow field so that the underlying dimensional state is unchanged. See
ember.block.Block.set_fluid()for full details.- Parameters:
fluid_obj (Fluid) – New fluid / equation of state object.
- set_L_ref(L_ref)[source]¶
Set reference length scale on all blocks, preserving dimensional geometry and flow field.
The reference length scale is used to normalise stored coordinates. This method rescales the stored nondimensional values so that the underlying dimensional geometry and flow field are unchanged. See
ember.block.Block.set_L_ref()for full details of the nondimensionalisation.- Parameters:
L_ref (float) – Reference length scale [m].
- set_primitive_cart_unstr(xyz, primitive_cart)[source]¶
Set primitive variables from Cartesian unstructured data.
Useful for importing flow solutions from unstructured CFD solvers that store data on arbitrary point clouds in Cartesian coordinates.
Automatically detects the coordinate permutation and sign mapping that aligns the Cartesian data with the structured grid, converts coordinates to polar, and rotates the velocity vector accordingly.
The input primitive state vector is:
\[\mathcal{P}_{\mathrm{cart}} = \begin{pmatrix} \rho,\ V_x,\ V_y,\ V_z,\ p \end{pmatrix}\]The Cartesian velocity components are rotated into the polar frame \((x, r, \theta)\) to give the polar primitive state:
\[\mathcal{P} = \begin{pmatrix} \rho,\ V_x,\ V_r,\ V_\theta,\ p \end{pmatrix}\]where:
\[\begin{split}V_r &= V_y \cos\theta - V_z \sin\theta \\ V_\theta &= -V_y \sin\theta - V_z \cos\theta\end{split}\]with \(\theta = \mathrm{atan2}(-z,\, y)\).
- get_convergence()[source]¶
Grid-representative convergence monitors at one step, non-dimensional.
Stations are taken from
row_station_bid_pid, derived from the grid’s own topology.- Returns:
Residual and station monitors for this step. See
ConvergenceStepfor the meaning of each field.- Return type:
- get_r_ref()[source]¶
Calculate reference radial coordinates for each row of blocks.
For each element in Grid.rows, calculates the maximum and minimum r coordinates across all blocks, then stores the mean value in Grid.r_ref as a numpy array with the same length as rows.
- accumulate_avg(n_step_avg)[source]¶
Add the current conserved field into the running time-average.
Accumulates each block’s
conserved_nd / n_step_avginto itsconserved_avg_ndbuffer via the Fortran kernel. Called once per step over the finaln_step_avgsteps of a march, this builds the mean of the converged limit cycle. The buffer is a read-only cached array, so itsflags.writeableis toggled around the in-place kernel write (mirrorsupdate_sources()).
- align_cart_unstr(xyz)[source]¶
Map an unstructured Cartesian point cloud onto this grid’s nodes.
Detects the coordinate permutation and sign flips that bring
xyzinto the grid’s own Cartesian frame, then locates each grid node within the cloud, returning the index correspondence between the two.This is the same alignment performed internally by
set_conserved_cart_unstr()andset_primitive_cart_unstr(). It is exposed separately so that data can also be sent out to an unstructured solver on the same correspondence – for example scatteringwdistinto an external solver’s node array.- Parameters:
xyz (array_like, shape (N, 3)) – Cartesian coordinates \((x, y, z)\), where
Nmust equalsize.- Returns:
perm (tuple of int) – Permutation mapping the input’s axes onto the grid’s.
signs (tuple of int) – Sign applied to each permuted axis, each
+1or-1.block_indices (list of ndarray) – One index array per block, of that block’s shape, giving the row of
xyzthat each node of the block corresponds to.
- Raises:
ValueError – If no transformation aligns the cloud with the grid to tolerance.
Examples
Transfer a nodal field out to the unstructured ordering:
>>> perm, signs, block_indices = grid.align_cart_unstr(xyz) >>> out = np.empty(xyz.shape[0]) >>> for block, ind in zip(grid, block_indices): ... out[ind.flatten()] = block.wdist.flatten()
- apply_bconds()[source]¶
Apply all boundary conditions across the grid once.
Imposes the physical inlet, outlet, and mixing patch conditions on every block, then averages the flow field across periodic boundaries.
- apply_guess_meridional(block_guess, refine_factor=1)[source]¶
Apply a circumferentially uniform flow field guess.
Uses a 1D meridional block as initial guess, interpolating flow properties to all blocks in the grid using nearest-neighbor search in the (x, r) meridional plane. Optionally refines the guess using curvilinear arc-length coordinates for improved interpolation quality.
- Parameters:
block_guess (Block) – Block containing the 1D guess flow field with shape (ni,). Use block_guess.squeeze() first if your block has singleton dimensions.
refine_factor (int, optional) – Refinement factor for the guess block (default=1, no refinement). If > 1, the guess is interpolated along curvilinear arc length, providing a denser point cloud and smoother results.
- Raises:
ValueError – If block_guess does not have shape (ni,)
- apply_guess_quasi3d(block_guess)[source]¶
Apply quasi-3D flow field guess by interpolating between two meridional planes.
Uses two 2D meridional faces as boundary conditions on the low- and high-theta k-faces of each block, then linearly interpolates conserved variables across k as a function of the circumferential coordinate theta.
- Parameters:
block_guess (Block, shape (ni, nj, 2)) – A single block whose k=0 face is the low-theta meridional plane and k=1 face is the high-theta meridional plane. i is streamwise, j is radial.
- Raises:
ValueError – If block_guess does not have shape (ni, nj, 2).
- calculate_wdist(limit_pitch=inf)[source]¶
This method creates a pitchwise-repeated grid to include neighboring passages, extracts all wall nodes from the repeated blocks, builds a KDTree for efficient nearest neighbor search, and calculates the distance from each node to the nearest wall surface.
- Parameters:
limit_pitch (float, optional) – Cap the wall distance at
limit_pitchtimes each row’s reference blade pitch (2*pi*r_ref/Nb). This bakes the mixing-length limit directly into the stored wall distance, so downstream turbulence models need no separate cap. Defaults tonp.inf(no limit).
Examples
>>> grid = Grid([block1, block2]) >>> # Wall distances are now available as block.wdist for each block
- check_nan()[source]¶
Scan every block’s density field for NaN; report the first bad block.
Cheap enough to call each solver step: only the density component
conserved_nd[..., 0]is inspected, since a NaN in any conserved variable propagates into density within a step through the pressure/flux coupling. On the duct smoke test this costs ~0.5% of a single full-field residual evaluation.- Raises:
DivergenceError – If any block contains a NaN. The message names the first such block (index and label), the
(i, j, k)node bounding box of the NaN region, and which of the six boundary faces it touches – enough to tell a boundary-seeded blow-up from an interior one. The grid is left untouched so the invalid field can be inspected.
- copy(keep_patches=True)[source]¶
Create a deep copy of the grid with copied blocks.
- Returns:
New Grid instance containing copies of all blocks.
- Return type:
Example
>>> grid_copy = grid.copy() >>> grid_copy[0].conserved[...] = 0 # Does not affect original grid
- finalise_average()[source]¶
Commit the accumulated time-average as the solution.
Copies each block’s
conserved_avg_ndintoconserved_nd, refreshes the conserved-dependent caches, then re-zeros the accumulator so any subsequent averaging window starts clean.
- interp_from_arrays(arrays)[source]¶
Interpolate a flow field onto this grid from plain arrays.
For a field that arrives without a mesh around it – read back from a file, say. Nothing is materialised on the source side, and with no patch layout to align to the mapping simply runs end to end in index space.
- Parameters:
arrays (sequence) – One entry per block, each a sequence of arrays matching
ember.block_util.STATE.
- interp_from_grid(src)[source]¶
Interpolate the solution from another grid onto this one.
Assumes the source grid has the same block topology as this one, but possibly a different resolution. Each block is interpolated in index space, which is exact for linear functions on a uniform grid only, and which holds patch boundaries where they started.
The state is transferred as pressure, temperature and velocity, so the two grids may carry different fluids – different reference scales, and different entropy and energy datums – with no conversion needed.
- Parameters:
src (Grid) – Source Grid providing the solution.
- update_bconds(freeze=False, cfl=1.0)[source]¶
Refresh boundary-condition targets across the grid once.
Advances the slowly-varying BC state that the per-substep
apply_bconds()then imposes: exchanges mixing-plane data, snapshots the inlet pressure datum, and re-derives the outlet throttle/spanwise pressure target. Should be called once per outer timestep, before the Runge-Kutta stages.When
freezeis True the targets are held stationary – the mixing exchange and the outlet target re-derivation are skipped – so an averaging window sees a fixed boundary. Theupdate_solnsnapshots still run so backflow density relaxation stays anchored to the current step.cflis handed straight toember.patch.OutletPatch.update_target(), which weights a mass flow throttle’s integral by it so one gain holds across a CFL sweep. It is passed per call rather than held on the patch so that it is always the number the march is running at; the default of 1 integrates per call, for a grid stepped by hand.
- update_cached_conserved()[source]¶
Refresh conserved-dependent caches on every block.
Fans
update_cached_conserved()out across the grid, forcing each block’s cached properties keyed on the conserved variables to recompute on next access. Needed after writingconserved_nddirectly (bypassing the setters), e.g. the explicit time march.
- update_filter(cfl, delta_filt)[source]¶
Evolve the SFD low-pass filter one step on every block.
First-order exponential moving average of each block’s cell-centred conserved state toward its current cell state, with per-cell timestep
dt = cfl * dt_vol * vol.cflmay be a per-cell/per-equation array of shape(ni-1, nj-1, nk-1, 5)or a single scalar; the rank selects the matching kernel.delta_filtis the filter time constant.Must run after the CFL and
dt_volfor the step are current. This is the only writer of the read-onlyconserved_filt_ndbuffer, so it owns theflags.writeabletoggle (mirrors the timestep writers).
- update_residual(sf=0.0)[source]¶
Rebuild the unintegrated net-flow residual on every block.
Fans the fused
set_residualkernel across the grid, writing each block’sresidual_ndfrom its frozen P/T cache, face areas, and body force. Purely per-block (no inter-block exchange), so it simply loops.Optional post-processing runs in place on each block’s residual: implicit residual smoothing (
sf).Note
A negative-feedback change limiter (multall’s
DAMP) used to run here, ahead of the smoother, soft-clipping cells whose per-step change was a large outlier against the per-variable block mean. It was removed: normalising by the running mean made it a solution-dependent per-cell gain that never switched off, and applying it to the residual broke the telescoping the multigrid box-sum restriction relies on. Measured on the clustered duct atcfl=3.0,fac_mgrid=0.4,n_levels=3: the same case converged 2.59 decades undamped, converged 2.57 decades damped with multigrid OFF, and diverged within 25 convergence records with both on. It also roughly doubled the time to settle where it did work (step 1975 against 975). Reducecflorfac_mgridfor robustness instead – both are uniform scalars, so neither creates a gain field nor disturbs the restriction.- Parameters:
sf (float, optional) – Implicit residual-smoothing (IRS) coefficient (epsilon).
0(the default) disables IRS;> 0applies the exact factored-tridiagonal smoother to each block viasmooth_residual_tri_tiled. IRS damps high-frequency residual content so the explicit march tolerates a higher CFL; because it acts only on the residual (which vanishes at convergence) it does not change the steady-state solution. Per-block only: block/periodic interfaces are treated as zero-gradient. Borrowsblock.scratchas its work buffer – free at this point, sinceset_residualstages its face flows further into the arena and the march reuses the head ofscratchonly afterwards.
- update_sources(inviscid, gain_filt)[source]¶
Zero and rebuild the body force on every block of this grid level.
Assembles, into each
block.F_body_nd, the viscous shear stresses (unlessinviscid), the polar source, and the optional SFD force (whengain_filtis nonzero) – in that order, so the viscous momentum/energy negation does not flip the polar source added afterwards.The viscous calculation is phased across the whole grid: every block’s BOUNDARY tau/q is computed first, then a single periodic seam exchange runs over the face buffers, then interior tau/q and the face fluxes are produced together in one walk per block. The nodal transport properties both kernels read (mu, kappa, cp) are borrowed from each block’s scratch arena rather than cached on the block, filled in the first phase and read back in the second. This keeps the seam consistent for block-to-block periodic interfaces, where a per-block exchange would read a stale neighbour halo.
- update_timestep(rf, fac_visc=1.0)[source]¶
Recompute the volumetric time step on every block.
Uses a max-of-directional-radii variant of the JST/Blazek definition
dt_vol = 1 / max(lam_conv, lam_diff), wherelam_convis the largest of the convective spectral radiiLambda_d = |V_rel . dA_d| + a*||dA_d||over the three directions andlam_diff = fac_visc * (mu_turb/rho)*max_d||dA_d||^2/volis the turbulent-diffusion radius over the same faces (set_timestep_spectral()). Taking the max of the directional radii (rather than Blazek’s sum) makes the CFL number the true 1D Courant limit (~``2*sqrt(2)`` for the 4-stage RK march) while staying aspect-ratio-independent for the viscous limit too.rfis the relaxation factor blending the newdt_volwith the existing buffer asrf*new + (1-rf)*old(passrf=1.0for a fresh recompute). This is the lone writer of each block’s read-onlydt_vol_ndbuffer, so it owns theflags.writeabletoggle.fac_visc(>= 1) multiplies the diffusion radius so the viscous march tolerates the same cfl as the inviscid one;1.0leaves the bare directional radius untouched.
- write_emb(filename, compress=False)[source]¶
Write grid to EMB binary format file.
- Parameters:
- Raises:
ValueError – If grid is empty (contains no blocks)
- write_fvbnd(filename, region_id=0)[source]¶
Write boundary conditions in FieldView boundary (.fvbnd) format.
The FVBND format is used by FieldView to specify boundary regions for visualization purposes. See FieldView Reference Manual page 520.
- Parameters:
- Raises:
ValueError – If grid is empty (contains no blocks)
- write_plot3d(p3d_filename, fvbnd_filename=None, flip_k=True, iregion=0)[source]¶
Write grid to Plot3D format with optional FVBND boundary file.
- Parameters:
p3d_filename (
str) – Output filename for Plot3D grid filefvbnd_filename (
str) – Output filename for FVBND boundary file. If None, no boundary file is writtenflip_k (
bool) – Whether to flip the k-axis for proper volume orientation in Pointwise (default True)iregion (
int) – Region number for labeling in FVBND file (default 0)
- Raises:
ValueError – If grid is empty (contains no blocks)
- Return type:
- property connectivity¶
Get the cached connectivity manager for this grid.
The manager is built once and cached on the grid, so the pairings and communicators it owns are computed lazily and reused. Call
grid.connectivity.clear()to invalidate the cache after changing the grid topology (adding/removing blocks or patches).- Returns:
Connectivity manager providing access to patch connections via patch-type-specific properties and methods.
- Return type:
- property n_row¶
Get the number of blade rows in the grid.
- Returns:
Number of blade rows (groups of blocks connected by periodic patches).
- Return type:
Examples
>>> grid = Grid([block1, block2]) # Single row >>> grid.n_row # Returns 1 >>> >>> grid_multi = Grid([stator_block, rotor_block]) # Two rows >>> grid_multi.n_row # Returns 2
- property patches¶
Get read-only view of all patches across all blocks.
- Returns:
Read-only collection providing access to all patches from all blocks with patch type filtering (periodic, mixing, inlet, outlet, wall).
- Return type:
- property row_station_bid_pid¶
Per-row upstream/downstream measurement-station patch identifiers.
- Returns:
[(up_idx, dn_idx), ...], one pair per blade row ordered inlet to outlet. Each entry is a list of(bid, pid)patch identifiers: the row’s upstream face (inlet for the first row, otherwise mixing) then its downstream face (outlet for the last row, otherwise mixing). Consumed byget_convergence()andember.convergence_history.ConvergenceHistory.from_grid().- Return type:
- property rows¶
Get blade rows by grouping blocks connected by periodic patches.
Blocks connected by periodic patches are in the same blade row. Multiple rows are separated by mixing patches. Rows are ordered from inlet to outlet based on mixing patch connectivity.
- Returns:
List of blade rows, where each row is a list of Block objects. Blocks within a row are connected by periodic patches. Rows are ordered from inlet (upstream) to outlet (downstream).
- Return type:
List[List[Block]]
Examples
>>> # Single-row grid >>> grid = Grid([block1, block2]) >>> rows = grid.rows # Returns [[block1, block2]] >>> >>> # Multi-row grid with mixing patches >>> grid = Grid([stator_blocks, rotor_blocks]) >>> rows = grid.rows # Returns [[stator_blocks...], [rotor_blocks...]]
- property size¶
Total number of grid points across all blocks.
- Returns:
Sum of all block sizes in the grid
- Return type:
- append(item)¶
Add item to collection.
- Parameters:
item (Any) – Item to add to the collection. The item manages its own label.
- Raises:
ValueError – If item’s label already exists in the collection.
- clear()¶
Remove all items from collection.
- extend(items)¶
Add multiple items to collection.
- Parameters:
items (list) – List of items to add. Each item manages its own label.
- Raises:
ValueError – If any item’s label already exists in the collection.
- index(item, start=0, stop=None)¶
Return index of first occurrence of item.
- insert(index, item)¶
Insert item at specific index.
- Parameters:
index (int) – Index at which to insert the item.
item (Any) – Item to insert. The item manages its own label.
- 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 grid.GridConnectivity(grid, patch_class)[source]¶
Bases:
objectInitialize connectivity manager for a specific patch type.
- Parameters:
- pair(rtol=1e-06)[source]¶
Pair patches of the specified type, caching the result.
Filters patches to only include instances of self.patch_class, then uses spatial proximity (KDTree in x,r coordinates) to find potential matches and delegates to each patch’s check_match method for validation. The result is cached; call
clear()to recompute after topology changes.- Parameters:
rtol (float, optional) – Relative tolerance for matching (passed to patch check_match methods)
- Returns:
Dictionary where keys are (bid, pid) tuples and values are ((matching_bid, matching_pid), transform) tuples. Both patches in each pair are included as separate keys.
- Return type:
- Raises:
ValueError – If any patch does not have a matching pair
- class grid.GridConnectivityManager(grid)[source]¶
Bases:
objectInitialize connectivity manager for a grid.
- Parameters:
grid (Grid) – The grid containing blocks with patches
- property cusp¶
Get connectivity manager for cusp patches.
- property mixing¶
Get connectivity manager for mixing patches.
- property nonmatch¶
Get connectivity manager for non-matching patches.
- property periodic¶
Get connectivity manager for periodic patches.
- class grid.ConvergenceStep(residual, mdot, ho, s, mdot_target=0.0, mdot_throttle=0.0, dP_throttle=0.0, dP_P=0.0, dP_I=0.0, dP_D=0.0)[source]¶
Bases:
objectGrid-wide convergence monitors at a single time step, non-dimensional.
Produced by
Grid.get_convergence()and consumed byember.convergence_history.ConvergenceHistory.record_convergence(), which unpacks the station vectors into one scalar column per station.Station vectors are ordered inlet to outlet, each blade row contributing its upstream then downstream face (
[row0_up, row0_dn, row1_up, row1_dn, ...]), so they have length2 * n_row.- residual: ndarray¶
Block-mean
|residual_nd|per conserved variable, shape(5,), ordered(rho, rhoVx, rhoVr, rhorVt, rhoe). TherhorVtentry is divided per block byblock.r_mid_ndso its magnitude is comparable to therhoVx/rhoVrresiduals; this rescaling is for monitoring only.
- mdot: ndarray¶
Station mass flow rates, shape
(2 * n_row,), non-dimensionalised by the fluid mass-flux scale.
- mdot_target: float = 0.0¶
Outlet throttle mass flow setpoint [kg/s]; zero when no outlet is throttled. Per passage, not per annulus, unlike
mdot.
- mdot_throttle: float = 0.0¶
Mass flow measured at the outlet patch on its last target update [kg/s].
- dP_P: float = 0.0¶
Proportional contribution to
dP_throttle[Pa].
- dP_I: float = 0.0¶
Integral contribution to
dP_throttle[Pa].
- dP_D: float = 0.0¶
the throttle is a PI controller. The column is retained so the pickled ConvergenceHistory (.cnv) layout reads in both directions.
- Type:
Derivative contribution to
dP_throttle[Pa]. Always zero
- exception grid.DivergenceError[source]¶
Bases:
RuntimeErrorRaised when a block’s conserved field contains a NaN.
A dedicated type lets a solver loop catch divergence precisely and exit cleanly (leaving the invalid field in place for debugging) while genuinely unexpected errors still propagate. See
Grid.check_nan().- add_note(object, /)¶
Exception.add_note(note) – add a note to the exception
- args¶
- with_traceback(object, /)¶
Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.