User Guide

Corner-point Grid

A corner-point grid is a tessellation of a 3D volume where each cell is a hexahedron.

Each cell is identified by a integer coordinate (i,j,k). For each i,j there is are four straight lines, defined by their end-points called a pillar. The end-points form two surfaces, one for the top end-points and one for the bottom end points, which are in the resfo_utilities.CornerpointGrid.coord array.

For the cell at position i,j,k, its eight corner vertices are defined by giving the z values along the pillars at [(i,j), (i+1, j), (i, j+1), (i+1, j+1)] which are in the resfo_utilities.CornerpointGrid.zcorn array.

Usually, a corner-point grid contains x,y values that needs to be transformed into a map coordinate system (which could be UTM-coordinates). That coordinate system is represented by resfo_utilities.MapAxes.

class resfo_utilities.CornerpointGrid(coord: npt.NDArray[np.float32], zcorn: npt.NDArray[np.float32], map_axes: MapAxes | None = None)

A corner-point grid.

coord

A (ni+1, nj+1, 2, 3) array where coord[i,j,0] is the top end point of the i,j pillar and coord[i,j,1] is the corresponding bottom end point.

Type:

numpy.ndarray[tuple[Any, …], numpy.dtype[numpy.float32]]

zcorn

A (ni, nj, nk, 8) array where zcorn[i,j,k] is the z value of the 8 corners of the cell at i,j,k. The order of the corner z values are as follows: [TSW, TSE, TNW, TNE, BSW, BSE, BNW, BNE] where N(orth) means higher y, E(east) means higher x, T(op) means lower z (when z is interpreted as depth).

Type:

numpy.ndarray[tuple[Any, …], numpy.dtype[numpy.float32]]

map_axes

Optionally each point is interpreted to be relative to some map coordinate system. Defaults to the unit coordinate system with origin at (0,0).

Type:

resfo_utilities._cornerpoint_grid.MapAxes | None

Raises:

InvalidGridError – If coord or zcorn does not have correct shape.

cell_corners(i: int, j: int, k: int) npt.NDArray[np.float32]

Array of coordinates for all corners of the cell at i,j,k

The order of the corners are the same as in zcorn.

find_cell_containing_point(points: ArrayLike, map_coordinates: bool = True, tolerance: float = 1e-06) list[tuple[int, int, int] | None]

Find a cell in the grid which contains the given point.

Parameters:
  • points – The points to find cells for.

  • map_coordinates – Whether points are in the map coordinate system. Defaults to True.

  • tolerance – The maximum distance to the cell boundary a point can have to be considered to be contained in the cell.

Returns:

