boundary_info.C
Go to the documentation of this file.00001 // The libMesh Finite Element Library. 00002 // Copyright (C) 2002-2012 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner 00003 00004 // This library is free software; you can redistribute it and/or 00005 // modify it under the terms of the GNU Lesser General Public 00006 // License as published by the Free Software Foundation; either 00007 // version 2.1 of the License, or (at your option) any later version. 00008 00009 // This library is distributed in the hope that it will be useful, 00010 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00012 // Lesser General Public License for more details. 00013 00014 // You should have received a copy of the GNU Lesser General Public 00015 // License along with this library; if not, write to the Free Software 00016 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 00017 00018 00019 00020 // C++ includes 00021 #include <iterator> // std::distance 00022 00023 // Local includes 00024 #include "libmesh/libmesh_config.h" 00025 #include "libmesh/boundary_info.h" 00026 #include "libmesh/elem.h" 00027 #include "libmesh/mesh_data.h" 00028 #include "libmesh/mesh_serializer.h" 00029 #include "libmesh/parallel.h" 00030 #include "libmesh/partitioner.h" 00031 #include "libmesh/unstructured_mesh.h" 00032 00033 namespace libMesh 00034 { 00035 00036 00037 00038 //------------------------------------------------------ 00039 // BoundaryInfo static member initializations 00040 const boundary_id_type BoundaryInfo::invalid_id = -123; 00041 00042 00043 00044 //------------------------------------------------------ 00045 // BoundaryInfo functions 00046 BoundaryInfo::BoundaryInfo(const MeshBase& m) : 00047 _mesh (m) 00048 { 00049 } 00050 00051 BoundaryInfo& BoundaryInfo::operator=(const BoundaryInfo& other_boundary_info) 00052 { 00059 { 00060 std::multimap<const Node*, boundary_id_type>::const_iterator it = other_boundary_info._boundary_node_id.begin(); 00061 const std::multimap<const Node*, boundary_id_type>::const_iterator end = other_boundary_info._boundary_node_id.end(); 00062 00063 for(; it != end; ++it) 00064 { 00065 const Node * other_node = it->first; 00066 _boundary_node_id.insert 00067 (std::pair<const Node*, boundary_id_type> 00068 (_mesh.node_ptr(other_node->id()), it->second) ); 00069 } 00070 } 00071 00072 00073 { 00074 std::multimap<const Elem*, std::pair<unsigned short int, boundary_id_type> >:: 00075 const_iterator it = other_boundary_info._boundary_side_id.begin(); 00076 const std::multimap<const Elem*, std::pair<unsigned short int, boundary_id_type> >:: 00077 const_iterator end = other_boundary_info._boundary_side_id.end(); 00078 00079 for(; it != end; ++it) 00080 { 00081 const Elem * other_elem = it->first; 00082 _boundary_side_id.insert 00083 (std::pair<const Elem*, std::pair<unsigned short int, boundary_id_type> > 00084 (_mesh.elem(other_elem->id()), it->second) ); 00085 } 00086 } 00087 00088 _boundary_ids = other_boundary_info._boundary_ids; 00089 _side_boundary_ids = other_boundary_info._side_boundary_ids; 00090 _node_boundary_ids = other_boundary_info._node_boundary_ids; 00091 00092 return *this; 00093 } 00094 00095 00096 BoundaryInfo::~BoundaryInfo() 00097 { 00098 this->clear(); 00099 } 00100 00101 00102 00103 void BoundaryInfo::clear() 00104 { 00105 _boundary_node_id.clear(); 00106 _boundary_side_id.clear(); 00107 _boundary_ids.clear(); 00108 _side_boundary_ids.clear(); 00109 _node_boundary_ids.clear(); 00110 } 00111 00112 00113 00114 void BoundaryInfo::sync (UnstructuredMesh& boundary_mesh, 00115 MeshData* boundary_mesh_data, 00116 MeshData* this_mesh_data) 00117 { 00118 std::set<boundary_id_type> request_boundary_ids(_boundary_ids); 00119 request_boundary_ids.insert(invalid_id); 00120 if (!_mesh.is_serial()) 00121 CommWorld.set_union(request_boundary_ids); 00122 00123 this->sync(request_boundary_ids, boundary_mesh, 00124 boundary_mesh_data, this_mesh_data); 00125 } 00126 00127 00128 00129 void BoundaryInfo::sync (const std::set<boundary_id_type> &requested_boundary_ids, 00130 UnstructuredMesh& boundary_mesh, 00131 MeshData* boundary_mesh_data, 00132 MeshData* this_mesh_data) 00133 { 00134 START_LOG("sync()", "BoundaryInfo"); 00135 00136 boundary_mesh.clear(); 00137 00143 if (!_mesh.is_serial()) 00144 boundary_mesh.delete_remote_elements(); 00145 00152 MeshSerializer(const_cast<MeshBase&>(_mesh), boundary_mesh.is_serial()); 00153 00158 boundary_mesh.set_mesh_dimension(_mesh.mesh_dimension() - 1); 00159 00164 boundary_mesh.set_n_partitions() = _mesh.n_partitions(); 00165 00166 std::map<dof_id_type, dof_id_type> node_id_map; 00167 std::map<std::pair<dof_id_type, unsigned char>, dof_id_type> side_id_map; 00168 00169 // We'll do the same modulus trick that ParallelMesh uses to avoid 00170 // id conflicts between different processors 00171 dof_id_type next_node_id = libMesh::processor_id(), 00172 next_elem_id = libMesh::processor_id(); 00173 00174 // We'll pass through the mesh once first to build 00175 // the maps and count boundary nodes and elements 00176 // We have to examine all elements here rather than just local 00177 // elements, because it's possible to have a local boundary node 00178 // that's not on a local boundary element, e.g. at the tip of a 00179 // triangle. 00180 const MeshBase::const_element_iterator end_el = _mesh.elements_end(); 00181 for (MeshBase::const_element_iterator el = _mesh.elements_begin(); 00182 el != end_el; ++el) 00183 { 00184 const Elem *elem = *el; 00185 00186 for (unsigned char s=0; s<elem->n_sides(); s++) 00187 if (elem->neighbor(s) == NULL) // on the boundary 00188 { 00189 // Get the top-level parent for this element 00190 const Elem* top_parent = elem->top_parent(); 00191 00192 // A convenient typedef 00193 typedef 00194 std::multimap<const Elem*, std::pair<unsigned short int, boundary_id_type> >:: 00195 const_iterator Iter; 00196 00197 // Find the right id number for that side 00198 std::pair<Iter, Iter> pos = _boundary_side_id.equal_range(top_parent); 00199 00200 bool add_this_side = false; 00201 boundary_id_type this_bcid = invalid_id; 00202 00203 for (; pos.first != pos.second; ++pos.first) 00204 { 00205 this_bcid = pos.first->second.second; 00206 00207 // if this side is flagged with a boundary condition 00208 // and the user wants this id 00209 if ((pos.first->second.first == s) && 00210 (requested_boundary_ids.count(this_bcid))) 00211 { 00212 add_this_side = true; 00213 break; 00214 } 00215 } 00216 00217 // if side s wasn't found or doesn't have a boundary 00218 // condition we may still want to add it 00219 if (pos.first == pos.second) 00220 { 00221 this_bcid = invalid_id; 00222 if (requested_boundary_ids.count(this_bcid)) 00223 add_this_side = true; 00224 } 00225 00226 if (add_this_side) 00227 { 00228 std::pair<dof_id_type, unsigned char> side_pair(elem->id(), s); 00229 libmesh_assert (!side_id_map.count(side_pair)); 00230 side_id_map[side_pair] = next_elem_id; 00231 next_elem_id += libMesh::n_processors() + 1; 00232 00233 // Use a proxy element for the side to query nodes 00234 AutoPtr<Elem> side (elem->build_side(s)); 00235 for (unsigned int n = 0; n != side->n_nodes(); ++n) 00236 { 00237 Node *node = side->get_node(n); 00238 libmesh_assert(node); 00239 00240 // In parallel we only know enough to number our own nodes. 00241 if (node->processor_id() != libMesh::processor_id()) 00242 continue; 00243 00244 dof_id_type node_id = node->id(); 00245 if (!node_id_map.count(node_id)) 00246 { 00247 node_id_map[node_id] = next_node_id; 00248 next_node_id += libMesh::n_processors() + 1; 00249 } 00250 } 00251 } 00252 } 00253 } 00254 00255 // Join up the results from other processors 00256 CommWorld.set_union(side_id_map); 00257 CommWorld.set_union(node_id_map); 00258 00259 // Finally we'll pass through any unpartitioned elements to add them 00260 // to the maps and counts. 00261 next_node_id = libMesh::n_processors(); 00262 next_elem_id = libMesh::n_processors(); 00263 00264 const MeshBase::const_element_iterator end_unpartitioned_el = 00265 _mesh.pid_elements_end(DofObject::invalid_processor_id); 00266 for (MeshBase::const_element_iterator el = 00267 _mesh.pid_elements_begin(DofObject::invalid_processor_id); 00268 el != end_unpartitioned_el; ++el) 00269 { 00270 const Elem *elem = *el; 00271 00272 for (unsigned char s=0; s<elem->n_sides(); s++) 00273 if (elem->neighbor(s) == NULL) // on the boundary 00274 { 00275 // Get the top-level parent for this element 00276 const Elem* top_parent = elem->top_parent(); 00277 00278 // A convenient typedef 00279 typedef 00280 std::multimap<const Elem*, std::pair<unsigned short int, boundary_id_type> >:: 00281 const_iterator Iter; 00282 00283 // Find the right id number for that side 00284 std::pair<Iter, Iter> pos = _boundary_side_id.equal_range(top_parent); 00285 00286 bool add_this_side = false; 00287 boundary_id_type this_bcid = invalid_id; 00288 00289 for (; pos.first != pos.second; ++pos.first) 00290 { 00291 this_bcid = pos.first->second.second; 00292 // if this side is flagged with a boundary condition 00293 // and the user wants this id 00294 if ((pos.first->second.first == s) && 00295 (requested_boundary_ids.count(this_bcid))) 00296 { 00297 add_this_side = true; 00298 break; 00299 } 00300 } 00301 00302 // if side s doesn't have a boundary condition we may 00303 // still want to add it 00304 if (pos.first == pos.second) 00305 { 00306 this_bcid = invalid_id; 00307 if (requested_boundary_ids.count(this_bcid)) 00308 add_this_side = true; 00309 } 00310 00311 if (add_this_side) 00312 { 00313 std::pair<dof_id_type, unsigned char> side_pair(elem->id(), s); 00314 libmesh_assert (!side_id_map.count(side_pair)); 00315 side_id_map[side_pair] = next_elem_id; 00316 next_elem_id += libMesh::n_processors() + 1; 00317 00318 // Use a proxy element for the side to query nodes 00319 AutoPtr<Elem> side (elem->build_side(s)); 00320 for (unsigned int n = 0; n != side->n_nodes(); ++n) 00321 { 00322 Node *node = side->get_node(n); 00323 libmesh_assert(node); 00324 dof_id_type node_id = node->id(); 00325 if (!node_id_map.count(node_id)) 00326 { 00327 node_id_map[node_id] = next_node_id; 00328 next_node_id += libMesh::n_processors() + 1; 00329 } 00330 } 00331 } 00332 } 00333 } 00334 00335 // FIXME: ought to renumber side/node_id_map image to be contiguous 00336 // to save memory, also ought to reserve memory 00337 00338 // Let's add all the nodes to the boundary mesh 00339 00340 MeshBase::const_node_iterator n_end = _mesh.nodes_end(); 00341 00342 for(MeshBase::const_node_iterator n_it = _mesh.nodes_begin(); 00343 n_it != n_end; ++n_it) 00344 { 00345 const Node* node = *n_it; 00346 dof_id_type node_id = node->id(); 00347 if (node_id_map.count(node_id)) 00348 boundary_mesh.add_point(*node, node_id_map[node_id], node->processor_id()); 00349 } 00350 00351 00352 // Finally let's add the elements 00353 00354 00355 for (MeshBase::const_element_iterator el = _mesh.elements_begin(); 00356 el != end_el; ++el) 00357 { 00358 const Elem* elem = *el; 00359 00360 for (unsigned int s=0; s<elem->n_sides(); s++) 00361 if (elem->neighbor(s) == NULL) // on the boundary 00362 { 00363 // Get the top-level parent for this element 00364 const Elem* top_parent = elem->top_parent(); 00365 00366 // A convenient typedef 00367 typedef 00368 std::multimap<const Elem*, std::pair<unsigned short int, boundary_id_type> >:: 00369 const_iterator Iter; 00370 00371 // Find the right id number for that side 00372 std::pair<Iter, Iter> pos = _boundary_side_id.equal_range(top_parent); 00373 00374 bool add_this_side = false; 00375 boundary_id_type this_bcid = invalid_id; 00376 00377 for (; pos.first != pos.second; ++pos.first) 00378 { 00379 this_bcid = pos.first->second.second; 00380 00381 // if this side is flagged with a boundary condition 00382 // and the user wants this id 00383 if ((pos.first->second.first == s) && 00384 (requested_boundary_ids.count(this_bcid))) 00385 { 00386 add_this_side = true; 00387 break; 00388 } 00389 } 00390 00391 // if side s wasn't found or doesn't have a boundary 00392 // condition we may still want to add it 00393 if (pos.first == pos.second) 00394 { 00395 this_bcid = invalid_id; 00396 if (requested_boundary_ids.count(this_bcid)) 00397 add_this_side = true; 00398 } 00399 00400 if (add_this_side) 00401 { 00402 // Build the side - do not use a "proxy" element here: 00403 // This will be going into the boundary_mesh and needs to 00404 // stand on its own. 00405 AutoPtr<Elem> side (elem->build_side(s, false)); 00406 00407 side->processor_id() = elem->processor_id(); 00408 00409 const std::pair<dof_id_type, unsigned char> side_pair(elem->id(), s); 00410 00411 libmesh_assert(side_id_map.count(side_pair)); 00412 00413 side->set_id(side_id_map[side_pair]); 00414 00415 // Add the side 00416 Elem* new_elem = boundary_mesh.add_elem(side.release()); 00417 00418 // This side's Node pointers still point to the nodes of the original mesh. 00419 // We need to re-point them to the boundary mesh's nodes! Since we copied *ALL* of 00420 // the original mesh's nodes over, we should be guaranteed to have the same ordering. 00421 for (unsigned int nn=0; nn<new_elem->n_nodes(); ++nn) 00422 { 00423 // Get the correct node pointer, based on the id() 00424 Node* new_node = boundary_mesh.node_ptr(node_id_map[new_elem->node(nn)]); 00425 00426 // sanity check: be sure that the new Node exists 00427 // and its global id really matches 00428 libmesh_assert (new_node); 00429 libmesh_assert_equal_to (new_node->id(), node_id_map[new_elem->node(nn)]); 00430 00431 // Assign the new node pointer 00432 new_elem->set_node(nn) = new_node; 00433 } 00434 00435 #ifdef LIBMESH_ENABLE_AMR 00436 // Finally, set the parent and interior_parent links 00437 if (elem->parent()) 00438 { 00439 const std::pair<dof_id_type, unsigned char> parent_side_pair(elem->parent()->id(), s); 00440 00441 libmesh_assert(side_id_map.count(parent_side_pair)); 00442 00443 Elem* side_parent = boundary_mesh.elem(side_id_map[parent_side_pair]); 00444 00445 libmesh_assert(side_parent); 00446 00447 new_elem->set_parent(side_parent); 00448 00449 side_parent->set_refinement_flag(Elem::INACTIVE); 00450 00451 // Figuring out which child we are of our parent 00452 // is a trick. Due to libMesh child numbering 00453 // conventions, if we are an element on a vertex, 00454 // then we share that vertex with our parent, with 00455 // the same local index. 00456 bool found_child = false; 00457 for (unsigned int v=0; v != new_elem->n_vertices(); ++v) 00458 if (new_elem->get_node(v) == side_parent->get_node(v)) 00459 { 00460 side_parent->add_child(new_elem, v); 00461 found_child = true; 00462 } 00463 00464 // If we don't share any vertex with our parent, 00465 // then we're the fourth child (index 3) of a 00466 // triangle. 00467 if (!found_child) 00468 { 00469 libmesh_assert_equal_to (new_elem->n_vertices(), 3); 00470 side_parent->add_child(new_elem, 3); 00471 } 00472 } 00473 #endif 00474 00475 new_elem->set_interior_parent (const_cast<Elem*>(elem)); 00476 } 00477 } 00478 } 00479 00480 // When desired, copy the MeshData 00481 // to the boundary_mesh 00482 if ((boundary_mesh_data != NULL) && (this_mesh_data != NULL)) 00483 boundary_mesh_data->assign(*this_mesh_data); 00484 00485 // Don't repartition this mesh; we want it to stay in sync with the 00486 // interior partitioning. 00487 boundary_mesh.partitioner().reset(NULL); 00488 00489 // Make boundary_mesh nodes and elements contiguous 00490 boundary_mesh.prepare_for_use(/*skip_renumber =*/ false); 00491 00492 // and finally distribute element partitioning to the nodes 00493 Partitioner::set_node_processor_ids(boundary_mesh); 00494 00495 STOP_LOG("sync()", "BoundaryInfo"); 00496 } 00497 00498 00499 00500 void BoundaryInfo::add_node(const dof_id_type node, 00501 const boundary_id_type id) 00502 { 00503 this->add_node (_mesh.node_ptr(node), id); 00504 } 00505 00506 00507 00508 void BoundaryInfo::add_node(const Node* node, 00509 const boundary_id_type id) 00510 { 00511 if (id == invalid_id) 00512 { 00513 libMesh::err << "ERROR: You may not set a boundary ID of " 00514 << invalid_id << std::endl 00515 << " That is reserved for internal use.\n" 00516 << std::endl; 00517 00518 libmesh_error(); 00519 } 00520 00521 // A convenient typedef 00522 typedef std::multimap<const Node*, boundary_id_type>::const_iterator Iter; 00523 00524 // Don't add the same ID twice 00525 std::pair<Iter, Iter> pos = _boundary_node_id.equal_range(node); 00526 00527 for (;pos.first != pos.second; ++pos.first) 00528 if (pos.first->second == id) 00529 return; 00530 00531 std::pair<const Node*, boundary_id_type> kv (node, id); 00532 00533 _boundary_node_id.insert(kv); 00534 _boundary_ids.insert(id); 00535 _node_boundary_ids.insert(id); // Also add this ID to the set of node boundary IDs 00536 } 00537 00538 void BoundaryInfo::add_node(const Node* node, 00539 const std::vector<boundary_id_type>& ids) 00540 { 00541 if (ids.empty()) 00542 return; 00543 00544 libmesh_assert(node); 00545 00546 // A convenient typedef 00547 typedef std::multimap<const Node*, boundary_id_type>::const_iterator Iter; 00548 00549 // Don't add the same ID twice 00550 std::pair<Iter, Iter> pos = _boundary_node_id.equal_range(node); 00551 00552 for (unsigned int i=0; i!= ids.size(); ++i) 00553 { 00554 boundary_id_type id=ids[i]; 00555 00556 if (id == invalid_id) 00557 { 00558 libMesh::err << "ERROR: You may not set a boundary ID of " 00559 << invalid_id << std::endl 00560 << " That is reserved for internal use.\n" 00561 << std::endl; 00562 00563 libmesh_error(); 00564 } 00565 00566 bool already_inserted = false; 00567 for (Iter p = pos.first;p != pos.second; ++p) 00568 if (p->second == id) 00569 { 00570 already_inserted = true; 00571 break; 00572 } 00573 if (already_inserted) 00574 continue; 00575 00576 std::pair<const Node*, boundary_id_type> kv (node, id); 00577 00578 _boundary_node_id.insert(kv); 00579 _boundary_ids.insert(id); 00580 _node_boundary_ids.insert(id); // Also add this ID to the set of node boundary IDs 00581 } 00582 } 00583 00584 00585 void BoundaryInfo::clear_boundary_node_ids() 00586 { 00587 _boundary_node_id.clear(); 00588 } 00589 00590 void BoundaryInfo::add_side(const dof_id_type e, 00591 const unsigned short int side, 00592 const boundary_id_type id) 00593 { 00594 this->add_side (_mesh.elem(e), side, id); 00595 } 00596 00597 00598 00599 void BoundaryInfo::add_side(const Elem* elem, 00600 const unsigned short int side, 00601 const boundary_id_type id) 00602 { 00603 libmesh_assert(elem); 00604 00605 // Only add BCs for level-0 elements. 00606 libmesh_assert_equal_to (elem->level(), 0); 00607 00608 if (id == invalid_id) 00609 { 00610 libMesh::err << "ERROR: You may not set a boundary ID of " 00611 << invalid_id << std::endl 00612 << " That is reserved for internal use.\n" 00613 << std::endl; 00614 00615 libmesh_error(); 00616 } 00617 00618 // A convenient typedef 00619 typedef std::multimap<const Elem*, std::pair<unsigned short int, boundary_id_type> >:: 00620 const_iterator Iter; 00621 00622 // Don't add the same ID twice 00623 std::pair<Iter, Iter> pos = _boundary_side_id.equal_range(elem); 00624 00625 for (;pos.first != pos.second; ++pos.first) 00626 if (pos.first->second.first == side && 00627 pos.first->second.second == id) 00628 return; 00629 00630 std::pair<unsigned short int, boundary_id_type> p(side,id); 00631 std::pair<const Elem*, std::pair<unsigned short int, boundary_id_type> > 00632 kv (elem, p); 00633 00634 _boundary_side_id.insert(kv); 00635 _boundary_ids.insert(id); 00636 _side_boundary_ids.insert(id); // Also add this ID to the set of side boundary IDs 00637 } 00638 00639 00640 00641 void BoundaryInfo::add_side(const Elem* elem, 00642 const unsigned short int side, 00643 const std::vector<boundary_id_type>& ids) 00644 { 00645 if (ids.empty()) 00646 return; 00647 00648 libmesh_assert(elem); 00649 00650 // Only add BCs for level-0 elements. 00651 libmesh_assert_equal_to (elem->level(), 0); 00652 00653 // A convenient typedef 00654 typedef std::multimap<const Elem*, std::pair<unsigned short int, boundary_id_type> >:: 00655 const_iterator Iter; 00656 00657 // Don't add the same ID twice 00658 std::pair<Iter, Iter> pos = _boundary_side_id.equal_range(elem); 00659 00660 for (unsigned int i=0; i!= ids.size(); ++i) 00661 { 00662 boundary_id_type id=ids[i]; 00663 00664 if (id == invalid_id) 00665 { 00666 libMesh::err << "ERROR: You may not set a boundary ID of " 00667 << invalid_id << std::endl 00668 << " That is reserved for internal use.\n" 00669 << std::endl; 00670 00671 libmesh_error(); 00672 } 00673 00674 bool already_inserted = false; 00675 for (Iter p = pos.first;p != pos.second; ++p) 00676 if (p->second.first == side && 00677 p->second.second == id) 00678 { 00679 already_inserted = true; 00680 break; 00681 } 00682 if (already_inserted) 00683 continue; 00684 00685 std::pair<unsigned short int, boundary_id_type> p(side,id); 00686 std::pair<const Elem*, std::pair<unsigned short int, boundary_id_type> > 00687 kv (elem, p); 00688 00689 _boundary_side_id.insert(kv); 00690 _boundary_ids.insert(id); 00691 _side_boundary_ids.insert(id); // Also add this ID to the set of side boundary IDs 00692 } 00693 } 00694 00695 00696 00697 bool BoundaryInfo::has_boundary_id(const Node* const node, 00698 const boundary_id_type id) const 00699 { 00700 // A convenient typedef 00701 typedef std::multimap<const Node*, boundary_id_type>::const_iterator Iter; 00702 00703 std::pair<Iter, Iter> pos = _boundary_node_id.equal_range(node); 00704 00705 for (;pos.first != pos.second; ++pos.first) 00706 if (pos.first->second == id) 00707 return true; 00708 00709 return false; 00710 } 00711 00712 00713 00714 std::vector<boundary_id_type> BoundaryInfo::boundary_ids(const Node* node) const 00715 { 00716 std::vector<boundary_id_type> ids; 00717 00718 // A convenient typedef 00719 typedef std::multimap<const Node*, boundary_id_type>::const_iterator Iter; 00720 00721 std::pair<Iter, Iter> pos = _boundary_node_id.equal_range(node); 00722 00723 for (;pos.first != pos.second; ++pos.first) 00724 ids.push_back(pos.first->second); 00725 00726 return ids; 00727 } 00728 00729 00730 00731 unsigned int BoundaryInfo::n_boundary_ids(const Node* node) const 00732 { 00733 // A convenient typedef 00734 typedef std::multimap<const Node*, boundary_id_type>::const_iterator Iter; 00735 00736 std::pair<Iter, Iter> pos = _boundary_node_id.equal_range(node); 00737 00738 return libmesh_cast_int<unsigned int> 00739 (std::distance(pos.first, pos.second)); 00740 } 00741 00742 00743 00744 boundary_id_type BoundaryInfo::boundary_id(const Elem* const elem, 00745 const unsigned short int side) const 00746 { 00747 // Asking for just one boundary id means your code isn't safe to use 00748 // on meshes with overlapping boundary ids. Try using 00749 // BoundaryInfo::boundary_ids or BoundaryInfo::has_boundary_id 00750 // instead. 00751 libmesh_deprecated(); 00752 00753 libmesh_assert(elem); 00754 00755 // Only level-0 elements store BCs. If this is not a level-0 00756 // element, one of its parent elements may have either internal 00757 // or external boundary IDs. We find that parent now. 00758 const Elem* searched_elem = elem; 00759 if (elem->level() != 0) 00760 { 00761 // Child element on external side: the top_parent will have the BCs 00762 if (elem->neighbor(side) == NULL) 00763 searched_elem = elem->top_parent (); 00764 00765 #ifdef LIBMESH_ENABLE_AMR 00766 // Child element is not on external side, but it may have internal 00767 // "boundary" IDs. We will walk up the tree, at each level checking that 00768 // the current child is actually on the same side of the parent that is 00769 // currently being searched for (i.e. that was passed in as "side"). 00770 else 00771 while (searched_elem->parent() != NULL) 00772 { 00773 const Elem * parent = searched_elem->parent(); 00774 if (parent->is_child_on_side(parent->which_child_am_i(searched_elem), side) == false) 00775 return invalid_id; 00776 searched_elem = parent; 00777 } 00778 #endif 00779 } 00780 00781 std::pair<std::multimap<const Elem*, 00782 std::pair<unsigned short int, boundary_id_type> >::const_iterator, 00783 std::multimap<const Elem*, 00784 std::pair<unsigned short int, boundary_id_type> >::const_iterator > 00785 e = _boundary_side_id.equal_range(searched_elem); 00786 00787 // elem not in the data structure 00788 if (e.first == e.second) 00789 return invalid_id; 00790 00791 // elem is there, maybe multiple occurances 00792 for (; e.first != e.second; ++e.first) 00793 // if this is true we found the requested side 00794 // of the element and want to return the id 00795 if (e.first->second.first == side) 00796 return e.first->second.second; 00797 00798 // if we get here, we found elem in the data structure but not 00799 // the requested side, so return the default value 00800 return invalid_id; 00801 } 00802 00803 00804 00805 bool BoundaryInfo::has_boundary_id(const Elem* const elem, 00806 const unsigned short int side, 00807 const boundary_id_type id) const 00808 { 00809 libmesh_assert(elem); 00810 00811 // Only level-0 elements store BCs. If this is not a level-0 00812 // element get its level-0 parent and infer the BCs. 00813 const Elem* searched_elem = elem; 00814 if (elem->level() != 0) 00815 { 00816 if (elem->neighbor(side) == NULL) 00817 searched_elem = elem->top_parent (); 00818 #ifdef LIBMESH_ENABLE_AMR 00819 else 00820 while (searched_elem->parent() != NULL) 00821 { 00822 const Elem * parent = searched_elem->parent(); 00823 if (parent->is_child_on_side(parent->which_child_am_i(searched_elem), side) == false) 00824 return false; 00825 searched_elem = parent; 00826 } 00827 #endif 00828 } 00829 00830 std::pair<std::multimap<const Elem*, 00831 std::pair<unsigned short int, boundary_id_type> >::const_iterator, 00832 std::multimap<const Elem*, 00833 std::pair<unsigned short int, boundary_id_type> >::const_iterator > 00834 e = _boundary_side_id.equal_range(searched_elem); 00835 00836 // elem is there, maybe multiple occurances 00837 for (; e.first != e.second; ++e.first) 00838 // if this is true we found the requested id on this side of the element 00839 if (e.first->second.first == side && 00840 e.first->second.second == id) 00841 return true; 00842 00843 return false; 00844 } 00845 00846 00847 00848 std::vector<boundary_id_type> BoundaryInfo::boundary_ids (const Elem* const elem, 00849 const unsigned short int side) const 00850 { 00851 libmesh_assert(elem); 00852 00853 std::vector<boundary_id_type> ids; 00854 00855 // Only level-0 elements store BCs. If this is not a level-0 00856 // element get its level-0 parent and infer the BCs. 00857 const Elem* searched_elem = elem; 00858 if (elem->level() != 0) 00859 { 00860 if (elem->neighbor(side) == NULL) 00861 searched_elem = elem->top_parent (); 00862 #ifdef LIBMESH_ENABLE_AMR 00863 else 00864 while (searched_elem->parent() != NULL) 00865 { 00866 const Elem * parent = searched_elem->parent(); 00867 if (parent->is_child_on_side(parent->which_child_am_i(searched_elem), side) == false) 00868 return ids; 00869 searched_elem = parent; 00870 } 00871 #endif 00872 } 00873 00874 std::pair<std::multimap<const Elem*, 00875 std::pair<unsigned short int, boundary_id_type> >::const_iterator, 00876 std::multimap<const Elem*, 00877 std::pair<unsigned short int, boundary_id_type> >::const_iterator > 00878 e = _boundary_side_id.equal_range(searched_elem); 00879 00880 // elem not in the data structure 00881 if (e.first == e.second) 00882 return ids; 00883 00884 // elem is there, maybe multiple occurances 00885 for (; e.first != e.second; ++e.first) 00886 // if this is true we found the requested side of the element 00887 if (e.first->second.first == side) 00888 ids.push_back(e.first->second.second); 00889 00890 // Whether or not we found anything, return "ids". If it's empty, it 00891 // means no valid bounary IDs were found for "side" 00892 return ids; 00893 } 00894 00895 00896 00897 unsigned int BoundaryInfo::n_boundary_ids (const Elem* const elem, 00898 const unsigned short int side) const 00899 { 00900 libmesh_assert(elem); 00901 00902 // Only level-0 elements store BCs. If this is not a level-0 00903 // element get its level-0 parent and infer the BCs. 00904 const Elem* searched_elem = elem; 00905 if (elem->level() != 0) 00906 { 00907 if (elem->neighbor(side) == NULL) 00908 searched_elem = elem->top_parent (); 00909 #ifdef LIBMESH_ENABLE_AMR 00910 else 00911 while (searched_elem->parent() != NULL) 00912 { 00913 const Elem * parent = searched_elem->parent(); 00914 if (parent->is_child_on_side(parent->which_child_am_i(searched_elem), side) == false) 00915 return 0; 00916 searched_elem = parent; 00917 } 00918 #endif 00919 } 00920 00921 std::pair<std::multimap<const Elem*, 00922 std::pair<unsigned short int, boundary_id_type> >::const_iterator, 00923 std::multimap<const Elem*, 00924 std::pair<unsigned short int, boundary_id_type> >::const_iterator > 00925 e = _boundary_side_id.equal_range(searched_elem); 00926 00927 unsigned int n_ids = 0; 00928 00929 // elem is there, maybe multiple occurances 00930 for (; e.first != e.second; ++e.first) 00931 // if this is true we found the requested side of the element 00932 if (e.first->second.first == side) 00933 n_ids++; 00934 00935 return n_ids; 00936 } 00937 00938 00939 00940 std::vector<boundary_id_type> BoundaryInfo::raw_boundary_ids (const Elem* const elem, 00941 const unsigned short int side) const 00942 { 00943 libmesh_assert(elem); 00944 00945 std::vector<boundary_id_type> ids; 00946 00947 // Only level-0 elements store BCs. 00948 if (elem->parent()) 00949 return ids; 00950 00951 std::pair<std::multimap<const Elem*, 00952 std::pair<unsigned short int, boundary_id_type> >::const_iterator, 00953 std::multimap<const Elem*, 00954 std::pair<unsigned short int, boundary_id_type> >::const_iterator > 00955 e = _boundary_side_id.equal_range(elem); 00956 00957 // Check any occurances 00958 for (; e.first != e.second; ++e.first) 00959 // if this is true we found the requested side of the element 00960 if (e.first->second.first == side) 00961 ids.push_back(e.first->second.second); 00962 00963 // if nothing got pushed back, we didn't find elem in the data 00964 // structure with the requested side, so return the default empty 00965 // vector 00966 return ids; 00967 } 00968 00969 00970 00971 void BoundaryInfo::remove_side (const Elem* elem, 00972 const unsigned short int side) 00973 { 00974 libmesh_assert(elem); 00975 00976 // The user shouldn't be trying to remove only one child's boundary 00977 // id 00978 libmesh_assert_equal_to (elem->level(), 0); 00979 00980 std::pair<std::multimap<const Elem*, 00981 std::pair<unsigned short int, boundary_id_type> >::iterator, 00982 std::multimap<const Elem*, 00983 std::pair<unsigned short int, boundary_id_type> >::iterator > 00984 e = _boundary_side_id.equal_range(elem); 00985 00986 // elem may be there, maybe multiple occurances 00987 while (e.first != e.second) 00988 { 00989 // if this is true we found the requested side 00990 // of the element and want to erase the id 00991 if (e.first->second.first == side) 00992 { 00993 // (postfix++ - increment the iterator before it's invalid) 00994 _boundary_side_id.erase(e.first++); 00995 } 00996 else 00997 ++e.first; 00998 } 00999 } 01000 01001 01002 01003 void BoundaryInfo::remove_side (const Elem* elem, 01004 const unsigned short int side, 01005 const boundary_id_type id) 01006 { 01007 libmesh_assert(elem); 01008 01009 // The user shouldn't be trying to remove only one child's boundary 01010 // id 01011 libmesh_assert_equal_to (elem->level(), 0); 01012 01013 std::pair<std::multimap<const Elem*, 01014 std::pair<unsigned short int, boundary_id_type> >::iterator, 01015 std::multimap<const Elem*, 01016 std::pair<unsigned short int, boundary_id_type> >::iterator > 01017 e = _boundary_side_id.equal_range(elem); 01018 01019 // elem may be there, maybe multiple occurances 01020 while (e.first != e.second) 01021 { 01022 // if this is true we found the requested side 01023 // of the element and want to erase the requested id 01024 if (e.first->second.first == side && 01025 e.first->second.second == id) 01026 { 01027 // (postfix++ - increment the iterator before it's invalid) 01028 _boundary_side_id.erase(e.first++); 01029 } 01030 else 01031 ++e.first; 01032 } 01033 } 01034 01035 01036 01037 unsigned int BoundaryInfo::side_with_boundary_id(const Elem* const elem, 01038 const boundary_id_type boundary_id_in) const 01039 { 01040 const Elem* searched_elem = elem; 01041 if (elem->level() != 0) 01042 searched_elem = elem->top_parent(); 01043 01044 std::pair<std::multimap<const Elem*, 01045 std::pair<unsigned short int, boundary_id_type> >::const_iterator, 01046 std::multimap<const Elem*, 01047 std::pair<unsigned short int, boundary_id_type> >::const_iterator > 01048 e = _boundary_side_id.equal_range(searched_elem); 01049 01050 // elem may have zero or multiple occurances 01051 for (; e.first != e.second; ++e.first) 01052 { 01053 // if this is true we found the requested boundary_id 01054 // of the element and want to return the side 01055 if (e.first->second.second == boundary_id_in) 01056 { 01057 unsigned int side = e.first->second.first; 01058 01059 // If we're on this external boundary then we share this 01060 // external boundary id 01061 if (elem->neighbor(side) == NULL) 01062 return side; 01063 01064 // If we're on an internal boundary then we need to be sure 01065 // it's the same internal boundary as our top_parent 01066 const Elem *p = elem; 01067 01068 #ifdef LIBMESH_ENABLE_AMR 01069 01070 while (p != NULL) 01071 { 01072 const Elem *parent = p->parent(); 01073 if (!parent->is_child_on_side(parent->which_child_am_i(p), side)) 01074 break; 01075 p = parent; 01076 } 01077 #endif 01078 // We're on that side of our top_parent; return it 01079 if (!p) 01080 return side; 01081 } 01082 } 01083 01084 // if we get here, we found elem in the data structure but not 01085 // the requested boundary id, so return the default value 01086 return libMesh::invalid_uint; 01087 } 01088 01089 void BoundaryInfo::build_node_boundary_ids(std::vector<boundary_id_type> &b_ids) 01090 { 01091 b_ids.clear(); 01092 01093 std::multimap<const Node*, boundary_id_type>::const_iterator pos 01094 = _boundary_node_id.begin(); 01095 01096 for (; pos != _boundary_node_id.end(); ++pos) 01097 { 01098 boundary_id_type id = pos->second; 01099 01100 if(std::find(b_ids.begin(),b_ids.end(),id) == b_ids.end()) 01101 b_ids.push_back(id); 01102 } 01103 } 01104 01105 void BoundaryInfo::build_side_boundary_ids(std::vector<boundary_id_type> &b_ids) 01106 { 01107 b_ids.clear(); 01108 01109 std::multimap<const Elem*, std::pair<unsigned short int, boundary_id_type> >::const_iterator pos 01110 = _boundary_side_id.begin(); 01111 01112 for (; pos != _boundary_side_id.end(); ++pos) 01113 { 01114 boundary_id_type id = pos->second.second; 01115 01116 if(std::find(b_ids.begin(),b_ids.end(),id) == b_ids.end()) 01117 b_ids.push_back(id); 01118 } 01119 } 01120 01121 std::size_t BoundaryInfo::n_boundary_conds () const 01122 { 01123 // in serial we know the number of bcs from the 01124 // size of the container 01125 if (_mesh.is_serial()) 01126 return _boundary_side_id.size(); 01127 01128 // in parallel we need to sum the number of local bcs 01129 parallel_only(); 01130 01131 std::size_t nbcs=0; 01132 01133 std::multimap<const Elem*, 01134 std::pair<unsigned short int, 01135 boundary_id_type> >::const_iterator pos; 01136 01137 for (pos=_boundary_side_id.begin(); pos != _boundary_side_id.end(); ++pos) 01138 if (pos->first->processor_id() == libMesh::processor_id()) 01139 nbcs++; 01140 01141 CommWorld.sum (nbcs); 01142 01143 return nbcs; 01144 } 01145 01146 01147 01148 void BoundaryInfo::build_node_list (std::vector<dof_id_type>& nl, 01149 std::vector<boundary_id_type>& il) const 01150 { 01151 // Reserve the size, then use push_back 01152 nl.reserve (_boundary_node_id.size()); 01153 il.reserve (_boundary_node_id.size()); 01154 01155 std::multimap<const Node*, boundary_id_type>::const_iterator pos 01156 = _boundary_node_id.begin(); 01157 01158 for (; pos != _boundary_node_id.end(); ++pos) 01159 { 01160 nl.push_back (pos->first->id()); 01161 il.push_back (pos->second); 01162 } 01163 } 01164 01165 01166 void 01167 BoundaryInfo::build_node_list_from_side_list() 01168 { 01169 std::multimap<const Elem*, 01170 std::pair<unsigned short int, 01171 boundary_id_type> >::const_iterator pos; 01172 01173 //Loop over the side list 01174 for (pos=_boundary_side_id.begin(); pos != _boundary_side_id.end(); ++pos) 01175 { 01176 // Don't add remote sides 01177 if(pos->first->is_remote()) 01178 continue; 01179 01180 //Need to loop over the sides of any possible children 01181 std::vector< const Elem * > family; 01182 #ifdef LIBMESH_ENABLE_AMR 01183 pos->first->active_family_tree_by_side (family, pos->second.first); 01184 #else 01185 family.push_back(pos->first); 01186 #endif 01187 01188 for(std::size_t elem_it=0; elem_it < family.size(); elem_it++) 01189 { 01190 const Elem * cur_elem = family[elem_it]; 01191 01192 AutoPtr<Elem> side = cur_elem->build_side(pos->second.first); 01193 01194 //Add each node node on the side with the side's boundary id 01195 for(unsigned int i=0; i<side->n_nodes(); i++) 01196 { 01197 Node * node = side->get_node(i); 01198 01199 this->add_node(node, pos->second.second); 01200 } 01201 } 01202 } 01203 } 01204 01205 01206 01207 01208 void BoundaryInfo::build_side_list_from_node_list() 01209 { 01210 // Check for early return 01211 if (_boundary_node_id.empty()) 01212 { 01213 libMesh::out << "No boundary node IDs have been added: cannot build side list!" << std::endl; 01214 return; 01215 } 01216 01217 // typedef for less typing! 01218 typedef std::multimap<const Node*, boundary_id_type>::const_iterator iterator_t; 01219 01220 // Return value and iterator for equal_range() 01221 iterator_t pos; 01222 std::pair<iterator_t, iterator_t> range; 01223 01224 MeshBase::const_element_iterator el = _mesh.active_elements_begin(); 01225 const MeshBase::const_element_iterator end_el = _mesh.active_elements_end(); 01226 01227 for (; el != end_el; ++el) 01228 { 01229 const Elem* elem = *el; 01230 01231 for (unsigned side=0; side<elem->n_sides(); ++side) 01232 { 01233 AutoPtr<Elem> side_elem = elem->build_side(side); 01234 01235 // map from nodeset_id to count for that ID 01236 std::map<dof_id_type, unsigned> nodesets_node_count; 01237 for (unsigned node_num=0; node_num < side_elem->n_nodes(); ++node_num) 01238 { 01239 Node* node = side_elem->get_node(node_num); 01240 range = _boundary_node_id.equal_range(node); 01241 01242 // For each nodeset that this node is a member of, increment the associated 01243 // nodeset ID count 01244 for (pos = range.first; pos != range.second; ++pos) 01245 { 01246 nodesets_node_count[pos->second]++; 01247 } 01248 } 01249 01250 // Now check to see what nodeset_counts have the correct number of nodes in them 01251 for (std::map<dof_id_type, unsigned>::const_iterator nodesets = nodesets_node_count.begin(); 01252 nodesets != nodesets_node_count.end(); ++nodesets) 01253 { 01254 if (nodesets->second == side_elem->n_nodes()) 01255 { 01256 // Add this side to the sideset 01257 add_side(elem, side, nodesets->first); 01258 } 01259 } 01260 } // end for side 01261 } // end for el 01262 } 01263 01264 01265 01266 01267 void BoundaryInfo::build_side_list (std::vector<dof_id_type>& el, 01268 std::vector<unsigned short int>& sl, 01269 std::vector<boundary_id_type>& il) const 01270 { 01271 // Reserve the size, then use push_back 01272 el.reserve (_boundary_side_id.size()); 01273 sl.reserve (_boundary_side_id.size()); 01274 il.reserve (_boundary_side_id.size()); 01275 01276 std::multimap<const Elem*, 01277 std::pair<unsigned short int, 01278 boundary_id_type> >::const_iterator pos; 01279 01280 for (pos=_boundary_side_id.begin(); pos != _boundary_side_id.end(); 01281 ++pos) 01282 { 01283 el.push_back (pos->first->id()); 01284 sl.push_back (pos->second.first); 01285 il.push_back (pos->second.second); 01286 } 01287 } 01288 01289 01290 01291 void BoundaryInfo::print_info(std::ostream& out_stream) const 01292 { 01293 // Print out the nodal BCs 01294 if (!_boundary_node_id.empty()) 01295 { 01296 out_stream << "Nodal Boundary conditions:" << std::endl 01297 << "--------------------------" << std::endl 01298 << " (Node No., ID) " << std::endl; 01299 01300 // std::for_each(_boundary_node_id.begin(), 01301 // _boundary_node_id.end(), 01302 // PrintNodeInfo()); 01303 01304 std::multimap<const Node*, boundary_id_type>::const_iterator it = _boundary_node_id.begin(); 01305 const std::multimap<const Node*, boundary_id_type>::const_iterator end = _boundary_node_id.end(); 01306 01307 for (; it != end; ++it) 01308 out_stream << " (" << (*it).first->id() 01309 << ", " << (*it).second 01310 << ")" << std::endl; 01311 } 01312 01313 // Print out the element BCs 01314 if (!_boundary_side_id.empty()) 01315 { 01316 out_stream << std::endl 01317 << "Side Boundary conditions:" << std::endl 01318 << "-------------------------" << std::endl 01319 << " (Elem No., Side No., ID) " << std::endl; 01320 01321 // std::for_each(_boundary_side_id.begin(), 01322 // _boundary_side_id.end(), 01323 // PrintSideInfo()); 01324 01325 std::multimap<const Elem*, 01326 std::pair<unsigned short int, boundary_id_type> >::const_iterator it = _boundary_side_id.begin(); 01327 const std::multimap<const Elem*, 01328 std::pair<unsigned short int, boundary_id_type> >::const_iterator end = _boundary_side_id.end(); 01329 01330 for (; it != end; ++it) 01331 out_stream << " (" << (*it).first->id() 01332 << ", " << (*it).second.first 01333 << ", " << (*it).second.second 01334 << ")" << std::endl; 01335 } 01336 } 01337 01338 01339 01340 void BoundaryInfo::print_summary(std::ostream& out_stream) const 01341 { 01342 // Print out the nodal BCs 01343 if (!_boundary_node_id.empty()) 01344 { 01345 out_stream << "Nodal Boundary conditions:" << std::endl 01346 << "--------------------------" << std::endl 01347 << " (ID, number of nodes) " << std::endl; 01348 01349 std::map<boundary_id_type, std::size_t> ID_counts; 01350 01351 std::multimap<const Node*, boundary_id_type>::const_iterator it = _boundary_node_id.begin(); 01352 const std::multimap<const Node*, boundary_id_type>::const_iterator end = _boundary_node_id.end(); 01353 01354 for (; it != end; ++it) 01355 ID_counts[(*it).second]++; 01356 01357 std::map<boundary_id_type, std::size_t>::const_iterator ID_it = ID_counts.begin(); 01358 const std::map<boundary_id_type, std::size_t>::const_iterator ID_end = ID_counts.end(); 01359 01360 for (; ID_it != ID_end; ++ID_it) 01361 out_stream << " (" << (*ID_it).first 01362 << ", " << (*ID_it).second 01363 << ")" << std::endl; 01364 } 01365 01366 // Print out the element BCs 01367 if (!_boundary_side_id.empty()) 01368 { 01369 out_stream << std::endl 01370 << "Side Boundary conditions:" << std::endl 01371 << "-------------------------" << std::endl 01372 << " (ID, number of sides) " << std::endl; 01373 01374 std::map<boundary_id_type, std::size_t> ID_counts; 01375 01376 std::multimap<const Elem*, 01377 std::pair<unsigned short int, boundary_id_type> >::const_iterator it = _boundary_side_id.begin(); 01378 const std::multimap<const Elem*, 01379 std::pair<unsigned short int, boundary_id_type> >::const_iterator end = _boundary_side_id.end(); 01380 01381 for (; it != end; ++it) 01382 ID_counts[(*it).second.second]++; 01383 01384 std::map<boundary_id_type, std::size_t>::const_iterator ID_it = ID_counts.begin(); 01385 const std::map<boundary_id_type, std::size_t>::const_iterator ID_end = ID_counts.end(); 01386 01387 for (; ID_it != ID_end; ++ID_it) 01388 out_stream << " (" << (*ID_it).first 01389 << ", " << (*ID_it).second 01390 << ")" << std::endl; 01391 } 01392 } 01393 01394 std::string& BoundaryInfo::sideset_name(boundary_id_type id) 01395 { 01396 return _ss_id_to_name[id]; 01397 } 01398 01399 std::string& BoundaryInfo::nodeset_name(boundary_id_type id) 01400 { 01401 return _ns_id_to_name[id]; 01402 } 01403 01404 boundary_id_type BoundaryInfo::get_id_by_name(const std::string& name) const 01405 { 01406 // This function is searching the keys of the map 01407 // We might want to make this more efficient 01408 std::map<boundary_id_type, std::string>::const_iterator iter = _ss_id_to_name.begin(); 01409 std::map<boundary_id_type, std::string>::const_iterator end_iter = _ss_id_to_name.end(); 01410 01411 for ( ; iter != end_iter; ++iter) 01412 { 01413 if (iter->second == name) 01414 return iter->first; 01415 } 01416 01417 // Loop over nodesets 01418 iter = _ns_id_to_name.begin(); 01419 end_iter = _ns_id_to_name.end(); 01420 for ( ; iter != end_iter; ++iter) 01421 { 01422 if (iter->second == name) 01423 return iter->first; 01424 } 01425 01426 std::cerr << "The sideset/nodeset name does not exist in mesh"; 01427 libmesh_error(); 01428 } 01429 01430 } // namespace libMesh
Site Created By: libMesh Developers
Last modified: February 05 2013 19:54:45 UTC
Hosted By: