Skip to content

Issue codes: the physics checks

PetroScript checks the physics of every model it builds. The checks are plain arithmetic over what the model states, run by m.check() and at compile time: no simulation, no files. Each has a code, and one of three tiers:

TierWhat it meansWhat you do
errorThe deck cannot run as stated: OPM Flow refuses it or aborts, the arithmetic is ill-posed (a negative cell volume, a porosity above 1, time running backwards), or the model contradicts itself (oil denser than the water under its own contact).Fix it: ps.compile refuses an error, and no acknowledgement lifts it.
warningImplausible but possible: a datum pressure that reads like another unit, a lift table whose BHP falls below its THP.Read it. If the model means it, state acknowledged_warnings=["code"] on the model, which turns it into an info row.
infoWorth knowing, never blocking: a sector-wedge radial grid, a schedule in which no well opens.Nothing.

ps.help(code) explains any code. An acknowledged code that no longer fires is itself an error (physics.acknowledge.stale): see Acknowledging warnings.

When a check stands down. Field-scale bands (the datum pressure’s unit, rock bands, depth and thickness bands, material balance) stand down for LAB-unit models, restart runs, and core-scale models (a datum shallower than 100 m, 328.08 ft). The laws that hold at any scale never stand down: PVT table order, capillary-pressure order, contact order, a datum pressure above zero, and the porosity and permeability domains. A check that walks the schedule (control feasibility, cumulative volumes) judges every dated change, not only the start.

More on the tiers and the stand-down rules

Every physics guard belongs to one of three tiers:

  • E (error) — one of three things, and only these: OPM Flow refuses the deck at load or aborts the run (a rising PVDG Bg, a control mode on an inactive phase); the arithmetic is ill-posed (negative-volume cells, porosity above 1, time running backwards); or the model contradicts itself in a way no real deck does (oil denser than the water under its own stated contact, a table out of the order its law requires). Errors block compiling (ps.compile refuses them) and can never be acknowledged away. Each error code names its basis — the Flow witness, or its place on the ill-posed or contradiction list — in ERROR_BASIS (petroscript.validation.catalog), and a law holds the source to that table.
  • W (warning) — implausible but conceivable: oil denser at the datum than its own column allows, a well’s lift table whose bottom-hole pressure falls below its tubing-head pressure, a datum pressure that reads as another unit. The model is built, but the warning demands an explicit acknowledged_warnings=["code"], which demotes it to an informational issue. Acknowledgment is a statement of intent, not a bypass: it never touches errors.
  • I (info) — worth stating, never blocking: a sector-wedge radial grid, a schedule in which no well ever opens. Informational issues require no action.

Acknowledgments must stay true. An acknowledged code whose warning does not fire on the current model is itself an error (physics.acknowledge.stale): the model would be claiming a weirdness it does not have. See Acknowledging warnings below.

Guards are plain arithmetic over the declared model, evaluated at model.check() and compile time — no simulation, no file I/O, cost proportional to the model itself. A guard stands down silently only when its input is genuinely not computable from the declared objects (an explicit tensor it cannot reduce, an unsupported unit system, an unparseable date); malformed input is an error owned by construction-time validation, never a silent pass. Where a guard walks the schedule — control feasibility, cumulative volumes, shut tracking — it applies at every event, not just the initial state: a BHP limit that is infeasible as a day-30 retarget is judged exactly like an infeasible initial control.

Field-scale plausibility bands (the datum pressure’s unit, rock bands, depth and thickness bands, material balance) stand down under three triggers: LAB-unit models, restart runs (the restart file holds the initial state), and core/laboratory-scale equilibrations (datum depth shallower than 100 m, which is 328.08 ft in FIELD): pseudo-fluids and bench-top datums are legitimate models that field physics must not judge. The laws that hold at any scale never stand down under any trigger: PVT table monotonicity and the PVTO family crossing, capillary-pressure order, contact order, a non-positive datum pressure, and the porosity and permeability domains. Reference checks — dangling region ids, dead-cell aquifer connections, interior-face boundary conditions — are scale-free too. Contact order is judged, as Flow judges it, only with oil, water and gas all active.

CodeTierWhat it checks
physics.pressure_gradient.nonpositive_datumerrorDatum pressure at or below zero.
physics.pressure_gradient.other_unitwarningThe datum pressure over the model’s own water column at the datum depth (atmospheric pressure plus the water’s gradient at the datum times the depth: the fact initial/<eq>/datum_pressure_over_water_column), when the pressure read in another unit lands nearer that column than the pressure as stated (bar under FIELD; psi or kPa under METRIC), or when the datum depth read in the other system’s length (m under FIELD, ft under METRIC) does so and that reading, not the depth as stated, lies inside the region’s cells (Cartesian and radial grids).
physics.equilibration.unsealed_contrastwarningAdjacent equilibration regions whose initial pressures, each projected from its own datum through its own phase columns (gas, oil and water legs between its contacts, densities rho_w,s/Bw, (rho_o,s + Rs rho_g,s)/Bo and (rho_g,s + Rv rho_o,s)/Bg read as Flow reads them), differ by more than 145 psi (FIELD) / 10 bar (METRIC) at their shared-face depth, with no barrier between them — no THPRES pair, no declared fault on the shared faces, no zero (or effectively zero) face multiplier.
physics.initial.depth_table_missingerrorEQUIL item 7 (live oil), 8 (wet gas) or 12 (humid gas) above 0 with no RSVD or PBVD, RVVD or PDVD, RVWVD table.
physics.initial.datum_off_gas_oil_contacterrorA live oil (item 7) or wet gas (item 8) whose item is 0 or not stated, with the datum depth not equal to the gas-oil contact, compared exactly as Flow compares them. Empty equilibration regions are skipped, as Flow skips them.
physics.initial.depth_table_ignoredwarningAn RSVD, PBVD, RVVD, PDVD or RVWVD table stated while its EQUIL item is 0 or not stated.
physics.initial.vaporized_water_zerowarningA humid gas (VAPWAT) with item 12 at 0 and the datum off its contact.
physics.initial.datum_zone_phase_inactiveerrorA datum below the water contact in a model without water, or above the gas-oil contact (gas-water contact without oil) in a model without gas; an unstated gas-oil contact is Flow’s 0.
physics.initial.array_missingerrorAn explicit initial state without SWAT (water active, two or more phases), SGAS (gas and oil), RS (dissolved gas) or RV (vaporized oil).
physics.initial.equilibration_spans_pvt_regionswarning or infoAn equilibration region holding active cells of more than one PVT region; the message states the pressure error at the water contact and the contact shift it implies, dh |rho_p - rho_q| / (rho_w - rho_o). A warning where the shift exceeds the thickness of the layer holding the contact, else information; information without geometry (corner-point grids).
physics.saturation.closureerrorDirect-initialization phase saturations must sum to 1 per cell within 1e-3 (or to at most 1 when one active phase is omitted as the remainder).
physics.saturation.outside_relperm_spanwarningDirect-initialization water saturation outside the tabulated relperm saturation span (tolerance 1e-6).
physics.swatinit.no_capillary_pressurewarningAn equilibrated initial water saturation (SWATINIT), single or regional, stated where a water-oil table some saturation region uses (SWOF or SWFN) has an identically zero capillary pressure column.

Why each check exists:

physics.pressure_gradient.nonpositive_datum

Arithmetic, not judgment: a hydrostatic column has to hang from a real pressure, and no unit system makes zero or a negative one meaningful.

physics.pressure_gradient.other_unit

A unit-slip catcher, with no band of its own: over- and under-pressure are a reservoir’s, and the ratio is a fact, never a finding. A pressure typed in the other unit is off by 14.5 (100 for kPa), further than any reservoir stands from its water column; the test is which reading is nearer in ratio. The corpus and the examples run from 0.75 to 2.7 and stay silent; three corpus decks at 7.2 (699 bar at 1000 m) and the examples that carry one system’s numbers under the other’s keyword are named. A depth is named only with the grid’s agreement, because a reservoir 1.8 times its water column already reads nearer in metres on the pressure alone (the seeds, at 2.2, among them). It stands down with the field bands.

physics.equilibration.unsealed_contrast

The OPM Flow manual’s THPRES section warns that communicating cells in different EQLNUM regions produce “an unstable initial equilibration”: the first timesteps then model the missing seal, not the field. Genuine seals hold pressures of order 1–10 bar (the manual’s own THPRES example spans 0.6–7 bar), so a contrast an order of magnitude beyond that with nothing holding it is compartmentalized initialization without compartment walls. Deliberate unsealed contrasts acknowledge.

physics.initial.depth_table_missing

Flow refuses to initialise (“RSVD or PBVD table not available”, and the RVVD and RVWVD twins; rc 1 on 2026.04).

physics.initial.datum_off_gas_oil_contact

With no table Flow saturates the oil (gas) at the contact’s pressure, which it takes from the datum, and refuses a datum elsewhere (“datum depth must be at the gas-oil-contact”, rc 1). A model that states no item 7 and no table meets this whenever its datum is off the contact.

physics.initial.depth_table_ignored

Flow reads no table and saturates at the contact without a word: SPE1 with RSVD Rs 0.5 and item 7 at 0 starts at Rs 1.53 in every cell. Eight corpus decks carry such dead tables.

physics.initial.vaporized_water_zero

Flow sets the gas’s water vapour to 0 in every cell (“Rvw is set to 0.0 in all cells”).

physics.initial.datum_zone_phase_inactive

