In progress, for discussion.
Proposal: Add well data standard results to fmu-datamodels
Table of contents
Context
We want to add well data to fmu-datamodels so these data can be exported through fmu-dataio, stored and discovered in Sumo, and later retrieved in webviz or other consumers for visualization in the same general way that well data from SMDA and SSDL are visualized today.
The main target data types are:
- Wellbore trajectories
- Well logs
- Blocked/upscaled/grid-extracted well logs
- Wellbore trajectory uncertainty
- Wellbore picks
We probably also need a local well parent concept. A local well can group one or more wellbores, which is important for multilateral wells where several branches belong to the same well. The wellbore remains the trajectory/log/pick carrier, while the local well parent gives consumers a stable grouping level for filtering, tree views, and cross-branch visualization.
The current expectation is that these should primarily be represented as tables with metadata, using standard result definitions and file format schemas where useful. This should fit Sumo's Arrow/table workflows and webviz's existing data access patterns.
Existing relevant behavior
fmu-datamodels
ObjectMetadataClass already supports well, points, and table object classes.
Content already includes wellpicks and well_completions.
wellpicks currently has only a thin Data specialization and no dedicated standard result/file schema. It is not currently used, so we can rewrite or replace it without needing a compatibility migration.
- There is already a
WellboreIdentifierMapping model for mapping wellbore identifiers between systems such as RMS, SMDA, simulator, and PDM.
- Standard results are represented as enum values, Pydantic models, and optional file schema references.
fmu-dataio
xtgeo.Points, xtgeo.Polygons, pandas dataframes, and Arrow tables are supported export objects.
xtgeo.Well and xtgeo.Wells are not currently supported directly.
- Current
wellpicks table index handling expects WELL and HORIZON.
- Table exports already support metadata fields such as
content, standard_result, table_index, spec.columns, and file format.
xtgeo
xtgeo.Well stores trajectory coordinates and logs in a dataframe.
- Standard well coordinate/log columns include
X_UTME, Y_UTMN, Z_TVDSS, and often M_MDEPTH or Q_MDEPTH.
xtgeo.Wells can stack multiple wells into one dataframe with WELLNAME.
xtgeo.Points can represent well picks and can read/write RMS well picks.
webviz
- The existing well router exposes trajectories, picks, completions/casings/perforations, log headers, and log curve data.
- Existing well trajectory schema is array-based:
tvdMslArr, mdArr, eastingArr, northingArr.
- Existing well pick schema contains coordinates, MD/TVD,
wellboreUuid, uniqueWellboreIdentifier, pickIdentifier, obsNo, confidence, and depth reference information.
- Existing log curve schema is header +
(index, value) data points.
Proposed standard results
| Standard result |
Purpose |
Suggested content |
Suggested class |
Realization scoped |
wellbore_trajectory |
One wellbore path |
wellbore_trajectory |
table |
Normally no, but yes for trajectory uncertainty |
wellbore_logs |
Raw or modelled well logs sampled along MD |
wellbore_logs |
table |
Yes |
wellbore_grid_logs |
Blocked/upscaled/grid-extracted logs |
wellbore_grid_logs |
table |
Yes |
wellbore_picks |
Wellbore picks for stratigraphic or other identifiers |
wellbore_picks |
table |
Yes |
The existing wellpicks content is not used and can be rewritten. The preferred direction is to replace it with a clearer wellbore_picks content/standard result, using class=table and the table schema proposed below.
Proposed table shapes
Local well and wellbore identifiers
The proposed tables should distinguish between a local well parent and the individual wellbore/branch.
| Term |
Description |
WELL |
Local well parent/grouping identifier. Multiple wellbores can share the same WELL. |
WELLBORE |
Local wellbore/branch identifier. This is the object that carries a trajectory, logs, and picks. |
WELLBORE_UUID |
Official/external wellbore UUID, where available. |
UWI |
Unique wellbore identifier, where available. |
Casing convention used in this document: UPPER_SNAKE_CASE for table column names, lower_snake_case for object/table metadata keys. The same concept may appear as both, e.g. column WELLBORE_UUID vs metadata wellbore_uuid.
Open question: do we also need a separate official/external well parent identifier, or is the local WELL grouping enough for the first version?
wellbore_trajectory
A WELLBORE may have one or more trajectory variants (for example a drilled trajectory and one or more planned/prognosis trajectories). Trajectory identity is therefore a first-class concern in this schema and must propagate to every table that references measured depth or trajectory-derived coordinates (wellbore_logs, wellbore_grid_logs, wellbore_picks).
The schema supports two equivalent ways of expressing trajectory identity, and exports must pick one consistently:
- Per-trajectory object (preferred default): one table = one trajectory.
TRAJECTORY_NAME and TRAJECTORY_STATUS are stored as object metadata, not columns.
- Multi-trajectory object: one table contains multiple trajectory variants.
TRAJECTORY_NAME and TRAJECTORY_STATUS become required columns and part of the table index.
TRAJECTORY_STATUS is a semantic category (e.g. drilled/existing, planned/prognosis), not just a copy of an RMS trajectory name. This distinction matters for filtering, styling, and interpretation in Sumo and webviz.
Required columns:
| Column |
Type |
Description |
WELL |
string |
Local well parent/grouping identifier. Several wellbores can belong to the same well. |
WELLBORE |
string |
Model/local wellbore identifier. Could be RMS name or another local identifier. |
X_UTME |
float |
Easting coordinate. |
Y_UTMN |
float |
Northing coordinate. |
Z_TVDSS |
float |
TVDSS coordinate. |
MD |
float |
Measured depth. |
Suggested metadata:
| Metadata |
Description |
rkb |
RKB/depth reference elevation where known. |
wellbore_uuid |
Official wellbore UUID, from mapping where available. |
unique_wellbore_identifier |
UWI or official wellbore identifier, from mapping where available. |
md_unit |
Unit for MD, normally m. |
xy_unit |
Unit for X_UTME and Y_UTMN, normally m. |
z_unit |
Unit for Z_TVDSS, normally m. |
trajectory_status |
Semantic category, e.g. drilled/existing or planned/prognosis. Required when the object holds one trajectory variant; otherwise carried as a column. |
trajectory_name |
Optional source trajectory name, e.g. RMS trajectory name. |
Conditionally required columns when one object contains multiple trajectory variants:
| Column |
Type |
Description |
TRAJECTORY_NAME |
string |
Source trajectory name or another stable local trajectory variant name. |
TRAJECTORY_STATUS |
string |
Semantic status, e.g. drilled/existing, planned/prognosis. |
Trajectory uncertainty uses the same standard result; the presence of realization metadata indicates that the trajectory is realization-specific.
Open questions specific to wellbore_trajectory are consolidated in Open questions.
wellbore_logs
Well logs are expected to exist per realization and should allow uncertainty.
Recommended default: one table per wellbore per trajectory per realization. WELL, WELLBORE, and trajectory identity are stored as object metadata so Sumo can filter without reading the table body.
Default shape (one object scoped to one wellbore and one trajectory):
| Column |
Type |
Description |
MD |
float |
Measured depth. |
<LOG_NAME> |
float/string/int |
One column per log. |
Multi-object shape, only when one table must contain multiple wellbores or multiple trajectory variants:
| Column |
Type |
Description |
WELL |
string |
Local well parent/grouping identifier. |
WELLBORE |
string |
Model/local wellbore identifier. |
MD |
float |
Measured depth. |
<LOG_NAME> |
float/string/int |
One column per log. |
Conditionally required columns when one object contains multiple trajectory variants (added to the multi-object shape):
| Column |
Type |
Description |
TRAJECTORY_NAME |
string |
Source trajectory name or another stable local trajectory variant name. |
TRAJECTORY_STATUS |
string |
Semantic status, e.g. drilled/existing, planned/prognosis. |
Suggested table metadata:
| Metadata |
Description |
well |
Local well parent/grouping identifier, if scoped to one wellbore. |
wellbore |
Local wellbore/branch identifier, if scoped to one wellbore. |
wellbore_uuid |
Official wellbore UUID, if scoped to one wellbore. |
unique_wellbore_identifier |
UWI/official identifier, if scoped to one wellbore. |
trajectory_name |
Source trajectory name, if logs are scoped to one trajectory. |
trajectory_status |
Planned/drilled status, if logs are scoped to one trajectory. |
md_unit |
Unit for MD, normally m. |
source |
Optional source/generation workflow. |
Suggested column metadata for each log column:
| Metadata |
Description |
log_name |
Original log name. |
unit |
Log value unit. |
log_type |
continuous, discrete, or flag. |
no_data_value |
No-data marker, if relevant. |
code_names |
Code-to-name mapping for discrete logs. |
This resembles the existing ert_parameters pattern, where Parquet column metadata is described by a standard result schema.
wellbore_grid_logs
This represents blocked, upscaled, or grid-extracted well logs. The exact name is unresolved.
Possible names:
wellbore_grid_logs
blocked_wellbore_logs
grid_extracted_wellbore_logs
Draft columns:
| Column |
Type |
Description |
WELL |
string |
Local well parent/grouping identifier. |
WELLBORE |
string |
Model/local wellbore identifier. |
MD |
float, optional? |
Measured depth, if available. |
Z_TVDSS |
float, optional? |
TVDSS coordinate, if available or relevant. |
GRID_NAME |
string |
Name of grid used for extraction/upscaling. |
I |
int |
Grid I index. |
J |
int |
Grid J index. |
K |
int |
Grid K index. |
DATE |
date/datetime, optional? |
Time/date for dynamic grid parameters, if relevant. |
REPORT_STEP |
int, optional? |
Simulator report step for dynamic grid parameters, if relevant. |
<LOG_NAME> |
float/string/int |
One column per extracted/upscaled log. |
Conditionally required columns when the object covers multiple trajectory variants for one WELLBORE:
| Column |
Type |
Description |
TRAJECTORY_NAME |
string |
Source trajectory name or another stable local trajectory variant name. |
TRAJECTORY_STATUS |
string |
Semantic status, e.g. drilled/existing, planned/prognosis. |
Suggested metadata:
| Metadata |
Description |
grid_name |
Grid name, if not a column. |
grid_relative_path |
Optional reference to exported grid geometry. |
cell_index_base |
Whether I, J, K are zero-based or one-based. |
sampling_method |
How the well path was sampled against the grid. |
upscaling_method |
Average, majority, nearest, thickness-weighted, etc. |
zone_log |
Zone log used, if relevant. |
time_index_type |
Whether dynamic values are indexed by date, report step, elapsed time, or another convention. |
wellbore_picks
Well picks should use class=table, not points, because Arrow/table calculations and standard result validation are the main intended usage. Wellbore picks can vary between realizations and should support realization-scoped results.
There may also be different sets of picks for the same ensemble/case. One important example is a set containing simulated pick uncertainty from Cohiba, from the Norwegian Computing Centre (NR). It is unresolved whether this should be represented as a named pick set under wellbore_picks, or as a separate standard result for simulated/uncertain wellbore picks.
Required columns:
| Column |
Type |
Description |
WELL |
string |
Local well parent/grouping identifier. |
WELLBORE |
string |
Model/local wellbore identifier. |
X_UTME |
float |
Easting coordinate. |
Y_UTMN |
float |
Northing coordinate. |
Z_TVDSS |
float |
TVDSS coordinate. |
MD |
float |
Measured depth. |
IDENTIFIER |
string |
Stratigraphic or other pick identifier, e.g. horizon or zone. |
IDENTIFIER_TYPE |
string |
Type/category of identifier, e.g. horizon, zone, fault, marker. |
OBS_NO |
int |
Observation number when several picks exist for the same identifier. |
Conditionally required columns if several trajectory variants can exist for one WELLBORE:
| Column |
Type |
Description |
TRAJECTORY_NAME |
string |
Source trajectory name or another stable local trajectory variant name. |
TRAJECTORY_STATUS |
string |
Planned/drilled status for the trajectory the pick is tied to. |
Optional columns:
| Column |
Type |
Description |
CONFIDENCE |
string |
Optional confidence/quality marker. |
STRAT_COLUMN |
string |
Stratigraphic column identifier, if relevant. |
PICK_SET |
string |
Named pick set, if several sets of picks exist for the same case/ensemble. |
WELLBORE_UUID |
string |
Official wellbore UUID, if table contains several wellbores. |
UWI |
string |
Unique wellbore identifier, if table contains several wellbores. |
Interpreter is intentionally not included in the proposed core schema.
Webviz consumer use cases
The table granularity choice should be driven by realistic consumer access patterns, not just by export-side ergonomics. Known target use cases in webviz:
| # |
Use case |
Wells |
Realizations |
Dominant axis |
Likely access pattern |
| 1 |
Trajectories in map view |
All |
One |
Wells × one realization |
Read many wellbore trajectories for one realization at once. |
| 2 |
Well log / well correlation view |
One (few) |
One |
Logs along MD |
Read all logs for one wellbore (and one trajectory) for one realization. |
| 3 |
Well log statistics |
One or many |
All |
Realization stack |
Aggregate per log per MD/cell across realizations. Likely grid-extracted logs. |
| 4 |
Trajectory uncertainty (fence view) |
One |
All |
Realization stack |
Read trajectory for one wellbore across all realizations. |
| 5 |
Pick QC against one or more surfaces |
All |
All |
Wells × realizations |
Read picks for all wells across all realizations, often joined to surface depth. |
Implications for granularity:
- Use case 1 favors a single trajectory table per realization (Option B) so a map view can fetch one object instead of many.
- Use case 2 favors per-wellbore-per-trajectory log objects (Option A) so Sumo can filter by
wellbore metadata before loading.
- Use cases 3 and 4 align well with Sumo's per-column aggregation across realizations, which is most efficient when the same logical column appears in the same file for every realization.
- Use case 5 wants picks queryable across all wells and realizations; a per-realization picks table containing all wells (Option B for picks) maps directly to Sumo's
REAL-indexed aggregation.
These use cases are also the natural starting point for sizing the Sumo aggregation and webviz retrieval design later.
Table granularity options
Option A: one table per wellbore
Pros:
- Clean metadata:
wellbore_uuid, UWI, and RKB can be object metadata.
- Easy Sumo filtering by well or wellbore metadata before loading data.
- Natural for logs and trajectories.
Cons:
- Many Sumo objects for many wells and realizations.
- More retrieval calls or aggregation logic in consumers.
- Cross-well operations require loading many tables.
- Multilateral wells require a shared well parent metadata field across several wellbore-scoped objects.
Option B: one table for all wellbores
Pros:
- Fewer Sumo objects.
- Efficient bulk retrieval for webviz map/log workflows.
- Easier Arrow operations across all wellbores.
Cons:
- Well and wellbore identity metadata must become columns or duplicated metadata is impossible.
- Sumo cannot filter individual wells/wellbores before reading the table unless important identifiers are also represented as metadata in another way.
- RKB and UUID/UWI values become repeated row data or require side metadata.
Option C: hybrid
This is currently the most likely practical default, driven by the webviz consumer use cases:
| Standard result |
Suggested granularity |
Driving use case(s) |
wellbore_trajectory |
One object per realization, containing all wellbores. |
1, 4 |
wellbore_logs |
One object per wellbore per trajectory per realization. |
2 |
wellbore_grid_logs |
One object per grid per realization, containing all wellbores. |
3 |
wellbore_picks |
One object per realization, containing all wellbores. |
5 |
Tradeoff: this introduces more conventions, but each convention is justified by a concrete consumer use case rather than chosen abstractly.
How a multi-wellbore table actually looks
Multi-wellbore tables (Option B and the multi-wellbore parts of Option C) only make sense as long-format tables. MD, X_UTME, Y_UTMN, and Z_TVDSS are not a shared axis across wells; each well has its own MD range, sampling, and coordinates. Wells co-exist in the same table by living in different rows, identified by the table_index columns.
Example for wellbore_trajectory, one realization, two wellbores:
WELL |
WELLBORE |
MD |
X_UTME |
Y_UTMN |
Z_TVDSS |
| A |
A-1 |
0.0 |
463000.0 |
6790000.0 |
-25.0 |
| A |
A-1 |
50.0 |
463001.2 |
6790000.5 |
24.8 |
| A |
A-1 |
… |
… |
… |
… |
| A |
A-1 |
2510.0 |
463412.0 |
6790890.0 |
2480.0 |
| B |
B-1 |
0.0 |
464200.0 |
6791500.0 |
-22.0 |
| B |
B-1 |
30.0 |
464200.4 |
6791500.6 |
7.9 |
| B |
B-1 |
… |
… |
… |
… |
| B |
B-1 |
3120.0 |
464590.0 |
6792100.0 |
3090.0 |
Key consequences:
- The table has one row per
(WELL, WELLBORE, MD). Different wells naturally have different MD values, different counts of rows, and different coordinate ranges. Parquet does not care.
- The
table_index for the standard result is the tuple of columns that uniquely identifies a row, not a single shared depth axis. For wellbore_trajectory this is [WELL, WELLBORE, MD] (+ trajectory identity when variants exist).
- For
wellbore_logs in multi-object shape, the same applies: each log column carries values for whichever rows have that log defined; rows for wells that do not have the log get null. The total column set is the union across all wells in the file.
- For
wellbore_picks the rows are per pick, so wells just contribute different numbers of rows.
This also clarifies what Sumo aggregation needs to align across realizations. The aggregation key is the table_index tuple plus REAL. For multi-wellbore trajectories the implicit assumption is therefore that (WELL, WELLBORE, MD) is reproducible per realization for the deterministic cases. If MD sampling differs between realizations for the same wellbore (typical for trajectory uncertainty), per-column aggregation across realizations cannot align rows by MD; that case must either resample to a common MD grid before export or be retrieved per realization rather than aggregated.
Open question: should wellbore_trajectory for trajectory uncertainty be required to use a common MD grid per wellbore across realizations so Sumo aggregation aligns, or should uncertainty trajectories be consumed per realization and never aggregated by MD?
Sumo storage and table aggregation
Sumo aggregates tables across realizations by transposing per-realization files into per-column files. The columns that act as the aggregation index come from the object's table_index metadata — a list of column names declared by the producer for each standard result. DATE and REAL are common examples (e.g. summary tables) but are not built-in; for well data the table_index is the identity/coordinate columns of the standard result. REAL is added by the aggregation step itself.
Originally the source format was Arrow, where a consumer had to download whole files to read a few columns; the format is moving to Parquet so that sufficiently clever readers can fetch only the columns they need. The aggregation code path is chosen by inspecting a randomly selected input file to detect the format.
Implications for the well-data standard results:
- Per-realization tables that share an identical
table_index and column shape across realizations are the natural input to Sumo aggregation. The hybrid granularity recommended above is chosen to make this work for trajectories, picks, and grid logs.
- The
table_index for each standard result must be declared explicitly in fmu-datamodels / fmu-dataio. Candidates:
wellbore_trajectory: [WELL, WELLBORE, MD], plus TRAJECTORY_NAME/TRAJECTORY_STATUS when one object holds multiple variants.
wellbore_logs: [MD] for the default per-wellbore-per-trajectory shape (identity is in metadata), [WELL, WELLBORE, MD] (+ trajectory identity) for the multi-object shape.
wellbore_grid_logs: [WELL, WELLBORE, GRID_NAME, I, J, K], plus DATE or REPORT_STEP if dynamic parameters are included.
wellbore_picks: [WELL, WELLBORE, IDENTIFIER, OBS_NO], plus trajectory identity when applicable.
- Parquet should be the preferred file format for wellbore tables once it is a supported aggregation input, because column-projection retrieval is essential for log statistics (use case 3) and pick QC (use case 5).
- Wellbore log tables can have a very large and variable column set across wells. The aggregation contract should be specified explicitly: which columns belong to
table_index and are therefore guaranteed to align across realizations, versus which are dynamic (log columns) and may differ between wells/realizations.
Open question: should Sumo store derived/precomputed views of these tables (e.g. per-well log statistics, per-pick QC summaries) as separate standard results, or should webviz compute them on demand from the raw aggregated tables?
Trajectory storage format
The current proposal stores trajectories as positional samples (X_UTME, Y_UTMN, Z_TVDSS, MD). An alternative is to store the deviation survey (MD, inclination, azimuth) plus a reference point (surface location, RKB) and a documented minimum-curvature reconstruction algorithm. This would reduce stored data size significantly and avoid sampling-density choices at export time, at the cost of requiring every consumer to reconstruct the 3D path.
Before choosing, we need to confirm:
- Is the deviation survey reliably available from the relevant sources (RMS, planned-trajectory tools, SMDA)? If not, positional samples are the only viable common shape.
- Is the chosen reconstruction algorithm (typically minimum curvature) part of the contract, including handling of tie-on points and any well-head datum?
- Do trajectory uncertainty realizations have a deviation survey, or only resampled positional paths? If only positional, mixing storage formats across
wellbore_trajectory objects is awkward.
- Does the data size of positional samples actually hurt at realistic well counts and realization counts in Sumo, given Parquet column compression?
A reasonable default for the first implementation scope is positional samples, with the deviation-survey representation kept as a follow-up if data size becomes a real problem and the survey data is reliably available.
RMS export selection and collections
A practical RMS export helper needs a user-friendly way to select which wells and trajectories to export. Passing explicit well names and trajectory names is useful for scripts and tests, but may be awkward for normal RMS users if their working sets are already maintained inside RMS.
RMS users commonly define collections as working sets of objects. Collections contain references to RMS data objects and can be accessed through the RMS API by giving the collection path and reading back the referenced objects. Based on initial investigation, there appear to be at least two relevant collection types for well data:
Wells collections: appear to return well names. It is not yet clear whether this corresponds most closely to the local WELL, the local WELLBORE, or RMS's current well/wellbore naming model.
Trajectories collections: appear to return full path-like references such as [wellname, wellborename, trajectoryname].
Using collections would likely improve usability because export scripts could refer to a named RMS working set rather than duplicating well selections in Python. It may also reduce mistakes when users maintain several well sets for different modelling workflows.
This should be treated as an investigation before making the RMS export API contractual. Questions to answer:
- How are RMS users currently using well and trajectory collections in real workflows?
- Are
Wells collections sufficient for simple trajectory/log exports, or do they hide important wellbore/trajectory choices?
- Are
Trajectories collections the better first-class input for wellbore_trajectory because they include well, wellbore, and trajectory names explicitly?
- Can a
Wells collection be combined with default trajectory/logrun names in a predictable way?
- How stable are collection paths and object reference shapes across relevant RMS versions?
- Can collection-derived selections map cleanly to the proposed
WELL and WELLBORE table columns?
Suggested prototype direction: keep explicit well/trajectory arguments for the first simple RMS exporter, but add an investigation task and possibly a second helper that accepts an RMS collection path once the API behavior is verified. If collections are reliable, they should probably become the preferred user-facing selection mechanism for multi-well exports.
Open questions
Identifiers and granularity:
- What is the exact relationship between local
WELL, local WELLBORE, official well identifiers, and official wellbore identifiers?
- Do we also need a separate official/external well parent identifier, or is the local
WELL grouping enough for the first version?
- Should
WELL be required in all wellbore-level tables to support multilateral wells from the start?
- Should
wellbore_uuid, UWI, RKB, and any official well parent identifier be metadata, columns, or both, including when one table contains multiple wellbores?
- Should trajectory, logs, and picks default to per-wellbore objects, per-well objects, or one object for all wellbores? (See Table granularity options.)
Trajectory variants:
- Do planned and drilled trajectories for the same physical wellbore represent two variants of the same
WELLBORE, or should a planned sidetrack/branch be a separate local WELLBORE?
- Should
TRAJECTORY_STATUS use a fixed controlled vocabulary (e.g. drilled, planned), and if so what are the allowed values?
- Which wellbore-level tables must carry trajectory identity when variants are allowed: logs, grid logs, picks, or all of them?
Logs and grid logs:
- Should table schemas validate only required/index columns, or also describe dynamic log columns through column metadata?
- What is the preferred name for blocked/upscaled/grid-extracted logs (
wellbore_grid_logs, blocked_wellbore_logs, grid_extracted_wellbore_logs)?
- Is
MD always available and meaningful for blocked/upscaled/grid-extracted logs?
- Should
I, J, K indices be required to be zero-based or one-based?
- Should
wellbore_grid_logs cover dynamic grid parameters such as pressure over time, and if so should time be represented by DATE, REPORT_STEP, elapsed time, metadata, or a separate time index table — or should dynamic wellbore grid parameters be a separate standard result?
Picks and uncertainty:
- Should the unused
wellpicks content value be renamed to wellbore_picks, or should a new wellbore_picks content value replace it while removing/deprecating wellpicks?
- Should trajectory uncertainty be detected only by realization metadata on
wellbore_trajectory, or should it have a separate standard result?
- Should different wellbore pick sets be represented with optional
PICK_SET metadata/columns under wellbore_picks, or should simulated pick uncertainty (e.g. Cohiba/NR) have a separate standard result?
Sumo and RMS:
- Which metadata fields must be contractual/searchable in Sumo for webviz workflows?
- Should Sumo store derived/precomputed views of these tables (e.g. per-well log statistics, pick QC summaries) as separate standard results, or should webviz compute them on demand from aggregated tables?
- Should the aggregation contract distinguish guaranteed-aligned columns (identity/index) from dynamic columns (logs) explicitly in the standard result schema?
- Should
wellbore_trajectory for trajectory uncertainty be required to use a common MD grid per wellbore across realizations so Sumo aggregation aligns, or should uncertainty trajectories be consumed per realization and never aggregated by MD?
- Should RMS collection paths be supported as a first-class way to select wells/trajectories for export, and if so should the exporter accept
Wells collections, Trajectories collections, or both?
- How should collection-derived RMS names map to local
WELL and WELLBORE identifiers, especially when trajectory collections return [wellname, wellborename, trajectoryname]?
Trajectory storage format:
- Should
wellbore_trajectory store positional samples, deviation surveys, or both?
- If deviation surveys are allowed, is the reconstruction algorithm (typically minimum curvature) part of the schema contract, and how are tie-on points and well-head datum handled?
- Is the deviation survey reliably available from RMS, planned-trajectory tools, and SMDA for the realizations that need to be exported?
Suggested first implementation scope
Four standard results are proposed in total. The first implementation scope covers three of them:
wellbore_trajectory
wellbore_logs
wellbore_picks
Defer wellbore_grid_logs until naming, grid index semantics, and upscaling metadata are better agreed.
Initial fmu-datamodels work:
- Add new content enum values.
- Add new standard result names.
- Add standard result classes with file schema references.
- Add file format schemas for the table shapes above.
- Add table index column helpers.
- Decide and document the
WELL/WELLBORE relationship for multilateral wells.
- Rewrite the unused
wellpicks model as the new table-based wellbore_picks model.
Initial fmu-dataio work:
- Support exporting prepared pandas/Arrow tables with these standard results.
- Add standard table indexes.
- Later add convenience exporters from
xtgeo.Well, xtgeo.Wells, and xtgeo.Points.
- Investigate RMS well and trajectory collections as a user-facing selection mechanism for simple RMS exports.
Initial webviz work:
- Add Sumo well access service(s) that retrieve these standard result tables through
ArrowTableLoader or similar.
- Convert Sumo table rows into existing well router response schemas where possible.
- Add source selection or separate Sumo endpoints for trajectories, logs, and picks.
Proposal: Add well data standard results to fmu-datamodels
Table of contents
Context
We want to add well data to
fmu-datamodelsso these data can be exported throughfmu-dataio, stored and discovered in Sumo, and later retrieved inwebvizor other consumers for visualization in the same general way that well data from SMDA and SSDL are visualized today.The main target data types are:
We probably also need a local well parent concept. A local well can group one or more wellbores, which is important for multilateral wells where several branches belong to the same well. The wellbore remains the trajectory/log/pick carrier, while the local well parent gives consumers a stable grouping level for filtering, tree views, and cross-branch visualization.
The current expectation is that these should primarily be represented as tables with metadata, using standard result definitions and file format schemas where useful. This should fit Sumo's Arrow/table workflows and webviz's existing data access patterns.
Existing relevant behavior
fmu-datamodels
ObjectMetadataClassalready supportswell,points, andtableobject classes.Contentalready includeswellpicksandwell_completions.wellpickscurrently has only a thinDataspecialization and no dedicated standard result/file schema. It is not currently used, so we can rewrite or replace it without needing a compatibility migration.WellboreIdentifierMappingmodel for mapping wellbore identifiers between systems such as RMS, SMDA, simulator, and PDM.fmu-dataio
xtgeo.Points,xtgeo.Polygons, pandas dataframes, and Arrow tables are supported export objects.xtgeo.Wellandxtgeo.Wellsare not currently supported directly.wellpickstable index handling expectsWELLandHORIZON.content,standard_result,table_index,spec.columns, and file format.xtgeo
xtgeo.Wellstores trajectory coordinates and logs in a dataframe.X_UTME,Y_UTMN,Z_TVDSS, and oftenM_MDEPTHorQ_MDEPTH.xtgeo.Wellscan stack multiple wells into one dataframe withWELLNAME.xtgeo.Pointscan represent well picks and can read/write RMS well picks.webviz
tvdMslArr,mdArr,eastingArr,northingArr.wellboreUuid,uniqueWellboreIdentifier,pickIdentifier,obsNo, confidence, and depth reference information.(index, value)data points.Proposed standard results
wellbore_trajectorywellbore_trajectorytablewellbore_logswellbore_logstablewellbore_grid_logswellbore_grid_logstablewellbore_pickswellbore_pickstableThe existing
wellpickscontent is not used and can be rewritten. The preferred direction is to replace it with a clearerwellbore_pickscontent/standard result, usingclass=tableand the table schema proposed below.Proposed table shapes
Local well and wellbore identifiers
The proposed tables should distinguish between a local well parent and the individual wellbore/branch.
WELLWELL.WELLBOREWELLBORE_UUIDUWICasing convention used in this document:
UPPER_SNAKE_CASEfor table column names,lower_snake_casefor object/table metadata keys. The same concept may appear as both, e.g. columnWELLBORE_UUIDvs metadatawellbore_uuid.Open question: do we also need a separate official/external well parent identifier, or is the local
WELLgrouping enough for the first version?wellbore_trajectoryA
WELLBOREmay have one or more trajectory variants (for example a drilled trajectory and one or more planned/prognosis trajectories). Trajectory identity is therefore a first-class concern in this schema and must propagate to every table that references measured depth or trajectory-derived coordinates (wellbore_logs,wellbore_grid_logs,wellbore_picks).The schema supports two equivalent ways of expressing trajectory identity, and exports must pick one consistently:
TRAJECTORY_NAMEandTRAJECTORY_STATUSare stored as object metadata, not columns.TRAJECTORY_NAMEandTRAJECTORY_STATUSbecome required columns and part of the table index.TRAJECTORY_STATUSis a semantic category (e.g.drilled/existing,planned/prognosis), not just a copy of an RMS trajectory name. This distinction matters for filtering, styling, and interpretation in Sumo and webviz.Required columns:
WELLWELLBOREX_UTMEY_UTMNZ_TVDSSMDSuggested metadata:
rkbwellbore_uuidunique_wellbore_identifiermd_unitMD, normallym.xy_unitX_UTMEandY_UTMN, normallym.z_unitZ_TVDSS, normallym.trajectory_statusdrilled/existingorplanned/prognosis. Required when the object holds one trajectory variant; otherwise carried as a column.trajectory_nameConditionally required columns when one object contains multiple trajectory variants:
TRAJECTORY_NAMETRAJECTORY_STATUSdrilled/existing,planned/prognosis.Trajectory uncertainty uses the same standard result; the presence of realization metadata indicates that the trajectory is realization-specific.
Open questions specific to
wellbore_trajectoryare consolidated in Open questions.wellbore_logsWell logs are expected to exist per realization and should allow uncertainty.
Recommended default: one table per wellbore per trajectory per realization.
WELL,WELLBORE, and trajectory identity are stored as object metadata so Sumo can filter without reading the table body.Default shape (one object scoped to one wellbore and one trajectory):
MD<LOG_NAME>Multi-object shape, only when one table must contain multiple wellbores or multiple trajectory variants:
WELLWELLBOREMD<LOG_NAME>Conditionally required columns when one object contains multiple trajectory variants (added to the multi-object shape):
TRAJECTORY_NAMETRAJECTORY_STATUSdrilled/existing,planned/prognosis.Suggested table metadata:
wellwellborewellbore_uuidunique_wellbore_identifiertrajectory_nametrajectory_statusmd_unitMD, normallym.sourceSuggested column metadata for each log column:
log_nameunitlog_typecontinuous,discrete, orflag.no_data_valuecode_namesThis resembles the existing
ert_parameterspattern, where Parquet column metadata is described by a standard result schema.wellbore_grid_logsThis represents blocked, upscaled, or grid-extracted well logs. The exact name is unresolved.
Possible names:
wellbore_grid_logsblocked_wellbore_logsgrid_extracted_wellbore_logsDraft columns:
WELLWELLBOREMDZ_TVDSSGRID_NAMEIJKDATEREPORT_STEP<LOG_NAME>Conditionally required columns when the object covers multiple trajectory variants for one
WELLBORE:TRAJECTORY_NAMETRAJECTORY_STATUSdrilled/existing,planned/prognosis.Suggested metadata:
grid_namegrid_relative_pathcell_index_baseI,J,Kare zero-based or one-based.sampling_methodupscaling_methodzone_logtime_index_typewellbore_picksWell picks should use
class=table, notpoints, because Arrow/table calculations and standard result validation are the main intended usage. Wellbore picks can vary between realizations and should support realization-scoped results.There may also be different sets of picks for the same ensemble/case. One important example is a set containing simulated pick uncertainty from Cohiba, from the Norwegian Computing Centre (NR). It is unresolved whether this should be represented as a named pick set under
wellbore_picks, or as a separate standard result for simulated/uncertain wellbore picks.Required columns:
WELLWELLBOREX_UTMEY_UTMNZ_TVDSSMDIDENTIFIERIDENTIFIER_TYPEhorizon,zone,fault,marker.OBS_NOConditionally required columns if several trajectory variants can exist for one
WELLBORE:TRAJECTORY_NAMETRAJECTORY_STATUSOptional columns:
CONFIDENCESTRAT_COLUMNPICK_SETWELLBORE_UUIDUWIInterpreter is intentionally not included in the proposed core schema.
Webviz consumer use cases
The table granularity choice should be driven by realistic consumer access patterns, not just by export-side ergonomics. Known target use cases in webviz:
Implications for granularity:
wellboremetadata before loading.REAL-indexed aggregation.These use cases are also the natural starting point for sizing the Sumo aggregation and webviz retrieval design later.
Table granularity options
Option A: one table per wellbore
Pros:
wellbore_uuid, UWI, and RKB can be object metadata.Cons:
Option B: one table for all wellbores
Pros:
Cons:
Option C: hybrid
This is currently the most likely practical default, driven by the webviz consumer use cases:
wellbore_trajectorywellbore_logswellbore_grid_logswellbore_picksTradeoff: this introduces more conventions, but each convention is justified by a concrete consumer use case rather than chosen abstractly.
How a multi-wellbore table actually looks
Multi-wellbore tables (Option B and the multi-wellbore parts of Option C) only make sense as long-format tables.
MD,X_UTME,Y_UTMN, andZ_TVDSSare not a shared axis across wells; each well has its own MD range, sampling, and coordinates. Wells co-exist in the same table by living in different rows, identified by thetable_indexcolumns.Example for
wellbore_trajectory, one realization, two wellbores:WELLWELLBOREMDX_UTMEY_UTMNZ_TVDSSKey consequences:
(WELL, WELLBORE, MD). Different wells naturally have different MD values, different counts of rows, and different coordinate ranges. Parquet does not care.table_indexfor the standard result is the tuple of columns that uniquely identifies a row, not a single shared depth axis. Forwellbore_trajectorythis is[WELL, WELLBORE, MD](+ trajectory identity when variants exist).wellbore_logsin multi-object shape, the same applies: each log column carries values for whichever rows have that log defined; rows for wells that do not have the log get null. The total column set is the union across all wells in the file.wellbore_picksthe rows are per pick, so wells just contribute different numbers of rows.This also clarifies what Sumo aggregation needs to align across realizations. The aggregation key is the
table_indextuple plusREAL. For multi-wellbore trajectories the implicit assumption is therefore that(WELL, WELLBORE, MD)is reproducible per realization for the deterministic cases. If MD sampling differs between realizations for the same wellbore (typical for trajectory uncertainty), per-column aggregation across realizations cannot align rows byMD; that case must either resample to a common MD grid before export or be retrieved per realization rather than aggregated.Open question: should
wellbore_trajectoryfor trajectory uncertainty be required to use a common MD grid per wellbore across realizations so Sumo aggregation aligns, or should uncertainty trajectories be consumed per realization and never aggregated byMD?Sumo storage and table aggregation
Sumo aggregates tables across realizations by transposing per-realization files into per-column files. The columns that act as the aggregation index come from the object's
table_indexmetadata — a list of column names declared by the producer for each standard result.DATEandREALare common examples (e.g. summary tables) but are not built-in; for well data thetable_indexis the identity/coordinate columns of the standard result.REALis added by the aggregation step itself.Originally the source format was Arrow, where a consumer had to download whole files to read a few columns; the format is moving to Parquet so that sufficiently clever readers can fetch only the columns they need. The aggregation code path is chosen by inspecting a randomly selected input file to detect the format.
Implications for the well-data standard results:
table_indexand column shape across realizations are the natural input to Sumo aggregation. The hybrid granularity recommended above is chosen to make this work for trajectories, picks, and grid logs.table_indexfor each standard result must be declared explicitly infmu-datamodels/fmu-dataio. Candidates:wellbore_trajectory:[WELL, WELLBORE, MD], plusTRAJECTORY_NAME/TRAJECTORY_STATUSwhen one object holds multiple variants.wellbore_logs:[MD]for the default per-wellbore-per-trajectory shape (identity is in metadata),[WELL, WELLBORE, MD](+ trajectory identity) for the multi-object shape.wellbore_grid_logs:[WELL, WELLBORE, GRID_NAME, I, J, K], plusDATEorREPORT_STEPif dynamic parameters are included.wellbore_picks:[WELL, WELLBORE, IDENTIFIER, OBS_NO], plus trajectory identity when applicable.table_indexand are therefore guaranteed to align across realizations, versus which are dynamic (log columns) and may differ between wells/realizations.Open question: should Sumo store derived/precomputed views of these tables (e.g. per-well log statistics, per-pick QC summaries) as separate standard results, or should webviz compute them on demand from the raw aggregated tables?
Trajectory storage format
The current proposal stores trajectories as positional samples (
X_UTME,Y_UTMN,Z_TVDSS,MD). An alternative is to store the deviation survey (MD, inclination, azimuth) plus a reference point (surface location, RKB) and a documented minimum-curvature reconstruction algorithm. This would reduce stored data size significantly and avoid sampling-density choices at export time, at the cost of requiring every consumer to reconstruct the 3D path.Before choosing, we need to confirm:
wellbore_trajectoryobjects is awkward.A reasonable default for the first implementation scope is positional samples, with the deviation-survey representation kept as a follow-up if data size becomes a real problem and the survey data is reliably available.
RMS export selection and collections
A practical RMS export helper needs a user-friendly way to select which wells and trajectories to export. Passing explicit well names and trajectory names is useful for scripts and tests, but may be awkward for normal RMS users if their working sets are already maintained inside RMS.
RMS users commonly define collections as working sets of objects. Collections contain references to RMS data objects and can be accessed through the RMS API by giving the collection path and reading back the referenced objects. Based on initial investigation, there appear to be at least two relevant collection types for well data:
Wellscollections: appear to return well names. It is not yet clear whether this corresponds most closely to the localWELL, the localWELLBORE, or RMS's current well/wellbore naming model.Trajectoriescollections: appear to return full path-like references such as[wellname, wellborename, trajectoryname].Using collections would likely improve usability because export scripts could refer to a named RMS working set rather than duplicating well selections in Python. It may also reduce mistakes when users maintain several well sets for different modelling workflows.
This should be treated as an investigation before making the RMS export API contractual. Questions to answer:
Wellscollections sufficient for simple trajectory/log exports, or do they hide important wellbore/trajectory choices?Trajectoriescollections the better first-class input forwellbore_trajectorybecause they include well, wellbore, and trajectory names explicitly?Wellscollection be combined with default trajectory/logrun names in a predictable way?WELLandWELLBOREtable columns?Suggested prototype direction: keep explicit well/trajectory arguments for the first simple RMS exporter, but add an investigation task and possibly a second helper that accepts an RMS collection path once the API behavior is verified. If collections are reliable, they should probably become the preferred user-facing selection mechanism for multi-well exports.
Open questions
Identifiers and granularity:
WELL, localWELLBORE, official well identifiers, and official wellbore identifiers?WELLgrouping enough for the first version?WELLbe required in all wellbore-level tables to support multilateral wells from the start?wellbore_uuid, UWI, RKB, and any official well parent identifier be metadata, columns, or both, including when one table contains multiple wellbores?Trajectory variants:
WELLBORE, or should a planned sidetrack/branch be a separate localWELLBORE?TRAJECTORY_STATUSuse a fixed controlled vocabulary (e.g.drilled,planned), and if so what are the allowed values?Logs and grid logs:
wellbore_grid_logs,blocked_wellbore_logs,grid_extracted_wellbore_logs)?MDalways available and meaningful for blocked/upscaled/grid-extracted logs?I,J,Kindices be required to be zero-based or one-based?wellbore_grid_logscover dynamic grid parameters such as pressure over time, and if so should time be represented byDATE,REPORT_STEP, elapsed time, metadata, or a separate time index table — or should dynamic wellbore grid parameters be a separate standard result?Picks and uncertainty:
wellpickscontent value be renamed towellbore_picks, or should a newwellbore_pickscontent value replace it while removing/deprecatingwellpicks?wellbore_trajectory, or should it have a separate standard result?PICK_SETmetadata/columns underwellbore_picks, or should simulated pick uncertainty (e.g. Cohiba/NR) have a separate standard result?Sumo and RMS:
wellbore_trajectoryfor trajectory uncertainty be required to use a common MD grid per wellbore across realizations so Sumo aggregation aligns, or should uncertainty trajectories be consumed per realization and never aggregated byMD?Wellscollections,Trajectoriescollections, or both?WELLandWELLBOREidentifiers, especially when trajectory collections return[wellname, wellborename, trajectoryname]?Trajectory storage format:
wellbore_trajectorystore positional samples, deviation surveys, or both?Suggested first implementation scope
Four standard results are proposed in total. The first implementation scope covers three of them:
wellbore_trajectorywellbore_logswellbore_picksDefer
wellbore_grid_logsuntil naming, grid index semantics, and upscaling metadata are better agreed.Initial
fmu-datamodelswork:WELL/WELLBORErelationship for multilateral wells.wellpicksmodel as the new table-basedwellbore_picksmodel.Initial
fmu-dataiowork:xtgeo.Well,xtgeo.Wells, andxtgeo.Points.Initial
webvizwork:ArrowTableLoaderor similar.