From 2fff09847cc83d870a61b12cbdc9f38264c4c26e Mon Sep 17 00:00:00 2001 From: Joakim Hove Date: Thu, 9 Nov 2017 09:15:44 +0100 Subject: [PATCH] Ecl sum keyword vector (#237) EclSumKeywordVector: can handle missing values. --- lib/ecl/ecl_sum_data.c | 43 +++++++++--- lib/ecl/ecl_sum_vector.c | 39 ++++++++++- lib/include/ert/ecl/ecl_sum_vector.h | 2 + python/python/ecl/ecl/ecl_sum.py | 4 +- .../python/ecl/ecl/ecl_sum_keyword_vector.py | 5 ++ python/tests/ecl/test_sum.py | 65 +++++++++++++++---- 6 files changed, 130 insertions(+), 28 deletions(-) diff --git a/lib/ecl/ecl_sum_data.c b/lib/ecl/ecl_sum_data.c index c0b28cdc43..a461cab4a3 100644 --- a/lib/ecl/ecl_sum_data.c +++ b/lib/ecl/ecl_sum_data.c @@ -17,6 +17,7 @@ */ #include +#include #include #include @@ -1180,12 +1181,10 @@ double ecl_sum_data_interp_get(const ecl_sum_data_type * data , int time_index1 } -static double ecl_sum_data_vector_iget(const ecl_sum_data_type * data, time_t sim_time, const ecl_sum_vector_type * keylist, int key_index, +static double ecl_sum_data_vector_iget(const ecl_sum_data_type * data, time_t sim_time, int params_index, bool is_rate, int time_index1 , int time_index2 , double weight1 , double weight2 ) { - int params_index = ecl_sum_vector_iget_param_index(keylist, key_index); double value = 0.0; - bool is_rate = ecl_sum_vector_iget_is_rate(keylist, key_index); if (is_rate) { int time_index = ecl_sum_data_get_index_from_sim_time(data, sim_time); // uses step function since it is a rate @@ -1205,15 +1204,35 @@ void ecl_sum_data_fwrite_interp_csv_line(const ecl_sum_data_type * data, time_t ecl_sum_data_init_interp_from_sim_time(data, sim_time, &time_index1, &time_index2, &weight1, &weight2); for (int i = 0; i < num_keywords; i++) { - double value = ecl_sum_data_vector_iget( data, sim_time, keylist , i , time_index1, time_index2, weight1, weight2); + if (ecl_sum_vector_iget_valid(keylist, i)) { + int params_index = ecl_sum_vector_iget_param_index(keylist, i); + bool is_rate = ecl_sum_vector_iget_is_rate(keylist, i); + double value = ecl_sum_data_vector_iget( data, sim_time, params_index , is_rate, time_index1, time_index2, weight1, weight2); - if (i == 0) - fprintf(fp, "%f", value); - else - fprintf(fp, ",%f", value); + if (i == 0) + fprintf(fp, "%f", value); + else + fprintf(fp, ",%f", value); + } else { + if (i == 0) + fputs("", fp); + else + fputs(",", fp); + } } } + +/* + If the keylist contains invalid indices the corresponding element in the + results vector will *not* be updated; i.e. it is smart to initialize the + results vector with an invalid-value marker before calling this function: + + double_vector_type * results = double_vector_alloc( ecl_sum_vector_get_size(keys), NAN); + ecl_sum_data_get_interp_vector( data, sim_time, keys, results); + +*/ + void ecl_sum_data_get_interp_vector( const ecl_sum_data_type * data , time_t sim_time, const ecl_sum_vector_type * keylist, double_vector_type * results){ int num_keywords = ecl_sum_vector_get_size(keylist); double weight1, weight2; @@ -1222,8 +1241,12 @@ void ecl_sum_data_get_interp_vector( const ecl_sum_data_type * data , time_t sim ecl_sum_data_init_interp_from_sim_time(data, sim_time, &time_index1, &time_index2, &weight1, &weight2); double_vector_reset( results ); for (int i = 0; i < num_keywords; i++) { - double value = ecl_sum_data_vector_iget( data, sim_time, keylist , i , time_index1, time_index2, weight1, weight2); - double_vector_iset( results, i , value ); + if (ecl_sum_vector_iget_valid(keylist, i)) { + int params_index = ecl_sum_vector_iget_param_index(keylist, i); + bool is_rate = ecl_sum_vector_iget_is_rate(keylist, i); + double value = ecl_sum_data_vector_iget( data, sim_time, params_index, is_rate, time_index1, time_index2, weight1, weight2); + double_vector_iset( results, i , value ); + } } } diff --git a/lib/ecl/ecl_sum_vector.c b/lib/ecl/ecl_sum_vector.c index 5e22358d18..c6f811ecfb 100644 --- a/lib/ecl/ecl_sum_vector.c +++ b/lib/ecl/ecl_sum_vector.c @@ -58,6 +58,39 @@ ecl_sum_vector_type * ecl_sum_vector_alloc(const ecl_sum_type * ecl_sum){ return ecl_sum_vector; } +static void ecl_sum_vector_add_invalid_key(ecl_sum_vector_type * vector, const char * key) { + int_vector_append(vector->node_index_list, -1); + bool_vector_append(vector->is_rate_list, false); + stringlist_append_copy(vector->key_list, key); +} + + +/* + This function will allocate a keyword vector for the keys in the @ecl_sum + argument passed in, it will contain all the same keys as in the input argument + @src_vector. If the @src_vector contains keys which are not present in + @ecl_sum an entry marked as *invalid* will be added. The whole point about + this function is to ensure that calls to: + + ecl_sum_fwrite_interp_csv_line( ) + + will result in nicely aligned output even if the different summary cases do + not have the exact same keys. +*/ + +ecl_sum_vector_type * ecl_sum_vector_alloc_layout_copy(const ecl_sum_vector_type * src_vector, const ecl_sum_type * ecl_sum) { + ecl_sum_vector_type * new_vector = ecl_sum_vector_alloc(ecl_sum); + for (int i=0; i < stringlist_get_size(src_vector->key_list); i++) { + const char * key = stringlist_iget(src_vector->key_list, i); + if (ecl_sum_has_general_var(ecl_sum, key)) + ecl_sum_vector_add_key(new_vector, key); + else + ecl_sum_vector_add_invalid_key(new_vector, key); + } + return new_vector; +} + + bool ecl_sum_vector_add_key( ecl_sum_vector_type * ecl_sum_vector, const char * key){ if (ecl_sum_has_general_var( ecl_sum_vector->ecl_sum , key)) { @@ -73,8 +106,6 @@ bool ecl_sum_vector_add_key( ecl_sum_vector_type * ecl_sum_vector, const char * return false; } - - void ecl_sum_vector_add_keys( ecl_sum_vector_type * ecl_sum_vector, const char * pattern){ stringlist_type * keylist = ecl_sum_alloc_matching_general_var_list(ecl_sum_vector->ecl_sum , pattern); @@ -101,6 +132,10 @@ bool ecl_sum_vector_iget_is_rate(const ecl_sum_vector_type * ecl_sum_vector, int return bool_vector_iget(ecl_sum_vector->is_rate_list, index); } +bool ecl_sum_vector_iget_valid(const ecl_sum_vector_type * ecl_sum_vector, int index) { + return (int_vector_iget(ecl_sum_vector->node_index_list, index) >= 0); +} + int ecl_sum_vector_iget_param_index(const ecl_sum_vector_type * ecl_sum_vector, int index){ return int_vector_iget(ecl_sum_vector->node_index_list, index); } diff --git a/lib/include/ert/ecl/ecl_sum_vector.h b/lib/include/ert/ecl/ecl_sum_vector.h index d460a23129..c51c640ee6 100644 --- a/lib/include/ert/ecl/ecl_sum_vector.h +++ b/lib/include/ert/ecl/ecl_sum_vector.h @@ -38,6 +38,8 @@ typedef struct ecl_sum_vector_struct ecl_sum_vector_type; bool ecl_sum_vector_iget_is_rate(const ecl_sum_vector_type * ecl_sum_vector, int index); int ecl_sum_vector_iget_param_index(const ecl_sum_vector_type * ecl_sum_vector, int index); int ecl_sum_vector_get_size(const ecl_sum_vector_type * ecl_sum_vector); + bool ecl_sum_vector_iget_valid(const ecl_sum_vector_type * ecl_sum_vector, int index); + UTIL_IS_INSTANCE_HEADER( ecl_sum_vector); diff --git a/python/python/ecl/ecl/ecl_sum.py b/python/python/ecl/ecl/ecl_sum.py index fe6ca681e2..c2cfd1b33e 100644 --- a/python/python/ecl/ecl/ecl_sum.py +++ b/python/python/ecl/ecl/ecl_sum.py @@ -538,9 +538,9 @@ def get_interp(self, key, days=None, date=None): raise ValueError("Must supply either days or date") - def get_interp_row(self, key_list, sim_time): + def get_interp_row(self, key_list, sim_time, invalid_value = -1): ctime = CTime(sim_time) - data = DoubleVector( initial_size = len(key_list) ) + data = DoubleVector( initial_size = len(key_list) , default_value = invalid_value) EclSum._get_interp_vector(self, ctime, key_list, data) return data diff --git a/python/python/ecl/ecl/ecl_sum_keyword_vector.py b/python/python/ecl/ecl/ecl_sum_keyword_vector.py index 1dea8aaa7a..9a871e9971 100644 --- a/python/python/ecl/ecl/ecl_sum_keyword_vector.py +++ b/python/python/ecl/ecl/ecl_sum_keyword_vector.py @@ -32,6 +32,7 @@ class EclSumKeyWordVector(BaseCClass): TYPE_NAME = "ecl_sum_vector" _alloc = EclPrototype("void* ecl_sum_vector_alloc(ecl_sum)", bind=False) + _alloc_copy = EclPrototype("ecl_sum_vector_obj ecl_sum_vector_alloc_layout_copy(ecl_sum_vector, ecl_sum)") _free = EclPrototype("void ecl_sum_vector_free(ecl_sum_vector)") _add = EclPrototype("bool ecl_sum_vector_add_key(ecl_sum_vector, char*)") _add_multiple = EclPrototype("void ecl_sum_vector_add_keys(ecl_sum_vector, char*)") @@ -68,5 +69,9 @@ def add_keywords(self, keyword_pattern): def __repr__(self): return self._create_repr('len=%d' % len(self)) + def copy(self, ecl_sum): + return self._alloc_copy(ecl_sum) + + monkey_the_camel(EclSumKeyWordVector, 'addKeyword', EclSumKeyWordVector.add_keyword) monkey_the_camel(EclSumKeyWordVector, 'addKeywords', EclSumKeyWordVector.add_keywords) diff --git a/python/tests/ecl/test_sum.py b/python/tests/ecl/test_sum.py index 2401a4c925..80fbd52a0c 100644 --- a/python/tests/ecl/test_sum.py +++ b/python/tests/ecl/test_sum.py @@ -15,6 +15,7 @@ # for more details. import os +import inspect import datetime import csv import shutil @@ -287,23 +288,32 @@ def test_invalid(self): def test_kw_vector(self): - case = createEclSum("CSV" , [("FOPT", None , 0) , ("FOPR" , None , 0), ("FGPT" , None , 0)], - sim_length_days = 100, - num_report_step = 10, - num_mini_step = 10, - func_table = {"FOPT" : fopt, - "FOPR" : fopr , - "FGPT" : fgpt }) - kw_list = EclSumKeyWordVector( case ) + case1 = createEclSum("CSV" , [("FOPT", None , 0) , ("FOPR" , None , 0), ("FGPT" , None , 0)], + sim_length_days = 100, + num_report_step = 10, + num_mini_step = 10, + func_table = {"FOPT" : fopt, + "FOPR" : fopr , + "FGPT" : fgpt }) + + case2 = createEclSum("CSV" , [("FOPR", None , 0) , ("FOPT" , None , 0), ("FWPT" , None , 0)], + sim_length_days = 100, + num_report_step = 10, + num_mini_step = 10, + func_table = {"FOPT" : fopt, + "FOPR" : fopr , + "FWPT" : fgpt }) + + kw_list = EclSumKeyWordVector( case1 ) kw_list.add_keyword("FOPT") - kw_list.add_keyword("FOPR") kw_list.add_keyword("FGPT") + kw_list.add_keyword("FOPR") - t = case.getDataStartTime( ) + datetime.timedelta( days = 43 ); - data = case.get_interp_row( kw_list , t ) - for d1,d2 in zip(data, [ case.get_interp("FOPT", date = t), - case.get_interp("FOPT", date = t), - case.get_interp("FOPT", date = t) ]): + t = case1.getDataStartTime( ) + datetime.timedelta( days = 43 ); + data = case1.get_interp_row( kw_list , t ) + for d1,d2 in zip(data, [ case1.get_interp("FOPT", date = t), + case1.get_interp("FOPT", date = t), + case1.get_interp("FOPT", date = t) ]): self.assertFloatEqual(d1,d2) @@ -313,3 +323,30 @@ def test_kw_vector(self): for (k1,k2) in zip(kw_list,tmp): self.assertEqual(k1,k2) + + kw_list2 = kw_list.copy(case2) + self.assertIn("FOPT", kw_list2) + self.assertIn("FOPR", kw_list2) + self.assertIn("FGPT", kw_list2) + data2 = case2.get_interp_row( kw_list2 , t ) + + self.assertEqual(len(data2), 3) + self.assertEqual(data[0], data2[0]) + self.assertEqual(data[2], data2[2]) + + with TestAreaContext("sum_vector"): + with open("f1.txt","w") as f: + case1.dumpCSVLine(t, kw_list, f) + + with open("f2.txt", "w") as f: + case2.dumpCSVLine(t,kw_list2,f) + + with open("f1.txt") as f: + d1 = f.readline().split(",") + + with open("f2.txt") as f: + d2 = f.readline().split(",") + + self.assertEqual(d1[0],d2[0]) + self.assertEqual(d1[2],d2[2]) + self.assertEqual(d2[1],"")