Flow cannot tell which phase the datum pressure belongs to and refuses (“Don’t know how to interpret EQUIL datum depth in GAS zone in model without active gas phase”, rc 1).

physics.initial.array_missing

Flow requires each and refuses to initialise (“requires the RS keyword …”, rc 1 for each on 2026.04).

physics.initial.equilibration_spans_pvt_regions

Flow builds each equilibration region’s pressure with its first active cell’s PVT tables: SPE1 with layer 1 in a second PVT region under one EQLNUM starts layer 2 1.4 psi off its own hydrostatic pressure and moves about 1 psi in one day with every well shut (2026.04).

physics.saturation.closure

Saturations are volume fractions of the same pore space; a sum away from unity creates or destroys fluid mass at time zero and the material balance is wrong before the first step.

physics.saturation.outside_relperm_span

Cells start at saturations where no tabulated mobility exists, so the simulator extrapolates or clamps the endpoints — the initial flow field is then an artifact of table layout, not of the rock.

physics.swatinit.no_capillary_pressure

SWATINIT is honoured by scaling the Pcow curve until the stated saturation is the equilibrium one, so a flat column leaves nothing to scale. Measured on both pinned releases: with the column flat, initial water in place came back identical to the same deck carrying no SWATINIT at all; with it live, the stated saturation was honoured exactly. Neither release warns — the keyword is logged as read and then has no effect, which is precisely why the model says so. Decks kept deliberately parallel to a Pc-bearing sibling acknowledge.

CodeTierWhat it checks
physics.volumetrics.exceeds_stoiipwarningCumulative scheduled oil production, observed history apart from target-rate upper bounds, against the stock-tank oil initially in place: each region’s pore volume between its contacts, read from the grid after its program (PORO, NTG, ACTNUM and the region maps as Flow reads them), grown by the ROCK pore-volume factor 1 + X + X^2/2 at the datum pressure, less connate water, over the initial oil’s Bo at the datum as Flow reads it (Rs from RSVD, PBVD or the gas-oil contact; 1/Bo interpolated at that Rs and pressure).
physics.volumetrics.exceeds_giipwarningCumulative scheduled gas production, history apart from targets, against an upper-bound gas initially in place plus every gas the schedule injects: the pore volume above the water contact (read after the grid program), grown by the ROCK factor, over Bg at the datum (1/Bg linear in pressure along PVDG or PVTG’s saturated line).
physics.volumetrics.implausible_recoveryinfoThe schedule’s oil recovery, history alone (in_place/field/history_recovery) or history then rate targets (forecast_recovery), past the largest maximum displacement efficiency the model’s own end points reach, 1 - Sor Boi / ((1 - Swc) Bo,max) for each saturation region (displacement/<sat>/max_displacement_efficiency), under end-point scaling with each cell’s scaled residual oil and connate water. Solvent, miscible and thermal models are not held to it; on a model whose gas carries vaporized oil the oil in place is not computed. It cites both facts.
physics.voidage.overpressuringinfoIn a closed tank (no aquifer, boundary or source term), scheduled water injection exceeding production at reservoir conditions, W_i Bw against N_p Bo + (G_p - N_p Rs) Bg + W_p Bw, by enough that the datum pressure plus the tank’s rise passes the lowest bottom-hole pressure limit an injector states. The net volume over each equilibration region’s total compressibility times its pore volume is the tank’s pressure rise (in_place/field/closed_tank_pressure_rise; injected_pore_volumes beside it), with the volume factors read at the datum as Flow reads them; injected gas is not counted (the free gas it sends to the producers is not in the schedule). Not computed when a rate cannot be summed; the schedule walk fails safe: an event on a well it does not read marks the rates unknown.
physics.voidage.group_without_producerswarningGroup injection under voidage-replacement control on a group whose subtree holds no producer anywhere in the schedule — producers introduced mid-schedule and groups moved by a later tree statement count — with no voidage source group named.

Why each check exists:

physics.volumetrics.exceeds_stoiip

A tank cannot deliver more oil than it holds, but this tank is an approximation of what Flow counts, so it cannot prove a contradiction: history and a rate target past it are both warnings, and the message names the tank, the history and the target part. The tank omits what Flow may count: end-point scaling (an ENDSCALE SWL below the table’s connate water), pore-volume edits (MULTPV in GRID or EDIT, PORV, MULTREGP), ROCK records by region or ROCKTAB, oil vaporized in the gas (VAPOIL), transition zones, and gas dissolved in water; it reads history at full rate, so an efficiency factor below 1 overstates it. On SPE1 the computed tank lands within 0.1% of Flow’s own STOIIP. A refusal would need an exact in-place volume, which these checks do not compute.

physics.volumetrics.exceeds_giip

Same conservation argument for the gas cap, and the same rule: a warning for history and targets alike, since the tank omits what Flow may count (gas dissolved in water, end-point scaling, pore-volume edits). Injected gas is gas the wells may withdraw: an aquifer gas store starts with none in place.

physics.volumetrics.implausible_recovery

The oil a displacement takes stays within what its own end points displace. The tank omits what Flow may count, so this is an estimate held against an estimate and informs: a fixed recovery bound would be one typical field’s (some water drives recover 35-75%), and a warning on history would need every corpus history fire to be a real defect.

physics.voidage.overpressuring

Water and rock are nearly incompressible, so net injection into a sealed volume drives pressure up by the rise the fact states; the rise alone is the fact’s, never a row. Past an injector’s own limit the injection is BHP-limited and the scheduled volume is not injected, and the row names the limit. Measured (2026.04): SPE1CASE1_BRINE’s injector sits on its 9014 psia limit through the run (opm-tests’ reference: WWIR 72,449 falling to 2,339 against 100,000 scheduled); a fixed ratio would be one typical field’s.

physics.voidage.group_without_producers

Voidage replacement is computed from the target group’s own production, so a group of injectors alone has zero voidage and injects nothing at all. Measured on both pinned releases: FWIR is 0.0 at every step and 0.0 in total, while the run reports itself clean and warns about nothing. Both repairs restore the full rate — put the producers in the group, or name their group as the voidage source.

Feasibility compares each well’s BHP limit against the reservoir pressure at its shallowest completion, hydrostatically corrected from the equilibration datum with the brine gradient (0.52 psi/ft / 0.1177 bar/m, formation brine at about 1.2 g/cc, the heaviest column a well or a reservoir holds). The judgment replays across every schedule event — control reissues, retargets, well introductions — using the validate-time datum.

CodeTierWhat it checks
physics.control.producer_cannot_flowwarningProducer bhp_limit at or above the corrected reservoir pressure at its completions, carried to the reference depth the limit is stated at (the well’s WELSPECS depth when it states one) through a wellbore between empty and full of brine.
physics.control.injector_cannot_injectwarningInjector bhp_limit at or below the corrected reservoir pressure at its completions, carried to the depth the limit is stated at in the same way (a wellhead limit counts its brine column).
physics.control.marginal_drawdownwarningA producer’s BHP limit within 2% below, or an injector’s within 2% above, the corrected reservoir pressure.
physics.control.inactive_phaseerror or warningA replayed schedule control, target, limit, or injection stream referencing a phase the model’s formulation does not carry. Tier: E for a control mode, target or injection stream on an inactive phase; W for a limit.
physics.schedule.time_reversalerrorEvery report time is later than the time the schedule has reached from the model’s start, compared to the time of day: a date behind it, the same date again, the start itself, or a date a run of steps has reached.
physics.schedule.century_stepwarningA single report step longer than 10957.5 days (~30 years).
physics.schedule.retune_ineffectivewarningA scheduled retune whose maximum timestep is no shorter than every report step it governs, or whose minimum timestep exceeds one of them.
physics.schedule.retarget_after_historywarningA retarget (ps.Adjust) of a well at a report time after its last history record, while that record is still its control.
physics.schedule.reversal_without_hysteresisinfoOn a model that states no hysteresis: a well that injects a phase and later produces, or one that injects water and gas in turn.
physics.schedule.all_wells_shutinfoEvery well shut or stopped for the schedule’s entire duration.
physics.schedule.negative_history_rateerrorObserved history production/injection rates must be non-negative.
physics.schedule.efficiency_above_unityerrorWell/group efficiency factors above 1.
physics.schedule.gaslift_without_gas_phaseerrorLift-gas optimization bound to a well in a model with no gas phase.

Why each check exists:

physics.control.producer_cannot_flow

Inflow requires drawdown, but the judgment is about TIME ZERO only: injection support and repressurization can restore drawdown a t0 snapshot lacks, and the BHP limit need not be the active control. Acknowledge it for a well that comes on after the pressure does.

physics.control.injector_cannot_inject

Injection requires overpressure, judged at TIME ZERO only: production drawdown can open the window later, and the BHP limit need not be the active control. Acknowledge it for a well that comes on after the pressure falls.

physics.control.marginal_drawdown

A sliver of drawdown technically flows but delivers next to nothing and turns the well into a numerical on/off switch around its limit; a real design leaves working pressure margin.

physics.control.inactive_phase

Flow refuses a control mode on an inactive phase at load, and a retarget or an injector of one aborts the run (“Canonical phase 2 is not active”). A limit on an inactive phase is a different thing: Flow says “The constraint will be ignored” and runs, so it warns. The declared control’s twin is well.control.inactive_phase, on the same terms.

physics.schedule.time_reversal

Time is monotone, and a report step has a length. OPM Flow 2026.04 refuses a DATES “not later than the end time of previous report step” at load.

