nemesis_io_helper.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 // C++ headers 00020 #include <iomanip> 00021 #include <set> 00022 #include <sstream> 00023 00024 // Libmesh headers 00025 #include "libmesh/nemesis_io_helper.h" 00026 #include "libmesh/node.h" 00027 #include "libmesh/elem.h" 00028 #include "libmesh/boundary_info.h" 00029 #include "libmesh/parallel.h" 00030 00031 #if defined(LIBMESH_HAVE_NEMESIS_API) && defined(LIBMESH_HAVE_EXODUS_API) 00032 00033 namespace libMesh 00034 { 00035 00036 00037 // Initialize the various integer members to zero. We can check 00038 // these later to see if they've been properly initialized... 00039 // The parent ExodusII_IO_Helper is created with the run_only_on_proc0 00040 // flag set to false, so that we can make use of its functionality 00041 // on multiple processors. 00042 Nemesis_IO_Helper::Nemesis_IO_Helper(bool verbose_in) : 00043 ExodusII_IO_Helper(verbose_in, /*run_only_on_proc0=*/false), 00044 nemesis_err_flag(0), 00045 num_nodes_global(0), 00046 num_elems_global(0), 00047 num_elem_blks_global(0), 00048 num_node_sets_global(0), 00049 num_side_sets_global(0), 00050 num_proc(0), 00051 num_proc_in_file(0), 00052 ftype('\0'), 00053 num_internal_nodes(0), 00054 num_border_nodes(0), 00055 num_external_nodes(0), 00056 num_internal_elems(0), 00057 num_border_elems(0), 00058 num_node_cmaps(0), 00059 num_elem_cmaps(0) 00060 { 00061 // Warn about using untested code! 00062 libmesh_experimental(); 00063 } 00064 00065 00066 Nemesis_IO_Helper::~Nemesis_IO_Helper() 00067 { 00068 // Our destructor is called from Nemesis_IO. We close the Exodus file here since we have 00069 // responsibility for managing the file's lifetime. 00070 this->ex_err = exII::ex_update(this->ex_id); 00071 this->check_err(ex_err, "Error flushing buffers to file."); 00072 this->close(); 00073 } 00074 00075 00076 00077 // void Nemesis_IO_Helper::verbose (bool set_verbosity) 00078 // { 00079 // _verbose = set_verbosity; 00080 00081 // // Also set verbosity in the exodus helper object 00082 // ex2helper.verbose(_verbose); 00083 // } 00084 00085 00086 00087 void Nemesis_IO_Helper::get_init_global() 00088 { 00089 nemesis_err_flag = 00090 Nemesis::ne_get_init_global(ex_id, 00091 &num_nodes_global, 00092 &num_elems_global, 00093 &num_elem_blks_global, 00094 &num_node_sets_global, 00095 &num_side_sets_global); 00096 this->check_err(nemesis_err_flag, "Error reading initial global data!"); 00097 00098 if (_verbose) 00099 { 00100 libMesh::out << "[" << libMesh::processor_id() << "] " << "num_nodes_global=" << num_nodes_global << std::endl; 00101 libMesh::out << "[" << libMesh::processor_id() << "] " << "num_elems_global=" << num_elems_global << std::endl; 00102 libMesh::out << "[" << libMesh::processor_id() << "] " << "num_elem_blks_global=" << num_elem_blks_global << std::endl; 00103 libMesh::out << "[" << libMesh::processor_id() << "] " << "num_node_sets_global=" << num_node_sets_global << std::endl; 00104 libMesh::out << "[" << libMesh::processor_id() << "] " << "num_side_sets_global=" << num_side_sets_global << std::endl; 00105 } 00106 } 00107 00108 00109 00110 void Nemesis_IO_Helper::get_ss_param_global() 00111 { 00112 if (num_side_sets_global > 0) 00113 { 00114 global_sideset_ids.resize(num_side_sets_global); 00115 num_global_side_counts.resize(num_side_sets_global); 00116 00117 // df = "distribution factor", not really sure what that is. I don't yet have a file 00118 // which has distribution factors so I guess we'll worry about it later... 00119 num_global_side_df_counts.resize(num_side_sets_global); 00120 00121 nemesis_err_flag = 00122 Nemesis::ne_get_ss_param_global(ex_id, 00123 global_sideset_ids.empty() ? NULL : &global_sideset_ids[0], 00124 num_global_side_counts.empty() ? NULL : &num_global_side_counts[0], 00125 num_global_side_df_counts.empty() ? NULL : &num_global_side_df_counts[0]); 00126 this->check_err(nemesis_err_flag, "Error reading global sideset parameters!"); 00127 00128 if (_verbose) 00129 { 00130 libMesh::out << "[" << libMesh::processor_id() << "] " << "Global Sideset IDs, Side Counts, and DF counts:" << std::endl; 00131 for (unsigned int bn=0; bn<global_sideset_ids.size(); ++bn) 00132 { 00133 libMesh::out << " [" << libMesh::processor_id() << "] " 00134 << "global_sideset_ids["<<bn<<"]=" << global_sideset_ids[bn] 00135 << ", num_global_side_counts["<<bn<<"]=" << num_global_side_counts[bn] 00136 << ", num_global_side_df_counts["<<bn<<"]=" << num_global_side_df_counts[bn] 00137 << std::endl; 00138 } 00139 } 00140 } 00141 } 00142 00143 00144 00145 00146 void Nemesis_IO_Helper::get_ns_param_global() 00147 { 00148 if (num_node_sets_global > 0) 00149 { 00150 global_nodeset_ids.resize(num_node_sets_global); 00151 num_global_node_counts.resize(num_node_sets_global); 00152 num_global_node_df_counts.resize(num_node_sets_global); 00153 00154 nemesis_err_flag = 00155 Nemesis::ne_get_ns_param_global(ex_id, 00156 global_nodeset_ids.empty() ? NULL : &global_nodeset_ids[0], 00157 num_global_node_counts.empty() ? NULL : &num_global_node_counts[0], 00158 num_global_node_df_counts.empty() ? NULL : &num_global_node_df_counts[0]); 00159 this->check_err(nemesis_err_flag, "Error reading global nodeset parameters!"); 00160 00161 if (_verbose) 00162 { 00163 libMesh::out << "[" << libMesh::processor_id() << "] " << "Global Nodeset IDs, Node Counts, and DF counts:" << std::endl; 00164 for (unsigned int bn=0; bn<global_nodeset_ids.size(); ++bn) 00165 { 00166 libMesh::out << " [" << libMesh::processor_id() << "] " 00167 << "global_nodeset_ids["<<bn<<"]=" << global_nodeset_ids[bn] 00168 << ", num_global_node_counts["<<bn<<"]=" << num_global_node_counts[bn] 00169 << ", num_global_node_df_counts["<<bn<<"]=" << num_global_node_df_counts[bn] 00170 << std::endl; 00171 } 00172 } 00173 } 00174 } 00175 00176 00177 00178 void Nemesis_IO_Helper::get_eb_info_global() 00179 { 00180 global_elem_blk_ids.resize(num_elem_blks_global); 00181 global_elem_blk_cnts.resize(num_elem_blks_global); 00182 00183 nemesis_err_flag = 00184 Nemesis::ne_get_eb_info_global(ex_id, 00185 global_elem_blk_ids.empty() ? NULL : &global_elem_blk_ids[0], 00186 global_elem_blk_cnts.empty() ? NULL : &global_elem_blk_cnts[0]); 00187 this->check_err(nemesis_err_flag, "Error reading global element block info!"); 00188 00189 if (_verbose) 00190 { 00191 libMesh::out << "[" << libMesh::processor_id() << "] " << "Global Element Block IDs and Counts:" << std::endl; 00192 for (unsigned int bn=0; bn<global_elem_blk_ids.size(); ++bn) 00193 { 00194 libMesh::out << " [" << libMesh::processor_id() << "] " 00195 << "global_elem_blk_ids["<<bn<<"]=" << global_elem_blk_ids[bn] 00196 << ", global_elem_blk_cnts["<<bn<<"]=" << global_elem_blk_cnts[bn] 00197 << std::endl; 00198 } 00199 } 00200 } 00201 00202 00203 00204 void Nemesis_IO_Helper::get_init_info() 00205 { 00206 nemesis_err_flag = 00207 Nemesis::ne_get_init_info(ex_id, 00208 &num_proc, 00209 &num_proc_in_file, 00210 &ftype); 00211 this->check_err(nemesis_err_flag, "Error reading initial info!"); 00212 00213 if (_verbose) 00214 { 00215 libMesh::out << "[" << libMesh::processor_id() << "] " << "num_proc=" << num_proc << std::endl; 00216 libMesh::out << "[" << libMesh::processor_id() << "] " << "num_proc_in_file=" << num_proc_in_file << std::endl; 00217 libMesh::out << "[" << libMesh::processor_id() << "] " << "ftype=" << ftype << std::endl; 00218 } 00219 } 00220 00221 00222 00223 void Nemesis_IO_Helper::get_loadbal_param() 00224 { 00225 nemesis_err_flag = 00226 Nemesis::ne_get_loadbal_param(ex_id, 00227 &num_internal_nodes, 00228 &num_border_nodes, 00229 &num_external_nodes, 00230 &num_internal_elems, 00231 &num_border_elems, 00232 &num_node_cmaps, 00233 &num_elem_cmaps, 00234 libMesh::processor_id() // The ID of the processor for which info is to be read 00235 ); 00236 this->check_err(nemesis_err_flag, "Error reading load balance parameters!"); 00237 00238 00239 if (_verbose) 00240 { 00241 libMesh::out << "[" << libMesh::processor_id() << "] " << "num_internal_nodes=" << num_internal_nodes << std::endl; 00242 libMesh::out << "[" << libMesh::processor_id() << "] " << "num_border_nodes=" << num_border_nodes << std::endl; 00243 libMesh::out << "[" << libMesh::processor_id() << "] " << "num_external_nodes=" << num_external_nodes << std::endl; 00244 libMesh::out << "[" << libMesh::processor_id() << "] " << "num_internal_elems=" << num_internal_elems << std::endl; 00245 libMesh::out << "[" << libMesh::processor_id() << "] " << "num_border_elems=" << num_border_elems << std::endl; 00246 libMesh::out << "[" << libMesh::processor_id() << "] " << "num_node_cmaps=" << num_node_cmaps << std::endl; 00247 libMesh::out << "[" << libMesh::processor_id() << "] " << "num_elem_cmaps=" << num_elem_cmaps << std::endl; 00248 } 00249 } 00250 00251 00252 00253 void Nemesis_IO_Helper::get_elem_map() 00254 { 00255 elem_mapi.resize(num_internal_elems); 00256 elem_mapb.resize(num_border_elems); 00257 00258 nemesis_err_flag = 00259 Nemesis::ne_get_elem_map(ex_id, 00260 elem_mapi.empty() ? NULL : &elem_mapi[0], 00261 elem_mapb.empty() ? NULL : &elem_mapb[0], 00262 libMesh::processor_id() 00263 ); 00264 this->check_err(nemesis_err_flag, "Error reading element maps!"); 00265 00266 00267 if (_verbose) 00268 { 00269 // These are not contiguous ranges.... 00270 //libMesh::out << "[" << libMesh::processor_id() << "] " << "first interior elem id=" << elem_mapi[0] << std::endl; 00271 //libMesh::out << "[" << libMesh::processor_id() << "] " << "last interior elem id=" << elem_mapi.back() << std::endl; 00272 //libMesh::out << "[" << libMesh::processor_id() << "] " << "first boundary elem id=" << elem_mapb[0] << std::endl; 00273 //libMesh::out << "[" << libMesh::processor_id() << "] " << "last boundary elem id=" << elem_mapb.back() << std::endl; 00274 libMesh::out << "[" << libMesh::processor_id() << "] elem_mapi[i] = "; 00275 for (unsigned int i=0; i< static_cast<unsigned int>(num_internal_elems-1); ++i) 00276 libMesh::out << elem_mapi[i] << ", "; 00277 libMesh::out << "... " << elem_mapi.back() << std::endl; 00278 00279 libMesh::out << "[" << libMesh::processor_id() << "] elem_mapb[i] = "; 00280 for (unsigned int i=0; i< static_cast<unsigned int>(std::min(10, num_border_elems-1)); ++i) 00281 libMesh::out << elem_mapb[i] << ", "; 00282 libMesh::out << "... " << elem_mapb.back() << std::endl; 00283 } 00284 } 00285 00286 00287 00288 00289 void Nemesis_IO_Helper::get_node_map() 00290 { 00291 node_mapi.resize(num_internal_nodes); 00292 node_mapb.resize(num_border_nodes); 00293 node_mape.resize(num_external_nodes); 00294 00295 nemesis_err_flag = 00296 Nemesis::ne_get_node_map(ex_id, 00297 node_mapi.empty() ? NULL : &node_mapi[0], 00298 node_mapb.empty() ? NULL : &node_mapb[0], 00299 node_mape.empty() ? NULL : &node_mape[0], 00300 libMesh::processor_id() 00301 ); 00302 this->check_err(nemesis_err_flag, "Error reading node maps!"); 00303 00304 if (_verbose) 00305 { 00306 // Remark: The Exodus/Nemesis node numbring is always (?) 1-based! This means the first interior node id will 00307 // always be == 1. 00308 libMesh::out << "[" << libMesh::processor_id() << "] " << "first interior node id=" << node_mapi[0] << std::endl; 00309 libMesh::out << "[" << libMesh::processor_id() << "] " << "last interior node id=" << node_mapi.back() << std::endl; 00310 00311 libMesh::out << "[" << libMesh::processor_id() << "] " << "first boundary node id=" << node_mapb[0] << std::endl; 00312 libMesh::out << "[" << libMesh::processor_id() << "] " << "last boundary node id=" << node_mapb.back() << std::endl; 00313 00314 // The number of external nodes is sometimes zero, don't try to access 00315 // node_mape.back() in this case! 00316 if (num_external_nodes > 0) 00317 { 00318 libMesh::out << "[" << libMesh::processor_id() << "] " << "first external node id=" << node_mape[0] << std::endl; 00319 libMesh::out << "[" << libMesh::processor_id() << "] " << "last external node id=" << node_mape.back() << std::endl; 00320 } 00321 } 00322 } 00323 00324 00325 00326 void Nemesis_IO_Helper::get_cmap_params() 00327 { 00328 node_cmap_ids.resize(num_node_cmaps); 00329 node_cmap_node_cnts.resize(num_node_cmaps); 00330 elem_cmap_ids.resize(num_elem_cmaps); 00331 elem_cmap_elem_cnts.resize(num_elem_cmaps); 00332 00333 nemesis_err_flag = 00334 Nemesis::ne_get_cmap_params(ex_id, 00335 node_cmap_ids.empty() ? NULL : &node_cmap_ids[0], 00336 node_cmap_node_cnts.empty() ? NULL : &node_cmap_node_cnts[0], 00337 elem_cmap_ids.empty() ? NULL : &elem_cmap_ids[0], 00338 elem_cmap_elem_cnts.empty() ? NULL : &elem_cmap_elem_cnts[0], 00339 libMesh::processor_id()); 00340 this->check_err(nemesis_err_flag, "Error reading cmap parameters!"); 00341 00342 00343 if (_verbose) 00344 { 00345 libMesh::out << "[" << libMesh::processor_id() << "] "; 00346 for (unsigned int i=0; i<node_cmap_ids.size(); ++i) 00347 libMesh::out << "node_cmap_ids["<<i<<"]=" << node_cmap_ids[i] << " "; 00348 libMesh::out << std::endl; 00349 00350 libMesh::out << "[" << libMesh::processor_id() << "] "; 00351 for (unsigned int i=0; i<node_cmap_node_cnts.size(); ++i) 00352 libMesh::out << "node_cmap_node_cnts["<<i<<"]=" << node_cmap_node_cnts[i] << " "; 00353 libMesh::out << std::endl; 00354 00355 libMesh::out << "[" << libMesh::processor_id() << "] "; 00356 for (unsigned int i=0; i<elem_cmap_ids.size(); ++i) 00357 libMesh::out << "elem_cmap_ids["<<i<<"]=" << elem_cmap_ids[i] << " "; 00358 libMesh::out << std::endl; 00359 00360 libMesh::out << "[" << libMesh::processor_id() << "] "; 00361 for (unsigned int i=0; i<elem_cmap_elem_cnts.size(); ++i) 00362 libMesh::out << "elem_cmap_elem_cnts["<<i<<"]=" << elem_cmap_elem_cnts[i] << " "; 00363 libMesh::out << std::endl; 00364 } 00365 } 00366 00367 00368 00369 void Nemesis_IO_Helper::get_node_cmap() 00370 { 00371 node_cmap_node_ids.resize(num_node_cmaps); 00372 node_cmap_proc_ids.resize(num_node_cmaps); 00373 00374 for (unsigned int i=0; i<node_cmap_node_ids.size(); ++i) 00375 { 00376 node_cmap_node_ids[i].resize(node_cmap_node_cnts[i]); 00377 node_cmap_proc_ids[i].resize(node_cmap_node_cnts[i]); 00378 00379 nemesis_err_flag = 00380 Nemesis::ne_get_node_cmap(ex_id, 00381 node_cmap_ids.empty() ? 0 : node_cmap_ids[i], 00382 node_cmap_node_ids[i].empty() ? NULL : &node_cmap_node_ids[i][0], 00383 node_cmap_proc_ids[i].empty() ? NULL : &node_cmap_proc_ids[i][0], 00384 libMesh::processor_id()); 00385 this->check_err(nemesis_err_flag, "Error reading node cmap node and processor ids!"); 00386 00387 if (_verbose) 00388 { 00389 libMesh::out << "[" << libMesh::processor_id() << "] node_cmap_node_ids["<<i<<"]="; 00390 for (unsigned int j=0; j<node_cmap_node_ids[i].size(); ++j) 00391 libMesh::out << node_cmap_node_ids[i][j] << " "; 00392 libMesh::out << std::endl; 00393 00394 // This is basically a vector, all entries of which are = node_cmap_ids[i] 00395 // Not sure if it's always guaranteed to be that or what... 00396 libMesh::out << "[" << libMesh::processor_id() << "] node_cmap_proc_ids["<<i<<"]="; 00397 for (unsigned int j=0; j<node_cmap_proc_ids[i].size(); ++j) 00398 libMesh::out << node_cmap_proc_ids[i][j] << " "; 00399 libMesh::out << std::endl; 00400 } 00401 } 00402 } 00403 00404 00405 00406 void Nemesis_IO_Helper::get_elem_cmap() 00407 { 00408 elem_cmap_elem_ids.resize(num_elem_cmaps); 00409 elem_cmap_side_ids.resize(num_elem_cmaps); 00410 elem_cmap_proc_ids.resize(num_elem_cmaps); 00411 00412 for (unsigned int i=0; i<elem_cmap_elem_ids.size(); ++i) 00413 { 00414 elem_cmap_elem_ids[i].resize(elem_cmap_elem_cnts[i]); 00415 elem_cmap_side_ids[i].resize(elem_cmap_elem_cnts[i]); 00416 elem_cmap_proc_ids[i].resize(elem_cmap_elem_cnts[i]); 00417 00418 nemesis_err_flag = 00419 Nemesis::ne_get_elem_cmap(ex_id, 00420 elem_cmap_ids.empty() ? 0 : elem_cmap_ids[i], 00421 elem_cmap_elem_ids[i].empty() ? NULL : &elem_cmap_elem_ids[i][0], 00422 elem_cmap_side_ids[i].empty() ? NULL : &elem_cmap_side_ids[i][0], 00423 elem_cmap_proc_ids[i].empty() ? NULL : &elem_cmap_proc_ids[i][0], 00424 libMesh::processor_id()); 00425 this->check_err(nemesis_err_flag, "Error reading elem cmap elem, side, and processor ids!"); 00426 00427 00428 if (_verbose) 00429 { 00430 libMesh::out << "[" << libMesh::processor_id() << "] elem_cmap_elem_ids["<<i<<"]="; 00431 for (unsigned int j=0; j<elem_cmap_elem_ids[i].size(); ++j) 00432 libMesh::out << elem_cmap_elem_ids[i][j] << " "; 00433 libMesh::out << std::endl; 00434 00435 // These must be the (local) side IDs (in the ExodusII face numbering scheme) 00436 // of the sides shared across processors. 00437 libMesh::out << "[" << libMesh::processor_id() << "] elem_cmap_side_ids["<<i<<"]="; 00438 for (unsigned int j=0; j<elem_cmap_side_ids[i].size(); ++j) 00439 libMesh::out << elem_cmap_side_ids[i][j] << " "; 00440 libMesh::out << std::endl; 00441 00442 // This is basically a vector, all entries of which are = elem_cmap_ids[i] 00443 // Not sure if it's always guaranteed to be that or what... 00444 libMesh::out << "[" << libMesh::processor_id() << "] elem_cmap_proc_ids["<<i<<"]="; 00445 for (unsigned int j=0; j<elem_cmap_proc_ids[i].size(); ++j) 00446 libMesh::out << elem_cmap_proc_ids[i][j] << " "; 00447 libMesh::out << std::endl; 00448 } 00449 } 00450 } 00451 00452 00453 00454 00455 void Nemesis_IO_Helper::put_init_info(unsigned num_proc_in, 00456 unsigned num_proc_in_file_in, 00457 const char* ftype_in) 00458 { 00459 nemesis_err_flag = 00460 Nemesis::ne_put_init_info(ex_id, 00461 num_proc_in, 00462 num_proc_in_file_in, 00463 const_cast<char*>(ftype_in)); 00464 00465 this->check_err(nemesis_err_flag, "Error writing initial information!"); 00466 } 00467 00468 00469 00470 00471 void Nemesis_IO_Helper::put_init_global(dof_id_type num_nodes_global_in, 00472 dof_id_type num_elems_global_in, 00473 unsigned num_elem_blks_global_in, 00474 unsigned num_node_sets_global_in, 00475 unsigned num_side_sets_global_in) 00476 { 00477 nemesis_err_flag = 00478 Nemesis::ne_put_init_global(ex_id, 00479 num_nodes_global_in, 00480 num_elems_global_in, 00481 num_elem_blks_global_in, 00482 num_node_sets_global_in, 00483 num_side_sets_global_in); 00484 00485 this->check_err(nemesis_err_flag, "Error writing initial global data!"); 00486 } 00487 00488 00489 00490 void Nemesis_IO_Helper::put_eb_info_global(std::vector<int>& global_elem_blk_ids_in, 00491 std::vector<int>& global_elem_blk_cnts_in) 00492 { 00493 nemesis_err_flag = 00494 Nemesis::ne_put_eb_info_global(ex_id, 00495 &global_elem_blk_ids_in[0], 00496 &global_elem_blk_cnts_in[0]); 00497 00498 this->check_err(nemesis_err_flag, "Error writing global element block information!"); 00499 } 00500 00501 00502 00503 00504 void Nemesis_IO_Helper::put_ns_param_global(std::vector<int>& global_nodeset_ids_in, 00505 std::vector<int>& num_global_node_counts_in, 00506 std::vector<int>& num_global_node_df_counts_in) 00507 { 00508 // Only add nodesets if there are some 00509 if(global_nodeset_ids.size()) 00510 { 00511 nemesis_err_flag = 00512 Nemesis::ne_put_ns_param_global(ex_id, 00513 &global_nodeset_ids_in[0], 00514 &num_global_node_counts_in[0], 00515 &num_global_node_df_counts_in[0]); 00516 } 00517 00518 this->check_err(nemesis_err_flag, "Error writing global nodeset parameters!"); 00519 } 00520 00521 00522 00523 00524 void Nemesis_IO_Helper::put_ss_param_global(std::vector<int>& global_sideset_ids_in, 00525 std::vector<int>& num_global_side_counts_in, 00526 std::vector<int>& num_global_side_df_counts_in) 00527 { 00528 // Only add sidesets if there are some 00529 if(global_sideset_ids.size()) 00530 { 00531 nemesis_err_flag = 00532 Nemesis::ne_put_ss_param_global(ex_id, 00533 &global_sideset_ids_in[0], 00534 &num_global_side_counts_in[0], 00535 &num_global_side_df_counts_in[0]); 00536 } 00537 00538 this->check_err(nemesis_err_flag, "Error writing global sideset parameters!"); 00539 } 00540 00541 00542 00543 00544 void Nemesis_IO_Helper::put_loadbal_param(unsigned num_internal_nodes_in, 00545 unsigned num_border_nodes_in, 00546 unsigned num_external_nodes_in, 00547 unsigned num_internal_elems_in, 00548 unsigned num_border_elems_in, 00549 unsigned num_node_cmaps_in, 00550 unsigned num_elem_cmaps_in) 00551 { 00552 nemesis_err_flag = 00553 Nemesis::ne_put_loadbal_param(ex_id, 00554 num_internal_nodes_in, 00555 num_border_nodes_in, 00556 num_external_nodes_in, 00557 num_internal_elems_in, 00558 num_border_elems_in, 00559 num_node_cmaps_in, 00560 num_elem_cmaps_in, 00561 libMesh::processor_id()); 00562 00563 this->check_err(nemesis_err_flag, "Error writing loadbal parameters!"); 00564 } 00565 00566 00567 00568 00569 00570 void Nemesis_IO_Helper::put_cmap_params(std::vector<int>& node_cmap_ids_in, 00571 std::vector<int>& node_cmap_node_cnts_in, 00572 std::vector<int>& elem_cmap_ids_in, 00573 std::vector<int>& elem_cmap_elem_cnts_in) 00574 { 00575 // We might not have cmaps on every processor in some corner 00576 // cases 00577 if(node_cmap_ids.size()) 00578 { 00579 nemesis_err_flag = 00580 Nemesis::ne_put_cmap_params(ex_id, 00581 &node_cmap_ids_in[0], 00582 &node_cmap_node_cnts_in[0], 00583 &elem_cmap_ids_in[0], 00584 &elem_cmap_elem_cnts_in[0], 00585 libMesh::processor_id()); 00586 } 00587 00588 this->check_err(nemesis_err_flag, "Error writing cmap parameters!"); 00589 } 00590 00591 00592 00593 00594 void Nemesis_IO_Helper::put_node_cmap(std::vector<std::vector<int> >& node_cmap_node_ids_in, 00595 std::vector<std::vector<int> >& node_cmap_proc_ids_in) 00596 { 00597 00598 // Print to screen what we are about to print to Nemesis file 00599 if (_verbose) 00600 { 00601 for (unsigned i=0; i<node_cmap_node_ids_in.size(); ++i) 00602 { 00603 libMesh::out << "[" << libMesh::processor_id() << "] put_node_cmap() : nodes communicated to proc " 00604 << this->node_cmap_ids[i] 00605 << " = "; 00606 for (unsigned j=0; j<node_cmap_node_ids_in[i].size(); ++j) 00607 libMesh::out << node_cmap_node_ids_in[i][j] << " "; 00608 libMesh::out << std::endl; 00609 } 00610 00611 for (unsigned i=0; i<node_cmap_node_ids_in.size(); ++i) 00612 { 00613 libMesh::out << "[" << libMesh::processor_id() << "] put_node_cmap() : processor IDs = "; 00614 for (unsigned j=0; j<node_cmap_proc_ids_in[i].size(); ++j) 00615 libMesh::out << node_cmap_proc_ids_in[i][j] << " "; 00616 libMesh::out << std::endl; 00617 } 00618 } 00619 00620 for (unsigned int i=0; i<node_cmap_node_ids_in.size(); ++i) 00621 { 00622 nemesis_err_flag = 00623 Nemesis::ne_put_node_cmap(ex_id, 00624 this->node_cmap_ids[i], 00625 &node_cmap_node_ids_in[i][0], 00626 &node_cmap_proc_ids_in[i][0], 00627 libMesh::processor_id()); 00628 00629 this->check_err(nemesis_err_flag, "Error writing node communication map to file!"); 00630 } 00631 } 00632 00633 00634 00635 00636 void Nemesis_IO_Helper::put_node_map(std::vector<int>& node_mapi_in, 00637 std::vector<int>& node_mapb_in, 00638 std::vector<int>& node_mape_in) 00639 { 00640 nemesis_err_flag = 00641 Nemesis::ne_put_node_map(ex_id, 00642 node_mapi_in.empty() ? NULL : &node_mapi_in[0], 00643 node_mapb_in.empty() ? NULL : &node_mapb_in[0], 00644 node_mape_in.empty() ? NULL : &node_mape_in[0], // Don't take address of empty vector... 00645 libMesh::processor_id()); 00646 00647 this->check_err(nemesis_err_flag, "Error writing Nemesis internal and border node maps to file!"); 00648 } 00649 00650 00651 00652 00653 void Nemesis_IO_Helper::put_elem_cmap(std::vector<std::vector<int> >& elem_cmap_elem_ids_in, 00654 std::vector<std::vector<int> >& elem_cmap_side_ids_in, 00655 std::vector<std::vector<int> >& elem_cmap_proc_ids_in) 00656 { 00657 for (unsigned int i=0; i<elem_cmap_ids.size(); ++i) 00658 { 00659 nemesis_err_flag = 00660 Nemesis::ne_put_elem_cmap(ex_id, 00661 this->elem_cmap_ids[i], 00662 &elem_cmap_elem_ids_in[i][0], 00663 &elem_cmap_side_ids_in[i][0], 00664 &elem_cmap_proc_ids_in[i][0], 00665 libMesh::processor_id()); 00666 00667 this->check_err(nemesis_err_flag, "Error writing elem communication map to file!"); 00668 } 00669 } 00670 00671 00672 00673 00674 void Nemesis_IO_Helper::put_elem_map(std::vector<int>& elem_mapi_in, 00675 std::vector<int>& elem_mapb_in) 00676 { 00677 nemesis_err_flag = 00678 Nemesis::ne_put_elem_map(ex_id, 00679 elem_mapi_in.empty() ? NULL : &elem_mapi_in[0], 00680 elem_mapb_in.empty() ? NULL : &elem_mapb_in[0], 00681 libMesh::processor_id()); 00682 00683 this->check_err(nemesis_err_flag, "Error writing Nemesis internal and border element maps to file!"); 00684 } 00685 00686 00687 00688 00689 00690 00691 void Nemesis_IO_Helper::put_n_coord(unsigned start_node_num, 00692 unsigned num_nodes_in, 00693 std::vector<Real>& x_coor, 00694 std::vector<Real>& y_coor, 00695 std::vector<Real>& z_coor) 00696 { 00697 nemesis_err_flag = 00698 Nemesis::ne_put_n_coord(ex_id, 00699 start_node_num, 00700 num_nodes_in, 00701 &x_coor[0], 00702 &y_coor[0], 00703 &z_coor[0]); 00704 00705 this->check_err(nemesis_err_flag, "Error writing coords to file!"); 00706 } 00707 00708 00709 00710 00711 00712 00713 00714 00715 // Note: we can't reuse the ExodusII_IO_Helper code directly, since it only runs 00716 // on processor 0. TODO: We could have the body of this function as a separate 00717 // function and then ExodusII_IO_Helper would only call it if on processor 0... 00718 void Nemesis_IO_Helper::create(std::string filename) 00719 { 00720 // Fall back on double precision when necessary since ExodusII 00721 // doesn't seem to support long double 00722 comp_ws = libmesh_cast_int<int> 00723 (std::min(sizeof(Real),sizeof(double))); 00724 io_ws = libmesh_cast_int<int> 00725 (std::min(sizeof(Real),sizeof(double))); 00726 00727 this->ex_id = exII::ex_create(filename.c_str(), EX_CLOBBER, &comp_ws, &io_ws); 00728 00729 check_err(ex_id, "Error creating Nemesis mesh file."); 00730 00731 if (_verbose) 00732 libMesh::out << "File created successfully." << std::endl; 00733 00734 this->_created = true; 00735 } 00736 00737 00738 00739 00740 00741 00742 00743 00744 void Nemesis_IO_Helper::initialize(std::string title_in, const MeshBase & mesh) 00745 { 00746 // Make sure that the reference passed in is really a ParallelMesh 00747 // const ParallelMesh& pmesh = libmesh_cast_ref<const ParallelMesh&>(mesh); 00748 const MeshBase& pmesh = mesh; 00749 00750 // According to Nemesis documentation, first call when writing should be to 00751 // ne_put_init_info(). Our reader doesn't actually call this, but we should 00752 // strive to be as close to a normal nemesis file as possible... 00753 this->put_init_info(libMesh::n_processors(), 1, "p"); 00754 00755 00756 // Gather global "initial" information for Nemesis. This consists of 00757 // three parts labelled I, II, and III below... 00758 00759 // 00760 // I.) Need to compute the number of global element blocks. To be consistent with 00761 // Exodus, we also incorrectly associate the number of element blocks with the 00762 // number of libmesh subdomains... 00763 // 00764 this->compute_num_global_elem_blocks(pmesh); 00765 00766 // 00767 // II.) Determine the global number of nodesets by communication. 00768 // This code relies on BoundaryInfo storing side and node 00769 // boundary IDs separately at the time they are added to the 00770 // BoundaryInfo object. 00771 // 00772 this->compute_num_global_nodesets(pmesh); 00773 00774 // 00775 // III.) Need to compute the global number of sidesets by communication: 00776 // This code relies on BoundaryInfo storing side and node 00777 // boundary IDs separately at the time they are added to the 00778 // BoundaryInfo object. 00779 // 00780 this->compute_num_global_sidesets(pmesh); 00781 00782 // Now write the global data obtained in steps I, II, and III to the Nemesis file 00783 this->put_init_global(pmesh.parallel_n_nodes(), 00784 pmesh.parallel_n_elem(), 00785 this->num_elem_blks_global, /* I. */ 00786 this->num_node_sets_global, /* II. */ 00787 this->num_side_sets_global /* III. */ 00788 ); 00789 00790 // Next, we'll write global element block information to the file. This was already 00791 // gathered in step I. above 00792 this->put_eb_info_global(this->global_elem_blk_ids, 00793 this->global_elem_blk_cnts); 00794 00795 00796 // Next, write global nodeset information to the file. This was already gathered in 00797 // step II. above. 00798 this->num_global_node_df_counts.clear(); 00799 this->num_global_node_df_counts.resize(this->global_nodeset_ids.size()); // distribution factors all zero... 00800 this->put_ns_param_global(this->global_nodeset_ids, 00801 this->num_global_node_counts, 00802 this->num_global_node_df_counts); 00803 00804 00805 // Next, write global sideset information to the file. This was already gathered in 00806 // step III. above. 00807 this->num_global_side_df_counts.clear(); 00808 this->num_global_side_df_counts.resize(this->global_sideset_ids.size()); // distribution factors all zero... 00809 this->put_ss_param_global(this->global_sideset_ids, 00810 this->num_global_side_counts, 00811 this->num_global_side_df_counts); 00812 00813 00814 // Before we go any further we need to derive consistent node and 00815 // element numbering schemes for all local elems and nodes connected 00816 // to local elements. 00817 // 00818 // Must be called *after* the local_subdomain_counts map has been constructed 00819 // by the compute_num_global_elem_blocks() function! 00820 this->build_element_and_node_maps(pmesh); 00821 00822 // Next step is to write "load balance" parameters. Several things need to 00823 // be computed first though... 00824 00825 // First we'll collect IDs of border nodes. 00826 this->compute_border_node_ids(pmesh); 00827 00828 // Next we'll collect numbers of internal and border elements, and internal nodes. 00829 // Note: "A border node does not a border element make...", that is, just because one 00830 // of an element's nodes has been identified as a border node, the element is not 00831 // necessarily a border element. It must have a side on the boundary between processors, 00832 // i.e. have a face neighbor with a different processor id... 00833 this->compute_internal_and_border_elems_and_internal_nodes(pmesh); 00834 00835 // Finally we are ready to write the loadbal information to the file 00836 this->put_loadbal_param(this->num_internal_nodes, 00837 this->num_border_nodes, 00838 this->num_external_nodes, 00839 this->num_internal_elems, 00840 this->num_border_elems, 00841 this->num_node_cmaps, 00842 this->num_elem_cmaps); 00843 00844 00845 // Now we need to compute the "communication map" parameters. These are basically 00846 // lists of nodes and elements which need to be communicated between different processors 00847 // when the mesh file is read back in. 00848 this->compute_communication_map_parameters(); 00849 00850 // Write communication map parameters to file. 00851 this->put_cmap_params(this->node_cmap_ids, 00852 this->node_cmap_node_cnts, 00853 this->elem_cmap_ids, 00854 this->elem_cmap_elem_cnts); 00855 00856 00857 // Ready the node communication maps. The node IDs which 00858 // are communicated are the ones currently stored in 00859 // proc_nodes_touched_intersections. 00860 this->compute_node_communication_maps(); 00861 00862 // Write the packed node communication vectors to file. 00863 this->put_node_cmap(this->node_cmap_node_ids, 00864 this->node_cmap_proc_ids); 00865 00866 00867 // Ready the node maps. These have nothing to do with communiction, they map 00868 // the nodes to internal, border, and external nodes in the file. 00869 this->compute_node_maps(); 00870 00871 // Call the Nemesis API to write the node maps to file. 00872 this->put_node_map(this->node_mapi, 00873 this->node_mapb, 00874 this->node_mape); 00875 00876 00877 00878 // Ready the element communication maps. This includes border 00879 // element IDs, sides which are on the border, and the processors to which 00880 // they are to be communicated... 00881 this->compute_elem_communication_maps(); 00882 00883 00884 00885 // Call the Nemesis API to write the packed element communication maps vectors to file 00886 this->put_elem_cmap(this->elem_cmap_elem_ids, 00887 this->elem_cmap_side_ids, 00888 this->elem_cmap_proc_ids); 00889 00890 00891 00892 00893 00894 00895 // Ready the Nemesis element maps (internal and border) for writing to file. 00896 this->compute_element_maps(); 00897 00898 // Call the Nemesis API to write the internal and border element IDs. 00899 this->put_elem_map(this->elem_mapi, 00900 this->elem_mapb); 00901 00902 00903 // Now write Exodus-specific initialization information, some of which is 00904 // different when you are using Nemesis. 00905 this->write_exodus_initialization_info(pmesh, title_in); 00906 } // end initialize() 00907 00908 00909 00910 00911 00912 00913 void Nemesis_IO_Helper::write_exodus_initialization_info(const MeshBase& pmesh, 00914 const std::string& title_in) 00915 { 00916 // This follows the convention of Exodus: we always write out the mesh as LIBMESH_DIM-dimensional, 00917 // even if it is 2D... 00918 this->num_dim = pmesh.spatial_dimension(); 00919 00920 this->num_elem = static_cast<unsigned int>(std::distance (pmesh.active_local_elements_begin(), 00921 pmesh.active_local_elements_end())); 00922 00923 // Exodus will also use *global* number of side and node sets, 00924 // though it will not write out entries for all of them... 00925 this->num_side_sets = 00926 libmesh_cast_int<int>(this->global_sideset_ids.size()); 00927 this->num_node_sets = 00928 libmesh_cast_int<int>(this->global_nodeset_ids.size()); 00929 00930 // We need to write the global number of blocks, even though this processor might not have 00931 // elements in some of them! 00932 this->num_elem_blk = this->num_elem_blks_global; 00933 00934 ex_err = exII::ex_put_init(ex_id, 00935 title_in.c_str(), 00936 this->num_dim, 00937 this->num_nodes, 00938 this->num_elem, 00939 this->num_elem_blk, 00940 this->num_node_sets, 00941 this->num_side_sets); 00942 00943 check_err(ex_err, "Error initializing new Nemesis file."); 00944 } 00945 00946 00947 00948 00949 00950 void Nemesis_IO_Helper::compute_element_maps() 00951 { 00952 // Make sure we don't have any leftover info 00953 this->elem_mapi.clear(); 00954 this->elem_mapb.clear(); 00955 00956 // Copy set contents into vectors 00957 this->elem_mapi.resize(this->internal_elem_ids.size()); 00958 this->elem_mapb.resize(this->border_elem_ids.size()); 00959 00960 { 00961 unsigned cnt = 0; 00962 for (std::set<unsigned>::iterator it=this->internal_elem_ids.begin(); 00963 it != this->internal_elem_ids.end(); 00964 ++it, ++cnt) 00965 this->elem_mapi[cnt] = libmesh_elem_num_to_exodus[(*it)]; // + 1; // Exodus is 1-based! 00966 } 00967 00968 { 00969 unsigned cnt = 0; 00970 for (std::set<unsigned>::iterator it=this->border_elem_ids.begin(); 00971 it != this->border_elem_ids.end(); 00972 ++it, ++cnt) 00973 this->elem_mapb[cnt] = libmesh_elem_num_to_exodus[(*it)]; // + 1; // Exodus is 1-based! 00974 } 00975 } 00976 00977 00978 00979 void Nemesis_IO_Helper::compute_elem_communication_maps() 00980 { 00981 // Make sure there is no leftover information 00982 this->elem_cmap_elem_ids.clear(); 00983 this->elem_cmap_side_ids.clear(); 00984 this->elem_cmap_proc_ids.clear(); 00985 00986 // Allocate enough space for all our element maps 00987 this->elem_cmap_elem_ids.resize(this->num_elem_cmaps); 00988 this->elem_cmap_side_ids.resize(this->num_elem_cmaps); 00989 this->elem_cmap_proc_ids.resize(this->num_elem_cmaps); 00990 { 00991 unsigned cnt=0; // Index into vectors 00992 for (proc_border_elem_sets_iterator it=this->proc_border_elem_sets.begin(); 00993 it != this->proc_border_elem_sets.end(); 00994 ++it) 00995 { 00996 // Make sure the current elem_cmap_id matches the index in our map of node intersections 00997 libmesh_assert_equal_to ( static_cast<unsigned>(this->elem_cmap_ids[cnt]), (*it).first ); 00998 00999 // Get reference to the set of IDs to be packed into the vector 01000 std::set<std::pair<unsigned,unsigned> >& elem_set = (*it).second; 01001 01002 // Resize the vectors to receive their payload 01003 this->elem_cmap_elem_ids[cnt].resize(elem_set.size()); 01004 this->elem_cmap_side_ids[cnt].resize(elem_set.size()); 01005 this->elem_cmap_proc_ids[cnt].resize(elem_set.size()); 01006 01007 std::set<std::pair<unsigned,unsigned> >::iterator elem_set_iter = elem_set.begin(); 01008 01009 // Pack the vectors with elem IDs, side IDs, and processor IDs. 01010 for (unsigned j=0; j<this->elem_cmap_elem_ids[cnt].size(); ++j, ++elem_set_iter) 01011 { 01012 this->elem_cmap_elem_ids[cnt][j] = libmesh_elem_num_to_exodus[(*elem_set_iter).first];// + 1; // Elem ID, Exodus is 1-based 01013 this->elem_cmap_side_ids[cnt][j] = (*elem_set_iter).second; // Side ID, this has already been converted above 01014 this->elem_cmap_proc_ids[cnt][j] = (*it).first; // All have the same processor ID 01015 } 01016 01017 cnt++;// increment vector index to go to next processor 01018 } 01019 } // end scope for packing 01020 } 01021 01022 01023 01024 01025 01026 void Nemesis_IO_Helper::compute_node_maps() 01027 { 01028 // Make sure we don't have any leftover information 01029 this->node_mapi.clear(); 01030 this->node_mapb.clear(); 01031 this->node_mape.clear(); 01032 01033 // Make sure there's enough space to hold all our node IDs 01034 this->node_mapi.resize(this->internal_node_ids.size()); 01035 this->node_mapb.resize(this->border_node_ids.size()); 01036 01037 // Copy set contents into vectors 01038 // 01039 // Can't use insert, since we are copying unsigned's into vector<int>... 01040 // this->node_mapi.insert(internal_node_ids.begin(), internal_node_ids.end()); 01041 // this->node_mapb.insert(boundary_node_ids.begin(), boundary_node_ids.end()); 01042 { 01043 unsigned cnt = 0; 01044 for (std::set<unsigned>::iterator it=this->internal_node_ids.begin(); 01045 it != this->internal_node_ids.end(); 01046 ++it, ++cnt) 01047 this->node_mapi[cnt] = this->libmesh_node_num_to_exodus[*it];// + 1; // Exodus is 1-based! 01048 } 01049 01050 { 01051 unsigned cnt=0; 01052 for (std::set<unsigned>::iterator it=this->border_node_ids.begin(); 01053 it != this->border_node_ids.end(); 01054 ++it, ++cnt) 01055 this->node_mapb[cnt] = this->libmesh_node_num_to_exodus[*it];// + 1; // Exodus is 1-based! 01056 } 01057 } 01058 01059 01060 01061 01062 01063 void Nemesis_IO_Helper::compute_node_communication_maps() 01064 { 01065 // Make sure there's no left-over information 01066 this->node_cmap_node_ids.clear(); 01067 this->node_cmap_proc_ids.clear(); 01068 01069 // Allocate enough space for all our node maps 01070 this->node_cmap_node_ids.resize(this->num_node_cmaps); 01071 this->node_cmap_proc_ids.resize(this->num_node_cmaps); 01072 { 01073 unsigned cnt=0; // Index into vectors 01074 for (proc_nodes_touched_iterator it = this->proc_nodes_touched_intersections.begin(); 01075 it != this->proc_nodes_touched_intersections.end(); 01076 ++it) 01077 { 01078 // Make sure the current node_cmap_id matches the index in our map of node intersections 01079 libmesh_assert_equal_to ( static_cast<unsigned>(this->node_cmap_ids[cnt]), (*it).first ); 01080 01081 // Get reference to the set of IDs to be packed into the vector. 01082 std::set<unsigned>& node_set = (*it).second; 01083 01084 //std::cout << "[" << libMesh::processor_id() << "] node_set.size()=" << node_set.size() << std::endl; 01085 01086 // Resize the vectors to receive their payload 01087 this->node_cmap_node_ids[cnt].resize(node_set.size()); 01088 this->node_cmap_proc_ids[cnt].resize(node_set.size()); 01089 01090 std::set<unsigned>::iterator node_set_iter = node_set.begin(); 01091 01092 // Pack the vectors with node IDs and processor IDs. 01093 for (unsigned j=0; j<this->node_cmap_node_ids[cnt].size(); ++j, ++node_set_iter) 01094 { 01095 this->node_cmap_node_ids[cnt][j] = this->libmesh_node_num_to_exodus[*node_set_iter];//(*node_set_iter) + 1; // Exodus is 1-based 01096 this->node_cmap_proc_ids[cnt][j] = (*it).first; 01097 } 01098 01099 cnt++;// increment vector index to go to next processor 01100 } 01101 } // end scope for packing 01102 01103 // if (_verbose) 01104 // libMesh::out << "Done packing." << std::endl; 01105 01106 // Print out the vectors we just packed 01107 if (_verbose) 01108 { 01109 for (unsigned i=0; i<this->node_cmap_node_ids.size(); ++i) 01110 { 01111 libMesh::out << "[" << libMesh::processor_id() << "] nodes communicated to proc " 01112 << this->node_cmap_ids[i] 01113 << " = "; 01114 for (unsigned j=0; j<this->node_cmap_node_ids[i].size(); ++j) 01115 libMesh::out << this->node_cmap_node_ids[i][j] << " "; 01116 libMesh::out << std::endl; 01117 } 01118 01119 for (unsigned i=0; i<this->node_cmap_node_ids.size(); ++i) 01120 { 01121 libMesh::out << "[" << libMesh::processor_id() << "] processor ID node communicated to = "; 01122 for (unsigned j=0; j<this->node_cmap_proc_ids[i].size(); ++j) 01123 libMesh::out << this->node_cmap_proc_ids[i][j] << " "; 01124 libMesh::out << std::endl; 01125 } 01126 } 01127 } 01128 01129 01130 01131 01132 void Nemesis_IO_Helper::compute_communication_map_parameters() 01133 { 01134 // For the nodes, these are the number of entries in the sets in proc_nodes_touched_intersections 01135 // map computed above. Note: this map does not contain self-intersections so we can loop over it 01136 // directly. 01137 this->node_cmap_node_cnts.clear(); // Make sure we don't have any leftover information... 01138 this->node_cmap_ids.clear(); // Make sure we don't have any leftover information... 01139 this->node_cmap_node_cnts.resize(this->num_node_cmaps); 01140 this->node_cmap_ids.resize(this->num_node_cmaps); 01141 01142 { 01143 unsigned cnt=0; // Index into the vector 01144 for (proc_nodes_touched_iterator it = this->proc_nodes_touched_intersections.begin(); 01145 it != this->proc_nodes_touched_intersections.end(); 01146 ++it) 01147 { 01148 this->node_cmap_ids[cnt] = (*it).first; // The ID of the proc we communicate with 01149 this->node_cmap_node_cnts[cnt] = (*it).second.size(); // The number of nodes we communicate 01150 cnt++; // increment vector index! 01151 } 01152 } 01153 01154 // Print the packed vectors we just filled 01155 if (_verbose) 01156 { 01157 libMesh::out << "[" << libMesh::processor_id() << "] node_cmap_node_cnts = "; 01158 for (unsigned i=0; i<node_cmap_node_cnts.size(); ++i) 01159 libMesh::out << node_cmap_node_cnts[i] << ", "; 01160 libMesh::out << std::endl; 01161 01162 libMesh::out << "[" << libMesh::processor_id() << "] node_cmap_ids = "; 01163 for (unsigned i=0; i<node_cmap_ids.size(); ++i) 01164 libMesh::out << node_cmap_ids[i] << ", "; 01165 libMesh::out << std::endl; 01166 } 01167 01168 // For the elements, we have not yet computed all this information.. 01169 this->elem_cmap_elem_cnts.clear(); // Make sure we don't have any leftover information... 01170 this->elem_cmap_ids.clear(); // Make sure we don't have any leftover information... 01171 this->elem_cmap_elem_cnts.resize(this->num_elem_cmaps); 01172 this->elem_cmap_ids.resize(this->num_elem_cmaps); 01173 01174 // Pack the elem_cmap_ids and elem_cmap_elem_cnts vectors 01175 { 01176 unsigned cnt=0; // Index into the vectors we're filling 01177 for (proc_border_elem_sets_iterator it = this->proc_border_elem_sets.begin(); 01178 it != this->proc_border_elem_sets.end(); 01179 ++it) 01180 { 01181 this->elem_cmap_ids[cnt] = (*it).first; // The ID of the proc we communicate with 01182 this->elem_cmap_elem_cnts[cnt] = (*it).second.size(); // The number of elems we communicate to/from that proc 01183 cnt++; // increment vector index! 01184 } 01185 } 01186 01187 // Print the packed vectors we just filled 01188 if (_verbose) 01189 { 01190 libMesh::out << "[" << libMesh::processor_id() << "] elem_cmap_elem_cnts = "; 01191 for (unsigned i=0; i<elem_cmap_elem_cnts.size(); ++i) 01192 libMesh::out << elem_cmap_elem_cnts[i] << ", "; 01193 libMesh::out << std::endl; 01194 01195 libMesh::out << "[" << libMesh::processor_id() << "] elem_cmap_ids = "; 01196 for (unsigned i=0; i<elem_cmap_ids.size(); ++i) 01197 libMesh::out << elem_cmap_ids[i] << ", "; 01198 libMesh::out << std::endl; 01199 } 01200 } 01201 01202 01203 01204 01205 void 01206 Nemesis_IO_Helper::compute_internal_and_border_elems_and_internal_nodes(const MeshBase& pmesh) 01207 { 01208 // Set of all local, active element IDs. After we have identified border element 01209 // IDs, the set_difference between this set and the border_elem_ids set will give us 01210 // the set of internal_elem_ids. 01211 std::set<unsigned> all_elem_ids; 01212 01213 // A set of processor IDs which elements on this processor have as 01214 // neighbors. The size of this set will determine the number of 01215 // element communication maps in Exodus. 01216 std::set<unsigned> neighboring_processor_ids; 01217 01218 // Will be used to create conversion objects capable of mapping libmesh 01219 // element numberings into Nemesis numberings. 01220 ExodusII_IO_Helper::ElementMaps element_mapper; 01221 01222 MeshBase::const_element_iterator elem_it = pmesh.active_local_elements_begin(); 01223 MeshBase::const_element_iterator elem_end = pmesh.active_local_elements_end(); 01224 01225 for (; elem_it != elem_end; ++elem_it) 01226 { 01227 const Elem* elem = *elem_it; 01228 01229 // Add this Elem's ID to all_elem_ids, later we will take the difference 01230 // between this set and the set of border_elem_ids, to get the set of 01231 // internal_elem_ids. 01232 all_elem_ids.insert(elem->id()); 01233 01234 // Will be set to true if element is determined to be a border element 01235 bool is_border_elem = false; 01236 01237 // Construct a conversion object for this Element. This will help us map 01238 // Libmesh numberings into Nemesis numberings for sides. 01239 ExodusII_IO_Helper::Conversion conv = element_mapper.assign_conversion(elem->type()); 01240 01241 // Add all this element's node IDs to the set of all node IDs. 01242 // The set of internal_node_ids will be the set difference between 01243 // the set of all nodes and the set of border nodes. 01244 // 01245 // In addition, if any node of a local node is listed in the 01246 // border nodes list, then this element goes into the proc_border_elem_sets. 01247 // Note that there is not a 1:1 correspondence between 01248 // border_elem_ids and the entries which go into proc_border_elem_sets. 01249 // The latter is for communication purposes, ie determining which elements 01250 // should be shared between processors. 01251 for (unsigned int node=0; node<elem->n_nodes(); ++node) 01252 { 01253 this->nodes_attached_to_local_elems.insert(elem->node(node)); 01254 } // end loop over element's nodes 01255 01256 // Loop over element's neighbors, see if it has a neighbor which is off-processor 01257 for (unsigned int n=0; n<elem->n_neighbors(); ++n) 01258 { 01259 if (elem->neighbor(n) != NULL) 01260 { 01261 unsigned neighbor_proc_id = elem->neighbor(n)->processor_id(); 01262 01263 // If my neighbor has a different processor ID, I must be a border element. 01264 // Also track the neighboring processor ID if it is are different from our processor ID 01265 if (neighbor_proc_id != libMesh::processor_id()) 01266 { 01267 is_border_elem = true; 01268 neighboring_processor_ids.insert(neighbor_proc_id); 01269 01270 // Convert libmesh side(n) of this element into a side ID for Nemesis 01271 unsigned nemesis_side_id = conv.get_inverse_side_map(n); 01272 01273 if (_verbose) 01274 libMesh::out << "[" << libMesh::processor_id() << "] LibMesh side " 01275 << n 01276 << " mapped to (1-based) Exodus side " 01277 << nemesis_side_id 01278 << std::endl; 01279 01280 // Add this element's ID and the ID of the side which is on the boundary 01281 // to the set of border elements for this processor. 01282 // Note: if the set does not already exist, this creates it. 01283 this->proc_border_elem_sets[ neighbor_proc_id ].insert( std::make_pair(elem->id(), nemesis_side_id) ); 01284 } 01285 } 01286 } // end for loop over neighbors 01287 01288 // If we're on a border element, add it to the set 01289 if (is_border_elem) 01290 this->border_elem_ids.insert( elem->id() ); 01291 01292 } // end for loop over active local elements 01293 01294 // Take the set_difference between all elements and border elements to get internal 01295 // element IDs 01296 std::set_difference(all_elem_ids.begin(), all_elem_ids.end(), 01297 this->border_elem_ids.begin(), this->border_elem_ids.end(), 01298 std::inserter(this->internal_elem_ids, this->internal_elem_ids.end())); 01299 01300 // Take the set_difference between all nodes and border nodes to get internal nodes 01301 std::set_difference(this->nodes_attached_to_local_elems.begin(), this->nodes_attached_to_local_elems.end(), 01302 this->border_node_ids.begin(), this->border_node_ids.end(), 01303 std::inserter(this->internal_node_ids, this->internal_node_ids.end())); 01304 01305 if (_verbose) 01306 { 01307 libMesh::out << "[" << libMesh::processor_id() << "] neighboring_processor_ids = "; 01308 for (std::set<unsigned>::iterator it = neighboring_processor_ids.begin(); 01309 it != neighboring_processor_ids.end(); 01310 ++it) 01311 { 01312 libMesh::out << *it << " "; 01313 } 01314 libMesh::out << std::endl; 01315 } 01316 01317 // The size of the neighboring_processor_ids set should be the number of element communication maps 01318 this->num_elem_cmaps = neighboring_processor_ids.size(); 01319 01320 if (_verbose) 01321 libMesh::out << "[" << libMesh::processor_id() << "] " 01322 << "Number of neighboring processor IDs=" 01323 << this->num_elem_cmaps 01324 << std::endl; 01325 01326 if (_verbose) 01327 { 01328 // Print out counts of border elements for each processor 01329 for (proc_border_elem_sets_iterator it=this->proc_border_elem_sets.begin(); 01330 it != this->proc_border_elem_sets.end(); ++it) 01331 { 01332 libMesh::out << "[" << libMesh::processor_id() << "] " 01333 << "Proc " 01334 << (*it).first << " communicates " 01335 << (*it).second.size() << " elements." << std::endl; 01336 } 01337 } 01338 01339 // Store the number of internal and border elements, and the number of internal nodes, 01340 // to be written to the Nemesis file. 01341 this->num_internal_elems = this->internal_elem_ids.size(); 01342 this->num_border_elems = this->border_elem_ids.size(); 01343 this->num_internal_nodes = this->internal_node_ids.size(); 01344 01345 if (_verbose) 01346 { 01347 libMesh::out << "[" << libMesh::processor_id() << "] num_internal_nodes=" << this->num_internal_nodes << std::endl; 01348 libMesh::out << "[" << libMesh::processor_id() << "] num_border_nodes=" << this->num_border_nodes << std::endl; 01349 libMesh::out << "[" << libMesh::processor_id() << "] num_border_elems=" << this->num_border_elems << std::endl; 01350 libMesh::out << "[" << libMesh::processor_id() << "] num_internal_elems=" << this->num_internal_elems << std::endl; 01351 } 01352 } 01353 01354 01355 01356 void Nemesis_IO_Helper::compute_num_global_sidesets(const MeshBase& pmesh) 01357 { 01358 std::set<boundary_id_type> local_side_boundary_ids; 01359 01360 // 1.) Get reference to the set of side boundary IDs 01361 std::set<boundary_id_type> global_side_boundary_ids 01362 (pmesh.boundary_info->get_side_boundary_ids().begin(), 01363 pmesh.boundary_info->get_side_boundary_ids().end()); 01364 01365 // Save this set of local boundary side IDs for later 01366 local_side_boundary_ids = global_side_boundary_ids; 01367 01368 // 2.) Gather boundary side IDs from other processors 01369 CommWorld.set_union(global_side_boundary_ids); 01370 01371 // 3.) Now global_side_boundary_ids actually contains a global list of all side boundary IDs 01372 this->num_side_sets_global = global_side_boundary_ids.size(); 01373 01374 // 4.) Pack these sidesets into a vector so they can be written by Nemesis 01375 this->global_sideset_ids.clear(); // Make sure there is no leftover information 01376 this->global_sideset_ids.insert(this->global_sideset_ids.end(), 01377 global_side_boundary_ids.begin(), 01378 global_side_boundary_ids.end()); 01379 01380 if (_verbose) 01381 { 01382 libMesh::out << "[" << libMesh::processor_id() << "] global_sideset_ids = "; 01383 for (unsigned i=0; i<this->global_sideset_ids.size(); ++i) 01384 libMesh::out << this->global_sideset_ids[i] << ", "; 01385 libMesh::out << std::endl; 01386 } 01387 01388 // We also need global counts of sides in each of the sidesets. Again, there may be a 01389 // better way to do this... 01390 std::vector<dof_id_type> bndry_elem_list; 01391 std::vector<unsigned short int> bndry_side_list; 01392 std::vector<boundary_id_type> bndry_id_list; 01393 pmesh.boundary_info->build_side_list(bndry_elem_list, bndry_side_list, bndry_id_list); 01394 01395 // Similarly to the nodes, we can't count any sides for elements which aren't local 01396 std::vector<dof_id_type>::iterator it_elem=bndry_elem_list.begin(); 01397 std::vector<unsigned short>::iterator it_side=bndry_side_list.begin(); 01398 std::vector<boundary_id_type>::iterator it_id=bndry_id_list.begin(); 01399 for ( ; it_elem != bndry_elem_list.end(); ) 01400 { 01401 if (pmesh.elem( *it_elem )->processor_id() != libMesh::processor_id()) 01402 { 01403 // Get rid of this elem, but do it efficiently for a vector, by popping 01404 // it off the back. 01405 std::swap (*it_elem, bndry_elem_list.back() ); 01406 std::swap (*it_side, bndry_side_list.back() ); 01407 std::swap (*it_id, bndry_id_list.back() ); 01408 01409 bndry_elem_list.pop_back(); 01410 bndry_side_list.pop_back(); 01411 bndry_id_list.pop_back(); 01412 } 01413 else // elem is local, go to next 01414 { 01415 ++it_elem; 01416 ++it_side; 01417 ++it_id; 01418 } 01419 } 01420 01421 this->num_global_side_counts.clear(); // Make sure we don't have any leftover information 01422 this->num_global_side_counts.resize(this->global_sideset_ids.size()); 01423 01424 // Get the count for each global sideset ID 01425 for (unsigned i=0; i<global_sideset_ids.size(); ++i) 01426 { 01427 this->num_global_side_counts[i] = std::count(bndry_id_list.begin(), 01428 bndry_id_list.end(), 01429 this->global_sideset_ids[i]); 01430 } 01431 01432 if (_verbose) 01433 { 01434 libMesh::out << "[" << libMesh::processor_id() << "] num_global_side_counts = "; 01435 for (unsigned i=0; i<this->num_global_side_counts.size(); ++i) 01436 libMesh::out << this->num_global_side_counts[i] << ", "; 01437 libMesh::out << std::endl; 01438 } 01439 01440 // Finally sum up the result 01441 CommWorld.sum(this->num_global_side_counts); 01442 01443 if (_verbose) 01444 { 01445 libMesh::out << "[" << libMesh::processor_id() << "] num_global_side_counts = "; 01446 for (unsigned i=0; i<this->num_global_side_counts.size(); ++i) 01447 libMesh::out << this->num_global_side_counts[i] << ", "; 01448 libMesh::out << std::endl; 01449 } 01450 } 01451 01452 01453 01454 01455 01456 01457 void Nemesis_IO_Helper::compute_num_global_nodesets(const MeshBase& pmesh) 01458 { 01459 std::set<boundary_id_type> local_node_boundary_ids; 01460 01461 // 1.) Get reference to the set of node boundary IDs *for this processor* 01462 std::set<boundary_id_type> global_node_boundary_ids 01463 (pmesh.boundary_info->get_node_boundary_ids().begin(), 01464 pmesh.boundary_info->get_node_boundary_ids().end()); 01465 01466 // Save a copy of the local_node_boundary_ids... 01467 local_node_boundary_ids = global_node_boundary_ids; 01468 01469 // 2.) Gather boundary node IDs from other processors 01470 CommWorld.set_union(global_node_boundary_ids); 01471 01472 // 3.) Now global_node_boundary_ids actually contains a global list of all node boundary IDs 01473 this->num_node_sets_global = global_node_boundary_ids.size(); 01474 01475 // 4.) Create a vector<int> from the global_node_boundary_ids set 01476 this->global_nodeset_ids.clear(); 01477 this->global_nodeset_ids.insert(this->global_nodeset_ids.end(), 01478 global_node_boundary_ids.begin(), 01479 global_node_boundary_ids.end()); 01480 01481 if (_verbose) 01482 { 01483 libMesh::out << "[" << libMesh::processor_id() << "] global_nodeset_ids = "; 01484 for (unsigned i=0; i<global_nodeset_ids.size(); ++i) 01485 libMesh::out << global_nodeset_ids[i] << ", "; 01486 libMesh::out << std::endl; 01487 01488 libMesh::out << "[" << libMesh::processor_id() << "] local_node_boundary_ids = "; 01489 for (std::set<boundary_id_type>::iterator it = local_node_boundary_ids.begin(); 01490 it != local_node_boundary_ids.end(); 01491 ++it) 01492 libMesh::out << *it << ", "; 01493 libMesh::out << std::endl; 01494 } 01495 01496 // 7.) We also need to know the number of nodes which is in each of the nodesets, globally. 01497 // There is probably a better way to do this... 01498 std::vector<dof_id_type> boundary_node_list; 01499 std::vector<boundary_id_type> boundary_node_boundary_id_list; 01500 pmesh.boundary_info->build_node_list(boundary_node_list, boundary_node_boundary_id_list); 01501 01502 if (_verbose) 01503 { 01504 libMesh::out << "[" << libMesh::processor_id() << "] boundary_node_list.size()=" 01505 << boundary_node_list.size() << std::endl; 01506 libMesh::out << "[" << libMesh::processor_id() << "] (boundary_node_id, boundary_id) = "; 01507 for (unsigned i=0; i<boundary_node_list.size(); ++i) 01508 { 01509 libMesh::out << "(" << boundary_node_list[i] << ", " << boundary_node_boundary_id_list[i] << ") "; 01510 } 01511 libMesh::out << std::endl; 01512 } 01513 01514 // Now get the global information. In this case, we only want to count boundary 01515 // information for nodes *owned* by this processor, so there are no duplicates. 01516 01517 // Make sure we don't have any left over information 01518 this->num_global_node_counts.clear(); 01519 this->num_global_node_counts.resize(this->global_nodeset_ids.size()); 01520 01521 // Unfortunately, we can't just count up all occurrences of a given id, 01522 // that would give us duplicate entries when we do the parallel summation. 01523 // So instead, only count entries for nodes owned by this processor. 01524 // Start by getting rid of all non-local node entries from the vectors. 01525 std::vector<dof_id_type>::iterator it_node=boundary_node_list.begin(); 01526 std::vector<boundary_id_type>::iterator it_id=boundary_node_boundary_id_list.begin(); 01527 for ( ; it_node != boundary_node_list.end(); ) 01528 { 01529 if (pmesh.node_ptr( *it_node )->processor_id() != libMesh::processor_id()) 01530 { 01531 // Get rid of this node, but do it efficiently for a vector, by popping 01532 // it off the back. 01533 std::swap (*it_node, boundary_node_list.back() ); 01534 std::swap (*it_id, boundary_node_boundary_id_list.back() ); 01535 01536 boundary_node_list.pop_back(); 01537 boundary_node_boundary_id_list.pop_back(); 01538 } 01539 else // node is local, go to next 01540 { 01541 ++it_node; 01542 ++it_id; 01543 } 01544 } 01545 01546 // Now we can do the local count for each ID... 01547 for (unsigned i=0; i<global_nodeset_ids.size(); ++i) 01548 { 01549 this->num_global_node_counts[i] = std::count(boundary_node_boundary_id_list.begin(), 01550 boundary_node_boundary_id_list.end(), 01551 this->global_nodeset_ids[i]); 01552 } 01553 01554 // And finally we can sum them up 01555 CommWorld.sum(this->num_global_node_counts); 01556 01557 if (_verbose) 01558 { 01559 libMesh::out << "[" << libMesh::processor_id() << "] num_global_node_counts = "; 01560 for (unsigned i=0; i<num_global_node_counts.size(); ++i) 01561 libMesh::out << num_global_node_counts[i] << ", "; 01562 libMesh::out << std::endl; 01563 } 01564 } 01565 01566 01567 01568 01569 void Nemesis_IO_Helper::compute_num_global_elem_blocks(const MeshBase& pmesh) 01570 { 01571 // 1.) Loop over active local elements, build up set of subdomain IDs. 01572 std::set<subdomain_id_type> global_subdomain_ids; 01573 01574 // This map keeps track of the number of elements in each subdomain over all processors 01575 std::map<subdomain_id_type, unsigned> global_subdomain_counts; 01576 01577 MeshBase::const_element_iterator elem_it = pmesh.active_local_elements_begin(); 01578 MeshBase::const_element_iterator elem_end = pmesh.active_local_elements_end(); 01579 01580 for (; elem_it != elem_end; ++elem_it) 01581 { 01582 const Elem* elem = *elem_it; 01583 01584 subdomain_id_type cur_subdomain = elem->subdomain_id(); 01585 01586 /* 01587 // We can't have a zero subdomain ID in Exodus (for some reason?) 01588 // so map zero subdomains to a max value... 01589 if (cur_subdomain == 0) 01590 cur_subdomain = std::numeric_limits<subdomain_id_type>::max(); 01591 */ 01592 01593 global_subdomain_ids.insert(cur_subdomain); 01594 01595 // Increment the count of elements in this subdomain 01596 global_subdomain_counts[cur_subdomain]++; 01597 } 01598 01599 // We're next going to CommWorld.sum the subdomain counts, so save the local counts 01600 this->local_subdomain_counts = global_subdomain_counts; 01601 01602 { 01603 // 2.) Copy local subdomain IDs into a vector for communication 01604 std::vector<subdomain_id_type> global_subdomain_ids_vector(global_subdomain_ids.begin(), 01605 global_subdomain_ids.end()); 01606 01607 // 3.) Gather them into an enlarged vector 01608 CommWorld.allgather(global_subdomain_ids_vector); 01609 01610 // 4.) Insert any new IDs into the set (any duplicates will be dropped) 01611 global_subdomain_ids.insert(global_subdomain_ids_vector.begin(), 01612 global_subdomain_ids_vector.end()); 01613 } 01614 01615 // 5.) Now global_subdomain_ids actually contains a global list of all subdomain IDs 01616 this->num_elem_blks_global = global_subdomain_ids.size(); 01617 01618 // Print the number of elements found locally in each subdomain 01619 if (_verbose) 01620 { 01621 libMesh::out << "[" << libMesh::processor_id() << "] "; 01622 for (std::map<subdomain_id_type, unsigned>::iterator it=global_subdomain_counts.begin(); 01623 it != global_subdomain_counts.end(); 01624 ++it) 01625 { 01626 libMesh::out << "ID: " 01627 << static_cast<unsigned>((*it).first) 01628 << ", Count: " << (*it).second << ", "; 01629 } 01630 libMesh::out << std::endl; 01631 } 01632 01633 // 6.) CommWorld.sum up the number of elements in each block. We know the global 01634 // subdomain IDs, so pack them into a vector one by one. Use a vector of int since 01635 // that is what Nemesis wants 01636 this->global_elem_blk_cnts.resize(global_subdomain_ids.size()); 01637 01638 unsigned cnt=0; 01639 for (std::set<subdomain_id_type>::iterator it=global_subdomain_ids.begin(); 01640 it != global_subdomain_ids.end(); ++it) 01641 { 01642 // Find the entry in the local map, note: if not found, will be created with 0 default value, which is OK... 01643 this->global_elem_blk_cnts[cnt++] = global_subdomain_counts[*it]; 01644 } 01645 01646 // Sum up subdomain counts from all processors 01647 CommWorld.sum(this->global_elem_blk_cnts); 01648 01649 if (_verbose) 01650 { 01651 libMesh::out << "[" << libMesh::processor_id() << "] global_elem_blk_cnts = "; 01652 for (unsigned i=0; i<this->global_elem_blk_cnts.size(); ++i) 01653 libMesh::out << this->global_elem_blk_cnts[i] << ", "; 01654 libMesh::out << std::endl; 01655 } 01656 01657 // 7.) Create a vector<int> from the global_subdomain_ids set, for passing to Nemesis 01658 this->global_elem_blk_ids.clear(); 01659 this->global_elem_blk_ids.insert(this->global_elem_blk_ids.end(), // pos 01660 global_subdomain_ids.begin(), 01661 global_subdomain_ids.end()); 01662 01663 if (_verbose) 01664 { 01665 libMesh::out << "[" << libMesh::processor_id() << "] global_elem_blk_ids = "; 01666 for (unsigned i=0; i<this->global_elem_blk_ids.size(); ++i) 01667 libMesh::out << this->global_elem_blk_ids[i] << ", "; 01668 libMesh::out << std::endl; 01669 } 01670 01671 01672 // 8.) We will call put_eb_info_global later, it must be called after this->put_init_global(). 01673 } 01674 01675 01676 01677 01678 void Nemesis_IO_Helper::build_element_and_node_maps(const MeshBase& pmesh) 01679 { 01680 // If we don't have any local subdomains, it had better be because 01681 // we don't have any local elements 01682 #ifdef DEBUG 01683 if (local_subdomain_counts.empty()) 01684 { 01685 libmesh_assert(pmesh.active_local_elements_begin() == 01686 pmesh.active_local_elements_end()); 01687 libmesh_assert(this->nodes_attached_to_local_elems.empty()); 01688 } 01689 #endif 01690 01691 // Elements have to be numbered contiguously based on what block 01692 // number they are in. Therefore we have to do a bit of work to get 01693 // the block (ie subdomain) numbers first and store them off as 01694 // block_ids. 01695 01696 // Make sure there is no leftover information in the subdomain_map, and reserve 01697 // enough space to store the elements we need. 01698 this->subdomain_map.clear(); 01699 for (std::map<subdomain_id_type, unsigned>::iterator it=this->local_subdomain_counts.begin(); 01700 it != this->local_subdomain_counts.end(); 01701 ++it) 01702 { 01703 subdomain_id_type cur_subdomain = (*it).first; 01704 01705 /* 01706 // We can't have a zero subdomain ID in Exodus (for some reason?) 01707 // so map zero subdomains to a max value... 01708 if (cur_subdomain == 0) 01709 cur_subdomain = std::numeric_limits<subdomain_id_type>::max(); 01710 */ 01711 01712 if (_verbose) 01713 { 01714 libMesh::out << "[" << libMesh::processor_id() << "] " 01715 << "local_subdomain_counts [" << static_cast<unsigned>(cur_subdomain) << "]= " 01716 << (*it).second 01717 << std::endl; 01718 } 01719 01720 // *it.first is the subodmain ID, *it.second is the number of elements it contains 01721 this->subdomain_map[ cur_subdomain ].reserve( (*it).second ); 01722 } 01723 01724 01725 // First loop over the elements to figure out which elements are in which subdomain 01726 MeshBase::const_element_iterator elem_it = pmesh.active_local_elements_begin(); 01727 MeshBase::const_element_iterator elem_end = pmesh.active_local_elements_end(); 01728 01729 for (; elem_it != elem_end; ++elem_it) 01730 { 01731 const Elem * elem = *elem_it; 01732 01733 // Grab the nodes while we're here. 01734 for (unsigned int n=0; n<elem->n_nodes(); ++n) 01735 this->nodes_attached_to_local_elems.insert( elem->node(n) ); 01736 01737 unsigned int cur_subdomain = elem->subdomain_id(); 01738 01739 /* 01740 // We can't have a zero subdomain ID in Exodus (for some reason?) 01741 // so map zero subdomains to a max value... 01742 if(cur_subdomain == 0) 01743 cur_subdomain = std::numeric_limits<subdomain_id_type>::max(); 01744 */ 01745 01746 this->subdomain_map[cur_subdomain].push_back(elem->id()); 01747 } 01748 01749 // Set num_nodes which is used by exodusII_io_helper 01750 this->num_nodes = this->nodes_attached_to_local_elems.size(); 01751 01752 // Now come up with a 1-based numbering for these nodes 01753 this->exodus_node_num_to_libmesh.clear(); // Make sure it's empty 01754 this->exodus_node_num_to_libmesh.reserve(this->nodes_attached_to_local_elems.size()); 01755 01756 // Also make sure there's no leftover information in the map which goes the 01757 // other direction. 01758 this->libmesh_node_num_to_exodus.clear(); 01759 01760 // Set the map for nodes 01761 for(std::set<int>::iterator it = this->nodes_attached_to_local_elems.begin(); 01762 it != this->nodes_attached_to_local_elems.end(); 01763 ++it) 01764 { 01765 // I.e. given exodus_node_id, 01766 // exodus_node_num_to_libmesh[ exodus_node_id ] returns the libmesh ID for that node. 01767 // Note that even though most of Exodus is 1-based, this code will map an Exodus ID of 01768 // zero to some libmesh node ID. Is that a problem? 01769 this->exodus_node_num_to_libmesh.push_back(*it); 01770 01771 // Likewise, given libmesh_node_id, 01772 // libmesh_node_num_to_exodus[ libmesh_node_id ] returns the *Exodus* ID for that node. 01773 // Unlike the exodus_node_num_to_libmesh vector above, this one is a std::map 01774 this->libmesh_node_num_to_exodus[*it] = this->exodus_node_num_to_libmesh.size(); // should never be zero... 01775 } 01776 01777 // Now we're going to loop over the subdomain map and build a few things right 01778 // now that we'll use later. 01779 01780 // First make sure our data structures don't have any leftover data... 01781 this->exodus_elem_num_to_libmesh.clear(); 01782 this->block_ids.clear(); 01783 this->libmesh_elem_num_to_exodus.clear(); 01784 01785 // Now loop over each subdomain and get a unique numbering for the elements 01786 for(std::map<subdomain_id_type, std::vector<unsigned int> >::iterator it = this->subdomain_map.begin(); 01787 it != this->subdomain_map.end(); 01788 ++it) 01789 { 01790 block_ids.push_back((*it).first); 01791 01792 // Vector of element IDs for this subdomain 01793 std::vector<unsigned int>& elem_ids_this_subdomain = (*it).second; 01794 01795 // The code below assumes this subdomain block is not empty, make sure that's the case! 01796 if (elem_ids_this_subdomain.size() == 0) 01797 { 01798 libMesh::err << "Error, no element IDs found in subdomain " << (*it).first << std::endl; 01799 libmesh_error(); 01800 } 01801 01802 ExodusII_IO_Helper::ElementMaps em; 01803 01804 // Use the first element in this block to get representative information. 01805 // Note that Exodus assumes all elements in a block are of the same type! 01806 // We are using that same assumption here! 01807 const ExodusII_IO_Helper::Conversion conv = em.assign_conversion(pmesh.elem(elem_ids_this_subdomain[0])->type()); 01808 this->num_nodes_per_elem = pmesh.elem(elem_ids_this_subdomain[0])->n_nodes(); 01809 01810 // Get a reference to the connectivity vector for this subdomain. This vector 01811 // is most likely empty, we are going to fill it up now. 01812 std::vector<int>& current_block_connectivity = this->block_id_to_elem_connectivity[(*it).first]; 01813 01814 // Just in case it's not already empty... 01815 current_block_connectivity.clear(); 01816 current_block_connectivity.resize(elem_ids_this_subdomain.size() * this->num_nodes_per_elem); 01817 01818 for (unsigned int i=0; i<elem_ids_this_subdomain.size(); i++) 01819 { 01820 unsigned int elem_id = elem_ids_this_subdomain[i]; 01821 01822 // Set the number map for elements 01823 this->exodus_elem_num_to_libmesh.push_back(elem_id); 01824 this->libmesh_elem_num_to_exodus[elem_id] = this->exodus_elem_num_to_libmesh.size(); 01825 01826 const Elem * elem = pmesh.elem(elem_id); 01827 01828 // Exodus/Nemesis want every block to have the same element type 01829 // libmesh_assert_equal_to (elem->type(), conv.get_canonical_type()); 01830 01831 // But we can get away with writing e.g. HEX8 and INFHEX8 in 01832 // the same block... 01833 libmesh_assert_equal_to (elem->n_nodes(), Elem::build(conv.get_canonical_type(), NULL)->n_nodes()); 01834 01835 for (unsigned int j=0; j < static_cast<unsigned int>(this->num_nodes_per_elem); j++) 01836 { 01837 const unsigned int connect_index = (i*this->num_nodes_per_elem)+j; 01838 const unsigned int elem_node_index = conv.get_node_map(j); 01839 current_block_connectivity[connect_index] = this->libmesh_node_num_to_exodus[elem->node(elem_node_index)]; 01840 } 01841 } // End loop over elems in this subdomain 01842 } // end loop over subdomain_map 01843 } 01844 01845 01846 01847 01848 01849 void Nemesis_IO_Helper::compute_border_node_ids(const MeshBase& pmesh) 01850 { 01851 // The set which will eventually contain the IDs of "border nodes". These are nodes 01852 // that lie on the boundary between one or more processors. 01853 //std::set<unsigned> border_node_ids; 01854 01855 // map from processor ID to set of nodes which elements from this processor "touch", 01856 // that is, 01857 // proc_nodes_touched[p] = (set all node IDs found in elements owned by processor p) 01858 std::map<unsigned, std::set<unsigned> > proc_nodes_touched; 01859 01860 01861 // We are going to create a lot of intermediate data structures here, so make sure 01862 // as many as possible all cleaned up by creating scope! 01863 { 01864 // Loop over active (not just active local) elements, make sets of node IDs for each 01865 // processor which has an element that "touches" a node. 01866 { 01867 MeshBase::const_element_iterator elem_it = pmesh.active_elements_begin(); 01868 MeshBase::const_element_iterator elem_end = pmesh.active_elements_end(); 01869 01870 for (; elem_it != elem_end; ++elem_it) 01871 { 01872 const Elem* elem = *elem_it; 01873 01874 // Get reference to the set for this processor. If it does not exist 01875 // it will be created. 01876 std::set<unsigned>& set_p = proc_nodes_touched[ elem->processor_id() ]; 01877 01878 // Insert all nodes touched by this element into the set 01879 for (unsigned int node=0; node<elem->n_nodes(); ++node) 01880 set_p.insert(elem->node(node)); 01881 } 01882 } 01883 01884 // The number of node communication maps is the number of other processors 01885 // with which we share nodes. (I think.) This is just the size of the map we just 01886 // created, minus 1. 01887 this->num_node_cmaps = proc_nodes_touched.size() - 1; 01888 01889 // If we've got no elements on this processor and haven't touched 01890 // any nodes, however, then that's 0 other processors with which 01891 // we share nodes, not -1. 01892 if (this->num_node_cmaps == -1) 01893 { 01894 libmesh_assert (pmesh.active_elements_begin() == pmesh.active_elements_end()); 01895 this->num_node_cmaps = 0; 01896 } 01897 01898 // We can't be connecting to more processors than exist outside 01899 // ourselves 01900 libmesh_assert_less (static_cast<unsigned>(this->num_node_cmaps), libMesh::n_processors()); 01901 01902 if (_verbose) 01903 { 01904 libMesh::out << "[" << libMesh::processor_id() 01905 << "] proc_nodes_touched contains " 01906 << proc_nodes_touched.size() 01907 << " sets of nodes." 01908 << std::endl; 01909 01910 for (proc_nodes_touched_iterator it = proc_nodes_touched.begin(); 01911 it != proc_nodes_touched.end(); 01912 ++it) 01913 { 01914 libMesh::out << "[" << libMesh::processor_id() 01915 << "] proc_nodes_touched[" << (*it).first << "] has " 01916 << (*it).second.size() 01917 << " entries." 01918 << std::endl; 01919 } 01920 } 01921 01922 01923 // Loop over all the sets we just created and compute intersections with the 01924 // this processor's set. Obviously, don't intersect with ourself. 01925 for (proc_nodes_touched_iterator it = proc_nodes_touched.begin(); 01926 it != proc_nodes_touched.end(); 01927 ++it) 01928 { 01929 // Don't compute intersections with ourself 01930 if ((*it).first == libMesh::processor_id()) 01931 continue; 01932 01933 // Otherwise, compute intersection with other processor and ourself 01934 std::set<unsigned>& my_set = proc_nodes_touched[libMesh::processor_id()]; 01935 std::set<unsigned>& other_set = (*it).second; 01936 std::set<unsigned>& result_set = this->proc_nodes_touched_intersections[ (*it).first ]; // created if does not exist 01937 01938 std::set_intersection(my_set.begin(), my_set.end(), 01939 other_set.begin(), other_set.end(), 01940 std::inserter(result_set, result_set.end())); 01941 } 01942 01943 if (_verbose) 01944 { 01945 for (proc_nodes_touched_iterator it = this->proc_nodes_touched_intersections.begin(); 01946 it != this->proc_nodes_touched_intersections.end(); 01947 ++it) 01948 { 01949 libMesh::out << "[" << libMesh::processor_id() 01950 << "] this->proc_nodes_touched_intersections[" << (*it).first << "] has " 01951 << (*it).second.size() 01952 << " entries." 01953 << std::endl; 01954 } 01955 } 01956 01957 // Compute the set_union of all the preceding intersections. This will be the set of 01958 // border node IDs for this processor. 01959 for (proc_nodes_touched_iterator it = this->proc_nodes_touched_intersections.begin(); 01960 it != this->proc_nodes_touched_intersections.end(); 01961 ++it) 01962 { 01963 std::set<unsigned>& other_set = (*it).second; 01964 std::set<unsigned> intermediate_result; // Don't think we can insert into one of the sets we're unioning... 01965 01966 std::set_union(this->border_node_ids.begin(), this->border_node_ids.end(), 01967 other_set.begin(), other_set.end(), 01968 std::inserter(intermediate_result, intermediate_result.end())); 01969 01970 // Swap our intermediate result into the final set 01971 this->border_node_ids.swap(intermediate_result); 01972 } 01973 01974 if (_verbose) 01975 { 01976 libMesh::out << "[" << libMesh::processor_id() 01977 << "] border_node_ids.size()=" << this->border_node_ids.size() 01978 << std::endl; 01979 } 01980 } // end scope for border node ID creation 01981 01982 // Store the number of border node IDs to be written to Nemesis file 01983 this->num_border_nodes = this->border_node_ids.size(); 01984 } 01985 01986 01987 01988 01989 01990 void Nemesis_IO_Helper::write_nodesets(const MeshBase & mesh) 01991 { 01992 // Write the nodesets. In Nemesis, the idea is to "create space" for the global 01993 // set of boundary nodesets, but to only write node IDs which are local to the current 01994 // processor. This is what is done in Nemesis files created by the "loadbal" script. 01995 01996 // Store a map of vectors for boundary node IDs on this processor. 01997 // Use a vector of int here so it can be passed directly to Exodus. 01998 std::map<boundary_id_type, std::vector<int> > local_node_boundary_id_lists; 01999 typedef std::map<boundary_id_type, std::vector<int> >::iterator local_node_boundary_id_lists_iterator; 02000 02001 // FIXME: We should build this list only one time!! We already built it above, but we 02002 // did not have the libmesh to exodus node mapping at that time... for now we'll just 02003 // build it here again, hopefully it's small relative to the size of the entire mesh. 02004 std::vector<dof_id_type> boundary_node_list; 02005 std::vector<boundary_id_type> boundary_node_boundary_id_list; 02006 mesh.boundary_info->build_node_list(boundary_node_list, boundary_node_boundary_id_list); 02007 02008 if (_verbose) 02009 { 02010 libMesh::out << "[" << libMesh::processor_id() << "] boundary_node_list.size()=" 02011 << boundary_node_list.size() << std::endl; 02012 libMesh::out << "[" << libMesh::processor_id() << "] (boundary_node_id, boundary_id) = "; 02013 for (unsigned i=0; i<boundary_node_list.size(); ++i) 02014 { 02015 libMesh::out << "(" << boundary_node_list[i] << ", " << boundary_node_boundary_id_list[i] << ") "; 02016 } 02017 libMesh::out << std::endl; 02018 } 02019 02020 // For each node in the node list, add it to the vector of node IDs for that 02021 // set for the local processor. This will be used later when writing Exodus 02022 // nodesets. 02023 for (unsigned i=0; i<boundary_node_list.size(); ++i) 02024 { 02025 // Don't try to grab a reference to the vector unless the current node is attached 02026 // to a local element. Otherwise, another processor will be responsible for writing it in its nodeset. 02027 std::map<int, int>::iterator it = this->libmesh_node_num_to_exodus.find( boundary_node_list[i] ); 02028 02029 if ( it != this->libmesh_node_num_to_exodus.end() ) 02030 { 02031 // Get reference to the vector where this node ID will be inserted. If it 02032 // doesn't yet exist, this will create it. 02033 std::vector<int>& current_id_set = local_node_boundary_id_lists[ boundary_node_boundary_id_list[i] ]; 02034 02035 // Push back Exodus-mapped node ID for this set 02036 // TODO: reserve space in these vectors somehow. 02037 current_id_set.push_back( (*it).second ); 02038 } 02039 } 02040 02041 // See what we got 02042 if (_verbose) 02043 { 02044 for(std::map<boundary_id_type, std::vector<int> >::iterator it = local_node_boundary_id_lists.begin(); 02045 it != local_node_boundary_id_lists.end(); 02046 ++it) 02047 { 02048 libMesh::out << "[" << libMesh::processor_id() << "] ID: " << (*it).first << ", "; 02049 02050 std::vector<int>& current_id_set = (*it).second; 02051 02052 // Libmesh node ID (Exodus Node ID) 02053 for (unsigned j=0; j<current_id_set.size(); ++j) 02054 libMesh::out << current_id_set[j] 02055 << ", "; 02056 02057 libMesh::out << std::endl; 02058 } 02059 } 02060 02061 // Loop over *global* nodeset IDs, call the Exodus API. Note that some nodesets may be empty 02062 // for a given processor. 02063 for (unsigned i=0; i<this->global_nodeset_ids.size(); ++i) 02064 { 02065 if (_verbose) 02066 { 02067 libMesh::out << "[" << libMesh::processor_id() 02068 << "] Writing out Exodus nodeset info for ID: " << global_nodeset_ids[i] << std::endl; 02069 } 02070 02071 // Convert current global_nodeset_id into an exodus ID, which can't be zero... 02072 int exodus_id = global_nodeset_ids[i]; 02073 02074 /* 02075 // Exodus can't handle zero nodeset IDs (?) Use max short here since 02076 // when libmesh reads it back in, it will want to store it as a short... 02077 if (exodus_id==0) 02078 exodus_id = std::numeric_limits<short>::max(); 02079 */ 02080 02081 // Try to find this boundary ID in the local list we created 02082 local_node_boundary_id_lists_iterator it = 02083 local_node_boundary_id_lists.find(this->global_nodeset_ids[i]); 02084 02085 // No nodes found for this boundary ID on this processor 02086 if (it == local_node_boundary_id_lists.end()) 02087 { 02088 if (_verbose) 02089 libMesh::out << "[" << libMesh::processor_id() 02090 << "] No nodeset data for ID: " << global_nodeset_ids[i] 02091 << " on this processor." << std::endl; 02092 02093 // Call the Exodus interface to write the parameters of this node set 02094 this->ex_err = exII::ex_put_node_set_param(this->ex_id, 02095 exodus_id, 02096 0, /* No nodes for this ID */ 02097 0 /* No distribution factors */); 02098 this->check_err(this->ex_err, "Error writing nodeset parameters in Nemesis"); 02099 02100 } 02101 else // Boundary ID *was* found in list 02102 { 02103 // Get reference to the vector of node IDs 02104 std::vector<int>& current_nodeset_ids = (*it).second; 02105 02106 // Call the Exodus interface to write the parameters of this node set 02107 this->ex_err = exII::ex_put_node_set_param(this->ex_id, 02108 exodus_id, 02109 current_nodeset_ids.size(), 02110 0 /* No distribution factors */); 02111 02112 this->check_err(this->ex_err, "Error writing nodeset parameters in Nemesis"); 02113 02114 // Call Exodus interface to write the actual node IDs for this boundary ID 02115 this->ex_err = exII::ex_put_node_set(this->ex_id, 02116 exodus_id, 02117 ¤t_nodeset_ids[0]); 02118 02119 this->check_err(this->ex_err, "Error writing nodesets in Nemesis"); 02120 02121 } 02122 } // end loop over global nodeset IDs 02123 } 02124 02125 02126 02127 02128 void Nemesis_IO_Helper::write_sidesets(const MeshBase & mesh) 02129 { 02130 // Write the sidesets. In Nemesis, the idea is to "create space" for the global 02131 // set of boundary sidesets, but to only write sideset IDs which are local to the current 02132 // processor. This is what is done in Nemesis files created by the "loadbal" script. 02133 // See also: ExodusII_IO_Helper::write_sidesets()... 02134 02135 02136 // Store a map of vectors for boundary side IDs on this processor. 02137 // Use a vector of int here so it can be passed directly to Exodus. 02138 std::map<boundary_id_type, std::vector<int> > local_elem_boundary_id_lists; 02139 std::map<boundary_id_type, std::vector<int> > local_elem_boundary_id_side_lists; 02140 typedef std::map<boundary_id_type, std::vector<int> >::iterator local_elem_boundary_id_lists_iterator; 02141 02142 ExodusII_IO_Helper::ElementMaps em; 02143 02144 // FIXME: We already built this list once, we should reuse that information! 02145 std::vector< dof_id_type > bndry_elem_list; 02146 std::vector< unsigned short int > bndry_side_list; 02147 std::vector< boundary_id_type > bndry_id_list; 02148 02149 mesh.boundary_info->build_side_list(bndry_elem_list, bndry_side_list, bndry_id_list); 02150 02151 // Integer looping, skipping non-local elements 02152 for (unsigned i=0; i<bndry_elem_list.size(); ++i) 02153 { 02154 // Get pointer to current Elem 02155 const Elem* elem = mesh.elem(bndry_elem_list[i]); 02156 02157 // If element is local, process it 02158 if (elem->processor_id() == libMesh::processor_id()) 02159 { 02160 std::vector<const Elem*> family; 02161 #ifdef LIBMESH_ENABLE_AMR 02162 // We need to build up active elements if AMR is enabled and add 02163 // them to the exodus sidesets instead of the potentially inactive "parent" elements 02164 // Technically we don't need to "reset" the tree since the vector was just created. 02165 elem->active_family_tree_by_side(family, bndry_side_list[i], /*reset tree=*/false); 02166 #else 02167 // If AMR is not even enabled, just push back the element itself 02168 family.push_back( elem ); 02169 #endif 02170 02171 // Loop over all the elements in the family tree, store their converted IDs 02172 // and side IDs to the map's vectors. TODO: Somehow reserve enough space for these 02173 // push_back's... 02174 for(unsigned int j=0; j<family.size(); ++j) 02175 { 02176 const ExodusII_IO_Helper::Conversion conv = em.assign_conversion(mesh.elem(family[j]->id())->type()); 02177 02178 // Use the libmesh to exodus datastructure map to get the proper sideset IDs 02179 // The datastructure contains the "collapsed" contiguous ids. 02180 // 02181 // We know the parent element is local, but let's be absolutely sure that all the children have been 02182 // actually mapped to Exodus IDs before we blindly try to add them... 02183 std::map<int,int>::iterator it = this->libmesh_elem_num_to_exodus.find( family[j]->id() ); 02184 if (it != this->libmesh_elem_num_to_exodus.end()) 02185 { 02186 local_elem_boundary_id_lists[ bndry_id_list[i] ].push_back( (*it).second ); 02187 local_elem_boundary_id_side_lists[ bndry_id_list[i] ].push_back(conv.get_inverse_side_map( bndry_side_list[i] )); 02188 } 02189 else 02190 { 02191 libMesh::err << "Error, no Exodus mapping for Elem " 02192 << family[j]->id() 02193 << " on processor " 02194 << libMesh::processor_id() 02195 << std::endl; 02196 libmesh_error(); 02197 } 02198 } 02199 } 02200 } 02201 02202 02203 // Loop over *global* sideset IDs, call the Exodus API. Note that some sidesets may be empty 02204 // for a given processor. 02205 for (unsigned i=0; i<this->global_sideset_ids.size(); ++i) 02206 { 02207 if (_verbose) 02208 { 02209 libMesh::out << "[" << libMesh::processor_id() 02210 << "] Writing out Exodus sideset info for ID: " << global_sideset_ids[i] << std::endl; 02211 } 02212 02213 // Convert current global_sideset_id into an exodus ID, which can't be zero... 02214 int exodus_id = global_sideset_ids[i]; 02215 02216 /* 02217 // Exodus can't handle zero sideset IDs (?) Use max short here since 02218 // when libmesh reads it back in, it will want to store it as a short... 02219 if (exodus_id==0) 02220 exodus_id = std::numeric_limits<short>::max(); 02221 */ 02222 02223 // Try to find this boundary ID in the local list we created 02224 local_elem_boundary_id_lists_iterator it = 02225 local_elem_boundary_id_lists.find(this->global_sideset_ids[i]); 02226 02227 // No sides found for this boundary ID on this processor 02228 if (it == local_elem_boundary_id_lists.end()) 02229 { 02230 if (_verbose) 02231 libMesh::out << "[" << libMesh::processor_id() 02232 << "] No sideset data for ID: " << global_sideset_ids[i] 02233 << " on this processor." << std::endl; 02234 02235 // Call the Exodus interface to write the parameters of this side set 02236 this->ex_err = exII::ex_put_side_set_param(this->ex_id, 02237 exodus_id, 02238 0, /* No sides for this ID */ 02239 0 /* No distribution factors */); 02240 this->check_err(this->ex_err, "Error writing sideset parameters in Nemesis"); 02241 02242 } 02243 else // Boundary ID *was* found in list 02244 { 02245 // Get iterator to sides vector as well 02246 local_elem_boundary_id_lists_iterator it_sides = 02247 local_elem_boundary_id_side_lists.find(this->global_sideset_ids[i]); 02248 02249 libmesh_assert (it_sides != local_elem_boundary_id_side_lists.end()); 02250 02251 // Get reference to the vector of elem IDs 02252 std::vector<int>& current_sideset_elem_ids = (*it).second; 02253 02254 // Get reference to the vector of side IDs 02255 std::vector<int>& current_sideset_side_ids = (*it_sides).second; 02256 02257 // Call the Exodus interface to write the parameters of this side set 02258 this->ex_err = exII::ex_put_side_set_param(this->ex_id, 02259 exodus_id, 02260 current_sideset_elem_ids.size(), 02261 0 /* No distribution factors */); 02262 02263 this->check_err(this->ex_err, "Error writing sideset parameters in Nemesis"); 02264 02265 // Call Exodus interface to write the actual side IDs for this boundary ID 02266 this->ex_err = exII::ex_put_side_set(this->ex_id, 02267 exodus_id, 02268 ¤t_sideset_elem_ids[0], 02269 ¤t_sideset_side_ids[0]); 02270 02271 this->check_err(this->ex_err, "Error writing sidesets in Nemesis"); 02272 } 02273 } // end for loop over global sideset IDs 02274 } 02275 02276 02277 02278 void Nemesis_IO_Helper::write_nodal_coordinates(const MeshBase & mesh) 02279 { 02280 // Make sure that the reference passed in is really a ParallelMesh 02281 // const ParallelMesh& pmesh = libmesh_cast_ref<const ParallelMesh&>(mesh); 02282 02283 unsigned local_num_nodes = this->exodus_node_num_to_libmesh.size(); 02284 02285 x.resize(local_num_nodes); 02286 y.resize(local_num_nodes); 02287 z.resize(local_num_nodes); 02288 02289 // Just loop over our list outputing the nodes the way we built the map 02290 for (unsigned int i=0; i<local_num_nodes; ++i) 02291 { 02292 const Node & node = *mesh.node_ptr(this->exodus_node_num_to_libmesh[i]); 02293 x[i]=node(0); 02294 y[i]=node(1); 02295 z[i]=node(2); 02296 } 02297 02298 if (local_num_nodes) 02299 { 02300 // Call Exodus API to write nodal coordinates... 02301 ex_err = exII::ex_put_coord(ex_id, &x[0], &y[0], &z[0]); 02302 check_err(ex_err, "Error writing node coordinates"); 02303 02304 // And write the nodal map we created for them 02305 ex_err = exII::ex_put_node_num_map(ex_id, &(this->exodus_node_num_to_libmesh[0])); 02306 check_err(ex_err, "Error writing node num map"); 02307 } 02308 else // Does the Exodus API want us to write empty nodal coordinates? 02309 { 02310 ex_err = exII::ex_put_coord(ex_id, NULL, NULL, NULL); 02311 check_err(ex_err, "Error writing empty node coordinates"); 02312 02313 ex_err = exII::ex_put_node_num_map(ex_id, NULL); 02314 check_err(ex_err, "Error writing empty node num map"); 02315 } 02316 } 02317 02318 02319 02320 02321 02322 void Nemesis_IO_Helper::write_elements(const MeshBase & mesh) 02323 { 02324 02325 // Loop over all blocks, even if we don't have elements in each block. 02326 // If we don't have elements we need to write out a 0 for that block... 02327 for (unsigned int i=0; i<static_cast<unsigned>(this->num_elem_blks_global); ++i) 02328 { 02329 // Search for the current global block ID in the map 02330 std::map<int, std::vector<int> >::iterator it = 02331 this->block_id_to_elem_connectivity.find( this->global_elem_blk_ids[i] ); 02332 02333 // If not found, write a zero to file.... 02334 if (it == this->block_id_to_elem_connectivity.end()) 02335 { 02336 this->ex_err = exII::ex_put_elem_block(this->ex_id, 02337 this->global_elem_blk_ids[i], 02338 "Empty", 02339 0, /* n. elements in this block */ 02340 0, /* n. nodes per element */ 02341 0); /* number of attributes per element */ 02342 02343 this->check_err(this->ex_err, "Error writing element block from Nemesis."); 02344 } 02345 02346 // Otherwise, write the actual block information and connectivity to file 02347 else 02348 { 02349 int block = (*it).first; 02350 std::vector<int> & this_block_connectivity = (*it).second; 02351 std::vector<unsigned int> & elements_in_this_block = subdomain_map[block]; 02352 02353 ExodusII_IO_Helper::ElementMaps em; 02354 02355 //Use the first element in this block to get representative information. 02356 //Note that Exodus assumes all elements in a block are of the same type! 02357 //We are using that same assumption here! 02358 const ExodusII_IO_Helper::Conversion conv = 02359 em.assign_conversion(mesh.elem(elements_in_this_block[0])->type()); 02360 02361 this->num_nodes_per_elem = mesh.elem(elements_in_this_block[0])->n_nodes(); 02362 02363 ex_err = exII::ex_put_elem_block(ex_id, 02364 block, 02365 conv.exodus_elem_type().c_str(), 02366 elements_in_this_block.size(), 02367 num_nodes_per_elem, 02368 0); 02369 check_err(ex_err, "Error writing element block from Nemesis."); 02370 02371 ex_err = exII::ex_put_elem_conn(ex_id, 02372 block, 02373 &this_block_connectivity[0]); 02374 check_err(ex_err, "Error writing element connectivities from Nemesis."); 02375 } 02376 } // end loop over global block IDs 02377 02378 // Only call this once, not in the loop above! 02379 ex_err = exII::ex_put_elem_num_map(ex_id, 02380 exodus_elem_num_to_libmesh.empty() ? NULL : &exodus_elem_num_to_libmesh[0]); 02381 check_err(ex_err, "Error writing element map"); 02382 } 02383 02384 02385 02386 02387 02388 void Nemesis_IO_Helper::write_nodal_solution(const std::vector<Number> & values, const std::vector<std::string> names, int timestep) 02389 { 02390 int num_vars = names.size(); 02391 //int num_values = values.size(); // Not used? 02392 02393 for (int c=0; c<num_vars; c++) 02394 { 02395 std::vector<Number> cur_soln(num_nodes); 02396 02397 //Copy out this variable's solution 02398 for(int i=0; i<num_nodes; i++) 02399 cur_soln[i] = values[this->exodus_node_num_to_libmesh[i]*num_vars + c]; 02400 02401 write_nodal_values(c+1,cur_soln,timestep); 02402 } 02403 } 02404 02405 02406 02407 02408 std::string Nemesis_IO_Helper::construct_nemesis_filename(const std::string& base_filename) 02409 { 02410 // Build a filename for this processor. This code is cut-n-pasted from the read function 02411 // and should probably be put into a separate function... 02412 std::ostringstream file_oss; 02413 02414 // We have to be a little careful here: Nemesis left pads its file 02415 // numbers based on the number of processors, so for example on 10 02416 // processors, we'd have: 02417 // mesh.e.10.00 02418 // mesh.e.10.01 02419 // mesh.e.10.02 02420 // ... 02421 // mesh.e.10.09 02422 02423 // And on 100 you'd have: 02424 // mesh.e.100.000 02425 // mesh.e.100.001 02426 // ... 02427 // mesh.e.128.099 02428 02429 // Find the length of the highest processor ID 02430 file_oss << (libMesh::n_processors()); 02431 unsigned field_width = file_oss.str().size(); 02432 02433 if (_verbose) 02434 libMesh::out << "field_width=" << field_width << std::endl; 02435 02436 file_oss.str(""); // reset the string stream 02437 file_oss << base_filename 02438 << '.' << libMesh::n_processors() 02439 << '.' << std::setfill('0') << std::setw(field_width) << libMesh::processor_id(); 02440 02441 // Return the resulting string 02442 return file_oss.str(); 02443 } 02444 02445 } // namespace libMesh 02446 02447 #endif // #if defined(LIBMESH_HAVE_NEMESIS_API) && defined(LIBMESH_HAVE_EXODUS_API)
Site Created By: libMesh Developers
Last modified: February 05 2013 19:54:48 UTC
Hosted By: