Skip to content

shore.io.proc_input

Python API for the Xall proc.input writer / merger plus the BalanceSummary dataclass. The CLI wrappers (shore proc-input, shore proc-input-merge) are thin adapters around these functions.

The file format and use case are documented at shore proc-input.

Public exports

NameRole
write_proc_inputGenerate proc.input from a .grd with greedy weight-balanced rank assignment.
merge_proc_inputConcatenate multiple proc.input files for a multi-component Xall run.
BalanceSummaryPer-rank load summary returned by both writers.

write_proc_input

python
def write_proc_input(
    grd_path: str | Path,
    out_path: str | Path,
    *,
    np: int = 1,
    proc_assignment:  dict[int, int] | None = None,
    group_assignment: dict[int, int] | None = None,
    body_assignment:  dict[int, int] | None = None,
    header_comment: str = "generated by SHORE",
) -> tuple[Path, BalanceSummary]

Read the .grd header (block count + per-block (ni, nj, nk)), assign each block to an MPI rank (greedy weight-balanced by default), and write the proc.input file.

ParameterDescription
grd_pathPath to a SHORE / Xall .grd file. Only the header is read; the coordinate payload is not touched.
out_pathOutput proc.input path.
npTotal MPI rank count. Default 1 (serial). Must be >= 1.
proc_assignmentOptional {block_idx (1-based) → proc (0-based)} map. Pinned blocks stay fixed; unpinned blocks fill in by greedy assignment among the remaining weight pool.
group_assignmentOptional {block_idx → group} map. Default 0 for every block.
body_assignmentOptional {block_idx → body} map. Default 0 for every block.
header_commentFree-form text for header line 1.

Returns (Path, BalanceSummary).

Raises

  • ParameterErrornp < 1, an assignment dict references an unknown block index, or proc_assignment references a rank outside range(np).
  • FileNotFoundError, ValueError — forwarded from read_grd_metadata.

Greedy weight-balanced assignment

When some or all blocks have no explicit proc_assignment, the unassigned blocks are placed via greedy descent: sort by weight (ni * nj * nk, descending), iteratively place each block on the currently-least-loaded rank. The pinned blocks' weights are counted toward their rank's running load before the greedy fill begins, so the algorithm respects the user's overrides without re-balancing them.

The greedy algorithm is the same one LoadBalance and overset-exploded use; for typical SHORE assemblies it converges to within ~5% of optimal.

merge_proc_input

python
def merge_proc_input(
    inputs: list[str | Path],
    out: str | Path,
    *,
    grd_path: str | Path | None = None,
    header_comment: str = "merged by SHORE",
) -> tuple[Path, BalanceSummary]

Concatenate two-or-more proc.input files. Block indices in inputs[1:] are shifted by the cumulative block count of the preceding inputs; group, body, and proc columns pass through verbatim — the merger does not re-run load balancing or renumber bodies / groups across components.

ParameterDescription
inputsTwo-or-more input proc.input paths in the desired output order.
outOutput path.
grd_pathOptional path to the merged .grd. When supplied, the returned BalanceSummary carries real cell counts and the meaningful imbalance figure (cells_known=True); otherwise the summary falls back to per-rank block counts only (cells_known=False).
header_commentFree-form text for header line 1 of the merged file.

Returns (Path, BalanceSummary).

Raises

  • ValueError — fewer than 2 inputs.
  • FileNotFoundError — missing input file.
  • ParameterError — malformed input file.

Coordinated invocation

Always invoke merge_proc_input, merge_grd, and merge_cc_par as a triple, with the same input orderingproc.input's block indices, cc.par's patch block fields, and the .grd's block list must agree.

BalanceSummary

python
@dataclass
class BalanceSummary:
    n_blocks:        int
    np:              int
    per_rank_blocks: list[int]
    per_rank_cells:  list[int]
    imbalance:       float        # (max - min) / mean, fractional
    cells_known:     bool = True

Returned by both write_proc_input and merge_proc_input. Used by the CLI to print the per-rank load summary on stderr for parallel runs.

AttributeDescription
n_blocksTotal block count.
npTotal MPI rank count.
per_rank_blocksBlock count per rank, indexed by 0-based rank.
per_rank_cellsCell-count (work) per rank, indexed by 0-based rank. Set to all-zero by merge_proc_input.
imbalance(max - min) / mean of per_rank_cells, in fractional units. 0.0 for perfect balance and for serial runs.
cells_knownTrue for write_proc_input (which reads weights from the .grd); False for merge_proc_input (which only sees block-to-rank assignments — proc.input doesn't store weights). When False, format() omits the cells / imbalance lines.

BalanceSummary.format

python
def format(self) -> str

Multi-line human-readable summary. With cell-count weights available (cells_known=True):

proc.input: 11 blocks across 4 ranks
  rank 0: 3 blocks, 1.84M cells (28.1%)
  rank 1: 3 blocks, 1.62M cells (24.7%)
  rank 2: 3 blocks, 1.55M cells (23.6%)
  rank 3: 2 blocks, 1.55M cells (23.6%)
  imbalance: 4.5%

Without (cells_known=False, e.g. the merger output):

proc.input: 11 blocks across 4 ranks
  rank 0: 3 blocks
  rank 1: 4 blocks
  rank 2: 2 blocks
  rank 3: 2 blocks

For serial runs (np == 1) the imbalance line is omitted.

Example

python
from shore.io.proc_input import write_proc_input
from shore.io.grd import write_grd

# 1) Pack the per-block .geo files into a binary .grd.
write_grd("wall.grd", [
    "wall_sub0.geo", "wall_sub1.geo",
    "wall_sub2.geo", "wall_sub3.geo",
    "wall_cap_north.geo", "wall_cap_south.geo",
])

# 2) Generate proc.input for a 4-rank parallel run.
out_path, summary = write_proc_input(
    "wall.grd",
    "wall.proc.input",
    np=4,
    body_assignment={i: 1 for i in range(1, 7)},  # all 6 blocks → body 1
)
print(summary.format())

See also

Released under the MIT License.