physics.schedule.century_step

One multi-decade step averages away every transient — breakthrough, decline, interference — that the simulation exists to resolve; a deliberate long-horizon coast acknowledges.

physics.schedule.retune_ineffective

The report steps already cut the step shorter than such a maximum, and a minimum above them asks for steps the report schedule will not allow. Either card reads as timestep control and changes nothing — the mirror image of century_step.

physics.schedule.retarget_after_history

A retarget changes one number and keeps the control, so the well stays under its history control, at the last record’s observed rates, to the schedule’s end unless the changed number binds. A forecast needs a control of its own from that date. A retarget at the record’s own report time is part of the history statement and is not named.

physics.schedule.reversal_without_hysteresis

The saturations around the well reverse direction, and with no hysteresis Flow follows the same curves both ways and traps nothing on the way back. One curve is a modelling choice, so the row informs.

physics.schedule.all_wells_shut

Nothing flows, so the run is an expensive equilibrium check. Legitimate for observation runs, but worth stating.

physics.schedule.negative_history_rate

History records report measured volumes; a negative value is a data error, and flow reversals belong on injection events, not negative production.

physics.schedule.efficiency_above_unity

The factor is the on-stream time fraction; more than 100% uptime has no physical meaning and silently scales rates above their targets.

physics.schedule.gaslift_without_gas_phase

The lift gas has no PVT and no surface stream to belong to; the optimizer would allocate a fluid that does not exist.

CodeTierWhat it checks
physics.schedule.decorative_econ_limitwarningGas-ratio economic limits (maximum_gas_oil_ratio, maximum_water_gas_ratio) in a model with no gas phase.
physics.schedule.injector_econ_limitinfoEconomic production limits attached to injectors.
physics.schedule.econ_min_above_targetwarningEconomic minimum oil rate above the well’s current control target.
physics.schedule.wtest_reopen_chatterwarningA well shut by an economic minimum above its target, then periodically re-tested for ECONOMIC recovery.
physics.schedule.reinjection_above_unitywarningGroup reinjection fraction above 1.
physics.schedule.vfp_off_axiswarningA well’s operating rate target outside its VFP table’s flow axis span by more than a factor of 2 in either direction.
physics.schedule.vfp_thp_off_axiswarningA tubing-head pressure a well states outside the THP axis of the lift table it holds there: a thp_limit or a thp target against the table the control binds, a ps.Adjust(thp=...) (WELTARG) against the table its well holds at that point (a control’s binding or a dated History’s lift_table moves it), and either inside an action rule’s body, where a binding holds for the rest of that body only; a zero limit (the record’s no-limit) and a value stated by an expression stay silent.
physics.lift.bhp_below_thpwarningA production lift table (ps.lift.ProductionTable) a producer binds (a ps.Produce or ps.History lift_table=, or a lift binding, a rule’s included) with a bottom-hole pressure below its tubing-head pressure, no pump or compressor on its lift axis, and not a network’s table.
physics.lift.injection_bhp_rises_with_rateinfoAn injection lift table whose bottom-hole pressure rises with the rate at one tubing-head pressure.
physics.lift.head_exceeds_water_columninfoA water-injection lift table whose bottom-hole pressure exceeds its tubing-head pressure by more than the model’s own water column to the table’s depth (the heavier of its surface water and its water at a datum).
physics.schedule.group_target_ungroundedinfoA group oil-rate target more than 100x the summed member-well targets.
physics.schedule.udq_zero_divisorwarningA UDQ dividing by a gas quantity in a model with no gas phase, with no ps.Numerics(user_quantity_undefined_value=...) declared.
physics.schedule.action_condition_unreachablewarningAn action triggering on a pressure beyond the physically reachable range (0–72500 psi FIELD, 0–5000 bar METRIC) on the impossible side.
physics.schedule.action_condition_tautologicalwarningAn action comparing pressure against a literal outside the reachable range on the always-true side, with no minimum_wait.

Why each check exists:

physics.schedule.decorative_econ_limit

The ratio is identically zero forever, so the protection can never trigger; the limit documents an intention the model cannot honor.

physics.schedule.injector_econ_limit

An injector produces nothing for the limit to test; the rows are semantically void and probably meant for the paired producers.

physics.schedule.econ_min_above_target

The well is born dead: it can never meet the minimum it is tested against and shuts at the first economic test. Staged later retargets that make the limit reachable acknowledge.

physics.schedule.wtest_reopen_chatter

Each test reopens the well into the same impossible economics and instantly re-shuts it — a chatter loop that produces nothing and pollutes the well events log.

physics.schedule.reinjection_above_unity

Reinjecting more than the group produces requires a make-up stream that the model does not express; the extra fluid appears from nowhere.

physics.schedule.vfp_off_axis

Every BHP lookup then extrapolates the lift curve from day zero, and extrapolated VFP behavior is notoriously unphysical — the well operates on an invented curve.

physics.schedule.vfp_thp_off_axis

OPM Flow reads the table there by extrapolation and prints no warning. Four corpus decks state one: WVFPDP-01 and -02 (a limit of 75 on [10, 35]) and WVFPEXP-01 and -02 (5 on [10, 35]). A well can also start above the axis with its stated THP inside it (NeqSim W0’s FL1 run: 86.9 bar on day 1 against a top node of 60), which no check of the model sees: span the THP the well starts at.

physics.lift.bhp_below_thp

A producing well’s fluid rises against its own weight and friction, so its bottom-hole pressure stands above its tubing-head pressure unless a pump or compressor adds the energy; a table below it delivers fluid uphill for free. A table no producer binds is no well’s tubing and changes no run: the flowline table five of flowl_c_vfp.ecl, below its head at 10,923 of its 61,560 pressures, is a network branch’s table in the decks that use it (model5’s 5_NETWORK_MODEL5_STDW, gaslift’s GASLIFT-13) and is bound by no well in the fourteen that only state it. The corpus’s tables a well binds fire nowhere; rescoup’s generated well tables do, and their decks are refused at import.

physics.lift.injection_bhp_rises_with_rate

At one tubing-head pressure friction takes more of an injector’s head the faster it injects, so its bottom-hole pressure falls with the rate. No corpus table does it, so it informs until a run backs it.

physics.lift.head_exceeds_water_column

Friction only takes from an injector’s head, so the bottom-hole pressure stands at most the tubing-head pressure plus the water’s weight. No corpus table does it, so it informs until a run backs it.

physics.schedule.group_target_ungrounded

The group control can never bind — the members saturate their own limits far below it — so the group target is decorative and probably a units or magnitude slip.

physics.schedule.udq_zero_divisor

The divisor is identically zero, so the quantity is undefined at every evaluation; downstream controls read whatever the simulator substitutes.

physics.schedule.action_condition_unreachable

The condition can never come true: the action is dead code carried as if it were protection.

physics.schedule.action_condition_tautological

The condition holds at every evaluation, so the action fires every step — usually a periodic policy missing its minimum_wait, executing far more often than intended.

CodeTierWhat it checks
physics.contacts.contact_ordererrorGas-oil contact deeper than the water-oil contact.
physics.contacts.all_waterwarningWater-oil contact above the grid top while oil-rate producers are scheduled.
physics.contacts.below_gridinfoWater-oil contact more than 50 total column thicknesses below the grid base, in a column with an oil leg.
physics.contacts.gas_water_below_gridwarningThe same distance in a gas-water formulation (gas and water active, no oil); in a storage model (CO2STORE or H2STORE), any gas-water contact below the grid base.
physics.contacts.datum_remotewarningEquilibration datum more than 1000 m (3281 ft) outside the grid’s depth span.
physics.contacts.producing_from_water_legwarningEvery completion of an oil-rate producer below the water-oil contact.

Why each check exists:

physics.contacts.contact_order

Gravity stacks gas over oil over water; an inverted contact pair describes a fluid column that cannot be in static equilibrium, and equilibration initializes nonsense saturations from it.

physics.contacts.all_water

The reservoir initializes 100% water with no oil anywhere, yet oil production is demanded — the schedule and the initial state describe two different reservoirs. Flow runs it (no oil in place), so it warns; acknowledge it for a deliberate water-only start.

physics.contacts.below_grid

No cell initializes water: every cell sits in the leg above the shallowest contact below the base, oil, or gas when the gas-oil contact is below the base too, and the message names which. Perfectly legal, but it should be a stated choice.

physics.contacts.gas_water_below_grid

Every cell then initializes in the gas leg, so a model written for gas in brine holds no water at all: a saline aquifer stated this way is a tank of gas.

physics.contacts.datum_remote

The entire initial pressure field is then a long-range hydrostatic extrapolation from a point that touches no rock; almost always a mistyped datum depth (genuine off-structure datum conventions acknowledge).

physics.contacts.producing_from_water_leg

The well can only drain the water leg, so it produces water against an oil target from day zero; deliberate water-leg completions (pressure support observation, coning studies) acknowledge.

