Skip to content

#13863 Roff: Load grid zonation (subgrids) as SUBGRIDS input property#914

Open
magnesj wants to merge 1 commit intodevfrom
roff-layer-info
Open

#13863 Roff: Load grid zonation (subgrids) as SUBGRIDS input property#914
magnesj wants to merge 1 commit intodevfrom
roff-layer-info

Conversation

@magnesj
Copy link
Copy Markdown
Owner

@magnesj magnesj commented Apr 18, 2026

Read the ROFF "subgrids.nLayers" array and expand it into a 1-based
per-cell integer property (SUBGRIDS) so ROFF zonation becomes a regular
grid property. If a companion "subgrids.names" array is present, zone
names are surfaced as category code-names via the existing color-legend
flow.

@qodo-code-review
Copy link
Copy Markdown

ⓘ You are approaching your monthly quota for Qodo. Upgrade your plan

Review Summary by Qodo

Load ROFF grid zonation as SUBGRIDS input property

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Load ROFF grid zonation (subgrids) as SUBGRIDS input property
• Expand subgrids.nLayers array into per-cell integer property
• Surface zone names as category code-names via color-legend
• Add comprehensive unit tests for zone value computation
Diagram
flowchart LR
  A["ROFF File<br/>subgrids.nLayers"] -->|"Read array"| B["computeZoneValuesFromSubgrids"]
  B -->|"Compute zone values<br/>with K-flip"| C["Per-cell values<br/>in reservoir order"]
  C -->|"Create property"| D["SUBGRIDS<br/>INPUT_PROPERTY"]
  E["subgrids.names<br/>optional"] -->|"Read zone names"| F["Create color legend"]
  F -->|"Attach to result"| D
Loading

Grey Divider

File Changes

1. ApplicationLibCode/FileInterface/RifRoffFileTools.cpp ✨ Enhancement +128/-0

Implement ROFF subgrids zonation import logic

• Added handling for "subgrids.nLayers" keyword in createInputProperties method
• Implemented computeZoneValuesFromSubgrids function to expand zone layer counts into per-cell
 values with K-flip transformation
• Implemented appendZoneIndexPropertyFromSubgrids function to create SUBGRIDS property and attach
 zone names as color legend
• Validates grid dimensions and layer counts before processing
• Marks inactive cells with HUGE_VAL

ApplicationLibCode/FileInterface/RifRoffFileTools.cpp


2. ApplicationLibCode/FileInterface/RifRoffFileTools.h ✨ Enhancement +6/-0

Add method declarations for subgrids processing

• Added public static method declaration for computeZoneValuesFromSubgrids
• Added private static method declaration for appendZoneIndexPropertyFromSubgrids

ApplicationLibCode/FileInterface/RifRoffFileTools.h


3. ApplicationLibCode/UnitTests/RifRoffFileTools-Test.cpp 🧪 Tests +90/-0

Add comprehensive unit tests for zone computation

• Added unit test for basic zone value computation with K-flip validation
• Added tests for validation: sum mismatch, non-positive layers, empty input
• Added integration test reading actual ROFF file with subgrids data
• Validates zone names are correctly read from file

ApplicationLibCode/UnitTests/RifRoffFileTools-Test.cpp


View more (2)
4. ApplicationLibCode/UnitTests/CMakeLists.txt ⚙️ Configuration changes +1/-0

Register new unit test file

• Added RifRoffFileTools-Test.cpp to unit test source files list

ApplicationLibCode/UnitTests/CMakeLists.txt


5. ApplicationLibCode/UnitTests/TestData/RifRoffReader/with_subgrids.roff 🧪 Tests +26/-0

Add test ROFF file with subgrids data

• Created test ROFF file with subgrids zonation data
• Includes 2x3x6 grid dimensions with 3 zones spanning 2, 1, and 3 layers
• Provides zone names: "Top Zone", "Middle Zone", "Bottom Zone"

ApplicationLibCode/UnitTests/TestData/RifRoffReader/with_subgrids.roff


Grey Divider

Qodo Logo

@qodo-code-review
Copy link
Copy Markdown

qodo-code-review bot commented Apr 18, 2026

Code Review by Qodo

🐞 Bugs (2) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider


Action required

