diff --git a/CMakeLists.txt b/CMakeLists.txt index ef724edce9..ed58b88564 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -116,7 +116,7 @@ endif() # Treat warnings as errors if not on Windows if (NOT ERT_WINDOWS) set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -Wall -Wno-unknown-pragmas ") - set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall " ) + set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror -Wno-unknown-pragmas -Wno-unused-result -Wno-unused-parameter" ) endif() if (MSVC) diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index beb33c9e57..097d903428 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -107,6 +107,7 @@ add_library(ecl util/rng.cpp ecl/ecl_grav_calc.cpp ecl/ecl_smspec.cpp ecl/ecl_sum_data.cpp + ecl/ecl_sum_file_data.cpp ecl/ecl_util.cpp ecl/ecl_kw.cpp ecl/ecl_sum.cpp @@ -178,6 +179,7 @@ target_include_directories(ecl util include e3 + ${CMAKE_CURRENT_SOURCE_DIR}/private-include ${CMAKE_CURRENT_BINARY_DIR}/include ) @@ -194,8 +196,8 @@ target_compile_options(ecl PUBLIC ${pthreadarg}) if (ERT_USE_OPENMP) - target_compile_options(ecl PUBLIC ${OpenMP_C_FLAGS}) - set_property(TARGET ecl APPEND PROPERTY LINK_FLAGS ${OpenMP_C_FLAGS}) + target_compile_options(ecl PUBLIC ${OpenMP_CXX_FLAGS}) + set_property(TARGET ecl APPEND PROPERTY LINK_FLAGS ${OpenMP_CXX_FLAGS}) target_link_libraries( ecl PUBLIC ${OpenMP_EXE_LINKER_FLAGS}) endif () @@ -385,6 +387,10 @@ foreach (name ecl_alloc_cpgrid add_test(NAME ${name} COMMAND ${name}) endforeach () +add_executable(ecl_sum_data_intermediate ecl/tests/ecl_sum_data_intermediate_test.cpp) +target_link_libraries(ecl_sum_data_intermediate ecl) +add_test(NAME ecl_sum_data_intermediate COMMAND ecl_sum_data_intermediate) + add_executable(ecl_grid_cell_contains ecl/tests/ecl_grid_cell_contains.cpp) target_link_libraries(ecl_grid_cell_contains ecl) add_test(NAME ecl_grid_cell_contains1 COMMAND ecl_grid_cell_contains) diff --git a/lib/ecl/ecl_smspec.cpp b/lib/ecl/ecl_smspec.cpp index 9008cb0033..6881cbc453 100644 --- a/lib/ecl/ecl_smspec.cpp +++ b/lib/ecl/ecl_smspec.cpp @@ -117,7 +117,6 @@ struct ecl_smspec_struct { vector_type * smspec_nodes; bool write_mode; bool need_nums; - bool locked; int_vector_type * index_map; /*-----------------------------------------------------------------*/ @@ -275,7 +274,6 @@ ecl_smspec_type * ecl_smspec_alloc_empty(bool write_mode , const char * key_join ecl_smspec->day_index = -1; ecl_smspec->year_index = -1; ecl_smspec->month_index = -1; - ecl_smspec->locked = false; ecl_smspec->time_seconds = -1; /* @@ -335,21 +333,6 @@ int ecl_smspec_num_nodes( const ecl_smspec_type * smspec) { } -/* - In the current implementation it is impossible to mix calls to - ecl_sum_add_var() and ecl_sum_add_tstep() - i.e. one must first add - *all* the variables with ecl_sum_add_var() calls, and then - subsequently add timesteps with ecl_sum_add_tstep(). - - The locked property of the smspec structure is to ensure that no new - variables are added to the ecl_smspec structure after the first - timestep has been added. -*/ - -void ecl_smspec_lock( ecl_smspec_type * smspec ) { - smspec->locked = true; -} - /** * Returns an ecl data type for which all names will fit. If the maximum name * length is at most 8, an ECL_CHAR is returned and otherwise a large enough @@ -1103,30 +1086,27 @@ static void ecl_smspec_set_params_size( ecl_smspec_type * ecl_smspec , int param void ecl_smspec_insert_node(ecl_smspec_type * ecl_smspec, smspec_node_type * smspec_node){ - if (!ecl_smspec->locked) { - int internal_index = vector_get_size( ecl_smspec->smspec_nodes ); + int internal_index = vector_get_size( ecl_smspec->smspec_nodes ); - /* This IF test should only apply in write_mode. */ - if (smspec_node_get_params_index( smspec_node ) < 0) { - if (!ecl_smspec->write_mode) - util_abort("%s: internal error \n",__func__); - smspec_node_set_params_index( smspec_node , internal_index); + /* This IF test should only apply in write_mode. */ + if (smspec_node_get_params_index( smspec_node ) < 0) { + if (!ecl_smspec->write_mode) + util_abort("%s: internal error \n",__func__); + smspec_node_set_params_index( smspec_node , internal_index); - if (internal_index >= ecl_smspec->params_size) - ecl_smspec_set_params_size( ecl_smspec , internal_index + 1); - } - vector_append_owned_ref( ecl_smspec->smspec_nodes , smspec_node , smspec_node_free__ ); + if (internal_index >= ecl_smspec->params_size) + ecl_smspec_set_params_size( ecl_smspec , internal_index + 1); + } + vector_append_owned_ref( ecl_smspec->smspec_nodes , smspec_node , smspec_node_free__ ); - { - int params_index = smspec_node_get_params_index( smspec_node ); + { + int params_index = smspec_node_get_params_index( smspec_node ); - /* This indexing must be used when writing. */ - int_vector_iset( ecl_smspec->index_map , internal_index , params_index); + /* This indexing must be used when writing. */ + int_vector_iset( ecl_smspec->index_map , internal_index , params_index); - float_vector_iset( ecl_smspec->params_default , params_index , smspec_node_get_default(smspec_node) ); - } - } else - util_abort("%s: sorry - the smspec header has been locked (can not mix ecl_sum_add_var() and ecl_sum_add_tstep() calls.)\n",__func__); + float_vector_iset( ecl_smspec->params_default , params_index , smspec_node_get_default(smspec_node) ); + } } diff --git a/lib/ecl/ecl_sum.cpp b/lib/ecl/ecl_sum.cpp index 5c7ac13c2e..9e7e84cbe4 100644 --- a/lib/ecl/ecl_sum.cpp +++ b/lib/ecl/ecl_sum.cpp @@ -16,6 +16,8 @@ for more details. */ +#include + #include #include #include @@ -279,6 +281,10 @@ void ecl_sum_set_fmt_case( ecl_sum_type * ecl_sum , bool fmt_case ) { smspec_node_type * ecl_sum_add_var( ecl_sum_type * ecl_sum , const char * keyword , const char * wgname , int num , const char * unit , float default_value) { + if (ecl_sum_data_get_length(ecl_sum->data) > 0) + throw std::invalid_argument("Can not interchange variable adding and timesteps.\n"); + + smspec_node_type * smspec_node = smspec_node_alloc( ecl_smspec_identify_var_type(keyword), wgname, keyword, @@ -289,6 +295,7 @@ smspec_node_type * ecl_sum_add_var( ecl_sum_type * ecl_sum , const char * keywor -1, default_value); ecl_smspec_add_node(ecl_sum->smspec, smspec_node); + ecl_sum_data_reset_self_map( ecl_sum->data ); return smspec_node; } @@ -754,7 +761,7 @@ const char * ecl_sum_get_general_var_unit( const ecl_sum_type * ecl_sum , const ecl_sum_type * ecl_sum_alloc_resample(const ecl_sum_type * ecl_sum, const char * ecl_case, const time_t_vector_type * times) { time_t start_time = ecl_sum_get_data_start(ecl_sum); - if ( time_t_vector_get_first(times) < start_time ) + if ( time_t_vector_get_first(times) < start_time ) return NULL; if ( time_t_vector_get_last(times) > ecl_sum_get_end_time(ecl_sum) ) return NULL; @@ -881,9 +888,6 @@ int ecl_sum_iget_report_end( const ecl_sum_type * ecl_sum, int report_step) { return ecl_sum_data_iget_report_end(ecl_sum->data , report_step ); } -int ecl_sum_iget_report_start( const ecl_sum_type * ecl_sum, int report_step) { - return ecl_sum_data_iget_report_start(ecl_sum->data , report_step ); -} int ecl_sum_iget_report_step( const ecl_sum_type * ecl_sum , int internal_index ){ return ecl_sum_data_iget_report_step( ecl_sum->data , internal_index ); diff --git a/lib/ecl/ecl_sum_data.cpp b/lib/ecl/ecl_sum_data.cpp index 163debae43..58b28b1288 100644 --- a/lib/ecl/ecl_sum_data.cpp +++ b/lib/ecl/ecl_sum_data.cpp @@ -19,11 +19,17 @@ #include #include +#include +#include +#include +#include + #include #include #include #include #include +#include #include #include @@ -36,240 +42,241 @@ #include #include - +#include "detail/ecl/ecl_sum_file_data.hpp" /* - This file implements the type ecl_sum_data_type. The data structure - is involved with holding all the actual summary data (i.e. the - PARAMS vectors in ECLIPSE speak), in addition the time-information - with MINISTEPS / REPORT_STEPS and so on is implemented here. - - This file has no information about how to index into the PARAMS - vector, i.e. at which location can the WWCT for well P6 be found, - that is responsability of the ecl_smspec_type. - - The time direction in this system is implemented in terms of - ministeps. There are some query / convert functons based on report - steps. + This file implements the sruct ecl_sum_data which manages the actual simulated + values from a summary file, including all time-related information. In the + case of restarted simulations the different summary cases will be internalized + as separate ecl_sum_file_data instances. The ecl_sum_file_data class is an + internal implemenation detail which is not exported. More details about the + internal storage of summary data can be found in that file. + + For this file the implementation mainly consists of maintaining an ordered + list of ecl_sum_file_data instances and dispatching method calls to the right + ecl_sum_file_data instance. */ +namespace { -/*****************************************************************/ /* - About ministeps and report steps. - --------------------------------- - - A sequence of summary data will typically look like this: - - ------------------ - SEQHDR \ - MINISTEP 0 | - PARAMS ..... | - MINISTEP 1 |==> This is REPORT STEP 1, in file BASE.S00001 - PARAMS ..... | - MINISTEP 2 | - PARAMS ..... / - ------------------ - SEQHDR \ - MINISTEP 3 | - PARAMS ..... | - MINISTEP 4 | - PARAMS ..... | - MINISTEP 5 |==> This is REPORT STEP 2, in file BASE.S0002 - PARAMS ..... | - MINISTEP 6 | - PARAMS ..... | - SEQHDR | - MINISTEP 7 | - PARAMS ..... / - ------------------ - - - Observe the following: - - * The MINISTEP counter runs continously, and does not - differentiate between unified files and not unified files. - - * When using multiple files we can read off the report number - from the filename, for unified files this is IMPOSSIBLE, and we - just have to assume that the first block corresponds to - report_step 1 and then count afterwards. - - * When asking for a summary variable at a particular REPORT STEP - (as we do in enkf) it is ambigous as to which ministep within - the block one should use. The convention we have employed - (which corresponds to the old RPTONLY based behaviour) is to - use the last ministep in the block. + The class TimeIndex and the struct IndexNode are used to maintain a list of + the ecl_sum_file_data instances, and lookup the correct one based one various + time related arguments. +*/ - * There is no BASE.SOOOO file + struct IndexNode { + IndexNode(int d, int o, int l) { + this->data_index = d; + this->offset = o; + this->length = l; + } - * The report steps are halfopen intervals in the "wrong way": - (....] + int end() const { + return this->offset + this->length; + } + int data_index; + int offset; + int length; + int report1; + int report2; + time_t time1; + time_t time2; + double days1; + double days2; + std::vector params_map; + }; + class TimeIndex { + public: - About MINISTEP, REPORTSTEP, rates and continous sim_time/sim_days: - ------------------------------------------------------------------ + IndexNode& add(int length) { + int offset = 0; + int data_index = this->index.size(); - For ECLIPSE summary files the smallest unit of time resolution is - called the ministep - a ministep corresponds to a time step in the - underlying partial differential equation, i.e. the length of the - timesteps is controlled by the simulator itself - there is no finer - temporal resolution. + if (!this->index.empty()) + offset = this->index.back().end(); - The user has told the simulator to store (i.e. save to file - results) the results at reportsteps. A reportstep will typically - consist of several ministeps. The timeline below shows a simulation - consisting of two reportsteps: + this->index.emplace_back(data_index, offset, length); + return this->index.back(); + } +/* + The lookup_time() and lookup_report() methods will lookup which file_data + instance corresponds to the time/report argument. The methods will return two + pointers to file_data instances, if the argument is inside one file_data + instance the pointers will be equal - otherwise they will point to the + file_data instance before and after the argument: + + File 1 File 2 + |------|-----|------| |----|----------|---| + /|\ /|\ + | | + | | + A B + + For time A the lookup_time function will return whereas for time + B the function will return . + */ + + std::pair lookup_time(time_t sim_time) const { + auto iter = this->index.begin(); + auto next = this->index.begin(); + if (sim_time < iter->time1) + throw std::invalid_argument("Simulation time out of range"); + + ++next; + while (true) { + double t1 = iter->time1; + double t2 = iter->time2; + + + if (sim_time>= t1) { + if (sim_time <= t2) + return std::make_pair(&(*iter), &(*iter)); + + if (next == this->index.end()) + throw std::invalid_argument("Simulation days out of range"); + + if (sim_time < next->time1) + return std::make_pair(&(*iter),&(*next)); + } + ++next; + ++iter; + } + } - S0001 S0002 - ||------|------|------------|------------------||----------------------|----------------------|| - M1 M2 M3 M4 M5 M6 - The first reportstep consist of four ministeps, the second - reportstep consits of only two ministeps. As a user you have no - control over the length/number of ministeps apart from: + std::pair lookup_days(double days) const { + auto iter = this->index.begin(); + auto next = this->index.begin(); + if (days < iter->days1) + throw std::invalid_argument("Simulation days out of range"); - 1. Indirectly through the TUNING keywords. - 2. A ministep will always end at a report step. + ++next; + while (true) { + double d1 = iter->days1; + double d2 = iter->days2; - RPTONLY: In conjunction with enkf it has been customary to use the - keyword RPTONLY. This is purely a storage directive, the effect is - that only the ministep ending at the REPORT step is reported, - i.e. in the case above we would get the ministeps [M4 , M6], where - the ministeps M4 and M6 will be unchanged, and there will be many - 'holes' in the timeline. + if (days >= d1) { + if (days <= d2) + return std::make_pair(&(*iter), &(*iter)); - About truetime: The ministeps have a finite length; this implies - that + if (next == this->index.end()) + throw std::invalid_argument("Simulation days out of range"); - [rates]: The ministep value is NOT actually an instantaneous - value, it is the total production during the ministepd period - - divided by the length of the ministep. I.e. it is an average - value. (I.e. the differential time element dt is actually quite - looong). + if (days < next->days1) + return std::make_pair(&(*iter),&(*next)); + } + ++next; + ++iter; + } + } - [state]: For state variables (this will include total production - of various phases), the ministep value corresponds to the - reservoir state at THE END OF THE MINISTEP. + const IndexNode& lookup(int internal_index) const { + for (const auto& node : this->index) + if (internal_index >= node.offset && internal_index < node.end()) + return node; - This difference between state variables and rates implies a - difference in how continous time-variables (in the middle of a - ministep) are reported, i.e. + throw std::invalid_argument("Internal error when looking up index: " + std::to_string(internal_index)); + } - S0000 S0001 - ||--------------|---------------|------------X-------------|| - M1 M2 /|\ M3 - | - | + const IndexNode& lookup_report(int report) const { + for (const auto& node : this->index) + if (node.report1 <= report && node.report2 >= report) + return node; - We have enteeed the sim_days/sim_time cooresponding to the location - of 'X' on the timeline, i.e. in the middle of ministep M3. If we - are interested in the rate at this time the function: + throw std::invalid_argument("Internal error when looking up report: " + std::to_string(report)); + } - ecl_sum_data_get_from_sim_time() + /* + This will check that we have a datafile which report range covers the + report argument, in adition there can be 'holes' in the series - that must + be checked by actually querying the data_file object. + */ - will just return the M3 value, whereas if you are interested in - e.g. pressure at this time the function will return a weighted - average of the M2 and M3 values. Whether a variable in question is - interpreted as a 'rate' is effectively determined by the - ecl_smspec_set_rate_var() function in ecl_smspec.c. + bool has_report(int report) const { + for (const auto& node : this->index) + if (node.report1 <= report && node.report2 >= report) + return true; + return false; + } + IndexNode& back() { + return this->index.back(); + } - Indexing and _get() versus _iget() - ---------------------------------- - As already mentionded the set of ministeps is not necessarrily a - continous series, we can easily have a series of ministeps with - "holes" in it, and the series can also start on a non-zero - value. Internally all the ministeps are stored in a dense, zero - offset vector instance; and we must be able to translate back and - forth between ministep_nr and internal index. + void clear() { + this->index.clear(); + } - Partly due to EnKF heritage the MINISTEP nr has been the main - method to access the time dimension of the data, i.e. all the - functions like ecl_sum_get_general_var() expect the time direction - to be given as a ministep; however it is also possible to get the - data by giving an internal (not that internal ...) index. In - ecl_sum_data.c the latter functions have _iget(): + int length() const { + return this->index.back().end(); + } + std::vector::const_iterator begin() const { + return this->index.begin(); + } - ecl_sum_data_get_xxx : Expects the time direction given as a ministep_nr. - ecl_sum_data_iget_xxx: Expects the time direction given as an internal index. + std::vector::const_iterator end() const { + return this->index.end(); + } -*/ + private: + std::vector index; + }; +} -#define INVALID_MINISTEP_NR -1 -#define INVALID_TIME_T 0 struct ecl_sum_data_struct { - ecl_smspec_type * smspec; /* A shared reference - only used for providing good error messages. */ - vector_type * data; /* Vector of ecl_sum_tstep_type instances. */ - double days_start; - double sim_length; - int_vector_type * report_first_index ; /* Indexed by report_step - giving first internal_index in report_step. */ - int_vector_type * report_last_index; /* Indexed by report_step - giving last internal_index in report_step. */ - int first_report_step; - int last_report_step; - bool index_valid; - time_t start_time; /* In the case of restarts the start might disagree with the value reported - in the smspec file. */ - time_t end_time; + const ecl_smspec_type * smspec; + std::vector data_files; // List of ecl_sum_file_data instances + TimeIndex index; }; +static void ecl_sum_data_build_index( ecl_sum_data_type * self ); /*****************************************************************/ void ecl_sum_data_free( ecl_sum_data_type * data ) { - vector_free( data->data ); - int_vector_free( data->report_first_index ); - int_vector_free( data->report_last_index ); - free(data); -} + if (!data) + throw std::invalid_argument(__func__ + std::string(": invalid delete") ); -/* - This function will clear/initialize all the mapping between - ministep, report step and internal index. This function should be - called before (re)building the indexes. -*/ + if (data->data_files.size() > 0) + delete data->data_files.back(); + delete data; +} -static void ecl_sum_data_clear_index( ecl_sum_data_type * data ) { - int_vector_reset( data->report_first_index); - int_vector_reset( data->report_last_index); - data->first_report_step = 1024 * 1024; - data->last_report_step = -1024 * 1024; - data->days_start = 0; - data->sim_length = -1; - data->index_valid = false; - data->start_time = INVALID_TIME_T; - data->end_time = INVALID_TIME_T; +ecl_sum_data_type * ecl_sum_data_alloc(ecl_smspec_type * smspec) { + ecl_sum_data_type * data = new ecl_sum_data_type(); + data->smspec = smspec; + return data; } -ecl_sum_data_type * ecl_sum_data_alloc(ecl_smspec_type * smspec) { - ecl_sum_data_type * data = (ecl_sum_data_type*)util_malloc( sizeof * data ); - data->data = vector_alloc_new(); - data->smspec = smspec; +void ecl_sum_data_reset_self_map( ecl_sum_data_type * data ) { + ecl_sum_data_build_index(data); +} - data->report_first_index = int_vector_alloc( 0 , INVALID_MINISTEP_NR ); - data->report_last_index = int_vector_alloc( 0 , INVALID_MINISTEP_NR ); - ecl_sum_data_clear_index( data ); - return data; +static void ecl_sum_data_append_file_data( ecl_sum_data_type * sum_data, ecl::ecl_sum_file_data * file_data) { + sum_data->data_files.push_back( file_data ); } + /** This function will take a report as input , and update the two pointers ministep1 and ministep2 with the range of the report step @@ -293,100 +300,30 @@ ecl_sum_data_type * ecl_sum_data_alloc(ecl_smspec_type * smspec) { static ecl_sum_tstep_type * ecl_sum_data_iget_ministep( const ecl_sum_data_type * data , int internal_index ) { - return (ecl_sum_tstep_type*)vector_iget( data->data , internal_index ); + const auto index_node = data->index.lookup(internal_index); + const auto data_file = data->data_files[index_node.data_index]; + return data_file->iget_ministep( internal_index - index_node.offset ); } -void ecl_sum_data_report2internal_range(const ecl_sum_data_type * data , int report_step , int * index1 , int * index2 ){ - if (index1 != NULL) - *index1 = int_vector_safe_iget( data->report_first_index , report_step ); - - if (index2 != NULL) - *index2 = int_vector_safe_iget( data->report_last_index , report_step ); -} - ecl_sum_data_type * ecl_sum_data_alloc_writer( ecl_smspec_type * smspec ) { ecl_sum_data_type * data = ecl_sum_data_alloc( smspec ); + ecl::ecl_sum_file_data * file_data = new ecl::ecl_sum_file_data( smspec ); + ecl_sum_data_append_file_data( data, file_data ); + ecl_sum_data_build_index(data); return data; } -static void ecl_sum_data_fwrite_report__( const ecl_sum_data_type * data , int report_step , fortio_type * fortio) { - { - ecl_kw_type * seqhdr_kw = ecl_kw_alloc( SEQHDR_KW , SEQHDR_SIZE , ECL_INT ); - ecl_kw_iset_int( seqhdr_kw , 0 , 0 ); - ecl_kw_fwrite( seqhdr_kw , fortio ); - ecl_kw_free( seqhdr_kw ); - } - - { - int index , index1 , index2; - - ecl_sum_data_report2internal_range( data , report_step , &index1 , &index2); - for (index = index1; index <= index2; index++) { - const ecl_sum_tstep_type * tstep = ecl_sum_data_iget_ministep( data , index ); - ecl_sum_tstep_fwrite( tstep , ecl_smspec_get_index_map( data->smspec ) , fortio ); - } - } -} - - - -static void ecl_sum_data_fwrite_multiple_step( const ecl_sum_data_type * data , const char * ecl_case , bool fmt_case , int report_step) { - char * filename = ecl_util_alloc_filename( NULL , ecl_case , ECL_UNIFIED_SUMMARY_FILE , fmt_case , 0 ); - fortio_type * fortio = fortio_open_readwrite( filename , fmt_case , ECL_ENDIAN_FLIP ); - - ecl_sum_data_fwrite_report__( data , report_step , fortio ); - - fortio_fclose( fortio ); - free(filename); -} - - -static void ecl_sum_data_fwrite_unified_step( const ecl_sum_data_type * data , const char * ecl_case , bool fmt_case , int report_step) { - char * filename = ecl_util_alloc_filename( NULL , ecl_case , ECL_UNIFIED_SUMMARY_FILE , fmt_case , 0 ); - fortio_type * fortio = fortio_open_readwrite( filename , fmt_case , ECL_ENDIAN_FLIP ); - - int current_step = 1; - if (report_step > 1) { - while (true) { - if (ecl_kw_fseek_kw( SEQHDR_KW , false , false , fortio )) { - if (current_step == report_step) - break; - current_step++; - } else { - current_step++; - break; - } - } - } - - if (current_step == report_step) { // We found the position: - long size = fortio_ftell( fortio ); - - util_ftruncate( fortio_get_FILE( fortio ) , size ); - ecl_sum_data_fwrite_report__( data , report_step , fortio ); - } else - util_abort("%s: hmm could not locate the position for report step:%d in summary file:%s \n",__func__ , report_step , filename); - - fortio_fclose( fortio ); - free( filename ); -} - - - static void ecl_sum_data_fwrite_unified( const ecl_sum_data_type * data , const char * ecl_case , bool fmt_case ) { char * filename = ecl_util_alloc_filename( NULL , ecl_case , ECL_UNIFIED_SUMMARY_FILE , fmt_case , 0 ); fortio_type * fortio = fortio_open_writer( filename , fmt_case , ECL_ENDIAN_FLIP ); - int report_step; - for (report_step = data->first_report_step; report_step <= data->last_report_step; report_step++) { - if (ecl_sum_data_has_report_step( data , report_step )) - ecl_sum_data_fwrite_report__( data , report_step , fortio ); - } + for (size_t index = 0; index < data->data_files.size(); index++) + data->data_files[index]->fwrite_unified( fortio ); fortio_fclose( fortio ); free( filename ); @@ -394,32 +331,13 @@ static void ecl_sum_data_fwrite_unified( const ecl_sum_data_type * data , const static void ecl_sum_data_fwrite_multiple( const ecl_sum_data_type * data , const char * ecl_case , bool fmt_case ) { - int report_step; - - for (report_step = data->first_report_step; report_step <= data->last_report_step; report_step++) { - if (ecl_sum_data_has_report_step( data , report_step )) { - char * filename = ecl_util_alloc_filename( NULL , ecl_case , ECL_SUMMARY_FILE , fmt_case , report_step ); - fortio_type * fortio = fortio_open_writer( filename , fmt_case , ECL_ENDIAN_FLIP ); - ecl_sum_data_fwrite_report__( data , report_step , fortio ); - - fortio_fclose( fortio ); - free( filename ); - } - } + for (size_t index = 0; index < data->data_files.size(); index++) + data->data_files[index]->fwrite_multiple(ecl_case, fmt_case); } - -void ecl_sum_data_fwrite_step( const ecl_sum_data_type * data , const char * ecl_case , bool fmt_case , bool unified, int report_step) { - if (unified) - ecl_sum_data_fwrite_unified_step( data , ecl_case , fmt_case , report_step); - else - ecl_sum_data_fwrite_multiple_step( data , ecl_case , fmt_case , report_step); -} - - void ecl_sum_data_fwrite( const ecl_sum_data_type * data , const char * ecl_case , bool fmt_case , bool unified) { if (unified) ecl_sum_data_fwrite_unified( data , ecl_case , fmt_case ); @@ -428,14 +346,20 @@ void ecl_sum_data_fwrite( const ecl_sum_data_type * data , const char * ecl_case } +time_t ecl_sum_data_get_sim_end(const ecl_sum_data_type * data ) { + const auto& file_data = data->data_files.back(); + return file_data->get_sim_end(); +} +time_t ecl_sum_data_get_data_start( const ecl_sum_data_type * data ) { + const auto& file_data = data->data_files[0]; + return file_data->get_data_start(); +} - -time_t ecl_sum_data_get_sim_end (const ecl_sum_data_type * data ) { return data->end_time; } - -time_t ecl_sum_data_get_data_start ( const ecl_sum_data_type * data ) { return data->start_time; } - -double ecl_sum_data_get_first_day( const ecl_sum_data_type * data) { return data->days_start; } +double ecl_sum_data_get_first_day( const ecl_sum_data_type * data) { + const auto& file_data = data->data_files[0]; + return file_data->get_days_start(); +} /** Returns the number of simulations days from the start of the @@ -444,7 +368,8 @@ double ecl_sum_data_get_first_day( const ecl_sum_data_type * data) { return data */ double ecl_sum_data_get_sim_length( const ecl_sum_data_type * data ) { - return data->sim_length; + const auto& file_data = data->data_files.back(); + return file_data->get_sim_length(); } @@ -462,10 +387,10 @@ double ecl_sum_data_get_sim_length( const ecl_sum_data_type * data ) { */ bool ecl_sum_data_check_sim_time( const ecl_sum_data_type * data , time_t sim_time) { - if (sim_time < data->start_time) + if (sim_time < ecl_sum_data_get_data_start(data)) return false; - if (sim_time > data->end_time) + if (sim_time > ecl_sum_data_get_sim_end(data)) return false; return true; @@ -473,7 +398,7 @@ bool ecl_sum_data_check_sim_time( const ecl_sum_data_type * data , time_t sim_ti bool ecl_sum_data_check_sim_days( const ecl_sum_data_type * data , double sim_days) { - return sim_days >= data->days_start && sim_days <= data->sim_length; + return sim_days >= ecl_sum_data_get_first_day(data) && sim_days <= ecl_sum_data_get_sim_length(data); } @@ -518,11 +443,14 @@ bool ecl_sum_data_check_sim_days( const ecl_sum_data_type * data , double sim_da static int ecl_sum_data_get_index_from_sim_time( const ecl_sum_data_type * data , time_t sim_time) { if (!ecl_sum_data_check_sim_time(data, sim_time)) { + time_t start_time = ecl_sum_data_get_data_start(data); + time_t end_time = ecl_sum_data_get_sim_end(data); + fprintf(stderr , "Simulation start: "); util_fprintf_date_utc( ecl_smspec_get_start_time( data->smspec ) , stderr ); - fprintf(stderr , "Data start......: "); util_fprintf_date_utc( data->start_time , stderr ); - fprintf(stderr , "Simulation end .: "); util_fprintf_date_utc( data->end_time , stderr ); + fprintf(stderr , "Data start......: "); util_fprintf_date_utc( start_time , stderr ); + fprintf(stderr , "Simulation end .: "); util_fprintf_date_utc( end_time , stderr ); fprintf(stderr , "Requested date .: "); util_fprintf_date_utc( sim_time , stderr ); - util_abort("%s: invalid time_t instance:%d interval: [%d,%d]\n",__func__, sim_time , data->start_time , data->end_time); + util_abort("%s: invalid time_t instance:%d interval: [%d,%d]\n",__func__, sim_time , start_time, end_time); } /* @@ -533,13 +461,12 @@ static int ecl_sum_data_get_index_from_sim_time( const ecl_sum_data_type * data */ int low_index = 0; - int high_index = vector_get_size(data->data) - 1; + int high_index = ecl_sum_data_get_length(data) - 1; // perform binary search while (low_index+1 < high_index) { int center_index = (low_index + high_index) / 2; - const ecl_sum_tstep_type * center_step = ecl_sum_data_iget_ministep(data, center_index); - const time_t center_time = ecl_sum_tstep_get_sim_time(center_step); + const time_t center_time = ecl_sum_data_iget_sim_time(data, center_index); if (sim_time > center_time) low_index = center_index; @@ -547,8 +474,7 @@ static int ecl_sum_data_get_index_from_sim_time( const ecl_sum_data_type * data high_index = center_index; } - const ecl_sum_tstep_type * low_step = ecl_sum_data_iget_ministep(data, low_index); - return sim_time <= ecl_sum_tstep_get_sim_time(low_step) ? low_index : high_index; + return sim_time <= ecl_sum_data_iget_sim_time(data, low_index) ? low_index : high_index; } int ecl_sum_data_get_index_from_sim_days( const ecl_sum_data_type * data , double sim_days) { @@ -582,6 +508,7 @@ int ecl_sum_data_get_index_from_sim_days( const ecl_sum_data_type * data , doubl function should be used), consult documentation at the top of this file. */ + void ecl_sum_data_init_interp_from_sim_time(const ecl_sum_data_type* data, time_t sim_time, int* index1, @@ -600,11 +527,8 @@ void ecl_sum_data_init_interp_from_sim_time(const ecl_sum_data_type* data, return; } - const ecl_sum_tstep_type * ministep1 = ecl_sum_data_iget_ministep(data, idx-1); - const ecl_sum_tstep_type * ministep2 = ecl_sum_data_iget_ministep(data, idx); - - time_t sim_time1 = ecl_sum_tstep_get_sim_time(ministep1); - time_t sim_time2 = ecl_sum_tstep_get_sim_time(ministep2); + time_t sim_time1 = ecl_sum_data_iget_sim_time(data, idx-1); + time_t sim_time2 = ecl_sum_data_iget_sim_time(data, idx); *index1 = idx-1; *index2 = idx; @@ -630,7 +554,7 @@ void ecl_sum_data_init_interp_from_sim_days( const ecl_sum_data_type * data , do double_vector_type * ecl_sum_data_alloc_seconds_solution(const ecl_sum_data_type * data, const smspec_node_type * node, double cmp_value, bool rates_clamp_lower) { double_vector_type * solution = double_vector_alloc(0, 0); const int param_index = smspec_node_get_params_index(node); - const int size = vector_get_size(data->data); + const int size = ecl_sum_data_get_length(data); if (size <= 1) return solution; @@ -640,11 +564,11 @@ double_vector_type * ecl_sum_data_alloc_seconds_solution(const ecl_sum_data_type const ecl_sum_tstep_type * ministep = ecl_sum_data_iget_ministep(data, index); const ecl_sum_tstep_type * prev_ministep = ecl_sum_data_iget_ministep(data, prev_index); - double value = ecl_sum_tstep_iget(ministep, param_index); - double prev_value = ecl_sum_tstep_iget(prev_ministep, param_index); + double value = ecl_sum_data_iget(data, index, param_index); + double prev_value = ecl_sum_data_iget(data, prev_index, param_index); // cmp_value in interval value (closed) and prev_value (open) - bool contained = value == cmp_value; + bool contained = (value == cmp_value); contained |= (util_double_min(prev_value, value) < cmp_value) && (cmp_value < util_double_max(prev_value, value)); @@ -659,7 +583,6 @@ double_vector_type * ecl_sum_data_alloc_seconds_solution(const ecl_sum_data_type } else { double slope = (value - prev_value) / (time - prev_time); double seconds = (cmp_value - prev_value)/slope + prev_time; - double_vector_append(solution, seconds); } } @@ -667,94 +590,44 @@ double_vector_type * ecl_sum_data_alloc_seconds_solution(const ecl_sum_data_type } +static void ecl_sum_data_build_index( ecl_sum_data_type * self ) { + std::sort(self->data_files.begin(), self->data_files.end(), + [](const ecl::ecl_sum_file_data* case1, + const ecl::ecl_sum_file_data* case2) + { + return case1->get_data_start() < case2->get_data_start(); + }); + self->index.clear(); + for (size_t i=0; i < self->data_files.size(); i++) { + const auto& data = self->data_files[i]; + int r1 = data->first_report(); + int r2; -static void ecl_sum_data_append_tstep__( ecl_sum_data_type * data , ecl_sum_tstep_type * tstep) { - /* - Here the tstep is just appended naively, the vector will be - sorted by ministep_nr before the data instance is returned. - */ - - vector_append_owned_ref( data->data , tstep , ecl_sum_tstep_free__); - data->index_valid = false; -} - - - -static int cmp_ministep( const void * arg1 , const void * arg2) { - const ecl_sum_tstep_type * ministep1 = ecl_sum_tstep_safe_cast_const( arg1 ); - const ecl_sum_tstep_type * ministep2 = ecl_sum_tstep_safe_cast_const( arg2 ); - - time_t time1 = ecl_sum_tstep_get_sim_time( ministep1 ); - time_t time2 = ecl_sum_tstep_get_sim_time( ministep2 ); - - if (time1 < time2) - return -1; - else if (time1 == time2) - return 0; - else - return 1; -} - - -static void ecl_sum_data_build_index( ecl_sum_data_type * sum_data ) { - /* Clear the existing index (if any): */ - ecl_sum_data_clear_index( sum_data ); - - /* - Sort the internal storage vector after sim_time. - */ - vector_sort( sum_data->data , cmp_ministep ); + if (i == (self->data_files.size() - 1)) { + self->index.add(data->get_length()); + r2 = data->last_report(); + } else { + const auto& next = self->data_files[i+1]; + self->index.add( data->length_before(next->get_data_start())); + r2 = data->report_before( next->get_data_start() ); + } + auto & node = self->index.back(); + { + int * tmp_map = ecl_smspec_alloc_mapping( self->smspec , data->smspec() ); + node.params_map.assign(tmp_map, tmp_map + ecl_smspec_get_params_size(self->smspec)); + free( tmp_map ); + } - /* Identify various global first and last values. */ - { - const ecl_sum_tstep_type * first_ministep = (const ecl_sum_tstep_type*)vector_iget_const( sum_data->data, 0 ); - const ecl_sum_tstep_type * last_ministep = (const ecl_sum_tstep_type*)vector_get_last_const( sum_data->data ); - /* - In most cases the days_start and data_start_time will agree - with the global simulation start; however in the case where we - have loaded a summary case from a restarted simulation where - the case we have restarted from is not available - then there - will be a difference. - */ - sum_data->days_start = ecl_sum_tstep_get_sim_days( first_ministep ); - sum_data->sim_length = ecl_sum_tstep_get_sim_days( last_ministep ); - sum_data->start_time = ecl_sum_tstep_get_sim_time( first_ministep); - sum_data->end_time = ecl_sum_tstep_get_sim_time( last_ministep ); + node.report1 = r1; + node.report2 = r2; + node.time1 = data->get_data_start(); + node.time2 = data->get_sim_end(); + node.days1 = data->get_days_start(); + node.days2 = data->get_sim_length(); } - /* Build up the report -> ministep mapping. */ - { - int internal_index; - for (internal_index = 0; internal_index < vector_get_size( sum_data->data ); internal_index++) { - const ecl_sum_tstep_type * ministep = ecl_sum_data_iget_ministep( sum_data , internal_index ); - int report_step = ecl_sum_tstep_get_report(ministep); - - /* Indexing internal_index - report_step */ - { - int current_first_index = int_vector_safe_iget( sum_data->report_first_index , report_step ); - if (current_first_index < 0) /* i.e. currently not set. */ - int_vector_iset( sum_data->report_first_index , report_step , internal_index); - else - if (internal_index < current_first_index) - int_vector_iset( sum_data->report_first_index , report_step , internal_index); - } - - { - int current_last_index = int_vector_safe_iget( sum_data->report_last_index , report_step ); - if (current_last_index < 0) - int_vector_iset( sum_data->report_last_index , report_step , internal_index); - else - if (internal_index > current_last_index) - int_vector_iset( sum_data->report_last_index , report_step , internal_index); - } - - sum_data->first_report_step = util_int_min( sum_data->first_report_step , report_step ); - sum_data->last_report_step = util_int_max( sum_data->last_report_step , report_step ); - } - } - sum_data->index_valid = true; } @@ -767,151 +640,30 @@ static void ecl_sum_data_build_index( ecl_sum_data_type * sum_data ) { */ ecl_sum_tstep_type * ecl_sum_data_add_new_tstep( ecl_sum_data_type * data , int report_step , double sim_seconds) { - int ministep_nr = vector_get_size( data->data ); - ecl_sum_tstep_type * tstep = ecl_sum_tstep_alloc_new( report_step , ministep_nr , sim_seconds , data->smspec ); - ecl_sum_tstep_type * prev_tstep = NULL; - - if (vector_get_size( data->data ) > 0) - prev_tstep = (ecl_sum_tstep_type*)vector_get_last( data->data ); - - ecl_sum_data_append_tstep__( data , tstep ); - { - bool rebuild_index = true; - - /* - In the simple case that we just add another timestep to the - currently active report_step, we do a limited update of the - index, otherwise we call ecl_sum_data_build_index() to get a - full recalculation of the index. - */ - - if (prev_tstep != NULL) { - if (ecl_sum_tstep_get_report( prev_tstep ) == ecl_sum_tstep_get_report( tstep )) { // Same report step - if (ecl_sum_tstep_get_sim_days( prev_tstep ) < ecl_sum_tstep_get_sim_days( tstep )) { // This tstep will become the new latest tstep - int internal_index = vector_get_size( data->data ) - 1; - int_vector_iset( data->report_last_index , report_step , internal_index ); - - data->sim_length = ecl_sum_tstep_get_sim_days( tstep ); - data->end_time = ecl_sum_tstep_get_sim_time(tstep); - - rebuild_index = false; - } - } - } - if (rebuild_index) - ecl_sum_data_build_index( data ); - } - ecl_smspec_lock( data->smspec ); - + ecl::ecl_sum_file_data * file_data = data->data_files.back(); + ecl_sum_tstep_type * tstep = file_data->add_new_tstep( report_step, sim_seconds ); + ecl_sum_data_build_index( data ); return tstep; } - - -/** - Malformed/incomplete files: - ---------------------------- - Observe that ECLIPSE works in the following way: - - 1. At the start of a report step a summary data section - containing only the 'SEQHDR' keyword is written - this is - currently an 'invalid' summary section. - - 2. ECLIPSE simulates as best it can. - - 3. When the time step is complete data is written to the summary - file. - - Now - if ECLIPSE goes down in flames during step 2 a malformed - summary file will be left around, to handle this situation - reasonably gracefully we check that the ecl_file instance has at - least one "PARAMS" keyword. - - One ecl_file corresponds to one report_step (limited by SEQHDR); in - the case of non unfied summary files these objects correspond to - one BASE.Annnn or BASE.Snnnn file, in the case of unified files the - calling routine will read the unified summary file partly. -*/ - -static void ecl_sum_data_add_ecl_file(ecl_sum_data_type * data , - int report_step , - const ecl_file_view_type * summary_view, - const ecl_smspec_type * smspec) { - - - int num_ministep = ecl_file_view_get_num_named_kw( summary_view , PARAMS_KW); - if (num_ministep > 0) { - int ikw; - - for (ikw = 0; ikw < num_ministep; ikw++) { - ecl_kw_type * ministep_kw = ecl_file_view_iget_named_kw( summary_view , MINISTEP_KW , ikw); - ecl_kw_type * params_kw = ecl_file_view_iget_named_kw( summary_view , PARAMS_KW , ikw); - - { - int ministep_nr = ecl_kw_iget_int( ministep_kw , 0 ); - ecl_sum_tstep_type * tstep = ecl_sum_tstep_alloc_from_file( report_step , - ministep_nr , - params_kw , - ecl_file_view_get_src_file( summary_view ), - smspec ); - - if (tstep) - ecl_sum_data_append_tstep__( data , tstep ); - } - } +int * ecl_sum_data_alloc_param_mapping( int * current_param_mapping, int * old_param_mapping, size_t size) { + int * new_param_mapping = (int*)util_malloc( size * sizeof * new_param_mapping ); + for (size_t i = 0; i < size; i++) { + if (current_param_mapping[i] >= 0) + new_param_mapping[i] = old_param_mapping[ current_param_mapping[i] ]; + else + new_param_mapping[i] = -1; } + return new_param_mapping; } void ecl_sum_data_add_case(ecl_sum_data_type * self, const ecl_sum_data_type * other) { - int * param_mapping = NULL; - bool header_equal = ecl_smspec_equal( self->smspec , other->smspec); - float default_value = 0; - - if (!header_equal) - param_mapping = ecl_smspec_alloc_mapping( self->smspec , other->smspec ); - - - for (int tstep_nr = 0; tstep_nr < ecl_sum_data_get_length( other ); tstep_nr++) { - ecl_sum_tstep_type * other_tstep = ecl_sum_data_iget_ministep( other , tstep_nr ); - - /* - The dataset 'self' is the authorative in the timeinterval where it has - data, so if 'other' also has data in the same time interval that is - discarded. In most cases 'other' will represent a history case, and 'self' - is a prediction which has been restarted. - - After implementing the time_interval_contains() based check it turned out - that the smspec structure also contains a restart_step integer value in - the DIMENS vector which could probably be used to achieve the same thing. - That field is currently not used. - */ - - if (!ecl_sum_data_check_sim_time(self, ecl_sum_tstep_get_sim_time(other_tstep))) { - ecl_sum_tstep_type * new_tstep; - - if (header_equal) - new_tstep = ecl_sum_tstep_alloc_copy( other_tstep ); - else - new_tstep = ecl_sum_tstep_alloc_remap_copy( other_tstep , self->smspec , default_value , param_mapping ); + for (auto other_file : other->data_files) + self->data_files.push_back( other_file ); - ecl_sum_data_append_tstep__( self , new_tstep ); - - } - } - - ecl_sum_data_build_index( self ); - free( param_mapping ); -} - - -static bool ecl_sum_data_check_file( ecl_file_type * ecl_file ) { - if (ecl_file_has_kw( ecl_file , PARAMS_KW ) && - (ecl_file_get_num_named_kw( ecl_file , PARAMS_KW ) == ecl_file_get_num_named_kw( ecl_file , MINISTEP_KW))) - return true; - else - return false; + ecl_sum_data_build_index(self); } @@ -925,65 +677,13 @@ static bool ecl_sum_data_check_file( ecl_file_type * ecl_file ) { */ bool ecl_sum_data_fread(ecl_sum_data_type * data , const stringlist_type * filelist) { - if (stringlist_get_size( filelist ) == 0) - return false; - - { - ecl_file_enum file_type = ecl_util_get_file_type( stringlist_iget( filelist , 0 ) , NULL , NULL); - if ((stringlist_get_size( filelist ) > 1) && (file_type != ECL_SUMMARY_FILE)) - util_abort("%s: internal error - when calling with more than one file - you can not supply a unified file - come on?! \n",__func__); - - { - int filenr; - if (file_type == ECL_SUMMARY_FILE) { - - /* Not unified. */ - for (filenr = 0; filenr < stringlist_get_size( filelist ); filenr++) { - const char * data_file = stringlist_iget( filelist , filenr); - ecl_file_enum file_type; - int report_step; - file_type = ecl_util_get_file_type( data_file , NULL , &report_step); - if (file_type != ECL_SUMMARY_FILE) - util_abort("%s: file:%s has wrong type \n",__func__ , data_file); - { - ecl_file_type * ecl_file = ecl_file_open( data_file , 0); - if (ecl_file && ecl_sum_data_check_file( ecl_file )) { - ecl_sum_data_add_ecl_file( data , report_step , ecl_file_get_global_view( ecl_file ) , data->smspec); - ecl_file_close( ecl_file ); - } - } - } - } else if (file_type == ECL_UNIFIED_SUMMARY_FILE) { - ecl_file_type * ecl_file = ecl_file_open( stringlist_iget(filelist ,0 ) , 0); - if (ecl_file && ecl_sum_data_check_file( ecl_file )) { - int report_step = 1; /* <- ECLIPSE numbering - starting at 1. */ - while (true) { - /* - Observe that there is a number discrepancy between ECLIPSE - and the ecl_file_select_smryblock() function. ECLIPSE - starts counting report steps at 1; whereas the first - SEQHDR block in the unified summary file is block zero (in - ert counting). - */ - ecl_file_view_type * summary_view = ecl_file_get_summary_view(ecl_file , report_step - 1 ); - if (summary_view) { - ecl_sum_data_add_ecl_file( data , report_step , summary_view , data->smspec); - report_step++; - } else break; - } - ecl_file_close( ecl_file ); - } - } - } - - - if (ecl_sum_data_get_length( data ) > 0) { - ecl_sum_data_build_index( data ); - return true; - } else - return false; - + ecl::ecl_sum_file_data * file_data = new ecl::ecl_sum_file_data( data->smspec ); + if (file_data->fread( filelist )) { + ecl_sum_data_append_file_data( data, file_data ); + ecl_sum_data_build_index(data); + return true; } + return false; } @@ -1021,7 +721,7 @@ void ecl_sum_data_summarize(const ecl_sum_data_type * data , FILE * stream) { fprintf(stream , "---------------------------------------------------------------\n"); { int index; - for (index = 0; index < vector_get_size( data->data ); index++) { + for (index = 0; index < ecl_sum_data_get_length(data); index++) { const ecl_sum_tstep_type * ministep = ecl_sum_data_iget_ministep( data , index ); int day,month,year; ecl_util_set_date_values( ecl_sum_tstep_get_sim_time( ministep ) , &day, &month , &year); @@ -1035,11 +735,14 @@ void ecl_sum_data_summarize(const ecl_sum_data_type * data , FILE * stream) { /*****************************************************************/ + bool ecl_sum_data_has_report_step(const ecl_sum_data_type * data , int report_step ) { - if (int_vector_safe_iget( data->report_first_index , report_step) >= 0) - return true; - else + if (!data->index.has_report(report_step)) return false; + + const auto& index_node = data->index.lookup_report(report_step); + const auto& file_data = data->data_files[index_node.data_index]; + return file_data->has_report(report_step); } @@ -1052,44 +755,31 @@ bool ecl_sum_data_has_report_step(const ecl_sum_data_type * data , int report_st */ int ecl_sum_data_iget_report_end( const ecl_sum_data_type * data , int report_step ) { - return int_vector_safe_iget( data->report_last_index , report_step ); + const auto& index_node = data->index.lookup_report(report_step); + const auto& file_data = data->data_files[index_node.data_index]; + auto range = file_data->report_range(report_step); + return range.second; } -/** - Returns the first index included in report step @report_step. - Observe that if the dataset does not include @report_step at all, - the function will return INVALID_MINISTEP_NR; this must be checked for in the - calling scope. -*/ - - -int ecl_sum_data_iget_report_start( const ecl_sum_data_type * data , int report_step ) { - return int_vector_safe_iget( data->report_first_index , report_step ); -} - - int ecl_sum_data_iget_report_step(const ecl_sum_data_type * data , int internal_index) { - const ecl_sum_tstep_type * ministep = ecl_sum_data_iget_ministep( data , internal_index ); - return ecl_sum_tstep_get_report( ministep ); + const auto& index_node = data->index.lookup(internal_index); + const auto& file_data = data->data_files[index_node.data_index]; + const auto& tstep = file_data->iget_ministep(internal_index - index_node.offset); + return ecl_sum_tstep_get_report( tstep ); } int ecl_sum_data_iget_mini_step(const ecl_sum_data_type * data , int internal_index) { - { - const ecl_sum_tstep_type * ministep = ecl_sum_data_iget_ministep( data , internal_index ); - return ecl_sum_tstep_get_ministep( ministep ); - } + const ecl_sum_tstep_type * ministep = ecl_sum_data_iget_ministep( data , internal_index ); + return ecl_sum_tstep_get_ministep( ministep ); } - - - /** This will look up a value based on an internal index. The internal index will ALWAYS run in the interval [0,num_ministep), without @@ -1098,11 +788,15 @@ int ecl_sum_data_iget_mini_step(const ecl_sum_data_type * data , int internal_in double ecl_sum_data_iget( const ecl_sum_data_type * data , int time_index , int params_index ) { - const ecl_sum_tstep_type * ministep_data = ecl_sum_data_iget_ministep( data , time_index ); - return ecl_sum_tstep_iget( ministep_data , params_index); + const auto& index_node = data->index.lookup( time_index ); + ecl::ecl_sum_file_data * file_data = data->data_files[index_node.data_index]; + const auto& params_map = index_node.params_map; + return file_data->iget( time_index - index_node.offset, params_map[params_index] ); } + + /** This function will form a weight average of the two ministeps @ministep1 and @ministep2. The weights and the ministep indices @@ -1115,11 +809,8 @@ double ecl_sum_data_iget( const ecl_sum_data_type * data , int time_index , int between two ministeps. */ -double ecl_sum_data_interp_get(const ecl_sum_data_type * data , int time_index1 , int time_index2 , double weight1 , double weight2 , int params_index) { - const ecl_sum_tstep_type * ministep_data1 = ecl_sum_data_iget_ministep( data , time_index1 ); - const ecl_sum_tstep_type * ministep_data2 = ecl_sum_data_iget_ministep( data , time_index2 ); - - return ecl_sum_tstep_iget( ministep_data1 , params_index ) * weight1 + ecl_sum_tstep_iget( ministep_data2 , params_index ) * weight2; +static double ecl_sum_data_interp_get(const ecl_sum_data_type * data , int time_index1 , int time_index2 , double weight1 , double weight2 , int params_index) { + return ecl_sum_data_iget(data, time_index1, params_index) * weight1 + ecl_sum_data_iget(data, time_index2, params_index) * weight2; } @@ -1223,34 +914,15 @@ double ecl_sum_data_get_from_sim_time( const ecl_sum_data_type * data , time_t s int ecl_sum_data_get_report_step_from_days(const ecl_sum_data_type * data , double sim_days) { - if ((sim_days < data->days_start) || (sim_days > data->sim_length)) + if ((sim_days < ecl_sum_data_get_first_day(data)) || (sim_days > ecl_sum_data_get_sim_length(data))) return -1; else { - int report_step = -1; - - double_vector_type * days_map = double_vector_alloc( 0 , 0 ); - int_vector_type * report_map = int_vector_alloc( 0 , 0 ); - int i; + auto files = data->index.lookup_days(sim_days); + if (files.first != files.second) + return -1; - for (i=1; i < int_vector_size( data->report_last_index ); i++) { - int ministep_index = int_vector_iget( data->report_last_index , i ); - const ecl_sum_tstep_type * ministep = (const ecl_sum_tstep_type*)vector_iget_const( data->data , ministep_index ); - - double_vector_iset( days_map , i , ecl_sum_tstep_get_sim_days( ministep )); - int_vector_iset( report_map , i , ecl_sum_tstep_get_report( ministep )); - } - - { - /** Hmmmm - double == comparison ... */ - int index = double_vector_index_sorted( days_map , sim_days ); - - if (index >= 0) - report_step = int_vector_iget( report_map , index ); - } - - int_vector_free( report_map ); - double_vector_free( days_map ); - return report_step; + const auto& data_file = data->data_files[files.first->data_index]; + return data_file->report_step_from_days(sim_days); } } @@ -1275,40 +947,18 @@ int ecl_sum_data_get_report_step_from_days(const ecl_sum_data_type * data , doub int ecl_sum_data_get_report_step_from_time(const ecl_sum_data_type * data , time_t sim_time) { - if (!ecl_sum_data_check_sim_time(data , sim_time)) return -1; + else { + auto files = data->index.lookup_time(sim_time); + if (files.first != files.second) + return -1; - { - int report_step = -1; - - time_t_vector_type * time_map = time_t_vector_alloc( 0 , 0 ); - int_vector_type * report_map = int_vector_alloc( 0 , 0 ); - int i; - - for (i=1; i < int_vector_size( data->report_last_index ); i++) { - int ministep_index = int_vector_iget( data->report_last_index , i ); - const ecl_sum_tstep_type * ministep = (const ecl_sum_tstep_type*)vector_iget_const( data->data , ministep_index ); - - time_t_vector_iset( time_map , i , ecl_sum_tstep_get_sim_time( ministep )); - int_vector_iset( report_map , i , ecl_sum_tstep_get_report( ministep )); - - } - - { - int index = time_t_vector_index_sorted( time_map , sim_time ); - - if (index >= 0) - report_step = int_vector_iget( report_map , index ); - } - - int_vector_free( report_map ); - time_t_vector_free( time_map ); - return report_step; + const auto& data_file = data->data_files[files.first->data_index]; + return data_file->report_step_from_time(sim_time); } } - double ecl_sum_data_time2days( const ecl_sum_data_type * data , time_t sim_time) { time_t start_time = ecl_smspec_get_start_time( data->smspec ); return util_difftime_days( start_time , sim_time ); @@ -1321,9 +971,10 @@ double ecl_sum_data_get_from_sim_days( const ecl_sum_data_type * data , double s } -time_t ecl_sum_data_iget_sim_time( const ecl_sum_data_type * data , int internal_index ) { - const ecl_sum_tstep_type * ministep_data = ecl_sum_data_iget_ministep( data , internal_index ); - return ecl_sum_tstep_get_sim_time( ministep_data ); +time_t ecl_sum_data_iget_sim_time(const ecl_sum_data_type * data, int ministep_index) { + const auto& index_node = data->index.lookup( ministep_index ); + const auto data_file = data->data_files[index_node.data_index]; + return data_file->iget_sim_time(ministep_index - index_node.offset); } @@ -1332,8 +983,7 @@ time_t ecl_sum_data_get_report_time( const ecl_sum_data_type * data , int report return ecl_smspec_get_start_time( data->smspec ); else { int internal_index = ecl_sum_data_iget_report_end( data , report_step ); - const ecl_sum_tstep_type * ministep = ecl_sum_data_iget_ministep( data , internal_index ); - return ecl_sum_tstep_get_sim_time( ministep ); + return ecl_sum_data_iget_sim_time(data, internal_index); } } @@ -1345,12 +995,14 @@ double ecl_sum_data_iget_sim_days( const ecl_sum_data_type * data , int internal int ecl_sum_data_get_first_report_step( const ecl_sum_data_type * data ) { - return data->first_report_step; + const auto& data_file = data->data_files[0]; + return data_file->first_report(); } int ecl_sum_data_get_last_report_step( const ecl_sum_data_type * data ) { - return data->last_report_step; + const auto& data_file = data->data_files.back(); + return data_file->last_report(); } @@ -1359,75 +1011,102 @@ int ecl_sum_data_get_last_report_step( const ecl_sum_data_type * data ) { -void ecl_sum_data_init_time_vector( const ecl_sum_data_type * data , time_t_vector_type * time_vector , bool report_only) { - time_t_vector_reset( time_vector ); - time_t_vector_append( time_vector , ecl_smspec_get_start_time( data->smspec )); - if (report_only) { - int report_step; - for (report_step = data->first_report_step; report_step <= data->last_report_step; report_step++) { - int last_index = int_vector_iget(data->report_last_index , report_step); - const ecl_sum_tstep_type * ministep = ecl_sum_data_iget_ministep( data , last_index ); - time_t_vector_append( time_vector , ecl_sum_tstep_get_sim_time( ministep ) ); - } - } else { - int i; - for (i = 1; i < vector_get_size(data->data); i++) { - const ecl_sum_tstep_type * ministep = ecl_sum_data_iget_ministep( data , i ); - time_t_vector_append( time_vector , ecl_sum_tstep_get_sim_time( ministep )); + +static void ecl_sum_data_init_time_vector__(const ecl_sum_data_type * data, time_t * output_data, bool report_only) { + int offset = 0; + for (const auto& index_node : data->index) { + const auto& data_file = data->data_files[index_node.data_index]; + + if (report_only) + offset += data_file->get_time_report(index_node.length, &output_data[offset]); + else { + data_file->get_time(index_node.length, &output_data[offset]); + offset += index_node.length; } } } + +void ecl_sum_data_init_time_vector(const ecl_sum_data_type * data, time_t * output_data) { + ecl_sum_data_init_time_vector__(data, output_data, false); +} + + + time_t_vector_type * ecl_sum_data_alloc_time_vector( const ecl_sum_data_type * data , bool report_only) { - time_t_vector_type * time_vector = time_t_vector_alloc(0,0); - ecl_sum_data_init_time_vector( data , time_vector , report_only); + std::vector output_data; + if (report_only) + output_data.resize( 1 + ecl_sum_data_get_last_report_step(data) - ecl_sum_data_get_first_report_step(data)); + else + output_data.resize( ecl_sum_data_get_length(data) ); + + ecl_sum_data_init_time_vector__(data, output_data.data(), report_only); + time_t_vector_type * time_vector = time_t_vector_alloc(output_data.size(),0); + { + time_t * tmp_data = time_t_vector_get_ptr( time_vector ); + memcpy(tmp_data, output_data.data(), output_data.size() * sizeof(time_t)); + } return time_vector; } -static void ecl_sum_data_init_data_vector( const ecl_sum_data_type * data , double_vector_type * data_vector , int data_index , bool report_only) { - double_vector_reset( data_vector ); - double_vector_append( data_vector , ecl_smspec_get_start_time( data->smspec )); - if (report_only) { - int report_step; - for (report_step = data->first_report_step; report_step <= data->last_report_step; report_step++) { - int last_index = int_vector_iget(data->report_last_index , report_step); - const ecl_sum_tstep_type * ministep = ecl_sum_data_iget_ministep( data , last_index ); - double_vector_append( data_vector , ecl_sum_tstep_iget( ministep , data_index )); - } - } else { - int i; - for (i = 0; i < vector_get_size(data->data); i++) { - const ecl_sum_tstep_type * ministep = ecl_sum_data_iget_ministep( data , i ); - double_vector_append( data_vector , ecl_sum_tstep_iget( ministep , data_index )); + + +static void ecl_sum_data_init_double_vector__(const ecl_sum_data_type * data, int main_params_index, double * output_data, bool report_only) { + int offset = 0; + for (const auto& index_node : data->index) { + const auto& data_file = data->data_files[index_node.data_index]; + const auto& params_map = index_node.params_map; + int params_index = params_map[main_params_index]; + + + if (report_only) + offset += data_file->get_data_report(params_index, index_node.length, &output_data[offset]); + else { + + if (params_index >= 0) + data_file->get_data(params_index, index_node.length, &output_data[offset]); + else { + for (int i=0; i < index_node.length; i++) + output_data[offset + i] = 0; + } + offset += index_node.length; } } } -double_vector_type * ecl_sum_data_alloc_data_vector( const ecl_sum_data_type * data , int data_index , bool report_only) { - double_vector_type * data_vector = double_vector_alloc(0,0); - ecl_sum_data_init_data_vector( data , data_vector , data_index , report_only); - return data_vector; +void ecl_sum_data_init_datetime64_vector(const ecl_sum_data_type * data, int64_t * output_data, int multiplier) { + int i; + for (i = 0; i < ecl_sum_data_get_length(data); i++) { + output_data[i] = ecl_sum_data_iget_sim_time(data, i) * multiplier; + } } + void ecl_sum_data_init_double_vector(const ecl_sum_data_type * data, int params_index, double * output_data) { - int i; - for (i = 0; i < vector_get_size(data->data); i++) { - const ecl_sum_tstep_type * ministep = ecl_sum_data_iget_ministep( data , i ); - output_data[i] = ecl_sum_tstep_iget(ministep, params_index); - } + ecl_sum_data_init_double_vector__(data, params_index, output_data, false); } -void ecl_sum_data_init_datetime64_vector(const ecl_sum_data_type * data, int64_t * output_data, int multiplier) { - int i; - for (i = 0; i < vector_get_size(data->data); i++) { - const ecl_sum_tstep_type * ministep = ecl_sum_data_iget_ministep( data , i ); - output_data[i] = ecl_sum_tstep_get_sim_time(ministep) * multiplier; + +double_vector_type * ecl_sum_data_alloc_data_vector( const ecl_sum_data_type * data , int params_index , bool report_only) { + std::vector output_data; + if (report_only) + output_data.resize( 1 + ecl_sum_data_get_last_report_step(data) - ecl_sum_data_get_first_report_step(data)); + else + output_data.resize( ecl_sum_data_get_length(data) ); + + ecl_sum_data_init_double_vector__(data, params_index, output_data.data(), report_only); + double_vector_type * data_vector = double_vector_alloc(output_data.size(), 0); + { + double * tmp_data = double_vector_get_ptr( data_vector ); + memcpy(tmp_data, output_data.data(), output_data.size() * sizeof(double)); } + return data_vector; } + void ecl_sum_data_init_double_vector_interp(const ecl_sum_data_type * data, const smspec_node_type * smspec_node, const time_t_vector_type * time_points, @@ -1477,13 +1156,12 @@ void ecl_sum_data_init_double_vector_interp(const ecl_sum_data_type * data, void ecl_sum_data_init_double_frame(const ecl_sum_data_type * data, const ecl_sum_vector_type * keywords, double *output_data) { int time_stride = ecl_sum_vector_get_size(keywords); int key_stride = 1; - for (int time_index=0; time_index < vector_get_size(data->data); time_index++) { - const ecl_sum_tstep_type * ministep = ecl_sum_data_iget_ministep(data , time_index); + for (int time_index=0; time_index < ecl_sum_data_get_length(data); time_index++) { for (int key_index = 0; key_index < ecl_sum_vector_get_size(keywords); key_index++) { int param_index = ecl_sum_vector_iget_param_index(keywords, key_index); int data_index = key_index*key_stride + time_index * time_stride; - output_data[data_index] = ecl_sum_tstep_iget(ministep, param_index); + output_data[data_index] = ecl_sum_data_iget(data, time_index, param_index); } } } @@ -1546,11 +1224,11 @@ void ecl_sum_data_init_double_frame_interp(const ecl_sum_data_type * data, */ int ecl_sum_data_get_length( const ecl_sum_data_type * data ) { - return vector_get_size( data->data ); + return data->index.length(); } void ecl_sum_data_scale_vector(ecl_sum_data_type * data, int index, double scalar) { - int len = vector_get_size(data->data); + int len = ecl_sum_data_get_length(data); for (int i = 0; i < len; i++) { ecl_sum_tstep_type * ministep = ecl_sum_data_iget_ministep(data,i); ecl_sum_tstep_iscale(ministep, index, scalar); @@ -1558,66 +1236,40 @@ void ecl_sum_data_scale_vector(ecl_sum_data_type * data, int index, double scala } void ecl_sum_data_shift_vector(ecl_sum_data_type * data, int index, double addend) { - int len = vector_get_size(data->data); + int len = ecl_sum_data_get_length(data); for (int i = 0; i < len; i++) { ecl_sum_tstep_type * ministep = ecl_sum_data_iget_ministep(data,i); ecl_sum_tstep_ishift(ministep, index, addend); } } -bool ecl_sum_data_report_step_equal( const ecl_sum_data_type * data1 , const ecl_sum_data_type * data2) { - bool equal = true; - if (int_vector_size( data1->report_last_index ) == int_vector_size(data2->report_last_index)) { - int i; - for (i = 0; i < int_vector_size( data1->report_last_index ); i++) { - int time_index1 = int_vector_iget( data1->report_last_index , i ); - int time_index2 = int_vector_iget( data2->report_last_index , i ); - - if ((time_index1 != INVALID_MINISTEP_NR) && (time_index2 != INVALID_MINISTEP_NR)) { - const ecl_sum_tstep_type * ministep1 = ecl_sum_data_iget_ministep( data1 , time_index1 ); - const ecl_sum_tstep_type * ministep2 = ecl_sum_data_iget_ministep( data2 , time_index2 ); - - if (!ecl_sum_tstep_sim_time_equal( ministep1 , ministep2)) { - equal = false; - break; - } - } else if (time_index1 != time_index2) { - equal = false; - break; - } - } - } else - equal = false; +static bool ecl_sum_data_report_step_equal__( const ecl_sum_data_type * data1 , const ecl_sum_data_type * data2, bool strict) { + if (data1->data_files.size() != data2->data_files.size()) + return false; + + for (size_t i=0; i < data1->data_files.size(); i++) { + const auto& data_file1 = data1->data_files[i]; + const auto& data_file2 = data2->data_files[i]; + + if (!data_file1->report_step_equal(*data_file2, strict)) + return false; + } - return equal; + return true; } bool ecl_sum_data_report_step_compatible( const ecl_sum_data_type * data1 , const ecl_sum_data_type * data2) { - bool compatible = true; - int min_size = util_int_min( int_vector_size( data1->report_last_index ) , int_vector_size( data2->report_last_index)); - int i; - for (i = 0; i < min_size; i++) { - int time_index1 = int_vector_iget( data1->report_last_index , i ); - int time_index2 = int_vector_iget( data2->report_last_index , i ); - - if ((time_index1 != INVALID_MINISTEP_NR) && (time_index2 != INVALID_MINISTEP_NR)) { - const ecl_sum_tstep_type * ministep1 = ecl_sum_data_iget_ministep( data1 , time_index1 ); - const ecl_sum_tstep_type * ministep2 = ecl_sum_data_iget_ministep( data2 , time_index2 ); + return ecl_sum_data_report_step_equal__(data1, data2, false); +} - if (!ecl_sum_tstep_sim_time_equal( ministep1 , ministep2)) { - compatible = false; - break; - } - } - } - return compatible; +bool ecl_sum_data_report_step_equal( const ecl_sum_data_type * data1 , const ecl_sum_data_type * data2) { + return ecl_sum_data_report_step_equal__(data1, data2, true); } double ecl_sum_data_iget_last_value(const ecl_sum_data_type * data, int param_index) { - const ecl_sum_tstep_type * tstep = (const ecl_sum_tstep_type*)vector_get_last_const(data->data); - return ecl_sum_tstep_iget( tstep, param_index); + return ecl_sum_data_iget(data, ecl_sum_data_get_length(data)-1, param_index); } double ecl_sum_data_get_last_value(const ecl_sum_data_type * data, int param_index) { @@ -1625,6 +1277,5 @@ double ecl_sum_data_get_last_value(const ecl_sum_data_type * data, int param_ind } double ecl_sum_data_iget_first_value(const ecl_sum_data_type * data, int param_index) { - const ecl_sum_tstep_type * tstep = (const ecl_sum_tstep_type*) vector_iget_const(data->data, 0); - return ecl_sum_tstep_iget(tstep, param_index); + return ecl_sum_data_iget(data, 0, param_index); } diff --git a/lib/ecl/ecl_sum_file_data.cpp b/lib/ecl/ecl_sum_file_data.cpp new file mode 100644 index 0000000000..afe9b24213 --- /dev/null +++ b/lib/ecl/ecl_sum_file_data.cpp @@ -0,0 +1,721 @@ +#include +#include +#include + +#include +#include +#include +#include + +#include "detail/ecl/ecl_sum_file_data.hpp" +/* + This file implements the type ecl_sum_data_type. The data structure + is involved with holding all the actual summary data (i.e. the + PARAMS vectors in ECLIPSE speak), in addition the time-information + with MINISTEPS / REPORT_STEPS and so on is implemented here. + + This file has no information about how to index into the PARAMS + vector, i.e. at which location can the WWCT for well P6 be found, + that is responsability of the ecl_smspec_type. + + The time direction in this system is implemented in terms of + ministeps. There are some query / convert functons based on report + steps. +*/ + + +/*****************************************************************/ +/* + About ministeps and report steps. + --------------------------------- + + A sequence of summary data will typically look like this: + + ------------------ + SEQHDR \ + MINISTEP 0 | + PARAMS ..... | + MINISTEP 1 |==> This is REPORT STEP 1, in file BASE.S00001 + PARAMS ..... | + MINISTEP 2 | + PARAMS ..... / + ------------------ + SEQHDR \ + MINISTEP 3 | + PARAMS ..... | + MINISTEP 4 | + PARAMS ..... | + MINISTEP 5 |==> This is REPORT STEP 2, in file BASE.S0002 + PARAMS ..... | + MINISTEP 6 | + PARAMS ..... | + SEQHDR | + MINISTEP 7 | + PARAMS ..... / + ------------------ + + + Observe the following: + + * The MINISTEP counter runs continously, and does not + differentiate between unified files and not unified files. + + * When using multiple files we can read off the report number + from the filename, for unified files this is IMPOSSIBLE, and we + just have to assume that the first block corresponds to + report_step 1 and then count afterwards. + + * When asking for a summary variable at a particular REPORT STEP + (as we do in enkf) it is ambigous as to which ministep within + the block one should use. The convention we have employed + (which corresponds to the old RPTONLY based behaviour) is to + use the last ministep in the block. + + * There is no BASE.SOOOO file + + * The report steps are halfopen intervals in the "wrong way": + (....] + + + + + About MINISTEP, REPORTSTEP, rates and continous sim_time/sim_days: + ------------------------------------------------------------------ + + For ECLIPSE summary files the smallest unit of time resolution is + called the ministep - a ministep corresponds to a time step in the + underlying partial differential equation, i.e. the length of the + timesteps is controlled by the simulator itself - there is no finer + temporal resolution. + + The user has told the simulator to store (i.e. save to file + results) the results at reportsteps. A reportstep will typically + consist of several ministeps. The timeline below shows a simulation + consisting of two reportsteps: + + + S0001 S0002 + ||------|------|------------|------------------||----------------------|----------------------|| + M1 M2 M3 M4 M5 M6 + + The first reportstep consist of four ministeps, the second + reportstep consits of only two ministeps. As a user you have no + control over the length/number of ministeps apart from: + + 1. Indirectly through the TUNING keywords. + 2. A ministep will always end at a report step. + + + RPTONLY: In conjunction with enkf it has been customary to use the + keyword RPTONLY. This is purely a storage directive, the effect is + that only the ministep ending at the REPORT step is reported, + i.e. in the case above we would get the ministeps [M4 , M6], where + the ministeps M4 and M6 will be unchanged, and there will be many + 'holes' in the timeline. + + About truetime: The ministeps have a finite length; this implies + that + + [rates]: The ministep value is NOT actually an instantaneous + value, it is the total production during the ministepd period + - divided by the length of the ministep. I.e. it is an average + value. (I.e. the differential time element dt is actually quite + looong). + + [state]: For state variables (this will include total production + of various phases), the ministep value corresponds to the + reservoir state at THE END OF THE MINISTEP. + + This difference between state variables and rates implies a + difference in how continous time-variables (in the middle of a + ministep) are reported, i.e. + + + S0000 S0001 + ||--------------|---------------|------------X-------------|| + M1 M2 /|\ M3 + | + | + + We have enteeed the sim_days/sim_time cooresponding to the location + of 'X' on the timeline, i.e. in the middle of ministep M3. If we + are interested in the rate at this time the function: + + ecl_sum_data_get_from_sim_time() + + will just return the M3 value, whereas if you are interested in + e.g. pressure at this time the function will return a weighted + average of the M2 and M3 values. Whether a variable in question is + interpreted as a 'rate' is effectively determined by the + ecl_smspec_set_rate_var() function in ecl_smspec.c. + + + + Indexing and _get() versus _iget() + ---------------------------------- + As already mentionded the set of ministeps is not necessarrily a + continous series, we can easily have a series of ministeps with + "holes" in it, and the series can also start on a non-zero + value. Internally all the ministeps are stored in a dense, zero + offset vector instance; and we must be able to translate back and + forth between ministep_nr and internal index. + + Partly due to EnKF heritage the MINISTEP nr has been the main + method to access the time dimension of the data, i.e. all the + functions like ecl_sum_get_general_var() expect the time direction + to be given as a ministep; however it is also possible to get the + data by giving an internal (not that internal ...) index. In + ecl_sum_data.c the latter functions have _iget(): + + + ecl_sum_data_get_xxx : Expects the time direction given as a ministep_nr. + ecl_sum_data_iget_xxx: Expects the time direction given as an internal index. + +*/ + + +namespace ecl { + + +ecl_sum_file_data::ecl_sum_file_data(const ecl_smspec_type * smspec) : + ecl_smspec( smspec ) +{ + data = vector_alloc_new(); + clear_index( ); + +} + +ecl_sum_file_data::~ecl_sum_file_data() { + vector_free( data ); +} + + +int ecl_sum_file_data::get_length() const { + return vector_get_size( data ); +} + + +int ecl_sum_file_data::length_before(time_t end_time) const { + int offset = 0; + while (true) { + time_t itime = this->iget_sim_time(offset); + if (itime >= end_time) + return offset; + + offset += 1; + if (offset == this->get_length()) + return offset; + } +} + + +int ecl_sum_file_data::report_before(time_t end_time) const { + if (end_time < this->first_report()) + throw std::invalid_argument("time argument before first report step"); + + int r = this->first_report(); + int last_report = this->last_report(); + while (true) { + if (r == last_report) + return last_report; + + auto next_range = this->report_map[r + 1]; + if (this->iget_sim_time(next_range.first) > end_time) + return r; + + r += 1; + } +} + + +int ecl_sum_file_data::first_report() const{ + return first_report_step; +} + +int ecl_sum_file_data::last_report() const { + return last_report_step; +} + + + +time_t ecl_sum_file_data::get_data_start() const { + return this->time_range.first; +} + + +time_t ecl_sum_file_data::get_sim_end() const { + return this->time_range.second; +} + +time_t ecl_sum_file_data::iget_sim_time(int time_index) const { + const ecl_sum_tstep_type * ministep = iget_ministep( time_index ); + return ecl_sum_tstep_get_sim_time(ministep); +} + + +double ecl_sum_file_data::get_sim_length() const { + return this->sim_length; +} + +double ecl_sum_file_data::iget( int time_index , int params_index ) const { + if (params_index >= 0) { + const ecl_sum_tstep_type * ministep_data = iget_ministep( time_index ); + return ecl_sum_tstep_iget( ministep_data , params_index); + } + else + return 0; +} + + + +void ecl_sum_file_data::clear_index() { + this->report_map.clear(); + first_report_step = 1024 * 1024; + last_report_step = -1024 * 1024; + days_start = 0; + sim_length = -1; + index_valid = false; + time_range = std::make_pair(INVALID_TIME_T, INVALID_TIME_T); +} + + +void ecl_sum_file_data::append_tstep(ecl_sum_tstep_type * tstep) { + /* + Here the tstep is just appended naively, the vector will be + sorted by ministep_nr before the data instance is returned. + */ + + vector_append_owned_ref( data , tstep , ecl_sum_tstep_free__); + index_valid = false; +} + + +/* + This function is meant to be called in write mode; and will create a + new and empty tstep which is appended to the current data. The tstep + will also be returned, so the calling scope can call + ecl_sum_tstep_iset() to set elements in the tstep. +*/ + +ecl_sum_tstep_type * ecl_sum_file_data::add_new_tstep( int report_step , double sim_seconds) { + int ministep_nr = vector_get_size( data ); + ecl_sum_tstep_type * tstep = ecl_sum_tstep_alloc_new( report_step , ministep_nr , sim_seconds , ecl_smspec ); + ecl_sum_tstep_type * prev_tstep = NULL; + + if (vector_get_size( data ) > 0) + prev_tstep = (ecl_sum_tstep_type*)vector_get_last( data ); + + append_tstep( tstep ); + + + bool rebuild_index = true; + /* + In the simple case that we just add another timestep to the + currently active report_step, we do a limited update of the + index, otherwise we call ecl_sum_data_build_index() to get a + full recalculation of the index. + */ + if (!prev_tstep) + goto exit; + + if (ecl_sum_tstep_get_report(prev_tstep) != ecl_sum_tstep_get_report( tstep )) + goto exit; + + if (ecl_sum_tstep_get_sim_days( prev_tstep ) >= ecl_sum_tstep_get_sim_days( tstep )) + goto exit; + + { + int internal_index = vector_get_size( data ) - 1; + this->report_map[report_step].second = internal_index; + this->sim_length = ecl_sum_tstep_get_sim_days( tstep ); + this->time_range.second = ecl_sum_tstep_get_sim_time(tstep); + rebuild_index = false; + } + +exit: + if (rebuild_index) + build_index(); + + return tstep; +} + + +ecl_sum_tstep_type * ecl_sum_file_data::iget_ministep( int internal_index ) const { + return (ecl_sum_tstep_type*)vector_iget( data , internal_index ); +} + + +static int cmp_ministep( const void * arg1 , const void * arg2) { + const ecl_sum_tstep_type * ministep1 = ecl_sum_tstep_safe_cast_const( arg1 ); + const ecl_sum_tstep_type * ministep2 = ecl_sum_tstep_safe_cast_const( arg2 ); + + time_t time1 = ecl_sum_tstep_get_sim_time( ministep1 ); + time_t time2 = ecl_sum_tstep_get_sim_time( ministep2 ); + + if (time1 < time2) + return -1; + else if (time1 == time2) + return 0; + else + return 1; +} + + + +void ecl_sum_file_data::build_index( ) { + /* Clear the existing index (if any): */ + clear_index(); + + /* + Sort the internal storage vector after sim_time. + */ + vector_sort( data , cmp_ministep ); + + + /* Identify various global first and last values. */ + { + const ecl_sum_tstep_type * first_ministep = (const ecl_sum_tstep_type*)vector_iget_const( data, 0 ); + const ecl_sum_tstep_type * last_ministep = (const ecl_sum_tstep_type*)vector_get_last_const( data ); + /* + In most cases the days_start and data_start_time will agree + with the global simulation start; however in the case where we + have loaded a summary case from a restarted simulation where + the case we have restarted from is not available - then there + will be a difference. + */ + days_start = ecl_sum_tstep_get_sim_days( first_ministep ); + sim_length = ecl_sum_tstep_get_sim_days( last_ministep ); + this->time_range = std::make_pair(ecl_sum_tstep_get_sim_time( first_ministep ), + ecl_sum_tstep_get_sim_time( last_ministep )); + } + + /* Build up the report -> ministep mapping. */ + for (int internal_index = 0; internal_index < vector_get_size( data ); internal_index++) { + const ecl_sum_tstep_type * ministep = iget_ministep( internal_index ); + size_t report_step = ecl_sum_tstep_get_report(ministep); + /* Indexing internal_index - report_step */ + if (this->report_map.size() <= report_step) + this->report_map.resize( report_step + 1, std::pair(std::numeric_limits::max(), -1)); + + auto& range = this->report_map[report_step]; + range.first = std::min(range.first, internal_index); + range.second = std::max(range.second, internal_index); + + first_report_step = util_int_min( first_report_step , report_step ); + last_report_step = util_int_max( last_report_step , report_step ); + } + index_valid = true; +} + +void ecl_sum_file_data::get_time(int length, time_t * data) { + for (int time_index=0; time_index < length; time_index++) + data[time_index] = this->iget_sim_time(time_index); +} + + +int ecl_sum_file_data::get_time_report(int end_index, time_t *data) { + int offset = 0; + + for (int report_step = this->first_report_step; report_step <= this->last_report_step; report_step++) { + const auto& range = this->report_map[report_step]; + int time_index = range.second; + if (time_index >= end_index) + break; + + data[offset] = this->iget_sim_time(time_index); + + offset += 1; + } + return offset; +} + + + +void ecl_sum_file_data::get_data(int params_index, int length, double *data) { + for (int time_index=0; time_index < length; time_index++) + data[time_index] = this->iget(time_index, params_index); +} + + +int ecl_sum_file_data::get_data_report(int params_index, int end_index, double *data) { + int offset = 0; + + for (int report_step = this->first_report_step; report_step <= this->last_report_step; report_step++) { + int time_index = this->report_map[report_step].second; + if (time_index >= end_index) + break; + + if (params_index >= 0) + data[offset] = this->iget(time_index, params_index); + else + data[offset] = 0; + + offset += 1; + } + return offset; +} + + + +bool ecl_sum_file_data::has_report(int report_step ) const { + if (report_step >= static_cast(this->report_map.size())) + return false; + + const auto& range_pair = this->report_map[report_step]; + if (range_pair.second < 0) + return false; + + return true; +} + + +// ******************** Start writing *************************************************** + + +std::pair ecl_sum_file_data::report_range(int report_step) const { + return this->report_map[report_step]; +} + + +void ecl_sum_file_data::fwrite_report( int report_step , fortio_type * fortio) const { + { + ecl_kw_type * seqhdr_kw = ecl_kw_alloc( SEQHDR_KW , SEQHDR_SIZE , ECL_INT ); + ecl_kw_iset_int( seqhdr_kw , 0 , 0 ); + ecl_kw_fwrite( seqhdr_kw , fortio ); + ecl_kw_free( seqhdr_kw ); + } + + { + auto range = this->report_range( report_step ); + for (int index = range.first; index <= range.second; index++) { + const ecl_sum_tstep_type * tstep = iget_ministep( index ); + ecl_sum_tstep_fwrite( tstep , ecl_smspec_get_index_map( ecl_smspec ) , fortio ); + } + } +} + + +void ecl_sum_file_data::fwrite_unified( fortio_type * fortio ) const { + + for (int report_step = first_report_step; report_step <= last_report_step; report_step++) { + if (has_report( report_step )) + fwrite_report( report_step , fortio ); + } +} + + +void ecl_sum_file_data::fwrite_multiple( const char * ecl_case , bool fmt_case ) const { + int report_step; + + for (report_step = first_report_step; report_step <= last_report_step; report_step++) { + if (this->has_report( report_step )) { + char * filename = ecl_util_alloc_filename( NULL , ecl_case , ECL_SUMMARY_FILE , fmt_case , report_step ); + fortio_type * fortio = fortio_open_writer( filename , fmt_case , ECL_ENDIAN_FLIP ); + + fwrite_report( report_step , fortio ); + + fortio_fclose( fortio ); + free( filename ); + } + } + +} + +double ecl_sum_file_data::get_days_start() const { + return this->days_start; +} + + +// ***************************** End writing ************************************* + +// **************************** Start Reading ************************************ + +bool ecl_sum_file_data::check_file( ecl_file_type * ecl_file ) { + return ecl_file_has_kw( ecl_file , PARAMS_KW ) && + (ecl_file_get_num_named_kw( ecl_file , PARAMS_KW ) == ecl_file_get_num_named_kw( ecl_file , MINISTEP_KW)); +} + +/** + Malformed/incomplete files: + ---------------------------- + Observe that ECLIPSE works in the following way: + + 1. At the start of a report step a summary data section + containing only the 'SEQHDR' keyword is written - this is + currently an 'invalid' summary section. + + 2. ECLIPSE simulates as best it can. + + 3. When the time step is complete data is written to the summary + file. + + Now - if ECLIPSE goes down in flames during step 2 a malformed + summary file will be left around, to handle this situation + reasonably gracefully we check that the ecl_file instance has at + least one "PARAMS" keyword. + + One ecl_file corresponds to one report_step (limited by SEQHDR); in + the case of non unfied summary files these objects correspond to + one BASE.Annnn or BASE.Snnnn file, in the case of unified files the + calling routine will read the unified summary file partly. +*/ + +void ecl_sum_file_data::add_ecl_file(int report_step, const ecl_file_view_type * summary_view, const ecl_smspec_type * smspec) { + + int num_ministep = ecl_file_view_get_num_named_kw( summary_view , PARAMS_KW); + if (num_ministep > 0) { + int ikw; + + for (ikw = 0; ikw < num_ministep; ikw++) { + ecl_kw_type * ministep_kw = ecl_file_view_iget_named_kw( summary_view , MINISTEP_KW , ikw); + ecl_kw_type * params_kw = ecl_file_view_iget_named_kw( summary_view , PARAMS_KW , ikw); + + { + int ministep_nr = ecl_kw_iget_int( ministep_kw , 0 ); + ecl_sum_tstep_type * tstep = ecl_sum_tstep_alloc_from_file( report_step , + ministep_nr , + params_kw , + ecl_file_view_get_src_file( summary_view ), + smspec ); + + if (tstep) + append_tstep( tstep ); + } + } + } +} + + +bool ecl_sum_file_data::fread(const stringlist_type * filelist) { + if (stringlist_get_size( filelist ) == 0) + return false; + + ecl_file_enum file_type = ecl_util_get_file_type( stringlist_iget( filelist , 0 ) , NULL , NULL); + if ((stringlist_get_size( filelist ) > 1) && (file_type != ECL_SUMMARY_FILE)) + util_abort("%s: internal error - when calling with more than one file - you can not supply a unified file - come on?! \n",__func__); + + if (file_type == ECL_SUMMARY_FILE) { + + /* Not unified. */ + for (int filenr = 0; filenr < stringlist_get_size( filelist ); filenr++) { + const char * data_file = stringlist_iget( filelist , filenr); + ecl_file_enum file_type; + int report_step; + file_type = ecl_util_get_file_type( data_file , NULL , &report_step); + if (file_type != ECL_SUMMARY_FILE) + util_abort("%s: file:%s has wrong type \n",__func__ , data_file); + { + ecl_file_type * ecl_file = ecl_file_open( data_file , 0); + if (ecl_file && check_file( ecl_file )) { + add_ecl_file( report_step , ecl_file_get_global_view( ecl_file ) , ecl_smspec); + ecl_file_close( ecl_file ); + } + } + } + } else if (file_type == ECL_UNIFIED_SUMMARY_FILE) { + ecl_file_type * ecl_file = ecl_file_open( stringlist_iget(filelist ,0 ) , 0); + if (ecl_file && check_file( ecl_file )) { + int report_step = 1; /* <- ECLIPSE numbering - starting at 1. */ + while (true) { + /* + Observe that there is a number discrepancy between ECLIPSE + and the ecl_file_select_smryblock() function. ECLIPSE + starts counting report steps at 1; whereas the first + SEQHDR block in the unified summary file is block zero (in + ert counting). + */ + ecl_file_view_type * summary_view = ecl_file_get_summary_view(ecl_file , report_step - 1 ); + if (summary_view) { + add_ecl_file(report_step , summary_view , ecl_smspec); + report_step++; + } else break; + } + ecl_file_close( ecl_file ); + } + } + + if (get_length() > 0) { + build_index(); + return true; + } else + return false; + +} + +const ecl_smspec_type * ecl_sum_file_data::smspec() const { + return this->ecl_smspec; +} + + +bool ecl_sum_file_data::report_step_equal( const ecl_sum_file_data& other, bool strict) const { + if (strict && this->first_report_step != other.first_report_step) + return false; + + if (strict && (this->last_report_step != other.last_report_step)) + return false; + + int report_step = std::max(this->first_report_step, other.first_report_step); + int last_report = std::min(this->last_report_step, other.last_report_step); + while (true) { + int time_index1 = this->report_map[report_step].second; + int time_index2 = other.report_map[report_step].second; + + if ((time_index1 != INVALID_MINISTEP_NR) && (time_index2 != INVALID_MINISTEP_NR)) { + const auto& ministep1 = this->iget_ministep( time_index1 ); + const auto& ministep2 = other.iget_ministep( time_index2 ); + + if (!ecl_sum_tstep_sim_time_equal( ministep1 , ministep2)) + return false; + + } else if (time_index1 != time_index2) { + if (strict) + return false; + } + + report_step++; + if (report_step > last_report) + break; + } + return true; +} + + +// ***************************** End reading ************************************* + +int ecl_sum_file_data::report_step_from_days(double sim_days) const { + int report_step = this->first_report_step; + while (true) { + const auto& range = this->report_map[report_step]; + if (range.second >= 0) { + const ecl_sum_tstep_type * tstep = this->iget_ministep(range.second); + + // Warning - this is a double == comparison! + if (sim_days == ecl_sum_tstep_get_sim_days(tstep)) + return report_step; + + report_step++; + if (report_step > this->last_report_step) + return -1; + } + } +} + + int ecl_sum_file_data::report_step_from_time(time_t sim_time) const { + int report_step = this->first_report_step; + while (true) { + const auto& range = this->report_map[report_step]; + if (range.second >= 0) { + const ecl_sum_tstep_type * tstep = this->iget_ministep(range.second); + if (sim_time == ecl_sum_tstep_get_sim_time(tstep)) + return report_step; + + report_step++; + if (report_step > this->last_report_step) + return -1; + } + } +} + +} //end namespace + + diff --git a/lib/ecl/smspec_node.cpp b/lib/ecl/smspec_node.cpp index e566eb66d2..7b9856069c 100644 --- a/lib/ecl/smspec_node.cpp +++ b/lib/ecl/smspec_node.cpp @@ -572,17 +572,14 @@ static void smspec_node_common_init( smspec_node_type * node , ecl_smspec_var_ty } -/* - This *should* become static. -*/ -void smspec_node_init( smspec_node_type * smspec_node, - ecl_smspec_var_type var_type , - const char * wgname , - const char * keyword , - const char * unit , - const char * key_join_string , - const int grid_dims[3] , - int num) { +static bool smspec_node_init__( smspec_node_type * smspec_node, + ecl_smspec_var_type var_type , + const char * wgname , + const char * keyword , + const char * unit , + const char * key_join_string , + const int grid_dims[3] , + int num) { bool initOK = true; @@ -653,6 +650,31 @@ void smspec_node_init( smspec_node_type * smspec_node, if (initOK) smspec_node_set_gen_keys( smspec_node , key_join_string ); + + return initOK; +} + +/* + This function should be removed from the public API. +*/ +void smspec_node_init( smspec_node_type * smspec_node, + ecl_smspec_var_type var_type , + const char * wgname , + const char * keyword , + const char * unit , + const char * key_join_string , + const int grid_dims[3] , + int num) { + + smspec_node_init__(smspec_node, + var_type, + wgname, + keyword, + unit, + key_join_string, + grid_dims, + num); + } /** @@ -714,8 +736,16 @@ smspec_node_type * smspec_node_alloc( ecl_smspec_var_type var_type , if (!smspec_node_type_supported(var_type)) return NULL; + /* + TODO: The alloc_new and init functions should be joined in one function. + */ smspec_node_type * smspec_node = smspec_node_alloc_new( param_index , default_value ); - smspec_node_init( smspec_node , var_type , wgname , keyword , unit , key_join_string , grid_dims, num); + bool initOK = smspec_node_init__( smspec_node , var_type , wgname , keyword , unit , key_join_string , grid_dims, num); + if (!initOK) { + smspec_node_free(smspec_node); + smspec_node = NULL; + } + return smspec_node; } diff --git a/lib/ecl/tests/ecl_sum_data_intermediate_test.cpp b/lib/ecl/tests/ecl_sum_data_intermediate_test.cpp new file mode 100644 index 0000000000..b4cf02e1b4 --- /dev/null +++ b/lib/ecl/tests/ecl_sum_data_intermediate_test.cpp @@ -0,0 +1,229 @@ +#include + +#include +#include +#include +#include + +#include + + +/* + +CASE1 +--------- +1 : BPR:1 100 200 300 400 +2 : BPR:2 110 210 310 410 +3 : BRP:3 120 220 320 430 + + + +CASE2 +--------- +1 : BRP:2 1100 2100 3100 4100 +2 : BPR:1 1000 2000 3000 4000 + + + +Total CASE2: +------------ +1 : BPR:2 110 210 310 1100 2100 3100 4100 +2 : BRP:1 100 200 300 1000 2000 3000 4000 + + + +CASE3 +----- +1 : BPR:3 12000 22000 32000 42000 +2 : BRP:2 11000 21000 31000 41000 +3 : BPR:1 10000 20000 30000 40000 + + +Total CASE3: +------------ +1 : BPR:3 120 220 320 0 0 12000 22000 32000 42000 +2 : BPR:2 110 210 310 1100 2100 11000 21000 31000 41000 +3 : BPR:1 100 200 300 1000 2000 10000 20000 30000 40000 + +*/ + + +#define ieq(d,i,v) test_assert_double_equal(double_vector_iget(d,(i)), v) + + +void verify_CASE1() { + ecl_sum_type * sum = ecl_sum_fread_alloc_case("CASE1", ":"); + + for (int i=1; i < 4; i++) { + double_vector_type * d = ecl_sum_alloc_data_vector(sum, i, false); + //test_assert_int_equal(4, double_vector_size(d)); + for (int j=0; j < 4; j++) { + test_assert_double_equal( double_vector_iget(d, j), (i - 1)*10 + (j + 1)*100); + } + } + + ecl_sum_free(sum); +} + + + +void write_CASE1(bool unified) { + time_t start_time = util_make_date_utc( 1,1,2010 ); + ecl_sum_type * ecl_sum = ecl_sum_alloc_writer( "CASE1" , false , unified, ":" , start_time , true , 10 , 10 , 10 ); + double sim_seconds = 0; + + int num_dates = 4; + double ministep_length = 86400; // seconds in a day + + smspec_node_type * node1 = ecl_sum_add_var( ecl_sum , "BPR" , NULL , 1 , "BARS" , 0.0 ); + smspec_node_type * node2 = ecl_sum_add_var( ecl_sum , "BPR" , NULL , 2 , "BARS" , 0.0 ); + smspec_node_type * node3 = ecl_sum_add_var( ecl_sum , "BPR" , NULL , 3 , "BARS" , 0.0 ); + + for (int report_step = 0; report_step < num_dates; report_step++) { + { + ecl_sum_tstep_type * tstep = ecl_sum_add_tstep( ecl_sum , report_step + 1 , sim_seconds ); + ecl_sum_tstep_set_from_node( tstep , node1 , (1 + report_step) * 100 ); + ecl_sum_tstep_set_from_node( tstep , node2 , (1 + report_step) * 100 + 10.0 ); + ecl_sum_tstep_set_from_node( tstep , node3 , (1 + report_step) * 100 + 20.0 ); + } + sim_seconds += ministep_length * 3; + } + ecl_sum_fwrite( ecl_sum ); + ecl_sum_free(ecl_sum); + + verify_CASE1(); +} + + + +void verify_CASE2() { + ecl_sum_type * sum = ecl_sum_fread_alloc_case__("CASE2", ":", false); + + for (int i=0; i < 2; i++) { + double_vector_type * d = ecl_sum_alloc_data_vector(sum, i+1, false); + //test_assert_int_equal(4, double_vector_size(d)); + for (int j=1; j < 4; j++) + test_assert_double_equal( double_vector_iget(d, j-1), (1 - i)*100 + j*1000); + } + + ecl_sum_free(sum); + + + sum = ecl_sum_fread_alloc_case("CASE2", ":"); + for (int i=0; i < 2; i++) { + double_vector_type * d = ecl_sum_alloc_data_vector(sum, i+1, false); + //test_assert_int_equal(double_vector_size(d), 7); + double_vector_fprintf(d, stdout, "d", "%g"); + ieq(d,0,(1 - i)*10 + 100); + ieq(d,1,(1 - i)*10 + 200); + ieq(d,2,(1 - i)*10 + 300); + ieq(d,3,(1 - i)*100 + 1000); + ieq(d,4,(1 - i)*100 + 2000); + ieq(d,5,(1 - i)*100 + 3000); + ieq(d,6,(1 - i)*100 + 4000); + double_vector_free(d); + } + + ecl_sum_free(sum); +} + + +void write_CASE2(bool unified) { + write_CASE1(unified); + { + time_t start_time = util_make_date_utc( 1,1,2010 ); + int num_dates = 4; + double ministep_length = 86400; // seconds in a day + double sim_seconds = ministep_length * 2.5 * 3; + ecl_sum_type * ecl_sum = ecl_sum_alloc_restart_writer( "CASE2" , "CASE1", false , true , ":" , start_time , true , 10 , 10 , 10 ); + + smspec_node_type * node2 = ecl_sum_add_var( ecl_sum , "BPR" , NULL , 2 , "BARS" , 0.0 ); + smspec_node_type * node1 = ecl_sum_add_var( ecl_sum , "BPR" , NULL , 1 , "BARS" , 0.0 ); + + for (int report_step = 1; report_step <= num_dates; report_step++) { + { + ecl_sum_tstep_type * tstep = ecl_sum_add_tstep( ecl_sum , report_step + 3 , sim_seconds ); + ecl_sum_tstep_set_from_node( tstep , node1 , report_step*1000); + ecl_sum_tstep_set_from_node( tstep , node2 , report_step*1000 + 100); + } + sim_seconds += ministep_length * 3; + } + ecl_sum_fwrite( ecl_sum ); + ecl_sum_free(ecl_sum); + verify_CASE2(); + } +} + +void verify_CASE3() { + ecl_sum_type * sum = ecl_sum_fread_alloc_case__("CASE3", ":", false); + + for (int i=0; i < 3; i++) { + double_vector_type * d = ecl_sum_alloc_data_vector(sum, i+1, false); + //test_assert_int_equal(4, double_vector_size(d)); + for (int j=0; j < 4; j++) { + test_assert_double_equal( double_vector_iget(d, j), (2 - i)*1000 + (j + 1)*10000); + } + } + ecl_sum_free(sum); + + sum = ecl_sum_fread_alloc_case("CASE3", ":"); + for (int i=0; i < 3; i++) { + double_vector_type * d = ecl_sum_alloc_data_vector(sum, i+1, false); + ieq(d,0,(2 - i)*10 + 100); + ieq(d,1,(2 - i)*10 + 200); + ieq(d,2,(2 - i)*10 + 300); + + if (i == 0) { + ieq(d,3,0); + ieq(d,4,0); + } else { + ieq(d,3,(2 - i)*100 + 1000); + ieq(d,4,(2 - i)*100 + 2000); + } + ieq(d,5,(2 - i)*1000 + 10000); + ieq(d,6,(2 - i)*1000 + 20000); + ieq(d,7,(2 - i)*1000 + 30000); + ieq(d,8,(2 - i)*1000 + 40000); + double_vector_free(d); + } + + ecl_sum_free(sum); +} + + +void write_CASE3(bool unified) { + test_work_area_type * work_area = test_work_area_alloc("SMSPEC"); + write_CASE2(unified); + { + time_t start_time = util_make_date_utc( 1,1,2010 ); + int num_dates = 4; + double ministep_length = 86400; // seconds in a day + double sim_seconds = ministep_length * 4.0 * 3; + ecl_sum_type * ecl_sum = ecl_sum_alloc_restart_writer( "CASE3" , "CASE2", false , true , ":" , start_time , true , 10 , 10 , 10 ); + + smspec_node_type * node3 = ecl_sum_add_var( ecl_sum , "BPR" , NULL , 3 , "BARS" , 0.0 ); + smspec_node_type * node2 = ecl_sum_add_var( ecl_sum , "BPR" , NULL , 2 , "BARS" , 0.0 ); + smspec_node_type * node1 = ecl_sum_add_var( ecl_sum , "BPR" , NULL , 1 , "BARS" , 0.0 ); + + for (int report_step = 1; report_step <= num_dates; report_step++) { + { + ecl_sum_tstep_type * tstep = ecl_sum_add_tstep( ecl_sum , report_step + 5 , sim_seconds ); + ecl_sum_tstep_set_from_node( tstep , node1 , report_step*10000); + ecl_sum_tstep_set_from_node( tstep , node2 , report_step*10000 + 1000); + ecl_sum_tstep_set_from_node( tstep , node3 , report_step*10000 + 2000); + } + sim_seconds += ministep_length * 3; + } + ecl_sum_fwrite( ecl_sum ); + ecl_sum_free(ecl_sum); + verify_CASE3(); + } + test_work_area_free(work_area); +} + + +int main() { + write_CASE3(true); + write_CASE3(false); + return 0; +} diff --git a/lib/ecl/tests/ecl_sum_report_step_compatible.cpp b/lib/ecl/tests/ecl_sum_report_step_compatible.cpp index 1129df234d..4cd4b1ece4 100644 --- a/lib/ecl/tests/ecl_sum_report_step_compatible.cpp +++ b/lib/ecl/tests/ecl_sum_report_step_compatible.cpp @@ -30,19 +30,19 @@ int main( int argc , char ** argv) { const char * case1 = argv[1]; const char * case2 = argv[2]; const char * compatible_string = argv[3]; - bool compatible; ecl_sum_type * ecl_sum1 = ecl_sum_fread_alloc_case( case1 , ":"); ecl_sum_type * ecl_sum2 = ecl_sum_fread_alloc_case( case2 , ":"); test_assert_true( ecl_sum_is_instance( ecl_sum1 )); test_assert_true( ecl_sum_is_instance( ecl_sum2 )); - test_assert_true( ecl_sum_report_step_compatible( ecl_sum1 , ecl_sum1) ); - test_assert_true( util_sscanf_bool( compatible_string , &compatible )); - test_assert_true( ecl_sum_report_step_compatible( ecl_sum1 , ecl_sum1) ); test_assert_true( ecl_sum_report_step_compatible( ecl_sum2 , ecl_sum2) ); - test_assert_bool_equal( compatible , ecl_sum_report_step_compatible( ecl_sum1 , ecl_sum2 )); + { + bool compatible; + test_assert_true( util_sscanf_bool( compatible_string , &compatible )); + test_assert_bool_equal( compatible , ecl_sum_report_step_compatible( ecl_sum1 , ecl_sum2 )); + } ecl_sum_free( ecl_sum1 ); ecl_sum_free( ecl_sum2 ); exit(0); diff --git a/lib/ecl/tests/well_lgr_load.cpp b/lib/ecl/tests/well_lgr_load.cpp index f510fa3998..4e060c1772 100644 --- a/lib/ecl/tests/well_lgr_load.cpp +++ b/lib/ecl/tests/well_lgr_load.cpp @@ -22,6 +22,7 @@ #include #include +#include #include #include @@ -54,6 +55,7 @@ int main( int argc , char ** argv) { for (int iwell = 0; iwell < well_info_get_num_wells( well_info ); iwell++) { well_ts_type * well_ts = well_info_get_ts( well_info , well_info_iget_well_name( well_info , iwell)); well_state_type * well_state = well_ts_get_last_state( well_ts ); + test_assert_not_NULL(well_state); } well_info_free( well_info ); } diff --git a/lib/include/ert/ecl/ecl_smspec.hpp b/lib/include/ert/ecl/ecl_smspec.hpp index 0a8662fe1c..c137b01093 100644 --- a/lib/include/ert/ecl/ecl_smspec.hpp +++ b/lib/include/ert/ecl/ecl_smspec.hpp @@ -142,7 +142,6 @@ typedef struct ecl_smspec_struct ecl_smspec_type; int ecl_smspec_get_params_size( const ecl_smspec_type * smspec ); int ecl_smspec_num_nodes( const ecl_smspec_type * smspec); const smspec_node_type * ecl_smspec_iget_node( const ecl_smspec_type * smspec , int index ); - void ecl_smspec_lock( ecl_smspec_type * smspec ); char * ecl_smspec_alloc_well_key( const ecl_smspec_type * smspec , const char * keyword , const char * wgname); diff --git a/lib/include/ert/ecl/ecl_sum.hpp b/lib/include/ert/ecl/ecl_sum.hpp index dd4d820555..137b2b5135 100644 --- a/lib/include/ert/ecl/ecl_sum.hpp +++ b/lib/include/ert/ecl/ecl_sum.hpp @@ -19,10 +19,6 @@ #ifndef ERT_ECL_SUM_H #define ERT_ECL_SUM_H -#ifdef __cplusplus -extern "C" { -#endif - #include #include #include @@ -35,6 +31,10 @@ extern "C" { #include #include +#ifdef __cplusplus +extern "C" { +#endif + typedef struct { char * locale; const char * sep; @@ -199,7 +199,6 @@ typedef struct ecl_sum_struct ecl_sum_type; bool ecl_sum_var_is_total( const ecl_sum_type * ecl_sum , const char * gen_key); int ecl_sum_iget_report_end( const ecl_sum_type * ecl_sum , int report_step ); - int ecl_sum_iget_report_start( const ecl_sum_type * ecl_sum , int report_step ); ecl_sum_type * ecl_sum_alloc_restart_writer2( const char * ecl_case, const char * restart_case, int restart_step, diff --git a/lib/include/ert/ecl/ecl_sum_data.hpp b/lib/include/ert/ecl/ecl_sum_data.hpp index 9eafd6304a..12925bdb4b 100644 --- a/lib/include/ert/ecl/ecl_sum_data.hpp +++ b/lib/include/ert/ecl/ecl_sum_data.hpp @@ -20,9 +20,6 @@ #define ERT_ECL_SUM_DATA_H -#ifdef __cplusplus -extern "C" { -#endif #include #include #include @@ -35,8 +32,13 @@ extern "C" { #include #include +#ifdef __cplusplus +extern "C" { +#endif + typedef struct ecl_sum_data_struct ecl_sum_data_type ; + void ecl_sum_data_reset_self_map( ecl_sum_data_type * data ); void ecl_sum_data_add_case(ecl_sum_data_type * self, const ecl_sum_data_type * other); void ecl_sum_data_fwrite_step( const ecl_sum_data_type * data , const char * ecl_case , bool fmt_case , bool unified, int report_step); void ecl_sum_data_fwrite( const ecl_sum_data_type * data , const char * ecl_case , bool fmt_case , bool unified); @@ -81,7 +83,6 @@ typedef struct ecl_sum_data_struct ecl_sum_data_type ; int ecl_sum_data_iget_report_step(const ecl_sum_data_type * data , int internal_index); int ecl_sum_data_iget_mini_step(const ecl_sum_data_type * data , int internal_index); int ecl_sum_data_iget_report_end( const ecl_sum_data_type * data , int report_step ); - int ecl_sum_data_iget_report_start( const ecl_sum_data_type * data , int report_step ); ecl_sum_tstep_type * ecl_sum_data_add_new_tstep( ecl_sum_data_type * data , int report_step , double sim_seconds); bool ecl_sum_data_report_step_equal( const ecl_sum_data_type * data1 , const ecl_sum_data_type * data2); bool ecl_sum_data_report_step_compatible( const ecl_sum_data_type * data1 , const ecl_sum_data_type * data2); diff --git a/lib/private-include/detail/ecl/ecl_sum_file_data.hpp b/lib/private-include/detail/ecl/ecl_sum_file_data.hpp new file mode 100644 index 0000000000..6974cc5270 --- /dev/null +++ b/lib/private-include/detail/ecl/ecl_sum_file_data.hpp @@ -0,0 +1,73 @@ +#include + +#include + +#include +#include +#include + +namespace ecl { + +#define INVALID_MINISTEP_NR -1 +#define INVALID_TIME_T 0 + +class ecl_sum_file_data { + +public: + ecl_sum_file_data(const ecl_smspec_type * smspec); + ~ecl_sum_file_data(); + const ecl_smspec_type * smspec() const; + + int length_before(time_t end_time) const; + void get_time(int length, time_t *data); + void get_data(int params_index, int length, double *data); + int get_length() const; + time_t get_data_start() const; + time_t get_sim_end() const; + double iget( int time_index , int params_index ) const; + time_t iget_sim_time(int time_index ) const; + ecl_sum_tstep_type * iget_ministep( int internal_index ) const; + double get_days_start() const; + double get_sim_length() const; + + std::pair report_range(int report_step) const; + bool report_step_equal( const ecl_sum_file_data& other, bool strict) const; + int report_before(time_t end_time) const; + int get_time_report(int max_internal_index, time_t *data); + int get_data_report(int params_index, int max_internal_index, double *data); + int first_report() const; + int last_report() const; + bool has_report(int report_step ) const; + int report_step_from_days(double sim_days) const; + int report_step_from_time(time_t sim_time) const; + + ecl_sum_tstep_type * add_new_tstep(int report_step , double sim_seconds); + void fwrite_unified( fortio_type * fortio ) const; + void fwrite_multiple( const char * ecl_case , bool fmt_case ) const; + bool fread(const stringlist_type * filelist); + +private: + const ecl_smspec_type * ecl_smspec; + double days_start; + double sim_length; + int first_report_step; + int last_report_step; + + std::vector> report_map; // This will map from a report step to first and last internal index. + std::pair time_range; + vector_type * data; + bool index_valid; + + + void clear_index(); + void append_tstep(ecl_sum_tstep_type * tstep); + void build_index(); + void fwrite_report( int report_step , fortio_type * fortio) const; + bool check_file( ecl_file_type * ecl_file ); + void add_ecl_file(int report_step, const ecl_file_view_type * summary_view, const ecl_smspec_type * smspec); +}; + + + + +} diff --git a/python/tests/ecl_tests/test_sum.py b/python/tests/ecl_tests/test_sum.py index fbe84a91a1..b13b2bc58d 100644 --- a/python/tests/ecl_tests/test_sum.py +++ b/python/tests/ecl_tests/test_sum.py @@ -570,3 +570,8 @@ def test_total_and_rate(self): self.assertTrue( EclSum.is_rate("WOPR:OP_4")) self.assertFalse( EclSum.is_rate("BPR:123")) self.assertTrue(EclSum.is_rate("FWIR")) + + + def test_load_case(self): + path = os.path.join(self.TESTDATA_ROOT, "local/ECLIPSE/cp_simple3/SIMPLE_SUMMARY3") + case = EclSum( path ) diff --git a/test-data/local/ECLIPSE/cp_simple3/SIMPLE_SUMMARY3.DATA b/test-data/local/ECLIPSE/cp_simple3/SIMPLE_SUMMARY3.DATA new file mode 100644 index 0000000000..ccfa835647 --- /dev/null +++ b/test-data/local/ECLIPSE/cp_simple3/SIMPLE_SUMMARY3.DATA @@ -0,0 +1,898 @@ +-- This reservoir simulation deck is made available under the Open Database +-- License: http://opendatacommons.org/licenses/odbl/1.0/. Any rights in +-- individual contents of the database are licensed under the Database Contents +-- License: http://opendatacommons.org/licenses/dbcl/1.0/ + +-- Copyright (C) 2016 Statoil + +-- NOTE: This deck is currently not supported by the OPM +-- simulator flow due to lack of support for LGR. + +-- This case is created for testing flow diagnostic implementations +-- in ResInsight. It is a simple shoe-box grid with an added fault +-- giving non-neighboring connections and two added local grid refinements. + + +RUNSPEC =============================================================== + +TITLE + SIMPLE 2PH MODEL WITH SINGLE FAULT, LGR, AQUIFER (CT & Num) and GRUPNET + +DIMENS + 20 20 10 / + +METRIC + +OIL + +WATER + +GAS + +TABDIMS + 2* 24 2* 20 20 1* 1 7* / +EQLDIMS + 2* 100 2* / +REGDIMS + 2 5* / +WELLDIMS + 12 100 4 12 0 0 0 0 0 0 0 0 / + +AQUDIMS + 5 5 1* 1* 1 50 1* 1* / + +WSEGDIMS + 4 30 4 / + +FAULTDIM + 10 / + + +TRACERS +-- NOTRAC NWTRAC NGTRAC NETRAC Diff/NODiff + 0 2 0 0 'DIFF' / + + +NSTACK + 25 / +START +1 JAN 2017 / + +LGR + 2 10000 / + + +UNIFOUT + +GRID =============================================================== + +INCLUDE + 'include/SHIFTTOP.GRDECL' / + +FAULTS +--name I1 I2 J1 J2 K1 K2 Dir + FLT1 10 10 1 10 1 5 X / + FLT2 10 10 1 10 6 10 X / + FLT3 10 10 11 20 1 5 X / + FLT4 10 10 11 20 6 10 X / + FLT5 4 8 15 15 1 10 Y / +/ + +MULTFLT + 'FLT1' 0.1 / + 'FLT2' 0.2 / + 'FLT3' 0.3 / + 'FLT4' 0.4 / + 'FLT5' 0.5 / +/ + + +PORO +4000*0.3 +/ + +PERMX +2000*80.0 +800*150.0 +1200*50.0 +/ + +PERMY +2000*80.0 +800*150.0 +1200*50.0 +/ + +PERMZ +2000*15.0 +800*25.0 +1200*15.0 +/ + +NOECHO + +CARFIN +---- Name I1 I2 J1 J2 K1 K2 NX NY NZ + 'CENTER' 8 12 8 12 4 6 20 20 9 / +ENDFIN +CARFIN + 'WELLI1' 1 3 1 3 1 10 9 9 20 2 / +ENDFIN + +INIT +/ + +AQUNUM +--# I J K A L Poro Perm Depth Pres PVT SAT + 1 20 11 5 1.0E+6 10000 1* 2000 1* 500 1* 1* / + 1 20 11 6 1.0E+6 10000 1* 2000 1* 500 1* 1* / + 1 20 11 7 1.0E+6 10000 1* 2000 1* 500 1* 1* / + 2 20 11 9 1.0E+6 10000 1* 2000 1* 500 1* 1* / + 2 20 11 10 1.0E+6 10000 1* 2000 1* 500 1* 1* / +/ + +AQUCON + 1 19 19 11 11 5 7 'I+' 1.0 / + 2 19 19 11 11 9 10 'I+' 1.0 / +/ + + +PROPS =============================================================== + +DENSITY + 900 1000 1 / +PVCDO -- pref Bo Co muo vo + 400 1.05 1.0E-05 5 0 + / + +PVTW + 400 1 1.0E-06 1 0 / + +ROCK + 400 0 / + +PVDG +--Press Bg Visc + 50 0.024958 0.01441 +110 0.011072 0.01609 +170 0.007156 0.01834 +190 0.006433 0.01923 +250.8 0.005013 0.02234 +301.59 0.004323 0.02542 +360.8 0.003819 0.02974 +374.31 0.003735 0.03087 +387.36 0.003662 0.03202 +399.99 0.003598 0.0332 +412.21 0.003543 0.03442 +424.05 0.003496 0.03566 +543.83 0.003295 0.05582 +594.29 0.003200 0.07567 +/ + + +--Corey parametrization using exponent of 2 +SWOF +-- Sw Krw Krow + 0.1000 0.0000 1.0000 0.0000 + 0.1500 0.0020 0.8789 0.0000 + 0.2000 0.0078 0.7656 0.0000 + 0.2500 0.0176 0.6602 0.0000 + 0.3000 0.0313 0.5625 0.0000 + 0.3500 0.0488 0.4727 0.0000 + 0.4000 0.0703 0.3906 0.0000 + 0.4500 0.0957 0.3164 0.0000 + 0.5000 0.1250 0.2500 0.0000 + 0.5500 0.1582 0.1914 0.0000 + 0.6000 0.1953 0.1406 0.0000 + 0.6500 0.2363 0.0977 0.0000 + 0.7000 0.2813 0.0625 0.0000 + 0.7500 0.3301 0.0352 0.0000 + 0.8000 0.3828 0.0156 0.0000 + 0.8500 0.4395 0.0039 0.0000 + 0.9000 0.5000 0.0000 0.0000 +/ + +SGOF +-- Sg Krg Krog Pcog + .0000 .0000 1.00 .00 + .0500 .0000 0.80 .00 + .1000 .0300 0.40 .00 + .2000 .1000 0.10 .00 + .3000 .2400 0.05 .00 + .4000 .33 0.00 .00 + .5000 .4200 0.00 .00 + .9000 1.000 0.00 .00 / table 1 + + +TRACER + 'TR1' WAT / + 'TR2' WAT / +/ + +TRACITVD + / + + + +REGIONS =============================================================== + +-- ARRAY VALUE ------- BOX ------ +-- I1 I2 J1 J2 K1 K2 +EQUALS + FIPNUM 1 1 10 1 20 1 10 / + FIPNUM 2 11 20 1 20 1 10 / +/ + +SOLUTION =============================================================== +EQUIL + 0 400 500 0 / + +RPTSOL + RESTART=2 / + +RPTRST + FLORES / + +TBLKFTR1 + 4000*0 / + +TBLKFTR2 + 4000*0 / + +AQUCT +--# Depth Pres Perm Por Compr Radius H Angle PVTW Table Csalt Taq +1 3 500 2000 0.2 1E-5 10000 20 90 1* / + +AQUANCON +1 20 20 11 11 1 3 'I+' / +/ + +SUMMARY =============================================================== +ALL +FNQT + +BPR + 10 10 5 / +/ + +COFR + 'P1' 18 2 5 / +/ + +LBOSAT +-- LGR Name Local Cell + 'CENTER' 5 5 5 / +/ + +LCWIT +-- LGR Name Well Name Local Cell + 'WELLI1' 'I1' 5 5 5 / +/ + +LWWIR +-- LGR Name Well Name + 'WELLI1' 'I1' / +/ + +RWFT + 1 2 / +/ + +ROIP +/ + +WTPRTR1 +/ + +WTPRTR2 +/ + +WTPTTR1 +/ + +WTPTTR2 +/ + +WTITTR1 +/ + +WTITTR2 +/ + +-- Analytical Aqufier +AAQR + / +AAQT + / +AAQP + / +-- Numerical Aquifer +ANQR + / + +ANQT + / + +ANQP + / +-- ----------------- + +-- Multi-Segment Wells +SOFR + 'P2' 1 / + 'P2' 2 / + 'P2' 3 / + 'P2' 10 / + 'P2' 13 / + 'P2' 18 / + 'P2' 20 / + 'P2' 22 / + 'P2' 23 / + / + +SOFRF + 'P2' 1 / + 'P2' 2 / + 'P2' 3 / + 'P2' 10 / + 'P2' 13 / + 'P2' 18 / + 'P2' 20 / + 'P2' 22 / + 'P2' 23 / + / + +SOFRS + 'P2' 1 / + 'P2' 2 / + 'P2' 3 / + 'P2' 10 / + 'P2' 13 / + 'P2' 18 / + 'P2' 20 / + 'P2' 22 / + 'P2' 23 / + / + +SWFR + 'P2' 1 / + / + +SGFR + 'P2' 1 / + 'P2' 2 / + 'P2' 3 / + 'P2' 10 / + 'P2' 13 / + 'P2' 18 / + 'P2' 20 / + 'P2' 22 / + 'P2' 23 / + / + +SGFRF + 'P2' 1 / + 'P2' 2 / + 'P2' 3 / + 'P2' 10 / + 'P2' 13 / + 'P2' 18 / + 'P2' 20 / + 'P2' 22 / + 'P2' 23 / + / + +SGFR + 'P2' 1 / + 'P2' 2 / + 'P2' 3 / + 'P2' 10 / + 'P2' 13 / + 'P2' 18 / + 'P2' 20 / + 'P2' 22 / + 'P2' 23 / + / + +SWCT + 'P2' 1 / + 'P2' 3 / + 'P2' 13 / + 'P2' 14 / + 'P2' 15 / + 'P2' 16 / + 'P2' 17 / + / + +SGOR + 'P2' 1 / + 'P2' 23 / + 'P2' 25 / + 'P2' 27 / + / + +SPR + 'P2' / + / + +SPRDH + 'P2' 2 / + 'P2' 3 / + / + +SPRDF + 'P2' / +/ + +SWFR + 'I2' 1 / + 'I2' 4 / + 'I2' 5 / + / + +SWCT + 'I2' 4 / + / + +SGOR + 'I2' 4 / + / + +SPR + 'I2' / + / + +SPRDH + 'I2' 2 / + 'I2' 3 / + / + +SPRDF + 'I2' / + / + + -- Network +GPR +/ + +GPRB +/ + +SCHEDULE =============================================================== + + +RPTSCHED + FIP WELSPECS WELLS / + +RPTRST + BASIC=2 FLORES / + +GRUPTREE + PRODA FIELD / + WIA FIELD / +/ + +GRUPNET + FIELD 1* / + PRODA 25 9999 / + WIA 400 9999 / +/ + + + +WELSPECS +-- 'I1' '1' 2 2 1* 'WATER' / + I2_GI WIA 2 18 0 GAS / + I2 WIA 2 18 0 WATER / + P1 PRODA 18 2 0 OIL / + P2 PRODA 18 18 0 OIL / + P1_RFT PRODA 18 2 0 OIL / + P2_RFT PRODA 18 18 0 OIL / +/ + +COMPDAT + 'P1' 2* 2 2 'SHUT' 2* 0.2 1* 0 / + 'P1' 2* 3 10 'OPEN' 2* 0.2 1* 0 / + 'P1_RFT' 2* 6 10 'OPEN' 2* 0.2 1* 0 / +/ + +COMPDAT + 'I2_GI' 2 18 2 2 'OPEN' 2* 0.2 3* 'Y' / + 'I2_GI' 2 17 2 2 'SHUT' 2* 0.2 3* 'Y' / + 'I2_GI' 2 16 2 2 'OPEN' 2* 0.2 3* 'Y' / + 'I2_GI' 2 15 2 2 'SHUT' 2* 0.2 3* 'Y' / + 'I2_GI' 2 14 2 2 'OPEN' 2* 0.2 3* 'Y' / + + 'I2_GI' 2 18 7 7 'SHUT' 2* 0.2 3* 'Y' / + 'I2_GI' 2 17 7 7 'OPEN' 2* 0.2 3* 'Y' / + 'I2_GI' 2 16 7 7 'SHUT' 2* 0.2 3* 'Y' / + 'I2_GI' 2 15 7 7 'OPEN' 2* 0.2 3* 'Y' / + 'I2_GI' 2 14 7 7 'SHUT' 2* 0.2 3* 'Y' / + + + 'I2' 2 18 2 2 'OPEN' 2* 0.2 3* 'Y' / + 'I2' 2 17 2 2 'OPEN' 2* 0.2 3* 'Y' / + 'I2' 2 16 2 2 'OPEN' 2* 0.2 3* 'Y' / + 'I2' 2 15 2 2 'OPEN' 2* 0.2 3* 'Y' / + 'I2' 2 14 2 2 'OPEN' 2* 0.2 3* 'Y' / + + 'I2' 2 18 7 7 'OPEN' 2* 0.2 3* 'Y' / + 'I2' 2 17 7 7 'SHUT' 2* 0.2 3* 'Y' / + 'I2' 2 16 7 7 'OPEN' 2* 0.2 3* 'Y' / + 'I2' 2 15 7 7 'SHUT' 2* 0.2 3* 'Y' / + 'I2' 2 14 7 7 'OPEN' 2* 0.2 3* 'Y' / + + 'P2' 18 18 2 2 'OPEN' 2* 0.2 3* 'Y' / + 'P2' 18 17 2 2 'OPEN' 2* 0.2 3* 'Y' / + 'P2' 18 16 2 2 'OPEN' 2* 0.2 3* 'Y' / + 'P2' 18 15 2 2 'OPEN' 2* 0.2 3* 'Y' / + 'P2' 18 14 2 2 'OPEN' 2* 0.2 3* 'Y' / + + 'P2' 18 18 5 5 'OPEN' 2* 0.2 3* 'Y' / + 'P2' 18 17 5 5 'OPEN' 2* 0.2 3* 'Y' / + 'P2' 18 16 5 5 'OPEN' 2* 0.2 3* 'Y' / + 'P2' 18 15 5 5 'OPEN' 2* 0.2 3* 'Y' / + 'P2' 18 14 5 5 'OPEN' 2* 0.2 3* 'Y' / + + 'P2' 18 18 7 7 'OPEN' 2* 0.2 3* 'Y' / + 'P2' 18 17 7 7 'OPEN' 2* 0.2 3* 'Y' / + 'P2' 18 16 7 7 'OPEN' 2* 0.2 3* 'Y' / + 'P2' 18 15 7 7 'OPEN' 2* 0.2 3* 'Y' / + 'P2' 18 14 7 7 'OPEN' 2* 0.2 3* 'Y' / + + 'P2_RFT' 18 18 2 2 'OPEN' 2* 0.2 3* 'Y' / + 'P2_RFT' 18 17 2 2 'OPEN' 2* 0.2 3* 'Y' / + 'P2_RFT' 18 16 2 2 'OPEN' 2* 0.2 3* 'Y' / + 'P2_RFT' 18 15 2 2 'OPEN' 2* 0.2 3* 'Y' / + 'P2_RFT' 18 14 2 2 'OPEN' 2* 0.2 3* 'Y' / + + 'P2_RFT' 18 18 5 5 'OPEN' 2* 0.2 3* 'Y' / + 'P2_RFT' 18 17 5 5 'OPEN' 2* 0.2 3* 'Y' / + 'P2_RFT' 18 16 5 5 'OPEN' 2* 0.2 3* 'Y' / + 'P2_RFT' 18 15 5 5 'OPEN' 2* 0.2 3* 'Y' / + 'P2_RFT' 18 14 5 5 'OPEN' 2* 0.2 3* 'Y' / + + 'P2_RFT' 18 18 7 7 'OPEN' 2* 0.2 3* 'Y' / + 'P2_RFT' 18 17 7 7 'OPEN' 2* 0.2 3* 'Y' / + 'P2_RFT' 18 16 7 7 'OPEN' 2* 0.2 3* 'Y' / + 'P2_RFT' 18 15 7 7 'OPEN' 2* 0.2 3* 'Y' / + 'P2_RFT' 18 14 7 7 'OPEN' 2* 0.2 3* 'Y' / +/ + +WELSEGS +-- Name Dep 1 Tlen 1 Vol 1 + 'I2_GI' 0 1 1* 'INC' / + +-- First Last Branch Outlet Length Depth Diam Ruff Area Vol +-- Seg Seg Num Seg Chang +-- Main Stem + 2 14 1 1 1 1 0.2 1.E-3 1* 1* / + -- Top Branch + 15 15 2 2 2 0 0.2 1.E-3 1* 1* / + 16 19 2 15 10 0 0.2 1.E-3 1* 1* / +-- Bottom Branch + 20 20 3 7 2 0 0.2 1.E-3 1* 1* / + 21 24 3 20 10 0 0.2 1.E-3 1* 1* / +/ + +COMPSEGS +-- Name + 'I2_GI' / + +-- I J K Brn Start End Dirn End +-- No Length Length Penet Range +-- Top Branch + 2 18 2 2 2 1* 'Y' 14 / +-- Bottom Branch + 2 18 7 3 7 1* 'Y' 14 / + / + +WELSEGS +-- Name Dep 1 Tlen 1 Vol 1 + 'I2' 0 1 1* 'INC' / + +-- First Last Branch Outlet Length Depth Diam Ruff Area Vol +-- Seg Seg Num Seg Chang +-- Main Stem + 2 14 1 1 1 1 0.2 1.E-3 1* 1* / + -- Top Branch + 15 15 2 2 2 0 0.2 1.E-3 1* 1* / + 16 19 2 15 10 0 0.2 1.E-3 1* 1* / +-- Bottom Branch + 20 20 3 7 2 0 0.2 1.E-3 1* 1* / + 21 24 3 20 10 0 0.2 1.E-3 1* 1* / +/ + +COMPSEGS +-- Name + 'I2' / + +-- I J K Brn Start End Dirn End +-- No Length Length Penet Range +-- Top Branch + 2 18 2 2 2 1* 'Y' 14 / +-- Bottom Branch + 2 18 7 3 7 1* 'Y' 14 / + / + +WELSEGS +-- Name Dep 1 Tlen 1 Vol 1 + 'P2' 0 1 1* 'INC' / + +-- First Last Branch Outlet Length Depth Diam Ruff Area Vol +-- Seg Seg Num Seg Chang +-- Main Stem + 2 12 1 1 1 1 0.2 1.E-3 1* 1* / +-- Top Branch + 13 13 2 2 2 0 0.2 1.E-3 1* 1* / + 14 17 2 13 10 0 0.2 1.E-3 1* 1* / +-- Middle Branch + 18 18 3 5 2 0 0.2 1.E-3 1* 1* / + 19 22 3 18 10 0 0.2 1.E-3 1* 1* / +-- Bottom Branch + 23 23 4 7 2 0 0.2 1.E-3 1* 1* / + 24 27 4 23 10 0 0.2 1.E-3 1* 1* / + / + + +COMPSEGS +-- Name + 'P2' / + +-- I J K Brn Start End Dirn End +-- No Length Length Penet Range +-- Top Branch + 18 18 2 2 2 1* 'Y' 14 / +-- Middle Branch + 18 18 5 3 5 1* 'Y' 14 / +-- Bottom Branch + 18 18 7 4 7 1* 'Y' 14 / +/ + + +WELSPECL + 'I1' 'WIA' 'WELLI1' 5 5 1* 'WATER' / Injector + 'I1_GI' 'WIA' 'WELLI1' 5 5 1* 'GAS' / Injector +/ +COMPDATL + 'I1' 'WELLI1' 1* 1* 1 12 'OPEN' 2* 0.2 1* 0 / + 'I1' 'WELLI1' 1* 1* 13 20 'SHUT' 2* 0.2 1* 0 / + 'I1_GI' 'WELLI1' 1* 1* 1 6 'SHUT' 2* 0.2 1* 0 / + 'I1_GI' 'WELLI1' 1* 1* 7 14 'OPEN' 2* 0.2 1* 0 / + 'I1_GI' 'WELLI1' 1* 1* 15 16 'SHUT' 2* 0.2 1* 0 / + 'I1_GI' 'WELLI1' 1* 1* 17 20 'OPEN' 2* 0.2 1* 0 / + +/ + + +WCONPROD + 'P1' 'OPEN' 'BHP' 5* 350/ + 'P2' 'OPEN' 'BHP' 5* 345/ +'P1_RFT' 'STOP' 'BHP' 5* 345/ +'P2_RFT' 'STOP' 'BHP' 5* 345/ + +/ + +WCONINJE + 'I1' 'WATER' 'OPEN' 'RATE' 40 1* 550/ + 'I2' 'WATER' 'OPEN' 'RATE' 10 1* 550/ + 'I1_GI' 'GAS' 'STOP' 'RATE' 0 1* 550/ + 'I2_GI' 'GAS' 'OPEN' 'RATE' 0 1* 550/ +/ + +WTRACER + 'I1' 'TR1' 1.0 / + 'I2' 'TR2' 1.0 / +/ + + +TUNING +0.1 30 / +/ +12 1 250 1* 25 / + +DATES + 1 FEB 2017 / + / + +WCONINJE + 'I1' 'WATER' 'OPEN' 'RATE' 0 1* 550/ + 'I2' 'WATER' 'OPEN' 'RATE' 0 1* 550/ + 'I1_GI' 'GAS' 'OPEN' 'RATE' 40000 1* 550/ + 'I2_GI' 'GAS' 'OPEN' 'RATE' 10000 1* 550/ +/ + +DATES + 1 MAR 2017 / + / + + +WCONINJE + 'I1' 'WATER' 'OPEN' 'RATE' 40 1* 550/ + 'I2' 'WATER' 'OPEN' 'RATE' 10 1* 550/ + 'I1_GI' 'GAS' 'OPEN' 'RATE' 0 1* 550/ + 'I2_GI' 'GAS' 'OPEN' 'RATE' 0 1* 550/ +/ + + +WRFTPLT +--Well RFT PLT Segment + P1_RFT YES REPT / + P2_RFT NO YES YES / + / + +DATES + 1 APR 2017 / + / + + +WCONINJE + 'I1' 'WATER' 'STOP' 'RATE' 0 1* 550/ + 'I2' 'WATER' 'OPEN' 'RATE' 0 1* 550/ + 'I1_GI' 'GAS' 'OPEN' 'RATE' 40000 1* 550/ + 'I2_GI' 'GAS' 'OPEN' 'RATE' 10000 1* 550/ +/ + +DATES + 1 MAY 2017 / + / + + +WCONINJE + 'I1' 'WATER' 'OPEN' 'RATE' 40 1* 550/ + 'I2' 'WATER' 'OPEN' 'RATE' 10 1* 550/ + 'I1_GI' 'GAS' 'STOP' 'RATE' 0 1* 550/ + 'I2_GI' 'GAS' 'OPEN' 'RATE' 0 1* 550/ +/ +DATES + 1 JUN 2017 / + / + + +WCONINJE + 'I1' 'WATER' 'STOP' 'RATE' 0 1* 550/ + 'I2' 'WATER' 'OPEN' 'RATE' 0 1* 550/ + 'I1_GI' 'GAS' 'OPEN' 'RATE' 40000 1* 550/ + 'I2_GI' 'GAS' 'OPEN' 'RATE' 10000 1* 550/ +/ + +DATES + 1 JUL 2017 / + / + +WCONINJE + 'I1' 'WATER' 'OPEN' 'RATE' 40 1* 550/ + 'I2' 'WATER' 'OPEN' 'RATE' 10 1* 550/ + 'I1_GI' 'GAS' 'STOP' 'RATE' 0 1* 550/ + 'I2_GI' 'GAS' 'OPEN' 'RATE' 0 1* 550/ +/ +DATES + 1 AUG 2017 / + / + + WCONINJE + 'I1' 'WATER' 'STOP' 'RATE' 0 1* 550/ + 'I2' 'WATER' 'OPEN' 'RATE' 0 1* 550/ + 'I1_GI' 'GAS' 'OPEN' 'RATE' 40000 1* 550/ + 'I2_GI' 'GAS' 'OPEN' 'RATE' 10000 1* 550/ +/ + +DATES + 1 SEP 2017 / + / + + +WCONINJE + 'I1' 'WATER' 'OPEN' 'RATE' 40 1* 550/ + 'I2' 'WATER' 'OPEN' 'RATE' 10 1* 550/ + 'I1_GI' 'GAS' 'STOP' 'RATE' 0 1* 550/ + 'I2_GI' 'GAS' 'OPEN' 'RATE' 0 1* 550/ +/ +DATES + 1 OCT 2017 / + / + + + WCONINJE + 'I1' 'WATER' 'SHUT' 'RATE' 0 1* 550/ + 'I2' 'WATER' 'OPEN' 'RATE' 0 1* 550/ + 'I1_GI' 'GAS' 'OPEN' 'RATE' 40000 1* 550/ + 'I2_GI' 'GAS' 'OPEN' 'RATE' 10000 1* 550/ +/ + +DATES + 1 NOV 2017 / + / + + +WCONINJE + 'I1' 'WATER' 'OPEN' 'RATE' 40 1* 550/ + 'I2' 'WATER' 'OPEN' 'RATE' 10 1* 550/ + 'I1_GI' 'GAS' 'SHUT' 'RATE' 0 1* 550/ + 'I2_GI' 'GAS' 'OPEN' 'RATE' 0 1* 550/ +/ + +DATES + 1 DEC 2017 / + / + + + WCONINJE + 'I1' 'WATER' 'SHUT' 'RATE' 0 1* 550/ + 'I2' 'WATER' 'OPEN' 'RATE' 0 1* 550/ + 'I1_GI' 'GAS' 'OPEN' 'RATE' 40000 1* 550/ + 'I2_GI' 'GAS' 'OPEN' 'RATE' 10000 1* 550/ +/ + +DATES + 1 JAN 2018 / + / + + +WCONINJE + 'I1' 'WATER' 'OPEN' 'RATE' 40 1* 550/ + 'I2' 'WATER' 'OPEN' 'RATE' 10 1* 550/ + 'I1_GI' 'GAS' 'SHUT' 'RATE' 0 1* 550/ + 'I2_GI' 'GAS' 'OPEN' 'RATE' 0 1* 550/ +/ + +DATES + 1 FEB 2018 / + / + + + WCONINJE + 'I1' 'WATER' 'SHUT' 'RATE' 0 1* 550/ + 'I2' 'WATER' 'OPEN' 'RATE' 0 1* 550/ + 'I1_GI' 'GAS' 'OPEN' 'RATE' 40000 1* 550/ + 'I2_GI' 'GAS' 'OPEN' 'RATE' 10000 1* 550/ +/ + +DATES + 1 MAR 2018 / + / + + +WCONINJE + 'I1' 'WATER' 'OPEN' 'RATE' 40 1* 550/ + 'I2' 'WATER' 'OPEN' 'RATE' 10 1* 550/ + 'I1_GI' 'GAS' 'SHUT' 'RATE' 0 1* 550/ + 'I2_GI' 'GAS' 'OPEN' 'RATE' 0 1* 550/ +/ + +DATES + 1 APR 2018 / + / + + + WCONINJE + 'I1' 'WATER' 'SHUT' 'RATE' 0 1* 550/ + 'I2' 'WATER' 'OPEN' 'RATE' 0 1* 550/ + 'I1_GI' 'GAS' 'OPEN' 'RATE' 40000 1* 550/ + 'I2_GI' 'GAS' 'OPEN' 'RATE' 10000 1* 550/ +/ + +DATES + 1 MAY 2018 / + / + + +WCONINJE + 'I1' 'WATER' 'OPEN' 'RATE' 40 1* 550/ + 'I2' 'WATER' 'OPEN' 'RATE' 10 1* 550/ + 'I1_GI' 'GAS' 'SHUT' 'RATE' 0 1* 550/ + 'I2_GI' 'GAS' 'OPEN' 'RATE' 0 1* 550/ +/ + + + + + +TSTEP + 4*15 / + + +END + + + diff --git a/test-data/local/ECLIPSE/cp_simple3/SIMPLE_SUMMARY3.SMSPEC b/test-data/local/ECLIPSE/cp_simple3/SIMPLE_SUMMARY3.SMSPEC new file mode 100644 index 0000000000..68c81eef2a Binary files /dev/null and b/test-data/local/ECLIPSE/cp_simple3/SIMPLE_SUMMARY3.SMSPEC differ diff --git a/test-data/local/ECLIPSE/cp_simple3/SIMPLE_SUMMARY3.UNSMRY b/test-data/local/ECLIPSE/cp_simple3/SIMPLE_SUMMARY3.UNSMRY new file mode 100644 index 0000000000..38dd0b61fa Binary files /dev/null and b/test-data/local/ECLIPSE/cp_simple3/SIMPLE_SUMMARY3.UNSMRY differ