CodeTierWhat it checks
physics.pvt.density_orderwarningSurface oil density above surface water density, in every PVT region.
physics.pvt.reservoir_density_ordererror or warningAt each equilibration datum, (rho_oil,surface + Rs x rho_gas,surface) / Bo with Rs and Bo read as Flow reads them (dead oil and oil-water models included), against rho_water,surface / Bw at that pressure, each region with its own PVT tables (one Equilibrium over several equilibration regions is judged in each, as Flow builds each column with its first cell’s PVT region); without a contact, each saturated PVTO node of every PVT region. Tier: E when the oil at an equilibration datum is denser than the water under its stated contact; else W.
physics.pvt.fvf_subunitywarningOil formation volume factor below 0.90 anywhere in the oil tables, when the oil slot holds a liquid.
physics.pvt.oil_fvf_marginalwarningOil FVF in the 0.90–0.98 band.
physics.pvt.oil_fvf_increases_with_pressureerrorBo rising with pressure along the dry-oil table or any undersaturated branch.
physics.pvt.family_bo_crossingerrorSaturated Bo falling as solution GOR rises across live-oil families, compared at a pressure both records state.
physics.pvt.gas_fvf_non_monotonicerrorGas FVF rising with pressure inside the table.
physics.pvt.gas_viscosity_decreasingerrorGas viscosity falling at any step of the tabulated pressure column.
physics.pvt.gas_range_below_initialwarningDry-gas table ending below the initial datum pressure.
physics.pvt.rsvd_exceeds_saturatederror or warningAt each equilibration datum, RSVD’s solution GOR (or PBVD’s bubble point read through the saturated line), when EQUIL item 7 asks for a table, against the saturated Rs at the datum pressure. Tier: W past 1.02x, E past 2x.
physics.pvt.viscosity_rangewarningPhase viscosities outside broad physical bands: oil 0.05–100000 cP, water 0.05–5 cP, gas 0.001–0.2 cP.
physics.pvt.water_compressibility_absurdwarningWater compressibility above 1.5e-4 1/bar (~1.03e-5 1/psi).
physics.pvt.water_fvf_rangewarningWater FVF below 0.90.
physics.pvt.saturated_nodeserrorA PVTO, PVTG or PVTGW table with fewer than two saturated rows (records).
physics.pvt.undersaturated_missingerrorA PVTO, PVTG or PVTGW table whose last record states its saturated row alone.
physics.pvt.column_ordererrorA column Flow’s schema orders and the table does not: PVDO’s and PVDG’s FVF and each PVTO record’s Bo not falling strictly (a level step), PVDO’s viscosity falling, a PVTG or PVTGW record’s vaporized ratio not falling strictly. Compared exactly, as Flow compares.
physics.pvt.bubble_point_ordererrorA PVTO record’s bubble point (its first pressure) below the previous record’s while its Rs is higher.
physics.pvt.rs_rv_producterrorSaturated Rs times saturated Rv at or above 1 at any pressure both lines span (DISGAS with VAPOIL).
physics.pvt.gas_heavier_than_oilerror or warningThe gas at the datum pressure, (rho_gas,surface + Rv rho_oil,surface) / Bg, at least as dense as the oil there, (rho_oil,surface + Rs rho_gas,surface) / Bo, each equilibration region with its own PVT tables; an oil slot holding a gas is not judged. Tier: E when the region’s gas-oil contact lies inside its active cells with an oil leg under it (the water contact deeper); else W.
physics.pvt.gas_heavier_than_waterwarningThe gas at the datum pressure at least as dense as the water there (when the oil check has not spoken).
physics.pvt.implied_z_factorwarningWith the fluid’s temperature (or a uniform thermal or storage temperature) stated, every node’s z = Bg p T_sc / (p_sc T) outside a real gas’s 0.2 to 3; without one, every node’s z T beyond any gas where water is liquid (z 0.2 to 3, T 273 to 647 K); and a named unit factor bringing every node inside.
physics.pvt.total_compressibility_negativewarning or infoOn an interval of a saturated line, c_t = [-dBo/dp + dRs/dp (Bg - Bo Rv) / (1 - Rs Rv)] / Bo at or below zero, as the tables state it (and its twin on the gas line). Tier: W where the interval stays at or below zero with the model’s own rock and connate-water compressibility added (c_r + c_t (1 - Swc) + c_w Swc, at each datum reading the tables); else I.
physics.pvt.phase_orderinfoAt one pressure on both saturated lines, the gas at least as viscous as the oil, or holding at least as much oil per reservoir volume (Rv / Bg) as the oil phase (1 / Bo).
physics.pvt.viscosity_trendinfoA viscosity falling with pressure along an undersaturated PVTO record, or along the saturated PVTG line.

Why each check exists:

physics.pvt.density_order

Stock-tank crude usually floats on water, and an inversion is most often a units or column slip; but oil below 10 degrees API is denser than water and Flow runs it (oil at 1010 kg/m3 against water at 1000 matched its base to five digits), so it warns. The density that decides the initial state is the reservoir one, below.

physics.pvt.reservoir_density_order

An equilibration states oil above water. Oil denser than the water under it contradicts that statement, and Flow follows gravity: a dead oil at Bo 0.85 ran with no oil in place at all. Extra-heavy oil and bitumen are real, so without a stated contact the check reports rather than refuses; an extra-heavy oil is initialized directly, not from contacts. The message names a unit slip only where one alone settles the column: a gas surface density beyond any gas at surface conditions (the gas the oil dissolves carries it), else, past twice the water’s density, which no liquid oil reaches, Rs typed in scf/stb.

physics.pvt.fvf_subunity

Reservoir oil holds dissolved gas and thermal expansion; it should not occupy 10%+ less volume than the same oil at stock tank, and Bo this low is usually a swapped column or a reciprocal. Flow runs it, so it warns; where the shrunken oil ends up denser than the water under its contact, reservoir_density_order refuses the model. Calibrated: the black-oil oil slot does not have to hold a liquid, and the guard stands down when surface oil density is below 100 kg/m3 METRIC / 6.24 lb/ft3 FIELD.

physics.pvt.oil_fvf_marginal

Slightly sub-unity Bo is representable but sits at the edge of real dead-oil behavior; usually the table was built against nonstandard surface conditions and deserves a check.

physics.pvt.oil_fvf_increases_with_pressure

dBo/dp > 0 is negative compressibility — compressed oil expanding. The simulator inverts it into unstable, non-physical pressure response.

physics.pvt.family_bo_crossing

More dissolved gas always swells oil. Crossing families make the saturated envelope non-monotone, and every interpolation between them lands on a fluid that cannot exist. Calibrated: two records that share no pressure node are one undersaturated branch written in two records — the documented idiom for extending past the nodes the first record had room for — so there is nothing to compare and the guard says nothing.

physics.pvt.gas_fvf_non_monotonic

Bg is inversely proportional to pressure through the z-factor; no real-gas correlation produces a rising segment. The simulator would compute negative compressibility for gas.

physics.pvt.gas_viscosity_decreasing

At fixed temperature gas viscosity rises with density, and density with pressure, so the law holds step by step. OPM Flow’s parser declares the PVDG viscosity column increasing and refuses one falling step at load.

physics.pvt.gas_range_below_initial

Every cell’s gas properties are extrapolated beyond the table from day zero, so the whole run rides on extrapolation behavior rather than data; accepted extrapolation acknowledges.

physics.pvt.rsvd_exceeds_saturated

The law holds at each depth, Rs(z) <= Rs_sat(P(z)); it is judged at the datum, where both sides are stated. Flow caps the initial Rs at saturation without a word, so a demand past it is dissolved gas the model states and the run drops. The corpus reaches 1.25x at most, and every unit slip measured is 5.6x or more, so past twice saturation it is refused as a slip. Negative RSVD or RVVD values and PBVD or PDVD pressures at or below zero are refused at construction (initialization.*.range), as is a directly initialized pressure at or below zero.

physics.pvt.viscosity_range

These bands span everything from condensate to cold bitumen and cool brine to hot steam; values outside them are almost always cP/Pa.s slips, which silently rescale every mobility by 1000.

physics.pvt.water_compressibility_absurd

A unit-slip catcher, not a field value: no brine, gas-saturated included, reaches 1.5e-4 1/bar (Osif 1988). The slip it catches is a 1/bar value typed into a FIELD deck, which reads 14.5 times too large (4.67e-5 becomes 6.8e-4 and passed a 1e-3 ceiling). Flow runs even 4.67e-3 1/bar, so it warns. The message states the ceiling and the slip, never a typical brine.

physics.pvt.water_fvf_range

Real Bw lives near [0.95, 1.1]; water cannot lose 10% of its volume downhole. Flow runs Bw 0.85 with every water volume scaled (oil in place moved 30%), so it warns.

physics.pvt.saturated_nodes

Flow refuses it at load (“Saturated PVTO must have at least two rows”): the saturated line cannot be interpolated from one node.

physics.pvt.undersaturated_missing

Flow extends every single-row record from the next record that has undersaturated rows, finds none past the last and refuses the table at load (“The last table must exhibit at least one entry for undersaturated oil!”).

physics.pvt.column_order

Flow refuses each at load (“Non-monotonic values in keyword PVDO … BO need to be entered in strictly decreasing order”; the flat-Bo PVDO witness, W3a). A rising Bo or Bg and a falling PVDG viscosity keep their own codes.

physics.pvt.bubble_point_order

Ill-posed: more dissolved gas needs more pressure to stay in solution, and with the bubble point falling Pb(Rs) has no inverse, so the saturated Rs at a pressure has no single answer. Flow sorts the line by pressure and runs (rc 0). Two records at one bubble point (padded tables start several at the standard pressure) leave the line monotone and are not judged.

physics.pvt.rs_rv_product

Ill-posed: the black-oil split of surface oil and gas into their reservoir phases divides by 1 - Rs Rv. SPE3 with Rv x10 ran (rc 0) through 140 failed well-equation inversions and five time-step chops in one day.