1. Subgrids handler unreachable 🐞 Bug ≡ Correctness
Description
In createInputProperties, the generic "array length == cellCount" import runs before the
special-case "subgrids.nLayers" handler, so when nLayers length happens to equal the grid cell
count, subgrids.nLayers will be imported as a per-cell property and SUBGRIDS will not be created.
This silently misimports zonation for grids where cellCount == numberOfZones (e.g., 1×1×NZ with one
layer per zone).
Code

ApplicationLibCode/FileInterface/RifRoffFileTools.cpp[R604-613]

                keywordMapping[QString::fromStdString( keyword )] = newResultName;
            }
+            else if ( keyword == "subgrids.nLayers" )
+            {
+                if ( !appendZoneIndexPropertyFromSubgrids( eclipseCaseData, reader, keywordMapping ) )
+                {
+                    RiaLogging::warning( QString( "Unable to import ROFF subgrids zonation from %1" ).arg( fileName ) );
+                }
+            }
Evidence
The keyword dispatch checks cellCount equality first and only then checks for the subgrids.nLayers
keyword, making the subgrids branch unreachable whenever keywordLength == mainGrid()->cellCount().

ApplicationLibCode/FileInterface/RifRoffFileTools.cpp[540-613]
ApplicationLibCode/ReservoirDataModel/RigGridBase.h[48-53]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`subgrids.nLayers` is handled after the generic `keywordLength == cellCount` check. If `subgrids.nLayers` length equals the grid cell count (possible for small grids / many zones), the code imports it as a per-cell property and never creates the intended `SUBGRIDS` zonation property.

### Issue Context
`subgrids.nLayers` is *not* a per-cell array; it is per-zone (number of layers per zone). It must be excluded from the generic per-cell import path.

### Fix Focus Areas
- ApplicationLibCode/FileInterface/RifRoffFileTools.cpp[553-613]

### Suggested fix
Move the `keyword == "subgrids.nLayers"` branch **above** the `cellCount == keywordLength` branch (or explicitly exclude `subgrids.nLayers` from the generic branch), ensuring `SUBGRIDS` is created regardless of array length coincidences.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Null RimProject dereference 🐞 Bug ☼ Reliability
Description
appendZoneIndexPropertyFromSubgrids dereferences RimProject::current()->colorLegendCollection
without checking that RiaApplication::instance() (and thus the current project) exists, which can
crash during ROFF import in early startup/headless contexts. This new crash path is triggered
whenever subgrids.names is present.
Code

ApplicationLibCode/FileInterface/RifRoffFileTools.cpp[R843-852]

+        RimColorLegendCollection* colorLegendCollection = RimProject::current()->colorLegendCollection;
+
+        int   caseId  = 0;
+        auto* rimCase = caseData->ownerCase();
+        if ( rimCase ) caseId = rimCase->caseId();
+
+        colorLegendCollection->deleteColorLegend( caseId, resultName );
+        RimColorLegend* colorLegend = colorLegendCollection->createColorLegend( resultName, codeNames );
+        colorLegendCollection->setDefaultColorLegendForResult( caseId, resultName, colorLegend );
+        colorLegendCollection->updateAllRequiredEditors();
Evidence
The new code calls RimProject::current() and dereferences the returned pointer.
RimProject::current() directly dereferences RiaApplication::instance(), and
RiaApplication::instance() can be nullptr until the application object is constructed.

ApplicationLibCode/FileInterface/RifRoffFileTools.cpp[832-853]
ApplicationLibCode/ProjectDataModel/RimProject.cpp[234-237]
ApplicationLibCode/Application/RiaApplication.cpp[152-160]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`appendZoneIndexPropertyFromSubgrids` unconditionally does:
- `RimProject::current()->colorLegendCollection`
If the application singleton or project is not initialized, this dereference can crash.

### Issue Context
`RimProject::current()` is implemented as `RiaApplication::instance()->project()` with no null checks, and `RiaApplication::instance()` is `nullptr` until a `RiaApplication` is constructed.

### Fix Focus Areas
- ApplicationLibCode/FileInterface/RifRoffFileTools.cpp[832-853]
- ApplicationLibCode/ProjectDataModel/RimProject.cpp[234-237]
- ApplicationLibCode/Application/RiaApplication.cpp[152-160]

