-
Notifications
You must be signed in to change notification settings - Fork 95
/
rd_grid.cpp
7109 lines (6001 loc) · 251 KB
/
rd_grid.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#include <cstdint>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdbool.h>
#include <math.h>
#include <vector>
#include <unordered_map>
#include <string>
#include <ert/util/util.h>
#include <ert/util/double_vector.hpp>
#include <ert/util/int_vector.hpp>
#include <ert/util/vector.hpp>
#include <ert/util/stringlist.hpp>
#include <ert/geometry/geo_util.hpp>
#include <ert/geometry/geo_polygon.hpp>
#include <resdata/rd_util.hpp>
#include <resdata/rd_type.hpp>
#include <resdata/rd_kw.hpp>
#include <resdata/rd_file.hpp>
#include <resdata/rd_kw_magic.hpp>
#include <resdata/rd_endian_flip.hpp>
#include <resdata/rd_coarse_cell.hpp>
#include <resdata/rd_grid.hpp>
#include <resdata/grid_dims.hpp>
#include <resdata/nnc_info.hpp>
/**
this function implements functionality to load eclispe grid files,
both .egrid and .grid files - in a transparent fashion.
observe the following convention:
global_index: [0 , nx*ny*nz)
active_index: [0 , nactive)
about indexing
--------------
there are three different ways to index/access a cell:
1. by ijk
2. by global index, [0 , nx*ny*nz)
3. by active index, [0 , nactive)
most of the query functions can take input in several of the
ways. the expected arguments are indicated as the last part of the
function name:
rd_grid_get_pos3() - 3: this function expects i,j,k
rd_grid_get_pos1() - 1: this function expects a global index
rd_grid_get_pos1a() - 1a: this function expects an active index.
*/
/**
note about lgr
--------------
the eclipse local grid refinement (lgr) is organised as follows:
1. you start with a normal grid.
2. some of the cells can be subdivided into further internal
grids, this is the lgr.
this is illustrated below:
+--------------------------------------+
| | | |
| | | |
| x | x | x |
| | | |
| | | |
|------------|------------|------------|
| | | | | |
| | x | x | | |
|-----x------|------x-----| x |
| x | x | x | | |
| | | | | |
-------------|------------|------------|
| | | |
| | | |
| x | | |
| | | |
| | | |
+--------------------------------------+
the grid above shows the following:
1. the coarse (i.e. normal) grid has 9 cells, of which 7 marked
with 'x' are active.
2. two of the cells have been refined into new 2x2 grids. in the
refined cells only three and two of the refined cells are
active.
in a grid file the keywords for this grid will look like like this:
..... __
coords \
corners |
coords |
corners |
coords |
corners | normal coord / corners kewyords
coords | for the nine coarse cells. observe
corners | that when reading in these cells it is
coords | impossible to know that some of the
corners | cells will be subdivieded in a following
coords | lgr definition.
corners |
coords |
corners |
coords |
corners |
coords |
corners __/________________________________________________________
lgr \
lgrilg |
dimens |
coords |
corners | first lgr, with some header information,
coords | and then normal coords/corners keywords for
corners | the four refined cells.
coords |
corners |
coords |
corners __/
lgr \
lgrilg |
dimens |
coords | second lgr.
corners |
coords |
corners |
coords |
corners |
coords |
corners __/
for egrid files it is essentially the same, except for replacing the
keywords coords/corners with coord/zcorn/actnum. also the lgr
headers are somewhat different.
solution data in restart files comes in a similar way, a restart
file with lgr can typically look like this:
..... __
..... \
startsol | all restart data for the ordinary
pressure | grid.
swat |
sgas |
.... |
endsol __/____________________________
lgr \
.... |
startsol | restart data for
pressure | the first lgr.
sgas |
swat |
... |
endsol |
endlgr __/
lgr \
.... |
startsol | restart data for
pressure | the second lgr.
sgas |
swat |
... |
endsol |
endlgr __/
the lgr implementation in is based on the following main principles:
1. when loading a egrid/grid file one rd_grid_type instance will
be allocated; this grid will contain the main grid, and all the
lgr grids.
2. only one datatype (rd_grid_type) is used both for the main grid
and the lgr grids.
3. the main grid will own (memory wise) all the lgr grids, this
even applies to nested subgrids whose parent is also a lgr.
4. when it comes to indexing and so on there is no difference
between lgr grid and the main grid.
example:
--------
{
rd_file_type * restart_data = rd_file_fread_alloc(restart_filename , true); // load some restart info to inspect
rd_grid_type * grid = rd_grid_alloc(grid_filename , true); // bootstrap rd_grid instance
stringlist_type * lgr_names = rd_grid_alloc_name_list( grid ); // get a list of all the lgr names.
printf("grid:%s has %d a total of %d lgr's \n", grid_filename , stringlist_get_size( lgr_names ));
for (int lgr_nr = 0; lgr_nr < stringlist_get_size( lgr_names); lgr_nr++) {
rd_grid_type * lgr_grid = rd_grid_get_lgr( grid , stringlist_iget( lgr_names , lgr_nr )); // get the rd_grid instance of the lgr - by name.
rd_kw_type * pressure_kw;
int nx,ny,nz,active_size;
rd_grid_get_dims( lgr_grid , &nx , &ny , &nz , &active_size); // get some size info from this lgr.
printf("lgr:%s has %d x %d x %d elements \n",stringlist_iget(lgr_names , lgr_nr ) , nx , ny , nz);
// ok - now we want to extract the solution vector (pressure) corresponding to this lgr:
pressure_kw = rd_file_iget_named_kw( rd_file , "pressure" , rd_grid_get_grid_nr( lgr_grid ));
/|\
|
|
we query the lgr_grid instance to find which
occurence of the solution data we should look
up in the rd_file instance with restart data. puuhh!!
{
int center_index = rd_grid_get_global_index3( lgr_grid , nx/2 , ny/2 , nz/2 ); // ask the lgr_grid to get the index at the center of the lgr grid.
printf("the pressure in the middle of %s is %g \n", stinglist_iget( lgr_names , lgr_nr ) , rd_kw_iget_as_double( pressure_kw , center_index ));
}
}
rd_file_free( restart_data );
rd_grid_free( grid );
stringlist_free( lgr_names );
}
*/
/*
About coarse groups
-------------------
It is possible to get ECLIPSE to join several cells together as one
coarse cell, to reduce the size of the numerical problem. In this
implementation this is supported as follows:
1. Each cell has an integer flag - coarse_group, which points to
the coarse group that the current cell is part of, or the value
COARSE_NONE for cells which are not part of a coarsening group.
2. The details of a coarse cell is implemented in rd_coarse_cell.c.
3. The rd_grid structure contains a list of rd_coarse_cell
instances in the coarse_cells vector.
4. The introduction of coarse groups makes the concept of active
cells slightly more complex:
a) All the cells in the coarse group count as one (or zero)
active cells when asking the grid how many active cells
there are.
b) In the case that several of the un-coarsened cells in the
coarse group are active the mapping between global index
and active index will no longer be unique - there will be
several different global indices mapping to the same
active index.
The api for coarse related tasks is briefly:
- int rd_grid_get_num_coarse_groups( const rd_grid_type * main_grid )
- bool rd_grid_have_coarse_cells( const rd_grid_type * main_grid )
- rd_coarse_cell_type * rd_grid_iget_coarse_group( const rd_grid_type * rd_grid , int coarse_nr );
- rd_coarse_cell_type * rd_grid_get_cell_coarse_group1( const rd_grid_type * rd_grid , int global_index);
In addition to the API presented by the rd_coarse_cell.c implementation.
*/
/*
About dual porosity
-------------------
Eclipse has support for dual porosity systems where the reservoir is
made from an interleaved system of matrix blocks and fractures. The
current implementation has some support for reading such properties
from the grid files:
- The active property of the cells is an integer which is a sum of
the flag values ACTIVE_MATRIX and ACTIVE_FRACTURE.
- All functions operating on fracture properties have 'fracture'
as part of the name. The functions operating on the matrix do
(typically) not have matrix as part of the name. The matrix
properties are the default properties which apply in the single
porosity case.
- In the EGRID files the dual porosity properties are set in the
ACTNUM field in the file. The numerical values are [0,1,2,3] for
inactive cells, matrix active, fracture active and
matrix+fracture active respectively.
- For the GRID files there is abolutely no metadata to tell that
this is a dual porosity run (I think ...) - instead the whole
grid is repeated one more time with cells for the fractures
following after the matrix cells.
Naively the GRID file of a dual porosity run will report that it
contains 2*NZ layers. In the current implementation heuristics
is used to detect the situation, and the grid will only be
loaded as consisting of 'NZ' geometric layers.
- The documentation seems to indicate that the number of active
fracture cells can in general be different from the number of
active matrix cells, and the current implementation takes pains
to support that possibility - but all examples I have come over
have nactive_fracture == nactive_matrix?
- Properties and solution data in restart/init/grdecl files are
not treated here. These properties will just increase (typically
double) in size - and how to treat them will be a question of
convention. The following shows a possible solution:
{
char fracture_kw[9];
char matrix_kw[9];
int matrix_size = rd_grid_get_nactive( rd_grid );
int fracture_size = rd_grid_get_nactive_fracture( rd_grid );
swat = rd_file_iget_name_kw( rst_file , "SWAT" , 0);
snsprintf(fracture_kw , 9 , "F-%6s" , rd_kw_get_header( swat ));
snsprintf(matrix_kw , 9 , "M-%6s" , rd_kw_get_header( swat ));
rd_kw_type * M = rd_kw_alloc_sub_copy( swat , matrix_kw , 0 , matrix_size );
rd_kw_type * F = rd_kw_alloc_sub_copy( swat , fracture_kw , matrix_size , fracture_size );
}
*/
/*
About nnc
---------
When loading a grid file the various NNC related keywords are read
in to assemble information of the NNC. The NNC information is
organized as follows:
For cells with NNC's attached the information is kept in a
nnc_info_type structure. For a particular cell the nnc_info
structure keeps track of which other cells this particular cell
is connected to, on a per grid (i.e. LGR) basis.
In the nnc_info structure the different grids are identified
through the lgr_nr.
Example usage:
--------------
rd_grid_type * grid = rd_grid_alloc("FILE.EGRID");
// Get a int_vector instance with all the cells which have nnc info
// attached.
const int_vector_type * cells_with_nnc = rd_grid_get_nnc_index_list( grid );
// Iterate over all the cells with nnc info:
for (int i=0; i < int_vector_size( cells_with_nnc ); i++) {
int cell_index = int_vector_iget( cells_with_nnc , i);
const nnc_info_type * nnc_info = rd_grid_get_nnc_info1( grid , cell_index);
// Get all the nnc connections from @cell_index to other cells in the same grid
{
const int_vector_type * nnc_list = nnc_info_get_self_index_list( nnc_info );
for (int j=0; j < int_vector_size( nnc_list ); j++)
printf("Cell[%d] -> %d in the same grid \n",cell_index , int_vector_iget(nnc_list , j));
}
{
for (int lgr_index=0; lgr_index < nnc_info_get_size( nnc_info ); lgr_index++) {
nnc_vector_type * nnc_vector = nnc_info_iget_vector( nnc_info , lgr_index );
int lgr_nr = nnc_vector_get_lgr_nr( nnc_vector );
if (lgr_nr != nnc_info_get_lgr_nr( nnc_info )) {
const int_vector_type * nnc_list = nnc_vector_get_index_list( nnc_vector );
for (int j=0; j < int_vector_size( nnc_list ); j++)
printf("Cell[%d] -> %d in lgr:%d/%s \n",cell_index , int_vector_iget(nnc_list , j) , lgr_nr , rd_grid_get_lgr_name(rd_grid , lgr_nr));
}
}
}
}
Dual porosity and nnc: In ECLIPSE the connection between the matrix
properties and the fracture properties in a cell is implemented as a
nnc where the fracture cell has global index in the range [nx*ny*nz,
2*nz*ny*nz). In ert we we have not implemented this double covering
in the case of dual porosity models so NNC involving
fracture cells are not considered.
*/
/*
about tetraheder decomposition
------------------------------
the table tetraheder_permutations describe how the cells can be
divided into twelve tetrahedrons. the dimensions in the the table
are as follows:
1. the first dimension is the "way" the cell is divided into
tetrahedrons, there are two different ways. for cells where the
four point corners on a face are not in the same plane, the two
methods will not give the same result. which one is "right"??
2. the second dimension is the tetrahedron number, for each way of
the two ways there are a total of twelve tetrahedrons.
3. the third and list dimension is the point number in this
tetrahedron. when forming a tetrahedron the first input point
should always be the point corresponding to center of the
cell. that is not explicit in this table.
i.e. for instance the third tetrahedron for the first method
consists of the cells:
tetraheheder_permutations[0][2] = {0 , 4 , 5}
in addition to the central point. the value [0..7] correspond the
the number scheme of the corners in a cell used by eclipse:
lower layer: upper layer (higher value of z - i.e. lower down in resrvoir).
2---3 6---7
| | | |
0---1 4---5
table entries are ripped from resdatapost code - file: kvpvos.f in
klib/
*/
/*
About ordering of the corners in the cell
-----------------------------------------
This code reads and builds the grid structure from the
GRID/EGRID files without really considering the question about where
the cells are located in "the real world", the format is quite general
and it should(?) be possible to formulate different conventions
(i.e. handedness and direction of z-axis) with the same format.
The corners in a cell are numbered 0 - 7, where corners 0-3 constitute
one layer and the corners 4-7 consitute the other layer. Observe the
numbering does not follow a consistent rotation around the face:
j
6---7 /|\
| | |
4---5 |
|
o----------> i
2---3
| |
0---1
Many grids are left-handed, i.e. the direction of increasing z will
point down towards the center of the earth. Hence in the figure above
the layer 4-7 will be deeper down in the reservoir than layer 0-3, and
also have higher z-value.
Warning: The main author of this code suspects that the coordinate
system can be right-handed as well, giving a z axis which will
increase 'towards the sky'; the safest is probaly to check this
explicitly if it matters for the case at hand.
Method 0 corresponds to a tetrahedron decomposition which will split
the lower layer along the 1-2 diagonal and the upper layer along the
4-7 diagonal, method 1 corresponds to the alternative decomposition
which splits the lower face along the 0-3 diagnoal and the upper face
along the 5-6 diagonal.
*/
static const int tetrahedron_permutations[2][12][3] = {{// K-
{0, 1, 2},
{3, 2, 1},
// J+
{6, 2, 7},
{3, 7, 2},
// I-
{0, 2, 4},
{6, 4, 2},
// I+
{3, 1, 7},
{5, 7, 1},
// J-
{0, 4, 1},
{5, 1, 4},
// K+
{5, 4, 7},
{6, 7, 4}},
{// K-
{1, 3, 0},
{2, 0, 3},
// J+
{2, 3, 6},
{7, 6, 3},
// I-
{2, 6, 0},
{4, 0, 6},
// I+
{7, 3, 5},
{1, 5, 3},
// J-
{1, 0, 5},
{4, 5, 0},
// K+
{7, 5, 6},
{4, 6, 5}}};
/*
the implementation is based on a hierarchy of three datatypes:
1. rd_grid - this is the only exported datatype
2. rd_cell - internal
3. point - internal
*/
typedef struct point_struct point_type;
struct point_struct {
double x;
double y;
double z;
};
static void point_mapaxes_transform(point_type *p, const double origo[2],
const double unit_x[2],
const double unit_y[2]) {
double new_x = origo[0] + p->x * unit_x[0] + p->y * unit_y[0];
double new_y = origo[1] + p->x * unit_x[1] + p->y * unit_y[1];
p->x = new_x;
p->y = new_y;
}
static void point_mapaxes_invtransform(point_type *p, const double origo[2],
const double unit_x[2],
const double unit_y[2]) {
double norm = 1.0 / (unit_x[0] * unit_y[1] - unit_x[1] * unit_y[0]);
double dx = p->x - origo[0];
double dy = p->y - origo[1];
double org_x = (dx * unit_y[1] - dy * unit_y[0]) * norm;
double org_y = (-dx * unit_x[1] + dy * unit_x[0]) * norm;
p->x = org_x;
p->y = org_y;
}
static void point_inplace_add(point_type *point, const point_type *add) {
point->x += add->x;
point->y += add->y;
point->z += add->z;
}
static void point_inplace_sub(point_type *point, const point_type *sub) {
point->x -= sub->x;
point->y -= sub->y;
point->z -= sub->z;
}
static void point_inplace_scale(point_type *point, double scale_factor) {
point->x *= scale_factor;
point->y *= scale_factor;
point->z *= scale_factor;
}
/**
Will compute the vector cross product B x C and store the result in A.
*/
static void point_vector_cross(point_type *A, const point_type *B,
const point_type *C) {
A->x = B->y * C->z - B->z * C->y;
A->y = -(B->x * C->z - B->z * C->x);
A->z = B->x * C->y - B->y * C->x;
}
static double point_dot_product(const point_type *v1, const point_type *v2) {
return v1->x * v2->x + v1->y * v2->y + v1->z * v2->z;
}
static void point_compare(const point_type *p1, const point_type *p2,
bool *equal) {
const double tolerance = 0.001;
double diff_x = (fabs(p1->x - p2->x) / fabs(p1->x + p2->x + 1));
double diff_y = (fabs(p1->y - p2->y) / fabs(p1->y + p2->y + 1));
double diff_z = (fabs(p1->z - p2->z) / fabs(p1->z + p2->z + 1));
if (diff_x + diff_y + diff_z > tolerance)
*equal = false;
}
static void point_dump(const point_type *p, FILE *stream) {
util_fwrite_double(p->x, stream);
util_fwrite_double(p->y, stream);
util_fwrite_double(p->z, stream);
}
static void point_dump_ascii(const point_type *p, FILE *stream,
const double *offset) {
if (offset)
fprintf(stream, "(%7.2f, %7.2f, %7.2f) ", p->x - offset[0],
p->y - offset[1], p->z - offset[2]);
else
fprintf(stream, "(%7.2f, %7.2f, %7.2f) ", p->x, p->y, p->z);
}
static void point_set(point_type *p, double x, double y, double z) {
p->x = x;
p->y = y;
p->z = z;
}
static void point_shift(point_type *p, double dx, double dy, double dz) {
p->x += dx;
p->y += dy;
p->z += dz;
}
static void point_copy_values(point_type *p, const point_type *src) {
point_set(p, src->x, src->y, src->z);
}
/*
Indices used in the cell->active_index[] array.
*/
#define MATRIX_INDEX 0
#define FRACTURE_INDEX 1
#define COARSE_GROUP_NONE -1
#define HOST_CELL_NONE -1
#define CELL_FLAG_VALID \
1 /* In the case of GRID files not necessarily all cells geometry values set - in that case this will be left as false. */
#define CELL_FLAG_CENTER \
2 /* Has the center value been calculated - this is by default not done to speed up loading a tiny bit. */
#define CELL_FLAG_TAINTED \
4 /* Keep invalid cells out of real-world calculations with some heuristics.*/
#define CELL_FLAG_VOLUME 8
typedef struct rd_cell_struct rd_cell_type;
#define GET_CELL_FLAG(cell, flag) \
(((cell->cell_flags & (flag)) == 0) ? false : true)
#define SET_CELL_FLAG(cell, flag) ((cell->cell_flags |= (flag)))
#define METER_TO_FEET_SCALE_FACTOR 3.28084
#define METER_TO_CM_SCALE_FACTOR 100.0
struct rd_cell_struct {
point_type center;
point_type corner_list[8];
double
volume; /* Cache volume - whether it is initialized or not is handled by a cell_flags. */
int active;
int active_index
[2]; /* [0]: The active matrix index; [1]: the active fracture index */
const rd_grid_type *
lgr; /* if this cell is part of an lgr; this will point to a grid instance for that lgr; NULL if not part of lgr. */
int host_cell; /* the global index of the host cell for an lgr cell, set to -1 for normal cells. */
int coarse_group; /* The index of the coarse group holding this cell -1 for non-coarsened cells. */
int cell_flags;
nnc_info_type *nnc_info; /* Non-neighbour connection info*/
};
static ert_rd_unit_enum
rd_grid_check_unit_system(const rd_kw_type *gridunit_kw);
static void rd_grid_init_mapaxes_data_float(const rd_grid_type *grid,
float *mapaxes);
float *rd_grid_alloc_coord_data(const rd_grid_type *grid);
static const float *rd_grid_get_mapaxes(const rd_grid_type *grid);
#define RD_GRID_ID 991010
struct rd_grid_struct {
UTIL_TYPE_ID_DECLARATION;
int lgr_nr; /* EGRID files: corresponds to item 4 in gridhead - 0 for the main grid.
GRID files: 0 for the main grid, then 1 -> number of LGRs in order read from file*/
char *
name; /* the name of the file for the main grid - name of the lgr for lgrs. */
int ny, nz, nx;
int size; /* == nx*ny*nz */
int total_active;
int total_active_fracture;
bool *
visited; /* internal helper struct used when searching for index - can be NULL. */
int *
index_map; /* this a list of nx*ny*nz elements, where value -1 means inactive cell .*/
int *
inv_index_map; /* this is list of total_active elements - which point back to the index_map. */
int *
fracture_index_map; /* For fractures: this a list of nx*ny*nz elements, where value -1 means inactive cell .*/
int *
inv_fracture_index_map; /* For fractures: this is list of total_active elements - which point back to the index_map. */
rd_cell_type *cells;
char *
parent_name; /* the name of the parent for a nested lgr - for the main grid, and also a
lgr descending directly from the main grid this will be NULL. */
std::unordered_map<std::string, rd_grid_type *> children;
const rd_grid_type *
parent_grid; /* the parent grid for this (lgr) - NULL for the main grid. */
const rd_grid_type
*global_grid; /* the global grid - NULL for the main grid. */
bool coarsening_active;
vector_type *coarse_cells;
/*
the two fields below are for *storing* lgr grid instances. observe
that these fields will be NULL for lgr grids, i.e. grids with
lgr_nr > 0.
*/
vector_type *
LGR_list; /* a vector of rd_grid instances for LGRs - the index corresponds to the order LGRs are read from file*/
int_vector_type *
lgr_index_map; /* a vector that maps LGR-nr for EGRID files to index into the LGR_list.*/
std::unordered_map<std::string, rd_grid_type *>
LGR_hash; /* a hash of pointers to rd_grid instances - for name based lookup of lgr. */
int parent_box
[6]; /* integers i1,i2, j1,j2, k1,k2 of the parent grid region containing this lgr. the indices are inclusive - zero offset */
/* not used yet .. */
int dualp_flag;
bool use_mapaxes;
double unit_x[2];
double unit_y[2];
double origo[2];
float *mapaxes;
/*------------------------------: the fields below this line are used for blocking algorithms - and not allocated by default.*/
int block_dim; /* == 2 for maps and 3 for fields. 0 when not in use. */
int block_size;
int last_block_index;
double_vector_type **values;
rd_kw_type *coord_kw; /* Retained for writing the grid to file.
In principal it should be possible to
recalculate this from the cell coordinates,
but in cases with skewed cells this has proved
numerically challenging. */
ert_rd_unit_enum unit_system;
int eclipse_version;
};
ert_rd_unit_enum rd_grid_get_unit_system(const rd_grid_type *grid) {
return grid->unit_system;
}
static void rd_cell_compare(const rd_cell_type *c1, const rd_cell_type *c2,
bool include_nnc, bool *equal) {
int i;
if (c1->active != c2->active)
*equal = false;
if (c1->active_index[0] != c2->active_index[0])
*equal = false;
if (c1->active_index[1] != c2->active_index[1])
*equal = false;
if (c1->coarse_group != c2->coarse_group)
*equal = false;
if (c1->host_cell != c2->host_cell)
*equal = false;
if (*equal) {
for (i = 0; i < 8; i++)
point_compare(&c1->corner_list[i], &c2->corner_list[i], equal);
}
if (include_nnc) {
if (*equal)
*equal = nnc_info_equal(c1->nnc_info, c2->nnc_info);
}
}
static void rd_cell_dump(const rd_cell_type *cell, FILE *stream) {
int i;
for (i = 0; i < 8; i++)
point_dump(&cell->corner_list[i], stream);
}
static void rd_cell_assert_center(rd_cell_type *cell);
static void rd_cell_dump_ascii(rd_cell_type *cell, int i, int j, int k,
FILE *stream, const double *offset) {
fprintf(stream,
"Cell: i:%3d j:%3d k:%3d host_cell:%d CoarseGroup:%4d "
"active_nr:%6d active:%d \nCorners:\n",
i, j, k, cell->host_cell, cell->coarse_group,
cell->active_index[MATRIX_INDEX], cell->active);
rd_cell_assert_center(cell);
fprintf(stream, "Center : ");
point_dump_ascii(&cell->center, stream, offset);
fprintf(stream, "\n");
{
int l;
for (l = 0; l < 8; l++) {
fprintf(stream, "Corner %d : ", l);
point_dump_ascii(&cell->corner_list[l], stream, offset);
fprintf(stream, "\n");
}
}
fprintf(stream, "\n");
}
static void rd_cell_fwrite_GRID(const rd_grid_type *grid,
const rd_cell_type *cell, bool fracture_cell,
int coords_size, int i, int j, int k,
int global_index, rd_kw_type *coords_kw,
rd_kw_type *corners_kw, fortio_type *fortio) {
rd_kw_iset_int(coords_kw, 0, i + 1);
rd_kw_iset_int(coords_kw, 1, j + 1);
rd_kw_iset_int(coords_kw, 2, k + 1);
rd_kw_iset_int(coords_kw, 3, global_index + 1);
rd_kw_iset_int(coords_kw, 4, 0);
if (fracture_cell) {
if (cell->active & CELL_ACTIVE_FRACTURE)
rd_kw_iset_int(coords_kw, 4, 1);
} else {
if (cell->active & CELL_ACTIVE_MATRIX)
rd_kw_iset_int(coords_kw, 4, 1);
}
if (coords_size == 7) {
rd_kw_iset_int(coords_kw, 5, cell->host_cell + 1);
rd_kw_iset_int(coords_kw, 6, cell->coarse_group + 1);
}
rd_kw_fwrite(coords_kw, fortio);
{
float *corners = (float *)rd_kw_get_void_ptr(corners_kw);
point_type point;
int c;
for (c = 0; c < 8; c++) {
point_copy_values(&point, &cell->corner_list[c]);
if (grid->use_mapaxes)
point_mapaxes_invtransform(&point, grid->origo, grid->unit_x,
grid->unit_y);
corners[3 * c] = point.x;
corners[3 * c + 1] = point.y;
corners[3 * c + 2] = point.z;
}
}
rd_kw_fwrite(corners_kw, fortio);
}
//static const size_t cellMappingRDRi[8] = { 0, 1, 3, 2, 4, 5, 7, 6 };
static void rd_cell_ri_export(const rd_cell_type *cell, double *ri_points) {
int rd_offset = 4;
int ri_offset = rd_offset * 3;
{
int point_nr;
// Handling the points 0,1 & 4,5 which map directly between ECLIPSE and RI
for (point_nr = 0; point_nr < 2; point_nr++) {
// Points 0 & 1
ri_points[point_nr * 3] = cell->corner_list[point_nr].x;
ri_points[point_nr * 3 + 1] = cell->corner_list[point_nr].y;
ri_points[point_nr * 3 + 2] = -cell->corner_list[point_nr].z;
// Points 4 & 5
ri_points[ri_offset + point_nr * 3] =
cell->corner_list[rd_offset + point_nr].x;
ri_points[ri_offset + point_nr * 3 + 1] =
cell->corner_list[rd_offset + point_nr].y;
ri_points[ri_offset + point_nr * 3 + 2] =
-cell->corner_list[rd_offset + point_nr].z;
}
}
{
int rd_point;
/*
Handling the points 2,3 & 6,7 which are flipped when (2,3) ->
(3,2) and (6,7) -> (7,6) when going between ECLIPSE and ResInsight.
*/
for (rd_point = 2; rd_point < 4; rd_point++) {
int ri_point = 5 - rd_point;
// Points 2 & 3
ri_points[ri_point * 3] = cell->corner_list[rd_point].x;
ri_points[ri_point * 3 + 1] = cell->corner_list[rd_point].y;
ri_points[ri_point * 3 + 2] = -cell->corner_list[rd_point].z;
// Points 6 & 7
ri_points[ri_offset + ri_point * 3] =
cell->corner_list[rd_offset + rd_point].x;
ri_points[ri_offset + ri_point * 3 + 1] =
cell->corner_list[rd_offset + rd_point].y;
ri_points[ri_offset + ri_point * 3 + 2] =
-cell->corner_list[rd_offset + rd_point].z;
}
}
}
static double max2(double x1, double x2) { return (x1 > x2) ? x1 : x2; }
static double min2(double x1, double x2) { return (x1 < x2) ? x1 : x2; }
static double min4(double x1, double x2, double x3, double x4) {
return min2(min2(x1, x2), min2(x3, x4));
}
static double max4(double x1, double x2, double x3, double x4) {
return max2(max2(x1, x2), max2(x3, x4));
}
static double max8(double x1, double x2, double x3, double x4, double x5,
double x6, double x7, double x8) {
return max2(max4(x1, x2, x3, x4), max4(x5, x6, x7, x8));
}
static double min8(double x1, double x2, double x3, double x4, double x5,
double x6, double x7, double x8) {
return min2(min4(x1, x2, x3, x4), min4(x5, x6, x7, x8));
}
static double rd_cell_min_z(const rd_cell_type *cell) {
return min4(cell->corner_list[0].z, cell->corner_list[1].z,
cell->corner_list[2].z, cell->corner_list[3].z);
}
static double rd_cell_max_z(const rd_cell_type *cell) {
return max4(cell->corner_list[4].z, cell->corner_list[5].z,
cell->corner_list[6].z, cell->corner_list[7].z);
}
/**
the grid can be rotated so that it is not safe to consider only one
plane for the x/y min/max.
*/
static double rd_cell_min_x(const rd_cell_type *cell) {
return min8(cell->corner_list[0].x, cell->corner_list[1].x,
cell->corner_list[2].x, cell->corner_list[3].x,
cell->corner_list[4].x, cell->corner_list[5].x,
cell->corner_list[6].x, cell->corner_list[7].x);
}
static double rd_cell_max_x(const rd_cell_type *cell) {
return max8(cell->corner_list[0].x, cell->corner_list[1].x,
cell->corner_list[2].x, cell->corner_list[3].x,
cell->corner_list[4].x, cell->corner_list[5].x,
cell->corner_list[6].x, cell->corner_list[7].x);
}
static double rd_cell_min_y(const rd_cell_type *cell) {
return min8(cell->corner_list[0].y, cell->corner_list[1].y,
cell->corner_list[2].y, cell->corner_list[3].y,
cell->corner_list[4].y, cell->corner_list[5].y,
cell->corner_list[6].y, cell->corner_list[7].y);
}
static double rd_cell_max_y(const rd_cell_type *cell) {
return max8(cell->corner_list[0].y, cell->corner_list[1].y,
cell->corner_list[2].y, cell->corner_list[3].y,
cell->corner_list[4].y, cell->corner_list[5].y,
cell->corner_list[6].y, cell->corner_list[7].y);
}
/**
The problem is that some grids have invalid cells. Typically
the cells accomodating numerical aquifers are located at an utm
position (0,0).
Cells which have some pillars located in (0,0) and some cells
located among the rest of the grid become completely warped - with
insane volumes, parts of the reservoir volume doubly covered, and
so on.
To keep these cells out of the real-world (i.e. involving utm
coordinates) computations they are marked as 'tainted' in this
function. The tainting procedure is completely heuristic, and
probably wrong.
--------------
There is second heuristic which marks cells as invalid. In some
cases (which ??) cells outside the area of interest are just set to
have all four corner at the same arbitrary depth; these cells are