/* Copyright (C) 2014 Equinor ASA, Norway. The file 'layer.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 for more details. */ #include #include #include #include #include #include #define LAYER_TYPE_ID 55185409 typedef struct { int cell_value; int edges[4]; bool bottom_barrier; bool left_barrier; bool active; } cell_type; struct layer_struct { UTIL_TYPE_ID_DECLARATION; int nx, ny; cell_type * data; int cell_sum; }; UTIL_IS_INSTANCE_FUNCTION( layer , LAYER_TYPE_ID ) UTIL_SAFE_CAST_FUNCTION( layer , LAYER_TYPE_ID ) layer_type * layer_alloc(int nx , int ny) { layer_type * layer = (layer_type*)util_malloc( sizeof * layer ); UTIL_TYPE_ID_INIT( layer , LAYER_TYPE_ID ); layer->nx = nx; layer->ny = ny; layer->cell_sum = 0; { int data_size = (layer->nx + 1)* (layer->ny + 1); layer->data = (cell_type*)util_malloc( data_size * sizeof * layer->data ); { int g; for (g=0; g < data_size; g++) { cell_type * cell = &layer->data[g]; cell->cell_value = 0; cell->edges[RIGHT_EDGE] = 0; cell->edges[LEFT_EDGE] = 0; cell->edges[TOP_EDGE] = 0; cell->edges[BOTTOM_EDGE] = 0; cell->active = true; cell->bottom_barrier = false; cell->left_barrier = false; } } } return layer; } void layer_free( layer_type * layer ) { free( layer->data ); free(layer); } static int layer_get_global_cell_index( const layer_type * layer , int i , int j) { if ((i < 0) || (i >= layer->nx)) util_abort("%s: invalid i value:%d Valid range: [0,%d) \n",__func__ , i , layer->nx); if ((j < 0) || (j >= layer->ny)) util_abort("%s: invalid j value:%d Valid range: [0,%d) \n",__func__ , j , layer->ny); return i + j*(layer->nx + 1); } static int layer_get_global_cell_index__( const layer_type * layer , int i , int j) { if ((i < 0) || (i > layer->nx)) util_abort("%s: invalid i value:%d Valid range: [0,%d] \n",__func__ , i , layer->nx); if ((j < 0) || (j > layer->ny)) util_abort("%s: invalid j value:%d Valid range: [0,%d] \n",__func__ , j , layer->ny); return i + j*(layer->nx + 1); } static cell_type* layer_iget_cell( const layer_type * layer , int i , int j) { int g = layer_get_global_cell_index( layer , i , j ); return &layer->data[g]; } /* To be able to update barriers on the edge we need access to the i = nx and j = ny cells. */ static cell_type* layer_iget_cell__( const layer_type * layer , int i , int j) { int g = layer_get_global_cell_index__( layer , i , j ); return &layer->data[g]; } bool layer_iget_left_barrier( const layer_type * layer, int i , int j) { cell_type * cell = layer_iget_cell(layer , i,j); return cell->left_barrier; } bool layer_iget_bottom_barrier( const layer_type * layer, int i , int j) { cell_type * cell = layer_iget_cell(layer,i,j); return cell->bottom_barrier; } int layer_iget_cell_value( const layer_type * layer, int i , int j) { int g = layer_get_global_cell_index( layer , i , j ); return layer->data[g].cell_value; } bool layer_iget_active( const layer_type * layer, int i , int j) { int g = layer_get_global_cell_index( layer , i , j ); return layer->data[g].active; } int layer_get_cell_sum( const layer_type * layer ) { return layer->cell_sum; } static void layer_cancel_edge( layer_type * layer , int i , int j , edge_dir_enum dir) { int g = layer_get_global_cell_index( layer , i , j ); cell_type * cell = &layer->data[g]; cell->edges[dir] = 0; } int layer_get_nx( const layer_type * layer ) { return layer->nx; } int layer_get_ny( const layer_type * layer ) { return layer->ny; } void layer_iset_cell_value( layer_type * layer , int i , int j , int value) { int g = layer_get_global_cell_index( layer , i , j ); cell_type * cell = &layer->data[g]; layer->cell_sum += (value - cell->cell_value); cell->cell_value = value; if (i > 0) { int neighbour_value = layer_iget_cell_value( layer , i - 1 , j); if (value == neighbour_value) { cell->edges[LEFT_EDGE] = 0; layer_cancel_edge( layer , i - 1, j , RIGHT_EDGE); } else cell->edges[LEFT_EDGE] = -value; } else cell->edges[LEFT_EDGE] = -value; if (i < (layer->nx - 1)) { int neighbour_value = layer_iget_cell_value( layer , i + 1 , j); if (value == neighbour_value) { cell->edges[RIGHT_EDGE] = 0; layer_cancel_edge( layer , i + 1, j , LEFT_EDGE); } else cell->edges[RIGHT_EDGE] = value; } else cell->edges[RIGHT_EDGE] = value; if (j < (layer->ny - 1)) { int neighbour_value = layer_iget_cell_value( layer , i , j + 1); if (value == neighbour_value) { cell->edges[TOP_EDGE] = 0; layer_cancel_edge( layer , i , j + 1, BOTTOM_EDGE); } else cell->edges[TOP_EDGE] = -value; } else cell->edges[TOP_EDGE] = -value; if (j > 0) { int neighbour_value = layer_iget_cell_value( layer , i , j - 1); if (value == neighbour_value) { cell->edges[BOTTOM_EDGE] = 0; layer_cancel_edge( layer , i , j - 1, TOP_EDGE); } else cell->edges[BOTTOM_EDGE] = value; } else cell->edges[BOTTOM_EDGE] = value; } static int layer_get_global_edge_index( const layer_type * layer , int i , int j , edge_dir_enum dir) { if ((i < 0) || (j < 0)) util_abort("%s: invalid value for i,j \n",__func__); if ((i > layer->nx) || (j > layer->ny)) util_abort("%s: invalid value for i,j \n",__func__); if (i == layer->nx) { if (j == layer->ny) util_abort("%s: invalid value for i,j \n",__func__); if (dir != LEFT_EDGE) util_abort("%s: invalid value for i,j \n",__func__); } if (j == layer->ny) { if (i == layer->nx) util_abort("%s: invalid value for i,j \n",__func__); if (dir != BOTTOM_EDGE) util_abort("%s: invalid value for i,j \n",__func__); } return i + j*(layer->nx + 1); } int layer_iget_edge_value( const layer_type * layer , int i , int j , edge_dir_enum dir) { int g = layer_get_global_edge_index( layer , i , j , dir); cell_type * cell = &layer->data[g]; return cell->edges[dir]; } bool layer_cell_on_edge( const layer_type * layer , int i , int j) { int g = layer_get_global_cell_index( layer , i , j); cell_type * cell = &layer->data[g]; if (cell->cell_value == cell->edges[LEFT_EDGE]) return true; if (cell->cell_value == cell->edges[RIGHT_EDGE]) return true; if (cell->cell_value == cell->edges[BOTTOM_EDGE]) return true; if (cell->cell_value == cell->edges[TOP_EDGE]) return true; return false; } static void point_shift(int_point2d_type * point , int di , int dj) { point->i += di; point->j += dj; } static bool point_equal(int_point2d_type * p1 , int_point2d_type *p2) { if ((p1->i == p2->i) && (p1->j == p2->j)) return true; else return false; } /* Possible edge transitions: BOTTOM_EDGE -> BOTTOM_EDGE{ i + 1, j } , RIGHT_EDGE{i,j} , LEFT_EDGE{i +1,j -1} RIGHT_EDGE -> TOP_EDGE{i,j} , RIGHT_EDGE{i,j+1} , BOTTOM_EDGE{i+1 , j+1} TOP_EDGE -> LEFT_EDGE{i,j} , TOP_EDGE{i-1,j} , RIGHT_EDGE{i-1,j+1} LEFT_EDGE -> BOTTOM_EDGE{i,j} , LEFT_EDGE{i,j-1} , TOP_EDGE{i-1 , j-1} */ static void layer_trace_block_edge__( const layer_type * layer , int_point2d_type start_point , int i, int j, int value, edge_dir_enum dir , std::vector& corner_list, int_vector_type * cell_list) { int_point2d_type current_point; int_point2d_type next_point; current_point.i = i; current_point.j = j; next_point = current_point; if (dir == BOTTOM_EDGE) point_shift( &next_point , 1 , 0 ); else if (dir == RIGHT_EDGE) { point_shift( ¤t_point , 1 , 0 ); point_shift( &next_point , 1 , 1 ); } else if (dir == TOP_EDGE) { point_shift( ¤t_point , 1 , 1 ); point_shift( &next_point , 0 , 1 ); } else if (dir == LEFT_EDGE) point_shift( ¤t_point , 0 , 1 ); corner_list.push_back(current_point); { int cell_index = i + j*layer->nx; int_vector_append( cell_list , cell_index ); } if ( !point_equal(&start_point , &next_point) ) { if (dir == BOTTOM_EDGE) { if (layer_iget_edge_value( layer , i,j,RIGHT_EDGE) == value) layer_trace_block_edge__( layer , start_point , i , j , value , RIGHT_EDGE , corner_list , cell_list); else if (layer_iget_edge_value( layer , i + 1 , j ,BOTTOM_EDGE) == value) layer_trace_block_edge__( layer , start_point , i + 1 , j , value , BOTTOM_EDGE , corner_list , cell_list); else if (layer_iget_edge_value( layer , i + 1 , j - 1 , LEFT_EDGE) == -value) layer_trace_block_edge__( layer , start_point , i + 1 , j -1 , value , LEFT_EDGE , corner_list , cell_list); else util_abort("%s: dir == BOTTOM_EDGE \n",__func__); } if (dir == RIGHT_EDGE) { if (layer_iget_edge_value( layer , i,j,TOP_EDGE) == -value) layer_trace_block_edge__( layer , start_point , i , j , value , TOP_EDGE , corner_list , cell_list); else if (layer_iget_edge_value( layer , i , j + 1 ,RIGHT_EDGE) == value) layer_trace_block_edge__( layer , start_point , i , j + 1, value , RIGHT_EDGE , corner_list , cell_list); else if (layer_iget_edge_value( layer , i + 1 , j + 1 ,BOTTOM_EDGE) == value) layer_trace_block_edge__( layer , start_point , i + 1 , j + 1, value , BOTTOM_EDGE , corner_list , cell_list); else util_abort("%s: dir == RIGHT_EDGE \n",__func__); } if (dir == TOP_EDGE) { if (layer_iget_edge_value( layer , i , j , LEFT_EDGE) == -value) layer_trace_block_edge__( layer , start_point , i , j , value , LEFT_EDGE , corner_list , cell_list); else if (layer_iget_edge_value( layer , i - 1 , j ,TOP_EDGE) == -value) layer_trace_block_edge__( layer , start_point , i - 1 , j , value , TOP_EDGE , corner_list , cell_list); else if (layer_iget_edge_value( layer , i - 1 , j + 1 ,RIGHT_EDGE) == value) layer_trace_block_edge__( layer , start_point , i - 1 , j + 1, value , RIGHT_EDGE , corner_list , cell_list); else util_abort("%s: dir == TOP_EDGE \n",__func__); } if (dir == LEFT_EDGE) { if (layer_iget_edge_value( layer , i , j , BOTTOM_EDGE) == value) layer_trace_block_edge__( layer , start_point , i , j , value , BOTTOM_EDGE , corner_list , cell_list); else if (layer_iget_edge_value( layer , i , j - 1 , LEFT_EDGE) == -value) layer_trace_block_edge__( layer , start_point , i , j - 1, value , LEFT_EDGE , corner_list , cell_list); else if (layer_iget_edge_value( layer , i -1 , j - 1 , TOP_EDGE) == -value) layer_trace_block_edge__( layer , start_point , i-1 , j - 1, value , TOP_EDGE , corner_list , cell_list); else util_abort("%s: dir == LEFT_EDGE \n",__func__); } } } static void layer_fprintf_dash( const layer_type * layer , FILE * stream, int i1 , int i2) { int i; fprintf(stream," --"); for (i=i1; i <= i2; i++) fprintf(stream , "----"); fprintf(stream , "----\n"); } static void layer_fprintf_header( const layer_type * layer , FILE * stream, int i1 , int i2) { int i; fprintf(stream," "); for (i=i1; i <= i2; i++) fprintf(stream , " %3d" , i); fprintf(stream , "\n"); } void layer_fprintf_box( const layer_type * layer , FILE * stream , int i1 , int i2 , int j1 , int j2) { int i,j; layer_fprintf_header( layer , stream , i1 , i2); layer_fprintf_dash( layer , stream , i1 , i2); for (j=j2; j >= j1; j--) { fprintf(stream , " %3d | " , j); for (i=i1; i <= i2; i++) { int g = layer_get_global_cell_index( layer , i , j); cell_type * cell = &layer->data[g]; fprintf(stream , " %3d" , cell->cell_value); } fprintf(stream , " | %3d \n" , j); } layer_fprintf_dash( layer , stream , i1 , i2); layer_fprintf_header( layer , stream , i1 , i2); } void layer_fprintf( const layer_type * layer , FILE * stream) { layer_fprintf_box( layer , stream , 0 , layer->nx - 1 , 0 , layer->ny - 1); } void layer_fprintf_cell( const layer_type * layer , int i , int j , FILE * stream) { int g = layer_get_global_cell_index( layer , i , j); cell_type * cell = &layer->data[g]; fprintf(stream , " i:%d j:%d \n",i,j); fprintf(stream , " *--- %4d ---* \n",cell->edges[TOP_EDGE]); fprintf(stream , " | | \n"); fprintf(stream , " %4d %4d %4d\n" , cell->edges[LEFT_EDGE] , cell->cell_value , cell->edges[RIGHT_EDGE]); fprintf(stream , " | | \n"); fprintf(stream , " *--- %4d ---* \n",cell->edges[BOTTOM_EDGE]); } static bool layer_find_edge( const layer_type * layer , int *i , int *j , int value) { int g = layer_get_global_cell_index( layer , *i , *j); cell_type * cell = &layer->data[g]; if (cell->cell_value == value) { while (!layer_cell_on_edge( layer , *i , *j)) (*i) += 1; return true; } else return false; } bool layer_trace_block_edge( const layer_type * layer , int start_i , int start_j , int value , std::vector& corner_list, int_vector_type * cell_list) { int g = layer_get_global_cell_index( layer , start_i , start_j); cell_type * cell = &layer->data[g]; if (cell->cell_value == value) { int i = start_i; int j = start_j; if (layer_find_edge( layer , &i , &j , value)) { int_point2d_type start_corner; g = layer_get_global_cell_index( layer , i , j); cell = &layer->data[g]; start_corner.i = i; start_corner.j = j; corner_list.clear(); int_vector_reset( cell_list ); if (cell->edges[BOTTOM_EDGE] == value) { point_shift( &start_corner , 0 , 0 ); layer_trace_block_edge__(layer , start_corner , i , j , value , BOTTOM_EDGE , corner_list , cell_list); } else if (cell->edges[RIGHT_EDGE] == value) { point_shift( &start_corner , 1 , 0 ); layer_trace_block_edge__(layer , start_corner , i , j , value , RIGHT_EDGE , corner_list , cell_list); } else if (cell->edges[TOP_EDGE] == -value) { point_shift( &start_corner , 1 , 1 ); layer_trace_block_edge__(layer , start_corner , i , j , value , TOP_EDGE , corner_list , cell_list); } else if (cell->edges[LEFT_EDGE] == -value) { point_shift( &start_corner , 0 , 1 ); layer_trace_block_edge__(layer , start_corner , i , j , value , LEFT_EDGE , corner_list , cell_list); } else util_abort("%s: internal error \n",__func__); int_vector_select_unique( cell_list ); return true; } } return false; } static void layer_trace_block_content__( layer_type * layer , bool erase , int i , int j , int value , bool * visited , int_vector_type * i_list , int_vector_type * j_list) { int g = layer_get_global_cell_index( layer , i , j); cell_type * cell = &layer->data[g]; if (cell->cell_value != value || visited[g]) return; { visited[g] = true; if (erase) layer_iset_cell_value( layer , i , j , 0); int_vector_append( i_list , i ); int_vector_append( j_list , j ); if (i > 0) layer_trace_block_content__( layer , erase , i - 1 , j , value , visited , i_list , j_list); if (i < (layer->nx - 1)) layer_trace_block_content__( layer , erase , i + 1 , j , value , visited , i_list , j_list); if (j > 0) layer_trace_block_content__( layer , erase , i , j - 1, value , visited , i_list , j_list); if (j < (layer->ny - 1)) layer_trace_block_content__( layer , erase , i , j + 1, value , visited , i_list , j_list); } } static bool * layer_alloc_visited_mask( const layer_type * layer ) { int total_size = (layer->nx + 1)* (layer->ny + 1); bool * visited = (bool*)util_malloc( total_size * sizeof * visited ); int g; for (g = 0; g < total_size; g++) visited[g] = false; return visited; } bool layer_trace_block_content( layer_type * layer , bool erase , int start_i , int start_j , int value , int_vector_type * i_list, int_vector_type * j_list) { bool start_tracing = false; int g = layer_get_global_cell_index( layer , start_i , start_j); cell_type * cell = &layer->data[g]; if ((value == 0) && (cell->cell_value != 0)) start_tracing = true; else if ((cell->cell_value == value) && (cell->cell_value != 0)) start_tracing = true; if (start_tracing) { bool * visited = layer_alloc_visited_mask( layer ); value = cell->cell_value; int_vector_reset( i_list ); int_vector_reset( j_list ); layer_trace_block_content__(layer , erase , start_i , start_j , value , visited , i_list , j_list ); free( visited ); return true; } else return false; } int layer_replace_cell_values( layer_type * layer , int old_value , int new_value) { int i,j; int replace_count = 0; for (j=0; j < layer->ny; j++) { for (i=0; i < layer->nx; i++) { if (layer_iget_cell_value( layer , i , j ) == old_value) { layer_iset_cell_value( layer , i , j , new_value); replace_count++; } } } return replace_count; } static void layer_assert_cell_index( const layer_type * layer , int i , int j ) { if ((i < 0) || (j < 0)) util_abort("%s: invalid value for i,j i:%d [0,%d) j:%d [0,%d) \n",__func__ , i , layer->nx , j , layer->ny); if ((i >= layer->nx) || (j >= layer->ny)) util_abort("%s: invalid value for i,j i:%d [0,%d) j:%d [0,%d) \n",__func__ , i , layer->nx , j , layer->ny); } bool layer_cell_contact( const layer_type * layer , int i1 , int j1 , int i2 , int j2) { layer_assert_cell_index( layer , i1 , j1 ); layer_assert_cell_index( layer , i2 , j2 ); { if ((abs(i1 - i2) == 1) && (j1 == j2)) { int i = util_int_max( i1,i2 ); const cell_type * cell = layer_iget_cell( layer , i , j1 ); return !cell->left_barrier; } if ((i1 == i2) && (abs(j1 - j2) == 1)) { int j = util_int_max( j1 , j2 ); const cell_type * cell = layer_iget_cell( layer , i1 , j ); return !cell->bottom_barrier; } return false; } } void layer_add_ijbarrier( layer_type * layer , int i1 , int j1 , int i2 , int j2 ) { if ((j1 == j2) || (i1 == i2)) { if (i1 == i2) { int j; int jmin = util_int_min(j1,j2); int jmax = util_int_max(j1,j2); for (j=jmin; j < jmax; j++) { cell_type * cell = layer_iget_cell__( layer , i1 , j ); cell->left_barrier = true; } } else { int i; int imin = util_int_min(i1,i2); int imax = util_int_max(i1,i2); for (i=imin; i < imax; i++) { cell_type * cell = layer_iget_cell__( layer , i , j1 ); cell->bottom_barrier = true; } } } else util_abort("%s: fatal error must have i1 == i2 || j1 == j2 \n",__func__); } void layer_add_barrier( layer_type * layer , int c1 , int c2) { int dimx = layer->nx + 1; int j1 = c1 / dimx; int i1 = c1 % dimx; int j2 = c2 / dimx; int i2 = c2 % dimx; layer_add_ijbarrier( layer , i1 , j1 , i2 , j2 ); } /* Line is parameterized as: ax + by + c = 0 */ static double distance_to_line(double a , double b , double c , int i , int j) { double x0 = 1.0 * i; double y0 = 1.0 * j; return fabs(a*x0 + b*y0 + c) / sqrt(a*a + b*b); } void layer_add_interp_barrier( layer_type * layer , int c1 , int c2) { int dimx = layer->nx + 1; int j1 = c1 / dimx; int i1 = c1 % dimx; int j2 = c2 / dimx; int i2 = c2 % dimx; if ((j1 == j2) || (i1 == i2)) layer_add_barrier( layer , c1 , c2 ); else { int di = abs(i2 - i1) / (i2 - i1); int dj = abs(j2 - j1) / (j2 - j1); double a = 1.0 * (j2 - j1) / (i2 - i1); double b = 1.0 * (j1 - a*i1); int i = i1; int j = j1; int c = c1; while (c != c2) { double dx = distance_to_line( a , -1 , b , i + di , j ); double dy = distance_to_line( a , -1 , b , i , j + dj); if (dx <= dy) i += di; else j += dj; layer_add_barrier( layer , c , i + j*dimx); c = i + j*dimx; } } } void layer_memcpy(layer_type * target_layer , const layer_type * src_layer) { if ((target_layer->nx == src_layer->nx) && (target_layer->ny == src_layer->ny)) { size_t data_size = target_layer->nx * target_layer->ny * sizeof(cell_type); memcpy(target_layer->data , src_layer->data , data_size ); target_layer->cell_sum = src_layer->cell_sum; } else util_abort("%s: fatal error - tried to copy elements between layers of different size\n",__func__); } static void layer_assign__( layer_type * layer, int value) { int i,j; for (j=0; j < layer->ny; j++) { for (i=0; i < layer->nx; i++) { cell_type * cell = layer_iget_cell( layer , i , j ); cell->cell_value = value; { int e; for (e=0; e < 4; e++) cell->edges[e] = 0; } } } layer->cell_sum = value * layer->nx*layer->ny; } void layer_clear_cells( layer_type * layer) { layer_assign__(layer , 0 ); } void layer_assign( layer_type * layer, int value) { layer_assign__( layer , value ); } void layer_update_connected_cells( layer_type * layer , int i , int j , int org_value , int new_value) { if (org_value != new_value) { if (layer_iget_cell_value( layer , i , j ) == org_value) { layer_iset_cell_value( layer , i , j , new_value); if (i < (layer->nx - 1) && layer_cell_contact( layer , i,j,i+1,j)) layer_update_connected_cells( layer , i + 1 , j , org_value , new_value); if (i > 0 && layer_cell_contact( layer , i,j,i-1,j)) layer_update_connected_cells( layer , i - 1 , j , org_value , new_value); if (j < (layer->ny - 1) && layer_cell_contact( layer , i,j,i,j+1)) layer_update_connected_cells( layer , i , j + 1, org_value , new_value); if (j > 0 && layer_cell_contact( layer , i,j,i,j-1)) layer_update_connected_cells( layer , i , j - 1, org_value , new_value); } } } void layer_cells_equal( const layer_type * layer , int value , int_vector_type * i_list , int_vector_type * j_list) { int i,j; for (j=0; j < layer->ny; j++) { for (i=0; i < layer->nx; i++) { cell_type * cell = layer_iget_cell( layer , i , j ); if (cell->cell_value == value) { int_vector_append( i_list , i ); int_vector_append( j_list , j ); } } } } int layer_count_equal( const layer_type * layer , int value ) { int num_equal = 0; int i,j; for (j=0; j < layer->ny; j++) { for (i=0; i < layer->nx; i++) { cell_type * cell = layer_iget_cell( layer , i , j ); if (cell->cell_value == value) num_equal++; } } return num_equal; } void layer_update_active( layer_type * layer , const ecl_grid_type * grid , int k) { int i,j; for (j=0; j< ecl_grid_get_ny( grid ); j++) { for (i=0; i < ecl_grid_get_nx( grid ); i++) { cell_type * cell = layer_iget_cell( layer , i , j ); cell->active = ecl_grid_cell_active3( grid , i , j , k ); } } }