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