mirror of
https://github.com/OPM/ResInsight.git
synced 2025-02-25 18:55:39 -06:00
1837 lines
67 KiB
C++
1837 lines
67 KiB
C++
/*
|
|
Copyright (C) 2011 Equinor ASA, Norway.
|
|
|
|
The file 'ecl_smspec.c' is part of ERT - Ensemble based Reservoir Tool.
|
|
|
|
ERT is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
ERT is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
|
|
for more details.
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include <stdbool.h>
|
|
#include <math.h>
|
|
#include <time.h>
|
|
#include <errno.h>
|
|
|
|
#include <string>
|
|
#include <vector>
|
|
#include <set>
|
|
#include <map>
|
|
#include <algorithm>
|
|
#include <memory>
|
|
#include <stdexcept>
|
|
|
|
#include <ert/util/hash.hpp>
|
|
#include <ert/util/util.h>
|
|
#include <ert/util/float_vector.hpp>
|
|
#include <ert/util/stringlist.hpp>
|
|
#include "detail/util/path.hpp"
|
|
|
|
#include <ert/ecl/ecl_smspec.hpp>
|
|
#include <ert/ecl/ecl_file.hpp>
|
|
#include <ert/ecl/ecl_kw_magic.hpp>
|
|
#include <ert/ecl/ecl_kw.hpp>
|
|
#include <ert/ecl/ecl_util.hpp>
|
|
#include <ert/ecl/smspec_node.hpp>
|
|
#include <ert/ecl/ecl_endian_flip.hpp>
|
|
#include <ert/ecl/ecl_type.hpp>
|
|
|
|
#ifdef HAVE_FNMATCH
|
|
#include <fnmatch.h>
|
|
#endif
|
|
|
|
|
|
/**
|
|
This file implements the indexing into the ECLIPSE summary files.
|
|
*/
|
|
|
|
/*
|
|
Supporting a new variable type:
|
|
-------------------------------
|
|
|
|
1. The function smspec_node_alloc() must be updated to return a valid
|
|
ecl::smspec_node instance when called with the new var_type.
|
|
|
|
2. Update the function ecl_smpec_install_gen_key() to install smpec_index
|
|
instances of this particular type. The format of the general key is
|
|
implicitly defined in this function.
|
|
|
|
3. The ecl_smspec structure supports two different types of lookup:
|
|
|
|
a) Lookup based on general key like e.g. WWCT:OP6
|
|
b) Specific lookup based on the variable type, i.e. :
|
|
|
|
ecl_smspec_get_well_var( xxx , well_name , var).
|
|
|
|
Historically everything started with specific lookup as in case b);
|
|
however the general lookup proved to be very convenient, and the
|
|
specfic lookup method has seen less use[*]. The final step in
|
|
supporting a new variable is to update the function
|
|
ecl_smspec_fread_header().
|
|
|
|
If you want to support specific lookup of the new variable type you
|
|
must add the necessary datastructures to the ecl_smspec_struct
|
|
structure and then subsequently fill that structure in the big
|
|
switch() in ecl_smspec_fread_header() - if you do not care about
|
|
specific lookup you just have to add an empty case() slot to the
|
|
switch in ecl_smspec_fread_header(). The LGR variables, and also
|
|
ECL_SMSPEC_SEGMENT_VAR do not support specific lookup.
|
|
|
|
[*]: The advantage of the specific lookup is that it is possible
|
|
to supply better error messages (The well 'XX' does not
|
|
exist, instead of just unknown key: 'WWCT:XX'), and it is
|
|
also possible to support queries like: give me all the
|
|
well names.
|
|
|
|
4. Mark the variable type as supported with a 'X' in the defintion of
|
|
ecl_smspec_var_type in ecl_smspec.h.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define ECL_SMSPEC_ID 806647
|
|
|
|
typedef std::map<std::string, const ecl::smspec_node * > node_map;
|
|
|
|
struct ecl_smspec_struct {
|
|
UTIL_TYPE_ID_DECLARATION;
|
|
/*
|
|
All the hash tables listed below here are different ways to access
|
|
smspec_node instances. The actual smspec_node instances are
|
|
owned by the smspec_nodes vector;
|
|
*/
|
|
node_map field_var_index;
|
|
node_map misc_var_index; /* Variables like 'TCPU' and 'NEWTON'. */
|
|
node_map gen_var_index /* This is "everything" - things can either be found as gen_var("WWCT:OP_X") or as well_var("WWCT" , "OP_X") */;
|
|
|
|
std::map<std::string, node_map> well_var_index; /* Indexes for all well variables:
|
|
{well1: {var1: index1 , var2: index2} , well2: {var1: index1 , var2: index2}} */
|
|
std::map<std::string, node_map> group_var_index; /* Indexes for group variables.*/
|
|
std::map<int, node_map> region_var_index; /* The stored index is an offset. */
|
|
std::map<int, node_map> block_var_index; /* Block variables like BPR */
|
|
std::map<std::string, std::map<int, node_map>> well_completion_var_index; /* Indexes for completion indexes .*/
|
|
|
|
|
|
std::vector<std::unique_ptr<ecl::smspec_node>> smspec_nodes;
|
|
bool write_mode;
|
|
bool need_nums;
|
|
std::vector<int> index_map;
|
|
std::map<int, int> inv_index_map;
|
|
int params_size;
|
|
/*-----------------------------------------------------------------*/
|
|
|
|
int time_seconds;
|
|
int grid_dims[3]; /* Grid dimensions - in DIMENS[1,2,3] */
|
|
int num_regions;
|
|
int Nwells , param_offset;
|
|
std::string key_join_string; /* The string used to join keys when building gen_key keys - typically ":" -
|
|
but arbitrary - NOT necessary to be able to invert the joining. */
|
|
std::string header_file; /* FULL path to the currenbtly loaded header_file. */
|
|
|
|
bool formatted; /* Has this summary instance been loaded from a formatted (i.e. FSMSPEC file) or unformatted (i.e. SMSPEC) file. */
|
|
time_t sim_start_time; /* When did the simulation start - worldtime. */
|
|
|
|
int time_index; /* The fields time_index, day_index, month_index and year_index */
|
|
int day_index; /* are used by the ecl_sum_data object to locate per. timestep */
|
|
int month_index; /* time information. */
|
|
int year_index;
|
|
bool has_lgr;
|
|
std::vector<float> params_default;
|
|
|
|
std::string restart_case;
|
|
ert_ecl_unit_enum unit_system;
|
|
int restart_step;
|
|
};
|
|
|
|
|
|
/**
|
|
About indexing:
|
|
---------------
|
|
|
|
The ECLISPE summary files are organised (roughly) like this:
|
|
|
|
1. A header-file called xxx.SMPSEC is written, which is common to
|
|
every timestep.
|
|
|
|
2. For each timestep the summary vector is written in the form of a
|
|
vector 'PARAMS' of length N with floats. In the PARAMS vector all
|
|
types of data are stacked togeheter, and one must use the header
|
|
info in the SMSPEC file to disentangle the summary data.
|
|
|
|
Here I will try to describe how the header in SMSPEC is organised, and
|
|
how that support is imlemented here. The SMSPEC header is organized
|
|
around three character vectors, of length N. To find the position in
|
|
the PARAMS vector of a certain quantity, you must consult one, two or
|
|
all three of these vectors. The most important vecor - which must
|
|
always be consulted is the KEYWORDS vector, then it is the WGNAMES and
|
|
NUMS (integer) vectors whcih must be consulted for some variable
|
|
types.
|
|
|
|
|
|
Let us a consider a system consisting of:
|
|
|
|
* Two wells: P1 and P2 - for each well we have variables WOPR, WWCT
|
|
and WGOR.
|
|
|
|
* Three regions: For each region we have variables RPR and RXX(??)
|
|
|
|
* We have stored field properties FOPT and FWPT
|
|
|
|
|
|
KEYWORDS = ['TIME','FOPR','FPR','FWCT','WOPR','WOPR,'WWCT','WWCT]
|
|
....
|
|
|
|
|
|
|
|
general_var:
|
|
------------
|
|
VAR_TYPE:(WELL_NAME|GROUP_NAME|NUMBER):NUMBER
|
|
|
|
Field var: VAR_TYPE
|
|
Misc var: VAR_TYPE
|
|
Well var: VAR_TYPE:WELL_NAME
|
|
Group var: VAR_TYPE:GROUP_NAME
|
|
Block var: VAR_TYPE:i,j,k (where i,j,k is calculated form NUM)
|
|
Region var VAR_TYPE:index (where index is NOT from the nums vector, it it is just an offset).
|
|
Completion var: VAR_TYPE:WELL_NAME:NUM
|
|
....
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/*
|
|
The smspec_required_keywords variable contains a list of keywords
|
|
which are *absolutely* required in the SMSPEC file, but observe that
|
|
depending on the content of the "KEYWORDS" array other keywords
|
|
might bre requred as well - this typically includes the NUMS
|
|
keyword. Such 'second-order' dependencies are not accounted for with
|
|
this simple list.
|
|
*/
|
|
|
|
static const size_t num_req_keywords = 5;
|
|
static const char* smspec_required_keywords[] = {
|
|
WGNAMES_KW,
|
|
KEYWORDS_KW,
|
|
STARTDAT_KW,
|
|
UNITS_KW,
|
|
DIMENS_KW
|
|
};
|
|
|
|
namespace {
|
|
|
|
const ecl::smspec_node * ecl_smspec_get_var_node( const node_map& mp, const char * var) {
|
|
if (!var) return nullptr;
|
|
|
|
const auto it = mp.find(var);
|
|
if (it == mp.end())
|
|
return nullptr;
|
|
|
|
return it->second;
|
|
}
|
|
|
|
const ecl::smspec_node * ecl_smspec_get_str_key_var_node( const std::map<std::string, node_map>& mp , const char * key , const char * var) {
|
|
const auto key_it = mp.find(key);
|
|
if (key_it == mp.end())
|
|
return nullptr;
|
|
|
|
const node_map& var_map = key_it->second;
|
|
return ecl_smspec_get_var_node(var_map, var);
|
|
}
|
|
|
|
const ecl::smspec_node * ecl_smspec_get_int_key_var_node(const std::map<int, node_map>& mp , int key , const char * var) {
|
|
const auto key_it = mp.find(key);
|
|
if (key_it == mp.end())
|
|
return nullptr;
|
|
|
|
const auto& var_map = key_it->second;
|
|
return ecl_smspec_get_var_node(var_map, var);
|
|
}
|
|
|
|
} //end namespace
|
|
|
|
int ecl_smspec_num_nodes( const ecl_smspec_type * smspec) {
|
|
return smspec->smspec_nodes.size();
|
|
}
|
|
|
|
/*
|
|
When loading a summary case from file many of the nodes can be ignored, in
|
|
that case the size of PARAMS vector in the data files is larger than the
|
|
number of internalized nodes. Therefor we need to maintain the
|
|
params_size member.
|
|
*/
|
|
|
|
int ecl_smspec_get_params_size( const ecl_smspec_type * smspec ) {
|
|
return smspec->params_size;
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************/
|
|
|
|
ecl_smspec_type * ecl_smspec_alloc_empty(bool write_mode , const char * key_join_string) {
|
|
ecl_smspec_type * ecl_smspec = new ecl_smspec_type();
|
|
UTIL_TYPE_ID_INIT(ecl_smspec , ECL_SMSPEC_ID);
|
|
|
|
ecl_smspec->sim_start_time = -1;
|
|
ecl_smspec->key_join_string = key_join_string;
|
|
ecl_smspec->header_file = "";
|
|
|
|
ecl_smspec->time_index = -1;
|
|
ecl_smspec->day_index = -1;
|
|
ecl_smspec->year_index = -1;
|
|
ecl_smspec->month_index = -1;
|
|
ecl_smspec->time_seconds = -1;
|
|
ecl_smspec->params_size = -1;
|
|
|
|
/*
|
|
The unit system is given as an integer in the INTEHEAD keyword. The INTEHEAD
|
|
keyword is optional, and we have for a long time been completely oblivious
|
|
to the possibility of extracting unit system information from the SMSPEC file.
|
|
*/
|
|
ecl_smspec->unit_system = ECL_METRIC_UNITS;
|
|
|
|
ecl_smspec->restart_step = -1;
|
|
ecl_smspec->write_mode = write_mode;
|
|
ecl_smspec->need_nums = false;
|
|
|
|
return ecl_smspec;
|
|
}
|
|
|
|
|
|
int * ecl_smspec_alloc_mapping( const ecl_smspec_type * self, const ecl_smspec_type * other) {
|
|
int params_size = ecl_smspec_get_params_size( self );
|
|
int * mapping = (int*) util_malloc( params_size * sizeof * mapping );
|
|
|
|
for (int i = 0; i < params_size; i++)
|
|
mapping[i] = -1;
|
|
|
|
|
|
for (int i=0; i < ecl_smspec_num_nodes( self ); i++) {
|
|
const ecl::smspec_node& self_node = ecl_smspec_iget_node_w_node_index( self , i );
|
|
int self_index = self_node.get_params_index();
|
|
const char * key = self_node.get_gen_key1();
|
|
if (ecl_smspec_has_general_var( other , key)) {
|
|
const ecl::smspec_node& other_node = ecl_smspec_get_general_var_node( other , key);
|
|
int other_index = other_node.get_params_index();
|
|
mapping[ self_index ] = other_index;
|
|
}
|
|
}
|
|
|
|
return mapping;
|
|
}
|
|
|
|
|
|
/**
|
|
Observe that the index here is into the __INTERNAL__ indexing in
|
|
the smspec_nodes vector; and in general widely different from the
|
|
params_index of the returned smspec_node instance.
|
|
*/
|
|
|
|
|
|
const ecl::smspec_node& ecl_smspec_iget_node_w_node_index( const ecl_smspec_type * smspec , int node_index ) {
|
|
const auto& node = smspec->smspec_nodes[node_index];
|
|
return *node.get();
|
|
}
|
|
|
|
|
|
/*
|
|
The ecl_smspec_iget_node() function is only retained for compatibility; should be
|
|
replaced with calls to the more explicit: ecl_smspec_iget_node_w_node_index().
|
|
*/
|
|
|
|
const ecl::smspec_node& ecl_smspec_iget_node(const ecl_smspec_type * smspec, int index) {
|
|
return ecl_smspec_iget_node_w_node_index(smspec, index);
|
|
}
|
|
|
|
const ecl::smspec_node& ecl_smspec_iget_node_w_params_index( const ecl_smspec_type * smspec , int params_index ) {
|
|
int node_index = smspec->inv_index_map.at(params_index);
|
|
return ecl_smspec_iget_node_w_node_index(smspec, node_index);
|
|
}
|
|
|
|
/**
|
|
* Returns an ecl data type for which all names will fit. If the maximum name
|
|
* length is at most 8, an ECL_CHAR is returned and otherwise a large enough
|
|
* ECL_STRING.
|
|
*/
|
|
static ecl_data_type get_wgnames_type(const ecl_smspec_type * smspec) {
|
|
size_t max_len = 0;
|
|
for(int i = 0; i < ecl_smspec_num_nodes(smspec); ++i) {
|
|
const ecl::smspec_node& node = ecl_smspec_iget_node_w_node_index(smspec, i);
|
|
const char * name = smspec_node_get_wgname( &node );
|
|
if(name)
|
|
max_len = util_size_t_max(max_len, strlen(name));
|
|
}
|
|
|
|
return max_len <= ECL_STRING8_LENGTH ? ECL_CHAR : ECL_STRING(max_len);
|
|
}
|
|
|
|
static void ecl_smspec_fwrite_INTEHEAD(const ecl_smspec_type * smspec, fortio_type * fortio) {
|
|
ecl_kw_type * intehead = ecl_kw_alloc( INTEHEAD_KW, INTEHEAD_SMSPEC_SIZE, ECL_INT);
|
|
ecl_kw_iset_int(intehead, INTEHEAD_SMSPEC_UNIT_INDEX, smspec->unit_system);
|
|
/*
|
|
The simulator type is just hardcoded to ECLIPSE100.
|
|
*/
|
|
ecl_kw_iset_int(intehead, INTEHEAD_SMSPEC_IPROG_INDEX, INTEHEAD_ECLIPSE100_VALUE);
|
|
ecl_kw_fwrite(intehead, fortio);
|
|
ecl_kw_free(intehead);
|
|
}
|
|
|
|
|
|
static void ecl_smspec_fwrite_RESTART(const ecl_smspec_type * smspec, fortio_type * fortio) {
|
|
ecl_kw_type * restart_kw = ecl_kw_alloc( RESTART_KW , SUMMARY_RESTART_SIZE , ECL_CHAR );
|
|
for (int i=0; i < SUMMARY_RESTART_SIZE; i++)
|
|
ecl_kw_iset_string8( restart_kw , i , "");
|
|
|
|
if (smspec->restart_case.size() > 0) {
|
|
size_t restart_case_len = smspec->restart_case.size();
|
|
|
|
size_t offset = 0;
|
|
for (size_t i = 0; i < SUMMARY_RESTART_SIZE ; i++) {
|
|
if (offset < restart_case_len)
|
|
ecl_kw_iset_string8( restart_kw , i , &smspec->restart_case[ offset ]);
|
|
offset += ECL_STRING8_LENGTH;
|
|
}
|
|
}
|
|
ecl_kw_fwrite( restart_kw , fortio );
|
|
ecl_kw_free( restart_kw );
|
|
}
|
|
|
|
|
|
|
|
static void ecl_smspec_fwrite_DIMENS(const ecl_smspec_type * smspec, fortio_type * fortio) {
|
|
ecl_kw_type * dimens_kw = ecl_kw_alloc( DIMENS_KW , DIMENS_SIZE , ECL_INT );
|
|
int num_nodes = ecl_smspec_num_nodes( smspec );
|
|
ecl_kw_iset_int( dimens_kw , DIMENS_SMSPEC_SIZE_INDEX , num_nodes );
|
|
ecl_kw_iset_int( dimens_kw , DIMENS_SMSPEC_NX_INDEX , smspec->grid_dims[0] );
|
|
ecl_kw_iset_int( dimens_kw , DIMENS_SMSPEC_NY_INDEX , smspec->grid_dims[1] );
|
|
ecl_kw_iset_int( dimens_kw , DIMENS_SMSPEC_NZ_INDEX , smspec->grid_dims[2] );
|
|
ecl_kw_iset_int( dimens_kw , 4 , 0 ); // Do not know what this is for.
|
|
ecl_kw_iset_int( dimens_kw , DIMENS_SMSPEC_RESTART_STEP_INDEX , smspec->restart_step );
|
|
|
|
ecl_kw_fwrite( dimens_kw , fortio );
|
|
ecl_kw_free( dimens_kw );
|
|
}
|
|
|
|
|
|
static void ecl_smspec_fwrite_STARTDAT(const ecl_smspec_type * smspec, fortio_type * fortio) {
|
|
ecl_kw_type * startdat_kw = ecl_kw_alloc( STARTDAT_KW , STARTDAT_SIZE , ECL_INT );
|
|
int day,month,year;
|
|
ecl_util_set_date_values( smspec->sim_start_time , &day, &month , &year);
|
|
|
|
ecl_kw_iset_int( startdat_kw , STARTDAT_DAY_INDEX , day );
|
|
ecl_kw_iset_int( startdat_kw , STARTDAT_MONTH_INDEX , month );
|
|
ecl_kw_iset_int( startdat_kw , STARTDAT_YEAR_INDEX , year );
|
|
|
|
ecl_kw_fwrite( startdat_kw , fortio );
|
|
ecl_kw_free( startdat_kw );
|
|
}
|
|
|
|
|
|
static void ecl_smspec_fortio_fwrite( const ecl_smspec_type * smspec , fortio_type * fortio) {
|
|
ecl_smspec_fwrite_INTEHEAD(smspec, fortio);
|
|
ecl_smspec_fwrite_RESTART(smspec, fortio);
|
|
ecl_smspec_fwrite_DIMENS(smspec, fortio);
|
|
|
|
int num_nodes = ecl_smspec_num_nodes( smspec );
|
|
ecl_kw_type * keywords_kw = ecl_kw_alloc( KEYWORDS_KW , num_nodes , ECL_CHAR );
|
|
ecl_kw_type * units_kw = ecl_kw_alloc( UNITS_KW , num_nodes , ECL_CHAR );
|
|
ecl_kw_type * nums_kw = NULL;
|
|
|
|
// If the names_type is an ECL_STRING we expect this to be an INTERSECT
|
|
// summary, otherwise an ECLIPSE summary.
|
|
ecl_data_type names_type = get_wgnames_type(smspec);
|
|
ecl_kw_type * wgnames_kw = ecl_kw_alloc( ecl_type_is_char(names_type) ? WGNAMES_KW : NAMES_KW,
|
|
num_nodes,
|
|
names_type );
|
|
|
|
if (smspec->need_nums)
|
|
nums_kw = ecl_kw_alloc( NUMS_KW , num_nodes , ECL_INT);
|
|
|
|
for (int i=0; i < ecl_smspec_num_nodes( smspec ); i++) {
|
|
const ecl::smspec_node& smspec_node = ecl_smspec_iget_node_w_node_index( smspec , i );
|
|
/*
|
|
It is possible to add variables with deferred initialisation
|
|
with the ecl_sum_add_blank_var() function. Before these
|
|
variables can be actually used for anything interesting they
|
|
must be initialized with the ecl_sum_init_var() function.
|
|
|
|
If a call to save the smspec file comes before all the
|
|
variable have been initialized things will potentially go
|
|
belly up. This is solved with the following uber-hack:
|
|
|
|
o One of the well related keywords is chosen; in
|
|
particular 'WWCT' in this case.
|
|
|
|
o The wgname value is set to DUMMY_WELL
|
|
|
|
The use of DUMMY_WELL ensures that this field will be
|
|
ignored when/if this smspec file is read in at a later
|
|
stage.
|
|
*/
|
|
if (smspec_node.get_var_type() == ECL_SMSPEC_INVALID_VAR) {
|
|
ecl_kw_iset_string8( keywords_kw , i , "WWCT" );
|
|
ecl_kw_iset_string8( units_kw , i , "????????");
|
|
ecl_kw_iset_string_ptr( wgnames_kw , i , DUMMY_WELL);
|
|
} else {
|
|
ecl_kw_iset_string8( keywords_kw , i , smspec_node_get_keyword( &smspec_node ));
|
|
ecl_kw_iset_string8( units_kw , i , smspec_node_get_unit( &smspec_node ));
|
|
{
|
|
const char * wgname = DUMMY_WELL;
|
|
if (smspec_node_get_wgname( &smspec_node ))
|
|
wgname = smspec_node_get_wgname( &smspec_node );
|
|
ecl_kw_iset_string_ptr( wgnames_kw , i , wgname);
|
|
}
|
|
}
|
|
|
|
if (nums_kw != NULL)
|
|
ecl_kw_iset_int( nums_kw , i , smspec_node.get_num());
|
|
}
|
|
ecl_kw_fwrite( keywords_kw , fortio );
|
|
ecl_kw_fwrite( wgnames_kw , fortio );
|
|
if (nums_kw != NULL)
|
|
ecl_kw_fwrite( nums_kw , fortio );
|
|
ecl_kw_fwrite( units_kw , fortio );
|
|
|
|
ecl_kw_free( keywords_kw );
|
|
ecl_kw_free( wgnames_kw );
|
|
ecl_kw_free( units_kw );
|
|
if (nums_kw != NULL)
|
|
ecl_kw_free( nums_kw );
|
|
|
|
ecl_smspec_fwrite_STARTDAT(smspec, fortio);
|
|
}
|
|
|
|
|
|
void ecl_smspec_fwrite( const ecl_smspec_type * smspec , const char * ecl_case , bool fmt_file ) {
|
|
char * filename = ecl_util_alloc_filename( NULL , ecl_case , ECL_SUMMARY_HEADER_FILE , fmt_file , 0 );
|
|
fortio_type * fortio = fortio_open_writer( filename , fmt_file , ECL_ENDIAN_FLIP);
|
|
|
|
if (!fortio) {
|
|
const char * error_fmt_msg = "%s: Unable to open fortio file %s, error: %s .\n";
|
|
util_abort( error_fmt_msg , __func__ , filename , strerror( errno ) );
|
|
}
|
|
|
|
ecl_smspec_fortio_fwrite( smspec , fortio );
|
|
|
|
fortio_fclose( fortio );
|
|
free( filename );
|
|
}
|
|
|
|
|
|
|
|
static ecl_smspec_type * ecl_smspec_alloc_writer__( const char * key_join_string , const char * restart_case, int restart_step, time_t sim_start , bool time_in_days , int nx , int ny , int nz) {
|
|
ecl_smspec_type * ecl_smspec = ecl_smspec_alloc_empty( true , key_join_string );
|
|
/*
|
|
Only a total of 9 * 8 characters is set aside for the restart keyword, if
|
|
the supplied restart case is longer than that we silently ignore it.
|
|
*/
|
|
if (restart_case) {
|
|
if (strlen(restart_case) <= (SUMMARY_RESTART_SIZE * ECL_STRING8_LENGTH)) {
|
|
ecl_smspec->restart_case = restart_case;
|
|
ecl_smspec->restart_step = restart_step;
|
|
}
|
|
}
|
|
ecl_smspec->grid_dims[0] = nx;
|
|
ecl_smspec->grid_dims[1] = ny;
|
|
ecl_smspec->grid_dims[2] = nz;
|
|
ecl_smspec->sim_start_time = sim_start;
|
|
|
|
{
|
|
const ecl::smspec_node * time_node;
|
|
|
|
if (time_in_days) {
|
|
ecl_smspec->time_seconds = 3600 * 24;
|
|
time_node = ecl_smspec_add_node(ecl_smspec, "TIME", "DAYS", 0);
|
|
} else {
|
|
ecl_smspec->time_seconds = 3600;
|
|
time_node = ecl_smspec_add_node(ecl_smspec, "TIME", "HOURS", 0);
|
|
}
|
|
ecl_smspec->time_index = time_node->get_params_index();
|
|
}
|
|
return ecl_smspec;
|
|
}
|
|
|
|
ecl_smspec_type * ecl_smspec_alloc_restart_writer( const char * key_join_string , const char * restart_case, int restart_step, time_t sim_start , bool time_in_days , int nx , int ny , int nz) {
|
|
return ecl_smspec_alloc_writer__(key_join_string, restart_case, restart_step, sim_start, time_in_days, nx, ny, nz);
|
|
}
|
|
|
|
ecl_smspec_type * ecl_smspec_alloc_writer(const char * key_join_string, time_t sim_start, bool time_in_days, int nx, int ny , int nz) {
|
|
return ecl_smspec_alloc_writer__(key_join_string, NULL, 0, sim_start, time_in_days, nx, ny, nz);
|
|
}
|
|
|
|
|
|
UTIL_SAFE_CAST_FUNCTION( ecl_smspec , ECL_SMSPEC_ID )
|
|
|
|
ecl_smspec_var_type ecl_smspec_identify_var_type(const char * var) {
|
|
return ecl::smspec_node::identify_var_type(var);
|
|
}
|
|
|
|
|
|
static bool ecl_smspec_lgr_var_type( ecl_smspec_var_type var_type) {
|
|
if ((var_type == ECL_SMSPEC_LOCAL_BLOCK_VAR) ||
|
|
(var_type == ECL_SMSPEC_LOCAL_WELL_VAR) ||
|
|
(var_type == ECL_SMSPEC_LOCAL_COMPLETION_VAR))
|
|
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
Takes a ecl_smspec_var_type variable as input, and return a string
|
|
representation of this var_type. Suitable for debug messages +++
|
|
*/
|
|
|
|
const char * ecl_smspec_get_var_type_name( ecl_smspec_var_type var_type ) {
|
|
switch(var_type) {
|
|
case(ECL_SMSPEC_INVALID_VAR):
|
|
return "INVALID_VAR";
|
|
break;
|
|
case(ECL_SMSPEC_AQUIFER_VAR):
|
|
return "AQUIFER_VAR";
|
|
break;
|
|
case(ECL_SMSPEC_WELL_VAR):
|
|
return "WELL_VAR";
|
|
break;
|
|
case(ECL_SMSPEC_REGION_VAR):
|
|
return "REGION_VAR";
|
|
break;
|
|
case(ECL_SMSPEC_FIELD_VAR):
|
|
return "FIELD_VAR";
|
|
break;
|
|
case(ECL_SMSPEC_GROUP_VAR):
|
|
return "GROUP_VAR";
|
|
break;
|
|
case(ECL_SMSPEC_BLOCK_VAR):
|
|
return "BLOCK_VAR";
|
|
break;
|
|
case(ECL_SMSPEC_COMPLETION_VAR):
|
|
return "COMPLETION_VAR";
|
|
break;
|
|
case(ECL_SMSPEC_LOCAL_BLOCK_VAR):
|
|
return "LOCAL_BLOCK_VAR";
|
|
break;
|
|
case(ECL_SMSPEC_LOCAL_COMPLETION_VAR):
|
|
return "LOCAL_COMPLETION_VAR";
|
|
break;
|
|
case(ECL_SMSPEC_LOCAL_WELL_VAR):
|
|
return "LOCAL_WELL_VAR";
|
|
break;
|
|
case(ECL_SMSPEC_NETWORK_VAR):
|
|
return "NETWORK_VAR";
|
|
break;
|
|
case(ECL_SMSPEC_REGION_2_REGION_VAR):
|
|
return "REGION_2_REGION_VAR";
|
|
break;
|
|
case(ECL_SMSPEC_SEGMENT_VAR):
|
|
return "SEGMENT_VAR";
|
|
break;
|
|
case(ECL_SMSPEC_MISC_VAR):
|
|
return "MISC_VAR";
|
|
break;
|
|
default:
|
|
util_abort("%s: Unrecognized variable type:%d \n",__func__ , var_type);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
Input i,j,k are assumed to be in the interval [1..nx] , [1..ny],
|
|
[1..nz], return value is a global index which can be used in the
|
|
xxx_block_xxx routines.
|
|
*/
|
|
|
|
|
|
static int ecl_smspec_get_global_grid_index(const ecl_smspec_type * smspec , int i , int j , int k) {
|
|
return i + (j - 1) * smspec->grid_dims[0] + (k - 1) * smspec->grid_dims[0] * smspec->grid_dims[1];
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
This function takes a fully initialized smspec_node instance, generates the
|
|
corresponding key and inserts smspec_node instance in the main hash table
|
|
smspec->gen_var_index.
|
|
|
|
The format strings used, i.e. VAR:WELL for well based variables is implicitly
|
|
defined through the format strings used in this function.
|
|
*/
|
|
|
|
static void ecl_smspec_install_gen_keys( ecl_smspec_type * smspec , const ecl::smspec_node& smspec_node ) {
|
|
/* Insert the default general mapping. */
|
|
{
|
|
const char * gen_key1 = smspec_node.get_gen_key1();
|
|
if (gen_key1)
|
|
smspec->gen_var_index[gen_key1] = &smspec_node;
|
|
}
|
|
|
|
/* Insert the (optional) extra mapping for block related variables and region_2_region variables: */
|
|
{
|
|
const char * gen_key2 = smspec_node.get_gen_key2();
|
|
if (gen_key2)
|
|
smspec->gen_var_index[gen_key2] = &smspec_node;
|
|
}
|
|
}
|
|
|
|
static void ecl_smspec_install_special_keys( ecl_smspec_type * ecl_smspec , const ecl::smspec_node& smspec_node) {
|
|
/**
|
|
This large switch is for installing keys which have custom lookup
|
|
paths, in addition to the lookup based on general keys. Examples
|
|
of this is e.g. well variables which can be looked up through:
|
|
|
|
ecl_smspec_get_well_var_index( smspec , well_name , var );
|
|
*/
|
|
|
|
std::string well;
|
|
if (smspec_node_get_wgname(&smspec_node)) well = smspec_node_get_wgname(&smspec_node);
|
|
|
|
std::string group = well;
|
|
const int num = smspec_node_get_num(&smspec_node);
|
|
|
|
std::string keyword;
|
|
if (smspec_node_get_keyword(&smspec_node)) keyword = smspec_node_get_keyword(&smspec_node);
|
|
|
|
ecl_smspec_var_type var_type = smspec_node_get_var_type(&smspec_node );
|
|
|
|
switch(var_type) {
|
|
case(ECL_SMSPEC_COMPLETION_VAR):
|
|
ecl_smspec->well_completion_var_index[well][num][keyword] = &smspec_node;
|
|
break;
|
|
case(ECL_SMSPEC_FIELD_VAR):
|
|
ecl_smspec->field_var_index[keyword] = &smspec_node;
|
|
break;
|
|
case(ECL_SMSPEC_GROUP_VAR):
|
|
ecl_smspec->group_var_index[group][keyword] = &smspec_node;
|
|
break;
|
|
case(ECL_SMSPEC_REGION_VAR):
|
|
ecl_smspec->region_var_index[num][keyword] = &smspec_node;
|
|
ecl_smspec->num_regions = util_int_max(ecl_smspec->num_regions , num);
|
|
break;
|
|
case (ECL_SMSPEC_WELL_VAR):
|
|
ecl_smspec->well_var_index[well][keyword] = &smspec_node;
|
|
break;
|
|
case(ECL_SMSPEC_MISC_VAR):
|
|
/* Misc variable - i.e. date or CPU time ... */
|
|
ecl_smspec->misc_var_index[keyword] = &smspec_node;
|
|
break;
|
|
case(ECL_SMSPEC_BLOCK_VAR):
|
|
ecl_smspec->block_var_index[num][keyword] = &smspec_node;
|
|
break;
|
|
/**
|
|
The variables below are ONLY accesable through the gen_key
|
|
setup; but the must be mentioned in this switch statement,
|
|
otherwise they will induce a hard failure in the default: target
|
|
below.
|
|
*/
|
|
case(ECL_SMSPEC_LOCAL_BLOCK_VAR):
|
|
break;
|
|
case(ECL_SMSPEC_LOCAL_COMPLETION_VAR):
|
|
break;
|
|
case(ECL_SMSPEC_LOCAL_WELL_VAR):
|
|
break;
|
|
case(ECL_SMSPEC_SEGMENT_VAR):
|
|
break;
|
|
case(ECL_SMSPEC_REGION_2_REGION_VAR):
|
|
break;
|
|
case(ECL_SMSPEC_AQUIFER_VAR):
|
|
break;
|
|
default:
|
|
throw std::invalid_argument("Internal error - should not be here \n");
|
|
}
|
|
}
|
|
|
|
/**
|
|
The usage of this functon breaks down completely if LGR's are involved.
|
|
*/
|
|
|
|
bool ecl_smspec_needs_wgname( ecl_smspec_var_type var_type ) {
|
|
switch( var_type ) {
|
|
case(ECL_SMSPEC_COMPLETION_VAR):
|
|
return true;
|
|
break;
|
|
case(ECL_SMSPEC_FIELD_VAR):
|
|
return false;
|
|
break;
|
|
case(ECL_SMSPEC_GROUP_VAR):
|
|
return true;
|
|
break;
|
|
case(ECL_SMSPEC_WELL_VAR):
|
|
return true;
|
|
break;
|
|
case(ECL_SMSPEC_REGION_VAR):
|
|
return false;
|
|
break;
|
|
case(ECL_SMSPEC_REGION_2_REGION_VAR):
|
|
return false;
|
|
break;
|
|
case(ECL_SMSPEC_MISC_VAR):
|
|
return false;
|
|
break;
|
|
case(ECL_SMSPEC_BLOCK_VAR):
|
|
return false;
|
|
break;
|
|
case(ECL_SMSPEC_AQUIFER_VAR):
|
|
return false;
|
|
break;
|
|
case(ECL_SMSPEC_SEGMENT_VAR):
|
|
return true;
|
|
break;
|
|
default:
|
|
util_exit("Sorry: support for variables of type:%s is not implemented in %s.\n",ecl_smspec_get_var_type_name( var_type ), __FILE__);
|
|
}
|
|
/* Really should not be here. */
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
The usage of this functon breaks down completely if LGR's are involved.
|
|
*/
|
|
bool ecl_smspec_needs_num( ecl_smspec_var_type var_type ) {
|
|
switch( var_type ) {
|
|
case(ECL_SMSPEC_COMPLETION_VAR):
|
|
return true;
|
|
break;
|
|
case(ECL_SMSPEC_AQUIFER_VAR):
|
|
return true;
|
|
break;
|
|
case(ECL_SMSPEC_FIELD_VAR):
|
|
return false;
|
|
break;
|
|
case(ECL_SMSPEC_GROUP_VAR):
|
|
return false;
|
|
break;
|
|
case(ECL_SMSPEC_WELL_VAR):
|
|
return false;
|
|
break;
|
|
case(ECL_SMSPEC_REGION_VAR):
|
|
return true;
|
|
break;
|
|
case(ECL_SMSPEC_REGION_2_REGION_VAR):
|
|
return true;
|
|
break;
|
|
case(ECL_SMSPEC_MISC_VAR):
|
|
return false;
|
|
break;
|
|
case(ECL_SMSPEC_BLOCK_VAR):
|
|
return true;
|
|
break;
|
|
default:
|
|
util_exit("Sorry: support for variables of type:%s is not implemented in %s.\n",ecl_smspec_get_var_type_name( var_type ), __FILE__);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
|
|
bool ecl_smspec_equal(const ecl_smspec_type * self,
|
|
const ecl_smspec_type * other) {
|
|
if (self->smspec_nodes.size() != other->smspec_nodes.size())
|
|
return false;
|
|
|
|
for (size_t i=0; i < self->smspec_nodes.size(); i++) {
|
|
const ecl::smspec_node* node1 = self->smspec_nodes[i].get();
|
|
const ecl::smspec_node* node2 = other->smspec_nodes[i].get();
|
|
|
|
if (node1->cmp(*node2) != 0)
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ecl_smspec_load_restart( ecl_smspec_type * ecl_smspec , const ecl_file_type * header ) {
|
|
if (ecl_file_has_kw( header , RESTART_KW )) {
|
|
const ecl_kw_type * restart_kw = ecl_file_iget_named_kw(header, RESTART_KW , 0);
|
|
char tmp_base[73]; /* To accomodate a maximum of 9 items which consist of 8 characters each. */
|
|
char * restart_base;
|
|
int i;
|
|
tmp_base[0] = '\0';
|
|
for (i=0; i < ecl_kw_get_size( restart_kw ); i++)
|
|
strcat( tmp_base , (const char*)ecl_kw_iget_ptr( restart_kw , i ));
|
|
|
|
restart_base = util_alloc_strip_copy( tmp_base );
|
|
if (strlen(restart_base)) { /* We ignore the empty ones. */
|
|
char * smspec_header;
|
|
|
|
/*
|
|
The conditional block here is to support the following situation:
|
|
|
|
1. A simulation with a restart has been performed on Posix with path
|
|
separator '/'.
|
|
|
|
2. The simulation is loaded on windows, where the native path
|
|
separator is '\'.
|
|
|
|
This code block will translate '/' -> '\' in the restart keyword which
|
|
is read from the summary file.
|
|
*/
|
|
|
|
#ifdef ERT_WINDOWS
|
|
for (int i=0; i < strlen(restart_base); i++) {
|
|
if (restart_base[i] == UTIL_POSIX_PATH_SEP_CHAR)
|
|
restart_base[i] = UTIL_PATH_SEP_CHAR;
|
|
}
|
|
#endif
|
|
|
|
std::string path = ecl::util::path::dirname( ecl_smspec->header_file );
|
|
smspec_header = ecl_util_alloc_exfilename( path.c_str() , restart_base , ECL_SUMMARY_HEADER_FILE , ecl_smspec->formatted , 0);
|
|
if (smspec_header) {
|
|
if (!util_same_file(smspec_header , ecl_smspec->header_file.c_str())) /* Restart from the current case is ignored. */ {
|
|
if (util_is_abs_path(restart_base))
|
|
ecl_smspec->restart_case = restart_base;
|
|
else {
|
|
char * tmp_path = util_alloc_filename( path.c_str() , restart_base , NULL );
|
|
char * abs_path = util_alloc_abs_path(tmp_path);
|
|
ecl_smspec->restart_case = abs_path;
|
|
free( abs_path );
|
|
free( tmp_path );
|
|
}
|
|
}
|
|
free( smspec_header );
|
|
}
|
|
}
|
|
free( restart_base );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static const ecl::smspec_node * ecl_smspec_insert_node(ecl_smspec_type * ecl_smspec, std::unique_ptr<ecl::smspec_node> smspec_node){
|
|
int params_index = smspec_node->get_params_index();
|
|
|
|
/* This indexing must be used when writing. */
|
|
ecl_smspec->index_map.push_back(params_index);
|
|
ecl_smspec->params_default.resize( params_index+1, std::numeric_limits<float>::quiet_NaN());
|
|
ecl_smspec->params_default[params_index] = smspec_node->get_default();
|
|
ecl_smspec->inv_index_map.insert( std::make_pair(params_index, ecl_smspec->smspec_nodes.size()));
|
|
|
|
ecl_smspec_install_gen_keys( ecl_smspec, *smspec_node.get() );
|
|
ecl_smspec_install_special_keys( ecl_smspec, *smspec_node.get() );
|
|
|
|
if (smspec_node->need_nums())
|
|
ecl_smspec->need_nums = true;
|
|
|
|
ecl_smspec->smspec_nodes.push_back(std::move(smspec_node));
|
|
|
|
if (params_index > ecl_smspec->params_size)
|
|
ecl_smspec->params_size = params_index + 1;
|
|
|
|
if (static_cast<int>(ecl_smspec->smspec_nodes.size()) > ecl_smspec->params_size)
|
|
ecl_smspec->params_size = ecl_smspec->smspec_nodes.size();
|
|
|
|
const auto& node = ecl_smspec->smspec_nodes.back();
|
|
return node.get();
|
|
}
|
|
|
|
|
|
const ecl::smspec_node * ecl_smspec_add_node(ecl_smspec_type * ecl_smspec, const char * keyword, int num, const char * unit, float default_value) {
|
|
int params_index = ecl_smspec->smspec_nodes.size();
|
|
return ecl_smspec_insert_node(ecl_smspec, std::unique_ptr<ecl::smspec_node>( new ecl::smspec_node(params_index,
|
|
keyword,
|
|
num,
|
|
unit,
|
|
ecl_smspec->grid_dims,
|
|
default_value,
|
|
ecl_smspec->key_join_string.c_str())));
|
|
}
|
|
|
|
//copy given node with a new index
|
|
const ecl::smspec_node * ecl_smspec_add_node(ecl_smspec_type * ecl_smspec, const ecl::smspec_node& node) {
|
|
int params_index = ecl_smspec->smspec_nodes.size();
|
|
return ecl_smspec_insert_node(ecl_smspec, std::unique_ptr<ecl::smspec_node>( new ecl::smspec_node(node, params_index)));
|
|
}
|
|
|
|
|
|
const ecl::smspec_node * ecl_smspec_add_node(ecl_smspec_type * ecl_smspec, const char * keyword, const char * unit, float default_value) {
|
|
int params_index = ecl_smspec->smspec_nodes.size();
|
|
return ecl_smspec_insert_node(ecl_smspec, std::unique_ptr<ecl::smspec_node>( new ecl::smspec_node(params_index,
|
|
keyword,
|
|
unit,
|
|
default_value)));
|
|
}
|
|
|
|
|
|
const ecl::smspec_node * ecl_smspec_add_node(ecl_smspec_type * ecl_smspec, const char * keyword, const char * wgname, const char * unit, float default_value) {
|
|
int params_index = ecl_smspec->smspec_nodes.size();
|
|
return ecl_smspec_insert_node(ecl_smspec, std::unique_ptr<ecl::smspec_node>( new ecl::smspec_node(params_index,
|
|
keyword,
|
|
wgname,
|
|
unit,
|
|
default_value,
|
|
ecl_smspec->key_join_string.c_str())));
|
|
}
|
|
|
|
|
|
const ecl::smspec_node * ecl_smspec_add_node(ecl_smspec_type * ecl_smspec,
|
|
const char * keyword,
|
|
const char * wgname,
|
|
int num,
|
|
const char * unit,
|
|
float default_value)
|
|
{
|
|
int params_index = ecl_smspec->smspec_nodes.size();
|
|
return ecl_smspec_insert_node(ecl_smspec, std::unique_ptr<ecl::smspec_node>( new ecl::smspec_node(params_index,
|
|
keyword,
|
|
wgname,
|
|
num,
|
|
unit,
|
|
ecl_smspec->grid_dims,
|
|
default_value,
|
|
ecl_smspec->key_join_string.c_str())));
|
|
}
|
|
|
|
|
|
const ecl::smspec_node * ecl_smspec_add_node(ecl_smspec_type * ecl_smspec,
|
|
int params_index,
|
|
const char * keyword,
|
|
const char * wgname,
|
|
int num,
|
|
const char * unit,
|
|
float default_value)
|
|
{
|
|
return ecl_smspec_insert_node(ecl_smspec, std::unique_ptr<ecl::smspec_node>( new ecl::smspec_node(params_index,
|
|
keyword,
|
|
wgname,
|
|
num,
|
|
unit,
|
|
ecl_smspec->grid_dims,
|
|
default_value,
|
|
ecl_smspec->key_join_string.c_str())));
|
|
}
|
|
|
|
const ecl::smspec_node * ecl_smspec_add_node(ecl_smspec_type * ecl_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)
|
|
{
|
|
return ecl_smspec_insert_node(ecl_smspec, std::unique_ptr<ecl::smspec_node>( new ecl::smspec_node(params_index,
|
|
keyword,
|
|
wgname,
|
|
unit,
|
|
lgr,
|
|
lgr_i, lgr_j, lgr_k,
|
|
default_value,
|
|
ecl_smspec->key_join_string.c_str())));
|
|
}
|
|
|
|
|
|
|
|
|
|
const int * ecl_smspec_get_index_map( const ecl_smspec_type * smspec ) {
|
|
return smspec->index_map.data();
|
|
}
|
|
|
|
/**
|
|
* This function is to support the NAMES alias for WGNAMES. If similar
|
|
* situations occur in the future, this is a sane starting point for general
|
|
* support.
|
|
*/
|
|
static const char * get_active_keyword_alias(ecl_file_type * header, const char * keyword) {
|
|
if (strcmp(keyword, WGNAMES_KW) == 0 || strcmp(keyword, NAMES_KW) == 0)
|
|
return ecl_file_has_kw(header, WGNAMES_KW) ? WGNAMES_KW : NAMES_KW;
|
|
|
|
return keyword;
|
|
}
|
|
|
|
static bool ecl_smspec_check_header( ecl_file_type * header ) {
|
|
bool OK = true;
|
|
for (size_t i=0; i < num_req_keywords && OK; i++) {
|
|
OK &= ecl_file_has_kw(
|
|
header,
|
|
get_active_keyword_alias(header, smspec_required_keywords[i])
|
|
);
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
|
|
static bool ecl_smspec_fread_header(ecl_smspec_type * ecl_smspec, const char * header_file , bool include_restart) {
|
|
ecl_file_type * header = ecl_file_open( header_file , 0);
|
|
if (header && ecl_smspec_check_header( header )) {
|
|
const char * names_alias = get_active_keyword_alias(header, WGNAMES_KW);
|
|
ecl_kw_type *wells = ecl_file_iget_named_kw(header, names_alias , 0);
|
|
ecl_kw_type *keywords = ecl_file_iget_named_kw(header, KEYWORDS_KW , 0);
|
|
ecl_kw_type *startdat = ecl_file_iget_named_kw(header, STARTDAT_KW , 0);
|
|
ecl_kw_type *units = ecl_file_iget_named_kw(header, UNITS_KW , 0);
|
|
ecl_kw_type *dimens = ecl_file_iget_named_kw(header, DIMENS_KW , 0);
|
|
ecl_kw_type *nums = NULL;
|
|
ecl_kw_type *lgrs = NULL;
|
|
ecl_kw_type *numlx = NULL;
|
|
ecl_kw_type *numly = NULL;
|
|
ecl_kw_type *numlz = NULL;
|
|
|
|
int params_index;
|
|
ecl_smspec->num_regions = 0;
|
|
ecl_smspec->params_size = ecl_kw_get_size(keywords);
|
|
if (startdat == NULL)
|
|
util_abort("%s: could not locate STARTDAT keyword in header - aborting \n",__func__);
|
|
|
|
if (ecl_file_has_kw(header , NUMS_KW))
|
|
nums = ecl_file_iget_named_kw(header , NUMS_KW , 0);
|
|
|
|
if (ecl_file_has_kw(header, INTEHEAD_KW)) {
|
|
const ecl_kw_type * intehead = ecl_file_iget_named_kw(header, INTEHEAD_KW, 0);
|
|
ecl_smspec->unit_system = (ert_ecl_unit_enum)ecl_kw_iget_int(intehead, INTEHEAD_SMSPEC_UNIT_INDEX);
|
|
/*
|
|
The second item in the INTEHEAD vector is an integer designating which
|
|
simulator has been used for the current simulation, that is currently
|
|
ignored.
|
|
*/
|
|
}
|
|
|
|
if (ecl_file_has_kw( header , LGRS_KW )) {/* The file has LGR information. */
|
|
lgrs = ecl_file_iget_named_kw( header , LGRS_KW , 0 );
|
|
numlx = ecl_file_iget_named_kw( header , NUMLX_KW , 0 );
|
|
numly = ecl_file_iget_named_kw( header , NUMLY_KW , 0 );
|
|
numlz = ecl_file_iget_named_kw( header , NUMLZ_KW , 0 );
|
|
ecl_smspec->has_lgr = true;
|
|
} else
|
|
ecl_smspec->has_lgr = false;
|
|
|
|
{
|
|
int * date = ecl_kw_get_int_ptr(startdat);
|
|
ecl_smspec->sim_start_time = ecl_util_make_date(date[STARTDAT_DAY_INDEX] ,
|
|
date[STARTDAT_MONTH_INDEX] ,
|
|
date[STARTDAT_YEAR_INDEX]);
|
|
}
|
|
|
|
ecl_smspec->grid_dims[0] = ecl_kw_iget_int(dimens , DIMENS_SMSPEC_NX_INDEX );
|
|
ecl_smspec->grid_dims[1] = ecl_kw_iget_int(dimens , DIMENS_SMSPEC_NY_INDEX );
|
|
ecl_smspec->grid_dims[2] = ecl_kw_iget_int(dimens , DIMENS_SMSPEC_NZ_INDEX );
|
|
ecl_smspec->restart_step = ecl_kw_iget_int(dimens , DIMENS_SMSPEC_RESTART_STEP_INDEX);
|
|
|
|
ecl_util_get_file_type( header_file , &ecl_smspec->formatted , NULL );
|
|
|
|
{
|
|
for (params_index=0; params_index < ecl_kw_get_size(wells); params_index++) {
|
|
float default_value = std::numeric_limits<float>::quiet_NaN();
|
|
int num = SMSPEC_NUMS_INVALID;
|
|
char * well = (char*)util_alloc_strip_copy((const char*)ecl_kw_iget_ptr(wells , params_index));
|
|
char * kw = (char*)util_alloc_strip_copy((const char*)ecl_kw_iget_ptr(keywords , params_index));
|
|
char * unit = (char*)util_alloc_strip_copy((const char*)ecl_kw_iget_ptr(units , params_index));
|
|
|
|
ecl_smspec_var_type var_type;
|
|
if (nums != NULL) num = ecl_kw_iget_int(nums , params_index);
|
|
var_type = ecl::smspec_node::valid_type(kw, well, num);
|
|
if (var_type == ECL_SMSPEC_INVALID_VAR) {
|
|
free( kw );
|
|
free( well );
|
|
free( unit );
|
|
continue;
|
|
}
|
|
|
|
if (ecl_smspec_lgr_var_type( var_type )) {
|
|
int lgr_i = ecl_kw_iget_int( numlx , params_index );
|
|
int lgr_j = ecl_kw_iget_int( numly , params_index );
|
|
int lgr_k = ecl_kw_iget_int( numlz , params_index );
|
|
char * lgr_name = (char*)util_alloc_strip_copy( (const char*)ecl_kw_iget_ptr( lgrs , params_index ));
|
|
|
|
ecl_smspec_insert_node(ecl_smspec, std::unique_ptr<ecl::smspec_node>( new ecl::smspec_node(params_index,
|
|
kw,
|
|
well,
|
|
unit,
|
|
lgr_name,
|
|
lgr_i, lgr_j, lgr_k,
|
|
default_value,
|
|
ecl_smspec->key_join_string.c_str())));
|
|
free(lgr_name);
|
|
} else
|
|
ecl_smspec_insert_node(ecl_smspec, std::unique_ptr<ecl::smspec_node>( new ecl::smspec_node(params_index,
|
|
kw,
|
|
well,
|
|
num,
|
|
unit,
|
|
ecl_smspec->grid_dims,
|
|
default_value,
|
|
ecl_smspec->key_join_string.c_str())));
|
|
|
|
free( kw );
|
|
free( well );
|
|
free( unit );
|
|
}
|
|
}
|
|
|
|
char * header_str = util_alloc_realpath( header_file );
|
|
ecl_smspec->header_file = header_str;
|
|
free(header_str);
|
|
if (include_restart)
|
|
ecl_smspec_load_restart( ecl_smspec , header );
|
|
|
|
ecl_file_close( header );
|
|
|
|
return true;
|
|
} else
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
ecl_smspec_type * ecl_smspec_fread_alloc(const char *header_file, const char * key_join_string , bool include_restart) {
|
|
ecl_smspec_type *ecl_smspec;
|
|
|
|
{
|
|
char *path;
|
|
util_alloc_file_components(header_file , &path , NULL , NULL);
|
|
ecl_smspec = ecl_smspec_alloc_empty(false , key_join_string);
|
|
free(path);
|
|
}
|
|
|
|
if (ecl_smspec_fread_header(ecl_smspec , header_file , include_restart)) {
|
|
|
|
const ecl::smspec_node * time_node = ecl_smspec_get_var_node(ecl_smspec->misc_var_index, "TIME");
|
|
if (time_node) {
|
|
const char * time_unit = time_node->get_unit();
|
|
ecl_smspec->time_index = time_node->get_params_index();
|
|
|
|
if (util_string_equal( time_unit , "DAYS"))
|
|
ecl_smspec->time_seconds = 3600 * 24;
|
|
else if (util_string_equal( time_unit , "HOURS"))
|
|
ecl_smspec->time_seconds = 3600;
|
|
else
|
|
util_abort("%s: time_unit:%s not recognized \n",__func__ , time_unit);
|
|
|
|
}
|
|
|
|
const ecl::smspec_node * day_node = ecl_smspec_get_var_node(ecl_smspec->misc_var_index, "DAY");
|
|
if (day_node != NULL) {
|
|
ecl_smspec->day_index = smspec_node_get_params_index( day_node );
|
|
|
|
const ecl::smspec_node * month_node = ecl_smspec_get_var_node(ecl_smspec->misc_var_index, "MONTH");
|
|
if (month_node != NULL) {
|
|
ecl_smspec->month_index = smspec_node_get_params_index( month_node );
|
|
}
|
|
|
|
const ecl::smspec_node * year_node = ecl_smspec_get_var_node(ecl_smspec->misc_var_index, "YEAR");
|
|
if (year_node != NULL) {
|
|
ecl_smspec->year_index = smspec_node_get_params_index( year_node );
|
|
}
|
|
}
|
|
|
|
if ((ecl_smspec->time_index == -1) && ( ecl_smspec->day_index == -1)) {
|
|
/* Unusable configuration.
|
|
|
|
Seems the ECLIPSE file can also have time specified with
|
|
'YEARS' as basic time unit; that mode is not supported.
|
|
*/
|
|
|
|
ecl_smspec_free( ecl_smspec );
|
|
return NULL;
|
|
}
|
|
return ecl_smspec;
|
|
} else {
|
|
/** Failed to load from disk. */
|
|
ecl_smspec_free( ecl_smspec );
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
|
|
int ecl_smspec_get_num_groups(const ecl_smspec_type * ecl_smspec) {
|
|
return ecl_smspec->group_var_index.size();
|
|
}
|
|
|
|
|
|
/*char ** ecl_smspec_alloc_group_names(const ecl_smspec_type * ecl_smspec) {
|
|
return hash_alloc_keylist(ecl_smspec->group_var_index);
|
|
}*/
|
|
|
|
int ecl_smspec_get_num_regions(const ecl_smspec_type * ecl_smspec) {
|
|
return ecl_smspec->num_regions;
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/*
|
|
For each type of summary data (according to the types in
|
|
ecl_smcspec_var_type there are a set accessor functions:
|
|
|
|
xx_get_xx: This function will take the apropriate input, and
|
|
return a double value. The function will fail with util_abort()
|
|
if the ecl_smspec object can not recognize the input. THis
|
|
function is not here.
|
|
|
|
xxx_has_xx: Ths will return true / false depending on whether the
|
|
ecl_smspec object the variable we ask for.
|
|
|
|
xxx_get_xxx_index: This function will rerturn an (internal)
|
|
integer index of where the variable in question is stored, this
|
|
index can then be subsequently used for faster lookup. If the
|
|
variable can not be found, the function will return -1.
|
|
|
|
In general the index function is the real function, the others are
|
|
only wrappers around this. In addition there are specialized
|
|
functions, like get_well_names() and so on.
|
|
*/
|
|
|
|
|
|
/*****************************************************************/
|
|
|
|
#define NODE_RETURN_INDEX(node_ptr) \
|
|
if (!node_ptr) \
|
|
return -1; \
|
|
else \
|
|
return smspec_node_get_params_index( node_ptr );
|
|
|
|
|
|
#define NODE_RETURN_EXISTS(node_ptr) \
|
|
if (!node_ptr) \
|
|
return false; \
|
|
else \
|
|
return true;
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* Well variables */
|
|
|
|
const ecl::smspec_node& ecl_smspec_get_well_var_node( const ecl_smspec_type * smspec , const char * well , const char * var) {
|
|
const auto node_ptr = ecl_smspec_get_str_key_var_node(smspec->well_var_index, well, var);
|
|
if (!node_ptr)
|
|
throw std::out_of_range("The well: " + std::string(well) + " variable: " + std::string(var) + " combination does not exist.");
|
|
|
|
return *node_ptr;
|
|
}
|
|
|
|
|
|
int ecl_smspec_get_well_var_params_index(const ecl_smspec_type * ecl_smspec , const char * well , const char *var) {
|
|
const auto node_ptr = ecl_smspec_get_str_key_var_node(ecl_smspec->well_var_index, well, var);
|
|
NODE_RETURN_INDEX(node_ptr);
|
|
}
|
|
|
|
|
|
bool ecl_smspec_has_well_var(const ecl_smspec_type * ecl_smspec , const char * well , const char *var) {
|
|
const auto node_ptr = ecl_smspec_get_str_key_var_node(ecl_smspec->well_var_index, well, var);
|
|
NODE_RETURN_EXISTS(node_ptr);
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************/
|
|
/* Group variables */
|
|
|
|
const ecl::smspec_node& ecl_smspec_get_group_var_node( const ecl_smspec_type * smspec , const char * group , const char * var) {
|
|
const auto node_ptr = ecl_smspec_get_str_key_var_node(smspec->group_var_index, group, var);
|
|
if (!node_ptr)
|
|
throw std::out_of_range("The group: " + std::string(group) + " variable: " + std::string(var) + " combination does not exist.");
|
|
|
|
return *node_ptr;
|
|
}
|
|
|
|
|
|
int ecl_smspec_get_group_var_params_index(const ecl_smspec_type * ecl_smspec , const char * group , const char *var) {
|
|
const auto node_ptr = ecl_smspec_get_str_key_var_node(ecl_smspec->group_var_index, group, var);
|
|
NODE_RETURN_INDEX(node_ptr);
|
|
}
|
|
|
|
|
|
bool ecl_smspec_has_group_var(const ecl_smspec_type * ecl_smspec , const char * group , const char *var) {
|
|
const auto node_ptr = ecl_smspec_get_str_key_var_node(ecl_smspec->group_var_index, group, var);
|
|
NODE_RETURN_EXISTS(node_ptr);
|
|
}
|
|
|
|
|
|
/*****************************************************************/
|
|
/* Field variables */
|
|
|
|
const ecl::smspec_node& ecl_smspec_get_field_var_node(const ecl_smspec_type * ecl_smspec , const char *var) {
|
|
const auto node_ptr = ecl_smspec_get_var_node( ecl_smspec->field_var_index, var);
|
|
if (!node_ptr)
|
|
throw std::out_of_range("The field variable: " + std::string(var) + " does not exist.");
|
|
|
|
return *node_ptr;
|
|
}
|
|
|
|
|
|
int ecl_smspec_get_field_var_params_index(const ecl_smspec_type * ecl_smspec , const char *var) {
|
|
const auto node_ptr = ecl_smspec_get_var_node(ecl_smspec->field_var_index, var);
|
|
NODE_RETURN_INDEX(node_ptr);
|
|
}
|
|
|
|
|
|
|
|
|
|
bool ecl_smspec_has_field_var(const ecl_smspec_type * ecl_smspec , const char *var) {
|
|
const auto node_ptr = ecl_smspec_get_var_node(ecl_smspec->field_var_index, var);
|
|
NODE_RETURN_EXISTS(node_ptr);
|
|
}
|
|
|
|
/*****************************************************************/
|
|
/* Block variables */
|
|
|
|
/**
|
|
Observe that block_nr is represented as char literal,
|
|
i.e. "2345". This is because it will be used as a hash key.
|
|
|
|
This is the final low level function which actually consults the
|
|
hash tables.
|
|
*/
|
|
|
|
|
|
const ecl::smspec_node& ecl_smspec_get_block_var_node(const ecl_smspec_type * ecl_smspec , const char * block_var , int block_nr) {
|
|
const auto node_ptr = ecl_smspec_get_int_key_var_node(ecl_smspec->block_var_index, block_nr, block_var);
|
|
if (!node_ptr)
|
|
throw std::out_of_range("No such block variable");
|
|
|
|
return *node_ptr;
|
|
}
|
|
|
|
|
|
const ecl::smspec_node& ecl_smspec_get_block_var_node_ijk(const ecl_smspec_type * ecl_smspec , const char * block_var , int i , int j , int k) {
|
|
return ecl_smspec_get_block_var_node( ecl_smspec , block_var , ecl_smspec_get_global_grid_index( ecl_smspec , i,j,k) );
|
|
}
|
|
|
|
|
|
bool ecl_smspec_has_block_var(const ecl_smspec_type * ecl_smspec , const char * block_var , int block_nr) {
|
|
const auto node_ptr = ecl_smspec_get_int_key_var_node(ecl_smspec->block_var_index, block_nr, block_var);
|
|
NODE_RETURN_EXISTS(node_ptr);
|
|
}
|
|
|
|
|
|
bool ecl_smspec_has_block_var_ijk(const ecl_smspec_type * ecl_smspec , const char * block_var , int i , int j , int k) {
|
|
return ecl_smspec_has_block_var(ecl_smspec, block_var, ecl_smspec_get_global_grid_index(ecl_smspec, i, j, k));
|
|
}
|
|
|
|
|
|
int ecl_smspec_get_block_var_params_index(const ecl_smspec_type * ecl_smspec , const char * block_var , int block_nr) {
|
|
const auto node_ptr = ecl_smspec_get_int_key_var_node(ecl_smspec->block_var_index, block_nr, block_var);
|
|
NODE_RETURN_INDEX(node_ptr);
|
|
}
|
|
|
|
|
|
int ecl_smspec_get_block_var_params_index_ijk(const ecl_smspec_type * ecl_smspec , const char * block_var , int i , int j , int k) {
|
|
return ecl_smspec_get_block_var_params_index(ecl_smspec, block_var, ecl_smspec_get_global_grid_index(ecl_smspec, i, j, k));
|
|
}
|
|
|
|
|
|
/*****************************************************************/
|
|
/* Region variables */
|
|
/**
|
|
region_nr: [1...num_regions] (NOT C-based indexing)
|
|
*/
|
|
|
|
|
|
|
|
const ecl::smspec_node& ecl_smspec_get_region_var_node(const ecl_smspec_type * ecl_smspec , const char *region_var , int region_nr) {
|
|
const auto node_ptr = ecl_smspec_get_int_key_var_node(ecl_smspec->region_var_index, region_nr, region_var);
|
|
if (!node_ptr)
|
|
throw std::out_of_range("No such block variable");
|
|
|
|
return *node_ptr;
|
|
}
|
|
|
|
|
|
bool ecl_smspec_has_region_var(const ecl_smspec_type * ecl_smspec , const char *region_var, int region_nr) {
|
|
const auto node_ptr = ecl_smspec_get_int_key_var_node(ecl_smspec->region_var_index, region_nr, region_var);
|
|
NODE_RETURN_EXISTS(node_ptr);
|
|
}
|
|
|
|
|
|
int ecl_smspec_get_region_var_params_index(const ecl_smspec_type * ecl_smspec , const char *region_var, int region_nr) {
|
|
const auto node_ptr = ecl_smspec_get_int_key_var_node(ecl_smspec->region_var_index, region_nr, region_var);
|
|
NODE_RETURN_INDEX(node_ptr);
|
|
}
|
|
|
|
/*****************************************************************/
|
|
/* Misc variables */
|
|
|
|
const ecl::smspec_node& ecl_smspec_get_misc_var_node(const ecl_smspec_type * ecl_smspec , const char *var) {
|
|
const auto node_ptr = ecl_smspec_get_var_node(ecl_smspec->misc_var_index, var);
|
|
if (!node_ptr)
|
|
throw std::out_of_range("No such misc variable");
|
|
|
|
return *node_ptr;
|
|
}
|
|
|
|
|
|
bool ecl_smspec_has_misc_var(const ecl_smspec_type * ecl_smspec , const char *var) {
|
|
const auto node_ptr = ecl_smspec_get_var_node(ecl_smspec->misc_var_index , var );
|
|
NODE_RETURN_EXISTS(node_ptr);
|
|
}
|
|
|
|
int ecl_smspec_get_misc_var_params_index(const ecl_smspec_type * ecl_smspec , const char *var) {
|
|
const auto node_ptr = ecl_smspec_get_var_node(ecl_smspec->misc_var_index , var );
|
|
NODE_RETURN_INDEX(node_ptr);
|
|
}
|
|
|
|
|
|
/*****************************************************************/
|
|
/* Well completion - not fully implemented ?? */
|
|
|
|
|
|
const ecl::smspec_node * ecl_smspec_get_well_completion_var_node__(const ecl_smspec_type * ecl_smspec , const char * well , const char *var, int cell_nr) {
|
|
const auto well_iter = ecl_smspec->well_completion_var_index.find(well);
|
|
if (well_iter == ecl_smspec->well_completion_var_index.end())
|
|
return nullptr;
|
|
|
|
const auto& num_map = well_iter->second;
|
|
return ecl_smspec_get_int_key_var_node( num_map, cell_nr, var );
|
|
}
|
|
|
|
const ecl::smspec_node& ecl_smspec_get_well_completion_var_node(const ecl_smspec_type * ecl_smspec , const char * well , const char *var, int cell_nr) {
|
|
const auto node_ptr = ecl_smspec_get_well_completion_var_node__(ecl_smspec, well, var, cell_nr);
|
|
if (!node_ptr)
|
|
throw std::out_of_range("No such well/var/completion");
|
|
|
|
return *node_ptr;
|
|
}
|
|
|
|
|
|
bool ecl_smspec_has_well_completion_var(const ecl_smspec_type * ecl_smspec , const char * well , const char *var, int cell_nr) {
|
|
const auto node_ptr = ecl_smspec_get_well_completion_var_node__( ecl_smspec , well , var , cell_nr );
|
|
NODE_RETURN_EXISTS( node_ptr );
|
|
}
|
|
|
|
|
|
int ecl_smspec_get_well_completion_var_params_index(const ecl_smspec_type * ecl_smspec , const char * well , const char *var, int cell_nr) {
|
|
const auto node_ptr = ecl_smspec_get_well_completion_var_node__( ecl_smspec , well , var , cell_nr );
|
|
NODE_RETURN_INDEX( node_ptr );
|
|
}
|
|
|
|
|
|
/*****************************************************************/
|
|
/* General variables ... */
|
|
|
|
|
|
/* There is a quite wide range of error which are just returned as
|
|
"Not found" (i.e. -1). */
|
|
/* Completions not supported yet. */
|
|
|
|
|
|
|
|
const ecl::smspec_node& ecl_smspec_get_general_var_node( const ecl_smspec_type * smspec , const char * lookup_kw ) {
|
|
const auto node_ptr = ecl_smspec_get_var_node(smspec->gen_var_index, lookup_kw);
|
|
if (!node_ptr)
|
|
throw std::out_of_range("No such variable: " + std::string(lookup_kw));
|
|
|
|
return *node_ptr;
|
|
}
|
|
|
|
|
|
int ecl_smspec_get_general_var_params_index(const ecl_smspec_type * ecl_smspec , const char * lookup_kw) {
|
|
const auto node_ptr = ecl_smspec_get_var_node(ecl_smspec->gen_var_index , lookup_kw );
|
|
NODE_RETURN_INDEX( node_ptr );
|
|
}
|
|
|
|
|
|
bool ecl_smspec_has_general_var(const ecl_smspec_type * ecl_smspec , const char * lookup_kw) {
|
|
const auto node_ptr = ecl_smspec_get_var_node(ecl_smspec->gen_var_index , lookup_kw );
|
|
NODE_RETURN_EXISTS( node_ptr );
|
|
}
|
|
|
|
|
|
/** DIES if the lookup_kw is not present. */
|
|
const char * ecl_smspec_get_general_var_unit( const ecl_smspec_type * ecl_smspec , const char * lookup_kw) {
|
|
const auto smspec_node = ecl_smspec_get_general_var_node(ecl_smspec, lookup_kw);
|
|
return smspec_node_get_unit( &smspec_node );
|
|
}
|
|
|
|
|
|
/*****************************************************************/
|
|
/*
|
|
Pure indexed lookup - these functions can be used after one of the
|
|
ecl_smspec_get_xxx_index() functions has been used first.
|
|
*/
|
|
|
|
//const char * ecl_smspec_iget_unit( const ecl_smspec_type * smspec , int node_index ) {
|
|
// const ecl::smspec_node * smspec_node = ecl_smspec_iget_node( smspec , node_index );
|
|
// return smspec_node_get_unit( smspec_node );
|
|
//}
|
|
//
|
|
//int ecl_smspec_iget_num( const ecl_smspec_type * smspec , int node_index ) {
|
|
// const ecl::smspec_node * smspec_node = ecl_smspec_iget_node( smspec , node_index );
|
|
// return smspec_node_get_num( smspec_node );
|
|
//}
|
|
//
|
|
//const char * ecl_smspec_iget_wgname( const ecl_smspec_type * smspec , int node_index ) {
|
|
// const ecl::smspec_node * smspec_node = ecl_smspec_iget_node( smspec , node_index );
|
|
// return smspec_node_get_wgname( smspec_node );
|
|
//}
|
|
//
|
|
//const char * ecl_smspec_iget_keyword( const ecl_smspec_type * smspec , int index ) {
|
|
// const ecl::smspec_node * smspec_node = ecl_smspec_iget_node( smspec , index );
|
|
// return smspec_node_get_keyword( smspec_node );
|
|
//}
|
|
|
|
|
|
/*****************************************************************/
|
|
|
|
int ecl_smspec_get_time_seconds( const ecl_smspec_type * ecl_smspec ) {
|
|
return ecl_smspec->time_seconds;
|
|
}
|
|
|
|
int ecl_smspec_get_time_index( const ecl_smspec_type * ecl_smspec ) {
|
|
return ecl_smspec->time_index;
|
|
}
|
|
|
|
time_t ecl_smspec_get_start_time(const ecl_smspec_type * ecl_smspec) {
|
|
return ecl_smspec->sim_start_time;
|
|
}
|
|
|
|
bool ecl_smspec_get_formatted( const ecl_smspec_type * ecl_smspec) {
|
|
return ecl_smspec->formatted;
|
|
}
|
|
|
|
const char * ecl_smspec_get_header_file( const ecl_smspec_type * ecl_smspec ) {
|
|
return ecl_smspec->header_file.c_str();
|
|
}
|
|
|
|
|
|
int ecl_smspec_get_restart_step(const ecl_smspec_type * ecl_smspec) {
|
|
return ecl_smspec->restart_step;
|
|
}
|
|
|
|
int ecl_smspec_get_first_step(const ecl_smspec_type * ecl_smspec) {
|
|
if (ecl_smspec->restart_step > 0)
|
|
return ecl_smspec->restart_step + 1;
|
|
else
|
|
return 1;
|
|
}
|
|
|
|
|
|
const char * ecl_smspec_get_restart_case( const ecl_smspec_type * ecl_smspec) {
|
|
if (ecl_smspec->restart_case.size() > 0)
|
|
return ecl_smspec->restart_case.c_str();
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
const std::vector<float>& ecl_smspec_get_params_default( const ecl_smspec_type * ecl_smspec ) {
|
|
return ecl_smspec->params_default;
|
|
}
|
|
|
|
void ecl_smspec_free(ecl_smspec_type *ecl_smspec) {
|
|
delete ecl_smspec;
|
|
}
|
|
|
|
|
|
void ecl_smspec_free__(void * __ecl_smspec) {
|
|
ecl_smspec_type * ecl_smspec = ecl_smspec_safe_cast( __ecl_smspec);
|
|
ecl_smspec_free( ecl_smspec );
|
|
}
|
|
|
|
|
|
int ecl_smspec_get_date_day_index( const ecl_smspec_type * smspec ) {
|
|
return smspec->day_index;
|
|
}
|
|
|
|
int ecl_smspec_get_date_month_index( const ecl_smspec_type * smspec ) {
|
|
return smspec->month_index;
|
|
}
|
|
|
|
int ecl_smspec_get_date_year_index( const ecl_smspec_type * smspec ) {
|
|
return smspec->year_index;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
This function checks whether an input general key (i.e. FWPR or
|
|
GGPT:NORTH) represents an accumulated total. If the variable is not
|
|
internalized the function will fail hard.
|
|
*/
|
|
|
|
|
|
bool ecl_smspec_general_is_total( const ecl_smspec_type * smspec , const char * gen_key) {
|
|
const ecl::smspec_node& smspec_node = ecl_smspec_get_general_var_node(smspec, gen_key);
|
|
return smspec_node_is_total( &smspec_node );
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************/
|
|
|
|
|
|
/**
|
|
Fills a stringlist instance with all the gen_key string matching
|
|
the supplied pattern. I.e.
|
|
|
|
ecl_smspec_alloc_matching_general_var_list( smspec , "WGOR:*");
|
|
|
|
will give a list of WGOR for ALL the wells. The function is
|
|
unfortunately not as useful as one might think because ECLIPSE is
|
|
quite stupid; it will for instance happily give you the WOPR for a
|
|
water injector or WWIR for an oil producer.
|
|
|
|
The function can be called several times with different patterns,
|
|
the stringlist is not cleared on startup; the keys in the list are
|
|
unique - keys are not added multiple times. If pattern == NULL all
|
|
keys will match.
|
|
*/
|
|
|
|
|
|
void ecl_smspec_select_matching_general_var_list( const ecl_smspec_type * smspec , const char * pattern , stringlist_type * keys) {
|
|
std::set<std::string> ex_keys;
|
|
for (int i=0; i < stringlist_get_size( keys ); i++)
|
|
ex_keys.insert( stringlist_iget(keys, i));
|
|
|
|
{
|
|
for (const auto& pair : smspec->gen_var_index) {
|
|
const char * key = pair.first.c_str();
|
|
|
|
/*
|
|
The TIME is typically special cased by output and will not
|
|
match the 'all keys' wildcard.
|
|
*/
|
|
if (util_string_equal( key , "TIME")) {
|
|
if ((pattern == NULL) || (util_string_equal( pattern , "*")))
|
|
continue;
|
|
}
|
|
|
|
|
|
if ((pattern == NULL) || (util_fnmatch( pattern , key ) == 0)) {
|
|
if (ex_keys.find(key) == ex_keys.end())
|
|
stringlist_append_copy( keys , key );
|
|
}
|
|
}
|
|
}
|
|
|
|
stringlist_sort( keys , (string_cmp_ftype *) util_strcmp_int );
|
|
}
|
|
|
|
|
|
/**
|
|
Allocates a new stringlist and initializes it with the
|
|
ecl_smspec_select_matching_general_var_list() function.
|
|
*/
|
|
|
|
stringlist_type * ecl_smspec_alloc_matching_general_var_list(const ecl_smspec_type * smspec , const char * pattern) {
|
|
stringlist_type * keys = stringlist_alloc_new();
|
|
ecl_smspec_select_matching_general_var_list( smspec , pattern , keys );
|
|
return keys;
|
|
}
|
|
|
|
|
|
|
|
const char * ecl_smspec_get_join_string( const ecl_smspec_type * smspec) {
|
|
return smspec->key_join_string.c_str();
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
Returns a stringlist instance with all the (valid) well names. It
|
|
is the responsability of the calling scope to free the stringlist
|
|
with stringlist_free();
|
|
|
|
|
|
If @pattern is different from NULL only wells which 'match' the
|
|
pattern is included; if @pattern == NULL all wells are
|
|
included. The match is done with function fnmatch() -
|
|
i.e. standard shell wildcards.
|
|
*/
|
|
|
|
static stringlist_type * ecl_smspec_alloc_map_list( const std::map<std::string, node_map>& mp , const char * pattern) {
|
|
stringlist_type * map_list = stringlist_alloc_new( );
|
|
|
|
for (const auto& pair : mp) {
|
|
const char * map_name = pair.first.c_str();
|
|
|
|
if (pattern == NULL)
|
|
stringlist_append_copy( map_list , map_name );
|
|
else if (util_fnmatch( pattern , map_name) == 0)
|
|
stringlist_append_copy( map_list , map_name );
|
|
|
|
}
|
|
stringlist_sort( map_list , (string_cmp_ftype *) util_strcmp_int );
|
|
return map_list;
|
|
}
|
|
|
|
stringlist_type * ecl_smspec_alloc_well_list( const ecl_smspec_type * smspec , const char * pattern) {
|
|
return ecl_smspec_alloc_map_list( smspec->well_var_index, pattern );
|
|
}
|
|
|
|
/**
|
|
Returns a stringlist instance with all the (valid) group names. It
|
|
is the responsability of the calling scope to free the stringlist
|
|
with stringlist_free();
|
|
*/
|
|
|
|
stringlist_type * ecl_smspec_alloc_group_list( const ecl_smspec_type * smspec , const char * pattern) {
|
|
return ecl_smspec_alloc_map_list( smspec->group_var_index, pattern );
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
Returns a stringlist instance with all the well variables. It is
|
|
the responsability of the calling scope to free the stringlist
|
|
with stringlist_free();
|
|
*/
|
|
|
|
stringlist_type * ecl_smspec_alloc_well_var_list( const ecl_smspec_type * smspec ) {
|
|
stringlist_type * stringlist = stringlist_alloc_new();
|
|
for (const auto& pair : smspec->well_var_index)
|
|
stringlist_append_copy(stringlist, pair.first.c_str());
|
|
|
|
return stringlist;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const int * ecl_smspec_get_grid_dims( const ecl_smspec_type * smspec ) {
|
|
return smspec->grid_dims;
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************/
|
|
|
|
char * ecl_smspec_alloc_well_key( const ecl_smspec_type * smspec , const char * keyword , const char * wgname) {
|
|
return smspec_alloc_well_key( smspec->key_join_string.c_str() , keyword , wgname );
|
|
}
|
|
|
|
|
|
/*void ecl_smspec_sort( ecl_smspec_type * smspec ) {
|
|
std::sort(smspec->smspec_nodes.begin(), smspec->smspec_nodes.end(), smspec_node_lt);
|
|
|
|
for (int i=0; i < static_cast<int>(smspec->smspec_nodes.size()); i++) {
|
|
ecl::smspec_node& node = *smspec->smspec_nodes[i].get();
|
|
smspec_node_set_params_index( &node , i );
|
|
}
|
|
}
|
|
*/
|
|
|
|
ert_ecl_unit_enum ecl_smspec_get_unit_system(const ecl_smspec_type * smspec) {
|
|
return smspec->unit_system;
|
|
}
|