physics.pvt.gas_heavier_than_oil

Under a contact inside the grid the model states a denser gas resting on a lighter oil, a contradiction: Flow builds the gas cap and it sinks at once (SPE1 with its contact in layer 1 and Bg x 0.3, wells shut: layer 1’s Sg fell 0.88 to 0.8716 in one day). Elsewhere it warns: a gas cap or freed gas would sink; with both contacts at one depth (the gas-condensate idiom) no oil lies under the gas and Flow holds the column as stated (the same deck with its water contact at 8340 ft: steps 0 and 1 identical). The message gives the gas’s own weight and the oil it carries, and names Rv where the oil it carries makes it the heavier (a condensate yield typed for Rv, a thousand times it, seen in test decks and a units audit); else Bg’s unit and the surface densities.

physics.pvt.gas_heavier_than_water

A gas zone over that water would sink through it; Flow runs it as stated (SPE1 with Bg x 0.2, rc 0). The message names Rv, as gas_heavier_than_oil’s does, where the oil the gas carries makes it the heavier, in either unit system; Bg’s unit is implied_z_factor’s to name, from the whole table, so this message names no Bg factor (one node’s factor named a Bg 5.6 times too small as the fix).

physics.pvt.implied_z_factor

A unit-slip catcher, not a field value: the band is the gas law’s and liquid water’s. The message names each factor that settles it (FIELD: a dimensionless Bg, rb/scf, ft3/Mscf, pressures in bar, a whole METRIC table; METRIC: rb/Mscf, pressures in psi, a whole FIELD table). At a stated temperature the row names the node, its z, Bg and the temperature, and each slip with the z it gives (Rosa’s rb/scf table typed as rm3/sm3: z 0.182 at 250 barsa and 90 degC, x 5.6146 gives 1.02; z T alone, 60 to 69 K, lies inside the band). The z-factor itself is the fact fluid/<pvt>/implied_z_factor_min and _max, computed at that temperature.

physics.pvt.total_compressibility_negative

A saturated system that shrinks as its pressure falls has no stable state (Singh, Fevang and Whitson). SPE1 started saturated at 4800 psia, its 4014.7-5014.7 psia interval made negative (Bo 2 for 1.827 at Rs 1.618), the injector shut: Flow took 29,312 time steps for its first 12.3 days, down to 6.5e-7 days, through 11 convergence failures and 11 chops, before a 600 s limit stopped it; the control ran ten years in 123. The warning’s fix names the FVF against the ratio over the interval with both bounds (a Bo under, or an Rs over, the value that keeps the line from shrinking). Where the rock’s compressibility outweighs the interval (a near-critical table) it informs. The datum’s value is the fact initial/<eq>/total_compressibility.

physics.pvt.phase_order

Near a critical point the phases converge, so it informs; elsewhere it marks a swapped or mistyped column.

physics.pvt.viscosity_trend

Compressed fluid is denser and no less viscous; Flow reads the column as stated (only the books behind it).

CodeTierWhat it checks
physics.relperm.exceeds_unitywarningAny relative permeability value above 1, interior rows included.
physics.relperm.nonzero_at_connateerrorA rising relative-permeability column (krw, krg, SOF2’s kro, both SOF3 columns, SLGOF’s krog) above TOLCRIT (1e-6 unless stated) at the table’s first row, as Flow’s binary search for the critical saturation reads it, in every family Flow searches. Here and in the two rows below, a cell left to the simulator (1*) is read as Flow fills it: linearly in the saturation between the stated cells either side, held at the nearest stated cell beyond them.
physics.relperm.kro_at_max_watererrorA falling relative-permeability column (SWOF’s krow, SGOF’s krog, SLGOF’s krg, SGWFN’s krw) never at or below TOLCRIT, Flow’s way.
physics.relperm.no_mobile_stateerrorA rising relative-permeability column never above TOLCRIT.
physics.relperm.column_directionerror or warningA relative-permeability column against its saturation: krw, krg and kro must not fall with their own saturation, krow and krog must not rise as water or gas rises. An error in the families Flow orders at load (SWFN, SGFN, SOF2, SOF3, SLGOF, WSF, GSF), a warning in SWOF, SGOF and SGWFN.
physics.relperm.gas_table_startwarningAn SGOF whose first gas saturation is not 0.
physics.relperm.mobile_oil_windowwarningFlow’s mobile-oil windows on the table end points: SOWCR < 1 - SWCR - SGL against water and SOGCR < 1 - SWL - SGCR against gas. It cites the displacement end-point facts.
physics.relperm.gas_table_shortwarning or infoA gas-oil table (SGOF, SLGOF, SGFN) whose last gas saturation is below 1 - Swc. A warning where the saturation region has cells above its gas-oil contact (Cartesian and radial grids), an info row where it has none or PetroScript cannot tell. Its fix keeps krog’s first zero where it is.
physics.relperm.three_phase_endpoints_disagreewarningThree-phase models: krow at connate water (SWOF’s first row, SOF3 at So = 1 - Swl - Sgl) against krog at connate gas (SGOF’s first row, SLGOF’s last).
physics.relperm.step_jumpinfoA relative-permeability interval carrying a fifth of the column’s span at ten times both its mean slope and each neighbour’s slope.
physics.pc.step_jumpinfoA capillary-pressure interval of the same shape inside the curve: the first and last intervals are not judged.
physics.relperm.immobile_window_overlaperrorLET critical saturations of the two phases summing to 1 or more.
physics.relperm.imbibition_outside_drainageerrorImbibition connate water/gas endpoints above their drainage baselines.
physics.relperm.imbibition_trapping_invertederrorImbibition critical (trapped) gas above its maximum gas saturation.
physics.relperm.imbibition_curve_above_drainagewarningFor each pair of saturation and imbibition regions an active cell uses: the imbibition table’s non-wetting relative permeability (gas; oil against water) above the drainage table’s at some saturation.
physics.pc.surface_tension_rangewarningA J-function surface tension above 75.6 dyn/cm, water’s own against air at its freezing point.
physics.pc.sign_conventionerrorCapillary pressure out of the order or sign its law requires: water-oil Pcow (SWOF, SWFN) rising with water saturation at any step; gas-oil Pcgo (SGOF, SGFN, SLGOF) or gas-water Pcgw falling with gas saturation at any step, or below zero. In a gas-water model OPM reads Pcgw from SWFN and zeroes SGFN’s column, so SGFN there is judged on its order only, which Flow’s parser refuses too. Defaulted (1*) cells are left out, since a fill between stated rows cannot change a step’s sign.

Why each check exists:

physics.relperm.exceeds_unity

Relative permeability is normally normalized to absolute permeability, but SCAL curves normalized against another base legitimately exceed 1. Both supported OPM Flow releases load, run, and USE such a table without a word, so refusing it would be a gate rather than a guard — the simulator happily computes the resulting super-mobilities.

physics.relperm.nonzero_at_connate

Flow finds each critical saturation as the last row at or below TOLCRIT; with none it reads before the table’s start and refuses the deck (“Value: 18446744073709551615 out of range”, rc 1 on 2026.04 for SPE1 with 0.05 added to SWOF’s krw).

physics.relperm.kro_at_max_water

Flow’s search finds no immobile row and the run aborts (SPE1 with SWOF’s krow ending at 0.05, or SGOF’s krog at 5e-4: rc 139 on 2026.04).

physics.relperm.no_mobile_state

The phase never flows, Flow’s search finds no mobile row and the run aborts (SPE1 with SGOF’s krg 0 throughout: rc 139 on 2026.04).

physics.relperm.column_direction

Flow refuses an ordered column at load (“Non-monotonic values in keyword SWFN”, rc 1); it runs a RANDOM one (rc 0), and its binary search then finds a critical saturation that is not the curve’s.

physics.relperm.gas_table_start

Flow reads the first row as connate gas, the least gas any cell holds: SPE1 with SGOF starting at Sg 0.02 starts every cell, the undersaturated oil leg included, with Sg 0.02 of free gas (rc 0 on 2026.04; Flow’s own warning names only SGL + SWU > 1).

physics.relperm.mobile_oil_window

Oil outside its window never flows; Flow reports the window at initialisation and runs on (OilPhaseConsistencyChecks, non-critical).

physics.relperm.gas_table_short

Flow does not extrapolate the table: it starts each gas-cap cell at the table’s last gas saturation and fills the rest with oil (one test deck’s 0.25 of oil in the gas cap, recovery read 11.6% against 8.5%). A krog whose first zero is at exactly 1 - Swc is a critical oil saturation of 0; Flow computes it as (1 - Sg) - Swc in doubles and aborts when rounding lands below zero (Swc 0.2, 0.1, 0.22 on 2026.04), so the SGOF writer puts that row the fewest units in the last place lower (target.critical_oil_rounding, an info row). The water half is not judged: its fix invites a flat krw tail.

physics.relperm.three_phase_endpoints_disagree

The model states the oil’s relative permeability with connate water and no gas twice, with two values, and the three-phase oil curve reads both; the manuals say they must be the same. 0 of 1,996 corpus pairs differ.

physics.relperm.step_jump

Flow interpolates tables linearly and differentiates them; a step reaches the solver as a near-vertical slope. Information only: no run has measured the harm.

physics.pc.step_jump

As for the relative permeability. Both ends of a capillary-pressure curve steepen by nature: the climb toward connate saturation, and an entry (threshold) pressure, which tables write as a drop to 0 in the last interval (13 corpus decks).

physics.relperm.immobile_window_overlap

