Skip to content

Distribute formation information to per-grid-case ownership #924

Description

@magnesj

Description

Today, formation/zonation data lives in a single project-wide pool: RimOilField::formationNamesCollection holds a RimFormationNamesCollection of RimFormationNames objects, and each RimCase / RimReservoirGridEnsemble carries a PdmPtrField named m_activeFormationNames that points into that pool. Two cases can share the same pool entry; an active pointer selects one of many for a case; RimEclipseCase::effectiveFormationNames() falls back to the parent ensemble when a case has no formation set.

This work makes formations a distributed property of a grid model: each grid case (and each ensemble) directly owns one RimFormationNames as a PdmChildField. The central RimFormationNamesCollection is removed from the data model (kept only as a migration-only artifact for one release). The active formation indirection is dropped — a case has one formation, period. The case → ensemble fallback is preserved so a single FMU layer_zone_table.txt does not have to be duplicated across every realization.

Why

  • Formations are intrinsic to a grid model, not a project-wide registry.
  • Removing the central pool removes a layer of indirection (PdmPtr lookups, dropdown UI) and makes the data model easier to reason about.
  • Each case is self-contained, which simplifies copy/duplicate semantics and per-case import.

Design

New ownership

  • RimCase gets caf::PdmChildField<RimFormationNames*> m_formationNames (direct ownership, nullable). XML keyword: FormationNames.
  • RimReservoirGridEnsemble gets the same field independently (it does not inherit RimCase).
  • RimFormationNames is reparented from the central pool to the case. No internal changes to RimFormationNames / RigFormationNames / RifFormationNamesReader.

Public API

  • RimCase::formationNames() returns RimFormationNames* (renamed from activeFormationNames()). Returns the case's own formation only — no fallback.
  • RimCase::setFormationNames(RimFormationNames*) — semantics now case takes ownership. Callers must hand over a heap-allocated object.
  • RimEclipseCase::effectiveFormationNames() is kept. Logic: case's own formation → parent RimReservoirGridEnsemble's formation → null. Internals updated to call formationNames() on each side. Same applies to any GeoMech equivalent.

Migration of existing project files

Maintain backward compatibility for one release using obsolete-only fields, mirroring the established m_fractureTemplateCollection_OBSOLETE pattern at RimOilField.cpp:104-105.

Owner Old field / keyword New treatment
RimOilField formationNamesCollection / FormationNamesCollection Renamed m_formationNamesCollection_OBSOLETE, IO write disabled, hidden
RimCase m_activeFormationNames / DefaultFormationNames (PdmPtrField) Renamed m_activeFormationNames_OBSOLETE — type and keyword preserved so PDM pointer resolution still wires up during load
RimReservoirGridEnsemble m_activeFormationNames (PdmPtrField) Same renaming
RimFormationNamesCollection class — Kept as migration-only helper (deleting it would prevent CAF from instantiating the old XML element). Schedule full deletion for the release after this one.

PDM children's initAfterRead runs before the parent's, so:

  • RimCase::initAfterRead() and RimReservoirGridEnsemble::initAfterRead(): if the obsolete pointer is non-null, deep-clone via caf::PdmObjectFactory::copyByXmlSerialization into the new m_formationNames child, then null out the obsolete pointer.
  • RimOilField::initAfterRead(): empty the obsolete collection (children have already been cloned into cases).

Sharing semantics: two cases that previously shared one pool entry now each get an independent clone — accepted change.

Import command

Rewrite RicImportFormationNamesFeature (Commands/RicImportFormationNamesFeature.cpp:59-143):

  • onActionTriggered: prompt for files; resolve the target case from the active reservoir view (RiaApplication::instance()->activeReservoirView()->ownerCase()); abort with a warning if no active case.
  • Construct a fresh RimFormationNames, set the file path, call readFormationNamesFile, then ownerCase->setFormationNames(formationNames) to reparent into the child field.
  • Keep the existing colorLegendCollection->createColorLegendFromFormationNames(...) and setFormationCellResultAndLegend post-import calls.
  • Retire the importFormationFiles(QStringList) static helper; replace with importFormationFile(const QString&, RimCase*) returning the heap-allocated RimFormationNames*.

Ensemble auto-detection

RimReservoirGridEnsemble.cpp:838-846 currently funnels auto-detected .lyr / layer_zone_table.txt files through the central import. Rewrite:

  • Detect a co-located formation file at the ensemble level. Construct one RimFormationNames, parse it, attach via ensemble->setFormationNames(...).
  • Realizations inherit through the kept effectiveFormationNames() fallback — no per-realization clones unless a realization has a distinct co-located file.
  • A realization with its own distinct file gets a fresh RimFormationNames (case-own takes precedence over ensemble in the fallback chain).

Critical files

  • ApplicationLibCode/ProjectDataModel/RimOilField.h / .cpp
  • ApplicationLibCode/ProjectDataModel/RimCase.h / .cpp
  • ApplicationLibCode/ProjectDataModel/RimReservoirGridEnsemble.h / .cpp
  • ApplicationLibCode/ProjectDataModel/RimEclipseCase.h / .cpp
  • ApplicationLibCode/ProjectDataModel/RimGeoMechCase.h / .cpp
  • ApplicationLibCode/ProjectDataModel/RimProject.cpp
  • ApplicationLibCode/ProjectDataModel/Formations/RimFormationNamesCollection.h / .cpp (kept as migration-only)
  • ApplicationLibCode/Commands/RicImportFormationNamesFeature.cpp
  • ApplicationLibCode/Commands/RicElasticPropertiesImportTools.cpp
  • ApplicationLibCode/Application/RiaApplication.cpp
  • All consumer files listed in the rename step below

