const MeshObjTopo *FlattenTopo(const MeshObjTopo &topo) { const std::string name = topo.name; if (name == "SHELL" || name == "SHELL4") { return GetTopo("QUAD"); } else if (name == "SHELL9") { return GetTopo("QUAD9"); } else if (name == "SHELL3" || name == "TRISHELL") { return GetTopo("TRI"); } return &topo; }
MeshObjTopo *LowerTopo(const MeshObjTopo &topo) { const std::string name = topo.name; if (name == "HEX27") { return GetTopo("HEX8"); } else if (name == "TETRA10") { return GetTopo("TETRA4"); } else if (name == "QUAD9") { return GetTopo("QUAD4"); } else if (name == "TRI6") { return GetTopo("TRI3"); } return NULL; }
MeshObjTopo *GetTopo(UInt t) { // This is 1 based in topo, so zero is no topo (nodes) static const char *tmap[] = { "NULL", "HEX", "HEX27", "TETRA", "TETRA10", "TRI3", "TRI3_3D", "TRI6", "TRI6_3D", "QUAD", "QUAD_3D", "QUAD9", "QUAD9_3D", "SHELL", "SHELL9", "SHELL3", "BAR2", "BAR2_2D", "BAR2_3D", "BAR3", "BAR3_2D", "BAR3_3D" }; return GetTopo(tmap[t]); }
// Add a homogeneous edge to a topo void AddHomoEdge(MeshObjTopo *topo, std::string edge_name) { MeshObjTopo *etopo = GetTopo(edge_name); if (!etopo) Throw() << "'Topo:" << edge_name << " invalid"; topo->side_topo_list.reserve(topo->num_edges); for (int i = 0; i < topo->num_edges; i++) { topo->edge_topo_list.push_back(etopo); } }
// Add a homogeneous side to a topo void AddHomoSide(MeshObjTopo *topo, std::string side_name) { MeshObjTopo *stopo = GetTopo(side_name); if (!stopo) Throw() << "'Topo:" << side_name << " invalid"; topo->side_topo_list.reserve(topo->num_sides); for (UInt i = 0; i < topo->num_sides; i++) { topo->side_topo_list.push_back(stopo); } }
MasterElement<METraits<> > *MEFamilyDG::getME(const std::string &toponame, METraits<>) const { // First get a suitable mapping const MeshObjTopo *topo = GetTopo(toponame); if (!topo) Throw() << "DG0 get Me, couldn't get topo"; if (topo->parametric_dim == 2) { return MasterElementDG<METraits<> >::instance(ndof); } else if (topo->parametric_dim == 3) { return MasterElementDG<METraits<> >::instance(ndof); } else Throw() << "DG0 getME, unexpected pdim"; }
const MeshObjTopo *FlattenTopo(const MeshObjTopo &topo) { const std::string name = topo.name; if (name == "SHELL" || name == "SHELL4" || name == "QUAD_3D") { return GetTopo("QUAD"); } else if (name == "SHELL9") { return GetTopo("QUAD9"); } else if (name == "SHELL3" || name == "TRISHELL") { return GetTopo("TRI"); } else if (name == "BAR2_2D") { return GetTopo("BAR2"); } else if (name == "BAR2_3D") { return GetTopo("BAR2"); } if (topo.parametric_dim != topo.spatial_dim) Throw() << "Please define flatten topo for:" << topo.name; return &topo; }
// *** Convert a grid to a mesh. The staggerLoc should describe // whether the mesh is at the dual location or coincident with the // grid itself. I am not sure how this is going to work, though, // since the dual of a nice multi-patch grid could be a very bad // object, not representable by a mesh. // // o------------o-------------o // | : | : | // | : | : | // |.....x......|......x......| // | : | : | // | : | : | // o------------o-------------o // | : | : | // | : | : | // |.....x......|......x......| // | : | : | // | : | : | // o------------o-------------o // // E.G. mesh o---o, dual mesh x....x // // I think there is work here. // For the moment, we should at least be able to represent the grid itself, // and, maybe, for simple single patch grids with a periodic component, // the dual, which is not so bad. This will put us equivalent with // SCRIP. void GridToMesh(const Grid &grid_, int staggerLoc, ESMCI::Mesh &mesh, const std::vector<ESMCI::Array*> &arrays) { Trace __trace("GridToMesh(const Grid &grid_, int staggerLoc, ESMCI::Mesh &mesh)"); // Initialize the parallel environment for mesh (if not already done) ESMCI::Par::Init("MESHLOG", false /* use log */); Grid &grid = const_cast<Grid&>(grid_); bool is_sphere = grid.isSphere(); try { // *** Grid error checking here *** // *** Set some meta-data *** // We set the topological dimension of the mesh (quad = 2, hex = 3, etc...) UInt pdim = grid.getDimCount(); mesh.set_parametric_dimension(pdim); // In what dimension is the grid embedded?? (sphere = 3, simple rectangle = 2, etc...) // At this point the topological and spatial dim of the Grid is the same this should change soon UInt sdim = grid.getDimCount(); if (is_sphere) { //std::cout << "g2m, is sphere=1" << std::endl; sdim = 3; } mesh.set_spatial_dimension(sdim); // We save the nodes in a linear list so that we can access then as such // for cell creation. //TODO: NEED MAX LID METHOD SOON ->Bob std::vector<MeshObj*> nodevect(100000); std::map<UInt,UInt> ngid2lid; UInt local_node_num = 0, local_elem_num = 0; // Set the id of this processor here (me) int localrc; int rc; int me = VM::getCurrent(&localrc)->getLocalPet(); // if (ESMC_LogDefault.MsgFoundError(localrc, ESMF_ERR_PASSTHRU, &rc)) // How should I handle ESMF LogError? // return; // Keep track of locally owned, shared and shared, not-locally-owned std::vector<UInt> owned_shared; std::vector<UInt> notowned_shared; // *** Create the Nodes *** // Loop nodes of the grid. Here we loop all nodes, both owned and not. ESMCI::GridIter *gni=new ESMCI::GridIter(&grid,staggerLoc,true); // Put Local in first, so we don't have to search for duplicates for every interation, // after locals are in put in non-local // loop through all LOCAL nodes in the Grid owned by cells for(gni->toBeg(); !gni->isDone(); gni->adv()) { // Only operate on Local Nodes if (gni->isLocal()) { MeshObj *node; // get the global id of this Grid node int gid=gni->getGlobalID(); // get the local id of this Grid node int lid=gni->getLocalID(); #ifdef G2M_DBG Par::Out() << "GID=" << gid << ", LID=" << lid << std::endl; #endif // Create new node in mesh object node = new MeshObj(MeshObj::NODE, // node... gid, // unique global id local_node_num ); ngid2lid[gid] = lid; local_node_num++; node->set_owner(me); // Set owner to this proc UInt nodeset = is_sphere ? gni->getPoleID() : 0; // Do we need to partition the nodes in any sets? mesh.add_node(node, nodeset); // If Shared add to list to use DistDir on if (gni->isShared()) { // Put gid in list std::vector<UInt>::iterator lb = std::lower_bound(owned_shared.begin(), owned_shared.end(), gid); if (lb == owned_shared.end() || *lb != gid) owned_shared.insert(lb, gid); } // Put node into vector nodevect[lid]=node; } } // gni // loop through all NON-LOCAL nodes in the Grid owned by cells for(gni->toBeg(); !gni->isDone(); gni->adv()) { // Only operate on non-local nodes if (!gni->isLocal()) { MeshObj *node; // get the global id of this Grid node int gid=gni->getGlobalID(); // get the local id of this Grid node int lid=gni->getLocalID(); // If Grid node is not already in the mesh then add Mesh::MeshObjIDMap::iterator mi = mesh.map_find(MeshObj::NODE, gid); if (mi == mesh.map_end(MeshObj::NODE)) { node = new MeshObj(MeshObj::NODE, // node... gid, // unique global id local_node_num // local ID for boostrapping field data ); ngid2lid[gid] = lid; local_node_num++; node->set_owner(std::numeric_limits<UInt>::max()); // Set owner to unknown (will have to ghost later) UInt nodeset = is_sphere ? gni->getPoleID() : 0; // Do we need to partition the nodes in any sets? mesh.add_node(node, nodeset); // Node must be shared std::vector<UInt>::iterator lb = std::lower_bound(notowned_shared.begin(), notowned_shared.end(), gid); if (lb == notowned_shared.end() || *lb != gid) notowned_shared.insert(lb, gid); } else { node=&*mi; } // Put node into vector nodevect[lid]=node; } } // gni // Use DistDir to fill node owners for non-local nodes // TODO: Use nodes which are gni->isLocal() && gni->isShared() to get owners of // non-local nodes ->David // *** Create the Cells *** // Presumably, for a structured grid there is only the // 'hypercube' topology, i.e. a quadrilateral for 2d // and a hexahedron for 3d const MeshObjTopo *ctopo = 0; if (pdim == 2) { ctopo = sdim == 2 ? GetTopo("QUAD") : GetTopo("QUAD_3D"); } else if (pdim == 3) { ThrowRequire(sdim == 3); ctopo = GetTopo("HEX"); } else Throw() << "Invalid parametric dim:" << pdim; if (!ctopo) Throw() << "Could not get topology for pdim=" << pdim; // Allocate vector to hold nodes for translation std::vector<MeshObj*> nodes(ctopo->num_nodes); // Loop Cells of the grid. ESMCI::GridCellIter *gci=new ESMCI::GridCellIter(&grid,staggerLoc); for(gci->toBeg(); !gci->isDone(); gci->adv()) { MeshObj *cell = new MeshObj(MeshObj::ELEMENT, // Mesh equivalent of Cell gci->getGlobalID(), // unique global id local_elem_num++ ); #ifdef G2M_DBG Par::Out() << "Cell:" << cell->get_id() << " uses nodes:"; #endif // Set Owner cell->set_owner(me); // Get Local Ids of Corners int cnrCount; int cnrList[16]; // ONLY WORKS FOR UP TO 4D gci->getCornersCellNodeLocalID(&cnrCount, cnrList); ThrowRequire(cnrCount == ctopo->num_nodes); // Get Nodes via Local IDs for (UInt n = 0; n < ctopo->num_nodes; ++n) { nodes[n] = nodevect[cnrList[n]]; #ifdef G2M_DBG Par::Out() << nodes[n]->get_id() << " "; #endif } // n #ifdef G2M_DBG Par::Out() << std::endl; #endif UInt block_id = 1; // Any reason to use different sets for cells? mesh.add_element(cell, nodes, block_id, ctopo); } // ci // Remove any superfluous nodes mesh.remove_unused_nodes(); mesh.linearize_data_index(); // Now set up the nodal coordinates IOField<NodalField> *node_coord = mesh.RegisterNodalField(mesh, "coordinates", sdim); // Create whatever fields the user wants std::vector<IOField<NodalField>*> nfields; for (UInt i = 0; i < arrays.size(); ++i) { char buf[512]; std::sprintf(buf, "array_%03d", i); nfields.push_back( mesh.RegisterNodalField(mesh, buf, 1) ); nfields.back()->set_output_status(true); } #ifdef G2M_DBG IOField<NodalField> *de_field = mesh.RegisterNodalField(mesh, "de", 1); de_field->set_output_status(true); #endif // Loop through Mesh nodes setting up coordinates MeshDB::iterator ni = mesh.node_begin(), ne = mesh.node_end(); for (; ni != ne; ++ni) { double *c = node_coord->data(*ni); double fdata; UInt lid = ngid2lid[ni->get_id()]; // we set this above when creating the node //Par::Out() << "node:" << ni->get_id() << ", lid=" << lid; // Move to corresponding grid node gni->moveToLocalID(lid); // If local fill in coords if (gni->isLocal()) { gni->getCoord(c); double DEG2RAD = M_PI/180.0; if (is_sphere) { double lon = c[0]; double lat = c[1]; double ninety = 90.0; double theta = DEG2RAD*lon, phi = DEG2RAD*(ninety-lat); c[0] = std::cos(theta)*std::sin(phi); c[1] = std::sin(theta)*std::sin(phi); c[2] = std::cos(phi); } } else { // set to Null value to be ghosted later for (int i=0; i<sdim; i++) { // c[i]=std::numeric_limits<double>::max(); c[i]=-10; } } // Other arrays for (UInt i = 0; i < arrays.size(); ++i) { gni->getArrayData(arrays[i], &fdata); double *data = nfields[i]->data(*ni); ThrowRequire(data); data[0] = fdata; } #ifdef G2M_DBG // De field double *data = de_field->data(*ni); ThrowRequire(data); data[0] = gni->getDE(); #endif //printf("%d :: %f %f\n",gni->getGlobalID(),c[0],c[1]); } // ni // Now go back and resolve the ownership for shared nodes. We create a distdir // from the owned, shared nodes, then look up the shared, not owned. { DDir<> dir; std::vector<UInt> lids(owned_shared.size(), 0); dir.Create(owned_shared.size(), &owned_shared[0], &lids[0]); std::vector<DDir<>::dentry> lookups; dir.RemoteGID(notowned_shared.size(), ¬owned_shared[0], lookups); // Loop through the results. Do a map lookup to find nodes--since the shared // porition of the mesh is a hypersurface, this should be cheap enough to do. std::vector<DDir<>::dentry>::iterator ri = lookups.begin(), re = lookups.end(); for (; ri != re; ++ri) { DDir<>::dentry &dent = *ri; #ifdef G2M_DBG Par::Out() << "Finding owner for gid" << dent.gid << " = " << dent.origin_proc << std::endl; #endif Mesh::MeshObjIDMap::iterator mi = mesh.map_find(MeshObj::NODE, dent.gid); // ThrowRequire(mi != mesh.map_end(MeshObj::NODE)); // May have been deleted as an unused node. if (mi == mesh.map_end(MeshObj::NODE)) { #ifdef G2M_DBG Par::Out() << "\tnot in mesh!!" << std::endl; #endif continue; } mi->set_owner(dent.origin_proc); } // ri } // ddir lookup // Can now build the communication pattern. mesh.build_sym_comm_rel(MeshObj::NODE); // ** That's it. The mesh is now in the pre-commit phase, so other // fields can be registered, sides/faces can be turned on, etc, or one // can simply call mesh.Commit() and then proceed. char buf[512]; //std::sprintf(buf, "g2m.%05d.txt", ) #ifdef G2M_DBG mesh.Print(Par::Out()); #endif mesh.Commit(); { std::vector<MEField<>*> fds; Mesh::FieldReg::MEField_iterator fi = mesh.Field_begin(), fe = mesh.Field_end(); for (; fi != fe; ++fi) fds.push_back(&*fi); mesh.HaloFields(fds.size(), &fds[0]); /* MEField<> *cptr = mesh.GetCoordField(); mesh.HaloFields(1, &cptr); */ } } // try catch block catch (std::exception &x) { // Set any ESMF error codes/messages } // catch catch(...) { // Set any ESMF error codes/messages } }