The two immobile windows cover the entire saturation axis: no saturation exists at which anything flows, so the model is a sealed rock wearing relperm curves.

physics.relperm.imbibition_outside_drainage

Scanning curves live inside the bounding drainage curve; an imbibition endpoint outside it gives the hysteresis model no valid interpolation space and the scanning logic produces undefined mobilities.

physics.relperm.imbibition_trapping_inverted

Land-type trapping cannot trap more gas than was ever present; the trapping model’s denominator goes non-physical.

physics.relperm.imbibition_curve_above_drainage

Carlson’s and Killough’s scanning curves leave the drainage curve at the reversal and run on or below it; an imbibition curve above the drainage curve has none, and one whose critical saturation is the lower traps nothing. The corpus’s hysteresis decks are silent.

physics.pc.surface_tension_range

A unit-slip catcher: no pair of reservoir fluids has a higher interfacial tension, and Flow reads the value in dyn/cm in every unit system and scales the capillary pressure with it. A laboratory mercury-air value (about 480) or a tension typed in another unit reads this way.

physics.pc.sign_convention

Pcow = Po - Pw falls as water fills the pore space in water-wet, oil-wet and mixed-wet rock alike (Anderson 1987, SPE-15271); its sign is free, because oil-wet rock carries a negative Pcow. Gas is non-wetting to both oil and water, so a gas capillary pressure rises with gas saturation and is never negative. OPM Flow runs a table out of this order without a word and reaches a different answer, so the contradiction is refused.

CodeTierWhat it checks
physics.polymer.viscosity_reducingerrorPolymer viscosity multiplier below 1 at any concentration.
physics.polymer.initial_concentration.shape / .range, physics.polymer.initial_molecular_weight.shape / .rangeerrorInitial polymer state fields must match the grid shape and be non-negative.
physics.foam.carrier_phase_inactiveerrorFoam mobility reduction carried by a phase not active in the model.
physics.storage.phase_inactiveerrorCO2/H2 storage package in a model with no gas phase.

Why each check exists:

physics.polymer.viscosity_reducing

Dissolved polymer thickens water — that is its entire purpose; drag-reducing agents act on turbulence, not viscosity. A multiplier below 1 makes the flood improve mobility ratio backwards.

`physics.polymer.initial_concentration.shape` / `.range`, `physics.polymer.initial_molecular_weight.shape` / `.range`

A misshapen field silently misassigns concentrations across cells, and negative concentration or molecular weight has no physical meaning.

physics.foam.carrier_phase_inactive

Foam rides its carrier phase; without that phase there is no stream to reduce, and the foam package would act on nothing.

physics.storage.phase_inactive

The stored component exists in the gas phase; without it there is no store — injection would have no phase to put the component into.

CodeTierWhat it checks
physics.rock.porosity_boundserrorAny porosity value outside [0, 1] in an active cell, judged after the grid program (MULTIPLY, COPY and the rest) has run.
physics.rock.permeability_negativeerrorAny negative permeability value in an active cell, judged after the grid program has run.
physics.rock.porosity_implausiblewarningPeak porosity above 0.45, or the most porous cell below 0.01.
physics.rock.permeability_implausiblewarningPeak permeability above 5e6 mD, or the most permeable cell below 1e-3 mD.
physics.contacts.datum_above_surfacewarningA datum depth below zero: above the surface, since depths run positive downward.
physics.stand_down.bandsinfo (printed)The field-scale bands stand down on this model, and why: LAB units, a restart, or a datum shallower than 100 m.
physics.stand_down.geometryinfo (printed)A grid whose cell geometry PetroScript does not compute yet (corner-point): the in-place, contact and well-against-contact checks stand down.
physics.rock.compressibility_unstatedwarning or infoA model whose wells produce or inject while its rock states no compressibility: a warning when oil or water carries the pressure response, an info row on a gas-water model. Stating compressibility=0.0 records incompressible rock as a choice.
physics.rock.compressibility_rangewarningRock compressibility above 5e-3 1/bar (~3.4e-4 1/psi).
physics.rock.reference_pressure_remoteinfoThe rock’s pore-volume factor at the datum pressure, 1 + X + X^2/2 with X the compressibility times the datum pressure above the reference pressure (the fact rock/<rk>/pore_volume_factor), more than 2% from 1. Models with a compaction table are not judged.
physics.rock.kv_above_khinfoOn a model of more than one layer, the vertical permeability above the horizontal (the geometric mean of x and y) in more than half the active cells (the facts resolution/grid/kv_above_kh_share and kv_over_kh_median).
physics.transport.dispersivity_below_gridinfoThe largest stated dispersivity below the grid’s own numerical dispersivity, half the median cell size, along an axis the active cells span (the facts resolution/grid/numerical_dispersivity_<axis>; Cartesian grids).
physics.grid.zcorn_invertederrorA corner-point cell’s bottom face above its top face (beyond 1e-6 depth slack).
physics.grid.zcorn_layer_ordererrorA deeper corner-point layer sitting above the layer over it on the same pillar.
physics.grid.no_active_cellserrorNo cell survives porosity, ACTNUM, and the minimum-pore-volume threshold.
physics.grid.tops_cliffwarningOn a cartesian grid with no declared fault, neighbouring columns whose tops step by at least the thickness of a layer, so the layer’s two cells share no depth across the face.
physics.grid.pinch_threshold_excessivewarningA PINCH threshold that bridges a column’s stack of inactive cells (ACTNUM 0 or below the minimum pore volume) thicker than the median active layer. Silent on an all-active grid.
physics.grid.radial_sector_incompleteinfoA radial grid with a single angular sector narrower than 15 degrees.
physics.grid.fault_super_conductivewarningFault transmissibility multiplier above 100.
physics.grid.nnc_magnitudewarningExplicit NNC or override transmissibility beyond 1000x the largest neighbor-face transmissibility the grid and rock imply.
physics.grid.nnc_between_neighborswarningAn explicit NNC linking cells that already share a face (stands down when explicit connection edits are declared alongside).
physics.grid.zero_thickness_layer_completedwarningA layer with zero thickness in every column that wells complete through.
physics.grid.depth_implausiblewarningGrid top deeper than 12000 m (39370 ft).
physics.grid.thickness_implausiblewarningThickest total column under 1 m (3.3 ft) at field scale.

Why each check exists:

physics.rock.porosity_bounds

Porosity is a volume fraction; outside the unit interval, pore volumes go negative or exceed the rock that hosts them.

physics.rock.permeability_negative

Permeability is a non-negative material property; a negative value makes transmissibilities change sign and flow run against potential gradients. Zero-permeability barrier cells are legitimate.

physics.rock.porosity_implausible

Sandstone and carbonate top out near 0.45; diatomite reaches 0.45-0.70 (Belridge) and is the acknowledged exception. A whole field tighter than 1% stores nothing worth simulating (individual zero-porosity barriers stay legal).

physics.rock.permeability_implausible

Natural rock tops out near 1e5 mD and even engineered porous media near a few darcy-millions; a whole field under a microdarcy flows nothing on any simulation timescale.

physics.contacts.datum_above_surface

The sign slip of a test deck (EQUIL -3150 with a contact at -3320), which earlier read as a core-scale model and stood every band down; the bands now judge such a model, and this warning names the slip.

physics.stand_down.bands

A check that cannot judge a model says so once per compile rather than falling silent (62 corpus decks stood down without a word).

physics.stand_down.geometry

Makes the corner-point gap visible on 526 corpus decks until PetroScript computes their cell geometry.

physics.rock.compressibility_unstated

OPM Flow runs a model without ROCK with incompressible rock: its pore volume does not change with pressure (measured on SPE1 with ROCK removed). Test decks whose card stated no compressibility lost it silently; PetroScript never supplies a value.

physics.rock.compressibility_range

The softest unconsolidated sediment sits near 1e-3 1/bar; beyond a few times that the rock is a sponge and the pressure solution is dominated by an unphysical storage term.

physics.rock.reference_pressure_remote

Flow grows the pore volume from the reference pressure, so the run starts with that much more (or less) pore volume, and fluid in place, than the stated porosity gives. A porosity measured at the reference pressure makes it the model’s own (SPE5 states its porosity at 14.7 psia and starts 2.0% larger), so the row informs; the 2% is a tolerance on the model’s own number.

physics.rock.kv_above_kh

Layered rock conducts better along its layers than across them, and a permeability_x swapped with permeability_z reads this way. Rock that does conduct better vertically exists (two corpus decks state it on purpose), so the row informs.

physics.transport.dispersivity_below_grid

An upstream-weighted grid spreads a front by about half a cell on its own, so a smaller stated dispersivity is lost in it.

physics.grid.zcorn_inverted

The cell has negative volume and self-intersects; pore volumes and transmissibilities computed from it are garbage with plausible magnitudes.

physics.grid.zcorn_layer_order

Depth must be non-decreasing down every pillar; regressing layers interleave cells so that layer k+1 physically overlaps layer k and vertical flow connects the wrong rock.

physics.grid.no_active_cells

There is no reservoir left to simulate; every downstream computation acts on an empty set.

physics.grid.tops_cliff

The layer loses its lateral connection to itself and Flow joins each cell to the other column’s layers through non-neighbour connections: a stepped TOPS grid written for a dipping reservoir does this across most of its faces (one test grid cut 408 of its 672 x-faces into 1,254 NNCs). A dipping reservoir is written as corner-point geometry; a deliberate throw is declared as a fault.