Reusable utilities

  • caf::PdmObjectFactory::copyByXmlSerialization — deep-clone in migration
  • RiaFieldHandleTools::disableWriteAndSetFieldHidden — mark obsolete fields
  • Existing OBSOLETE pattern in RimOilField.cpp:104-105 and RimOilField::initAfterRead() at :150-158
  • RifFormationNamesReader::readFormationNamesFile — file parsing unchanged
  • RimColorLegendCollection::createColorLegendFromFormationNames — legend creation unchanged

Steps

  • Add m_formationNames PdmChildField to RimCase and RimReservoirGridEnsemble (XML keyword FormationNames). Add public formationNames() getter and update setFormationNames() to reparent into the child field.
  • Rename existing m_activeFormationNames → m_activeFormationNames_OBSOLETE on RimCase and RimReservoirGridEnsemble. Apply disableWriteAndSetFieldHidden. Keep XML keyword DefaultFormationNames.
  • Rename RimOilField::formationNamesCollection → m_formationNamesCollection_OBSOLETE, disable write, hide.
  • Implement initAfterRead migration in RimCase and RimReservoirGridEnsemble: clone obsolete pointer target into the new child field. Empty the obsolete collection in RimOilField::initAfterRead().
  • Keep RimEclipseCase::effectiveFormationNames() (and any GeoMech equivalent); update its internal calls to the renamed formationNames() on case and ensemble.
  • Delete the RimCase::calculateValueOptions block for formations (the central-pool dropdown logic at RimCase.cpp:264-283). The new field is a PdmChildField and shows as a tree child, not a dropdown.
  • Rewrite RicImportFormationNamesFeature for per-case import.
  • Rewrite the .lyr auto-detection in RimReservoirGridEnsemble.cpp:838-846 to assign at the ensemble level; per-realization assignment only when a distinct file is co-located.
  • Drop the central read in RiaApplication.cpp:625-640; replace with a per-case loop calling c->formationNames()->readFormationNamesFile(...) when non-null. Same for ensembles.
  • Update RicElasticPropertiesImportTools::getFormationNames to take a RimCase* and return that case's owned formation.
  • Remove the central tree-ordering entry in RimProject.cpp:1544.
  • Mechanical rename activeFormationNames() → formationNames() across consumers:
    • ProjectDataModel/WellLog/RimWellLogTrack.cpp:128
    • ProjectDataModel/WellLog/RimWellRftPlot.*
    • ProjectDataModel/Flow/RimWellAllocationPlot.cpp:910
    • ProjectDataModel/RimEnsembleWellLogCurveSet.cpp:1245
    • ProjectDataModel/RimEclipseView.*, RimGeoMechView.*
    • ProjectDataModel/ContourMap/RimStatisticsContourMap.cpp:77
    • ProjectDataModel/RimColorLegendCollection.cpp:223
    • FileInterface/RifStimPlanModelGeologicalFrkExporter.cpp
    • GeoMech/GeoMechDataModel/RigFemPartResultCalculatorFormationIndices.*
  • Run clang-format on every changed .cpp / .h.
  • Build with ninja from build/: cmake --build build. Must compile clean.
  • Run ResInsightUnitTests — RifFormationNamesReader-Test should still pass unchanged.

Verification

  • Old project round-trip: open a pre-refactor .rsp with formations assigned. Verify each case shows its formation as a child node; 3D view shades correctly; save → reopen preserves it under the new schema (no <FormationNamesCollection> written, only per-case <FormationNames>).
  • Fresh import: in the active eclipse view, run Import Formations, pick a .lyr, confirm it lands as a child of the case and the formation cell-result legend updates.
  • FMU ensemble auto-detect: open an ensemble with a co-located layer_zone_table.txt. Verify the ensemble owns the RimFormationNames and realizations without their own file inherit via effectiveFormationNames(). Verify a realization with its own distinct file shadows the ensemble.
  • Plot consumers: verify well log tracks, RFT plots, well allocation plots, statistics contour maps, color legends, and StimPlan geological export render formation names correctly against case->formationNames().
  • GeoMech path: open a .odb case with formations; verify formation-based result calculations still work via RigFemPartResultsCollection.

Links

  • Existing OBSOLETE pattern reference: ApplicationLibCode/ProjectDataModel/RimOilField.cpp lines 66, 104-105, 150-158
  • Central collection class: ApplicationLibCode/ProjectDataModel/Formations/RimFormationNamesCollection.h
  • Per-file formation entry: ApplicationLibCode/ProjectDataModel/Formations/RimFormationNames.h
  • Runtime data: ApplicationLibCode/ReservoirDataModel/RigFormationNames.h
  • File reader: ApplicationLibCode/FileInterface/RifFormationNamesReader.h
  • Import command: ApplicationLibCode/Commands/RicImportFormationNamesFeature.cpp
  • Existing fallback: ApplicationLibCode/ProjectDataModel/RimEclipseCase.cpp:551-565

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions