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.
338 lines
11 KiB
338 lines
11 KiB
2 years ago
|
/*
|
||
|
* Copyright(C) 1999-2021, 2023 National Technology & Engineering Solutions
|
||
|
* of Sandia, LLC (NTESS). Under the terms of Contract DE-NA0003525 with
|
||
|
* NTESS, the U.S. Government retains certain rights in this software.
|
||
|
*
|
||
|
* See packages/seacas/LICENSE for details
|
||
|
*/
|
||
|
|
||
|
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||
|
*----------------------------------------------------------------------------
|
||
|
* Functions contained in this file:
|
||
|
*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
|
||
|
|
||
|
#include "elb.h" // for Problem_Description, etc
|
||
|
#include "elb_elem.h" // for elem_name_from_enum
|
||
|
#include "elb_err.h" // for Gen_Error
|
||
|
#include "elb_groups.h"
|
||
|
#include "elb_util.h"
|
||
|
#include <cstdio> // for sscanf, nullptr
|
||
|
#include <cstdlib> // for free, malloc
|
||
|
#include <cstring> // for strchr, strlen
|
||
|
#include <fmt/format.h>
|
||
|
#include <vector> // for vector
|
||
|
|
||
|
/*****************************************************************************/
|
||
|
namespace {
|
||
|
template <typename INT>
|
||
|
void scandescriptor(const char *d, INT *blkids, int n, int nblks, Problem_Description *prob);
|
||
|
template <typename INT>
|
||
|
void chgrp(int grp_id, size_t blk, INT *blkids, int nblks, Problem_Description *prob);
|
||
|
} // namespace
|
||
|
extern int ilog2i(size_t n);
|
||
|
|
||
|
/*****************************************************************************/
|
||
|
|
||
|
/*****************************************************************************/
|
||
|
/*****************************************************************************/
|
||
|
/*****************************************************************************/
|
||
|
/* Function parse_groups() begins:
|
||
|
*----------------------------------------------------------------------------
|
||
|
* This function will parse the group designation string and set up which
|
||
|
* element blocks will be in which groups.
|
||
|
*
|
||
|
* the group designator prob->groups follows these rules:
|
||
|
* - Blocks are grouped using the slash "/" character.
|
||
|
* - Ids are separated with white space, comma, or by the hyphen "-" character.
|
||
|
* - Any blocks not included in the list, are added to a separate group.
|
||
|
* - Duplicates in the list are permitted, but the last group to which a
|
||
|
* block is placed is where the block will go.
|
||
|
* - Block IDs not in the exodus file are quietly ignored.
|
||
|
*
|
||
|
* Examples.
|
||
|
* Assume block IDs= 1-20 31-45
|
||
|
*
|
||
|
* descriptor group1 group2 group3
|
||
|
* - "1-20" 1-20 31-45
|
||
|
* - "30-45 3/ 10-12" 3, 30-45 10,11,12 1,2,4-20
|
||
|
* - "1-20/40-45/5-10 21-41 1-4,11-20 42,43,45 5-10 31-41
|
||
|
*
|
||
|
*****************************************************************************/
|
||
|
template int parse_groups(Mesh_Description<int> *mesh, Problem_Description *prob);
|
||
|
template int parse_groups(Mesh_Description<int64_t> *mesh, Problem_Description *prob);
|
||
|
|
||
|
template <typename INT> int parse_groups(Mesh_Description<INT> *mesh, Problem_Description *prob)
|
||
|
{
|
||
|
/* prepare the group number array, and copy the element block counts */
|
||
|
prob->group_no.resize(mesh->num_el_blks, -1);
|
||
|
|
||
|
/* convert any comma's to blank spaces in the designator string */
|
||
|
for (size_t i = 0; i < strlen(prob->groups); i++) {
|
||
|
if (prob->groups[i] == ',') {
|
||
|
prob->groups[i] = ' ';
|
||
|
|
||
|
/* fill in the group identifier for each block */
|
||
|
}
|
||
|
}
|
||
|
char *id = prob->groups;
|
||
|
size_t i = 0;
|
||
|
do {
|
||
|
if (*id == '/') {
|
||
|
id++;
|
||
|
}
|
||
|
scandescriptor(id, mesh->eb_ids.data(), i, mesh->num_el_blks, prob);
|
||
|
id = strchr(id, '/');
|
||
|
i++;
|
||
|
} while (id != nullptr);
|
||
|
int last = i;
|
||
|
|
||
|
/* set any remaining blocks to new group */
|
||
|
int found = 0;
|
||
|
for (i = 0; i < mesh->num_el_blks; i++) {
|
||
|
if (prob->group_no[i] < 0) {
|
||
|
prob->group_no[i] = last;
|
||
|
found = 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (found) {
|
||
|
last++;
|
||
|
}
|
||
|
|
||
|
prob->num_groups = last;
|
||
|
|
||
|
{
|
||
|
size_t first_el = 0;
|
||
|
fmt::print("\nNumber of blocks: {}\n", mesh->num_el_blks);
|
||
|
fmt::print("Block ID and associated groups:\n");
|
||
|
fmt::print(" block #elems group type\n");
|
||
|
for (i = 0; i < mesh->num_el_blks; i++) {
|
||
|
fmt::print("{:8d}{:8d}{:8d}{:8s}\n", (size_t)mesh->eb_ids[i], (size_t)mesh->eb_cnts[i],
|
||
|
prob->group_no[i], elem_name_from_enum(mesh->elem_type[first_el]));
|
||
|
first_el += mesh->eb_cnts[i];
|
||
|
}
|
||
|
fmt::print("There are {} groups of blocks\n", prob->num_groups);
|
||
|
}
|
||
|
|
||
|
/* finished with the group designator string */
|
||
|
delete[] prob->groups;
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
/*****************************************************************************/
|
||
|
/*****************************************************************************/
|
||
|
/*****************************************************************************/
|
||
|
/* Function get_group_info() begins:
|
||
|
*----------------------------------------------------------------------------
|
||
|
* This function will get the information that is needed to break up the
|
||
|
* groups before passing them to Chaco.
|
||
|
* It generates the following:
|
||
|
* - an array of with the group number for each element
|
||
|
* - an array of elements per group
|
||
|
* - an array of processors to be used per group
|
||
|
* - the max number of vertecies and adjacencies for all of the groups
|
||
|
* this value is needed so that arrays used to pass information
|
||
|
* to Chaco can be allocated
|
||
|
*****************************************************************************/
|
||
|
template int get_group_info(Machine_Description *machine, Problem_Description *prob,
|
||
|
Mesh_Description<int> *mesh, Graph_Description<int> *graph,
|
||
|
int elem2grp[], int nprocg[], int nelemg[], size_t *max_vtx,
|
||
|
size_t *max_adj);
|
||
|
template int get_group_info(Machine_Description *machine, Problem_Description *prob,
|
||
|
Mesh_Description<int64_t> *mesh, Graph_Description<int64_t> *graph,
|
||
|
int elem2grp[], int nprocg[], int nelemg[], size_t *max_vtx,
|
||
|
size_t *max_adj);
|
||
|
|
||
|
template <typename INT>
|
||
|
int get_group_info(Machine_Description *machine, Problem_Description *prob,
|
||
|
Mesh_Description<INT> *mesh, Graph_Description<INT> *graph, int elem2grp[],
|
||
|
int nprocg[], int nelemg[], size_t *max_vtx, size_t *max_adj)
|
||
|
{
|
||
|
int nproc = 0;
|
||
|
std::vector<int> nadj_per_grp;
|
||
|
|
||
|
/* allocate array to hold adjacency counts, if necessary */
|
||
|
if (prob->alloc_graph == ELB_TRUE) {
|
||
|
nadj_per_grp.resize(prob->num_groups);
|
||
|
}
|
||
|
|
||
|
/* initialize the group counts arrays */
|
||
|
for (int i = 0; i < prob->num_groups; i++) {
|
||
|
nelemg[i] = 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* fill the vertex2proc array with the group number of the individual
|
||
|
* elements, calculate how many elements in each group and determine
|
||
|
* how many adjacencies are in each group (if necessary).
|
||
|
*/
|
||
|
INT sum = 0;
|
||
|
int iblk = 0;
|
||
|
for (size_t i = 0; i < prob->num_vertices; i++) {
|
||
|
|
||
|
/* figure out which element block this is */
|
||
|
if (sum == mesh->eb_cnts[iblk]) {
|
||
|
sum = 0;
|
||
|
iblk++;
|
||
|
}
|
||
|
sum++;
|
||
|
|
||
|
/*
|
||
|
* use negative numbers to specify the groups, in order to
|
||
|
* avoid having a problem with group 0, add 1 to the group
|
||
|
* number
|
||
|
*/
|
||
|
elem2grp[i] = -(prob->group_no[iblk] + 1);
|
||
|
|
||
|
nelemg[prob->group_no[iblk]]++;
|
||
|
|
||
|
if (prob->alloc_graph == ELB_TRUE) {
|
||
|
nadj_per_grp[prob->group_no[iblk]] += graph->start[i + 1] - graph->start[i];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* calculate how many processors to use for each group
|
||
|
* using method from the materials group, haven't really checked it
|
||
|
*/
|
||
|
if (machine->type == MESH) {
|
||
|
nproc = machine->procs_per_box;
|
||
|
}
|
||
|
else if (machine->type == HCUBE) {
|
||
|
nproc = ilog2i(machine->procs_per_box);
|
||
|
}
|
||
|
for (int i = 0; i < prob->num_groups; i++) {
|
||
|
nprocg[i] = int((nproc * (nelemg[i] + 0.5F)) / static_cast<float>(prob->num_vertices));
|
||
|
if (nelemg[i] && !nprocg[i]) {
|
||
|
nprocg[i] = 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* check to see if correct number of processors have been allocated
|
||
|
* and get the maximum number of vertices
|
||
|
*/
|
||
|
sum = 0;
|
||
|
size_t j = 0;
|
||
|
*max_vtx = 0;
|
||
|
*max_adj = 0;
|
||
|
for (int i = 0; i < prob->num_groups; i++) {
|
||
|
sum += nprocg[i];
|
||
|
if (nprocg[i] > nprocg[j]) {
|
||
|
j = i;
|
||
|
*max_vtx = nelemg[j]; /* most processors implies most elements */
|
||
|
}
|
||
|
|
||
|
/* determine how large to make temporary arrays */
|
||
|
if (static_cast<size_t>(nelemg[i]) > *max_vtx) {
|
||
|
*max_vtx = nelemg[i];
|
||
|
}
|
||
|
if (prob->alloc_graph == ELB_TRUE) {
|
||
|
if (static_cast<size_t>(nadj_per_grp[i]) > *max_adj) {
|
||
|
*max_adj = nadj_per_grp[i];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (sum != nproc) {
|
||
|
/* correct group with most processors (j determined above) */
|
||
|
nprocg[j] -= (sum - nproc);
|
||
|
if (nprocg[j] <= 0) {
|
||
|
Gen_Error(0, "Unable to balance # processors in get_group_info().");
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
fmt::print("Load balance information\n");
|
||
|
for (int i = 0; i < prob->num_groups; i++) {
|
||
|
fmt::print("group[{}] #elements={:10d} #proc={}\n", i, nelemg[i], nprocg[i]);
|
||
|
}
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
/**********************************************************************/
|
||
|
namespace {
|
||
|
template <typename INT>
|
||
|
void scandescriptor(const char *d, INT *blkids, int n, int nblks, Problem_Description *prob)
|
||
|
{
|
||
|
/* reads the descriptor up to the next "/" character. interprets the
|
||
|
descriptor. The ranges specified in the descriptor are then stored
|
||
|
in the grp array. */
|
||
|
int i;
|
||
|
int last = 0; /* last integer read */
|
||
|
int stop; /* stop value in a string range */
|
||
|
int qn; /* number of bytes read */
|
||
|
int c; /* integer index when spanning a range */
|
||
|
|
||
|
const char *p = d;
|
||
|
while (*p != '/' && *p != 0) {
|
||
|
int q = sscanf(p, "%d%n", &i, &qn);
|
||
|
if (q == 0 || i < 0) {
|
||
|
if (p[qn - 1] == '/') {
|
||
|
return;
|
||
|
}
|
||
|
if (i < 0) {
|
||
|
stop = -i;
|
||
|
for (c = last; c <= stop; c++) {
|
||
|
chgrp(n, c, blkids, nblks, prob);
|
||
|
}
|
||
|
}
|
||
|
else if (p[qn - 1] == '-') {
|
||
|
p += qn;
|
||
|
sscanf(p, "%d%n", &stop, &qn);
|
||
|
for (c = last; c <= stop; c++) {
|
||
|
chgrp(n, c, blkids, nblks, prob);
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
/* check for minus sign */
|
||
|
for (c = 0; c < qn; c++) {
|
||
|
if (p[c] == '-') {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (c < qn) {
|
||
|
p += qn;
|
||
|
sscanf(p, "%d%n", &stop, &qn);
|
||
|
for (c = last; c <= stop; c++) {
|
||
|
chgrp(n, c, blkids, nblks, prob);
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
fmt::print("Error reading descriptor '{}'\n", d);
|
||
|
fmt::print(" ");
|
||
|
for (c = 0; c < qn; c++) {
|
||
|
fmt::print(" ");
|
||
|
}
|
||
|
fmt::print("^\n");
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
last = i;
|
||
|
}
|
||
|
chgrp(n, i, blkids, nblks, prob);
|
||
|
p += qn;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**********************************************************************/
|
||
|
/* changes the grp[] entry corresponding to block=blk to the value grp_id
|
||
|
* It uses the extern variables "nblks", "grp" and "blkid". only grp
|
||
|
* is altered.
|
||
|
*
|
||
|
* Not very efficient. To find the blk, it loops through all blks.
|
||
|
*/
|
||
|
template <typename INT>
|
||
|
void chgrp(int grp_id, size_t blk, INT *blkids, int nblks, Problem_Description *prob)
|
||
|
{
|
||
|
for (int j = 0; j < nblks; j++) {
|
||
|
if (blkids[j] == (INT)blk) {
|
||
|
prob->group_no[j] = grp_id;
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
} // namespace
|