diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 5772297c9..24de86400 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -240,7 +240,6 @@ add_executable( tests/test_rd_grid_bounds_checking.cpp tests/test_rd_sum.cpp tests/test_rd_type.cpp - tests/test_time_index.cpp tests/test_rd_kw.cpp tests/test_well_info.cpp tests/test_well_keyword_validation.cpp diff --git a/lib/include/resdata/FortIO.hpp b/lib/include/resdata/FortIO.hpp index 8bccaa90e..21a1ea6b1 100644 --- a/lib/include/resdata/FortIO.hpp +++ b/lib/include/resdata/FortIO.hpp @@ -78,7 +78,8 @@ class FortIO { bool data_fskip(size_t element_size, size_t element_count, size_t block_count); void data_fseek(offset_type data_offset, size_t data_element, - size_t element_size, int element_count, int block_size); + size_t element_size, size_t element_count, + size_t block_size); bool ftruncate(offset_type size); int fclean(); bool fclose_stream(); diff --git a/lib/include/resdata/rd_file.hpp b/lib/include/resdata/rd_file.hpp index 0a41f76a9..3a80d6e03 100644 --- a/lib/include/resdata/rd_file.hpp +++ b/lib/include/resdata/rd_file.hpp @@ -65,7 +65,7 @@ class File { size_t occurence) { return global_view->blockview(kw, kw, occurence); } - std::shared_ptr summary_view(int report_step) { + std::shared_ptr summary_view(size_t report_step) { return global_view->summary_view(report_step); } diff --git a/lib/include/resdata/rd_file_view.hpp b/lib/include/resdata/rd_file_view.hpp index f6c40196b..13437c9ba 100644 --- a/lib/include/resdata/rd_file_view.hpp +++ b/lib/include/resdata/rd_file_view.hpp @@ -48,11 +48,11 @@ class FileView { /** Validates the arguments of index_fload_kw and returns the keyword. - Throws std::invalid_argument if @index is negative, if there is no - such keyword in the view, if the keyword does not hold data of type + Throws std::invalid_argument if there is no such keyword in the + view, if the keyword does not hold data of type @expected, or if @out_count is smaller than @requested_count. */ [[nodiscard]] std::shared_ptr - validate_index_fload_kw(const std::string &kw, int index, + validate_index_fload_kw(const std::string &kw, size_t index, rd_type_enum expected, size_t requested_count, size_t out_count) const; @@ -159,8 +159,8 @@ class FileView { Throws std::ios_base::failure if the underlying file cannot be opened; @out is only left unwritten when this function throws. */ template - void index_fload_kw(const std::string &kw, int index, - const std::vector &index_map, + void index_fload_kw(const std::string &kw, size_t index, + const std::vector &index_map, std::array &out) { auto file_kw = validate_index_fload_kw(kw, index, iotype::tag, index_map.size(), N); @@ -217,7 +217,7 @@ class FileView { std::shared_ptr restart_view_from_sim_time(time_t sim_time); std::shared_ptr restart_view_from_sim_days(double sim_days); - std::shared_ptr summary_view(int report_step); + std::shared_ptr summary_view(size_t report_step); void close(); /** Write the index of this view to @ostream. diff --git a/lib/include/resdata/rd_kw.hpp b/lib/include/resdata/rd_kw.hpp index a02257bdb..512d333ce 100644 --- a/lib/include/resdata/rd_kw.hpp +++ b/lib/include/resdata/rd_kw.hpp @@ -115,8 +115,8 @@ class KW { the start of the keyword (i.e. its header) in the file, as stored by rd_file_kw. */ static void fread_indexed_data(ERT::FortIO &fortio, offset_type kw_offset, - rd_data_type data_type, int element_count, - const std::vector &index_map, + rd_data_type data_type, size_t element_count, + const std::vector &index_map, char *io_buffer); static std::unique_ptr make_actnum(const rd::KW *porv_kw, float porv_limit); diff --git a/lib/include/resdata/rd_smspec.hpp b/lib/include/resdata/rd_smspec.hpp index 0dd124c87..2e74d58c1 100644 --- a/lib/include/resdata/rd_smspec.hpp +++ b/lib/include/resdata/rd_smspec.hpp @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -16,15 +17,15 @@ namespace rd { /** A summary which stores elapsed time in a TIME vector. */ struct TimeParamsIndex { - int params_index; + size_t params_index; int seconds_per_unit; }; /** A summary which stores the time of each step as a whole date. */ struct DateParamsIndex { - int day; - int month; - int year; + size_t day; + size_t month; + size_t year; }; using TimeInfo = std::variant; @@ -41,10 +42,11 @@ const rd::smspec_node & rd_smspec_get_general_var_node(const rd_smspec_type *smspec, const char *lookup_kw); const rd::smspec_node & -rd_smspec_iget_node_w_node_index(const rd_smspec_type *smspec, int node_index); +rd_smspec_iget_node_w_node_index(const rd_smspec_type *smspec, + size_t node_index); const rd::smspec_node & rd_smspec_iget_node_w_params_index(const rd_smspec_type *smspec, - int params_index); + size_t params_index); /** These are the different variable types, see table 3.4 in the ECLIPSE file format docuemntation for naming conventions. @@ -55,9 +57,11 @@ rd_smspec_iget_node_w_params_index(const rd_smspec_type *smspec, rd_smspec_install_gen_key() must be updated. */ -std::vector rd_smspec_alloc_mapping(const rd_smspec_type *self, - const rd_smspec_type *other); -const int *rd_smspec_get_index_map(const rd_smspec_type *smspec); +std::vector> +rd_smspec_alloc_mapping(const rd_smspec_type *self, + const rd_smspec_type *other); +const std::vector & +rd_smspec_get_index_map(const rd_smspec_type *smspec); rd_smspec_var_type rd_smspec_identify_var_type(const char *var); rd_smspec_type *rd_smspec_alloc_restart_writer( const char *key_join_string, const char *restart_case, int restart_step, @@ -74,8 +78,8 @@ rd_smspec_type *rd_smspec_fread_alloc(const std::string &header_file, bool include_restart); void rd_smspec_free(rd_smspec_type *); -int rd_smspec_get_general_var_params_index(const rd_smspec_type *rd_smspec, - const char *lookup_kw); +size_t rd_smspec_get_general_var_params_index(const rd_smspec_type *rd_smspec, + const char *lookup_kw); bool rd_smspec_has_general_var(const rd_smspec_type *rd_smspec, const char *lookup_kw); std::vector @@ -91,12 +95,12 @@ std::vector rd_smspec_alloc_well_list(const rd_smspec_type *smspec, const char *pattern); std::vector rd_smspec_alloc_group_list(const rd_smspec_type *smspec, const char *pattern); -int rd_smspec_get_first_step(const rd_smspec_type *rd_smspec); +size_t rd_smspec_get_first_step(const rd_smspec_type *rd_smspec); int rd_smspec_get_restart_step(const rd_smspec_type *rd_smspec); const char *rd_smspec_get_restart_case(const rd_smspec_type *rd_smspec); const int *rd_smspec_get_grid_dims(const rd_smspec_type *smspec); -int rd_smspec_get_params_size(const rd_smspec_type *smspec); -int rd_smspec_num_nodes(const rd_smspec_type *smspec); +size_t rd_smspec_get_params_size(const rd_smspec_type *smspec); +size_t rd_smspec_num_nodes(const rd_smspec_type *smspec); bool rd_smspec_equal(const rd_smspec_type *self, const rd_smspec_type *other); UnitSystem rd_smspec_get_unit_system(const rd_smspec_type *smspec); @@ -123,18 +127,16 @@ const rd::smspec_node *rd_smspec_add_node(rd_smspec_type *rd_smspec, const char *unit, float default_value); -const rd::smspec_node *rd_smspec_add_node(rd_smspec_type *rd_smspec, - int params_index, const char *keyword, - const char *wgname, int num, - const char *unit, - float default_value); +const rd::smspec_node * +rd_smspec_add_node(rd_smspec_type *rd_smspec, size_t params_index, + const char *keyword, const char *wgname, int num, + const char *unit, float default_value); -const rd::smspec_node *rd_smspec_add_node(rd_smspec_type *rd_smspec, - int params_index, const char *keyword, - const char *wgname, int num, - const char *unit, const char *lgr, - int lgr_i, int lgr_j, int lgr_k, - float default_value); +const rd::smspec_node * +rd_smspec_add_node(rd_smspec_type *rd_smspec, size_t params_index, + const char *keyword, const char *wgname, int num, + const char *unit, const char *lgr, int lgr_i, int lgr_j, + int lgr_k, float default_value); using rd_smspec_ptr = std::unique_ptr; diff --git a/lib/include/resdata/rd_sum.hpp b/lib/include/resdata/rd_sum.hpp index 0815ba208..7a8ca7f28 100644 --- a/lib/include/resdata/rd_sum.hpp +++ b/lib/include/resdata/rd_sum.hpp @@ -27,14 +27,17 @@ double rd_sum_get_from_sim_time(const rd_sum_type *rd_sum, time_t sim_time, const rd::smspec_node *node); double rd_sum_time2days(const rd_sum_type *rd_sum, time_t sim_time); -int rd_sum_get_report_step_from_time(const rd_sum_type *sum, time_t sim_time); -int rd_sum_get_report_step_from_days(const rd_sum_type *sum, double sim_days); +std::optional rd_sum_get_report_step_from_time(const rd_sum_type *sum, + time_t sim_time); +std::optional rd_sum_get_report_step_from_days(const rd_sum_type *sum, + double sim_days); bool rd_sum_check_sim_time(const rd_sum_type *sum, time_t sim_time); bool rd_sum_check_sim_days(const rd_sum_type *sum, double sim_days); const char *rd_sum_get_unit(const rd_sum_type *sum, const char *gen_key); -double rd_sum_iget(const rd_sum_type *rd_sum, int time_index, int param_index); -int rd_sum_get_data_length(const rd_sum_type *rd_sum); +double rd_sum_iget(const rd_sum_type *rd_sum, size_t time_index, + size_t param_index); +size_t rd_sum_get_data_length(const rd_sum_type *rd_sum); rd_sum_type *rd_sum_fread_alloc(const char *, const std::vector &data_files, @@ -49,10 +52,10 @@ rd_sum_ptr rd_sum_alloc_resample(const rd_sum_type *rd_sum, const char *rd_case, bool lower_extrapolation, bool upper_extrapolation); -double rd_sum_get_general_var(const rd_sum_type *rd_sum, int time_index, +double rd_sum_get_general_var(const rd_sum_type *rd_sum, size_t time_index, const char *lookup_kw); -int rd_sum_get_general_var_params_index(const rd_sum_type *rd_sum, - const char *lookup_kw); +size_t rd_sum_get_general_var_params_index(const rd_sum_type *rd_sum, + const char *lookup_kw); const rd::smspec_node *rd_sum_get_general_var_node(const rd_sum_type *rd_sum, const char *lookup_kw); bool rd_sum_has_general_var(const rd_sum_type *rd_sum, const char *lookup_kw); @@ -64,19 +67,21 @@ double rd_sum_get_general_var_from_sim_time(const rd_sum_type *rd_sum, UnitSystem rd_sum_get_unit_system(const rd_sum_type *rd_sum); int rd_sum_get_restart_step(const rd_sum_type *rd_sum); -int rd_sum_get_first_gt(const rd_sum_type *rd_sum, int param_index, - double limit); -int rd_sum_get_first_lt(const rd_sum_type *rd_sum, int param_index, - double limit); -int rd_sum_get_last_report_step(const rd_sum_type *rd_sum); -int rd_sum_get_first_report_step(const rd_sum_type *rd_sum); -time_t rd_sum_get_report_time(const rd_sum_type *rd_sum, int report_step); -time_t rd_sum_iget_sim_time(const rd_sum_type *rd_sum, int index); -double rd_sum_iget_sim_days(const rd_sum_type *rd_sum, int time_index); -int rd_sum_iget_report_step(const rd_sum_type *rd_sum, int internal_index); +std::optional rd_sum_get_first_gt(const rd_sum_type *rd_sum, + size_t param_index, double limit); +std::optional rd_sum_get_first_lt(const rd_sum_type *rd_sum, + size_t param_index, double limit); +size_t rd_sum_get_last_report_step(const rd_sum_type *rd_sum); +size_t rd_sum_get_first_report_step(const rd_sum_type *rd_sum); +time_t rd_sum_get_report_time(const rd_sum_type *rd_sum, size_t report_step); +time_t rd_sum_iget_sim_time(const rd_sum_type *rd_sum, size_t index); +double rd_sum_iget_sim_days(const rd_sum_type *rd_sum, size_t time_index); +size_t rd_sum_iget_report_step(const rd_sum_type *rd_sum, + size_t internal_index); std::vector rd_sum_alloc_data_vector(const rd_sum_type *rd_sum, - int data_index, bool report_only); + size_t data_index, + bool report_only); std::vector rd_sum_alloc_time_vector(const rd_sum_type *rd_sum, bool report_only); time_t rd_sum_get_data_start(const rd_sum_type *rd_sum); @@ -102,7 +107,7 @@ rd_sum_select_matching_general_var_list(const rd_sum_type *rd_sum, rd_smspec_type *rd_sum_get_smspec(const rd_sum_type *rd_sum); rd_smspec_var_type rd_sum_identify_var_type(const char *var); -int rd_sum_iget_report_end(const rd_sum_type *rd_sum, int report_step); +size_t rd_sum_iget_report_end(const rd_sum_type *rd_sum, size_t report_step); void rd_sum_set_case(rd_sum_type *rd_sum, const std::string &input_arg); void rd_sum_fwrite(const rd_sum_type *rd_sum); bool rd_sum_can_write(const rd_sum_type *rd_sum); @@ -117,7 +122,7 @@ const rd::smspec_node *rd_sum_add_local_var(rd_sum_type *rd_sum, const char *unit, const char *lgr, int lgr_i, int lgr_j, int lgr_k, float default_value); -rd_sum_tstep_type *rd_sum_add_tstep(rd_sum_type *rd_sum, int report_step, +rd_sum_tstep_type *rd_sum_add_tstep(rd_sum_type *rd_sum, size_t report_step, double sim_seconds); void rd_sum_export_csv(const rd_sum_type *rd_sum, const std::string &filename, diff --git a/lib/include/resdata/rd_sum_data.hpp b/lib/include/resdata/rd_sum_data.hpp index be62b09c7..8e7c37b09 100644 --- a/lib/include/resdata/rd_sum_data.hpp +++ b/lib/include/resdata/rd_sum_data.hpp @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -25,36 +26,43 @@ bool rd_sum_data_fread(rd_sum_data_type *data, rd_sum_data_type *rd_sum_data_alloc_writer(rd_smspec_type *smspec); rd_sum_data_type *rd_sum_data_alloc(rd_smspec_type *smspec); double rd_sum_data_time2days(const rd_sum_data_type *data, time_t sim_time); -int rd_sum_data_get_report_step_from_time(const rd_sum_data_type *data, - time_t sim_time); -int rd_sum_data_get_report_step_from_days(const rd_sum_data_type *data, - double days); +std::optional +rd_sum_data_get_report_step_from_time(const rd_sum_data_type *data, + time_t sim_time); +std::optional +rd_sum_data_get_report_step_from_days(const rd_sum_data_type *data, + double days); bool rd_sum_data_check_sim_time(const rd_sum_data_type *data, time_t sim_time); bool rd_sum_data_check_sim_days(const rd_sum_data_type *data, double sim_days); +/** One value per time step. Throws std::out_of_range for an invalid index. */ std::vector rd_sum_data_alloc_data_vector(const rd_sum_data_type *data, - int data_index, - bool report_only); -std::vector rd_sum_data_alloc_time_vector(const rd_sum_data_type *data, - bool report_only); + size_t params_index); +/** One value per report step present in the data. */ +std::vector +rd_sum_data_alloc_report_data_vector(const rd_sum_data_type *data, + size_t params_index); +std::vector rd_sum_data_alloc_time_vector(const rd_sum_data_type *data); +std::vector +rd_sum_data_alloc_report_time_vector(const rd_sum_data_type *data); time_t rd_sum_data_get_data_start(const rd_sum_data_type *data); time_t rd_sum_data_get_report_time(const rd_sum_data_type *data, - int report_step); + size_t report_step); double rd_sum_data_get_first_day(const rd_sum_data_type *data); time_t rd_sum_data_get_sim_end(const rd_sum_data_type *data); double rd_sum_data_get_sim_length(const rd_sum_data_type *data); -double rd_sum_data_iget(const rd_sum_data_type *data, int internal_index, - int params_index); +double rd_sum_data_iget(const rd_sum_data_type *data, size_t internal_index, + size_t params_index); -double rd_sum_data_iget_sim_days(const rd_sum_data_type *, int); -time_t rd_sum_data_iget_sim_time(const rd_sum_data_type *, int); +double rd_sum_data_iget_sim_days(const rd_sum_data_type *, size_t); +time_t rd_sum_data_iget_sim_time(const rd_sum_data_type *, size_t); std::vector rd_sum_data_get_interp_vector(const rd_sum_data_type *data, time_t sim_time, const rd_sum_vector_type *keylist, double missing_value); void rd_sum_data_free(rd_sum_data_type *); -int rd_sum_data_get_last_report_step(const rd_sum_data_type *data); -int rd_sum_data_get_first_report_step(const rd_sum_data_type *data); +size_t rd_sum_data_get_last_report_step(const rd_sum_data_type *data); +size_t rd_sum_data_get_first_report_step(const rd_sum_data_type *data); double rd_sum_data_get_from_sim_time(const rd_sum_data_type *data, time_t sim_time, @@ -63,19 +71,20 @@ double rd_sum_data_get_from_sim_days(const rd_sum_data_type *data, double sim_days, const rd::smspec_node &smspec_node); -int rd_sum_data_get_length(const rd_sum_data_type *data); -int rd_sum_data_iget_report_step(const rd_sum_data_type *data, - int internal_index); -int rd_sum_data_iget_report_end(const rd_sum_data_type *data, int report_step); +size_t rd_sum_data_get_length(const rd_sum_data_type *data); +size_t rd_sum_data_iget_report_step(const rd_sum_data_type *data, + size_t internal_index); +size_t rd_sum_data_iget_report_end(const rd_sum_data_type *data, + size_t report_step); rd_sum_tstep_type *rd_sum_data_add_new_tstep(rd_sum_data_type *data, - int report_step, + size_t report_step, double sim_seconds); double rd_sum_data_iget_last_value(const rd_sum_data_type *data, - int param_index); + size_t param_index); double rd_sum_data_iget_first_value(const rd_sum_data_type *data, - int param_index); + size_t param_index); void rd_sum_data_init_double_vector(const rd_sum_data_type *data, - int params_index, double *output_data); + size_t params_index, double *output_data); std::vector rd_sum_data_alloc_seconds_solution(const rd_sum_data_type *data, const rd::smspec_node &node, double value, diff --git a/lib/include/resdata/rd_sum_tstep.hpp b/lib/include/resdata/rd_sum_tstep.hpp index bf11e3f9c..44f632f84 100644 --- a/lib/include/resdata/rd_sum_tstep.hpp +++ b/lib/include/resdata/rd_sum_tstep.hpp @@ -13,13 +13,13 @@ typedef struct rd_sum_tstep_struct rd_sum_tstep_type; void rd_sum_tstep_free(rd_sum_tstep_type *ministep); -rd_sum_tstep_type *rd_sum_tstep_alloc_from_file(int report_step, +rd_sum_tstep_type *rd_sum_tstep_alloc_from_file(size_t report_step, int ministep_nr, const rd::KW *params_kw, const char *src_file, const rd_smspec_type *smspec); -rd_sum_tstep_type *rd_sum_tstep_alloc_new(int report_step, int ministep, +rd_sum_tstep_type *rd_sum_tstep_alloc_new(size_t report_step, int ministep, float sim_seconds, const rd_smspec_type *smspec); @@ -29,18 +29,18 @@ void rd_sum_tstep_set_from_node(rd_sum_tstep_type *tstep, double rd_sum_tstep_get_from_node(const rd_sum_tstep_type *tstep, const rd::smspec_node &smspec_node); -double rd_sum_tstep_iget(const rd_sum_tstep_type *ministep, int index); +double rd_sum_tstep_iget(const rd_sum_tstep_type *ministep, size_t index); time_t rd_sum_tstep_get_sim_time(const rd_sum_tstep_type *ministep); double rd_sum_tstep_get_sim_days(const rd_sum_tstep_type *ministep); double rd_sum_tstep_get_sim_seconds(const rd_sum_tstep_type *ministep); -int rd_sum_tstep_get_report(const rd_sum_tstep_type *ministep); +size_t rd_sum_tstep_get_report(const rd_sum_tstep_type *ministep); int rd_sum_tstep_get_ministep(const rd_sum_tstep_type *ministep); void rd_sum_tstep_fwrite(const rd_sum_tstep_type *ministep, - const int *index_map, int index_map_size, + const std::vector &index_map, ERT::FortIO &fortio); -void rd_sum_tstep_iset(rd_sum_tstep_type *tstep, int index, float value); +void rd_sum_tstep_iset(rd_sum_tstep_type *tstep, size_t index, float value); void rd_sum_tstep_set_from_key(rd_sum_tstep_type *tstep, const char *gen_key, float value); diff --git a/lib/include/resdata/rd_sum_vector.hpp b/lib/include/resdata/rd_sum_vector.hpp index 0b065cc5a..5389115d3 100644 --- a/lib/include/resdata/rd_sum_vector.hpp +++ b/lib/include/resdata/rd_sum_vector.hpp @@ -1,8 +1,10 @@ #pragma once -#include +#include #include +#include #include +#include typedef struct rd_sum_vector_struct rd_sum_vector_type; @@ -14,14 +16,15 @@ bool rd_sum_vector_add_key(rd_sum_vector_type *keylist, const char *key); void rd_sum_vector_add_keys(rd_sum_vector_type *keylist, const char *pattern); const char *rd_sum_vector_iget_key(const rd_sum_vector_type *rd_sum_vector, - int index); + size_t index); bool rd_sum_vector_iget_is_rate(const rd_sum_vector_type *rd_sum_vector, - int index); -int rd_sum_vector_iget_param_index(const rd_sum_vector_type *rd_sum_vector, - int index); -int rd_sum_vector_get_size(const rd_sum_vector_type *rd_sum_vector); -bool rd_sum_vector_iget_valid(const rd_sum_vector_type *rd_sum_vector, - int index); + size_t index); +/** Returns std::nullopt for keys that are not present in the summary case + * (see rd_sum_vector_alloc_layout_copy). */ +std::optional +rd_sum_vector_iget_param_index(const rd_sum_vector_type *rd_sum_vector, + size_t index); +size_t rd_sum_vector_get_size(const rd_sum_vector_type *rd_sum_vector); rd_sum_vector_type * rd_sum_vector_alloc_layout_copy(const rd_sum_vector_type *src_vector, diff --git a/lib/include/resdata/rd_util.hpp b/lib/include/resdata/rd_util.hpp index fa1e8ede6..7984d418a 100644 --- a/lib/include/resdata/rd_util.hpp +++ b/lib/include/resdata/rd_util.hpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -103,8 +104,10 @@ void rd_set_datetime_values(time_t t, int *sec, int *min, int *hour, int *mday, int *month, int *year); bool rd_path_access(const char *rd_case); namespace rd { +/// Instantiated for int and size_t. +template std::filesystem::path filename(std::filesystem::path path, FileType, - bool fmt_file, int report_nr = -1); + bool fmt_file, ReportNr report_nr = -1); inline bool try_exists(std::filesystem::path p) noexcept { std::error_code ec; diff --git a/lib/include/resdata/smspec_node.hpp b/lib/include/resdata/smspec_node.hpp index 77d05ac37..5f4d09fa8 100644 --- a/lib/include/resdata/smspec_node.hpp +++ b/lib/include/resdata/smspec_node.hpp @@ -62,7 +62,8 @@ class smspec_node { rate_variable; /* Is this a rate variable (i.e. WOPR) or a state variable (i.e. BPR). Relevant when doing time interpolation. */ bool total_variable; /* Is this a total variable like WOPT? */ bool historical; /* Does the name end with 'H'? */ - int params_index; /* The index of this variable (applies to all the vectors - in particular the PARAMS vectors of the summary files *.Snnnn / *.UNSMRY ). */ + size_t + params_index; /* The index of this variable (applies to all the vectors - in particular the PARAMS vectors of the summary files *.Snnnn / *.UNSMRY ). */ float default_value; /* Default value for this variable. */ static rd_smspec_var_type identify_special_var(const char *var); @@ -96,28 +97,29 @@ class smspec_node { int cmp(const smspec_node &node2) const; static int cmp(const smspec_node &node1, const smspec_node &node2); - smspec_node(int param_index, const char *keyword, const char *wgname, + smspec_node(size_t param_index, const char *keyword, const char *wgname, int num, const char *unit, const int grid_dims[3], float default_value, const char *key_join_string); - smspec_node(int param_index, const char *keyword, const char *wgname, + smspec_node(size_t param_index, const char *keyword, const char *wgname, const char *unit, const char *lgr, int lgr_i, int lgr_j, int lgr_k, float default_value, const char *key_join_string); - smspec_node(int param_index, const char *keyword, const char *unit, + smspec_node(size_t param_index, const char *keyword, const char *unit, float default_value); - smspec_node(int param_index, const char *keyword, int num, const char *unit, - const int grid_dims[3], float default_value, + smspec_node(size_t param_index, const char *keyword, int num, + const char *unit, const int grid_dims[3], float default_value, const char *key_join_string); - smspec_node(int param_index, const char *keyword, int num, const char *unit, - float default_value, const char *key_join_string); - smspec_node(int param_index, const char *keyword, const char *wgname, + smspec_node(size_t param_index, const char *keyword, int num, + const char *unit, float default_value, + const char *key_join_string); + smspec_node(size_t param_index, const char *keyword, const char *wgname, const char *unit, float default_value, const char *key_join_string); - smspec_node(int param_index, const char *keyword, const char *wgname, + smspec_node(size_t param_index, const char *keyword, const char *wgname, int num, const char *unit, float default_value, const char *key_join_string); - smspec_node(const smspec_node &node, int param_index); + smspec_node(const smspec_node &node, size_t param_index); static rd_smspec_var_type identify_var_type(const char *var); @@ -142,7 +144,7 @@ class smspec_node { [[nodiscard]] bool is_total() const { return total_variable; }; [[nodiscard]] bool is_historical() const { return historical; }; [[nodiscard]] bool need_nums() const; - [[nodiscard]] int get_params_index() const { return params_index; }; + [[nodiscard]] size_t get_params_index() const { return params_index; }; [[nodiscard]] float get_default() const { return default_value; }; [[nodiscard]] const char *get_lgr_name() const; // Will be garbage for smspec_nodes which are not related to an LGR. diff --git a/lib/private-include/detail/resdata/rd_sum_file_data.hpp b/lib/private-include/detail/resdata/rd_sum_file_data.hpp index 58f803306..a27384090 100644 --- a/lib/private-include/detail/resdata/rd_sum_file_data.hpp +++ b/lib/private-include/detail/resdata/rd_sum_file_data.hpp @@ -1,9 +1,10 @@ #include #include +#include #include #include #include -#include +#include #include #include @@ -16,52 +17,47 @@ namespace rd { -#define INVALID_MINISTEP_NR -1 -#define INVALID_TIME_T 0 - struct IndexNode { - IndexNode(time_t sim_time, double sim_seconds, int report_step) + IndexNode(time_t sim_time, double sim_seconds, size_t report_step) : sim_time(sim_time), sim_seconds(sim_seconds), report_step(report_step) {} time_t sim_time; double sim_seconds; - int report_step; + size_t report_step; +}; + +struct ReportRange { + size_t first; + size_t last; }; class TimeIndex { public: - void add(time_t sim_time, double sim_seconds, int report_step) { - if (report_step < 0) - throw std::invalid_argument("report step cannot be negative"); - int internal_index = static_cast(this->nodes.size()); + void add(time_t sim_time, double sim_seconds, size_t report_step) { + if (report_step == std::numeric_limits::max()) + throw std::invalid_argument("report step is too large"); + size_t internal_index = this->nodes.size(); this->nodes.emplace_back(sim_time, sim_seconds, report_step); /* Indexing internal_index - report_step */ - if (static_cast(this->report_map.size()) <= report_step) - this->report_map.resize( - report_step + 1, - std::pair(std::numeric_limits::max(), -1)); + if (this->report_map.size() <= report_step) + this->report_map.resize(report_step + 1, std::nullopt); auto &range = this->report_map[report_step]; - range.first = std::min(range.first, internal_index); - range.second = std::max(range.second, internal_index); + if (range) { + range->first = std::min(range->first, internal_index); + range->last = std::max(range->last, internal_index); + } else + range = ReportRange{internal_index, internal_index}; } - bool has_report(int report_step) const { - - if (report_step < 0) + bool has_report(size_t report_step) const { + if (report_step >= this->report_map.size()) return false; - 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; + return this->report_map[report_step].has_value(); } void clear() { @@ -70,28 +66,26 @@ class TimeIndex { } const IndexNode &operator[](size_t index) const { - return this->nodes[index]; + return this->nodes.at(index); } const IndexNode &back() const { return this->nodes.back(); } + bool empty() const { return this->nodes.empty(); } + size_t size() const { return this->nodes.size(); } - std::pair &report_range(int report_step) { - if (report_step < 0) - throw std::invalid_argument("report step cannot be negative"); - return this->report_map[report_step]; - } + /** Returns std::nullopt when @report_step is not present in this file. */ + std::optional report_range(size_t report_step) const { + if (report_step >= this->report_map.size()) + return std::nullopt; - const std::pair &report_range(int report_step) const { - if (report_step < 0) - throw std::invalid_argument("report step cannot be negative"); return this->report_map[report_step]; } private: std::vector nodes; - std::vector> report_map; + std::vector> report_map; }; class unsmry_loader; @@ -103,35 +97,37 @@ class rd_sum_file_data { ~rd_sum_file_data(); const rd_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 length() const; + size_t length_before(time_t end_time) const; + void get_time(size_t length, time_t *data); + void get_data(size_t params_index, size_t length, double *data); + size_t 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; - double iget_sim_days(int time_index) const; - double iget_sim_seconds(int time_index) const; - rd_sum_tstep_type *iget_ministep(int internal_index) const; + double iget(size_t time_index, size_t params_index) const; + time_t iget_sim_time(size_t time_index) const; + double iget_sim_days(size_t time_index) const; + double iget_sim_seconds(size_t time_index) const; + rd_sum_tstep_type *iget_ministep(size_t internal_index) const; double get_days_start() const; double get_sim_length() const; - std::pair report_range(int report_step) const; + /** Returns std::nullopt when @report_step is not present in this file. */ + std::optional report_range(size_t report_step) const; /** Returns the last report step strictly before @end_time */ - 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, - double default_value); - int first_report() const; - int last_report() const; - int iget_report(int time_index) 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; - - rd_sum_tstep_type *add_new_tstep(int report_step, double sim_seconds); + size_t report_before(time_t end_time) const; + /** Appends one value per present report with time index below @end_index */ + void get_time_report(size_t end_index, std::vector &data); + void get_data_report(std::optional params_index, size_t end_index, + double default_value, std::vector &data); + size_t first_report() const; + size_t last_report() const; + size_t iget_report(size_t time_index) const; + bool has_report(size_t report_step) const; + std::optional report_step_from_days(double sim_days) const; + std::optional report_step_from_time(time_t sim_time) const; + + rd_sum_tstep_type *add_new_tstep(size_t report_step, double sim_seconds); bool can_write() const; void fwrite_unified(ERT::FortIO &fortio) const; void fwrite_multiple(const std::string &rd_case, bool fmt_case) const; @@ -147,9 +143,9 @@ class rd_sum_file_data { std::unique_ptr loader; void build_index(); - void fwrite_report(int report_step, ERT::FortIO &fortio) const; + void fwrite_report(size_t report_step, ERT::FortIO &fortio) const; bool check_file(rd::File *rd_file); - void add_rd_file(int report_step, rd::FileView &summary_view); + void add_rd_file(size_t report_step, rd::FileView &summary_view); }; } // namespace rd diff --git a/lib/private-include/detail/resdata/rd_unsmry_loader.hpp b/lib/private-include/detail/resdata/rd_unsmry_loader.hpp index c94508993..8c48db586 100644 --- a/lib/private-include/detail/resdata/rd_unsmry_loader.hpp +++ b/lib/private-include/detail/resdata/rd_unsmry_loader.hpp @@ -16,23 +16,23 @@ class unsmry_loader { unsmry_loader(const rd_smspec_type *smspec, const std::string &filename, FileMode file_options = FileMode::DEFAULT); - std::vector get_vector(int pos) const; + std::vector get_vector(size_t pos) const; std::vector sim_seconds() const; std::vector sim_time() const; - int length() const; + size_t length() const; - time_t iget_sim_time(int time_index) const; - double iget_sim_seconds(int time_index) const; - std::vector report_steps(int offset) const; - double iget(int time_index, int params_index) const; + time_t iget_sim_time(size_t time_index) const; + double iget_sim_seconds(size_t time_index) const; + std::vector report_steps(size_t offset) const; + double iget(size_t time_index, size_t params_index) const; private: - int size; //Number of entries in the smspec index + size_t size; //Number of entries in the smspec index rd::TimeInfo time_info; time_t sim_start; - int m_length; //Number of PARAMS in the UNSMRY file + size_t m_length; //Number of PARAMS in the UNSMRY file - std::array date_index; + std::array date_index; std::unique_ptr file{nullptr}; std::shared_ptr file_view; }; diff --git a/lib/resdata/FortIO.cpp b/lib/resdata/FortIO.cpp index b4b146c05..5614e1047 100644 --- a/lib/resdata/FortIO.cpp +++ b/lib/resdata/FortIO.cpp @@ -312,9 +312,9 @@ bool FortIO::data_fskip(size_t element_size, size_t element_count, } void FortIO::data_fseek(offset_type data_offset, size_t data_element, - size_t element_size, int element_count, - int block_size) { - if (element_count < 0 || data_element >= static_cast(element_count)) + size_t element_size, size_t element_count, + size_t block_size) { + if (data_element >= element_count) throw std::invalid_argument( fmt::format("Element index is out of range: 0 <= {} < {}", data_element, element_count)); diff --git a/lib/resdata/rd_file_view.cpp b/lib/resdata/rd_file_view.cpp index 2dd48835b..3a855ab4c 100644 --- a/lib/resdata/rd_file_view.cpp +++ b/lib/resdata/rd_file_view.cpp @@ -75,28 +75,24 @@ rd::KW *FileView::get_kw(const std::shared_ptr &file_kw) { } std::shared_ptr -FileView::validate_index_fload_kw(const std::string &kw, int index, +FileView::validate_index_fload_kw(const std::string &kw, size_t index, rd_type_enum expected, size_t requested_count, size_t out_count) const { - if (index < 0) - throw std::invalid_argument( - fmt::format("Got negative index in index_fload_kw: {}", index)); - if (out_count < requested_count) throw std::invalid_argument(fmt::format( "Buffer of {} elements is too small for the {} elements requested " "from keyword {}", out_count, requested_count, kw)); - auto file_kw = get_file_kw(kw, static_cast(index)); + auto file_kw = get_file_kw(kw, index); if (!file_kw) throw std::invalid_argument(std::string("Keyword '") + kw + "' index " + std::to_string(index) + " not found in file view"); - assert_kw_data_type(file_kw->get_data_type(), expected, kw, - static_cast(index), filename()); + assert_kw_data_type(file_kw->get_data_type(), expected, kw, index, + filename()); return file_kw; } @@ -369,7 +365,7 @@ FileView::restart_view_from_sim_days(double sim_days) { return restart_view_from_seqnum_index(block.value()); } -std::shared_ptr FileView::summary_view(int report_step) { +std::shared_ptr FileView::summary_view(size_t report_step) { return blockview(SEQHDR_KW, SEQHDR_KW, report_step); } diff --git a/lib/resdata/rd_kw.cpp b/lib/resdata/rd_kw.cpp index 082eaa1a9..3cd98a0fa 100644 --- a/lib/resdata/rd_kw.cpp +++ b/lib/resdata/rd_kw.cpp @@ -471,8 +471,8 @@ void rd::KW::fread_data(rd::KW *rd_kw, ERT::FortIO &fortio) { as stored by rd_file_kw. */ void rd::KW::fread_indexed_data(ERT::FortIO &fortio, offset_type kw_offset, - rd_data_type data_type, int element_count, - const std::vector &index_map, + rd_data_type data_type, size_t element_count, + const std::vector &index_map, char *io_buffer) { size_t sizeof_iotype = rd_type_get_sizeof_iotype(data_type); @@ -497,8 +497,8 @@ void rd::KW::fread_indexed_data(ERT::FortIO &fortio, offset_type kw_offset, [&](const auto &vec) { using VecT = std::decay_t; for (size_t index = 0; index < index_map.size(); index++) { - int element_index = index_map[index]; - if (element_index < 0 || element_index >= element_count) + size_t element_index = index_map[index]; + if (element_index >= element_count) throw std::invalid_argument(fmt::format( "Element index is out of range 0 <= {} < {}", element_index, element_count)); @@ -529,9 +529,9 @@ void rd::KW::fread_indexed_data(ERT::FortIO &fortio, offset_type kw_offset, offset_type data_offset = kw_offset + RD_KW_HEADER_FORTIO_SIZE; for (size_t index = 0; index < index_map.size(); index++) { - int element_index = index_map[index]; + size_t element_index = index_map[index]; - if (element_index < 0 || element_index >= element_count) + if (element_index >= element_count) throw std::invalid_argument( fmt::format("Element index is out of range 0 <= {} < {}", element_index, element_count)); diff --git a/lib/resdata/rd_smspec.cpp b/lib/resdata/rd_smspec.cpp index 08dc7ef72..2ec748114 100644 --- a/lib/resdata/rd_smspec.cpp +++ b/lib/resdata/rd_smspec.cpp @@ -107,9 +107,9 @@ struct rd_smspec_struct { std::vector> smspec_nodes; bool write_mode; bool need_nums; - std::vector index_map; - std::map inv_index_map; - int params_size; + std::vector index_map; + std::map inv_index_map; + size_t params_size; int grid_dims[3]; /* Grid dimensions - in DIMENS[1,2,3] */ int num_regions; @@ -213,7 +213,7 @@ const rd::smspec_node *rd_smspec_get_var_node(const node_map &mp, } //end namespace -int rd_smspec_num_nodes(const rd_smspec_type *smspec) { +size_t rd_smspec_num_nodes(const rd_smspec_type *smspec) { return smspec->smspec_nodes.size(); } @@ -224,7 +224,7 @@ int rd_smspec_num_nodes(const rd_smspec_type *smspec) { params_size member. */ -int rd_smspec_get_params_size(const rd_smspec_type *smspec) { +size_t rd_smspec_get_params_size(const rd_smspec_type *smspec) { return smspec->params_size; } @@ -237,7 +237,7 @@ static rd_smspec_ptr rd_smspec_alloc_empty(bool write_mode, rd_smspec->key_join_string = key_join_string; rd_smspec->header_file = ""; - rd_smspec->params_size = -1; + rd_smspec->params_size = 0; /* The unit system is given as an integer in the INTEHEAD keyword. The INTEHEAD @@ -253,20 +253,21 @@ static rd_smspec_ptr rd_smspec_alloc_empty(bool write_mode, return rd_smspec; } -std::vector rd_smspec_alloc_mapping(const rd_smspec_type *self, - const rd_smspec_type *other) { - int params_size = rd_smspec_get_params_size(self); - std::vector mapping(params_size, -1); +std::vector> +rd_smspec_alloc_mapping(const rd_smspec_type *self, + const rd_smspec_type *other) { + size_t params_size = rd_smspec_get_params_size(self); + std::vector> mapping(params_size, std::nullopt); - for (int i = 0; i < rd_smspec_num_nodes(self); i++) { + for (size_t i = 0; i < rd_smspec_num_nodes(self); i++) { const rd::smspec_node &self_node = rd_smspec_iget_node_w_node_index(self, i); - int self_index = self_node.get_params_index(); + size_t self_index = self_node.get_params_index(); const char *key = self_node.get_gen_key1(); if (rd_smspec_has_general_var(other, key)) { const rd::smspec_node &other_node = rd_smspec_get_general_var_node(other, key); - int other_index = other_node.get_params_index(); + size_t other_index = other_node.get_params_index(); mapping[self_index] = other_index; } } @@ -281,15 +282,16 @@ std::vector rd_smspec_alloc_mapping(const rd_smspec_type *self, */ const rd::smspec_node & -rd_smspec_iget_node_w_node_index(const rd_smspec_type *smspec, int node_index) { +rd_smspec_iget_node_w_node_index(const rd_smspec_type *smspec, + size_t node_index) { const auto &node = smspec->smspec_nodes[node_index]; return *node.get(); } const rd::smspec_node & rd_smspec_iget_node_w_params_index(const rd_smspec_type *smspec, - int params_index) { - int node_index = smspec->inv_index_map.at(params_index); + size_t params_index) { + size_t node_index = smspec->inv_index_map.at(params_index); return rd_smspec_iget_node_w_node_index(smspec, node_index); } @@ -300,7 +302,7 @@ rd_smspec_iget_node_w_params_index(const rd_smspec_type *smspec, */ static rd_data_type get_wgnames_type(const rd_smspec_type *smspec) { size_t max_len = 0; - for (int i = 0; i < rd_smspec_num_nodes(smspec); ++i) { + for (size_t i = 0; i < rd_smspec_num_nodes(smspec); ++i) { const rd::smspec_node &node = rd_smspec_iget_node_w_node_index(smspec, i); const char *name = node.get_wgname(); @@ -331,7 +333,7 @@ static void rd_smspec_fwrite_RESTART(const rd_smspec_type *smspec, static void rd_smspec_fwrite_DIMENS(const rd_smspec_type *smspec, ERT::FortIO &fortio) { - int num_nodes = rd_smspec_num_nodes(smspec); + int num_nodes = static_cast(rd_smspec_num_nodes(smspec)); rd::KW dimens_kw{ DIMENS_KW, std::vector{num_nodes, smspec->grid_dims[0], smspec->grid_dims[1], @@ -357,7 +359,7 @@ static void rd_smspec_fortio_fwrite(const rd_smspec_type *smspec, rd_smspec_fwrite_RESTART(smspec, fortio); rd_smspec_fwrite_DIMENS(smspec, fortio); - int num_nodes = rd_smspec_num_nodes(smspec); + size_t num_nodes = rd_smspec_num_nodes(smspec); rd::KW keywords_kw{KEYWORDS_KW, num_nodes, RD_CHAR}; rd::KW units_kw{UNITS_KW, num_nodes, RD_CHAR}; std::unique_ptr nums_kw{nullptr}; @@ -371,7 +373,7 @@ static void rd_smspec_fortio_fwrite(const rd_smspec_type *smspec, if (smspec->need_nums) nums_kw = std::make_unique(NUMS_KW, num_nodes, RD_INT); - for (int i = 0; i < rd_smspec_num_nodes(smspec); i++) { + for (size_t i = 0; i < rd_smspec_num_nodes(smspec); i++) { const rd::smspec_node &smspec_node = rd_smspec_iget_node_w_node_index(smspec, i); /* @@ -393,25 +395,23 @@ static void rd_smspec_fortio_fwrite(const rd_smspec_type *smspec, ignored when/if this smspec file is read at a later stage. */ - size_t ii = static_cast(i); - std::string wgname{DUMMY_WELL}; if (smspec_node.get_var_type() == RD_SMSPEC_INVALID_VAR) { - keywords_kw.set_padded(ii, "WWCT"); - units_kw.set_padded(ii, "????????"); - wgnames_kw.set_padded(ii, wgname); + keywords_kw.set_padded(i, "WWCT"); + units_kw.set_padded(i, "????????"); + wgnames_kw.set_padded(i, wgname); } else { - keywords_kw.set_padded(ii, smspec_node.get_keyword()); - units_kw.set_padded(ii, smspec_node.get_unit()); + keywords_kw.set_padded(i, smspec_node.get_keyword()); + units_kw.set_padded(i, smspec_node.get_unit()); { if (smspec_node.get_wgname()) wgname = smspec_node.get_wgname(); - wgnames_kw.set_padded(ii, wgname); + wgnames_kw.set_padded(i, wgname); } } if (nums_kw) - nums_kw->at(ii) = smspec_node.get_num(); + nums_kw->at(i) = smspec_node.get_num(); } keywords_kw.fwrite(fortio); wgnames_kw.fwrite(fortio); @@ -669,7 +669,7 @@ static void rd_smspec_load_restart(rd_smspec_type *rd_smspec, static const rd::smspec_node * rd_smspec_insert_node(rd_smspec_type *rd_smspec, std::unique_ptr smspec_node) { - int params_index = smspec_node->get_params_index(); + size_t params_index = smspec_node->get_params_index(); /* This indexing must be used when writing. */ rd_smspec->index_map.push_back(params_index); @@ -686,11 +686,10 @@ rd_smspec_insert_node(rd_smspec_type *rd_smspec, rd_smspec->smspec_nodes.push_back(std::move(smspec_node)); - if (params_index > rd_smspec->params_size) + if (params_index >= rd_smspec->params_size) rd_smspec->params_size = params_index + 1; - if (static_cast(rd_smspec->smspec_nodes.size()) > - rd_smspec->params_size) + if (rd_smspec->smspec_nodes.size() > rd_smspec->params_size) rd_smspec->params_size = rd_smspec->smspec_nodes.size(); const auto &node = rd_smspec->smspec_nodes.back(); @@ -701,7 +700,7 @@ const rd::smspec_node *rd_smspec_add_node(rd_smspec_type *rd_smspec, const char *keyword, int num, const char *unit, float default_value) { - int params_index = rd_smspec->smspec_nodes.size(); + size_t params_index = rd_smspec->smspec_nodes.size(); return rd_smspec_insert_node( rd_smspec, std::unique_ptr(new rd::smspec_node( params_index, keyword, num, unit, rd_smspec->grid_dims, @@ -711,7 +710,7 @@ const rd::smspec_node *rd_smspec_add_node(rd_smspec_type *rd_smspec, //copy given node with a new index const rd::smspec_node *rd_smspec_add_node(rd_smspec_type *rd_smspec, const rd::smspec_node &node) { - int params_index = rd_smspec->smspec_nodes.size(); + size_t params_index = rd_smspec->smspec_nodes.size(); return rd_smspec_insert_node(rd_smspec, std::unique_ptr( new rd::smspec_node(node, params_index))); @@ -720,7 +719,7 @@ const rd::smspec_node *rd_smspec_add_node(rd_smspec_type *rd_smspec, const rd::smspec_node *rd_smspec_add_node(rd_smspec_type *rd_smspec, const char *keyword, const char *unit, float default_value) { - int params_index = rd_smspec->smspec_nodes.size(); + size_t params_index = rd_smspec->smspec_nodes.size(); return rd_smspec_insert_node( rd_smspec, std::unique_ptr(new rd::smspec_node( params_index, keyword, unit, default_value))); @@ -730,7 +729,7 @@ const rd::smspec_node *rd_smspec_add_node(rd_smspec_type *rd_smspec, const char *keyword, const char *wgname, const char *unit, float default_value) { - int params_index = rd_smspec->smspec_nodes.size(); + size_t params_index = rd_smspec->smspec_nodes.size(); return rd_smspec_insert_node( rd_smspec, std::unique_ptr(new rd::smspec_node( params_index, keyword, wgname, unit, default_value, @@ -742,7 +741,7 @@ const rd::smspec_node *rd_smspec_add_node(rd_smspec_type *rd_smspec, const char *wgname, int num, const char *unit, float default_value) { - int params_index = rd_smspec->smspec_nodes.size(); + size_t params_index = rd_smspec->smspec_nodes.size(); return rd_smspec_insert_node( rd_smspec, std::unique_ptr(new rd::smspec_node( @@ -750,11 +749,10 @@ const rd::smspec_node *rd_smspec_add_node(rd_smspec_type *rd_smspec, default_value, rd_smspec->key_join_string.c_str()))); } -const rd::smspec_node *rd_smspec_add_node(rd_smspec_type *rd_smspec, - int params_index, const char *keyword, - const char *wgname, int num, - const char *unit, - float default_value) { +const rd::smspec_node * +rd_smspec_add_node(rd_smspec_type *rd_smspec, size_t params_index, + const char *keyword, const char *wgname, int num, + const char *unit, float default_value) { return rd_smspec_insert_node( rd_smspec, std::unique_ptr(new rd::smspec_node( @@ -762,12 +760,11 @@ const rd::smspec_node *rd_smspec_add_node(rd_smspec_type *rd_smspec, default_value, rd_smspec->key_join_string.c_str()))); } -const rd::smspec_node *rd_smspec_add_node(rd_smspec_type *rd_smspec, - int params_index, const char *keyword, - const char *wgname, int num, - const char *unit, const char *lgr, - int lgr_i, int lgr_j, int lgr_k, - float default_value) { +const rd::smspec_node * +rd_smspec_add_node(rd_smspec_type *rd_smspec, size_t params_index, + const char *keyword, const char *wgname, int num, + const char *unit, const char *lgr, int lgr_i, int lgr_j, + int lgr_k, float default_value) { return rd_smspec_insert_node( rd_smspec, std::unique_ptr(new rd::smspec_node( @@ -775,8 +772,9 @@ const rd::smspec_node *rd_smspec_add_node(rd_smspec_type *rd_smspec, default_value, rd_smspec->key_join_string.c_str()))); } -const int *rd_smspec_get_index_map(const rd_smspec_type *smspec) { - return smspec->index_map.data(); +const std::vector & +rd_smspec_get_index_map(const rd_smspec_type *smspec) { + return smspec->index_map; } /** @@ -931,19 +929,19 @@ static bool rd_smspec_fread_header(rd_smspec_type *rd_smspec, std::string lgr_name = rd::strip_spaces(lgrs->at(params_index)); - rd_smspec_insert_node( - rd_smspec, std::make_unique( - static_cast(params_index), kw.c_str(), - well.c_str(), unit.c_str(), lgr_name.c_str(), - lgr_i, lgr_j, lgr_k, default_value, - rd_smspec->key_join_string.c_str())); - } else rd_smspec_insert_node( rd_smspec, std::make_unique( - static_cast(params_index), kw.c_str(), - well.c_str(), num, unit.c_str(), rd_smspec->grid_dims, - default_value, rd_smspec->key_join_string.c_str())); + params_index, kw.c_str(), well.c_str(), unit.c_str(), + lgr_name.c_str(), lgr_i, lgr_j, lgr_k, default_value, + rd_smspec->key_join_string.c_str())); + } else + rd_smspec_insert_node(rd_smspec, + std::make_unique( + params_index, kw.c_str(), + well.c_str(), num, unit.c_str(), + rd_smspec->grid_dims, default_value, + rd_smspec->key_join_string.c_str())); } rd_smspec->header_file = fs::canonical(header_file).string(); @@ -1002,6 +1000,7 @@ rd_smspec_type *rd_smspec_fread_alloc(const std::string &header_file, day_node->get_params_index(), month_node->get_params_index(), year_node->get_params_index()}; } + return rd_smspec.release(); } else { /** Failed to load from disk. */ @@ -1040,7 +1039,7 @@ bool node_exists(const rd::smspec_node *node_ptr) { return false; } -int node_valid_index(const rd::smspec_node *node_ptr) { +size_t node_valid_index(const rd::smspec_node *node_ptr) { if (node_ptr) return node_ptr->get_params_index(); @@ -1049,8 +1048,7 @@ int node_valid_index(const rd::smspec_node *node_ptr) { } // namespace -/* There is a quite wide range of error which are just returned as - "Not found" (i.e. -1). */ +/* A failed lookup throws std::out_of_range. */ /* Completions not supported yet. */ const rd::smspec_node & @@ -1064,8 +1062,8 @@ rd_smspec_get_general_var_node(const rd_smspec_type *smspec, return *node_ptr; } -int rd_smspec_get_general_var_params_index(const rd_smspec_type *rd_smspec, - const char *lookup_kw) { +size_t rd_smspec_get_general_var_params_index(const rd_smspec_type *rd_smspec, + const char *lookup_kw) { const auto node_ptr = rd_smspec_get_var_node(rd_smspec->gen_var_index, lookup_kw); return node_valid_index(node_ptr); @@ -1108,9 +1106,9 @@ int rd_smspec_get_restart_step(const rd_smspec_type *rd_smspec) { return rd_smspec->restart_step; } -int rd_smspec_get_first_step(const rd_smspec_type *rd_smspec) { +size_t rd_smspec_get_first_step(const rd_smspec_type *rd_smspec) { if (rd_smspec->restart_step > 0) - return rd_smspec->restart_step + 1; + return static_cast(rd_smspec->restart_step) + 1; else return 1; } diff --git a/lib/resdata/rd_sum.cpp b/lib/resdata/rd_sum.cpp index 993e9226b..198ec635e 100644 --- a/lib/resdata/rd_sum.cpp +++ b/lib/resdata/rd_sum.cpp @@ -486,7 +486,7 @@ const rd::smspec_node *rd_sum_add_local_var(rd_sum_type *rd_sum, throw std::invalid_argument( "Can not interchange variable adding and timesteps.\n"); - int params_index = rd_smspec_num_nodes(rd_sum->smspec.get()); + size_t params_index = rd_smspec_num_nodes(rd_sum->smspec.get()); return rd_smspec_add_node(rd_sum->smspec.get(), params_index, keyword, wgname, num, unit, lgr, lgr_i, lgr_j, lgr_k, default_value); @@ -509,7 +509,7 @@ const rd::smspec_node *rd_sum_add_smspec_node(rd_sum_type *rd_sum, true the output time unit will be days, otherwise it will be hours. */ -rd_sum_tstep_type *rd_sum_add_tstep(rd_sum_type *rd_sum, int report_step, +rd_sum_tstep_type *rd_sum_add_tstep(rd_sum_type *rd_sum, size_t report_step, double sim_seconds) { rd_sum_tstep_type *new_tstep = rd_sum_data_add_new_tstep(rd_sum->data.get(), report_step, sim_seconds); @@ -603,8 +603,8 @@ const rd::smspec_node *rd_sum_get_general_var_node(const rd_sum_type *rd_sum, return &node; } -int rd_sum_get_general_var_params_index(const rd_sum_type *rd_sum, - const char *lookup_kw) { +size_t rd_sum_get_general_var_params_index(const rd_sum_type *rd_sum, + const char *lookup_kw) { return rd_smspec_get_general_var_params_index(rd_sum->smspec.get(), lookup_kw); } @@ -617,9 +617,10 @@ bool rd_sum_has_key(const rd_sum_type *rd_sum, const char *lookup_kw) { return rd_sum_has_general_var(rd_sum, lookup_kw); } -double rd_sum_get_general_var(const rd_sum_type *rd_sum, int time_index, +double rd_sum_get_general_var(const rd_sum_type *rd_sum, size_t time_index, const char *lookup_kw) { - int params_index = rd_sum_get_general_var_params_index(rd_sum, lookup_kw); + size_t params_index = + rd_sum_get_general_var_params_index(rd_sum, lookup_kw); return rd_sum_data_iget(rd_sum->data.get(), time_index, params_index); } @@ -684,7 +685,7 @@ rd_sum_ptr rd_sum_alloc_resample(const rd_sum_type *rd_sum, const char *rd_case, grid_dims[0], grid_dims[1], grid_dims[2]); //add remaining nodes - for (int i = 0; i < rd_smspec_num_nodes(rd_sum->smspec.get()); i++) { + for (size_t i = 0; i < rd_smspec_num_nodes(rd_sum->smspec.get()); i++) { const rd::smspec_node &node = rd_smspec_iget_node_w_node_index(rd_sum->smspec.get(), i); if (util_string_equal(node.get_gen_key1(), "TIME")) @@ -699,16 +700,16 @@ rd_sum_ptr rd_sum_alloc_resample(const rd_sum_type *rd_sum, const char *rd_case, */ rd_sum_vector_ptr rd_sum_vector = make_sum_vector(rd_sum, true); - int num_nodes = rd_smspec_num_nodes(rd_sum->smspec.get()); + size_t num_nodes = rd_smspec_num_nodes(rd_sum->smspec.get()); std::vector data{}; - int report_step = 0; + size_t report_step = 0; for (auto it = times_begin; it != times_end; ++it, ++report_step) { time_t input_t = *it; if (input_t < start_time) { data.resize(num_nodes, 0.0); //clamping to the first value for t < start_time or if it is a rate than derivative is 0 - for (int i = 1; i < num_nodes; i++) { + for (size_t i = 1; i < num_nodes; i++) { double value = 0; const rd::smspec_node &node = rd_smspec_iget_node_w_node_index(rd_sum->smspec.get(), i); @@ -720,7 +721,7 @@ rd_sum_ptr rd_sum_alloc_resample(const rd_sum_type *rd_sum, const char *rd_case, } else if (input_t > end_time) { //clamping to the last value for t > end_time or if it is a rate than derivative is 0 data.resize(num_nodes, 0.0); - for (int i = 1; i < num_nodes; i++) { + for (size_t i = 1; i < num_nodes; i++) { double value = 0; const rd::smspec_node &node = rd_smspec_iget_node_w_node_index(rd_sum->smspec.get(), i); @@ -736,22 +737,24 @@ rd_sum_ptr rd_sum_alloc_resample(const rd_sum_type *rd_sum, const char *rd_case, } /* Add timestep corresponding to the interpolated data in the resampled case. */ - rd_sum_tstep_type *tstep = rd_sum_add_tstep( - rd_sum_resampled.get(), report_step, input_t - input_start); - for (int data_index = 0; + rd_sum_tstep_type *tstep = + rd_sum_add_tstep(rd_sum_resampled.get(), report_step, + static_cast(input_t - input_start)); + for (size_t data_index = 0; data_index < rd_sum_vector_get_size(rd_sum_vector.get()); data_index++) { double value = data.at(data_index); - int params_index = + size_t params_index = data_index + 1; // The +1 shift is because the first element in the tstep is time value. - rd_sum_tstep_iset(tstep, params_index, value); + rd_sum_tstep_iset(tstep, params_index, static_cast(value)); } } return rd_sum_resampled; } -double rd_sum_iget(const rd_sum_type *rd_sum, int time_index, int param_index) { +double rd_sum_iget(const rd_sum_type *rd_sum, size_t time_index, + size_t param_index) { return rd_sum_data_iget(rd_sum->data.get(), time_index, param_index); } @@ -764,30 +767,33 @@ const char *rd_sum_get_unit(const rd_sum_type *rd_sum, const char *gen_key) { return node->get_unit(); } -int rd_sum_get_last_report_step(const rd_sum_type *rd_sum) { +size_t rd_sum_get_last_report_step(const rd_sum_type *rd_sum) { return rd_sum_data_get_last_report_step(rd_sum->data.get()); } -int rd_sum_get_first_report_step(const rd_sum_type *rd_sum) { +size_t rd_sum_get_first_report_step(const rd_sum_type *rd_sum) { return rd_sum_data_get_first_report_step(rd_sum->data.get()); } -int rd_sum_iget_report_end(const rd_sum_type *rd_sum, int report_step) { +size_t rd_sum_iget_report_end(const rd_sum_type *rd_sum, size_t report_step) { return rd_sum_data_iget_report_end(rd_sum->data.get(), report_step); } -int rd_sum_iget_report_step(const rd_sum_type *rd_sum, int internal_index) { +size_t rd_sum_iget_report_step(const rd_sum_type *rd_sum, + size_t internal_index) { return rd_sum_data_iget_report_step(rd_sum->data.get(), internal_index); } std::vector rd_sum_alloc_time_vector(const rd_sum_type *rd_sum, bool report_only) { - return rd_sum_data_alloc_time_vector(rd_sum->data.get(), report_only); + return report_only + ? rd_sum_data_alloc_report_time_vector(rd_sum->data.get()) + : rd_sum_data_alloc_time_vector(rd_sum->data.get()); } void rd_sum_init_double_vector(const rd_sum_type *rd_sum, const char *gen_key, double *data) { - int params_index = rd_sum_get_general_var_params_index(rd_sum, gen_key); + size_t params_index = rd_sum_get_general_var_params_index(rd_sum, gen_key); rd_sum_data_init_double_vector(rd_sum->data.get(), params_index, data); } @@ -810,9 +816,12 @@ void rd_sum_init_double_frame_interp(const rd_sum_type *rd_sum, } std::vector rd_sum_alloc_data_vector(const rd_sum_type *rd_sum, - int data_index, bool report_only) { - return rd_sum_data_alloc_data_vector(rd_sum->data.get(), data_index, - report_only); + size_t data_index, + bool report_only) { + return report_only + ? rd_sum_data_alloc_report_data_vector(rd_sum->data.get(), + data_index) + : rd_sum_data_alloc_data_vector(rd_sum->data.get(), data_index); } /** @@ -827,44 +836,34 @@ std::vector rd_sum_alloc_data_vector(const rd_sum_type *rd_sum, */ -static int rd_sum_get_limiting(const rd_sum_type *rd_sum, int smspec_index, - double limit, bool gt) { - const int length = rd_sum_data_get_length(rd_sum->data.get()); - int internal_index = 0; - do { +static std::optional rd_sum_get_limiting(const rd_sum_type *rd_sum, + size_t smspec_index, + double limit, bool gt) { + const size_t length = rd_sum_data_get_length(rd_sum->data.get()); + for (size_t internal_index = 0; internal_index < length; internal_index++) { double value = rd_sum_data_iget(rd_sum->data.get(), internal_index, smspec_index); - if (gt) { - if (value > limit) - break; - } else { - if (value < limit) - break; - } - internal_index++; - } while (internal_index < length); - - if (internal_index == length) /* Did not find it */ - internal_index = -1; - - return internal_index; + if (gt ? value > limit : value < limit) + return internal_index; + } + return std::nullopt; } -int rd_sum_get_first_gt(const rd_sum_type *rd_sum, int param_index, - double limit) { +std::optional rd_sum_get_first_gt(const rd_sum_type *rd_sum, + size_t param_index, double limit) { return rd_sum_get_limiting(rd_sum, param_index, limit, true); } -int rd_sum_get_first_lt(const rd_sum_type *rd_sum, int param_index, - double limit) { +std::optional rd_sum_get_first_lt(const rd_sum_type *rd_sum, + size_t param_index, double limit) { return rd_sum_get_limiting(rd_sum, param_index, limit, false); } -time_t rd_sum_get_report_time(const rd_sum_type *rd_sum, int report_step) { +time_t rd_sum_get_report_time(const rd_sum_type *rd_sum, size_t report_step) { return rd_sum_data_get_report_time(rd_sum->data.get(), report_step); } -time_t rd_sum_iget_sim_time(const rd_sum_type *rd_sum, int index) { +time_t rd_sum_iget_sim_time(const rd_sum_type *rd_sum, size_t index) { return rd_sum_data_iget_sim_time(rd_sum->data.get(), index); } @@ -884,7 +883,7 @@ time_t rd_sum_get_end_time(const rd_sum_type *rd_sum) { } } -double rd_sum_iget_sim_days(const rd_sum_type *rd_sum, int index) { +double rd_sum_iget_sim_days(const rd_sum_type *rd_sum, size_t index) { return rd_sum_data_iget_sim_days(rd_sum->data.get(), index); } @@ -897,11 +896,11 @@ double rd_sum_iget_sim_days(const rd_sum_type *rd_sum, int index) { #define DATE_STRING_LENGTH 128 -static void rd_sum_write_line(const rd_sum_type *rd_sum, std::ostream &stream, - int internal_index, - const std::vector &has_var, - const std::vector &var_index, - const char *date_format, const char *sep) { +static void +rd_sum_write_line(const rd_sum_type *rd_sum, std::ostream &stream, + size_t internal_index, + const std::vector> &var_index, + const char *date_format, const char *sep) { stream << fmt::format("{:7.2f}", rd_sum_iget_sim_days(rd_sum, internal_index)) << sep; @@ -917,22 +916,23 @@ static void rd_sum_write_line(const rd_sum_type *rd_sum, std::ostream &stream, stream << date_string.data(); } - for (size_t ivar = 0; ivar < var_index.size(); ivar++) { - if (has_var.at(ivar)) + for (const auto &index : var_index) { + if (index) stream << sep << fmt::format("{:g}", rd_sum_iget(rd_sum, internal_index, - var_index[ivar])); + index.value())); } stream << "\r\n"; } -static void rd_sum_write_header(const std::vector &key_list, - const std::vector &has_var, - std::ostream &stream, const char *sep) { +static void +rd_sum_write_header(const std::vector &key_list, + const std::vector> &var_index, + std::ostream &stream, const char *sep) { stream << "DAYS" << sep << "DATE"; for (size_t i = 0; i < key_list.size(); i++) - if (has_var.at(i)) + if (var_index.at(i)) stream << sep << key_list[i]; stream << "\r\n"; } @@ -941,29 +941,25 @@ static void rd_sum_write_csv(const rd_sum_type *rd_sum, std::ostream &stream, const std::vector &var_list, const char *date_format, const char *sep) { - std::vector has_var(var_list.size(), false); - std::vector var_index(var_list.size(), -1); + std::vector> var_index(var_list.size(), std::nullopt); - for (int ivar = 0; static_cast(ivar) < var_list.size(); ivar++) { - if (rd_sum_has_general_var(rd_sum, var_list[ivar].c_str())) { - has_var[ivar] = true; + for (size_t ivar = 0; ivar < var_list.size(); ivar++) { + if (rd_sum_has_general_var(rd_sum, var_list[ivar].c_str())) var_index[ivar] = rd_sum_get_general_var_params_index( rd_sum, var_list[ivar].c_str()); - } else { + else fprintf(stderr, "** Warning: could not find variable: \'%s\' in " "summary file \n", var_list[ivar].c_str()); - has_var[ivar] = false; - } } - rd_sum_write_header(var_list, has_var, stream, sep); + rd_sum_write_header(var_list, var_index, stream, sep); - for (int time_index = 0; time_index < rd_sum_get_data_length(rd_sum); + for (size_t time_index = 0; time_index < rd_sum_get_data_length(rd_sum); time_index++) - rd_sum_write_line(rd_sum, stream, time_index, has_var, var_index, - date_format, sep); + rd_sum_write_line(rd_sum, stream, time_index, var_index, date_format, + sep); } #undef DATE_STRING_LENGTH @@ -1053,7 +1049,7 @@ double rd_sum_get_sim_length(const rd_sum_type *rd_sum) { /** Will return the number of data blocks. */ -int rd_sum_get_data_length(const rd_sum_type *rd_sum) { +size_t rd_sum_get_data_length(const rd_sum_type *rd_sum) { return rd_sum_data_get_length(rd_sum->data.get()); } @@ -1065,11 +1061,13 @@ bool rd_sum_check_sim_days(const rd_sum_type *sum, double sim_days) { return rd_sum_data_check_sim_days(sum->data.get(), sim_days); } -int rd_sum_get_report_step_from_time(const rd_sum_type *sum, time_t sim_time) { +std::optional rd_sum_get_report_step_from_time(const rd_sum_type *sum, + time_t sim_time) { return rd_sum_data_get_report_step_from_time(sum->data.get(), sim_time); } -int rd_sum_get_report_step_from_days(const rd_sum_type *sum, double sim_days) { +std::optional rd_sum_get_report_step_from_days(const rd_sum_type *sum, + double sim_days) { return rd_sum_data_get_report_step_from_days(sum->data.get(), sim_days); } diff --git a/lib/resdata/rd_sum_data.cpp b/lib/resdata/rd_sum_data.cpp index 154330886..0497884d8 100644 --- a/lib/resdata/rd_sum_data.cpp +++ b/lib/resdata/rd_sum_data.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -5,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -54,37 +56,48 @@ namespace { */ struct IndexNode { - IndexNode(int d, int o, int l) { - this->data_index = d; - this->offset = o; - this->length = l; - } - - int end() const { return this->offset + this->length; } + size_t end() const { return this->offset + this->length; } - int data_index; - int offset; - int length; - int report1; - int report2; + size_t data_index; + size_t offset; + size_t length; + size_t report1; + size_t report2; time_t time1; time_t time2; double days1; double days2; - std::vector params_map; + std::vector> params_map; }; class CaseIndex { public: - IndexNode &add(int length) { - int offset = 0; - int data_index = this->index.size(); - - if (!this->index.empty()) - offset = this->index.back().end(); - - this->index.emplace_back(data_index, offset, length); - return this->index.back(); + /* + Adds the data file with index @data_index in the list of data files. If + @next_start is given, the data file is truncated to the timesteps before + @next_start. Data files which contribute no timesteps are not added, so + every node in the index has length > 0. + */ + void add(size_t data_index, const rd::rd_sum_file_data &data, + std::optional next_start, const rd_smspec_type *smspec) { + size_t length = + next_start ? data.length_before(*next_start) : data.length(); + if (length == 0) + return; + + size_t offset = this->index.empty() ? 0 : this->index.back().end(); + this->index.push_back(IndexNode{ + data_index, + offset, + length, + data.first_report(), + next_start ? data.report_before(*next_start) : data.last_report(), + data.get_data_start(), + data.get_sim_end(), + data.get_days_start(), + data.get_sim_length(), + rd_smspec_alloc_mapping(smspec, data.smspec()), + }); } /* @@ -107,6 +120,9 @@ class CaseIndex { std::pair lookup_time(time_t sim_time) const { + if (this->index.empty()) + throw std::invalid_argument("Simulation time out of range"); + auto iter = this->index.begin(); auto next = this->index.begin(); if (sim_time < iter->time1) @@ -114,8 +130,8 @@ class CaseIndex { ++next; while (true) { - double t1 = iter->time1; - double t2 = iter->time2; + time_t t1 = iter->time1; + time_t t2 = iter->time2; if (sim_time >= t1) { if (sim_time <= t2) @@ -136,6 +152,9 @@ class CaseIndex { std::pair lookup_days(double days) const { + if (this->index.empty()) + throw std::invalid_argument("Simulation days out of range"); + auto iter = this->index.begin(); auto next = this->index.begin(); if (days < iter->days1) @@ -163,7 +182,7 @@ class CaseIndex { } } - const IndexNode &lookup(int internal_index) const { + const IndexNode &lookup(size_t internal_index) const { for (const auto &node : this->index) if (internal_index >= node.offset && internal_index < node.end()) return node; @@ -172,7 +191,7 @@ class CaseIndex { std::to_string(internal_index)); } - const IndexNode &lookup_report(int report) const { + const IndexNode &lookup_report(size_t report) const { for (const auto &node : this->index) if (node.report1 <= report && node.report2 >= report) return node; @@ -181,11 +200,13 @@ class CaseIndex { std::to_string(report)); } - IndexNode &back() { return this->index.back(); } - void clear() { this->index.clear(); } - int length() const { return this->index.back().end(); } + size_t length() const { + if (this->index.empty()) + return 0; + return this->index.back().end(); + } std::vector::const_iterator begin() const { return this->index.begin(); @@ -212,7 +233,7 @@ struct rd_sum_data_struct { static void rd_sum_data_build_index(rd_sum_data_type *self); static double rd_sum_data_iget_sim_seconds(const rd_sum_data_type *data, - int internal_index); + size_t internal_index); void rd_sum_data_free(rd_sum_data_type *data) { if (!data) @@ -248,14 +269,14 @@ rd_sum_data_type *rd_sum_data_alloc(rd_smspec_type *smspec) { */ static double rd_sum_data_iget_sim_seconds(const rd_sum_data_type *data, - int internal_index) { + size_t 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_sim_seconds(internal_index - index_node.offset); } double rd_sum_data_iget_sim_days(const rd_sum_data_type *data, - int internal_index) { + size_t 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_sim_days(internal_index - index_node.offset); @@ -393,8 +414,8 @@ bool rd_sum_data_check_sim_days(const rd_sum_data_type *data, double sim_days) { sequence has no holes. */ -static int rd_sum_data_get_index_from_sim_time(const rd_sum_data_type *data, - time_t sim_time) { +static size_t rd_sum_data_get_index_from_sim_time(const rd_sum_data_type *data, + time_t sim_time) { if (!rd_sum_data_check_sim_time(data, sim_time)) { time_t start_time = rd_sum_data_get_data_start(data); time_t end_time = rd_sum_data_get_sim_end(data); @@ -410,13 +431,16 @@ static int rd_sum_data_get_index_from_sim_time(const rd_sum_data_type *data, perfectly well be 'holes' in the time domain, because of e.g. the RPTONLY keyword. */ + const size_t length = rd_sum_data_get_length(data); + if (length == 0) + throw std::logic_error("reached found time_t in empty data"); - int low_index = 0; - int high_index = rd_sum_data_get_length(data) - 1; + size_t low_index = 0; + size_t high_index = length - 1; // perform binary search while (low_index + 1 < high_index) { - int center_index = (low_index + high_index) / 2; + size_t center_index = (low_index + high_index) / 2; const time_t center_time = rd_sum_data_iget_sim_time(data, center_index); @@ -455,11 +479,10 @@ static int rd_sum_data_get_index_from_sim_time(const rd_sum_data_type *data, file. */ -static void rd_sum_data_init_interp_from_sim_time(const rd_sum_data_type *data, - time_t sim_time, int *index1, - int *index2, double *weight1, - double *weight2) { - int idx = rd_sum_data_get_index_from_sim_time(data, sim_time); +static void rd_sum_data_init_interp_from_sim_time( + const rd_sum_data_type *data, time_t sim_time, size_t *index1, + size_t *index2, double *weight1, double *weight2) { + size_t idx = rd_sum_data_get_index_from_sim_time(data, sim_time); // if sim_time is first date, idx=0 and then we cannot interpolate, so we give // weight 1 to index1=index2=0. @@ -478,9 +501,9 @@ static void rd_sum_data_init_interp_from_sim_time(const rd_sum_data_type *data, *index2 = idx; // weights the interpolation each of the ministeps according to distance from sim_time - double time_diff = sim_time2 - sim_time1; - double time_dist1 = (sim_time - sim_time1); - double time_dist2 = -(sim_time - sim_time2); + double time_diff = static_cast(sim_time2 - sim_time1); + double time_dist1 = static_cast(sim_time - sim_time1); + double time_dist2 = -static_cast(sim_time - sim_time2); *weight1 = time_dist2 / time_diff; *weight2 = time_dist1 / time_diff; @@ -491,14 +514,14 @@ rd_sum_data_alloc_seconds_solution(const rd_sum_data_type *data, const rd::smspec_node &node, double cmp_value, bool rates_clamp_lower) { std::vector solution; - const int param_index = node.get_params_index(); - const int size = rd_sum_data_get_length(data); + const size_t param_index = node.get_params_index(); + const size_t size = rd_sum_data_get_length(data); if (size <= 1) return solution; - for (int index = 0; index < size; ++index) { - int prev_index = std::max(0, index - 1); + for (size_t index = 0; index < size; ++index) { + size_t prev_index = (index == 0) ? 0 : index - 1; double value = rd_sum_data_iget(data, index, param_index); double prev_value = rd_sum_data_iget(data, prev_index, param_index); @@ -533,34 +556,11 @@ static void rd_sum_data_build_index(rd_sum_data_type *self) { self->index.clear(); for (size_t i = 0; i < self->data_files.size(); i++) { - const auto &data = self->data_files[i]; - bool main_case = (i == (self->data_files.size() - 1)); - time_t next_start; - - if (main_case) - self->index.add(data->length()); - else { - const auto &next = self->data_files[i + 1]; - next_start = next->get_data_start(); - self->index.add(data->length_before(next_start)); - } + std::optional next_start = std::nullopt; + if (i + 1 < self->data_files.size()) + next_start = self->data_files[i + 1]->get_data_start(); - auto &node = self->index.back(); - if (node.length > 0) { - node.report1 = data->first_report(); - - if (main_case) - node.report2 = data->last_report(); - else - node.report2 = data->report_before(next_start); - - node.time1 = data->get_data_start(); - node.time2 = data->get_sim_end(); - node.days1 = data->get_days_start(); - node.days2 = data->get_sim_length(); - node.params_map = - rd_smspec_alloc_mapping(self->smspec, data->smspec()); - } + self->index.add(i, *self->data_files[i], next_start, self->smspec); } } @@ -572,7 +572,7 @@ static void rd_sum_data_build_index(rd_sum_data_type *self) { */ rd_sum_tstep_type *rd_sum_data_add_new_tstep(rd_sum_data_type *data, - int report_step, + size_t report_step, double sim_seconds) { const auto &file_data = data->data_files.back(); rd_sum_tstep_type *tstep = @@ -612,11 +612,17 @@ bool rd_sum_data_fread(rd_sum_data_type *data, /** The last index included in report step @report_step. If the dataset does not contain @report_step, the function will raise std::invalid_argument. */ -int rd_sum_data_iget_report_end(const rd_sum_data_type *data, int report_step) { +size_t rd_sum_data_iget_report_end(const rd_sum_data_type *data, + size_t 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); - int end = range.second; + const auto range = file_data->report_range(report_step); + if (!range) + throw std::invalid_argument("Could not locate report step " + + std::to_string(report_step) + + " within its summary file"); + + size_t end = range->last; if (end >= index_node.length) { /* report_range() returns the report's extent within the file, but if the time of steps overlap between files, the final timesteps are @@ -633,8 +639,8 @@ int rd_sum_data_iget_report_end(const rd_sum_data_type *data, int report_step) { return end + index_node.offset; } -int rd_sum_data_iget_report_step(const rd_sum_data_type *data, - int internal_index) { +size_t rd_sum_data_iget_report_step(const rd_sum_data_type *data, + size_t internal_index) { const auto &index_node = data->index.lookup(internal_index); const auto &file_data = data->data_files[index_node.data_index]; return file_data->iget_report(internal_index - index_node.offset); @@ -646,14 +652,20 @@ int rd_sum_data_iget_report_step(const rd_sum_data_type *data, any holes. */ -double rd_sum_data_iget(const rd_sum_data_type *data, int time_index, - int params_index) { +double rd_sum_data_iget(const rd_sum_data_type *data, size_t time_index, + size_t params_index) { + if (params_index >= rd_smspec_get_params_size(data->smspec)) + throw std::out_of_range( + fmt::format("params_index:{} is out of range - valid range: " + "[0, {})", + params_index, rd_smspec_get_params_size(data->smspec))); + const auto &index_node = data->index.lookup(time_index); const auto &file_data = data->data_files[index_node.data_index]; const auto ¶ms_map = index_node.params_map; - if (params_map[params_index] >= 0) + if (params_map[params_index]) return file_data->iget(time_index - index_node.offset, - params_map[params_index]); + *params_map[params_index]); else { const rd::smspec_node &smspec_node = rd_smspec_iget_node_w_params_index(data->smspec, params_index); @@ -674,22 +686,22 @@ double rd_sum_data_iget(const rd_sum_data_type *data, int time_index, */ static double rd_sum_data_interp_get(const rd_sum_data_type *data, - int time_index1, int time_index2, + size_t time_index1, size_t time_index2, double weight1, double weight2, - int params_index) { + size_t params_index) { return rd_sum_data_iget(data, time_index1, params_index) * weight1 + rd_sum_data_iget(data, time_index2, params_index) * weight2; } static double rd_sum_data_vector_iget(const rd_sum_data_type *data, - time_t sim_time, int params_index, - bool is_rate, int time_index1, - int time_index2, double weight1, + time_t sim_time, size_t params_index, + bool is_rate, size_t time_index1, + size_t time_index2, double weight1, double weight2) { double value = 0.0; if (is_rate) { - int time_index = rd_sum_data_get_index_from_sim_time(data, sim_time); + size_t time_index = rd_sum_data_get_index_from_sim_time(data, sim_time); // uses step function since it is a rate value = rd_sum_data_iget(data, time_index, params_index); } else { @@ -706,20 +718,19 @@ std::vector rd_sum_data_get_interp_vector(const rd_sum_data_type *data, time_t sim_time, const rd_sum_vector_type *keylist, double missing_value) { - int num_keywords = rd_sum_vector_get_size(keylist); + size_t num_keywords = rd_sum_vector_get_size(keylist); double weight1, weight2; - int time_index1, time_index2; + size_t time_index1, time_index2; rd_sum_data_init_interp_from_sim_time(data, sim_time, &time_index1, &time_index2, &weight1, &weight2); std::vector results(num_keywords, missing_value); - for (int i = 0; i < num_keywords; i++) { - if (rd_sum_vector_iget_valid(keylist, i)) { - int params_index = rd_sum_vector_iget_param_index(keylist, i); + for (size_t i = 0; i < num_keywords; i++) { + if (auto params_index = rd_sum_vector_iget_param_index(keylist, i)) { bool is_rate = rd_sum_vector_iget_is_rate(keylist, i); double value = rd_sum_data_vector_iget( - data, sim_time, params_index, is_rate, time_index1, time_index2, - weight1, weight2); + data, sim_time, *params_index, is_rate, time_index1, + time_index2, weight1, weight2); results[i] = value; } } @@ -729,7 +740,7 @@ rd_sum_data_get_interp_vector(const rd_sum_data_type *data, time_t sim_time, double rd_sum_data_get_from_sim_time(const rd_sum_data_type *data, time_t sim_time, const rd::smspec_node &smspec_node) { - int params_index = smspec_node.get_params_index(); + size_t params_index = smspec_node.get_params_index(); if (smspec_node.is_rate()) { /* In general the mapping from sim_time to index is based on half @@ -744,12 +755,12 @@ double rd_sum_data_get_from_sim_time(const rd_sum_data_type *data, with the simulator results if you ask for a value interpolated to the starting time. */ - int time_index = rd_sum_data_get_index_from_sim_time(data, sim_time); + size_t time_index = rd_sum_data_get_index_from_sim_time(data, sim_time); return rd_sum_data_iget(data, time_index, params_index); } else { /* Interpolated lookup based on two (hopefully) consecutive ministeps. */ double weight1, weight2; - int time_index1, time_index2; + size_t time_index1, time_index2; rd_sum_data_init_interp_from_sim_time(data, sim_time, &time_index1, &time_index2, &weight1, &weight2); @@ -758,15 +769,16 @@ double rd_sum_data_get_from_sim_time(const rd_sum_data_type *data, } } -int rd_sum_data_get_report_step_from_days(const rd_sum_data_type *data, - double sim_days) { +std::optional +rd_sum_data_get_report_step_from_days(const rd_sum_data_type *data, + double sim_days) { if ((sim_days < rd_sum_data_get_first_day(data)) || (sim_days > rd_sum_data_get_sim_length(data))) - return -1; + return std::nullopt; else { auto files = data->index.lookup_days(sim_days); if (files.first != files.second) - return -1; + return std::nullopt; const auto &data_file = data->data_files[files.first->data_index]; return data_file->report_step_from_days(sim_days); @@ -792,14 +804,15 @@ int rd_sum_data_get_report_step_from_days(const rd_sum_data_type *data, report_step input. */ -int rd_sum_data_get_report_step_from_time(const rd_sum_data_type *data, - time_t sim_time) { +std::optional +rd_sum_data_get_report_step_from_time(const rd_sum_data_type *data, + time_t sim_time) { if (!rd_sum_data_check_sim_time(data, sim_time)) - return -1; + return std::nullopt; else { auto files = data->index.lookup_time(sim_time); if (files.first != files.second) - return -1; + return std::nullopt; const auto &data_file = data->data_files[files.first->data_index]; return data_file->report_step_from_time(sim_time); @@ -820,117 +833,100 @@ double rd_sum_data_get_from_sim_days(const rd_sum_data_type *data, } time_t rd_sum_data_iget_sim_time(const rd_sum_data_type *data, - int ministep_index) { + size_t 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); } time_t rd_sum_data_get_report_time(const rd_sum_data_type *data, - int report_step) { + size_t report_step) { if (report_step == 0) return rd_smspec_get_start_time(data->smspec); else { - int internal_index = rd_sum_data_iget_report_end(data, report_step); + size_t internal_index = rd_sum_data_iget_report_end(data, report_step); return rd_sum_data_iget_sim_time(data, internal_index); } } -int rd_sum_data_get_first_report_step(const rd_sum_data_type *data) { +size_t rd_sum_data_get_first_report_step(const rd_sum_data_type *data) { const auto &data_file = data->data_files[0]; return data_file->first_report(); } -int rd_sum_data_get_last_report_step(const rd_sum_data_type *data) { +size_t rd_sum_data_get_last_report_step(const rd_sum_data_type *data) { const auto &data_file = data->data_files.back(); return data_file->last_report(); } -static void rd_sum_data_init_time_vector__(const rd_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]; +std::vector +rd_sum_data_alloc_time_vector(const rd_sum_data_type *data) { + std::vector output_data(rd_sum_data_get_length(data)); - 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; - } + size_t offset = 0; + for (const auto &index_node : data->index) { + data->data_files[index_node.data_index]->get_time(index_node.length, + &output_data[offset]); + offset += index_node.length; } + return output_data; } -std::vector rd_sum_data_alloc_time_vector(const rd_sum_data_type *data, - bool report_only) { +std::vector +rd_sum_data_alloc_report_time_vector(const rd_sum_data_type *data) { std::vector output_data; - if (report_only) - output_data.resize(1 + rd_sum_data_get_last_report_step(data) - - rd_sum_data_get_first_report_step(data)); - else - output_data.resize(rd_sum_data_get_length(data)); - - rd_sum_data_init_time_vector__(data, output_data.data(), report_only); + for (const auto &index_node : data->index) + data->data_files[index_node.data_index]->get_time_report( + index_node.length, output_data); return output_data; } -static void rd_sum_data_init_double_vector__(const rd_sum_data_type *data, - int main_params_index, - double *output_data, - bool report_only) { - int offset = 0; +void rd_sum_data_init_double_vector(const rd_sum_data_type *data, + size_t params_index, double *output_data) { + if (params_index >= rd_smspec_get_params_size(data->smspec)) + throw std::out_of_range("Out of range"); + + const double default_value = + rd_smspec_iget_node_w_params_index(data->smspec, params_index) + .get_default(); + + size_t offset = 0; for (const auto &index_node : data->index) { const auto &data_file = data->data_files[index_node.data_index]; - const auto ¶ms_map = index_node.params_map; - int params_index = params_map[main_params_index]; - - if (report_only) { - const rd::smspec_node &smspec_node = - rd_smspec_iget_node_w_params_index(data->smspec, - main_params_index); - double default_value = smspec_node.get_default(); - offset += - data_file->get_data_report(params_index, index_node.length, - &output_data[offset], default_value); - } else { + const std::optional file_params_index = + index_node.params_map[params_index]; - if (params_index >= 0) - data_file->get_data(params_index, index_node.length, - &output_data[offset]); - else { - const rd::smspec_node &smspec_node = - rd_smspec_iget_node_w_params_index(data->smspec, - main_params_index); - for (int i = 0; i < index_node.length; i++) - output_data[offset + i] = smspec_node.get_default(); - } - offset += index_node.length; - } + if (file_params_index) + data_file->get_data(*file_params_index, index_node.length, + &output_data[offset]); + else + std::fill_n(&output_data[offset], index_node.length, default_value); + offset += index_node.length; } } -void rd_sum_data_init_double_vector(const rd_sum_data_type *data, - int params_index, double *output_data) { - rd_sum_data_init_double_vector__(data, params_index, output_data, false); -} - std::vector rd_sum_data_alloc_data_vector(const rd_sum_data_type *data, - int params_index, - bool report_only) { - std::vector output_data; - if (report_only) - output_data.resize(1 + rd_sum_data_get_last_report_step(data) - - rd_sum_data_get_first_report_step(data)); - else - output_data.resize(rd_sum_data_get_length(data)); + size_t params_index) { + std::vector output_data(rd_sum_data_get_length(data)); + rd_sum_data_init_double_vector(data, params_index, output_data.data()); + return output_data; +} +std::vector +rd_sum_data_alloc_report_data_vector(const rd_sum_data_type *data, + size_t params_index) { if (params_index >= rd_smspec_get_params_size(data->smspec)) throw std::out_of_range("Out of range"); - rd_sum_data_init_double_vector__(data, params_index, output_data.data(), - report_only); + const double default_value = + rd_smspec_iget_node_w_params_index(data->smspec, params_index) + .get_default(); + + std::vector output_data; + for (const auto &index_node : data->index) + data->data_files[index_node.data_index]->get_data_report( + index_node.params_map[params_index], index_node.length, + default_value, output_data); return output_data; } @@ -938,7 +934,7 @@ void rd_sum_data_init_double_vector_interp( const rd_sum_data_type *data, const rd::smspec_node &smspec_node, const std::vector &time_points, double *output_data) { bool is_rate = smspec_node.is_rate(); - int params_index = smspec_node.get_params_index(); + size_t params_index = smspec_node.get_params_index(); time_t start_time = rd_sum_data_get_data_start(data); time_t end_time = rd_sum_data_get_sim_end(data); double start_value = 0; @@ -959,7 +955,7 @@ void rd_sum_data_init_double_vector_interp( value = end_value; else { - int time_index1, time_index2; + size_t time_index1, time_index2; double weight1, weight2; rd_sum_data_init_interp_from_sim_time( data, sim_time, &time_index1, &time_index2, &weight1, &weight2); @@ -975,55 +971,67 @@ void rd_sum_data_init_double_vector_interp( void rd_sum_data_init_double_frame_interp( const rd_sum_data_type *data, const rd_sum_vector_type *keywords, const std::vector &time_points, double *output_data) { - int num_keywords = rd_sum_vector_get_size(keywords); - int time_stride = num_keywords; - int key_stride = 1; + size_t num_keywords = rd_sum_vector_get_size(keywords); + size_t time_stride = num_keywords; + size_t key_stride = 1; time_t start_time = rd_sum_data_get_data_start(data); time_t end_time = rd_sum_data_get_sim_end(data); for (size_t time_index = 0; time_index < time_points.size(); time_index++) { time_t sim_time = time_points[time_index]; if (sim_time < start_time) { - for (int key_index = 0; key_index < num_keywords; key_index++) { - int param_index = - rd_sum_vector_iget_param_index(keywords, key_index); - int data_index = + for (size_t key_index = 0; key_index < num_keywords; key_index++) { + size_t data_index = key_index * key_stride + time_index * time_stride; + auto param_index = + rd_sum_vector_iget_param_index(keywords, key_index); + if (!param_index) { + output_data[data_index] = std::nan(""); + continue; + } bool is_rate = rd_sum_vector_iget_is_rate(keywords, key_index); if (is_rate) output_data[data_index] = 0; else output_data[data_index] = - rd_sum_data_iget_first_value(data, param_index); + rd_sum_data_iget_first_value(data, *param_index); } } else if (sim_time > end_time) { - for (int key_index = 0; key_index < num_keywords; key_index++) { - int param_index = - rd_sum_vector_iget_param_index(keywords, key_index); - int data_index = + for (size_t key_index = 0; key_index < num_keywords; key_index++) { + size_t data_index = key_index * key_stride + time_index * time_stride; + auto param_index = + rd_sum_vector_iget_param_index(keywords, key_index); + if (!param_index) { + output_data[data_index] = std::nan(""); + continue; + } bool is_rate = rd_sum_vector_iget_is_rate(keywords, key_index); if (is_rate) output_data[data_index] = 0; else output_data[data_index] = - rd_sum_data_iget_last_value(data, param_index); + rd_sum_data_iget_last_value(data, *param_index); } } else { double weight1, weight2; - int time_index1, time_index2; + size_t time_index1, time_index2; rd_sum_data_init_interp_from_sim_time( data, sim_time, &time_index1, &time_index2, &weight1, &weight2); - for (int key_index = 0; key_index < num_keywords; key_index++) { - int param_index = - rd_sum_vector_iget_param_index(keywords, key_index); - int data_index = + for (size_t key_index = 0; key_index < num_keywords; key_index++) { + size_t data_index = key_index * key_stride + time_index * time_stride; + auto param_index = + rd_sum_vector_iget_param_index(keywords, key_index); + if (!param_index) { + output_data[data_index] = std::nan(""); + continue; + } bool is_rate = rd_sum_vector_iget_is_rate(keywords, key_index); double value = rd_sum_data_vector_iget( - data, sim_time, param_index, is_rate, time_index1, + data, sim_time, *param_index, is_rate, time_index1, time_index2, weight1, weight2); output_data[data_index] = value; } @@ -1037,17 +1045,19 @@ void rd_sum_data_init_double_frame_interp( ministeps can have non-zero offset and also "holes" in the series. */ -int rd_sum_data_get_length(const rd_sum_data_type *data) { +size_t rd_sum_data_get_length(const rd_sum_data_type *data) { return data->index.length(); } double rd_sum_data_iget_last_value(const rd_sum_data_type *data, - int param_index) { - return rd_sum_data_iget(data, rd_sum_data_get_length(data) - 1, - param_index); + size_t param_index) { + const size_t length = rd_sum_data_get_length(data); + if (length == 0) + throw std::out_of_range("rd_sum_data_iget_last_value: no data"); + return rd_sum_data_iget(data, length - 1, param_index); } double rd_sum_data_iget_first_value(const rd_sum_data_type *data, - int param_index) { + size_t param_index) { return rd_sum_data_iget(data, 0, param_index); } diff --git a/lib/resdata/rd_sum_file_data.cpp b/lib/resdata/rd_sum_file_data.cpp index c5b63f89e..fc95d3474 100644 --- a/lib/resdata/rd_sum_file_data.cpp +++ b/lib/resdata/rd_sum_file_data.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -31,9 +32,12 @@ namespace fs = std::filesystem; namespace { -void validate_report_step(int report_step) { - if (report_step < 0) - throw std::invalid_argument("report step cannot be negative"); +/** Report steps are derived from file names and stored as signed 32 bit values + * in the summary files; validate before using them as indices. */ +size_t validate_non_negative(int value, const char *what) { + if (value < 0) + throw std::invalid_argument(std::string(what) + " cannot be negative"); + return static_cast(value); } } // namespace @@ -208,15 +212,15 @@ rd_sum_file_data::rd_sum_file_data(const rd_smspec_type *smspec) rd_sum_file_data::~rd_sum_file_data() = default; -int rd_sum_file_data::length() const { +size_t rd_sum_file_data::length() const { if (this->loader) return this->loader->length(); else return this->index.size(); } -int rd_sum_file_data::length_before(time_t end_time) const { - int offset = 0; +size_t rd_sum_file_data::length_before(time_t end_time) const { + size_t offset = 0; while (true) { time_t itime = this->iget_sim_time(offset); if (itime >= end_time) @@ -228,20 +232,24 @@ int rd_sum_file_data::length_before(time_t end_time) const { } } -int rd_sum_file_data::report_before(time_t end_time) const { - int len = this->length_before(end_time); - if (len <= 0) +size_t rd_sum_file_data::report_before(time_t end_time) const { + size_t len = this->length_before(end_time); + if (len == 0) throw std::invalid_argument("time argument before first report step"); return this->index[len - 1].report_step; } -int rd_sum_file_data::first_report() const { +size_t rd_sum_file_data::first_report() const { + if (this->index.empty()) + throw std::out_of_range("rd_sum_file_data::first_report(): no data"); const auto &node = this->index[0]; return node.report_step; } -int rd_sum_file_data::last_report() const { +size_t rd_sum_file_data::last_report() const { + if (this->index.empty()) + throw std::out_of_range("rd_sum_file_data::last_report(): no data"); const auto &node = this->index.back(); return node.report_step; } @@ -261,7 +269,7 @@ time_t rd_sum_file_data::get_sim_end() const { return node.sim_time; } -time_t rd_sum_file_data::iget_sim_time(int time_index) const { +time_t rd_sum_file_data::iget_sim_time(size_t time_index) const { const auto &node = this->index[time_index]; return node.sim_time; } @@ -270,11 +278,13 @@ time_t rd_sum_file_data::iget_sim_time(int time_index) const { Will return the length of the simulation in whatever units were used in input. */ double rd_sum_file_data::get_sim_length() const { + if (this->index.empty()) + return 0.0; const auto &node = this->index.back(); return node.sim_seconds / rd_smspec_get_time_seconds(this->rd_smspec); } -double rd_sum_file_data::iget(int time_index, int params_index) const { +double rd_sum_file_data::iget(size_t time_index, size_t params_index) const { if (this->loader) return this->loader->iget(time_index, params_index); else { @@ -290,14 +300,15 @@ double rd_sum_file_data::iget(int time_index, int params_index) const { rd_sum_tstep_iset() to set elements in the tstep. */ -rd_sum_tstep_type *rd_sum_file_data::add_new_tstep(int report_step, +rd_sum_tstep_type *rd_sum_file_data::add_new_tstep(size_t report_step, double sim_seconds) { - validate_report_step(report_step); - + if (report_step == std::numeric_limits::max()) + throw std::invalid_argument("report step is too large"); int ministep_nr = static_cast(data.size()); - rd_sum_tstep_ptr tstep(rd_sum_tstep_alloc_new(report_step, ministep_nr, - sim_seconds, rd_smspec), - &rd_sum_tstep_free); + rd_sum_tstep_ptr tstep( + rd_sum_tstep_alloc_new(report_step, ministep_nr, + static_cast(sim_seconds), rd_smspec), + &rd_sum_tstep_free); rd_sum_tstep_type *prev_tstep = NULL; if (!data.empty()) @@ -335,16 +346,17 @@ rd_sum_tstep_type *rd_sum_file_data::add_new_tstep(int report_step, return new_tstep; } -rd_sum_tstep_type *rd_sum_file_data::iget_ministep(int internal_index) const { +rd_sum_tstep_type * +rd_sum_file_data::iget_ministep(size_t internal_index) const { return data.at(internal_index).get(); } -double rd_sum_file_data::iget_sim_days(int time_index) const { +double rd_sum_file_data::iget_sim_days(size_t time_index) const { const auto &node = this->index[time_index]; return node.sim_seconds / 86400; } -double rd_sum_file_data::iget_sim_seconds(int time_index) const { +double rd_sum_file_data::iget_sim_seconds(size_t time_index) const { const auto &node = this->index[time_index]; return node.sim_seconds; } @@ -359,12 +371,12 @@ void rd_sum_file_data::build_index() { this->index.clear(); if (this->loader) { - int offset = rd_smspec_get_first_step(this->rd_smspec) - 1; - std::vector report_steps = this->loader->report_steps(offset); + size_t offset = rd_smspec_get_first_step(this->rd_smspec) - 1; + std::vector report_steps = this->loader->report_steps(offset); std::vector sim_time = this->loader->sim_time(); std::vector sim_seconds = this->loader->sim_seconds(); - for (int i = 0; i < this->loader->length(); i++) { + for (size_t i = 0; i < this->loader->length(); i++) { this->index.add(sim_time[i], sim_seconds[i], report_steps[i]); } } else { @@ -376,67 +388,64 @@ void rd_sum_file_data::build_index() { } } -void rd_sum_file_data::get_time(int length, time_t *data) { - for (int time_index = 0; time_index < length; time_index++) +void rd_sum_file_data::get_time(size_t length, time_t *data) { + for (size_t time_index = 0; time_index < length; time_index++) data[time_index] = this->iget_sim_time(time_index); } -int rd_sum_file_data::get_time_report(int end_index, time_t *data) { - int offset = 0; - - for (int report_step = this->first_report(); +void rd_sum_file_data::get_time_report(size_t end_index, + std::vector &data) { + for (size_t report_step = this->first_report(); report_step <= this->last_report(); report_step++) { - const auto &range = this->report_range(report_step); - int time_index = range.second; - if (time_index >= end_index) - break; + const auto range = this->report_range(report_step); + if (!range) + continue; - data[offset] = this->iget_sim_time(time_index); + if (range->last >= end_index) + break; - offset += 1; + data.push_back(this->iget_sim_time(range->last)); } - return offset; } -void rd_sum_file_data::get_data(int params_index, int length, double *data) { +void rd_sum_file_data::get_data(size_t params_index, size_t length, + double *data) { if (this->loader) { const auto tmp_data = loader->get_vector(params_index); memcpy(data, tmp_data.data(), length * sizeof data); } else { - for (int time_index = 0; time_index < length; time_index++) + for (size_t time_index = 0; time_index < length; time_index++) data[time_index] = this->iget(time_index, params_index); } } -int rd_sum_file_data::get_data_report(int params_index, int end_index, - double *data, double default_value) { - int offset = 0; - - for (int report_step = this->first_report(); +void rd_sum_file_data::get_data_report(std::optional params_index, + size_t end_index, double default_value, + std::vector &data) { + for (size_t report_step = this->first_report(); report_step <= this->last_report(); report_step++) { - int time_index = this->index.report_range(report_step).second; - if (time_index >= end_index) - break; + const auto range = this->report_range(report_step); + if (!range) + continue; - if (params_index >= 0) - data[offset] = this->iget(time_index, params_index); - else - data[offset] = default_value; + if (range->last >= end_index) + break; - offset += 1; + data.push_back(params_index ? this->iget(range->last, *params_index) + : default_value); } - return offset; } -bool rd_sum_file_data::has_report(int report_step) const { +bool rd_sum_file_data::has_report(size_t report_step) const { return this->index.has_report(report_step); } -std::pair rd_sum_file_data::report_range(int report_step) const { +std::optional +rd_sum_file_data::report_range(size_t report_step) const { return this->index.report_range(report_step); } -void rd_sum_file_data::fwrite_report(int report_step, +void rd_sum_file_data::fwrite_report(size_t report_step, ERT::FortIO &fortio) const { { rd::KW seqhdr_kw{SEQHDR_KW, std::vector(SEQHDR_SIZE, 0)}; @@ -444,12 +453,14 @@ void rd_sum_file_data::fwrite_report(int report_step, } { - auto range = this->report_range(report_step); - for (int index = range.first; index <= range.second; index++) { + const auto range = this->report_range(report_step); + if (!range) + return; + + for (size_t index = range->first; index <= range->last; index++) { const rd_sum_tstep_type *tstep = iget_ministep(index); - //rd_sum_tstep_fwrite( tstep , rd_smspec_get_index_map( rd_smspec ) , fortio ); rd_sum_tstep_fwrite(tstep, rd_smspec_get_index_map(rd_smspec), - rd_smspec_num_nodes(rd_smspec), fortio); + fortio); } } } @@ -458,7 +469,7 @@ void rd_sum_file_data::fwrite_unified(ERT::FortIO &fortio) const { if (this->length() == 0) return; - for (int report_step = first_report(); report_step <= last_report(); + for (size_t report_step = first_report(); report_step <= last_report(); report_step++) { if (has_report(report_step)) fwrite_report(report_step, fortio); @@ -470,7 +481,7 @@ void rd_sum_file_data::fwrite_multiple(const std::string &rd_case, if (this->length() == 0) return; - for (int report_step = this->first_report(); + for (size_t report_step = this->first_report(); report_step <= this->last_report(); report_step++) { if (this->has_report(report_step)) { fs::path filename = @@ -490,8 +501,11 @@ bool rd_sum_file_data::can_write() const { } double rd_sum_file_data::get_days_start() const { + if (this->index.empty()) + throw std::out_of_range( + "rd_sum_file_data::get_days_start(): index size is 0"); const auto &node = this->index[0]; - return node.sim_seconds * 86400; + return node.sim_seconds / 86400; } bool rd_sum_file_data::check_file(rd::File *rd_file) { @@ -524,10 +538,8 @@ bool rd_sum_file_data::check_file(rd::File *rd_file) { calling routine will read the unified summary file partly. */ -void rd_sum_file_data::add_rd_file(int report_step, +void rd_sum_file_data::add_rd_file(size_t report_step, rd::FileView &summary_view) { - validate_report_step(report_step); - size_t num_ministep = summary_view.num_named_kw(PARAMS_KW); if (num_ministep > 0) { @@ -575,7 +587,9 @@ bool rd_sum_file_data::fread(const std::vector &filelist, std::unique_ptr rd_file = rd::File::open(data_file); if (rd_file && check_file(rd_file.get())) { auto global_view = rd_file->get_global_view(); - this->add_rd_file(report_step, *global_view); + this->add_rd_file( + validate_non_negative(report_step, "report step"), + *global_view); } } } @@ -593,9 +607,9 @@ bool rd_sum_file_data::fread(const std::vector &filelist, // report step sequence will be restarted? std::unique_ptr rd_file = rd::File::open(first_file); if (rd_file && check_file(rd_file.get())) { - int first_report_step = + size_t first_report_step = rd_smspec_get_first_step(this->rd_smspec); - int block_index = 0; + size_t block_index = 0; while (true) { /* Observe that there is a number discrepancy between ECLIPSE @@ -624,42 +638,40 @@ const rd_smspec_type *rd_sum_file_data::smspec() const { return this->rd_smspec; } -int rd_sum_file_data::report_step_from_days(double sim_days) const { - int report_step = this->first_report(); +std::optional +rd_sum_file_data::report_step_from_days(double sim_days) const { double sim_seconds = sim_days * 86400; - while (true) { - const auto &range = this->index.report_range(report_step); - if (range.second >= 0) { - const auto &node = this->index[range.second]; + for (size_t report_step = this->first_report(); + report_step <= this->last_report(); report_step++) { + const auto range = this->index.report_range(report_step); + if (!range) + continue; - // Warning - this is a double == comparison! - if (sim_seconds == node.sim_seconds) - return report_step; + const auto &node = this->index[range->last]; - report_step++; - if (report_step > this->last_report()) - return -1; - } + // Warning - this is a double == comparison! + if (sim_seconds == node.sim_seconds) + return report_step; } + return std::nullopt; } -int rd_sum_file_data::report_step_from_time(time_t sim_time) const { - int report_step = this->first_report(); - while (true) { - const auto &range = this->index.report_range(report_step); - if (range.second >= 0) { - const auto &node = this->index[range.second]; - if (sim_time == node.sim_time) - return report_step; - - report_step++; - if (report_step > this->last_report()) - return -1; - } +std::optional +rd_sum_file_data::report_step_from_time(time_t sim_time) const { + for (size_t report_step = this->first_report(); + report_step <= this->last_report(); report_step++) { + const auto range = this->index.report_range(report_step); + if (!range) + continue; + + const auto &node = this->index[range->last]; + if (sim_time == node.sim_time) + return report_step; } + return std::nullopt; } -int rd_sum_file_data::iget_report(int time_index) const { +size_t rd_sum_file_data::iget_report(size_t time_index) const { const auto &index_node = this->index[time_index]; return index_node.report_step; } diff --git a/lib/resdata/rd_sum_pybind.cpp b/lib/resdata/rd_sum_pybind.cpp index 150dbc358..1214af7b6 100644 --- a/lib/resdata/rd_sum_pybind.cpp +++ b/lib/resdata/rd_sum_pybind.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -22,6 +23,14 @@ namespace py = pybind11; namespace { +/** The Python API documents "not found" as -1; the C++ API returns + * std::nullopt. */ +py::int_ optional_index_to_py(std::optional index) { + if (index) + return py::int_(*index); + return py::int_(-1); +} + PYBIND11_MODULE(_rd_sum, m) { register_exceptions(m); m.doc() = "pybind11 bindings between rd_sum.py and rd_sum.cpp"; @@ -72,28 +81,28 @@ PYBIND11_MODULE(_rd_sum, m) { py::return_value_policy::reference); m.def("_free", [](py::handle self) { rd_sum_free(from_cwrap(self)); }); - m.def("_iiget", [](py::handle self, int time_index, int param_index) { + m.def("_iiget", [](py::handle self, size_t time_index, size_t param_index) { return rd_sum_iget(from_cwrap(self), time_index, param_index); }); m.def("_data_length", [](py::handle self) { return rd_sum_get_data_length(from_cwrap(self)); }); - m.def("_iget_sim_days", [](py::handle self, int index) { + m.def("_iget_sim_days", [](py::handle self, size_t index) { return rd_sum_iget_sim_days(from_cwrap(self), index); }); - m.def("_iget_report_step", [](py::handle self, int index) { + m.def("_iget_report_step", [](py::handle self, size_t index) { return rd_sum_iget_report_step(from_cwrap(self), index); }); - m.def("_iget_sim_time", [](py::handle self, int index) -> std::time_t { + m.def("_iget_sim_time", [](py::handle self, size_t index) -> std::time_t { return rd_sum_iget_sim_time(from_cwrap(self), index); }); - m.def("_get_report_end", [](py::handle self, int report_step) { + m.def("_get_report_end", [](py::handle self, size_t report_step) { return rd_sum_iget_report_end(from_cwrap(self), report_step); }); m.def("_get_general_var", - [](py::handle self, int time_index, std::string lookup_kw) { + [](py::handle self, size_t time_index, std::string lookup_kw) { return rd_sum_get_general_var(from_cwrap(self), time_index, lookup_kw.c_str()); }); @@ -111,14 +120,16 @@ PYBIND11_MODULE(_rd_sum, m) { return rd_sum_get_general_var_from_sim_time( from_cwrap(self), sim_time, var.c_str()); }); - m.def("_get_first_gt", [](py::handle self, int param_index, double limit) { - return rd_sum_get_first_gt(from_cwrap(self), param_index, - limit); - }); - m.def("_get_first_lt", [](py::handle self, int param_index, double limit) { - return rd_sum_get_first_lt(from_cwrap(self), param_index, - limit); - }); + m.def("_get_first_gt", + [](py::handle self, size_t param_index, double limit) { + return optional_index_to_py(rd_sum_get_first_gt( + from_cwrap(self), param_index, limit)); + }); + m.def("_get_first_lt", + [](py::handle self, size_t param_index, double limit) { + return optional_index_to_py(rd_sum_get_first_lt( + from_cwrap(self), param_index, limit)); + }); m.def("_get_start_date", [](py::handle self) -> std::time_t { return rd_sum_get_start_time(from_cwrap(self)); }); @@ -190,15 +201,15 @@ PYBIND11_MODULE(_rd_sum, m) { }); m.def("_get_report_step_from_time", [](py::handle self, std::time_t sim_time) { - return rd_sum_get_report_step_from_time( - from_cwrap(self), sim_time); + return optional_index_to_py(rd_sum_get_report_step_from_time( + from_cwrap(self), sim_time)); }); m.def("_get_report_step_from_days", [](py::handle self, double sim_days) { - return rd_sum_get_report_step_from_days(from_cwrap(self), - sim_days); + return optional_index_to_py(rd_sum_get_report_step_from_days( + from_cwrap(self), sim_days)); }); m.def("_get_report_time", - [](py::handle self, int report_step) -> std::time_t { + [](py::handle self, size_t report_step) -> std::time_t { return rd_sum_get_report_time(from_cwrap(self), report_step); }); @@ -224,7 +235,7 @@ PYBIND11_MODULE(_rd_sum, m) { return result; }); m.def("_alloc_data_vector", - [](py::handle self, int data_index, bool report_only) { + [](py::handle self, size_t data_index, bool report_only) { return rd_sum_alloc_data_vector(from_cwrap(self), data_index, report_only); }); @@ -285,10 +296,10 @@ PYBIND11_MODULE(_rd_sum, m) { [](py::handle self, std::string keyword, std::optional wgname, int num, std::string unit, double default_value) { - return const_cast( - rd_sum_add_var(from_cwrap(self), keyword.c_str(), - wgname.has_value() ? wgname->c_str() : nullptr, - num, unit.c_str(), default_value)); + return const_cast(rd_sum_add_var( + from_cwrap(self), keyword.c_str(), + wgname.has_value() ? wgname->c_str() : nullptr, num, + unit.c_str(), static_cast(default_value))); }, py::return_value_policy::reference, py::keep_alive<0, 1>()); m.def( @@ -300,12 +311,13 @@ PYBIND11_MODULE(_rd_sum, m) { return const_cast(rd_sum_add_local_var( from_cwrap(self), keyword.c_str(), wgname.has_value() ? wgname->c_str() : nullptr, num, - unit.c_str(), lgr.c_str(), lgr_i, lgr_j, lgr_k, default_value)); + unit.c_str(), lgr.c_str(), lgr_i, lgr_j, lgr_k, + static_cast(default_value))); }, py::return_value_policy::reference, py::keep_alive<0, 1>()); m.def( "_add_tstep", - [](py::handle self, int report_step, double sim_seconds) { + [](py::handle self, size_t report_step, double sim_seconds) { return to_capsule(rd_sum_add_tstep(from_cwrap(self), report_step, sim_seconds)); }, @@ -331,17 +343,19 @@ PYBIND11_MODULE(_rd_sum, m) { auto keyvec = from_cwrap(keywords); auto rd_sum = from_cwrap(self); double *out = data.mutable_data(); - int keylen = rd_sum_vector_get_size(keyvec); - int timelen = rd_sum_get_data_length(rd_sum); - if (data.request().size < timelen * keylen) + size_t keylen = rd_sum_vector_get_size(keyvec); + size_t timelen = rd_sum_get_data_length(rd_sum); + if (static_cast(data.request().size) < timelen * keylen) throw std::invalid_argument("Incorrect size of buffer"); - for (int time_index = 0; time_index < timelen; time_index++) { - for (int key_index = 0; key_index < keylen; key_index++) { - int param_index = + for (size_t time_index = 0; time_index < timelen; time_index++) { + for (size_t key_index = 0; key_index < keylen; key_index++) { + size_t data_index = key_index + time_index * keylen; + auto param_index = rd_sum_vector_iget_param_index(keyvec, key_index); - int data_index = key_index + time_index * keylen; out[data_index] = - rd_sum_iget(rd_sum, time_index, param_index); + param_index + ? rd_sum_iget(rd_sum, time_index, *param_index) + : std::nan(""); } } }); @@ -396,10 +410,10 @@ PYBIND11_MODULE(_rd_sum, m) { int multiplier) { int64_t *out = data.mutable_data(); auto rd_sum = from_cwrap(self); - int len = rd_sum_get_data_length(rd_sum); - if (data.request().size < len) + size_t len = rd_sum_get_data_length(rd_sum); + if (static_cast(data.request().size) < len) throw std::invalid_argument("Incorrect size of buffer"); - for (int i = 0; i < len; i++) + for (size_t i = 0; i < len; i++) out[i] = rd_sum_iget_sim_time(rd_sum, i) * multiplier; }); } diff --git a/lib/resdata/rd_sum_tstep.cpp b/lib/resdata/rd_sum_tstep.cpp index 8a9d7181b..97854373f 100644 --- a/lib/resdata/rd_sum_tstep.cpp +++ b/lib/resdata/rd_sum_tstep.cpp @@ -53,14 +53,16 @@ struct rd_sum_tstep_struct { time_t sim_time; /* The true time (i.e. 20.th of october 2010) of corresponding to this timestep. */ int ministep; /* The simulator time-step number; one ministep per numerical timestep. */ - int report_step; /* The report step this time-step is part of - in general there can be many timestep for each report step. */ + size_t + report_step; /* The report step this time-step is part of - in general there can be many timestep for each report step. */ double sim_seconds; /* Accumulated simulation time up to this ministep. */ int internal_index; /* Used for lookups of the next / previous ministep based on an existing ministep. */ const rd_smspec_type * smspec; /* The smespec header information for this tstep - must be compatible. */ }; -static rd_sum_tstep_type *rd_sum_tstep_alloc(int report_step, int ministep_nr, +static rd_sum_tstep_type *rd_sum_tstep_alloc(size_t report_step, + int ministep_nr, const rd_smspec_type *smspec) { auto tstep = std::make_unique(); UTIL_TYPE_ID_INIT(tstep.get(), RD_SUM_TSTEP_ID); @@ -138,13 +140,13 @@ static void rd_sum_tstep_set_time_info(rd_sum_tstep_type *tstep, the function will return NULL: */ -rd_sum_tstep_type *rd_sum_tstep_alloc_from_file(int report_step, +rd_sum_tstep_type *rd_sum_tstep_alloc_from_file(size_t report_step, int ministep_nr, const rd::KW *params_kw, const char *src_file, const rd_smspec_type *smspec) { - int data_size = rd::kw_get_size(params_kw); + size_t data_size = params_kw->size(); if (data_size == rd_smspec_get_params_size(smspec)) { std::unique_ptr @@ -161,8 +163,8 @@ rd_sum_tstep_type *rd_sum_tstep_alloc_from_file(int report_step, discarded. */ fprintf(stderr, - "** Warning size mismatch between timestep loaded from:%s(%d) " - "and header:%s(%d) - timestep discarded.\n", + "** Warning size mismatch between timestep loaded from:%s(%zu) " + "and header:%s(%zu) - timestep discarded.\n", src_file, data_size, rd_smspec_get_header_file(smspec), rd_smspec_get_params_size(smspec)); return NULL; @@ -173,7 +175,7 @@ rd_sum_tstep_type *rd_sum_tstep_alloc_from_file(int report_step, Should be called in write mode. */ -rd_sum_tstep_type *rd_sum_tstep_alloc_new(int report_step, int ministep, +rd_sum_tstep_type *rd_sum_tstep_alloc_new(size_t report_step, int ministep, float sim_seconds, const rd_smspec_type *smspec) { std::unique_ptr tstep( @@ -188,12 +190,12 @@ rd_sum_tstep_type *rd_sum_tstep_alloc_new(int report_step, int ministep, throw std::invalid_argument("Cannot create a new summary timestep for " "a SMSPEC without a TIME variable"); rd_sum_tstep_iset(tstep.get(), time->params_index, - sim_seconds / time->seconds_per_unit); + sim_seconds / static_cast(time->seconds_per_unit)); return tstep.release(); } -double rd_sum_tstep_iget(const rd_sum_tstep_type *ministep, int index) { - if ((index >= 0) && (index < (int)ministep->data.size())) +double rd_sum_tstep_iget(const rd_sum_tstep_type *ministep, size_t index) { + if (index < ministep->data.size()) return ministep->data[index]; else { throw std::out_of_range( @@ -214,7 +216,7 @@ double rd_sum_tstep_get_sim_seconds(const rd_sum_tstep_type *ministep) { return ministep->sim_seconds; } -int rd_sum_tstep_get_report(const rd_sum_tstep_type *ministep) { +size_t rd_sum_tstep_get_report(const rd_sum_tstep_type *ministep) { return ministep->report_step; } @@ -223,22 +225,21 @@ int rd_sum_tstep_get_ministep(const rd_sum_tstep_type *ministep) { } void rd_sum_tstep_fwrite(const rd_sum_tstep_type *ministep, - const int *index_map, int index_map_size, + const std::vector &index_map, ERT::FortIO &fortio) { rd::KW ministep_kw{MINISTEP_KW, std::vector{ministep->ministep}}; ministep_kw.fwrite(fortio); - int compact_size = index_map_size; - std::vector data(compact_size); - for (int i = 0; i < compact_size; i++) + std::vector data(index_map.size()); + for (size_t i = 0; i < index_map.size(); i++) data[i] = ministep->data[index_map[i]]; rd::KW params_kw{PARAMS_KW, data}; params_kw.fwrite(fortio); } -void rd_sum_tstep_iset(rd_sum_tstep_type *tstep, int index, float value) { - if ((index < static_cast(tstep->data.size())) && (index >= 0)) +void rd_sum_tstep_iset(rd_sum_tstep_type *tstep, size_t index, float value) { + if (index < tstep->data.size()) tstep->data[index] = value; else throw std::out_of_range( @@ -249,13 +250,13 @@ void rd_sum_tstep_iset(rd_sum_tstep_type *tstep, int index, float value) { void rd_sum_tstep_set_from_node(rd_sum_tstep_type *tstep, const rd::smspec_node &smspec_node, float value) { - int data_index = smspec_node.get_params_index(); + size_t data_index = smspec_node.get_params_index(); rd_sum_tstep_iset(tstep, data_index, value); } double rd_sum_tstep_get_from_node(const rd_sum_tstep_type *tstep, const rd::smspec_node &smspec_node) { - int data_index = smspec_node.get_params_index(); + size_t data_index = smspec_node.get_params_index(); return rd_sum_tstep_iget(tstep, data_index); } diff --git a/lib/resdata/rd_sum_vector.cpp b/lib/resdata/rd_sum_vector.cpp index 389a3e622..940fc19e6 100644 --- a/lib/resdata/rd_sum_vector.cpp +++ b/lib/resdata/rd_sum_vector.cpp @@ -1,4 +1,6 @@ +#include #include +#include #include #include @@ -14,7 +16,7 @@ struct rd_sum_vector_struct { UTIL_TYPE_ID_DECLARATION; - std::vector node_index_list; + std::vector> node_index_list; std::vector is_rate_list; std::vector key_list; const rd_sum_type *rd_sum; @@ -29,7 +31,7 @@ UTIL_IS_INSTANCE_FUNCTION(rd_sum_vector, RD_SUM_VECTOR_TYPE_ID) static void rd_sum_vector_add_node(rd_sum_vector_type *vector, const rd::smspec_node *node, const char *key) { - int params_index = node->get_params_index(); + size_t params_index = node->get_params_index(); bool is_rate_key = node->is_rate(); vector->node_index_list.push_back(params_index); @@ -44,7 +46,7 @@ rd_sum_vector_type *rd_sum_vector_alloc(const rd_sum_type *rd_sum, rd_sum_vector->rd_sum = rd_sum; if (add_keywords) { const rd_smspec_type *smspec = rd_sum_get_smspec(rd_sum); - for (int i = 0; i < rd_smspec_num_nodes(smspec); i++) { + for (size_t i = 0; i < rd_smspec_num_nodes(smspec); i++) { const rd::smspec_node &node = rd_smspec_iget_node_w_node_index(smspec, i); const char *key = node.get_gen_key1(); @@ -61,7 +63,7 @@ rd_sum_vector_type *rd_sum_vector_alloc(const rd_sum_type *rd_sum, static void rd_sum_vector_add_invalid_key(rd_sum_vector_type *vector, const char *key) { - vector->node_index_list.push_back(-1); + vector->node_index_list.push_back(std::nullopt); vector->is_rate_list.push_back(false); vector->key_list.push_back(key); } @@ -114,26 +116,22 @@ void rd_sum_vector_add_keys(rd_sum_vector_type *rd_sum_vector, } } -int rd_sum_vector_get_size(const rd_sum_vector_type *rd_sum_vector) { +size_t rd_sum_vector_get_size(const rd_sum_vector_type *rd_sum_vector) { return rd_sum_vector->node_index_list.size(); } bool rd_sum_vector_iget_is_rate(const rd_sum_vector_type *rd_sum_vector, - int index) { - return rd_sum_vector->is_rate_list[index]; + size_t index) { + return rd_sum_vector->is_rate_list.at(index); } -bool rd_sum_vector_iget_valid(const rd_sum_vector_type *rd_sum_vector, - int index) { - return (rd_sum_vector->node_index_list[index] >= 0); -} - -int rd_sum_vector_iget_param_index(const rd_sum_vector_type *rd_sum_vector, - int index) { - return rd_sum_vector->node_index_list[index]; +std::optional +rd_sum_vector_iget_param_index(const rd_sum_vector_type *rd_sum_vector, + size_t index) { + return rd_sum_vector->node_index_list.at(index); } const char *rd_sum_vector_iget_key(const rd_sum_vector_type *rd_sum_vector, - int index) { - return rd_sum_vector->key_list[index].c_str(); + size_t index) { + return rd_sum_vector->key_list.at(index).c_str(); } diff --git a/lib/resdata/rd_sum_vector_pybind.cpp b/lib/resdata/rd_sum_vector_pybind.cpp index 355c96021..03e8d37a6 100644 --- a/lib/resdata/rd_sum_vector_pybind.cpp +++ b/lib/resdata/rd_sum_vector_pybind.cpp @@ -43,7 +43,7 @@ PYBIND11_MODULE(_rd_sum_keyword_vector, m) { m.def("_get_size", [](py::handle self) { return rd_sum_vector_get_size(from_cwrap(self)); }); - m.def("_iget_key", [](py::handle self, int index) { + m.def("_iget_key", [](py::handle self, size_t index) { return std::string(rd_sum_vector_iget_key( from_cwrap(self), index)); }); diff --git a/lib/resdata/rd_unsmry_loader.cpp b/lib/resdata/rd_unsmry_loader.cpp index 01d5ae28f..b704db7d2 100644 --- a/lib/resdata/rd_unsmry_loader.cpp +++ b/lib/resdata/rd_unsmry_loader.cpp @@ -44,7 +44,7 @@ unsmry_loader::unsmry_loader(const rd_smspec_type *smspec, if (const auto *date = std::get_if(&this->time_info)) this->date_index = {{date->day, date->month, date->year}}; auto file_view = this->file->get_global_view(); - int length = file_view->num_named_kw(PARAMS_KW); + size_t length = file_view->num_named_kw(PARAMS_KW); if (length > 0) { const rd::KW *params_kw = file_view->get_kw(PARAMS_KW, 0); @@ -62,9 +62,9 @@ unsmry_loader::unsmry_loader(const rd_smspec_type *smspec, this->m_length = length; } -int unsmry_loader::length() const { return this->m_length; } +size_t unsmry_loader::length() const { return this->m_length; } -std::vector unsmry_loader::get_vector(int pos) const { +std::vector unsmry_loader::get_vector(size_t pos) const { if (pos >= size) throw std::out_of_range( "unsmry_loader::get_vector pos: " + std::to_string(pos) + @@ -73,7 +73,7 @@ std::vector unsmry_loader::get_vector(int pos) const { std::vector data(this->length()); std::array value; - for (int index = 0; index < this->length(); index++) { + for (size_t index = 0; index < this->length(); index++) { file_view->index_fload_kw(PARAMS_KW, index, {pos}, value); data[index] = value[0]; } @@ -85,13 +85,13 @@ std::vector unsmry_loader::get_vector(int pos) const { } // This is horribly inefficient -double unsmry_loader::iget(int time_index, int params_index) const { +double unsmry_loader::iget(size_t time_index, size_t params_index) const { std::array value; file_view->index_fload_kw(PARAMS_KW, time_index, {params_index}, value); return value[0]; } -time_t unsmry_loader::iget_sim_time(int time_index) const { +time_t unsmry_loader::iget_sim_time(size_t time_index) const { if (std::holds_alternative(this->time_info)) { double sim_seconds = this->iget_sim_seconds(time_index); time_t sim_time = this->sim_start; @@ -109,7 +109,7 @@ time_t unsmry_loader::iget_sim_time(int time_index) const { } } -double unsmry_loader::iget_sim_seconds(int time_index) const { +double unsmry_loader::iget_sim_seconds(size_t time_index) const { if (const auto *time = std::get_if(&this->time_info)) { double raw_time = this->iget(time_index, time->params_index); return raw_time * time->seconds_per_unit; @@ -119,9 +119,9 @@ double unsmry_loader::iget_sim_seconds(int time_index) const { } } -std::vector unsmry_loader::report_steps(int offset) const { - std::vector report_steps; - int current_step = offset; +std::vector unsmry_loader::report_steps(size_t offset) const { + std::vector report_steps; + size_t current_step = offset; for (const auto &file_kw : *file_view) { if (SEQHDR_KW == file_kw->get_header()) current_step++; diff --git a/lib/resdata/rd_util.cpp b/lib/resdata/rd_util.cpp index 45cb4a54d..4d577ee25 100644 --- a/lib/resdata/rd_util.cpp +++ b/lib/resdata/rd_util.cpp @@ -336,8 +336,9 @@ namespace rd { * the extension. The extension is lower cased whenever the base name is, so * that a case named 'simple' gets 'simple.egrid'. */ +template fs::path filename(fs::path casepath, FileType file_type, bool fmt_file, - int report_nr) { + ReportNr report_nr) { const std::string base = casepath.filename().string(); std::string ext; @@ -392,6 +393,9 @@ fs::path filename(fs::path casepath, FileType file_type, bool fmt_file, return casepath.parent_path() / (base + "." + ext); } + +template fs::path filename(fs::path, FileType, bool, int); +template fs::path filename(fs::path, FileType, bool, size_t); } // namespace rd /** This function assumes that: diff --git a/lib/resdata/smspec_node.cpp b/lib/resdata/smspec_node.cpp index 0c789437c..175aadaae 100644 --- a/lib/resdata/smspec_node.cpp +++ b/lib/resdata/smspec_node.cpp @@ -845,42 +845,42 @@ rd_smspec_var_type smspec_node::valid_type(const char *keyword, rd_smspec_fread_header() functions in addition. UGGGLY */ -smspec_node::smspec_node(int param_index, const char *keyword, int num, +smspec_node::smspec_node(size_t param_index, const char *keyword, int num, const char *unit, const int grid_dims[3], float default_value, const char *key_join_string) : smspec_node(param_index, keyword, nullptr, num, unit, grid_dims, default_value, key_join_string) {} -smspec_node::smspec_node(int param_index, const char *keyword, int num, +smspec_node::smspec_node(size_t param_index, const char *keyword, int num, const char *unit, float default_value, const char *key_join_string) : smspec_node(param_index, keyword, nullptr, num, unit, nullptr, default_value, key_join_string) {} -smspec_node::smspec_node(int param_index, const char *keyword, +smspec_node::smspec_node(size_t param_index, const char *keyword, const char *wgname, const char *unit, float default_value, const char *key_join_string) : smspec_node(param_index, keyword, wgname, nums_unused, unit, nullptr, default_value, key_join_string) {} -smspec_node::smspec_node(int param_index, const char *keyword, +smspec_node::smspec_node(size_t param_index, const char *keyword, const char *wgname, int num, const char *unit, float default_value, const char *key_join_string) : smspec_node(param_index, keyword, wgname, num, unit, nullptr, default_value, key_join_string) {} -smspec_node::smspec_node(int param_index, const char *keyword, const char *unit, - float default_value) +smspec_node::smspec_node(size_t param_index, const char *keyword, + const char *unit, float default_value) : smspec_node(param_index, keyword, nullptr, nums_unused, unit, nullptr, default_value, nullptr) {} //copy constructor with a new id -smspec_node::smspec_node(const smspec_node &node, int param_index) { +smspec_node::smspec_node(const smspec_node &node, size_t param_index) { *this = node; this->params_index = param_index; } -smspec_node::smspec_node(int param_index, const char *keyword, +smspec_node::smspec_node(size_t param_index, const char *keyword, const char *wgname, int num, const char *unit, const int grid_dims[3], float default_value, const char *key_join_string) { @@ -991,7 +991,7 @@ smspec_node::smspec_node(int param_index, const char *keyword, set_gen_keys(key_join_string); } -smspec_node::smspec_node(int param_index_, const char *keyword_, +smspec_node::smspec_node(size_t param_index_, const char *keyword_, const char *wgname_, const char *unit_, const char *lgr_, int lgr_i, int lgr_j, int lgr_k, float default_value_, const char *key_join_string_) { diff --git a/lib/resdata/well_rseg_loader.cpp b/lib/resdata/well_rseg_loader.cpp index b9bd1926f..6a0ecdfdc 100644 --- a/lib/resdata/well_rseg_loader.cpp +++ b/lib/resdata/well_rseg_loader.cpp @@ -1,6 +1,5 @@ #include #include -#include #include #include #include @@ -17,8 +16,8 @@ struct well_rseg_loader_struct { rd::FileView *rst_view; - std::vector relative_index_map; - std::vector absolute_index_map; + std::vector relative_index_map; + std::vector absolute_index_map; std::array buffer; const char *kw; }; @@ -53,14 +52,8 @@ double *well_rseg_loader_load_values(well_rseg_loader_type *loader, size_t rseg_offset) { loader->absolute_index_map.resize(loader->relative_index_map.size(), 0); for (size_t index = 0; index < loader->relative_index_map.size(); index++) { - size_t absolute_index = - static_cast(loader->relative_index_map[index]) + - rseg_offset; - if (absolute_index > - static_cast(std::numeric_limits::max())) - throw std::out_of_range("RSEG index out of range: " + - std::to_string(absolute_index)); - loader->absolute_index_map[index] = static_cast(absolute_index); + loader->absolute_index_map[index] = + loader->relative_index_map[index] + rseg_offset; } loader->rst_view->index_fload_kw(loader->kw, 0, loader->absolute_index_map, diff --git a/lib/tests/test_fortio.cpp b/lib/tests/test_fortio.cpp index 2b8bbc676..28fc157ce 100644 --- a/lib/tests/test_fortio.cpp +++ b/lib/tests/test_fortio.cpp @@ -42,10 +42,6 @@ TEST_CASE_METHOD(Tmpdir, "Basic FortIO operations") { else REQUIRE_FALSE(fortio.fseek(0, 2000)); } - THEN("Seeking data with negative count raises") { - REQUIRE_THROWS_AS(fortio.data_fseek(0, 1, 1, -1, 1), - std::invalid_argument); - } THEN("Seeking data beyond count raises") { REQUIRE_THROWS_AS(fortio.data_fseek(0, 6, 1, 5, 1), std::invalid_argument); diff --git a/lib/tests/test_rd_sum.cpp b/lib/tests/test_rd_sum.cpp index 6c41f71f6..58f259098 100644 --- a/lib/tests/test_rd_sum.cpp +++ b/lib/tests/test_rd_sum.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -58,8 +59,8 @@ struct WriteSpec { int nx = 10; int ny = 11; int nz = 12; - int num_report_steps = 5; - int num_ministep = 10; + size_t num_report_steps = 5; + size_t num_ministep = 10; double start_seconds = 0.0; double ministep_length = 86400.0; // one day in seconds }; @@ -82,9 +83,9 @@ time_t write_test_summary(const std::string &case_path, const WriteSpec &spec, rd_sum_add_var(rd_sum.get(), "WWCT", "OP-1", 0, "(1)", 0.0f); double sim_seconds = spec.start_seconds; - for (int report_step = 0; report_step < spec.num_report_steps; + for (size_t report_step = 0; report_step < spec.num_report_steps; ++report_step) { - for (int step = 0; step < spec.num_ministep; ++step) { + for (size_t step = 0; step < spec.num_ministep; ++step) { rd_sum_tstep_type *tstep = rd_sum_add_tstep(rd_sum.get(), report_step + 1, sim_seconds); rd_sum_tstep_set_from_node(tstep, *fopt, sim_seconds); @@ -198,7 +199,7 @@ TEST_CASE_METHOD(Tmpdir, "Read summary written by writer") { REQUIRE_THROWS_AS( rd_sum_iget_report_end(rd_sum.get(), spec.num_report_steps + 1), std::invalid_argument); - REQUIRE_THROWS_AS(rd_sum_iget_report_step(rd_sum.get(), -1), + REQUIRE_THROWS_AS(rd_sum_iget_report_step(rd_sum.get(), SIZE_MAX), std::invalid_argument); REQUIRE_THROWS_AS( rd_sum_iget_report_step(rd_sum.get(), spec.num_report_steps * @@ -253,8 +254,8 @@ TEST_CASE_METHOD(Tmpdir, "Read summary written by writer") { } THEN("iget returns the expected time at every step") { - const int n = rd_sum_get_data_length(rd_sum.get()); - for (int i = 0; i < n; ++i) { + const size_t n = rd_sum_get_data_length(rd_sum.get()); + for (size_t i = 0; i < n; ++i) { const double expected = i * spec.ministep_length; time_t expected_time = spec.start_time; util_inplace_forward_seconds_utc(&expected_time, expected); @@ -287,8 +288,8 @@ TEST_CASE_METHOD(Tmpdir, "Read summary written by writer") { } THEN("iget with out-of-range internal index throws") { - const int n = rd_sum_get_data_length(rd_sum.get()); - REQUIRE_THROWS_AS(rd_sum_iget_sim_time(rd_sum.get(), -1), + const size_t n = rd_sum_get_data_length(rd_sum.get()); + REQUIRE_THROWS_AS(rd_sum_iget_sim_time(rd_sum.get(), SIZE_MAX), std::invalid_argument); REQUIRE_THROWS_AS(rd_sum_iget_sim_time(rd_sum.get(), n), std::invalid_argument); @@ -297,7 +298,7 @@ TEST_CASE_METHOD(Tmpdir, "Read summary written by writer") { } THEN("first_gt returns the first index exceeding the limit") { - const int fopt_idx = + const size_t fopt_idx = rd_sum_get_general_var_params_index(rd_sum.get(), "FOPT"); REQUIRE(rd_sum_get_first_gt(rd_sum.get(), fopt_idx, -1.0) == 0); @@ -311,23 +312,25 @@ TEST_CASE_METHOD(Tmpdir, "Read summary written by writer") { (spec.num_report_steps * spec.num_ministep - 1) * spec.ministep_length; REQUIRE(rd_sum_get_first_gt(rd_sum.get(), fopt_idx, - last_sim_seconds) == -1); + last_sim_seconds) == std::nullopt); - const int bpr_idx = + const size_t bpr_idx = rd_sum_get_general_var_params_index(rd_sum.get(), "BPR:567"); REQUIRE(rd_sum_get_first_gt(rd_sum.get(), bpr_idx, 10.0 * spec.ministep_length) == 2); } THEN("first_lt returns the first index below the limit") { - const int fopt_idx = + const size_t fopt_idx = rd_sum_get_general_var_params_index(rd_sum.get(), "FOPT"); REQUIRE(rd_sum_get_first_lt(rd_sum.get(), fopt_idx, 1.0) == 0); - REQUIRE(rd_sum_get_first_lt(rd_sum.get(), fopt_idx, 0.0) == -1); - REQUIRE(rd_sum_get_first_lt(rd_sum.get(), fopt_idx, -1.0) == -1); + REQUIRE(rd_sum_get_first_lt(rd_sum.get(), fopt_idx, 0.0) == + std::nullopt); + REQUIRE(rd_sum_get_first_lt(rd_sum.get(), fopt_idx, -1.0) == + std::nullopt); - const int wwct_idx = + const size_t wwct_idx = rd_sum_get_general_var_params_index(rd_sum.get(), "WWCT:OP-1"); REQUIRE(rd_sum_get_first_lt(rd_sum.get(), wwct_idx, 1.0) == 0); } @@ -400,12 +403,11 @@ TEST_CASE_METHOD(Tmpdir, "Read summary written by writer") { (size_t)rd_sum_get_data_length(rd_sum.get())); REQUIRE(times[0] == spec.start_time); - const int param_index = + const size_t param_index = rd_sum_get_general_var_params_index(rd_sum.get(), "FOPT"); auto data = rd_sum_alloc_data_vector(rd_sum.get(), param_index, false); - REQUIRE(data.size() == - static_cast(rd_sum_get_data_length(rd_sum.get()))); + REQUIRE(data.size() == rd_sum_get_data_length(rd_sum.get())); REQUIRE_THAT(data[0], WithinAbs(0.0, 1e-6)); REQUIRE_THROWS_AS( @@ -483,15 +485,19 @@ TEST_CASE_METHOD(Tmpdir, "Read summary written by writer") { } } -TEST_CASE_METHOD(Tmpdir, "rd_sum_add_tstep rejects negative report steps") { - const auto case_path = (dirname / "NEGATIVE_REPORT_STEP").string(); - auto rd_sum = make_summary_writer(case_path, /*fmt_output=*/false, - /*unified=*/true, ":", +TEST_CASE_METHOD(Tmpdir, "first_gt and first_lt find nothing in empty data") { + auto rd_sum = make_summary_writer((dirname / "EMPTY").string(), + /*fmt=*/false, /*unified=*/true, ":", util_make_date_utc(1, 1, 2010), - /*time_in_days=*/true, 10, 11, 12); + /*time_in_days=*/true, 10, 10, 10); + const rd::smspec_node *fopt = + rd_sum_add_var(rd_sum.get(), "FOPT", nullptr, 0, "SM3", 0.0f); + REQUIRE(rd_sum_get_data_length(rd_sum.get()) == 0); - REQUIRE_THROWS_AS(rd_sum_add_tstep(rd_sum.get(), -1, 0.0), - std::invalid_argument); + REQUIRE(rd_sum_get_first_gt(rd_sum.get(), fopt->get_params_index(), 0.0) == + std::nullopt); + REQUIRE(rd_sum_get_first_lt(rd_sum.get(), fopt->get_params_index(), 0.0) == + std::nullopt); } TEST_CASE_METHOD(Tmpdir, @@ -603,6 +609,52 @@ TEST_CASE_METHOD(Tmpdir, "rd_sum_add_local_var registers LGR smspec nodes") { } } +TEST_CASE_METHOD( + Tmpdir, "report-only vectors skip report steps missing from the data") { + const auto case_path = (dirname / "GAPCASE").string(); + const time_t start_time = util_make_date_utc(1, 1, 2010); + auto rd_sum = make_summary_writer(case_path, /*fmt_output=*/false, + /*unified=*/true, ":", start_time, true, + 10, 10, 10); + const rd::smspec_node *fopt = + rd_sum_add_var(rd_sum.get(), "FOPT", nullptr, 0, "SM3", 0.0f); + + // Report step 2 is intentionally absent. + const size_t report_steps[] = {1, 3}; + for (size_t i = 0; i < 2; ++i) { + const double sim_seconds = 86400.0 * (i + 1); + rd_sum_tstep_type *tstep = + rd_sum_add_tstep(rd_sum.get(), report_steps[i], sim_seconds); + rd_sum_tstep_set_from_node(tstep, *fopt, 10.0 * (i + 1)); + } + + const size_t params_index = + rd_sum_get_general_var_params_index(rd_sum.get(), "FOPT"); + + auto times = rd_sum_alloc_time_vector(rd_sum.get(), true); + REQUIRE(times.size() == 2); + REQUIRE(times[0] == start_time + 86400); + REQUIRE(times[1] == start_time + 2 * 86400); + + auto values = rd_sum_alloc_data_vector(rd_sum.get(), params_index, true); + REQUIRE(values.size() == 2); + REQUIRE_THAT(values[0], WithinAbs(10.0, 1e-6)); + REQUIRE_THAT(values[1], WithinAbs(20.0, 1e-6)); +} + +TEST_CASE_METHOD(Tmpdir, "adding a tstep with report step SIZE_MAX throws") { + const auto case_path = (dirname / "MAXCASE").string(); + auto rd_sum = + make_summary_writer(case_path, /*fmt_output=*/false, + /*unified=*/true, ":", + util_make_date_utc(1, 1, 2010), true, 10, 10, 10); + rd_sum_add_var(rd_sum.get(), "FOPT", nullptr, 0, "SM3", 0.0f); + + REQUIRE_THROWS_AS(rd_sum_add_tstep(rd_sum.get(), SIZE_MAX, 0.0), + std::invalid_argument); + REQUIRE(rd_sum_get_data_length(rd_sum.get()) == 0); +} + TEST_CASE_METHOD(Tmpdir, "rd_sum_alloc_group_list returns group names") { const auto case_path = (dirname / "GCASE").string(); @@ -690,12 +742,12 @@ TEST_CASE_METHOD(Tmpdir, "Loading a case with data before its parent in time") { REQUIRE(rd_sum != nullptr); REQUIRE(rd_sum_has_key(rd_sum.get(), "FOPT")); - const int length = rd_sum_get_data_length(rd_sum.get()); + const size_t length = rd_sum_get_data_length(rd_sum.get()); REQUIRE(length > 0); REQUIRE_THAT(rd_sum_get_general_var(rd_sum.get(), 0, "FOPT"), WithinAbs(child_fopt_value, 1e-3)); - for (int i = 1; i < length; ++i) { + for (size_t i = 1; i < length; ++i) { REQUIRE_THAT( rd_sum_get_general_var(rd_sum.get(), i, "FOPT"), WithinAbs(spec.start_seconds + (i - 1) * spec.ministep_length, @@ -747,8 +799,8 @@ TEST_CASE_METHOD(Tmpdir, "Restart writer writes has restart kw") { auto base_sum = read_summary(base_name); REQUIRE(restart_sum != nullptr); REQUIRE(base_sum != nullptr); - const int n = rd_sum_get_data_length(base_sum.get()); - for (int i = 0; i < n; ++i) { + const size_t n = rd_sum_get_data_length(base_sum.get()); + for (size_t i = 0; i < n; ++i) { REQUIRE(rd_sum_get_general_var(base_sum.get(), i, "FOPT") == rd_sum_get_general_var(restart_sum.get(), i, "FOPT")); } @@ -884,9 +936,9 @@ TEST_CASE_METHOD(Tmpdir, "Without extension the newest data files are chosen") { fs::rename(dirname / "CASE.SMSPEC.bak", dirname / "CASE.SMSPEC"); SECTION("unified newer than split picks unified") { - for (int n = 1; n <= split_spec.num_report_steps; ++n) { + for (size_t n = 1; n <= split_spec.num_report_steps; ++n) { char ext[8]; - sprintf(ext, ".S%04d", n); + sprintf(ext, ".S%04zu", n); make_older(dirname / ("CASE" + std::string(ext)), dirname / "CASE.UNSMRY"); } @@ -1269,7 +1321,7 @@ SCENARIO_METHOD(Tmpdir, "Loading Restarts") { auto check_vector = [](rd_sum_type *sum, const std::string &key, const std::vector &expected) { - const int idx = + const size_t idx = rd_sum_get_general_var_params_index(sum, key.c_str()); auto data = rd_sum_alloc_data_vector(sum, idx, false); REQUIRE(data.size() == expected.size()); @@ -1328,7 +1380,7 @@ SCENARIO_METHOD(Tmpdir, "Loading Restarts") { check_vector(sum.get(), "BPR:1", case2_bpr[0]); check_vector(sum.get(), "BPR:2", case2_bpr[1]); REQUIRE(rd_sum_get_data_length(sum.get()) == - int(case2_bpr[0].size())); + case2_bpr[0].size()); } } @@ -1340,7 +1392,7 @@ SCENARIO_METHOD(Tmpdir, "Loading Restarts") { check_vector(sum.get(), "BPR:2", case3_bpr[1]); check_vector(sum.get(), "BPR:3", case3_bpr[2]); REQUIRE(rd_sum_get_data_length(sum.get()) == - int(case3_bpr[0].size())); + case3_bpr[0].size()); } } @@ -1704,9 +1756,9 @@ TEST_CASE_METHOD(Tmpdir, "fread_alloc_case guesses base when given directory") { const rd::smspec_node *wopr = rd_sum_add_var( rd_sum.get(), "WOPR", unique_well.c_str(), 0, "SM3/DAY", 0.0f); double sim_seconds = spec.start_seconds; - for (int report_step = 0; report_step < spec.num_report_steps; + for (size_t report_step = 0; report_step < spec.num_report_steps; ++report_step) { - for (int step = 0; step < spec.num_ministep; ++step) { + for (size_t step = 0; step < spec.num_ministep; ++step) { rd_sum_tstep_type *tstep = rd_sum_add_tstep( rd_sum.get(), report_step + 1, sim_seconds); rd_sum_tstep_set_from_node(tstep, *wopr, @@ -1750,7 +1802,7 @@ TEST_CASE_METHOD(Tmpdir, "fread_alloc_case guesses base when given directory") { REQUIRE(rd_sum_get_end_time(guessed.get()) == rd_sum_get_end_time(explicit_sum.get())); - const int n = rd_sum_get_data_length(guessed.get()); + const size_t n = rd_sum_get_data_length(guessed.get()); REQUIRE(n == rd_sum_get_data_length(explicit_sum.get())); REQUIRE(n == spec.num_report_steps * spec.num_ministep); @@ -1761,7 +1813,7 @@ TEST_CASE_METHOD(Tmpdir, "fread_alloc_case guesses base when given directory") { unique_key.c_str())) == "SM3/DAY"); - for (int i = 0; i < n; ++i) { + for (size_t i = 0; i < n; ++i) { const double expected = unique_scale * i * spec.ministep_length; REQUIRE_THAT(rd_sum_get_general_var(guessed.get(), i, @@ -1813,7 +1865,7 @@ TEST_CASE_METHOD(Tmpdir, "rd_sum_export_csv writes the requested keys") { /*unified=*/true); auto rd_sum = read_summary(case_path); REQUIRE(rd_sum.get() != nullptr); - const int n = rd_sum_get_data_length(rd_sum.get()); + const size_t n = rd_sum_get_data_length(rd_sum.get()); SECTION("subset of keys with comma separator and ISO date") { std::vector vars{"FOPT", "BPR:567"}; @@ -1847,7 +1899,7 @@ TEST_CASE_METHOD(Tmpdir, "rd_sum_export_csv writes the requested keys") { REQUIRE(header[2] == "FOPT"); REQUIRE(header[3] == "BPR:567"); - for (int i = 0; i < n; ++i) { + for (size_t i = 0; i < n; ++i) { const auto cols = split(lines[i + 1], ','); REQUIRE(cols.size() == 4); REQUIRE_THAT( diff --git a/lib/tests/test_time_index.cpp b/lib/tests/test_time_index.cpp deleted file mode 100644 index 2ca23b570..000000000 --- a/lib/tests/test_time_index.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include -#include -#include - -#include -#include - -#include "detail/resdata/rd_sum_file_data.hpp" - -TEST_CASE("TimeIndex rejects negative report steps") { - rd::TimeIndex index; - - REQUIRE_THROWS_WITH( - index.add(0, 0.0, -1), - Catch::Matchers::ContainsSubstring("report step cannot be negative")); - REQUIRE(index.size() == 0); -} diff --git a/python/resdata/summary/rd_sum_keyword_vector.py b/python/resdata/summary/rd_sum_keyword_vector.py index ccb74f77d..9503bef9e 100644 --- a/python/resdata/summary/rd_sum_keyword_vector.py +++ b/python/resdata/summary/rd_sum_keyword_vector.py @@ -19,7 +19,7 @@ def __getitem__(self, index): if index < 0: index += len(self) - if index >= len(self): + if index < 0 or index >= len(self): raise IndexError("Out of range") return _rd_sum_keyword_vector._iget_key(self, index) diff --git a/tests/rd_tests/test_rd_sum.py b/tests/rd_tests/test_rd_sum.py index ee11e6562..f53ef120d 100644 --- a/tests/rd_tests/test_rd_sum.py +++ b/tests/rd_tests/test_rd_sum.py @@ -2363,15 +2363,35 @@ def test_that_solve_days_precision_is_unaffected_by_start_date(start_date): @pytest.mark.usefixtures("use_tmpdir") -def test_that_add_t_step_rejects_negative_report_step(): +def test_that_add_t_step_raises_type_error_on_negative_report_step(): start_date = datetime.datetime(2000, 1, 1) writer = Summary.writer("NEGATIVE_REPORT_STEP", start_date, 5, 5, 5) writer.add_variable("FOPT") - with pytest.raises(ValueError, match="report step cannot be negative"): + with pytest.raises(TypeError): writer.add_t_step(-1, 1.0) +@pytest.mark.usefixtures("use_tmpdir") +@pytest.mark.parametrize( + "call", + [ + lambda s: s.iget("FOPT", -1), + lambda s: s.iget_days(-1), + lambda s: s.iget_date(-1), + lambda s: s.iget_report(-1), + lambda s: s.get_report_time(-1), + ], +) +def test_that_negative_indices_raise_type_error(call): + writer = Summary.writer("NEGATIVE_INDEX", datetime.datetime(2000, 1, 1), 5, 5, 5) + writer.add_variable("FOPT") + writer.add_t_step(1, 1.0) + + with pytest.raises(TypeError): + call(writer) + + @pytest.mark.usefixtures("use_tmpdir") def test_summary_fwrite(): start_date = datetime.datetime(2000, 1, 1) diff --git a/tests/rd_tests/test_sum.py b/tests/rd_tests/test_sum.py index cc0d971ae..cc3258414 100644 --- a/tests/rd_tests/test_sum.py +++ b/tests/rd_tests/test_sum.py @@ -5,8 +5,11 @@ import os.path import shutil import stat +import tempfile +import numpy as np import pytest +import resfo from resfo_utilities.testing import ( Date, Simulator, @@ -33,7 +36,7 @@ def assert_frame_equal(a, b): from contextlib import contextmanager -from resdata import ResDataType, UnitSystem +from resdata import FileMode, ResDataType, UnitSystem from resdata.resfile import FortIO, ResdataFile, ResdataKW, openFortIO from resdata.summary import Summary, SummaryKeyWordVector, SummaryVarType from resdata.util.util import CTime @@ -1102,3 +1105,1057 @@ def _create(): vector.add_keywords("FOPR") assert len(vector) == 1 assert vector[0] == "FOPR" + + +NUM_REPORTS = 4 +NUM_MINISTEPS = 3 + + +def _write_case( + case, + *, + unified=True, + fmt_output=False, + num_reports=NUM_REPORTS, + num_ministeps=NUM_MINISTEPS, + start=datetime.datetime(2010, 1, 1), +): + """Writes FOPT=10*days, FOPR=100-days and WOPR:OP1=days on a case with + `num_reports` report steps, each of `num_ministeps` daily ministeps.""" + writer = Summary.writer( + case, start, 5, 5, 5, fmt_output=fmt_output, unified=unified + ) + writer.add_variable("FOPT", unit="SM3") + writer.add_variable("FOPR", unit="SM3/DAY") + writer.add_variable("WOPR", wgname="OP1", unit="SM3/DAY") + days = 0 + for report in range(1, num_reports + 1): + for _ in range(num_ministeps): + tstep = writer.add_t_step(report, days) + tstep["FOPT"] = 10.0 * days + tstep["FOPR"] = 100.0 - days + tstep["WOPR:OP1"] = float(days) + days += 1 + writer.fwrite() + return writer + + +ALL_FILE_LAYOUTS = pytest.mark.parametrize( + "unified,fmt_output,lazy_load", + [ + (unified, fmt_output, lazy_load) + for unified in (True, False) + for fmt_output in (True, False) + for lazy_load in (True, False) + ], +) + + +@pytest.mark.usefixtures("use_tmpdir") +@ALL_FILE_LAYOUTS +@pytest.mark.parametrize( + "open_mode", (FileMode.DEFAULT, FileMode.CLOSE_STREAM, FileMode.WRITABLE) +) +def test_that_summary_roundtrips(unified, fmt_output, lazy_load, open_mode): + _write_case("LAYOUT", unified=unified, fmt_output=fmt_output) + + summary = Summary("LAYOUT", lazy_load=lazy_load, file_options=open_mode) + + total = NUM_REPORTS * NUM_MINISTEPS + assert len(summary) == total + assert summary.days == [float(d) for d in range(total)] + assert list(summary["FOPT"].values) == pytest.approx( + [10.0 * d for d in range(total)] + ) + assert list(summary["WOPR:OP1"].values) == pytest.approx( + [float(d) for d in range(total)] + ) + assert summary.report_step == [ + r for r in range(1, NUM_REPORTS + 1) for _ in range(NUM_MINISTEPS) + ] + assert summary.first_report == 1 + assert summary.last_report == NUM_REPORTS + assert summary.start_date == datetime.date(2010, 1, 1) + assert summary.end_date == datetime.date(2010, 1, 1) + datetime.timedelta( + days=total - 1 + ) + assert summary.case == "LAYOUT" + assert summary.base == "LAYOUT" + assert summary.path is None + assert summary.abs_path == os.getcwd() + assert summary.unit("FOPR") == "SM3/DAY" + assert summary.restart_case is None + assert summary.restart_step == -1 + assert summary.length == NUM_REPORTS * NUM_MINISTEPS + assert summary.first_value("FOPT") == 0.0 + assert summary.last_value("FOPT") == 110.0 + assert summary.get_last("FOPT").value == 110.0 + assert summary.get_key_index("FOPT") >= 0 + assert summary.iiget(0, summary.get_key_index("FOPT")) == 0.0 + assert summary.get_report_time(1) == datetime.date(2010, 1, 3) + assert summary.get_from_report("FOPT", 1) == 20.0 + assert summary.report_index_list() == [2, 5, 8, 11] + assert summary.get_report_step(report_only=True) == [1, 2, 3, 4] + assert summary.get_days(report_only=True) == [2.0, 5.0, 8.0, 11.0] + assert summary.get_dates(report_only=True) == [ + datetime.date(2010, 1, 3), + datetime.date(2010, 1, 6), + datetime.date(2010, 1, 9), + datetime.date(2010, 1, 12), + ] + + assert summary.get_report(days=0.5 + 2) == -1 + assert summary.get_report(days=NUM_MINISTEPS - 1) == 1 + assert summary.get_report(days=2 * NUM_MINISTEPS - 1) == 2 + assert summary.get_report(days=NUM_REPORTS * NUM_MINISTEPS - 1) == NUM_REPORTS + assert summary.get_report(date=datetime.date(2010, 1, 3)) == 1 + assert summary.get_report(date=datetime.date(2010, 1, 4)) == -1 + with pytest.raises(ValueError, match="Must supply either"): + summary.get_report(date=datetime.date(2010, 1, 3), days=2) + + expected_days = [NUM_MINISTEPS * r - 1.0 for r in range(1, NUM_REPORTS + 1)] + expected_dates = [ + datetime.date(2010, 1, 1) + datetime.timedelta(days=d) for d in expected_days + ] + assert summary.get_report_step(report_only=True) == [1, 2, 3, 4] + assert summary.get_days(report_only=True) == expected_days + assert summary.report_dates == expected_dates + assert summary.get_dates(report_only=True) == expected_dates + assert summary.report_index_list() == [ + NUM_MINISTEPS * r - 1 for r in range(1, NUM_REPORTS + 1) + ] + assert list(summary.numpy_vector("FOPT", report_only=True)) == pytest.approx( + [10.0 * d for d in expected_days] + ) + assert len(summary.alloc_time_vector(True)) == NUM_REPORTS + assert len(summary.alloc_time_vector(False)) == NUM_REPORTS * NUM_MINISTEPS + for report, day in enumerate(expected_days, start=1): + assert summary.get_report_time(report) == datetime.date( + 2010, 1, 1 + ) + datetime.timedelta(days=day) + assert summary.get_from_report("FOPT", report) == pytest.approx(10.0 * day) + + for index in (0, 5, NUM_REPORTS * NUM_MINISTEPS - 1): + assert summary.iget("FOPT", index) == pytest.approx(10.0 * index) + assert summary.iget_days(index) == index + assert summary.iget_date(index) == datetime.datetime( + 2010, 1, 1 + ) + datetime.timedelta(days=index) + assert summary.iget_report(index) == index // NUM_MINISTEPS + 1 + key_index = summary.get_key_index("FOPR") + assert summary.iiget(index, key_index) == pytest.approx(100.0 - index) + + +@pytest.mark.usefixtures("use_tmpdir") +def test_that_first_gt_and_first_lt_index_are_minus_one_when_the_limit_is_never_crossed(): + _write_case("FIRST") + summary = Summary("FIRST") + last = NUM_REPORTS * NUM_MINISTEPS - 1 + + assert summary.first_gt_index("FOPT", 25.0) == 3 + assert summary.first_gt_index("FOPT", 10.0 * last) == -1 + assert summary.first_lt_index("FOPR", 100.0 - last + 0.5) == last + assert summary.first_lt_index("FOPR", 0.0) == -1 + assert summary.first_gt("FOPT", 25.0).days == 3 + assert summary.first_lt("FOPR", 90.5).days == 10 + + +@pytest.mark.usefixtures("use_tmpdir") +@ALL_FILE_LAYOUTS +def test_that_interpolation_outside_the_simulated_range_raises_value_error( + unified, fmt_output, lazy_load +): + _write_case("INTERP", unified=unified, fmt_output=fmt_output) + summary = Summary("INTERP", lazy_load=lazy_load) + last = NUM_REPORTS * NUM_MINISTEPS - 1 + + assert summary.get_interp("FOPT", days=2.5) == pytest.approx(25.0) + assert summary.get_interp("FOPT", date=datetime.datetime(2010, 1, 2, 12)) == ( + pytest.approx(15.0) + ) + assert list(summary.get_interp_vector("FOPT", days_list=[0.5, 1.5])) == ( + pytest.approx([5.0, 15.0]) + ) + assert list( + summary.get_interp_vector("FOPT", date_list=[datetime.datetime(2010, 1, 2, 12)]) + ) == pytest.approx([15.0]) + with pytest.raises(ValueError, match="outside range"): + summary.get_interp("FOPT", days=last + 1) + with pytest.raises(ValueError, match="outside range"): + summary.get_interp("FOPT", date=datetime.datetime(2011, 1, 1)) + with pytest.raises(ValueError, match="Invalid days"): + summary.get_interp_vector("FOPT", days_list=[-1.0]) + with pytest.raises(ValueError, match="Invalid days"): + summary.get_interp_vector("FOPT", days_list=[last + 1.0]) + with pytest.raises(ValueError, match="Invalid date"): + summary.get_interp_vector("FOPT", date_list=[datetime.datetime(2011, 1, 1)]) + with pytest.raises(ValueError, match="Invalid date"): + summary.get_interp_vector("FOPT", date_list=[datetime.datetime(2009, 1, 1)]) + with pytest.raises(ValueError): + summary.get_interp_vector("FOPT") + + +@pytest.mark.usefixtures("use_tmpdir") +def test_that_the_unified_file_is_ignored_when_the_extension_asks_for_multiple_files(): + _write_case("MIXED", unified=True) + _write_case("MIXED", unified=False, num_reports=2) + + assert len(Summary("MIXED.S0001")) == 2 * NUM_MINISTEPS + + +@pytest.mark.usefixtures("use_tmpdir") +def test_that_the_newest_of_unified_and_multiple_files_is_loaded(): + _write_case("NEWEST", unified=False, num_reports=2) + os.utime("NEWEST.S0001", (1, 1)) + os.utime("NEWEST.S0002", (1, 1)) + _write_case("NEWEST", unified=True, num_reports=3) + + assert len(Summary("NEWEST")) == 3 * NUM_MINISTEPS + + +@pytest.mark.usefixtures("use_tmpdir") +def test_that_multiple_files_are_loaded_when_they_are_newer_than_the_unified_file(): + _write_case("OLDUNIF", unified=True, num_reports=3) + os.utime("OLDUNIF.UNSMRY", (1, 1)) + _write_case("OLDUNIF", unified=False, num_reports=2) + + assert len(Summary("OLDUNIF")) == 2 * NUM_MINISTEPS + + +@pytest.mark.usefixtures("use_tmpdir") +def test_that_loading_a_case_without_data_files_raises_os_error(): + _write_case("NODATA") + os.remove("NODATA.UNSMRY") + + with pytest.raises(OSError, match="Failed to create summary"): + Summary("NODATA") + + +@pytest.mark.usefixtures("use_tmpdir") +def test_that_loading_with_an_unformatted_extension_ignores_a_formatted_only_case(): + _write_case("ONLYFMT", fmt_output=True) + + with pytest.raises(OSError): + Summary("ONLYFMT.UNSMRY") + + +@pytest.mark.usefixtures("use_tmpdir") +def test_that_loading_a_case_without_header_raises_os_error(): + with pytest.raises(OSError): + Summary("DOES_NOT_EXIST") + + +@pytest.mark.usefixtures("use_tmpdir") +def test_that_the_case_is_guessed_when_the_directory_has_a_single_data_file(): + os.makedirs("run") + with pushd("run"): + _write_case("GUESSED") + open("GUESSED.DATA", "w").close() + + assert len(Summary("run/")) == NUM_REPORTS * NUM_MINISTEPS + + +@pytest.mark.usefixtures("use_tmpdir") +def test_that_the_case_cannot_be_guessed_when_the_directory_has_multiple_data_files(): + os.makedirs("run") + with pushd("run"): + _write_case("ONE") + open("ONE.DATA", "w").close() + open("TWO.DATA", "w").close() + + with pytest.raises(OSError): + Summary("run/") + + +@pytest.mark.usefixtures("use_tmpdir") +def test_that_the_case_cannot_be_guessed_when_the_directory_has_no_data_file(): + os.makedirs("run") + with pushd("run"): + _write_case("ONE") + + with pytest.raises(OSError): + Summary("run/") + + +@pytest.mark.usefixtures("use_tmpdir") +@pytest.mark.parametrize("lazy_load", [True, False]) +def test_that_summary_load_reads_explicitly_named_header_and_data_files(lazy_load): + _write_case("EXPLICIT", unified=False) + + summary = Summary.load("EXPLICIT.SMSPEC", "EXPLICIT.S0001") + + assert len(summary) == NUM_MINISTEPS + + +@pytest.mark.usefixtures("use_tmpdir") +def test_that_summary_load_reads_a_unified_file(): + _write_case("EXPLICIT_UNIF") + + summary = Summary.load("EXPLICIT_UNIF.SMSPEC", "EXPLICIT_UNIF.UNSMRY") + + assert len(summary) == NUM_REPORTS * NUM_MINISTEPS + + +@pytest.mark.usefixtures("use_tmpdir") +@pytest.mark.parametrize("lazy_load", [True, False]) +def test_that_a_day_month_year_summary_can_be_interpolated_and_sliced(lazy_load): + case = "DMY_INTERP" + dates = [ + datetime.datetime(2010, 1, 1), + datetime.datetime(2010, 1, 11), + datetime.datetime(2010, 1, 21), + ] + _write_day_month_year_case(case, dates, [0.0, 10.0, 20.0]) + summary = Summary(case, lazy_load=lazy_load) + + assert summary.get_interp("FOPT", date=datetime.datetime(2010, 1, 6)) == ( + pytest.approx(5.0) + ) + assert summary.get_interp("FOPT", days=15) == pytest.approx(15.0) + row = summary.get_interp_row( + SummaryKeyWordVector(summary, add_keywords=True), datetime.datetime(2010, 1, 16) + ) + assert list(row) == pytest.approx([16.0, 1.0, 2010.0, 15.0]) + assert summary.get_data_start_time() == datetime.datetime(2010, 1, 1) + assert summary.get_end_time() == datetime.datetime(2010, 1, 21) + + +@pytest.mark.usefixtures("use_tmpdir") +def test_that_a_unsmry_without_params_for_the_first_step_is_reported_as_error(): + case = "NO_PARAMS" + dates = [datetime.datetime(2010, 1, 1)] + _write_day_month_year_case(case, dates, [1.0]) + Unsmry(steps=[SummaryStep(seqnum=0, ministeps=[])]).to_file(f"{case}.UNSMRY") + + with pytest.raises(OSError, match="Failed to create summary"): + Summary(case, lazy_load=False) + + +@pytest.mark.usefixtures("use_tmpdir") +@pytest.mark.parametrize("lazy_load", [True, False]) +def test_that_keys_and_wells_are_listed_with_optional_patterns(lazy_load): + _write_case("KEYS") + summary = Summary("KEYS", lazy_load=lazy_load) + + assert sorted(summary.keys()) == ["FOPR", "FOPT", "WOPR:OP1"] + assert summary.keys("FOP*") == ["FOPR", "FOPT"] + assert summary.keys("NOMATCH*") == [] + assert summary.wells() == ["OP1"] + assert summary.wells("OP*") == ["OP1"] + assert summary.wells("XX*") == [] + assert summary.groups() == [] + assert summary.has_key("WOPR:OP1") + assert not summary.has_key("WOPR:OP2") + assert summary.unit_system == UnitSystem.METRIC + + +@pytest.mark.usefixtures("use_tmpdir") +@pytest.mark.parametrize("lazy_load", [True, False]) +def test_that_blocked_production_is_the_difference_of_a_total_over_time_ranges( + lazy_load, +): + _write_case("BLOCKED", num_reports=2) + summary = Summary("BLOCKED", lazy_load=lazy_load) + points = [datetime.datetime(2010, 1, d) for d in (1, 3, 5)] + + assert list(summary.blocked_production("FOPT", points)) == pytest.approx( + [20.0, 20.0] + ) + with pytest.raises(TypeError): + summary.blocked_production("FOPR", points) + + +@pytest.mark.usefixtures("use_tmpdir") +@pytest.mark.parametrize("lazy_load", [True, False]) +def test_that_time_range_samples_the_simulated_period(lazy_load): + _write_case("TRANGE", num_reports=4, num_ministeps=5) + summary = Summary("TRANGE", lazy_load=lazy_load) + + by_count = summary.time_range(num_timestep=5) + assert [t.datetime() for t in by_count] == [ + datetime.datetime(2010, 1, 1, 0), + datetime.datetime(2010, 1, 5, 18), + datetime.datetime(2010, 1, 10, 12), + datetime.datetime(2010, 1, 15, 6), + datetime.datetime(2010, 1, 20, 0), + ] + daily = summary.time_range(interval="5d") + assert [t.datetime() for t in daily] == [ + datetime.datetime(2010, 1, d) for d in (1, 6, 11, 16, 21) + ] + monthly = summary.time_range(interval="1m") + assert [t.datetime() for t in monthly] == [ + datetime.datetime(2010, 1, 1), + datetime.datetime(2010, 2, 1), + ] + yearly = summary.time_range(interval="1y", extend_end=False) + assert [t.datetime() for t in yearly] == [datetime.datetime(2010, 1, 1)] + + +@pytest.mark.usefixtures("use_tmpdir") +@pytest.mark.parametrize("lazy_load", [True, False]) +def test_that_solve_days_finds_when_a_value_is_crossed(lazy_load): + _write_case("SOLVE", num_reports=2) + summary = Summary("SOLVE", lazy_load=lazy_load) + + assert summary.solve_days("FOPT", 25.0) == pytest.approx([2.5]) + assert summary.solve_days("FOPR", 97.5, rates_clamp_lower=False) == ( + pytest.approx([3.0]) + ) + + +@pytest.mark.usefixtures("use_tmpdir") +@pytest.mark.parametrize("lazy_load", [True, False]) +def test_that_resampling_gives_values_at_the_requested_times(lazy_load): + _write_case("SOURCE", num_reports=2) + source = Summary("SOURCE", lazy_load=lazy_load) + points = [datetime.datetime(2010, 1, 1), datetime.datetime(2010, 1, 3)] + + resampled = source.resample("RESAMPLED", points) + + assert list(resampled["FOPT"].values) == pytest.approx([0.0, 20.0]) + assert resampled.dates == points + with pytest.raises(ValueError): + source.resample("EMPTY", []) + with pytest.raises(ValueError): + source.resample("OUTSIDE", [datetime.datetime(2020, 1, 1)]) + + +@pytest.mark.usefixtures("use_tmpdir") +def test_that_an_oversized_index_raises_value_error(): + _write_case("INDEX") + summary = Summary("INDEX") + + with pytest.raises(TypeError): + summary.iget("FOPT", -1) + with pytest.raises(TypeError): + summary.get_report_time(-1) + with pytest.raises(TypeError): + summary.get_from_report("FOPT", -1) + with pytest.raises(ValueError, match="looking up index"): + summary.iget("FOPT", 10_000) + + +@pytest.mark.usefixtures("use_tmpdir") +def test_that_a_keyword_vector_can_be_built_from_a_summary_and_copied(): + _write_case("VEC") + summary = Summary("VEC") + vector = SummaryKeyWordVector(summary, add_keywords=True) + + assert list(vector) == ["FOPT", "FOPR", "WOPR:OP1"] + copied = vector.copy(summary) + assert list(copied) == list(vector) + with pytest.raises(KeyError): + vector.add_keyword("NOT_A_KEY") + assert list(summary.get_interp_row(vector, datetime.datetime(2010, 1, 2, 12))) == ( + pytest.approx([15.0, 98.0, 2.0]) + ) + with pytest.raises(IndexError): + vector[len(vector)] + with pytest.raises(IndexError): + vector[-len(vector) - 1] + + +@pytest.mark.usefixtures("use_tmpdir") +def test_that_frames_from_a_copied_vector_are_nan_for_keys_missing_in_the_case(): + import resdata.summary._rd_sum as _rd_sum + + _write_case("VEC") + writer = Summary.writer("SMALL", datetime.datetime(2010, 1, 1), 5, 5, 5) + writer.add_variable("FOPT", unit="SM3") + for report in range(1, 3): + writer.add_t_step(report, report)["FOPT"] = 10.0 * report + writer.fwrite() + small = Summary("SMALL") + + layout = SummaryKeyWordVector(Summary("VEC"), add_keywords=True).copy(small) + assert list(layout) == ["FOPT", "FOPR", "WOPR:OP1"] + + data = np.zeros([len(small.dates), len(layout)]) + _rd_sum._init_pandas_frame(small, layout, data) + assert not np.isnan(data[:, 0]).any() + assert np.isnan(data[:, 1:]).all() + + # Includes times before and after the simulation to cover all branches + times = [datetime.datetime(2009, 1, 1), datetime.datetime(2010, 1, 2, 12)] + times.append(datetime.datetime(2011, 1, 1)) + time_points = [CTime(t).value() for t in times] + data = np.zeros([len(time_points), len(layout)]) + _rd_sum._init_pandas_frame_interp(small, layout, time_points, data) + assert not np.isnan(data[:, 0]).any() + assert np.isnan(data[:, 1:]).all() + + +@pytest.mark.usefixtures("use_tmpdir") +@pytest.mark.parametrize("lazy_load", [True, False]) +def test_that_solve_dates_and_first_crossing_find_the_same_point(lazy_load): + _write_case("CROSS", num_reports=2) + summary = Summary("CROSS", lazy_load=lazy_load) + + dates = summary.solve_dates("FOPT", 25.0) + assert len(dates) == 1 + assert dates[0] == datetime.datetime(2010, 1, 3, 12) + assert summary.first_gt_index("FOPT", 25.0) == 3 + assert summary.first_lt_index("FOPR", 97.5) == 3 + assert summary.first_gt("FOPT", 25.0).value == 30.0 + assert summary.first_lt("FOPR", 97.5).value == 97.0 + + +@pytest.mark.usefixtures("use_tmpdir") +def test_that_a_summary_can_be_written_under_a_different_case_name(): + _write_case("ORIGINAL", num_reports=2) + summary = Summary("ORIGINAL", lazy_load=False) + + summary.fwrite("COPY") + + copy = Summary("COPY") + assert list(copy["FOPT"].values) == list(summary["FOPT"].values) + assert copy.dates == summary.dates + + +@pytest.mark.usefixtures("use_tmpdir") +@pytest.mark.parametrize("lazy_load", [True, False]) +def test_that_interpolation_and_report_lookup_at_the_boundaries_are_exact(lazy_load): + _write_case("EDGE", num_reports=2) + summary = Summary("EDGE", lazy_load=lazy_load) + + assert summary.get_interp("FOPT", days=0) == 0.0 + assert summary.get_interp("FOPT", days=5) == 50.0 + assert summary.get_interp("FOPT", date=datetime.datetime(2010, 1, 6)) == 50.0 + assert summary.get_report(days=5) == 2 + + +@pytest.mark.usefixtures("use_tmpdir") +@pytest.mark.parametrize("lazy_load", [True, False]) +@pytest.mark.parametrize( + "call, error", + [ + (lambda s: s.iget("NOPE", 0), IndexError), + (lambda s: s.iget("FOPT", 10**6), ValueError), + (lambda s: s.iiget(10**6, 0), ValueError), + (lambda s: s.iiget(0, 10**6), IndexError), + (lambda s: s.get_report_time(100), ValueError), + (lambda s: s.get_from_report("FOPT", 100), ValueError), + (lambda s: s.iget_days(10**6), ValueError), + (lambda s: s.iget_date(10**6), ValueError), + (lambda s: s.iget_report(10**6), ValueError), + (lambda s: s.alloc_data_vector(10**6, False), IndexError), + (lambda s: s.first_gt_index("NOPE", 1), IndexError), + (lambda s: s.get_interp("NOPE", days=1), KeyError), + (lambda s: s.unit("NOPE"), KeyError), + (lambda s: s.numpy_vector("NOPE"), KeyError), + (lambda s: s.solve_days("NOPE", 1), KeyError), + (lambda s: s.last_value("NOPE"), KeyError), + (lambda s: s.smspec_node("NOPE"), KeyError), + (lambda s: SummaryKeyWordVector(s).add_keyword("NOPE"), KeyError), + ( + lambda s: s.get_interp("FOPT", date=datetime.datetime(2000, 1, 1)), + ValueError, + ), + ( + lambda s: s.get_interp_row(["FOPT"], datetime.datetime(2010, 1, 2)), + TypeError, + ), + ], +) +def test_that_invalid_keys_indices_and_times_raise_the_appropriate_exception( + lazy_load, call, error +): + _write_case("ERRORS") + summary = Summary("ERRORS", lazy_load=lazy_load) + + with pytest.raises(error): + call(summary) + + +def _corrupt_unsmry(case, corruption): + _write_day_month_year_case( + case, [datetime.datetime(2010, 1, 1), datetime.datetime(2010, 2, 1)], [1.0, 2.0] + ) + path = f"{case}.UNSMRY" + if corruption == "truncated": + with open(path, "rb") as f: + data = f.read() + with open(path, "wb") as f: + f.write(data[:-30]) + elif corruption == "garbage": + with open(path, "wb") as f: + f.write(b"garbage" * 50) + elif corruption == "no_steps": + Unsmry(steps=[]).to_file(path) + elif corruption == "params_too_short": + Unsmry( + steps=[ + SummaryStep( + seqnum=0, + ministeps=[SummaryMiniStep(mini_step=0, params=[1.0, 1.0, 2010.0])], + ) + ] + ).to_file(path) + + +@pytest.mark.usefixtures("use_tmpdir") +@pytest.mark.parametrize("lazy_load", [True, False]) +@pytest.mark.parametrize("corruption", ["truncated", "garbage", "no_steps"]) +def test_that_an_unreadable_data_file_makes_loading_fail_with_os_error( + lazy_load, corruption +): + _corrupt_unsmry("CORRUPT", corruption) + + with pytest.raises(OSError, match="Failed to create summary"): + Summary("CORRUPT", lazy_load=lazy_load) + + +@pytest.mark.usefixtures("use_tmpdir") +def test_that_a_step_with_fewer_params_than_the_header_declares_is_discarded(): + _corrupt_unsmry("SHORT", "params_too_short") + + with pytest.raises(OSError, match="Failed to create summary"): + Summary("SHORT", lazy_load=False) + + +def _smspec_for(keywords, units, wells, nums, **kwargs): + return Smspec( + nx=2, + ny=2, + nz=2, + restarted_from_step=kwargs.pop("restarted_from_step", 0), + num_keywords=len(keywords), + restart=kwargs.pop("restart", " "), + keywords=keywords, + well_names=wells, + region_numbers=nums, + units=units, + start_date=Date(day=1, month=1, year=2010, hour=0, minutes=0, micro_seconds=0), + intehead=SmspecIntehead( + unit=ResfoUnitSystem.METRIC, simulator=Simulator.ECLIPSE_100 + ), + **kwargs, + ) + + +def _write_header_case(case, smspec, drop=(), time_values=(0.0, 1.0)): + """Writes `smspec` (without the keywords named in `drop`) and an UNSMRY + with one ministep per entry in `time_values`; TIME is the first keyword + and all other parameters are zero.""" + items = [kw for kw in smspec.to_smry() if kw[0].strip() not in drop] + resfo.write(f"{case}.SMSPEC", items, resfo.Format.UNFORMATTED) + Unsmry( + steps=[ + SummaryStep( + seqnum=i, + ministeps=[ + SummaryMiniStep( + mini_step=i, + params=[t] + [0.0] * (smspec.num_keywords - 1), + ) + ], + ) + for i, t in enumerate(time_values) + ] + ).to_file(f"{case}.UNSMRY") + + +def _time_and_fopt(time_unit="DAYS", **kwargs): + return _smspec_for( + ["TIME ", "FOPT "], + [time_unit.ljust(8), "SM3 "], + [":+:+:+:+", " "], + [0, 0], + **kwargs, + ) + + +@pytest.mark.usefixtures("use_tmpdir") +@pytest.mark.parametrize("lazy_load", [True, False]) +@pytest.mark.parametrize( + "missing", ["WGNAMES", "KEYWORDS", "STARTDAT", "UNITS", "DIMENS"] +) +def test_that_a_header_lacking_a_required_keyword_is_rejected(lazy_load, missing): + _write_header_case("HEADER", _time_and_fopt(), drop=(missing,)) + + with pytest.raises(OSError, match="Failed to create summary"): + Summary("HEADER", lazy_load=lazy_load) + + +@pytest.mark.usefixtures("use_tmpdir") +@pytest.mark.parametrize("lazy_load", [True, False]) +@pytest.mark.parametrize("optional", ["INTEHEAD", "RESTART", "NUMS"]) +def test_that_a_header_lacking_an_optional_keyword_is_still_loaded(lazy_load, optional): + _write_header_case("HEADER", _time_and_fopt(), drop=(optional,)) + + assert Summary("HEADER", lazy_load=lazy_load).keys() == ["FOPT"] + + +@pytest.mark.usefixtures("use_tmpdir") +@pytest.mark.parametrize("lazy_load", [True, False]) +def test_that_the_names_keyword_is_accepted_in_place_of_wgnames(lazy_load): + _write_header_case("NAMES", _time_and_fopt(use_names=True)) + + assert Summary("NAMES", lazy_load=lazy_load).keys() == ["FOPT"] + + +@pytest.mark.usefixtures("use_tmpdir") +@pytest.mark.parametrize("lazy_load", [True, False]) +@pytest.mark.parametrize( + "time_unit, seconds_per_unit", [("DAYS", 86400.0), ("HOURS", 3600.0)] +) +def test_that_the_time_unit_of_the_header_scales_the_time_values( + lazy_load, time_unit, seconds_per_unit +): + _write_header_case("TIMEUNIT", _time_and_fopt(time_unit), time_values=(0.0, 2.0)) + + summary = Summary("TIMEUNIT", lazy_load=lazy_load) + + expected_end = datetime.datetime(2010, 1, 1) + datetime.timedelta( + seconds=2.0 * seconds_per_unit + ) + assert summary.dates[-1] == expected_end + + +@pytest.mark.usefixtures("use_tmpdir") +@pytest.mark.parametrize("lazy_load", [True, False]) +@pytest.mark.parametrize("time_unit", ["YEARS", ""]) +def test_that_an_unrecognized_time_unit_is_rejected(lazy_load, time_unit): + _write_header_case("BADUNIT", _time_and_fopt(time_unit)) + + with pytest.raises(ValueError, match="not recognized"): + Summary("BADUNIT", lazy_load=lazy_load) + + +@pytest.mark.usefixtures("use_tmpdir") +@pytest.mark.parametrize("lazy_load", [True, False]) +def test_that_lgr_variables_are_named_by_lgr_and_cell_index(lazy_load): + smspec = _smspec_for( + ["TIME ", "LBPR "], + ["DAYS ", "BARSA "], + [":+:+:+:+", "LGR1 "], + [0, 1], + lgrs=[":+:+:+:+", "LGR1 "], + numlx=[0, 1], + numly=[0, 2], + numlz=[0, 3], + ) + _write_header_case("LGR", smspec) + + assert Summary("LGR", lazy_load=lazy_load).keys() == ["LBPR:LGR1:1,2,3"] + + +@pytest.mark.usefixtures("use_tmpdir") +@pytest.mark.parametrize("lazy_load", [True, False]) +def test_that_lgrs_without_all_index_keywords_is_rejected(lazy_load): + smspec = _time_and_fopt(lgrs=[":+:+:+:+", "LGR1 "]) + _write_header_case("LGR", smspec) + + with pytest.raises(ValueError, match="NUMLX, NUMLY, NUMLZ"): + Summary("LGR", lazy_load=lazy_load) + + +@pytest.mark.usefixtures("use_tmpdir") +@pytest.mark.parametrize("lazy_load", [True, False]) +def test_that_an_lgr_variable_without_lgr_metadata_is_rejected(lazy_load): + smspec = _smspec_for( + ["TIME ", "LBPR "], + ["DAYS ", "BARSA "], + [":+:+:+:+", "LGR1 "], + [0, 1], + ) + _write_header_case("LGR", smspec) + + with pytest.raises(ValueError, match="LGR variable 'LBPR'"): + Summary("LGR", lazy_load=lazy_load) + + +@pytest.mark.usefixtures("use_tmpdir") +def test_that_a_case_restarting_from_itself_ignores_the_restart(): + _write_header_case("SELF", _time_and_fopt(restart="SELF")) + + assert Summary("SELF").restart_case is None + + +@pytest.mark.usefixtures("use_tmpdir") +def test_that_a_restart_case_is_found_when_a_file_with_the_case_name_exists(): + _write_case("HIST") + with open("HIST", "w") as f: + f.write("not a summary") + _write_header_case("RESTARTED", _time_and_fopt(restart="HIST")) + + restarted = Summary("RESTARTED") + + assert isinstance(restarted.restart_case, Summary) + assert restarted.restart_case.case == os.path.join(os.getcwd(), "HIST") + + +@pytest.mark.usefixtures("use_tmpdir") +def test_that_a_restart_case_without_header_file_is_ignored(): + _write_header_case("RESTARTED", _time_and_fopt(restart="MISSING")) + + assert Summary("RESTARTED").restart_case is None + + +@pytest.mark.usefixtures("use_tmpdir") +def test_that_report_lookups_ignore_a_case_that_is_superseded_by_its_history(): + _write_header_case("HIST", _time_and_fopt(), time_values=(0.0, 1.0, 2.0)) + _write_header_case( + "MAIN", _time_and_fopt(restart="HIST"), time_values=(0.0, 5.0, 10.0) + ) + + summary = Summary("MAIN") + + assert summary.days == [0.0, 1.0, 2.0] + assert summary.get_report_time(2) == datetime.date(2010, 1, 2) + assert summary.get_report(days=1.0) == 2 + + +@pytest.mark.usefixtures("use_tmpdir") +def test_that_a_writer_with_time_in_hours_round_trips_the_dates(): + writer = Summary.writer( + "HOURS", datetime.datetime(2010, 1, 1), 5, 5, 5, time_in_days=False + ) + writer.add_variable("FOPT", unit="SM3") + for days in range(3): + writer.add_t_step(1, days)["FOPT"] = float(days) + writer.fwrite() + + summary = Summary("HOURS") + + assert summary.dates == [datetime.datetime(2010, 1, d) for d in (1, 2, 3)] + + +def _keys_of_single_variable(case, keyword, well, num, unit, lazy_load=False): + smspec = _smspec_for( + ["TIME ", keyword.ljust(8)], + ["DAYS ", unit.ljust(8)], + [":+:+:+:+", well.ljust(8)], + [0, num], + ) + _write_header_case(case, smspec) + return Summary(case, lazy_load=lazy_load).keys() + + +@pytest.mark.usefixtures("use_tmpdir") +@pytest.mark.parametrize("lazy_load", [True, False]) +@pytest.mark.parametrize( + "keyword, well, num, unit, expected", + [ + ("NPR", "NODE1", 0, "BARSA", ["NPR:NODE1"]), + ("FOPR", "", 0, "SM3/DAY", ["FOPR"]), + ("FOO", "", 0, "SM3", ["FOO"]), + ("LLINEAR", "", 0, "1", ["LLINEAR"]), + ("GOPR", "G1", 0, "SM3/DAY", ["GOPR:G1"]), + ("WBHP", "W1", 0, "BARSA", ["WBHP:W1"]), + ("RPR", "", 3, "BARSA", ["RPR:3"]), + ("RORFR", "", 3, "SM3", ["RORFR:3"]), + ("AAQR", "", 1, "SM3", ["AAQR:1"]), + ("SOFT", "W1", 2, "SM3", ["SOFT:W1:2"]), + ("BPR", "", 3, "BARSA", ["BPR:1,2,1", "BPR:3"]), + ("CWIR", "W1", 3, "SM3", ["CWIR:W1:1,2,1", "CWIR:W1:3"]), + ("RGFR", "", 5, "SM3/DAY", ["RGFR:5", "RGFR:5--10"]), + ("RGFT", "", 5, "SM3", ["RGFT:5", "RGFT:5--10"]), + ("RNLF", "", 5, "SM3", ["RNLF:5", "RNLF:5--10"]), + ("ROFT", "", 3 + 32768 * 11, "SM3", ["ROFT:3-1", "ROFT:360451"]), + ], +) +def test_that_variables_are_keyed_by_their_type_specific_identifiers( + lazy_load, keyword, well, num, unit, expected +): + keys = _keys_of_single_variable("TYPES", keyword, well, num, unit, lazy_load) + + assert sorted(keys) == sorted(expected) + + +@pytest.mark.usefixtures("use_tmpdir") +@pytest.mark.parametrize( + "keyword, well, num", + [ + ("WOPR", "", 0), + ("WOPR", ":+:+:+:+", 0), + ("CWIR", "W1", -1), + ("BPR", "", -1), + ], +) +def test_that_variables_lacking_a_required_name_or_number_are_skipped( + keyword, well, num +): + assert _keys_of_single_variable("SKIP", keyword, well, num, "SM3") == [] + + +@pytest.mark.usefixtures("use_tmpdir") +@pytest.mark.parametrize("keyword, well", [("BPR", ""), ("CWIR", "W1")]) +@pytest.mark.parametrize("num", [0, 100]) +def test_that_a_cell_number_outside_the_grid_raises_value_error(keyword, well, num): + with pytest.raises(ValueError, match="Invalid NUMS value"): + _keys_of_single_variable("BADNUM", keyword, well, num, "SM3") + + +@pytest.mark.usefixtures("use_tmpdir") +def test_that_region_to_region_variables_are_total_or_rate_by_name(): + smspec = _smspec_for( + ["TIME ", "RGFR ", "RGFT ", "ROFT "], + ["DAYS ", "SM3/DAY ", "SM3 ", "SM3 "], + [":+:+:+:+"] * 4, + [0, 5, 5, 3 + 32768 * 11], + ) + _write_header_case("R2R", smspec) + + summary = Summary("R2R") + + assert summary.smspec_node("RGFT:5").is_total() + assert not summary.smspec_node("RGFR:5").is_total() + assert summary.smspec_node("ROFT:3-1").is_total() + assert summary.smspec_node("RGFR:5").is_rate() + + +@pytest.mark.usefixtures("use_tmpdir") +@pytest.mark.parametrize( + "points", + [ + [datetime.datetime(2010, 1, 3), datetime.datetime(2010, 1, 1)], + [datetime.datetime(2009, 1, 1), datetime.datetime(2010, 1, 3)], + [datetime.datetime(2010, 1, 1), datetime.datetime(2011, 1, 1)], + ], + ids=["unsorted", "before_start", "after_end"], +) +def test_that_resampling_without_extrapolation_rejects_invalid_time_points(points): + _write_case("SOURCE", num_reports=2) + + with pytest.raises(ValueError, match="Failed to create new resampled case"): + Summary("SOURCE").resample("RESAMPLED", points) + + +@pytest.mark.usefixtures("use_tmpdir") +def test_that_resampling_with_extrapolation_clamps_to_the_first_and_last_value(): + _write_case("SOURCE", num_reports=2) + source = Summary("SOURCE") + before = datetime.datetime(2009, 1, 1) + after = datetime.datetime(2011, 1, 1) + inside = [datetime.datetime(2010, 1, 1), datetime.datetime(2010, 1, 3)] + + low = source.resample( + "LOW", [before, *inside], lower_extrapolation=True, upper_extrapolation=False + ) + high = source.resample("HIGH", [*inside, after], upper_extrapolation=True) + + assert list(low["FOPT"].values) == pytest.approx([0.0, 0.0, 20.0]) + assert list(high["FOPT"].values) == pytest.approx([0.0, 20.0, 50.0]) + + +@pytest.mark.usefixtures("use_tmpdir") +def test_that_export_csv_creates_missing_parent_directories(): + _write_case("CSV_SRC", num_reports=2) + + Summary("CSV_SRC").export_csv(os.path.join("new", "sub", "out.csv")) + + assert os.path.isfile(os.path.join("new", "sub", "out.csv")) + + +@pytest.mark.usefixtures("use_tmpdir") +def test_that_export_csv_to_a_directory_raises_runtime_error(): + _write_case("CSV_SRC", num_reports=2) + os.mkdir("a_directory") + + with pytest.raises(RuntimeError, match="Failed to open 'a_directory'"): + Summary("CSV_SRC").export_csv("a_directory") + + +@pytest.mark.usefixtures("use_tmpdir") +def test_that_loading_from_a_nonexistent_directory_raises_os_error(): + with pytest.raises(OSError, match="Failed to create summary"): + Summary(os.path.join("no_such_dir", "CASE")) + + +@pytest.mark.usefixtures("use_tmpdir") +@pytest.mark.parametrize("add", ["variable", "local_variable"]) +def test_that_variables_cannot_be_added_after_the_first_time_step(add): + writer = Summary.writer("WRITER", datetime.datetime(2010, 1, 1), 5, 5, 5) + writer.add_variable("FOPT", unit="SM3") + writer.add_t_step(1, 1) + + with pytest.raises(ValueError, match="Can not interchange"): + if add == "variable": + writer.add_variable("FOPR", unit="SM3/DAY") + else: + writer.add_variable( + "LBPR", wgname="L1", lgr="L1", lgr_ijk=(1, 1, 1), unit="BARSA" + ) + + +@pytest.mark.usefixtures("use_tmpdir") +def test_that_numpy_vector_of_a_writer_without_time_steps_is_empty(): + writer = Summary.writer("WRITER", datetime.datetime(2010, 1, 1), 5, 5, 5) + writer.add_variable("FOPT", unit="SM3") + + assert len(writer.numpy_vector("FOPT")) == 0 + + +@pytest.mark.usefixtures("use_tmpdir") +def test_that_first_day_is_the_time_of_the_first_step_in_days(): + _write_header_case("LATE", _time_and_fopt(), time_values=(3.0, 4.0)) + + assert Summary("LATE").first_day == 3.0 + + +@pytest.mark.usefixtures("use_tmpdir") +def test_that_sim_length_of_a_writer_without_time_steps_is_zero(): + writer = Summary.writer("WRITER", datetime.datetime(2010, 1, 1), 5, 5, 5) + + assert writer.sim_length == 0.0 + + +@pytest.mark.usefixtures("use_tmpdir") +def test_that_first_day_of_a_writer_without_time_steps_raises_index_error(): + writer = Summary.writer("WRITER", datetime.datetime(2010, 1, 1), 5, 5, 5) + + with pytest.raises(IndexError): + _ = writer.first_day + + +@pytest.mark.usefixtures("use_tmpdir") +def test_that_the_case_is_guessed_from_a_lowercase_data_file(): + _write_case("CASE", num_reports=2) + os.mkdir("run") + for ext in ("SMSPEC", "UNSMRY"): + shutil.move(f"CASE.{ext}", f"run/CASE.{ext}") + with open("run/CASE.data", "w"): + pass + + assert sorted(Summary("run/").keys()) == ["FOPR", "FOPT", "WOPR:OP1"] + + +@pytest.mark.usefixtures("use_tmpdir") +@pytest.mark.parametrize("formatted_is_newer", [True, False]) +def test_that_a_case_with_both_formatted_and_unformatted_headers_can_be_loaded( + formatted_is_newer, +): + _write_case("BOTH", fmt_output=False, unified=True, num_reports=3) + _write_case("BOTH", fmt_output=True, unified=True, num_reports=4) + older, newer = ("BOTH.SMSPEC", "BOTH.FSMSPEC") + if not formatted_is_newer: + older, newer = newer, older + os.utime(older, (1_000_000_000, 1_000_000_000)) + os.utime(newer, (1_100_000_000, 1_100_000_000)) + + summary = Summary("BOTH") + + num_reports = 3 if newer == "BOTH.FSMSPEC" else 4 + + assert list(summary["FOPT"].values) == [ + 10.0 * d for d in range(num_reports * NUM_MINISTEPS) + ] + + +@pytest.mark.usefixtures("use_tmpdir") +@pytest.mark.parametrize("lazy_load", [True, False]) +@pytest.mark.parametrize("dtype", ["int32", "float64"]) +def test_that_a_params_keyword_which_is_not_single_precision_float_is_rejected( + lazy_load, dtype +): + _write_case("WRONGTYPE", num_reports=2) + contents = [ + (kw, np.asarray(array).astype(dtype) if kw.strip() == "PARAMS" else array) + for kw, array in resfo.read("WRONGTYPE.UNSMRY") + ] + resfo.write("WRONGTYPE.UNSMRY", contents) + + with pytest.raises(ValueError, match="PARAMS"): + Summary("WRONGTYPE", lazy_load=lazy_load)