Add Python support for import of property files for .roff and .grdecl (#10183)

- Add RimcEclipseCase
- Add tests for import case properties (.roff and .grdecl)
- Make rips handle list of strings as input in Pyton API
This commit is contained in:
Jørgen Herje 2023-04-27 10:53:33 +02:00 committed by GitHub
parent f3faf4642a
commit 862e67755a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 27220 additions and 5 deletions

View File

@ -89,7 +89,7 @@ CAF_PDM_XML_ABSTRACT_SOURCE_INIT( RimEclipseCase, "RimReservoir" );
//--------------------------------------------------------------------------------------------------
RimEclipseCase::RimEclipseCase()
{
CAF_PDM_InitScriptableObjectWithNameAndComment( "EclipseCase", ":/Case48x48.png", "", "", "Reservoir", "Abtract base class for Eclipse Cases" );
CAF_PDM_InitScriptableObjectWithNameAndComment( "EclipseCase", ":/Case48x48.png", "", "", "Reservoir", "Abstract base class for Eclipse Cases" );
CAF_PDM_InitScriptableFieldWithScriptKeywordNoDefault( &reservoirViews, "ReservoirViews", "Views", "", "", "", "All Eclipse Views in the case" );
reservoirViews.uiCapability()->setUiTreeHidden( true );

View File

@ -23,6 +23,7 @@ set(SOURCE_GROUP_HEADER_FILES
${CMAKE_CURRENT_LIST_DIR}/RimcFractureTemplate.h
${CMAKE_CURRENT_LIST_DIR}/RimcThermalFractureTemplate.h
${CMAKE_CURRENT_LIST_DIR}/RimcIntersection.h
${CMAKE_CURRENT_LIST_DIR}/RimcEclipseCase.h
)
set(SOURCE_GROUP_SOURCE_FILES
@ -50,6 +51,7 @@ set(SOURCE_GROUP_SOURCE_FILES
${CMAKE_CURRENT_LIST_DIR}/RimcFractureTemplate.cpp
${CMAKE_CURRENT_LIST_DIR}/RimcThermalFractureTemplate.cpp
${CMAKE_CURRENT_LIST_DIR}/RimcIntersection.cpp
${CMAKE_CURRENT_LIST_DIR}/RimcEclipseCase.cpp
)
list(APPEND CODE_HEADER_FILES ${SOURCE_GROUP_HEADER_FILES})

View File

@ -0,0 +1,99 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2020- Equinor ASA
//
// ResInsight is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.
//
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
// for more details.
//
/////////////////////////////////////////////////////////////////////////////////
#include "RimcEclipseCase.h"
#include "RiaApplication.h"
#include "RiaGuiApplication.h"
#include "RicImportSummaryCasesFeature.h"
#include "RimEclipseCase.h"
#include "RimFileSummaryCase.h"
#include "RimOilField.h"
#include "RimProject.h"
#include "RimSummaryCase.h"
#include "RimSurfaceCollection.h"
#include "RiuPlotMainWindow.h"
#include "cafPdmFieldScriptingCapability.h"
#include <QDir>
#include <QFileInfo>
#include <memory>
CAF_PDM_OBJECT_METHOD_SOURCE_INIT( RimEclipseCase, RimcEclipseCase_importProperties, "import_properties" );
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimcEclipseCase_importProperties::RimcEclipseCase_importProperties( caf::PdmObjectHandle* self )
: caf::PdmObjectMethod( self )
{
CAF_PDM_InitObject( "Import Properties", "", "", "Import Properties" );
CAF_PDM_InitScriptableFieldNoDefault( &m_fileNames, "FileNames", "" );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
caf::PdmObjectHandle* RimcEclipseCase_importProperties::execute()
{
std::vector<QString> absolutePaths = m_fileNames;
for ( auto& path : absolutePaths )
{
QFileInfo projectPathInfo( path );
if ( !projectPathInfo.exists() )
{
QDir startDir( RiaApplication::instance()->startDir() );
path = startDir.absoluteFilePath( path );
}
}
const QStringList propertyFileNames = QStringList::fromVector( QVector<QString>::fromStdVector( absolutePaths ) );
auto eclipseCase = self<RimEclipseCase>();
eclipseCase->importAsciiInputProperties( propertyFileNames );
return nullptr;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimcEclipseCase_importProperties::resultIsPersistent() const
{
return false;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::unique_ptr<caf::PdmObjectHandle> RimcEclipseCase_importProperties::defaultResult() const
{
return nullptr;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimcEclipseCase_importProperties::isNullptrValidResult() const
{
return true;
}

View File

@ -0,0 +1,46 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2020- Equinor ASA
//
// ResInsight is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.
//
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
// for more details.
//
/////////////////////////////////////////////////////////////////////////////////
#pragma once
#include "cafPdmField.h"
#include "cafPdmObjectHandle.h"
#include "cafPdmObjectMethod.h"
#include <QString>
#include <memory>
//==================================================================================================
///
//==================================================================================================
class RimcEclipseCase_importProperties : public caf::PdmObjectMethod
{
CAF_PDM_HEADER_INIT;
public:
RimcEclipseCase_importProperties( caf::PdmObjectHandle* self );
caf::PdmObjectHandle* execute() override;
bool resultIsPersistent() const override;
std::unique_ptr<PdmObjectHandle> defaultResult() const override;
bool isNullptrValidResult() const override;
private:
caf::PdmField<std::vector<QString>> m_fileNames;
};

View File

@ -246,6 +246,8 @@ struct PdmFieldScriptingCapabilityIOHandler<std::vector<T>>
QChar chr = errorMessageContainer->readCharWithLineNumberCount( inputStream );
if ( chr == QChar( '[' ) )
{
std::vector<QString> allValues;
QString currentValue;
while ( !inputStream.atEnd() )
{
errorMessageContainer->skipWhiteSpaceWithLineNumberCount( inputStream );
@ -259,11 +261,25 @@ struct PdmFieldScriptingCapabilityIOHandler<std::vector<T>>
{
nextChar = errorMessageContainer->readCharWithLineNumberCount( inputStream );
errorMessageContainer->skipWhiteSpaceWithLineNumberCount( inputStream );
if ( !currentValue.isEmpty() ) allValues.push_back( currentValue );
currentValue = "";
}
else
{
currentValue += errorMessageContainer->readCharWithLineNumberCount( inputStream );
}
}
if ( !currentValue.isEmpty() ) allValues.push_back( currentValue );
T value;
PdmFieldScriptingCapabilityIOHandler<T>::writeToField( value, inputStream, errorMessageContainer, true );
fieldValue.push_back( value );
for ( QString textValue : allValues )
{
QTextStream singleValueStream( &textValue, QIODevice::ReadOnly );
T singleValue;
PdmFieldScriptingCapabilityIOHandler<T>::writeToField( singleValue,
singleValueStream,
errorMessageContainer,
false );
fieldValue.push_back( singleValue );
}
}
else

View File

@ -1,13 +1,22 @@
import sys
import os
from typing import Any, Dict, List, TypedDict
import math
from generated.generated_classes import Case
sys.path.insert(1, os.path.join(sys.path[0], "../../"))
import rips
import dataroot
class NameAndPath(TypedDict):
# Typed dict container for name of property and path of property file
name: str
path: str
def check_corner(actual, expected):
assert math.isclose(actual.x, expected[0], abs_tol=0.1)
assert math.isclose(actual.y, expected[1], abs_tol=0.1)
@ -60,7 +69,7 @@ def test_10k(rips_instance, initialize_test):
check_corner(cell_corners[cell_index].c7, expected_corners[7])
def check_reek_grid_box(case):
def check_reek_grid_box(case: Case):
assert len(case.grids()) == 1
grid = case.grid(index=0)
@ -95,3 +104,46 @@ def test_load_grdecl_grid(rips_instance, initialize_test):
casePath = dataroot.PATH + "/reek/reek_box_grid_w_props.grdecl"
case = rips_instance.project.load_case(path=casePath)
check_reek_grid_box(case)
def verify_load_grid_and_separate_properties(
case: Case, property_name_and_paths: NameAndPath
):
# Load case without properties
available_properties = case.available_properties("INPUT_PROPERTY")
for [name, _path] in property_name_and_paths.items():
assert name not in available_properties
grid = case.grid(index=0)
dimensions = grid.dimensions()
total_size = dimensions.i * dimensions.j * dimensions.k
# Import properties to case
case.import_properties(file_names=list(property_name_and_paths.values()))
available_properties = case.available_properties("INPUT_PROPERTY")
for [name, _path] in property_name_and_paths.items():
assert name in available_properties
property_values = case.active_cell_property("INPUT_PROPERTY", name, 0)
assert len(property_values) == total_size
def test_load_grdecl_grid_with_separate_properties(rips_instance, initialize_test):
grid_file_path: str = dataroot.PATH + "/reek/reek_box_grid_w_out_props.grdecl"
property_name_and_paths: NameAndPath = {
"EQLNUM": dataroot.PATH + "/reek/reek_box_EQLNUM_property.grdecl",
"PORO": dataroot.PATH + "/reek/reek_box_PORO_property.grdecl",
}
case = rips_instance.project.load_case(path=grid_file_path)
verify_load_grid_and_separate_properties(case, property_name_and_paths)
def test_load_roff_grid_with_separate_properties(rips_instance, initialize_test):
grid_file_path: str = dataroot.PATH + "/reek/reek_box_grid_w_out_props.roffasc"
property_name_and_paths: NameAndPath = {
"EQLNUM": dataroot.PATH + "/reek/reek_box_EQLNUM_property.roffasc",
"PORO": dataroot.PATH + "/reek/reek_box_PORO_property.roffasc",
}
case = rips_instance.project.load_case(path=grid_file_path)
verify_load_grid_and_separate_properties(case, property_name_and_paths)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff