You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
725 lines
26 KiB
725 lines
26 KiB
2 years ago
|
/*
|
||
|
* Copyright(C) 1999-2021, 2023 National Technology & Engineering Solutions
|
||
|
* of Sandia, LLC (NTESS). Under the terms of Contract DE-NA0003525 with
|
||
|
* NTESS, the U.S. Government retains certain rights in this software.
|
||
|
*
|
||
|
* See packages/seacas/LICENSE for details
|
||
|
*/
|
||
|
|
||
|
#include "elb.h" // for Machine_Description, etc
|
||
|
#include "elb_allo.h" // for array_alloc
|
||
|
#include "elb_elem.h" // for NNODES, get_elem_info
|
||
|
#include "elb_err.h" // for Gen_Error, error_lev
|
||
|
#include "elb_output.h"
|
||
|
#include "elb_util.h" // for gds_qsort, qsort2, in_list, etc
|
||
|
#include "fmt/chrono.h"
|
||
|
#include "fmt/ostream.h"
|
||
|
#include "scopeguard.h"
|
||
|
#include <copy_string_cpp.h>
|
||
|
#include <cstddef> // for size_t, nullptr
|
||
|
#include <cstdlib> // for free, malloc, realloc
|
||
|
#include <cstring> // for strcat, strlen, etc
|
||
|
#include <ctime> // for asctime, localtime, time, etc
|
||
|
#include <exodusII.h> // for ex_close, ex_opts, etc
|
||
|
#include <iostream>
|
||
|
#include <vector> // for vector
|
||
|
|
||
|
namespace {
|
||
|
std::string remove_extension(const std::string &filename)
|
||
|
{
|
||
|
// Strip off the extension
|
||
|
size_t ind = filename.find_last_of('.', filename.size());
|
||
|
if (ind != std::string::npos) {
|
||
|
return filename.substr(0, ind);
|
||
|
}
|
||
|
return filename;
|
||
|
}
|
||
|
} // namespace
|
||
|
|
||
|
/*****************************************************************************/
|
||
|
/*****************************************************************************/
|
||
|
/*****************************************************************************/
|
||
|
/* This function outputs a load balance file using the ExodusII and NemesisI
|
||
|
* API.
|
||
|
*****************************************************************************/
|
||
|
template int write_nemesis(std::string &nemI_out_file, Machine_Description *machine,
|
||
|
Problem_Description *problem, Mesh_Description<int> *mesh,
|
||
|
LB_Description<int> *lb, Sphere_Info *sphere);
|
||
|
template int write_nemesis(std::string &nemI_out_file, Machine_Description *machine,
|
||
|
Problem_Description *problem, Mesh_Description<int64_t> *mesh,
|
||
|
LB_Description<int64_t> *lb, Sphere_Info *sphere);
|
||
|
|
||
|
template <typename INT>
|
||
|
int write_nemesis(std::string &nemI_out_file, Machine_Description *machine,
|
||
|
Problem_Description *problem, Mesh_Description<INT> *mesh,
|
||
|
LB_Description<INT> *lb, Sphere_Info *sphere)
|
||
|
{
|
||
|
int exoid;
|
||
|
std::string method1{}, method2{};
|
||
|
std::string title;
|
||
|
|
||
|
int cpu_ws = sizeof(float);
|
||
|
int io_ws = sizeof(float);
|
||
|
|
||
|
fmt::print("Outputting load balance to file {}\n", nemI_out_file);
|
||
|
|
||
|
/* Create the load balance file */
|
||
|
/* Attempt to create a netcdf4-format file; if it fails, then assume
|
||
|
that the netcdf library does not support that mode and fall back
|
||
|
to classic netcdf3 format. If that fails, issue an error and
|
||
|
return failure.
|
||
|
*/
|
||
|
int mode3 = EX_CLOBBER;
|
||
|
int mode4 = mode3 | EX_NETCDF4 | EX_NOCLASSIC | problem->int64db | problem->int64api;
|
||
|
|
||
|
ex_opts(EX_DEFAULT); // Eliminate misleading error if the first ex_create fails, but the second
|
||
|
// succeeds.
|
||
|
if ((exoid = ex_create(nemI_out_file.c_str(), mode4, &cpu_ws, &io_ws)) < 0) {
|
||
|
/* If int64api or int64db non-zero, then netcdf-4 format is required, so
|
||
|
fail now...
|
||
|
*/
|
||
|
if (problem->int64db | problem->int64api) {
|
||
|
Gen_Error(0, "fatal: failed to create Nemesis netcdf-4 file");
|
||
|
return 0;
|
||
|
}
|
||
|
if ((exoid = ex_create(nemI_out_file.c_str(), mode3, &cpu_ws, &io_ws)) < 0) {
|
||
|
Gen_Error(0, "fatal: failed to create Nemesis file");
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
ON_BLOCK_EXIT(ex_close, exoid);
|
||
|
|
||
|
/* Set the error reporting value */
|
||
|
if (error_lev > 1) {
|
||
|
ex_opts(EX_VERBOSE | EX_DEBUG);
|
||
|
}
|
||
|
else {
|
||
|
ex_opts(EX_VERBOSE);
|
||
|
}
|
||
|
|
||
|
/* Enable compression (if netcdf-4) */
|
||
|
ex_set_option(exoid, EX_OPT_COMPRESSION_LEVEL, 1);
|
||
|
ex_set_option(exoid, EX_OPT_COMPRESSION_SHUFFLE, 1);
|
||
|
|
||
|
/* Create the title */
|
||
|
if (problem->type == NODAL) {
|
||
|
method1 = "nodal";
|
||
|
}
|
||
|
else {
|
||
|
method1 = "elemental";
|
||
|
}
|
||
|
|
||
|
title = fmt::format("nem_slice {} load balance file", method1);
|
||
|
|
||
|
method1 = "method1: ";
|
||
|
method2 = "method2: ";
|
||
|
|
||
|
switch (lb->type) {
|
||
|
case MULTIKL:
|
||
|
method1 += "Multilevel-KL decomposition";
|
||
|
method2 += "With Kernighan-Lin refinement";
|
||
|
break;
|
||
|
case SPECTRAL: method1 += "Spectral decomposition"; break;
|
||
|
case INERTIAL: method1 += "Inertial decomposition"; break;
|
||
|
case ZPINCH: method1 += "ZPINCH decomposition"; break;
|
||
|
case BRICK: method1 += "BRICK decomposition"; break;
|
||
|
case ZOLTAN_RCB: method1 += "RCB decomposition"; break;
|
||
|
case ZOLTAN_RIB: method1 += "RIB decomposition"; break;
|
||
|
case ZOLTAN_HSFC: method1 += "HSFC decomposition"; break;
|
||
|
case LINEAR: method1 += "Linear decomposition"; break;
|
||
|
case RANDOM: method1 += "Random decomposition"; break;
|
||
|
case SCATTERED: method1 += "Scattered decomposition"; break;
|
||
|
}
|
||
|
|
||
|
if (lb->refine == KL_REFINE && lb->type != MULTIKL) {
|
||
|
method2 += "with Kernighan-Lin refinement";
|
||
|
}
|
||
|
else if (lb->type != MULTIKL) {
|
||
|
method2 += "no refinement";
|
||
|
}
|
||
|
|
||
|
switch (lb->num_sects) {
|
||
|
case 1: method1 += " via bisection"; break;
|
||
|
case 2: method1 += " via quadrasection"; break;
|
||
|
case 3: method1 += " via octasection"; break;
|
||
|
}
|
||
|
|
||
|
/* Do some sorting */
|
||
|
for (int proc = 0; proc < machine->num_procs; proc++) {
|
||
|
|
||
|
/* Sort node maps */
|
||
|
gds_qsort(lb->int_nodes[proc].data(), lb->int_nodes[proc].size());
|
||
|
if (problem->type == NODAL) {
|
||
|
sort2(lb->ext_nodes[proc].size(), lb->ext_nodes[proc].data(), lb->ext_procs[proc].data());
|
||
|
}
|
||
|
|
||
|
/* Sort element maps */
|
||
|
gds_qsort(lb->int_elems[proc].data(), lb->int_elems[proc].size());
|
||
|
}
|
||
|
|
||
|
/* Output the info records */
|
||
|
char *info[3];
|
||
|
info[0] = const_cast<char *>(title.c_str());
|
||
|
info[1] = const_cast<char *>(method1.c_str());
|
||
|
info[2] = const_cast<char *>(method2.c_str());
|
||
|
|
||
|
if (ex_put_info(exoid, 3, info) < 0) {
|
||
|
Gen_Error(0, "warning: output of info records failed");
|
||
|
}
|
||
|
|
||
|
/* Generate a QA record for the utility */
|
||
|
time_t time_val = time(nullptr);
|
||
|
auto *lt = std::localtime(&time_val);
|
||
|
std::string time = fmt::format("{:%H:%M:%S}", *lt);
|
||
|
std::string date = fmt::format("{:%Y/%m/%d}", *lt);
|
||
|
|
||
|
char qa_date[15];
|
||
|
char qa_time[10];
|
||
|
char qa_name[MAX_STR_LENGTH];
|
||
|
char qa_vers[10];
|
||
|
|
||
|
copy_string(qa_time, time);
|
||
|
copy_string(qa_date, date);
|
||
|
copy_string(qa_name, UTIL_NAME);
|
||
|
copy_string(qa_vers, ELB_VERSION);
|
||
|
|
||
|
char **lqa_record = reinterpret_cast<char **>(array_alloc(1, 4, sizeof(char *)));
|
||
|
for (int i2 = 0; i2 < 4; i2++) {
|
||
|
lqa_record[i2] = reinterpret_cast<char *>(array_alloc(1, MAX_STR_LENGTH + 1, sizeof(char)));
|
||
|
}
|
||
|
|
||
|
copy_string(lqa_record[0], qa_name, MAX_STR_LENGTH + 1);
|
||
|
copy_string(lqa_record[1], qa_vers, MAX_STR_LENGTH + 1);
|
||
|
copy_string(lqa_record[2], qa_date, MAX_STR_LENGTH + 1);
|
||
|
copy_string(lqa_record[3], qa_time, MAX_STR_LENGTH + 1);
|
||
|
|
||
|
fmt::print("QA Record:\n");
|
||
|
for (int i2 = 0; i2 < 4; i2++) {
|
||
|
fmt::print("\t{}\n", lqa_record[i2]);
|
||
|
}
|
||
|
|
||
|
if (ex_put_qa(exoid, 1, reinterpret_cast<char *(*)[4]>(&lqa_record[0])) < 0) {
|
||
|
Gen_Error(0, "fatal: unable to output QA records");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* free up memory */
|
||
|
for (int i2 = 0; i2 < 4; i2++) {
|
||
|
free(lqa_record[i2]);
|
||
|
}
|
||
|
|
||
|
free(lqa_record);
|
||
|
|
||
|
/* Output the the initial Nemesis global information */
|
||
|
if (ex_put_init_global(exoid, mesh->num_nodes, mesh->num_elems, mesh->num_el_blks, 0, 0) < 0) {
|
||
|
Gen_Error(0, "fatal: failed to output initial Nemesis parameters");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
ex_put_eb_info_global(exoid, mesh->eb_ids.data(), mesh->eb_cnts.data());
|
||
|
|
||
|
/* Set up dummy arrays for output */
|
||
|
std::vector<INT> num_nmap_cnts(machine->num_procs);
|
||
|
std::vector<INT> num_emap_cnts(machine->num_procs);
|
||
|
|
||
|
if (problem->type == NODAL) {
|
||
|
/* need to check and make sure that there really are comm maps */
|
||
|
for (int cnt = 0; cnt < machine->num_procs; cnt++) {
|
||
|
if (!lb->bor_nodes[cnt].empty()) {
|
||
|
num_nmap_cnts[cnt] = 1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else { /* Elemental load balance */
|
||
|
if (problem->num_vertices > sphere->num) {
|
||
|
/* need to check and make sure that there really are comm maps */
|
||
|
for (int cnt = 0; cnt < machine->num_procs; cnt++) {
|
||
|
if (!lb->bor_nodes[cnt].empty()) {
|
||
|
num_nmap_cnts[cnt] = 1;
|
||
|
}
|
||
|
}
|
||
|
for (int cnt = 0; cnt < machine->num_procs; cnt++) {
|
||
|
if (!lb->bor_elems[cnt].empty()) {
|
||
|
num_emap_cnts[cnt] = 1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (ex_put_init_info(exoid, machine->num_procs, machine->num_procs, (char *)"s") < 0) {
|
||
|
Gen_Error(0, "fatal: unable to output init info");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
// Need to create 5 arrays with the sizes of lb->int_nodes[i].size()...
|
||
|
{
|
||
|
std::vector<INT> ins(machine->num_procs);
|
||
|
std::vector<INT> bns(machine->num_procs);
|
||
|
std::vector<INT> ens(machine->num_procs);
|
||
|
std::vector<INT> ies(machine->num_procs);
|
||
|
std::vector<INT> bes(machine->num_procs);
|
||
|
|
||
|
for (int iproc = 0; iproc < machine->num_procs; iproc++) {
|
||
|
ins[iproc] = lb->int_nodes[iproc].size();
|
||
|
bns[iproc] = lb->bor_nodes[iproc].size();
|
||
|
ens[iproc] = lb->ext_nodes[iproc].size();
|
||
|
ies[iproc] = lb->int_elems[iproc].size();
|
||
|
bes[iproc] = lb->bor_elems[iproc].size();
|
||
|
}
|
||
|
|
||
|
if (ex_put_loadbal_param_cc(exoid, ins.data(), bns.data(), ens.data(), ies.data(), bes.data(),
|
||
|
num_nmap_cnts.data(), num_emap_cnts.data()) < 0) {
|
||
|
Gen_Error(0, "fatal: unable to output load-balance parameters");
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (problem->type == NODAL) /* Nodal load balance output */
|
||
|
{
|
||
|
/* Set up for the concatenated communication map parameters */
|
||
|
std::vector<INT> node_proc_ptr(machine->num_procs + 1);
|
||
|
std::vector<INT> node_cmap_ids_cc(machine->num_procs);
|
||
|
std::vector<INT> node_cmap_cnts_cc(machine->num_procs);
|
||
|
|
||
|
node_proc_ptr[0] = 0;
|
||
|
for (int proc = 0; proc < machine->num_procs; proc++) {
|
||
|
node_proc_ptr[proc + 1] = node_proc_ptr[proc] + 1;
|
||
|
node_cmap_cnts_cc[proc] = lb->ext_nodes[proc].size();
|
||
|
node_cmap_ids_cc[proc] = 1;
|
||
|
}
|
||
|
|
||
|
/* Output the communication map parameters */
|
||
|
if (ex_put_cmap_params_cc(exoid, node_cmap_ids_cc.data(), node_cmap_cnts_cc.data(),
|
||
|
node_proc_ptr.data(), nullptr, nullptr, nullptr) < 0) {
|
||
|
Gen_Error(0, "fatal: unable to output communication map parameters");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* Output the node and element maps */
|
||
|
for (int proc = 0; proc < machine->num_procs; proc++) {
|
||
|
/* Output the nodal map */
|
||
|
if (ex_put_processor_node_maps(exoid, lb->int_nodes[proc].data(), lb->bor_nodes[proc].data(),
|
||
|
lb->ext_nodes[proc].data(), proc) < 0) {
|
||
|
Gen_Error(0, "fatal: failed to output node map");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* Output the elemental map */
|
||
|
if (ex_put_processor_elem_maps(exoid, lb->int_elems[proc].data(), nullptr, proc) < 0) {
|
||
|
Gen_Error(0, "fatal: failed to output element map");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Reorder the nodal communication maps so that they are ordered
|
||
|
* by processor and then by global ID.
|
||
|
*/
|
||
|
|
||
|
/* This is a 2-key sort */
|
||
|
qsort2(lb->ext_procs[proc].data(), lb->ext_nodes[proc].data(), lb->ext_nodes[proc].size());
|
||
|
|
||
|
/* Output the nodal communication map */
|
||
|
if (ex_put_node_cmap(exoid, 1, lb->ext_nodes[proc].data(), lb->ext_procs[proc].data(), proc) <
|
||
|
0) {
|
||
|
Gen_Error(0, "fatal: failed to output nodal communication map");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
} /* End "for(proc=0; proc < machine->num_procs; proc++)" */
|
||
|
}
|
||
|
else if (problem->type == ELEMENTAL) /* Elemental load balance output */
|
||
|
{
|
||
|
std::vector<INT> node_proc_ptr(machine->num_procs + 1);
|
||
|
std::vector<INT> node_cmap_ids_cc(machine->num_procs);
|
||
|
std::vector<INT> node_cmap_cnts_cc(machine->num_procs);
|
||
|
|
||
|
node_proc_ptr[0] = 0;
|
||
|
for (int proc = 0; proc < machine->num_procs; proc++) {
|
||
|
node_proc_ptr[proc + 1] = node_proc_ptr[proc] + 1;
|
||
|
|
||
|
node_cmap_cnts_cc[proc] = 0;
|
||
|
for (size_t cnt = 0; cnt < lb->bor_nodes[proc].size(); cnt++) {
|
||
|
node_cmap_cnts_cc[proc] += lb->born_procs[proc][cnt].size();
|
||
|
}
|
||
|
|
||
|
node_cmap_ids_cc[proc] = 1;
|
||
|
}
|
||
|
|
||
|
std::vector<INT> elem_proc_ptr(machine->num_procs + 1);
|
||
|
std::vector<INT> elem_cmap_ids_cc(machine->num_procs);
|
||
|
std::vector<INT> elem_cmap_cnts_cc(machine->num_procs);
|
||
|
|
||
|
elem_proc_ptr[0] = 0;
|
||
|
for (int proc = 0; proc < machine->num_procs; proc++) {
|
||
|
elem_proc_ptr[proc + 1] = elem_proc_ptr[proc] + 1;
|
||
|
elem_cmap_cnts_cc[proc] = lb->e_cmap_elems[proc].size();
|
||
|
elem_cmap_ids_cc[proc] = 1;
|
||
|
}
|
||
|
|
||
|
/* Output the communication map parameters */
|
||
|
if (ex_put_cmap_params_cc(exoid, node_cmap_ids_cc.data(), node_cmap_cnts_cc.data(),
|
||
|
node_proc_ptr.data(), elem_cmap_ids_cc.data(),
|
||
|
elem_cmap_cnts_cc.data(), elem_proc_ptr.data()) < 0) {
|
||
|
Gen_Error(0, "fatal: unable to output communication map parameters");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* Output the node and element maps */
|
||
|
for (int proc = 0; proc < machine->num_procs; proc++) {
|
||
|
/* Output the nodal map */
|
||
|
if (ex_put_processor_node_maps(exoid, lb->int_nodes[proc].data(), lb->bor_nodes[proc].data(),
|
||
|
nullptr, proc) < 0) {
|
||
|
Gen_Error(0, "fatal: failed to output node map");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* Output the elemental map */
|
||
|
if (ex_put_processor_elem_maps(exoid, lb->int_elems[proc].data(), lb->bor_elems[proc].data(),
|
||
|
proc) < 0) {
|
||
|
Gen_Error(0, "fatal: failed to output element map");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Build a nodal communication map from the list of border nodes
|
||
|
* and their associated processors and side IDs.
|
||
|
*/
|
||
|
size_t nsize = 0;
|
||
|
for (size_t cnt = 0; cnt < lb->bor_nodes[proc].size(); cnt++) {
|
||
|
nsize += lb->born_procs[proc][cnt].size();
|
||
|
}
|
||
|
|
||
|
if (nsize > 0) {
|
||
|
std::vector<INT> n_cmap_nodes(nsize);
|
||
|
std::vector<INT> n_cmap_procs(nsize);
|
||
|
|
||
|
size_t cnt3 = 0;
|
||
|
for (size_t cnt = 0; cnt < lb->bor_nodes[proc].size(); cnt++) {
|
||
|
for (size_t cnt2 = 0; cnt2 < lb->born_procs[proc][cnt].size(); cnt2++) {
|
||
|
n_cmap_nodes[cnt3] = lb->bor_nodes[proc][cnt];
|
||
|
n_cmap_procs[cnt3++] = lb->born_procs[proc][cnt][cnt2];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Reorder the nodal communication maps so that they are ordered
|
||
|
* by processor and then by global ID.
|
||
|
*/
|
||
|
/* This is a 2-key sort */
|
||
|
qsort2(n_cmap_procs.data(), n_cmap_nodes.data(), cnt3);
|
||
|
|
||
|
/* Output the nodal communication map */
|
||
|
if (ex_put_node_cmap(exoid, 1, n_cmap_nodes.data(), n_cmap_procs.data(), proc) < 0) {
|
||
|
Gen_Error(0, "fatal: unable to output nodal communication map");
|
||
|
return 0;
|
||
|
}
|
||
|
} /* End "if (nsize > 0)" */
|
||
|
|
||
|
/* Output the elemental communication map */
|
||
|
if (!lb->e_cmap_elems[proc].empty()) {
|
||
|
if (ex_put_elem_cmap(exoid, 1, lb->e_cmap_elems[proc].data(), lb->e_cmap_sides[proc].data(),
|
||
|
lb->e_cmap_procs[proc].data(), proc) < 0) {
|
||
|
Gen_Error(0, "fatal: unable to output elemental communication map");
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
} /* End "for(proc=0; proc < machine->num_procs; proc++)" */
|
||
|
}
|
||
|
return 1;
|
||
|
} /*------------------------End write_nemesis()------------------------------*/
|
||
|
|
||
|
/*****************************************************************************/
|
||
|
/*****************************************************************************/
|
||
|
/*****************************************************************************/
|
||
|
/* This function outputs an ExodusII file for the purposes of visualizing
|
||
|
* the load balance.
|
||
|
*****************************************************************************/
|
||
|
template int write_vis(std::string &nemI_out_file, std::string &exoII_inp_file,
|
||
|
Machine_Description *machine, Problem_Description *prob,
|
||
|
Mesh_Description<int> *mesh, LB_Description<int> *lb);
|
||
|
template int write_vis(std::string &nemI_out_file, std::string &exoII_inp_file,
|
||
|
Machine_Description *machine, Problem_Description *prob,
|
||
|
Mesh_Description<int64_t> *mesh, LB_Description<int64_t> *lb);
|
||
|
|
||
|
template <typename INT>
|
||
|
int write_vis(std::string &nemI_out_file, std::string &exoII_inp_file, Machine_Description *machine,
|
||
|
Problem_Description *prob, Mesh_Description<INT> *mesh, LB_Description<INT> *lb)
|
||
|
{
|
||
|
/* Generate the file name for the visualization file */
|
||
|
std::string vis_file_name = remove_extension(nemI_out_file);
|
||
|
vis_file_name += "-vis.exoII";
|
||
|
|
||
|
/* Generate the title for the file */
|
||
|
std::string title = fmt::format("{} {} load balance visualization file", UTIL_NAME, ELB_VERSION);
|
||
|
|
||
|
/*
|
||
|
* If the vis technique is to be by element block then calculate the
|
||
|
* number of element blocks.
|
||
|
*/
|
||
|
int vis_nelem_blks;
|
||
|
if (prob->type == ELEMENTAL) {
|
||
|
vis_nelem_blks = machine->num_procs;
|
||
|
}
|
||
|
else {
|
||
|
vis_nelem_blks = machine->num_procs + 1;
|
||
|
}
|
||
|
|
||
|
/* Create the ExodusII file */
|
||
|
fmt::print("Outputting load balance visualization file {}\n", vis_file_name);
|
||
|
int cpu_ws = 0;
|
||
|
int io_ws = 0;
|
||
|
int mode = EX_CLOBBER;
|
||
|
if (prob->int64db | prob->int64api) {
|
||
|
mode |= EX_NETCDF4 | EX_NOCLASSIC | prob->int64db | prob->int64api;
|
||
|
}
|
||
|
int exid_vis;
|
||
|
if ((exid_vis = ex_create(vis_file_name.c_str(), mode, &cpu_ws, &io_ws)) < 0) {
|
||
|
Gen_Error(0, "fatal: unable to create visualization output file");
|
||
|
return 0;
|
||
|
}
|
||
|
ON_BLOCK_EXIT(ex_close, exid_vis);
|
||
|
|
||
|
/*
|
||
|
* Open the original input ExodusII file, read the values for the
|
||
|
* element blocks and output them to the visualization file.
|
||
|
*/
|
||
|
int icpu_ws = 0;
|
||
|
int iio_ws = 0;
|
||
|
float vers = 0.0;
|
||
|
mode = EX_READ | prob->int64api;
|
||
|
int exid_inp;
|
||
|
if ((exid_inp = ex_open(exoII_inp_file.c_str(), mode, &icpu_ws, &iio_ws, &vers)) < 0) {
|
||
|
Gen_Error(0, "fatal: unable to open input ExodusII file");
|
||
|
return 0;
|
||
|
}
|
||
|
ON_BLOCK_EXIT(ex_close, exid_inp);
|
||
|
|
||
|
std::vector<std::string> elem_type(mesh->num_el_blks);
|
||
|
std::vector<INT> el_blk_ids(mesh->num_el_blks);
|
||
|
std::vector<INT> el_cnt_blk(mesh->num_el_blks);
|
||
|
std::vector<INT> node_pel_blk(mesh->num_el_blks);
|
||
|
std::vector<INT> nattr_el_blk(mesh->num_el_blks);
|
||
|
|
||
|
if (ex_get_ids(exid_inp, EX_ELEM_BLOCK, el_blk_ids.data()) < 0) {
|
||
|
Gen_Error(0, "fatal: unable to get element block IDs");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int acc_vis = ELB_TRUE; // Output a different element block per processor
|
||
|
if (prob->vis_out == 2) {
|
||
|
acc_vis = ELB_FALSE; // Output a nodal/element variable showing processor
|
||
|
}
|
||
|
|
||
|
size_t nsize = 0;
|
||
|
|
||
|
/*
|
||
|
* Find out if the mesh consists of mixed elements. If not then
|
||
|
* element blocks will be used to visualize the partitioning. Otherwise
|
||
|
* nodal/element results will be used.
|
||
|
*/
|
||
|
for (size_t ecnt = 0; ecnt < mesh->num_el_blks; ecnt++) {
|
||
|
char type[MAX_STR_LENGTH + 1];
|
||
|
if (ex_get_block(exid_inp, EX_ELEM_BLOCK, el_blk_ids[ecnt], type, &el_cnt_blk[ecnt],
|
||
|
&node_pel_blk[ecnt], nullptr, nullptr, &nattr_el_blk[ecnt]) < 0) {
|
||
|
Gen_Error(0, "fatal: unable to get element block parameters");
|
||
|
return 0;
|
||
|
}
|
||
|
elem_type[ecnt] = type;
|
||
|
nsize += el_cnt_blk[ecnt] * node_pel_blk[ecnt];
|
||
|
|
||
|
if (elem_type[0] == elem_type[ecnt]) {
|
||
|
if (node_pel_blk[0] != node_pel_blk[ecnt]) {
|
||
|
acc_vis = ELB_FALSE;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
acc_vis = ELB_FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (acc_vis == ELB_TRUE) {
|
||
|
/* Output the initial information */
|
||
|
if (ex_put_init(exid_vis, title.c_str(), mesh->num_dims, mesh->num_nodes, mesh->num_elems,
|
||
|
vis_nelem_blks, 0, 0) < 0) {
|
||
|
Gen_Error(0, "fatal: unable to output initial params to vis file");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* Output the nodal coordinates */
|
||
|
float *xptr = nullptr;
|
||
|
float *yptr = nullptr;
|
||
|
float *zptr = nullptr;
|
||
|
switch (mesh->num_dims) {
|
||
|
case 3: zptr = mesh->coords.data() + 2 * mesh->num_nodes; FALL_THROUGH;
|
||
|
case 2: yptr = mesh->coords.data() + mesh->num_nodes; FALL_THROUGH;
|
||
|
case 1: xptr = mesh->coords.data();
|
||
|
}
|
||
|
if (ex_put_coord(exid_vis, xptr, yptr, zptr) < 0) {
|
||
|
Gen_Error(0, "fatal: unable to output coords to vis file");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
const char *coord_names[] = {"X", "Y", "Z"};
|
||
|
if (ex_put_coord_names(exid_vis, (char **)coord_names) < 0) {
|
||
|
Gen_Error(0, "fatal: unable to output coordinate names");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
std::vector<INT> elem_block(mesh->num_elems);
|
||
|
std::vector<INT> elem_map(mesh->num_elems);
|
||
|
std::vector<INT> tmp_connect(nsize);
|
||
|
for (size_t ecnt = 0; ecnt < mesh->num_elems; ecnt++) {
|
||
|
elem_map[ecnt] = ecnt + 1;
|
||
|
if (prob->type == ELEMENTAL) {
|
||
|
elem_block[ecnt] = lb->vertex2proc[ecnt];
|
||
|
}
|
||
|
else {
|
||
|
int proc = lb->vertex2proc[mesh->connect[ecnt][0]];
|
||
|
int nnodes = get_elem_info(NNODES, mesh->elem_type[ecnt]);
|
||
|
elem_block[ecnt] = proc;
|
||
|
for (int ncnt = 1; ncnt < nnodes; ncnt++) {
|
||
|
if (lb->vertex2proc[mesh->connect[ecnt][ncnt]] != proc) {
|
||
|
elem_block[ecnt] = machine->num_procs;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int ccnt = 0;
|
||
|
std::vector<INT> vis_el_blk_ptr(vis_nelem_blks + 1);
|
||
|
for (INT bcnt = 0; bcnt < vis_nelem_blks; bcnt++) {
|
||
|
vis_el_blk_ptr[bcnt] = ccnt;
|
||
|
int pos = 0;
|
||
|
int old_pos = 0;
|
||
|
INT *el_ptr = elem_block.data();
|
||
|
size_t ecnt = mesh->num_elems;
|
||
|
while (pos != -1) {
|
||
|
pos = in_list(bcnt, ecnt, el_ptr);
|
||
|
if (pos != -1) {
|
||
|
old_pos += pos + 1;
|
||
|
ecnt = mesh->num_elems - old_pos;
|
||
|
el_ptr = elem_block.data() + old_pos;
|
||
|
int nnodes = get_elem_info(NNODES, mesh->elem_type[old_pos - 1]);
|
||
|
for (int ncnt = 0; ncnt < nnodes; ncnt++) {
|
||
|
tmp_connect[ccnt++] = mesh->connect[old_pos - 1][ncnt] + 1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
vis_el_blk_ptr[vis_nelem_blks] = ccnt;
|
||
|
|
||
|
/* Output the element map */
|
||
|
if (ex_put_map(exid_vis, elem_map.data()) < 0) {
|
||
|
Gen_Error(0, "fatal: unable to output element number map");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* Output the visualization element blocks */
|
||
|
for (int bcnt = 0; bcnt < vis_nelem_blks; bcnt++) {
|
||
|
/*
|
||
|
* Note this assumes all the blocks contain the same type
|
||
|
* element.
|
||
|
*/
|
||
|
int ecnt = (vis_el_blk_ptr[bcnt + 1] - vis_el_blk_ptr[bcnt]) / node_pel_blk[0];
|
||
|
if (ex_put_block(exid_vis, EX_ELEM_BLOCK, bcnt + 1, elem_type[0].c_str(), ecnt,
|
||
|
node_pel_blk[0], 0, 0, 0) < 0) {
|
||
|
Gen_Error(0, "fatal: unable to output element block params");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* Output the connectivity */
|
||
|
if (ex_put_conn(exid_vis, EX_ELEM_BLOCK, bcnt + 1, &tmp_connect[vis_el_blk_ptr[bcnt]],
|
||
|
nullptr, nullptr) < 0) {
|
||
|
Gen_Error(0, "fatal: unable to output element connectivity");
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
else { /* For nodal/element results visualization of the partitioning. */
|
||
|
// Copy the mesh portion to the vis file.
|
||
|
ex_copy(exid_inp, exid_vis);
|
||
|
|
||
|
/* Set up the file for nodal/element results */
|
||
|
float time_val = 0.0;
|
||
|
if (ex_put_time(exid_vis, 1, &time_val) < 0) {
|
||
|
Gen_Error(0, "fatal: unable to output time to vis file");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
const char *var_names[] = {"proc"};
|
||
|
if (prob->type == NODAL) {
|
||
|
/* Allocate memory for the nodal values */
|
||
|
std::vector<float> proc_vals(mesh->num_nodes);
|
||
|
|
||
|
if (ex_put_variable_param(exid_vis, EX_NODAL, 1) < 0) {
|
||
|
Gen_Error(0, "fatal: unable to output var params to vis file");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if (ex_put_variable_names(exid_vis, EX_NODAL, 1, (char **)var_names) < 0) {
|
||
|
Gen_Error(0, "fatal: unable to output variable name");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* Do some problem specific assignment */
|
||
|
for (size_t ncnt = 0; ncnt < mesh->num_nodes; ncnt++) {
|
||
|
proc_vals[ncnt] = lb->vertex2proc[ncnt];
|
||
|
}
|
||
|
|
||
|
for (int pcnt = 0; pcnt < machine->num_procs; pcnt++) {
|
||
|
for (const auto &elem : lb->bor_nodes[pcnt]) {
|
||
|
proc_vals[elem] = machine->num_procs + 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Output the nodal variables */
|
||
|
if (ex_put_var(exid_vis, 1, EX_NODAL, 1, 1, mesh->num_nodes, proc_vals.data()) < 0) {
|
||
|
Gen_Error(0, "fatal: unable to output nodal variables");
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
else if (prob->type == ELEMENTAL) {
|
||
|
/* Allocate memory for the element values */
|
||
|
std::vector<float> proc_vals(mesh->num_elems);
|
||
|
|
||
|
if (ex_put_variable_param(exid_vis, EX_ELEM_BLOCK, 1) < 0) {
|
||
|
Gen_Error(0, "fatal: unable to output var params to vis file");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if (ex_put_variable_names(exid_vis, EX_ELEM_BLOCK, 1, (char **)var_names) < 0) {
|
||
|
Gen_Error(0, "fatal: unable to output variable name");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* Do some problem specific assignment */
|
||
|
for (int proc = 0; proc < machine->num_procs; proc++) {
|
||
|
for (size_t e = 0; e < lb->int_elems[proc].size(); e++) {
|
||
|
size_t ecnt = lb->int_elems[proc][e];
|
||
|
proc_vals[ecnt] = proc;
|
||
|
}
|
||
|
|
||
|
for (size_t e = 0; e < lb->bor_elems[proc].size(); e++) {
|
||
|
size_t ecnt = lb->bor_elems[proc][e];
|
||
|
proc_vals[ecnt] = proc;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Output the element variables */
|
||
|
size_t offset = 0;
|
||
|
for (size_t i = 0; i < mesh->num_el_blks; i++) {
|
||
|
if (ex_put_var(exid_vis, 1, EX_ELEM_BLOCK, 1, el_blk_ids[i], el_cnt_blk[i],
|
||
|
&proc_vals[offset]) < 0) {
|
||
|
Gen_Error(0, "fatal: unable to output nodal variables");
|
||
|
return 0;
|
||
|
}
|
||
|
offset += el_cnt_blk[i];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return 1;
|
||
|
} /*---------------------------End write_vis()-------------------------------*/
|