$extrastylesheet
00001 // The libMesh Finite Element Library. 00002 // Copyright (C) 2002-2014 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 // C++ includes 00020 #include <numeric> // std::accumulate 00021 00022 // LibMesh includes 00023 #include "libmesh/elem.h" 00024 #include "libmesh/exodusII_io.h" 00025 #include "libmesh/libmesh_logging.h" 00026 #include "libmesh/nemesis_io.h" 00027 #include "libmesh/nemesis_io_helper.h" 00028 #include "libmesh/node.h" 00029 #include "libmesh/parallel_mesh.h" 00030 #include "libmesh/parallel.h" 00031 #include "libmesh/utility.h" // is_sorted, deallocate 00032 #include "libmesh/boundary_info.h" 00033 #include "libmesh/mesh_communication.h" 00034 00035 namespace libMesh 00036 { 00037 00038 00039 //----------------------------------------------- 00040 // anonymous namespace for implementation details 00041 namespace { 00042 struct CompareGlobalIdxMappings 00043 { 00044 // strict weak ordering for a.first -> a.second mapping. since we can only map to one 00045 // value only order the first entry 00046 bool operator()(const std::pair<unsigned int, unsigned int> &a, 00047 const std::pair<unsigned int, unsigned int> &b) const 00048 { return a.first < b.first; } 00049 00050 // strict weak ordering for a.first -> a.second mapping. lookups will 00051 // be in terms of a single integer, which is why we need this method. 00052 bool operator()(const std::pair<unsigned int, unsigned int> &a, 00053 const unsigned int b) const 00054 { return a.first < b; } 00055 }; 00056 00057 // Nemesis & ExodusII use int for all integer values, even the ones which 00058 // should never be negative. we like to use unsigned as a force of habit, 00059 // this trivial little method saves some typing & also makes sure something 00060 // is not horribly wrong. 00061 template <typename T> 00062 inline unsigned int to_uint ( const T &t ) 00063 { 00064 libmesh_assert_equal_to (t, static_cast<T>(static_cast<unsigned int>(t))); 00065 00066 return static_cast<unsigned int>(t); 00067 } 00068 00069 // test equality for a.first -> a.second mapping. since we can only map to one 00070 // value only test the first entry 00071 inline bool global_idx_mapping_equality (const std::pair<unsigned int, unsigned int> &a, 00072 const std::pair<unsigned int, unsigned int> &b) 00073 { return a.first == b.first; } 00074 } 00075 00076 00077 00078 // ------------------------------------------------------------ 00079 // Nemesis_IO class members 00080 Nemesis_IO::Nemesis_IO (MeshBase& mesh, 00081 #if defined(LIBMESH_HAVE_EXODUS_API) && defined(LIBMESH_HAVE_NEMESIS_API) 00082 bool single_precision 00083 #else 00084 bool 00085 #endif 00086 ) : 00087 MeshInput<MeshBase> (mesh, /*is_parallel_format=*/true), 00088 MeshOutput<MeshBase> (mesh, /*is_parallel_format=*/true), 00089 ParallelObject (mesh), 00090 #if defined(LIBMESH_HAVE_EXODUS_API) && defined(LIBMESH_HAVE_NEMESIS_API) 00091 nemhelper(new Nemesis_IO_Helper(*this, false, single_precision)), 00092 #endif 00093 _timestep(1), 00094 _verbose (false), 00095 _append(false) 00096 { 00097 } 00098 00099 00100 Nemesis_IO::~Nemesis_IO () 00101 { 00102 #if defined(LIBMESH_HAVE_EXODUS_API) && defined(LIBMESH_HAVE_NEMESIS_API) 00103 00104 delete nemhelper; 00105 #endif 00106 } 00107 00108 00109 00110 void Nemesis_IO::verbose (bool set_verbosity) 00111 { 00112 _verbose = set_verbosity; 00113 00114 #if defined(LIBMESH_HAVE_EXODUS_API) && defined(LIBMESH_HAVE_NEMESIS_API) 00115 // Set the verbose flag in the helper object 00116 // as well. 00117 nemhelper->verbose = _verbose; 00118 #endif 00119 } 00120 00121 00122 00123 void Nemesis_IO::append(bool val) 00124 { 00125 _append = val; 00126 } 00127 00128 00129 00130 #if defined(LIBMESH_HAVE_EXODUS_API) && defined(LIBMESH_HAVE_NEMESIS_API) 00131 void Nemesis_IO::read (const std::string& base_filename) 00132 { 00133 // On one processor, Nemesis and ExodusII should be equivalent, so 00134 // let's cowardly defer to that implementation... 00135 if (this->n_processors() == 1) 00136 { 00137 // We can do this in one line but if the verbose flag was set in this 00138 // object, it will no longer be set... thus no extra print-outs for serial runs. 00139 // ExodusII_IO(this->mesh()).read (base_filename); // ambiguous when Nemesis_IO is multiply-inherited 00140 00141 MeshBase& mesh = MeshInput<MeshBase>::mesh(); 00142 ExodusII_IO(mesh).read (base_filename); 00143 return; 00144 } 00145 00146 START_LOG ("read()","Nemesis_IO"); 00147 00148 // This function must be run on all processors at once 00149 parallel_object_only(); 00150 00151 if (_verbose) 00152 { 00153 libMesh::out << "[" << this->processor_id() << "] "; 00154 libMesh::out << "Reading Nemesis file on processor: " << this->processor_id() << std::endl; 00155 } 00156 00157 // Construct the Nemesis filename based on the number of processors and the 00158 // current processor ID. 00159 std::string nemesis_filename = nemhelper->construct_nemesis_filename(base_filename); 00160 00161 if (_verbose) 00162 libMesh::out << "Opening file: " << nemesis_filename << std::endl; 00163 00164 // Open the Exodus file in EX_READ mode 00165 nemhelper->open(nemesis_filename.c_str(), /*read_only=*/true); 00166 00167 // Get a reference to the mesh. We need to be specific 00168 // since Nemesis_IO is multiply-inherited 00169 // MeshBase& mesh = this->mesh(); 00170 MeshBase& mesh = MeshInput<MeshBase>::mesh(); 00171 00172 // We're reading a file on each processor, so our mesh is 00173 // partitioned into that many parts as it's created 00174 this->set_n_partitions(this->n_processors()); 00175 00176 // Local information: Read the following information from the standard Exodus header 00177 // title[0] 00178 // num_dim 00179 // num_nodes 00180 // num_elem 00181 // num_elem_blk 00182 // num_node_sets 00183 // num_side_sets 00184 nemhelper->read_header(); 00185 nemhelper->print_header(); 00186 00187 // Get global information: number of nodes, elems, blocks, nodesets and sidesets 00188 nemhelper->get_init_global(); 00189 00190 // Get "load balance" information. This includes the number of internal & border 00191 // nodes and elements as well as the number of communication maps. 00192 nemhelper->get_loadbal_param(); 00193 00194 // Do some error checking 00195 if (nemhelper->num_external_nodes) 00196 libmesh_error_msg("ERROR: there should be no external nodes in an element-based partitioning!"); 00197 00198 libmesh_assert_equal_to (nemhelper->num_nodes, 00199 (nemhelper->num_internal_nodes + 00200 nemhelper->num_border_nodes)); 00201 00202 libmesh_assert_equal_to (nemhelper->num_elem, 00203 (nemhelper->num_internal_elems + 00204 nemhelper->num_border_elems)); 00205 00206 libmesh_assert_less_equal (nemhelper->num_nodes, nemhelper->num_nodes_global); 00207 libmesh_assert_less_equal (nemhelper->num_elem, nemhelper->num_elems_global); 00208 00209 // Read nodes from the exodus file: this fills the nemhelper->x,y,z arrays. 00210 nemhelper->read_nodes(); 00211 00212 // Reads the nemhelper->node_num_map array, node_num_map[i] is the global node number for 00213 // local node number i. 00214 nemhelper->read_node_num_map(); 00215 00216 // The get_cmap_params() function reads in the: 00217 // node_cmap_ids[], 00218 // node_cmap_node_cnts[], 00219 // elem_cmap_ids[], 00220 // elem_cmap_elem_cnts[], 00221 nemhelper->get_cmap_params(); 00222 00223 // Read the IDs of the interior, boundary, and external nodes. This function 00224 // fills the vectors: 00225 // node_mapi[], 00226 // node_mapb[], 00227 // node_mape[] 00228 nemhelper->get_node_map(); 00229 00230 // Read each node communication map for this processor. This function 00231 // fills the vectors of vectors named: 00232 // node_cmap_node_ids[][] 00233 // node_cmap_proc_ids[][] 00234 nemhelper->get_node_cmap(); 00235 00236 libmesh_assert_equal_to (to_uint(nemhelper->num_node_cmaps), nemhelper->node_cmap_node_cnts.size()); 00237 libmesh_assert_equal_to (to_uint(nemhelper->num_node_cmaps), nemhelper->node_cmap_node_ids.size()); 00238 libmesh_assert_equal_to (to_uint(nemhelper->num_node_cmaps), nemhelper->node_cmap_proc_ids.size()); 00239 00240 #ifndef NDEBUG 00241 // We expect the communication maps to be symmetric - e.g. if processor i thinks it 00242 // communicates with processor j, then processor j should also be expecting to 00243 // communicate with i. We can assert that here easily enough with an alltoall, 00244 // but let's only do it when not in optimized mode to limit unnecessary communication. 00245 { 00246 std::vector<unsigned char> pid_send_partner (this->n_processors(), 0); 00247 00248 // strictly speaking, we should expect to communicate with ourself... 00249 pid_send_partner[this->processor_id()] = 1; 00250 00251 // mark each processor id we reference with a node cmap 00252 for (unsigned int cmap=0; cmap<to_uint(nemhelper->num_node_cmaps); cmap++) 00253 { 00254 libmesh_assert_less (to_uint(nemhelper->node_cmap_ids[cmap]), this->n_processors()); 00255 00256 pid_send_partner[nemhelper->node_cmap_ids[cmap]] = 1; 00257 } 00258 00259 // Copy the send pairing so we can catch the receive paring and 00260 // test for equality 00261 const std::vector<unsigned char> pid_recv_partner (pid_send_partner); 00262 00263 this->comm().alltoall (pid_send_partner); 00264 00265 libmesh_assert (pid_send_partner == pid_recv_partner); 00266 } 00267 #endif 00268 00269 // We now have enough information to infer node ownership. We start by assuming 00270 // we own all the nodes on this processor. We will then interrogate the 00271 // node cmaps and see if a lower-rank processor is associated with any of 00272 // our nodes. If so, then that processor owns the node, not us... 00273 std::vector<processor_id_type> node_ownership (nemhelper->num_internal_nodes + 00274 nemhelper->num_border_nodes, 00275 this->processor_id()); 00276 00277 // a map from processor id to cmap number, to be used later 00278 std::map<unsigned int, unsigned int> pid_to_cmap_map; 00279 00280 // For each node_cmap... 00281 for (unsigned int cmap=0; cmap<to_uint(nemhelper->num_node_cmaps); cmap++) 00282 { 00283 // Good time for error checking... 00284 libmesh_assert_equal_to (to_uint(nemhelper->node_cmap_node_cnts[cmap]), 00285 nemhelper->node_cmap_node_ids[cmap].size()); 00286 00287 libmesh_assert_equal_to (to_uint(nemhelper->node_cmap_node_cnts[cmap]), 00288 nemhelper->node_cmap_proc_ids[cmap].size()); 00289 00290 // In all the samples I have seen, node_cmap_ids[cmap] is the processor 00291 // rank of the remote processor... 00292 const int adjcnt_pid_idx = nemhelper->node_cmap_ids[cmap]; 00293 00294 libmesh_assert_less (adjcnt_pid_idx, this->n_processors()); 00295 libmesh_assert_not_equal_to (adjcnt_pid_idx, this->processor_id()); 00296 00297 // We only expect one cmap per adjacent processor 00298 libmesh_assert (!pid_to_cmap_map.count(adjcnt_pid_idx)); 00299 00300 pid_to_cmap_map[adjcnt_pid_idx] = cmap; 00301 00302 // ...and each node in that cmap... 00303 for (unsigned int idx=0; idx<to_uint(nemhelper->node_cmap_node_cnts[cmap]); idx++) 00304 { 00305 // Are the node_cmap_ids and node_cmap_proc_ids really redundant? 00306 libmesh_assert_equal_to (adjcnt_pid_idx, nemhelper->node_cmap_proc_ids[cmap][idx]); 00307 00308 // we are expecting the exodus node numbering to be 1-based... 00309 const unsigned int local_node_idx = nemhelper->node_cmap_node_ids[cmap][idx]-1; 00310 00311 libmesh_assert_less (local_node_idx, node_ownership.size()); 00312 00313 // if the adjacent processor is lower rank than the current 00314 // owner for this node, then it will get the node... 00315 node_ownership[local_node_idx] = 00316 std::min(node_ownership[local_node_idx], 00317 cast_int<processor_id_type>(adjcnt_pid_idx)); 00318 } 00319 } // We now should have established proper node ownership. 00320 00321 // now that ownership is established, we can figure out how many nodes we 00322 // will be responsible for numbering. 00323 unsigned int num_nodes_i_must_number = 0; 00324 00325 for (unsigned int idx=0; idx<node_ownership.size(); idx++) 00326 if (node_ownership[idx] == this->processor_id()) 00327 num_nodes_i_must_number++; 00328 00329 // more error checking... 00330 libmesh_assert_greater_equal (num_nodes_i_must_number, to_uint(nemhelper->num_internal_nodes)); 00331 libmesh_assert (num_nodes_i_must_number <= to_uint(nemhelper->num_internal_nodes + 00332 nemhelper->num_border_nodes)); 00333 if (_verbose) 00334 libMesh::out << "[" << this->processor_id() << "] " 00335 << "num_nodes_i_must_number=" 00336 << num_nodes_i_must_number 00337 << std::endl; 00338 00339 // The call to get_loadbal_param() gets 7 pieces of information. We allgather 00340 // these now across all processors to determine some global numberings. We should 00341 // also gather the number of nodes each processor thinks it will number so that 00342 // we can (i) determine our offset, and (ii) do some error checking. 00343 std::vector<int> all_loadbal_data ( 8 ); 00344 all_loadbal_data[0] = nemhelper->num_internal_nodes; 00345 all_loadbal_data[1] = nemhelper->num_border_nodes; 00346 all_loadbal_data[2] = nemhelper->num_external_nodes; 00347 all_loadbal_data[3] = nemhelper->num_internal_elems; 00348 all_loadbal_data[4] = nemhelper->num_border_elems; 00349 all_loadbal_data[5] = nemhelper->num_node_cmaps; 00350 all_loadbal_data[6] = nemhelper->num_elem_cmaps; 00351 all_loadbal_data[7] = num_nodes_i_must_number; 00352 00353 this->comm().allgather (all_loadbal_data, /* identical_buffer_sizes = */ true); 00354 00355 // OK, we are now in a position to request new global indices for all the nodes 00356 // we do not own 00357 00358 // Let's get a unique message tag to use for send()/receive() 00359 Parallel::MessageTag nodes_tag = mesh.comm().get_unique_tag(12345); 00360 00361 std::vector<std::vector<int> > 00362 needed_node_idxs (nemhelper->num_node_cmaps); // the indices we will ask for 00363 00364 std::vector<Parallel::Request> 00365 needed_nodes_requests (nemhelper->num_node_cmaps); 00366 00367 for (unsigned int cmap=0; cmap<to_uint(nemhelper->num_node_cmaps); cmap++) 00368 { 00369 // We know we will need no more indices than there are nodes 00370 // in this cmap, but that number is an upper bound in general 00371 // since the neighboring processor associated with the cmap 00372 // may not actually own it 00373 needed_node_idxs[cmap].reserve (nemhelper->node_cmap_node_cnts[cmap]); 00374 00375 const unsigned int adjcnt_pid_idx = nemhelper->node_cmap_ids[cmap]; 00376 00377 // ...and each node in that cmap... 00378 for (unsigned int idx=0; idx<to_uint(nemhelper->node_cmap_node_cnts[cmap]); idx++) 00379 { 00380 const unsigned int 00381 local_node_idx = nemhelper->node_cmap_node_ids[cmap][idx]-1, 00382 owning_pid_idx = node_ownership[local_node_idx]; 00383 00384 // add it to the request list for its owning processor. 00385 if (owning_pid_idx == adjcnt_pid_idx) 00386 { 00387 const unsigned int 00388 global_node_idx = nemhelper->node_num_map[local_node_idx]-1; 00389 needed_node_idxs[cmap].push_back(global_node_idx); 00390 } 00391 } 00392 // now post the send for this cmap 00393 this->comm().send (adjcnt_pid_idx, // destination 00394 needed_node_idxs[cmap], // send buffer 00395 needed_nodes_requests[cmap], // request 00396 nodes_tag); 00397 } // all communication requests for getting updated global indices for border 00398 // nodes have been initiated 00399 00400 // Figure out how many nodes each processor thinks it will number and make sure 00401 // that it adds up to the global number of nodes. Also, set up global node 00402 // index offsets for each processor. 00403 std::vector<unsigned int> 00404 all_num_nodes_i_must_number (this->n_processors()); 00405 00406 for (unsigned int pid=0; pid<this->n_processors(); pid++) 00407 all_num_nodes_i_must_number[pid] = all_loadbal_data[8*pid + 7]; 00408 00409 // The sum of all the entries in this vector should sum to the number of global nodes 00410 libmesh_assert (std::accumulate(all_num_nodes_i_must_number.begin(), 00411 all_num_nodes_i_must_number.end(), 00412 0) == nemhelper->num_nodes_global); 00413 00414 unsigned int my_next_node = 0; 00415 for (unsigned int pid=0; pid<this->processor_id(); pid++) 00416 my_next_node += all_num_nodes_i_must_number[pid]; 00417 00418 const unsigned int my_node_offset = my_next_node; 00419 00420 if (_verbose) 00421 libMesh::out << "[" << this->processor_id() << "] " 00422 << "my_node_offset=" 00423 << my_node_offset 00424 << std::endl; 00425 00426 // Add internal nodes to the ParallelMesh, using the node ID offset we 00427 // computed and the current processor's ID. 00428 for (unsigned int i=0; i<to_uint(nemhelper->num_internal_nodes); ++i) 00429 { 00430 const unsigned int local_node_idx = nemhelper->node_mapi[i]-1; 00431 #ifndef NDEBUG 00432 const unsigned int owning_pid_idx = node_ownership[local_node_idx]; 00433 #endif 00434 00435 // an internal node we do not own? huh?? 00436 libmesh_assert_equal_to (owning_pid_idx, this->processor_id()); 00437 libmesh_assert_less (my_next_node, to_uint(nemhelper->num_nodes_global)); 00438 00439 // "Catch" the node pointer after addition, make sure the 00440 // ID matches the requested value. 00441 Node* added_node = 00442 mesh.add_point (Point(nemhelper->x[local_node_idx], 00443 nemhelper->y[local_node_idx], 00444 nemhelper->z[local_node_idx]), 00445 my_next_node, 00446 this->processor_id()); 00447 00448 // Make sure the node we added has the ID we thought it would 00449 if (added_node->id() != my_next_node) 00450 { 00451 libMesh::err << "Error, node added with ID " << added_node->id() 00452 << ", but we wanted ID " << my_next_node << std::endl; 00453 } 00454 00455 // update the local->global index map, when we are done 00456 // it will be 0-based. 00457 nemhelper->node_num_map[local_node_idx] = my_next_node++; 00458 } 00459 00460 // Now, for the boundary nodes... We may very well own some of them, 00461 // but there may be others for which we have requested the new global 00462 // id. We expect to be asked for the ids of the ones we own, so 00463 // we need to create a map from the old global id to the new one 00464 // we are about to create. 00465 typedef std::vector<std::pair<unsigned int, unsigned int> > global_idx_mapping_type; 00466 global_idx_mapping_type old_global_to_new_global_map; 00467 old_global_to_new_global_map.reserve (num_nodes_i_must_number // total # i will have 00468 - (my_next_node // amount i have thus far 00469 - my_node_offset)); // this should be exact! 00470 CompareGlobalIdxMappings global_idx_mapping_comp; 00471 00472 for (unsigned int i=0; i<to_uint(nemhelper->num_border_nodes); ++i) 00473 { 00474 const unsigned int 00475 local_node_idx = nemhelper->node_mapb[i]-1, 00476 owning_pid_idx = node_ownership[local_node_idx]; 00477 00478 // if we own it... 00479 if (owning_pid_idx == this->processor_id()) 00480 { 00481 const unsigned int 00482 global_node_idx = nemhelper->node_num_map[local_node_idx]-1; 00483 00484 // we will number it, and create a mapping from its old global index to 00485 // the new global index, for lookup purposes when neighbors come calling 00486 old_global_to_new_global_map.push_back(std::make_pair(global_node_idx, 00487 my_next_node)); 00488 00489 // "Catch" the node pointer after addition, make sure the 00490 // ID matches the requested value. 00491 Node* added_node = 00492 mesh.add_point (Point(nemhelper->x[local_node_idx], 00493 nemhelper->y[local_node_idx], 00494 nemhelper->z[local_node_idx]), 00495 my_next_node, 00496 this->processor_id()); 00497 00498 // Make sure the node we added has the ID we thought it would 00499 if (added_node->id() != my_next_node) 00500 { 00501 libMesh::err << "Error, node added with ID " << added_node->id() 00502 << ", but we wanted ID " << my_next_node << std::endl; 00503 } 00504 00505 // update the local->global index map, when we are done 00506 // it will be 0-based. 00507 nemhelper->node_num_map[local_node_idx] = my_next_node++; 00508 } 00509 } 00510 // That should cover numbering all the nodes which belong to us... 00511 libmesh_assert_equal_to (num_nodes_i_must_number, (my_next_node - my_node_offset)); 00512 00513 // Let's sort the mapping so we can efficiently answer requests 00514 std::sort (old_global_to_new_global_map.begin(), 00515 old_global_to_new_global_map.end(), 00516 global_idx_mapping_comp); 00517 00518 // and it had better be unique... 00519 libmesh_assert (std::unique (old_global_to_new_global_map.begin(), 00520 old_global_to_new_global_map.end(), 00521 global_idx_mapping_equality) == 00522 old_global_to_new_global_map.end()); 00523 00524 // We can now catch incoming requests and process them. for efficiency 00525 // let's do whatever is available next 00526 std::map<unsigned int, std::vector<int> > requested_node_idxs; // the indices asked of us 00527 00528 std::vector<Parallel::Request> requested_nodes_requests(nemhelper->num_node_cmaps); 00529 00530 // We know we will receive the request from a given processor before 00531 // we receive its reply to our request. However, we may receive 00532 // a request and a response from one processor before getting 00533 // a request from another processor. So what we are doing here 00534 // is processing whatever message comes next, while recognizing 00535 // we will receive a request from a processor before receiving 00536 // its reply 00537 std::vector<bool> processed_cmap (nemhelper->num_node_cmaps, false); 00538 00539 for (unsigned int comm_step=0; comm_step<2*to_uint(nemhelper->num_node_cmaps); comm_step++) 00540 { 00541 // query the first message which is available 00542 const Parallel::Status 00543 status (this->comm().probe (Parallel::any_source, 00544 nodes_tag)); 00545 const unsigned int 00546 requesting_pid_idx = status.source(), 00547 source_pid_idx = status.source(); 00548 00549 // this had better be from a processor we are expecting... 00550 libmesh_assert (pid_to_cmap_map.count(requesting_pid_idx)); 00551 00552 // the local cmap which corresponds to the source processor 00553 const unsigned int cmap = pid_to_cmap_map[source_pid_idx]; 00554 00555 if (!processed_cmap[cmap]) 00556 { 00557 processed_cmap[cmap] = true; 00558 00559 // we should only get one request per paired processor 00560 libmesh_assert (!requested_node_idxs.count(requesting_pid_idx)); 00561 00562 // get a reference to the request buffer for this processor to 00563 // avoid repeated map lookups 00564 std::vector<int> &xfer_buf (requested_node_idxs[requesting_pid_idx]); 00565 00566 // actually receive the message. 00567 this->comm().receive (requesting_pid_idx, xfer_buf, nodes_tag); 00568 00569 // Fill the request 00570 for (unsigned int i=0; i<xfer_buf.size(); i++) 00571 { 00572 // the requested old global node index, *now 0-based* 00573 const unsigned int old_global_node_idx = xfer_buf[i]; 00574 00575 // find the new global node index for the requested node - 00576 // note that requesting_pid_idx thinks we own this node, 00577 // so we better! 00578 const global_idx_mapping_type::const_iterator it = 00579 std::lower_bound (old_global_to_new_global_map.begin(), 00580 old_global_to_new_global_map.end(), 00581 old_global_node_idx, 00582 global_idx_mapping_comp); 00583 00584 libmesh_assert (it != old_global_to_new_global_map.end()); 00585 libmesh_assert_equal_to (it->first, old_global_node_idx); 00586 libmesh_assert_greater_equal (it->second, my_node_offset); 00587 libmesh_assert_less (it->second, my_next_node); 00588 00589 // overwrite the requested old global node index with the new global index 00590 xfer_buf[i] = it->second; 00591 } 00592 00593 // and send the new global indices back to the processor which asked for them 00594 this->comm().send (requesting_pid_idx, 00595 xfer_buf, 00596 requested_nodes_requests[cmap], 00597 nodes_tag); 00598 } // done processing the request 00599 00600 // this is the second time we have heard from this processor, 00601 // so it must be its reply to our request 00602 else 00603 { 00604 // a long time ago, we sent off our own requests. now it is time to catch the 00605 // replies and get the new global node numbering. note that for any reply 00606 // we receive, the corresponding nonblocking send from above *must* have been 00607 // completed, since the reply is in response to that request!! 00608 00609 // if we have received a reply, our send *must* have completed 00610 // (note we never actually need to wait on the request) 00611 libmesh_assert (needed_nodes_requests[cmap].test()); 00612 libmesh_assert_equal_to (to_uint(nemhelper->node_cmap_ids[cmap]), source_pid_idx); 00613 00614 // now post the receive for this cmap 00615 this->comm().receive (source_pid_idx, 00616 needed_node_idxs[cmap], 00617 nodes_tag); 00618 00619 libmesh_assert_less_equal (needed_node_idxs[cmap].size(), 00620 nemhelper->node_cmap_node_ids[cmap].size()); 00621 00622 for (unsigned int i=0,j=0; i<nemhelper->node_cmap_node_ids[cmap].size(); i++) 00623 { 00624 const unsigned int 00625 local_node_idx = nemhelper->node_cmap_node_ids[cmap][i]-1, 00626 owning_pid_idx = node_ownership[local_node_idx]; 00627 00628 // if this node is owned by source_pid_idx, its new global id 00629 // is in the buffer we just received 00630 if (owning_pid_idx == source_pid_idx) 00631 { 00632 libmesh_assert_less (j, needed_node_idxs[cmap].size()); 00633 00634 const unsigned int // now 0-based! 00635 global_node_idx = needed_node_idxs[cmap][j++]; 00636 00637 // "Catch" the node pointer after addition, make sure the 00638 // ID matches the requested value. 00639 Node* added_node = 00640 mesh.add_point (Point(nemhelper->x[local_node_idx], 00641 nemhelper->y[local_node_idx], 00642 nemhelper->z[local_node_idx]), 00643 cast_int<dof_id_type>(global_node_idx), 00644 cast_int<processor_id_type>(source_pid_idx)); 00645 00646 // Make sure the node we added has the ID we thought it would 00647 if (added_node->id() != global_node_idx) 00648 { 00649 libMesh::err << "Error, node added with ID " << added_node->id() 00650 << ", but we wanted ID " << global_node_idx << std::endl; 00651 } 00652 00653 // update the local->global index map, when we are done 00654 // it will be 0-based. 00655 nemhelper->node_num_map[local_node_idx] = global_node_idx; 00656 00657 // we are not really going to use my_next_node again, but we can 00658 // keep incrimenting it to track how many nodes we have added 00659 // to the mesh 00660 my_next_node++; 00661 } 00662 } 00663 } 00664 } // end of node index communication loop 00665 00666 // we had better have added all the nodes we need to! 00667 libmesh_assert_equal_to ((my_next_node - my_node_offset), to_uint(nemhelper->num_nodes)); 00668 00669 // After all that, we should be done with all node-related arrays *except* the 00670 // node_num_map, which we have transformed to use our new numbering... 00671 // So let's clean up the arrays we are done with. 00672 { 00673 Utility::deallocate (nemhelper->node_mapi); 00674 Utility::deallocate (nemhelper->node_mapb); 00675 Utility::deallocate (nemhelper->node_mape); 00676 Utility::deallocate (nemhelper->node_cmap_ids); 00677 Utility::deallocate (nemhelper->node_cmap_node_cnts); 00678 Utility::deallocate (nemhelper->node_cmap_node_ids); 00679 Utility::deallocate (nemhelper->node_cmap_proc_ids); 00680 Utility::deallocate (nemhelper->x); 00681 Utility::deallocate (nemhelper->y); 00682 Utility::deallocate (nemhelper->z); 00683 Utility::deallocate (needed_node_idxs); 00684 Utility::deallocate (node_ownership); 00685 } 00686 00687 Parallel::wait (requested_nodes_requests); 00688 requested_node_idxs.clear(); 00689 00690 // See what the node count is up to now. 00691 if (_verbose) 00692 { 00693 // Report the number of nodes which have been added locally 00694 libMesh::out << "[" << this->processor_id() << "] "; 00695 libMesh::out << "mesh.n_nodes()=" << mesh.n_nodes() << std::endl; 00696 00697 // Reports the number of nodes that have been added in total. 00698 libMesh::out << "[" << this->processor_id() << "] "; 00699 libMesh::out << "mesh.parallel_n_nodes()=" << mesh.parallel_n_nodes() << std::endl; 00700 } 00701 00702 00703 00704 // -------------------------------------------------------------------------------- 00705 // -------------------------------------------------------------------------------- 00706 // -------------------------------------------------------------------------------- 00707 00708 00709 // We can now read in the elements...Exodus stores them in blocks in which all 00710 // elements have the same geometric type. This code is adapted directly from exodusII_io.C 00711 00712 // Assertion: The sum of the border and internal elements on all processors 00713 // should equal nemhelper->num_elems_global 00714 #ifndef NDEBUG 00715 { 00716 int sum_internal_elems=0, sum_border_elems=0; 00717 for (unsigned int j=3,c=0; c<this->n_processors(); j+=8,++c) 00718 sum_internal_elems += all_loadbal_data[j]; 00719 00720 for (unsigned int j=4,c=0; c<this->n_processors(); j+=8,++c) 00721 sum_border_elems += all_loadbal_data[j]; 00722 00723 if (_verbose) 00724 { 00725 libMesh::out << "[" << this->processor_id() << "] "; 00726 libMesh::out << "sum_internal_elems=" << sum_internal_elems << std::endl; 00727 00728 libMesh::out << "[" << this->processor_id() << "] "; 00729 libMesh::out << "sum_border_elems=" << sum_border_elems << std::endl; 00730 } 00731 00732 libmesh_assert_equal_to (sum_internal_elems+sum_border_elems, nemhelper->num_elems_global); 00733 } 00734 #endif 00735 00736 // We need to set the mesh dimension, but the following... 00737 // mesh.set_mesh_dimension(static_cast<unsigned int>(nemhelper->num_dim)); 00738 00739 // ... is not sufficient since some codes report num_dim==3 for two dimensional 00740 // meshes living in 3D, even though all the elements are of 2D type. Therefore, 00741 // we instead use the dimension of the highest element found for the Mesh dimension, 00742 // similar to what is done by the Exodus reader, except here it requires a 00743 // parallel communication. 00744 elems_of_dimension.resize(4, false); // will use 1-based 00745 00746 // Compute my_elem_offset, the amount by which to offset the local elem numbering 00747 // on my processor. 00748 unsigned int my_next_elem = 0; 00749 for (unsigned int pid=0; pid<this->processor_id(); ++pid) 00750 my_next_elem += (all_loadbal_data[8*pid + 3]+ // num_internal_elems, proc pid 00751 all_loadbal_data[8*pid + 4]); // num_border_elems, proc pid 00752 const unsigned int my_elem_offset = my_next_elem; 00753 00754 if (_verbose) 00755 libMesh::out << "[" << this->processor_id() << "] " 00756 << "my_elem_offset=" << my_elem_offset << std::endl; 00757 00758 00759 // Fills in the: 00760 // global_elem_blk_ids[] and 00761 // global_elem_blk_cnts[] arrays. 00762 nemhelper->get_eb_info_global(); 00763 00764 // // Fills in the vectors 00765 // // elem_mapi[num_internal_elems] 00766 // // elem_mapb[num_border_elems ] 00767 // // These tell which of the (locally-numbered) elements are internal and which are border elements. 00768 // // In our test example these arrays are sorted (but non-contiguous), which makes it possible to 00769 // // binary search for each element ID... however I don't think we need to distinguish between the 00770 // // two types, since either can have nodes the boundary! 00771 // nemhelper->get_elem_map(); 00772 00773 // Fills in the vectors of vectors: 00774 // elem_cmap_elem_ids[][] 00775 // elem_cmap_side_ids[][] 00776 // elem_cmap_proc_ids[][] 00777 // These arrays are of size num_elem_cmaps * elem_cmap_elem_cnts[i], i = 0..num_elem_cmaps 00778 nemhelper->get_elem_cmap(); 00779 00780 // Get information about the element blocks: 00781 // (read in the array nemhelper->block_ids[]) 00782 nemhelper->read_block_info(); 00783 00784 // Reads the nemhelper->elem_num_map array, elem_num_map[i] is the global element number for 00785 // local element number i. 00786 nemhelper->read_elem_num_map(); 00787 00788 // Instantiate the ElementMaps interface. This is what translates LibMesh's 00789 // element numbering scheme to Exodus's. 00790 ExodusII_IO_Helper::ElementMaps em; 00791 00792 // Read in the element connectivity for each block by 00793 // looping over all the blocks. 00794 for (unsigned int i=0; i<to_uint(nemhelper->num_elem_blk); i++) 00795 { 00796 // Read the information for block i: For nemhelper->block_ids[i], reads 00797 // elem_type 00798 // num_elem_this_blk 00799 // num_nodes_per_elem 00800 // num_attr 00801 // connect <-- the nodal connectivity array for each element in the block. 00802 nemhelper->read_elem_in_block(i); 00803 00804 // Note that with parallel files it is possible we have no elements in 00805 // this block! 00806 if (!nemhelper->num_elem_this_blk) continue; 00807 00808 // Set subdomain ID based on the block ID. 00809 subdomain_id_type subdomain_id = 00810 cast_int<subdomain_id_type>(nemhelper->block_ids[i]); 00811 00812 // Create a type string (this uses the null-terminated string ctor). 00813 const std::string type_str ( &(nemhelper->elem_type[0]) ); 00814 00815 // Set any relevant node/edge maps for this element 00816 const ExodusII_IO_Helper::Conversion conv = em.assign_conversion(type_str); 00817 00818 if (_verbose) 00819 libMesh::out << "Reading a block of " << type_str << " elements." << std::endl; 00820 00821 // Loop over all the elements in this block 00822 for (unsigned int j=0; j<to_uint(nemhelper->num_elem_this_blk); j++) 00823 { 00824 Elem* elem = Elem::build (conv.get_canonical_type()).release(); 00825 libmesh_assert (elem); 00826 00827 // Assign subdomain and processor ID to the newly-created Elem. 00828 // Assigning the processor ID beforehand ensures that the Elem is 00829 // not added as an "unpartitioned" element. Note that the element 00830 // numbering in Exodus is also 1-based. 00831 elem->subdomain_id() = subdomain_id; 00832 elem->processor_id() = this->processor_id(); 00833 elem->set_id() = my_next_elem++; 00834 00835 // Mark that we have seen an element of the current element's 00836 // dimension. 00837 elems_of_dimension[elem->dim()] = true; 00838 00839 // Add the created Elem to the Mesh, catch the Elem 00840 // pointer that the Mesh throws back. 00841 elem = mesh.add_elem (elem); 00842 00843 // We are expecting the element "thrown back" by libmesh to have the ID we specified for it... 00844 // Check to see that really is the case. Note that my_next_elem was post-incremented, so 00845 // subtract 1 when performing the check. 00846 if (elem->id() != my_next_elem-1) 00847 libmesh_error_msg("Unexpected ID " \ 00848 << elem->id() \ 00849 << " set by parallel mesh. (expecting " \ 00850 << my_next_elem-1 \ 00851 << ")."); 00852 00853 // Set all the nodes for this element 00854 if (_verbose) 00855 libMesh::out << "[" << this->processor_id() << "] " 00856 << "Setting nodes for Elem " << elem->id() << std::endl; 00857 00858 for (unsigned int k=0; k<to_uint(nemhelper->num_nodes_per_elem); k++) 00859 { 00860 const unsigned int 00861 gi = (j*nemhelper->num_nodes_per_elem + // index into connectivity array 00862 conv.get_node_map(k)), 00863 local_node_idx = nemhelper->connect[gi]-1, // local node index 00864 global_node_idx = nemhelper->node_num_map[local_node_idx]; // new global node index 00865 00866 // Set node number 00867 elem->set_node(k) = mesh.node_ptr(global_node_idx); 00868 } 00869 } // for (unsigned int j=0; j<nemhelper->num_elem_this_blk; j++) 00870 } // end for (unsigned int i=0; i<nemhelper->num_elem_blk; i++) 00871 00872 libmesh_assert_equal_to ((my_next_elem - my_elem_offset), to_uint(nemhelper->num_elem)); 00873 00874 if (_verbose) 00875 { 00876 // Print local elems_of_dimension information 00877 for (unsigned int i=1; i<elems_of_dimension.size(); ++i) 00878 libMesh::out << "[" << this->processor_id() << "] " 00879 << "elems_of_dimension[" << i << "]=" << elems_of_dimension[i] << std::endl; 00880 } 00881 00882 // Get the max dimension seen on the current processor 00883 unsigned char max_dim_seen = 0; 00884 for (unsigned char i=1; i<elems_of_dimension.size(); ++i) 00885 if (elems_of_dimension[i]) 00886 max_dim_seen = i; 00887 00888 // Do a global max to determine the max dimension seen by all processors. 00889 // It should match -- I don't think we even support calculations on meshes 00890 // with elements of different dimension... 00891 this->comm().max(max_dim_seen); 00892 00893 if (_verbose) 00894 { 00895 // Print the max element dimension from all processors 00896 libMesh::out << "[" << this->processor_id() << "] " 00897 << "max_dim_seen=" << +max_dim_seen << std::endl; 00898 } 00899 00900 // Set the mesh dimension to the largest encountered for an element 00901 mesh.set_mesh_dimension(max_dim_seen); 00902 00903 #if LIBMESH_DIM < 3 00904 if (mesh.mesh_dimension() > LIBMESH_DIM) 00905 libmesh_error_msg("Cannot open dimension " \ 00906 << mesh.mesh_dimension() \ 00907 << " mesh file when configured without " \ 00908 << mesh.mesh_dimension() \ 00909 << "D support." ); 00910 #endif 00911 00912 00913 // Global sideset information, they are distributed as well, not sure if they will require communication...? 00914 nemhelper->get_ss_param_global(); 00915 00916 if (_verbose) 00917 { 00918 libMesh::out << "[" << this->processor_id() << "] " 00919 << "Read global sideset parameter information." << std::endl; 00920 00921 // These global values should be the same on all processors... 00922 libMesh::out << "[" << this->processor_id() << "] " 00923 << "Number of global sideset IDs: " << nemhelper->global_sideset_ids.size() << std::endl; 00924 } 00925 00926 // Read *local* sideset info the same way it is done in 00927 // exodusII_io_helper. May be called any time after 00928 // nem_helper->read_header(); This sets num_side_sets and resizes 00929 // elem_list, side_list, and id_list to num_elem_all_sidesets. Note 00930 // that there appears to be the same number of sidesets in each file 00931 // but they all have different numbers of entries (some are empty). 00932 // Note that the sum of "nemhelper->num_elem_all_sidesets" over all 00933 // processors should equal the sum of the entries in the "num_global_side_counts" array 00934 // filled up by nemhelper->get_ss_param_global() 00935 nemhelper->read_sideset_info(); 00936 00937 if (_verbose) 00938 { 00939 libMesh::out << "[" << this->processor_id() << "] " 00940 << "nemhelper->num_side_sets = " << nemhelper->num_side_sets << std::endl; 00941 00942 libMesh::out << "[" << this->processor_id() << "] " 00943 << "nemhelper->num_elem_all_sidesets = " << nemhelper->num_elem_all_sidesets << std::endl; 00944 } 00945 00946 #ifdef DEBUG 00947 { 00948 // In DEBUG mode, check that the global number of sidesets reported 00949 // in each nemesis file matches the sum of all local sideset counts 00950 // from each processor. This requires a small communication, so only 00951 // do it in DEBUG mode. 00952 int sum_num_global_side_counts = std::accumulate(nemhelper->num_global_side_counts.begin(), 00953 nemhelper->num_global_side_counts.end(), 00954 0); 00955 00956 // MPI sum up the local files contributions 00957 int sum_num_elem_all_sidesets = nemhelper->num_elem_all_sidesets; 00958 this->comm().sum(sum_num_elem_all_sidesets); 00959 00960 if (sum_num_global_side_counts != sum_num_elem_all_sidesets) 00961 libmesh_error_msg("Error! global side count reported by Nemesis does not " \ 00962 << "match the side count reported by the individual files!"); 00963 } 00964 #endif 00965 00966 // Note that exodus stores sidesets in separate vectors but we want to pack 00967 // them all into a single vector. So when we call read_sideset(), we pass an offset 00968 // into the single vector of all IDs 00969 for (int offset=0, i=0; i<nemhelper->num_side_sets; i++) 00970 { 00971 offset += (i > 0 ? nemhelper->num_sides_per_set[i-1] : 0); // Compute new offset 00972 nemhelper->read_sideset (i, offset); 00973 } 00974 00975 // Now that we have the lists of elements, sides, and IDs, we are ready to set them 00976 // in the BoundaryInfo object of our Mesh object. This is slightly different in parallel... 00977 // For example, I think the IDs in each of the split Exodus files are numbered locally, 00978 // and we have to know the appropriate ID for this processor to be able to set the 00979 // entry in BoundaryInfo. This offset should be given by my_elem_offset determined in 00980 // this function... 00981 00982 // Debugging: 00983 // Print entries of elem_list 00984 // libMesh::out << "[" << this->processor_id() << "] " 00985 // << "elem_list = "; 00986 // for (unsigned int e=0; e<nemhelper->elem_list.size(); e++) 00987 // { 00988 // libMesh::out << nemhelper->elem_list[e] << ", "; 00989 // } 00990 // libMesh::out << std::endl; 00991 00992 // Print entries of side_list 00993 // libMesh::out << "[" << this->processor_id() << "] " 00994 // << "side_list = "; 00995 // for (unsigned int e=0; e<nemhelper->side_list.size(); e++) 00996 // { 00997 // libMesh::out << nemhelper->side_list[e] << ", "; 00998 // } 00999 // libMesh::out << std::endl; 01000 01001 01002 // Loop over the entries of the elem_list, get their pointers from the 01003 // Mesh data structure, and assign the appropriate side to the BoundaryInfo object. 01004 for (unsigned int e=0; e<nemhelper->elem_list.size(); e++) 01005 { 01006 // Calling mesh.elem() feels wrong, for example, in 01007 // ParallelMesh, Mesh::elem() can return NULL so we have to check for this... 01008 // 01009 // Perhaps we should iterate over elements and look them up in 01010 // the elem list instead? Note that the IDs in elem_list are 01011 // not necessarily in order, so if we did instead loop over the 01012 // mesh, we would have to search the (unsorted) elem_list vector 01013 // for each entry! We'll settle for doing some error checking instead. 01014 Elem* elem = mesh.elem(my_elem_offset + (nemhelper->elem_list[e]-1)/*Exodus numbering is 1-based!*/); 01015 01016 if (elem == NULL) 01017 libmesh_error_msg("Mesh returned a NULL pointer when asked for element " \ 01018 << my_elem_offset \ 01019 << " + " \ 01020 << nemhelper->elem_list[e] \ 01021 << " = " \ 01022 << my_elem_offset+nemhelper->elem_list[e]); 01023 01024 // The side numberings in libmesh and exodus are not 1:1, so we need to map 01025 // whatever side number is stored in Exodus into a libmesh side number using 01026 // a conv object... 01027 const ExodusII_IO_Helper::Conversion conv = 01028 em.assign_conversion(elem->type()); 01029 01030 // Finally, we are ready to add the element and its side to the BoundaryInfo object. 01031 // Call the version of add_side which takes a pointer, since we have already gone to 01032 // the trouble of getting said pointer... 01033 mesh.get_boundary_info().add_side(elem, 01034 cast_int<unsigned short>(conv.get_side_map(nemhelper->side_list[e]-1)), // Exodus numbering is 1-based 01035 cast_int<boundary_id_type>(nemhelper->id_list[e])); 01036 } 01037 01038 // Debugging: make sure there are as many boundary conditions in the 01039 // boundary ID object as expected. Note that, at this point, the 01040 // mesh still thinks it's serial, so n_boundary_conds() returns the 01041 // local number of boundary conditions (and is therefore cheap) 01042 // which should match nemhelper->elem_list.size(). 01043 { 01044 std::size_t nbcs = mesh.get_boundary_info().n_boundary_conds(); 01045 if (nbcs != nemhelper->elem_list.size()) 01046 libmesh_error_msg("[" << this->processor_id() << "] " \ 01047 << "BoundaryInfo contains " \ 01048 << nbcs \ 01049 << " boundary conditions, while the Exodus file had " \ 01050 << nemhelper->elem_list.size()); 01051 } 01052 01053 // Read global nodeset parameters? We might be able to use this to verify 01054 // something about the local files, but I haven't figured out what yet... 01055 nemhelper->get_ns_param_global(); 01056 01057 // Read local nodeset info 01058 nemhelper->read_nodeset_info(); 01059 01060 if (_verbose) 01061 { 01062 libMesh::out << "[" << this->processor_id() << "] "; 01063 libMesh::out << "nemhelper->num_node_sets=" << nemhelper->num_node_sets << std::endl; 01064 } 01065 01066 // // Debugging, what is currently in nemhelper->node_num_map anyway? 01067 // libMesh::out << "[" << this->processor_id() << "] " 01068 // << "nemhelper->node_num_map = "; 01069 // 01070 // for (unsigned int i=0; i<nemhelper->node_num_map.size(); ++i) 01071 // libMesh::out << nemhelper->node_num_map[i] << ", "; 01072 // libMesh::out << std::endl; 01073 01074 // For each nodeset, 01075 for (int nodeset=0; nodeset<nemhelper->num_node_sets; nodeset++) 01076 { 01077 // Get the user-defined ID associcated with the nodeset 01078 int nodeset_id = nemhelper->nodeset_ids[nodeset]; 01079 01080 if (_verbose) 01081 { 01082 libMesh::out << "[" << this->processor_id() << "] "; 01083 libMesh::out << "nemhelper->nodeset_ids[" << nodeset << "]=" << nodeset_id << std::endl; 01084 } 01085 01086 // Read the nodeset from file, store them in a vector 01087 nemhelper->read_nodeset(nodeset); 01088 01089 // Add nodes from the node_list to the BoundaryInfo object 01090 for (unsigned int node=0; node<nemhelper->node_list.size(); node++) 01091 { 01092 // Don't run past the end of our node map! 01093 if (to_uint(nemhelper->node_list[node]-1) >= nemhelper->node_num_map.size()) 01094 libmesh_error_msg("Error, index is past the end of node_num_map array!"); 01095 01096 // We should be able to use the node_num_map data structure set up previously to determine 01097 // the proper global node index. 01098 unsigned global_node_id = nemhelper->node_num_map[ nemhelper->node_list[node]-1 /*Exodus is 1-based!*/ ]; 01099 01100 if (_verbose) 01101 { 01102 libMesh::out << "[" << this->processor_id() << "] " 01103 << "nodeset " << nodeset 01104 << ", local node number: " << nemhelper->node_list[node]-1 01105 << ", global node id: " << global_node_id 01106 << std::endl; 01107 } 01108 01109 // Add the node to the BoundaryInfo object with the proper nodeset_id 01110 mesh.get_boundary_info().add_node 01111 (cast_int<dof_id_type>(global_node_id), 01112 cast_int<boundary_id_type>(nodeset_id)); 01113 } 01114 } 01115 01116 // See what the elem count is up to now. 01117 if (_verbose) 01118 { 01119 // Report the number of elements which have been added locally 01120 libMesh::out << "[" << this->processor_id() << "] "; 01121 libMesh::out << "mesh.n_elem()=" << mesh.n_elem() << std::endl; 01122 01123 // Reports the number of elements that have been added in total. 01124 libMesh::out << "[" << this->processor_id() << "] "; 01125 libMesh::out << "mesh.parallel_n_elem()=" << mesh.parallel_n_elem() << std::endl; 01126 } 01127 01128 STOP_LOG ("read()","Nemesis_IO"); 01129 01130 // For ParallelMesh, it seems that _is_serial is true by default. A hack to 01131 // make the Mesh think it's parallel might be to call: 01132 mesh.update_post_partitioning(); 01133 mesh.delete_remote_elements(); 01134 01135 // And if that didn't work, then we're actually reading into a 01136 // SerialMesh, so forget about gathering neighboring elements 01137 if (mesh.is_serial()) 01138 return; 01139 01140 // Gather neighboring elements so that the mesh has the proper "ghost" neighbor information. 01141 MeshCommunication().gather_neighboring_elements(cast_ref<ParallelMesh&>(mesh)); 01142 } 01143 01144 #else 01145 01146 void Nemesis_IO::read (const std::string& ) 01147 { 01148 libmesh_error_msg("ERROR, Nemesis API is not defined!"); 01149 } 01150 01151 #endif // #if defined(LIBMESH_HAVE_EXODUS_API) && defined(LIBMESH_HAVE_NEMESIS_API) 01152 01153 01154 01155 01156 01157 #if defined(LIBMESH_HAVE_EXODUS_API) && defined(LIBMESH_HAVE_NEMESIS_API) 01158 01159 void Nemesis_IO::write (const std::string& base_filename) 01160 { 01161 // Get a constant reference to the mesh for writing 01162 const MeshBase & mesh = MeshOutput<MeshBase>::mesh(); 01163 01164 // Create the filename for this processor given the base_filename passed in. 01165 std::string nemesis_filename = nemhelper->construct_nemesis_filename(base_filename); 01166 01167 // If the user has set the append flag here, it doesn't really make 01168 // sense: the intent of this function is to write a Mesh with no 01169 // data, while "appending" is really intended to add data to an 01170 // existing file. If we're verbose, print a message to this effect. 01171 if (_append && _verbose) 01172 libMesh::out << "Warning: Appending in Nemesis_IO::write() does not make sense.\n" 01173 << "Creating a new file instead!" 01174 << std::endl; 01175 01176 nemhelper->create(nemesis_filename); 01177 01178 // Initialize data structures and write some global Nemesis-specifc data, such as 01179 // communication maps, to file. 01180 nemhelper->initialize(nemesis_filename,mesh); 01181 01182 // Call the Nemesis-specialized version of write_nodal_coordinates() to write 01183 // the nodal coordinates. 01184 nemhelper->write_nodal_coordinates(mesh); 01185 01186 // Call the Nemesis-specialized version of write_elements() to write 01187 // the elements. Note: Must write a zero if a given global block ID has no 01188 // elements... 01189 nemhelper->write_elements(mesh); 01190 01191 // Call our specialized function to write the nodesets 01192 nemhelper->write_nodesets(mesh); 01193 01194 // Call our specialized write_sidesets() function to write the sidesets to file 01195 nemhelper->write_sidesets(mesh); 01196 01197 // Not sure if this is really necessary, but go ahead and flush the file 01198 // once we have written all this stuff. 01199 nemhelper->ex_err = exII::ex_update(nemhelper->ex_id); 01200 01201 if( (mesh.get_boundary_info().n_edge_conds() > 0) && 01202 _verbose ) 01203 { 01204 libMesh::out << "Warning: Mesh contains edge boundary IDs, but these " 01205 << "are not supported by the Nemesis format." 01206 << std::endl; 01207 } 01208 } 01209 01210 #else 01211 01212 void Nemesis_IO::write (const std::string& ) 01213 { 01214 libmesh_error_msg("ERROR, Nemesis API is not defined!"); 01215 } 01216 01217 #endif // #if defined(LIBMESH_HAVE_EXODUS_API) && defined(LIBMESH_HAVE_NEMESIS_API) 01218 01219 01220 #if defined(LIBMESH_HAVE_EXODUS_API) && defined(LIBMESH_HAVE_NEMESIS_API) 01221 01222 void Nemesis_IO::write_timestep (const std::string& fname, 01223 const EquationSystems& es, 01224 const int timestep, 01225 const Real time) 01226 { 01227 _timestep=timestep; 01228 write_equation_systems(fname,es); 01229 01230 nemhelper->write_timestep(timestep, time); 01231 } 01232 01233 #else 01234 01235 void Nemesis_IO::write_timestep (const std::string&, 01236 const EquationSystems&, 01237 const int, 01238 const Real) 01239 { 01240 libmesh_error_msg("ERROR, Nemesis API is not defined!"); 01241 } 01242 01243 #endif // #if defined(LIBMESH_HAVE_EXODUS_API) && defined(LIBMESH_HAVE_NEMESIS_API) 01244 01245 #if defined(LIBMESH_HAVE_EXODUS_API) && defined(LIBMESH_HAVE_NEMESIS_API) 01246 01247 void Nemesis_IO::write_nodal_data (const std::string& base_filename, 01248 const std::vector<Number>& soln, 01249 const std::vector<std::string>& names) 01250 { 01251 START_LOG("write_nodal_data()", "Nemesis_IO"); 01252 01253 const MeshBase & mesh = MeshOutput<MeshBase>::mesh(); 01254 01255 std::string nemesis_filename = nemhelper->construct_nemesis_filename(base_filename); 01256 01257 if (!nemhelper->opened_for_writing) 01258 { 01259 // If we're appending, open() the file with read_only=false, 01260 // otherwise create() it and write the contents of the mesh to 01261 // it. 01262 if (_append) 01263 { 01264 nemhelper->open(nemesis_filename.c_str(), /*read_only=*/false); 01265 // After opening the file, read the header so that certain 01266 // fields, such as the number of nodes and the number of 01267 // elements, are correctly initialized for the subsequent 01268 // call to write the nodal solution. 01269 nemhelper->read_header(); 01270 } 01271 else 01272 { 01273 nemhelper->create(nemesis_filename); 01274 nemhelper->initialize(nemesis_filename,mesh); 01275 nemhelper->write_nodal_coordinates(mesh); 01276 nemhelper->write_elements(mesh); 01277 nemhelper->write_nodesets(mesh); 01278 nemhelper->write_sidesets(mesh); 01279 01280 if( (mesh.get_boundary_info().n_edge_conds() > 0) && 01281 _verbose ) 01282 { 01283 libMesh::out << "Warning: Mesh contains edge boundary IDs, but these " 01284 << "are not supported by the ExodusII format." 01285 << std::endl; 01286 } 01287 01288 // If we don't have any nodes written out on this processor, 01289 // Exodus seems to like us better if we don't try to write out any 01290 // variable names too... 01291 #ifdef LIBMESH_USE_COMPLEX_NUMBERS 01292 01293 std::vector<std::string> complex_names = nemhelper->get_complex_names(names); 01294 01295 nemhelper->initialize_nodal_variables(complex_names); 01296 #else 01297 nemhelper->initialize_nodal_variables(names); 01298 #endif 01299 } 01300 } 01301 01302 nemhelper->write_nodal_solution(soln, names, _timestep); 01303 01304 STOP_LOG("write_nodal_data()", "Nemesis_IO"); 01305 } 01306 01307 #else 01308 01309 void Nemesis_IO::write_nodal_data (const std::string& , 01310 const std::vector<Number>& , 01311 const std::vector<std::string>& ) 01312 { 01313 libmesh_error_msg("ERROR, Nemesis API is not defined."); 01314 } 01315 01316 01317 #endif // #if defined(LIBMESH_HAVE_EXODUS_API) && defined(LIBMESH_HAVE_NEMESIS_API) 01318 01319 01320 01321 01322 01323 #if defined(LIBMESH_HAVE_EXODUS_API) && defined(LIBMESH_HAVE_NEMESIS_API) 01324 01325 void Nemesis_IO::write_global_data (const std::vector<Number>& soln, 01326 const std::vector<std::string>& names) 01327 { 01328 if (!nemhelper->opened_for_writing) 01329 libmesh_error_msg("ERROR, Nemesis file must be initialized before outputting global variables."); 01330 01331 #ifdef LIBMESH_USE_COMPLEX_NUMBERS 01332 01333 std::vector<std::string> complex_names = nemhelper->get_complex_names(names); 01334 01335 nemhelper->initialize_global_variables(complex_names); 01336 01337 unsigned int num_values = soln.size(); 01338 unsigned int num_vars = names.size(); 01339 unsigned int num_elems = num_values / num_vars; 01340 01341 // This will contain the real and imaginary parts and the magnitude 01342 // of the values in soln 01343 std::vector<Real> complex_soln(3*num_values); 01344 01345 for (unsigned i=0; i<num_vars; ++i) 01346 { 01347 for (unsigned int j=0; j<num_elems; ++j) 01348 { 01349 Number value = soln[i*num_vars + j]; 01350 complex_soln[3*i*num_elems + j] = value.real(); 01351 } 01352 for (unsigned int j=0; j<num_elems; ++j) 01353 { 01354 Number value = soln[i*num_vars + j]; 01355 complex_soln[3*i*num_elems + num_elems +j] = value.imag(); 01356 } 01357 for (unsigned int j=0; j<num_elems; ++j) 01358 { 01359 Number value = soln[i*num_vars + j]; 01360 complex_soln[3*i*num_elems + 2*num_elems + j] = std::abs(value); 01361 } 01362 } 01363 01364 nemhelper->write_global_values(complex_soln, _timestep); 01365 01366 #else 01367 01368 // Call the Exodus writer implementation 01369 nemhelper->initialize_global_variables( names ); 01370 nemhelper->write_global_values( soln, _timestep); 01371 01372 #endif 01373 01374 } 01375 01376 #else 01377 01378 void Nemesis_IO::write_global_data (const std::vector<Number>&, 01379 const std::vector<std::string>&) 01380 { 01381 libmesh_error_msg("ERROR, Nemesis API is not defined."); 01382 } 01383 01384 #endif // #if defined(LIBMESH_HAVE_EXODUS_API) && defined(LIBMESH_HAVE_NEMESIS_API) 01385 01386 01387 01388 #if defined(LIBMESH_HAVE_EXODUS_API) && defined(LIBMESH_HAVE_NEMESIS_API) 01389 01390 void Nemesis_IO::write_information_records (const std::vector<std::string>& records) 01391 { 01392 if (!nemhelper->opened_for_writing) 01393 libmesh_error_msg("ERROR, Nemesis file must be initialized before outputting information records."); 01394 01395 // Call the Exodus writer implementation 01396 nemhelper->write_information_records( records ); 01397 } 01398 01399 01400 #else 01401 01402 void Nemesis_IO::write_information_records ( const std::vector<std::string>& ) 01403 { 01404 libmesh_error_msg("ERROR, Nemesis API is not defined."); 01405 } 01406 01407 #endif // #if defined(LIBMESH_HAVE_EXODUS_API) && defined(LIBMESH_HAVE_NEMESIS_API) 01408 01409 } // namespace libMesh