### Suggested fix
Before accessing `RimProject::current()->colorLegendCollection`, add a defensive guard:
- If `RiaApplication::instance() == nullptr` or `RimProject::current() == nullptr` or `colorLegendCollection == nullptr`, skip legend creation (still import the SUBGRIDS values) and return `true`.
This keeps the data import functional while avoiding a hard crash in non-UI/early-init contexts.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

Comment on lines 604 to +613

keywordMapping[QString::fromStdString( keyword )] = newResultName;
}
else if ( keyword == "subgrids.nLayers" )
{
if ( !appendZoneIndexPropertyFromSubgrids( eclipseCaseData, reader, keywordMapping ) )
{
RiaLogging::warning( QString( "Unable to import ROFF subgrids zonation from %1" ).arg( fileName ) );
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Subgrids handler unreachable 🐞 Bug ≡ Correctness

In createInputProperties, the generic "array length == cellCount" import runs before the
special-case "subgrids.nLayers" handler, so when nLayers length happens to equal the grid cell
count, subgrids.nLayers will be imported as a per-cell property and SUBGRIDS will not be created.
This silently misimports zonation for grids where cellCount == numberOfZones (e.g., 1×1×NZ with one
layer per zone).
Agent Prompt
### Issue description
`subgrids.nLayers` is handled after the generic `keywordLength == cellCount` check. If `subgrids.nLayers` length equals the grid cell count (possible for small grids / many zones), the code imports it as a per-cell property and never creates the intended `SUBGRIDS` zonation property.

### Issue Context
`subgrids.nLayers` is *not* a per-cell array; it is per-zone (number of layers per zone). It must be excluded from the generic per-cell import path.

### Fix Focus Areas
- ApplicationLibCode/FileInterface/RifRoffFileTools.cpp[553-613]

### Suggested fix
Move the `keyword == "subgrids.nLayers"` branch **above** the `cellCount == keywordLength` branch (or explicitly exclude `subgrids.nLayers` from the generic branch), ensuring `SUBGRIDS` is created regardless of array length coincidences.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +843 to +852
RimColorLegendCollection* colorLegendCollection = RimProject::current()->colorLegendCollection;

int caseId = 0;
auto* rimCase = caseData->ownerCase();
if ( rimCase ) caseId = rimCase->caseId();

colorLegendCollection->deleteColorLegend( caseId, resultName );
RimColorLegend* colorLegend = colorLegendCollection->createColorLegend( resultName, codeNames );
colorLegendCollection->setDefaultColorLegendForResult( caseId, resultName, colorLegend );
colorLegendCollection->updateAllRequiredEditors();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

2. Null rimproject dereference 🐞 Bug ☼ Reliability

appendZoneIndexPropertyFromSubgrids dereferences RimProject::current()->colorLegendCollection
without checking that RiaApplication::instance() (and thus the current project) exists, which can
crash during ROFF import in early startup/headless contexts. This new crash path is triggered
whenever subgrids.names is present.
Agent Prompt
### Issue description
`appendZoneIndexPropertyFromSubgrids` unconditionally does:
- `RimProject::current()->colorLegendCollection`
If the application singleton or project is not initialized, this dereference can crash.

### Issue Context
`RimProject::current()` is implemented as `RiaApplication::instance()->project()` with no null checks, and `RiaApplication::instance()` is `nullptr` until a `RiaApplication` is constructed.

### Fix Focus Areas
- ApplicationLibCode/FileInterface/RifRoffFileTools.cpp[832-853]
- ApplicationLibCode/ProjectDataModel/RimProject.cpp[234-237]
- ApplicationLibCode/Application/RiaApplication.cpp[152-160]

### Suggested fix
Before accessing `RimProject::current()->colorLegendCollection`, add a defensive guard:
- If `RiaApplication::instance() == nullptr` or `RimProject::current() == nullptr` or `colorLegendCollection == nullptr`, skip legend creation (still import the SUBGRIDS values) and return `true`.
This keeps the data import functional while avoiding a hard crash in non-UI/early-init contexts.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Read the ROFF "subgrids.nLayers" array and expand it into a 1-based
per-cell integer property (SUBGRIDS) so ROFF zonation becomes a regular
grid property. If a companion "subgrids.names" array is present, zone
names are surfaced as category code-names via the existing color-legend
flow.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant