// // Class ArbitraryDomain // Interface to iterative solver and boundary geometry // for space charge calculation // // Copyright (c) 2008, Yves Ineichen, ETH Zürich, // 2013 - 2015, Tülin Kaman, Paul Scherrer Institut, Villigen PSI, Switzerland // 2016, Daniel Winklehner, Massachusetts Institute of Technology // 2017 - 2020, Paul Scherrer Institut, Villigen PSI, Switzerland // All rights reserved // // Implemented as part of the master thesis // "A Parallel Multigrid Solver for Beam Dynamics" // and the paper // "A fast parallel Poisson solver on irregular domains applied to beam dynamics simulations" // (https://doi.org/10.1016/j.jcp.2010.02.022) // // This file is part of OPAL. // // OPAL 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. // // You should have received a copy of the GNU General Public License // along with OPAL. If not, see . // //#define DEBUG_INTERSECT_RAY_BOUNDARY #include "Solvers/ArbitraryDomain.h" #include "Structure/BoundaryGeometry.h" #include #include #include #include "Utilities/OpalException.h" #include "Index/NDIndex.h" ArbitraryDomain::ArbitraryDomain( BoundaryGeometry * bgeom, IntVector_t nr, Vector_t hr, std::string interpl) : IrregularDomain(nr, hr, interpl) { bgeom_m = bgeom; setRangeMin(bgeom->getmincoords()); setRangeMax(bgeom->getmaxcoords()); bool have_inside_pt = bgeom->getInsidePoint(globalInsideP0_m); if (have_inside_pt == false) { throw OpalException( "ArbitraryDomain::ArbitraryDomain()", "No point inside geometry found/set!"); } throw OpalException("ArbitraryDomain::ArbitraryDomain()", "This domain is currently not available."); } ArbitraryDomain::~ArbitraryDomain() { //nothing so far } void ArbitraryDomain::compute(Vector_t hr, NDIndex<3> localId){ INFOMSG(level2 << "* Starting the Boundary Intersection Tests..." << endl); setHr(hr); int zGhostOffsetLeft = (localId[2].first()== 0) ? 0 : 1; int zGhostOffsetRight = (localId[2].last() == nr_m[2] - 1) ? 0 : 1; int yGhostOffsetLeft = (localId[1].first()== 0) ? 0 : 1; int yGhostOffsetRight = (localId[1].last() == nr_m[1] - 1) ? 0 : 1; int xGhostOffsetLeft = (localId[0].first()== 0) ? 0 : 1; int xGhostOffsetRight = (localId[0].last() == nr_m[0] - 1) ? 0 : 1; hasGeometryChanged_m = true; intersectLoX_m.clear(); intersectHiX_m.clear(); intersectLoY_m.clear(); intersectHiY_m.clear(); intersectLoZ_m.clear(); intersectHiZ_m.clear(); // Calculate intersection Vector_t P, dir, I; Vector_t P0 = globalInsideP0_m; // We cannot assume that the geometry is symmetric about the xy, xz, and yz planes! // In my spiral inflector simulation, this is not the case for z direction for // example (-0.13 to +0.025). -DW for (int idz = localId[2].first()-zGhostOffsetLeft; idz <= localId[2].last()+zGhostOffsetRight; idz++) { P[2] = getZRangeMin() + (idz + 0.5) * hr[2]; for (int idy = localId[1].first()-yGhostOffsetLeft; idy <= localId[1].last()+yGhostOffsetRight; idy++) { P[1] = getYRangeMin() + (idy + 0.5) * hr[1]; for (int idx = localId[0].first()-xGhostOffsetLeft; idx <= localId[0].last()+xGhostOffsetRight; idx++) { P[0] = getXRangeMin() + (idx + 0.5) * hr[0]; if (bgeom_m->fastIsInside(P0, P) % 2 == 0) { // Fill the map with true or false values for very fast isInside tests // during the rest of the fieldsolve. isInsideMap_m[toCoordIdx(idx, idy, idz)] = true; // Replace the old reference point with the new point (which we know is // inside because we just tested for it. This makes the algorithm faster // because fastIsInside() creates a number of segments that depends on the // distance between P and P0. Using the previous P as the new P0 // assures the smallest possible distance in most cases. -DW P0 = P; std::tuple pos(idx, idy, idz); dir = Vector_t(0, 0, 1); if (bgeom_m->intersectRayBoundary(P, dir, I)) { intersectHiZ_m.insert(std::pair< std::tuple, double >(pos, I[2])); } else { #ifdef DEBUG_INTERSECT_RAY_BOUNDARY *gmsg << "zdir=+1 " << dir << " x,y,z= " << idx << "," << idy << "," << idz << " P=" << P <<" I=" << I << endl; #endif } if (bgeom_m->intersectRayBoundary(P, -dir, I)) { intersectLoZ_m.insert(std::pair< std::tuple, double >(pos, I[2])); } else { #ifdef DEBUG_INTERSECT_RAY_BOUNDARY *gmsg << "zdir=-1 " << -dir << " x,y,z= " << idx << "," << idy << "," << idz << " P=" << P <<" I=" << I << endl; #endif } dir = Vector_t(0, 1, 0); if (bgeom_m->intersectRayBoundary(P, dir, I)) { intersectHiY_m.insert(std::pair< std::tuple, double >(pos, I[1])); } else { #ifdef DEBUG_INTERSECT_RAY_BOUNDARY *gmsg << "ydir=+1 " << dir << " x,y,z= " << idx << "," << idy << "," << idz << " P=" << P <<" I=" << I << endl; #endif } if (bgeom_m->intersectRayBoundary(P, -dir, I)) { intersectLoY_m.insert(std::pair< std::tuple, double >(pos, I[1])); } else { #ifdef DEBUG_INTERSECT_RAY_BOUNDARY *gmsg << "ydir=-1" << -dir << " x,y,z= " << idx << "," << idy << "," << idz << " P=" << P <<" I=" << I << endl; #endif } dir = Vector_t(1, 0, 0); if (bgeom_m->intersectRayBoundary(P, dir, I)) { intersectHiX_m.insert(std::pair< std::tuple, double >(pos, I[0])); } else { #ifdef DEBUG_INTERSECT_RAY_BOUNDARY *gmsg << "xdir=+1 " << dir << " x,y,z= " << idx << "," << idy << "," << idz << " P=" << P <<" I=" << I << endl; #endif } if (bgeom_m->intersectRayBoundary(P, -dir, I)){ intersectLoX_m.insert(std::pair< std::tuple, double >(pos, I[0])); } else { #ifdef DEBUG_INTERSECT_RAY_BOUNDARY *gmsg << "xdir=-1 " << -dir << " x,y,z= " << idx << "," << idy << "," << idz << " P=" << P <<" I=" << I << endl; #endif } } else { isInsideMap_m[toCoordIdx(idx, idy, idz)] = false; #ifdef DEBUG_INTERSECT_RAY_BOUNDARY *gmsg << "OUTSIDE" << " x,y,z= " << idx << "," << idy << "," << idz << " P=" << P <<" I=" << I << endl; #endif } } } } INFOMSG(level2 << "* Finding number of ghost nodes to the left..." << endl); //number of ghost nodes to the left int numGhostNodesLeft = 0; if(localId[2].first() != 0) { for(int idx = 0; idx < nr_m[0]; idx++) { for(int idy = 0; idy < nr_m[1]; idy++) { if(isInside(idx, idy, localId[2].first() - zGhostOffsetLeft)) numGhostNodesLeft++; } } } INFOMSG(level2 << "* Finding number of xy points in each plane along z..." << endl); //xy points in z plane int numtotal = 0; numXY_m.clear(); for(int idz = localId[2].first(); idz <= localId[2].last(); idz++) { int numxy = 0; for(int idx = 0; idx < nr_m[0]; idx++) { for(int idy = 0; idy < nr_m[1]; idy++) { if(isInside(idx, idy, idz)) numxy++; } } numXY_m[idz-localId[2].first()] = numxy; numtotal += numxy; } int startIdx = 0; MPI_Scan(&numtotal, &startIdx, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD); startIdx -= numtotal; // Build up index and coord map idxMap_m.clear(); coordMap_m.clear(); int index = startIdx - numGhostNodesLeft; INFOMSG(level2 << "* Building up index and coordinate map..." << endl); for(int idz = localId[2].first() - zGhostOffsetLeft; idz <= localId[2].last() + zGhostOffsetRight; idz++) { for(int idy = 0; idy < nr_m[1]; idy++) { for(int idx = 0; idx < nr_m[0]; idx++) { if(isInside(idx, idy, idz)) { idxMap_m[toCoordIdx(idx, idy, idz)] = index; coordMap_m[index] = toCoordIdx(idx, idy, idz); index++; } } } } INFOMSG(level2 << "* Done." << endl); } void ArbitraryDomain::constantInterpolation(int idx, int idy, int idz, StencilValue_t& value, double& /*scaleFactor*/) const { value.west = -1/(hr_m[0]*hr_m[0]); value.east = -1/(hr_m[0]*hr_m[0]); value.north = -1/(hr_m[1]*hr_m[1]); value.south = -1/(hr_m[1]*hr_m[1]); value.front = -1/(hr_m[2]*hr_m[2]); value.back = -1/(hr_m[2]*hr_m[2]); value.center = 2/(hr_m[0]*hr_m[0]) + 2/(hr_m[1]*hr_m[1]) + 2/(hr_m[2]*hr_m[2]); if(!isInside(idx-1,idy,idz)) value.west = 0.0; if(!isInside(idx+1,idy,idz)) value.east = 0.0; if(!isInside(idx,idy+1,idz)) value.north = 0.0; if(!isInside(idx,idy-1,idz)) value.south = 0.0; if(!isInside(idx,idy,idz-1)) value.front = 0.0; if(!isInside(idx,idy,idz+1)) value.back = 0.0; } void ArbitraryDomain::linearInterpolation(int idx, int idy, int idz, StencilValue_t& value, double &scaleFactor) const { scaleFactor = 1; double cx = (idx - (nr_m[0]-1)/2.0)*hr_m[0]; double cy = (idy - (nr_m[1]-1)/2.0)*hr_m[1]; double cz = (idz - (nr_m[2]-1)/2.0)*hr_m[2]; double dx_w=hr_m[0]; double dx_e=hr_m[0]; double dy_n=hr_m[1]; double dy_s=hr_m[1]; double dz_f=hr_m[2]; double dz_b=hr_m[2]; value.center = 0.0; std::tuple coordxyz(idx, idy, idz); if (idx == nr_m[0]-1) dx_e = std::abs(intersectHiX_m.find(coordxyz)->second - cx); if (idx == 0) dx_w = std::abs(intersectLoX_m.find(coordxyz)->second - cx); if (idy == nr_m[1]-1) dy_n = std::abs(intersectHiY_m.find(coordxyz)->second - cy); if (idy == 0) dy_s = std::abs(intersectLoY_m.find(coordxyz)->second - cy); if (idz == nr_m[2]-1) dz_b = std::abs(intersectHiZ_m.find(coordxyz)->second - cz); if (idz == 0) dz_f = std::abs(intersectLoZ_m.find(coordxyz)->second - cz); if(dx_w != 0) value.west = -(dz_f + dz_b) * (dy_n + dy_s) / dx_w; else value.west = 0; if(dx_e != 0) value.east = -(dz_f + dz_b) * (dy_n + dy_s) / dx_e; else value.east = 0; if(dy_n != 0) value.north = -(dz_f + dz_b) * (dx_w + dx_e) / dy_n; else value.north = 0; if(dy_s != 0) value.south = -(dz_f + dz_b) * (dx_w + dx_e) / dy_s; else value.south = 0; if(dz_f != 0) value.front = -(dx_w + dx_e) * (dy_n + dy_s) / dz_f; else value.front = 0; if(dz_b != 0) value.back = -(dx_w + dx_e) * (dy_n + dy_s) / dz_b; else value.back = 0; //RHS scaleFactor for current 3D index //0.5* comes from discretiztaion //scaleFactor = 0.5*(dw+de)*(dn+ds)*(df+db); scaleFactor = 0.5; if(dx_w + dx_e != 0) scaleFactor *= (dx_w + dx_e); if(dy_n + dy_s != 0) scaleFactor *= (dy_n + dy_s); if(dz_f + dz_b != 0) scaleFactor *= (dz_f + dz_b); //catch the case where a point lies on the boundary double m1 = dx_w * dx_e; double m2 = dy_n * dy_s; if(dx_e == 0) m1 = dx_w; if(dx_w == 0) m1 = dx_e; if(dy_n == 0) m2 = dy_s; if(dy_s == 0) m2 = dy_n; value.center = 2 / hr_m[2]; if(dx_w != 0 || dx_e != 0) value.center *= (dx_w + dx_e); if(dy_n != 0 || dy_s != 0) value.center *= (dy_n + dy_s); if(dx_w != 0 || dx_e != 0) value.center += (dz_f + dz_b) * (dy_n + dy_s) * (dx_w + dx_e) / m1; if(dy_n != 0 || dy_s != 0) value.center += (dz_f + dz_b) * (dx_w + dx_e) * (dy_n + dy_s) / m2; }