Compare commits

..

102 Commits

Author SHA1 Message Date
Bård Skaflestad
4010399f08 Merge pull request #773 from tskille/convertECL
Fix issue with missing STARTSOL and ENDSOL headers when converting un…
2019-05-15 16:01:43 +02:00
Bård Skaflestad
5f43a18050 Merge pull request #758 from jalvestad/pvt-type-eclrst
Generalize eclipse compatible restart to a broad set of PVT-options
2019-05-15 13:44:33 +02:00
Torbjørn Skille
cf970df48f Fix issue with missing STARTSOL and ENDSOL headers when converting unified restart files 2019-05-15 10:25:55 +02:00
Atgeirr Flø Rasmussen
c1f4c69c30 Merge pull request #772 from blattms/alternative-clang-fix
Fixes clang linking error when using std::vector<bool>::const_reference.
2019-05-15 09:20:07 +02:00
Joakim Hove
0c70d71df8 Merge pull request #766 from joakim-hove/summary-eval
Split Summary::add_timestep in eval()
2019-05-15 07:02:57 +02:00
Joakim Hove
07b02ed151 Factor out eval() method from Summary::add_timestep() 2019-05-15 06:58:10 +02:00
Bård Skaflestad
2c7eb20726 Merge pull request #724 from jalvestad/cell-summary-props
Changes to provide Summary output of more Block properties
2019-05-14 18:30:36 +02:00
Markus Blatt
255ffc5bba Fixes clang linking error when using std::vector<bool>::const_reference.
Turns out that for clang this is no a bool and therefore a template
specialization is missing. We workaround this by some template (black) magic
that does the explicit instantiation for clang and for others adds another
unused instantiation for char.
2019-05-14 14:12:42 +02:00
Jostein Alvestad
d7e41a5ac0 Remove two tests for initalisation in conflict with more general model 2019-05-14 09:40:13 +02:00
Bård Skaflestad
d554334316 Merge pull request #768 from joakim-hove/summary-state-update
Summary state update
2019-05-13 18:22:02 +02:00
Jostein Alvestad
4989c6cfec Added change to improve code quality
Make sure that the changes made in PVTModel is not overwritten by
variousParam
2019-05-13 15:36:20 +02:00
Jostein Alvestad
c92d74107f Initial changes for allowing all pvt-type data for eclipse restart file 2019-05-13 15:36:19 +02:00
Jostein Alvestad
8126e8cb66 Added initial change to account for different PVT-types in ECL Restart file
Copied changes already made for INIT-file export to restart file export
for eclipse compatible restart file export. The changes apply to
LOGIHEAD and account for whether live oil or wet gas is present
2019-05-13 15:36:18 +02:00
Jostein Alvestad
ba1c30fd4a Added unit tests for grid block summary data variables 2019-05-13 15:29:57 +02:00
Jostein Alvestad
25f25d15bf Added unit tests to new grid block summary properties 2019-05-13 15:29:56 +02:00
Jostein Alvestad
bd58468930 changes to allow Summary output of additional block variables
Allow output of:
BOSAT
BWKR
BOKR
BKRO
BGKR
BKRG
BKRW
BWPC
BGPC
BVWAT
BWVIS
BVGAS
BGVIS
BVOIL
BOVIS

For this to work, appropriate changes to opm-simulators in
ecloutputblackoilmodule.hh needs to be included. This is
made in a separate branch since it belongs to a different repo.
2019-05-13 15:29:56 +02:00
Jostein Alvestad
546517784e Initial changes to add more block props to summary file
this  branch will add
BOSAT
BWKR
BKRW (=BWKR)
BOKR
BKRO (=BOKR)
BGKR
BKRG (=BGKR)
to summary file
2019-05-13 15:29:55 +02:00
Joakim Hove
9e288a9d78 Refactor SummaryState
- Change SummaryState::add() -> SummaryState::update(), the
   SummaryState::update() method is semantically aware of totals.

 - Add an internal variable to the SummaryState class to keep track of the
   elapsed simulation time.
2019-05-13 11:06:32 +02:00
Joakim Hove
294c8aa2f7 Merge pull request #767 from GitPaean/fixing_wconprod
clearning existing controls when handleWCONPROD
2019-05-12 11:58:37 +02:00
Kai Bao
de17c851bc clearning existing controls when handleWCONPROD 2019-05-11 14:07:15 +02:00
Joakim Hove
38f483ae37 Merge pull request #764 from akva2/janitoring
mark constructors explicit
2019-05-10 14:56:00 +02:00
Arne Morten Kvarving
9b9fc2ef37 mark constructors explicit 2019-05-10 12:43:02 +02:00
Joakim Hove
a8e7c03a4f Merge pull request #740 from joakim-hove/well-static
Add alternative well implementation Well2
2019-05-09 11:07:34 +02:00
Joakim Hove
1c82a8ad39 Add alternative well implementation Well2 2019-05-09 09:20:23 +02:00
Arne Morten Kvarving
0ddf4d675e Merge pull request #703 from akva2/compareECL
Use new ECL I/O classes in compareECL
2019-05-09 08:54:03 +02:00
Bård Skaflestad
bce535c19f Merge pull request #707 from bska/print-pvtfunc-to-init
Structurally Correct PVT Tables in INIT File (Part 2)
2019-05-08 21:24:32 +02:00
Bård Skaflestad
a265a99469 Init File LOGIHEAD: Correctly Set Constant Co Flag
We were simply defaulting this flag to fals for all runs.  While
using constant Co is uncommon for real field models, it does come up
in academic settings.

Pointy Hat: @bska
2019-05-08 11:24:02 +02:00
Bård Skaflestad
7835eeb142 Init File: Activate New PVT Table Normalisation
This commit replaces the existing calls to

    Tables::addPVTO()
    Tables::addPVTG()
    Tables::addPVTW()

with the new umbrella member function

    Tables::addPVTTables()

This, in turn, activates table normalisation for all PV{D,T}{G,O},
PVCDO, and PVTW keywords and produces ECLIPSE compatibly sized TAB
vector representations for these data sources.

As a prerequisite for this work, to enable users such as ResInsight
to correctly interpret the table contents, we need to ensure that
the contents of LOGIHEAD reflect the data sources used to form TAB.
As a consequence, we now control the contents of INTEHEAD, LOGIHEAD,
and DOUBHEAD directly from EclipseIO.cpp rather than from LibECL
library function ecl_init_file_fwrite_header().

We reuse header functionality from the restart files.  This changes
the size of the *HEAD vectors in the init file:

    Vector    Old  New
    INTEHEAD   95  411
    LOGIHEAD   80  121
    DOUBHEAD    1  229

which matches ECLIPSE 2017.2.  Note that this is taking a bit of a
shortcut, because there are items in restart files that differ from
those of the init file.  However, for the items that affect tables,
the items are the same in both init and restart files.  We will need
to refine this at a later point.
2019-05-08 11:24:02 +02:00
Bård Skaflestad
4b8743d10f Eclipse IO: Add Ability to Output vector<bool> Data
Needed to support dynamically controlling contents of LOGIHEAD
vector in the INIT file.
2019-05-08 11:24:02 +02:00
Bård Skaflestad
30733e4966 LogiHEAD: Add Protocol for Communicating PVT and Satfunc Models
This commit introduces two functions,

    LogiHEAD::pvtModel()
    LogiHEAD::saturationFunction()

that set LOGIHEAD flags pertaining to a subset of activated
features.  In particular, we add the ability to control whether or
not a particular run applies to a live/dead oil case, a wet/dry gas
case and/or whether or not a dead oil case is specified in terms of
constant oil compressibility (PVCDO keyword).  Saturation function
flags identify attributes of end-point scaling and directionally
dependent and/or reversible relative permeability functions.

Add unit tests to exercise these features.
2019-05-08 11:24:02 +02:00
Bård Skaflestad
9fe4b23dcb LogiHEAD: Identify Selection of Array Elements
Information mostly gleaned from LibECL's ecl_kw_magic.h.
2019-05-08 11:24:02 +02:00
Arne Morten Kvarving
eb30732444 Merge pull request #753 from tskille/fixESmry
Fixed defect in ESmry constructor.
2019-05-08 10:56:35 +02:00
Atgeirr Flø Rasmussen
e8dbf7d8ee Merge pull request #754 from joakim-hove/whistctl-unknown-well
Internalize a global WHISTCTL setting in the Schedule object
2019-05-03 10:37:55 +02:00
Joakim Hove
0660ced877 Merge pull request #752 from joakim-hove/region-wells-required
Add query function need_wells() in the Summary implementation
2019-05-03 07:18:24 +02:00
Kai Bao
ccd6a2b9da Merge pull request #751 from GitPaean/fixing_over_flow_of_c_string
fixing an overflow problem in EclOutput::make_doub_string
2019-05-02 21:35:13 +02:00
Joakim Hove
452742a6a2 Add query function need_wells() in the Summary implementation 2019-05-02 19:08:26 +02:00
Joakim Hove
0f3dabc3aa Internalize a global WHISTCTL setting in the Schedule object 2019-05-02 16:01:29 +02:00
Torbjørn Skille
49cc815136 Fixed defect in ESmry constructor. Fails when using class on Summary files generated from commercial simulator Eclipse 2019-05-02 10:32:18 +02:00
Kai Bao
a3da1e23a2 fixing an overflow problem in EclOutput::make_doub_string 2019-05-02 10:18:48 +02:00
Joakim Hove
7dba7a50cf Merge pull request #748 from GitPaean/test_WCONHIST_control_mode
modifying the ScheduleTests to handle control mode change with WCONHIST
2019-05-02 10:13:38 +02:00
Kai Bao
8687a8a709 modifying the ScheduleTests to handle control mode change with WCONHIST 2019-05-02 09:25:52 +02:00
Joakim Hove
92caf4f010 Merge pull request #747 from joakim-hove/grouptree<<
Add operator<< to GroupTree class + whitespace
2019-05-01 14:42:26 +02:00
Joakim Hove
894ad7a226 Add operator<< to GroupTree class + whitespace 2019-05-01 13:42:07 +02:00
Bård Skaflestad
a9775f6945 Merge pull request #746 from atgeirr/minor-fixes
Two small but critical fixes
2019-04-30 17:38:03 +02:00
Atgeirr Flø Rasmussen
559783d311 Bugfix: do not keep old control modes.
They will be treated as limits, and cause wrong behaviour.
2019-04-30 14:48:49 +02:00
Joakim Hove
b7b9697fc5 Merge pull request #745 from joakim-hove/upgrade-dune-module
Change version and label in dune.module file
2019-04-30 12:55:45 +02:00
Joakim Hove
35516f1b90 Change version and label in dune.module file 2019-04-30 12:54:45 +02:00
Atgeirr Flø Rasmussen
7a47f7cf2d Add missing standard library includes. 2019-04-30 11:39:21 +02:00
Joakim Hove
0466effc93 Merge pull request #725 from joakim-hove/uda-value
UDA values in the deck
2019-04-30 07:57:08 +02:00
Atgeirr Flø Rasmussen
bf6964522e Merge pull request #744 from bska/continuous-cumulatives-opm-ext-rst
Restart: Provide Continuous Cumulatives in OPM Extended Files
2019-04-29 14:30:40 +02:00
Bård Skaflestad
d08760a922 Restart: Provide Continuous Cumulatives in OPM Extended Files
This commit removes the restriction that cumulative totals are not
output in the case of creating OPM Extended Restart files.  There
was a reason why we did not do this when the new restart facility
was introduced, but that reason no longer applies.
2019-04-28 21:44:13 +02:00
Joakim Hove
b148769cc6 Internalize UDA values in the deck 2019-04-28 16:08:02 +02:00
Joakim Hove
656878d649 Add class UDAValue to hold UDA values from the Deck 2019-04-28 16:08:02 +02:00
Joakim Hove
8f7724409c UDA datatype can only be used with scalar input data 2019-04-28 16:08:02 +02:00
Joakim Hove
de96af682a Rename variable to increase readability 2019-04-28 16:08:02 +02:00
Joakim Hove
f4009fc6d8 Add default constructor for unit Dimension 2019-04-28 16:08:02 +02:00
Arne Morten Kvarving
9d8b08a8d8 Merge pull request #699 from akva2/new_ecl_io
New ECL I/O classes
2019-04-26 09:53:01 +02:00
Bård Skaflestad
605d7399a6 Merge pull request #742 from jalvestad/msw-rstout-corr
An improvement / correction of the writing of MSW data to Eclipse compatible restart file
2019-04-26 08:28:59 +02:00
Jostein Alvestad
58a039c04a Added a improvement to the handling of MSW data for writing to restart file
Improvement to handle more general MSW data  for writing to
Eclipse compatible restart file
2019-04-25 19:14:55 +02:00
Atgeirr Flø Rasmussen
e954836774 Merge pull request #741 from hnil/fixed_unset_return
fixed unset return value causing segfault in release build on ubuntu …
2019-04-25 08:29:52 +02:00
hnil
2d69121b5a fixed unset return value causing segfault in release build on ubuntu 18.10 2019-04-24 13:11:20 +02:00
Joakim Hove
1cde5a4879 Merge pull request #739 from joakim-hove/minor-minor
Misc minor changes: whitespace++
2019-04-23 08:32:02 +02:00
Joakim Hove
fffe259621 Misc minor changes: whitespace++ 2019-04-23 07:25:05 +02:00
Joakim Hove
b955ecf6b4 Merge pull request #737 from joakim-hove/process-segments
Add WellSegments::process() front-end method
2019-04-20 13:14:01 +02:00
Joakim Hove
5cfb91df24 Merge pull request #736 from joakim-hove/remove-branch-number
Remove unused member WellSegments::m_branch_number
2019-04-20 13:13:46 +02:00
Joakim Hove
a4bdba5841 Merge pull request #734 from joakim-hove/del-getwells
Use wellNames() instead of getWells()
2019-04-20 13:12:56 +02:00
Joakim Hove
dcdcc659b2 Merge pull request #738 from joakim-hove/segment-cmp-bug
Fix bug in WellSegments==
2019-04-20 12:59:22 +02:00
Joakim Hove
e833377bea Fix bug in WellSegments== 2019-04-20 10:55:22 +02:00
Joakim Hove
fbece57167 Add WellSegments::process() front-end method 2019-04-19 12:35:10 +02:00
Joakim Hove
54f45eaba7 Remove unused member WellSegments::m_branch_number 2019-04-19 12:03:01 +02:00
Joakim Hove
7aa55f86a0 Merge pull request #735 from joakim-hove/FLUXNUM
Add configuration for keyword FLUXTYPE
2019-04-19 12:02:30 +02:00
Joakim Hove
c3bd4b9e3a Add configuration for keyword FLUXTYPE 2019-04-19 10:57:23 +02:00
Joakim Hove
688edd6f63 Remove well iteration out of function 2019-04-19 08:08:22 +02:00
Joakim Hove
c1af5a313f Use wellNames() instead of getWells() 2019-04-19 07:24:10 +02:00
Joakim Hove
167a7a8c38 Merge pull request #733 from joakim-hove/use-wellname
Use wellname in Group::addWell()
2019-04-18 21:12:29 +02:00
Joakim Hove
abc674d40f Merge pull request #732 from joakim-hove/use-to_bool
Use DeckItem::to_bool( )
2019-04-18 21:11:49 +02:00
Joakim Hove
9c93a9349c Use wellname in Group::addWell() 2019-04-18 16:45:24 +02:00
Joakim Hove
44f8a5d94e Use DeckItem::to_bool( ) 2019-04-18 16:24:54 +02:00
Joakim Hove
7ef2d8b9a3 Merge pull request #729 from joakim-hove/dynamic-state-unique
Implement unique() method on DynamicState
2019-04-18 08:57:06 +02:00
Joakim Hove
efb8375bac Merge pull request #730 from joakim-hove/white-space
White space
2019-04-18 08:56:07 +02:00
Joakim Hove
eff44e4fdc White space 2019-04-18 08:29:10 +02:00
Joakim Hove
d5f240050a Implement unique() method on DynamicState 2019-04-18 08:09:31 +02:00
Joakim Hove
454a933b5a Merge pull request #727 from joakim-hove/get-child-wells
Remove Schedule::getWells(group, step)
2019-04-17 15:50:49 +02:00
Joakim Hove
5e76f624ef Remove Schedule::getWells(group, step) 2019-04-17 14:47:00 +02:00
Joakim Hove
14168f3ea6 Merge pull request #728 from joakim-hove/inline-ncwmax
Inline Schedule::getMaxNumCompletionsForWells() in test
2019-04-17 14:29:50 +02:00
Joakim Hove
b5dbc3d8e5 Delete unused code for ?CON serialization 2019-04-17 14:28:58 +02:00
Joakim Hove
34dfbee01b Merge pull request #726 from joakim-hove/parse-invalid-keyword
Add "size" : 1 attribute to CARFIN keyword
2019-04-17 07:20:27 +02:00
Joakim Hove
027be42f70 Add "size" : 1 attribute to CARFIN keyword 2019-04-17 06:38:34 +02:00
Bård Skaflestad
eef4cad54f Merge pull request #722 from blattms/remove-warnings-g++-8.3
Removes nearly all warnings experienced with g++-8.3
2019-04-12 23:09:12 +02:00
Markus Blatt
19ccc21786 Reintroduce overeagerly removed const qualifiers before function parameters.
Actually, the compiler does not complain about const in void func(const T type).
It just complains about const qualifiers before by-value return types.
2019-04-12 21:31:51 +02:00
Markus Blatt
521838dbdd reintroduce some const with elegance 2019-04-12 21:14:36 +02:00
Markus Blatt
393a543ed5 Removes nearly all warnings experienced with g++-8.3
Namely:
- unused parameter
- type qualifiers ignored ...
- catchinhg polymorphic type ... by value
2019-04-12 13:37:03 +02:00
Torbjørn Skille
135f2d491a remove unused summary comparison classes 2019-04-08 11:38:34 +02:00
Torbjørn Skille
fbfac1f797 update compareECL to use new ECL I/O classes 2019-04-08 11:38:34 +02:00
Torbjørn Skille
4768f72400 added: new utility convertECL
for converting between formatted and binary ecl files
2019-04-08 11:36:20 +02:00
Torbjørn Skille
db142f698b added: ESmry class for reading summary data from ECL files 2019-04-08 11:36:20 +02:00
Torbjørn Skille
c54cb2195b added: ERst class for reading restart simulation data from ECL files 2019-04-08 11:36:20 +02:00
Torbjørn Skille
24a8efb2e3 added: ERft class for reading RFT simulation data from ECL files 2019-04-08 11:36:20 +02:00
Torbjørn Skille
181eeffbdb added: EGrid class for reading GRID data from ECL files 2019-04-08 11:36:20 +02:00
Torbjørn Skille
46f2542487 finish class EclFile
this is a base class for ECL I/O classes
2019-04-08 11:36:20 +02:00
Torbjørn Skille
6d1b2f3f4f added: EclOutput class for writing ECL files 2019-04-08 11:36:20 +02:00
Torbjørn Skille
188f78f2d6 add some common utilities used by Ecl IO classes 2019-04-08 11:36:20 +02:00
130 changed files with 22093 additions and 4685 deletions

View File

@@ -158,24 +158,30 @@ endif()
# Build the compare utilities
if(ENABLE_ECL_INPUT)
add_library(testutil STATIC
examples/test_util/EclFile.cpp
examples/test_util/EclFilesComparator.cpp
examples/test_util/EclIntegrationTest.cpp
examples/test_util/EclOutput.cpp
examples/test_util/EclRegressionTest.cpp
examples/test_util/summaryComparator.cpp
examples/test_util/summaryIntegrationTest.cpp
examples/test_util/summaryRegressionTest.cpp)
target_link_libraries(testutil ecl)
add_executable(compareECL examples/test_util/compareECL.cpp)
target_link_libraries(compareECL testutil opmcommon)
examples/test_util/EclUtil.cpp
examples/test_util/EGrid.cpp
examples/test_util/ERft.cpp
examples/test_util/ERst.cpp
examples/test_util/ESmry.cpp)
foreach(target compareECL convertECL)
add_executable(${target} examples/test_util/${target}.cpp)
target_link_libraries(${target} testutil opmcommon)
install(TARGETS ${target} DESTINATION bin)
endforeach()
# Add the tests
set(_libs testutil opmcommon
${Boost_UNIT_TEST_FRAMEWORK_LIBRARY})
foreach(test test_compareSummary test_EclFilesComparator)
foreach(test test_EclFilesComparator test_EclIO test_EclRegressionTest
test_EGrid test_ERft test_ERst test_ESmry)
opm_add_test(${test} CONDITION ENABLE_ECL_INPUT
LIBRARIES ${_libs})
LIBRARIES ${_libs}
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}/tests)
endforeach()
install(TARGETS compareECL DESTINATION bin)
endif()
# Install build system files

View File

@@ -21,7 +21,6 @@
# the library needs it.
list (APPEND MAIN_SOURCE_FILES
examples/test_util/EclFile.cpp
src/opm/common/data/SimulationDataContainer.cpp
src/opm/common/OpmLog/CounterLog.cpp
src/opm/common/OpmLog/EclipsePRTLog.cpp
@@ -46,6 +45,7 @@ if(ENABLE_ECL_INPUT)
src/opm/parser/eclipse/Deck/DeckRecord.cpp
src/opm/parser/eclipse/Deck/DeckOutput.cpp
src/opm/parser/eclipse/Deck/Section.cpp
src/opm/parser/eclipse/Deck/UDAValue.cpp
src/opm/parser/eclipse/EclipseState/AquiferCT.cpp
src/opm/parser/eclipse/EclipseState/Aquifetp.cpp
src/opm/parser/eclipse/EclipseState/Aquancon.cpp
@@ -102,6 +102,7 @@ if(ENABLE_ECL_INPUT)
src/opm/parser/eclipse/EclipseState/Schedule/Tuning.cpp
src/opm/parser/eclipse/EclipseState/Schedule/Well/Connection.cpp
src/opm/parser/eclipse/EclipseState/Schedule/Well/Well.cpp
src/opm/parser/eclipse/EclipseState/Schedule/Well/Well2.cpp
src/opm/parser/eclipse/EclipseState/Schedule/Well/WellConnections.cpp
src/opm/parser/eclipse/EclipseState/Schedule/Well/WList.cpp
src/opm/parser/eclipse/EclipseState/Schedule/Well/WListManager.cpp
@@ -171,7 +172,6 @@ if(ENABLE_ECL_OUTPUT)
src/opm/output/eclipse/CreateDoubHead.cpp
src/opm/output/eclipse/CreateInteHead.cpp
src/opm/output/eclipse/CreateLogiHead.cpp
src/opm/output/eclipse/WellDataSerializers.cpp
src/opm/output/eclipse/DoubHEAD.cpp
src/opm/output/eclipse/EclipseGridInspector.cpp
src/opm/output/eclipse/EclipseIO.cpp
@@ -190,7 +190,6 @@ if(ENABLE_ECL_OUTPUT)
endif()
list (APPEND TEST_SOURCE_FILES
tests/test_EclFile.cpp
tests/test_calculateCellVol.cpp
tests/test_cmp.cpp
tests/test_cubic.cpp
@@ -290,8 +289,6 @@ if(ENABLE_ECL_OUTPUT)
tests/test_Wells.cpp
tests/test_WindowedArray.cpp
tests/test_writenumwells.cpp
tests/test_serialize_ICON.cpp
tests/test_serialize_SCON.cpp
)
endif()
@@ -320,6 +317,17 @@ endif()
list (APPEND EXAMPLE_SOURCE_FILES
)
if(ENABLE_ECL_INPUT)
list (APPEND TEST_DATA_FILES
tests/ECLFILE.INIT
tests/ECLFILE.FINIT
tests/SPE1CASE1.EGRID
tests/SPE1CASE1.RFT
tests/SPE1_TESTCASE.UNRST
tests/SPE1_TESTCASE.FUNRST
tests/SPE1CASE1.UNSMRY
tests/SPE1CASE1_RST60.SMSPEC
tests/SPE1CASE1_RST60.UNSMRY
)
list (APPEND EXAMPLE_SOURCE_FILES
examples/opmi.cpp
examples/opmpack.cpp
@@ -340,8 +348,6 @@ if(ENABLE_ECL_INPUT)
endif()
list( APPEND PUBLIC_HEADER_FILES
examples/test_util/EclFile.hpp
examples/test_util/data/EclIOdata.hpp
opm/common/ErrorMacros.hpp
opm/common/Exceptions.hpp
opm/common/data/SimulationDataContainer.hpp
@@ -501,6 +507,7 @@ if(ENABLE_ECL_INPUT)
opm/parser/eclipse/EclipseState/Schedule/Well/Connection.hpp
opm/parser/eclipse/EclipseState/Schedule/Well/Well.hpp
opm/parser/eclipse/EclipseState/Schedule/Well/WellInjectionProperties.hpp
opm/parser/eclipse/EclipseState/Schedule/Well/Well2.hpp
opm/parser/eclipse/EclipseState/Schedule/Well/WList.hpp
opm/parser/eclipse/EclipseState/Schedule/Well/WListManager.hpp
opm/parser/eclipse/EclipseState/Schedule/Well/WellEconProductionLimits.hpp
@@ -548,6 +555,7 @@ if(ENABLE_ECL_INPUT)
opm/parser/eclipse/Deck/DeckOutput.hpp
opm/parser/eclipse/Deck/DeckKeyword.hpp
opm/parser/eclipse/Deck/DeckRecord.hpp
opm/parser/eclipse/Deck/UDAValue.hpp
opm/parser/eclipse/RawDeck/StarToken.hpp
opm/parser/eclipse/RawDeck/RawEnums.hpp
opm/parser/eclipse/RawDeck/RawRecord.hpp
@@ -562,6 +570,7 @@ if(ENABLE_ECL_OUTPUT)
opm/output/eclipse/VectorItems/connection.hpp
opm/output/eclipse/VectorItems/group.hpp
opm/output/eclipse/VectorItems/intehead.hpp
opm/output/eclipse/VectorItems/logihead.hpp
opm/output/eclipse/VectorItems/msw.hpp
opm/output/eclipse/VectorItems/well.hpp
opm/output/eclipse/AggregateGroupData.hpp

View File

@@ -1,5 +1,6 @@
set(genkw_SOURCES src/opm/json/JsonObject.cpp
src/opm/parser/eclipse/Parser/createDefaultKeywordList.cpp
src/opm/parser/eclipse/Deck/UDAValue.cpp
src/opm/parser/eclipse/Deck/Deck.cpp
src/opm/parser/eclipse/Deck/DeckItem.cpp
src/opm/parser/eclipse/Deck/DeckKeyword.cpp

2
debian/changelog vendored
View File

@@ -1,4 +1,4 @@
opm-common (2019.04-rc2-1~xenial) xenial; urgency=medium
opm-common (2019.04-pre~xenial) xenial; urgency=medium
* New release

View File

@@ -5,8 +5,8 @@
Module: opm-common
Description: Open Porous Media Initiative shared infrastructure
Version: 2019.04-rc2
Label: 2019.04-rc2
Version: 2019.10-pre
Label: 2019.10-pre
Maintainer: opm@opm-project.org
MaintainerName: OPM community
Url: http://opm-project.org

View File

@@ -0,0 +1,186 @@
/*
Copyright 2019 Equinor ASA.
This file is part of the Open Porous Media project (OPM).
OPM 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.
OPM 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 for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#include "EGrid.hpp"
#include <opm/common/ErrorMacros.hpp>
#include <string>
#include <string.h>
#include <sstream>
#include <iterator>
#include <iomanip>
#include <algorithm>
#include <numeric>
EGrid::EGrid(const std::string &filename) : EclFile(filename)
{
EclFile file(filename);
auto gridhead = get<int>("GRIDHEAD");
nijk[0] = gridhead[1];
nijk[1] = gridhead[2];
nijk[2] = gridhead[3];
if (file.hasKey("ACTNUM")) {
auto actnum = get<int>("ACTNUM");
nactive = 0;
for (unsigned int i = 0; i < actnum.size(); i++) {
if (actnum[i] > 0) {
act_index.push_back(nactive);
glob_index.push_back(i);
nactive++;
} else {
act_index.push_back(-1);
}
}
} else {
int nCells = nijk[0] * nijk[1] * nijk[2];
act_index.resize(nCells);
glob_index.resize(nCells);
std::iota(act_index.begin(), act_index.end(), 0);
std::iota(glob_index.begin(), glob_index.end(), 0);
}
coord_array = get<float>("COORD");
zcorn_array = get<float>("ZCORN");
}
int EGrid::global_index(int i, int j, int k) const
{
if (i < 0 || i >= nijk[0] || j < 0 || j >= nijk[1] || k < 0 || k >= nijk[2]) {
OPM_THROW(std::invalid_argument, "i, j or/and k out of range");
}
return i + j * nijk[0] + k * nijk[0] * nijk[1];
}
int EGrid::active_index(int i, int j, int k) const
{
int n = i + j * nijk[0] + k * nijk[0] * nijk[1];
if (i < 0 || i >= nijk[0] || j < 0 || j >= nijk[1] || k < 0 || k >= nijk[2]) {
OPM_THROW(std::invalid_argument, "i, j or/and k out of range");
}
return act_index[n];
}
std::array<int, 3> EGrid::ijk_from_active_index(int actInd) const
{
if (actInd < 0 || actInd >= nactive) {
OPM_THROW(std::invalid_argument, "active index out of range");
}
int _glob = glob_index[actInd];
std::array<int, 3> result;
result[2] = _glob / (nijk[0] * nijk[1]);
int rest = _glob % (nijk[0] * nijk[1]);
result[1] = rest / nijk[0];
result[0] = rest % nijk[0];
return result;
}
std::array<int, 3> EGrid::ijk_from_global_index(int globInd) const
{
if (globInd < 0 || globInd >= nijk[0] * nijk[1] * nijk[2]) {
OPM_THROW(std::invalid_argument, "global index out of range");
}
std::array<int, 3> result;
result[2] = globInd / (nijk[0] * nijk[1]);
int rest = globInd % (nijk[0] * nijk[1]);
result[1] = rest / nijk[0];
result[0] = rest % nijk[0];
return result;
}
void EGrid::getCellCorners(const std::array<int, 3>& ijk,
std::vector<double>& X,
std::vector<double>& Y,
std::vector<double>& Z) const
{
std::vector<int> zind;
std::vector<int> pind;
if (X.size() < 8 || Y.size() < 8 || Z.size() < 8) {
OPM_THROW(std::invalid_argument,
"In routine cellConrner. X, Y and Z should be a vector of size 8");
}
if (ijk[0] < 0 || ijk[0] >= nijk[0] || ijk[1] < 0 || ijk[1] >= nijk[1] || ijk[2] < 0 || ijk[2] >= nijk[2]) {
OPM_THROW(std::invalid_argument, "i, j and/or k out of range");
}
// calculate indices for grid pillars in COORD arrray
pind.push_back(ijk[1]*(nijk[0]+1)*6 + ijk[0]*6);
pind.push_back(pind[0] + 6);
pind.push_back(pind[0] + (nijk[0]+1)*6);
pind.push_back(pind[2] + 6);
// get depths from zcorn array in ZCORN array
zind.push_back(ijk[2]*nijk[0]*nijk[1]*8 + ijk[1]*nijk[0]*4 + ijk[0]*2);
zind.push_back(zind[0] + 1);
zind.push_back(zind[0] + nijk[0]*2);
zind.push_back(zind[2] + 1);
for (int n = 0; n < 4; n++) {
zind.push_back(zind[n] + nijk[0]*nijk[1]*4);
}
for (int n = 0; n< 8; n++){
Z[n] = zcorn_array[zind[n]];
}
for (int n = 0; n < 4; n++) {
double xt = coord_array[pind[n]];
double yt = coord_array[pind[n] + 1];
double zt = coord_array[pind[n] + 2];
double xb = coord_array[pind[n] + 3];
double yb = coord_array[pind[n] + 4];
double zb = coord_array[pind[n]+5];
X[n] = xt + (xb-xt) / (zt-zb) * (zt - Z[n]);
X[n+4] = xt + (xb-xt) / (zt-zb) * (zt-Z[n+4]);
Y[n] = yt+(yb-yt)/(zt-zb)*(zt-Z[n]);
Y[n+4] = yt+(yb-yt)/(zt-zb)*(zt-Z[n+4]);
}
}
void EGrid::getCellCorners(int globindex, std::vector<double>& X,
std::vector<double>& Y, std::vector<double>& Z) const
{
return getCellCorners(ijk_from_global_index(globindex),X,Y,Z);
}

View File

@@ -0,0 +1,62 @@
/*
Copyright 2019 Equinor ASA.
This file is part of the Open Porous Media project (OPM).
OPM 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.
OPM 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 for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef EGRID_HPP
#define EGRID_HPP
#include "EclFile.hpp"
#include <array>
#include <iostream>
#include <string>
#include <fstream>
#include <vector>
#include <ctime>
#include <map>
class EGrid : public EclFile
{
public:
explicit EGrid(const std::string& filename);
int global_index(int i, int j, int k) const;
int active_index(int i, int j, int k) const;
const std::array<int, 3>& dimension() const { return nijk; }
std::array<int, 3> ijk_from_active_index(int actInd) const;
std::array<int, 3> ijk_from_global_index(int globInd) const;
void getCellCorners(int globindex, std::vector<double>& X, std::vector<double>& Y, std::vector<double>& Z) const;
void getCellCorners(const std::array<int, 3>& ijk, std::vector<double>& X, std::vector<double>& Y, std::vector<double>& Z) const;
int activeCells() const { return nactive; }
int totalNumberOfCells() const { return nijk[0] * nijk[1] * nijk[2]; }
private:
std::array<int, 3> nijk;
int nNNC, nactive;
std::vector<int> act_index;
std::vector<int> glob_index;
std::vector<float> coord_array;
std::vector<float> zcorn_array;
};
#endif

310
examples/test_util/ERft.cpp Normal file
View File

@@ -0,0 +1,310 @@
/*
Copyright 2019 Equinor ASA.
This file is part of the Open Porous Media project (OPM).
OPM 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.
OPM 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 for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ERft.hpp"
#include <opm/common/ErrorMacros.hpp>
#include <string>
#include <string.h>
#include <sstream>
#include <iterator>
#include <iomanip>
#include <algorithm>
ERft::ERft(const std::string &filename) : EclFile(filename)
{
loadData();
std::vector<int> first;
std::vector<int> second;
std::vector<std::string> wellName;
std::vector<RftDate> dates;
auto listOfArrays = getList();
for (size_t i = 0; i < listOfArrays.size(); i++) {
std::string name = std::get<0>(listOfArrays[i]);
if (name == "TIME") {
first.push_back(i);
auto vect1 = get<float>(i);
timeList.push_back(vect1[0]);
}
if (name == "DATE") {
auto vect1 = get<int>(i);
RftDate date(vect1[2],vect1[1],vect1[0]);
dateList.insert(date);
dates.push_back(date);
}
if (name == "WELLETC"){
auto vect1 = get<std::string>(i);
wellList.insert(vect1[1]);
wellName.push_back(vect1[1]);
}
}
for (size_t i = 0; i < first.size(); i++) {
std::pair<int,int> range;
range.first = first[i];
if (i == first.size() - 1) {
range.second = listOfArrays.size();
} else {
range.second = first[i+1];
}
arrIndexRange[i] = range;
}
numReports = first.size();
for (size_t i = 0; i < wellName.size(); i++) {
std::pair<std::string,RftDate> wellDatePair(wellName[i],dates[i]);
reportIndex[wellDatePair] = i;
rftReportList.push_back(wellDatePair);
}
}
bool ERft::hasRft(const std::string& wellName, const RftDate& date) const
{
return reportIndex.find({wellName, date}) != reportIndex.end();
}
bool ERft::hasRft(const std::string& wellName, int year, int month, int day) const
{
RftDate date(year, month, day);
return reportIndex.find({wellName,date}) != reportIndex.end();
}
int ERft::getReportIndex(const std::string& wellName, const RftDate& date) const
{
std::pair<std::string,std::tuple<int,int,int>> wellDatePair(wellName,date);
auto rIndIt = reportIndex.find(wellDatePair);
if (rIndIt == reportIndex.end()) {
int y = std::get<0>(date);
int m = std::get<1>(date);
int d = std::get<2>(date);
std::string dateStr=std::to_string(y) + "/" + std::to_string(m) + "/" + std::to_string(d);
std::string message="RFT data not found for well " + wellName + " at date: " + dateStr;
OPM_THROW(std::invalid_argument, message);
}
return rIndIt->second;
}
bool ERft::hasArray(const std::string& arrayName, const std::string& wellName,
const RftDate& date) const
{
int reportInd = getReportIndex(wellName, date);
auto searchInd = arrIndexRange.find(reportInd);
int fromInd = searchInd->second.first;
int toInd = searchInd->second.second;
auto it = std::find(array_name.begin()+fromInd,array_name.begin()+toInd,arrayName);
return it != array_name.begin() + toInd;
}
int ERft::getArrayIndex(const std::string& name, const std::string& wellName,
const RftDate& date) const
{
int rInd= getReportIndex(wellName, date);
auto searchInd = arrIndexRange.find(rInd);
int fromInd =searchInd->second.first;
int toInd = searchInd->second.second;
auto it=std::find(array_name.begin()+fromInd,array_name.begin()+toInd,name);
if (std::distance(array_name.begin(),it) == toInd) {
int y = std::get<0>(date);
int m = std::get<1>(date);
int d = std::get<2>(date);
std::string dateStr = std::to_string(y) + "/" + std::to_string(m) + "/" + std::to_string(d);
std::string message = "Array " + name + " not found for RFT, well: " + wellName + " date: " + dateStr;
OPM_THROW(std::invalid_argument, message);
}
return std::distance(array_name.begin(),it);
}
template<> const std::vector<float>&
ERft::getRft<float>(const std::string& name, const std::string &wellName,
const RftDate& date) const
{
int arrInd = getArrayIndex(name, wellName, date);
if (array_type[arrInd] != EIOD::REAL) {
std::string message = "Array " + name + " found in RFT file for selected date and well, but called with wrong type";
OPM_THROW(std::runtime_error, message);
}
auto search_array = real_array.find(arrInd);
return search_array->second;
}
template<> const std::vector<double>&
ERft::getRft<double>(const std::string& name, const std::string& wellName,
const RftDate& date) const
{
int arrInd = getArrayIndex(name, wellName, date);
if (array_type[arrInd] != EIOD::DOUB) {
std::string message = "Array " + name + " found in RFT file for selected date and well, but called with wrong type";
OPM_THROW(std::runtime_error, message);
}
auto search_array = doub_array.find(arrInd);
return search_array->second;
}
template<> const std::vector<int>&
ERft::getRft<int>(const std::string& name, const std::string& wellName,
const RftDate& date) const
{
int arrInd = getArrayIndex(name, wellName, date);
if (array_type[arrInd] != EIOD::INTE) {
std::string message = "Array " + name + " found in RFT file for selected date and well, but called with wrong type";
OPM_THROW(std::runtime_error, message);
}
auto search_array = inte_array.find(arrInd);
return search_array->second;
}
template<> const std::vector<bool>&
ERft::getRft<bool>(const std::string& name, const std::string& wellName,
const RftDate& date) const
{
int arrInd = getArrayIndex(name, wellName, date);
if (array_type[arrInd] != EIOD::LOGI) {
std::string message = "Array " + name + " found in RFT file for selected date and well, but called with wrong type";
OPM_THROW(std::runtime_error, message);
}
auto search_array = logi_array.find(arrInd);
return search_array->second;
}
template<> const std::vector<std::string>&
ERft::getRft<std::string>(const std::string& name, const std::string& wellName,
const RftDate& date) const
{
int arrInd = getArrayIndex(name, wellName, date);
if (array_type[arrInd] != EIOD::CHAR) {
std::string message = "Array " + name + " found in RFT file for selected date and well, but called with wrong type";
OPM_THROW(std::runtime_error, message);
}
auto search_array = char_array.find(arrInd);
return search_array->second;
}
template<> const std::vector<int>&
ERft::getRft<int>(const std::string& name, const std::string& wellName,
int year, int month, int day) const
{
return getRft<int>(name, wellName, RftDate{year, month, day});
}
template<> const std::vector<float>&
ERft::getRft<float>(const std::string& name, const std::string& wellName,
int year, int month, int day) const
{
return getRft<float>(name, wellName, RftDate{year, month, day});
}
template<> const std::vector<double>&
ERft::getRft<double>(const std::string& name, const std::string& wellName,
int year, int month, int day) const
{
return getRft<double>(name, wellName, RftDate{year, month, day});
}
template<> const std::vector<std::string>&
ERft::getRft<std::string>(const std::string& name, const std::string& wellName,
int year, int month, int day) const
{
return getRft<std::string>(name, wellName, RftDate{year, month, day});
}
template<> const std::vector<bool>&
ERft::getRft<bool>(const std::string& name, const std::string& wellName,
int year, int month, int day) const
{
return getRft<bool>(name, wellName, RftDate{year, month, day});
}
std::vector<EclFile::EclEntry> ERft::listOfRftArrays(const std::string& wellName,
const RftDate& date) const
{
std::vector<EclEntry> list;
int rInd = getReportIndex(wellName, date);
auto searchInd = arrIndexRange.find(rInd);
for (int i = searchInd->second.first; i < searchInd->second.second; i++) {
list.emplace_back(array_name[i], array_type[i], array_size[i]);
}
return list;
}
std::vector<EclFile::EclEntry> ERft::listOfRftArrays(const std::string& wellName,
int year, int month, int day) const
{
return listOfRftArrays(wellName, RftDate{year, month, day});
}
std::vector<std::string> ERft::listOfWells() const
{
return { this->wellList.begin(), this->wellList.end() };
}
std::vector<ERft::RftDate> ERft::listOfdates() const
{
return { this->dateList.begin(), this->dateList.end() };
}

View File

@@ -0,0 +1,83 @@
/*
Copyright 2019 Equinor ASA.
This file is part of the Open Porous Media project (OPM).
OPM 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.
OPM 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 for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ERFT_HPP
#define ERFT_HPP
#include "EclFile.hpp"
#include <iostream>
#include <string>
#include <fstream>
#include <vector>
#include <ctime>
#include <map>
#include <set>
class ERft : public EclFile
{
public:
explicit ERft(const std::string &filename);
using RftDate = std::tuple<int,int,int>;
template <typename T>
const std::vector<T>& getRft(const std::string& name, const std::string& wellName,
const RftDate& date) const;
template <typename T>
const std::vector<T>& getRft(const std::string& name, const std::string& wellName,
int year, int month, int day) const;
std::vector<std::string> listOfWells() const;
std::vector<RftDate> listOfdates() const;
using RftReportList = std::vector<std::pair<std::string, RftDate>>;
const RftReportList& listOfRftReports() const { return rftReportList; }
bool hasRft(const std::string& wellName, const RftDate& date) const;
bool hasRft(const std::string& wellName, int year, int month, int day) const;
std::vector<EclEntry> listOfRftArrays(const std::string& wellName,
const RftDate& date) const;
std::vector<EclEntry> listOfRftArrays(const std::string& wellName,
int year, int month, int day) const;
bool hasArray(const std::string& arrayName, const std::string& wellName,
const RftDate& date) const;
private:
std::map<int, std::pair<int,int>> arrIndexRange;
int numReports;
std::vector<float> timeList;
std::set<std::string> wellList;
std::set<RftDate> dateList;
RftReportList rftReportList;
std::map<std::pair<std::string,RftDate>,int> reportIndex; // mapping report index to wellName and date (tupe)
int getReportIndex(const std::string& wellName, const RftDate& date) const;
int getArrayIndex(const std::string& name, const std::string& wellName,
const RftDate& date) const;
};
#endif

178
examples/test_util/ERst.cpp Normal file
View File

@@ -0,0 +1,178 @@
/*
Copyright 2019 Equinor ASA.
This file is part of the Open Porous Media project (OPM).
OPM 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.
OPM 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 for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ERst.hpp"
#include <string>
#include <string.h>
#include <sstream>
#include <iterator>
#include <iomanip>
#include <algorithm>
ERst::ERst(const std::string& filename) : EclFile(filename)
{
loadData("SEQNUM");
std::vector<int> firstIndex;
for (size_t i = 0; i < array_name.size(); i++) {
if (array_name[i] == "SEQNUM") {
auto seqn = get<int>(i);
seqnum.push_back(seqn[0]);
firstIndex.push_back(i);
}
}
for (size_t i = 0; i < seqnum.size(); i++) {
std::pair<int,int> range;
range.first = firstIndex[i];
if (i != seqnum.size() - 1) {
range.second = firstIndex[i+1];
} else {
range.second = array_name.size();
}
arrIndexRange[seqnum[i]] = range;
}
nReports = seqnum.size();
for (int i = 0; i < nReports; i++) {
reportLoaded[seqnum[i]] = false;
}
};
bool ERst::hasReportStepNumber(int number) const
{
auto search = arrIndexRange.find(number);
return search != arrIndexRange.end();
}
void ERst::loadReportStepNumber(int number)
{
if (!hasReportStepNumber(number)) {
std::string message="Trying to load non existing report step number " + std::to_string(number);
OPM_THROW(std::invalid_argument, message);
}
std::vector<int> arrayIndexList;
arrayIndexList.reserve(arrIndexRange[number].second - arrIndexRange[number].first + 1);
for (int i = arrIndexRange[number].first; i < arrIndexRange[number].second; i++) {
arrayIndexList.push_back(i);
}
loadData(arrayIndexList);
reportLoaded[number] = true;
}
std::vector<EclFile::EclEntry> ERst::listOfRstArrays(int reportStepNumber)
{
std::vector<EclEntry> list;
if (!hasReportStepNumber(reportStepNumber)) {
std::string message = "Trying to get list of arrays from non existing report step number " + std::to_string(reportStepNumber);
OPM_THROW(std::invalid_argument, message);
}
const auto& rng = this->arrIndexRange[reportStepNumber];
list.reserve(rng.second - rng.first);
for (int i = rng.first; i < rng.second; i++) {
list.emplace_back(array_name[i], array_type[i], array_size[i]);
}
return list;
}
int ERst::getArrayIndex(const std::string& name, int number) const
{
if (!hasReportStepNumber(number)) {
std::string message = "Trying to get vector " + name + " from non existing sequence " + std::to_string(number);
OPM_THROW(std::invalid_argument, message);
}
auto search = reportLoaded.find(number);
if (!search->second) {
std::string message = "Data not loaded for sequence " + std::to_string(number);
OPM_THROW(std::runtime_error, message);
}
auto range_it = arrIndexRange.find(number);
std::pair<int,int> indexRange = range_it->second;
auto it = std::find(array_name.begin() + indexRange.first,
array_name.begin() + indexRange.second, name);
if (std::distance(array_name.begin(),it) == indexRange.second) {
std::string message = "Array " + name + " not found in sequence " + std::to_string(number);
OPM_THROW(std::runtime_error, message);
}
return std::distance(array_name.begin(), it);
}
template<>
const std::vector<int>& ERst::getRst<int>(const std::string& name, int reportStepNumber)
{
int ind = getArrayIndex(name, reportStepNumber);
return getImpl(ind, EIOD::INTE, inte_array, "integer");
}
template<>
const std::vector<float>& ERst::getRst<float>(const std::string& name, int reportStepNumber)
{
int ind = getArrayIndex(name, reportStepNumber);
return getImpl(ind, EIOD::REAL, real_array, "float");
}
template<>
const std::vector<double>& ERst::getRst<double>(const std::string& name, int reportStepNumber)
{
int ind = getArrayIndex(name, reportStepNumber);
return getImpl(ind, EIOD::DOUB, doub_array, "double");
}
template<>
const std::vector<bool>& ERst::getRst<bool>(const std::string& name, int reportStepNumber)
{
int ind = getArrayIndex(name, reportStepNumber);
return getImpl(ind, EIOD::LOGI, logi_array, "bool");
}
template<>
const std::vector<std::string>& ERst::getRst<std::string>(const std::string& name, int reportStepNumber)
{
int ind = getArrayIndex(name, reportStepNumber);
return getImpl(ind, EIOD::CHAR, char_array, "string");
}

View File

@@ -0,0 +1,57 @@
/*
Copyright 2019 Equinor ASA.
This file is part of the Open Porous Media project (OPM).
OPM 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.
OPM 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 for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ERST_HPP
#define ERST_HPP
#include "EclFile.hpp"
#include <iostream>
#include <string>
#include <fstream>
#include <vector>
#include <ctime>
#include <map>
class ERst : public EclFile
{
public:
explicit ERst(const std::string& filename);
bool hasReportStepNumber(int number) const;
void loadReportStepNumber(int number);
template <typename T>
const std::vector<T>& getRst(const std::string& name, int reportStepNumber);
const std::vector<int>& listOfReportStepNumbers() const { return seqnum; }
std::vector<EclEntry> listOfRstArrays(int reportStepNumber);
private:
int nReports;
std::vector<int> seqnum; // report step numbers, from SEQNUM array in restart file
std::unordered_map<int,bool> reportLoaded;
std::map<int, std::pair<int,int>> arrIndexRange; // mapping report step number to array indeces (start and end)
int getArrayIndex(const std::string& name, int seqnum) const;
};
#endif

View File

@@ -0,0 +1,431 @@
/*
Copyright 2019 Equinor ASA.
This file is part of the Open Porous Media project (OPM).
OPM 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.
OPM 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 for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string>
#include <string.h>
#include <sstream>
#include <iterator>
#include <iomanip>
#include <algorithm>
#include <unistd.h>
#include <limits>
#include <set>
#include "EclFile.hpp"
#include "ESmry.hpp"
/*
KEYWORDS WGNAMES NUMS | PARAM index Corresponding ERT key
------------------------------------------------+--------------------------------------------------
WGOR OP_1 0 | 0 WGOR:OP_1
FOPT +-+-+-+- 0 | 1 FOPT
WWCT OP_1 0 | 2 WWCT:OP_1
WIR OP_1 0 | 3 WIR:OP_1
WGOR WI_1 0 | 4 WWCT:OP_1
WWCT W1_1 0 | 5 WWCT:WI_1
BPR +-+-+- 12675 | 6 BPR:12675, BPR:i,j,k
RPR +-+-+- 1 | 7 RPR:1
FOPT +-+-+- 0 | 8 FOPT
GGPR NORTH 0 | 9 GGPR:NORTH
COPR OP_1 5628 | 10 COPR:OP_1:56286, COPR:OP_1:i,j,k
RXF +-+-+- 32768*R1(R2 + 10) | 11 RXF:2-3
SOFX OP_1 12675 | 12 SOFX:OP_1:12675, SOFX:OP_1:i,j,jk
*/
ESmry::ESmry(const std::string &filename, bool loadBaseRunData)
{
std::string rootN;
std::vector<int> actInd;
bool formatted=false;
char buff[1000];
getcwd( buff, 1000 );
std::string currentWorkingDir(buff);
std::string currentDir=currentWorkingDir;
std::string smspec_filen;
std::string unsmry_filen;
if (filename.substr(filename.length() - 7, 7) == ".SMSPEC") {
rootN = filename.substr(0,filename.length() -7);
} else if (filename.substr(filename.length() -8, 8) == ".FSMSPEC") {
rootN=filename.substr(0,filename.length() -8);
formatted = true;
} else {
rootN = filename;
}
path = currentWorkingDir;
updatePathAndRootName(path, rootN);
if (formatted) {
smspec_filen = path + "/" + rootN + ".FSMSPEC";
unsmry_filen = path + "/" + rootN + ".FUNSMRY";
} else {
smspec_filen = path + "/" + rootN + ".SMSPEC";
unsmry_filen = path + "/" + rootN + ".UNSMRY";
}
std::vector<std::pair<std::string,int>> smryArray;
EclFile smspec1(smspec_filen);
smspec1.loadData(); // loading all data
std::set<std::string> keywList;
std::vector<int> dimens = smspec1.get<int>("DIMENS");
nI = dimens[1];
nJ = dimens[2];
nK = dimens[3];
std::vector<std::string> restartArray = smspec1.get<std::string>("RESTART");
std::vector<std::string> keywords = smspec1.get<std::string>("KEYWORDS");
std::vector<std::string> wgnames = smspec1.get<std::string>("WGNAMES");
std::vector<int> nums = smspec1.get<int>("NUMS");
for (unsigned int i=0; i<keywords.size(); i++) {
std::string str1 = makeKeyString(keywords[i], wgnames[i], nums[i]);
if (str1.length() > 0) {
keywList.insert(str1);
}
}
std::string rstRootN = "";
std::string pathRstFile = path;
getRstString(restartArray, pathRstFile, rstRootN);
smryArray.push_back({smspec_filen, dimens[5]});
// checking if this is a restart run. Supporting nested restarts (restart, from restart, ...)
// std::set keywList is storing keywords from all runs involved
while ((rstRootN != "") && (loadBaseRunData)) {
std::string rstFile=pathRstFile+"/"+rstRootN+".SMSPEC";
EclFile smspec_rst(rstFile);
smspec_rst.loadData();
std::vector<int> dimens = smspec_rst.get<int>("DIMENS");
std::vector<std::string> restartArray = smspec_rst.get<std::string>("RESTART");
std::vector<std::string> keywords = smspec_rst.get<std::string>("KEYWORDS");
std::vector<std::string> wgnames = smspec_rst.get<std::string>("WGNAMES");
std::vector<int> nums = smspec_rst.get<int>("NUMS");
std::vector<std::string> units = smspec_rst.get<std::string>("UNITS");
for (size_t i = 0; i < keywords.size(); i++) {
std::string str1 = makeKeyString(keywords[i], wgnames[i], nums[i]);
if (str1.length() > 0) {
keywList.insert(str1);
}
}
smryArray.push_back({rstFile,dimens[5]});
getRstString(restartArray, pathRstFile, rstRootN);
}
int nFiles = static_cast<int>(smryArray.size());
// arrayInd should hold indices for each vector and runs
// n=file number, i = position in param array in file n (one array pr time step), example arrayInd[n][i] = position in keyword list (std::set)
std::vector<std::vector<int>> arrayInd;
for (int i = 0; i < nFiles; i++){
arrayInd.push_back({});
}
int n = nFiles - 1;
while (n >= 0){
auto smry = smryArray[n];
EclFile smspec(std::get<0>(smry));
smspec.loadData();
std::vector<int> dimens = smspec.get<int>("DIMENS");
nI = dimens[1];
nJ = dimens[2];
nK = dimens[3];
std::vector<std::string> keywords = smspec.get<std::string>("KEYWORDS");
std::vector<std::string> wgnames = smspec.get<std::string>("WGNAMES");
std::vector<int> nums = smspec.get<int>("NUMS");
std::vector<int> tmpVect(keywords.size(), -1);
arrayInd[n]=tmpVect;
std::set<std::string>::iterator it;
for (size_t i=0; i < keywords.size(); i++) {
std::string keyw = makeKeyString(keywords[i], wgnames[i], nums[i]);
it = std::find(keywList.begin(), keywList.end(), keyw);
if (it != keywList.end()){
arrayInd[n][i] = distance(keywList.begin(), it);
}
}
n--;
}
// param array used to stor data for the object, defined in the private section of the class
param.assign(keywList.size(), {});
int fromReportStepNumber = 0;
int reportStepNumber = 0;
int toReportStepNumber;
float time = 0.0;
int step = 0;
n = nFiles - 1;
while (n >= 0){
reportStepNumber = fromReportStepNumber;
if (n > 0) {
auto rstFrom = smryArray[n-1];
toReportStepNumber = std::get<1>(rstFrom);
} else {
toReportStepNumber = std::numeric_limits<int>::max();
}
std::string smspecFile = std::get<0>(smryArray[n]);
std::string unsmryFile = smspecFile.substr(0, smspecFile.size() - 6) + "UNSMRY";
EclFile unsmry(unsmryFile);
unsmry.loadData();
std::vector<EclFile::EclEntry> list1 = unsmry.getList();
// 2 or 3 arrays pr time step.
// If timestep is a report step: MINISTEP, PARAMS and SEQHDR
// else : MINISTEP and PARAMS
// if summary file starts with a SEQHDR, this will be ignored
int i = 0;
if (std::get<0>(list1[0]) == "SEQHDR") {
i = 1;
}
while (i < static_cast<int>(list1.size())){
if (std::get<0>(list1[i]) != "MINISTEP"){
std::string message="Reading summary file, expecting keyword MINISTEP, found '" + std::get<0>(list1[i]) + "'";
throw std::invalid_argument(message);
}
std::vector<int> ministep = unsmry.get<int>(i);
i++;
if (std::get<0>(list1[i]) != "PARAMS") {
std::string message="Reading summary file, expecting keyword PARAMS, found '" + std::get<0>(list1[i]) + "'";
throw std::invalid_argument(message);
}
std::vector<float> tmpData = unsmry.get<float>(i);
time = tmpData[0];
if (time == 0.0) {
seqTime.push_back(time);
seqIndex.push_back(step);
}
i++;
if (i < static_cast<int>(list1.size())){
if (std::get<0>(list1[i]) == "SEQHDR") {
i++;
reportStepNumber++;
seqTime.push_back(time);
seqIndex.push_back(step);
}
} else {
reportStepNumber++;
seqTime.push_back(time);
seqIndex.push_back(step);
}
// adding defaut values (0.0) in case vector not found in this particular summary file
for (size_t i = 0; i < param.size(); i++){
param[i].push_back(0.0);
}
for (size_t j = 0; j < tmpData.size(); j++) {
int ind = arrayInd[n][j];
if (ind > -1) {
param[ind][step] = tmpData[j];
}
}
if (reportStepNumber >= toReportStepNumber) {
i = static_cast<int>(list1.size());
}
step++;
}
fromReportStepNumber = toReportStepNumber;
n--;
}
nVect = keywList.size();
for (auto keyw : keywList){
keyword.push_back(keyw);
}
};
void ESmry::getRstString(const std::vector<std::string>& restartArray, std::string& pathRst, std::string& rootN) const {
rootN = "";
for (auto str : restartArray) {
rootN = rootN + str;
}
updatePathAndRootName(pathRst, rootN);
}
void ESmry::updatePathAndRootName(std::string& dir, std::string& rootN) const {
if (rootN.substr(0,2) == "./") {
rootN = rootN.substr(2, rootN.size() - 2);
}
if (rootN.substr(0,1) == "/") {
int p = rootN.find_last_of("/");
dir = rootN.substr(0, p);
rootN = rootN.substr(p + 1, rootN.size() - p - 1);
} else if (rootN.find_first_of("/") != std::string::npos) {
int p = rootN.find_last_of("/");
dir = dir + "/" + rootN.substr(0, p);
rootN = rootN.substr(p + 1, rootN.size() - p - 1);
};
}
bool ESmry::hasKey(const std::string &key) const
{
return std::find(keyword.begin(), keyword.end(), key) != keyword.end();
}
void ESmry::ijk_from_global_index(int glob,int &i,int &j,int &k)
{
int tmpGlob = glob - 1;
k = 1 + tmpGlob / (nI * nJ);
int rest = tmpGlob % (nI * nJ);
j = 1 + rest / nI;
i = 1 + rest % nI;
}
std::string ESmry::makeKeyString(const std::string &keyword, const std::string &wgname, int num)
{
std::string keyStr;
std::vector<std::string> segmExcep= {"STEPTYPE", "SEPARATE", "SUMTHIN"};
if (keyword.substr(0, 1) == "A") {
keyStr = keyword + ":" + std::to_string(num);
} else if (keyword.substr(0, 1) == "B") {
int _i,_j,_k;
ijk_from_global_index(num, _i, _j, _k);
keyStr = keyword + ":" + std::to_string(_i) + "," + std::to_string(_j) + "," + std::to_string(_k);
} else if (keyword.substr(0, 1) == "C") {
int _i,_j,_k;
if (num > 0) {
ijk_from_global_index(num, _i, _j, _k);
keyStr = keyword + ":" + wgname+ ":" + std::to_string(_i) + "," + std::to_string(_j) + "," + std::to_string(_k);
}
} else if (keyword.substr(0, 1) == "G") {
if ( wgname != ":+:+:+:+") {
keyStr = keyword + ":" + wgname;
}
} else if (keyword.substr(0, 1) == "R" && keyword.substr(2, 1) == "F") {
// NUMS = R1 + 32768*(R2 + 10)
int r2 = 0;
int y = 32768 * (r2 + 10) - num;
while (y <0 ) {
r2++;
y = 32768 * (r2 + 10) - num;
}
r2--;
int r1 = num - 32768 * (r2 + 10);
keyStr = keyword + ":" + std::to_string(r1) + "-" + std::to_string(r2);
} else if (keyword.substr(0, 1) == "R") {
keyStr = keyword + ":" + std::to_string(num);
} else if (keyword.substr(0, 1) == "S") {
auto it = std::find(segmExcep.begin(), segmExcep.end(), keyword);
if (it != segmExcep.end()) {
keyStr = keyword;
} else {
keyStr = keyword + ":" + wgname + ":" + std::to_string(num);
}
} else if (keyword.substr(0,1) == "W") {
if (wgname != ":+:+:+:+") {
keyStr = keyword + ":" + wgname;
}
} else {
keyStr = keyword;
}
return keyStr;
}
const std::vector<float>& ESmry::get(const std::string& name) const
{
auto it = std::find(keyword.begin(), keyword.end(), name);
if (it == keyword.end()) {
std::string message="keyword " + name + " not found ";
OPM_THROW(std::invalid_argument, message);
}
int ind = std::distance(keyword.begin(), it);
return param[ind];
}

View File

@@ -0,0 +1,60 @@
/*
Copyright 2019 Equinor ASA.
This file is part of the Open Porous Media project (OPM).
OPM 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.
OPM 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 for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ESMRY_HPP
#define ESMRY_HPP
#include <iostream>
#include <string>
#include <fstream>
#include <vector>
#include <ctime>
#include <map>
class ESmry
{
public:
explicit ESmry(const std::string& filename, bool loadBaseRunData=false); // filename (smspec file) or file root name
const int numberOfVectors() const { return nVect; }
bool hasKey(const std::string& key) const;
const std::vector<float>& get(const std::string& name) const;
const std::vector<std::string>& keywordList() const { return keyword; }
private:
int nVect, nI, nJ, nK;
std::string path="";
void ijk_from_global_index(int glob, int &i, int &j, int &k);
std::vector<std::vector<float>> param;
std::vector<std::string> keyword;
std::vector<int> seqIndex;
std::vector<float> seqTime;
void getRstString(const std::vector<std::string> &restartArray, std::string &path, std::string &rootN) const;
void updatePathAndRootName(std::string &path, std::string &rootN) const;
std::string makeKeyString(const std::string& keyword, const std::string& wgname, int num);
};
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -16,26 +16,95 @@
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ECLFILE_HPP
#define ECLFILE_HPP
#include <opm/common/ErrorMacros.hpp>
#include <examples/test_util/data/EclIOdata.hpp>
//#include "EclIO.hpp"
#include <iostream>
#include <string>
#include <fstream>
#include <vector>
#include <ctime>
#include <map>
#include <examples/test_util/data/EclIOdata.hpp>
#include <unordered_map>
#include <stdio.h>
namespace EIOD = Opm::ecl;
class EclFile
class EclFile
{
public:
explicit EclFile(const std::string& filename);
};
bool formattedInput() { return formatted; }
void loadData(); // load all data
void loadData(const std::string& arrName); // load all arrays with array name equal to arrName
void loadData(int arrIndex); // load data based on array indices in vector arrIndex
void loadData(const std::vector<int>& arrIndex); // load data based on array indices in vector arrIndex
void clearData()
{
inte_array.clear();
real_array.clear();
doub_array.clear();
logi_array.clear();
char_array.clear();
}
using EclEntry = std::tuple<std::string, EIOD::eclArrType, int>;
std::vector<EclEntry> getList() const;
template <typename T>
const std::vector<T>& get(int arrIndex);
template <typename T>
const std::vector<T>& get(const std::string& name);
bool hasKey(const std::string &name) const;
const std::vector<std::string>& arrayNames() const { return array_name; }
protected:
bool formatted;
std::string inputFilename;
std::unordered_map<int, std::vector<int>> inte_array;
std::unordered_map<int, std::vector<bool>> logi_array;
std::unordered_map<int, std::vector<double>> doub_array;
std::unordered_map<int, std::vector<float>> real_array;
std::unordered_map<int, std::vector<std::string>> char_array;
std::vector<std::string> array_name;
std::vector<EIOD::eclArrType> array_type;
std::vector<int> array_size;
std::vector<unsigned long int> ifStreamPos;
std::map<std::string, int> array_index;
template<class T>
const std::vector<T>& getImpl(int arrIndex, EIOD::eclArrType type,
const std::unordered_map<int, std::vector<T>>& array,
const std::string& typeStr)
{
if (array_type[arrIndex] != type) {
std::string message = "Array with index " + std::to_string(arrIndex) + " is not of type " + typeStr;
OPM_THROW(std::runtime_error, message);
}
if (!arrayLoaded[arrIndex]) {
loadData(arrIndex);
}
return array.find(arrIndex)->second;
}
private:
std::vector<bool> arrayLoaded;
void loadArray(std::fstream& fileH, int arrIndex);
};
#endif

View File

@@ -18,7 +18,6 @@
#include "EclFilesComparator.hpp"
#include <opm/common/ErrorMacros.hpp>
#include <opm/common/utility/numeric/calculateCellVol.hpp>
#include <stdio.h>
@@ -28,13 +27,8 @@
#include <algorithm>
#include <cmath>
#include <numeric>
#include <ert/ecl/ecl_file.h>
#include <ert/ecl/ecl_grid.h>
#include <ert/ecl/ecl_type.h>
#include <ert/ecl_well/well_info.h>
#include <vector>
#include <type_traits>
// helper macro to handle error throws or not
#define HANDLE_ERROR(type, message) \
@@ -48,203 +42,70 @@
}
namespace {
/*
This is just a basic survival test; we verify that the ERT well
loader which is used in Resinsight can load the well description
from the restart file.
*/
void loadWells( const ecl_grid_type * grid , ecl_file_type * rst_file ) {
well_info_type * well_info = well_info_alloc( grid );
well_info_add_UNRST_wells2( well_info , ecl_file_get_global_view( rst_file ), true );
well_info_free( well_info );
}
}
void ECLFilesComparator::keywordValidForComparing(const std::string& keyword) const {
auto it = std::find(keywords1.begin(), keywords1.end(), keyword);
if (it == keywords1.end()) {
OPM_THROW(std::runtime_error, "Keyword " << keyword << " does not exist in first file.");
}
it = find(keywords2.begin(), keywords2.end(), keyword);
if (it == keywords2.end()) {
OPM_THROW(std::runtime_error, "Keyword " << keyword << " does not exist in second file.");
}
}
unsigned int ECLFilesComparator::getEclKeywordData(ecl_kw_type*& ecl_kw1, ecl_kw_type*& ecl_kw2, const std::string& keyword, int occurrence1, int occurrence2) const {
ecl_kw1 = ecl_file_iget_named_kw(ecl_file1, keyword.c_str(), occurrence1);
ecl_kw2 = ecl_file_iget_named_kw(ecl_file2, keyword.c_str(), occurrence2);
const unsigned int numCells1 = ecl_kw_get_size(ecl_kw1);
const unsigned int numCells2 = ecl_kw_get_size(ecl_kw2);
if (numCells1 != numCells2) {
OPM_THROW(std::runtime_error, "For keyword " << keyword << ":"
<< "\nOccurrence in first file " << occurrence1
<< "\nOccurrence in second file " << occurrence2
<< "\nCells in first file: " << numCells1
<< "\nCells in second file: " << numCells2
<< "\nThe number of cells differ.");
}
return numCells1;
}
template <typename T>
void ECLFilesComparator::printValuesForCell(const std::string& /*keyword*/, int occurrence1, int occurrence2, size_t kw_size, size_t cell, const T& value1, const T& value2) const {
if (kw_size == static_cast<size_t>(ecl_grid_get_active_size(ecl_grid1))) {
int i, j, k;
ecl_grid_get_ijk1A(ecl_grid1, cell, &i, &j, &k);
// Coordinates from this function are zero-based, hence incrementing
i++, j++, k++;
std::cout << std::endl
<< "Occurrence in first file = " << occurrence1 << "\n"
<< "Occurrence in second file = " << occurrence2 << "\n"
<< "Value index = " << cell << "\n"
<< "Grid coordinate = (" << i << ", " << j << ", " << k << ")" << "\n"
<< "(first value, second value) = (" << value1 << ", " << value2 << ")\n\n";
void ECLFilesComparator::printValuesForCell(const std::string& keyword, const std::string reference, size_t kw_size, size_t cell, EGrid *grid, const T& value1, const T& value2) const {
int nActive = -1;
int nTot = -1;
if (grid) {
nActive = grid->activeCells();
nTot = grid->totalNumberOfCells();
}
if (static_cast<int>(kw_size) == nActive) {
auto ijk = grid->ijk_from_active_index(cell);
ijk[0]++, ijk[1]++, ijk[2]++;
std::cout << std::endl
<< "\nKeyword: " << keyword << ", origin " << reference << "\n"
<< "Global index (zero based) = " << cell << "\n"
<< "Grid coordinate = (" << ijk[0] << ", " << ijk[1] << ", " << ijk[2] << ")" << "\n"
<< "(first value, second value) = (" << value1 << ", " << value2 << ")\n\n";
return;
}
if (kw_size == static_cast<size_t>(ecl_grid_get_global_size(ecl_grid1))) {
int i, j, k;
ecl_grid_get_ijk1(ecl_grid1, cell, &i, &j, &k);
// Coordinates from this function are zero-based, hence incrementing
i++, j++, k++;
if (static_cast<int>(kw_size) == nTot) {
auto ijk = grid->ijk_from_global_index(cell);
ijk[0]++, ijk[1]++, ijk[2]++;
std::cout << std::endl
<< "Occurrence in first file = " << occurrence1 << "\n"
<< "Occurrence in second file = " << occurrence2 << "\n"
<< "Value index = " << cell << "\n"
<< "Grid coordinate = (" << i << ", " << j << ", " << k << ")" << "\n"
<< "\nKeyword: " << keyword << ", origin " << reference << "\n\n"
<< "Global index (zero based) = " << cell << "\n"
<< "Grid coordinate = (" << ijk[0] << ", " << ijk[1] << ", " << ijk[2] << ")" << "\n"
<< "(first value, second value) = (" << value1 << ", " << value2 << ")\n\n";
return;
}
std::cout << std::endl
<< "Occurrence in first file = " << occurrence1 << "\n"
<< "Occurrence in second file = " << occurrence2 << "\n"
<< "\nKeyword: " << keyword << ", origin " << reference << "\n\n"
<< "Value index = " << cell << "\n"
<< "(first value, second value) = (" << value1 << ", " << value2 << ")\n\n";
}
template void ECLFilesComparator::printValuesForCell<bool> (const std::string& keyword, int occurrence1, int occurrence2, size_t kw_size, size_t cell, const bool& value1, const bool& value2) const;
template void ECLFilesComparator::printValuesForCell<int> (const std::string& keyword, int occurrence1, int occurrence2, size_t kw_size, size_t cell, const int& value1, const int& value2) const;
template void ECLFilesComparator::printValuesForCell<double> (const std::string& keyword, int occurrence1, int occurrence2, size_t kw_size, size_t cell, const double& value1, const double& value2) const;
template void ECLFilesComparator::printValuesForCell<std::string>(const std::string& keyword, int occurrence1, int occurrence2, size_t kw_size, size_t cell, const std::string& value1, const std::string& value2) const;
template void ECLFilesComparator::printValuesForCell<bool> (const std::string& keyword, const std::string reference, size_t kw_size, size_t cell, EGrid *grid, const bool& value1, const bool& value2) const;
template void ECLFilesComparator::printValuesForCell<int> (const std::string& keyword, const std::string reference, size_t kw_size, size_t cell, EGrid *grid, const int& value1, const int& value2) const;
template void ECLFilesComparator::printValuesForCell<double> (const std::string& keyword, const std::string reference, size_t kw_size, size_t cell, EGrid *grid, const double& value1, const double& value2) const;
template void ECLFilesComparator::printValuesForCell<std::string>(const std::string& keyword, const std::string reference, size_t kw_size, size_t cell, EGrid *grid, const std::string& value1, const std::string& value2) const;
// Hack to work around case where std::vector<bool>::const_reference is not a bool. If it is we will initialize printValuesForCell<char> otherwise printValuesForCell<std::vector<bool>::const_reference>
using boolConstReference = typename std::vector<bool>::const_reference;
using boolTypeHelper = typename std::remove_const<typename std::remove_reference<boolConstReference>::type>::type;
using boolType = typename std::conditional<std::is_same<boolTypeHelper, bool>::value, char, boolTypeHelper>::type;
template void ECLFilesComparator::printValuesForCell<boolType> (const std::string& keyword, const std::string reference, size_t kw_size, size_t cell, EGrid *grid, const boolType& value1, const boolType& value2) const;
ECLFilesComparator::ECLFilesComparator(int file_type_arg, const std::string& basename1,
ECLFilesComparator::ECLFilesComparator(const std::string& basename1,
const std::string& basename2,
double absToleranceArg, double relToleranceArg) :
file_type(file_type_arg), absTolerance(absToleranceArg), relTolerance(relToleranceArg) {
absTolerance(absToleranceArg), relTolerance(relToleranceArg) {
std::string file1, file2;
if (file_type == ECL_UNIFIED_RESTART_FILE) {
file1 = basename1 + ".UNRST";
file2 = basename2 + ".UNRST";
}
else if (file_type == ECL_INIT_FILE) {
file1 = basename1 + ".INIT";
file2 = basename2 + ".INIT";
}
else if (file_type == ECL_RFT_FILE) {
file1 = basename1 + ".RFT";
file2 = basename2 + ".RFT";
}
else {
OPM_THROW(std::invalid_argument, "Unsupported filetype sent to ECLFilesComparator's constructor."
<< "Only unified restart (.UNRST), initial (.INIT) and .RFT files are supported.");
}
ecl_file1 = ecl_file_open(file1.c_str(), 0);
ecl_file2 = ecl_file_open(file2.c_str(), 0);
ecl_grid1 = ecl_grid_load_case(basename1.c_str());
ecl_grid2 = ecl_grid_load_case(basename2.c_str());
if (ecl_file1 == nullptr) {
OPM_THROW(std::invalid_argument, "Error opening first file: " << file1);
}
if (ecl_file2 == nullptr) {
OPM_THROW(std::invalid_argument, "Error opening second file: " << file2);
}
if (ecl_grid1 == nullptr) {
OPM_THROW(std::invalid_argument, "Error opening first grid file: " << basename1);
}
if (ecl_grid2 == nullptr) {
OPM_THROW(std::invalid_argument, "Error opening second grid file. " << basename2);
}
unsigned int numKeywords1 = ecl_file_get_num_distinct_kw(ecl_file1);
unsigned int numKeywords2 = ecl_file_get_num_distinct_kw(ecl_file2);
keywords1.reserve(numKeywords1);
keywords2.reserve(numKeywords2);
for (unsigned int i = 0; i < numKeywords1; ++i) {
std::string keyword(ecl_file_iget_distinct_kw(ecl_file1, i));
keywords1.push_back(keyword);
}
for (unsigned int i = 0; i < numKeywords2; ++i) {
std::string keyword(ecl_file_iget_distinct_kw(ecl_file2, i));
keywords2.push_back(keyword);
}
if (file_type == ECL_UNIFIED_RESTART_FILE) {
loadWells( ecl_grid1 , ecl_file1 );
loadWells( ecl_grid2 , ecl_file2 );
}
rootName1 = basename1;
rootName2 = basename2;
}
ECLFilesComparator::~ECLFilesComparator() {
ecl_file_close(ecl_file1);
ecl_file_close(ecl_file2);
ecl_grid_free(ecl_grid1);
ecl_grid_free(ecl_grid2);
}
void ECLFilesComparator::printKeywords() const {
std::cout << "\nKeywords in the first file:\n";
for (const auto& it : keywords1) {
std::cout << std::setw(15) << std::left << it << " of type " << ecl_type_get_name( ecl_file_iget_named_data_type(ecl_file1, it.c_str(), 0)) << std::endl;
}
std::cout << "\nKeywords in second file:\n";
for (const auto& it : keywords2) {
std::cout << std::setw(15) << std::left << it << " of type " << ecl_type_get_name( ecl_file_iget_named_data_type(ecl_file2, it.c_str(), 0)) << std::endl;
}
}
void ECLFilesComparator::printKeywordsDifference() const {
std::vector<std::string> common;
std::vector<std::string> uncommon;
const std::vector<std::string>* keywordsShort = &keywords1;
const std::vector<std::string>* keywordsLong = &keywords2;
if (keywords1.size() > keywords2.size()) {
keywordsLong = &keywords1;
keywordsShort = &keywords2;
}
for (const auto& it : *keywordsLong) {
const auto position = std::find(keywordsShort->begin(), keywordsShort->end(), it);
if (position != keywordsShort->end()) {
common.push_back(*position);
}
else {
uncommon.push_back(it);
}
}
std::cout << "\nCommon keywords for the two cases:\n";
for (const auto& it : common) std::cout << it << std::endl;
std::cout << "\nUncommon keywords for the two cases:\n";
for (const auto& it : uncommon) std::cout << it << std::endl;
}
Deviation ECLFilesComparator::calculateDeviations(double val1, double val2) {
val1 = std::abs(val1);
val2 = std::abs(val2);
@@ -259,16 +120,15 @@ Deviation ECLFilesComparator::calculateDeviations(double val1, double val2) {
}
double ECLFilesComparator::median(std::vector<double> vec) {
if (vec.empty()) {
return 0;
}
else {
size_t n = vec.size()/2;
size_t n = vec.size() / 2;
nth_element(vec.begin(), vec.begin() + n, vec.end());
if (vec.size() % 2 == 0) {
return 0.5*(vec[n-1]+vec[n]);
return 0.5 * (vec[n-1] + vec[n]);
}
else {
return vec[n];
@@ -277,7 +137,6 @@ double ECLFilesComparator::median(std::vector<double> vec) {
}
double ECLFilesComparator::average(const std::vector<double>& vec) {
if (vec.empty()) {
return 0;
@@ -287,14 +146,3 @@ double ECLFilesComparator::average(const std::vector<double>& vec) {
}
double ECLFilesComparator::getCellVolume(const ecl_grid_type* ecl_grid,
const int globalIndex) {
std::vector<double> x(8, 0.0);
std::vector<double> y(8, 0.0);
std::vector<double> z(8, 0.0);
for (int i = 0; i < 8; i++) {
ecl_grid_get_cell_corner_xyz1(ecl_grid, globalIndex, i, &x.data()[i], &y.data()[i], &z.data()[i]);
}
return calculateCellVol(x,y,z);
}

View File

@@ -25,103 +25,61 @@
#include <vector>
#include <string>
struct ecl_file_struct; //!< Prototype for eclipse file struct, from ERT library.
typedef struct ecl_file_struct ecl_file_type;
struct ecl_grid_struct; //!< Prototype for eclipse grid struct, from ERT library.
typedef struct ecl_grid_struct ecl_grid_type;
struct ecl_kw_struct; //!< Prototype for eclipse keyword struct, from ERT library.
typedef struct ecl_kw_struct ecl_kw_type;
#include <examples/test_util/EGrid.hpp>
/*! \brief A class for comparing ECLIPSE files.
\details ECLFilesComparator opens ECLIPSE files
(unified restart, initial and RFT in addition to grid file)
from two simulations. This class has only the functions
printKeywords() and printKeywordsDifference(), in addition to a
couple of get-functions: the comparison logic is implemented in
the subclasses RegressionTest and IntegrationTest. */
class ECLFilesComparator {
private:
int file_type;
double absTolerance = 0;
double relTolerance = 0;
protected:
ecl_file_type* ecl_file1 = nullptr;
ecl_grid_type* ecl_grid1 = nullptr;
ecl_file_type* ecl_file2 = nullptr;
ecl_grid_type* ecl_grid2 = nullptr;
std::vector<std::string> keywords1, keywords2;
bool throwOnError = true; //!< Throw on first error
bool analysis = false; //!< Perform full error analysis
std::map<std::string, std::vector<Deviation>> deviations;
mutable size_t num_errors = 0;
public:
ECLFilesComparator(const std::string& basename1,
const std::string& basename2,
double absTolerance, double relTolerance);
//! \brief Checks if the keyword exists in both cases.
//! \param[in] keyword Keyword to check.
//! \details If the keyword does not exist in one of the cases, the function throws an exception.
void keywordValidForComparing(const std::string& keyword) const;
//! \brief Stores keyword data for a given occurrence
//! \param[out] ecl_kw1 Pointer to a ecl_kw_type, which stores keyword data for first case given the occurrence.
//! \param[out] ecl_kw2 Pointer to a ecl_kw_type, which stores keyword data for second case given the occurrence.
//! \param[in] keyword Which keyword to consider.
//! \param[in] occurrence Which keyword occurrence to consider.
//! \details This function stores keyword data for the given keyword and occurrence in #ecl_kw1 and #ecl_kw2, and returns the number of cells (for which the keyword has a value at the occurrence). If the number of cells differ for the two cases, an exception is thrown.
unsigned int getEclKeywordData(ecl_kw_type*& ecl_kw1, ecl_kw_type*& ecl_kw2, const std::string& keyword, int occurrence1, int occurrence2) const;
//! \brief Prints values for a given keyword, occurrence and cell
//! \param[in] keyword Which keyword to consider.
//! \param[in] occurrence Which keyword occurrence to consider.
//! \param[in] cell Which cell occurrence to consider (numbered by global index).
//! \param[in] value1 Value for first file, the data type can be bool, int, double or std::string.
//! \param[in] value2 Value for second file, the data type can be bool, int, double or std::string.
//! \details Templatefunction for printing values when exceptions are thrown. The function is defined for bool, int, double and std::string.
template <typename T>
void printValuesForCell(const std::string& keyword, int occurrence1, int occurrence2, size_t kw_size, size_t cell, const T& value1, const T& value2) const;
void throwOnErrors(bool dothrow) {
throwOnError = dothrow;
}
public:
//! \brief Open ECLIPSE files and set tolerances and keywords.
//! \param[in] file_type Specifies which filetype to be compared, possible inputs are UNRSTFILE, INITFILE and RFTFILE.
//! \param[in] basename1 Full path without file extension to the first case.
//! \param[in] basename2 Full path without file extension to the second case.
//! \param[in] absTolerance Tolerance for absolute deviation.
//! \param[in] relTolerance Tolerance for relative deviation.
//! \details The content of the ECLIPSE files specified in the input is stored in the ecl_file_type and ecl_grid_type member variables. In addition the keywords and absolute and relative tolerances (member variables) are set. If the constructor is unable to open one of the ECLIPSE files, an exception will be thrown.
ECLFilesComparator(int file_type, const std::string& basename1, const std::string& basename2, double absTolerance, double relTolerance);
//! \brief Closing the ECLIPSE files.
~ECLFilesComparator();
void doAnalysis(bool analize) {
analysis = analize;
}
//! \brief Set whether to throw on errors or not.
void throwOnErrors(bool dothrow) { throwOnError = dothrow; }
size_t getNoErrors() const {
return num_errors;
}
//! \brief Set whether to perform a full error analysis.
void doAnalysis(bool analize) { analysis = analize; }
//! \brief Returns the absolute tolerance stored as a private member variable in the class
double getAbsTolerance() const {
return absTolerance;
}
//! \brief Returns the relative tolerance stored as a private member variable in the class
double getRelTolerance() const {
return relTolerance;
}
//! \brief Returns the number of errors encountered in the performed comparisons.
size_t getNoErrors() const { return num_errors; }
//! \brief Calculate deviations for two values.
//! \details Using absolute values of the input arguments: If one of the values are non-zero, the Deviation::abs returned is the difference between the two input values. In addition, if both values are non-zero, the Deviation::rel returned is the absolute deviation divided by the largest value.
static Deviation calculateDeviations(double val1, double val2);
//! \brief Calculate median of a vector.
//! \details Returning the median of the input vector, i.e. the middle value of the sorted vector if the number of elements is odd or the mean of the two middle values if the number of elements are even. Copy is intentional.
static double median(std::vector<double> vec);
//! \brief Calculate average of a vector.
//! \details Returning the average of the input vector, i.e. the sum of all values divided by the number of elements.
static double average(const std::vector<double>& vec);
//! \brief Returns the ECLIPSE filetype of this
int getFileType() const {return file_type;}
//! \brief Returns the absolute tolerance stored as a private member variable in the class
double getAbsTolerance() const {return absTolerance;}
//! \brief Returns the relative tolerance stored as a private member variable in the class
double getRelTolerance() const {return relTolerance;}
protected:
std::vector<std::string> keywords1, keywords2;
bool throwOnError = true; //!< Throw on first error
bool analysis = false; //!< Perform full error analysis
std::map<std::string, std::vector<Deviation>> deviations;
mutable size_t num_errors = 0;
//! \brief Print all keywords and their respective Eclipse type for the two input cases.
void printKeywords() const;
//! \brief Print common and uncommon keywords for the two input cases.
void printKeywordsDifference() const;
std::string rootName1, rootName2;
//! \brief Calculate deviations for two values.
//! \details Using absolute values of the input arguments: If one of the values are non-zero, the Deviation::abs returned is the difference between the two input values. In addition, if both values are non-zero, the Deviation::rel returned is the absolute deviation divided by the largest value.
static Deviation calculateDeviations(double val1, double val2);
//! \brief Calculate median of a vector.
//! \details Returning the median of the input vector, i.e. the middle value of the sorted vector if the number of elements is odd or the mean of the two middle values if the number of elements are even.
static double median(std::vector<double> vec);
//! \brief Calculate average of a vector.
//! \details Returning the average of the input vector, i.e. the sum of all values divided by the number of elements.
static double average(const std::vector<double>& vec);
//! \brief Obtain the volume of a cell.
static double getCellVolume(const ecl_grid_type* ecl_grid, const int globalIndex);
template <typename T>
void printValuesForCell(const std::string& keyword, const std::string reference, size_t kw_size, size_t cell, EGrid *grid, const T& value1, const T& value2) const;
private:
double absTolerance = 0;
double relTolerance = 0;
};
#endif

View File

@@ -1,199 +0,0 @@
/*
Copyright 2016 Statoil ASA.
This file is part of the Open Porous Media project (OPM).
OPM 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.
OPM 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 for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#include "EclIntegrationTest.hpp"
#include <opm/common/ErrorMacros.hpp>
#include <algorithm>
#include <cmath>
#include <iostream>
#include <set>
#include <ert/ecl/ecl_file.h>
#include <ert/ecl/ecl_grid.h>
#include <ert/ecl/ecl_type.h>
#include <ert/ecl_well/well_info.h>
// helper macro to handle error throws or not
#define HANDLE_ERROR(type, message) \
{ \
if (throwOnError) \
OPM_THROW(type, message); \
else { \
std::cerr << message << std::endl; \
++num_errors; \
} \
}
void ECLIntegrationTest::setCellVolumes() {
double absTolerance = getAbsTolerance();
double relTolerance = getRelTolerance();
const unsigned int globalGridCount1 = ecl_grid_get_global_size(ecl_grid1);
const unsigned int activeGridCount1 = ecl_grid_get_active_size(ecl_grid1);
const unsigned int globalGridCount2 = ecl_grid_get_global_size(ecl_grid2);
const unsigned int activeGridCount2 = ecl_grid_get_active_size(ecl_grid2);
if (globalGridCount1 != globalGridCount2) {
OPM_THROW(std::runtime_error, "In grid file:"
<< "\nCells in first file: " << globalGridCount1
<< "\nCells in second file: " << globalGridCount2
<< "\nThe number of global cells differ.");
}
if (activeGridCount1 != activeGridCount2) {
OPM_THROW(std::runtime_error, "In grid file:"
<< "\nCells in first file: " << activeGridCount1
<< "\nCells in second file: " << activeGridCount2
<< "\nThe number of active cells differ.");
}
for (unsigned int cell = 0; cell < globalGridCount1; ++cell) {
const double cellVolume1 = getCellVolume(ecl_grid1, cell);
const double cellVolume2 = getCellVolume(ecl_grid2, cell);
Deviation dev = calculateDeviations(cellVolume1, cellVolume2);
if (dev.abs > absTolerance && dev.rel > relTolerance) {
int i, j, k;
ecl_grid_get_ijk1(ecl_grid1, cell, &i, &j, &k);
// Coordinates from this function are zero-based, hence incrementing
i++, j++, k++;
OPM_THROW(std::runtime_error, "In grid file: Deviations of cell volume exceed tolerances. "
<< "\nFor cell with coordinate (" << i << ", " << j << ", " << k << "):"
<< "\nCell volume in first file: " << cellVolume1
<< "\nCell volume in second file: " << cellVolume2
<< "\nThe absolute deviation is " << dev.abs << ", and the tolerance limit is " << absTolerance << "."
<< "\nThe relative deviation is " << dev.rel << ", and the tolerance limit is " << relTolerance << ".");
} // The second input case is used as reference.
cellVolumes.push_back(cellVolume2);
}
}
void ECLIntegrationTest::initialOccurrenceCompare(const std::string& keyword) {
ecl_kw_type* ecl_kw1 = nullptr;
ecl_kw_type* ecl_kw2 = nullptr;
const unsigned int numCells = getEclKeywordData(ecl_kw1, ecl_kw2, keyword, 0, 0);
std::vector<double> values1(numCells);
initialCellValues.resize(numCells);
ecl_kw_get_data_as_double(ecl_kw1, values1.data());
ecl_kw_get_data_as_double(ecl_kw2, initialCellValues.data());
// This variable sums up the difference between the keyword value for the first case and the keyword value for the second case, for each cell. The sum is weighted with respect to the cell volume of each cell.
double weightedDifference = 0;
// This variable sums up the keyword value for the first case for each cell. The sum is weighted with respect to the cell volume of each cell.
double weightedTotal = 0;
for (size_t cell = 0; cell < initialCellValues.size(); ++cell) {
weightedTotal += initialCellValues[cell]*cellVolumes[cell];
weightedDifference += std::abs(values1[cell] - initialCellValues[cell])*cellVolumes[cell];
}
if (weightedTotal != 0) {
double ratioValue = weightedDifference/weightedTotal;
if ((ratioValue) > getRelTolerance()) {
OPM_THROW(std::runtime_error, "\nFor keyword " << keyword << " and occurrence 0:"
<< "\nThe ratio of the deviation and the total value is " << ratioValue
<< ", which exceeds the relative tolerance of " << getRelTolerance() << "."
<< "\nSee the docs for more information about how the ratio is computed.");
}
}
}
void ECLIntegrationTest::occurrenceCompare(const std::string& keyword, int occurrence) const {
ecl_kw_type* ecl_kw1 = nullptr;
ecl_kw_type* ecl_kw2 = nullptr;
const unsigned int numCells = getEclKeywordData(ecl_kw1, ecl_kw2, keyword, occurrence, occurrence);
std::vector<double> values1(numCells), values2(numCells);
ecl_kw_get_data_as_double(ecl_kw1, values1.data());
ecl_kw_get_data_as_double(ecl_kw2, values2.data());
// This variable sums up the difference between the keyword value for the first case and the keyword value for the second case, for each cell. The sum is weighted with respect to the cell volume of each cell.
double weightedDifference = 0;
// This variable sums up the difference between the keyword value for the occurrence and the initial keyword value for each cell. The sum is weighted with respect to the cell volume of each cell.
double relativeWeightedTotal = 0;
for (size_t cell = 0; cell < values1.size(); ++cell) {
relativeWeightedTotal += std::abs(values1[cell] - initialCellValues[cell])*cellVolumes[cell];
weightedDifference += std::abs(values1[cell] - values2[cell])*cellVolumes[cell];
}
if (relativeWeightedTotal != 0) {
double ratioValue = weightedDifference/relativeWeightedTotal;
if ((ratioValue) > getRelTolerance()) {
OPM_THROW(std::runtime_error, "\nFor keyword " << keyword << " and occurrence " << occurrence << ":"
<< "\nThe ratio of the deviation and the total value is " << ratioValue
<< ", which exceeds the relative tolerance of " << getRelTolerance() << "."
<< "\nSee the docs for more information about how the ratio is computed.");
}
}
}
ECLIntegrationTest::ECLIntegrationTest(const std::string& basename1,
const std::string& basename2,
double absTolerance, double relTolerance) :
ECLFilesComparator(ECL_UNIFIED_RESTART_FILE, basename1, basename2, absTolerance, relTolerance) {
std::cout << "\nUsing cell volumes and keyword values from case " << basename2
<< " as reference." << std::endl << std::endl;
setCellVolumes();
}
bool ECLIntegrationTest::elementInWhitelist(const std::string& keyword) const {
auto it = std::find(keywordWhitelist.begin(), keywordWhitelist.end(), keyword);
return it != keywordWhitelist.end();
}
void ECLIntegrationTest::equalNumKeywords() const {
if (keywords1.size() != keywords2.size()) {
OPM_THROW(std::runtime_error, "\nKeywords in first file: " << keywords1.size()
<< "\nKeywords in second file: " << keywords2.size()
<< "\nThe number of keywords differ.");
}
}
void ECLIntegrationTest::results() {
for (const auto& it : keywordWhitelist)
resultsForKeyword(it);
}
void ECLIntegrationTest::resultsForKeyword(const std::string& keyword) {
std::cout << "Comparing " << keyword << "...";
keywordValidForComparing(keyword);
const unsigned int occurrences1 = ecl_file_get_num_named_kw(ecl_file1, keyword.c_str());
const unsigned int occurrences2 = ecl_file_get_num_named_kw(ecl_file2, keyword.c_str());
if (occurrences1 != occurrences2) {
OPM_THROW(std::runtime_error, "For keyword " << keyword << ":"
<< "\nKeyword occurrences in first file: " << occurrences1
<< "\nKeyword occurrences in second file: " << occurrences2
<< "\nThe number of occurrences differ.");
}
initialOccurrenceCompare(keyword);
for (unsigned int occurrence = 1; occurrence < occurrences1; ++occurrence) {
occurrenceCompare(keyword, occurrence);
}
std::cout << "done." << std::endl;
}

View File

@@ -1,78 +0,0 @@
/*
Copyright 2016 Statoil ASA.
This file is part of the Open Porous Media project (OPM).
OPM 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.
OPM 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 for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ECLINTEGRATIONTEST_HPP
#define ECLINTEGRATIONTEST_HPP
#include "EclFilesComparator.hpp"
/*! \brief A class for executing a integration test for two ECLIPSE files.
\details This class inherits from ECLFilesComparator, which opens and closes
the input cases and stores keywordnames. The three public functions
equalNumKeywords(), results() and resultsForKeyword() can be invoked
to compare griddata or keyworddata for all keywords or a given
keyword (resultsForKeyword()).
*/
class ECLIntegrationTest: public ECLFilesComparator {
private:
std::vector<double> cellVolumes; //!< Vector of cell volumes in second input case (indexed by global index)
std::vector<double> initialCellValues; //!< Keyword values for all cells at first occurrence (index by global index)
// These are the only keywords which are compared, since SWAT should be "1 - SOIL - SGAS", this keyword is omitted.
const std::vector<std::string> keywordWhitelist = {"SGAS", "SWAT", "PRESSURE"};
void setCellVolumes();
void initialOccurrenceCompare(const std::string& keyword);
void occurrenceCompare(const std::string& keyword, int occurrence) const;
public:
//! \brief Sets up the integration test.
//! \param[in] basename1 Full path without file extension to the first case.
//! \param[in] basename2 Full path without file extension to the second case.
//! \param[in] absTolerance Tolerance for absolute deviation.
//! \param[in] relTolerance Tolerance for relative deviation.
//! \details This constructor calls the constructor of the superclass, with input filetype unified restart. See the docs for ECLFilesComparator for more information.
ECLIntegrationTest(const std::string& basename1, const std::string& basename2, double absTolerance, double relTolerance);
//! \brief Checks if a keyword is supported for comparison.
//! \param[in] keyword Keyword to check.
bool elementInWhitelist(const std::string& keyword) const;
//! \brief Checks if the number of keywords equal in the two input cases.
//! \param[in] keyword Keyword to check.
void equalNumKeywords() const;
//! \brief Finds deviations for all supported keywords.
//! \details results() loops through all supported keywords for integration test (defined in keywordWhitelist -- this is SGAS, SWAT and PRESSURE) and calls resultsForKeyword() for each keyword.
void results();
//! \brief Finds deviations for a specific keyword.
//! \param[in] keyword Keyword to check.
/*! \details First, resultsForKeyword() checks if the keyword exits in both cases, and if the number of keyword occurrences in the two cases differ. If these tests fail, an exception is thrown. Then deviaitons are calculated as described below for each occurrence, and an exception is thrown if the relative error ratio \f$E\f$ is larger than the relative tolerance.
* Calculation:\n
* Let the keyword values for occurrence \f$n\f$ and cell \f$i\f$ be \f$p_{n,i}\f$ and \f$q_{n,i}\f$ for input case 1 and 2, respectively.
* Consider first the initial occurrence (\f$n=0\f$). The function uses the second cases as reference, and calculates the volume weighted sum of \f$q_{0,i}\f$ over all cells \f$i\f$:
* \f[ S_0 = \sum_{i} q_{0,i} v_i \f]
* where \f$v_{i}\f$ is the volume of cell \f$i\f$ in case 2. Then, the deviations between the cases for each cell are calculated:
* \f[ \Delta = \sum_{i} |p_{0,i} - q_{0,i}| v_i.\f]
* The error ratio is then \f$E = \Delta/S_0\f$.\n
* For all other occurrences \f$n\f$, the deviation value \f$\Delta\f$ is calculated the same way, but the total value \f$S\f$ is calculated relative to the initial occurrence total \f$S_0\f$:
* \f[ S = \sum_{i} |q_{n,i} - q_{0,i}| v_i. \f]
* The error ratio is \f$ E = \Delta/S\f$. */
void resultsForKeyword(const std::string& keyword);
};
#endif

View File

@@ -0,0 +1,397 @@
/*
Copyright 2019 Statoil ASA.
This file is part of the Open Porous Media project (OPM).
OPM 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.
OPM 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 for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#include "EclOutput.hpp"
#include "EclUtil.hpp"
#include <opm/common/ErrorMacros.hpp>
#include <algorithm>
#include <iterator>
#include <iomanip>
#include <stdexcept>
#include <typeinfo>
#include <sstream>
#include <stdio.h>
EclOutput::EclOutput(const std::string& inputFile, bool formatted) :
isFormatted(formatted)
{
ofileH.open(inputFile, isFormatted ? std::ios::out : std::ios::out | std::ios::binary);
}
template<>
void EclOutput::write<std::string>(const std::string& name,
const std::vector<std::string>& data)
{
if (isFormatted)
{
writeFormattedHeader(name, data.size(), EIOD::CHAR);
writeFormattedCharArray(data);
}
else
{
writeBinaryHeader(name, data.size(), EIOD::CHAR);
writeBinaryCharArray(data);
}
}
void EclOutput::writeBinaryHeader(const std::string&arrName, int size, EIOD::eclArrType arrType)
{
std::string name = arrName + std::string(8 - arrName.size(),' ');
int flippedSize = EIOD::flipEndianInt(size);
int bhead = EIOD::flipEndianInt(16);
ofileH.write(reinterpret_cast<char*>(&bhead), sizeof(bhead));
ofileH.write(name.c_str(), 8);
ofileH.write(reinterpret_cast<char*>(&flippedSize), sizeof(flippedSize));
switch(arrType) {
case EIOD::INTE:
ofileH.write("INTE", 4);
break;
case EIOD::REAL:
ofileH.write("REAL", 4);
break;
case EIOD::DOUB:
ofileH.write("DOUB", 4);
break;
case EIOD::LOGI:
ofileH.write("LOGI", 4);
break;
case EIOD::CHAR:
ofileH.write("CHAR", 4);
break;
case EIOD::MESS:
ofileH.write("MESS", 4);
break;
}
ofileH.write(reinterpret_cast<char *>(&bhead), sizeof(bhead));
}
template <typename T>
void EclOutput::writeBinaryArray(const std::vector<T>& data)
{
int rest,num,rval;
int dhead;
float value_f;
double value_d;
int intVal;
int n = 0;
int size = data.size();
EIOD::eclArrType arrType = EIOD::MESS;
if (typeid(std::vector<T>) == typeid(std::vector<int>)) {
arrType = EIOD::INTE;
} else if (typeid(std::vector<T>) == typeid(std::vector<float>)) {
arrType = EIOD::REAL;
} else if (typeid(std::vector<T>) == typeid(std::vector<double>)) {
arrType = EIOD::DOUB;
} else if (typeid(std::vector<T>) == typeid(std::vector<bool>)) {
arrType = EIOD::LOGI;
}
auto sizeData = block_size_data_binary(arrType);
int sizeOfElement = std::get<0>(sizeData);
int maxBlockSize = std::get<1>(sizeData);
int maxNumberOfElements = maxBlockSize / sizeOfElement;
if (!ofileH.is_open()) {
OPM_THROW(std::runtime_error, "fstream fileH not open for writing");
}
rest = size * sizeOfElement;
while (rest > 0) {
if (rest > maxBlockSize) {
rest -= maxBlockSize;
num = maxNumberOfElements;
} else {
num = rest / sizeOfElement;
rest = 0;
}
dhead = EIOD::flipEndianInt(num * sizeOfElement);
ofileH.write(reinterpret_cast<char*>(&dhead), sizeof(dhead));
for (int i = 0; i < num; i++) {
if (arrType == EIOD::INTE) {
rval = EIOD::flipEndianInt(data[n]);
ofileH.write(reinterpret_cast<char*>(&rval), sizeof(rval));
} else if (arrType == EIOD::REAL) {
value_f = EIOD::flipEndianFloat(data[n]);
ofileH.write(reinterpret_cast<char*>(&value_f), sizeof(value_f));
} else if (arrType == EIOD::DOUB) {
value_d = EIOD::flipEndianDouble(data[n]);
ofileH.write(reinterpret_cast<char*>(&value_d), sizeof(value_d));
} else if (arrType == EIOD::LOGI) {
intVal = data[n] ? EIOD::true_value : EIOD::false_value;
ofileH.write(reinterpret_cast<char*>(&intVal), sizeOfElement);
} else {
std::cout << "type not supported in write binaryarray" << std::endl;
exit(1);
}
n++;
}
ofileH.write(reinterpret_cast<char*>(&dhead), sizeof(dhead));
}
}
template void EclOutput::writeBinaryArray<int>(const std::vector<int>& data);
template void EclOutput::writeBinaryArray<float>(const std::vector<float>& data);
template void EclOutput::writeBinaryArray<double>(const std::vector<double>& data);
template void EclOutput::writeBinaryArray<bool>(const std::vector<bool>& data);
template void EclOutput::writeBinaryArray<char>(const std::vector<char>& data);
void EclOutput::writeBinaryCharArray(const std::vector<std::string>& data)
{
int num,dhead;
int n = 0;
int size = data.size();
auto sizeData = EIOD::block_size_data_binary(EIOD::CHAR);
int sizeOfElement = std::get<0>(sizeData);
int maxBlockSize = std::get<1>(sizeData);
int maxNumberOfElements = maxBlockSize / sizeOfElement;
int rest = size * sizeOfElement;
if (!ofileH.is_open()) {
OPM_THROW(std::runtime_error,"fstream fileH not open for writing");
}
while (rest > 0) {
if (rest > maxBlockSize) {
rest -= maxBlockSize;
num = maxNumberOfElements;
} else {
num = rest / sizeOfElement;
rest = 0;
}
dhead = EIOD::flipEndianInt(num * sizeOfElement);
ofileH.write(reinterpret_cast<char*>(&dhead), sizeof(dhead));
for (int i = 0; i < num; i++) {
std::string tmpStr = data[n] + std::string(8 - data[n].size(),' ');
ofileH.write(tmpStr.c_str(), sizeOfElement);
n++;
}
ofileH.write(reinterpret_cast<char*>(&dhead), sizeof(dhead));
}
}
void EclOutput::writeFormattedHeader(const std::string& arrName, int size, EIOD::eclArrType arrType)
{
std::string name = arrName + std::string(8 - arrName.size(),' ');
ofileH << " '" << name << "' " << std::setw(11) << size;
switch (arrType) {
case EIOD::INTE:
ofileH << " 'INTE'" << std::endl;
break;
case EIOD::REAL:
ofileH << " 'REAL'" << std::endl;
break;
case EIOD::DOUB:
ofileH << " 'DOUB'" << std::endl;
break;
case EIOD::LOGI:
ofileH << " 'LOGI'" << std::endl;
break;
case EIOD::CHAR:
ofileH << " 'CHAR'" << std::endl;
break;
case EIOD::MESS:
ofileH << " 'MESS'" << std::endl;
break;
}
}
std::string EclOutput::make_real_string(float value) const
{
char buffer [15];
sprintf (buffer, "%10.7E", value);
if (value == 0.0) {
return "0.00000000E+00";
} else {
std::string tmpstr(buffer);
int exp = value < 0.0 ? std::stoi(tmpstr.substr(11, 3)) : std::stoi(tmpstr.substr(10, 3));
if (value < 0.0) {
tmpstr = "-0." + tmpstr.substr(1, 1) + tmpstr.substr(3, 7) + "E";
} else {
tmpstr = "0." + tmpstr.substr(0, 1) + tmpstr.substr(2, 7) +"E";
}
sprintf (buffer, "%+03i", exp+1);
tmpstr = tmpstr+buffer;
return tmpstr;
}
}
std::string EclOutput::make_doub_string(double value) const
{
char buffer [21];
sprintf (buffer, "%19.13E", value);
if (value == 0.0) {
return "0.00000000000000D+00";
} else {
std::string tmpstr(buffer);
int exp = value < 0.0 ? std::stoi(tmpstr.substr(17, 4)) : std::stoi(tmpstr.substr(16, 4));
if (value < 0.0) {
if (abs(exp) < 100) {
tmpstr = "-0." + tmpstr.substr(1, 1) + tmpstr.substr(3, 13) + "D";
} else {
tmpstr = "-0." + tmpstr.substr(1, 1) + tmpstr.substr(3, 13);
}
} else {
if (abs(exp) < 100) {
tmpstr = "0." + tmpstr.substr(0, 1) + tmpstr.substr(2, 13) + "D";
} else {
tmpstr = "0." + tmpstr.substr(0, 1) + tmpstr.substr(2, 13);
}
}
sprintf (buffer, "%+03i", exp+1);
tmpstr = tmpstr + buffer;
return tmpstr;
}
}
template <typename T>
void EclOutput::writeFormattedArray(const std::vector<T>& data)
{
int size = data.size();
int n = 0;
EIOD::eclArrType arrType = EIOD::MESS;
if (typeid(T) == typeid(int)) {
arrType = EIOD::INTE;
} else if (typeid(T) == typeid(float)) {
arrType = EIOD::REAL;
} else if (typeid(T) == typeid(double)) {
arrType = EIOD::DOUB;
} else if (typeid(T) == typeid(bool)) {
arrType = EIOD::LOGI;
}
auto sizeData = EIOD::block_size_data_formatted(arrType);
int maxBlockSize = std::get<0>(sizeData);
int nColumns = std::get<1>(sizeData);
int columnWidth = std::get<2>(sizeData);
for (int i = 0; i < size; i++) {
n++;
switch (arrType) {
case EIOD::INTE:
ofileH << std::setw(columnWidth) << data[i];
break;
case EIOD::REAL:
ofileH << std::setw(columnWidth) << make_real_string(data[i]);
break;
case EIOD::DOUB:
ofileH << std::setw(columnWidth) << make_doub_string(data[i]);
break;
case EIOD::LOGI:
if (data[i]) {
ofileH << " T";
} else {
ofileH << " F";
}
break;
default:
break;
}
if ((n % nColumns) == 0 || (n % maxBlockSize) == 0) {
ofileH << std::endl;
}
if ((n % maxBlockSize) == 0) {
n=0;
}
}
if ((n % nColumns) != 0 && (n % maxBlockSize) != 0) {
ofileH << std::endl;
}
}
template void EclOutput::writeFormattedArray<int>(const std::vector<int>& data);
template void EclOutput::writeFormattedArray<float>(const std::vector<float>& data);
template void EclOutput::writeFormattedArray<double>(const std::vector<double>& data);
template void EclOutput::writeFormattedArray<bool>(const std::vector<bool>& data);
template void EclOutput::writeFormattedArray<char>(const std::vector<char>& data);
void EclOutput::writeFormattedCharArray(const std::vector<std::string>& data)
{
auto sizeData = EIOD::block_size_data_formatted(EIOD::CHAR);
int nColumns = std::get<1>(sizeData);
int size = data.size();
for (int i = 0; i < size; i++) {
std::string str1(8,' ');
str1 = data[i] + std::string(8 - data[i].size(),' ');
ofileH << " '" << str1 << "'";
if ((i+1) % nColumns == 0) {
ofileH << std::endl;
}
}
if ((size % nColumns) != 0) {
ofileH << std::endl;
}
}

View File

@@ -0,0 +1,94 @@
/*
Copyright 2019 Statoil ASA.
This file is part of the Open Porous Media project (OPM).
OPM 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.
OPM 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 for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ECL_OUTPUT_HPP
#define ECL_OUTPUT_HPP
#include <iostream>
#include <fstream>
#include <vector>
#include <iomanip>
#include <typeinfo>
#include <examples/test_util/data/EclIOdata.hpp>
namespace EIOD = Opm::ecl;
class EclOutput
{
public:
EclOutput(const std::string& inputFile, bool formatted);
template<typename T>
void write(const std::string& name,
const std::vector<T>& data)
{
EIOD::eclArrType arrType = EIOD::MESS;
if (typeid(T) == typeid(int))
arrType = EIOD::INTE;
else if (typeid(T) == typeid(float))
arrType = EIOD::REAL;
else if (typeid(T) == typeid(double))
arrType = EIOD::DOUB;
else if (typeid(T) == typeid(bool))
arrType = EIOD::LOGI;
else if (typeid(T) == typeid(char))
arrType = EIOD::MESS;
if (isFormatted)
{
writeFormattedHeader(name, data.size(), arrType);
if (arrType != EIOD::MESS)
writeFormattedArray(data);
}
else
{
writeBinaryHeader(name, data.size(), arrType);
if (arrType != EIOD::MESS)
writeBinaryArray(data);
}
}
private:
void writeBinaryHeader(const std::string& arrName, int size, EIOD::eclArrType arrType);
template <typename T>
void writeBinaryArray(const std::vector<T>& data);
void writeBinaryCharArray(const std::vector<std::string>& data);
void writeFormattedHeader(const std::string& arrName, int size, EIOD::eclArrType arrType);
template <typename T>
void writeFormattedArray(const std::vector<T>& data);
void writeFormattedCharArray(const std::vector<std::string>& data);
std::string make_real_string(float value) const;
std::string make_doub_string(double value) const;
std::ofstream ofileH;
bool isFormatted;
};
template<>
void EclOutput::write<std::string>(const std::string& name,
const std::vector<std::string>& data);
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
/*
Copyright 2016 Statoil ASA.
Copyright 2019 Equinor ASA.
This file is part of the Open Porous Media project (OPM).
@@ -21,6 +21,11 @@
#define ECLREGRESSIONTEST_HPP
#include "EclFilesComparator.hpp"
#include "data/EclIOdata.hpp"
namespace EIOD = Opm::ecl;
/*! \brief A class for executing a regression test for two ECLIPSE files.
\details This class inherits from ECLFilesComparator, which opens and
@@ -29,65 +34,146 @@
resultsForKeyword() can be invoked to compare griddata
or keyworddata for all keywords or a given keyword (resultsForKeyword()).
*/
class ECLRegressionTest: public ECLFilesComparator {
private:
// These vectors store absolute and relative deviations, respecively. Note that they are whiped clean for every new keyword comparison.
std::vector<double> absDeviation, relDeviation;
// Keywords which should not contain negative values, i.e. uses allowNegativeValues = false in deviationsForCell():
const std::vector<std::string> keywordDisallowNegatives = {"SGAS", "SWAT", "PRESSURE"};
public:
//! \brief Sets up the regression test.
//! \param[in] basename1 Full path without file extension to the first case.
//! \param[in] basename2 Full path without file extension to the second case.
//! \param[in] absTolerance Tolerance for absolute deviation.
//! \param[in] relTolerance Tolerance for relative deviation.
//! \details This constructor only calls the constructor of the superclass, see the docs for ECLFilesComparator for more information.
ECLRegressionTest(const std::string& basename1, const std::string& basename2, double absTolerance, double relTolerance):
ECLFilesComparator(basename1, basename2, absTolerance, relTolerance) {}
// Only compare last occurrence
bool onlyLastOccurrence = false;
~ECLRegressionTest();
// Accept extra keywords in the restart file of the 'new' simulation.
bool acceptExtraKeywords = false;
//! \brief Option to only compare last occurrence
void setOnlyLastReportNumber(bool onlyLastSequenceArg) {
this->onlyLastSequence = onlyLastSequenceArg;
}
int countDev() { return deviations.size(); }
// Prints results stored in absDeviation and relDeviation.
void printResultsForKeyword(const std::string& keyword) const;
// Accept extra keywords: If this switch is set to true the comparison
// will ignore extra keywords which are only present
// in the new simulation.
// Function which compares data at specific occurrences and for a specific keyword type. The functions takes two occurrence inputs to also be able to
// compare keywords which are shifted relative to each other in the two files. This is for instance handy when running flow with restart from different timesteps,
// and comparing the last timestep from the two runs.
void boolComparisonForOccurrence(const std::string& keyword, int occurrence1, int occurrence2) const;
void charComparisonForOccurrence(const std::string& keyword, int occurrence1, int occurrence2) const;
void intComparisonForOccurrence(const std::string& keyword, int occurrence1, int occurrence2) const;
void doubleComparisonForOccurrence(const std::string& keyword, int occurrence1, int occurrence2);
// deviationsForCell throws an exception if both the absolute deviation AND the relative deviation
// are larger than absTolerance and relTolerance, respectively. In addition,
// if allowNegativeValues is passed as false, an exception will be thrown when the absolute value
// of a negative value exceeds absTolerance. If no exceptions are thrown, the absolute and relative deviations are added to absDeviation and relDeviation.
void deviationsForCell(double val1, double val2, const std::string& keyword, int occurrence1, int occurrence2, size_t kw_size, size_t cell, bool allowNegativeValues = true);
public:
//! \brief Sets up the regression test.
//! \param[in] file_type Specifies which filetype to be compared, possible inputs are UNRSTFILE, INITFILE and RFTFILE.
//! \param[in] basename1 Full path without file extension to the first case.
//! \param[in] basename2 Full path without file extension to the second case.
//! \param[in] absTolerance Tolerance for absolute deviation.
//! \param[in] relTolerance Tolerance for relative deviation.
//! \details This constructor only calls the constructor of the superclass, see the docs for ECLFilesComparator for more information.
ECLRegressionTest(int file_type, const std::string& basename1, const std::string& basename2, double absTolerance, double relTolerance):
ECLFilesComparator(file_type, basename1, basename2, absTolerance, relTolerance) {}
void setAcceptExtraKeywords(bool acceptExtraKeywordsArg) {
this->acceptExtraKeywords = acceptExtraKeywordsArg;
}
//! \brief Option to only compare last occurrence
void setOnlyLastOccurrence(bool onlyLastOccurrenceArg) {this->onlyLastOccurrence = onlyLastOccurrenceArg;}
void setIntegrationTest(bool inregrationTestArg) {
this->integrationTest = inregrationTestArg;
}
// Accept extra keywords: If this switch is set to true the comparison
// of restart files will ignore extra keywords which are only present
// in the new simulation.
void setAcceptExtraKeywords(bool acceptExtraKeywordsArg) { this->acceptExtraKeywords = acceptExtraKeywordsArg; }
void setPrintKeywordOnly(bool printArg) {
this->printKeywordOnly = printArg;
}
//! \brief Compares grid properties of the two cases.
// gridCompare() checks if both the number of active and global cells in the two cases are the same. If they are, and volumecheck is true, all cells are looped over to calculate the cell volume deviation for the two cases. If the both the relative and absolute deviation exceeds the tolerances, an exception is thrown.
void gridCompare(const bool volumecheck) const;
//! \brief Calculates deviations for all keywords.
// This function checks if the number of keywords of the two cases are equal, and if it is, resultsForKeyword() is called for every keyword. If not, an exception is thrown.
void results();
//! \brief Calculates deviations for a specific keyword.
//! \param[in] keyword Keyword which should be compared, if this keyword is absent in one of the cases, an exception will be thrown.
//! \details This function loops through every report step and every cell and compares the values for the given keyword from the two input cases. If the absolute or relative deviation between the two values for each step exceeds both the absolute tolerance and the relative tolerance (stored in ECLFilesComparator), an exception is thrown. In addition, some keywords are marked for "disallow negative values" -- these are SGAS, SWAT and PRESSURE. An exception is thrown if a value of one of these keywords is both negative and has an absolute value larger than the absolute tolerance. If no exceptions are thrown, resultsForKeyword() uses the private member funtion printResultsForKeyword to print the average and median deviations.
void resultsForKeyword(const std::string& keyword);
void compareSpesificKeyword(std::string keyword) {
this->spesificKeyword = keyword;
}
void compareSpesificRstReportStepNumber(int seqn) {
this->spesificSequence = seqn;
}
void setLoadBaseRunData(bool loadArg) {
this->loadBaseRunData = loadArg;
}
void loadGrids();
void printDeviationReport();
void gridCompare();
void results_rst();
void results_init();
void results_smry();
void results_rft();
private:
bool checkFileName(const std::string& rootName, const std::string& extension, std::string& filename);
// Prints results stored in absDeviation and relDeviation.
void printResultsForKeyword(const std::string& keyword) const;
void printComparisonForKeywordLists(const std::vector<std::string>& arrayList1,
const std::vector<std::string>& arrayList2) const;
void printComparisonForKeywordLists(const std::vector<std::string>& arrayList1,
const std::vector<std::string>& arrayList2,
const std::vector<EIOD::eclArrType>& arrayType1,
const std::vector<EIOD::eclArrType>& arrayType2) const;
void printMissingKeywords(const std::vector<std::string>& arrayList1,
const std::vector<std::string>& arrayList2) const;
void compareKeywords(const std::vector<std::string>& keywords1,
const std::vector<std::string>& keywords2,
const std::string& reference);
void checkSpesificKeyword(std::vector<std::string>& keywords1,
std::vector<std::string>& keywords2,
std::vector<EIOD::eclArrType>& arrayType1,
std::vector<EIOD::eclArrType>& arrayType2,
const std::string& reference);
template <typename T>
void compareVectors(const std::vector<T>& t1, const std::vector<T>& t2,
const std::string& keyword, const std::string& reference);
template <typename T>
void compareFloatingPointVectors(const std::vector<T>& t1, const std::vector<T> &t2,
const std::string& keyword, const std::string& reference);
// deviationsForCell throws an exception if both the absolute deviation AND the relative deviation
// are larger than absTolerance and relTolerance, respectively. In addition,
// if allowNegativeValues is passed as false, an exception will be thrown when the absolute value
// of a negative value exceeds absTolerance. If no exceptions are thrown, the absolute and relative deviations are added to absDeviation and relDeviation.
// void deviationsForCell(double val1, double val2, const std::string& keyword, const std::string reference, size_t kw_size, size_t cell, bool allowNegativeValues = true);
void deviationsForCell(double val1, double val2, const std::string& keyword,
const std::string& reference, size_t kw_size, size_t cell,
bool allowNegativeValues, bool useStrictTol);
template <typename T>
void deviationsForNonFloatingPoints(T val1, T val2, const std::string& keyword,
const std::string& reference,
size_t kw_size, size_t cell);
// These vectors store absolute and relative deviations, respecively. Note that they are whiped clean for every new keyword comparison.
std::vector<double> absDeviation, relDeviation;
// Keywords which should not contain negative values, i.e. uses allowNegativeValues = false in deviationsForCell():
const std::vector<std::string> keywordDisallowNegatives = {"SGAS", "SWAT", "PRESSURE"};
double strictAbsTol = 1e-6;
double strictRelTol = 1e-6;
// keywords that triggers strict tolerances
const std::vector<std::string> keywordsStrictTol = {"COORD", "ZCORN", "PORV", "DEPTH", "DX", "DY", "DZ", "PERMX", "PERMY", "PERMZ", "NTG",
"TRANX", "TRANY", "TRANZ", "TRANNNC", "SGRP", "SWEL", "SCON", "DOUBHEAD"
};
// Only compare last occurrence
bool onlyLastSequence = false;
bool integrationTest = false;
bool printKeywordOnly = false;
bool loadBaseRunData = false;
// spesific keyword to be compared
std::string spesificKeyword;
// spesific restart sequence to be compared
int spesificSequence = -1;
// Accept extra keywords in the restart file of the 'new' simulation.
bool acceptExtraKeywords = false;
EGrid* grid1 = nullptr;
EGrid* grid2 = nullptr;
};
#endif

View File

@@ -0,0 +1,127 @@
/*
Copyright 2019 Equinor ASA.
This file is part of the Open Porous Media project (OPM).
OPM 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.
OPM 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 for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#include "EclUtil.hpp"
#include <opm/common/ErrorMacros.hpp>
#include <algorithm>
#include <stdexcept>
namespace EIOD = Opm::ecl;
int Opm::ecl::flipEndianInt(int num)
{
unsigned int tmp = __builtin_bswap32(num);
return static_cast<int>(tmp);
}
float Opm::ecl::flipEndianFloat(float num)
{
float value = num;
char* floatToConvert = reinterpret_cast<char*>(&value);
std::reverse(floatToConvert, floatToConvert+4);
return value;
}
double Opm::ecl::flipEndianDouble(double num)
{
double value = num;
char* doubleToConvert = reinterpret_cast<char*>(&value);
std::reverse(doubleToConvert, doubleToConvert+8);
return value;
}
std::tuple<int, int> Opm::ecl::block_size_data_binary(eclArrType arrType)
{
using BlockSizeTuple = std::tuple<int, int>;
switch (arrType) {
case EIOD::INTE:
return BlockSizeTuple{EIOD::sizeOfInte, EIOD::MaxBlockSizeInte};
break;
case EIOD::REAL:
return BlockSizeTuple{EIOD::sizeOfReal, EIOD::MaxBlockSizeReal};
break;
case EIOD::DOUB:
return BlockSizeTuple{EIOD::sizeOfDoub, EIOD::MaxBlockSizeDoub};
break;
case EIOD::LOGI:
return BlockSizeTuple{EIOD::sizeOfLogi, EIOD::MaxBlockSizeLogi};
break;
case EIOD::CHAR:
return BlockSizeTuple{EIOD::sizeOfChar, EIOD::MaxBlockSizeChar};
break;
case EIOD::MESS:
OPM_THROW(std::invalid_argument, "Type 'MESS' have no associated data");
break;
default:
OPM_THROW(std::invalid_argument, "Unknown field type");
break;
}
}
std::tuple<int, int, int> Opm::ecl::block_size_data_formatted(EIOD::eclArrType arrType)
{
using BlockSizeTuple = std::tuple<int, int, int>;
switch (arrType) {
case EIOD::INTE:
return BlockSizeTuple{EIOD::MaxNumBlockInte, EIOD::numColumnsInte, EIOD::columnWidthInte};
break;
case EIOD::REAL:
return BlockSizeTuple{EIOD::MaxNumBlockReal,EIOD::numColumnsReal, EIOD::columnWidthReal};
break;
case EIOD::DOUB:
return BlockSizeTuple{EIOD::MaxNumBlockDoub,EIOD::numColumnsDoub, EIOD::columnWidthDoub};
break;
case EIOD::LOGI:
return BlockSizeTuple{EIOD::MaxNumBlockLogi,EIOD::numColumnsLogi, EIOD::columnWidthLogi};
break;
case EIOD::CHAR:
return BlockSizeTuple{EIOD::MaxNumBlockChar,EIOD::numColumnsChar, EIOD::columnWidthChar};
break;
case EIOD::MESS:
OPM_THROW(std::invalid_argument, "Type 'MESS' have no associated data") ;
break;
default:
OPM_THROW(std::invalid_argument, "Unknown field type");
break;
}
}
std::string Opm::ecl::trimr(const std::string &str1)
{
if (str1 == " ") {
return "";
} else {
int p = str1.find_last_not_of(" ");
return str1.substr(0,p+1);
}
}

View File

@@ -0,0 +1,40 @@
/*
Copyright 2019 Equinor ASA.
This file is part of the Open Porous Media project (OPM).
OPM 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.
OPM 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 for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ECL_UTIL_HPP
#define ECL_UTIL_HPP
#include <string>
#include <tuple>
#include <examples/test_util/data/EclIOdata.hpp>
namespace Opm {
namespace ecl {
int flipEndianInt(int num);
float flipEndianFloat(float num);
double flipEndianDouble(double num);
std::tuple<int, int> block_size_data_binary(eclArrType arrType);
std::tuple<int, int, int> block_size_data_formatted(eclArrType arrType);
std::string trimr(const std::string &str1);
}
}
#endif

View File

@@ -1,5 +1,5 @@
/*
Copyright 2016 Statoil ASA.
Copyright 2019 Equinor ASA.
This file is part of the Open Porous Media project (OPM).
@@ -16,221 +16,129 @@
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#include "EclIntegrationTest.hpp"
#include "EclRegressionTest.hpp"
#include "summaryIntegrationTest.hpp"
#include "summaryRegressionTest.hpp"
#include <opm/common/ErrorMacros.hpp>
#include <ert/util/util.h>
#include <ert/util/stringlist.h>
#include <ert/ecl/ecl_endian_flip.h>
#include <ert/ecl/ecl_file.h>
#include <iostream>
#include <string>
#include <getopt.h>
static void printHelp() {
std::cout << "\ncompareECL compares ECLIPSE files (restart (.RST), unified restart (.UNRST), initial (.INIT), summary (.SMRY), unified summary (.UNSMRY) or .RFT) and gridsizes (from .EGRID or .GRID file) from two simulations.\n"
<< "The program takes four arguments:\n\n"
<< "1. Case number 1 (full path without extension)\n"
<< "2. Case number 2 (full path without extension)\n"
<< "3. Absolute tolerance\n"
<< "4. Relative tolerance (between 0 and 1)\n\n"
<< "In addition, the program takes these options (which must be given before the arguments):\n\n"
<< "-a Run a full analysis of errors.\n"
<< "-g Will print the vector with the greatest error ratio.\n"
<< "-h Print help and exit.\n"
<< "-i Execute integration test (regression test is default).\n"
<< " The integration test compares SGAS, SWAT and PRESSURE in unified restart files, so this option can not be used in combination with -t.\n"
<< "-I Same as -i, but throws an exception when the number of keywords in the two cases differ. Can not be used in combination with -t.\n"
<< "-k Specify specific keyword to compare (capitalized), for example -k PRESSURE.\n"
<< "-K Will not allow different amount of keywords in the two files. Throws an exception if the amount are different.\n"
<< "-l Only do comparison for the last occurrence. This option is only for the regression test, and can therefore not be used in combination with -i or -I.\n"
<< "-m mainVar. Will calculate the error ratio for one main variable. Valid input is WOPR, WWPR, WGPR or WBHP.\n"
<< "-n Do not throw on errors.\n"
<< "-p Print keywords in both cases and exit. Can not be used in combination with -P.\n"
<< "-P Print common and uncommon keywords in both cases and exit. Can not be used in combination with -p.\n"
<< "-R Will allow comparison between a restarted simulation and a normal simulation for summary regression tests. The files must end at the same time.\n"
<< "-s int Sets the number of spikes that are allowed for each keyword in summary integration tests.\n"
<< "-t Specify ECLIPSE filetype to compare (unified restart is default). Can not be used in combination with -i or -I. Different possible arguments are:\n"
<< " -t UNRST \t Compare two unified restart files (.UNRST). This the default value, so it is the same as not passing option -t.\n"
<< " -t INIT \t Compare two initial files (.INIT).\n"
<< " -t RFT \t Compare two RFT files (.RFT).\n"
<< " -t RST \t Compare two cases consisting of restart files (.Xnnnn).\n"
<< " -t SMRY \t Compare two cases consistent of (unified) summary files.\n"
<< " -t RST1 \t Compare two cases where the first case consists of restart files (.Xnnnn), and the second case consists of a unified restart file (.UNRST).\n"
<< " -t RST2 \t Compare two cases where the first case consists of a unified restart file (.UNRST), and the second case consists of restart files (.Xnnnn).\n"
<< " Note that when dealing with restart files (.Xnnnn), the program concatenates all of them into one unified restart file, which is used for comparison and stored in the same directory as the restart files.\n"
<< " This will overwrite any existing unified restart file in that directory.\n\n"
<< "-v For the rate keywords WOPR, WGPR, WWPR and WBHP. Calculates the error volume of the two summary files. This is printed to screen.\n"
<< "\nExample usage of the program: \n\n"
<< "compareECL -k PRESSURE <path to first casefile> <path to second casefile> 1e-3 1e-5\n"
<< "compareECL -t INIT -k PORO <path to first casefile> <path to second casefile> 1e-3 1e-5\n"
<< "compareECL -i <path to first casefile> <path to second casefile> 0.01 1e-6\n\n"
<< "Exceptions are thrown (and hence program exits) when deviations are larger than the specified "
<< "tolerances, or when the number of cells does not match -- either in the grid file or for a "
<< "specific keyword. Information about the keyword, keyword occurrence (zero based) and cell "
<< "coordinate is printed when an exception is thrown. For more information about how the cases "
<< "are compared, see the documentation of the EclFilesComparator class.\n\n";
<< "The program takes four arguments:\n\n"
<< "1. Case number 1 (full path without extension)\n"
<< "2. Case number 2 (full path without extension)\n"
<< "3. Absolute tolerance\n"
<< "4. Relative tolerance (between 0 and 1)\n\n"
<< "In addition, the program takes these options (which must be given before the arguments):\n\n"
<< "-a Run a full analysis of errors.\n"
<< "-h Print help and exit.\n"
<< "-i Execute integration test (regression test is default).\n"
<< " The integration test compares SGAS, SWAT and PRESSURE in unified restart files, and WOPR, WGPR, WWPR and WBHP (all wells) in summary file. \n"
<< "-k Specify specific keyword to compare (capitalized), for examples -k PRESSURE or -k WOPR:A-1H \n"
<< "-l Only do comparison for the last Report Step. This option is only valid for restart files.\n"
<< "-n Do not throw on errors.\n"
<< "-p Print keywords in both cases and exit.\n"
<< "-r compare a spesific report time step number in a restart file.\n"
<< "-t Specify ECLIPSE filetype to compare, (default behaviour is that all files are compared if found). Different possible arguments are:\n"
<< " -t UNRST \t Compare two unified restart files (.UNRST). This the default value, so it is the same as not passing option -t.\n"
<< " -t EGRID \t Compare two EGrid files (.EGRID).\n"
<< " -t INIT \t Compare two initial files (.INIT).\n"
<< " -t RFT \t Compare two RFT files (.RFT).\n"
<< " -t SMRY \t Compare two cases consistent of (unified) summary files.\n"
<< "-x Allow extra keywords in case number 2. These additional keywords (not found in case number1) will be ignored in the comparison.\n"
<< "\nExample usage of the program: \n\n"
<< "compareECL -k PRESSURE <path to first casefile> <path to second casefile> 1e-3 1e-5\n"
<< "compareECL -t INIT -k PORO <path to first casefile> <path to second casefile> 1e-3 1e-5\n"
<< "compareECL -i <path to first casefile> <path to second casefile> 0.01 1e-6\n\n"
<< "Exceptions are thrown (and hence program exits) when deviations are larger than the specified "
<< "tolerances, or when the number of cells does not match -- either in the grid file or for a "
<< "specific keyword. Information about the keyword, keyword occurrence (zero based) and cell "
<< "coordinate is printed when an exception is thrown. For more information about how the cases "
<< "are compared, see the documentation of the EclFilesComparator class.\n\n";
}
void splitBasename(const std::string& basename, std::string& path, std::string& filename) {
const size_t lastSlashIndex = basename.find_last_of("/\\");
path = basename.substr(0,lastSlashIndex);
filename = basename.substr(lastSlashIndex+1);
}
// Inspired by the ecl_pack application in the ERT library
void concatenateRestart(const std::string& basename) {
std::string inputPath, inputBase;
splitBasename(basename, inputPath, inputBase);
stringlist_type* inputFiles = stringlist_alloc_new();
const int numFiles = ecl_util_select_filelist(inputPath.c_str(), inputBase.c_str(), ECL_RESTART_FILE, false, inputFiles);
const char* target_file_name = ecl_util_alloc_filename(inputPath.c_str(), inputBase.c_str(), ECL_UNIFIED_RESTART_FILE, false, -1);
fortio_type* target = fortio_open_writer(target_file_name, false, ECL_ENDIAN_FLIP);
int dummy;
ecl_kw_type* seqnum_kw = ecl_kw_alloc_new("SEQNUM", 1, ECL_INT, &dummy);
int reportStep = 0;
for (int i = 0; i < numFiles; ++i) {
ecl_util_get_file_type(stringlist_iget(inputFiles, i), nullptr, &reportStep);
ecl_file_type* src_file = ecl_file_open(stringlist_iget(inputFiles, i), 0);
ecl_kw_iset_int(seqnum_kw, 0, reportStep);
ecl_kw_fwrite(seqnum_kw, target);
ecl_file_fwrite_fortio(src_file, target, 0);
ecl_file_close(src_file);
}
fortio_fclose(target);
stringlist_free(inputFiles);
}
//------------------------------------------------//
int main(int argc, char** argv) {
// Restart is default
ecl_file_enum file_type = ECL_UNIFIED_RESTART_FILE;
// RegressionTest is default
bool integrationTest = false;
bool allowDifferentAmount = true;
bool checkNumKeywords = false;
bool findGreatestErrorRatio = false;
bool findVolumeError = false;
bool onlyLastOccurrence = false;
bool printKeywords = false;
bool printKeywordsDifference = false;
bool restartFile = false;
bool specificKeyword = false;
bool specificFileType = false;
bool allowSpikes = false;
bool throwOnError = true;
bool throwTooGreatErrorRatio = true;
bool acceptExtraKeywords = false;
bool analysis = false;
bool volumecheck = true;
char* keyword = nullptr;
char* fileTypeCstr = nullptr;
const char* mainVariable = nullptr;
int c = 0;
int spikeLimit = -1;
bool integrationTest = false;
bool onlyLastSequence = false;
bool printKeywords = false;
bool specificKeyword = false;
bool specificReportStepNumber = false;
bool specificFileType = false;
bool throwOnError = true;
bool restartFile = false;
bool acceptExtraKeywords = false;
bool analysis = false;
char* keyword = nullptr;
int c = 0;
int reportStepNumber = -1;
std::string fileTypeString;
while ((c = getopt(argc, argv, "hiIk:alnpPt:VRgs:m:vKx")) != -1) {
while ((c = getopt(argc, argv, "hik:alnpt:Rr:x")) != -1) {
switch (c) {
case 'a':
analysis = true;
break;
case 'g':
findGreatestErrorRatio = true;
throwTooGreatErrorRatio = false;
break;
case 'h':
printHelp();
return 0;
case 'i':
integrationTest = true;
break;
case 'I':
integrationTest = true;
checkNumKeywords = true;
break;
case 'k':
specificKeyword = true;
keyword = optarg;
break;
case 'K':
allowDifferentAmount = false;
break;
case 'l':
onlyLastOccurrence = true;
break;
case 'm':
mainVariable = optarg;
break;
case 'n':
throwOnError = false;
break;
case 'p':
printKeywords = true;
break;
case 'P':
printKeywordsDifference = true;
break;
case 'R':
restartFile = true;
break;
case 's':
allowSpikes = true;
spikeLimit = atof(optarg);
break;
case 't':
specificFileType = true;
fileTypeCstr = optarg;
break;
case 'v':
findVolumeError = true;
break;
case 'V':
volumecheck = false;
break;
case 'x':
acceptExtraKeywords = true;
break;
case '?':
if (optopt == 'k' || optopt == 'm' || optopt == 's') {
std::cerr << "Option " << optopt << " requires a keyword as argument, see manual (-h) for more information." << std::endl;
return EXIT_FAILURE;
}
else if (optopt == 't') {
std::cerr << "Option t requires an ECLIPSE filetype as argument, see manual (-h) for more information." << std::endl;
return EXIT_FAILURE;
}
else {
std::cerr << "Unknown option." << std::endl;
return EXIT_FAILURE;
}
default:
case 'a':
analysis = true;
break;
case 'h':
printHelp();
return 0;
case 'i':
integrationTest = true;
break;
case 'k':
specificKeyword = true;
keyword = optarg;
break;
case 'l':
onlyLastSequence = true;
break;
case 'n':
throwOnError = false;
break;
case 'p':
printKeywords = true;
break;
case 'r':
specificReportStepNumber=true;
reportStepNumber = atoi(optarg);
break;
case 'R':
restartFile = true;
break;
case 't':
specificFileType = true;
fileTypeString=optarg;
break;
case 'x':
acceptExtraKeywords = true;
break;
case '?':
if (optopt == 'k' || optopt == 'm' || optopt == 's') {
std::cerr << "Option " << optopt << " requires a keyword as argument, see manual (-h) for more information." << std::endl;
return EXIT_FAILURE;
}
else if (optopt == 't') {
std::cerr << "Option t requires an ECLIPSE filetype as argument, see manual (-h) for more information." << std::endl;
return EXIT_FAILURE;
}
else {
std::cerr << "Unknown option." << std::endl;
return EXIT_FAILURE;
}
default:
return EXIT_FAILURE;
}
}
int argOffset = optind;
if ((printKeywords && printKeywordsDifference) ||
(integrationTest && specificFileType) ||
(integrationTest && onlyLastOccurrence)) {
std::cerr << "Error: Options given which can not be combined. "
<< "Please see the manual (-h) for more information." << std::endl;
return EXIT_FAILURE;
}
if (argc != argOffset + 4) {
std::cerr << "Error: The number of options and arguments given is not correct. "
<< "Please run compareECL -h to see manual." << std::endl;
<< "Please run compareECL -h to see manual." << std::endl;
return EXIT_FAILURE;
}
@@ -239,143 +147,85 @@ int main(int argc, char** argv) {
double absTolerance = strtod(argv[argOffset + 2], nullptr);
double relTolerance = strtod(argv[argOffset + 3], nullptr);
if (specificFileType) {
std::string fileTypeString(fileTypeCstr);
for (auto& ch: fileTypeString) ch = toupper(ch);
if (fileTypeString== "UNRST") {} //Do nothing
else if (fileTypeString == "RST") {
concatenateRestart(basename1);
concatenateRestart(basename2);
}
else if (fileTypeString == "RST1") {
concatenateRestart(basename1);
}
else if (fileTypeString == "RST2") {
concatenateRestart(basename2);
}
else if (fileTypeString == "INIT") {
file_type = ECL_INIT_FILE;
}
else if (fileTypeString == "RFT") {
file_type = ECL_RFT_FILE;
}
else if (fileTypeString == "SMRY")
file_type = ECL_SUMMARY_FILE;
else {
std::cerr << "Unknown ECLIPSE filetype specified with option -t. Please run compareECL -h to see manual." << std::endl;
return EXIT_FAILURE;
}
}
if (restartFile && (file_type != ECL_SUMMARY_FILE || integrationTest)) {
std::cerr << "Error: -R can only be used in for summary regression tests." << std::endl;
return EXIT_FAILURE;
}
std::cout << "Comparing '" << basename1 << "' to '" << basename2 << "'." << std::endl;
try {
if (file_type == ECL_SUMMARY_FILE) {
if(!integrationTest){
SummaryRegressionTest compare(basename1,basename2,absTolerance,relTolerance);
compare.throwOnErrors(throwOnError);
compare.doAnalysis(analysis);
compare.setPrintKeywords(printKeywords);
compare.setIsRestartFile(restartFile);
compare.setAllowDifferentNumberOfKeywords(acceptExtraKeywords);
if(specificKeyword){
compare.getRegressionTest(keyword);
}
else{
compare.setPrintKeywords(printKeywords);
compare.getRegressionTest();
}
ECLRegressionTest comparator(basename1, basename2, absTolerance, relTolerance);
comparator.throwOnErrors(throwOnError);
comparator.doAnalysis(analysis);
comparator.setAcceptExtraKeywords(acceptExtraKeywords);
if (integrationTest) {
comparator.setIntegrationTest(true);
}
if (printKeywords) {
comparator.setPrintKeywordOnly(printKeywords);
}
if (onlyLastSequence) {
comparator.setOnlyLastReportNumber(true);
}
if (specificKeyword) {
comparator.compareSpesificKeyword(keyword);
}
if (specificReportStepNumber) {
comparator.compareSpesificRstReportStepNumber(reportStepNumber);
}
if (restartFile) {
comparator.setLoadBaseRunData(true);
}
comparator.loadGrids();
if (integrationTest && specificFileType) {
if (fileTypeString=="EGRID" || fileTypeString=="INIT" || fileTypeString=="RFT") {
std::cerr << "Integration test and spesific file type, only valid for UNRST and SMRY" << std::endl;
return EXIT_FAILURE;
}
}
if (specificFileType) {
if (fileTypeString == "EGRID") {
comparator.gridCompare();
} else if (fileTypeString == "INIT") {
comparator.results_init();
} else if (fileTypeString == "UNRST") {
comparator.results_rst();
} else if (fileTypeString == "SMRY") {
comparator.results_smry();
} else if (fileTypeString == "RFT") {
comparator.results_rft();
} else {
SummaryIntegrationTest compare(basename1,basename2,absTolerance,relTolerance);
compare.throwOnErrors(throwOnError);
compare.setFindVectorWithGreatestErrorRatio(findGreatestErrorRatio);
compare.setAllowSpikes(allowSpikes);
if (mainVariable) {
compare.setOneOfTheMainVariables(true);
std::string str(mainVariable);
std::transform(str.begin(), str.end(),str.begin(), ::toupper);
if(str == "WOPR" ||str=="WWPR" ||str=="WGPR" || str == "WBHP"){
compare.setMainVariable(str);
}else{
throw std::invalid_argument("The input is not a main variable. -m option requires a valid main variable.");
}
}
compare.setFindVolumeError(findVolumeError);
if (spikeLimit != -1) {
compare.setSpikeLimit(spikeLimit);
}
compare.setAllowDifferentAmountOfKeywords(allowDifferentAmount);
compare.setPrintKeywords(printKeywords);
compare.setThrowExceptionForTooGreatErrorRatio(throwTooGreatErrorRatio);
if(specificKeyword){
compare.setPrintSpecificKeyword(specificKeyword);
compare.getIntegrationTest(keyword);
return 0;
}
compare.getIntegrationTest();
std::cerr << "Unknown ECLIPSE filetype specified with option -t. Please run compareECL -h to see manual." << std::endl;
return EXIT_FAILURE;
}
} else if (integrationTest) {
comparator.results_rst();
comparator.results_smry();
} else {
comparator.gridCompare();
comparator.results_init();
comparator.results_rst();
comparator.results_smry();
comparator.results_rft();
}
else if (integrationTest) {
ECLIntegrationTest comparator(basename1, basename2, absTolerance, relTolerance);
if (printKeywords) {
comparator.printKeywords();
return 0;
}
if (printKeywordsDifference) {
comparator.printKeywordsDifference();
return 0;
}
if (checkNumKeywords) {
comparator.equalNumKeywords();
}
if (specificKeyword) {
if (comparator.elementInWhitelist(keyword)) {
comparator.resultsForKeyword(keyword);
}
else {
std::cerr << "Keyword " << keyword << " is not supported for the integration test. Use SGAS, SWAT or PRESSURE." << std::endl;
return EXIT_FAILURE;
}
}
else {
comparator.results();
}
}
else {
ECLRegressionTest comparator(file_type, basename1, basename2, absTolerance, relTolerance);
comparator.throwOnErrors(throwOnError);
comparator.doAnalysis(analysis);
comparator.setAcceptExtraKeywords(acceptExtraKeywords);
if (printKeywords) {
comparator.printKeywords();
return 0;
}
if (printKeywordsDifference) {
comparator.printKeywordsDifference();
return 0;
}
if (onlyLastOccurrence) {
comparator.setOnlyLastOccurrence(true);
}
if (specificKeyword) {
comparator.gridCompare(volumecheck);
comparator.resultsForKeyword(keyword);
}
else {
comparator.gridCompare(volumecheck);
comparator.results();
}
if (comparator.getNoErrors() > 0)
OPM_THROW(std::runtime_error, comparator.getNoErrors() << " errors encountered in comparisons.");
}
if (comparator.getNoErrors() > 0)
OPM_THROW(std::runtime_error, comparator.getNoErrors() << " errors encountered in comparisons.");
}
catch (const std::exception& e) {
std::cerr << "Program threw an exception: " << e.what() << std::endl;
return EXIT_FAILURE;
}
std::cout << "\nProgram finished \n" << std::endl;
return 0;
}

View File

@@ -0,0 +1,88 @@
#include <iostream>
#include <chrono>
#include <tuple>
#include <iomanip>
#include <examples/test_util/EclFile.hpp>
#include <examples/test_util/EclOutput.hpp>
template<typename T>
void write(EclOutput& outFile, EclFile& file1,
const std::string& name, int index)
{
auto vect = file1.get<T>(index);
outFile.write(name, vect);
}
int main(int argc, char **argv) {
if (argc != 2) {
std::cout << "\nInvalid input, need 1 argument which should be the eclipse output file to be converted \n" << std::endl;
exit(1);
}
// start reading
auto start = std::chrono::system_clock::now();
std::string filename = argv[1];
EclFile file1(filename);
file1.loadData();
auto end1 = std::chrono::system_clock::now();
std::chrono::duration<double> elapsed_seconds1 = end1 - start;
bool formattedOutput = file1.formattedInput() ? false : true;
int p = filename.find_last_of(".");
int l = filename.length();
std::string rootN = filename.substr(0,p);
std::string extension = filename.substr(p,l-p);
std::string resFile;
if (formattedOutput) {
resFile = rootN + ".F" + extension.substr(1, extension.length() - 1);
} else {
resFile = rootN + "." + extension.substr(2, extension.length() - 2);
}
std::cout << "\033[1;31m" << "\nconverting " << argv[1] << " -> " << resFile << "\033[0m\n" << std::endl;
EclOutput outFile(resFile, formattedOutput);
auto arrayList = file1.getList();
for (size_t index = 0; index < arrayList.size(); index++) {
std::string name = std::get<0>(arrayList[index]);
EIOD::eclArrType arrType = std::get<1>(arrayList[index]);
if (arrType == EIOD::INTE) {
write<int>(outFile, file1, name, index);
} else if (arrType == EIOD::REAL) {
write<float>(outFile, file1, name,index);
} else if (arrType == EIOD::DOUB) {
write<double>(outFile, file1, name, index);
} else if (arrType == EIOD::LOGI) {
write<bool>(outFile, file1, name, index);
} else if (arrType == EIOD::CHAR) {
write<std::string>(outFile, file1, name, index);
} else if (arrType == EIOD::MESS) {
// shold not be any associated data
outFile.write(name,std::vector<char>());
} else {
std::cout << "unknown array type " << std::endl;
exit(1);
}
}
auto end2 = std::chrono::system_clock::now();
std::chrono::duration<double> elapsed_seconds2 = end2-end1;
std::cout << "\ntime to load from file : " << argv[1] << ": " << elapsed_seconds1.count() << " seconds" << std::endl;
std::cout << "time to write to file : " << resFile << ": " << elapsed_seconds2.count() << " seconds\n" << std::endl;
return 0;
}

View File

@@ -32,6 +32,8 @@ namespace Opm {
enum eclArrType {
INTE, REAL, DOUB, CHAR, LOGI, MESS
};
// named constants related to binary file format
const unsigned int true_value = 0xffffffff;
const unsigned int false_value = 0x00000000;
@@ -48,6 +50,26 @@ namespace Opm {
const int MaxBlockSizeLogi = 4000; // Maximum block size for LOGI arrays in binary files
const int MaxBlockSizeChar = 840; // Maximum block size for CHAR arrays in binary files
// named constants related to formatted file file format
const int MaxNumBlockInte = 1000; // maximum number of Inte values in block => hard line shift
const int MaxNumBlockReal = 1000; // maximum number of Real values in block => hard line shift
const int MaxNumBlockDoub = 1000; // maximum number of Doub values in block => hard line shift
const int MaxNumBlockLogi = 1000; // maximum number of Logi values in block => hard line shift
const int MaxNumBlockChar = 105; // maximum number of Char values in block => hard line shift
const int numColumnsInte = 6; // number of columns for Inte values
const int numColumnsReal = 4; // number of columns for Real values
const int numColumnsDoub = 3; // number of columns for Doub values
const int numColumnsLogi = 25; // number of columns for Logi values
const int numColumnsChar = 7; // number of columns for Char values
const int columnWidthInte = 12; // number of characters fore each Inte Element
const int columnWidthReal = 17; // number of characters fore each Inte Element
const int columnWidthDoub = 23; // number of characters fore each Inte Element
const int columnWidthLogi = 3; // number of characters fore each Inte Element
const int columnWidthChar = 11; // number of characters fore each Inte Element
} // ecl
} // Opm

View File

@@ -1,240 +0,0 @@
/*
Copyright 2016 Statoil ASA.
This file is part of the Open Porous Media project (OPM).
OPM 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.
OPM 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 for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#include "summaryComparator.hpp"
#include <ert/ecl/ecl_sum.hpp>
#include <ert/util/stringlist.hpp>
#include <ert/util/int_vector.hpp>
#include <ert/util/bool_vector.hpp>
#include <opm/common/ErrorMacros.hpp>
#include <cmath>
#include <numeric>
SummaryComparator::SummaryComparator(const std::string& basename1,
const std::string& basename2,
double absoluteTol, double relativeTol){
ecl_sum1 = ecl_sum_fread_alloc_case(basename1.c_str(), ":");
ecl_sum2 = ecl_sum_fread_alloc_case(basename2.c_str(), ":");
if (ecl_sum1 == nullptr || ecl_sum2 == nullptr) {
OPM_THROW(std::runtime_error, "Not able to open files");
}
absoluteTolerance = absoluteTol;
relativeTolerance = relativeTol;
keys1 = stringlist_alloc_new();
keys2 = stringlist_alloc_new();
ecl_sum_select_matching_general_var_list( ecl_sum1 , "*" , this->keys1);
stringlist_sort(this->keys1 , nullptr );
ecl_sum_select_matching_general_var_list( ecl_sum2 , "*" , this->keys2);
stringlist_sort(this->keys2 , nullptr );
if(stringlist_get_size(keys1) <= stringlist_get_size(keys2)){
this->keysShort = this->keys1;
this->keysLong = this->keys2;
}else{
this->keysShort = this->keys2;
this->keysLong = this->keys1;
}
}
SummaryComparator::~SummaryComparator(){
ecl_sum_free(ecl_sum1);
ecl_sum_free(ecl_sum2);
stringlist_free(keys1);
stringlist_free(keys2);
}
Deviation SummaryComparator::calculateDeviations(double val1, double val2){
double absDev;
Deviation deviation;
absDev = std::abs(val1 - val2);
deviation.abs = absDev;
if (val1 != 0 || val2 != 0) {
deviation.rel = absDev/double(std::max(std::abs(val1), std::abs(val2)));
}
return deviation;
}
void SummaryComparator::setTimeVecs(std::vector<double> &timeVec1,
std::vector<double> &timeVec2){
timeVec1.reserve(ecl_sum_get_data_length(ecl_sum1));
for (int time_index = 0; time_index < ecl_sum_get_data_length(ecl_sum1); time_index++){
timeVec1.push_back(ecl_sum_iget_sim_days(ecl_sum1 , time_index ));
}
timeVec2.reserve(ecl_sum_get_data_length(ecl_sum2));
for (int time_index = 0; time_index < ecl_sum_get_data_length(ecl_sum2); time_index++){
timeVec2.push_back(ecl_sum_iget_sim_days(ecl_sum2 , time_index ));
}
}
void SummaryComparator::getDataVecs(std::vector<double> &dataVec1,
std::vector<double> &dataVec2,
const char* keyword){
dataVec1.reserve(ecl_sum_get_data_length(ecl_sum1));
for (int time_index = 0; time_index < ecl_sum_get_data_length(ecl_sum1); time_index++){
dataVec1.push_back(ecl_sum_iget(ecl_sum1, time_index, ecl_sum_get_general_var_params_index( ecl_sum1 , keyword )));
}
dataVec2.reserve(ecl_sum_get_data_length(ecl_sum2));
for (int time_index = 0; time_index < ecl_sum_get_data_length(ecl_sum2); time_index++){
dataVec2.push_back(ecl_sum_iget(ecl_sum2, time_index, ecl_sum_get_general_var_params_index( ecl_sum2 , keyword )));
}
}
void SummaryComparator::setDataSets(const std::vector<double>& timeVec1,
const std::vector<double>& timeVec2){
if(timeVec1.size() < timeVec2.size()){
ecl_sum_fileShort = this->ecl_sum1;
ecl_sum_fileLong = this->ecl_sum2;
}
else{
ecl_sum_fileShort = this->ecl_sum2;
ecl_sum_fileLong = this->ecl_sum1;
}
}
void SummaryComparator::chooseReference(const std::vector<double>& timeVec1,
const std::vector<double>& timeVec2,
const std::vector<double>& dataVec1,
const std::vector<double>& dataVec2){
if(timeVec1.size() <= timeVec2.size()){
referenceVec = &timeVec1; // time vector
referenceDataVec = &dataVec1; //data vector
checkVec = &timeVec2;
checkDataVec = &dataVec2;
}
else{
referenceVec = &timeVec2;
referenceDataVec = &dataVec2;
checkVec = &timeVec1;
checkDataVec = &dataVec1;
}
}
void SummaryComparator::getDeviation(size_t refIndex, size_t &checkIndex, Deviation &dev){
if((*referenceVec)[refIndex] == (*checkVec)[checkIndex]){
dev = SummaryComparator::calculateDeviations((*referenceDataVec)[refIndex], (*checkDataVec)[checkIndex]);
checkIndex++;
return;
}
else if((*referenceVec)[refIndex]<(*checkVec)[checkIndex]){
double value = SummaryComparator::unitStep((*checkDataVec)[checkIndex]);
/*Must be a little careful here. Flow writes out old value first,
than changes value. Say there should be a change in production rate from A to B at timestep 300.
Then the data of time step 300 is A and the next timestep will have value B. Must use the upper limit. */
dev = SummaryComparator::calculateDeviations((*referenceDataVec)[refIndex], value);
checkIndex++;
return;
}
else{
checkIndex++;
getDeviation(refIndex, checkIndex , dev);
}
if(checkIndex == checkVec->size() -1 ){
return;
}
}
void SummaryComparator::printUnits(){
std::vector<double> timeVec1, timeVec2;
setTimeVecs(timeVec1, timeVec2); // Sets the time vectors, they are equal for all keywords (WPOR:PROD01 etc)
setDataSets(timeVec1, timeVec2);
for (int jvar = 0; jvar < stringlist_get_size(keysLong); jvar++){
std::cout << stringlist_iget(keysLong, jvar) << " unit: " << ecl_sum_get_unit(ecl_sum_fileShort, stringlist_iget(keysLong, jvar)) << std::endl;
}
}
//Called only when the keywords are equal in the getDeviations()-function
const char* SummaryComparator::getUnit(const char* keyword){
return ecl_sum_get_unit(ecl_sum_fileShort, keyword);
}
void SummaryComparator::printKeywords(){
int ivar = 0;
std::vector<std::string> noMatchString;
std::cout << "Keywords that are common for the files:" << std::endl;
while(ivar < stringlist_get_size(keysLong)){
const char* keyword = stringlist_iget(keysLong, ivar);
if (stringlist_contains(keysLong, keyword) && stringlist_contains(keysShort, keyword)){
std::cout << keyword << std::endl;
ivar++;
}
else{
noMatchString.push_back(keyword);
ivar++;
}
}
if(noMatchString.size() == 0){
std::cout << "No keywords were different" << std::endl;
return;
}
std::cout << "Keywords that are different: " << std::endl;
for (const auto& it : noMatchString) std::cout << it << std::endl;
std::cout << "\nOf the " << stringlist_get_size(keysLong) << " keywords " << stringlist_get_size(keysLong)-noMatchString.size() << " were equal and " << noMatchString.size() << " were different" << std::endl;
}
void SummaryComparator::printDataOfSpecificKeyword(const std::vector<double>& timeVec1,
const std::vector<double>& timeVec2,
const char* keyword){
std::vector<double> dataVec1, dataVec2;
getDataVecs(dataVec1,dataVec2,keyword);
chooseReference(timeVec1, timeVec2,dataVec1,dataVec2);
size_t ivar = 0;
size_t jvar = 0;
const char separator = ' ';
const int numWidth = 14;
std::cout << std::left << std::setw(numWidth) << std::setfill(separator) << "Time";
std::cout << std::left << std::setw(numWidth) << std::setfill(separator) << "Ref data";
std::cout << std::left << std::setw(numWidth) << std::setfill(separator) << "Check data" << std::endl;
while(ivar < referenceVec->size()){
if(ivar == referenceVec->size() || jvar == checkVec->size() ){
break;
}
if((*referenceVec)[ivar] == (*checkVec)[jvar]){
std::cout << std::left << std::setw(numWidth) << std::setfill(separator) << (*referenceVec)[ivar];
std::cout << std::left << std::setw(numWidth) << std::setfill(separator) << (*referenceDataVec)[ivar];
std::cout << std::left << std::setw(numWidth) << std::setfill(separator) << (*checkDataVec)[jvar] << std::endl;
ivar++;
jvar++;
}else if((*referenceVec)[ivar] < (*checkVec)[jvar]){
std::cout << std::left << std::setw(numWidth) << std::setfill(separator) << (*referenceVec)[ivar];
std::cout << std::left << std::setw(numWidth) << std::setfill(separator) << (*referenceDataVec)[ivar];
std::cout << std::left << std::setw(numWidth) << std::setfill(separator) << "" << std::endl;
ivar++;
}
else{
std::cout << std::left << std::setw(numWidth) << std::setfill(separator) << (*checkVec)[jvar];
std::cout << std::left << std::setw(numWidth) << std::setfill(separator) << "";
std::cout << std::left << std::setw(numWidth) << std::setfill(separator) << (*checkDataVec)[jvar] << std::endl;
jvar++;
}
}
}

View File

@@ -1,182 +0,0 @@
/*
Copyright 2016 Statoil ASA.
This file is part of the Open Porous Media project (OPM).
OPM 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.
OPM 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 for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SUMMARYCOMPARATOR_HPP
#define SUMMARYCOMPARATOR_HPP
#include "Deviation.hpp"
#include <iostream>
#include <iomanip>
#include <map>
#include <vector>
#include <algorithm>
#include <string>
// helper macro to handle error throws or not
#define HANDLE_ERROR(type, message) \
{ \
if (throwOnError) \
OPM_THROW(type, message); \
else \
std::cerr << message << std::endl; \
}
//! \brief Prototyping struct, encapsuling the stringlist libraries.
struct stringlist_struct;
typedef struct stringlist_struct stringlist_type;
//! \brief Prototyping struct, encapsuling the ert libraries.
struct ecl_sum_struct;
typedef struct ecl_sum_struct ecl_sum_type;
class SummaryComparator {
private:
double absoluteTolerance = 0; //!< The maximum absolute deviation that is allowed between two values.
double relativeTolerance = 0; //!< The maximum relative deviation that is allowed between twi values.
protected:
ecl_sum_type * ecl_sum1 = nullptr; //!< Struct that contains file1
ecl_sum_type * ecl_sum2 = nullptr; //!< Struct that contains file2
ecl_sum_type * ecl_sum_fileShort = nullptr; //!< For keeping track of the file with most/fewest timesteps
ecl_sum_type * ecl_sum_fileLong = nullptr; //!< For keeping track of the file with most/fewest timesteps
stringlist_type* keys1 = nullptr; //!< For storing all the keywords of file1
stringlist_type* keys2 = nullptr; //!< For storing all the keywords of file2
stringlist_type * keysShort = nullptr; //!< For keeping track of the file with most/fewest keywords
stringlist_type * keysLong = nullptr; //!< For keeping track of the file with most/fewest keywords
const std::vector<double> * referenceVec = nullptr; //!< For storing the values of each time step for the file containing the fewer time steps.
const std::vector<double> * referenceDataVec = nullptr; //!< For storing the data corresponding to each time step for the file containing the fewer time steps.
const std::vector<double> * checkVec = nullptr; //!< For storing the values of each time step for the file containing the more time steps.
const std::vector<double> * checkDataVec = nullptr; //!< For storing the data values corresponding to each time step for the file containing the more time steps.
bool printKeyword = false; //!< Boolean value for choosing whether to print the keywords or not
bool printSpecificKeyword = false; //!< Boolean value for choosing whether to print the vectors of a keyword or not
bool throwOnError = true; //!< Throw on first error
bool analysis = false; //!< Perform error analysis
std::map<std::string, std::vector<Deviation>> deviations;
//! \brief Calculate deviation between two data values and stores it in a Deviation struct.
//! \param[in] refIndex Index in reference data
//! \param[in] checkindex Index in data to be checked.
//! \param[out] dev Holds the result from the comparison on return.
//! \details Uses the #referenceVec as basis, and checks its values against the values in #checkDataVec. The function is reccursive, and will update the iterative index j of the #checkVec until #checkVec[j] >= #referenceVec[i]. \n When #referenceVec and #checkVec have the same time value (i.e. #referenceVec[i] == #checkVec[j]) a direct comparison is used, \n when this is not the case, when #referenceVec[i] do not excist as an element in #checkVec, a value is generated, either by the principle of unit step or by interpolation.
void getDeviation(size_t refIndex, size_t &checkIndex, Deviation &dev);
//! \brief Figure out which data file contains the most / less timesteps and assign member variable pointers accordingly.
//! \param[in] timeVec1 Data from first file
//! \param[in] timeVec2 Data from second file
//! \details Figure out which data file that contains the more/fewer time steps and assigns the private member variable pointers #ecl_sum_fileShort / #ecl_sum_fileLong to the correct data sets #ecl_sum1 / #ecl_sum2.
void setDataSets(const std::vector<double>& timeVec1,
const std::vector<double>& timeVec2);
//! \brief Reads in the time values of each time step.
//! \param[in] timeVec1 Vector for storing the time steps from file1
//! \param[in] timeVec2 Vector for storing the time steps from file2
void setTimeVecs(std::vector<double> &timeVec1,std::vector<double> &timeVec2);
//! \brief Read the data for one specific keyword into two separate vectors.
//! \param[in] dataVec1 Vector for storing the data for one specific keyword from file1
//! \param[in] dataVec2 Vector for storing the data for one specific keyword from file2
//! \details The two data files do not necessarily have the same amount of data values, but the values must correspond to the same interval in time. Thus possible to interpolate values.
void getDataVecs(std::vector<double> &dataVec1,
std::vector<double> &dataVec2, const char* keyword);
//! \brief Sets one data set as a basis and the other as values to check against.
//! \param[in] timeVec1 Used to figure out which dataset that have the more/fewer time steps.
//! \param[in] timeVec2 Used to figure out which dataset that have the more/fewer time steps.
//! \param[in] dataVec1 For assiging the the correct pointer to the data vector.
//! \param[in] dataVec2 For assiging the the correct pointer to the data vector.
//! \details Figures out which time vector that contains the fewer elements. Sets this as #referenceVec and its corresponding data as #referenceDataVec. \n The remaining data set is set as #checkVec (the time vector) and #checkDataVec.
void chooseReference(const std::vector<double> &timeVec1,
const std::vector<double> &timeVec2,
const std::vector<double> &dataVec1,
const std::vector<double> &dataVec2);
//! \brief Returns the relative tolerance.
double getRelTolerance(){return this->relativeTolerance;}
//! \brief Returns the absolute tolerance.
double getAbsTolerance(){return this->absoluteTolerance;}
//! \brief Returns the unit of the values of a keyword
//! \param[in] keyword The keyword of interest.
//! \param[out] ret The unit of the keyword as a const char*.
const char* getUnit(const char* keyword);
//! \brief Prints the units of the files.
void printUnits();
//! \brief Prints the keywords of the files.
//! \details The function prints first the common keywords, than the keywords that are different.
void printKeywords();
//! \brief Prints the summary vectors from the two files.
//! \details The function requires that the summary vectors of the specific file have been read into the member variables referenceVec etc.
void printDataOfSpecificKeyword(const std::vector<double>& timeVec1,
const std::vector<double>& timeVec2,
const char* keyword);
public:
//! \brief Creates an SummaryComparator class object
//! \param[in] basename1 Path to file1 without extension.
//! \param[in] basename1 Path to file2 without extension.
//! \param[in] absoluteTolerance The absolute tolerance which is to be used in the test.
//! \param[in] relativeTolerance The relative tolerance which is to be used in the test.
//! \details The constructor creates an object of the class, and openes the files, an exception is thrown if the opening of the files fails. \n It creates stringlists, in which keywords are to be stored, and figures out which keylist that contains the more/less keywords. \n Also the private member variables aboluteTolerance and relativeTolerance are set.
SummaryComparator(const std::string& basename1,
const std::string& basename2,
double absoluteTolerance, double relativeTolerance);
//! \brief Destructor
//! \details The destructor takes care of the allocated memory in which data has been stored.
~SummaryComparator();
//! \brief Calculates the deviation between two values
//! \param[in] val1 The first value of interest.
//! \param[in] val2 The second value if interest.
//! \param[out] ret Returns a Deviation struct.
//! \details The function takes two values, calculates the absolute and relative deviation and returns the result as a Deviation struct.
static Deviation calculateDeviations( double val1, double val2);
//! \brief Sets the private member variable printKeywords
//! \param[in] boolean Boolean value
//! \details The function sets the private member variable printKeywords. When it is true the function printKeywords will be called.
void setPrintKeywords(bool boolean){this->printKeyword = boolean;}
//! \brief Sets the private member variable printSpecificKeyword
//! \param[in] boolean Boolean value
//! \details The function sets the private member variable printSpecificKeyword. When true, the summary vector of the keyword for both files will be printed.
void setPrintSpecificKeyword(bool boolean){this->printSpecificKeyword = boolean;}
//! \brief Unit step function
//! \param[in] value The input value should be the last know value
//! \param[out] ret Return the unit-step-function value.
//! \details In this case: The unit step function is used when the data from the two data set, which is to be compared, don't match in time. \n The unit step function is then to be called on the #checkDataVec 's value at the last time step which is before the time of comparison.
//! \brief Returns a value based on the unit step principle.
static double unitStep(double value){return value;}
//! \brief Set whether to throw on errors or not.
void throwOnErrors(bool dothrow) { throwOnError = dothrow; }
//! \brief Set whether or not to perform error analysis.
void doAnalysis(bool analyse) { analysis = analyse; }
};
#endif

View File

@@ -1,386 +0,0 @@
/*
Copyright 2016 Statoil ASA.
This file is part of the Open Porous Media project (OPM).
OPM 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.
OPM 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 for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#include "summaryIntegrationTest.hpp"
#include <opm/common/ErrorMacros.hpp>
#include <ert/ecl/ecl_sum.hpp>
#include <ert/util/stringlist.hpp>
#include <cmath>
void SummaryIntegrationTest::getIntegrationTest(){
std::vector<double> timeVec1, timeVec2;
setTimeVecs(timeVec1, timeVec2); // Sets the time vectors, they are equal for all keywords (WPOR:PROD01 etc)
setDataSets(timeVec1, timeVec2);
int ivar = 0;
if(!allowDifferentAmountOfKeywords){
if(stringlist_get_size(keysShort) != stringlist_get_size(keysLong)){
OPM_THROW(std::invalid_argument, "Different ammont of keywords in the two summary files.");
}
}
if(printKeyword){
printKeywords();
return;
}
std::string keywordWithGreatestErrorRatio;
double greatestRatio = 0;
//Iterates over all keywords from the restricted file, use iterator "ivar". Searches for a match in the file with more keywords, use the itarator "jvar".
while(ivar < stringlist_get_size(keysShort)){
const char* keyword = stringlist_iget(keysShort, ivar);
if(oneOfTheMainVariables){
std::string keywordString(keyword);
std::string substr = keywordString.substr(0,4);
if(substr!= mainVariable){
ivar++;
continue;
}
}
for (int jvar = 0; jvar < stringlist_get_size(keysLong); jvar++){
if (strcmp(keyword, stringlist_iget(keysLong, jvar)) == 0){ //When the keywords are equal, proceed in comparing summary files.
/* if(!checkUnits(keyword)){
OPM_THROW(std::runtime_error, "For keyword " << keyword << " the unit of the two files is not equal. Not possible to compare.");
} //Comparing the unit of the two vectors.*/
checkForKeyword(timeVec1, timeVec2, keyword);
if(findVectorWithGreatestErrorRatio){
WellProductionVolume volume = getSpecificWellVolume(timeVec1,timeVec2, keyword);
findGreatestErrorRatio(volume,greatestRatio, keyword, keywordWithGreatestErrorRatio);
}
break;
}
//will only enter here if no keyword match
if(jvar == stringlist_get_size(keysLong)-1){
if(!allowDifferentAmountOfKeywords){
OPM_THROW(std::invalid_argument, "No match on keyword");
}
}
}
ivar++;
}
if(findVectorWithGreatestErrorRatio){
std::cout << "The keyword " << keywordWithGreatestErrorRatio << " had the greatest error ratio, which was " << greatestRatio << std::endl;
}
if((findVolumeError || oneOfTheMainVariables) && !findVectorWithGreatestErrorRatio){
evaluateWellProductionVolume();
}
if(allowSpikes){
std::cout << "checkWithSpikes succeeded." << std::endl;
}
}
void SummaryIntegrationTest::getIntegrationTest(const char* keyword){
if(stringlist_contains(keysShort,keyword) && stringlist_contains(keysLong, keyword)){
std::vector<double> timeVec1, timeVec2;
setTimeVecs(timeVec1, timeVec2); // Sets the time vectors, they are equal for all keywords (WPOR:PROD01 etc)
setDataSets(timeVec1, timeVec2);
if(printSpecificKeyword){
printDataOfSpecificKeyword(timeVec1, timeVec2, keyword);
}
if(findVolumeError){
WellProductionVolume volume = getSpecificWellVolume(timeVec1, timeVec2, keyword);
if(volume.error == 0){
std::cout << "For keyword " << keyword << " the total production volume is 0" << std::endl;
}
else{
std::cout << "For keyword " << keyword << " the total production volume is "<< volume.total;
std::cout << ", the error volume is " << volume.error << " the error ratio is " << volume.error/volume.total << std::endl;
}
}
checkForKeyword(timeVec1, timeVec2, keyword);
return;
}
OPM_THROW(std::invalid_argument, "The keyword used is not common for the two files.");
}
void SummaryIntegrationTest::checkForKeyword(const std::vector<double>& timeVec1,
const std::vector<double>& timeVec2,
const char* keyword){
std::vector<double> dataVec1, dataVec2;
getDataVecs(dataVec1,dataVec2,keyword);
chooseReference(timeVec1, timeVec2,dataVec1,dataVec2);
if(allowSpikes){
checkWithSpikes(keyword);
}
if(findVolumeError ||oneOfTheMainVariables ){
volumeErrorCheck(keyword);
}
}
int SummaryIntegrationTest::checkDeviation(const Deviation& deviation){
double absTol = getAbsTolerance();
double relTol = getRelTolerance();
if (deviation.rel> relTol && deviation.abs > absTol){
return 1;
}
return 0;
}
void SummaryIntegrationTest::findGreatestErrorRatio(const WellProductionVolume& volume,
double &greatestRatio,
const char* currentKeyword,
std::string &greatestErrorRatio){
if (volume.total != 0 && (volume.total - volume.error > getAbsTolerance()) ){
if(volume.error/volume.total > greatestRatio){
greatestRatio = volume.error/volume.total;
std::string currentKeywordStr(currentKeyword);
greatestErrorRatio = currentKeywordStr;
}
}
}
void SummaryIntegrationTest::volumeErrorCheck(const char* keyword){
const ecl::smspec_node * node = ecl_sum_get_general_var_node (ecl_sum_fileShort ,keyword);//doesn't matter which ecl_sum_file one uses, the kewyord SHOULD be equal in terms of smspec data.
if (node->is_historical())
return;
if (!mainVariable.empty()){
std::string keywordString(keyword);
std::string firstFour = keywordString.substr(0,4);
if(mainVariable == firstFour && firstFour == "WOPR"){
if(firstFour == "WOPR"){
WellProductionVolume result = getWellProductionVolume(keyword);
WOP += result;
return;
}
}
if(mainVariable == firstFour && firstFour == "WWPR"){
if(firstFour == "WWPR"){
WellProductionVolume result = getWellProductionVolume(keyword);
WWP += result;
return;
}
}
if(mainVariable == firstFour && firstFour == "WGPR"){
if(firstFour == "WGPR"){
WellProductionVolume result = getWellProductionVolume(keyword);
WGP += result;
return;
}
}
if(mainVariable == firstFour && firstFour == "WBHP"){
if(firstFour == "WBHP"){
WellProductionVolume result = getWellProductionVolume(keyword);
WBHP += result;
return;
}
}
}
updateVolumeError(keyword);
}
void SummaryIntegrationTest::updateVolumeError(const char* keyword){
std::string keywordString(keyword);
std::string firstFour = keywordString.substr(0,4);
if(firstFour == "WOPR"){
WellProductionVolume result = getWellProductionVolume(keyword);
WOP += result;
}
if(firstFour == "WWPR"){
WellProductionVolume result = getWellProductionVolume(keyword);
WWP += result;
}
if(firstFour == "WGPR"){
WellProductionVolume result = getWellProductionVolume(keyword);
WGP += result;
}
if(firstFour == "WBHP"){
WellProductionVolume result = getWellProductionVolume(keyword);
WBHP += result;
}
}
WellProductionVolume SummaryIntegrationTest::getWellProductionVolume(const char * keyword){
double total = integrate(*referenceVec, *referenceDataVec);
double error = integrateError(*referenceVec, *referenceDataVec,
*checkVec, *checkDataVec);
WellProductionVolume wPV;
wPV.total = total;
wPV.error = error;
if(wPV.total != 0 && wPV.total-wPV.error > getAbsTolerance()){
if( (wPV.error/wPV.total > getRelTolerance()) && throwExceptionForTooGreatErrorRatio){
OPM_THROW(std::runtime_error, "For the keyword "<< keyword << " the error ratio was " << wPV.error/wPV.total << " which is greater than the tolerance " << getRelTolerance());
}
}
return wPV;
}
void SummaryIntegrationTest::evaluateWellProductionVolume(){
if(mainVariable.empty()){
double ratioWOP, ratioWWP, ratioWGP, ratioWBHP;
ratioWOP = WOP.error/WOP.total;
ratioWWP = WWP.error/WWP.total;
ratioWGP = WGP.error/WGP.total;
ratioWBHP = WBHP.error/WBHP.total;
std::cout << "\n The total oil volume is " << WOP.total << ". The error volume is "<< WOP.error << ". The error ratio is " << ratioWOP << std::endl;
std::cout << "\n The total water volume is " << WWP.total << ". The error volume is "<< WWP.error << ". The error ratio is " << ratioWWP << std::endl;
std::cout << "\n The total gas volume is " << WGP.total <<". The error volume is "<< WGP.error << ". The error ratio is " << ratioWGP << std::endl;
std::cout << "\n The total area under the WBHP curve is " << WBHP.total << ". The area under the error curve is "<< WBHP.error << ". The error ratio is " << ratioWBHP << std::endl << std::endl;
}
if(mainVariable == "WOPR"){
std::cout << "\nThe total oil volume is " << WOP.total << ". The error volume is "<< WOP.error << ". The error ratio is " << WOP.error/WOP.total << std::endl<< std::endl;
}
if(mainVariable == "WWPR"){
std::cout << "\nThe total water volume is " << WWP.total << ". The error volume is "<< WWP.error << ". The error ratio is " << WWP.error/WWP.total << std::endl<< std::endl;
}
if(mainVariable == "WGPR"){
std::cout << "\nThe total gas volume is " << WGP.total <<". The error volume is "<< WGP.error << ". The error ratio is " << WGP.error/WGP.total << std::endl<< std::endl;
}
if(mainVariable == "WBHP"){
std::cout << "\nThe total area under the WBHP curve " << WBHP.total << ". The area under the error curve is "<< WBHP.error << ". The error ratio is " << WBHP.error/WBHP.total << std::endl << std::endl;
}
}
void SummaryIntegrationTest::checkWithSpikes(const char* keyword){
int errorOccurrences = 0;
size_t jvar = 0 ;
bool spikeCurrent = false;
Deviation deviation;
for (size_t ivar = 0; ivar < referenceVec->size(); ivar++){
int errorOccurrencesPrev = errorOccurrences;
bool spikePrev = spikeCurrent;
getDeviation(ivar,jvar, deviation);
errorOccurrences += checkDeviation(deviation);
if (errorOccurrences != errorOccurrencesPrev){
spikeCurrent = true;
} else{
spikeCurrent = false;
}
if(spikePrev&&spikeCurrent){
std::cout << "For keyword " << keyword << " at time step " << (*referenceVec)[ivar] <<std::endl;
OPM_THROW(std::invalid_argument, "For keyword " << keyword << " at time step " << (*referenceVec)[ivar] << ", wwo deviations in a row exceed the limit. Not a spike value. Integration test fails." );
}
if(errorOccurrences > this->spikeLimit){
std::cout << "For keyword " << keyword << std::endl;
OPM_THROW(std::invalid_argument, "For keyword " << keyword << " too many spikes in the vector. Integration test fails.");
}
}
}
WellProductionVolume
SummaryIntegrationTest::getSpecificWellVolume(const std::vector<double>& timeVec1,
const std::vector<double>& timeVec2,
const char* keyword){
std::vector<double> dataVec1, dataVec2;
getDataVecs(dataVec1,dataVec2,keyword);
chooseReference(timeVec1, timeVec2,dataVec1,dataVec2);
return getWellProductionVolume(keyword);
}
double SummaryIntegrationTest::integrate(const std::vector<double>& timeVec,
const std::vector<double>& dataVec){
double totalSum = 0;
if(timeVec.size() != dataVec.size()){
OPM_THROW(std::runtime_error, "The size of the time vector does not match the size of the data vector.");
}
for(size_t i = 0; i < timeVec.size()-1; i++){
double width = timeVec[i+1] - timeVec[i];
double height = dataVec[i+1];
totalSum += getRectangleArea(height, width);
}
return totalSum;
}
double SummaryIntegrationTest::integrateError(const std::vector<double>& timeVec1,
const std::vector<double>& dataVec1,
const std::vector<double>& timeVec2,
const std::vector<double>& dataVec2){
// When the data corresponds to a rate the integration will become a Riemann
// sum. This function calculates the Riemann sum of the error. The reason why
// a Riemann sum is used is because of the way the data is written to file.
// When a change occur (e.g. change of a rate), the data (value and time) is
// written to file, THEN the change happens in the simulator, i.e., we will
// notice the change at the next step.
//
// Keep in mind that the summary vector is NOT a continuous curve, only points
// of data (time, value). We have to guess what happens between the data
// points, we do this by saying: "There are no change, the only change happens
// at the data points." As stated above, the value of this constant "height" of
// the rectangle corresponds to the value of the last time step. Thus we have
// to use the "right hand side value of the rectangle as height
//
// someDataVector[ivar] instead of someDataVector[ivar-1]
//
// (which intuition is saying is the correct value to use).
if(timeVec1.size() != dataVec1.size() || timeVec2.size() != dataVec2.size() ){
OPM_THROW(std::runtime_error, "The size of the time vector does not match the size of the data vector.");
}
double errorSum = 0;
double rightEdge, leftEdge, width;
size_t i = 1;
size_t j = 1;
leftEdge = timeVec1[0];
while(i < timeVec1.size()){
if(j == timeVec2.size() ){
break;
}
if(timeVec1[i] == timeVec2[j]){
rightEdge = timeVec1[i];
width = rightEdge - leftEdge;
double dev = std::fabs(dataVec1[i] - dataVec2[j]);
errorSum += getRectangleArea(dev, width);
leftEdge = rightEdge;
i++;
j++;
continue;
}
if(timeVec1[i] < timeVec2[j]){
rightEdge = timeVec1[i];
width = rightEdge - leftEdge;
double value = unitStep(dataVec2[j]);
double dev = std::fabs(dataVec1[i]-value);
errorSum += getRectangleArea(dev, width);
leftEdge = rightEdge;
i++;
continue;
}
if(timeVec2[j] < timeVec1[i]){
rightEdge = timeVec2[j];
width = rightEdge - leftEdge;
double value = unitStep(dataVec1[i]);
double dev = std::fabs(dataVec2[j]-value);
errorSum += getRectangleArea(dev, width);
leftEdge = rightEdge;
j++;
continue;
}
}
return errorSum;
}

View File

@@ -1,217 +0,0 @@
/*
Copyright 2016 Statoil ASA.
This file is part of the Open Porous Media project (OPM).
OPM 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.
OPM 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 for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#include "summaryComparator.hpp"
//! \brief Struct for storing the total area under a graph.
//! \details Used when plotting summary vector against time. In most cases this represents a volume.
struct WellProductionVolume{
double total=0; //!< The total area under the graph when plotting the summary vector against time. In most cases the total production volume.
double error=0; //!< The total area under the graph when plotting the deviation vector against time. In most cases the total error volume.
//! \brief Overloaded operator
//! \param[in] rhs WellProductionVolume struct
WellProductionVolume& operator+=(const WellProductionVolume& rhs){
this->total += rhs.total;
this->error += rhs.error;
return *this;
}
};
//! \details The class inherits from the SummaryComparator class, which takes care of all file reading. \n The IntegrationTest class compares values from the two different files and throws exceptions when the deviation is unsatisfying.
class SummaryIntegrationTest: public SummaryComparator {
private:
bool allowSpikes = false; //!< Boolean value, when true checkForSpikes is included as a sub test in the integration test. By default: false.
bool findVolumeError = false; //!< Boolean value, when true volumeErrorCheck() is included as a sub test in the integration test. By default: false.
bool allowDifferentAmountOfKeywords = true; //!< Boolean value, when false the integration test will check wheter the two files have the same amount of keywords. \nIf they don't, an exception will be thrown. By default: true.
bool findVectorWithGreatestErrorRatio = false; //!< Boolean value, when true the integration test will find the vector that has the greatest error ratio. By default: false.
bool oneOfTheMainVariables = false; //!< Boolean value, when true the integration test will only check for one of the primary variables (WOPR, WGPR, WWPR. WBHP), which will be specified by user. By default: false.
bool throwExceptionForTooGreatErrorRatio = true; //!< Boolean value, when true any volume error ratio that exceeds the relativeTolerance will cause an exception to be thrown. By default: true.
std::string mainVariable; //!< String variable, where the name of the main variable of interest (one of WOPR, WBHP, WWPR, WGPR) is stored. Can be empty.
int spikeLimit = 13370; //!< The limit for how many spikes to allow in the data set of a certain keyword. By default: Set to a high number, \n should not trig the (if spikeOccurrences > spikeLimit){ // throw exception }.
WellProductionVolume WOP; //!< WellProductionVolume struct for storing the total production volume and total error volume of all the keywords which start with WOPR
WellProductionVolume WWP; //!< WellProductionVolume struct for storing the total production volume and total error volume of all the keywords which start with WWPR
WellProductionVolume WGP;//!< WellProductionVolume struct for storing the total production volume and total error volume of all the keywords which start with WGPR
WellProductionVolume WBHP; //!< WellProductionVolume struct for storing the value of the area under the graph when plotting summary vector/deviation vector against time.This is for keywords starting with WBHP. \nNote: the name of the struct may be misleading, this is not an actual volume.
//! \brief The function gathers the correct data for comparison for a specific keyword
//! \param[in] timeVec1 A std::vector<double> that contains the time steps of file 1.
//! \param[in] timeVec2 A std::vector<double> that contains the time steps of file 2.
//! \param[in] keyword The keyword of interest
//! \details The function requires an outer loop which iterates over the keywords of the files. It prepares an integration test by gathering the data, stroing it into two vectors, \n deciding which is to be used as a reference/basis and calling the test function.
void checkForKeyword(const std::vector<double>& timeVec1,
const std::vector<double>& timeVec2, const char* keyword);
//! \brief The function compares the volume error to the total production volume of a certain type of keyword.
//! param[in] keyword The keyword of interest.
//! \details The function takes in a keyword and checks if it is of interest. Only keywords which say something about the well oil production, well water production, \n well gas production and the well BHP are of interest. The function sums up the total production in the cases where it is possible, \n and sums up the error volumes by a trapezoid integration method. The resulting values are stored in member variable structs of type WellProductionVolume, and double variables. For proper use of the function all the keywords of the file should be checked. This is satisfied if it is called by checkForKeyword.
void volumeErrorCheck(const char* keyword);
//! \brief The function calculates the total production volume and total error volume of a specific keyword
//! \param[in] timeVec1 A std::vector<double> that contains the time steps of file 1.
//! \param[in] timeVec2 A std::vector<double> that contains the time steps of file 2.
//! \param[in] keyword The keyword of interest
//! \param[out] ret Returns a WellProductionWolume struct
//! \details The function reads the data from the two files into the member variable vectors (of the super class). It returns a WellProductionVolume struct calculated from the vectors corresponding to the keyword.
WellProductionVolume getSpecificWellVolume(const std::vector<double>& timeVec1,
const std::vector<double>& timeVec2,
const char* keyword);
//! \brief The function is a regression test which allows spikes.
//! \param[in] keyword The keyword of interest, the keyword the summary vectors "belong" to.
//! \details The function requires the protected member variables referenceVec, referenceDataVec, checkVec and checkDataVec to be stored with data, which is staisfied if it is called by checkForKeyword. \n It compares the two vectors value by value, and if the deviation is unsatisfying, the errorOccurrenceCounter is incremented. If the errorOccurrenceCounter becomes greater than the errorOccurrenceLimit, \n a exception is thrown. The function will allow spike values, however, if two values in a row exceed the deviation limit, they are no longer spikes, and an exception is thrown.
void checkWithSpikes(const char* keyword);
//! \brief Caluculates a deviation, throws exceptions and writes and error message.
//! \param[in] deviation Deviation struct
//! \param[out] int Returns 0/1, depending on wheter the deviation exceeded the limit or not.
//! \details The function checks the values of the Deviation struct against the absolute and relative tolerance, which are private member values of the super class. \n When comparing against the relative tolerance an additional term is added, the absolute deviation has to be greater than 1e-6 for the function to throw an exception. \n When the deviations are too great, the function returns 1.
int checkDeviation(const Deviation& deviation);
//! \brief Calculates the keyword's total production volume and error volume
//! \param[in] keyword The keyword of interest.
//! \param[out] wellProductionVolume A struct containing the total production volume and the total error volume.
//! \details The function calculates the total production volume and total error volume of a keyword, by the trapezoid integral method. \n The function throws and exception if the total error volume is negative. The function returns the results as a struct.
WellProductionVolume getWellProductionVolume(const char* keyword);
//! \brief The function function works properly when the private member variables are set (after running the integration test which findVolumeError = true). \n It prints out the total production volume, the total error volume and the error ratio.
void evaluateWellProductionVolume();
//! \brief The function calculates the total production volume and total error volume
//! \param keyword The keyword of interest
//! \details The function uses the data that is stored in the member variable vectors. It calculates the total production volume \n and the total error volume of the specified keyword, and adds the result to the private member WellProductionVolume variables of the class.
void updateVolumeError(const char* keyword);
//! \brief Finds the keyword which has the greates error volume ratio
//! \param[in] volume WellProductionVolume struct which contains the data used for comparison
//! \param[in] greatestRatio Double value taken in by reference. Stores the greatest error ratio value.
//! \param[in] currentKeyword The keyword that is under evaluation
//! \param[in] greatestErrorRatio String which contains the name of the keyword which has the greatest error ratio
//! \details The function requires an outer loop which iterates over the keywords in the files, and calls the function for each keyword. \nThe valiables double greatestRatio and std::string keywordWithTheGreatestErrorRatio must be declared outside the loop. \nWhen the current error ratio is greater than the value stored in greatestRatio, the gratestRatio value is updated with the current error ratio.
void findGreatestErrorRatio(const WellProductionVolume& volume,
double &greatestRatio,
const char* currentKeyword,
std::string &greatestErrorRatio);
#if 0
//! \brief Checks whether the unit of the two data vectors is the same
//! \param[in] keyword The keyword of interest
//! \param[out] boolean True/false, depending on whether the units are equal or not
bool checkUnits(const char* keyword);
#endif
public:
//! \brief Constructor, creates an object of IntegrationTest class.
//! \param[in] basename1 Path to file1 without extension.
//! \param[in] basename1 Path to file2 without extension.
//! \param[in] atol The absolute tolerance which is to be used in the test.
//! \param[in] rtol The relative tolerance which is to be used in the test.
//! \details The constructor calls the constructor of the super class.
SummaryIntegrationTest(const std::string& basename1,
const std::string& basename2,
double atol, double rtol) :
SummaryComparator(basename1, basename2, atol, rtol) {}
//! \brief This function sets the private member variable allowSpikes.
//! \param[in] allowSpikes Boolean value
//! \details When allowSpikes is true, the integration test checkWithSpikes is excecuted.
void setAllowSpikes(bool allowSpikesArg){this->allowSpikes = allowSpikesArg;}
//! \brief This function sets the private member variable findVolumeError.
//! \param[in] findVolumeError Boolean value
//! \details When findVolumeError is true, the integration test volumeErrorCheck and the function evaluateWellProductionVolume are excecuted.
void setFindVolumeError(bool findVolumeErrorArg){this->findVolumeError = findVolumeErrorArg;}
//! \brief This function sets the private member variable oneOfTheMainVariables
//! \param[in] oneOfTheMainVariables Boolean value
//! \details When oneOfTheMainVariables is true, the integration test runs the substest volumeErrorCheckForOneSpecificVariable.
void setOneOfTheMainVariables(bool oneOfTheMainVariablesArg){this->oneOfTheMainVariables = oneOfTheMainVariablesArg;}
//! \brief This function sets the member variable string #mainVariable
//! \param[in] mainVar This is the string should contain one of the main variables. e.g. WOPR
void setMainVariable(std::string mainVar){this->mainVariable = mainVar;}
//! \brief This function sets the private member variable spikeLimit.
//! \param[in] lim The value which the spike limit is to be given.
void setSpikeLimit(int lim){this->spikeLimit = lim;}
//! \brief This function sets the private member variable findVectorWithGreatestErrorRatio
//! \param[in] findVolumeError Boolean value
//! \details When findVectorWithGreatestErrorRatio is true, the integration test will print the vector with the greatest error ratio.
void setFindVectorWithGreatestErrorRatio(bool boolean){this->findVectorWithGreatestErrorRatio = boolean;}
//! \brief This function sets the private member variable allowDifferentAmountsOfKeywords
//! \param[in] boolean Boolean value
//! \details When allowDifferentAmountOfKeywords is false, the amount of kewyord in the two files will be compared. \nIf the number of keywords are different an exception will be thrown.
void setAllowDifferentAmountOfKeywords(bool boolean){this->allowDifferentAmountOfKeywords = boolean;}
//! \brief This function sets the private member variable throwExceptionForTooGreatErrorRatio
//! \param[in] boolean Boolean value
//! \details When throwExceptionForTooGreatErrorRatio is false, the function getWellProductionVolume will throw an exception.
void setThrowExceptionForTooGreatErrorRatio(bool boolean){this->throwExceptionForTooGreatErrorRatio = boolean;}
//! \brief This function executes a integration test for all the keywords. If the two files do not match in amount of keywords, an exception is thrown. \n Uses the boolean member variables to know which tests to execute.
void getIntegrationTest();
//! \brief This function executes a integration test for one specific keyword. If one or both of the files do not have the keyword, an exception is thorwn. \n Uses the boolean member variables to know which tests to execute.
void getIntegrationTest(const char* keyword);
//! \brief This function calculates the area of an rectangle of height height and width time-timePrev
//! \param[in] height The height of the rectangle. See important statement of use below.
//! \param[in] width The width of the rectangle
//! \param[out] area Returns the area of the rectangle
//! \details This function is simple. When using it on a summary vector (data values plotted againt time), calculating the area between the two points i and i+1 note this:\nThe width is time_of_i+1 - time_of_i, the height is data_of_i+1 NOT data_of_i. The upper limit must be used.
static double getRectangleArea(double height, double width){return height*width;}
//! \brief This function calculates the area under a graph by doing a Riemann sum
//! \param[in] timeVec Contains the time values
//! \param[in] dataVec Contains the data values
//! \details The function does a Riemann sum integration of the graph formed
//! by the points (timeVec[i] , dataVec[i]).
//! In the case of a summary vector, the summary vector of quantity
//! corresponding to a rate, is a piecewise continus function consisting
//! of unit step functions. Thus the Riemann sum will become an
//! exact expression for the integral of the graph.
//! Important: For the data values correspoding to time i and i-1,
//! the fixed value of the height of the rectangles in the Riemann sum
//! is set by the data value i. The upper limit must be used.
static double integrate(const std::vector<double>& timeVec,
const std::vector<double>& dataVec);
//! \brief This function calculates the Riemann sum of the error between two graphs.
//! \param[in] timeVec1 Contains the time values of graph 1
//! \param[in] dataVec1 Contains the data values of graph 1
//! \param[in] timeVec2 Contains the time values of graph 2
//! \param[in] dataVec2 Contains the data values of graph 2
//! \details This function takes in two graphs and returns the integrated error.
//! In case of ecl summary vectors: if the vectors correspond to a
//! quantity which is a rate, the vectors will be piecewise
//! continous unit step functions. In this case the error will also
//! be a piecewise continous unit step function. The function uses
//! a Riemann sum when calculating the integral. Thus the integral
//! will become exact. Important: For the data values corresponding
//! to time i and i-1, the fixed value of the height of the rectangles
//! in the Riemann sum is set by the data value i.
//! The upper limit must be used.
static double integrateError(const std::vector<double>& timeVec1,
const std::vector<double>& dataVec1,
const std::vector<double>& timeVec2,
const std::vector<double>& dataVec2);
};

View File

@@ -1,178 +0,0 @@
/*
Copyright 2016 Statoil ASA.
This file is part of the Open Porous Media project (OPM).
OPM 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.
OPM 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 for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#include "summaryRegressionTest.hpp"
#include <opm/common/ErrorMacros.hpp>
#include <ert/ecl/ecl_sum.hpp>
#include <ert/util/stringlist.h>
#include <string>
void SummaryRegressionTest::getRegressionTest(){
std::vector<double> timeVec1, timeVec2;
setTimeVecs(timeVec1, timeVec2); // Sets the time vectors, they are equal for all keywords (WPOR:PROD01 etc)
setDataSets(timeVec1, timeVec2); //Figures which dataset that contains more/less values pr keyword vector.
std::cout << "Comparing " << timeVec1.size() << " steps." << std::endl;
int ivar = 0;
if((! this->allowDifferentNumberOfKeywords) &&
(stringlist_get_size(keysShort) != stringlist_get_size(keysLong)))
{
int missing_count = 0;
std::cout << "Keywords missing from one case: " << std::endl;
for (int i=0; i < stringlist_get_size( keysLong); i++) {
const char * key = stringlist_iget( keysLong , i );
if (!stringlist_contains( keysShort , key)) {
std::cout << key << " ";
missing_count++;
if ((missing_count % 8) == 0)
std::cout << std::endl;
}
}
std::cout << std::endl;
HANDLE_ERROR(std::runtime_error, "Different amount of keywords in the two summary files.");
}
if(printKeyword){
printKeywords();
}
//Iterates over all keywords from the restricted file, use iterator "ivar". Searches for a match in the file with more keywords, use the iterator "jvar".
bool throwAtEnd = false;
while(ivar < stringlist_get_size(keysShort)){
const char* keyword = stringlist_iget(keysShort, ivar);
std::string keywordString(keyword);
for (int jvar = 0; jvar < stringlist_get_size(keysLong); jvar++){
if (strcmp(keyword, stringlist_iget(keysLong, jvar)) == 0){ //When the keywords are equal, proceed in comparing summary files.
if (isRestartFile && keywordString.substr(3,1)=="T"){
break;
}
throwAtEnd |= !checkForKeyword(timeVec1, timeVec2, keyword);
break;
}
//will only enter here if no keyword match
if(jvar == stringlist_get_size(keysLong)-1){
std::cout << "Could not find keyword: " << stringlist_iget(keysShort, ivar) << std::endl;
OPM_THROW(std::runtime_error, "No match on keyword");
}
}
ivar++;
}
if (analysis) {
std::cout << deviations.size() << " summary keyword"
<< (deviations.size() > 1 ? "s":"") << " exhibit failures" << std::endl;
size_t len = ecl_sum_get_data_length(ecl_sum1);
for (const auto& iter : deviations) {
std::cout << "\t" << iter.first << std::endl;
std::cout << "\t\tFails for " << iter.second.size() << " / " << len << " steps." << std::endl;
std::cout.precision(7);
double absErr = std::max_element(iter.second.begin(), iter.second.end(),
[](const Deviation& a, const Deviation& b)
{
return a.abs < b.abs;
})->abs;
double relErr = std::max_element(iter.second.begin(), iter.second.end(),
[](const Deviation& a, const Deviation& b)
{
return a.rel < b.rel;
})->rel;
std::cout << "\t\tLargest absolute error: "
<< std::scientific << absErr << std::endl;
std::cout << "\t\tLargest relative error: "
<< std::scientific << relErr << std::endl;
}
}
if (throwAtEnd)
OPM_THROW(std::runtime_error, "Regression test failed.");
else if (deviations.empty())
std::cout << "Regression test succeeded." << std::endl;
}
void SummaryRegressionTest::getRegressionTest(const char* keyword){
std::vector<double> timeVec1, timeVec2;
setTimeVecs(timeVec1, timeVec2); // Sets the time vectors, they are equal for all keywords (WPOR:PROD01 etc)
setDataSets(timeVec1, timeVec2); //Figures which dataset that contains more/less values pr keyword vector.
std::string keywordString(keyword);
if(stringlist_contains(keysShort,keyword) && stringlist_contains(keysLong, keyword)){
if (isRestartFile && keywordString.substr(3,1)=="T"){
return;
}
if (checkForKeyword(timeVec1, timeVec2, keyword))
std::cout << "Regression test succeeded." << std::endl;
else
OPM_THROW(std::runtime_error, "Regression test failed");
return;
}
std::cout << "The keyword suggested, " << keyword << ", is not supported by one or both of the summary files. Please use a different keyword." << std::endl;
OPM_THROW(std::runtime_error, "Input keyword from user does not exist in/is not common for the two summary files.");
}
bool SummaryRegressionTest::checkDeviation(Deviation deviation, const char* keyword, int refIndex, int checkIndex){
double absTol = getAbsTolerance();
double relTol = getRelTolerance();
if (deviation.rel > relTol && deviation.abs > absTol){
if (analysis) {
deviations[keyword].push_back(deviation);
} else {
std::cout << "For keyword " << keyword << std::endl;
std::cout << "(days, reference value) and (days, check value) = (" << (*referenceVec)[refIndex] << ", " << (*referenceDataVec)[refIndex]
<< ") and (" << (*checkVec)[checkIndex-1] << ", " << (*checkDataVec)[checkIndex-1] << ")\n";
// -1 in [checkIndex -1] because checkIndex is updated after leaving getDeviation function
std::cout << "The absolute deviation is " << deviation.abs << ". The tolerance limit is " << absTol << std::endl;
std::cout << "The relative deviation is " << deviation.rel << ". The tolerance limit is " << relTol << std::endl;
HANDLE_ERROR(std::runtime_error, "Deviation exceed the limit.");
}
return false;
}
return true;
}
bool SummaryRegressionTest::checkForKeyword(std::vector<double>& timeVec1, std::vector<double>& timeVec2, const char* keyword){
std::vector<double> dataVec1, dataVec2;
getDataVecs(dataVec1,dataVec2,keyword);
chooseReference(timeVec1, timeVec2,dataVec1,dataVec2);
return startTest(keyword);
}
bool SummaryRegressionTest::startTest(const char* keyword){
size_t jvar = 0;
Deviation deviation;
bool result = true;
for (size_t ivar = 0; ivar < referenceVec->size(); ivar++){
getDeviation(ivar, jvar, deviation);//Reads from the protected member variables in the super class.
result = checkDeviation(deviation, keyword,ivar, jvar);
}
return result;
}
void SummaryRegressionTest::setAllowDifferentNumberOfKeywords(const bool allow)
{
this->allowDifferentNumberOfKeywords = allow;
}

View File

@@ -1,87 +0,0 @@
/*
Copyright 2016 Statoil ASA.
This file is part of the Open Porous Media project (OPM).
OPM 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.
OPM 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 for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SUMMARYREGRESSIONTEST_HPP
#define SUMMARYREGRESSIONTEST_HPP
#include "summaryComparator.hpp"
//! \details The class inherits from the SummaryComparator class, which takes care of all file reading. \n The RegressionTest class compares the values from the two different files and throws exceptions when the deviation is unsatisfying.
class SummaryRegressionTest: public SummaryComparator {
private:
//! \brief Gathers the correct data for comparison for a specific keyword
//! \param[in] timeVec1 The time steps of file 1.
//! \param[in] timeVec2 The time steps of file 2.
//! \param[in] keyword The keyword of interest
//! \details The function prepares a regression test by gathering the data, stroing it into two vectors, \n deciding which is to be used as a reference/basis and calling the test function.
//! \return True if check passed, false otherwise.
bool checkForKeyword(std::vector<double>& timeVec1, std::vector<double>& timeVec2, const char* keyword);
//! \brief The regression test
//! \param[in] keyword The keyword common for both the files. The vectors associated with the keyword are used for comparison.
//! \details Start test uses the private member variables, pointers of std::vector<double> type, which are set to point to the correct vectors in SummaryComparison::chooseReference(...). \n The function iterates over the referenceVev/basis and for each iteration it calculates the deviation with SummaryComparison::getDeviation(..) and stors it in a Deviation struct. \n SummaryComparison::getDeviation takes the int jvar as an reference input, and using it as an iterative index for the values which are to be compared with the basis. \n Thus, by updating the jvar variable every time a deviation is calculated, one keep track jvar and do not have to iterate over already checked values.
bool startTest(const char* keyword);
//! \brief Caluculates a deviation, throws exceptions and writes and error message.
//! \param[in] deviation Deviation struct
//! \param[in] keyword The keyword that the data that are being compared belongs to.
//! \param[in] refIndex The report step of which the deviation originates from in #referenceDataVec.
//! \param[in] checkIndex The report step of which the deviation originates from in #checkDataVec.
//! \details The function checks the values of the Deviation struct against the absolute and relative tolerance, which are private member values of the super class. \n When comparing against the relative tolerance an additional term is added, the absolute deviation has to be greater than 1e-6 for the function to throw an exception. \n When the deviations are too great, the function writes out which keyword, and at what report step the deviation is too great before optionally throwing an exception.
//! \return True if check passed, false otherwise.
bool checkDeviation(Deviation deviation, const char* keyword, int refIndex, int checkIndex);
bool isRestartFile = false; //!< Private member variable, when true the files that are being compared is a restart file vs a normal file
/// Whether or not to require that the two files have the same
/// number of keywords. Throw exception if not.
/// Default value: false (don't allow different number of keywords).
bool allowDifferentNumberOfKeywords = false;
public:
//! \brief Constructor, creates an object of RefressionTest class.
//! \param[in] basename1 Path to file1 without extension.
//! \param[in] basename1 Path to file2 without extension.
//! \param[in] relativeTol The relative tolerance which is to be used in the test.
//! \param[in] absoluteTol The absolute tolerance which is to be used in the test.
//! \details The constructor calls the constructor of the super class.
SummaryRegressionTest(const std::string& basename1,
const std::string& basename2,
double relativeTol, double absoluteTol) :
SummaryComparator(basename1, basename2, relativeTol, absoluteTol) {}
//! \details The function executes a regression test for all the keywords. If the two files do not match in amount of keywords, an exception is thrown.
void getRegressionTest();
//! \details The function executes a regression test for one specific keyword. If one or both of the files do not have the keyword, an exception is thrown.
void getRegressionTest(const char* keyword);///< Regression test for a certain keyword of the files.
//! \brief This function sets the private member variable isRestartFiles
//! \param[in] boolean Boolean value
void setIsRestartFile(bool boolean){this->isRestartFile = boolean;}
/// \brief Dynamically control whether or not to require equal
/// number of keywords (vectors) in the two result sets.
///
/// \param[in] allow Whether or not to allow different number of
/// summary keywords in the two result sets.
void setAllowDifferentNumberOfKeywords(const bool allow);
};
#endif

View File

@@ -61,7 +61,6 @@ namespace Opm { namespace RestartIO { namespace Helpers {
const std::vector<std::string>& restart_field_keys,
const std::map<std::string, size_t>& groupKeyToIndex,
const std::map<std::string, size_t>& fieldKeyToIndex,
const bool ecl_compatible_rst,
const std::size_t simStep,
const Opm::SummaryState& sumState,
const std::vector<int>& inteHead);

View File

@@ -52,7 +52,6 @@ namespace Opm { namespace RestartIO { namespace Helpers {
void captureDynamicWellData(const Opm::Schedule& sched,
const std::size_t sim_step,
const bool ecl_compatible_rst,
const Opm::data::WellRates& xw,
const Opm::SummaryState& smry);

View File

@@ -1,4 +1,5 @@
/*
Copyright 2019 Equinor ASA.
Copyright 2016, 2017 Statoil ASA.
This file is part of the Open Porous Media Project (OPM).
@@ -27,6 +28,49 @@ namespace Opm { namespace RestartIO {
class LogiHEAD
{
public:
/// Key characteristics of simulation run's PVT model.
struct PVTModel
{
/// Whether or not simulation run uses a live oil model (with
/// dissolved gas).
bool isLiveOil { false };
/// Whether or not simulation run uses a wet gas model (with
/// vaporised oil).
bool isWetGas { false };
/// Whether or not simulation run uses a constant
/// compressibility oil model (keyword PVCDO).
bool constComprOil { false };
};
/// Key characteristics of simulation model's saturation functions.
struct SatfuncFlags
{
/// Whether or not simulation run uses directionally dependent
/// relative permeability.
bool useDirectionalRelPerm { false };
/// Whether or not simulation run uses reversible relative
/// permeability functions.
bool useReversibleRelPerm { true };
/// Whether or not simulation run uses end-point scaling.
bool useEndScale { false };
/// Whether or not simulation run uses directionally dependent
/// end-point scaling.
bool useDirectionalEPS { false };
/// Whether or not simulation run uses reversible end-point
/// scaling.
bool useReversibleEPS { true };
/// Whether or not simulation run activates the alternative
/// (three-point) end-point scaling feature.
bool useAlternateEPS { false };
};
LogiHEAD();
~LogiHEAD() = default;
@@ -42,6 +86,21 @@ namespace Opm { namespace RestartIO {
const bool enableHyster
);
/// Assign PVT model characteristics.
///
/// \param[in] pvt Current run's PVT model characteristics.
///
/// \return \code *this \endcode.
LogiHEAD& pvtModel(const PVTModel& pvt);
/// Assign saturation function characteristics.
///
/// \param[in] satfunc Current run's saturation function
/// characteristics.
///
/// \return \code *this \endcode.
LogiHEAD& saturationFunction(const SatfuncFlags& satfunc);
const std::vector<bool>& data() const
{
return this->data_;

View File

@@ -43,37 +43,52 @@ namespace Opm {
namespace out {
class Summary {
public:
Summary( const EclipseState&, const SummaryConfig&, const EclipseGrid&, const Schedule& );
Summary( const EclipseState&, const SummaryConfig&, const EclipseGrid&, const Schedule&, const std::string& );
Summary( const EclipseState&, const SummaryConfig&, const EclipseGrid&, const Schedule&, const char* basename );
public:
Summary( const EclipseState&, const SummaryConfig&, const EclipseGrid&, const Schedule& );
Summary( const EclipseState&, const SummaryConfig&, const EclipseGrid&, const Schedule&, const std::string& );
Summary( const EclipseState&, const SummaryConfig&, const EclipseGrid&, const Schedule&, const char* basename );
void add_timestep(int report_step,
double secs_elapsed,
const EclipseState& es,
const Schedule& schedule,
const data::Wells&,
const std::map<std::string, double>& single_values,
const std::map<std::string, std::vector<double>>& region_values = {},
const std::map<std::pair<std::string, int>, double>& block_values = {});
void add_timestep(int report_step,
double secs_elapsed,
const EclipseState& es,
const Schedule& schedule,
const data::Wells&,
const std::map<std::string, double>& single_values,
const std::map<std::string, std::vector<double>>& region_values = {},
const std::map<std::pair<std::string, int>, double>& block_values = {});
void write();
~Summary();
void eval(SummaryState& summary_state,
int report_step,
double secs_elapsed,
const EclipseState& es,
const Schedule& schedule,
const data::Wells&,
const std::map<std::string, double>& single_values,
const std::map<std::string, std::vector<double>>& region_values = {},
const std::map<std::pair<std::string, int>, double>& block_values = {}) const;
const SummaryState& get_restart_vectors() const;
void reset_cumulative_quantities(const SummaryState& rstrt);
private:
class keyword_handlers;
~Summary();
const EclipseGrid& grid;
out::RegionCache regionCache;
ERT::ert_unique_ptr< ecl_sum_type, ecl_sum_free > ecl_sum;
std::unique_ptr< keyword_handlers > handlers;
double prev_time_elapsed = 0;
SummaryState prev_state;
const SummaryState& get_restart_vectors() const;
void reset_cumulative_quantities(const SummaryState& rstrt);
void write() const;
private:
void internal_store(const SummaryState& summary_state, int report_step, double seconds_elapsed);
class keyword_handlers;
const EclipseGrid& grid;
out::RegionCache regionCache;
ERT::ert_unique_ptr< ecl_sum_type, ecl_sum_free > ecl_sum;
std::unique_ptr< keyword_handlers > handlers;
double prev_time_elapsed = 0;
SummaryState prev_state;
};
}

View File

@@ -0,0 +1,48 @@
/*
Copyright (c) 2019 Equinor ASA
This file is part of the Open Porous Media project (OPM).
OPM 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.
OPM 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 for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef OPM_OUTPUT_ECLIPSE_VECTOR_LOGIHEAD_HPP
#define OPM_OUTPUT_ECLIPSE_VECTOR_LOGIHEAD_HPP
#include <vector>
namespace Opm { namespace RestartIO { namespace Helpers { namespace VectorItems {
// This is a subset of the items in src/opm/output/eclipse/LogiHEAD.cpp .
// Promote items from that list to this in order to make them public.
enum logihead : std::vector<bool>::size_type {
IsLiveOil = 0, // Oil phase w/dissolved gas
IsWetGas = 1, // Gas phase w/vaporised oil
DirKr = 2, // Directional relative permeability
E100RevKr = 3, // Reversible rel. perm. (E100)
E100Radial = 4, // Radial model (E100)
E300Radial = 3, // Radial model (E300, others)
E300RevKr = 4, // Reversible rel. perm. (E300, others)
Hyster = 6, // Enable hysteresis
DualPoro = 14, // Enable dual porosity
EndScale = 16, // Enable end-point scaling
DirEPS = 17, // Directional end-point scaling
RevEPS = 18, // Reversible end-point scaling
AltEPS = 19, // Alternative (3-pt) end-point scaling
ConstCo = 38, // Constant oil compressibility (PVCDO)
HasMSWells = 75, // Whether or not model has MS Wells.
};
}}}} // Opm::RestartIO::Helpers::VectorItems
#endif // OPM_OUTPUT_ECLIPSE_VECTOR_LOGIHEAD_HPP

View File

@@ -64,17 +64,6 @@ namespace Opm { namespace RestartIO { namespace Helpers {
createLogiHead(const EclipseState& es);
std::vector<int> serialize_ICON(int lookup_step, // The integer index used to look up dynamic properties, e.g. the number of well.
int ncwmax, // Max number of completions per well, should be entry 17 from createInteHead.
int niconz, // Number of elements per completion in ICON, should be entry 32 from createInteHead.
const std::vector<const Well*>& sched_wells);
std::vector<double> serialize_SCON(int lookup_step, // The integer index used to look up dynamic properties, e.g. the number of well.
int ncwmax, // Max number of completions per well, should be entry 17 from createInteHead.
int nsconz, // Number of elements per completion in SCON, should be entry 33 from createInteHead.
const std::vector<const Well*>& sched_wells,
const UnitSystem& units);
}}} // Opm::RestartIO::Helpers
#endif // OPM_WRITE_RESTART_HELPERS_HPP

View File

@@ -27,6 +27,7 @@
#include <opm/parser/eclipse/Units/Dimension.hpp>
#include <opm/parser/eclipse/Utility/Typetools.hpp>
#include <opm/parser/eclipse/Deck/UDAValue.hpp>
namespace Opm {
class DeckOutput;
@@ -39,6 +40,7 @@ namespace Opm {
DeckItem( const std::string&, int, size_t size_hint = 8 );
DeckItem( const std::string&, double, size_t size_hint = 8 );
DeckItem( const std::string&, std::string, size_t size_hint = 8 );
DeckItem( const std::string&, UDAValue, size_t size_hint = 8 );
const std::string& name() const;
@@ -58,6 +60,7 @@ namespace Opm {
size_t size() const;
size_t out_size() const;
//template< typename T > T& get( size_t ) ;
template< typename T > const T& get( size_t ) const;
double getSIDouble( size_t ) const;
std::string getTrimmedString( size_t ) const;
@@ -65,12 +68,15 @@ namespace Opm {
template< typename T > const std::vector< T >& getData() const;
const std::vector< double >& getSIDoubleData() const;
void push_back( UDAValue );
void push_back( int );
void push_back( double );
void push_back( std::string );
void push_back( UDAValue, size_t );
void push_back( int, size_t );
void push_back( double, size_t );
void push_back( std::string, size_t );
void push_backDefault( UDAValue );
void push_backDefault( int );
void push_backDefault( double );
void push_backDefault( std::string );
@@ -107,9 +113,10 @@ namespace Opm {
bool operator!=(const DeckItem& other) const;
static bool to_bool(std::string string_value);
private:
std::vector< double > dval;
mutable std::vector< double > dval;
std::vector< int > ival;
std::vector< std::string > sval;
std::vector< UDAValue > uval;
type_tag type = type_tag::unknown;

View File

@@ -0,0 +1,61 @@
/*
Copyright 2019 Equinor ASA.
This file is part of the Open Porous Media project (OPM).
OPM 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.
OPM 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 for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef UDA_VALUE_HPP
#define UDA_VALUE_HPP
#include <stdexcept>
#include <vector>
#include <string>
#include <opm/parser/eclipse/Units/Dimension.hpp>
namespace Opm {
class UDAValue {
public:
UDAValue();
explicit UDAValue(double);
explicit UDAValue(const std::string&);
template<typename T>
T get() const;
template<typename T>
bool is() const;
void set_dim(const Dimension& dim) const;
const Dimension& get_dim() const;
bool operator==(const UDAValue& other) const;
bool operator!=(const UDAValue& other) const;
private:
bool numeric_value;
double double_value;
std::string string_value;
/* This 'mutable' modifier is a hack to avoid tampering with the overall
const-ness of the data in a deck item. */
mutable Dimension dim;
};
}
#endif

View File

@@ -89,6 +89,29 @@ class DynamicState {
std::fill_n( this->m_data.begin(), this->initial_range, initial );
}
std::vector<std::pair<std::size_t, T>> unique() {
if (this->m_data.empty())
return {};
auto& current_value = this->m_data[0];
std::size_t current_index = 0;
std::vector<std::pair<std::size_t, T>> result{{current_index, current_value}};
while (true) {
if (this->m_data[current_index] != current_value) {
current_value = this->m_data[current_index];
result.emplace_back(current_index, current_value);
}
current_index++;
if (current_index == this->m_data.size())
break;
}
return result;
}
/**
If the current value has been changed the method will
return true, otherwise it will return false.

View File

@@ -34,7 +34,6 @@ namespace Opm {
class TimeMap;
class Well;
namespace GroupInjection {
struct InjectionData {
@@ -129,7 +128,7 @@ namespace Opm {
bool hasWell(const std::string& wellName , size_t time_step) const;
const std::set< std::string >& getWells( size_t time_step ) const;
size_t numWells(size_t time_step) const;
void addWell(size_t time_step, const Well* well);
void addWell(size_t time_step, const std::string& well_name);
void delWell(size_t time_step, const std::string& wellName );
private:

View File

@@ -27,39 +27,46 @@
namespace Opm {
class GroupTree {
public:
void update( const std::string& name);
void update( const std::string& name, const std::string& parent);
void updateSeqIndex( const std::string& name, const std::string& other_parent);
bool exists( const std::string& group ) const;
const std::string& parent( const std::string& name ) const;
std::vector< std::string > children( const std::string& parent ) const;
const std::map<std::string , size_t>& nameSeqIndMap() const;
const std::map<size_t, std::string >& seqIndNameMap() const;
bool operator==( const GroupTree& ) const;
bool operator!=( const GroupTree& ) const;
public:
struct group {
std::string name;
std::string parent;
private:
struct group {
std::string name;
std::string parent;
bool operator<( const group& rhs ) const;
bool operator==( const std::string& name ) const;
bool operator!=( const std::string& name ) const;
bool operator<( const group& rhs ) const;
bool operator==( const std::string& name ) const;
bool operator!=( const std::string& name ) const;
bool operator<( const std::string& name ) const;
bool operator==( const group& rhs ) const;
bool operator!=( const group& rhs ) const;
};
bool operator<( const std::string& name ) const;
bool operator==( const group& rhs ) const;
bool operator!=( const group& rhs ) const;
};
void update( const std::string& name);
void update( const std::string& name, const std::string& parent);
void updateSeqIndex( const std::string& name, const std::string& other_parent);
bool exists( const std::string& group ) const;
const std::string& parent( const std::string& name ) const;
std::vector< std::string > children( const std::string& parent ) const;
const std::map<std::string , size_t>& nameSeqIndMap() const;
const std::map<size_t, std::string >& seqIndNameMap() const;
bool operator==( const GroupTree& ) const;
bool operator!=( const GroupTree& ) const;
std::vector<GroupTree::group>::const_iterator begin() const;
std::vector<GroupTree::group>::const_iterator end() const;
std::vector< group > groups = { group { "FIELD", "" } };
friend bool operator<( const std::string&, const group& );
std::vector< group >::iterator find( const std::string& );
std::map<std::string , size_t> m_nameSeqIndMap;
std::map<size_t, std::string > m_seqIndNameMap;
private:
std::vector< group >::iterator find( const std::string& );
friend bool operator<( const std::string&, const group& );
std::vector< group > groups = { group { "FIELD", "" } };
std::map<std::string , size_t> m_nameSeqIndMap;
std::map<size_t, std::string > m_seqIndNameMap;
};
std::ostream& operator<<(std::ostream& stream, const GroupTree& gt);
}
#endif /* GROUPTREE_HPP */

View File

@@ -34,14 +34,12 @@ namespace Opm {
public:
WellSegments() = default;
std::string wellName() const;
int numberBranch() const;
const std::string& wellName() const;
int size() const;
double depthTopSegment() const;
double lengthTopSegment() const;
double volumeTopSegment() const;
WellSegment::LengthDepthEnum lengthDepthType() const;
WellSegment::CompPressureDropEnum compPressureDrop() const;
WellSegment::MultiPhaseModelEnum multiPhaseModel() const;
@@ -56,18 +54,17 @@ namespace Opm {
const Segment& operator[](size_t idx) const;
void orderSegments();
void processABS();
void processINC(const bool first_time);
void process(bool first_time);
bool operator==( const WellSegments& ) const;
bool operator!=( const WellSegments& ) const;
private:
void processABS();
void processINC(const bool first_time);
// name of the well
std::string m_well_name;
// number of the branches
int m_number_branch;
// depth of the nodal point of the top segment
// it is taken as the BHP reference depth of the well
// BHP reference depth data from elsewhere will be ignored for multi-segmented wells
@@ -93,6 +90,7 @@ namespace Opm {
// storage index in the vector
std::map<int, int> segment_number_to_index;
};
std::ostream& operator<<( std::ostream&, const WellSegments& );
}
#endif

View File

@@ -40,6 +40,7 @@
#include <opm/parser/eclipse/EclipseState/Schedule/VFPInjTable.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/VFPProdTable.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Well/Well.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Well/Well2.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Well/WellTestConfig.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Action/Actions.hpp>
@@ -113,7 +114,6 @@ namespace Opm
size_t numWells() const;
size_t numWells(size_t timestep) const;
size_t getMaxNumConnectionsForWells(size_t timestep) const;
bool hasWell(const std::string& wellName) const;
std::vector<std::string> wellNames(const std::string& pattern, size_t timeStep, const std::vector<std::string>& matching_wells = {}) const;
@@ -121,24 +121,14 @@ namespace Opm
std::vector<std::string> wellNames(size_t timeStep) const;
std::vector<std::string> wellNames() const;
void updateWell(std::shared_ptr<Well2> well, size_t reportStep);
const Well2& getWell2(const std::string& wellName, size_t timeStep) const;
std::vector< const Well2* > getChildWells2(const std::string& group_name, size_t timeStep, GroupWellQueryMode query_mode) const;
const Well* getWell(const std::string& wellName) const;
std::vector< const Well* > getOpenWells(size_t timeStep) const;
std::vector< const Well* > getWells() const;
std::vector< const Well* > getWells(size_t timeStep) const;
/*
The overload with a group name argument will return all
wells beneath that particular group; i.e.
getWells("FIELD",t);
is an inefficient way to get all the wells defined at time
't'.
*/
//std::vector< const Group& > getChildGroups(const std::string& group_name, size_t timeStep) const;
std::vector< const Group* > getChildGroups(const std::string& group_name, size_t timeStep) const;
std::vector< const Well* > getWells(const std::string& group, size_t timeStep) const;
std::vector< const Well* > getChildWells(const std::string& group_name, size_t timeStep) const;
std::vector< const Well* > getChildWells(const std::string& group_name, size_t timeStep, GroupWellQueryMode query_mode) const;
const OilVaporizationProperties& getOilVaporizationProperties(size_t timestep) const;
const WellTestConfig& wtestConfig(size_t timestep) const;
@@ -148,6 +138,7 @@ namespace Opm
void evalAction(const SummaryState& summary_state, size_t timeStep);
const GroupTree& getGroupTree(size_t t) const;
std::vector< const Group* > getChildGroups(const std::string& group_name, size_t timeStep) const;
size_t numGroups() const;
size_t numGroups(size_t timeStep) const;
bool hasGroup(const std::string& groupName) const;
@@ -180,6 +171,7 @@ namespace Opm
TimeMap m_timeMap;
OrderedMap< std::string, Well > m_wells;
OrderedMap< std::string, Group > m_groups;
OrderedMap< std::string, DynamicState<std::shared_ptr<Well2>>> wells_static;
DynamicState< GroupTree > m_rootGroupTree;
DynamicState< OilVaporizationProperties > m_oilvaporizationproperties;
Events m_events;
@@ -192,6 +184,7 @@ namespace Opm
DynamicState<std::shared_ptr<WellTestConfig>> wtest_config;
DynamicState<std::shared_ptr<WListManager>> wlist_manager;
DynamicState<std::shared_ptr<UDQInput>> udq_config;
DynamicState<WellProducer::ControlModeEnum> global_whistctl_mode;
RFTConfig rft_config;
Actions m_actions;
@@ -200,8 +193,8 @@ namespace Opm
std::vector< Group* > getGroups(const std::string& groupNamePattern);
std::map<std::string,Events> well_events;
void updateWellStatus( Well& well, size_t reportStep , WellCommon::StatusEnum status);
void addWellToGroup( Group& newGroup , Well& well , size_t timeStep);
bool updateWellStatus( const std::string& well, size_t reportStep , WellCommon::StatusEnum status);
void addWellToGroup( Group& newGroup , const std::string& wellName , size_t timeStep);
void iterateScheduleSection(const ParseContext& parseContext , ErrorGuard& errors, const SCHEDULESection& , const EclipseGrid& grid,
const Eclipse3DProperties& eclipseProperties);
bool handleGroupFromWELSPECS(const std::string& groupName, GroupTree& newTree) const;
@@ -211,7 +204,6 @@ namespace Opm
void handleWLIST(const DeckKeyword& keyword, size_t currentStep);
void handleCOMPORD(const ParseContext& parseContext, ErrorGuard& errors, const DeckKeyword& compordKeyword, size_t currentStep);
void handleWELSPECS( const SCHEDULESection&, size_t, size_t );
void handleWCONProducer( const DeckKeyword& keyword, size_t currentStep, bool isPredictionMode, const ParseContext& parseContext, ErrorGuard& errors);
void handleWCONHIST( const DeckKeyword& keyword, size_t currentStep, const ParseContext& parseContext, ErrorGuard& errors);
void handleWCONPROD( const DeckKeyword& keyword, size_t currentStep, const ParseContext& parseContext, ErrorGuard& errors);
void handleWGRUPCON( const DeckKeyword& keyword, size_t currentStep);
@@ -263,9 +255,7 @@ namespace Opm
const UnitSystem& unit_system,
std::vector<std::pair<const DeckKeyword*, size_t > >& rftProperties);
void addWellEvent(const std::string& well, ScheduleEvents::Events event, size_t reportStep);
static bool convertEclipseStringToBool(const std::string& eclipseString);
#ifdef CHECK_WELLS
#ifdef WELL_TEST
bool checkWells(const ParseContext& parseContext, ErrorGuard& errors) const;
#endif
};

View File

@@ -310,6 +310,11 @@ namespace Opm {
}
enum class GroupWellQueryMode {
Immediate,
Recursive
};
}
#endif

View File

@@ -66,20 +66,30 @@ class SummaryState {
public:
typedef std::unordered_map<std::string, double>::const_iterator const_iterator;
double get(const std::string&) const;
bool has(const std::string& key) const;
void add(const std::string& key, double value);
void add(const ecl::smspec_node& node, double value);
/*
The set() function has to be retained temporarily to support updating of
cumulatives from restart files.
*/
void set(const std::string& key, double value);
void add_well_var(const std::string& well, const std::string& var, double value);
bool has(const std::string& key) const;
bool has_well_var(const std::string& well, const std::string& var) const;
void update(const std::string& key, double value);
void update(const ecl::smspec_node& node, double value);
void update_well_var(const std::string& well, const std::string& var, double value);
void update_elapsed(double delta);
double get(const std::string&) const;
double get_well_var(const std::string& well, const std::string& var) const;
double get_elapsed() const;
std::vector<std::string> wells() const;
std::vector<std::string> wells(const std::string& var) const;
const_iterator begin() const;
const_iterator end() const;
private:
double elapsed;
std::unordered_map<std::string,double> values;
// The first key is the variable and the second key is the well.

View File

@@ -50,11 +50,10 @@ namespace Opm {
double skin_factor,
const int satTableId,
const WellCompletion::DirectionEnum direction,
const std::size_t seqIndex,
const double segDistStart,
const double segDistEnd,
const bool defaultSatTabId
);
const std::size_t seqIndex,
const double segDistStart,
const double segDistEnd,
const bool defaultSatTabId);
bool attachedToSegment() const;
@@ -79,15 +78,16 @@ namespace Opm {
void setComplnum(int compnum);
void scaleWellPi(double wellPi);
void updateSegment(int segment_number, double center_depth, std::size_t seqIndex);
const std::size_t& getSeqIndex() const;
const bool& getDefaultSatTabId() const;
const std::size_t& getCompSegSeqIndex() const;
void setCompSegSeqIndex(std::size_t index);
void setDefaultSatTabId(bool id);
const double& getSegDistStart() const;
const double& getSegDistEnd() const;
void setSegDistStart(const double& distStart);
void setSegDistEnd(const double& distEnd);
const std::size_t& getSeqIndex() const;
const bool& getDefaultSatTabId() const;
const std::size_t& getCompSegSeqIndex() const;
void setCompSegSeqIndex(std::size_t index);
void setDefaultSatTabId(bool id);
const double& getSegDistStart() const;
const double& getSegDistEnd() const;
void setSegDistStart(const double& distStart);
void setSegDistEnd(const double& distEnd);
std::string str() const;
bool operator==( const Connection& ) const;
bool operator!=( const Connection& ) const;
@@ -104,11 +104,11 @@ namespace Opm {
double m_skin_factor;
std::array<int,3> ijk;
std::size_t m_seqIndex;
double m_segDistStart;
double m_segDistEnd;
bool m_defaultSatTabId;
std::size_t m_compSeg_seqIndex=0;
std::size_t m_seqIndex;
double m_segDistStart;
double m_segDistEnd;
bool m_defaultSatTabId;
std::size_t m_compSeg_seqIndex=0;
// related segment number
// -1 means the completion is not related to segment

View File

@@ -52,6 +52,7 @@ namespace Opm {
public:
Well(const std::string& name, const size_t& seqIndex, int headI,
int headJ, double refDepth, double drainageRadius, Phase preferredPhase,
WellProducer::ControlModeEnum whist_ctl,
const TimeMap& timeMap, size_t creationTimeStep,
WellCompletion::CompletionOrderEnum completionOrdering = WellCompletion::TRACK,
bool allowCrossFlow = true, bool automaticShutIn = true);
@@ -95,6 +96,7 @@ namespace Opm {
void switchToInjector( size_t timeStep);
void switchToProducer( size_t timeStep);
bool setProducer(size_t timeStep, bool producer);
bool isProducer(size_t timeStep) const;
bool isInjector(size_t timeStep) const;
void addWELSPECS(const DeckRecord& deckRecord);

View File

@@ -0,0 +1,169 @@
/*
Copyright 2019 Equinor ASA.
This file is part of the Open Porous Media project (OPM).
OPM 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.
OPM 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 for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef WELL2_HPP
#define WELL2_HPP
#include <string>
#include <opm/parser/eclipse/EclipseState/Runspec.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/ScheduleEnums.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Well/WellConnections.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/MSW/WellSegments.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Well/WellTracerProperties.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Well/WellInjectionProperties.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Well/WellProductionProperties.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Well/WellPolymerProperties.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Well/WellEconProductionLimits.hpp>
namespace Opm {
class DeckRecord;
class EclipseGrid;
class DeckKeyword;
struct WellGuideRate {
bool available;
double guide_rate;
GuideRate::GuideRatePhaseEnum guide_phase;
double scale_factor;
};
class Well2 {
public:
Well2(const std::string& wname,
const std::string& gname,
std::size_t init_step,
std::size_t insert_index,
int headI,
int headJ,
double ref_depth,
Phase phase,
WellProducer::ControlModeEnum whistctl_cmode,
WellCompletion::CompletionOrderEnum ordering);
bool isMultiSegment() const;
bool isAvailableForGroupControl() const;
double getGuideRate() const;
GuideRate::GuideRatePhaseEnum getGuideRatePhase() const;
double getGuideRateScalingFactor() const;
std::size_t firstTimeStep() const;
bool canOpen() const;
bool isProducer() const;
size_t seqIndex() const;
bool getAutomaticShutIn() const;
bool getAllowCrossFlow() const;
const std::string& name() const;
int getHeadI() const;
int getHeadJ() const;
double getRefDepth() const;
double getDrainageRadius() const;
double getEfficiencyFactor() const;
WellCompletion::CompletionOrderEnum getWellConnectionOrdering() const;
const WellProductionProperties& getProductionProperties() const;
const WellInjectionProperties& getInjectionProperties() const;
const WellEconProductionLimits& getEconLimits() const;
const WellPolymerProperties& getPolymerProperties() const;
const WellTracerProperties& getTracerProperties() const;
const WellConnections& getConnections() const;
const WellSegments& getSegments() const;
double getSolventFraction() const;
WellCommon::StatusEnum getStatus() const;
const std::string& groupName() const;
Phase getPreferredPhase() const;
/*
The getCompletions() function will return a map:
{
1 : [Connection, Connection],
2 : [Connection, Connection, Connecton],
3 : [Connection],
4 : [Connection]
}
The integer ID's correspond to the COMPLETION id given by the COMPLUMP
keyword.
*/
std::map<int, std::vector<Connection>> getCompletions() const;
bool updateAutoShutin(bool auto_shutin);
bool updateCrossFlow(bool allow_cross_flow);
bool updateHead(int I, int J);
bool updateRefDepth(double ref_dpeth);
bool updateDrainageRadius(double drainage_radius);
bool updateConnections(const std::shared_ptr<WellConnections> connections);
bool updateStatus(WellCommon::StatusEnum status);
bool updateGroup(const std::string& group);
bool updateProducer(bool is_producer);
bool updateWellGuideRate(bool available, double guide_rate, GuideRate::GuideRatePhaseEnum guide_phase, double scale_factor);
bool updateWellGuideRate(double guide_rate);
bool updateEfficiencyFactor(double efficiency_factor);
bool updateSolventFraction(double solvent_fraction);
bool updateTracer(std::shared_ptr<WellTracerProperties> tracer_properties);
bool updatePolymerProperties(std::shared_ptr<WellPolymerProperties> polymer_properties);
bool updateEconLimits(std::shared_ptr<WellEconProductionLimits> econ_limits);
bool updateProduction(std::shared_ptr<WellProductionProperties> production);
bool updateInjection(std::shared_ptr<WellInjectionProperties> injection);
void switchToProducer();
void switchToInjector();
bool handleWELSEGS(const DeckKeyword& keyword);
bool handleCOMPSEGS(const DeckKeyword& keyword, const EclipseGrid& grid);
bool handleWELOPEN(const DeckRecord& record, WellCompletion::StateEnum status);
bool handleCOMPLUMP(const DeckRecord& record);
bool handleWPIMULT(const DeckRecord& record);
void filterConnections(const EclipseGrid& grid);
private:
std::string wname;
std::string group_name;
std::size_t init_step;
std::size_t insert_index;
int headI;
int headJ;
double ref_depth;
Phase phase;
WellCompletion::CompletionOrderEnum ordering;
WellCommon::StatusEnum status;
double drainage_radius;
bool allow_cross_flow;
bool automatic_shutin;
bool producer;
WellGuideRate guide_rate;
double efficiency_factor;
double solvent_fraction;
std::shared_ptr<const WellEconProductionLimits> econ_limits;
std::shared_ptr<const WellPolymerProperties> polymer_properties;
std::shared_ptr<const WellTracerProperties> tracer_properties;
std::shared_ptr<WellConnections> connections; // The WellConnections object can not be const because of the filterConnections method - would be beneficial to rewrite to enable const
std::shared_ptr<const WellProductionProperties> production;
std::shared_ptr<const WellInjectionProperties> injection;
std::shared_ptr<const WellSegments> segments;
};
}
#endif

View File

@@ -28,7 +28,6 @@ namespace Opm {
class Eclipse3DProperties;
class WellConnections {
public:
WellConnections() = default;
WellConnections(int headI, int headJ);
// cppcheck-suppress noExplicitConstructor
WellConnections(const WellConnections& src, const EclipseGrid& grid);

View File

@@ -23,6 +23,8 @@
namespace Opm {
class DeckRecord;
struct WellPolymerProperties {
double m_polymerConcentration;
double m_saltConcentration;
@@ -33,6 +35,9 @@ namespace Opm {
bool operator==(const WellPolymerProperties& other) const;
bool operator!=(const WellPolymerProperties& other) const;
WellPolymerProperties();
void handleWPOLYMER(const DeckRecord& record);
void handleWPMITAB(const DeckRecord& record);
void handleWSKPTAB(const DeckRecord& record);
};
}

View File

@@ -75,6 +75,7 @@ namespace Opm {
void handleWCONHIST( const DeckRecord& record);
void handleWELTARG(WellTarget::ControlModeEnum cmode, double newValue, double siFactorG, double siFactorL, double siFactorP);
void resetDefaultBHPLimit();
void clearControls();
private:
int m_productionControls = 0;

View File

@@ -331,6 +331,7 @@ namespace Opm {
const static std::string RPT_UNKNOWN_MNEMONIC;
const static std::string SCHEDULE_WELL_ERROR;
/*
The SIMULATOR_KEYWORD_ errormodes are for the situation where the

View File

@@ -80,6 +80,7 @@ namespace Opm {
size_t numDimensions() const;
const std::string& name() const;
item_size sizeType() const;
type_tag dataType() const;
void setSizeType(item_size size_type);
std::string getDescription() const;
bool scalar() const;
@@ -108,6 +109,7 @@ namespace Opm {
double dval;
int ival;
std::string sval;
UDAValue uval;
std::vector< std::string > dimensions;
std::string m_name;

View File

@@ -26,7 +26,7 @@ namespace Opm {
class Dimension {
public:
Dimension() = default;
Dimension();
Dimension(const std::string& name, double SIfactor, double SIoffset = 0.0);
double getSIScaling() const;

View File

@@ -1,16 +1,15 @@
#ifndef OPM_TYPETOOLS_HPP
#define OPM_TYPETOOLS_HPP
#include <opm/parser/eclipse/Deck/UDAValue.hpp>
namespace Opm {
enum class type_tag {
/* for python interop as well as queries, must be manually synchronised
* with cdeck_item.cc and opm/deck/item_type_enum.py
*/
unknown = 0,
integer = 1,
string = 2,
fdouble = 3,
uda = 4,
};
inline std::string tag_name( type_tag x ) {
@@ -18,23 +17,30 @@ inline std::string tag_name( type_tag x ) {
case type_tag::integer: return "int";
case type_tag::string: return "std::string";
case type_tag::fdouble: return "double";
case type_tag::uda: return "UDAValue";
case type_tag::unknown: return "unknown";
}
return "unknown";
}
template< typename T > type_tag get_type();
template<> inline type_tag get_type< int >() {
return type_tag::integer;
}
template<> inline type_tag get_type< double >() {
return type_tag::fdouble;
}
template<> inline type_tag get_type< std::string >() {
return type_tag::string;
}
template<> inline type_tag get_type<UDAValue>() {
return type_tag::uda;
}
}
#endif //OPM_TYPETOOLS_HPP

View File

@@ -2,10 +2,10 @@
# spec file for package opm-common
#
%define tag rc2
%define tag final
Name: opm-common
Version: 2019.04
Version: 2018.10
Release: 0
Summary: Open Porous Media - common helpers and buildsystem
License: GPL-3.0

View File

@@ -178,7 +178,7 @@ namespace {
// location nwgmax +1 in the iGrp array
const auto childGroups = sched.getChildGroups(group.name(), simStep);
const auto childWells = sched.getChildWells(group.name(), simStep);
const auto childWells = sched.getChildWells(group.name(), simStep, Opm::GroupWellQueryMode::Immediate);
const auto groupMapNameIndex = currentGroupMapNameIndex(sched, simStep, inteHead);
const auto mapIndexGroup = currentGroupMapIndexGroup(sched, simStep, inteHead);
if ((childGroups.size() != 0) && (childWells.size()!=0))
@@ -369,31 +369,6 @@ namespace {
};
}
std::vector<std::string>
filter_cumulative(const bool ecl_compatible_rst,
const std::vector<std::string>& keys)
{
if (ecl_compatible_rst) {
// User wants ECLIPSE-compatible output. Write all vectors.
return keys;
}
auto ret = std::vector<std::string>{};
ret.reserve(keys.size());
for (const auto& key : keys) {
if ((key[3] == 'T') && ((key[2] == 'I') || (key[2] == 'P'))) {
// Don't write cumulative quantities in case of OPM
// extended restart files.
continue;
}
ret.push_back(key);
}
return ret;
}
// here define the dynamic group quantities to be written to the restart file
template <class XGrpArray>
void dynamicContrib(const std::vector<std::string>& restart_group_keys,
@@ -402,7 +377,6 @@ namespace {
const std::map<std::string, size_t>& fieldKeyToIndex,
const Opm::Group& group,
const Opm::SummaryState& sumState,
const bool ecl_compatible_rst,
XGrpArray& xGrp)
{
std::string groupName = group.name();
@@ -411,7 +385,7 @@ namespace {
const std::map<std::string, size_t>& keyToIndex = (groupName == "FIELD")
? fieldKeyToIndex : groupKeyToIndex;
for (const auto key : filter_cumulative(ecl_compatible_rst,keys)) {
for (const auto& key : keys) {
std::string compKey = (groupName == "FIELD")
? key : key + ":" + groupName;
@@ -517,7 +491,6 @@ captureDeclaredGroupData(const Opm::Schedule& sched,
const std::vector<std::string>& restart_field_keys,
const std::map<std::string, size_t>& groupKeyToIndex,
const std::map<std::string, size_t>& fieldKeyToIndex,
const bool ecl_compatible_rst,
const std::size_t simStep,
const Opm::SummaryState& sumState,
const std::vector<int>& inteHead)
@@ -554,14 +527,14 @@ captureDeclaredGroupData(const Opm::Schedule& sched,
// Define Dynamic Contributions to XGrp Array.
groupLoop(curGroups, [&restart_group_keys, &restart_field_keys,
&groupKeyToIndex, &fieldKeyToIndex,
ecl_compatible_rst, &sumState, this]
&sumState, this]
(const Group& group, const std::size_t groupID) -> void
{
auto xg = this->xGroup_[groupID];
XGrp::dynamicContrib(restart_group_keys, restart_field_keys,
groupKeyToIndex, fieldKeyToIndex, group,
sumState, ecl_compatible_rst, xg);
sumState, xg);
});
// Define Static Contributions to ZGrp Array.

View File

@@ -613,7 +613,6 @@ namespace {
template <class XWellArray>
void assignProducer(const std::string& well,
const ::Opm::SummaryState& smry,
const bool ecl_compatible_rst,
XWellArray& xWell)
{
using Ix = ::Opm::RestartIO::Helpers::VectorItems::XWell::index;
@@ -638,47 +637,39 @@ namespace {
xWell[Ix::WatCut] = get("WWCT");
xWell[Ix::GORatio] = get("WGOR");
if (ecl_compatible_rst) {
xWell[Ix::OilPrTotal] = get("WOPT");
xWell[Ix::WatPrTotal] = get("WWPT");
xWell[Ix::GasPrTotal] = get("WGPT");
xWell[Ix::VoidPrTotal] = get("WVPT");
}
xWell[Ix::OilPrTotal] = get("WOPT");
xWell[Ix::WatPrTotal] = get("WWPT");
xWell[Ix::GasPrTotal] = get("WGPT");
xWell[Ix::VoidPrTotal] = get("WVPT");
// Not fully characterised.
xWell[Ix::item37] = xWell[Ix::WatPrRate];
xWell[Ix::item38] = xWell[Ix::GasPrRate];
if (ecl_compatible_rst) {
xWell[Ix::HistOilPrTotal] = get("WOPTH");
xWell[Ix::HistWatPrTotal] = get("WWPTH");
xWell[Ix::HistGasPrTotal] = get("WGPTH");
}
xWell[Ix::HistOilPrTotal] = get("WOPTH");
xWell[Ix::HistWatPrTotal] = get("WWPTH");
xWell[Ix::HistGasPrTotal] = get("WGPTH");
}
template <class GetSummaryVector, class XWellArray>
void assignCommonInjector(GetSummaryVector& get,
const bool ecl_compatible_rst,
XWellArray& xWell)
{
using Ix = ::Opm::RestartIO::Helpers::VectorItems::XWell::index;
xWell[Ix::FlowBHP] = get("WBHP");
if (ecl_compatible_rst) {
// Note: Assign both water and gas cumulatives to support
// case of well alternating between injecting water and gas.
xWell[Ix::WatInjTotal] = get("WWIT");
xWell[Ix::GasInjTotal] = get("WGIT");
xWell[Ix::HistWatInjTotal] = get("WWITH");
xWell[Ix::HistGasInjTotal] = get("WGITH");
}
// Note: Assign both water and gas cumulatives to support
// case of well alternating between injecting water and gas.
xWell[Ix::WatInjTotal] = get("WWIT");
xWell[Ix::GasInjTotal] = get("WGIT");
xWell[Ix::HistWatInjTotal] = get("WWITH");
xWell[Ix::HistGasInjTotal] = get("WGITH");
}
template <class XWellArray>
void assignWaterInjector(const std::string& well,
const ::Opm::SummaryState& smry,
const bool ecl_compatible_rst,
XWellArray& xWell)
{
using Ix = ::Opm::RestartIO::Helpers::VectorItems::XWell::index;
@@ -690,7 +681,7 @@ namespace {
return smry.has(key) ? smry.get(key) : 0.0;
};
assignCommonInjector(get, ecl_compatible_rst, xWell);
assignCommonInjector(get, xWell);
// Injection rates reported as negative.
xWell[Ix::WatPrRate] = -get("WWIR");
@@ -705,7 +696,6 @@ namespace {
template <class XWellArray>
void assignGasInjector(const std::string& well,
const ::Opm::SummaryState& smry,
const bool ecl_compatible_rst,
XWellArray& xWell)
{
using Ix = ::Opm::RestartIO::Helpers::VectorItems::XWell::index;
@@ -717,7 +707,7 @@ namespace {
return smry.has(key) ? smry.get(key) : 0.0;
};
assignCommonInjector(get, ecl_compatible_rst, xWell);
assignCommonInjector(get, xWell);
// Injection rates reported as negative production rates.
xWell[Ix::GasPrRate] = -get("WGIR");
@@ -741,11 +731,10 @@ namespace {
void dynamicContrib(const ::Opm::Well& well,
const ::Opm::SummaryState& smry,
const std::size_t sim_step,
const bool ecl_compatible_rst,
XWellArray& xWell)
{
if (well.isProducer(sim_step)) {
assignProducer(well.name(), smry, ecl_compatible_rst, xWell);
assignProducer(well.name(), smry, xWell);
}
else if (well.isInjector(sim_step)) {
using IType = ::Opm::WellInjector::TypeEnum;
@@ -759,16 +748,16 @@ namespace {
break;
case IType::WATER:
assignWaterInjector(well.name(), smry, ecl_compatible_rst, xWell);
assignWaterInjector(well.name(), smry, xWell);
break;
case IType::GAS:
assignGasInjector(well.name(), smry, ecl_compatible_rst, xWell);
assignGasInjector(well.name(), smry, xWell);
break;
case IType::MULTI:
assignWaterInjector(well.name(), smry, ecl_compatible_rst, xWell);
assignGasInjector (well.name(), smry, ecl_compatible_rst, xWell);
assignWaterInjector(well.name(), smry, xWell);
assignGasInjector (well.name(), smry, xWell);
break;
}
}
@@ -883,7 +872,6 @@ void
Opm::RestartIO::Helpers::AggregateWellData::
captureDynamicWellData(const Schedule& sched,
const std::size_t sim_step,
const bool ecl_compatible_rst,
const Opm::data::WellRates& xw,
const ::Opm::SummaryState& smry)
{
@@ -905,11 +893,11 @@ captureDynamicWellData(const Schedule& sched,
});
// Dynamic contributions to XWEL array.
wellLoop(wells, [this, sim_step, ecl_compatible_rst, &smry]
wellLoop(wells, [this, sim_step, &smry]
(const Well& well, const std::size_t wellID) -> void
{
auto xw = this->xWell_[wellID];
XWell::dynamicContrib(well, smry, sim_step, ecl_compatible_rst, xw);
XWell::dynamicContrib(well, smry, sim_step, xw);
});
}

View File

@@ -35,12 +35,28 @@ Opm::RestartIO::Helpers::
createLogiHead(const EclipseState& es)
{
const auto& rspec = es.runspec();
const auto& tabMgr = es.getTableManager();
const auto& phases = rspec.phases();
const auto& wsd = rspec.wellSegmentDimensions();
const auto& hystPar = rspec.hysterPar();
auto pvt = ::Opm::RestartIO::LogiHEAD::PVTModel{};
pvt.isLiveOil = phases.active(::Opm::Phase::OIL) &&
!tabMgr.getPvtoTables().empty();
pvt.isWetGas = phases.active(::Opm::Phase::GAS) &&
!tabMgr.getPvtgTables().empty();
pvt.constComprOil = phases.active(::Opm::Phase::OIL) &&
!(pvt.isLiveOil ||
tabMgr.hasTables("PVDO") ||
tabMgr.getPvcdoTable().empty());
const auto lh = LogiHEAD{}
.variousParam(false, false, wsd.maxSegmentedWells(), hystPar.active())
.pvtModel(pvt)
;
return lh.data();
}

View File

@@ -31,15 +31,18 @@
#include <opm/parser/eclipse/EclipseState/EclipseState.hpp>
#include <opm/parser/eclipse/EclipseState/IOConfig/IOConfig.hpp>
#include <opm/parser/eclipse/EclipseState/Grid/GridProperty.hpp>
#include <opm/parser/eclipse/EclipseState/Runspec.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/ScheduleEnums.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Well/Well.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Well/WellConnections.hpp>
#include <opm/parser/eclipse/Utility/Functional.hpp>
#include <opm/output/eclipse/LogiHEAD.hpp>
#include <opm/output/eclipse/RestartIO.hpp>
#include <opm/output/eclipse/Summary.hpp>
#include <opm/output/eclipse/Tables.hpp>
#include <opm/output/eclipse/WriteRestartHelpers.hpp>
#include <cstdlib>
#include <memory> // unique_ptr
@@ -50,6 +53,7 @@
#include <ert/ecl/EclFilename.hpp>
#include <ert/ecl/ecl_kw_magic.h>
#include <ert/ecl/ecl_kw.h>
#include <ert/ecl/ecl_init_file.h>
#include <ert/ecl/ecl_file.h>
#include <ert/ecl/ecl_grid.h>
@@ -92,8 +96,95 @@ void writeKeyword( ERT::FortIO& fortio ,
}
void writeKeyword(ERT::FortIO& fortio,
const std::string& keywordName,
const std::vector<bool>& data)
{
auto freeKw = [](ecl_kw_type* kw)
{
if (kw != nullptr) { ecl_kw_free(kw); }
};
using KWPtr =
std::unique_ptr<ecl_kw_type, decltype(freeKw)>;
auto kw = KWPtr {
ecl_kw_alloc(keywordName.c_str(), data.size(), ECL_BOOL),
freeKw
};
if (kw == nullptr) { return; }
const auto n = data.size();
for (auto i = 0*n; i < n; ++i) {
ecl_kw_iset_bool(kw.get(), static_cast<int>(i), data[i]);
}
ecl_kw_fwrite(kw.get(), fortio.get());
}
void writeInitFileHeader(const ::Opm::EclipseState& es,
const ::Opm::EclipseGrid& grid,
const ::Opm::Schedule& sched,
ERT::FortIO& fortio)
{
// Expected order of header vectors:
// 1. INTEHEAD
// 2. LOGIHEAD
// 3. DOUBHEAD
// INTEHEAD
{
const auto ih = ::Opm::RestartIO::Helpers::
createInteHead(es, grid, sched, 0.0, 0, 0);
writeKeyword(fortio, "INTEHEAD", ih);
}
// LOGIHEAD
{
const auto& tabMgr = es.getTableManager();
const auto& rspec = es.runspec();
const auto& phases = rspec.phases();
const auto& wsd = rspec.wellSegmentDimensions();
auto pvt = ::Opm::RestartIO::LogiHEAD::PVTModel{};
pvt.isLiveOil = phases.active(::Opm::Phase::OIL) &&
!tabMgr.getPvtoTables().empty();
pvt.isWetGas = phases.active(::Opm::Phase::GAS) &&
!tabMgr.getPvtgTables().empty();
pvt.constComprOil = phases.active(::Opm::Phase::OIL) &&
!(pvt.isLiveOil ||
tabMgr.hasTables("PVDO") ||
tabMgr.getPvcdoTable().empty());
auto lh = ::Opm::RestartIO::LogiHEAD{}
.variousParam(false, false,
wsd.maxSegmentedWells(),
rspec.hysterPar().active());
// Sequenced after 'lh' constructor to ensure that any changes
// to live oil/wet gas elements are from this particular call.
lh.pvtModel(pvt);
writeKeyword(fortio, "LOGIHEAD", lh.data());
}
// DOUBHEAD
{
const auto dh = ::Opm::RestartIO::Helpers::
createDoubHead(es, sched, 0, 0.0, 0.0);
// DOUBHEAD must be output as double precision so we can't use
// writeKeyword() here (double -> float conversion).
ERT::EclKW<double> kw("DOUBHEAD", dh);
kw.fwrite(fortio);
}
}
class RFT {
@@ -243,15 +334,14 @@ void EclipseIO::Impl::writeINITFile( const data::Solution& simProps, std::map<st
ioConfig.getFMTOUT(),
ECL_ENDIAN_FLIP );
writeInitFileHeader(this->es, this->grid, this->schedule, fortio);
// Write INIT header. Observe that the PORV vector is treated
// specially; that is because for this particulat vector we write
// a total of nx*ny*nz values, where the PORV vector has been
// explicitly set to zero for inactive cells. The convention is
// that the active/inactive cell mapping can be inferred by
// reading the PORV vector.
// The PORV vector is a special case. This particular vector always
// holds a total of nx*ny*nz elements, and the elements are explicitly
// set to zero for inactive cells. This treatment implies that the
// active/inactive cell mapping can be inferred by reading the PORV
// vector from the result set.
{
const auto& opm_data = this->es.get3DProperties().getDoubleGridProperty("PORV").getData();
auto ecl_data = opm_data;
@@ -259,14 +349,6 @@ void EclipseIO::Impl::writeINITFile( const data::Solution& simProps, std::map<st
if (!this->grid.cellActive( global_index ))
ecl_data[global_index] = 0;
ecl_init_file_fwrite_header( fortio.get(),
this->grid.c_ptr(),
NULL,
units.getEclType(),
this->es.runspec( ).eclPhaseMask( ),
this->schedule.posixStartTime( ));
units.from_si( UnitSystem::measure::volume, ecl_data );
writeKeyword( fortio, "PORV" , ecl_data );
}
@@ -318,9 +400,7 @@ void EclipseIO::Impl::writeINITFile( const data::Solution& simProps, std::map<st
// Write tables
{
Tables tables( this->es.getUnits() );
tables.addPVTO( this->es.getTableManager().getPvtoTables() );
tables.addPVTG( this->es.getTableManager().getPvtgTables() );
tables.addPVTW( this->es.getTableManager().getPvtwTable() );
tables.addPVTTables(this->es);
tables.addDensity( this->es.getTableManager().getDensityTable( ) );
tables.addSatFunc(this->es);
fwrite(tables, fortio);

View File

@@ -1086,20 +1086,20 @@ namespace {
const auto xwel = wellData.xwel(wellID);
// No unit conversion here. Smry expects output units.
smry.add(key("WOPT"), xwel[VI::XWell::index::OilPrTotal]);
smry.add(key("WWPT"), xwel[VI::XWell::index::WatPrTotal]);
smry.add(key("WGPT"), xwel[VI::XWell::index::GasPrTotal]);
smry.add(key("WVPT"), xwel[VI::XWell::index::VoidPrTotal]);
smry.update(key("WOPT"), xwel[VI::XWell::index::OilPrTotal]);
smry.update(key("WWPT"), xwel[VI::XWell::index::WatPrTotal]);
smry.update(key("WGPT"), xwel[VI::XWell::index::GasPrTotal]);
smry.update(key("WVPT"), xwel[VI::XWell::index::VoidPrTotal]);
smry.add(key("WWIT"), xwel[VI::XWell::index::WatInjTotal]);
smry.add(key("WGIT"), xwel[VI::XWell::index::GasInjTotal]);
smry.update(key("WWIT"), xwel[VI::XWell::index::WatInjTotal]);
smry.update(key("WGIT"), xwel[VI::XWell::index::GasInjTotal]);
smry.add(key("WOPTH"), xwel[VI::XWell::index::HistOilPrTotal]);
smry.add(key("WWPTH"), xwel[VI::XWell::index::HistWatPrTotal]);
smry.add(key("WGPTH"), xwel[VI::XWell::index::HistGasPrTotal]);
smry.update(key("WOPTH"), xwel[VI::XWell::index::HistOilPrTotal]);
smry.update(key("WWPTH"), xwel[VI::XWell::index::HistWatPrTotal]);
smry.update(key("WGPTH"), xwel[VI::XWell::index::HistGasPrTotal]);
smry.add(key("WWITH"), xwel[VI::XWell::index::HistWatInjTotal]);
smry.add(key("WGITH"), xwel[VI::XWell::index::HistGasInjTotal]);
smry.update(key("WWITH"), xwel[VI::XWell::index::HistWatInjTotal]);
smry.update(key("WGITH"), xwel[VI::XWell::index::HistGasInjTotal]);
}
void assign_group_cumulatives(const std::string& group,
@@ -1123,19 +1123,19 @@ namespace {
const auto xgrp = groupData.xgrp(groupID);
// No unit conversion here. Smry expects output units.
smry.add(key("OPT"), xgrp[VI::XGroup::index::OilPrTotal]);
smry.add(key("WPT"), xgrp[VI::XGroup::index::WatPrTotal]);
smry.add(key("GPT"), xgrp[VI::XGroup::index::GasPrTotal]);
smry.add(key("VPT"), xgrp[VI::XGroup::index::VoidPrTotal]);
smry.update(key("OPT"), xgrp[VI::XGroup::index::OilPrTotal]);
smry.update(key("WPT"), xgrp[VI::XGroup::index::WatPrTotal]);
smry.update(key("GPT"), xgrp[VI::XGroup::index::GasPrTotal]);
smry.update(key("VPT"), xgrp[VI::XGroup::index::VoidPrTotal]);
smry.add(key("WIT"), xgrp[VI::XGroup::index::WatInjTotal]);
smry.add(key("GIT"), xgrp[VI::XGroup::index::GasInjTotal]);
smry.update(key("WIT"), xgrp[VI::XGroup::index::WatInjTotal]);
smry.update(key("GIT"), xgrp[VI::XGroup::index::GasInjTotal]);
smry.add(key("OPTH"), xgrp[VI::XGroup::index::HistOilPrTotal]);
smry.add(key("WPTH"), xgrp[VI::XGroup::index::HistWatPrTotal]);
smry.add(key("GPTH"), xgrp[VI::XGroup::index::HistGasPrTotal]);
smry.add(key("WITH"), xgrp[VI::XGroup::index::HistWatInjTotal]);
smry.add(key("GITH"), xgrp[VI::XGroup::index::HistGasInjTotal]);
smry.update(key("OPTH"), xgrp[VI::XGroup::index::HistOilPrTotal]);
smry.update(key("WPTH"), xgrp[VI::XGroup::index::HistWatPrTotal]);
smry.update(key("GPTH"), xgrp[VI::XGroup::index::HistGasPrTotal]);
smry.update(key("WITH"), xgrp[VI::XGroup::index::HistWatInjTotal]);
smry.update(key("GITH"), xgrp[VI::XGroup::index::HistGasInjTotal]);
}
Opm::SummaryState

View File

@@ -1,16 +1,39 @@
#include <opm/output/eclipse/LogiHEAD.hpp>
#include <opm/output/eclipse/VectorItems/logihead.hpp>
#include <utility>
#include <vector>
namespace VI = ::Opm::RestartIO::Helpers::VectorItems;
enum index : std::vector<int>::size_type {
lh_000 = 0 , // TRUE
lh_001 = 1 , // TRUE
lh_002 = 2 , // FALSE
lh_003 = 3 , // FALSE Flag set to FALSE for a non-radial model, TRUE for a radial model (ECLIPSE 300 and other simulators)
lh_004 = 4 , // FALSE Flag set to FALSE for a non-radial model, TRUE for a radial model (ECLIPSE 100)
// Flag to signify live oil (dissolved gas) PVT
IsLiveOil = VI::logihead::IsLiveOil,
// Flag to signify wet gas (vaporised oil) PVT
IsWetGas = VI::logihead::IsWetGas,
// Flag to signify directional relative permeability.
DirKr = VI::logihead::DirKr,
// ECLIPSE 100 flag for reversible rel.perm.
E100RevKr = VI::logihead::E100RevKr,
// ECLIPSE 100 flag for radial geometry.
E100Radial = VI::logihead::E100Radial,
// ECLIPSE 300 flag for radial geometry.
E300Radial = VI::logihead::E300Radial,
// ECLIPSE 300 flag for reversible rel.perm.
E300RevKr = VI::logihead::E300RevKr,
lh_005 = 5 , // FALSE
lh_006 = 6 , // FALSE Flag set to FALSE when no hysteresis, TRUE when hysteresis option is used
// Flag to enable hysteresis
Hyster = VI::logihead::Hyster,
lh_007 = 7 , // FALSE
lh_008 = 8 , // FALSE
lh_009 = 9 , // FALSE
@@ -18,12 +41,24 @@ lh_010 = 10 , // FALSE
lh_011 = 11 , // FALSE
lh_012 = 12 , // FALSE
lh_013 = 13 , // FALSE
lh_014 = 14 , // FALSE Flag for dual porosity model
// Flag to activate dual porosity.
DualPoro = VI::logihead::DualPoro,
lh_015 = 15 , // FALSE
lh_016 = 16 , // TRUE
lh_017 = 17 , // FALSE
lh_018 = 18 , // TRUE
lh_019 = 19 , //
// Flag to activate saturation function end-point scaling.
EndScale = VI::logihead::EndScale,
// Flag to activate directional end-point scaling.
DirEPS = VI::logihead::DirEPS,
// Flag to activate reversible end-point scaling. Typically True.
RevEPS = VI::logihead::RevEPS, // TRUE
// Flag to activate alternative end-point scaling (3-pt option)
AltEPS = VI::logihead::AltEPS,
lh_020 = 20 , // FALSE
lh_021 = 21 , // FALSE
lh_022 = 22 , // FALSE
@@ -42,7 +77,10 @@ lh_034 = 34 , // FALSE
lh_035 = 35 , // FALSE
lh_036 = 36 , // FALSE
lh_037 = 37 , //
lh_038 = 38 , // FALSE
// Flag to signify constant oil compressibility
ConstCo = VI::logihead::ConstCo,
lh_039 = 39 , // FALSE
lh_040 = 40 , // FALSE
lh_041 = 41 , // FALSE
@@ -79,7 +117,10 @@ lh_071 = 71 , // FALSE
lh_072 = 72 , // FALSE
lh_073 = 73 , // FALSE
lh_074 = 74 , // FALSE
lh_075 = 75 , // TRUE If segmented well model is used
// TRUE if segmented well model is used
HasMSWells = VI::logihead::HasMSWells,
lh_076 = 76 , // TRUE
lh_077 = 77 , // FALSE
lh_078 = 78 , // FALSE
@@ -126,10 +167,10 @@ lh_118 = 118 , // FALSE
lh_119 = 119 , // FALSE
lh_120 = 120 , // FALSE
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
LOGIHEAD_NUMBER_OF_ITEMS // MUST be last element of enum.
LOGIHEAD_NUMBER_OF_ITEMS // MUST be last element of enum.
};
// =====================================================================
@@ -142,25 +183,38 @@ Opm::RestartIO::LogiHEAD::LogiHEAD()
Opm::RestartIO::LogiHEAD&
Opm::RestartIO::LogiHEAD::
variousParam(const bool e300_radial, const bool e100_radial, const int nswlmx, const bool enableHyster)
variousParam(const bool e300_radial,
const bool e100_radial,
const int nswlmx,
const bool enableHyster)
{
this -> data_[lh_000] = true;
this -> data_[lh_001] = true;
this -> data_[lh_003] = e300_radial;
this -> data_[lh_004] = e100_radial;
this -> data_[lh_006] = enableHyster;
//this -> data_[lh_016] = true;
//this -> data_[lh_018] = true;
//this -> data_[lh_031] = true;
//this -> data_[lh_044] = true;
this -> data_[lh_075] = nswlmx >= 1; // True if MS Wells exist.
//this -> data_[lh_076] = true;
//this -> data_[lh_087] = true;
//this -> data_[lh_099] = true;
//this -> data_[lh_113] = true;
//this -> data_[lh_114] = true;
//this -> data_[lh_115] = true;
//this -> data_[lh_117] = true;
this -> data_[E300Radial] = e300_radial;
this -> data_[E100Radial] = e100_radial;
this -> data_[Hyster] = enableHyster;
this -> data_[HasMSWells] = nswlmx >= 1; // True if MS Wells exist.
return *this;
}
Opm::RestartIO::LogiHEAD&
Opm::RestartIO::LogiHEAD::pvtModel(const PVTModel& pvt)
{
this->data_[IsLiveOil] = pvt.isLiveOil;
this->data_[IsWetGas ] = pvt.isWetGas;
this->data_[ConstCo ] = pvt.constComprOil;
return *this;
}
Opm::RestartIO::LogiHEAD&
Opm::RestartIO::LogiHEAD::saturationFunction(const SatfuncFlags& satfunc)
{
this->data_[DirKr] = satfunc.useDirectionalRelPerm;
this->data_[E100RevKr] = satfunc.useReversibleRelPerm;
this->data_[EndScale] = satfunc.useEndScale;
this->data_[DirEPS] = satfunc.useDirectionalEPS;
this->data_[RevEPS] = satfunc.useReversibleEPS;
this->data_[AltEPS] = satfunc.useAlternateEPS;
return *this;
}

View File

@@ -84,10 +84,10 @@ namespace {
std::vector<int>
serialize_OPM_IWEL(const data::Wells& wells,
const std::vector<const Well*>& sched_wells)
const std::vector<std::string>& sched_wells)
{
const auto getctrl = [&]( const Well* w ) {
const auto itr = wells.find( w->name() );
const auto getctrl = [&]( const std::string& wname ) {
const auto itr = wells.find( wname );
return itr == wells.end() ? 0 : itr->second.control;
};
@@ -302,7 +302,6 @@ namespace {
void writeGroup(::Opm::RestartIO::ecl_rst_file_type* rst_file,
int sim_step,
const bool ecl_compatible_rst,
const Schedule& schedule,
const Opm::SummaryState& sumState,
const std::vector<int>& ih)
@@ -320,12 +319,12 @@ namespace {
groupData.captureDeclaredGroupData(schedule,
rst_g_keys, rst_f_keys,
grpKeyToInd, fldKeyToInd,
ecl_compatible_rst,
simStep, sumState, ih);
write_kw(rst_file, "IGRP", groupData.getIGroup());
write_kw(rst_file, "SGRP", groupData.getSGroup());
write_kw(rst_file, "XGRP", groupData.getXGroup());
write_kw(rst_file, "ZGRP", serialize_ZWEL(groupData.getZGroup()));
write_kw(rst_file, "ZGRP", serialize_ZWEL(groupData.getZGroup()));
}
void writeMSWData(::Opm::RestartIO::ecl_rst_file_type* rst_file,
@@ -364,7 +363,6 @@ namespace {
auto wellData = Helpers::AggregateWellData(ih);
wellData.captureDeclaredWellData(schedule, units, sim_step, sumState, ih);
wellData.captureDynamicWellData(schedule, sim_step,
ecl_compatible_rst,
wells, sumState);
write_kw(rst_file, "IWEL", wellData.getIWell());
@@ -376,11 +374,12 @@ namespace {
if (!ecl_compatible_rst)
{
const auto sched_wells = schedule.getWells(sim_step);
const auto sched_well_names = schedule.wellNames(sim_step);
const auto opm_xwel =
serialize_OPM_XWEL(wells, sim_step, sched_wells, phases, grid);
const auto opm_iwel = serialize_OPM_IWEL(wells, sched_wells);
const auto opm_iwel = serialize_OPM_IWEL(wells, sched_well_names);
write_kw(rst_file, "OPM_IWEL", opm_iwel);
write_kw(rst_file, "OPM_XWEL", opm_xwel);
@@ -554,8 +553,7 @@ void save(const std::string& filename,
nextStepSize(value), seconds_elapsed,
schedule, grid, es);
writeGroup(rst_file.get(), sim_step, ecl_compatible_rst,
schedule, sumState, inteHD);
writeGroup(rst_file.get(), sim_step, schedule, sumState, inteHD);
// Write well and MSW data only when applicable (i.e., when present)
{

View File

@@ -85,9 +85,7 @@ namespace {
}
};
for (const auto* well : sched.getWells()) {
const auto& well_name = well->name();
for (const auto& well_name : sched.wellNames()) {
makeEntities('W', well_name);
entities.emplace_back("WBHP" , well_name);
@@ -1064,6 +1062,21 @@ static const std::unordered_map< std::string, UnitSystem::measure> block_units =
{"BWSAT" , UnitSystem::measure::identity},
{"BSGAS" , UnitSystem::measure::identity},
{"BGSAT" , UnitSystem::measure::identity},
{"BOSAT" , UnitSystem::measure::identity},
{"BWKR" , UnitSystem::measure::identity},
{"BOKR" , UnitSystem::measure::identity},
{"BKRO" , UnitSystem::measure::identity},
{"BGKR" , UnitSystem::measure::identity},
{"BKRG" , UnitSystem::measure::identity},
{"BKRW" , UnitSystem::measure::identity},
{"BWPC" , UnitSystem::measure::pressure},
{"BGPC" , UnitSystem::measure::pressure},
{"BVWAT" , UnitSystem::measure::viscosity},
{"BWVIS" , UnitSystem::measure::viscosity},
{"BVGAS" , UnitSystem::measure::viscosity},
{"BGVIS" , UnitSystem::measure::viscosity},
{"BVOIL" , UnitSystem::measure::viscosity},
{"BOVIS" , UnitSystem::measure::viscosity},
};
inline std::vector< const Well* > find_wells( const Schedule& schedule,
@@ -1086,7 +1099,7 @@ inline std::vector< const Well* > find_wells( const Schedule& schedule,
if( type == ECL_SMSPEC_GROUP_VAR ) {
if( !schedule.hasGroup( name ) ) return {};
return schedule.getWells( name, sim_step );
return schedule.getChildWells( name, sim_step, GroupWellQueryMode::Recursive);
}
if( type == ECL_SMSPEC_FIELD_VAR )
@@ -1115,6 +1128,39 @@ inline std::vector< const Well* > find_wells( const Schedule& schedule,
}
bool need_wells(ecl_smspec_var_type var_type, const std::string& keyword) {
static const std::set<std::string> region_keywords{"ROIR", "RGIR", "RWIR", "ROPR", "RGPR", "RWPR", "ROIT", "RWIT", "RGIT", "ROPT", "RGPT", "RWPT"};
if (var_type == ECL_SMSPEC_WELL_VAR)
return true;
if (var_type == ECL_SMSPEC_GROUP_VAR)
return true;
if (var_type == ECL_SMSPEC_FIELD_VAR)
return true;
if (var_type == ECL_SMSPEC_COMPLETION_VAR)
return true;
if (var_type == ECL_SMSPEC_SEGMENT_VAR)
return true;
/*
Some of the region keywords are based on summing over all the connections
which fall in the region; i.e. RGIR is the total gas injection rate in the
region and consequently the list of defined wells is required, other
region keywords like 'ROIP' do not require well information.
*/
if (var_type == ECL_SMSPEC_REGION_VAR) {
if (region_keywords.count(keyword) > 0)
return true;
}
return false;
}
bool is_udq(const std::string& keyword) {
return (keyword.size() > 1 && keyword[1] == 'U');
}
@@ -1125,15 +1171,15 @@ void eval_udq(const Schedule& schedule, std::size_t sim_step, SummaryState& st)
const auto& func_table = udq.function_table();
UDQContext context(func_table, st);
std::vector<std::string> wells;
for (const auto* well : schedule.getWells())
wells.push_back(well->name());
for (const auto& well_name : schedule.wellNames())
wells.push_back(well_name);
for (const auto& assign : udq.assignments(UDQVarType::WELL_VAR)) {
auto ws = assign.eval_wells(wells);
for (const auto& well : wells) {
const auto& udq_value = ws[well];
if (udq_value)
st.add_well_var(well, ws.name(), udq_value.value());
st.update_well_var(well, ws.name(), udq_value.value());
}
}
@@ -1142,7 +1188,7 @@ void eval_udq(const Schedule& schedule, std::size_t sim_step, SummaryState& st)
for (const auto& well : wells) {
const auto& udq_value = ws[well];
if (udq_value)
st.add_well_var(well, def.keyword(), udq_value.value());
st.update_well_var(well, def.keyword(), udq_value.value());
}
}
}
@@ -1350,7 +1396,7 @@ Summary::Summary( const EclipseState& st,
for (const auto& pair : this->handlers->handlers) {
const auto * nodeptr = pair.first;
if (nodeptr->is_total())
this->prev_state.add(*nodeptr, 0);
this->prev_state.update(*nodeptr, 0);
}
}
@@ -1415,14 +1461,15 @@ well_efficiency_factors( const ecl::smspec_node* node,
return efac;
}
void Summary::add_timestep( int report_step,
double secs_elapsed,
const EclipseState& es,
const Schedule& schedule,
const data::Wells& wells ,
const std::map<std::string, double>& single_values,
const std::map<std::string, std::vector<double>>& region_values,
const std::map<std::pair<std::string, int>, double>& block_values) {
void Summary::eval( SummaryState& st,
int report_step,
double secs_elapsed,
const EclipseState& es,
const Schedule& schedule,
const data::Wells& wells ,
const std::map<std::string, double>& single_values,
const std::map<std::string, std::vector<double>>& region_values,
const std::map<std::pair<std::string, int>, double>& block_values) const {
if (secs_elapsed < this->prev_time_elapsed) {
const auto& usys = es.getUnits();
@@ -1439,9 +1486,7 @@ void Summary::add_timestep( int report_step,
};
}
auto* tstep = ecl_sum_add_tstep( this->ecl_sum.get(), report_step, secs_elapsed );
const double duration = secs_elapsed - this->prev_time_elapsed;
SummaryState st;
/* report_step is the number of the file we are about to write - i.e. for instance CASE.S$report_step
* for the data in a non-unified summary file.
@@ -1451,26 +1496,47 @@ void Summary::add_timestep( int report_step,
for( auto& f : this->handlers->handlers ) {
const int num = smspec_node_get_num( f.first );
double unit_applied_val = smspec_node_get_default( f.first );
const auto schedule_wells = find_wells( schedule, f.first, sim_step, this->regionCache );
auto eff_factors = well_efficiency_factors( f.first, schedule, schedule_wells, sim_step );
if (need_wells(smspec_node_get_var_type(f.first ),
smspec_node_get_keyword(f.first))) {
const auto val = f.second( { schedule_wells,
duration,
sim_step,
num,
wells,
this->regionCache,
this->grid,
eff_factors});
const auto schedule_wells = find_wells( schedule, f.first, sim_step, this->regionCache );
/*
It is not a bug as such if the schedule_wells list comes back
empty; it just means that at the current timestep no relevant
wells have been defined and we do not calculate a value.
*/
if (schedule_wells.size() > 0) {
auto eff_factors = well_efficiency_factors( f.first, schedule, schedule_wells, sim_step );
const auto val = f.second( { schedule_wells,
duration,
sim_step,
num,
wells,
this->regionCache,
this->grid,
eff_factors});
unit_applied_val = es.getUnits().from_si( val.unit, val.value );
}
} else {
const auto val = f.second({ {},
duration,
sim_step,
num,
{},
this->regionCache,
this->grid,
{} });
unit_applied_val = es.getUnits().from_si( val.unit, val.value );
}
double unit_applied_val = es.getUnits().from_si( val.unit, val.value );
if (smspec_node_is_total(f.first)) {
const auto* genkey = smspec_node_get_gen_key1( f.first );
unit_applied_val += this->prev_state.get(genkey);
}
st.add(*f.first, unit_applied_val);
st.update(*f.first, unit_applied_val);
}
for( const auto& value_pair : single_values ) {
@@ -1480,7 +1546,7 @@ void Summary::add_timestep( int report_step,
const auto unit = single_values_units.at( key );
double si_value = value_pair.second;
double output_value = es.getUnits().from_si(unit , si_value );
st.add(*node_pair->second, output_value);
st.update(*node_pair->second, output_value);
}
}
@@ -1495,7 +1561,7 @@ void Summary::add_timestep( int report_step,
assert (smspec_node_get_num( nodeptr ) - 1 == static_cast<int>(reg));
double si_value = value_pair.second[reg];
double output_value = es.getUnits().from_si(unit , si_value );
st.add(*nodeptr, output_value);
st.update(*nodeptr, output_value);
}
}
}
@@ -1508,41 +1574,60 @@ void Summary::add_timestep( int report_step,
const auto unit = block_units.at( key.first );
double si_value = value_pair.second;
double output_value = es.getUnits().from_si(unit , si_value );
st.add(*nodeptr, output_value);
st.update(*nodeptr, output_value);
}
}
eval_udq(schedule, sim_step, st);
{
const ecl_sum_type * ecl_sum = this->ecl_sum.get();
const ecl_smspec_type * smspec = ecl_sum_get_smspec(ecl_sum);
auto num_nodes = ecl_smspec_num_nodes(smspec);
for (int node_index = 0; node_index < num_nodes; node_index++) {
const auto& smspec_node = ecl_smspec_iget_node(smspec, node_index);
// The TIME node is treated specially, it is created internally in
// the ecl_sum instance when the timestep is created - and
// furthermore it is not in st SummaryState instance.
if (smspec_node.get_params_index() == ecl_smspec_get_time_index(smspec))
continue;
}
const std::string key = smspec_node.get_gen_key1();
if (st.has(key))
ecl_sum_tstep_iset(tstep, smspec_node.get_params_index(), st.get(key));
/*
else
OpmLog::warning("Have configured summary variable " + key + " for summary output - but it has not been calculated");
*/
}
void Summary::internal_store(const SummaryState& st, int report_step, double secs_elapsed) {
auto* tstep = ecl_sum_add_tstep( this->ecl_sum.get(), report_step, secs_elapsed );
const ecl_sum_type * ecl_sum = this->ecl_sum.get();
const ecl_smspec_type * smspec = ecl_sum_get_smspec(ecl_sum);
auto num_nodes = ecl_smspec_num_nodes(smspec);
for (int node_index = 0; node_index < num_nodes; node_index++) {
const auto& smspec_node = ecl_smspec_iget_node(smspec, node_index);
// The TIME node is treated specially, it is created internally in
// the ecl_sum instance when the timestep is created - and
// furthermore it is not in st SummaryState instance.
if (smspec_node.get_params_index() == ecl_smspec_get_time_index(smspec))
continue;
const std::string key = smspec_node.get_gen_key1();
if (st.has(key))
ecl_sum_tstep_iset(tstep, smspec_node.get_params_index(), st.get(key));
/*
else
OpmLog::warning("Have configured summary variable " + key + " for summary output - but it has not been calculated");
*/
}
}
void Summary::add_timestep( int report_step,
double secs_elapsed,
const EclipseState& es,
const Schedule& schedule,
const data::Wells& wells ,
const std::map<std::string, double>& single_values,
const std::map<std::string, std::vector<double>>& region_values,
const std::map<std::pair<std::string, int>, double>& block_values) {
SummaryState st;
this->eval(st, report_step, secs_elapsed, es, schedule, wells, single_values, region_values, block_values);
this->internal_store(st, report_step, secs_elapsed);
this->prev_state = st;
this->prev_time_elapsed = secs_elapsed;
}
void Summary::write() {
void Summary::write() const {
ecl_sum_fwrite( this->ecl_sum.get() );
}
Summary::~Summary() {}
const SummaryState& Summary::get_restart_vectors() const
@@ -1564,9 +1649,10 @@ void Summary::reset_cumulative_quantities(const SummaryState& rstrt)
// is constructed from information in a restart file--i.e., from
// the double precision restart vectors 'XGRP' and 'XWEL' during
// RestartIO::load().
this->prev_state.add(genkey, rstrt.get(genkey));
this->prev_state.set(genkey, rstrt.get(genkey));
}
}
}
}} // namespace Opm::out

View File

@@ -1,94 +0,0 @@
/*
Copyright (c) 2018 Statoil ASA
This file is part of the Open Porous Media project (OPM).
OPM 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.
OPM 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 for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#include <opm/output/eclipse/WriteRestartHelpers.hpp>
#include <ert/ecl_well/well_const.h> // containts ICON_XXX_INDEX
#include <opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp>
#include <opm/parser/eclipse/Units/UnitSystem.hpp>
#include <vector>
// ----------------------------------------------------------------------------
std::vector<double>
Opm::RestartIO::Helpers::
serialize_SCON(int lookup_step,
int ncwmax,
int nsconz,
const std::vector<const Well*>& sched_wells,
const UnitSystem& units)
// ----------------------------------------------------------------------------
{
const size_t well_field_size = ncwmax * nsconz;
std::vector<double> data(sched_wells.size() * well_field_size, 0);
size_t well_offset = 0;
for (const Opm::Well* well : sched_wells) {
const auto& connections = well->getConnections( lookup_step );
size_t connection_offset = 0;
bool explicit_ctf_not_found = false;
for (const auto& connection : connections) {
const size_t offset = well_offset + connection_offset;
data[ offset + SCON_CF_INDEX ] = units.from_si(Opm::UnitSystem::measure::transmissibility,connection.CF());
data[ offset + SCON_KH_INDEX ] = units.from_si(Opm::UnitSystem::measure::effective_Kh, connection.Kh());
connection_offset += nsconz;
}
if (explicit_ctf_not_found) {
OpmLog::warning("restart output connection data missing",
"Explicit connection transmissibility factors for well " + well->name() + " missing, writing dummy values to restart file.");
}
well_offset += well_field_size;
}
return data;
}
// ----------------------------------------------------------------------------
std::vector<int>
Opm::RestartIO::Helpers::
serialize_ICON(int lookup_step,
int ncwmax,
int niconz,
const std::vector<const Opm::Well*>& sched_wells)
// ----------------------------------------------------------------------------
{
const size_t well_field_size = ncwmax * niconz;
std::vector<int> data(sched_wells.size() * well_field_size, 0);
size_t well_offset = 0;
for (const Opm::Well* well : sched_wells) {
const auto& connections = well->getConnections( lookup_step );
size_t connection_offset = 0;
for (const auto& connection : connections) {
const size_t offset = well_offset + connection_offset;
data[ offset + ICON_IC_INDEX ] = connection.complnum();
data[ offset + ICON_I_INDEX ] = connection.getI() + 1;
data[ offset + ICON_J_INDEX ] = connection.getJ() + 1;
data[ offset + ICON_K_INDEX ] = connection.getK() + 1;
data[ offset + ICON_DIRECTION_INDEX ] = connection.dir();
data[ offset + ICON_STATUS_INDEX ] =
(connection.state() == WellCompletion::StateEnum::OPEN) ?
1 : -1000;
data[ offset + ICON_SEGMENT_INDEX ] =
connection.attachedToSegment() ?
connection.segment() : 0;
connection_offset += niconz;
}
well_offset += well_field_size;
}
return data;
}

View File

@@ -41,27 +41,46 @@ std::vector< T >& DeckItem::value_ref() {
template<>
const std::vector< int >& DeckItem::value_ref< int >() const {
if( this->type != get_type< int >() )
throw std::invalid_argument( "Item of wrong type." );
throw std::invalid_argument( "DekcItem::velut_ref<int> Item of wrong type." );
return this->ival;
}
template<>
const std::vector< double >& DeckItem::value_ref< double >() const {
if( this->type != get_type< double >() )
throw std::invalid_argument( "Item of wrong type." );
if (this->type == get_type<double>())
return this->dval;
return this->dval;
/* Temporary return double values when user code asks for double on
something which is really a UDAValue.
*/
if( this->type == get_type< UDAValue >() ) {
this->dval.clear();
for (const auto& uv : this->uval)
this->dval.push_back(uv.get<double>());
return this->dval;
}
throw std::invalid_argument( "DeckItem::value_ref<double> Item of wrong type." );
}
template<>
const std::vector< std::string >& DeckItem::value_ref< std::string >() const {
if( this->type != get_type< std::string >() )
throw std::invalid_argument( "Item of wrong type." );
throw std::invalid_argument( "DeckItem::value_ref<std::string> Item of wrong type." );
return this->sval;
}
template<>
const std::vector< UDAValue >& DeckItem::value_ref< UDAValue >() const {
if( this->type != get_type< UDAValue >() )
throw std::invalid_argument( "DeckItem::value_ref<UDAValue> Item of wrong type." );
return this->uval;
}
DeckItem::DeckItem( const std::string& nm ) : item_name( nm ) {}
DeckItem::DeckItem( const std::string& nm, int, size_t hint ) :
@@ -80,6 +99,14 @@ DeckItem::DeckItem( const std::string& nm, double, size_t hint ) :
this->defaulted.reserve( hint );
}
DeckItem::DeckItem( const std::string& nm, UDAValue , size_t hint ) :
type( get_type< UDAValue >() ),
item_name( nm )
{
this->uval.reserve( hint );
this->defaulted.reserve( hint );
}
DeckItem::DeckItem( const std::string& nm, std::string, size_t hint ) :
type( get_type< std::string >() ),
item_name( nm )
@@ -101,7 +128,8 @@ bool DeckItem::hasValue( size_t index ) const {
case type_tag::integer: return this->ival.size() > index;
case type_tag::fdouble: return this->dval.size() > index;
case type_tag::string: return this->sval.size() > index;
default: throw std::logic_error( "Type not set." );
case type_tag::uda: return this->uval.size() > index;
default: throw std::logic_error( "DeckItem::hasValue: Type not set." );
}
}
@@ -110,7 +138,8 @@ size_t DeckItem::size() const {
case type_tag::integer: return this->ival.size();
case type_tag::fdouble: return this->dval.size();
case type_tag::string: return this->sval.size();
default: throw std::logic_error( "Type not set." );
case type_tag::uda: return this->uval.size();
default: throw std::logic_error( "DeckItem::size: Type not set." );
}
}
@@ -119,6 +148,16 @@ size_t DeckItem::out_size() const {
return std::max( data_size , this->defaulted.size() );
}
/*
The non const overload is only used for the UDAValue type because we need to
mutate the UDAValue object and add dimension to it retroactively.
*/
/*template<>
UDAValue& DeckItem::get( size_t index ) {
return this->uval[index];
}
*/
template< typename T >
const T& DeckItem::get( size_t index ) const {
return this->value_ref< T >().at( index );
@@ -149,6 +188,10 @@ void DeckItem::push_back( std::string x ) {
this->push( std::move( x ) );
}
void DeckItem::push_back( UDAValue x ) {
this->push( std::move( x ) );
}
template< typename T >
void DeckItem::push( T x, size_t n ) {
auto& val = this->value_ref< T >();
@@ -169,6 +212,10 @@ void DeckItem::push_back( std::string x, size_t n ) {
this->push( std::move( x ), n );
}
void DeckItem::push_back( UDAValue x, size_t n ) {
this->push( std::move( x ), n );
}
template< typename T >
void DeckItem::push_default( T x ) {
auto& val = this->value_ref< T >();
@@ -192,6 +239,10 @@ void DeckItem::push_backDefault( std::string x ) {
this->push_default( std::move( x ) );
}
void DeckItem::push_backDefault( UDAValue x ) {
this->push_default( std::move( x ) );
}
void DeckItem::push_backDummyDefault() {
if( !this->defaulted.empty() )
@@ -238,12 +289,26 @@ const std::vector< double >& DeckItem::getSIDoubleData() const {
}
void DeckItem::push_backDimension( const Dimension& active,
const Dimension& def ) {
const auto& ds = this->value_ref< double >();
const bool dim_inactive = ds.empty()
|| this->defaultApplied( ds.size() - 1 );
const Dimension& def ) {
if (this->type == type_tag::fdouble) {
const auto& ds = this->value_ref< double >();
const bool dim_inactive = ds.empty()
|| this->defaultApplied( ds.size() - 1 );
this->dimensions.push_back( dim_inactive ? def : active );
this->dimensions.push_back( dim_inactive ? def : active );
return;
}
if (this->type == type_tag::uda) {
auto& du = this->value_ref< UDAValue >();
const bool dim_inactive = du.empty()
|| this->defaultApplied( du.size() - 1 );
this->dimensions.push_back( dim_inactive ? def : active );
return;
}
throw std::logic_error("Tried to push dimensions to an item which can not hold dimension. ");
}
type_tag DeckItem::getType() const {
@@ -274,8 +339,11 @@ void DeckItem::write(DeckOutput& stream) const {
case type_tag::string:
this->write_vector( stream, this->sval );
break;
case type_tag::uda:
this->write_vector( stream, this->uval );
break;
default:
throw std::logic_error( "Type not set." );
throw std::logic_error( "DeckItem::write: Type not set." );
}
}
@@ -405,8 +473,10 @@ bool DeckItem::to_bool(std::string string_value) {
template const int& DeckItem::get< int >( size_t ) const;
template const double& DeckItem::get< double >( size_t ) const;
template const std::string& DeckItem::get< std::string >( size_t ) const;
template const UDAValue& DeckItem::get< UDAValue >( size_t ) const;
template const std::vector< int >& DeckItem::getData< int >() const;
template const std::vector< double >& DeckItem::getData< double >() const;
template const std::vector< UDAValue >& DeckItem::getData< UDAValue >() const;
template const std::vector< std::string >& DeckItem::getData< std::string >() const;
}

View File

@@ -20,6 +20,7 @@
#include <ostream>
#include <opm/parser/eclipse/Deck/DeckOutput.hpp>
#include <opm/parser/eclipse/Deck/UDAValue.hpp>
namespace Opm {
@@ -82,6 +83,14 @@ namespace Opm {
this->os << value;
}
template <>
void DeckOutput::write_value( const UDAValue& value ) {
if (value.is<double>())
this->write_value(value.get<double>());
else
this->write_value(value.get<std::string>());
}
void DeckOutput::stash_default( ) {
this->default_count++;
}
@@ -132,4 +141,5 @@ namespace Opm {
template void DeckOutput::write( const int& value);
template void DeckOutput::write( const double& value);
template void DeckOutput::write( const std::string& value);
template void DeckOutput::write( const UDAValue& value);
}

View File

@@ -0,0 +1,96 @@
/*
Copyright 2019 Statoil ASA.
This file is part of the Open Porous Media project (OPM).
OPM 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.
OPM 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 for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#include <opm/parser/eclipse/Deck/UDAValue.hpp>
namespace Opm {
UDAValue::UDAValue(double value):
numeric_value(true),
double_value(value)
{
}
UDAValue::UDAValue() :
UDAValue(0)
{}
UDAValue::UDAValue(const std::string& value):
numeric_value(false),
string_value(value)
{
}
template<>
bool UDAValue::is<double>() const {
return this->numeric_value;
}
template<>
bool UDAValue::is<std::string>() const {
return !this->numeric_value;
}
template<>
double UDAValue::get() const {
if (this->numeric_value)
return this->double_value;
throw std::invalid_argument("UDAValue does not hold a numerical value");
}
template<>
std::string UDAValue::get() const {
if (!this->numeric_value)
return this->string_value;
throw std::invalid_argument("UDAValue does not hold a string value");
}
void UDAValue::set_dim(const Dimension& dim) const {
this->dim = dim;
}
const Dimension& UDAValue::get_dim() const {
return this->dim;
}
bool UDAValue::operator==(const UDAValue& other) const {
if (this->numeric_value != other.numeric_value)
return false;
if (this->numeric_value)
return (this->double_value == other.double_value);
return this->string_value == other.string_value;
}
bool UDAValue::operator!=(const UDAValue& other) const {
return !(*this == other);
}
}

View File

@@ -291,9 +291,9 @@ namespace Opm {
return this->m_wells.at( time_step ).size();
}
void Group::addWell(size_t time_step, const Well* well ) {
void Group::addWell(size_t time_step, const std::string& well_name ) {
auto new_set = this->m_wells.at( time_step );
new_set.insert( well->name() );
new_set.insert( well_name );
this->m_wells.update( time_step, new_set );
}

View File

@@ -36,13 +36,13 @@ void GroupTree::update( const std::string& name) {
* this grouptree class is just meta data for the actual group objects (stored
* and represented elsewhere)
*/
const std::map<std::string , size_t>& GroupTree::nameSeqIndMap() const {
return m_nameSeqIndMap;
return m_nameSeqIndMap;
}
const std::map<size_t, std::string >& GroupTree::seqIndNameMap() const {
return m_seqIndNameMap;
return m_seqIndNameMap;
}
void GroupTree::update( const std::string& name, const std::string& other_parent) {
@@ -53,7 +53,7 @@ void GroupTree::update( const std::string& name, const std::string& other_parent
throw std::invalid_argument( "Parent group must have a name." );
auto root = this->find( other_parent );
if( root == this->groups.end() || root->name != other_parent )
if( root == this->groups.end() || root->name != other_parent )
this->groups.insert( root, 1, group { other_parent, "FIELD" } );
auto node = this->find( name );
@@ -77,14 +77,14 @@ void GroupTree::updateSeqIndex( const std::string& name, const std::string& othe
size_t index = this->m_nameSeqIndMap.size();
auto name_itr = this->m_nameSeqIndMap.find(name);
if (name_itr == this->m_nameSeqIndMap.end()) {
this->m_nameSeqIndMap.insert(std::make_pair(name, index));
this->m_seqIndNameMap.insert(std::make_pair(index, name));
index +=1;
this->m_nameSeqIndMap.insert(std::make_pair(name, index));
this->m_seqIndNameMap.insert(std::make_pair(index, name));
index +=1;
}
auto parent_itr = this->m_nameSeqIndMap.find(other_parent);
if (parent_itr == this->m_nameSeqIndMap.end()) {
this->m_nameSeqIndMap.insert(std::make_pair(other_parent, index));
this->m_seqIndNameMap.insert(std::make_pair(index, other_parent));
this->m_nameSeqIndMap.insert(std::make_pair(other_parent, index));
this->m_seqIndNameMap.insert(std::make_pair(index, other_parent));
}
}
@@ -99,7 +99,7 @@ const std::string& GroupTree::parent( const std::string& name ) const {
auto node = std::find( this->groups.begin(), this->groups.end(), name );
if( node == this->groups.end() )
throw std::out_of_range( "No such parent '" + name + "'." );
throw std::out_of_range( "No such group: '" + name + "'." );
return node->parent;
}
@@ -109,11 +109,6 @@ std::vector< std::string > GroupTree::children( const std::string& other_parent
throw std::out_of_range( "Node '" + other_parent + "' does not exist." );
std::vector< std::string > kids;
/* for( const auto& node : this->groups ) {
if( node.parent != other_parent ) continue;
kids.push_back( node.name );
}
*/
for( auto it = this->groups.begin(); it != this->groups.end(); it++ ) {
if( (*it).parent != other_parent ) continue;
kids.push_back( (*it).name );
@@ -167,4 +162,20 @@ std::vector< GroupTree::group >::iterator GroupTree::find( const std::string& na
name );
}
std::vector<GroupTree::group>::const_iterator GroupTree::begin() const {
return this->groups.begin();
}
std::vector<GroupTree::group>::const_iterator GroupTree::end() const {
return this->groups.end();
}
std::ostream& operator<<(std::ostream& stream, const GroupTree& gt) {
for (const auto& group_pair : gt)
stream << group_pair.parent << " -> " << group_pair.name << std::endl;
stream << std::endl << std::endl;
return stream;
}
}

View File

@@ -35,13 +35,10 @@
namespace Opm {
std::string WellSegments::wellName() const {
const std::string& WellSegments::wellName() const {
return m_well_name;
}
int WellSegments::numberBranch() const {
return m_number_branch;
}
int WellSegments::size() const {
return m_segments.size();
@@ -59,9 +56,6 @@ namespace Opm {
return m_volume_top;
}
WellSegment::LengthDepthEnum WellSegments::lengthDepthType() const {
return m_length_depth_type;
}
WellSegment::CompPressureDropEnum WellSegments::compPressureDrop() const {
return m_comp_pressure_drop;
@@ -228,6 +222,16 @@ namespace Opm {
return m_segments[segment_index];
}
void WellSegments::process(bool first_time) {
if (this->m_length_depth_type == WellSegment::ABS)
this->processABS();
else if (this->m_length_depth_type == WellSegment::INC)
this->processINC(first_time);
else
throw std::logic_error("Invalid llength/depth/type in segment data structure");
}
void WellSegments::processABS() {
const double invalid_value = Segment::invalidValue(); // meaningless value to indicate unspecified/uncompleted values
@@ -397,7 +401,6 @@ namespace Opm {
bool WellSegments::operator==( const WellSegments& rhs ) const {
return this->m_well_name == rhs.m_well_name
&& this->m_number_branch == rhs.m_number_branch
&& this->m_depth_top == rhs.m_depth_top
&& this->m_length_top == rhs.m_length_top
&& this->m_volume_top == rhs.m_volume_top
@@ -417,4 +420,13 @@ namespace Opm {
bool WellSegments::operator!=( const WellSegments& rhs ) const {
return !( *this == rhs );
}
std::ostream& operator<<( std::ostream& stream, const WellSegments& well_segments) {
return stream
<< well_segments.wellName() << " { top: {" <<
" L: " << well_segments.lengthTopSegment() <<
" D: " << well_segments.depthTopSegment() <<
" V: " << well_segments.volumeTopSegment() << " }}";
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -21,16 +21,71 @@
#include <opm/parser/eclipse/EclipseState/Schedule/SummaryState.hpp>
namespace Opm{
void SummaryState::add(const ecl::smspec_node& node, double value) {
if (node.get_var_type() == ECL_SMSPEC_WELL_VAR)
this->add_well_var(node.get_wgname(),
node.get_keyword(),
value);
else
this->add(node.get_gen_key1(), value);
namespace {
bool is_total(const std::string& key) {
auto sep_pos = key.find(':');
if (sep_pos == 0)
return false;
if (sep_pos == std::string::npos) {
if (key.back() == 'T')
return true;
return (key.compare(key.size() - 2, 2, "TH") == 0);
} else
return is_total(key.substr(0,sep_pos));
}
void SummaryState::add(const std::string& key, double value) {
}
void SummaryState::update_elapsed(double delta) {
this->elapsed += delta;
}
double SummaryState::get_elapsed() const {
return this->elapsed;
}
void SummaryState::update(const std::string& key, double value) {
if (is_total(key))
this->values[key] += value;
else
this->values[key] = value;
}
void SummaryState::update(const ecl::smspec_node& node, double value) {
if (node.get_var_type() == ECL_SMSPEC_WELL_VAR)
this->update_well_var(node.get_wgname(),
node.get_keyword(),
value);
else {
const std::string& key = node.get_gen_key1();
if (node.is_total())
this->values[key] += value;
else
this->values[key] = value;
}
}
void SummaryState::update_well_var(const std::string& well, const std::string& var, double value) {
std::string key = var + ":" + well;
if (is_total(var)) {
this->values[key] += value;
this->well_values[var][well] += value;
} else {
this->values[key] = value;
this->well_values[var][well] = value;
}
this->m_wells.insert(well);
}
void SummaryState::set(const std::string& key, double value) {
this->values[key] = value;
}
@@ -48,12 +103,6 @@ namespace Opm{
return iter->second;
}
void SummaryState::add_well_var(const std::string& well, const std::string& var, double value) {
this->add(var + ":" + well, value);
this->well_values[var][well] = value;
this->m_wells.insert(well);
}
bool SummaryState::has_well_var(const std::string& well, const std::string& var) const {
const auto& var_iter = this->well_values.find(var);
if (var_iter == this->well_values.end())

View File

@@ -20,6 +20,8 @@
#include <algorithm>
#include <cassert>
#include <vector>
#include <sstream>
#include <iostream>
#include <opm/parser/eclipse/Deck/DeckItem.hpp>
#include <opm/parser/eclipse/Deck/DeckKeyword.hpp>
@@ -103,15 +105,15 @@ namespace Opm {
bool Connection::attachedToSegment() const {
return (segment_number > 0);
}
const std::size_t& Connection::getSeqIndex() const {
return m_seqIndex;
}
const bool& Connection::getDefaultSatTabId() const {
return m_defaultSatTabId;
}
const std::size_t& Connection::getCompSegSeqIndex() const {
return m_compSeg_seqIndex;
}
@@ -128,15 +130,15 @@ namespace Opm {
return m_segDistEnd;
}
void Connection::setCompSegSeqIndex(std::size_t index) {
m_compSeg_seqIndex = index;
}
void Connection::setDefaultSatTabId(bool id) {
m_defaultSatTabId = id;
}
void Connection::setSegDistStart(const double& distStart) {
m_segDistStart = distStart;
}
@@ -144,7 +146,7 @@ namespace Opm {
void Connection::setSegDistEnd(const double& distEnd) {
m_segDistEnd = distEnd;
}
double Connection::depth() const {
return this->center_depth;
}
@@ -207,8 +209,30 @@ namespace Opm {
return this->wPi;
}
std::string Connection::str() const {
std::stringstream ss;
ss << "ijk: " << this->ijk[0] << "," << this->ijk[1] << "," << this->ijk[2] << std::endl;
ss << "COMPLNUM " << this->m_complnum << std::endl;
ss << "CF " << this->m_CF << std::endl;
ss << "RW " << this->m_rw << std::endl;
ss << "R0 " << this->m_r0 << std::endl;
ss << "skinf " << this->m_skin_factor << std::endl;
ss << "wPi " << this->wPi << std::endl;
ss << "kh " << this->m_Kh << std::endl;
ss << "sat_tableId " << this->sat_tableId << std::endl;
ss << "open_state " << this->open_state << std::endl;
ss << "direction " << this->direction << std::endl;
ss << "segment_nr " << this->segment_number << std::endl;
ss << "center_depth " << this->center_depth << std::endl;
ss << "seqIndex " << this->m_seqIndex << std::endl;
return ss.str();
}
bool Connection::operator==( const Connection& rhs ) const {
return this->ijk == rhs.ijk
bool eq = this->ijk == rhs.ijk
&& this->m_complnum == rhs.m_complnum
&& this->m_CF == rhs.m_CF
&& this->m_rw == rhs.m_rw
@@ -222,6 +246,10 @@ namespace Opm {
&& this->segment_number == rhs.segment_number
&& this->center_depth == rhs.center_depth
&& this->m_seqIndex == rhs.m_seqIndex;
if (!eq) {
//std::cout << this->str() << rhs.str() << std::endl;
}
return eq;
}
bool Connection::operator!=( const Connection& rhs ) const {

View File

@@ -35,6 +35,7 @@ namespace Opm {
Well::Well(const std::string& name_, const size_t& seqIndex_, int headI,
int headJ, double refDepth , double drainageRadius, Phase preferredPhase,
WellProducer::ControlModeEnum whist_ctl,
const TimeMap& timeMap, size_t creationTimeStep,
WellCompletion::CompletionOrderEnum completionOrdering,
bool allowCrossFlow, bool automaticShutIn)
@@ -67,6 +68,9 @@ namespace Opm {
m_segmentset( timeMap, WellSegments{} ),
timesteps( timeMap.numTimesteps() )
{
WellProductionProperties p;
p.whistctl_cmode = whist_ctl;
this->m_productionProperties = DynamicState<WellProductionProperties>(timeMap, p);
}
const std::string& Well::name() const {
@@ -147,7 +151,7 @@ namespace Opm {
if (isInjector(timeStep))
switchToProducer( timeStep );
m_isProducer.update(timeStep , true);
this->setProducer(timeStep, true);
return m_productionProperties.update(timeStep, newProperties);
}
@@ -163,7 +167,7 @@ namespace Opm {
if (isProducer(timeStep))
switchToInjector( timeStep );
m_isProducer.update(timeStep , false);
this->setProducer(timeStep, false);
return m_injectionProperties.update(timeStep, newProperties);
}
@@ -176,8 +180,8 @@ namespace Opm {
}
bool Well::setPolymerProperties(size_t timeStep , const WellPolymerProperties& newProperties) {
m_isProducer.update(timeStep , false);
bool update = m_polymerProperties.update(timeStep, newProperties);
this->setProducer(timeStep, false);
return update;
}
@@ -240,6 +244,10 @@ namespace Opm {
return m_status.update( timeStep , status );
}
bool Well::setProducer(size_t timeStep, bool producer) {
return this->m_isProducer.update(timeStep, producer);
}
bool Well::isProducer(size_t timeStep) const {
return bool( m_isProducer.get(timeStep) );
}
@@ -459,14 +467,7 @@ namespace Opm {
// overwrite the BHP reference depth with the one from WELSEGS keyword
const double ref_depth = new_segmentset.depthTopSegment();
m_refDepth.update( time_step, ref_depth );
if (new_segmentset.lengthDepthType() == WellSegment::ABS) {
new_segmentset.processABS();
} else if (new_segmentset.lengthDepthType() == WellSegment::INC) {
new_segmentset.processINC(first_time);
} else {
throw std::logic_error(" unknown length_depth_type in the new_segmentset");
}
new_segmentset.process(first_time);
m_segmentset.update(time_step, new_segmentset);
}
@@ -480,7 +481,10 @@ namespace Opm {
const auto headJ = this->m_headJ[ time_step ];
new_set->orderConnections( headI, headJ );
}
//This breaks test at line 824 in ScheduleTests
/*if (new_set->allConnectionsShut())
this->setStatus(time_step, WellCommon::SHUT );
*/
m_completions.update( time_step, std::shared_ptr<WellConnections>( new_set ));
}

View File

@@ -0,0 +1,608 @@
/*
Copyright 2019 Equinor ASA.
This file is part of the Open Porous Media project (OPM).
OPM 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.
OPM 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 for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#include <opm/parser/eclipse/Deck/DeckRecord.hpp>
#include <opm/parser/eclipse/Deck/DeckKeyword.hpp>
#include <opm/parser/eclipse/Parser/ParserKeywords/W.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/MSW/updatingConnectionsWithSegments.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Well/Well2.hpp>
namespace Opm {
namespace {
bool defaulted(const DeckRecord& rec, const std::string& s) {
const auto& item = rec.getItem( s );
if (item.defaultApplied(0))
return true;
if (item.get<int>(0) == 0)
return true;
return false;
}
int limit(const DeckRecord& rec, const std::string&s , int shift) {
const auto& item = rec.getItem( s );
return shift + item.get<int>(0);
}
bool match_le(int value, const DeckRecord& rec, const std::string& s, int shift = 0) {
if (defaulted(rec,s))
return true;
return (value <= limit(rec,s,shift));
}
bool match_ge(int value, const DeckRecord& rec, const std::string& s, int shift = 0) {
if (defaulted(rec,s))
return true;
return (value >= limit(rec,s,shift));
}
bool match_eq(int value, const DeckRecord& rec, const std::string& s, int shift = 0) {
if (defaulted(rec,s))
return true;
return (limit(rec,s,shift) == value);
}
}
Well2::Well2(const std::string& wname,
const std::string& gname,
std::size_t init_step,
std::size_t insert_index,
int headI,
int headJ,
double ref_depth,
Phase phase,
WellProducer::ControlModeEnum whistctl_cmode,
WellCompletion::CompletionOrderEnum ordering):
wname(wname),
group_name(gname),
init_step(init_step),
insert_index(insert_index),
headI(headI),
headJ(headJ),
ref_depth(ref_depth),
phase(phase),
ordering(ordering),
status(WellCommon::SHUT),
drainage_radius(ParserKeywords::WELSPECS::D_RADIUS::defaultValue),
allow_cross_flow(DeckItem::to_bool(ParserKeywords::WELSPECS::CROSSFLOW::defaultValue)),
automatic_shutin( ParserKeywords::WELSPECS::CROSSFLOW::defaultValue == "SHUT"),
producer(true),
guide_rate({true, -1, GuideRate::UNDEFINED,ParserKeywords::WGRUPCON::SCALING_FACTOR::defaultValue}),
efficiency_factor(1.0),
solvent_fraction(0.0),
econ_limits(std::make_shared<WellEconProductionLimits>()),
polymer_properties(std::make_shared<WellPolymerProperties>()),
tracer_properties(std::make_shared<WellTracerProperties>()),
connections(std::make_shared<WellConnections>(headI, headJ)),
production(std::make_shared<WellProductionProperties>()),
injection(std::make_shared<WellInjectionProperties>())
{
WellProductionProperties * p = new WellProductionProperties(*this->production);
p->whistctl_cmode = whistctl_cmode;
this->production.reset( p );
}
bool Well2::updateEfficiencyFactor(double efficiency_factor) {
if (this->efficiency_factor != efficiency_factor) {
this->efficiency_factor = efficiency_factor;
return true;
}
return false;
}
bool Well2::updateWellGuideRate(double guide_rate) {
if (this->guide_rate.guide_rate != guide_rate) {
this->guide_rate.guide_rate = guide_rate;
return true;
}
return false;
}
bool Well2::updatePolymerProperties(std::shared_ptr<WellPolymerProperties> polymer_properties) {
if (*this->polymer_properties != *polymer_properties) {
this->polymer_properties = polymer_properties;
this->producer = false;
return true;
}
return false;
}
bool Well2::updateEconLimits(std::shared_ptr<WellEconProductionLimits> econ_limits) {
if (*this->econ_limits != *econ_limits) {
this->econ_limits = econ_limits;
return true;
}
return false;
}
void Well2::switchToProducer() {
auto p = std::make_shared<WellInjectionProperties>(this->getInjectionProperties());
p->BHPLimit = 0;
p->dropInjectionControl( Opm::WellInjector::BHP );
this->updateInjection( p );
this->updateProducer(true);
}
void Well2::switchToInjector() {
auto p = std::make_shared<WellProductionProperties>(getProductionProperties());
p->BHPLimit = 0;
p->dropProductionControl( Opm::WellProducer::BHP );
this->updateProduction( p );
this->updateProducer( false );
}
bool Well2::updateInjection(std::shared_ptr<WellInjectionProperties> injection) {
if (this->producer)
this->switchToInjector( );
if (*this->injection != *injection) {
this->injection = injection;
return true;
}
return false;
}
bool Well2::updateProduction(std::shared_ptr<WellProductionProperties> production) {
if (!this->producer)
this->switchToProducer( );
if (*this->production != *production) {
this->production = production;
return true;
}
return false;
}
bool Well2::updateTracer(std::shared_ptr<WellTracerProperties> tracer_properties) {
if (*this->tracer_properties != *tracer_properties) {
this->tracer_properties = tracer_properties;
return true;
}
return false;
}
bool Well2::updateWellGuideRate(bool available, double guide_rate, GuideRate::GuideRatePhaseEnum guide_phase, double scale_factor) {
bool update = false;
if (this->guide_rate.available != available) {
this->guide_rate.available = available;
update = true;
}
if(this->guide_rate.guide_rate != guide_rate) {
this->guide_rate.guide_rate = guide_rate;
update = true;
}
if(this->guide_rate.guide_phase != guide_phase) {
this->guide_rate.guide_phase = guide_phase;
update = true;
}
if(this->guide_rate.scale_factor != scale_factor) {
this->guide_rate.scale_factor = scale_factor;
update = true;
}
return update;
}
bool Well2::updateProducer(bool producer) {
if (this->producer != producer) {
this->producer = producer;
return true;
}
return false;
}
bool Well2::updateGroup(const std::string& group) {
if (this->group_name != group) {
this->group_name = group;
return true;
}
return false;
}
bool Well2::updateHead(int I, int J) {
bool update = false;
if (this->headI != I) {
this->headI = I;
update = true;
}
if (this->headJ != J) {
this->headJ = J;
update = true;
}
return update;
}
bool Well2::updateStatus(WellCommon::StatusEnum status) {
if (this->status != status) {
this->status = status;
return true;
}
return false;
}
bool Well2::updateRefDepth(double ref_depth) {
if (this->ref_depth != ref_depth) {
this->ref_depth = ref_depth;
return true;
}
return false;
}
bool Well2::updateDrainageRadius(double drainage_radius) {
if (this->drainage_radius != drainage_radius) {
this->drainage_radius = drainage_radius;
return true;
}
return false;
}
bool Well2::updateCrossFlow(bool allow_cross_flow) {
if (this->allow_cross_flow != allow_cross_flow) {
this->allow_cross_flow = allow_cross_flow;
return true;
}
return false;
}
bool Well2::updateAutoShutin(bool auto_shutin) {
if (this->automatic_shutin != auto_shutin) {
this->automatic_shutin = auto_shutin;
return true;
}
return false;
}
bool Well2::updateConnections(const std::shared_ptr<WellConnections> connections) {
if( this->ordering == WellCompletion::TRACK)
connections->orderConnections( this->headI, this->headJ );
if (*this->connections != *connections) {
this->connections = connections;
//if (this->connections->allConnectionsShut()) {}
// This status update breaks line 825 in ScheduleTests
//this->status = WellCommon::StatusEnum::SHUT;
return true;
}
return false;
}
bool Well2::updateSolventFraction(double solvent_fraction) {
if (this->solvent_fraction != solvent_fraction) {
this->solvent_fraction = solvent_fraction;
return true;
}
return false;
}
bool Well2::handleCOMPSEGS(const DeckKeyword& keyword, const EclipseGrid& grid) {
std::shared_ptr<WellConnections> new_connection_set( newConnectionsWithSegments(keyword, *this->connections, *this->segments , grid) );
return this->updateConnections(new_connection_set);
}
const std::string& Well2::groupName() const {
return this->group_name;
}
bool Well2::isMultiSegment() const {
if (this->segments)
return true;
return false;
}
bool Well2::isProducer() const {
return this->producer;
}
bool Well2::isAvailableForGroupControl() const {
return this->guide_rate.available;
}
double Well2::getGuideRate() const {
return this->guide_rate.guide_rate;
};
GuideRate::GuideRatePhaseEnum Well2::getGuideRatePhase() const {
return this->guide_rate.guide_phase;
};
double Well2::getGuideRateScalingFactor() const {
return this->guide_rate.scale_factor;
};
double Well2::getEfficiencyFactor() const {
return this->efficiency_factor;
}
double Well2::getSolventFraction() const {
return this->solvent_fraction;
}
std::size_t Well2::seqIndex() const {
return this->insert_index;
}
int Well2::getHeadI() const {
return this->headI;
}
int Well2::getHeadJ() const {
return this->headJ;
}
bool Well2::getAutomaticShutIn() const {
return this->automatic_shutin;
}
bool Well2::getAllowCrossFlow() const {
return this->allow_cross_flow;
}
double Well2::getRefDepth() const {
if( this->ref_depth >= 0.0 )
return this->ref_depth;
// ref depth was defaulted and we get the depth of the first completion
if( this->connections->size() == 0 ) {
throw std::invalid_argument( "No completions defined for well: "
+ name()
+ ". Can not infer reference depth" );
}
return this->connections->get(0).depth();
}
double Well2::getDrainageRadius() const {
return this->drainage_radius;
}
const std::string& Well2::name() const {
return this->wname;
}
const WellConnections& Well2::getConnections() const {
return *this->connections;
}
const WellPolymerProperties& Well2::getPolymerProperties() const {
return *this->polymer_properties;
}
const WellTracerProperties& Well2::getTracerProperties() const {
return *this->tracer_properties;
}
const WellEconProductionLimits& Well2::getEconLimits() const {
return *this->econ_limits;
}
const WellProductionProperties& Well2::getProductionProperties() const {
return *this->production;
}
const WellSegments& Well2::getSegments() const {
if (this->segments)
return *this->segments;
else
throw std::logic_error("Asked for segment information in not MSW well: " + this->name());
}
const WellInjectionProperties& Well2::getInjectionProperties() const {
return *this->injection;
}
WellCommon::StatusEnum Well2::getStatus() const {
return this->status;
}
std::map<int, std::vector<Connection>> Well2::getCompletions() const {
std::map<int, std::vector<Connection>> completions;
for (const auto& conn : *this->connections) {
auto pair = completions.find( conn.complnum() );
if (pair == completions.end())
completions[conn.complnum()] = {};
pair = completions.find(conn.complnum());
pair->second.push_back(conn);
}
return completions;
}
Phase Well2::getPreferredPhase() const {
return this->phase;
}
bool Well2::handleWELOPEN(const DeckRecord& record, WellCompletion::StateEnum status) {
auto match = [=]( const Connection &c) -> bool {
if (!match_eq(c.getI(), record, "I" , -1)) return false;
if (!match_eq(c.getJ(), record, "J" , -1)) return false;
if (!match_eq(c.getK(), record, "K", -1)) return false;
if (!match_ge(c.complnum(), record, "C1")) return false;
if (!match_le(c.complnum(), record, "C2")) return false;
return true;
};
auto new_connections = std::make_shared<WellConnections>(this->headI, this->headJ);
for (auto c : *this->connections) {
if (match(c))
c.setState( status );
new_connections->add(c);
}
return this->updateConnections(new_connections);
}
bool Well2::handleCOMPLUMP(const DeckRecord& record) {
auto match = [=]( const Connection &c) -> bool {
if (!match_eq(c.getI(), record, "I" , -1)) return false;
if (!match_eq(c.getJ(), record, "J" , -1)) return false;
if (!match_ge(c.getK(), record, "K1", -1)) return false;
if (!match_le(c.getK(), record, "K2", -1)) return false;
return true;
};
auto new_connections = std::make_shared<WellConnections>(this->headI, this->headJ);
const int complnum = record.getItem("N").get<int>(0);
if (complnum <= 0)
throw std::invalid_argument("Completion number must be >= 1. COMPLNUM=" + std::to_string(complnum) + "is invalid");
for (auto c : *this->connections) {
if (match(c))
c.setComplnum( complnum );
new_connections->add(c);
}
return this->updateConnections(new_connections);
}
bool Well2::handleWPIMULT(const DeckRecord& record) {
auto match = [=]( const Connection &c) -> bool {
if (!match_ge(c.complnum(), record, "FIRST")) return false;
if (!match_le(c.complnum(), record, "LAST")) return false;
if (!match_eq(c.getI() , record, "I", -1)) return false;
if (!match_eq(c.getJ() , record, "J", -1)) return false;
if (!match_eq(c.getK() , record, "K", -1)) return false;
return true;
};
auto new_connections = std::make_shared<WellConnections>(this->headI, this->headJ);
double wellPi = record.getItem("WELLPI").get< double >(0);
for (auto c : *this->connections) {
if (match(c))
c.scaleWellPi( wellPi );
new_connections->add(c);
}
return this->updateConnections(new_connections);
}
bool Well2::handleWELSEGS(const DeckKeyword& keyword) {
if( this->segments )
throw std::logic_error("re-entering WELSEGS for a well is not supported yet!!.");
auto new_segmentset = std::make_shared<WellSegments>();
new_segmentset->loadWELSEGS(keyword);
new_segmentset->process(true);
if (new_segmentset != this->segments) {
this->segments = new_segmentset;
this->ref_depth = new_segmentset->depthTopSegment();
return true;
} else
return false;
}
void Well2::filterConnections(const EclipseGrid& grid) {
this->connections->filter(grid);
}
std::size_t Well2::firstTimeStep() const {
return this->init_step;
}
bool Well2::canOpen() const {
if (this->allow_cross_flow)
return true;
if (this->producer) {
const auto& prod = *this->production;
return (prod.WaterRate + prod.OilRate + prod.GasRate) != 0;
} else {
const auto& inj = *this->injection;
return inj.surfaceInjectionRate != 0;
}
}
WellCompletion::CompletionOrderEnum Well2::getWellConnectionOrdering() const {
return this->ordering;
}
}

View File

@@ -144,10 +144,10 @@ namespace {
double skin_factor,
const int satTableId,
const WellCompletion::DirectionEnum direction,
const std::size_t seqIndex,
const double segDistStart,
const double segDistEnd,
const bool defaultSatTabId)
const std::size_t seqIndex,
const double segDistStart,
const double segDistEnd,
const bool defaultSatTabId)
{
int conn_i = (i < 0) ? this->headI : i;
int conn_j = (j < 0) ? this->headJ : j;
@@ -167,10 +167,10 @@ namespace {
double skin_factor,
const int satTableId,
const WellCompletion::DirectionEnum direction,
const std::size_t seqIndex,
const double segDistStart,
const double segDistEnd,
const bool defaultSatTabId)
const std::size_t seqIndex,
const double segDistStart,
const double segDistEnd,
const bool defaultSatTabId)
{
int complnum = (this->m_connections.size() + 1);
this->addConnection(i,
@@ -186,10 +186,10 @@ namespace {
skin_factor,
satTableId,
direction,
seqIndex,
segDistStart,
segDistEnd,
defaultSatTabId);
seqIndex,
segDistStart,
segDistEnd,
defaultSatTabId);
}
void WellConnections::loadCOMPDAT(const DeckRecord& record, const EclipseGrid& grid, const Eclipse3DProperties& eclipseProperties) {
@@ -296,48 +296,47 @@ namespace {
same_ijk );
// Only add connection for active grid cells
if (grid.cellActive(I, J, k)) {
if (prev == this->m_connections.end()) {
std::size_t noConn = this->m_connections.size();
this->addConnection(I,J,k,
grid.getCellDepth( I,J,k ),
state,
CF,
Kh,
rw,
r0,
skin_factor,
satTableId,
direction,
noConn, 0., 0., defaultSatTable);
}
else {
std::size_t noConn = prev->getSeqIndex();
// The complnum value carries over; the rest of the state is fully specified by
// the current COMPDAT keyword.
int complnum = prev->complnum();
std::size_t css_ind = prev->getCompSegSeqIndex();
int conSegNo = prev->segment();
std::size_t con_SIndex = prev->getSeqIndex();
double conCDepth = prev->depth();
double conSDStart = prev->getSegDistStart();
double conSDEnd = prev->getSegDistEnd();
*prev = Connection(I,J,k,
complnum,
grid.getCellDepth(I,J,k),
state,
CF,
Kh,
rw,
r0,
skin_factor,
satTableId,
direction,
noConn, conSDStart, conSDEnd, defaultSatTable);
prev->setCompSegSeqIndex(css_ind);
prev->updateSegment(conSegNo, conCDepth, con_SIndex);
}
}
}
if (prev == this->m_connections.end()) {
std::size_t noConn = this->m_connections.size();
this->addConnection(I,J,k,
grid.getCellDepth( I,J,k ),
state,
CF,
Kh,
rw,
r0,
skin_factor,
satTableId,
direction,
noConn, 0., 0., defaultSatTable);
} else {
std::size_t noConn = prev->getSeqIndex();
// The complnum value carries over; the rest of the state is fully specified by
// the current COMPDAT keyword.
int complnum = prev->complnum();
std::size_t css_ind = prev->getCompSegSeqIndex();
int conSegNo = prev->segment();
std::size_t con_SIndex = prev->getSeqIndex();
double conCDepth = prev->depth();
double conSDStart = prev->getSegDistStart();
double conSDEnd = prev->getSegDistEnd();
*prev = Connection(I,J,k,
complnum,
grid.getCellDepth(I,J,k),
state,
CF,
Kh,
rw,
r0,
skin_factor,
satTableId,
direction,
noConn, conSDStart, conSDEnd, defaultSatTable);
prev->setCompSegSeqIndex(css_ind);
prev->updateSegment(conSegNo, conCDepth, con_SIndex);
}
}
}
}
@@ -458,8 +457,9 @@ namespace {
}
bool WellConnections::operator==( const WellConnections& rhs ) const {
return this->size() == rhs.size()
&& std::equal( this->begin(), this->end(), rhs.begin() );
return this->size() == rhs.size() &&
this->num_removed == rhs.num_removed &&
std::equal( this->begin(), this->end(), rhs.begin() );
}
bool WellConnections::operator!=( const WellConnections& rhs ) const {

View File

@@ -17,6 +17,7 @@
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#include <opm/parser/eclipse/EclipseState/Schedule/Well/WellPolymerProperties.hpp>
#include <opm/parser/eclipse/Deck/DeckRecord.hpp>
#include <string>
#include <vector>
@@ -43,6 +44,30 @@ namespace Opm {
}
void WellPolymerProperties::handleWPOLYMER(const DeckRecord& record) {
this->m_polymerConcentration = record.getItem("POLYMER_CONCENTRATION").getSIDouble(0);
this->m_saltConcentration = record.getItem("SALT_CONCENTRATION").getSIDouble(0);
const auto& group_polymer_item = record.getItem("GROUP_POLYMER_CONCENTRATION");
const auto& group_salt_item = record.getItem("GROUP_SALT_CONCENTRATION");
if (!group_polymer_item.defaultApplied(0))
throw std::logic_error("Sorry explicit setting of \'GROUP_POLYMER_CONCENTRATION\' is not supported!");
if (!group_salt_item.defaultApplied(0))
throw std::logic_error("Sorry explicit setting of \'GROUP_SALT_CONCENTRATION\' is not supported!");
}
void WellPolymerProperties::handleWPMITAB(const DeckRecord& record) {
this->m_plymwinjtable = record.getItem("TABLE_NUMBER").get<int>(0);
}
void WellPolymerProperties::handleWSKPTAB(const DeckRecord& record) {
this->m_skprwattable = record.getItem("TABLE_NUMBER_WATER").get<int>(0);
this->m_skprpolytable = record.getItem("TABLE_NUMBER_POLYMER").get<int>(0);
}
bool WellPolymerProperties::operator!=(const WellPolymerProperties& other) const {
return !(*this == other);
}

View File

@@ -67,6 +67,9 @@ namespace Opm {
else
cmode = wp::ControlModeFromString( cmodeItem.getTrimmedString( 0 ) );
// clearing the existing targets/limits
clearControls();
if (effectiveHistoryProductionControl(cmode)) {
this->addProductionControl( cmode );
this->controlMode = cmode;
@@ -115,6 +118,7 @@ namespace Opm {
this->init_rates(record);
for( const auto& cmode : modes ) {
if( !record.getItem( cmode.first ).defaultApplied( 0 ) ) {
@@ -198,6 +202,7 @@ namespace Opm {
}
bool WellProductionProperties::operator==(const WellProductionProperties& other) const {
return OilRate == other.OilRate
&& WaterRate == other.WaterRate
@@ -235,6 +240,7 @@ namespace Opm {
<< "THPH: " << wp.THPH << ", "
<< "VFP table: " << wp.VFPTableNumber << ", "
<< "ALQ: " << wp.ALQValue << ", "
<< "WHISTCTL: " << wp.whistctl_cmode << ", "
<< "prediction: " << wp.predictionMode << " }";
}
@@ -249,6 +255,10 @@ namespace Opm {
BHPLimit = 1. * unit::atm;
}
void WellProductionProperties::clearControls() {
m_productionControls = 0;
}
void WellProductionProperties::setBHPLimit(const double limit) {
BHPLimit = limit;
}

View File

@@ -168,8 +168,8 @@ inline void keywordW( SummaryConfig::keyword_list& list,
list.push_back( SummaryConfig::keyword_type( keyword.name(), well_name ));
}
} else
for (const auto* well : schedule.getWells())
list.push_back( SummaryConfig::keyword_type( keyword.name(), well->name()));
for (const auto& wname : schedule.wellNames())
list.push_back( SummaryConfig::keyword_type( keyword.name(), wname));
}
@@ -388,7 +388,7 @@ inline void keywordMISC( SummaryConfig::keyword_list& list,
void makeSegmentNodes(const std::size_t last_timestep,
const int segID,
const DeckKeyword& keyword,
const std::vector<const Well*>& wells,
const Well* well,
SummaryConfig::keyword_list& list)
{
// Modifies 'list' in place.
@@ -398,30 +398,27 @@ inline void keywordMISC( SummaryConfig::keyword_list& list,
list.push_back(SummaryConfig::keyword_type( keyword.name(), well, segNumber ));
};
for (const auto* well : wells) {
if (! isMultiSegmentWell(last_timestep, well)) {
// Not an MSW. Don't create summary vectors for segments.
continue;
}
if (! isMultiSegmentWell(last_timestep, well))
// Not an MSW. Don't create summary vectors for segments.
return;
const auto& wname = well->name();
if (segID < 1) {
// Segment number defaulted. Allocate a summary
// vector for each segment.
const auto nSeg = maxNumWellSegments(last_timestep, well);
const auto& wname = well->name();
if (segID < 1) {
// Segment number defaulted. Allocate a summary
// vector for each segment.
const auto nSeg = maxNumWellSegments(last_timestep, well);
for (auto segNumber = 0*nSeg;
segNumber < nSeg; ++segNumber)
for (auto segNumber = 0*nSeg;
segNumber < nSeg; ++segNumber)
{
// One-based segment number.
makeNode(wname, segNumber + 1);
}
}
else {
// Segment number specified. Allocate single
// summary vector for that segment number.
makeNode(wname, segID);
}
}
else {
// Segment number specified. Allocate single
// summary vector for that segment number.
makeNode(wname, segID);
}
}
@@ -440,8 +437,9 @@ inline void keywordMISC( SummaryConfig::keyword_list& list,
const auto segID = -1;
makeSegmentNodes(last_timestep, segID, keyword,
schedule.getWells(), list);
for (const auto& well : schedule.getWells())
makeSegmentNodes(last_timestep, segID, keyword,
well, list);
}
void keywordSWithRecords(const std::size_t last_timestep,
@@ -481,11 +479,9 @@ inline void keywordMISC( SummaryConfig::keyword_list& list,
// segment number in record implies all segments.
const auto segID = record.getItem(1).defaultApplied(0)
? -1 : record.getItem(1).get<int>(0);
std::vector<const Well*> wells;
for (const auto& well_name : well_names)
wells.push_back( schedule.getWell(well_name) );
makeSegmentNodes(last_timestep, segID, keyword, wells, list);
for (const auto& well_name : well_names)
makeSegmentNodes(last_timestep, segID, keyword, schedule.getWell(well_name), list);
}
}

View File

@@ -49,6 +49,7 @@ const std::string testHeader =
"auto unitSystem = UnitSystem::newMETRIC();\n";
const std::string sourceHeader =
"#include <opm/parser/eclipse/Deck/UDAValue.hpp>\n"
"#include <opm/parser/eclipse/Parser/ParserKeyword.hpp>\n"
"#include <opm/parser/eclipse/Parser/ParserItem.hpp>\n"
"#include <opm/parser/eclipse/Parser/ParserRecord.hpp>\n"
@@ -124,7 +125,7 @@ namespace Opm {
newSource << "}" << std::endl;
newSource << "void Parser::addDefaultKeywords() {" << std::endl
<< "Opm::ParserKeywords::addDefaultKeywords(*this);" << std::endl
<< " Opm::ParserKeywords::addDefaultKeywords(*this);" << std::endl
<< "}}" << std::endl;
return write_file( newSource, sourceFile, m_verbose, "source" );

View File

@@ -109,6 +109,7 @@ namespace Opm {
addKey(SIMULATOR_KEYWORD_ITEM_NOT_SUPPORTED, InputError::WARN);
addKey(UDQ_PARSE_ERROR, InputError::THROW_EXCEPTION);
this->addKey(SCHEDULE_WELL_ERROR, InputError::THROW_EXCEPTION);
}
void ParseContext::initEnv() {
@@ -342,4 +343,5 @@ namespace Opm {
const std::string ParseContext::SIMULATOR_KEYWORD_ITEM_NOT_SUPPORTED = "SIMULATOR_KEYWORD_ITEM_NOT_SUPPORTED";
const std::string ParseContext::UDQ_PARSE_ERROR = "UDQ_PARSE_ERROR";
const std::string ParseContext::SCHEDULE_WELL_ERROR = "SCHEDULE_WELL_ERROR";
}

View File

@@ -94,5 +94,4 @@ namespace Opm {
}
}

View File

@@ -30,7 +30,7 @@
#include <opm/parser/eclipse/Parser/ParserEnums.hpp>
#include <opm/parser/eclipse/RawDeck/RawRecord.hpp>
#include <opm/parser/eclipse/RawDeck/StarToken.hpp>
#include <opm/parser/eclipse/Deck/UDAValue.hpp>
namespace Opm {
@@ -47,6 +47,11 @@ template<> const double& default_value< double >() {
return value;
}
template<> const UDAValue& default_value< UDAValue >() {
static const UDAValue value = UDAValue(std::numeric_limits<double>::quiet_NaN());
return value;
}
template<> const std::string& default_value< std::string >() {
static const std::string value = "";
return value;
@@ -57,7 +62,7 @@ type_tag get_data_type_json( const std::string& str ) {
if( str == "DOUBLE" ) return type_tag::fdouble;
if( str == "STRING" ) return type_tag::string;
if( str == "RAW_STRING") return type_tag::string;
if( str == "UDA") return type_tag::fdouble;
if( str == "UDA") return type_tag::uda;
throw std::invalid_argument( str + " cannot be converted to enum 'tag'" );
}
@@ -88,7 +93,14 @@ std::string ParserItem::string_from_size( ParserItem::item_size sz ) {
case item_size::SINGLE: return "SINGLE";
}
throw std::logic_error( "Fatal error; should not be reachable" );
throw std::logic_error( "ParserItem::string_from_size: Fatal error; should not be reachable" );
}
template<> const UDAValue& ParserItem::value_ref< UDAValue >() const {
if( this->data_type != get_type< UDAValue >() )
throw std::invalid_argument("ValueRef<UDAValue> Wrong type." );
return this->uval;
}
template<> const int& ParserItem::value_ref< int >() const {
@@ -164,17 +176,17 @@ ParserItem::ParserItem( const Json::JsonObject& json ) :
this->setDefault( json.get_double( "default" ) );
break;
case itype::UDA:
this->setDefault( UDAValue(json.get_double( "default" )) );
break;
case itype::STRING:
case itype::RAW_STRING:
this->setDefault( json.get_string( "default" ) );
break;
case itype::UDA:
this->setDefault( json.get_double( "default" ) );
break;
default:
throw std::logic_error( "Item of unknown type." );
throw std::logic_error( "Item of unknown type: <" + json.get_string("value_type") + ">" );
}
}
@@ -205,7 +217,7 @@ void ParserItem::setInputType(ParserItem::itype input_type) {
this->setDataType( std::string() );
else if (input_type == itype::UDA)
this->setDataType( double() );
this->setDataType( UDAValue(0) );
else
throw std::invalid_argument("BUG: input type not recognized in setInputType()");
@@ -238,22 +250,17 @@ const T& ParserItem::getDefault() const {
}
bool ParserItem::hasDimension() const {
if( this->data_type != type_tag::fdouble )
return false;
return !this->dimensions.empty();
}
size_t ParserItem::numDimensions() const {
if( this->data_type != type_tag::fdouble ) return 0;
return this->dimensions.size();
}
const std::string& ParserItem::getDimension( size_t index ) const {
if( this->data_type != type_tag::fdouble )
throw std::invalid_argument("Item is not double.");
return this->dimensions.at( index );
if( this->data_type == type_tag::fdouble || this->data_type == type_tag::uda)
return this->dimensions.at( index );
throw std::invalid_argument("Item is not double / UDA .");
}
void ParserItem::push_backDimension( const std::string& dim ) {
@@ -278,6 +285,10 @@ void ParserItem::push_backDimension( const std::string& dim ) {
}
type_tag ParserItem::dataType() const {
return this->data_type;
}
ParserItem::item_size ParserItem::sizeType() const {
return m_sizeType;
@@ -293,6 +304,18 @@ void ParserItem::push_backDimension( const std::string& dim ) {
void ParserItem::setSizeType(item_size size_type) {
/*
The restriction that data type UDA can only be combined with size_type
SINGLE is due to the way units are bolted on to the Deck
datastructures after the parsing has completed. UDA values are
currently only used as scalars in well/group control and the
restriction does not have any effect. If at some stage in the future
this should change the way units are applied to the deck must be
refactored.
*/
if (this->data_type == type_tag::uda && size_type != item_size::SINGLE)
throw std::invalid_argument("Sorry - the UDA datatype can only be used with size type SINGLE");
this->m_sizeType = size_type;
}
@@ -329,8 +352,12 @@ bool ParserItem::operator==( const ParserItem& rhs ) const {
if( this->sval != rhs.sval ) return false;
break;
case type_tag::uda:
if( this->uval != rhs.uval ) return false;
break;
default:
throw std::logic_error( "Item of unknown type." );
throw std::logic_error( "Item of unknown type data_type:" + tag_name(this->data_type));
}
}
if( this->data_type != type_tag::fdouble ) return true;
@@ -415,6 +442,13 @@ std::string ParserItem::createCode(const std::string& indent) const {
stream << "double(" << as_string(this->getDefault<double>()) << ")";
break;
case type_tag::uda:
{
double double_value =this->getDefault<UDAValue>().get<double>();
stream << "UDAValue(" << as_string(double_value) << ")";
}
break;
case type_tag::string:
stream << "std::string(\"" << this->getDefault< std::string >() << "\")";
break;
@@ -425,6 +459,9 @@ std::string ParserItem::createCode(const std::string& indent) const {
stream << " );" << '\n';
}
for (size_t idim=0; idim < this->numDimensions(); idim++)
stream << indent <<"item.push_backDimension(\"" << this->getDimension( idim ) << "\");" << '\n';
if (this->m_description.size() > 0)
stream << indent << "item.setDescription(\"" << this->m_description << "\");" << '\n';
@@ -540,8 +577,10 @@ DeckItem ParserItem::scan( RawRecord& record ) const {
return scan_item< double >( *this, record );
case type_tag::string:
return scan_item< std::string >( *this, record );
case type_tag::uda:
return scan_item<UDAValue>(*this, record);
default:
throw std::logic_error( "Fatal error; should not be reachable" );
throw std::logic_error( "ParserItem::scan: Fatal error; should not be reachable" );
}
}
@@ -580,11 +619,16 @@ std::string ParserItem::inlineClassInit(const std::string& parentClass,
return std::to_string( this->getDefault< int >() );
case type_tag::fdouble:
return std::to_string( this->getDefault< double >() );
case type_tag::uda:
{
double value = this->getDefault<UDAValue>().get<double>();
return "UDAValue(" + std::to_string(value) + ")";
}
case type_tag::string:
return "\"" + this->getDefault< std::string >() + "\"";
default:
throw std::logic_error( "Fatal error; should not be reachable" );
throw std::logic_error( "ParserItem::inlineClassInit: Fatal error; should not be reachable" );
}
};
@@ -649,13 +693,16 @@ bool ParserItem::parseRaw( ) const {
template void ParserItem::setDefault( int );
template void ParserItem::setDefault( double );
template void ParserItem::setDefault( std::string );
template void ParserItem::setDefault( UDAValue );
template void ParserItem::setDataType( int );
template void ParserItem::setDataType( double );
template void ParserItem::setDataType( std::string );
template void ParserItem::setDataType( UDAValue );
template const int& ParserItem::getDefault() const;
template const double& ParserItem::getDefault() const;
template const std::string& ParserItem::getDefault() const;
template const UDAValue& ParserItem::getDefault() const;
}

View File

@@ -648,8 +648,6 @@ void set_dimensions( ParserItem& item,
{
std::string indent3 = local_indent + " ";
ss << item.createCode(indent3);
for (size_t idim=0; idim < item.numDimensions(); idim++)
ss << indent3 <<"item.push_backDimension(\"" << item.getDimension( idim ) << "\");" << '\n';
{
std::string addItemMethod = "addItem";
if (isDataKeyword())
@@ -701,7 +699,7 @@ void set_dimensions( ParserItem& item,
|| m_keywordSizeType != rhs.m_keywordSizeType
|| isDataKeyword() != rhs.isDataKeyword()
|| m_isTableCollection != rhs.m_isTableCollection )
return false;
return false;
switch( m_keywordSizeType ) {
case FIXED:

View File

@@ -95,16 +95,30 @@ namespace {
void ParserRecord::applyUnitsToDeck( Deck& deck, DeckRecord& deckRecord ) const {
for( const auto& item : *this ) {
if( !item.hasDimension() ) continue;
for( const auto& parser_item : *this ) {
if( !parser_item.hasDimension() ) continue;
auto& deckItem = deckRecord.getItem( item.name() );
for (size_t idim = 0; idim < item.numDimensions(); idim++) {
auto activeDimension = deck.getActiveUnitSystem().getNewDimension( item.getDimension(idim) );
auto defaultDimension = deck.getDefaultUnitSystem().getNewDimension( item.getDimension(idim) );
auto& deckItem = deckRecord.getItem( parser_item.name() );
for (size_t idim = 0; idim < parser_item.numDimensions(); idim++) {
auto activeDimension = deck.getActiveUnitSystem().getNewDimension( parser_item.getDimension(idim) );
auto defaultDimension = deck.getDefaultUnitSystem().getNewDimension( parser_item.getDimension(idim) );
deckItem.push_backDimension( activeDimension , defaultDimension );
}
/*
A little special casing ... the UDAValue elements in the deck must
carry their own dimension. Observe that the
ParserItem::setSizeType() method guarantees that UDA data is
scalar, i.e. this special case loop can be simpler than the
general code in the block above.
*/
if (parser_item.dataType() == type_tag::uda && deckItem.size() > 0) {
auto uda = deckItem.get<UDAValue>(0);
if (deckItem.defaultApplied(0))
uda.set_dim( deck.getDefaultUnitSystem().getNewDimension( parser_item.getDimension(0)));
else
uda.set_dim( deck.getActiveUnitSystem().getNewDimension( parser_item.getDimension(0)));
}
}
}

View File

@@ -28,6 +28,7 @@
#include <opm/parser/eclipse/RawDeck/StarToken.hpp>
#include <opm/parser/eclipse/Utility/Stringview.hpp>
#include <opm/parser/eclipse/Deck/UDAValue.hpp>
namespace qi = boost::spirit::qi;
@@ -105,6 +106,7 @@ namespace Opm {
throw std::invalid_argument( "Malformed floating point number '" + view + "'" );
}
template <>
std::string readValueToken< std::string >( string_view view ) {
if( view.size() == 0 || view[ 0 ] != '\'' )
@@ -116,6 +118,17 @@ namespace Opm {
return view.substr( 1, view.size() - 1 );
}
template<>
UDAValue readValueToken< UDAValue >( string_view view ) {
double n = 0;
qi::real_parser< double, fortran_double< double > > double_;
auto cursor = view.begin();
const auto ok = qi::parse( cursor, view.end(), double_, n );
if( ok && cursor == view.end() ) return UDAValue(n);
return UDAValue( readValueToken<std::string>(view) );
}
void StarToken::init_( const string_view& token ) {
// special-case the interpretation of a lone star as "1*" but do not
// allow constructs like "*123"...

View File

@@ -25,6 +25,14 @@
namespace Opm {
Dimension::Dimension()
{
this->m_name = "Unit";
this->m_SIfactor = 1.0;
this->m_SIoffset = 0.0;
}
Dimension::Dimension(const std::string& name, double SIfactor, double SIoffset)
{
for (auto iter = name.begin(); iter != name.end(); ++iter) {

1737
tests/ECLFILE.FINIT Normal file

File diff suppressed because it is too large Load Diff

BIN
tests/ECLFILE.INIT Normal file

Binary file not shown.

BIN
tests/SPE1CASE1.EGRID Normal file

Binary file not shown.

BIN
tests/SPE1CASE1.RFT Normal file

Binary file not shown.

BIN
tests/SPE1CASE1.UNSMRY Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

8577
tests/SPE1_TESTCASE.FUNRST Normal file

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More