ps.bundle
ps.bundle.BundleRefusal
Section titled “ps.bundle.BundleRefusal”ps.bundle.BundleRefusal(reason: str, message: str, *, file: str, schema: str | None = None, sheet: str | None = None, column: str | None = None, columns: list[str] | None = None, row: Any = None, given: str | None = None, expected: str | None = None) -> None
A typed refusal at the bundle door: its code, its class and where in the bundle it is.
except ps.bundle.BundleRefusal as error: catches a refused bundle; error.reason is its
code (bundle.*) and error.code the same.
Every field is data a program can act on: the reason code, its class (kind: one of
unit, column, key, vocabulary, shape, reference, range, grid,
recipe and schedule), the schema id, the sheet, the column (and columns, every
column a unit refusal names at once), the row key, and what was given and what was
expected. The message is for a person and names the problem, the expected value and what
was sent; the fields are for the program that answers it. It is a ValueError
and a ps.PetroScriptError.
ps.bundle.Recipe
Section titled “ps.bundle.Recipe”ps.bundle.Recipe(*, recipe_version: Literal['0.1'], units: Literal['METRIC', 'FIELD'], title: Optional[Annotated[str, 'title']] = None, start: Annotated[str, 'date'], history, forecast, phases: list[str] | None = None, selected_deck: Optional[Annotated[str, 'line']] = None, grid=None, sheets: list[str], schedule, options=None, outputs=None, ensemble=None) -> None
recipe.json, the one file that says what a bundle is.
Strict: every key has its type, a required key missing, a key it does not know at any
depth, a wrong type, a bad date and a word outside a key’s vocabulary each refuse by name
with the dotted path (bundle.recipe.*) before the assembler reads a key. The door keeps
the recipe as the JSON it read; recipe_schema() is this model, read.
ps.bundle.SCHEMAS
Section titled “ps.bundle.SCHEMAS”dict() -> new empty dictionary dict(mapping) -> new dictionary initialized from a mapping object’s (key, value) pairs dict(iterable) -> new dictionary initialized as if via: d = {} for k, v in iterable: d[k] = v dict(**kwargs) -> new dictionary initialized with the name=value pairs in the keyword argument list. For example: dict(one=1, two=2)
ps.bundle.SheetSchema
Section titled “ps.bundle.SheetSchema”ps.bundle.SheetSchema(kind: str, version: str, columns: tuple[Column, ...] | list[Column], doc: str = '') -> None
A registered sheet: its id, its columns, and nothing a deck would not need.
Attributes
Section titled “Attributes”kind : str
The sheet’s name (wells, history_panels): the stem of its file under tables/.
version : str
The schema’s version (0.1); a recipe or a file names the schema kind@version.
columns : tuple
The sheet’s columns, in the order it states them: each one’s name, dtype, units per unit
system, its key and nullable flags and its vocabulary. Given as a list, they are held as a
tuple.
doc : str
What the sheet states and how the door reads it.
ps.bundle.assemble
Section titled “ps.bundle.assemble”ps.bundle.assemble(bundle, *, member: int | None = None) -> Model
Assemble the bundle into a ps.Model: every sheet and grid file as statements.
With no member the base case, exactly as the sheets state it. With member=i (1..N,
N the recipe’s ensemble.members) the i-th member of the ensemble: the base case with that
member’s draws from the uncertainty sheet applied (a multiplier over its target’s cells,
kv_kh as PERMZ = ratio x PERMX, a contact shift added to the contacts row, an operations draw
written into the producer control rows it targets: the THP floor, its start month, the
uptime, the retest interval), deterministically, the same member for the same seed;
ps.bundle.member_draws states every member’s numbers.
The grid files are read into the model (a corner-point geometry, the property and region
arrays, each stamped with its file and checksum) and render inline, so the sealed deck is one
self-contained file. The PVT and SCAL sheets become the tables of the family the recipe’s
phases name, gas-water or oil-gas-water; contacts becomes EQUIL, one record per region,
each stamped sheet:<id> row=<region>; wells and completions become the wells, a
well’s head cell the (i, j) of its earliest completion by date and layer, each cell declared
at the start as the sheet has it over the start’s month and each later change of its state a
COMPDAT under the DATES of the month it holds from; faults becomes FAULTS and MULTFLT;
the schedule sheets enter as dated statements, with those dated COMPDAT records among them and
a producer’s THP limit through the lift_tables table its row names; tubing is read
through the door and its wells checked against the wells sheet, and nothing renders from it
(a deck carries a lift table, never tubing); the observables are the field totals and
pressure and each well’s totals. Nothing is defaulted from a seed.
Parameters
Section titled “Parameters”bundle : Bundle, str or Path
An opened bundle, or the path ps.bundle.open takes.
member : int, optional
The ensemble member to assemble, 1 to the recipe’s ensemble.members; None assembles
the base case.
Returns
Section titled “Returns”Model The model the sheets state, in the recipe’s unit system.
Raises
Section titled “Raises”BundleRefusal
A sheet the model needs that the recipe does not name (bundle.assemble.sheet_unnamed)
or whose file is gone (bundle.assemble.sheet_missing), a region file naming a region
no sheet states (bundle.assemble.region_unstated), a well with no completion
(bundle.assemble.well.no_completion), a table family that contradicts the phases
(bundle.assemble.phases_mismatch), a phase set this version does not spell, or a PVT
sheet stating any region but a lone region 1 (bundle.assemble.fluids.unsupported), a
grid file this version does not read (bundle.assemble.grid.unsupported), a property
array the model needs that the recipe does not name
(bundle.assemble.grid.property_unnamed), a member outside the ensemble
(bundle.recipe.ensemble), and a model the model layer refuses, a contradiction
(bundle.model.invalid, its first row’s code as column) or an input
(bundle.model.input). A path given is opened first, with ps.bundle.open’s
refusals, and each sheet read refuses as Bundle.sheet does.
ps.bundle.check_fluids
Section titled “ps.bundle.check_fluids”ps.bundle.check_fluids(path)
Ask the PVT sheets what the fluids weigh where the model starts, and report it.
The report states the family, how it was decided, and per equilibration region the datum
pressure, the saturated solution ratio each table carries there and the derived phase
densities (water from its compressibility branch, the hydrocarbons from each table’s
saturated curve read linearly between its nodes), with the formula and the unit words beside
every number. Nothing is extrapolated: a datum pressure outside a table’s nodes gives no
number, the reason and a warnings line. Each region with oil states
saturation_pressure, the datum pressure the door saturates it at, and an oil region whose
gas-oil contact lies above its active depth span (read off the grid, as the assembler reads
it) is a warnings line: no gas cap is in the grid, and a bubble point below the datum is
not stated. A report accepts nothing and refuses nothing about the numbers it derives; a
hydrocarbon heavier than its water at the datum is a warning here and refuses through
assemble (bundle.pvt.density_order), where the two numbers decide the whole initial
state. Every warnings line is also an issues row (severity, code, message) a
program can act on.
Parameters
Section titled “Parameters”path : str, Path or Bundle
The workspace or its sidecars directory, or a bundle already opened: a caller that
holds one for assemble hands it in, and nothing is opened twice.
Returns
Section titled “Returns”dict
The report: the family, each region’s numbers with their formulas and unit words,
warnings (lines) and issues (rows of severity, code and message).
Raises
Section titled “Raises”BundleRefusal
A sheet the report reads that the recipe does not name, or whose file is gone, refuses
as it does through assemble (bundle.assemble.sheet_unnamed,
bundle.assemble.sheet_missing), a set of phases no family spells refuses
bundle.assemble.fluids.unsupported, and a formation-volume factor at or below 0 on
any row of a table it reads, which every density is divided by, refuses
bundle.model.invalid with the model layer’s rule id. A path given is opened first,
with ps.bundle.open’s refusals.
ps.bundle.check_grid
Section titled “ps.bundle.check_grid”ps.bundle.check_grid(path, *, geometry: str = 'sidecars/grid/GRID.GRDECL', properties: tuple[str, ...] | list[str] = (), regions: tuple[str, ...] | list[str] = (), contacts=None)
Check a grid alone, with no sheets and no recipe, and report what it measures.
It runs the checks the assembler runs before it reads a sheet (the geometry reads as
corner-point, every active cell has thickness and a positive depth, every named array holds
one value per cell, at least one cell is active) with the same typed refusals, and when they
pass returns a report of raw measurements: dims, active, active_per_layer, thickness min and
max over the active cells, the active depth span, the footprint (x and y extent, active area
in km2), the median cell size, how well laterally adjacent cells agree at the corners they
share, and warnings (more than half of the shared corners disagreeing usually means a
ZCORN written in the wrong order: a warning, never a refusal, since faults are real). A quiet
report accepts nothing about the geometry. layout states how a property array is laid
out (one value per cell of the whole grid, I fastest, then J, then K), and properties
carries, per array named in properties=, its mean over the active cells along K
(by_layer), along J (by_row) and along I (by_column), eight equal-count bins
where an axis runs past 64: an index inside the grid proves nothing about placement, and an
array written transposed or in the wrong order reads without error and shows here as the
gradient the engineer put in one axis appearing in another.
A bundle already opened is checked with its own recipe’s grid keys, but for a key the call
states (a geometry equal to the default and an empty properties or regions read
as not stated: the recipe’s key stands; pass the path to check without the recipe). Every
warnings line is also an issues row (severity, code, message) a program can act on.
Parameters
Section titled “Parameters”path : str, Path or Bundle
The workspace or its sidecars directory, or a bundle already opened.
geometry : str, optional
The geometry’s GRDECL file, as the recipe’s grid.geometry key names it. Left out,
sidecars/grid/GRID.GRDECL, or an opened bundle’s key where its recipe states one.
properties : list of str, optional
The property arrays to read and measure (["PORO", "PERMX"]), as grid.properties.
Left out, none, or an opened bundle’s key where its recipe states one.
regions : list of str, optional
The region arrays to read (["EQLNUM"]), as grid.regions. Left out, none, or an
opened bundle’s key where its recipe states one.
contacts : str or Path, optional
A written contacts sheet (sidecars/tables/contacts.parquet, or a path): it is
validated from its own stamp, a datum or contact the bundle reads as an elevation is a
warnings line (bundle.contacts.outside_grid: Flow runs it as written), and the
report gains contacts: each stated contact and datum against the active depth span,
of its own region when regions names EQLNUM, above, inside or below.
Returns
Section titled “Returns”dict
The report: the measurements above by name, warnings (lines) and issues (rows of
severity, code and message).
Raises
Section titled “Raises”BundleRefusal
bundle.sheet.missing when contacts is not a regular file, and
bundle.schema.mismatch when it is a sheet of another kind (the file is
validated as a sheet from its own stamp first, with a sheet’s refusals). The
grid’s checks refuse with bundle.assemble.grid.unsupported (the geometry
does not read as corner-point, or a named array’s file is absent, unreadable,
defaulted or of the wrong size), bundle.grid.depth (an active cell at or
above zero depth), bundle.grid.degenerate (an active cell with no
thickness) and bundle.grid.inactive (no cell is active).
ps.bundle.export_schemas
Section titled “ps.bundle.export_schemas”ps.bundle.export_schemas()
Return the sheet registry as the one JSON the bundle’s readers take (SCHEMAS.json).
ps.bundle.export_schemas() is every registered sheet’s schema: its kind, doc, key and
columns (each one’s name, dtype, units per unit system, nullable and key flags and
vocabulary).
Returns
Section titled “Returns”dict
recipe_version, unit_systems, schemas (each schema by its id,
kind@version), vocabularies and recipe_fields; json.dumps-able.
ps.bundle.family_of
Section titled “ps.bundle.family_of”ps.bundle.family_of(bundle) -> str
Return the fluid family the assembler reads off the recipe’s phases and the PVT sheets named.
It is gas_water (the phases gas and water), three_phase (oil, gas and water, the
recipe naming no wet-gas pvt_pvtg sheet, so the gas is the dry-gas pvt_pvdg sheet)
or gas_condensate (oil, gas and water with the wet-gas pvt_pvtg sheet: the oil is the
condensate the gas carries, and the deck produces gas, water and condensate).
Parameters
Section titled “Parameters”bundle : Bundle, str or Path
An opened bundle, or the path ps.bundle.open takes.
Returns
Section titled “Returns”str
"gas_water", "three_phase" or "gas_condensate".
Raises
Section titled “Raises”BundleRefusal
bundle.assemble.fluids.unsupported for any other set of phases; a path given is
opened first, with ps.bundle.open’s refusals.
ps.bundle.member_draws
Section titled “ps.bundle.member_draws”ps.bundle.member_draws(bundle)
Return every member’s draws as one polars frame: the numbers its deck was rendered from.
The draw is a Latin hypercube over each uncertainty row’s box: the members are dealt the N
strata by a permutation seeded by (seed, row), and each sits at a point of its stratum seeded
by (seed, row, member), so twenty members cover the box where twenty independent draws may
not, the same seed gives the same numbers, and the population is the row’s box from low
to high, uniform in log10 for a log-uniform row.
Parameters
Section titled “Parameters”bundle : Bundle, str or Path
An opened bundle, or the path ps.bundle.open takes.
Returns
Section titled “Returns”polars.DataFrame Columns member, parameter, target, value and units: one row per member and uncertainty row, members first, each member’s rows in the sheet’s (parameter, target) order; a value is in its row’s units.
Raises
Section titled “Raises”BundleRefusal
bundle.recipe.ensemble_missing when the recipe draws no ensemble. A path
given is opened first, with ps.bundle.open’s refusals, and the uncertainty
sheet is read as Bundle.sheet reads it, with its refusals.
ps.bundle.open
Section titled “ps.bundle.open”ps.bundle.open(path) -> Bundle
Open the bundle at path (the workspace, or its sidecars directory).
A bundle is a model stated as sheets: recipe.json says what it is, each sheet is a parquet
file under tables/ and the grid files sit under grid/. ps.bundle.open("workspace")
opens the bundle there; ps.bundle.assemble builds its ps.Model.
Parameters
Section titled “Parameters”path : str or Path
The folder holding recipe.json, or the folder whose sidecars directory holds it.
Returns
Section titled “Returns”Bundle
The opened bundle: its recipe checked, each sheet read and checked when it is first asked
for (bundle.sheet("wells")).
Raises
Section titled “Raises”BundleRefusal
bundle.recipe.missing when neither path nor its sidecars directory holds a
recipe.json, and bundle.recipe.unreadable when that file is not JSON. The recipe
is checked before the bundle opens, each fault by its code: its shape under
bundle.recipe.* (a key missing, unknown or of the wrong type, a date malformed or out
of order, a word outside its key’s vocabulary), a sheet with no registered schema
(bundle.sheet.unknown) or whose tables/<kind>.parquet is absent
(bundle.sheet.missing), a sheet needing a phase the recipe’s phases do not state
(bundle.recipe.phase_disagrees), a grid file it names that is absent
(bundle.grid.missing), and an ensemble of fewer than two members or whose
uncertainty names no uncertainty sheet the recipe lists (bundle.recipe.ensemble).
ps.bundle.params_keys_doc
Section titled “ps.bundle.params_keys_doc”ps.bundle.params_keys_doc() -> str
Return the params keys each schedule event class renders, as one text.
Each event class with the numeric params keys it renders (each with its units twin,
bhp_limit beside bhp_limit_units), then the producer’s rate targets with the control
mode each selects and the unit words a rate or a pressure takes.
Returns
Section titled “Returns”str
One paragraph: <event class>: <keys> for each class, joined by semicolons, then the
producer’s targets and the unit words.
ps.bundle.recipe_schema
Section titled “ps.bundle.recipe_schema”ps.bundle.recipe_schema()
Return the recipe as a table a caller reads before writing one (RECIPE.json).
It holds every key with its type, whether it is required, one line of meaning and an example,
plus one minimal recipe that assembles. ps.bundle.recipe_schema() lists every recipe key
with its type and meaning.
Returns
Section titled “Returns”dict
recipe_version, keys (one row per key: path, type, required, doc,
example) and example, a whole recipe; json.dumps-able.
PetroScript is licensed under the Apache License 2.0. © 2026 Blauweiss EDV LLC. PetroScript by Blauweiss AI.For AI agents: llms.txt