/* Copyright (C) 2012 Statoil ASA, Norway. The file 'sum_write' 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 for more details. */ #include #include #include #include #include #include #include /* The ECLIPSE summary data is stored in two different files: - A header file which contains metadata of the stored data; this header file has extension SMSPEC. - The actual data is stored as blocks of data where each block contains all the variables for one timestep (called MINISTEP in ECLIPSE lingo). The data can be stored in a unified summary file with all the data in one file, or alternatively several separate files - one for each report step. In the case of a unified file the extension is UNSMRY whereas the multiple summary files haver extension Snnnn. The format is quite primitive, and very much influenced by Fortran77 arrays which are fixed-size-large-enough thank you. The header file is (mainly) based on three different ecl_kw vectors which together completely specify the variables. The example below consists of the following: - Variables: Field Oil Production Total (FOPT), Group Oil Production Rate (GOPR) in group P-North, the Block Pressure (BPR) in cell 5423 and the Well Gas Production Rate (WGPR) in the well GasW. - A total of two report steps (dates), where the first consist of two timesteps and the latter of three timesteps. CASE.SMSPEC CASE.S0001 CASE.S0002 ------------------------------ -------------------------- --------------------------------------- | KEYWORD | WGNAMES | NUMS | | MINISTEP 1| MINISTEP 2| | MINISTEP 3| MINISTEP 4| MINISTEP 5| +----------------------------+ +------------------------+ +-------------------------------------+ | TIME | | | --> | | | --> | | | | | FOPT | | | --> | | | --> | | | | | GOPR | P-North | | --> | | | --> | | | | | BPR | | 5423 | --> | | | --> | | | | | WGPR | GasW | | --> | | | --> | | | | +----------------------------+ +------------------------+ +-------------------------------------+ |<--- -- CASE.UNSMRY -- --->| Observe the following points: 1. The variable name supplied in the KEYWORD array is significant; ECLIPSE will determine the type of variable by looking at the first letter of the keyword: "W" for well variables, "F" for field variables and so on. This is further documented in the ecl_smspec_identify_var_type() function. 2. The KEYWORD vector in the SMSPEC file is relevant for all the entries, whereas the WGNAMES and NUMS entries should be considered as further qualifiers which apply selectively depending on the variable type - for instance both the WGNAMES and NUMS vectors are ignored in the case of field variable like FOPT. 3. The actual data is organised in blocks of data, one continous block for each (simulator) timestep. These blocks are collected in report steps; when stored the data can be either lumped together in a unified file, or split in many different files. 4. Each block of data is exactly as large as the header. This implies that: - The number of elements in the summary file must be known up front; it is not possible to add/remove variables during simulation. - All timesteps are equally large. Due to this fixed size nature an important element in the ecl_sum implementation is "the index of a variable" - i.e. the "FOPT" variable in the example above will be found at index 1 of every datablock. 5. The header contains a "TIME" variable; to some extent this looks like just any other variable, but it must be present in the SMSPEC header. In the example above the first element in every data block is the current time (in days) for that datablock; if labunits is used the stored value is the elapsed time in hours. 6. In the ecl_sum library the concept of a 'gen_key' is used quite extensively. The gen_key is a string combination of the KEYWORD, WGNAMES and NUMS values from the smspec header file. Only the relevant elements are combined when forming a gen_key; i.e. for the example above we will have: ------------------------------ | KEYWORD | WGNAMES | NUMS | General key +----------------------------+ ------------ | TIME | | | --> TIME | FOPT | | | --> FOPT | GOPR | P-North | | --> GOPR:P-North | BPR | | 5423 | --> BPR:5423 , BPR:i,j,k | WGPR | GasW | | --> WGPR:GasW +----------------------------+ Note the following: o The keys are joined with a string typically called 'key_join_string' in the code; in the example above this is a ":" which is quite close to a standard. The gen_key values are never inverted, so the join string can be arbitrary. o For the block quantities, like the BPR in the example above the NUMS value is the cell number in (i,j,k) ordering: NUMS = i + (j - 1)*nx + (k-1)*nx*ny The block related properties are also available as the general key BPR:i,j,k where the coordinates (i,j,k) are offset 1. Due to the fixed size nature of the summary file format described in point 4 above, the library is somewhat cumbersome and unflexible to use. Very briefly the usage pattern will be like: 1. Create a ecl_sum instance and add variables to it with the ecl_sum_add_var() function. 2. Use your simulator to step forward in time, and add timesteps with ecl_sum_add_tstep(). Now - the important thing is that steps 1 and 2 two can not be interchanged, that will lead to crash and burn. */ int main( int argc , char ** argv) { time_t start_time = util_make_date_utc( 1,1,2010 ); int nx = 10; int ny = 10; int nz = 10; /* We create a new summary case which will be used for writing. The arguments are: 1: The case - this an ECLIPSE basename, with an optional leading path component. Can later be modified with ecl_sum_set_case(). 2: Should formatted files be used? Can be modified with ecl_sum_set_fmt_output(). 3: Character(s) to separate the items from the smspec header generate a gen_key value. The value ":" is close to a standard. 4: Should the data be output as a unified file, or as a collection of files - one for each report step. Can be modified with ecl_sum_set_unified_output(). 5: The real-world start time of this simulation, as a unix time_t value. 6-8: Grid dimensions. */ bool time_in_days = true; ecl_sum_type * ecl_sum = ecl_sum_alloc_writer( "/tmp/CASE" , false , true , ":" , start_time , time_in_days , nx , ny , nz ); /* We add the variables we wish to measure. Due to the rather inflexible nature of the format we must add all the variables we are interested in before we start adding data. The arguments to this function are: 1. self / this 2. The KEYWORD value for the variable we are considering; the function ecl_smspec_identify_var_type() will be called with this string - i.e. you must follow the ECLIPSE rules (see table 3.4 in the ECLIPSE file format reference manual). 3. The WGNAME value for this variable. WGNAME is the well or group name; if the variable in question is neither a well nor a group variable you can just send in NULL. 4. The NUMS value for this variable. 5. The unit for this variable. 6. A defualt value for this variable. */ ecl_sum_add_var( ecl_sum , "FOPT" , NULL , 0 , "Barrels" , 99.0 ); ecl_sum_add_var( ecl_sum , "BPR" , NULL , 567 , "BARS" , 0.0 ); ecl_sum_add_var( ecl_sum , "WWCT" , "OP-1" , 0 , "(1)" , 0.0 ); ecl_sum_add_var( ecl_sum , "WOPR" , "OP-1" , 0 , "Barrels" , 0.0 ); /* The return value from the ecl_sum_add_var() function is an smspec_node_type instance (implemented in file smspec_node.c); which is essentially a struct holding header information about this varible. There are several options on how to handle the return value from the ecl_sum_add_var() function; which affect how you can add data at a later stage: 1. You can just ignore it - that is not very clean; in this case you must make an assumption of gen_key format at a later stage. 2. You can use the smspec_node_get_gen_key1() or smspec_node_get_params_index() and hold on to the gen_key or params_index values. You will need these later. 3. You can hold on to the complete smspec_node instance, and then later on call one of the smspec_node_get_params_index() or smspec_node_get_gen_key1() functions. */ smspec_node_type * wwct_wellx = ecl_sum_add_var( ecl_sum , "WWCT" , NULL , 0 , "(1)" , 0.0); smspec_node_type * wopr_wellx = ecl_sum_add_var( ecl_sum , "WOPR" , NULL , 0 , "Barrels" , 0.0); { int num_dates = 10; int num_step = 10; double sim_days = 0; int report_step; int step; for (report_step = 0; report_step < num_dates; report_step++) { for (step = 0; step < num_step; step++) { /* Simulate .... */ sim_days += 10; { /* Here we add a new timestep. The report steps are not added explicitly; the ecl_sum layer will just see if this is a new report step. Observe that report_step == 1 is the first permissible report step. The new time step is essentially a block of data with one element for each variable, and now we must set the various elements in the block. There are different ways to do this - depending on which metadata information we are holding on to from the ecl_sum_add_var() calls. Observe that one element for the number of simulation days has already been inserted in the timestep. The elements which are not set explicitly will have the default value given in the ecl_sum_add_var() call. */ ecl_sum_tstep_type * tstep = ecl_sum_add_tstep( ecl_sum , report_step + 1 , sim_days ); /* We can just set a value by it's index using the ecl_sum_tstep_iset() function. The index value should come from the smspec_get_params_index() function. */ ecl_sum_tstep_iset( tstep , 3 , 0.98); /* We can use the gen_key value to set the numerical values. The gen_key value should could from the smpec_get_gen_key1() function: */ ecl_sum_tstep_set_from_key( tstep , "WWCT:OP-1" , sim_days / 10); if (report_step >= 5) { /* We can use the smspec_node value from the ecl_sum_add_var() function directly: */ ecl_sum_tstep_set_from_node( tstep , wwct_wellx , sim_days ); ecl_sum_tstep_set_from_node( tstep, wopr_wellx, sim_days * 100); } } } } } ecl_sum_fwrite( ecl_sum ); ecl_sum_free( ecl_sum ); }