Skip to content

Deck records

ps.testing.records(path_or_text, *, includes="refuse") reads a native deck, or deck text, as the ordered keyword occurrences it states and returns a tuple of keyword records. A str holding a line break is deck text; any other str or path names a file. Both are read as ps.load reads them, and a deck it refuses is refused here with the same ps.DeckImportError. It is a reader for consumers that check a file against what they meant to write, the investigator’s effective-readback among them. It is not an authoring door: no verb on the authoring surface accepts a record, and a law keeps it that way.

fieldwhat it holds
namethe keyword as the deck spells it, upper case
sectionthe section the occurrence stands in (RUNSPEC .. SCHEDULE), or None before the first section keyword
recordsthe data records in deck order, each a tuple of int, float, str or None
at(file, line) of the keyword line, the included file’s own name when expansion was asked for
linesthe verbatim body of a keyword the reader keeps as text (TITLE, UDQ, ACTIONX); such a record has no records

These are the scalar semantics the reader has always given the importer, named here as the contract so that a change to any of them is a declared change:

  • a n*value repeat is expanded to n values (2*0.2 is (0.2, 0.2));
  • a defaulted slot is None, one per slot (1* is (None,); 5* before a BHP is five Nones);
  • a quoted word is the word without its quotes; a number is int when the deck wrote an integer and float otherwise;
  • the trailing empty record that closes a record list (WELSPECS, COMPDAT, WCONPROD and their kind) is not a record;
  • every occurrence is kept, duplicates included, in deck order: a keyword the deck states twice is two records, never one map entry that lost the first.

Content the deck skips (SKIP/ENDSKIP, anything after END) is not read. ps.load stays the door for what a deck means; this door says only what it states.

includes="refuse", the default, refuses a deck that includes another file with ps.DeckImportError whose code is deck.include.present, naming the INCLUDE line; what comes back is therefore one self-contained file. includes="expand" reads the included files in place, and each record’s at names the file it came from. Text has no folder to include from, so an INCLUDE in text is refused either way, and each record’s at names the text <rendered deck>.

ps.io.deck_files(path) names the files themselves: the deck and every file its INCLUDE stanzas read, in the order they are opened, each as the deck’s text names it from the deck’s folder, with the sha256 of its bytes.

import petroscript as ps
records = ps.testing.records("opm-tests/spe1/SPE1CASE1.DATA")
welspecs = [record for record in records if record.name == "WELSPECS"]
assert [record.section for record in welspecs] == ["SCHEDULE"]
assert welspecs[0].records[0][:4] == ("PROD", "G1", 10, 10)
assert welspecs[0].at[1] == 384

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