diff --git a/ApplicationCode/Application/Tools/RiaQDateTimeTools.cpp b/ApplicationCode/Application/Tools/RiaQDateTimeTools.cpp index 614fe25021..6a54e1d2da 100644 --- a/ApplicationCode/Application/Tools/RiaQDateTimeTools.cpp +++ b/ApplicationCode/Application/Tools/RiaQDateTimeTools.cpp @@ -43,40 +43,43 @@ const DateTimeSpan RiaQDateTimeTools::TIMESPAN_DECADE = DateTimeSpan( 10, 0, 0 namespace caf { +// clang-format off + template <> void caf::AppEnum::setUp() { - addItem( RiaQDateTimeTools::DATE_FORMAT_NONE, "NO_DATE", "No Date" ); - addItem( RiaQDateTimeTools::DATE_FORMAT_YEAR, "YEAR", "Year Only" ); - addItem( RiaQDateTimeTools::DATE_FORMAT_YEAR_MONTH, "YEAR_MONTH", "Year and Month" ); - addItem( RiaQDateTimeTools::DATE_FORMAT_YEAR_MONTH_DAY, "YEAR_MONTH_DAY", "Year, Month and Day" ); + addItem( RiaQDateTimeTools::DATE_FORMAT_NONE, "NO_DATE", "No Date" ); + addItem( RiaQDateTimeTools::DATE_FORMAT_YEAR, "YEAR", "Year Only" ); + addItem( RiaQDateTimeTools::DATE_FORMAT_YEAR_MONTH, "YEAR_MONTH", "Year and Month" ); + addItem( RiaQDateTimeTools::DATE_FORMAT_YEAR_MONTH_DAY, "YEAR_MONTH_DAY", "Year, Month and Day" ); setDefault( RiaQDateTimeTools::DATE_FORMAT_YEAR_MONTH_DAY ); } template <> void caf::AppEnum::setUp() { - addItem( RiaQDateTimeTools::TIME_FORMAT_NONE, "NO_TIME", "No Time of Day" ); - addItem( RiaQDateTimeTools::TIME_FORMAT_HOUR, "HOUR", "Hour Only" ); - addItem( RiaQDateTimeTools::TIME_FORMAT_HOUR_MINUTE, "HOUR_MINUTE", "Hour and Minute" ); - addItem( RiaQDateTimeTools::TIME_FORMAT_HOUR_MINUTE_SECOND, "HOUR_MINUTE_SECONDS", "Hour, Minutes and Seconds" ); + addItem( RiaQDateTimeTools::TIME_FORMAT_NONE, "NO_TIME", "No Time of Day" ); + addItem( RiaQDateTimeTools::TIME_FORMAT_HOUR, "HOUR", "Hour Only" ); + addItem( RiaQDateTimeTools::TIME_FORMAT_HOUR_MINUTE, "HOUR_MINUTE", "Hour and Minute" ); + addItem( RiaQDateTimeTools::TIME_FORMAT_HOUR_MINUTE_SECOND, "HOUR_MINUTE_SECONDS", "Hour, Minutes and Seconds" ); setDefault( RiaQDateTimeTools::TIME_FORMAT_NONE ); } template <> void caf::AppEnum::setUp() { - addItem( RiaQDateTimeTools::DateTimePeriod::NONE, "NONE", "None" ); - addItem( RiaQDateTimeTools::DateTimePeriod::DAY, "DAY", "Day" ); - addItem( RiaQDateTimeTools::DateTimePeriod::WEEK, "WEEK", "Week" ); - addItem( RiaQDateTimeTools::DateTimePeriod::MONTH, "MONTH", "Month" ); - addItem( RiaQDateTimeTools::DateTimePeriod::QUARTER, "QUARTER", "Quarter," ); - addItem( RiaQDateTimeTools::DateTimePeriod::HALFYEAR, "HALFYEAR", "Half Year" ); - addItem( RiaQDateTimeTools::DateTimePeriod::YEAR, "YEAR,", "Year," ); - addItem( RiaQDateTimeTools::DateTimePeriod::DECADE, "DECADE", "Decade" ); + addItem( RiaQDateTimeTools::DateTimePeriod::NONE, "NONE", "None" ); + addItem( RiaQDateTimeTools::DateTimePeriod::DAY, "DAY", "Day" ); + addItem( RiaQDateTimeTools::DateTimePeriod::WEEK, "WEEK", "Week" ); + addItem( RiaQDateTimeTools::DateTimePeriod::MONTH, "MONTH", "Month" ); + addItem( RiaQDateTimeTools::DateTimePeriod::QUARTER, "QUARTER", "Quarter" ); + addItem( RiaQDateTimeTools::DateTimePeriod::HALFYEAR, "HALFYEAR", "Half Year" ); + addItem( RiaQDateTimeTools::DateTimePeriod::YEAR, "YEAR", "Year" ); + addItem( RiaQDateTimeTools::DateTimePeriod::DECADE, "DECADE", "Decade" ); setDefault( RiaQDateTimeTools::DateTimePeriod::NONE ); } +// clang-format on } // namespace caf //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationCode/Commands/RicImportSummaryCasesFeature.cpp b/ApplicationCode/Commands/RicImportSummaryCasesFeature.cpp index e0eb26ebb8..f7091eeaac 100644 --- a/ApplicationCode/Commands/RicImportSummaryCasesFeature.cpp +++ b/ApplicationCode/Commands/RicImportSummaryCasesFeature.cpp @@ -198,7 +198,6 @@ bool RicImportSummaryCasesFeature::createSummaryCasesFromFiles( const QStringLis { QString errorMessage = fileSelector.createCombinedErrorMessage(); RiaLogging::error( errorMessage ); - QMessageBox::warning( nullptr, QString( "Problem Importing Summary Case File(s)" ), errorMessage ); } return true; diff --git a/ApplicationCode/GrpcInterface/Python/rips/PythonExamples/summary_vectors.py b/ApplicationCode/GrpcInterface/Python/rips/PythonExamples/summary_vectors.py index 05a988409e..b55e0ebc23 100644 --- a/ApplicationCode/GrpcInterface/Python/rips/PythonExamples/summary_vectors.py +++ b/ApplicationCode/GrpcInterface/Python/rips/PythonExamples/summary_vectors.py @@ -1,18 +1,31 @@ import rips +import time resinsight = rips.Instance.find() project = resinsight.project -summary_cases = project.descendants(rips.SummaryCase) -# Assumes at least one summery case loaded -firstCase = summary_cases[0] +# Use the following commented lines to import a file from disk +# filename = "path/to/file/1_R001_REEK-0.SMSPEC" +# summary_case = project.import_summary_case(filename) + +# Assumes at least one summery case loaded with case_id 1 +summary_case = project.summary_case(1) +if summary_case is None: + print("No summary case found") + exit() vector_name = "FOPT" -summary_data = firstCase.summary_vector_values(vector_name) +summary_data = summary_case.summary_vector_values(vector_name) print("Data for summary vector " + vector_name) print(summary_data.values) -time_steps = firstCase.available_time_steps() +time_steps = summary_case.available_time_steps() print(time_steps.values) + +summary_data_sampled = summary_case.resample_values("FOPT", "QUARTER") +print("\nResampled data") + +for t, value in zip(summary_data_sampled.time_steps, summary_data_sampled.values): + print(time.strftime("%a, %d %b %Y ", time.gmtime(t)) + " | " + str(value)) diff --git a/ApplicationCode/GrpcInterface/Python/rips/case.py b/ApplicationCode/GrpcInterface/Python/rips/case.py index 79afb1ef20..2f983a3af2 100644 --- a/ApplicationCode/GrpcInterface/Python/rips/case.py +++ b/ApplicationCode/GrpcInterface/Python/rips/case.py @@ -5,6 +5,22 @@ """ Module containing the Case class + +ResInsight Case class + +Operate on a ResInsight case specified by a Case Id integer. +Not meant to be constructed separately but created by one of the following +methods in Project: loadCase, case, allCases, selectedCases + +Attributes: + id (int): Case Id corresponding to case Id in ResInsight project. + name (str): Case name + group_id (int): Case Group id + chunkSize(int): The size of each chunk during value streaming. + A good chunk size is 64KiB = 65536B. + Meaning the ideal number of doubles would be 8192. + However we need overhead space, so the default is 8160. + This leaves 256B for overhead. """ import builtins @@ -29,22 +45,6 @@ from rips.view import View from rips.generated.pdm_objects import WellBoreStabilityPlot, WbsParameters from rips.simulation_well import SimulationWell -"""ResInsight case class - -Operate on a ResInsight case specified by a Case Id integer. -Not meant to be constructed separately but created by one of the following -methods in Project: loadCase, case, allCases, selectedCases - -Attributes: - id (int): Case Id corresponding to case Id in ResInsight project. - name (str): Case name - group_id (int): Case Group id - chunkSize(int): The size of each chunk during value streaming. - A good chunk size is 64KiB = 65536B. - Meaning the ideal number of doubles would be 8192. - However we need overhead space, so the default is 8160. - This leaves 256B for overhead. -""" @add_method(Case) def __custom_init__(self, pb2_object, channel): self.__case_stub = Case_pb2_grpc.CaseStub(self._channel) @@ -102,18 +102,21 @@ def __generate_property_input_chunks(self, array, parameters): @add_method(Case) def grid(self, index): - """Get Grid of a given index. Returns a rips Grid object + """Get Grid of a given index Arguments: index (int): The grid index - Returns: Grid object + Returns: :class:`rips.grid.Grid` """ return Grid(index, self, self.channel()) @add_method(Case) def grids(self): - """Get a list of all rips Grid objects in the case""" + """Get a list of all rips Grid objects in the case + + Returns: List of :class:`rips.grid.Grid` + """ grid_list = [] for i in range(0, self.__grid_count()): grid_list.append(Grid(i, self, self.channel())) @@ -870,8 +873,8 @@ def import_formation_names(self, formation_files=None): elif isinstance(formation_files, str): formation_files = [formation_files] - res = self._execute_command(importFormationNames=Cmd.ImportFormationNamesRequest(formationFiles=formation_files, - applyToCaseId=self.id)) + self._execute_command(importFormationNames=Cmd.ImportFormationNamesRequest(formationFiles=formation_files, + applyToCaseId=self.id)) @add_method(Case) def simulation_wells(self): diff --git a/ApplicationCode/GrpcInterface/Python/rips/pdmobject.py b/ApplicationCode/GrpcInterface/Python/rips/pdmobject.py index b6b5ddb14c..c58811f3ac 100644 --- a/ApplicationCode/GrpcInterface/Python/rips/pdmobject.py +++ b/ApplicationCode/GrpcInterface/Python/rips/pdmobject.py @@ -366,6 +366,9 @@ def _call_pdm_method(self, method_name, **kwargs): pb2_object = self._pdm_object_stub.CallPdmObjectMethod(request) child_class_definition = class_from_keyword(pb2_object.class_keyword) + if child_class_definition is None: + return None + pdm_object = child_class_definition(pb2_object=pb2_object, channel=self.channel()) return pdm_object diff --git a/ApplicationCode/GrpcInterface/Python/rips/project.py b/ApplicationCode/GrpcInterface/Python/rips/project.py index 305d87f0ca..f3013a38e0 100644 --- a/ApplicationCode/GrpcInterface/Python/rips/project.py +++ b/ApplicationCode/GrpcInterface/Python/rips/project.py @@ -341,5 +341,6 @@ def import_formation_names(self, formation_files=None): elif isinstance(formation_files, str): formation_files = [formation_files] - res = self._execute_command(importFormationNames=Cmd.ImportFormationNamesRequest(formationFiles=formation_files, + self._execute_command(importFormationNames=Cmd.ImportFormationNamesRequest(formationFiles=formation_files, applyToCaseId=-1)) + diff --git a/ApplicationCode/GrpcInterface/Python/rips/tests/test_summary_cases.py b/ApplicationCode/GrpcInterface/Python/rips/tests/test_summary_cases.py new file mode 100644 index 0000000000..3c79c41a70 --- /dev/null +++ b/ApplicationCode/GrpcInterface/Python/rips/tests/test_summary_cases.py @@ -0,0 +1,63 @@ +import sys +import os +import math + +sys.path.insert(1, os.path.join(sys.path[0], '../../')) +import rips + +import dataroot + +def test_summary_import_and_find(rips_instance, initialize_test): + casePath = dataroot.PATH + "/flow_diagnostics_test/SIMPLE_SUMMARY2.SMSPEC" + summary_case = rips_instance.project.import_summary_case(casePath) + assert(summary_case.id == 1) + + case_id = 234 + found_summary_case = rips_instance.project.summary_case(case_id) + assert(found_summary_case is None) + + correct_case_id = 1 + found_summary_case = rips_instance.project.summary_case(correct_case_id) + assert(found_summary_case is not None) + + rips_instance.project.close() + correct_case_id = 1 + found_summary_case = rips_instance.project.summary_case(correct_case_id) + assert(found_summary_case is None) + + +def test_summary_data(rips_instance, initialize_test): + casePath = dataroot.PATH + "/flow_diagnostics_test/SIMPLE_SUMMARY2.SMSPEC" + summary_case = rips_instance.project.import_summary_case(casePath) + assert(summary_case.id == 1) + + addresses = summary_case.available_addresses() + assert(len(addresses.values) == 343) + + summary_data = summary_case.summary_vector_values("FOPT") + assert(len(summary_data.values) == 60) + +def test_summary_resample(rips_instance, initialize_test): + casePath = dataroot.PATH + "/flow_diagnostics_test/SIMPLE_SUMMARY2.SMSPEC" + summary_case = rips_instance.project.import_summary_case(casePath) + assert(summary_case.id == 1) + + summary_data_sampled = summary_case.resample_values("FOPT", "NONE") + assert(len(summary_data_sampled.values) == 60) + assert(len(summary_data_sampled.time_steps) == 60) + + summary_data_sampled = summary_case.resample_values("FOPT", "DAY") + assert(len(summary_data_sampled.values) == 721) + assert(len(summary_data_sampled.time_steps) == 721) + + summary_data_sampled = summary_case.resample_values("FOPT", "MONTH") + assert(len(summary_data_sampled.values) == 24) + assert(len(summary_data_sampled.time_steps) == 24) + + summary_data_sampled = summary_case.resample_values("FOPT", "QUARTER") + assert(len(summary_data_sampled.values) == 8) + assert(len(summary_data_sampled.time_steps) == 8) + + summary_data_sampled = summary_case.resample_values("FOPT", "YEAR") + assert(len(summary_data_sampled.values) == 3) + assert(len(summary_data_sampled.time_steps) == 3) diff --git a/ApplicationCode/GrpcInterface/RiaGrpcPdmObjectService.cpp b/ApplicationCode/GrpcInterface/RiaGrpcPdmObjectService.cpp index e07eeabc5e..f8cbd8544b 100644 --- a/ApplicationCode/GrpcInterface/RiaGrpcPdmObjectService.cpp +++ b/ApplicationCode/GrpcInterface/RiaGrpcPdmObjectService.cpp @@ -538,6 +538,11 @@ grpc::Status RiaGrpcPdmObjectService::CallPdmObjectMethod( grpc::ServerContext* } else { + if ( method->isNullptrValidResult() ) + { + return grpc::Status::OK; + } + return grpc::Status( grpc::NOT_FOUND, "No result returned from Method" ); } } diff --git a/ApplicationCode/ProjectDataModelCommands/CMakeLists_files.cmake b/ApplicationCode/ProjectDataModelCommands/CMakeLists_files.cmake index 90e39ebab2..19398b7624 100644 --- a/ApplicationCode/ProjectDataModelCommands/CMakeLists_files.cmake +++ b/ApplicationCode/ProjectDataModelCommands/CMakeLists_files.cmake @@ -3,6 +3,7 @@ set (SOURCE_GROUP_HEADER_FILES ${CMAKE_CURRENT_LIST_DIR}/RimcSummaryPlotCollection.h ${CMAKE_CURRENT_LIST_DIR}/RimcSummaryCase.h ${CMAKE_CURRENT_LIST_DIR}/RimcSummaryResampleData.h +${CMAKE_CURRENT_LIST_DIR}/RimcProject.h ${CMAKE_CURRENT_LIST_DIR}/RimcDataContainerDouble.h ${CMAKE_CURRENT_LIST_DIR}/RimcDataContainerString.h @@ -13,6 +14,7 @@ set (SOURCE_GROUP_SOURCE_FILES ${CMAKE_CURRENT_LIST_DIR}/RimcSummaryPlotCollection.cpp ${CMAKE_CURRENT_LIST_DIR}/RimcSummaryCase.cpp ${CMAKE_CURRENT_LIST_DIR}/RimcSummaryResampleData.cpp +${CMAKE_CURRENT_LIST_DIR}/RimcProject.cpp ${CMAKE_CURRENT_LIST_DIR}/RimcDataContainerDouble.cpp ${CMAKE_CURRENT_LIST_DIR}/RimcDataContainerString.cpp diff --git a/ApplicationCode/ProjectDataModelCommands/RimcProject.cpp b/ApplicationCode/ProjectDataModelCommands/RimcProject.cpp new file mode 100644 index 0000000000..ce8e1646ac --- /dev/null +++ b/ApplicationCode/ProjectDataModelCommands/RimcProject.cpp @@ -0,0 +1,141 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// 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 +// for more details. +// +///////////////////////////////////////////////////////////////////////////////// + +#include "RimcProject.h" + +#include "RiaApplication.h" +#include "RicImportSummaryCasesFeature.h" + +#include "RimFileSummaryCase.h" +#include "RimProject.h" +#include "RimSummaryCase.h" +#include "RiuPlotMainWindow.h" + +#include "cafPdmFieldIOScriptability.h" + +#include + +CAF_PDM_OBJECT_METHOD_SOURCE_INIT( RimProject, RimProject_importSummaryCase, "importSummaryCase" ); + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RimProject_importSummaryCase::RimProject_importSummaryCase( caf::PdmObjectHandle* self ) + : caf::PdmObjectMethod( self ) +{ + CAF_PDM_InitObject( "Import Summary Case", "", "", "Import Summary Case" ); + CAF_PDM_InitScriptableFieldWithIONoDefault( &m_fileName, "FileName", "", "", "", "" ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +caf::PdmObjectHandle* RimProject_importSummaryCase::execute() +{ + QStringList summaryFileNames{m_fileName}; + std::vector newCases; + + if ( RicImportSummaryCasesFeature::createSummaryCasesFromFiles( summaryFileNames, &newCases ) ) + { + RicImportSummaryCasesFeature::addSummaryCases( newCases ); + + if ( RiaGuiApplication::isRunning() ) + { + RiuPlotMainWindow* mainPlotWindow = RiaGuiApplication::instance()->mainPlotWindow(); + if ( mainPlotWindow && !newCases.empty() ) + { + mainPlotWindow->updateSummaryPlotToolBar(); + } + } + + if ( newCases.size() == 1 ) + { + return newCases[0]; + } + } + + return nullptr; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool RimProject_importSummaryCase::resultIsPersistent() const +{ + return true; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::unique_ptr RimProject_importSummaryCase::defaultResult() const +{ + return std::unique_ptr( new RimFileSummaryCase ); +} + +CAF_PDM_OBJECT_METHOD_SOURCE_INIT( RimProject, RimProject_summaryCase, "summaryCase" ); + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RimProject_summaryCase::RimProject_summaryCase( caf::PdmObjectHandle* self ) + : caf::PdmObjectMethod( self ) +{ + CAF_PDM_InitObject( "Find Summary Case", "", "", "Find Summary Case" ); + CAF_PDM_InitScriptableFieldWithIONoDefault( &m_caseId, "CaseId", "", "", "", "" ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +caf::PdmObjectHandle* RimProject_summaryCase::execute() +{ + auto proj = RiaApplication::instance()->project(); + auto sumCases = proj->allSummaryCases(); + + for ( auto s : sumCases ) + { + if ( s->caseId() == m_caseId ) return s; + } + + return nullptr; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool RimProject_summaryCase::resultIsPersistent() const +{ + return true; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::unique_ptr RimProject_summaryCase::defaultResult() const +{ + return std::unique_ptr( new RimFileSummaryCase ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool RimProject_summaryCase::isNullptrValidResult() const +{ + return true; +} diff --git a/ApplicationCode/ProjectDataModelCommands/RimcProject.h b/ApplicationCode/ProjectDataModelCommands/RimcProject.h new file mode 100644 index 0000000000..75a7a8c38e --- /dev/null +++ b/ApplicationCode/ProjectDataModelCommands/RimcProject.h @@ -0,0 +1,64 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// 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 +// for more details. +// +///////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "cafPdmField.h" +#include "cafPdmObjectHandle.h" +#include "cafPdmObjectMethod.h" + +#include + +#include + +//================================================================================================== +/// +//================================================================================================== +class RimProject_importSummaryCase : public caf::PdmObjectMethod +{ + CAF_PDM_HEADER_INIT; + +public: + RimProject_importSummaryCase( caf::PdmObjectHandle* self ); + + caf::PdmObjectHandle* execute(); + bool resultIsPersistent() const override; + std::unique_ptr defaultResult() const override; + +private: + caf::PdmField m_fileName; +}; + +//================================================================================================== +/// +//================================================================================================== +class RimProject_summaryCase : public caf::PdmObjectMethod +{ + CAF_PDM_HEADER_INIT; + +public: + RimProject_summaryCase( caf::PdmObjectHandle* self ); + + caf::PdmObjectHandle* execute(); + bool resultIsPersistent() const override; + std::unique_ptr defaultResult() const override; + bool isNullptrValidResult() const override; + +private: + caf::PdmField m_caseId; +}; diff --git a/ApplicationCode/ProjectDataModelCommands/RimcSummaryCase.cpp b/ApplicationCode/ProjectDataModelCommands/RimcSummaryCase.cpp index d634bf1e71..842055abb4 100644 --- a/ApplicationCode/ProjectDataModelCommands/RimcSummaryCase.cpp +++ b/ApplicationCode/ProjectDataModelCommands/RimcSummaryCase.cpp @@ -225,22 +225,31 @@ caf::PdmObjectHandle* RimSummaryCase_resampleValues::execute() QString periodString = m_resamplingPeriod().trimmed(); RiaQDateTimeTools::DateTimePeriod period = RiaQDateTimeTools::DateTimePeriodEnum::fromText( periodString ); - RiaTimeHistoryCurveResampler resampler; - resampler.setCurveData( values, timeValues ); + auto dataObject = new RimcSummaryResampleData(); - if ( RiaSummaryTools::hasAccumulatedData( adr ) ) + if ( period != RiaQDateTimeTools::DateTimePeriod::NONE ) { - resampler.resampleAndComputePeriodEndValues( period ); + RiaTimeHistoryCurveResampler resampler; + resampler.setCurveData( values, timeValues ); + + if ( RiaSummaryTools::hasAccumulatedData( adr ) ) + { + resampler.resampleAndComputePeriodEndValues( period ); + } + else + { + resampler.resampleAndComputeWeightedMeanValues( period ); + } + + dataObject->m_timeValues = resampler.resampledTimeSteps(); + dataObject->m_doubleValues = resampler.resampledValues(); } else { - resampler.resampleAndComputeWeightedMeanValues( period ); + dataObject->m_timeValues = timeValues; + dataObject->m_doubleValues = values; } - auto dataObject = new RimcSummaryResampleData(); - dataObject->m_timeValues = resampler.resampledTimeSteps(); - dataObject->m_doubleValues = resampler.resampledValues(); - return dataObject; } diff --git a/Fwk/AppFwk/cafPdmScripting/cafPdmObjectMethod.h b/Fwk/AppFwk/cafPdmScripting/cafPdmObjectMethod.h index da2477ed17..b75230506b 100644 --- a/Fwk/AppFwk/cafPdmScripting/cafPdmObjectMethod.h +++ b/Fwk/AppFwk/cafPdmScripting/cafPdmObjectMethod.h @@ -61,9 +61,15 @@ class PdmObjectMethod : public PdmObject public: PdmObjectMethod( PdmObjectHandle* self ); + // The returned object contains the results of the method and is the responsibility of the caller. virtual PdmObjectHandle* execute() = 0; - virtual QString selfClassKeyword() const { return m_self->xmlCapability()->classKeyword(); } + + // Some execute() methods can return a null pointer as a valid return value. + // Return true here to allow this + virtual bool isNullptrValidResult() const { return false; } + + virtual QString selfClassKeyword() const { return m_self->xmlCapability()->classKeyword(); } // True if object is a persistent project tree item. False if the object is to be deleted on completion. virtual bool resultIsPersistent() const = 0; diff --git a/docs/rips/case.py b/docs/rips/case.py index 79afb1ef20..2f983a3af2 100644 --- a/docs/rips/case.py +++ b/docs/rips/case.py @@ -5,6 +5,22 @@ """ Module containing the Case class + +ResInsight Case class + +Operate on a ResInsight case specified by a Case Id integer. +Not meant to be constructed separately but created by one of the following +methods in Project: loadCase, case, allCases, selectedCases + +Attributes: + id (int): Case Id corresponding to case Id in ResInsight project. + name (str): Case name + group_id (int): Case Group id + chunkSize(int): The size of each chunk during value streaming. + A good chunk size is 64KiB = 65536B. + Meaning the ideal number of doubles would be 8192. + However we need overhead space, so the default is 8160. + This leaves 256B for overhead. """ import builtins @@ -29,22 +45,6 @@ from rips.view import View from rips.generated.pdm_objects import WellBoreStabilityPlot, WbsParameters from rips.simulation_well import SimulationWell -"""ResInsight case class - -Operate on a ResInsight case specified by a Case Id integer. -Not meant to be constructed separately but created by one of the following -methods in Project: loadCase, case, allCases, selectedCases - -Attributes: - id (int): Case Id corresponding to case Id in ResInsight project. - name (str): Case name - group_id (int): Case Group id - chunkSize(int): The size of each chunk during value streaming. - A good chunk size is 64KiB = 65536B. - Meaning the ideal number of doubles would be 8192. - However we need overhead space, so the default is 8160. - This leaves 256B for overhead. -""" @add_method(Case) def __custom_init__(self, pb2_object, channel): self.__case_stub = Case_pb2_grpc.CaseStub(self._channel) @@ -102,18 +102,21 @@ def __generate_property_input_chunks(self, array, parameters): @add_method(Case) def grid(self, index): - """Get Grid of a given index. Returns a rips Grid object + """Get Grid of a given index Arguments: index (int): The grid index - Returns: Grid object + Returns: :class:`rips.grid.Grid` """ return Grid(index, self, self.channel()) @add_method(Case) def grids(self): - """Get a list of all rips Grid objects in the case""" + """Get a list of all rips Grid objects in the case + + Returns: List of :class:`rips.grid.Grid` + """ grid_list = [] for i in range(0, self.__grid_count()): grid_list.append(Grid(i, self, self.channel())) @@ -870,8 +873,8 @@ def import_formation_names(self, formation_files=None): elif isinstance(formation_files, str): formation_files = [formation_files] - res = self._execute_command(importFormationNames=Cmd.ImportFormationNamesRequest(formationFiles=formation_files, - applyToCaseId=self.id)) + self._execute_command(importFormationNames=Cmd.ImportFormationNamesRequest(formationFiles=formation_files, + applyToCaseId=self.id)) @add_method(Case) def simulation_wells(self): diff --git a/docs/rips/generated/pdm_objects.py b/docs/rips/generated/pdm_objects.py index 9379917ccf..000739791a 100644 --- a/docs/rips/generated/pdm_objects.py +++ b/docs/rips/generated/pdm_objects.py @@ -5,6 +5,45 @@ class PdmObject: if PdmObject.__custom_init__ is not None: PdmObject.__custom_init__(self, pb2_object=pb2_object, channel=channel) +class DataContainerFloat(PdmObject): + """ + Attributes: + values (List of float): Float Values + """ + __custom_init__ = None #: Assign a custom init routine to be run at __init__ + + def __init__(self, pb2_object=None, channel=None): + self.values = [] + PdmObject.__init__(self, pb2_object, channel) + if DataContainerFloat.__custom_init__ is not None: + DataContainerFloat.__custom_init__(self, pb2_object=pb2_object, channel=channel) + +class DataContainerString(PdmObject): + """ + Attributes: + values (List of str): String Values + """ + __custom_init__ = None #: Assign a custom init routine to be run at __init__ + + def __init__(self, pb2_object=None, channel=None): + self.values = [] + PdmObject.__init__(self, pb2_object, channel) + if DataContainerString.__custom_init__ is not None: + DataContainerString.__custom_init__(self, pb2_object=pb2_object, channel=channel) + +class DataContainerTime(PdmObject): + """ + Attributes: + values (List of time): Time Values + """ + __custom_init__ = None #: Assign a custom init routine to be run at __init__ + + def __init__(self, pb2_object=None, channel=None): + self.values = [] + PdmObject.__init__(self, pb2_object, channel) + if DataContainerTime.__custom_init__ is not None: + DataContainerTime.__custom_init__(self, pb2_object=pb2_object, channel=channel) + class Case(PdmObject): """ The ResInsight base class for Cases @@ -62,6 +101,7 @@ class SummaryCase(PdmObject): Attributes: auto_shorty_name (str): Use Auto Display Name + id (int): Case ID short_name (str): Display Name summary_header_filename (str): Summary Header File """ @@ -69,12 +109,58 @@ class SummaryCase(PdmObject): def __init__(self, pb2_object=None, channel=None): self.auto_shorty_name = False + self.id = -1 self.short_name = "Display Name" self.summary_header_filename = "" PdmObject.__init__(self, pb2_object, channel) if SummaryCase.__custom_init__ is not None: SummaryCase.__custom_init__(self, pb2_object=pb2_object, channel=channel) + def available_addresses(self, ): + """ + + Arguments: + + Returns: + DataContainerString + """ + return self._call_pdm_method("availableAddresses") + + + def available_time_steps(self, ): + """ + + Arguments: + + Returns: + DataContainerTime + """ + return self._call_pdm_method("availableTimeSteps") + + + def resample_values(self, address=None, resampling_period=None): + """ + + Arguments: + address (str): Formatted address specifying the summary vector + resampling_period (str): Resampling Period + Returns: + ResampleData + """ + return self._call_pdm_method("resampleValues", address=address, resampling_period=resampling_period) + + + def summary_vector_values(self, address=None): + """ + Create a new Summary Plot + Arguments: + address (str): Formatted address specifying the summary vector + Returns: + DataContainerFloat + """ + return self._call_pdm_method("summaryVectorValues", address=address) + + class FileSummaryCase(SummaryCase): """ A Summary Case based on SMSPEC files @@ -212,6 +298,43 @@ class Project(PdmObject): if Project.__custom_init__ is not None: Project.__custom_init__(self, pb2_object=pb2_object, channel=channel) + def import_summary_case(self, file_name=None): + """ + Import Summary Case + Arguments: + file_name (str): + Returns: + FileSummaryCase + """ + return self._call_pdm_method("importSummaryCase", file_name=file_name) + + + def summary_case(self, case_id=None): + """ + Find Summary Case + Arguments: + case_id (int): + Returns: + FileSummaryCase + """ + return self._call_pdm_method("summaryCase", case_id=case_id) + + +class ResampleData(PdmObject): + """ + Attributes: + time_steps (List of time): Time Steps + values (List of float): Values + """ + __custom_init__ = None #: Assign a custom init routine to be run at __init__ + + def __init__(self, pb2_object=None, channel=None): + self.time_steps = [] + self.values = [] + PdmObject.__init__(self, pb2_object, channel) + if ResampleData.__custom_init__ is not None: + ResampleData.__custom_init__(self, pb2_object=pb2_object, channel=channel) + class EclipseView(View): """ The Eclipse 3d Reservoir View @@ -259,9 +382,9 @@ class EclipseResult(PdmObject): porosity_model_type (str): Porosity result_type (str): Type result_variable (str): Variable - selected_injector_tracers (str): Injector Tracers - selected_producer_tracers (str): Producer Tracers - selected_souring_tracers (str): Tracers + selected_injector_tracers (List of str): Injector Tracers + selected_producer_tracers (List of str): Producer Tracers + selected_souring_tracers (List of str): Tracers """ __custom_init__ = None #: Assign a custom init routine to be run at __init__ @@ -331,6 +454,25 @@ class GridCaseGroup(PdmObject): if GridCaseGroup.__custom_init__ is not None: GridCaseGroup.__custom_init__(self, pb2_object=pb2_object, channel=channel) +class SummaryCaseSubCollection(PdmObject): + """ + Attributes: + id (int): Ensemble ID + is_ensemble (str): Is Ensemble + name_count (str): Name + summary_collection_name (str): Name + """ + __custom_init__ = None #: Assign a custom init routine to be run at __init__ + + def __init__(self, pb2_object=None, channel=None): + self.id = -1 + self.is_ensemble = False + self.name_count = "Group" + self.summary_collection_name = "Group" + PdmObject.__init__(self, pb2_object, channel) + if SummaryCaseSubCollection.__custom_init__ is not None: + SummaryCaseSubCollection.__custom_init__(self, pb2_object=pb2_object, channel=channel) + class PlotWindow(ViewWindow): """ The Abstract base class for all MDI Windows in the Plot Window @@ -438,7 +580,7 @@ class WbsParameters(PdmObject): self.user_k0_fg = 0.75 self.user_k0_sh = 0.65 self.user_poisson_ratio = 0.35 - self.user_pp_non_reservoir = 1.05 + self.user_pp_non_reservoir = 1 self.user_ucs = 100 self.water_density = 1.03 PdmObject.__init__(self, pb2_object, channel) @@ -524,6 +666,9 @@ def class_dict(): classes = {} classes['Case'] = Case classes['CellColors'] = CellColors + classes['DataContainerFloat'] = DataContainerFloat + classes['DataContainerString'] = DataContainerString + classes['DataContainerTime'] = DataContainerTime classes['EclipseCase'] = EclipseCase classes['EclipseContourMap'] = EclipseContourMap classes['EclipseResult'] = EclipseResult @@ -540,9 +685,11 @@ def class_dict(): classes['Plot'] = Plot classes['PlotWindow'] = PlotWindow classes['Project'] = Project + classes['ResampleData'] = ResampleData classes['Reservoir'] = Reservoir classes['SimulationWell'] = SimulationWell classes['SummaryCase'] = SummaryCase + classes['SummaryCaseSubCollection'] = SummaryCaseSubCollection classes['SummaryPlot'] = SummaryPlot classes['SummaryPlotCollection'] = SummaryPlotCollection classes['View'] = View diff --git a/docs/rips/pdmobject.py b/docs/rips/pdmobject.py index 0d2ed58638..c58811f3ac 100644 --- a/docs/rips/pdmobject.py +++ b/docs/rips/pdmobject.py @@ -362,7 +362,15 @@ def _call_pdm_method(self, method_name, **kwargs): for key, value in kwargs.items(): pb2_params.parameters[snake_to_camel(key)] = self.__convert_to_grpc_value(value) request = PdmObject_pb2.PdmObjectMethodRequest(object=self._pb2_object, method=method_name, params=pb2_params) - return self._pdm_object_stub.CallPdmObjectMethod(request) + + pb2_object = self._pdm_object_stub.CallPdmObjectMethod(request) + + child_class_definition = class_from_keyword(pb2_object.class_keyword) + if child_class_definition is None: + return None + + pdm_object = child_class_definition(pb2_object=pb2_object, channel=self.channel()) + return pdm_object @add_method(PdmObject) def update(self): diff --git a/docs/rips/project.py b/docs/rips/project.py index 305d87f0ca..f3013a38e0 100644 --- a/docs/rips/project.py +++ b/docs/rips/project.py @@ -341,5 +341,6 @@ def import_formation_names(self, formation_files=None): elif isinstance(formation_files, str): formation_files = [formation_files] - res = self._execute_command(importFormationNames=Cmd.ImportFormationNamesRequest(formationFiles=formation_files, + self._execute_command(importFormationNames=Cmd.ImportFormationNamesRequest(formationFiles=formation_files, applyToCaseId=-1)) + diff --git a/docs/source/pdm_objects.rst b/docs/source/pdm_objects.rst index 024a466778..c42c70c88a 100644 --- a/docs/source/pdm_objects.rst +++ b/docs/source/pdm_objects.rst @@ -6,8 +6,6 @@ script writer does not have to look here, but the documentation is included for .. automodule:: rips.generated.pdm_objects :members: - :undoc-members: :show-inheritance: - :inherited-members: .. autoattribute:: rips.generated.pdm_objects \ No newline at end of file