#10861 Add import/export for grid calculations

This commit is contained in:
Kristian Bendiksen 2023-11-24 11:42:31 +01:00
parent 52259a69f3
commit fa7a56f1e2
18 changed files with 440 additions and 42 deletions

3
.gitmodules vendored
View File

@ -22,3 +22,6 @@
[submodule "ThirdParty/regression-analysis"]
path = ThirdParty/regression-analysis
url = https://github.com/CeetronSolutions/regression-analysis.git
[submodule "ThirdParty/tomlplusplus"]
path = ThirdParty/tomlplusplus
url = https://github.com/marzer/tomlplusplus.git

View File

@ -22,9 +22,13 @@
#include "RiaLogging.h"
#include "RiaPreferences.h"
#include "RifGridCalculation.h"
#include "RifGridCalculationExporter.h"
#include "RimGridCalculationCollection.h"
#include "RimGridCalculationVariable.h"
#include "RimProject.h"
#include "Riu3DMainWindowTools.h"
#include "RiuFileDialogTools.h"
#include <QAction>
@ -51,9 +55,6 @@ void RicExportGridCalculationExpressionsFeature::onActionTriggered( bool isCheck
if ( calcColl->calculations().empty() ) return;
auto objectAsText = calcColl->writeObjectToXmlString();
if ( objectAsText.isEmpty() ) return;
QString fallbackPath = RiaPreferences::current()->gridCalculationExpressionFolder();
auto app = RiaGuiApplication::instance();
QString startPath =
@ -62,23 +63,41 @@ void RicExportGridCalculationExpressionsFeature::onActionTriggered( bool isCheck
QString fileName = RiuFileDialogTools::getSaveFileName( nullptr,
"Select File for Grid Calculation Expression Export",
startPath,
"Xml File(*.xml);;All files(*.*)" );
"Toml File(*.toml);;All files(*.*)" );
if ( fileName.isEmpty() ) return;
QFile exportFile( fileName );
if ( !exportFile.open( QIODevice::WriteOnly | QIODevice::Text ) )
{
RiaLogging::errorInMessageBox( nullptr,
"Export Grid Calculation Expressions",
QString( "Could not save to the file: %1" ).arg( fileName ) );
return;
}
QString absPath = QFileInfo( fileName ).absolutePath();
app->setLastUsedDialogDirectory( RicExportGridCalculationExpressionsFeature::gridCalculationExpressionId(), absPath );
QTextStream stream( &exportFile );
stream << objectAsText;
std::vector<RifGridCalculation> calculations;
for ( auto calculation : calcColl->calculations() )
{
RifGridCalculation calc;
calc.description = calculation->description().toStdString();
calc.expression = calculation->expression().toStdString();
for ( auto variable : calculation->allVariables() )
{
if ( auto gridVariable = dynamic_cast<RimGridCalculationVariable*>( variable ) )
{
RifGridCalculationVariable var;
var.resultVariable = gridVariable->resultVariable().toStdString();
var.resultType = caf::AppEnum<RiaDefines::ResultCatType>::text( gridVariable->resultCategoryType() ).toStdString();
var.name = gridVariable->name().toStdString();
calc.variables.push_back( var );
}
}
calculations.push_back( calc );
}
auto [isOk, errorMessage] = RifGridCalculationExporter::writeToFile( calculations, fileName.toStdString() );
if ( !isOk )
{
RiaLogging::errorInMessageBox( Riu3DMainWindowTools::mainWindowWidget(),
"Grid Calculation Export Error",
QString::fromStdString( errorMessage ) );
}
}
//--------------------------------------------------------------------------------------------------

View File

