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.
1815 lines
60 KiB
1815 lines
60 KiB
/*
|
|
* Copyright(C) 1999-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
|
|
*/
|
|
|
|
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
*----------------------------------------------------------------------------
|
|
* Functions contained in this file:
|
|
* cmd_line_arg_parse()
|
|
* read_cmd_file()
|
|
* check_inp_specs()
|
|
*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
|
|
#include "copy_string_cpp.h"
|
|
#include "elb.h" // for Problem_Description, etc
|
|
#include "elb_err.h" // for Gen_Error, error_lev
|
|
#include "elb_inp.h"
|
|
#include "elb_util.h" // for strip_string, token_compare, etc
|
|
#include "fmt/ostream.h"
|
|
#if defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) || \
|
|
defined(__MINGW32__) || defined(_WIN64) || defined(__MINGW64__)
|
|
#include "XGetopt.h"
|
|
#include <unistd.h>
|
|
#else
|
|
#include "getopt.h" // for getopt
|
|
#endif
|
|
#include "scopeguard.h"
|
|
#include <cstddef> // for size_t
|
|
#ifndef _XOPEN_SOURCE
|
|
#define _XOPEN_SOURCE 500
|
|
#endif
|
|
#include <cstdlib> // for malloc, exit, free
|
|
#include <cstring> // for strcmp, strstr, strchr, etc
|
|
#include <exodusII.h> // for ex_close, EX_READ, etc
|
|
|
|
namespace {
|
|
int my_getsubopt(char **optionp, char *const *tokens, char **valuep);
|
|
|
|
void print_usage();
|
|
|
|
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 parses the command line options and stores the information
|
|
* in the appropriate data locations.
|
|
*---------------------------------------------------------------------------*/
|
|
template int cmd_line_arg_parse(int argc, char *argv[], std::string &exoII_inp_file,
|
|
std::string &ascii_inp_file, std::string &nemI_out_file,
|
|
Machine_Description *machine, LB_Description<int> *lb,
|
|
Problem_Description *prob, Solver_Description *solver,
|
|
Weight_Description<int> *weight);
|
|
|
|
template int cmd_line_arg_parse(int argc, char *argv[], std::string &exoII_inp_file,
|
|
std::string &ascii_inp_file, std::string &nemI_out_file,
|
|
Machine_Description *machine, LB_Description<int64_t> *lb,
|
|
Problem_Description *prob, Solver_Description *solver,
|
|
Weight_Description<int64_t> *weight);
|
|
|
|
template <typename INT>
|
|
int cmd_line_arg_parse(int argc, char *argv[], /* Args as passed by main() */
|
|
std::string &exoII_inp_file, /* The input ExodusII file name */
|
|
std::string &ascii_inp_file, /* The ASCII input file name */
|
|
std::string &nemI_out_file, /* Output NemesisI file name */
|
|
Machine_Description *machine, /* Structure for machine description */
|
|
LB_Description<INT> *lb, /* Structure for load balance description */
|
|
Problem_Description *prob, /* Structure for various problem params */
|
|
Solver_Description *solver, /* Structure for eigen solver params */
|
|
Weight_Description<INT> *weight /* Structure for weighting graph */
|
|
)
|
|
{
|
|
int opt_let;
|
|
int iret;
|
|
int el_blk;
|
|
int wgt;
|
|
int max_dim = 0;
|
|
int i;
|
|
char *sub_opt = nullptr, *value = nullptr, *cptr = nullptr, *cptr2 = nullptr;
|
|
std::string ctemp;
|
|
|
|
/* see NOTE in elb.h about the order of the following array */
|
|
const char *weight_subopts[] = {"none", "read", "eb", "var_index",
|
|
"edges", "time_index", "var_name", nullptr};
|
|
|
|
const char *mach_subopts[] = {"mesh", "hcube", "hypercube", "cluster", nullptr};
|
|
|
|
const char *lb_subopts[] = {"multikl", "spectral", "inertial", "linear", "random",
|
|
"scattered", "infile", "kl", "none", "num_sects",
|
|
"cnctd_dom", "outfile", "zpinch", "brick", "rcb",
|
|
"rib", "hsfc", "ignore_z", nullptr};
|
|
|
|
const char *solve_subopts[] = {"tolerance", "use_rqi", "vmax", nullptr};
|
|
|
|
/*---------------------------Execution Begins--------------------------------*/
|
|
|
|
/*
|
|
* Make sure there were command line options given. If not assign the
|
|
* name of the default ascii input file.
|
|
*/
|
|
if (argc <= 1) {
|
|
print_usage();
|
|
exit(0);
|
|
}
|
|
|
|
/* Loop over each command line option */
|
|
while ((opt_let = getopt(argc, argv, "3264a:hm:l:nes:x:w:vyo:cg:fpS")) != EOF) {
|
|
|
|
/* case over the option letter */
|
|
switch (opt_let) {
|
|
case 'v':
|
|
/* Should an output visualization file be output */
|
|
prob->vis_out = 1;
|
|
break;
|
|
|
|
case 'y':
|
|
/* Should an output visualization file be output */
|
|
prob->vis_out = 2;
|
|
break;
|
|
|
|
case 'x':
|
|
/* Undocumented flag for setting the error level */
|
|
if (optarg == nullptr) {
|
|
error_lev = 1;
|
|
}
|
|
else {
|
|
iret = sscanf(optarg, "%d", &error_lev);
|
|
if (iret != 1) {
|
|
error_lev = 1;
|
|
}
|
|
|
|
if (error_lev > 3) {
|
|
error_lev = 3;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 'c':
|
|
/* flag to allow the user to skip some error checks */
|
|
prob->skip_checks = 1;
|
|
break;
|
|
|
|
case 'f':
|
|
/*
|
|
* use the face method to calculate adjacencies for
|
|
* elemental decompositions
|
|
*/
|
|
prob->face_adj = 1;
|
|
break;
|
|
|
|
case 'C':
|
|
/*
|
|
* detect vertical columns of elements and ensure that
|
|
* elements of a columns are all in one partition
|
|
*/
|
|
prob->fix_columns = 1;
|
|
break;
|
|
|
|
case 'p':
|
|
/*
|
|
* use the partial method to determine adjacencies:
|
|
* only 3/4 of face nodes must match instead of all
|
|
*/
|
|
prob->partial_adj = 1;
|
|
break;
|
|
|
|
case 'w':
|
|
/* Weighting options */
|
|
sub_opt = optarg;
|
|
while (sub_opt != nullptr && *sub_opt != '\0') {
|
|
switch (my_getsubopt(&sub_opt, (char *const *)weight_subopts, &value)) {
|
|
case READ_EXO:
|
|
if (value == nullptr || strlen(value) == 0) {
|
|
ctemp =
|
|
fmt::format("FATAL: must specify a file name with {}", weight_subopts[READ_EXO]);
|
|
Gen_Error(0, ctemp);
|
|
return 0;
|
|
}
|
|
|
|
if (weight->type < 0) {
|
|
weight->type = READ_EXO;
|
|
}
|
|
else if (!(weight->type & READ_EXO)) {
|
|
weight->type += READ_EXO;
|
|
}
|
|
|
|
/* check if the read is after an element block weight */
|
|
if (weight->type & EL_BLK) {
|
|
weight->ow_read = 0;
|
|
}
|
|
|
|
weight->exo_filename = value;
|
|
|
|
break; /* End "case READ_EXO" */
|
|
|
|
case VAR_INDX:
|
|
if (value == nullptr || strlen(value) == 0) {
|
|
ctemp = fmt::format("FATAL: must specify a value with {}", weight_subopts[VAR_INDX]);
|
|
Gen_Error(0, ctemp);
|
|
return 0;
|
|
}
|
|
|
|
iret = sscanf(value, "%d", &(weight->exo_vindx));
|
|
if (iret != 1) {
|
|
ctemp = fmt::format("FATAL: invalid value specified for {}", weight_subopts[VAR_INDX]);
|
|
Gen_Error(0, ctemp);
|
|
return 0;
|
|
}
|
|
break;
|
|
|
|
case TIME_INDX:
|
|
if (value == nullptr || strlen(value) == 0) {
|
|
ctemp = fmt::format("FATAL: must specify a value with {}", weight_subopts[TIME_INDX]);
|
|
Gen_Error(0, ctemp);
|
|
return 0;
|
|
}
|
|
|
|
iret = sscanf(value, "%d", &(weight->exo_tindx));
|
|
if (iret != 1) {
|
|
ctemp = fmt::format("FATAL: invalid value specified for {}", weight_subopts[TIME_INDX]);
|
|
Gen_Error(0, ctemp);
|
|
return 0;
|
|
}
|
|
break;
|
|
|
|
case VAR_NAME:
|
|
if (value == nullptr || strlen(value) == 0) {
|
|
ctemp = fmt::format("FATAL: must specify a value with {}", weight_subopts[VAR_NAME]);
|
|
Gen_Error(0, ctemp);
|
|
return 0;
|
|
}
|
|
|
|
weight->exo_varname = value;
|
|
|
|
break;
|
|
|
|
case EL_BLK:
|
|
if (value == nullptr) {
|
|
ctemp = fmt::format("FATAL: must specify an element block and weight with {}",
|
|
weight_subopts[EL_BLK]);
|
|
Gen_Error(0, ctemp);
|
|
return 0;
|
|
}
|
|
el_blk = -1;
|
|
wgt = -1;
|
|
|
|
iret = sscanf(value, "%d:%d", &el_blk, &wgt);
|
|
if (iret != 2) {
|
|
Gen_Error(0, "invalid element block weight");
|
|
return 0;
|
|
}
|
|
if (el_blk <= 0) {
|
|
ctemp = fmt::format("invalid element block, %d", el_blk);
|
|
Gen_Error(0, ctemp);
|
|
return 0;
|
|
}
|
|
if (wgt < 0) {
|
|
ctemp = fmt::format("invalid weight, %d", wgt);
|
|
Gen_Error(0, ctemp);
|
|
return 0;
|
|
}
|
|
|
|
weight->elemblk.push_back(el_blk);
|
|
weight->elemblk_wgt.push_back(wgt);
|
|
|
|
if (weight->type < 0) {
|
|
weight->type = EL_BLK;
|
|
}
|
|
else if (!(weight->type & EL_BLK)) {
|
|
weight->type += EL_BLK;
|
|
}
|
|
|
|
/* check if the element block weight needs to over write the read */
|
|
if (weight->type & READ_EXO) {
|
|
weight->ow_read = 1;
|
|
}
|
|
|
|
break;
|
|
|
|
case EDGE_WGT:
|
|
if (weight->type < 0) {
|
|
weight->type = EDGE_WGT;
|
|
}
|
|
else if (!(weight->type & EDGE_WGT)) {
|
|
weight->type += EDGE_WGT;
|
|
}
|
|
break;
|
|
|
|
case NO_WEIGHT: weight->type = NO_WEIGHT; break;
|
|
|
|
default:
|
|
ctemp = fmt::format("FATAL: unknown suboption {} specified", value);
|
|
Gen_Error(0, ctemp);
|
|
return 0;
|
|
|
|
} /* End "switch(my_getsubopt(&sub_opt, weight_subopts, &value))" */
|
|
|
|
} /* End "while(*sub_opt != '\0')" */
|
|
break; /* End "case 'w'" */
|
|
|
|
case 'a':
|
|
/* Only an ASCII input file name */
|
|
if (optarg != nullptr) {
|
|
ascii_inp_file = optarg;
|
|
}
|
|
break;
|
|
|
|
case 'o':
|
|
/* Output NemesisI file name */
|
|
if (optarg != nullptr) {
|
|
nemI_out_file = optarg;
|
|
}
|
|
break;
|
|
|
|
case 'n':
|
|
/* Nodal decomposition */
|
|
if (prob->type == ELEMENTAL) {
|
|
Gen_Error(0, "FATAL: -e and -n are mutually exclusive");
|
|
return 0;
|
|
}
|
|
prob->type = NODAL;
|
|
break;
|
|
|
|
case 'e':
|
|
/* Elemental decomposition */
|
|
if (prob->type == NODAL) {
|
|
Gen_Error(0, "FATAL: -e and -n are mutually exclusive\n");
|
|
return 0;
|
|
}
|
|
prob->type = ELEMENTAL;
|
|
break;
|
|
|
|
case 'm':
|
|
/* Machine type */
|
|
sub_opt = optarg;
|
|
if (sub_opt != nullptr) {
|
|
string_to_lower(sub_opt, '\0');
|
|
}
|
|
while (sub_opt != nullptr && *sub_opt != '\0') {
|
|
|
|
/* Switch over the machine description */
|
|
switch (my_getsubopt(&sub_opt, (char *const *)mach_subopts, &value)) {
|
|
case HCUBE:
|
|
case HYPERCUBE:
|
|
if (machine->type < 0) {
|
|
machine->type = HCUBE;
|
|
max_dim = 1;
|
|
}
|
|
FALL_THROUGH;
|
|
|
|
case MESH:
|
|
if (machine->type < 0) {
|
|
machine->type = MESH;
|
|
max_dim = 3;
|
|
}
|
|
|
|
cptr = value; /* want to set this for both mesh and hcube */
|
|
FALL_THROUGH;
|
|
|
|
case CLUSTER:
|
|
if (machine->type < 0) /* so, get the number of boxes */
|
|
{
|
|
if (value == nullptr || strlen(value) == 0) {
|
|
Gen_Error(0, "FATAL: need to specify number of boxes");
|
|
return 0;
|
|
}
|
|
|
|
/* now need to find what each box consists of */
|
|
cptr = strpbrk(value, "mMhH");
|
|
if (*cptr == 'm' || *cptr == 'M') {
|
|
machine->type = MESH;
|
|
max_dim = 3;
|
|
}
|
|
else if (*cptr == 'h' || *cptr == 'H') {
|
|
machine->type = HCUBE;
|
|
max_dim = 1;
|
|
}
|
|
else {
|
|
Gen_Error(0, "FATAL: unknown type specified with cluster");
|
|
return 0;
|
|
}
|
|
/* blank out character and move cptr to next char */
|
|
*cptr = '\0';
|
|
cptr++;
|
|
|
|
/* get the number of boxes from value */
|
|
iret = sscanf(value, "%d", &(machine->num_boxes));
|
|
if (iret <= 0 || machine->num_boxes <= 0) {
|
|
Gen_Error(0, "FATAL: invalid number of boxes");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (cptr == nullptr || strlen(cptr) == 0) {
|
|
Gen_Error(0, "FATAL: need to specify dimension");
|
|
return 0;
|
|
}
|
|
cptr2 = strtok(cptr, "xX");
|
|
if (cptr2 == nullptr) {
|
|
Gen_Error(0, "FATAL: bad size for dimension specification");
|
|
return 0;
|
|
}
|
|
machine->num_dims = 0;
|
|
for (i = 0; i < max_dim; i++) {
|
|
machine->dim[i] = 1;
|
|
}
|
|
while (cptr2) {
|
|
iret = sscanf(cptr2, "%d", &(machine->dim[machine->num_dims]));
|
|
if (iret <= 0 || machine->dim[machine->num_dims] <= 0) {
|
|
Gen_Error(0, "FATAL: invalid dimension specification");
|
|
return 0;
|
|
}
|
|
|
|
machine->num_dims++;
|
|
cptr2 = strtok(nullptr, "xX");
|
|
|
|
/* Only up to three-dimensional allowed */
|
|
if (machine->num_dims == max_dim && cptr2 != nullptr) {
|
|
Gen_Error(0, "FATAL: maximum number of dimensions exceeded");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
break; /* End "case MESH or HCUBE or CLUSTER" */
|
|
|
|
default: Gen_Error(0, "FATAL: unknown machine type"); return 0;
|
|
|
|
} /* End "switch(my_getsubopt(&sub_opt, mach_subopts, &value))" */
|
|
|
|
} /* End "while(*sub_opt != '\0')" */
|
|
break; /* End "case 'm'" */
|
|
|
|
case 'l':
|
|
/* Load balance information */
|
|
sub_opt = optarg;
|
|
if (sub_opt != nullptr) {
|
|
string_to_lower(sub_opt, '\0');
|
|
}
|
|
while (sub_opt != nullptr && *sub_opt != '\0') {
|
|
switch (my_getsubopt(&sub_opt, (char *const *)lb_subopts, &value)) {
|
|
case MULTIKL: lb->type = MULTIKL; break;
|
|
|
|
case SPECTRAL: lb->type = SPECTRAL; break;
|
|
|
|
case INERTIAL: lb->type = INERTIAL; break;
|
|
|
|
case ZPINCH: lb->type = ZPINCH; break;
|
|
|
|
case BRICK: lb->type = BRICK; break;
|
|
|
|
case ZOLTAN_RCB: lb->type = ZOLTAN_RCB; break;
|
|
|
|
case ZOLTAN_RIB: lb->type = ZOLTAN_RIB; break;
|
|
|
|
case ZOLTAN_HSFC: lb->type = ZOLTAN_HSFC; break;
|
|
|
|
case LINEAR: lb->type = LINEAR; break;
|
|
|
|
case RANDOM: lb->type = RANDOM; break;
|
|
|
|
case SCATTERED: lb->type = SCATTERED; break;
|
|
|
|
case INFILE:
|
|
if (value == nullptr) {
|
|
Gen_Error(0, "FATAL: need to specify a value with file");
|
|
return 0;
|
|
}
|
|
char tmpstr[2048];
|
|
iret = sscanf(value, "%2047s", tmpstr);
|
|
lb->file = tmpstr;
|
|
if (iret != 1) {
|
|
Gen_Error(0, "FATAL: invalid value associated with file");
|
|
return 0;
|
|
}
|
|
lb->type = INFILE;
|
|
break;
|
|
|
|
case KL_REFINE: lb->refine = KL_REFINE; break;
|
|
|
|
case NO_REFINE: lb->refine = NO_REFINE; break;
|
|
|
|
case NUM_SECTS:
|
|
if (value == nullptr) {
|
|
Gen_Error(0, "FATAL: need to specify a value with num_sects");
|
|
return 0;
|
|
}
|
|
iret = sscanf(value, "%d", &(lb->num_sects));
|
|
if (iret != 1) {
|
|
Gen_Error(0, "FATAL: invalid value associated with num_sects");
|
|
return 0;
|
|
}
|
|
break;
|
|
|
|
case CNCT_DOM: lb->cnctd_dom = 1; break;
|
|
|
|
case IGNORE_Z: lb->ignore_z = 1; break;
|
|
|
|
case OUTFILE:
|
|
if (value == nullptr) {
|
|
Gen_Error(0, "FATAL: need to specify a value with outfile");
|
|
return 0;
|
|
}
|
|
iret = sscanf(value, "%2047s", tmpstr);
|
|
lb->file = tmpstr;
|
|
if (iret != 1) {
|
|
Gen_Error(0, "FATAL: invalid value associated with outfile");
|
|
return 0;
|
|
}
|
|
lb->outfile = ELB_TRUE;
|
|
break;
|
|
|
|
default:
|
|
ctemp = fmt::format("FATAL: unknown lb param \"{}\"", value);
|
|
Gen_Error(0, ctemp);
|
|
return 0;
|
|
|
|
} /* End "switch(my_getsubopt(&sup_opt, mach_subopts, &value))" */
|
|
|
|
} /* End "while(*sup_opt != '\0')" */
|
|
break; /* End "case 'l'" */
|
|
|
|
case 'S': prob->no_sph = 1; break;
|
|
|
|
case 's':
|
|
/* Eigen solver options */
|
|
sub_opt = optarg;
|
|
if (sub_opt != nullptr) {
|
|
string_to_lower(sub_opt, '\0');
|
|
}
|
|
while (sub_opt != nullptr && *sub_opt != '\0') {
|
|
switch (my_getsubopt(&sub_opt, (char *const *)solve_subopts, &value)) {
|
|
case TOLER:
|
|
if (value == nullptr) {
|
|
fmt::print(stderr, "FATAL: tolerance specification requires \
|
|
value\n");
|
|
return 0;
|
|
}
|
|
iret = sscanf(value, "%le", &(solver->tolerance));
|
|
if (iret != 1) {
|
|
fmt::print(stderr, "FATAL: incorrect value for tolerance\n");
|
|
return 0;
|
|
}
|
|
|
|
break;
|
|
|
|
case USE_RQI:
|
|
if (solver->rqi_flag == USE_RQI) {
|
|
solver->rqi_flag = -1;
|
|
}
|
|
else {
|
|
solver->rqi_flag = USE_RQI;
|
|
}
|
|
|
|
break;
|
|
|
|
case VMAX:
|
|
if (value == nullptr) {
|
|
fmt::print(stderr, "FATAL: must specify a value with {}\n", solve_subopts[VMAX]);
|
|
return 0;
|
|
}
|
|
iret = sscanf(value, "%d", &(solver->vmax));
|
|
if (iret != 1) {
|
|
fmt::print(stderr, "FATAL: invalid value read for {}\n", solve_subopts[VMAX]);
|
|
return 0;
|
|
}
|
|
|
|
break;
|
|
|
|
default: fmt::print(stderr, "FATAL: unknown solver option\n"); return 0;
|
|
|
|
} /* End "switch(my_getsubopt(&sub_opt, solve_subopts, &value))" */
|
|
|
|
} /* End "while(sub_opt != '\0')" */
|
|
break; /* End "case 's'" */
|
|
|
|
case 'g':
|
|
/* group designations */
|
|
/* allocate string to hold designation */
|
|
if (optarg != nullptr) {
|
|
prob->groups = reinterpret_cast<char *>(malloc(strlen(optarg) + 1));
|
|
copy_string(prob->groups, optarg, strlen(optarg) + 1);
|
|
}
|
|
break;
|
|
|
|
case 'h':
|
|
/* Usage info was requested */
|
|
print_usage();
|
|
exit(0);
|
|
|
|
case '6':
|
|
case '4':
|
|
/* ignore -- used to parse -64 option */
|
|
break;
|
|
|
|
case '3':
|
|
case '2':
|
|
/* ignore -- used to parse -32 option */
|
|
break;
|
|
|
|
default:
|
|
/* Default case. Error on unknown argument. */
|
|
return 0;
|
|
|
|
} /* End "switch(opt_let)" */
|
|
|
|
} /* End "while((opt_let=getopt(argc, argv, "i")) != EOF)" */
|
|
/* Get the input file name, if specified on the command line */
|
|
if ((argc - optind) >= 1) {
|
|
exoII_inp_file = argv[optind];
|
|
}
|
|
else {
|
|
exoII_inp_file = "";
|
|
}
|
|
return 1;
|
|
|
|
} /*-------End cmd_line_arg_parse()--------*/
|
|
|
|
/*****************************************************************************/
|
|
/*****************************************************************************/
|
|
/*****************************************************************************/
|
|
/* This function reads in the ASCII command file.
|
|
*---------------------------------------------------------------------------*/
|
|
template int read_cmd_file(std::string &ascii_inp_file, std::string &exoII_inp_file,
|
|
std::string &nemI_out_file, Machine_Description *machine,
|
|
LB_Description<int> *lb, Problem_Description *problem,
|
|
Solver_Description *solver, Weight_Description<int> *weight);
|
|
template int read_cmd_file(std::string &ascii_inp_file, std::string &exoII_inp_file,
|
|
std::string &nemI_out_file, Machine_Description *machine,
|
|
LB_Description<int64_t> *lb, Problem_Description *problem,
|
|
Solver_Description *solver, Weight_Description<int64_t> *weight);
|
|
|
|
template <typename INT>
|
|
int read_cmd_file(std::string &ascii_inp_file, std::string &exoII_inp_file,
|
|
std::string &nemI_out_file, Machine_Description *machine, LB_Description<INT> *lb,
|
|
Problem_Description *problem, Solver_Description *solver,
|
|
Weight_Description<INT> *weight)
|
|
{
|
|
FILE *inp_fd;
|
|
std::string ctemp;
|
|
char inp_line[MAX_INP_LINE];
|
|
char inp_copy[MAX_INP_LINE];
|
|
char *cptr, *cptr2;
|
|
|
|
int iret;
|
|
int el_blk;
|
|
int wgt;
|
|
int i;
|
|
int ilen;
|
|
int max_dim;
|
|
char tmpstr[2048];
|
|
/*-----------------------------Execution Begins------------------------------*/
|
|
if (!(inp_fd = fopen(ascii_inp_file.c_str(), "r"))) {
|
|
ctemp = fmt::format("FATAL: unable to open ASCII input file {}", ascii_inp_file);
|
|
Gen_Error(0, ctemp);
|
|
return 0;
|
|
}
|
|
ON_BLOCK_EXIT(fclose, inp_fd);
|
|
/* Begin parsing the input file */
|
|
while (fgets(inp_line, MAX_INP_LINE, inp_fd)) {
|
|
if (inp_line[0] != '#') {
|
|
copy_string(inp_copy, inp_line);
|
|
clean_string(inp_line, " \t");
|
|
cptr = strtok(inp_line, "\t=");
|
|
if (token_compare(cptr, "input exodusii file")) {
|
|
/* The input ExodusII file name */
|
|
if (exoII_inp_file.empty()) {
|
|
cptr = strtok(nullptr, "\t=");
|
|
strip_string(cptr, " \t\n");
|
|
exoII_inp_file = cptr;
|
|
}
|
|
}
|
|
else if (token_compare(cptr, "output visualization file")) {
|
|
if (problem->vis_out < 0) {
|
|
/* Output a visualization file */
|
|
cptr = strtok(nullptr, "\t=");
|
|
strip_string(cptr, " \t\n");
|
|
if (strcasecmp(cptr, "yes") == 0 || strcasecmp(cptr, "true") == 0) {
|
|
problem->vis_out = 1;
|
|
}
|
|
else if (strcasecmp(cptr, "no") == 0 || strcasecmp(cptr, "false") == 0) {
|
|
problem->vis_out = 0;
|
|
}
|
|
else {
|
|
iret = sscanf(cptr, "%d", &problem->vis_out);
|
|
if (iret != 1) {
|
|
Gen_Error(1, "WARNING: unknown visualization output flag");
|
|
problem->vis_out = 0;
|
|
}
|
|
else {
|
|
if (problem->vis_out != 1 && problem->vis_out != 2) {
|
|
problem->vis_out = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (token_compare(cptr, "output nemesisi file")) {
|
|
/* The NemesisI output file name */
|
|
if (nemI_out_file.empty()) {
|
|
cptr = strtok(nullptr, "\t=");
|
|
strip_string(cptr, " \t\n");
|
|
nemI_out_file = cptr;
|
|
}
|
|
}
|
|
else if (token_compare(cptr, "decomposition method")) {
|
|
/* The method to use for decomposing the graph */
|
|
if (lb->type < 0 || lb->refine < 0 || lb->num_sects < 0) {
|
|
|
|
/* Search to the first null character */
|
|
cptr = strchr(cptr, '\0');
|
|
cptr++;
|
|
strip_string(cptr, " \t\n=");
|
|
cptr = strtok(cptr, ",");
|
|
while (cptr != nullptr) {
|
|
strip_string(cptr, " \t\n");
|
|
string_to_lower(cptr, '\0');
|
|
if (strcmp(cptr, "multikl") == 0) {
|
|
if (lb->type < 0) {
|
|
lb->type = MULTIKL;
|
|
}
|
|
}
|
|
else if (strcmp(cptr, "spectral") == 0) {
|
|
if (lb->type < 0) {
|
|
lb->type = SPECTRAL;
|
|
}
|
|
}
|
|
else if (strcmp(cptr, "scattered") == 0) {
|
|
if (lb->type < 0) {
|
|
lb->type = SCATTERED;
|
|
}
|
|
}
|
|
else if (strcmp(cptr, "linear") == 0) {
|
|
if (lb->type < 0) {
|
|
lb->type = LINEAR;
|
|
}
|
|
}
|
|
else if (strcmp(cptr, "inertial") == 0) {
|
|
if (lb->type < 0) {
|
|
lb->type = INERTIAL;
|
|
}
|
|
}
|
|
else if (strcmp(cptr, "zpinch") == 0) {
|
|
if (lb->type < 0) {
|
|
lb->type = ZPINCH;
|
|
}
|
|
}
|
|
else if (strcmp(cptr, "brick") == 0) {
|
|
if (lb->type < 0) {
|
|
lb->type = BRICK;
|
|
}
|
|
}
|
|
else if (strcmp(cptr, "rcb") == 0) {
|
|
if (lb->type < 0) {
|
|
lb->type = ZOLTAN_RCB;
|
|
}
|
|
}
|
|
else if (strcmp(cptr, "rib") == 0) {
|
|
if (lb->type < 0) {
|
|
lb->type = ZOLTAN_RIB;
|
|
}
|
|
}
|
|
else if (strcmp(cptr, "hsfc") == 0) {
|
|
if (lb->type < 0) {
|
|
lb->type = ZOLTAN_HSFC;
|
|
}
|
|
}
|
|
else if (strcmp(cptr, "random") == 0) {
|
|
if (lb->type < 0) {
|
|
lb->type = RANDOM;
|
|
}
|
|
}
|
|
else if (strstr(cptr, "infile")) {
|
|
if (lb->type < 0) {
|
|
lb->type = INFILE;
|
|
cptr2 = strchr(cptr, '=');
|
|
if (cptr2 == nullptr) {
|
|
Gen_Error(0, "FATAL: need to specify a value with infile");
|
|
return 0;
|
|
}
|
|
|
|
cptr2++;
|
|
iret = sscanf(cptr2, "%2047s", tmpstr);
|
|
lb->file = tmpstr;
|
|
if (iret != 1) {
|
|
Gen_Error(0, "FATAL: invalid value for infile");
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
else if (strcmp(cptr, "kl") == 0) {
|
|
if (lb->refine < 0) {
|
|
lb->refine = KL_REFINE;
|
|
}
|
|
}
|
|
else if (strcmp(cptr, "none") == 0) {
|
|
if (lb->refine < 0) {
|
|
lb->refine = NS_NONE;
|
|
}
|
|
}
|
|
else if (strcmp(cptr, "ignore_z") == 0) {
|
|
lb->ignore_z = 1;
|
|
}
|
|
else if (strstr(cptr, "num_sects")) {
|
|
if (lb->num_sects < 0) {
|
|
cptr2 = strchr(cptr, '=');
|
|
if (cptr2 == nullptr) {
|
|
Gen_Error(0, "FATAL: need to specify a value with num_sects");
|
|
return 0;
|
|
}
|
|
|
|
cptr2++;
|
|
iret = sscanf(cptr2, "%d", &(lb->num_sects));
|
|
if (iret != 1) {
|
|
Gen_Error(0, "FATAL: invalid value for num_sects");
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
else if (strcmp(cptr, "cnctd_dom") == 0) {
|
|
if (lb->cnctd_dom < 0) {
|
|
lb->cnctd_dom = ELB_TRUE;
|
|
}
|
|
}
|
|
else if (strstr(cptr, "outfile")) {
|
|
if (lb->outfile < 0) {
|
|
lb->outfile = ELB_TRUE;
|
|
cptr2 = strchr(cptr, '=');
|
|
if (cptr2 == nullptr) {
|
|
Gen_Error(0, "FATAL: need to specify a value with outfile");
|
|
return 0;
|
|
}
|
|
|
|
cptr2++;
|
|
iret = sscanf(cptr2, "%2047s", tmpstr);
|
|
lb->file = tmpstr;
|
|
if (iret != 1) {
|
|
Gen_Error(0, "FATAL: invalid value for outfile");
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
ctemp =
|
|
fmt::format("FATAL: unknown LB method \"{}\" specified in command file", cptr);
|
|
Gen_Error(0, ctemp);
|
|
return 0;
|
|
}
|
|
cptr = strtok(nullptr, ",");
|
|
}
|
|
}
|
|
}
|
|
else if (token_compare(cptr, "solver specifications")) {
|
|
/* Solver specs */
|
|
|
|
/* Search to the first null character */
|
|
cptr = strchr(cptr, '\0');
|
|
cptr++;
|
|
strip_string(cptr, " \t\n=");
|
|
cptr = strtok(cptr, ",");
|
|
|
|
/* Loop until all the suboptions have been specified */
|
|
while (cptr) {
|
|
strip_string(cptr, " \t\n");
|
|
string_to_lower(cptr, '\0');
|
|
|
|
/* Check to see if this is the "tolerance" suboption */
|
|
if (strstr(cptr, "tolerance")) {
|
|
if (solver->tolerance < 0.0) {
|
|
cptr2 = strchr(cptr, '=');
|
|
if (cptr2 == nullptr) {
|
|
Gen_Error(0, "FATAL: tolerance specification requires a value");
|
|
return 0;
|
|
}
|
|
|
|
cptr2++;
|
|
iret = sscanf(cptr2, "%le", &(solver->tolerance));
|
|
if (iret != 1) {
|
|
Gen_Error(0, "FATAL: invalid value for tolerance");
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
else if (strcmp(cptr, "use_rqi") == 0) {
|
|
if (solver->rqi_flag == USE_RQI) {
|
|
solver->rqi_flag = -1;
|
|
}
|
|
else {
|
|
solver->rqi_flag = USE_RQI;
|
|
}
|
|
}
|
|
else if (strstr(cptr, "vmax")) {
|
|
if (solver->vmax < 0) {
|
|
cptr2 = strchr(cptr, '=');
|
|
if (cptr2 == nullptr) {
|
|
Gen_Error(0, "FATAL: vmax must have a value");
|
|
return 0;
|
|
}
|
|
|
|
cptr2++;
|
|
iret = sscanf(cptr2, "%d", &(solver->vmax));
|
|
if (iret != 1) {
|
|
Gen_Error(0, "FATAL: invalid value for vmax");
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
ctemp = fmt::format("WARNING: unknown solver suboption {}", cptr);
|
|
Gen_Error(1, ctemp);
|
|
}
|
|
|
|
cptr = strtok(nullptr, ",");
|
|
}
|
|
}
|
|
else if (token_compare(cptr, "graph type")) {
|
|
if (problem->type < 0) {
|
|
cptr = strtok(nullptr, "\t=");
|
|
strip_string(cptr, " \t\n");
|
|
string_to_lower(cptr, '\0');
|
|
if (strcmp(cptr, "nodal") == 0) {
|
|
problem->type = NODAL;
|
|
}
|
|
else if (strcmp(cptr, "node") == 0) {
|
|
problem->type = NODAL;
|
|
}
|
|
else if (strcmp(cptr, "elemental") == 0) {
|
|
problem->type = ELEMENTAL;
|
|
}
|
|
else if (strcmp(cptr, "element") == 0) {
|
|
problem->type = ELEMENTAL;
|
|
}
|
|
else {
|
|
Gen_Error(0, "FATAL: unknown graph type specified");
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
else if (token_compare(cptr, "machine description")) {
|
|
/* Machine specs */
|
|
if (machine->num_dims < 0) {
|
|
/* Search to first null character */
|
|
cptr = strchr(cptr, '\0');
|
|
cptr++;
|
|
strip_string(cptr, " \t\n=");
|
|
|
|
/* Search to equal sign */
|
|
cptr2 = strchr(cptr, '=');
|
|
if (cptr2 == nullptr) {
|
|
Gen_Error(0, "FATAL: machine must have a dimension specified");
|
|
return 0;
|
|
}
|
|
|
|
*cptr2 = '\0';
|
|
|
|
/* Find out the machine type */
|
|
strip_string(cptr, " \t\n");
|
|
if (strcasecmp(cptr, "mesh") == 0) {
|
|
machine->type = MESH;
|
|
max_dim = 3;
|
|
}
|
|
else if (strcasecmp(cptr, "hcube") == 0 || strcasecmp(cptr, "hypercube") == 0) {
|
|
machine->type = HCUBE;
|
|
max_dim = 1;
|
|
}
|
|
else if (strcasecmp(cptr, "cluster") == 0) {
|
|
/* now need to find what each box consists of */
|
|
cptr = cptr2 + 1;
|
|
cptr2 = strpbrk(cptr, "mMhH");
|
|
if (*cptr2 == 'm' || *cptr2 == 'M') {
|
|
machine->type = MESH;
|
|
max_dim = 3;
|
|
}
|
|
else if (*cptr2 == 'h' || *cptr2 == 'H') {
|
|
machine->type = HCUBE;
|
|
max_dim = 1;
|
|
}
|
|
else {
|
|
Gen_Error(0, "FATAL: unknown type specified with cluster");
|
|
return 0;
|
|
}
|
|
/* blank out character and move cptr to next char */
|
|
*cptr2 = '\0';
|
|
|
|
/* get the number of boxes from value */
|
|
iret = sscanf(cptr, "%d", &(machine->num_boxes));
|
|
if (iret <= 0 || machine->num_boxes <= 0) {
|
|
Gen_Error(0, "FATAL: invalid number of boxes");
|
|
return 0;
|
|
}
|
|
}
|
|
else {
|
|
Gen_Error(0, "FATAL: unknown machine type specified");
|
|
return 0;
|
|
}
|
|
|
|
machine->num_dims = 0;
|
|
cptr = cptr2 + 1;
|
|
cptr = strtok(cptr, "xX");
|
|
while (cptr) {
|
|
iret = sscanf(cptr, "%d", &(machine->dim[machine->num_dims]));
|
|
if (iret != 1) {
|
|
Gen_Error(0, "FATAL: invalid dimension specified for machine");
|
|
return 0;
|
|
}
|
|
|
|
machine->num_dims++;
|
|
cptr = strtok(nullptr, "xX");
|
|
|
|
/* Check how many dimensions there are */
|
|
if (machine->num_dims == max_dim && cptr != nullptr) {
|
|
Gen_Error(0, "FATAL: maximum number of dimensions exceeded");
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (token_compare(cptr, "weighting specifications")) {
|
|
/* Parameters for weighting the graph */
|
|
if (weight->type < 0) {
|
|
cptr = strchr(cptr, '\0');
|
|
cptr++;
|
|
strip_string(cptr, " \t\n=");
|
|
cptr = strtok(cptr, ",");
|
|
|
|
while (cptr != nullptr) {
|
|
strip_string(cptr, " \t\n");
|
|
string_to_lower(cptr, '\0');
|
|
if (strstr(cptr, "read")) {
|
|
cptr2 = strchr(cptr, '=');
|
|
if (cptr2 == nullptr) {
|
|
Gen_Error(0, "FATAL: must specify file name with \"read\"");
|
|
return 0;
|
|
}
|
|
cptr2++;
|
|
if (strlen(cptr2) == 0) {
|
|
Gen_Error(0, "FATAL: invalid file name with \"read\"");
|
|
return 0;
|
|
}
|
|
weight->exo_filename = cptr2;
|
|
if (weight->type < 0) {
|
|
weight->type = READ_EXO;
|
|
}
|
|
else if (!(weight->type & READ_EXO)) {
|
|
weight->type += READ_EXO;
|
|
}
|
|
|
|
/* check if the read is after an element block weight */
|
|
if (weight->type & EL_BLK) {
|
|
weight->ow_read = 0;
|
|
}
|
|
}
|
|
else if (strstr(cptr, "var_name")) {
|
|
cptr2 = strchr(cptr, '=');
|
|
if (cptr2 == nullptr) {
|
|
Gen_Error(0, "FATAL: must specify a name with \"var_name\"");
|
|
return 0;
|
|
}
|
|
cptr2++;
|
|
if (strlen(cptr2) == 0) {
|
|
Gen_Error(0, "FATAL: invalid variable name specified with"
|
|
" \"var_name\"");
|
|
return 0;
|
|
}
|
|
weight->exo_varname = cptr2;
|
|
}
|
|
else if (strstr(cptr, "var_index")) {
|
|
cptr2 = strchr(cptr, '=');
|
|
if (cptr2 == nullptr) {
|
|
Gen_Error(0, "FATAL: must specify a value with \"var_index\"");
|
|
return 0;
|
|
}
|
|
cptr2++;
|
|
|
|
iret = sscanf(cptr2, "%d", &(weight->exo_vindx));
|
|
if (iret != 1) {
|
|
Gen_Error(0, "FATAL: invalid value with \"var_index\"");
|
|
return 0;
|
|
}
|
|
}
|
|
else if (strstr(cptr, "time_index")) {
|
|
cptr2 = strchr(cptr, '=');
|
|
if (cptr2 == nullptr) {
|
|
Gen_Error(0, "FATAL: must specify a value with \"time_index\"");
|
|
return 0;
|
|
}
|
|
cptr2++;
|
|
|
|
iret = sscanf(cptr2, "%d", &(weight->exo_tindx));
|
|
if (iret != 1) {
|
|
Gen_Error(0, "FATAL: invalid value with \"time_index\"");
|
|
return 0;
|
|
}
|
|
}
|
|
else if (strstr(cptr, "eb")) {
|
|
cptr2 = strchr(cptr, '=');
|
|
if (cptr2 == nullptr) {
|
|
Gen_Error(0, "FATAL: must specify a value with \"eb\"");
|
|
return 0;
|
|
}
|
|
cptr2++;
|
|
|
|
el_blk = -1;
|
|
wgt = -1;
|
|
|
|
iret = sscanf(cptr2, "%d:%d", &el_blk, &wgt);
|
|
if (iret != 2) {
|
|
Gen_Error(0, "FATAL: invalid value with \"eb\"");
|
|
return 0;
|
|
}
|
|
if (el_blk <= 0) {
|
|
ctemp = fmt::format("invalid element block, %d", el_blk);
|
|
Gen_Error(0, ctemp);
|
|
return 0;
|
|
}
|
|
if (wgt < 1) {
|
|
ctemp = fmt::format("invalid weight, %d", wgt);
|
|
Gen_Error(0, ctemp);
|
|
return 0;
|
|
}
|
|
|
|
if (weight->type < 0) {
|
|
weight->type = EL_BLK;
|
|
}
|
|
else if (!(weight->type & EL_BLK)) {
|
|
weight->type += EL_BLK;
|
|
}
|
|
|
|
weight->elemblk.push_back(el_blk);
|
|
weight->elemblk_wgt.push_back(wgt);
|
|
|
|
/* check if the elem block weight needs to overwrite the read */
|
|
if (weight->type & READ_EXO) {
|
|
weight->ow_read = 1;
|
|
}
|
|
}
|
|
else if (strstr(cptr, "edges")) {
|
|
if (weight->type < 0) {
|
|
weight->type = EDGE_WGT;
|
|
}
|
|
else if (!(weight->type & EDGE_WGT)) {
|
|
weight->type += EDGE_WGT;
|
|
}
|
|
}
|
|
else {
|
|
ctemp = fmt::format("FATAL: unknown suboption \"{}\" specified", cptr);
|
|
Gen_Error(0, ctemp);
|
|
return 0;
|
|
}
|
|
|
|
cptr = strtok(nullptr, ",");
|
|
}
|
|
|
|
} /* End "if(weight->type < 0)" */
|
|
}
|
|
else if (token_compare(cptr, "misc options")) {
|
|
/* Misc Options */
|
|
|
|
/* Search to the first null character */
|
|
cptr = strchr(cptr, '\0');
|
|
cptr++;
|
|
/*
|
|
* Need to handle case where users have put comma's in
|
|
* the group descriptor. This will mess up getting the
|
|
* tokens using strtok(). So, search for commas between
|
|
* the beginning delimiter, "{", and the end delimiter,
|
|
* "}", and change them to blank spaces.
|
|
*/
|
|
cptr2 = strchr(cptr, '{');
|
|
if (cptr2 != nullptr) {
|
|
ilen = strlen(cptr2);
|
|
for (i = 0; i < ilen; i++) {
|
|
if (*cptr2 == '}') {
|
|
break;
|
|
}
|
|
if (*cptr2 == ',') {
|
|
*cptr2 = ' ';
|
|
}
|
|
cptr2++;
|
|
}
|
|
}
|
|
|
|
strip_string(cptr, " \t\n=");
|
|
cptr = strtok(cptr, ",");
|
|
|
|
/* Loop until all the suboptions have been specified */
|
|
while (cptr != nullptr) {
|
|
strip_string(cptr, " \t\n");
|
|
string_to_lower(cptr, '\0');
|
|
|
|
/* Check to see if the side id error checks need to be skipped */
|
|
if (strstr(cptr, "checks_off")) {
|
|
if (problem->skip_checks < 0) {
|
|
problem->skip_checks = 1;
|
|
}
|
|
}
|
|
/* Check to see if using face definition of adjacency */
|
|
else if (strstr(cptr, "face_adj")) {
|
|
if (problem->face_adj < 0) {
|
|
problem->face_adj = 1;
|
|
}
|
|
}
|
|
/* Check if element columns are to be detected and fixed so
|
|
* that all elements of a column are in the same partition */
|
|
else if (strstr(cptr, "fix_columns")) {
|
|
problem->fix_columns = 1;
|
|
}
|
|
/* Check to see if looking for global mechanisms */
|
|
else if (strstr(cptr, "global_mech")) {
|
|
if (problem->global_mech < 0) {
|
|
problem->global_mech = 1;
|
|
}
|
|
}
|
|
/* Check to see if looking for introduced mechanisms */
|
|
else if (strstr(cptr, "local_mech")) {
|
|
if (problem->local_mech < 0) {
|
|
problem->local_mech = 1;
|
|
}
|
|
}
|
|
/* Check to see if looking for connected domains */
|
|
else if (strstr(cptr, "find_cnt_domains")) {
|
|
if (problem->find_cnt_domains < 0) {
|
|
problem->find_cnt_domains = 1;
|
|
}
|
|
}
|
|
/* Check to see if user wants to add processors to take care of
|
|
* introduced mechanisms
|
|
*/
|
|
else if (strstr(cptr, "mech_add_procs")) {
|
|
if (problem->mech_add_procs < 0) {
|
|
problem->mech_add_procs = 1;
|
|
}
|
|
}
|
|
/* Check to see if user wants to add processors to take care of
|
|
* introduced mechanisms
|
|
*/
|
|
else if (strstr(cptr, "dsd_add_procs")) {
|
|
if (problem->dsd_add_procs < 0) {
|
|
problem->dsd_add_procs = 1;
|
|
}
|
|
}
|
|
/* Check for treating spheres as concentrated masses*/
|
|
else if (strstr(cptr, "no_sph")) {
|
|
if (problem->no_sph < 0) {
|
|
problem->no_sph = 1;
|
|
}
|
|
}
|
|
/* Check for group designation sub-option */
|
|
else if (strstr(cptr, "groups")) {
|
|
/* "{" defines the beginning of the group designator */
|
|
cptr2 = strchr(cptr, '{');
|
|
if (cptr2 == nullptr) {
|
|
Gen_Error(0, "FATAL: group start designator \"}\" not found");
|
|
return 0;
|
|
}
|
|
cptr2++;
|
|
/* allocate space to hold the group designator */
|
|
problem->groups = reinterpret_cast<char *>(malloc(strlen(cptr2) + 1));
|
|
copy_string(problem->groups, cptr2, strlen(cptr2) + 1);
|
|
/* get rid of ending bracket */
|
|
cptr2 = strchr(problem->groups, '}');
|
|
*cptr2 = '\0';
|
|
}
|
|
else {
|
|
ctemp = fmt::format("WARNING: unknown miscellaneous suboption {}", cptr);
|
|
Gen_Error(1, ctemp);
|
|
}
|
|
cptr = strtok(nullptr, ",");
|
|
}
|
|
}
|
|
else {
|
|
/* Generate an error, but continue reading for an unknown key */
|
|
strip_string(inp_copy, " #\t");
|
|
if (strlen(inp_copy) > 5) {
|
|
ctemp = fmt::format(
|
|
"WARNING: don't know how to process line: \n{}\nin command file, ignored", inp_copy);
|
|
Gen_Error(1, ctemp);
|
|
}
|
|
}
|
|
|
|
} /* End "if(inp_line[0] != '#')" */
|
|
|
|
} /* End "while(fgets(inp_line, MAX_INP_LINE, inp_fd))" */
|
|
return 1;
|
|
} /*------------End read_cmd_file()---------------*/
|
|
|
|
/*****************************************************************************/
|
|
/*****************************************************************************/
|
|
/*****************************************************************************/
|
|
/* This function performs error checks on the user input.
|
|
*---------------------------------------------------------------------------*/
|
|
template int check_inp_specs(std::string &exoII_inp_file, std::string &nemI_out_file,
|
|
Machine_Description *machine, LB_Description<int> *lb,
|
|
Problem_Description *prob, Solver_Description *solver,
|
|
Weight_Description<int> *weight);
|
|
|
|
template int check_inp_specs(std::string &exoII_inp_file, std::string &nemI_out_file,
|
|
Machine_Description *machine, LB_Description<int64_t> *lb,
|
|
Problem_Description *prob, Solver_Description *solver,
|
|
Weight_Description<int64_t> *weight);
|
|
|
|
template <typename INT>
|
|
int check_inp_specs(std::string &exoII_inp_file, std::string &nemI_out_file,
|
|
Machine_Description *machine, LB_Description<INT> *lb,
|
|
Problem_Description *prob, Solver_Description *solver,
|
|
Weight_Description<INT> *weight)
|
|
{
|
|
/* Check that an input ExodusII file name was specified */
|
|
if (exoII_inp_file.empty()) {
|
|
Gen_Error(0, "FATAL: no input ExodusII file specified");
|
|
return 0;
|
|
}
|
|
|
|
/* Check for the existence and readability of the input file */
|
|
int icpu_ws = 0;
|
|
int iio_ws = 0;
|
|
float vers;
|
|
int exid_inp;
|
|
if ((exid_inp = ex_open(exoII_inp_file.c_str(), EX_READ, &icpu_ws, &iio_ws, &vers)) < 0) {
|
|
std::string ctemp = fmt::format("FATAL: unable to open input ExodusII file {}", exoII_inp_file);
|
|
Gen_Error(0, ctemp);
|
|
return 0;
|
|
}
|
|
|
|
/* Get the integer size stored on the database */
|
|
prob->int64db = ex_int64_status(exid_inp) & EX_ALL_INT64_DB;
|
|
|
|
ex_close(exid_inp);
|
|
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
|
|
/* Check the machine specification */
|
|
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
|
|
if (machine->type != MESH && machine->type != HCUBE) {
|
|
Gen_Error(0, "FATAL: machine type not properly set");
|
|
return 0;
|
|
}
|
|
if (machine->type == HCUBE && machine->num_dims != 1) {
|
|
Gen_Error(0, "FATAL: improper number of dimension for a hypercube, only"
|
|
" 1 allowed");
|
|
return 0;
|
|
}
|
|
if (machine->type == MESH && machine->num_dims > 3) {
|
|
Gen_Error(0, "FATAL: maximum of 3 dimensions for a mesh exceeded");
|
|
return 0;
|
|
}
|
|
|
|
/* non-cluster machines have only one box */
|
|
if (machine->num_boxes < 0) {
|
|
machine->num_boxes = 1;
|
|
}
|
|
|
|
/* Find out the number of processors */
|
|
if (machine->type == HCUBE) {
|
|
machine->procs_per_box = 1 << machine->dim[0];
|
|
}
|
|
else {
|
|
machine->procs_per_box = machine->dim[0];
|
|
for (int cnt = 1; cnt < machine->num_dims; cnt++) {
|
|
machine->procs_per_box *= machine->dim[cnt];
|
|
}
|
|
}
|
|
|
|
/* now calculate the total number of processors */
|
|
machine->num_procs = machine->num_boxes * machine->procs_per_box;
|
|
|
|
/*
|
|
* currently, do not allow groups and clusters since the
|
|
* loops to chaco get a bit too confusing
|
|
*/
|
|
if (machine->num_boxes > 1 && prob->groups != nullptr) {
|
|
Gen_Error(0, "FATAL: groups cannot be designated for a cluster machine");
|
|
return 0;
|
|
}
|
|
|
|
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
|
|
/* Check the problem specifications */
|
|
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
|
|
if (prob->type != ELEMENTAL && prob->type != NODAL) {
|
|
Gen_Error(0, "FATAL: unknown problem type specified");
|
|
return 0;
|
|
}
|
|
|
|
if (prob->skip_checks < 0) {
|
|
prob->skip_checks = 0;
|
|
}
|
|
|
|
if (prob->face_adj < 0) {
|
|
prob->face_adj = 0;
|
|
}
|
|
|
|
/*
|
|
* using face definition of adjacencies only makes sense
|
|
* with an elemental decomposition
|
|
*/
|
|
if (prob->type != ELEMENTAL && prob->face_adj) {
|
|
Gen_Error(1, "WARNING: can only use face definition of");
|
|
Gen_Error(1, "WARNING: adjacency with elemental decomposition");
|
|
Gen_Error(1, "WARNING: face definition turned off");
|
|
prob->face_adj = 0;
|
|
}
|
|
|
|
/*
|
|
* Detecting columns and fixing their partitioning only makes sense
|
|
* with an elemental decomposition
|
|
*/
|
|
if (prob->type != ELEMENTAL && prob->fix_columns) {
|
|
Gen_Error(1, "WARNING: can only use fix columns options");
|
|
Gen_Error(1, "WARNING: with elemental decomposition");
|
|
Gen_Error(1, "WARNING: fix columns option turned off");
|
|
prob->fix_columns = 0;
|
|
}
|
|
|
|
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
|
|
/* Check the load balance parameters */
|
|
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
|
|
if (lb->type != MULTIKL && lb->type != SPECTRAL && lb->type != INERTIAL && lb->type != LINEAR &&
|
|
lb->type != RANDOM && lb->type != SCATTERED && lb->type != INFILE && lb->type != ZPINCH &&
|
|
lb->type != BRICK && lb->type != ZOLTAN_RCB && lb->type != ZOLTAN_RIB &&
|
|
lb->type != ZOLTAN_HSFC) {
|
|
Gen_Error(0, "FATAL: unknown load balance type requested");
|
|
return 0;
|
|
}
|
|
|
|
if ((sizeof(INT) == 8) && (lb->type != LINEAR && lb->type != SCATTERED)) {
|
|
Gen_Error(1, "WARNING: This mesh is using 64-bit integers. The only supported");
|
|
Gen_Error(1, " load balance methods for 64-bit integers are 'linear' or 'scattered'");
|
|
Gen_Error(1, " The load balance method will be automatically changed to 'linear'.");
|
|
lb->type = LINEAR;
|
|
}
|
|
|
|
if (lb->type == MULTIKL) {
|
|
lb->refine = KL_REFINE;
|
|
}
|
|
|
|
if (lb->refine != KL_REFINE && lb->refine != NO_REFINE) {
|
|
lb->refine = NO_REFINE; /* Default if not specified */
|
|
}
|
|
|
|
if (lb->num_sects <= 0) {
|
|
lb->num_sects = 1; /* Default if not specified */
|
|
}
|
|
|
|
if (lb->cnctd_dom < 0) {
|
|
lb->cnctd_dom = 0;
|
|
}
|
|
else if (!prob->face_adj) {
|
|
Gen_Error(1, "WARNING: can only set connected domain");
|
|
Gen_Error(1, "WARNING: when using face definition of adjacency");
|
|
Gen_Error(1, "WARNING: connected domain turned off");
|
|
lb->cnctd_dom = 0;
|
|
}
|
|
|
|
if (lb->outfile < 0) {
|
|
lb->outfile = ELB_FALSE;
|
|
}
|
|
|
|
if (lb->type == INFILE) {
|
|
if (lb->outfile) {
|
|
Gen_Error(0, "FATAL: both infile and outfile cannot be specified");
|
|
return 0;
|
|
}
|
|
|
|
if (lb->refine != NO_REFINE) {
|
|
Gen_Error(1, "WARNING: no refinement can be specified with infile");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
|
|
/* Check the eigensolver parameters */
|
|
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
|
|
if (lb->type == SPECTRAL || lb->type == MULTIKL) {
|
|
if (solver->tolerance < 0.0) {
|
|
Gen_Error(1, "WARNING: using default value for eigensolver"
|
|
" tolerance");
|
|
solver->tolerance = 1.0e-3;
|
|
}
|
|
|
|
if (solver->rqi_flag < 0) {
|
|
solver->rqi_flag = 0;
|
|
}
|
|
|
|
if (solver->vmax < 0) {
|
|
Gen_Error(1, "WARNING: no value for vmax specified,"
|
|
" using default");
|
|
solver->vmax = 200;
|
|
}
|
|
}
|
|
|
|
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
|
|
/* Check the output file name */
|
|
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
|
|
if (nemI_out_file.empty()) {
|
|
/*
|
|
* Generate the file name from the input file name and the requested
|
|
* load balance method.
|
|
*/
|
|
std::string ctemp2;
|
|
switch (machine->type) {
|
|
case MESH: ctemp2 = fmt::format("-m{}-", machine->num_procs); break;
|
|
|
|
case HCUBE: ctemp2 = fmt::format("-h{}-", machine->num_procs); break;
|
|
}
|
|
|
|
switch (lb->type) {
|
|
case MULTIKL:
|
|
case SPECTRAL:
|
|
if (lb->num_sects == 1) {
|
|
ctemp2 += "b";
|
|
}
|
|
else if (lb->num_sects == 2) {
|
|
ctemp2 += "q";
|
|
}
|
|
else if (lb->num_sects == 3) {
|
|
ctemp2 += "o";
|
|
}
|
|
|
|
break;
|
|
|
|
case INERTIAL: ctemp2 += "i"; break;
|
|
|
|
case ZPINCH: ctemp2 += "z"; break;
|
|
|
|
case BRICK: ctemp2 += "x"; break;
|
|
|
|
case ZOLTAN_RCB:
|
|
case ZOLTAN_RIB:
|
|
case ZOLTAN_HSFC: ctemp2 += "Z"; break;
|
|
|
|
case SCATTERED: ctemp2 += "s"; break;
|
|
|
|
case RANDOM: ctemp2 += "r"; break;
|
|
|
|
case LINEAR: ctemp2 += "l"; break;
|
|
}
|
|
|
|
if (lb->refine == KL_REFINE) {
|
|
ctemp2 += "KL";
|
|
}
|
|
|
|
/* Generate the complete file name */
|
|
|
|
nemI_out_file = remove_extension(exoII_inp_file);
|
|
nemI_out_file += ctemp2;
|
|
nemI_out_file += ".nemI";
|
|
} /* End "if(strlen(nemI_out_file) <= 0)" */
|
|
|
|
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
|
|
/* Check the weighting specifications */
|
|
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
|
|
if (weight->exo_filename.length() > 0) {
|
|
/* Check that a variable name and/or index was specified. */
|
|
if (weight->exo_varname.empty() && weight->exo_vindx <= 0) {
|
|
Gen_Error(0, "FATAL: must specify an index and/or a name for weighting"
|
|
" variable");
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* If a variable name and or index was specified then open the ExodusII
|
|
* file and compare the specified name against what exists in the file.
|
|
*/
|
|
int exoid;
|
|
float version;
|
|
int cpu_ws = 0;
|
|
int io_ws = 0;
|
|
if ((exoid = ex_open(weight->exo_filename.c_str(), EX_READ, &cpu_ws, &io_ws, &version)) < 0) {
|
|
std::string ctemp =
|
|
fmt::format("FATAL: failed to open ExodusII weighting file {}", weight->exo_filename);
|
|
Gen_Error(0, ctemp);
|
|
return 0;
|
|
}
|
|
|
|
int ntimes = ex_inquire_int(exoid, EX_INQ_TIME);
|
|
|
|
/* Check the time index */
|
|
if (weight->exo_tindx <= 0) {
|
|
weight->exo_tindx = 1; /* Defaults to 1 */
|
|
}
|
|
|
|
if (weight->exo_tindx > ntimes) {
|
|
std::string ctemp = fmt::format(
|
|
"FATAL: requested time index %d not available in weighting file", weight->exo_tindx);
|
|
Gen_Error(0, ctemp);
|
|
return 0;
|
|
}
|
|
|
|
ex_entity_type type;
|
|
if (prob->type == NODAL) {
|
|
type = EX_NODAL;
|
|
}
|
|
else {
|
|
type = EX_ELEM_BLOCK;
|
|
}
|
|
|
|
/*
|
|
* First check that there are variables of the requested type in the
|
|
* specified ExodusII file.
|
|
*/
|
|
int nvars;
|
|
if (ex_get_variable_param(exoid, type, &nvars) < 0) {
|
|
Gen_Error(0, "FATAL: unable to get variable params from ExodusII"
|
|
" weighting file");
|
|
return 0;
|
|
}
|
|
if (nvars <= 0) {
|
|
Gen_Error(0, "FATAL: no variables found in the ExodusII weighting file");
|
|
return 0;
|
|
}
|
|
|
|
/* Read the variable names from the requested file */
|
|
char **var_names = reinterpret_cast<char **>(malloc(nvars * sizeof(char *)));
|
|
if (!var_names) {
|
|
Gen_Error(0, "FATAL: insufficient memory");
|
|
return 0;
|
|
}
|
|
|
|
{
|
|
int max_name_length = ex_inquire_int(exoid, EX_INQ_DB_MAX_USED_NAME_LENGTH);
|
|
ex_set_max_name_length(exoid, max_name_length);
|
|
|
|
for (int cnt = 0; cnt < nvars; cnt++) {
|
|
var_names[cnt] = reinterpret_cast<char *>(malloc((max_name_length + 1) * sizeof(char)));
|
|
if (!var_names[cnt]) {
|
|
Gen_Error(0, "FATAL: insufficient memory");
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ex_get_variable_names(exoid, type, nvars, var_names) < 0) {
|
|
Gen_Error(0, "FATAL: unable to obtain variable names for weighting");
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* If there was a variable name specified but no index then get the
|
|
* index. If a variable name AND an index were specified then make
|
|
* sure they match.
|
|
*/
|
|
if (!weight->exo_varname.empty()) {
|
|
int tmp_vindx = 0;
|
|
for (int cnt = 0; cnt < nvars; cnt++) {
|
|
if (strcmp(var_names[cnt], weight->exo_varname.c_str()) == 0) {
|
|
tmp_vindx = cnt + 1;
|
|
|
|
break; /* out of "for" loop */
|
|
}
|
|
}
|
|
|
|
if (weight->exo_vindx <= 0) {
|
|
weight->exo_vindx = tmp_vindx;
|
|
}
|
|
else if (weight->exo_vindx != tmp_vindx) {
|
|
Gen_Error(0, "FATAL: requested weight variable index doesn't match"
|
|
" the variable name in the ExodusII file");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* Free up memory */
|
|
for (int cnt = 0; cnt < nvars; cnt++) {
|
|
free(var_names[cnt]);
|
|
}
|
|
free(var_names);
|
|
|
|
/*
|
|
* If there is still no valid index then the variable name does
|
|
* not exist in the specified file.
|
|
*/
|
|
if (weight->exo_vindx <= 0) {
|
|
std::string ctemp = fmt::format(
|
|
"FATAL: requested weighting variable {} not found in ExodusII file", weight->exo_varname);
|
|
Gen_Error(0, ctemp);
|
|
return 0;
|
|
}
|
|
|
|
if (nvars < weight->exo_vindx) {
|
|
Gen_Error(0, "FATAL: requested variable index is larger than number in"
|
|
" ExodusII weighting file");
|
|
return 0;
|
|
}
|
|
|
|
ex_close(exoid);
|
|
|
|
} /* End "if(strlen(weight->exo_filename) > 0)" */
|
|
|
|
if ((weight->type & EL_BLK) && (weight->ow_read)) {
|
|
if (weight->elemblk.size() > 1) {
|
|
/* start by sorting the two arrays by the element block number */
|
|
sort2(weight->elemblk.size(), weight->elemblk.data(), weight->elemblk_wgt.data());
|
|
|
|
/* now loop through, and make sure that we don't have multiple values */
|
|
for (int cnt = 1; cnt < (int)weight->elemblk.size(); cnt++) {
|
|
if (weight->elemblk[cnt] == weight->elemblk[cnt - 1]) {
|
|
std::string ctemp =
|
|
fmt::format("WARNING: multiple weight specified for block {}", weight->elemblk[cnt]);
|
|
Gen_Error(1, ctemp);
|
|
}
|
|
}
|
|
}
|
|
} /* if ((weight->type & EL_BLK) && (weight->ow_read)) */
|
|
|
|
return 1;
|
|
|
|
} /*-------------------End check_inp_specs()-----------------*/
|
|
|
|
namespace {
|
|
/* Parse comma separate list into words.
|
|
Copyright (C) 1996, 1997, 1999, 2004 Free Software Foundation, Inc.
|
|
This file is part of the GNU C Library.
|
|
Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
|
|
The GNU C Library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Lesser General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2.1 of the License, or (at your option) any later version.
|
|
The GNU C Library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Lesser General Public License for more details.
|
|
You should have received a copy of the GNU Lesser General Public
|
|
License along with the GNU C Library; if not, write to the Free
|
|
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
|
02111-1307 USA. */
|
|
|
|
const char *my_strchrnul(const char *s, int c)
|
|
{
|
|
const char *result = strchr(s, c);
|
|
|
|
if (!result) {
|
|
result = (char *)s + strlen(s);
|
|
}
|
|
|
|
return (result);
|
|
}
|
|
|
|
/* Parse comma separated suboption from *OPTIONP and match against
|
|
strings in TOKENS. If found return index and set *VALUEP to
|
|
optional value introduced by an equal sign. If the suboption is
|
|
not part of TOKENS return in *VALUEP beginning of unknown
|
|
suboption. On exit *OPTIONP is set to the beginning of the next
|
|
token or at the terminating NUL character. */
|
|
int my_getsubopt(char **optionp, char *const *tokens, char **valuep)
|
|
{
|
|
if (**optionp == '\0')
|
|
return -1;
|
|
|
|
/* Find end of next token. */
|
|
char *endp = (char *)my_strchrnul(*optionp, ',');
|
|
|
|
/* Find start of value. */
|
|
char *vstart = (char *)memchr(*optionp, '=', endp - *optionp);
|
|
if (vstart == nullptr)
|
|
vstart = endp;
|
|
|
|
/* Try to match the characters between *OPTIONP and VSTART against
|
|
one of the TOKENS. */
|
|
for (int cnt = 0; tokens[cnt] != nullptr; ++cnt)
|
|
if (strncmp(*optionp, tokens[cnt], vstart - *optionp) == 0 &&
|
|
tokens[cnt][vstart - *optionp] == '\0') {
|
|
/* We found the current option in TOKENS. */
|
|
*valuep = vstart != endp ? vstart + 1 : nullptr;
|
|
|
|
if (*endp != '\0')
|
|
*endp++ = '\0';
|
|
*optionp = endp;
|
|
|
|
return cnt;
|
|
}
|
|
|
|
/* The current suboption does not match any option. */
|
|
*valuep = *optionp;
|
|
|
|
if (*endp != '\0')
|
|
*endp++ = '\0';
|
|
*optionp = endp;
|
|
|
|
return -1;
|
|
}
|
|
|
|
void print_usage()
|
|
{
|
|
fmt::print("\nusage:\t{} [-h] [<-n|-e> -o <output file>"
|
|
" -m <machine description>\n"
|
|
"\t -l <load bal description> -s <eigen solver specs>\n"
|
|
"\t -w <weighting options> -g <group list> -f]\n"
|
|
"\t [-a <ascii file>] exoII_file\n\n"
|
|
" -32\t\tforce use of 32-bit integers\n"
|
|
" -64\t\tforce use of 64-bit integers\n"
|
|
" -n\t\tperform a nodal based load balance\n"
|
|
" -e\t\tperform an elemental based load balance\n"
|
|
" -o NemI file\toutput NemesisI load-balance file name\n"
|
|
" -m mach desc\tdescription of the machine to load balance for\n"
|
|
" -l LB data\tload balance specifications\n"
|
|
" -s Eigen specs\tEigen solver specifications\n"
|
|
" -w weighting\tweighting specs for load balance\n"
|
|
" -g groupings\tgrouping specifications for load balance\n"
|
|
" -f\t\tuse face definition of adjacency\n"
|
|
" -p\t\tuse partial definition of adjacency: \n"
|
|
" \t\trequire only 3 matching quad face nodes\n"
|
|
" -C\tavoid splitting vertical element columns\n"
|
|
" \t\tacross partitions\n"
|
|
" -h\t\tusage information\n"
|
|
" -a ascii file\tget info from ascii input file name\n",
|
|
UTIL_NAME);
|
|
}
|
|
} // namespace
|
|
|