// ATC header files #include "FE_Element.h" #include "FE_Mesh.h" #include "LammpsInterface.h" #include "ATC_Error.h" #include "OutputManager.h" #include "Utility.h" #include #include #include #include #include // Other headers #include using namespace std; using ATC_Utility::dbl_geq; using ATC_Utility::parse_min; using ATC_Utility::parse_max; using ATC_Utility::parse_minmax; using ATC_Utility::split_values; using ATC_Utility::plane_coords; using ATC_Utility::norm3; using ATC_Utility::to_string; using ATC_Utility::tolerance; namespace ATC { // constants const static double tangentTolerance = 0.01; const static double tol = 1.0e-10; // ============================================================= // class FE_Mesh // ============================================================= FE_Mesh::FE_Mesh() : decomposition_(false), lammpsPartition_(false), partitioned_(false), nNodes_(0), nNodesUnique_(0), feElement_(NULL), twoDimensional_(false), hasPlanarFaces_(false) { } // ------------------------------------------------------------- FE_Mesh::~FE_Mesh() { if (feElement_) delete feElement_; } // ------------------------------------------------------------- // modify // ------------------------------------------------------------- bool FE_Mesh::modify(int narg, char **arg) { bool match = false; if (strcmp(arg[0],"mesh")==0) { /*! \page man_mesh_create_faceset_box fix_modify AtC mesh create_faceset box \section syntax fix_modify AtC mesh create_faceset box [units] - = id to assign to the collection of FE faces - = coordinates of the bounding box that is coincident with the desired FE faces - = "in" gives inner faces to the box, "out" gives the outer faces to the box - units = option to specify real as opposed to lattice units \section examples fix_modify AtC mesh create_faceset obndy box -4.0 4.0 -12 12 -12 12 out \section description Command to assign an id to a set of FE faces. \section restrictions Only viable for rectangular grids. \section related \section default The default options are units = lattice and the use of outer faces */ /*! \page man_mesh_create_faceset_plane fix_modify AtC mesh create_faceset plane \section syntax fix_modify AtC mesh create_faceset plane [units] - = id to assign to the collection of FE faces - = coordinate directions that define plane on which faceset lies - ,, = plane is specified as the x|y|z=val1 plane bounded by the segments x|y|z = [lval2,uval2] - units = option to specify real as opposed to lattice units \section examples fix_modify AtC mesh create_faceset xyplane plane y 0 x -4 0 \section description Command to assign an id to a set of FE faces. \section restrictions Only viable for rectangular grids. \section related \section default The default option is units = lattice. */ if (strcmp(arg[1],"create_faceset")==0) { int argIdx = 2; string tag = arg[argIdx++]; if (strcmp(arg[argIdx],"plane")==0) { argIdx++; int ndir, idir[3], isgn; double xlimits[3][2]; parse_plane(argIdx, narg, arg, ndir, idir, isgn, xlimits); if (xlimits[idir[1]][0] == xlimits[idir[1]][1]) split_values(xlimits[idir[1]][0],xlimits[idir[1]][1]); if (xlimits[idir[2]][0] == xlimits[idir[2]][1]) split_values(xlimits[idir[2]][0],xlimits[idir[2]][1]); parse_units(argIdx,narg,arg, xlimits[0][0],xlimits[0][1], xlimits[1][0],xlimits[1][1], xlimits[2][0],xlimits[2][1]); if (ndir > 1) { create_faceset(tag, xlimits[idir[0]][0], idir[0], isgn, idir[1], xlimits[idir[1]][0], xlimits[idir[1]][1], idir[2], xlimits[idir[2]][0], xlimits[idir[2]][1]); } else { create_faceset(tag, xlimits[idir[0]][0], idir[0], isgn); } match = true; } // bounding_box else { if (strcmp(arg[argIdx],"box")==0) argIdx++; double xmin = parse_min(arg[argIdx++]); double xmax = parse_max(arg[argIdx++]); double ymin = parse_min(arg[argIdx++]); double ymax = parse_max(arg[argIdx++]); double zmin = parse_min(arg[argIdx++]); double zmax = parse_max(arg[argIdx++]); bool outward = true; if (narg > argIdx && (strcmp(arg[argIdx++],"in") == 0)) outward = false; parse_units(argIdx,narg,arg, xmin,xmax,ymin,ymax,zmin,zmax); create_faceset(tag, xmin, xmax, ymin, ymax, zmin, zmax, outward); match = true; } } /*! \page man_mesh_create_nodeset fix_modify AtC mesh create_nodeset \section syntax fix_modify AtC mesh create_nodeset - = id to assign to the collection of FE nodes - = coordinates of the bounding box that contains only the desired nodes \section examples fix_modify AtC mesh create_nodeset lbc -12.1 -11.9 -12 12 -12 12 \section description Command to assign an id to a set of FE nodes to be used subsequently in defining boundary conditions. \section restrictions \section related \section default Coordinates are assumed to be in lattice units. */ else if (strcmp(arg[1],"create_nodeset")==0) { int argIdx = 2; string tag = arg[argIdx++]; double xmin, xmax, ymin, ymax, zmin, zmax; if (strcmp(arg[argIdx],"plane")==0) { argIdx++; int ndir, idir[3], isgn; double xlimits[3][2]; parse_plane(argIdx, narg, arg, ndir, idir, isgn, xlimits); xmin = xlimits[0][0]; xmax = xlimits[0][1]; ymin = xlimits[1][0]; ymax = xlimits[1][1]; zmin = xlimits[2][0]; zmax = xlimits[2][1]; } else { xmin = parse_min(arg[argIdx++]); xmax = parse_max(arg[argIdx++]); ymin = parse_min(arg[argIdx++]); ymax = parse_max(arg[argIdx++]); zmin = parse_min(arg[argIdx++]); zmax = parse_max(arg[argIdx++]); } if (xmin == xmax) split_values(xmin,xmax); if (ymin == ymax) split_values(ymin,ymax); if (zmin == zmax) split_values(zmin,zmax); parse_units(argIdx,narg,arg, xmin,xmax,ymin,ymax,zmin,zmax); create_nodeset(tag, xmin, xmax, ymin, ymax, zmin, zmax); match = true; } /*! \page man_mesh_add_to_nodeset fix_modify AtC mesh add_to_nodeset \section syntax fix_modify AtC mesh add_to_nodeset - = id of FE nodeset to be added to - = coordinates of the bounding box that contains the desired nodes to be added \section examples fix_modify AtC mesh add_to_nodeset lbc -11.9 -11 -12 12 -12 12 \section description Command to add nodes to an already existing FE nodeset. \section restrictions \section related \section default Coordinates are assumed to be in lattice units. */ else if (strcmp(arg[1],"add_to_nodeset")==0) { string tag = arg[2]; double xmin = parse_min(arg[3]); double xmax = parse_max(arg[4]); double ymin = parse_min(arg[5]); double ymax = parse_max(arg[6]); double zmin = parse_min(arg[7]); double zmax = parse_max(arg[8]); add_to_nodeset(tag, xmin, xmax, ymin, ymax, zmin, zmax); match = true; } /*! \page man_mesh_create_elementset fix_modify AtC mesh create_elementset \section syntax fix_modify AtC create_elementset - = id to assign to the collection of FE element - = coordinates of the bounding box that contains only the desired elements \section examples fix_modify AtC mesh create_elementset middle -4.1 4.1 -100 100 -100 1100 \section description Command to assign an id to a set of FE elements to be used subsequently in defining material and mesh-based operations. \section restrictions Only viable for rectangular grids. \section related \section default Coordinates are assumed to be in lattice units. */ else if (strcmp(arg[1],"create_elementset")==0) { int argIdx = 2; string tag = arg[argIdx++]; double xmin = 0; double xmax = 0; double ymin = 0; double ymax = 0; double zmin = 0; double zmax = 0; if (narg > 4) { xmin = parse_min(arg[argIdx++]); xmax = parse_max(arg[argIdx++]); ymin = parse_min(arg[argIdx++]); ymax = parse_max(arg[argIdx++]); zmin = parse_min(arg[argIdx++]); zmax = parse_max(arg[argIdx++]); if (xmin == xmax) split_values(xmin,xmax); if (ymin == ymax) split_values(ymin,ymax); if (zmin == zmax) split_values(zmin,zmax); parse_units(argIdx,narg,arg, xmin,xmax,ymin,ymax,zmin,zmax); } else { string regionName = arg[argIdx++]; LammpsInterface::instance()-> region_bounds(regionName.c_str(),xmin,xmax,ymin,ymax,zmin,zmax); } create_elementset(tag, xmin, xmax, ymin, ymax, zmin, zmax); match = true; } /*! \page man_mesh_nodeset_to_elementset fix_modify AtC mesh nodeset_to_elementset \section syntax fix_modify AtC nodeset_to_elementset - = id of desired nodeset from which to create elementset - = id to assign to the collection of FE element - = flag to choose either the maximal or minimal elementset \section examples fix_modify AtC mesh nodeset_to_elementset myNodeset myElementset min \section description Command to create an elementset from an existing nodeset. Either the minimal element set of elements with all nodes in the set, or maximal element set with all elements with at least one node in the set, can be created \section restrictions None. \section related \section default Unless specified, the maximal element set is created */ else if (strcmp(arg[1],"nodeset_to_elementset")==0) { string nodesetId = arg[2]; string elementsetId = arg[3]; set elementSet; if (narg < 5) { this->nodeset_to_maximal_elementset(nodesetId,elementSet); } else { if (strcmp(arg[4],"max")==0) { this->nodeset_to_maximal_elementset(nodesetId,elementSet); } else if (strcmp(arg[4],"min")==0) { this->nodeset_to_minimal_elementset(nodesetId,elementSet); } else { return false; } } elementSetMap_[elementsetId] = elementSet; match = true; } /*! \page man_mesh_output fix_modify AtC mesh output \section syntax fix_modify AtC mesh output \section examples fix_modify AtC mesh output meshData \n \section description Command to output mesh and associated data: nodesets, facesets, and elementsets. This data is only output once upon initialization since currently the mesh is static. Creates (binary, "gold" format) Ensight output of mesh data. \section restrictions none \section related \section default none */ else if (strcmp(arg[1],"output")==0) { string outputPrefix = arg[2]; output(outputPrefix); match = true; } } return match; } // ------------------------------------------------------------- void FE_Mesh::parse_units(int & argIdx, int narg, char ** arg, double & xmin, double & xmax, double & ymin, double & ymax, double & zmin, double & zmax) { if (narg > argIdx && (strcmp(arg[argIdx++],"units") == 0)) {} else { // scale from lattice units to physical units xmin *= xscale_; xmax *= xscale_; ymin *= yscale_; ymax *= yscale_; zmin *= zscale_; zmax *= zscale_; } } // ------------------------------------------------------------- // parse plane // ------------------------------------------------------------- void FE_Mesh::parse_plane(int & argIdx, int narg, char ** arg, int & ndir, int * idir, int & isgn, double xlimits[][2]) { ndir = 0; xlimits[0][0] = parse_min("-INF"); xlimits[0][1] = parse_max("INF"); xlimits[1][0] = parse_min("-INF"); xlimits[1][1] = parse_max("INF"); xlimits[2][0] = parse_min("-INF"); xlimits[2][1] = parse_max("INF"); string n(arg[argIdx++]); int i1dir = -1, i2dir = -1, i3dir = -1; string_to_index(n, i1dir, isgn); idir[ndir++] = i1dir; idir[ndir] = (i1dir+1) % 3; idir[ndir+1] = (i1dir+2) % 3; xlimits[i1dir][0] = parse_minmax(arg[argIdx++]); xlimits[i1dir][1] = xlimits[i1dir][0]; if (narg > argIdx ) { if (string_to_index(arg[argIdx],i2dir)) { argIdx++; xlimits[i2dir][0] = parse_min(arg[argIdx++]); xlimits[i2dir][1] = parse_max(arg[argIdx++]); idir[ndir++] = i2dir; } if (narg > argIdx ) { if (string_to_index(arg[argIdx],i3dir)) { argIdx++; xlimits[i3dir][0] = parse_min(arg[argIdx++]); xlimits[i3dir][1] = parse_max(arg[argIdx++]); } } else { i3dir = 0; if ((i1dir == 0 && i2dir == 1) || (i1dir == 1 && i2dir == 0) ) { i3dir = 2; } else if ((i1dir == 0 && i2dir == 2) || (i1dir == 2 && i2dir == 0) ) { i3dir = 1; } } idir[ndir++] = i3dir; } if ((idir[0]==idir[1]) || (idir[0]==idir[2]) || (idir[1]==idir[2]) ) { throw ATC_Error( "inconsistent directions in plane:"+to_string(idir[0]+1)+" "+to_string(idir[1]+1)+" "+to_string(idir[2]+1)); } } // ------------------------------------------------------------- // initialize // ------------------------------------------------------------- void FE_Mesh::initialize(void) { bool aligned = is_aligned(); if (!aligned) { feElement_->set_projection_guess(CENTROID_LINEARIZED); ATC::LammpsInterface::instance()->print_msg_once("WARNING: mesh is not aligned with the coordinate directions atom-to-element mapping will be expensive"); // if HEX8 -> orient(); } bool twoD = is_two_dimensional(); if (twoD) { feElement_->set_projection_guess(TWOD_ANALYTIC); if (feElement_->order()< 3) hasPlanarFaces_ = true; ATC::LammpsInterface::instance()->print_msg_once(" mesh is two dimensional"); } } //----------------------------------------------------------------- //----------------------------------------------------------------- void FE_Mesh::write_mesh(string meshFile) { ofstream out; out.open(meshFile.c_str()); DENS_MAT & x = *(coordinates()); Array2D & conn = *(connectivity()); int nNodes = x.nCols(); // transpose int ndm = x.nRows(); int nElems = conn.nCols(); int nodesPerElem = conn.nRows(); // transpose out << "Coordinates " << nNodes << "\n"; for (int n = 0; n < nNodes; n++) { for (int i = 0; i < ndm; i++) { out << " " << std::setprecision(16) << x(i,n); } out << "\n"; } out << "\n"; string type = element_type(); out << "Elements " << nElems << " " << type << "\n"; for (int n = 0; n < nElems; n++) { for (int i = 0; i < nodesPerElem; i++) { out << 1+conn(i,n) << " "; } out << "\n"; } out << "\n"; if (nodeSetMap_.size()) { out << "Nodesets " << nodeSetMap_.size() <<"\n"; NODE_SET_MAP::const_iterator niter; map nodesets; for (niter = nodeSetMap_.begin(); niter != nodeSetMap_.end(); niter++) { string name = niter->first; const set & nset = niter->second; out << name << " " << nset.size() << "\n"; set::const_iterator iter; for (iter = nset.begin(); iter != nset.end(); iter++) { out << *iter << " " ; } out << "\n"; } } } // ------------------------------------------------------------- // test whether almost structured // ------------------------------------------------------------- bool FE_Mesh::is_aligned(void) const { vector foundBestMatch(nSD_,false); vector tangents(nSD_); DENS_VEC xi0(nSD_); xi0 = 0; DENS_MAT eltCoords; for (int ielem = 0; ielem < nElts_; ielem++) { element_coordinates(ielem,eltCoords); feElement_->tangents(eltCoords,xi0,tangents,true); for (unsigned i = 0; i < tangents.size(); i++) { // find maximum value for which global axis its closest to int maxIndex = 0; double maxValue = abs(tangents[i](0)); for (int j = 1; j < nSD_; j++) { if (abs(tangents[i](j)) > maxValue) { maxValue = abs(tangents[i](j)); maxIndex = j; } } // make sure no other tangent is associated with this direction if (foundBestMatch[maxIndex]) { return false; } else { foundBestMatch[maxIndex] = true; } // compute deviation from a perfectly aligned vector double error = 0.; for (int j = 1; j < nSD_; j++) { if (j != maxIndex) { error += abs(tangents[i](j)); } } error /= maxValue; if (error > tangentTolerance) { return false; } } } return true; } // ------------------------------------------------------------- // element_type // ------------------------------------------------------------- string FE_Mesh::element_type(void) const { int npe = feElement_->num_elt_nodes(); if (npe == 4) { return "TET4"; } else if (npe == 8) { return "HEX8"; } else if (npe == 20) { return "HEX20"; } else if (npe == 27) { return "HEX27"; } return "UNKNOWN"; } // ------------------------------------------------------------- // element_coordinates // ------------------------------------------------------------- void FE_Mesh::element_coordinates(const int eltID, DENS_MAT & xCoords) const { const int nne = num_nodes_per_element(); xCoords.reset(nSD_, nne, false); for (int inode=0; inodeshape_function(xi,N); x.reset(nSD_); for (int inode=0; inode & local_conn = local_face_connectivity(); xCoords.reset(nSD_, nnf, false); for (int inode=0; inode < nnf; inode++) { int id = element_connectivity_global(eltID, local_conn(faceID,inode)); for (int isd=0; isd & FE_Mesh::nodeset(const string & name) const { NODE_SET_MAP::const_iterator iter = nodeSetMap_.find(name); if (name == "all") return nodeSetAll_; else if (iter == nodeSetMap_.end()) throw ATC_Error( "No nodeset with name " + name + " found."); else return iter->second; } // ------------------------------------------------------------- // get_elementset // ------------------------------------------------------------- const set & FE_Mesh::elementset(const string & name) const { NODE_SET_MAP::const_iterator iter = elementSetMap_.find(name); if (name == "all") return elementSetAll_; else if (iter == elementSetMap_.end()) throw ATC_Error( "No elementset with name " + name + " found."); else return iter->second; } // ------------------------------------------------------------- // nodeset_to_minimal_elementset // ------------------------------------------------------------- void FE_Mesh::nodeset_to_minimal_elementset (const string & name, set & elemSet) const { if (name == "all") { for (int ielem = 0; ielem < nElts_; ielem++) { elemSet.insert(ielem); } } else { NODE_SET_MAP::const_iterator iter = nodeSetMap_.find(name); if (iter == nodeSetMap_.end()) throw ATC_Error( "No nodeset with name " + name + " found."); nodeset_to_minimal_elementset(iter->second,elemSet); if (elemSet.size()==0) { throw ATC_Error("No elements found in minimal condensation of nodeset " + name); } } } // ------------------------------------------------------------- // nodeset_to_minimal_elementset // ------------------------------------------------------------- void FE_Mesh::nodeset_to_minimal_elementset (const set & nodeSet, set & elemSet) const { int npe = num_nodes_per_element(); for (int ielem=0; ielem < nElts_; ielem++) { int inode = 0; bool in = true; while (in && inode < npe) { int node = element_connectivity_unique(ielem, inode); set::const_iterator iter = nodeSet.find(node); if (iter == nodeSet.end()) { in=false; } inode++; } if (in) elemSet.insert(ielem); } } // ------------------------------------------------------------- // nodeset_to_maximal_elementset // ------------------------------------------------------------- void FE_Mesh::nodeset_to_maximal_elementset(const string &name, set &elemSet) const { if (name == "all") { for (int ielem = 0; ielem < nElts_; ielem++) { elemSet.insert(ielem); } } else { NODE_SET_MAP::const_iterator iter = nodeSetMap_.find(name); if (iter == nodeSetMap_.end()) throw ATC_Error( "No nodeset with name " + name + " found."); nodeset_to_maximal_elementset(iter->second,elemSet); if (elemSet.size()==0) { throw ATC_Error("No elements found in maximal condensation of nodeset " + name); } } } // ------------------------------------------------------------- // nodeset_to_maximal_elementset // ------------------------------------------------------------- void FE_Mesh::nodeset_to_maximal_elementset(const set &nodeSet, set &elemSet) const { int npe = num_nodes_per_element(); for (int ielem = 0; ielem < nElts_; ielem++) { int inode = 0; bool in = false; while (!in && inode < npe) { int node = element_connectivity_unique(ielem, inode); set::const_iterator iter = nodeSet.find(node); if (iter != nodeSet.end()) { in = true; } inode++; } if (in) elemSet.insert(ielem); } } // ------------------------------------------------------------- // elementset_to_nodeset // ------------------------------------------------------------- void FE_Mesh::elementset_to_nodeset (const set & elemSet, set nodeSet) const { int npe = num_nodes_per_element(); set::const_iterator itr; for (itr = elemSet.begin(); itr != elemSet.end(); itr++) { int ielem = *itr; for (int inode=0; inode < npe; inode++) { int node = element_connectivity_global(ielem, inode); nodeSet.insert(node); } } } // ------------------------------------------------------------- // elementset_to_nodeset // ------------------------------------------------------------- void FE_Mesh::elementset_to_nodeset (const string & name, set nodeSet) const { if (name == "all") for (int ielem = 0; ielem < nElts_; ielem++) nodeSet.insert(ielem); else { ELEMENT_SET_MAP::const_iterator iter = elementSetMap_.find(name); if (iter == elementSetMap_.end()) throw ATC_Error( "No elementset with name " + name + " found."); int npe = num_nodes_per_element(); const set &elemSet = iter->second; set::const_iterator itr; for (itr = elemSet.begin(); itr != elemSet.end(); itr++) { int ielem = *itr; for (int inode=0; inode < npe; inode++) { int node = element_connectivity_unique(ielem, inode); nodeSet.insert(node); inode++; } } } } set FE_Mesh::elementset_to_nodeset (const string & name) const { set nset; elementset_to_nodeset(name,nset); return nset; } // ------------------------------------------------------------- // elementset_to_minimal_nodeset // ------------------------------------------------------------- void FE_Mesh::elementset_to_minimal_nodeset (const string & name, set & nodeSet) const { // return: set - complement_of_set if (name == "all") { return;} else { elementset_to_nodeset(name,nodeSet); set compElemSet; elementset_complement(name,compElemSet); int npe = num_nodes_per_element(); set::const_iterator itr; for (itr = compElemSet.begin(); itr != compElemSet.end(); itr++) { int ielem = *itr; for (int inode=0; inode < npe; inode++) { int node = element_connectivity_unique(ielem, inode); nodeSet.erase(node); inode++; } } } } // ------------------------------------------------------------- // elementset_complement // ------------------------------------------------------------- void FE_Mesh::elementset_complement (const string & name, set & cElemSet) const { // return: set - complement_of_set if (name == "all") { return;} else { ELEMENT_SET_MAP::const_iterator iter = elementSetMap_.find(name); if (iter == elementSetMap_.end()) throw ATC_Error( "No elementset with name " + name + " found."); const set &elemSet = iter->second; for (int ielem = 0; ielem < nElts_; ielem++) { if(elemSet.find(ielem) == elemSet.end() ) cElemSet.insert(ielem); } } } // ------------------------------------------------------------- // elementset_complement // ------------------------------------------------------------- void FE_Mesh::elementset_complement (const set & elemSet, set & cElemSet) const { for (int ielem = 0; ielem < nElts_; ielem++) { if(elemSet.find(ielem) == elemSet.end() ) cElemSet.insert(ielem); } } // ------------------------------------------------------------- // faceset_to_nodeset // ------------------------------------------------------------- void FE_Mesh::faceset_to_nodeset(const string &name, set &nodeSet) const { if (name == "all") { for (int inode = 0; inode < nNodesUnique_; inode++) nodeSet.insert(inode); } else { FACE_SET_MAP::const_iterator faceset = faceSetMap_.find(name); if (faceset == faceSetMap_.end()) throw ATC_Error( "No faceset with name " + name + " found."); const set & faceSet = faceset->second; set::const_iterator iter; Array conn; for (iter = faceSet.begin(); iter != faceSet.end(); iter++) { PAIR face = *iter; face_connectivity_unique(face,conn); for (int i = 0; i < conn.size() ; ++i) { int inode = conn(i); nodeSet.insert(inode); } } } } void FE_Mesh::faceset_to_nodeset(const set &faceSet, set &nodeSet) const { set::const_iterator iter; Array conn; for (iter = faceSet.begin(); iter != faceSet.end(); iter++) { PAIR face = *iter; face_connectivity_unique(face,conn); for (int i = 0; i < conn.size() ; ++i) { int inode = conn(i); nodeSet.insert(inode); } } } // ------------------------------------------------------------- // faceset_to_nodeset // ------------------------------------------------------------- void FE_Mesh::faceset_to_nodeset_global(const string &name, set &nodeSet) const { if (name == "all") { for (int inode = 0; inode < nNodes_; inode++) nodeSet.insert(inode); } else { FACE_SET_MAP::const_iterator faceset = faceSetMap_.find(name); if (faceset == faceSetMap_.end()) throw ATC_Error( "No faceset with name " + name + " found."); const set & faceSet = faceset->second; set::const_iterator iter; Array conn; for (iter = faceSet.begin(); iter != faceSet.end(); iter++) { PAIR face = *iter; face_connectivity(face,conn); for (int i = 0; i < conn.size() ; ++i) { int inode = conn(i); nodeSet.insert(inode); } } } } void FE_Mesh::faceset_to_nodeset_global(const set &faceSet, set &nodeSet) const { set::const_iterator iter; Array conn; for (iter = faceSet.begin(); iter != faceSet.end(); iter++) { PAIR face = *iter; face_connectivity(face,conn); for (int i = 0; i < conn.size() ; ++i) { int inode = conn(i); nodeSet.insert(inode); } } } // ------------------------------------------------------------- // get_faceset // ------------------------------------------------------------- const set &FE_Mesh::faceset(const string & name) const { FACE_SET_MAP::const_iterator iter = faceSetMap_.find(name); if (iter == faceSetMap_.end()) { throw ATC_Error( "No faceset with name " + name + " found."); } return iter->second; } // ------------------------------------------------------------- // create_nodeset // ------------------------------------------------------------- void FE_Mesh::create_nodeset(const string & name, const set & nodeSet) { // Make sure we don't already have a nodeset with this name NODE_SET_MAP::iterator iter = nodeSetMap_.find(name); if (iter != nodeSetMap_.end()) { string message("A nodeset with name " + name + " is already defined."); throw ATC_Error( message); } nodeSetMap_[name] = nodeSet; if (ATC::LammpsInterface::instance()->rank_zero()) { stringstream ss; ss << "created nodeset " << name << " with " << nodeSet.size() << " nodes"; ATC::LammpsInterface::instance()->print_msg_once(ss.str()); } } void FE_Mesh::create_nodeset(const string & name, double xmin, double xmax, double ymin, double ymax, double zmin, double zmax) { // Make sure we don't already have a nodeset with this name NODE_SET_MAP::iterator iter = nodeSetMap_.find(name); if (iter != nodeSetMap_.end()) { string message("A nodeset with name " + name + " is already defined."); throw ATC_Error( message); } set nodeSet; // Loop over nodes and add their unique id's to the set if they're // in the correct range for (int inode = 0; inode < nNodes_; inode++) { double x = nodalCoords_(0,inode); double y = nodalCoords_(1,inode); double z = nodalCoords_(2,inode); if ( (xmin <= x) && (x <= xmax) && (ymin <= y) && (y <= ymax) && (zmin <= z) && (z <= zmax) ) { int uid = globalToUniqueMap_(inode); nodeSet.insert(uid); } } if (nodeSet.size() == 0) { string message("nodeset " + name + " has zero size."); throw ATC_Error( message); } nodeSetMap_[name] = nodeSet; if (ATC::LammpsInterface::instance()->rank_zero()) { stringstream ss; ss << "created nodeset " << name << " with " << nodeSet.size() << " nodes"; ATC::LammpsInterface::instance()->print_msg_once(ss.str()); } } // ------------------------------------------------------------- // add_to_nodeset // ------------------------------------------------------------- void FE_Mesh::add_to_nodeset(const string & name, double xmin, double xmax, double ymin, double ymax, double zmin, double zmax) { // Make sure we already have a nodeset with this name NODE_SET_MAP::iterator iter = nodeSetMap_.find(name); if (iter == nodeSetMap_.end()) { string message("A nodeset with name " +name + " is not already defined."); throw ATC_Error( message); } set nodeSet; // Loop over nodes and add their unique id's to the set if they're // in the correct range for (int inode = 0; inode < nNodes_; inode++) { double x = nodalCoords_(0,inode); double y = nodalCoords_(1,inode); double z = nodalCoords_(2,inode); if ( (xmin <= x) && (x <= xmax) && (ymin <= y) && (y <= ymax) && (zmin <= z) && (z <= zmax) ) { int uid = globalToUniqueMap_(inode); nodeSet.insert(uid); } } if (nodeSet.size() == 0) { string message("nodeset " + name + " has zero size."); throw ATC_Error( message); } nodeSetMap_[name].insert(nodeSet.begin(),nodeSet.end()); if (ATC::LammpsInterface::instance()->rank_zero()) { stringstream ss; ss << "added " << nodeSet.size() << " nodes to nodeset " << name ; ATC::LammpsInterface::instance()->print_msg_once(ss.str()); } } // ------------------------------------------------------------- // create_faceset // ------------------------------------------------------------- void FE_Mesh::create_faceset(const string & name, double xmin, double xmax, double ymin, double ymax, double zmin, double zmax, bool outward) { // Make sure we don't already have a nodeset with this name FACE_SET_MAP::iterator iter = faceSetMap_.find(name); if (iter != faceSetMap_.end()) throw ATC_Error( "A faceset with name " + name + " is already defined."); set faceSet; // Loop over face and add their unique id's to the set if they concide // with region const int nf = num_faces_per_element(); const int npf = num_nodes_per_face(); const Array2D & face_conn = local_face_connectivity(); for (int ielem = 0; ielem < nElts_; ielem++) { for (int iface = 0; iface < nf; iface++) { bool in = true; bool on_xmin = true, on_xmax = true; bool on_ymin = true, on_ymax = true; bool on_zmin = true, on_zmax = true; bool x_neg = false, x_pos = false; bool y_neg = false, y_pos = false; bool z_neg = false, z_pos = false; double x,y,z; for (int inode = 0; inode < npf; inode++) { x = nodalCoords_(0,connectivity_(face_conn(iface,inode),ielem)); y = nodalCoords_(1,connectivity_(face_conn(iface,inode),ielem)); z = nodalCoords_(2,connectivity_(face_conn(iface,inode),ielem)); if ( x + tol < xmin) { in = false; break; } if ( x - tol > xmax) { in = false; break; } if ( y + tol < ymin) { in = false; break; } if ( y - tol > ymax) { in = false; break; } if ( z + tol < zmin) { in = false; break; } if ( z - tol > zmax) { in = false; break; } on_xmin = on_xmin && fabs(x-xmin) <= tol; on_xmax = on_xmax && fabs(x-xmax) <= tol; on_ymin = on_ymin && fabs(y-ymin) <= tol; on_ymax = on_ymax && fabs(y-ymax) <= tol; on_zmin = on_zmin && fabs(z-zmin) <= tol; on_zmax = on_zmax && fabs(z-zmax) <= tol; } if (in) { // note based on structured grid if (outward) { if (on_xmin && iface==0) { x_neg = true;} if (on_xmax && iface==1) { x_pos = true;} if (on_ymin && iface==2) { y_neg = true;} if (on_ymax && iface==3) { y_pos = true;} if (on_zmin && iface==4) { z_neg = true;} if (on_zmax && iface==5) { z_pos = true;} } else { if (on_xmin && iface==1) { x_pos = true;} if (on_xmax && iface==0) { x_neg = true;} if (on_ymin && iface==3) { y_pos = true;} if (on_ymax && iface==2) { y_neg = true;} if (on_zmin && iface==5) { z_pos = true;} if (on_zmax && iface==4) { z_neg = true;} } if ( (x_neg || x_pos) || (y_neg || y_pos) || (z_neg || z_pos) ) { PAIR face(ielem,iface); faceSet.insert(face); } } } } if (faceSet.empty()) throw ATC_Error( "faceset "+name+" is empty."); faceSetMap_[name] = faceSet; if (ATC::LammpsInterface::instance()->comm_rank() == 0) { stringstream ss; ss << "created faceset " << name << " with " << faceSet.size() << " faces"; ATC::LammpsInterface::instance()->print_msg_once(ss.str()); } } void FE_Mesh::create_faceset(const string & name, double xRef, int nIdx, int nSgn, int nIdx2, double x2lo, double x2hi, int nIdx3, double x3lo, double x3hi) { double xtol = tolerance(xRef); // Make sure we don't already have a faceset with this name FACE_SET_MAP::iterator iter = faceSetMap_.find(name); if (iter != faceSetMap_.end()) throw ATC_Error( "A faceset with name "+name+" is already defined."); bool finite2 = (nIdx2 >= 0); bool finite3 = (nIdx3 >= 0); set faceSet; // Loop over faces i& add unique id's to the set if concide w/ plane int nf = num_faces_per_element(); int npf = num_nodes_per_face(); const Array2D & face_conn = local_face_connectivity(); for (int ielem = 0; ielem < nElts_; ielem++) { for (int iface = 0; iface < nf; iface++) { bool in = true; // all nodes must be on the plane for (int inode = 0; inode < npf; inode++) { int node = connectivity_(face_conn(iface,inode),ielem); double x = nodalCoords_(nIdx,node); if ( fabs(x-xRef) > xtol){ in = false; break;} if (finite2) { double y = nodalCoords_(nIdx2,node); if ( y < x2lo || y > x2hi){ in = false; break;} } if (finite3) { double y = nodalCoords_(nIdx3,node); if ( y < x3lo || y > x3hi){ in = false; break;} } } // check correct orientation if (in) { if ( (nIdx == 0 && iface==0 && nSgn == -1) || (nIdx == 0 && iface==1 && nSgn == 1) || (nIdx == 1 && iface==2 && nSgn == -1) || (nIdx == 1 && iface==3 && nSgn == 1) || (nIdx == 2 && iface==4 && nSgn == -1) || (nIdx == 3 && iface==5 && nSgn == 1) ) { PAIR face(ielem,iface); faceSet.insert(face); } } } } if (faceSet.empty()) throw ATC_Error( "faceset "+name+" is empty."); faceSetMap_[name] = faceSet; if (ATC::LammpsInterface::instance()->comm_rank() == 0) { stringstream ss; ss << "created faceset " << name << " with " << faceSet.size() << " faces"; ATC::LammpsInterface::instance()->print_msg_once(ss.str()); } } // ------------------------------------------------------------- // create_elementset // ------------------------------------------------------------- void FE_Mesh::create_elementset(const string & name, double xmin, double xmax, double ymin, double ymax, double zmin, double zmax) { // Make sure we don't already have a elementset with this name ELEMENT_SET_MAP::iterator iter = elementSetMap_.find(name); if (iter != elementSetMap_.end()) { string message("An elementset with name "+name+" is already defined."); throw ATC_Error( message); } set nodeSet; // Loop over nodes and add their unique id's to the set if they're // in the correct range for (int inode = 0; inode < nNodes_; inode++) { double x = nodalCoords_(0,inode); double y = nodalCoords_(1,inode); double z = nodalCoords_(2,inode); if ( (xmin <= x) && (x <= xmax) && (ymin <= y) && (y <= ymax) && (zmin <= z) && (z <= zmax) ) { int uid = globalToUniqueMap_(inode); nodeSet.insert(uid); } } if (nodeSet.size() == 0) { string message("elementset " + name + " has zero size."); throw ATC_Error( message); } // create a minimal element set from all the nodes included in the region set elemSet; int npe = num_nodes_per_element(); for (int ielem=0; ielem < nElts_; ielem++) { int inode = 0; bool in = true; while (in && inode < npe) { int node = connectivityUnique_(inode, ielem); set::const_iterator iter = nodeSet.find(node); if (iter == nodeSet.end()) { in=false; } inode++; } if (in) elemSet.insert(ielem); } if (elemSet.size() == 0) { string message("element set " + name + " has zero size."); throw ATC_Error( message); } elementSetMap_[name] = elemSet; if (ATC::LammpsInterface::instance()->comm_rank() == 0) { stringstream ss; ss << "created elementset " << name << " with " << elemSet.size() << " elements"; ATC::LammpsInterface::instance()->print_msg_once(ss.str()); } } // ------------------------------------------------------------- // get_numIPsPerElement() // ------------------------------------------------------------- int FE_Mesh::num_ips_per_element() const { return feElement_->num_ips(); } // ------------------------------------------------------------- // get_numNodesPerElement() // ------------------------------------------------------------- int FE_Mesh::num_nodes_per_element() const { return feElement_->num_elt_nodes(); } // ------------------------------------------------------------- // get_numFacesPerElement() // ------------------------------------------------------------- int FE_Mesh::num_faces_per_element() const { return feElement_->num_faces(); } // ------------------------------------------------------------- // get_num_ips_per_face() // ------------------------------------------------------------- int FE_Mesh::num_ips_per_face() const { return feElement_->num_face_ips(); } // ------------------------------------------------------------- // get_num_nodes_per_face() // ------------------------------------------------------------- int FE_Mesh::num_nodes_per_face() const { return feElement_->num_face_nodes(); } // ------------------------------------------------------------- // mappings from element id to associated nodes // ------------------------------------------------------------- void FE_Mesh::element_connectivity_global(const int eltID, Array & nodes) const { const int npe = num_nodes_per_element(); nodes.reset(npe); // use connectivity arrays if (decomposition_ && partitioned_) { for (int inode = 0; inode < npe; inode++) { nodes(inode) = myConnectivity_(inode, map_elem_to_myElem(eltID)); } } else { for (int inode = 0; inode < npe; inode++) { nodes(inode) = connectivity_(inode, eltID); } } } // ------------------------------------------------------------- // // ------------------------------------------------------------- void FE_Mesh::element_connectivity_unique(const int eltID, Array & nodes) const { const int npe = num_nodes_per_element(); nodes.reset(npe); // use connectivity arrays if (decomposition_ && partitioned_) { for (int inode = 0; inode < npe; inode++) { nodes(inode) = myConnectivityUnique_(inode, map_elem_to_myElem(eltID)); } } else { for (int inode = 0; inode < npe; inode++) { nodes(inode) = connectivityUnique_(inode, eltID); } } } // ------------------------------------------------------------- // // ------------------------------------------------------------- int FE_Mesh::element_connectivity_global(const int eltID, const int inode) const { if (decomposition_ && partitioned_) { return myConnectivity_(inode, map_elem_to_myElem(eltID)); } else { return connectivity_(inode, eltID); } } // ------------------------------------------------------------- // // ------------------------------------------------------------- int FE_Mesh::element_connectivity_unique(const int eltID, const int inode) const { if (decomposition_ && partitioned_) { return myConnectivityUnique_(inode, map_elem_to_myElem(eltID)); } else { return connectivityUnique_(inode, eltID); } } // ------------------------------------------------------------- // // ------------------------------------------------------------- AliasArray FE_Mesh::element_connectivity_global(const int eltID) const { if (decomposition_ && partitioned_) { return myConnectivity_.column(map_elem_to_myElem(eltID)); } else { return connectivity_.column(eltID); } } // ------------------------------------------------------------- // // ------------------------------------------------------------- AliasArray FE_Mesh::element_connectivity_unique(const int eltID) const { if (decomposition_ && partitioned_) { return myConnectivityUnique_.column(map_elem_to_myElem(eltID)); } else { return connectivityUnique_.column(eltID); } } // ------------------------------------------------------------- // local_face_connectivity() // ------------------------------------------------------------- const Array2D &FE_Mesh::local_face_connectivity() const { return feElement_->local_face_conn(); } // ------------------------------------------------------------- // maps to/from partitioned element data // ------------------------------------------------------------- int FE_Mesh::map_elem_to_myElem(int elemID) const { return elemToMyElemMap_.find(elemID)->second; } int FE_Mesh::map_myElem_to_elem(int myElemID) const { return myElts_[myElemID]; } // ------------------------------------------------------------- // shape function evaluation // ------------------------------------------------------------- // set quadrature scheme pass-through void FE_Mesh::set_quadrature(FeIntQuadrature type) { feElement_->set_quadrature(type); } // shape function evaluation void FE_Mesh::shape_functions(const VECTOR &x, DENS_VEC &N, Array &nodeList) const { // get element id from global coordinates int eltID = map_to_element(x); // call appropriate function below, with eltID shape_functions(x,eltID,N,nodeList); } void FE_Mesh::shape_functions(const DENS_VEC &x, DENS_VEC &N, DENS_MAT &dNdx, Array &nodeList) const { // get element id from global coordinates int eltID = map_to_element(x); // call appropriate function below, with eltID shape_functions(x,eltID,N,dNdx,nodeList); } void FE_Mesh::shape_functions(const VECTOR &x, const int eltID, DENS_VEC &N, Array &nodeList) const { // Get element node coordinates from mesh DENS_MAT eltCoords; element_coordinates(eltID, eltCoords); // pass through call feElement_->shape_function(eltCoords,x,N); // determine nodes which correspond to shape function indices element_connectivity_unique(eltID,nodeList); } void FE_Mesh::shape_functions(const DENS_VEC &x, const int eltID, DENS_VEC &N, DENS_MAT &dNdx, Array &nodeList) const { // Get element node coordinates from mesh DENS_MAT eltCoords; element_coordinates(eltID,eltCoords); // pass through call feElement_->shape_function(eltCoords,x,N,dNdx); // determine nodes which correspond to shp function indices element_connectivity_unique(eltID,nodeList); } void FE_Mesh::shape_function_derivatives(const DENS_VEC &x, const int eltID, DENS_MAT &dNdx, Array &nodeList) const { // Get element node coordinates from mesh DENS_MAT eltCoords; element_coordinates(eltID,eltCoords); // pass through call feElement_->shape_function_derivatives(eltCoords,x,dNdx); // determine nodes which correspond to shp function indices element_connectivity_unique(eltID,nodeList); } void FE_Mesh::shape_function(const int eltID, DENS_MAT &N, DIAG_MAT &weights) const { // unused data (but required to calc weights) vector dN; // call below function with dN to avoid duplicated code shape_function(eltID,N,dN,weights); } void FE_Mesh::shape_function(int eltID, DENS_MAT &N, vector &dN, DIAG_MAT &weights) const { // Get element node coordinates from mesh DENS_MAT eltCoords; element_coordinates(eltID,eltCoords); // pass through call feElement_->shape_function(eltCoords,N,dN,weights); } void FE_Mesh::face_shape_function(const PAIR &face, DENS_MAT &N, DENS_MAT &n, DIAG_MAT &weights) const { int eltID = face.first; int faceID = face.second; // Get element node coordinates from mesh DENS_MAT eltCoords; element_coordinates(eltID,eltCoords); // pass through call feElement_->face_shape_function(eltCoords,faceID,N,n,weights); } void FE_Mesh::face_shape_function(const PAIR &face, DENS_MAT &N, vector &dN, vector &Nn, DIAG_MAT &weights) const { int eltID = face.first; int faceID = face.second; // Get element node coordinates from mesh DENS_MAT eltCoords; element_coordinates(eltID,eltCoords); // pass through call feElement_->face_shape_function(eltCoords,faceID,N,dN,Nn,weights); } double FE_Mesh::face_normal(const PAIR &face, int ip, DENS_VEC &normal) const { int eltID = face.first; int faceID = face.second; // Get element node coordinates from mesh DENS_MAT eltCoords; element_coordinates(eltID,eltCoords); // pass through call double J = feElement_->face_normal(eltCoords,faceID,ip,normal); return J; } //----------------------------------------------------------------------- void FE_Mesh::output(string prefix) const { set otypes; otypes.insert(ENSIGHT); otypes.insert(FULL_GNUPLOT); OutputManager meshSets(prefix,otypes); meshSets.write_geometry(&nodalCoords_, & connectivity_); OUTPUT_LIST subsetData; int size = nNodesUnique_; // material // DENS_MAT material(nNodes_,1); // material = 1; // subsetData["material"] = &material; //string name = "global_to_unique_map"; // nodesets NODE_SET_MAP::const_iterator niter; map nodesets; for (niter = nodeSetMap_.begin(); niter != nodeSetMap_.end(); niter++) { string name = niter->first; const set & nset = niter->second; string nodeset = "nodeset_"+name; nodesets[nodeset].reset(size,1); set::const_iterator iter; for (iter = nset.begin(); iter != nset.end(); iter++) { (nodesets[nodeset])(*iter,0) = 1; } subsetData[nodeset] = & nodesets[nodeset]; } // facesets FACE_SET_MAP::const_iterator fiter; map facesets; for (fiter = faceSetMap_.begin(); fiter != faceSetMap_.end(); fiter++) { string name = fiter->first; string faceset = "faceset_"+name; facesets[faceset].reset(size,1); set nset; faceset_to_nodeset(name,nset); set::const_iterator iter; for (iter = nset.begin(); iter != nset.end(); iter++) { (facesets[faceset])(*iter,0) = 1; } subsetData[faceset] = & facesets[faceset]; } // elementsets ELEMENT_SET_MAP::const_iterator eiter; map elemsets; for (eiter = elementSetMap_.begin();eiter != elementSetMap_.end();eiter++) { string name = eiter->first; const set & eset = eiter->second; string elemset = "elementset_"+name; elemsets[elemset].reset(size,1); set::const_iterator iter; for (iter = eset.begin(); iter != eset.end(); iter++) { Array nodes; element_connectivity_unique(*iter, nodes); for(int i = 0; i < nodes.size(); ++i) { (elemsets[elemset])(nodes(i),0) = 1; } } subsetData[elemset] = & elemsets[elemset]; } // output const int * map = globalToUniqueMap_.data(); if (subsetData.size() > 0 ) meshSets.write_data(0.0,&subsetData,map); else if (LammpsInterface::instance()->comm_rank() == 0) { stringstream ss; ss << "Warning mesh output requested without any mesh entities, output suppressed"; ATC::LammpsInterface::instance()->print_msg(ss.str()); } } bool FE_Mesh::is_owned_elt(int elt) const { return (find(myElts_.begin(), myElts_.end(), elt) != myElts_.end()); } // ------------------------------------------------------------- // ------------------------------------------------------------- // class FE_3DMesh // ------------------------------------------------------------- // ------------------------------------------------------------- FE_3DMesh::FE_3DMesh(const string elementType, const int nNodes, const int nElements, const Array2D *connectivity, const DENS_MAT *nodalCoordinates, const Array periodicity, const Array< pair< string, set > > *nodeSets): FE_Mesh(), minEltSize_(0), tree_(NULL) { // Pick which element class to make if (elementType == "HEX8") { feElement_ = new FE_ElementHex(8,4,2); } else if (elementType == "HEX20") { feElement_ = new FE_ElementHex(20,8,3); } else if (elementType == "HEX27") { feElement_ = new FE_ElementHex(27,9,3); } else if (elementType == "TET4") { feElement_ = new FE_ElementTet(4,3,2); hasPlanarFaces_ = true; } else { throw ATC_Error("Unrecognized element type specified."); } nSD_ = 3; nNodes_ = nNodes; nNodesUnique_ = nNodes; nElts_ = nElements; xscale_ = yscale_ = zscale_ = 1; periodicity_ = periodicity; // Connectivity and coordinates describe the mesh geometry. connectivity_.reset(connectivity->nRows(),connectivity->nCols()); connectivity_ = (*connectivity); nodalCoords_.reset(nodalCoordinates->nRows(),nodalCoordinates->nCols()); nodalCoords_ = (*nodalCoordinates); // set minimum element size minEltSize_ = 1.e20; for (int i=0; i< connectivity_.nCols(); ++i) { int n1 = connectivity_(0,i); int n2 = connectivity_(1,i); double dx[3] = {fabs(nodalCoords_(0,n1)-nodalCoords_(0,n2)), fabs(nodalCoords_(1,n1)-nodalCoords_(1,n2)), fabs(nodalCoords_(2,n1)-nodalCoords_(2,n2))}; minEltSize_ = min(minEltSize_,norm3(dx)); } // create global-unique maps coordTol_ = this->coordinate_tolerance(); setup_periodicity(); // Create the "all" elementset, the "all" nodeset, and the read-in nodesets. for (int elem = 0; elem < nElts_; elem++) elementSetAll_.insert(elem); for (int node = 0; node < nNodesUnique_; node++) nodeSetAll_.insert(node); const Array > > & sets = *nodeSets; for (int nodeSet = 0; nodeSet < sets.size(); ++nodeSet) { const set & nset = sets(nodeSet).second; set copy; if (compactRemap_.size() > 0) { for (set::iterator itr = nset.begin(); itr != nset.end(); itr++) { copy.insert(globalToUniqueMap_(compactRemap_(*itr))); } } else { for (set::iterator itr = nset.begin(); itr != nset.end(); itr++) { copy.insert(globalToUniqueMap_(*itr)); } } create_nodeset(sets(nodeSet).first, copy); } // Insert nodes and elements into KD-tree for PIE search. if (tree_ == NULL) { tree_ = KD_Tree::create_KD_tree(feElement_->num_elt_nodes(), nNodes_, &nodalCoords_, nElts_, connectivity_); } } FE_3DMesh::~FE_3DMesh() { if (tree_) delete tree_; } // ------------------------------------------------------------- // setup_periodicity // ------------------------------------------------------------- void FE_3DMesh::setup_periodicity(double tol) { // unique <-> global id maps globalToUniqueMap_.reset(nNodes_); uniqueToGlobalMap_.reset(nNodes_); for (int node = 0; node < nNodes_; node++) { globalToUniqueMap_(node) = node; } // recursive fix and setup global to unique map if (periodicity_(2)) { fix_periodicity(3); } if (periodicity_(1)) { fix_periodicity(2); } if (periodicity_(0)) { fix_periodicity(1); } // renumber to compact unique numbering // unique nodes map to the same id with global to unique if (nNodesUnique_ < nNodes_) { int n = 0; int m = nNodesUnique_; compactRemap_.reset(nNodes_); // old to new global numbering compactRemap_ = -1; for (int node = 0; node < nNodes_; node++) { if (globalToUniqueMap_(node) == node) { // unique nodes compactRemap_(node) = n++; } else { // periodic nodes compactRemap_(node) = m++; } } if (n != nNodesUnique_) throw ATC_Error("didn't compact numbering"); int npe = num_nodes_per_element(); // temporary copy DENS_MAT coor = DENS_MAT(nodalCoords_); Array2D conn(connectivity_); Array oldGlobalToUniqueMap = globalToUniqueMap_; for (int node = 0; node < nNodes_; node++) { // unique = remap * global2unique global globalToUniqueMap_(compactRemap_(node)) = compactRemap_(oldGlobalToUniqueMap(node)); if (compactRemap_(node) < 0) throw ATC_Error("mis-map to compact numbering"); // swap coordinates for (int i = 0; i < nSD_; i++) { nodalCoords_(i,compactRemap_(node)) = coor(i,node); } } for (int elem = 0; elem < nElts_; elem++) { for (int i = 0; i < npe; i++) { connectivity_(i,elem) = compactRemap_(conn(i,elem)); } } } for (int node = 0; node < nNodes_; node++) { uniqueToGlobalMap_(globalToUniqueMap_(node)) = node; } set_unique_connectivity(); //amended_ = true; // would always be true since setup_always called } void FE_3DMesh::fix_periodicity(int idir) { set topNodes,botNodes; int ntop = find_boundary_nodes( idir,topNodes); int nbot = find_boundary_nodes(-idir,botNodes); if (ntop != nbot) throw ATC_Error("can't fix periodicity, number of top and bottom nodes are different "); bool match = match_nodes(idir,topNodes,botNodes,globalToUniqueMap_); if (!match) { stringstream ss; ss << "can't match periodic nodes with tolerance " << coordTol_; throw ATC_Error(ss.str()); } } int FE_3DMesh::find_boundary_nodes(int idir, set & nodes) { nodes.clear(); double limit = 0; int idm = abs(idir)-1; DENS_MAT & x = nodalCoords_; if (idir > 0) limit = x.row_max(idm); else limit = x.row_min(idm); for (int i=0; i < x.nCols(); ++i) { double xi = x(idm,i); if (fabs(xi-limit) < coordTol_) nodes.insert(i); } // stringstream ss; // ss << "found " << nodes.size() << " nodes at x_" << abs(idir) << "= "<< limit ; // ATC::LammpsInterface::instance()->print_msg_once(ss.str()); return nodes.size(); } bool FE_3DMesh::match_nodes(int idir, set & nodes1, set & nodes2, Array & map) { int i1=0,i2=1; plane_coords(idir-1,i1,i2); vector found(nNodes_,false); DENS_MAT & x = nodalCoords_; for (set::iterator it1=nodes1.begin(); it1 != nodes1.end(); it1++) { int n1 = *it1; double x1 = x(i1,n1); double x2 = x(i2,n1); for (set::iterator it2=nodes2.begin(); it2 != nodes2.end(); it2++) { int n2 = *it2; if (!found[n2]) { double y1 = x(i1,n2); double y2 = x(i2,n2); if (fabs(x1-y1) < coordTol_ && fabs(x2-y2) < coordTol_) { map(n1) = n2; found[n2] = true; // coincidence x(i1,n2) = x1; x(i2,n2) = x2; } } } if (map(n1) == n1) return false; } nNodesUnique_ -= nodes1.size(); stringstream ss; ss << "condensed " << nodes1.size() << " periodic nodes in the " << abs(idir) << " direction"; ATC::LammpsInterface::instance()->print_msg_once(ss.str()); return true; } void FE_3DMesh::set_unique_connectivity(void) { int numEltNodes = feElement_->num_elt_nodes(); connectivityUnique_.reset(numEltNodes, nElts_); uniqueNodeToElementMap_.reset(nNodes_); for (int node = 0; node < nNodes_; node++) { uniqueNodeToElementMap_(node) = vector(); } for (int elem = 0; elem < nElts_; elem++) { for (int node = 0; node < numEltNodes; node++) { int global_node = connectivity_(node, elem); int unique_node = globalToUniqueMap_(global_node); connectivityUnique_(node,elem) = unique_node; uniqueNodeToElementMap_(unique_node).push_back(elem); } } } // orient the local coordinate with the global one bool FE_3DMesh::orient(int idm) { int numEltNodes = feElement_->num_elt_nodes(); if (numEltNodes != 8) throw ATC_Error("can't currently orient non HEX8 elements"); DENS_MAT x; for (int elem = 0; elem < nElts_; elem++) { element_coordinates(elem,x); double xmax = x.row_max(idm); double xmin = x.row_min(idm); set top,bot; for (int node = 0; node < numEltNodes; node++) { // find top nodes if ((x(idm,node) - xmax) < coordTol_) top.insert(node); // find bottom nodes else if ((x(idm,node) - xmin) < coordTol_) bot.insert(node); else return false; } // order by rh rule // start with one } throw ATC_Error("not completely implemented function: FE_3DMesh::orient"); return true; } // ------------------------------------------------------------- // amend mesh for cut at specified faces // ------------------------------------------------------------- void FE_3DMesh::cut_mesh(const set & faceSet, const set & nodeSet) { int nsd = nSD_; // get all nodes to create an old to new number map set dupNodes; map oldToNewMap; faceset_to_nodeset_global(faceSet,dupNodes); // remove edge nodes Array & node_map = globalToUniqueMap_; set::const_iterator itr; for (itr = dupNodes.begin(); itr != dupNodes.end(); itr++) { int gnode = *itr; int unode = node_map(gnode); if (nodeSet.find(unode) != nodeSet.end()) { dupNodes.erase(gnode); } } int nNodesAdded = dupNodes.size(); // copy coordinates and nodemap DENS_MAT &coordinates = nodalCoords_; int nNodesNew = coordinates.nCols() + nNodesAdded; coordinates.resize(nsd,nNodesNew,true); node_map.resize(nNodesNew,true); // add duplicate coordinates int iNew = nNodes_; int iNewUnique = nNodesUnique_; for (itr = dupNodes.begin(); itr != dupNodes.end(); itr++,iNew++) { int iOld = *itr; oldToNewMap[iOld] = iNew; // global ids if (iOld == node_map(iOld)) { // non-image atom node_map(iNew) = iNewUnique++; } else { node_map(iNew) = -1; } for(int j = 0; j < nsd; j++) { coordinates(j,iNew) = coordinates(j,iOld); } } nNodes_ = iNew; nNodesUnique_ = iNewUnique; for (itr = dupNodes.begin(); itr != dupNodes.end(); itr++,iNew++) { int iOld = *itr; iNew = oldToNewMap[iOld]; // global ids int iOldImage = node_map(iOld); if (iOld != iOldImage) { // image atom int iNewImage = oldToNewMap[iOldImage]; node_map(iNew) = node_map(iNewImage); } } // update element connectivities const int nnf = num_nodes_per_face(); const Array2D & local_conn = feElement_->local_face_conn(); set< PAIR >::iterator iter; for (iter = faceSet.begin(); iter != faceSet.end(); iter++) { PAIR face = *iter; int eltID=face.first, faceID=face.second; for (int inode=0; inode < nnf; inode++) { int lid = local_conn(faceID,inode); int id = connectivity_(lid, eltID); if (oldToNewMap.find(id) != oldToNewMap.end() ) { int new_id = (*oldToNewMap.find(id)).second; connectivity_(lid,eltID) = new_id; connectivityUnique_(lid, eltID) = node_map(new_id); } } } } // ------------------------------------------------------------- // amend mesh for deleted elements // ------------------------------------------------------------- void FE_3DMesh::delete_elements(const set &elementList) { int nPE = num_nodes_per_element(); set elementsNew; elementset_complement(elementList,elementsNew); int nElementsNew = elementsNew.size(); set newToOld; map oldToNewMap; elementset_to_nodeset(elementsNew,newToOld); int nNodesNew = newToOld.size(); set::const_iterator itr; // coordinates & node map (from nodes to data) const DENS_MAT &coordinates = nodal_coordinates(); const Array & node_map = global_to_unique_map(); DENS_MAT *newCoords = new DENS_MAT(nSD_,nNodesNew); Array *newNodeMap = new Array (nNodesNew); Array2D * newConnectivity = new Array2D(nPE,nElementsNew); int k = 0, i = 0; for (itr = newToOld.begin(); itr != newToOld.end(); itr++) { int node = *itr; oldToNewMap[node] = i++; (*newNodeMap)(k) = node_map(node); for(int j = 0; j < nSD_; j++) { (*newCoords)(j,k) = coordinates(j,node); } k++; } // nNodes_ = nNodesNew; ??? // connectivity k = 0; for (itr = elementsNew.begin(); itr != elementsNew.end(); itr++) { int ielem = *itr; for(int j = 0; j < nPE; j++) { int old_node = connectivity_(j,ielem); map::iterator map_itr = oldToNewMap.find(old_node); if (map_itr == oldToNewMap.end()) { stringstream ss; ss << "map failure " << old_node << "\n"; ATC::LammpsInterface::instance()->print_msg(ss.str()); } int node = map_itr->second; (*newConnectivity)(j,k) = node; } k++; } delete newCoords; delete newNodeMap; delete newConnectivity; } // ------------------------------------------------------------- // partition_mesh // ------------------------------------------------------------- void FE_3DMesh::partition_mesh() { if (lammpsPartition_) { lammps_partition_mesh(); } if (partitioned_) return; int nProcs, myrank; MPI_Comm_size(MPI_COMM_WORLD, &nProcs); // get the number of processors MPI_Comm_rank(MPI_COMM_WORLD, &myrank); // get the current processor's rank // use the KD tree for partitioning, getting more blocks than // processors if (tree_ == NULL) { tree_ = KD_Tree::create_KD_tree(feElement_->num_elt_nodes(), nNodes_, &nodalCoords_, nElts_, connectivity_); } // Get the divisions of the elements from the KD-tree. int depth = ceil(log(nProcs)/log(2)); // In order to make sure there are enough divisions to evenly // divide between all processors, we get the next-highest // power of 2. vector > procEltLists = tree_->getElemIDs(depth); int numEltLists = procEltLists.size(); // Make sure the KD tree is behaving as expected. assert(numEltLists >= nProcs); // If the KD-tree was not able to return enough divisions, // duplicate the largest list. // elements, then the division would be more even. vector >::iterator it; if (numNonempty(procEltLists) < nProcs) { // Find the list of maximum size and assign it to empty processors vector maxSizeList (0); for (it = procEltLists.begin(); it != procEltLists.end(); ++it) { if (it->size() > maxSizeList.size()) maxSizeList.assign(it->begin(), it->end()); } for (it = procEltLists.begin(); it != procEltLists.end(); ++it) { if (it->empty()) { if (numNonempty(procEltLists) >= nProcs) break; it->assign(maxSizeList.begin(), maxSizeList.end()); } } } // We will store the owning processor for each element. int * eltToOwners = new int[nElts_]; for (int i = 0; i < nElts_; ++i) { eltToOwners[i] = -1; // -1 here means the element is unowned. } // Prune elements that appear on more than one processor. // Simultaneously fill in the ownership array. prune_duplicate_elements( procEltLists, eltToOwners); // If we have more lists than processors, get rid of the // extras and redistribute their elements. if (numNonempty(procEltLists) > nProcs) { redistribute_extra_proclists(procEltLists, eltToOwners, nProcs); } // Sort the lists so that the fuller ones are in the front. sort(procEltLists.begin(), procEltLists.end(), vectorCompSize); // Assign each processor a list of elements. myElts_ = procEltLists[myrank]; //mesh_distribute(eltStartIDs); delete[] eltToOwners; // We should do nodes later. if (decomposition_) distribute_mesh_data(); partitioned_ = true; } void FE_3DMesh::departition_mesh() { if (!partitioned_) return; partitioned_ = false; } void FE_3DMesh::prune_duplicate_elements(vector > & procEltLists, int * eltToOwners) { int procID = 0; vector >::iterator topIt; vector * conflictingProc; vector::iterator it, toErase; for (topIt = procEltLists.begin(); topIt != procEltLists.end(); ++topIt) { // Simultaneously fill in eltToOwners and prune, if an element // appears on multiple processors. for (it = topIt->begin(); it != topIt->end(); ++it) { // If the element has no corresponding processor in eltToOwners, // record it as belonging to processor *it. if (eltToOwners[*it] == -1) { eltToOwners[*it] = procID; } else { // If it does have a processor in eltToOwners, then we need // to remove it from either processor *topIt or from the processor // listed in eltToOwners. We discriminate based on size. conflictingProc = &(procEltLists[eltToOwners[*it]]); if (conflictingProc->size() <= procEltLists[procID].size()) { // Delete element from processor *topIt, if it has more elements. it = topIt->erase(it); --it; } else { // Delete element from conflicting processor otherwise. toErase = find(conflictingProc->begin(), conflictingProc->end(), *it); conflictingProc->erase(toErase); eltToOwners[*it] = procID; } } } ++procID; } } // ------------------------------------------------------------- // lammps_partition_mesh // ------------------------------------------------------------- void FE_3DMesh::lammps_partition_mesh() { if (LammpsInterface::instance()->domain_triclinic()) { LammpsInterface::instance()->print_msg_once("Cannot use lammps partitioning, domain is triclinic"); return; } LammpsInterface::instance()->print_msg_once("Warning: Using native lammps partitioning"); double xlo, xhi, ylo, yhi, zlo, zhi; double boxxlo, boxxhi, boxylo, boxyhi, boxzlo, boxzhi; LammpsInterface::instance()->sub_bounds(xlo, xhi, ylo, yhi, zlo, zhi); LammpsInterface::instance()->box_bounds(boxxlo, boxxhi, boxylo, boxyhi, boxzlo, boxzhi); myElts_.clear(); double xCent, yCent, zCent; // Assign elements to processors based on the centroid of the element. int numNodes = num_nodes_per_element(); for (int i = 0; i < nElts_; ++i) { xCent = 0.0; yCent = 0.0; zCent = 0.0; for (int j = 0; j < numNodes; ++ j) { xCent += nodalCoords_(0, connectivity_(j,i)); yCent += nodalCoords_(1, connectivity_(j,i)); zCent += nodalCoords_(2, connectivity_(j,i)); } xCent /= numNodes; yCent /= numNodes; zCent /= numNodes; if (xCent < boxxlo) xCent = boxxlo; if (xCent < boxxhi) xCent = boxxhi; if (yCent < boxylo) yCent = boxylo; if (yCent < boxyhi) yCent = boxyhi; if (zCent < boxzlo) zCent = boxzlo; if (zCent < boxzhi) zCent = boxzhi; if ( dbl_geq(xCent, xlo) && ((xhi == boxxhi) || !dbl_geq(xCent, xhi)) && dbl_geq(yCent, ylo) && ((yhi == boxyhi) || !dbl_geq(yCent, yhi)) && dbl_geq(zCent, zlo) && ((zhi == boxzhi) || !dbl_geq(zCent, zhi))) { myElts_.push_back(i); } } // if decomposing add in myAtomElts list based on nodal locations, i.e., all elements with a local node // myElts: for FE assembly // myAndGhost : for atom ops like restrict if (decomposition_) { set elms; vector::const_iterator itr; for (itr=myElts_.begin(); itr!=myElts_.end(); itr++) {elms.insert(*itr); } set nodes; elementset_to_nodeset(elms,nodes); elms.clear(); nodeset_to_maximal_elementset(nodes,elms); myAndGhostElts_.clear(); set::const_iterator iter; for (iter=elms.begin(); iter!=elms.end(); iter++) {myAndGhostElts_.push_back(*iter);} distribute_mesh_data(); } partitioned_ = true; return; } void FE_3DMesh::redistribute_extra_proclists(vector > &procEltLists, int *eltToOwners, int nProcs) { DENS_MAT faceAdjacencies(nElts_, num_faces_per_element()); faceAdjacencies = -1; // Set all values to -1, indicating uninitialized/uncalculated int currentElt, adjacentElt, procID; // Put all of the hobos onto one master list, allHomelessElts. list allHomelessElts; vector oneHomelessList; vector >::iterator current; int nHoboLists = numNonempty(procEltLists) - nProcs; for (int i = 0; i < nHoboLists; ++i) { current = min_element(procEltLists.begin(), procEltLists.end(), vectorCompSize); oneHomelessList = *current; allHomelessElts.insert(allHomelessElts.end(), oneHomelessList.begin(), oneHomelessList.end()); current->clear(); } // Make sure the hobos lose their association with their old processor. list::iterator it; for (it = allHomelessElts.begin(); it != allHomelessElts.end(); it++){ eltToOwners[*it] = -1; } // Figure out which elements the hobos are adjacent to. That way, they // will know what processors they can be redistributed to. compute_face_adjacencies(allHomelessElts, faceAdjacencies); // Place homeless elements onto lists that correspond to actual processors. while (!allHomelessElts.empty()) { currentElt = allHomelessElts.back(); // This will store the ID of the processor with the fewest elements // so far that has an element adjacent to currentElt. PAIR smallestProc(-1, INT_MAX); // Iterate over the faces, check the processors of adjacent elements, // and slate the element to go on the adjacent processor with the fewest // elements. for (int localFaceID = 0; localFaceID < num_faces_per_element(); ++localFaceID) { adjacentElt = faceAdjacencies(currentElt, localFaceID); // This means that there is no adjacency through this face. if (adjacentElt >= nElts_) continue; procID = eltToOwners[adjacentElt]; // The procID > -1 check makes sure we're not adjacent to another // homeless element by this face, in which case it won't have a // processor to put currentElt onto yet. if (procID > -1 && ((int) procEltLists[procID].size()) < smallestProc.second) { smallestProc = PAIR(procID, procEltLists[procID].size()); } } allHomelessElts.pop_back(); // If we couldn't find an adjacent element that had a processor, // skip for now and come back to it later. if (smallestProc.first == -1) { allHomelessElts.push_front(currentElt); } // Otherwise, put it onto the processor with the fewest elements that // we found. else { procEltLists[smallestProc.first].push_back(currentElt); eltToOwners[currentElt] = smallestProc.first; } } } void FE_3DMesh::compute_face_adjacencies(const list &elts, DENS_MAT &faceAdjacencies) { list::const_iterator cit; for (cit = elts.begin(); cit != elts.end(); ++cit) { // For each element, look at ever face, get the nodes on that face, // and find out which elements are in the intersection of elements // containing that node. Those two elements are adjacent, and we // mark it as such in the faceAdjacencies array. for (int localFaceID = 0; localFaceID < num_faces_per_element(); ++localFaceID) { Array faceNodes; face_connectivity(PAIR(*(cit), localFaceID), faceNodes); // Put the first node's elements into the accumulator to start. vector vIntersect = uniqueNodeToElementMap_(faceNodes(0)); vector vCurrent; // set_intersect requires a vector large enough to contain the // max possible intersect, which cannot be larger than the entirety // of the first vector involved. vector vTemp(vIntersect.size(), -1); // Find the intersection of each of the nodes' element vectors. for (int ithOnFace = 1; ithOnFace < num_nodes_per_face(); ++ithOnFace) { vCurrent = uniqueNodeToElementMap_(faceNodes(ithOnFace)); // Vector of elements for this node set_intersection(vCurrent.begin(), vCurrent.end(), vIntersect.begin(), vIntersect.end(), vTemp.begin()); vIntersect = vTemp; // Because we initialized the vector to be larger than necessary, maybe, // we remove all of the meaningless values used in initialization. while (vIntersect.back() == -1) vIntersect.pop_back(); vTemp.clear(); vTemp.resize(vIntersect.size(),-1); } // This means there is an adjacent face, since there // are two elements sharing all of the nodes on the face. if (vIntersect.size() == 2) { // We want to choose the element id of NOT the current // element to be listed as the adjacency. // well, but that requires more complicated memoization and // this doesn't take much extra time. if (*cit == vIntersect[0]) { faceAdjacencies(*cit, localFaceID) = vIntersect[1]; } else { faceAdjacencies(*cit, localFaceID) = vIntersect[0]; } } // This means the element is on the border. else if (vIntersect.size() == 1) { faceAdjacencies(*cit, localFaceID) = INT_MAX; } else { // This should never ever happen! The nodes should at least // share one element, since they are all on one face! // There should also never be more than two elements on the // same face... that would defy mathematics and physics in // every way. } } } } // Sometimes we want to count the number of vectors that actually // have stuff in them. We use this. int FE_3DMesh::numNonempty(vector > & v) { int result = 0; vector >::iterator it; for (it = v.begin(); it != v.end(); ++it){ if (!(it->empty())){ result++; } } return result; } int FE_3DMesh::map_to_element(const DENS_VEC &query) const { DENS_MAT eltCoords; vector candidates = tree_->find_nearest(query); vector matches = vector(); // Search through each of the nearest elements for (vector::iterator elem = candidates.begin(); elem < candidates.end(); elem++) { if (contains_point(*elem, query)) { matches.push_back(*elem); // Keep track of the elements // which contain it } } // Return the index of the parent element which does contain the point if (matches.size() == 1) { // x is conclusively in a single element return matches[0]; } else if (matches.size() == 0) { // not so much throw ATC_Error("FE_3DMesh::map_to_element could not find an element"); } else { // definitely not so much throw ATC_Error("FE_3DMesh::map_to_element found multiple elements"); } } bool FE_3DMesh::contains_point(const int eltID, const DENS_VEC &x) const { DENS_MAT eltCoords; element_coordinates(eltID, eltCoords); return feElement_->contains_point(eltCoords, x); } //----------------------------------------------------------------------- void FE_3DMesh::distribute_mesh_data() { myNElts_ = myElts_.size(); //create elemToMyElemMap_ elemToMyElemMap_.clear(); for (int myID = 0; myID < myNElts_; ++myID) { int baseID = myElts_[myID]; elemToMyElemMap_[baseID] = myID; } // create myConnectivity_, myConnectivityUnique_ int numEltNodes = feElement_->num_elt_nodes(); myConnectivity_.reset(numEltNodes, myNElts_); myConnectivityUnique_.reset(numEltNodes, myNElts_); for (int elem = 0; elem < myNElts_; ++elem) { for (int node = 0; node < numEltNodes; ++node) { myConnectivity_(node,elem) = connectivity_(node,map_myElem_to_elem(elem)); myConnectivityUnique_(node,elem) = connectivityUnique_(node,map_myElem_to_elem(elem)); } } } // ------------------------------------------------------------- // ------------------------------------------------------------- // class FE_Rectangular3DMesh // ------------------------------------------------------------- // ------------------------------------------------------------- FE_Rectangular3DMesh::FE_Rectangular3DMesh( const Array & hx, const Array & hy, const Array & hz, const double xmin, const double xmax, const double ymin, const double ymax, const double zmin, const double zmax, const Array periodicity, const double xscale, const double yscale, const double zscale) : hx_(hx), hy_(hy), hz_(hz) { tree_ = NULL; hasPlanarFaces_ = true; xscale_ = xscale; yscale_ = yscale; zscale_ = zscale; borders_[0][0] = xmin; borders_[0][1] = ymin; borders_[0][2] = zmin; borders_[1][0] = xmax; borders_[1][1] = ymax; borders_[1][2] = zmax; L_[0] = xmax-xmin; L_[1] = ymax-ymin; L_[2] = zmax-zmin; n_[0] = hx_.size(); n_[1] = hy_.size(); n_[2] = hz_.size(); // Compute region size and element size double Lx = 0; for (int i = 0; i < n_[0]; ++i) { Lx += hx_(i); } double Ly = 0; for (int i = 0; i < n_[1]; ++i) { Ly += hy_(i); } double Lz = 0; for (int i = 0; i < n_[2]; ++i) { Lz += hz_(i); } // rescale to fit box double ax = L_[0]/Lx; for (int i = 0; i < n_[0]; ++i) { hx_(i) *= ax; } double ay = L_[1]/Ly; for (int i = 0; i < n_[1]; ++i) { hy_(i) *= ay; } double az = L_[2]/Lz; for (int i = 0; i < n_[2]; ++i) { hz_(i) *= az; } // fill node locations nSD_ = 3; x_.reserve(nSD_); for (int i = 0; i < nSD_; ++i) {x_.push_back(Array(n_[i]+1)); } Array & xI = x_[0]; xI(0) = xmin; for (int i = 0; i < n_[0]; ++i) { xI(i+1) = xI(i)+hx_(i); } Array & yI = x_[1]; yI(0) = ymin; for (int i = 0; i < n_[1]; ++i) { yI(i+1) = yI(i)+hy_(i); } Array & zI = x_[2]; zI(0) = zmin; for (int i = 0; i < n_[2]; ++i) { zI(i+1) = zI(i)+hz_(i); } // Member data setup nElts_ = n_[0] * n_[1] * n_[2]; nNodes_ = (n_[0]+1) * (n_[1]+1) * (n_[2]+1); periodicity_ = Array(periodicity); feElement_ = new FE_ElementRect(); nodalCoords_.reset(3, nNodes_); connectivity_.reset(feElement_->num_elt_nodes(), nElts_); // Fill nodal coordinates double x[3] = {xmin,ymin,zmin}; int inode = 0; for (int k = 0; k <= n_[2]; ++k) { for (int j = 0; j <= n_[1]; ++j) { for (int i = 0; i <= n_[0]; ++i) { for (int m = 0; m < 3; ++m) { nodalCoords_(m,inode) = x[m]; } ++inode; if (i < n_[0]) x[0] += hx_(i); } if (j < n_[1]) x[1] += hy_(j); x[0] = xmin; } if (k < n_[2]) x[2] += hz_(k); x[1] = ymin; } // Compute element connectivities int ielt = 0; int noffx = 1; int noffy = n_[0] + 1; int noffz = (n_[0]+1) * (n_[1]+1); for (int k = 0; k < n_[2]; ++k) { for (int j = 0; j < n_[1]; ++j) { for (int i = 0; i < n_[0]; ++i) { int i1 = i + j*noffy + k*noffz; connectivity_(0,ielt) = i1; connectivity_(1,ielt) = i1 + noffx; connectivity_(2,ielt) = i1 + noffx + noffy; connectivity_(3,ielt) = i1 + noffy; connectivity_(4,ielt) = i1 + noffz; connectivity_(5,ielt) = i1 + noffx + noffz; connectivity_(6,ielt) = i1 + noffx + noffy + noffz; connectivity_(7,ielt) = i1 + noffy + noffz; ++ielt; } } } setup_periodicity(); } // ------------------------------------------------------------- // partition_mesh // ------------------------------------------------------------- void FE_Rectangular3DMesh::partition_mesh() { if (lammpsPartition_) { lammps_partition_mesh(); } if (partitioned_) return; // Currently this code has been rather naively copied from // FE_Uniform3DMesh::partition_mesh() // Determine dimensions of mesh in order to partition according to largest dimension. double xmin = borders_[0][0]; double xmax = borders_[1][0]; double ymin = borders_[0][1]; double ymax = borders_[1][1]; double zmin = borders_[0][2]; double zmax = borders_[1][2]; int numProcs; MPI_Comm_size(MPI_COMM_WORLD, &numProcs); int processorRank; MPI_Comm_rank(MPI_COMM_WORLD, &processorRank); // Spatially partition along the largest dimension. procs_.clear(); if (max(max(L_[0], L_[1]), L_[2]) == L_[0]) { partitionAxis_ = 0; for (int i = 0; i < numProcs; ++i) { procs_.push_back(xmin + (L_[0]*i)/numProcs); } procs_.push_back(xmax); } else if (max(max(L_[0], L_[1]), L_[2]) == L_[1]) { partitionAxis_ = 1; for (int i = 0; i < numProcs; ++i) { procs_.push_back(ymin + (L_[1]*i)/numProcs); } procs_.push_back(ymax); } else { partitionAxis_ = 2; for (int i = 0; i < numProcs; ++i) { procs_.push_back(zmin + (L_[2]*i)/numProcs); } procs_.push_back(zmax); } // Distribute each node to the correct processor myNodes_.clear(); for (int i = 0; i < nNodes_; ++i) { // Allocate node to this processor if it lies between processor's left and right boundaries. if ( dbl_geq(nodalCoords_(partitionAxis_, i), procs_[processorRank]) && !dbl_geq(nodalCoords_(partitionAxis_, i), procs_[processorRank + 1])) { myNodes_.push_back(i); } // Also allocate nodes on the right boundary to the last processor. if ((processorRank == numProcs - 1) && dbl_geq(nodalCoords_(partitionAxis_, i), procs_[processorRank + 1])) { myNodes_.push_back(i); } } // Distribute each element to the correct processor - assign it to the processor // which owns its node of lowest index. (this partitioning scheme is unambiguous) myElts_.clear(); for (int i = 0; i < nElts_; ++i) { int min = INT_MAX; for (int j = 0; j < connectivity_.nRows(); j++) { if (connectivity_(j, i) < min) min = connectivity_(j, i); } if (find(myNodes_.begin(), myNodes_.end(), min) != myNodes_.end()) { myElts_.push_back(i); } } /* Commented out because ghost nodes are never used and dx_ is not a member of FE_Rectangular3DMesh. // Compute the facesets that describes the left and right boundaries // in order to determine ghost nodes. int leftMult = 0; while ((leftMult+1)*dx_[partitionAxis_] < procs_[processorRank]) { ++leftMult; } int rightMult = 0; while ((rightMult)*dx_[partitionAxis_] < procs_[processorRank+1]) { ++rightMult; } // Compute our ghost nodes - nodes that we need that belong to adjacent processors, // and our shared nodes - our nodes that are ghosted on the adjacent processors. for (int i = 0; i < nNodes_; ++i) { if (nodalCoords_(partitionAxis_, i) == leftMult*dx_[partitionAxis_]) ghostNodesL_.push_back(i); else if (nodalCoords_(partitionAxis_, i) == rightMult*dx_[partitionAxis_]) ghostNodesR_.push_back(i); else if (nodalCoords_(partitionAxis_, i) == (leftMult+1)*dx_[partitionAxis_]) shareNodesL_.push_back(i); else if (nodalCoords_(partitionAxis_, i) == (rightMult-1)*dx_[partitionAxis_]) shareNodesR_.push_back(i); }*/ if (decomposition_) distribute_mesh_data(); partitioned_ = true; } void FE_Rectangular3DMesh::departition_mesh() { if (!partitioned_) return; partitioned_ = false; } // ------------------------------------------------------------- // setup_periodicity // ------------------------------------------------------------- void FE_Rectangular3DMesh::setup_periodicity() { nNodesUnique_ = 1; for (int i = 0; i < 3; i++) { nNodesUnique_ *= (n_[i] + 1 - periodicity_(i)); } // form maximal nodeset for (int i = 0; i < nNodesUnique_; i++) { nodeSetAll_.insert(i); } // Create global-to-unique map: globalToUniqueMap_(ig) = iu globalToUniqueMap_.reset(nNodes_); uniqueToGlobalMap_.reset(nNodesUnique_); for (int k = 0; k <= n_[2]; ++k) { int kper = (k == n_[2] && periodicity_(2)) ? 0 : k; for (int j = 0; j <= n_[1]; ++j) { int jper = (j == n_[1] && periodicity_(1)) ? 0 : j; for (int i = 0; i <= n_[0]; ++i) { int iper = (i == n_[0] && periodicity_(0)) ? 0 : i; int id = i + j*(n_[0]+1) + k*(n_[0]+1)*(n_[1]+1); int uid = iper + jper*(n_[0]+1-periodicity_(0)) + kper*(n_[0]+1-periodicity_(0))*(n_[1]+1-periodicity_(1)); globalToUniqueMap_(id) = uid; uniqueToGlobalMap_(uid) = id; } } } set_unique_connectivity(); // form maximal elementset for (int i = 0; i < nElts_; i++) { elementSetAll_.insert(i); } } int FE_Rectangular3DMesh::map_to_element(const DENS_VEC &x) const { int ix[3]; // ix[i] is the element in the ith direction for (int i = 0; i < 3; i++) { // map to box double y = x(i); if (periodicity_(i)) { double diff = y-borders_[0][i]; int shift = int(diff/L_[i]); if (diff < 0.) shift--; y -= shift*L_[i]; } // project into element ix[i] = x_[i].index(y); if (fabs(y-borders_[0][i]) < tol) { ix[i] = 0; } // on the lower boundary if (ix[i] < 0 || ix[i] >= n_[i]) { string msg = "FE_Rectangular3DMesh:: point maps outside of mesh, coordinate " + index_to_string(i) + "=" + to_string(x(i)) + " image=" + to_string(y) + " not in " + to_string(borders_[0][i]) + ":" + to_string(borders_[1][i]); throw ATC_Error(msg); } } int elt = ix[2]*(n_[0]*n_[1]) + ix[1]*n_[0] + ix[0]; return elt; } // ------------------------------------------------------------- // ------------------------------------------------------------- // class FE_Uniform3DMesh // ------------------------------------------------------------- // ------------------------------------------------------------- FE_Uniform3DMesh::FE_Uniform3DMesh(const int nx, const int ny, const int nz, const double xmin, const double xmax, const double ymin, const double ymax, const double zmin, const double zmax, const Array periodicity, const double xscale, const double yscale, const double zscale) { hasPlanarFaces_ = true; tree_ = NULL; xscale_ = xscale; yscale_ = yscale; zscale_ = zscale; n_[0] = nx; n_[1] = ny; n_[2] = nz; borders_[0][0] = xmin; borders_[1][0] = xmax; borders_[0][1] = ymin; borders_[1][1] = ymax; borders_[0][2] = zmin; borders_[1][2] = zmax; periodicity_ = Array(periodicity); // Compute region size and element size for (int i = 0; i < 3; i++) { L_[i] = borders_[1][i] - borders_[0][i]; dx_[i] = L_[i]/n_[i]; } // Member data setup nSD_ = 3; nElts_ = n_[0] * n_[1] * n_[2]; nNodes_ = (n_[0]+1) * (n_[1]+1) * (n_[2]+1); feElement_ = new FE_ElementRect(); nodalCoords_.reset(3, nNodes_); connectivity_.reset(feElement_->num_elt_nodes(), nElts_); // Fill nodal coordinates double ix[3]; int inode = 0; for (int k = 0; k <= n_[2]; ++k) { ix[2] = borders_[0][2] + k*dx_[2]; for (int j = 0; j <= n_[1]; ++j) { ix[1] = borders_[0][1] + j*dx_[1]; for (int i = 0; i <= n_[0]; ++i) { ix[0] = borders_[0][0] + i*dx_[0]; for (int m = 0; m < 3; ++m) { nodalCoords_(m,inode) = ix[m]; } ++inode; } } } // Compute element connectivities int ielt = 0; int noffx = 1; int noffy = n_[0] + 1; int noffz = (n_[0]+1) * (n_[1]+1); for (int k = 0; k < n_[2]; ++k) { for (int j = 0; j < n_[1]; ++j) { for (int i = 0; i < n_[0]; ++i) { int i1 = i + j*noffy + k*noffz; connectivity_(0,ielt) = i1; connectivity_(1,ielt) = i1 + noffx; connectivity_(2,ielt) = i1 + noffx + noffy; connectivity_(3,ielt) = i1 + noffy; connectivity_(4,ielt) = i1 + noffz; connectivity_(5,ielt) = i1 + noffx + noffz; connectivity_(6,ielt) = i1 + noffx + noffy + noffz; connectivity_(7,ielt) = i1 + noffy + noffz; ++ielt; } } } setup_periodicity(); } // ------------------------------------------------------------- // destructor // ------------------------------------------------------------- FE_Uniform3DMesh::~FE_Uniform3DMesh() { // Clean up is currently unimplemented } // ------------------------------------------------------------- // partition_mesh // ------------------------------------------------------------- void FE_Uniform3DMesh::partition_mesh() { if (lammpsPartition_) { lammps_partition_mesh(); } if (partitioned_) return; // Determine dimensions of mesh in order to partition according to largest dimension. double xmin = borders_[0][0]; double xmax = borders_[1][0]; double ymin = borders_[0][1]; double ymax = borders_[1][1]; double zmin = borders_[0][2]; double zmax = borders_[1][2]; int numProcs; MPI_Comm_size(MPI_COMM_WORLD, &numProcs); int processorRank; MPI_Comm_rank(MPI_COMM_WORLD, &processorRank); // Spatially partition along the largest dimension. procs_.clear(); if (max(max(L_[0], L_[1]), L_[2]) == L_[0]) { partitionAxis_ = 0; for (int i = 0; i < numProcs; ++i) { procs_.push_back(xmin + (L_[0]*i)/numProcs); } procs_.push_back(xmax); } else if (max(max(L_[0], L_[1]), L_[2]) == L_[1]) { partitionAxis_ = 1; for (int i = 0; i < numProcs; ++i) { procs_.push_back(ymin + (L_[1]*i)/numProcs); } procs_.push_back(ymax); } else { partitionAxis_ = 2; for (int i = 0; i < numProcs; ++i) { procs_.push_back(zmin + (L_[2]*i)/numProcs); } procs_.push_back(zmax); } // Distribute each node to the correct processor myNodes_.clear(); for (int i = 0; i < nNodes_; ++i) { // Allocate node to this processor if it lies between processor's left and right boundaries. if ( dbl_geq(nodalCoords_(partitionAxis_, i), procs_[processorRank]) && !dbl_geq(nodalCoords_(partitionAxis_, i), procs_[processorRank + 1])) { myNodes_.push_back(i); } // Also allocate nodes on the right boundary to the last processor. if ((processorRank == numProcs - 1) && dbl_geq(nodalCoords_(partitionAxis_, i), procs_[processorRank + 1])) { myNodes_.push_back(i); } } // Distribute each element to the correct processor - assign it to the processor // which owns its node of lowest index. (this partitioning scheme is unambiguous) myElts_.clear(); for (int i = 0; i < nElts_; ++i) { int min = INT_MAX; for (int j = 0; j < connectivity_.nRows(); j++) { if (connectivity_(j, i) < min) min = connectivity_(j, i); } if (find(myNodes_.begin(), myNodes_.end(), min) != myNodes_.end()) { myElts_.push_back(i); } } // Compute the facesets that describes the left and right boundaries // in order to determine ghost nodes. int leftMult = 0; while ((leftMult+1)*dx_[partitionAxis_] < procs_[processorRank]) { ++leftMult; } int rightMult = 0; while ((rightMult)*dx_[partitionAxis_] < procs_[processorRank+1]) { ++rightMult; } // Compute our ghost nodes - nodes that we need that belong to adjacent processors, // and our shared nodes - our nodes that are ghosted on the adjacent processors. for (int i = 0; i < nNodes_; ++i) { if (nodalCoords_(partitionAxis_, i) == leftMult*dx_[partitionAxis_]) ghostNodesL_.push_back(i); else if (nodalCoords_(partitionAxis_, i) == rightMult*dx_[partitionAxis_]) ghostNodesR_.push_back(i); else if (nodalCoords_(partitionAxis_, i) == (leftMult+1)*dx_[partitionAxis_]) shareNodesL_.push_back(i); else if (nodalCoords_(partitionAxis_, i) == (rightMult-1)*dx_[partitionAxis_]) shareNodesR_.push_back(i); } if (decomposition_) distribute_mesh_data(); partitioned_ = true; } void FE_Uniform3DMesh::departition_mesh() { if (!partitioned_) return; partitioned_ = false; } // ------------------------------------------------------------- // map_to_element // ------------------------------------------------------------- int FE_Uniform3DMesh::map_to_element(const DENS_VEC &x) const { // countx[i] is the number of the element, where 1 is the // element adjacent to the lower border, in the ith direction int countx[3]; for (int i = 0; i < 3; ++i) { // handle points on upper boundary; not sure why this is // hard-coded in, though... if (fabs(x(i)-borders_[1][i]) < tol) { countx[i] = n_[i] - 1; } else { // find the x, y, and z bins for the point in the mesh countx[i] = (int)floor((x(i)-borders_[0][i])/dx_[i]); } // handle points out-of-range [0:nx-1] w/ periodicity if (countx[i] < 0 || countx[i] >= n_[i]) { if (periodicity_(i)) { countx[i] = countx[i] % n_[i]; // handles c++ ambiguous mod problems if (countx[i] < 0) countx[i] += n_[i]; } else { string msg = " point maps outside " "of mesh, coordinate " + index_to_string(i) + " " + to_string(x(i)) + " not in " + to_string(borders_[0][i]) + " : " + to_string(borders_[1][i]); throw ATC_Error(FILELINE,msg); } } } int elt = countx[2]*(n_[0]*n_[1]) + countx[1]*n_[0] + countx[0]; return elt; } } // namespace ATC