list of i,j,k indices for each point (or None if the point is not contained in any cell.

point_in_cell(points: npt.ArrayLike, i: int, j: int, k: int, tolerance: float = 1e-06, map_coordinates: bool = True) npt.NDArray[np.bool_]

Whether the points (x,y,z) is in the cell at (i,j,k).

For containment the cell are considered to have bilinear faces.

Param:
points:

x,y,z triple or array of x,y,z triples to be tested for containment.

tolerance:

The tolerance used for numerical precision in the linear interpolation calculation.

map_coordinates:

Whether the given points are in the mapaxes coordinate system, defaults to true.

Returns:

Array of boolean values for each triplet describing whether it is contained in the cell.

classmethod read_egrid(file_like: str | PathLike[str] | IO[Any]) Self

Read the global grid from an .EGRID or .FEGRID file.

If the EGRID contains Local Grid Refinements or Coarsening Groups, that is silently ignored and only the host grid is read. Radial grids are not supported and will cause InvalidEgridFileError to be raised.

Parameters:

file_like – The EGRID file, could either be a filename, pathlike or an opened EGRID file. The function also handles formatted egrid files (.FEGRID). Whether the file is formatted or not is determined by looking at the extension a filepath is given and by whether the stream is a byte-stream (unformatted) or a text-stream when an opened file is given.

Raises:
  • InvalidEgridFileError – When the egrid file is not valid, or contains a radial grid.

  • OSError – If the given filepath cannot be opened.

class resfo_utilities.MapAxes(y_axis: tuple[float32, float32], origin: tuple[float32, float32], x_axis: tuple[float32, float32])

The axes of the map coordinate system.

Note that regardless of the size of the axes, when transforming from the grid coordinate system to the map coordinate system, scaling is not applied.

y_axis

A point along the map y axis.

Type:

tuple[numpy.float32, numpy.float32]

origin

The origin of the map coordinate system.

Type:

tuple[numpy.float32, numpy.float32]

x_axis

A point along the map x axis.

Type:

tuple[numpy.float32, numpy.float32]

transform_map_points(points: npt.NDArray[np.float32]) npt.NDArray[np.float32]

Transforms points from map coordinates to grid coordinates.

Scaling according to length of the axes is not applied.

Returns:

The given map points in the grid coordinate system.

Summaries

The summary files contain a number of time vectors. There is a .SMSPEC (or .FSMSPEC for formatted files) which describes what is in each time vector, and what the start date is.

The time vector is guaranteed to have one value for each report step (described in the schedule section of the .DATA file), but could have additional values at times between the report steps called ministeps.

Summary files can also be unified or split. A unified summary file has the extenion .UNSMRY (or .FUNSMRY for formatted files) and is enabled by adding the keyword UNIFOUT to the .DATA file. For split summaries there is one file for each report step, named .S0001, .S0002 and so on (.A0001 for formatted files).

SummaryReader lazily reads these files, and and can look for what combination of split and formatted files are present:

from resfo_utilities import SummaryReader

summary = SummaryReader("BASENAME")
print(f"The start date is {summary.start_date}")

for step, val in enumerate(summary.values()):
    print(f"For step {step}:")
    for kw, v in zip(summary.summary_keywords, val):
        print(f" The keyword {kw} had the value {v}")

This will print all the summary vectors produced from BASENAME.DATA (regardless of whether it is .UNSMRY, .FUNSMRY, etc.)

See OPM Flow manual section F for details.

class resfo_utilities.SummaryReader(*, case_path: str | PathLike[str], smspec: None = None, summaries: None = None)
class resfo_utilities.SummaryReader(*, smspec: Callable[[], IO[Any]], summaries: Iterable[Callable[[], IO[Any]]], case_path: None = None)

Reader for summary files.

Each file is opened when the corresponding data is requested so asking for the properties of SummaryReader may raise InvalidSummaryError if the corresponding file or its content is invalid.

property dimensions: tuple[int, int, int] | None

The dimensions of the grid used in the simulation.

property restart: str | None

The name of the case the simulation was restarted from (if any).

property smspec_filename: str

The filename of the summary spec file.

e.g. “CASE.SMSPEC”

property start_date: datetime

The start date of the simulation.

property summary_filenames: Iterator[str]

The filename of the summary file(s).

e.g. [“CASE.UNSMRY”] for unified or [“CASE.S0001”, “CASE.S0002”] for split.

property summary_keywords: list[SummaryKeyword]

The list of keywords in the summary.

values(report_step_only: bool = True) Iterator[ndarray[tuple[Any, ...], dtype[float32]]]

Iterate over the values for the summary keywords.

Parameters:

report_step_only – If True, yield only at report steps (DATES).

Yields:

arrays of the keyword values in the order of the summary_keywords.

Raises:

InvalidSummaryError – If the summary files cannot be read from or contains invalid contents.

class resfo_utilities.SummaryKeyword(summary_variable: str, number: int | None = None, name: str | None = None, lgr_name: str | None = None, li: int | None = None, lj: int | None = None, lk: int | None = None, unit: str | None = None)

One member of the KEYWORDS array.

summary_variable

The variable name, eg WOPR, or FOPT.

Type:

str

number

A number associated with the keyword, eg. for block variables it is the index of the block.

Type:

int | None

name

A name associated with the keyword, eg. for well variables it is the name of the well.

Type:

str | None

lgr_name

If a local variable then the name of the Local Grid Refinement.

Type:

str | None

li

The i index of the host cell for the LGR.

Type:

int | None

lj

The j index of the host cell for the LGR.

Type:

int | None

lk

The k index of the host cell for the LGR.

Type:

int | None

unit

The units for the value of the keyword, eg. for FOPR it may be SM3/DAY.

Type:

str | None

RFT

The RFT files contains several properties for selected wells along the well connections.

The type of properties come in three categories: RFT, PLT and segment. For each well, any subset of these categories may be present. Which categories are present is controlled by the WRFT and WRFTPLT keywords in the .DATA file.

Typical usage example:

from resfo_utilities import RFTReader

with RFTReader.open("CASE.RFT") as rft:
    for entry in rft:
        if "PRESSURE" in entry:
            print(f"Pressure for well {entry.well} at {entry.date}:")
            for pos, pressure in zip(entry.connections, entry["PRESSURE"]):
                print("{pos}: {pressure} {entry.pressure_units}")
class resfo_utilities.RFTEntry(time_since_start: timedelta, date: date, connections: ndarray[tuple[int, int], dtype[int32]], well: str, lgr_name: str | None = None, depth_units: str | None = None, pressure_units: str | None = None, types_of_data: Container[RFTDataCategory] | None = None, type_of_well: TypeOfWell | None = None, liquid_flow_rate_units: str | None = None, gas_flow_rate_units: str | None = None, local_volumetric_flow_rate_units: str | None = None, flow_velocity_units: str | None = None, liquid_and_gas_viscosity_units: str | None = None, polymer_and_brine_concentration_units: str | None = None, polymer_and_brine_flow_rate_units: str | None = None, absorbed_polymer_concentration_units: str | None = None)

A single RFT entry representing well data at a specific time.

Acts as a mapping from data array names (e.g., “PRESSURE”, “DEPTH”) to numpy arrays containing values for each connection in the well.

Parameters:
  • time_since_start – Time elapsed since simulation start.

  • date – Calendar date of the measurement.

  • connections – List of (i, j, k) grid cell indices for each connection.

  • well – Well name.

  • lgr_name – Local grid refinement name, if applicable.

  • depth_units – Units for depth measurements.

  • pressure_units – Units for pressure measurements.

  • types_of_data – Type of test data (RFT, PLT, or SEGMENT).

  • type_of_well – Well completion type (STANDARD or MULTI_SEGMENT).

  • liquid_flow_rate_units – Units for liquid flow rates.

  • gas_flow_rate_units – Units for gas flow rates.

  • local_volumetric_flow_rate_units – Units for local volumetric flow rates.

  • flow_velocity_units – Units for flow velocity.

  • liquid_and_gas_viscosity_units – Units for viscosity.

  • polymer_and_brine_concentration_units – Units for polymer/brine concentration.

  • polymer_and_brine_flow_rate_units – Units for polymer/brine flow rates.

  • absorbed_polymer_concentration_units – Units for absorbed polymer concentration.

property absorbed_polymer_concentration_units: str | None

Units for absorbed polymer concentration.

property date: date

Calendar date of the measurement.

property depth_units: str | None

Units for depth measurements.

property flow_velocity_units: str | None

Units for flow velocity.

property gas_flow_rate_units: str | None

Units for gas flow rates.

property lgr_name: str | None

Local grid refinement name, if applicable.

property liquid_and_gas_viscosity_units: str | None

Units for liquid and gas viscosity.

property liquid_flow_rate_units: str | None

Units for liquid flow rates.

property local_volumetric_flow_rate_units: str | None

Units for local volumetric flow rates.

property polymer_and_brine_concentration_units: str | None

Units for polymer and brine concentration.

property polymer_and_brine_flow_rate_units: str | None

Units for polymer and brine flow rates.

property pressure_units: str | None

Units for pressure measurements.

property time_since_start: timedelta

Time elapsed since simulation start.

property type_of_well: str | None

Well completion type (STANDARD or MULTI_SEGMENT).

property types_of_data: Container[RFTDataCategory] | None

Types of test data (RFT, PLT, and/or SEGMENT).

property well: str

Well name.

class resfo_utilities.RFTReader(file_stream: IO[Any])

Reader for RFT files.

Parameters:

file_stream – Open file stream to read from.

classmethod open(file_like: str | PathLike[str]) Self

Open an RFT file for reading.

Automatically detects file format based on extension (.RFT or .FRFT). If no extension is provided, searches for matching files.

Parameters:

file_like – Path to RFT file, with or without extension.

Raises:

FileNotFoundError – If no matching RFT file is found.

Summarykeys

Some applications use a colon separated list, called a summarykey, of the required properties needed to uniquely specify a summary vector.

What properties are required is specified in OPM Flow manual section F.9.2. Summary variables are described in the OPM Flow manual section 11.1.

A summary vector is uniquely specified by giving a summary variable, and potentially one or more of the following properties: well name, region name, lgr name, block index, completion index, network name.

For example for field variables, no additional information is required so the summary key is just the variable name: FOPR, FWPR, etc. For well variables, the well name need to be specified: WOPR:WELL_NAME, WAQR:MY_WELL etc. For block variables, the index has to be given: BOPR:10,9,50. For a local completion, both the lgr name, well name, and index has to be given: LWWITH:LGR1:WELL2:3,5,5.

resfo_utilities.make_summary_key(keyword: str, number: int | None = None, name: str | None = None, nx: int | None = None, ny: int | None = None, lgr_name: str | None = None, li: int | None = None, lj: int | None = None, lk: int | None = None) str

Converts values found in the um to the summary_key format.

>>> make_summary_key(keyword="WOPR", name="WELL1")
'WOPR:WELL1'
>>> make_summary_key(keyword="BOPR", number=4, nx=2, ny=2)
'BOPR:2,2,1'
Parameters:
  • keyword – Summary variable name (e.g., "WOPR", "BPR").

  • number – Numeric qualifier from NUMS (cell index, region id, etc.).

  • name – Text qualifier from WGNAMES (well/group name).

  • nx – Grid dimension in x for block/completion keys.

  • ny – Grid dimension in y for block/completion keys.

  • lgr_name – Local grid name for local keys.

  • li – Local i-index for local block/completion.

  • lj – Local j-index for local block/completion.

  • lk – Local k-index for local block/completion.

Raises:

InvalidSummaryKeyError – If the key is invalid

class resfo_utilities.SummaryKeyType(*values)

Summary keys are divided into types based on summary variable name.

classmethod from_variable(summary_variable: str) SummaryKeyType

Returns the type corresponding to the given summary variable

>>> SummaryKeyType.from_variable("FOPR").name
'FIELD'
>>> SummaryKeyType.from_variable("LWWIT").name
'LOCAL_WELL'
resfo_utilities.is_rate(summary_variable: str) bool

Whether the given summary variable is a rate.

See opm flow reference manual <https://opm-project.org/wp-content/uploads/2023/06/OPM_Flow_Reference_Manual_2023-04_Rev-0_Reduced.pdf> table 11.4 for details.

resfo_utilities.history_key(key: str) str

The history summary key responding to given summary key

>>> history_key("FOPR")
'FOPRH'
>>> history_key("BPR:1,3,8")
'BPRH:1,3,8'
>>> history_key("LWWIT:WNAME:LGRNAME")
'LWWITH:WNAME:LGRNAME'