@ -24,11 +24,16 @@
#include "RiaPreferences.h"
#include "RimEclipseCaseTools.h"
#include "RimEclipseResultAddress.h"
#include "RimGridCalculationCollection.h"
#include "RimGridCalculationVariable.h"
#include "RimProject.h"
#include "Riu3DMainWindowTools.h"
#include "RiuFileDialogTools.h"
#include "RifGridCalculationImporter.h"
#include "cafPdmDefaultObjectFactory.h"
#include "cafPdmXmlObjectHandle.h"
@ -50,20 +55,18 @@ void RicImportGridCalculationExpressionsFeature::onActionTriggered( bool isCheck
RiaPreferences::current()->gridCalculationExpressionFolder() );
QString fileName =
RiuFileDialogTools::getOpenFileName( nullptr, "Import Grid Calculation Expressions", defaultDir, "Xml File(*.xml);;All files(*.*)" );
RiuFileDialogTools::getOpenFileName( nullptr, "Import Grid Calculation Expressions", defaultDir, "Toml File(*.toml);;All files(*.*)" );
if ( fileName.isEmpty() ) return;
QFile importFile( fileName );
if ( !importFile.open( QIODevice::ReadOnly | QIODevice::Text ) )
auto [calculations, errorMessage] = RifGridCalculationImporter::readFromFile( fileName.toStdString() );
if ( !errorMessage.empty() )
{
RiaLogging::error( QString( "Import Grid Calculation Expressions : Could not open the file: %1" ).arg( fileName ) );
RiaLogging::errorInMessageBox( Riu3DMainWindowTools::mainWindowWidget(),
"Grid Calculation Import Error",
QString::fromStdString( errorMessage ) );
return;
}
QTextStream stream( &importFile );
QString objectAsText = stream.readAll();
auto proj = RimProject::current();
auto calcColl = proj->gridCalculationCollection();
@ -71,14 +74,32 @@ void RicImportGridCalculationExpressionsFeature::onActionTriggered( bool isCheck
auto eclCases = RimEclipseCaseTools::allEclipseGridCases();
if ( !eclCases.empty() ) firstCase = eclCases.front();
RimGridCalculationCollection tmp;
tmp.xmlCapability()->readObjectFromXmlString( objectAsText, caf::PdmDefaultObjectFactory::instance() );
for ( auto calc : tmp.calculations() )
for ( auto calc : calculations )
{
auto gridCalculation = dynamic_cast<RimGridCalculation*>( calcColl->addCalculationCopy( calc ) );
if ( gridCalculation && firstCase )
bool addDefaultExpression = false;
auto gridCalculation = dynamic_cast<RimGridCalculation*>( calcColl->addCalculation( addDefaultExpression ) );
if ( gridCalculation )
{
gridCalculation->assignEclipseCaseForNullPointers( firstCase );
gridCalculation->setExpression( QString::fromStdString( calc.expression ) );
gridCalculation->setDescription( QString::fromStdString( calc.description ) );
gridCalculation->setUnit( QString::fromStdString( calc.unit ) );
for ( auto var : calc.variables )
{
auto variable = dynamic_cast<RimGridCalculationVariable*>( gridCalculation->addVariable( QString::fromStdString( var.name ) ) );
RimEclipseResultAddress address;
RiaDefines::ResultCatType myEnum =
caf::AppEnum<RiaDefines::ResultCatType>::fromText( QString::fromStdString( var.resultType ) );
address.setEclipseCase( firstCase );
address.setResultName( QString::fromStdString( var.resultVariable ) );
address.setResultType( myEnum );
variable->setEclipseResultAddress( address );
}
if ( firstCase )
{
gridCalculation->assignEclipseCaseForNullPointers( firstCase );
}
}
}

View File

@ -89,6 +89,9 @@ set(SOURCE_GROUP_HEADER_FILES
${CMAKE_CURRENT_LIST_DIR}/RifFaultReactivationModelExporter.h
${CMAKE_CURRENT_LIST_DIR}/RifThermalToStimPlanFractureXmlOutput.h
${CMAKE_CURRENT_LIST_DIR}/RifEclipseSummaryAddressDefines.h
${CMAKE_CURRENT_LIST_DIR}/RifGridCalculation.h
${CMAKE_CURRENT_LIST_DIR}/RifGridCalculationImporter.h
${CMAKE_CURRENT_LIST_DIR}/RifGridCalculationExporter.h
)
set(SOURCE_GROUP_SOURCE_FILES
@ -179,6 +182,8 @@ set(SOURCE_GROUP_SOURCE_FILES
${CMAKE_CURRENT_LIST_DIR}/RifFaultReactivationModelExporter.cpp
${CMAKE_CURRENT_LIST_DIR}/RifThermalToStimPlanFractureXmlOutput.cpp
${CMAKE_CURRENT_LIST_DIR}/RifEclipseSummaryAddressDefines.cpp
${CMAKE_CURRENT_LIST_DIR}/RifGridCalculationImporter.cpp
${CMAKE_CURRENT_LIST_DIR}/RifGridCalculationExporter.cpp
)
list(APPEND CODE_HEADER_FILES ${SOURCE_GROUP_HEADER_FILES})

View File

@ -0,0 +1,40 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2023- 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 <string>
#include <vector>
//==================================================================================================
//
//==================================================================================================
struct RifGridCalculationVariable
{
std::string name;
std::string resultType;
std::string resultVariable;
};
struct RifGridCalculation
{
std::string description;
std::string expression;
std::string unit;
std::vector<RifGridCalculationVariable> variables;
};

View File

@ -0,0 +1,72 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2023- 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 "RifGridCalculationExporter.h"
#include <fstream>
#include <tomlplusplus/toml.hpp>
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::pair<bool, std::string> RifGridCalculationExporter::writeToFile( const std::vector<RifGridCalculation>& calculations,
const std::string& filePath )
{
std::ofstream stream( filePath );
if ( !stream.good() ) return { false, "Unable to open file: " + filePath };
return writeToStream( calculations, stream );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::pair<bool, std::string> RifGridCalculationExporter::writeToStream( const std::vector<RifGridCalculation>& calculations,
std::ostream& stream )
{
auto calculationsVector = toml::array();
for ( auto calculation : calculations )
{
auto variablesVector = toml::array{};
for ( auto variable : calculation.variables )
{
variablesVector.push_back( toml::table{
{ "name", variable.name },
{ "variable", variable.resultVariable },
{ "type", variable.resultType },
} );
}
calculationsVector.push_back( toml::table{
{ "description", calculation.description },
{ "expression", calculation.expression },
{ "unit", calculation.unit },
{ "variables", variablesVector },
} );
}
auto tbl = toml::table{
{ "grid-calculation", calculationsVector },
};
stream << tbl;
return { stream.good(), "" };
}

View File

@ -0,0 +1,34 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2023- 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 "RifGridCalculation.h"
#include <string>
#include <vector>
//==================================================================================================
//
//==================================================================================================
class RifGridCalculationExporter
{
public:
static std::pair<bool, std::string> writeToFile( const std::vector<RifGridCalculation>& calculations, const std::string& filePath );
static std::pair<bool, std::string> writeToStream( const std::vector<RifGridCalculation>& calculations, std::ostream& stream );
};

View File

@ -0,0 +1,81 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2023- 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 "RifGridCalculationImporter.h"
#include <fstream>
#include <tomlplusplus/toml.hpp>
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::pair<std::vector<RifGridCalculation>, std::string> RifGridCalculationImporter::readFromFile( const std::string& filePath )
{
std::ifstream stream( filePath );
if ( !stream.good() ) return { {}, "Unable to open file: " + filePath };
return readFromStream( stream );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::pair<std::vector<RifGridCalculation>, std::string> RifGridCalculationImporter::readFromStream( std::istream& stream )
{
toml::table tbl = toml::parse( stream );
auto calculationsVector = tbl["grid-calculation"];
std::vector<RifGridCalculation> calculations;
if ( toml::array* arr = calculationsVector.as_array() )
{
for ( auto&& a : *arr )
{
RifGridCalculation calculation;
if ( toml::table* calc = a.as_table() )
{
calculation.description = calc->at_path( "description" ).as_string()->value_or( "" );
calculation.expression = calc->at_path( "expression" ).as_string()->value_or( "" );
calculation.unit = calc->at_path( "unit" ).as_string()->value_or( "" );
if ( toml::array* vars = calc->at_path( "variables" ).as_array() )
{
std::vector<RifGridCalculationVariable> variables;
for ( auto&& v : *vars )
{
if ( toml::table* var = v.as_table() )
{
RifGridCalculationVariable variable;
variable.name = var->at_path( "name" ).as_string()->value_or( "" );
variable.resultType = var->at_path( "type" ).as_string()->value_or( "" );
variable.resultVariable = var->at_path( "variable" ).as_string()->value_or( "" );
variables.push_back( variable );
}
}
calculation.variables = variables;
}
calculations.push_back( calculation );
}
}
}
return { calculations, "" };
}

View File

@ -0,0 +1,34 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2023- 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 "RifGridCalculation.h"
#include <string>
#include <vector>
//==================================================================================================
//
//==================================================================================================
class RifGridCalculationImporter
{
public:
static std::pair<std::vector<RifGridCalculation>, std::string> readFromFile( const std::string& filePath );
static std::pair<std::vector<RifGridCalculation>, std::string> readFromStream( std::istream& stream );
};

View File

@ -64,11 +64,12 @@ public:
std::vector<RimEclipseCase*> inputCases() const;
RimGridCalculationVariable* createVariable() override;
protected:
void onChildrenUpdated( caf::PdmChildArrayFieldHandle* childArray, std::vector<caf::PdmObjectHandle*>& updatedObjects ) override;
RimGridCalculationVariable* createVariable() override;
std::pair<bool, QString> validateVariables();
std::pair<bool, QString> validateVariables();
std::vector<double> getInputVectorForVariable( RimGridCalculationVariable* v,
size_t tsId,

View File

@ -178,6 +178,14 @@ QString RimUserDefinedCalculation::unitName() const
return m_unit;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimUserDefinedCalculation::setUnit( const QString& unit )
{
m_unit = unit;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------

View File

@ -54,10 +54,10 @@ public:
std::vector<RimUserDefinedCalculationVariable*> allVariables() const;
void setExpression( const QString& expr );
QString expression() const;
QString unitName() const;
void setExpression( const QString& expr );
QString expression() const;
void setUnit( const QString& unit );
QString unitName() const;
bool parseExpression();
virtual bool preCalculate() const;
virtual bool calculate() = 0;
@ -71,6 +71,8 @@ public:
QString shortName() const;
RimUserDefinedCalculationVariable* addVariable( const QString& name );
protected:
virtual RimUserDefinedCalculationVariable* createVariable() = 0;
@ -79,7 +81,6 @@ protected:
void defineEditorAttribute( const caf::PdmFieldHandle* field, QString uiConfigName, caf::PdmUiEditorAttribute* attribute ) override;
RimUserDefinedCalculationVariable* findByName( const QString& name ) const;
RimUserDefinedCalculationVariable* addVariable( const QString& name );
void deleteVariable( RimUserDefinedCalculationVariable* calcVariable );
virtual QString buildCalculationName() const;

View File

@ -40,15 +40,18 @@ RimUserDefinedCalculationCollection::RimUserDefinedCalculationCollection()
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimUserDefinedCalculation* RimUserDefinedCalculationCollection::addCalculation()
RimUserDefinedCalculation* RimUserDefinedCalculationCollection::addCalculation( bool addDefaultExpression )
{
RimUserDefinedCalculation* calculation = createCalculation();
assignCalculationIdToCalculation( calculation );
QString varName = QString( "Calculation_%1" ).arg( calculation->id() );
calculation->setDescription( varName );
calculation->setExpression( varName + " := x + y" );
calculation->parseExpression();
if ( addDefaultExpression )
{
QString varName = QString( "Calculation_%1" ).arg( calculation->id() );
calculation->setDescription( varName );
calculation->setExpression( varName + " := x + y" );
calculation->parseExpression();
}
m_calculations.push_back( calculation );

View File

@ -37,7 +37,7 @@ class RimUserDefinedCalculationCollection : public caf::PdmObject
public:
RimUserDefinedCalculationCollection();
RimUserDefinedCalculation* addCalculation();
RimUserDefinedCalculation* addCalculation( bool addDefaultExpression = true );
RimUserDefinedCalculation* addCalculationCopy( const RimUserDefinedCalculation* sourceCalculation );
void deleteCalculation( RimUserDefinedCalculation* calculation );
std::vector<RimUserDefinedCalculation*> calculations() const;

View File

@ -98,6 +98,7 @@ set(SOURCE_GROUP_SOURCE_FILES
${CMAKE_CURRENT_LIST_DIR}/RiaNumericalTools-Test.cpp
${CMAKE_CURRENT_LIST_DIR}/opm-import-well-data-Test.cpp
${CMAKE_CURRENT_LIST_DIR}/RifInpExportTools-Test.cpp
${CMAKE_CURRENT_LIST_DIR}/RifGridCalculationIO-Test.cpp
)
if(RESINSIGHT_ENABLE_GRPC)

View File

@ -0,0 +1,67 @@
#include "gtest/gtest.h"
#include "RiaTestDataDirectory.h"
#include "RifGridCalculation.h"
#include "RifGridCalculationExporter.h"
#include "RifGridCalculationImporter.h"
static const std::string GRID_CALCULATION_DIR = std::string( TEST_DATA_DIR ) + std::string( "/RifGridCalculationIO/" );
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
TEST( RifGridCalculationIO, importAndExport )
{
std::string fileName = GRID_CALCULATION_DIR + "calculations.toml";
RifGridCalculation calc1;
calc1.description = "My first calculation";
calc1.expression = "answer = a + b";
calc1.unit = "meter";
RifGridCalculationVariable variable1;
variable1.name = "a";
variable1.resultVariable = "PRESSURE";
variable1.resultType = "DYNAMIC_NATIVE";
RifGridCalculationVariable variable2;
variable2.name = "b";
variable2.resultVariable = "PORO";
variable2.resultType = "STATIC_NATIVE";
calc1.variables = { variable1, variable2 };
RifGridCalculation calc2;
calc2.description = "My second calculation";
calc2.expression = "answer = x + y";
calc2.unit = "meter";
RifGridCalculationVariable variable3;
variable3.name = "x";
variable3.resultVariable = "PRESSURE";
variable3.resultType = "DYNAMIC_NATIVE";
RifGridCalculationVariable variable4;
variable4.name = "y";
variable4.resultVariable = "PORO";
variable4.resultType = "STATIC_NATIVE";
calc2.variables = { variable3, variable4 };
std::vector<RifGridCalculation> calculations = { calc1, calc2 };
std::stringstream stream;
auto [isOk, errorMessage] = RifGridCalculationExporter::writeToStream( calculations, stream );
EXPECT_TRUE( isOk );
EXPECT_TRUE( errorMessage.empty() );
auto [importedCalculations, importErrorMessage] = RifGridCalculationImporter::readFromStream( stream );
ASSERT_EQ( calculations.size(), importedCalculations.size() );
ASSERT_EQ( calculations[0].description, importedCalculations[0].description );
ASSERT_EQ( calculations[0].expression, importedCalculations[0].expression );
ASSERT_EQ( calculations[0].unit, importedCalculations[0].unit );
ASSERT_EQ( calculations[0].variables.size(), importedCalculations[0].variables.size() );
for ( size_t v = 0; v < calculations[0].variables.size(); v++ )
{
ASSERT_EQ( calculations[0].variables[v].name, importedCalculations[0].variables[v].name );
ASSERT_EQ( calculations[0].variables[v].resultType, importedCalculations[0].variables[v].resultType );
ASSERT_EQ( calculations[0].variables[v].resultVariable, importedCalculations[0].variables[v].resultVariable );
}
}

View File

@ -49,6 +49,7 @@ if(RESINSIGHT_VCPKG_AUTO_INSTALL)
endif()
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
@ -614,6 +615,12 @@ set_property(
"Thirdparty"
)
# ##############################################################################
# toml++
# ##############################################################################
add_subdirectory(ThirdParty/tomlplusplus)
# ##############################################################################
# Thirdparty libraries are put in ThirdParty solution folder
# ##############################################################################

1
ThirdParty/tomlplusplus vendored Submodule

@ -0,0 +1 @@
Subproject commit d8fa9a1fddc90254cac2366dde23f0b613bc1280