physics.grid.pinch_threshold_excessive

PINCH item 1 generates a non-neighbour connection across inactive cells whose combined thickness is under the threshold (manual 6.3.175); it does not pinch thin active layers, and on an all-active grid it changes nothing. A threshold that bridges a thick inactive stack connects across a barrier the model itself states.

physics.grid.radial_sector_incomplete

All volumes and rates are silently scaled by angle/360 relative to the full cylinder — correct for a sector study, but the scaling should be a stated choice, not a surprise in the volumetrics.

physics.grid.fault_super_conductive

Conductive fault rock tops out around 10–100x the host; beyond that the fault is a super-conduit no geology provides, short-circuiting the field.

physics.grid.nnc_magnitude

The connection is an infinite-conductivity teleport between cells; besides being unphysical, one enormous off-band coefficient wrecks matrix conditioning.

physics.grid.nnc_between_neighbors

The NNC transmissibility adds to the existing neighbor face, double-counting the flow area — the pair is twice as connected as the geometry says.

physics.grid.zero_thickness_layer_completed

The completions connect to no rock there: the declared interval quietly contributes nothing, which usually means the layering and the completion table disagree.

physics.grid.depth_implausible

No producing reservoir sits below ~12 km — the deepest wells ever drilled barely reach it; a top this deep is almost always metres-vs-feet confusion.

physics.grid.thickness_implausible

A field-wide column under a metre is a seismic artifact, not a reservoir; core-scale models are exempt via the scale gate.

CodeTierWhat it checks
physics.wells.completed_in_voidwarningEvery completion of a well landing in cells with no active pore volume (zero porosity, inactive, or below the minimum-pore-volume threshold).
physics.wells.bore_exceeds_cellwarningWellbore radius at or beyond the Peaceman equivalent radius r0 of a connected cell, when the skin keeps OPM’s denominator positive.
physics.wells.short_circuitwarningA producer and an injector completing the same cell.
physics.wells.shared_cell_same_roleinfoTwo wells of one role completing the same cell with the schedule holding both open at the same time.
physics.wells.rate_target_without_pressure_limitinfoA forecast oil-, water- or gas-rate target (a control, never a history record) that holds, while the well is open at a report step, no bottom-hole pressure limit, no tubing-head pressure limit through a lift table, and no liquid or reservoir-volume limit; a retarget before the step counts. The weakest limit of each well’s forecast rate controls is a fact (wells/<w>/pressure_limit).
physics.wells.isolated_by_sealingwarningA well completed only in cells whose every outward face is sealed by factor-zero (or ≤1e-9) face multipliers.
physics.wells.completion_diameter_rangewarningCompletion diameter outside 0.05–1.0 m (0.16–3.3 ft).
physics.wells.reference_depth_remotewarningWell BHP reference depth more than 1000 m (3281 ft) below the grid’s depth span.
physics.completion.skin_infeasibleerrorOPM’s well-index denominator, ln(r0/min(rw, r0)) + skin, at or below zero in any cell the completion connects, with r0 Peaceman’s equivalent radius for the cell’s own dimensions and permeabilities across the bore (Peaceman 1983, SPE-10528): dx, dy with kx, ky for a vertical connection; dy, dz along X; dx, dz along Y, dz thinned by NTG as OPM thins it. A stated pressure-equivalent radius (COMPDAT item 14) is r0 itself, as in OPM. A defaulted skin is 0; a stated connection factor is not judged.
physics.completion.kh_inconsistentwarningExplicit completion kh disagreeing with rock permeability times the completed interval by more than 100x in either direction.
physics.completion.edit_misses_connection_factorinfoA ps.grid.Scale (by other than 1) or ps.grid.Assign of permeability_x, _y or _z over a cell where an open connection states its connection factor.

Why each check exists:

physics.wells.completed_in_void

The well touches no reservoir, so it produces silently zero flow and any rate target on it is demanded from rock that is not there. Calibrated to W: the target shuts such a well and continues, and the upstream corpus tests that behaviour on purpose, so this is a supported state and not a malformed model.

physics.wells.bore_exceeds_cell

OPM computes ln(r0/min(rw, r0)) + skin: past r0 it clamps rw to r0, so only the skin is left in the denominator and Peaceman’s model no longer describes the connection. Flow runs it (a skin-5 witness ran), so it warns.

physics.wells.short_circuit

Injected fluid short-circuits straight into the producer within one cell: perfect sweep on paper, no displacement in fact. Coarse grids do put real pattern wells in one cell, so the message names both wells, the cell, and whether the schedule ever has the two open at the same moment — never-simultaneous is sequential use of a cell, not a short circuit.

physics.wells.shared_cell_same_role

Each takes the cell’s full connection factor, as if it were the cell’s only well, so how the two share the cell’s inflow is not represented. The corpus’s WAG pairs (SPE5, WAGHYSTR) share a cell in turn, a control or a status change shutting one as the other opens on the same date, and are not named; with no run behind it, two open at once informs. How far a producer stands from its nearest injector is a fact (wells/<w>/nearest_injector, cells_to_nearest_injector, Cartesian grids); the fixed 50 m floor between opposite roles is gone.

physics.wells.rate_target_without_pressure_limit

OPM Flow holds the rate down to its default bottom-hole pressure of 1 atm. Measured (2026.04, SPE1 with a water injector two cells from the producer): with no limit the producer fell to 14.7 psia and, by the run’s end, drew 21,396 stb/d of liquid at a water cut of 0.71, against 18,944 stb/d at a 1000 psia limit. Corpus decks state such targets on purpose, so it informs.

physics.wells.isolated_by_sealing

The well cannot exchange fluid with the rest of the grid; it produces only its own cells’ storage and then behaves erratically against its controls. Deliberately compartmentalized studies acknowledge.

physics.wells.completion_diameter_range

Wellbores outside [5 cm, 1 m] are outside any drilling program; the usual cause is a radius-vs-diameter or inch-vs-foot slip, which shifts every well index.

physics.wells.reference_depth_remote

Every reported BHP is corrected across a fictitious wellbore column, so limits and reports are systematically offset. Wellhead-referenced (shallow) datums are a sanctioned convention and stay silent; only the downward direction is judged.

physics.completion.skin_infeasible

The connection factor divides by this number, so at or below zero it is infinite or negative: ill-posed. At skin 0 with the bore at r0 Flow aborts (“Non-finite primary variable”); at skin -20 it runs with the inflow sign-inverted, a producer reporting a BHP above the reservoir while delivering its target. It is a question about each connected cell, not a constant: in a 10 m cell with a 0.3 m bore even skin -3 gives -0.42, while in a 10 km cell skin -8.5 still gives +0.99.

physics.completion.kh_inconsistent

Explicit kh is a legal override (test-derived kh, partial penetration), but a two-orders-of-magnitude self-contradiction between two descriptions of the same connection is a darcy-for-millidarcy class of slip. The band stays two-sided and this wide: a well test’s kh can stand from about a tenth of the rock’s to some fifty times it within one field, so the mD.m-vs-mD.ft slip (3.28x) lies inside what tests measure. A skin is judged only by skin_infeasible’s denominator: the +100 and -8 skins and the D-factor ceiling were a typical field’s, and are gone.

physics.completion.edit_misses_connection_factor

OPM Flow keeps a stated connection factor, so the edit moves the cell’s transmissibility to its neighbours and leaves the well’s inflow as stated. Measured (2026.04, SPE1, CTFAC at the producer’s cell): a stated factor of 100 stayed 100 with PERMX x10 over the cell; the factor Flow computes went from 10.61 to 32.96. A history-matching edit meant for the transmissibility alone is deliberate, so it informs.

CodeTierWhat it checks
physics.aquifer.zero_compressibilityerrorAnalytic aquifer total compressibility of zero.
physics.aquifer.no_active_connectionwarningEvery cell an aquifer connects to is inactive or holds no pore volume.
physics.aquifer.volume_implausibleinfoA Fetkovich aquifer whose time constant, compressibility times water volume over productivity index (the fact aquifers/<n>/time_constant), is longer than the run (run_length_in_time_constants below 1).
physics.aquifer.past_influence_tablewarning or infoA Carter-Tracy aquifer whose dimensionless time at the run’s end (run_length_in_time_constants) is beyond its influence table’s last row: a warning on OPM Flow’s built-in table of an infinite aquifer, which ends at 10,000; an info row on a stated table.
physics.aquifer.influence_table_kindwarningAn influence table whose dimensionless pressure falls as the dimensionless time rises, or whose rows between 0.01 and 0.1 (two at least) all follow the constant-pressure influx function’s short-time series within 3%.
physics.aquifer.datum_inconsistentwarningAquifer datum pressure more than 90% (of the envelope magnitude) outside the hydrostatic envelope between the reservoir datum pressure and its water-gradient projection to the aquifer datum depth.
physics.boundary.interior_faceerrorA lab boundary condition declared on cells that do not lie on the named domain face (i/j/k equal to 1 or the grid extent).
physics.thpres.undefined_regionwarningA threshold pressure referencing an equilibration region no cell of the region map declares (a model without a map has the single region 1).
physics.thpres.no_shared_faceinfoA threshold-pressure pair whose regions meet nowhere: no shared cell face, no face across which a fault throw or a stepped TOPS juxtaposes their cells (on a cartesian grid, from the cell depths), and no stated non-neighbour connection between them. It stands down on a corner-point grid with offset columns, or under a PINCH policy, where Flow may join cells this reading cannot see.

Why each check exists:

physics.aquifer.zero_compressibility

The analytic drive term is proportional to total compressibility; at zero the aquifer can never deliver a barrel regardless of drawdown — it is an aquifer in name only.

physics.aquifer.no_active_connection

The aquifer exchanges water through its connection set; connected entirely to dead cells it can never exchange anything. Flow ignores each connection (“Connection to inactive cell … is ignored”) and runs, so the intended pressure support silently does not exist: a warning.

physics.aquifer.volume_implausible

Over such a run the aquifer does not deplete and acts as a constant-pressure boundary, which makes any depletion look supported. It is a modelling choice, so it informs; the row states both numbers.

physics.aquifer.past_influence_table

Flow extends the table’s last segment as a straight line. Past the built-in table that line leaves the infinite aquifer’s pressure function, which grows as half the logarithm of the time (9.86 against 6.16 at 100,000), so the pressure function and its slope are wrong there; a bounded aquifer’s own late-time function is a straight line, so a stated table only informs.

physics.aquifer.influence_table_kind

A Carter-Tracy aquifer reads its table as the pressure function at a constant influx rate, which never falls; the influx function at a constant pressure starts from the same 0.112 at 0.01 and then runs above it (0.404 against 0.315 at 0.1), so a table copied from the wrong one of the two classic tables reads this way. The 38 corpus tables are silent.

physics.aquifer.datum_inconsistent

The water column connecting reservoir and aquifer fixes their pressure relationship; a datum pressure far outside that envelope starts the pair violently out of equilibrium, and the first timesteps model the typo, not the aquifer.

physics.boundary.interior_face

An interior cell-cell interface is definitionally not a boundary; the condition would inject or withdraw fluid across a face that has rock on both sides.

physics.thpres.undefined_region

The intended barrier applies to nothing; Flow says the region values are not compatible with EQLNUM, ignores the pair and runs, so it warns.

physics.thpres.no_shared_face

The declared threshold can never act — legal and harmless, but the barrier it models is fiction and the author probably meant a different region pair.

CodeTierWhat it checks
physics.units.system_suspectwarningEither vote: (a) the surface densities, in both directions: an oil or water density inside the other system’s band (FIELD oil 28.1-68.7, water 59.3-81.2, gas 0.0031-0.2185 lb/ft3; METRIC oil 450-1100, water 950-1300, gas 0.05-3.5 kg/m3) and outside its own, with none reading as its own system (a gas density in both bands abstains, and a gas in the oil slot is not judged as oil); (b) in a METRIC deck, a datum gradient of 0.43-0.65 bar/m, where the FIELD hydrostatic gradient reads numerically, together with a datum deeper than 4800 or a pressure above 1500.

Why each check exists:

physics.units.system_suspect

Numbers typed in the other unit system pass each band on their own and fail as a set. The surface densities are the sharpest vote: the two systems’ oil and water bands do not overlap. The gradient vote’s window was 0.35-0.55 and missed SPE1 labelled METRIC at 0.571.

CodeTierWhat it checks
physics.thermal.below_absolute_zeroerrorInitial reservoir temperature below -459.67 degF (FIELD) or -273.15 degC (METRIC).
physics.thermal.inverted_profilewarningA depth-temperature profile segment falling faster than -0.05 degC/m (-0.0275 degF/ft).
physics.thermal.oil_viscosity_increases_with_temperatureerrorThermal oil-viscosity table strictly rising across the whole temperature span.
physics.thermal.unsupportederrorphysics.thermal holding anything other than a semantic thermal package (including thermal-conductivity region-pair coupling requested without one).

Why each check exists:

physics.thermal.below_absolute_zero

Temperatures below absolute zero do not exist; every thermal property evaluated there is undefined.

physics.thermal.inverted_profile

Real geothermal inversions (permafrost, groundwater flow) are of order -0.01 degC/m; a gradient this steeply negative is a swapped depth/temperature column or a Celsius/Fahrenheit confusion.

physics.thermal.oil_viscosity_increases_with_temperature

Liquid oil thins with heat (Arrhenius behavior), never thickens; a rising table inverts the entire point of thermal recovery.

physics.thermal.unsupported

Thermal semantics flow through one typed package; anything else cannot be validated or lowered, and rendering it would emit an unchecked deck.

A physics guard judges the reservoir. A target limitation judges the simulator: the model is sound, states nothing impossible, and would run somewhere — but the named target refuses or aborts on it. These codes carry the target. prefix, fire at the warning tier, and acknowledge through the same gate, because a deck authored for a target without the limitation is a legitimate deck.

CodeTierWhat it checks
target.restart.field_group_wellswarningAn initial state that restarts from a previous run while any well sits in the root group — no group of its own, or a group named FIELD. Both spell the same WELSPECS token.
target.thermal.multisegment_energywarningA thermal package in energy mode (not temperature-only) together with any multisegment well topology.
target.keyword_ignoredinfoA rendered directive the named target parses and does nothing with.

Why each check exists:

target.restart.field_group_wells

OPM Flow rebuilds a restarted run’s wells from the groups its restart file records, and aborts while restoring a well whose parent group is FIELD: Internal error: unordered_map::at, measured on 2025.10 and 2026.04 alike, so it is a standing limitation rather than one release’s regression. Naming a group for each well is the whole fix; the physics does not change.

target.thermal.multisegment_energy

OPM Flow does not solve energy for multisegment wells: energy is not supported by multisegment well yet, raised from MultisegmentWell_impl.hpp on 2025.10 and 2026.04 alike, after which the solver fails to converge and the run stops. The combination is physically ordinary — a heated well completed in segments — so the model is not at fault; the simulator has not implemented it. Temperature-only thermal models are unaffected, because Flow refuses the energy equation specifically.

target.keyword_ignored

The deck carries the request faithfully and the run is unaffected; the issue exists so a study is not built on output the target never produces.

The construction-time layer beneath the guards

Section titled “The construction-time layer beneath the guards”

The physics guards are the top of a two-layer defense. Beneath them, construction-time validation owns type and structure: strict typed fields reject strings-for-numbers and unit-less quantities at assignment; positivity and sign checks hold on rates, dimensions, table entries, initial pressures, dissolved and vaporized ratios by depth, bubble and dew points by depth, and identifiers; PVT and relperm tables enforce monotone pressure and saturation columns, non-empty families, and per-region family counts; and phase gating ties every package, control, and table to the phases the formulation actually declares. Those diagnostics carry their own domain codes (fluids.*, relperm.*, grid.*, schedule.*, …) and are always errors — the physics guards assume structurally valid input and judge only whether the physics it describes can exist.

CodeTierWhat it checks
physics.acknowledge.staleerrorEvery code in the model’s acknowledged_warnings must currently fire as a warning on the model.

Why each check exists:

physics.acknowledge.stale

An acknowledgment is a claim (“this model is deliberately unusual in this specific way”); once the weirdness is gone, the claim is false, and stale claims rot into acknowledge-everything boilerplate that defeats the tier system.

The model states its acknowledgments as acknowledged_warnings=[...], by keyword or by address (model.set("acknowledged_warnings", [...])). It accepts only warning-tier codes — unknown or error-tier codes are refused by name when the model is made — and demotes each matching firing warning to an informational issue:

import petroscript as ps
base = ps.Model(
name="BASE",
units="field",
start="2020-01-01",
grid=ps.grid.Cartesian(shape=(2, 2, 1), dx=500.0, dy=500.0, dz=40.0, top=2500.0),
rock=ps.Rock(porosity=0.25, permeability=100.0, vertical_ratio=1.0),
fluid=ps.fluid.OilWater(
oil=ps.pvt.DeadOil(pressure=[14.7, 3500.0], oil_fvf=[1.06, 1.0], oil_viscosity=[1.0, 1.2]),
water=ps.pvt.Water(
reference_pressure=3000.0,
fvf=1.0,
compressibility=3.0e-6,
viscosity=0.5,
viscosibility=0.0,
),
oil_surface_density=53.0,
water_surface_density=64.0,
),
relperm=[ps.relperm.OilWater(sw=[0.20, 0.80], krw=[0.0, 1.0], krow=[1.0, 0.0], pcow=[0.0, 0.0])],
initial=ps.initial.Equilibrium(
datum_depth=2520.0, datum_pressure=3000.0, oil_water_contact=2600.0, gas_oil_contact=0.0
),
wells=[ps.Well(name="PROD1", i=1, j=1)],
schedule=ps.Schedule(
report=[ps.Every(30.0)],
events=[
ps.Event(
"2020-01-01",
ps.Perforate(well="PROD1", k=(1, 1), diameter=0.3),
ps.Produce(well="PROD1", oil_rate=220.0, bhp_limit=1200.0),
)
],
),
)
model = base.set({
"schedule/2020-01-01/PROD1/control": ps.Produce(well="PROD1", oil_rate=0.01, bhp_limit=1200.0),
"schedule/report": [ps.Every(36500.0)],
"acknowledged_warnings": ["physics.schedule.century_step"],
})

Without the acknowledged_warnings entry this model is still made — warnings never block — but the century-scale step stays a warning in model.check(). With it, the warning is demoted to info. If the schedule is later shortened so the warning no longer fires, the same entry is refused as a physics.acknowledge.stale error (a ps.ModelError) until it is removed: acknowledgments must stay true.

PetroScript is licensed under the Apache License 2.0. © 2026 Blauweiss EDV LLC. PetroScript by Blauweiss AI.For AI agents: llms.txt