/* Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University Copyright Equnior ASA This file is part of the Open Porous Media project (OPM). OPM 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. OPM 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 for more details. You should have received a copy of the GNU General Public License along with OPM. If not, see . */ #include "analysis/distance.h" /****************************************************************** * A fast distance calculation * ******************************************************************/ template void CalcDist(Array &Distance, const Array &ID, const Domain &Dm, const std::array &periodic, const std::array &dx) { ASSERT(Distance.size() == ID.size()); std::array n = {Dm.Nx - 2, Dm.Ny - 2, Dm.Nz - 2}; fillHalo fillData(Dm.Comm, Dm.rank_info, n, {1, 1, 1}, 50, 1, {true, false, false}, periodic); Array id(ID.size()); Array vecDist(Distance.size()); for (size_t i = 0; i < ID.length(); i++) id(i) = ID(i) == 0 ? -1 : 1; fillData.fill(id); CalcVecDist(vecDist, id, Dm, periodic, dx); for (size_t i = 0; i < Distance.length(); i++) Distance(i) = id(i) * vecDist(i).norm(); } /****************************************************************** * Vector-based distance calculation * * Initialize cells adjacent to boundaries * ******************************************************************/ static void calcVecInitialize(Array &d, const Array &ID, double dx, double dy, double dz) { d.fill(Vec(1e50, 1e50, 1e50)); const double dx0 = 0.5 * dx; const double dy0 = 0.5 * dy; const double dz0 = 0.5 * dz; //const double dxy0 = 0.25*sqrt( dx*dx + dy*dy ); //const double dxz0 = 0.25*sqrt( dx*dx + dz*dz ); //const double dyz0 = 0.25*sqrt( dy*dy + dz*dz ); //const double dxyz0 = sqrt( dx*dx + dy*dy + dz*dz ); int Nx = d.size(0); int Ny = d.size(1); int Nz = d.size(2); for (int k = 1; k < Nz - 1; k++) { for (int j = 1; j < Ny - 1; j++) { for (int i = 1; i < Nx - 1; i++) { int id = ID(i, j, k); bool x[2] = {id != ID(i - 1, j, k), id != ID(i + 1, j, k)}; bool y[2] = {id != ID(i, j - 1, k), id != ID(i, j + 1, k)}; bool z[2] = {id != ID(i, j, k - 1), id != ID(i, j, k + 1)}; if (x[0]) d(i, j, k) = Vec(dx0, 0, 0); if (x[1]) d(i, j, k) = Vec(-dx0, 0, 0); if (y[0]) d(i, j, k) = Vec(0, dy0, 0); if (y[1]) d(i, j, k) = Vec(0, -dy0, 0); if (z[0]) d(i, j, k) = Vec(0, 0, dz0); if (z[1]) d(i, j, k) = Vec(0, 0, -dz0); /*if ( x[0] && y[0] ) d(i,j,k) = Vec( dxy0, dxy0, 0 ); if ( x[0] && y[1] ) d(i,j,k) = Vec( dxy0, -dxy0, 0 ); if ( x[1] && y[0] ) d(i,j,k) = Vec( -dxy0, dxy0, 0 ); if ( x[1] && y[1] ) d(i,j,k) = Vec( -dxy0, -dxy0, 0 ); if ( x[0] && z[0] ) d(i,j,k) = Vec( dxz0, 0, dxz0 ); if ( x[0] && z[1] ) d(i,j,k) = Vec( dxz0, 0, -dxz0 ); if ( x[1] && z[0] ) d(i,j,k) = Vec( -dxz0, 0, dxz0 ); if ( x[1] && z[1] ) d(i,j,k) = Vec( -dxz0, 0, -dxz0 ); if ( y[0] && z[0] ) d(i,j,k) = Vec( 0, dyz0, dyz0 ); if ( y[0] && z[1] ) d(i,j,k) = Vec( 0, dyz0, -dyz0 ); if ( y[1] && z[0] ) d(i,j,k) = Vec( 0, -dyz0, dyz0 ); if ( y[1] && z[1] ) d(i,j,k) = Vec( 0, -dyz0, -dyz0 );*/ } } } } /****************************************************************** * Vector-based distance calculation * * Update interior cells * ******************************************************************/ static double calcVecUpdateInterior(Array &d, double dx, double dy, double dz) { double err = 0; int Nx = d.size(0); int Ny = d.size(1); int Nz = d.size(2); // Propagate (+,+,+) for (int k = 1; k < Nz; k++) { for (int j = 1; j < Ny; j++) { for (int i = 1; i < Nx; i++) { auto vx = d(i - 1, j, k); auto vy = d(i, j - 1, k); auto vz = d(i, j, k - 1); vx.x += dx; vy.y += dy; vz.z += dz; auto v = std::min(std::min(vx, vy), vz); double d1 = v.norm2(); double d2 = d(i, j, k).norm2(); if (d1 < d2) { d(i, j, k) = v; err = std::max(err, sqrt(d2) - sqrt(d1)); } } } } // Propagate (-,-,-) for (int k = Nz - 2; k >= 0; k--) { for (int j = Ny - 2; j >= 0; j--) { for (int i = Nx - 2; i >= 0; i--) { auto vx = d(i + 1, j, k); auto vy = d(i, j + 1, k); auto vz = d(i, j, k + 1); vx.x -= dx; vy.y -= dy; vz.z -= dz; auto v = std::min(std::min(vx, vy), vz); double d1 = v.norm2(); double d2 = d(i, j, k).norm2(); if (d1 < d2) { d(i, j, k) = v; err = std::max(err, sqrt(d2) - sqrt(d1)); } } } } return err; } /****************************************************************** * Vector-based distance calculation * ******************************************************************/ void CalcVecDist(Array &d, const Array &ID0, const Domain &Dm, const std::array &periodic, const std::array &dx) { std::array N = {Dm.Nx, Dm.Ny, Dm.Nz}; std::array n = {Dm.Nx - 2, Dm.Ny - 2, Dm.Nz - 2}; // Create ID with ghosts Array ID(N[0], N[1], N[2]); fillHalo fillDataID(Dm.Comm, Dm.rank_info, n, {1, 1, 1}, 50, 1, {true, true, true}, periodic); fillDataID.copy(ID0, ID); // Fill ghosts with nearest neighbor for (int k = 1; k < N[2] - 1; k++) { for (int j = 1; j < N[1] - 1; j++) { ID(0, j, k) = ID(1, j, k); ID(N[0] - 1, j, k) = ID(N[0] - 2, j, k); } } for (int k = 1; k < N[2] - 1; k++) { for (int i = 0; i < N[0]; i++) { ID(i, 0, k) = ID(i, 1, k); ID(i, N[1] - 1, k) = ID(i, N[1] - 2, k); } } for (int i = 0; i < N[0]; i++) { for (int j = 0; j < N[1]; j++) { ID(i, j, 0) = ID(i, j, 1); ID(i, j, N[2] - 1) = ID(i, j, N[2] - 2); } } // Communicate ghosts fillDataID.fill(ID); // Create communicator for distance fillHalo fillData(Dm.Comm, Dm.rank_info, n, {1, 1, 1}, 50, 1, {true, false, false}, periodic); // Calculate the local distances calcVecInitialize(d, ID, dx[0], dx[1], dx[2]); double err = 1e100; double tol = 0.5 * std::min(std::min(dx[0], dx[1]), dx[2]); for (int it = 0; it <= 50 && err > tol; it++) { err = calcVecUpdateInterior(d, dx[0], dx[1], dx[2]); } // Calculate the global distances int N_it = Dm.nprocx() + Dm.nprocy() + Dm.nprocz() + 100; for (int it = 0; it < N_it; it++) { // Update ghosts fillData.fill(d); // Update distance double err = calcVecUpdateInterior(d, dx[0], dx[1], dx[2]); // Check if we are finished err = Dm.Comm.maxReduce(err); if (err < tol) break; } } // Explicit instantiations template void CalcDist(Array &, const Array &, const Domain &, const std::array &, const std::array &); template void CalcDist(Array &, const Array &, const Domain &, const std::array &, const std::array &);