/* * 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 #else #include "getopt.h" // for getopt #endif #include "scopeguard.h" #include // for size_t #ifndef _XOPEN_SOURCE #define _XOPEN_SOURCE 500 #endif #include // for malloc, exit, free #include // for strcmp, strstr, strchr, etc #include // 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 *lb, Problem_Description *prob, Solver_Description *solver, Weight_Description *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 *lb, Problem_Description *prob, Solver_Description *solver, Weight_Description *weight); template 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 *lb, /* Structure for load balance description */ Problem_Description *prob, /* Structure for various problem params */ Solver_Description *solver, /* Structure for eigen solver params */ Weight_Description *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(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 *lb, Problem_Description *problem, Solver_Description *solver, Weight_Description *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 *lb, Problem_Description *problem, Solver_Description *solver, Weight_Description *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 *lb, Problem_Description *problem, Solver_Description *solver, Weight_Description *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(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 *lb, Problem_Description *prob, Solver_Description *solver, Weight_Description *weight); template int check_inp_specs(std::string &exoII_inp_file, std::string &nemI_out_file, Machine_Description *machine, LB_Description *lb, Problem_Description *prob, Solver_Description *solver, Weight_Description *weight); template int check_inp_specs(std::string &exoII_inp_file, std::string &nemI_out_file, Machine_Description *machine, LB_Description *lb, Problem_Description *prob, Solver_Description *solver, Weight_Description *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(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(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 , 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 " " -m \n" "\t -l -s \n" "\t -w -g -f]\n" "\t [-a ] 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