int write_vis(std::string &nemI_out_file, std::string &exoII_inp_file, Machine_Description* machine, Problem_Description* prob, Mesh_Description<INT>* mesh, LB_Description<INT>* lb) { int exid_vis, exid_inp; char title[MAX_LINE_LENGTH+1]; const char *coord_names[] = {"X", "Y", "Z"}; /*-----------------------------Execution Begins------------------------------*/ /* Generate the file name for the visualization file */ std::string vis_file_name = remove_extension(nemI_out_file); vis_file_name += "-vis.exoII"; /* Generate the title for the file */ strcpy(title, UTIL_NAME); strcat(title, " "); strcat(title, ELB_VERSION); strcat(title, " load balance visualization file"); /* * If the vis technique is to be by element block then calculate the * number of element blocks. */ int vis_nelem_blks; if(prob->type == ELEMENTAL) vis_nelem_blks = machine->num_procs; else vis_nelem_blks = machine->num_procs + 1; /* Create the ExodusII file */ std::cout << "Outputting load balance visualization file " << vis_file_name.c_str() << "\n"; int cpu_ws = 0; int io_ws = 0; int mode = EX_CLOBBER; if (prob->int64db|prob->int64api) { mode |= EX_NETCDF4|EX_NOCLASSIC|prob->int64db|prob->int64api; } if((exid_vis=ex_create(vis_file_name.c_str(), mode, &cpu_ws, &io_ws)) < 0) { Gen_Error(0, "fatal: unable to create visualization output file"); return 0; } ON_BLOCK_EXIT(ex_close, exid_vis); /* * Open the original input ExodusII file, read the values for the * element blocks and output them to the visualization file. */ int icpu_ws=0; int iio_ws=0; float vers=0.0; mode = EX_READ | prob->int64api; if((exid_inp=ex_open(exoII_inp_file.c_str(), mode, &icpu_ws, &iio_ws, &vers)) < 0) { Gen_Error(0, "fatal: unable to open input ExodusII file"); return 0; } ON_BLOCK_EXIT(ex_close, exid_inp); char **elem_type = (char**)array_alloc(2, mesh->num_el_blks, MAX_STR_LENGTH+1, sizeof(char)); if(!elem_type) { Gen_Error(0, "fatal: insufficient memory"); return 0; } ON_BLOCK_EXIT(free, elem_type); std::vector<INT> el_blk_ids(mesh->num_el_blks); std::vector<INT> el_cnt_blk(mesh->num_el_blks); std::vector<INT> node_pel_blk(mesh->num_el_blks); std::vector<INT> nattr_el_blk(mesh->num_el_blks); if(ex_get_elem_blk_ids(exid_inp, TOPTR(el_blk_ids)) < 0) { Gen_Error(0, "fatal: unable to get element block IDs"); return 0; } int acc_vis = ELB_TRUE; // Output a different element block per processor if (prob->vis_out == 2) acc_vis = ELB_FALSE; // Output a nodal/element variable showing processor size_t nsize = 0; /* * Find out if the mesh consists of mixed elements. If not then * element blocks will be used to visualize the partitioning. Otherwise * nodal/element results will be used. */ for(size_t ecnt=0; ecnt < mesh->num_el_blks; ecnt++) { if(ex_get_elem_block(exid_inp, el_blk_ids[ecnt], elem_type[ecnt], &el_cnt_blk[ecnt], &node_pel_blk[ecnt], &nattr_el_blk[ecnt]) < 0) { Gen_Error(0, "fatal: unable to get element block parameters"); return 0; } nsize += el_cnt_blk[ecnt]*node_pel_blk[ecnt]; if(strcmp(elem_type[0], elem_type[ecnt]) == 0) { if(node_pel_blk[0] != node_pel_blk[ecnt]) acc_vis = ELB_FALSE; } else acc_vis = ELB_FALSE; } if(acc_vis == ELB_TRUE) { /* Output the initial information */ if(ex_put_init(exid_vis, title, mesh->num_dims, mesh->num_nodes, mesh->num_elems, vis_nelem_blks, 0, 0) < 0) { Gen_Error(0, "fatal: unable to output initial params to vis file"); return 0; } /* Output the nodal coordinates */ float *xptr = nullptr; float *yptr = nullptr; float *zptr = nullptr; switch(mesh->num_dims) { case 3: zptr = (mesh->coords) + 2*mesh->num_nodes; /* FALLTHRU */ case 2: yptr = (mesh->coords) + mesh->num_nodes; /* FALLTHRU */ case 1: xptr = mesh->coords; } if(ex_put_coord(exid_vis, xptr, yptr, zptr) < 0) { Gen_Error(0, "fatal: unable to output coords to vis file"); return 0; } if(ex_put_coord_names(exid_vis, (char**)coord_names) < 0) { Gen_Error(0, "fatal: unable to output coordinate names"); return 0; } std::vector<INT> elem_block(mesh->num_elems); std::vector<INT> elem_map(mesh->num_elems); std::vector<INT> tmp_connect(nsize); for(size_t ecnt=0; ecnt < mesh->num_elems; ecnt++) { elem_map[ecnt] = ecnt+1; if(prob->type == ELEMENTAL) elem_block[ecnt] = lb->vertex2proc[ecnt]; else { int proc = lb->vertex2proc[mesh->connect[ecnt][0]]; int nnodes = get_elem_info(NNODES, mesh->elem_type[ecnt]); elem_block[ecnt] = proc; for(int ncnt=1; ncnt < nnodes; ncnt++) { if(lb->vertex2proc[mesh->connect[ecnt][ncnt]] != proc) { elem_block[ecnt] = machine->num_procs; break; } } } } int ccnt = 0; std::vector<INT> vis_el_blk_ptr(vis_nelem_blks+1); for(INT bcnt=0; bcnt < vis_nelem_blks; bcnt++) { vis_el_blk_ptr[bcnt] = ccnt; int pos = 0; int old_pos = 0; INT* el_ptr = TOPTR(elem_block); size_t ecnt = mesh->num_elems; while(pos != -1) { pos = in_list(bcnt, ecnt, el_ptr); if(pos != -1) { old_pos += pos + 1; ecnt = mesh->num_elems - old_pos; el_ptr = TOPTR(elem_block) + old_pos; int nnodes = get_elem_info(NNODES, mesh->elem_type[old_pos-1]); for(int ncnt=0; ncnt < nnodes; ncnt++) tmp_connect[ccnt++] = mesh->connect[old_pos-1][ncnt] + 1; } } } vis_el_blk_ptr[vis_nelem_blks] = ccnt; /* Output the element map */ if(ex_put_map(exid_vis, TOPTR(elem_map)) < 0) { Gen_Error(0, "fatal: unable to output element number map"); return 0; } /* Output the visualization element blocks */ for(int bcnt=0; bcnt < vis_nelem_blks; bcnt++) { /* * Note this assumes all the blocks contain the same type * element. */ int ecnt = (vis_el_blk_ptr[bcnt+1]-vis_el_blk_ptr[bcnt])/node_pel_blk[0]; if(ex_put_elem_block(exid_vis, bcnt+1, elem_type[0], ecnt, node_pel_blk[0], 0) < 0) { Gen_Error(0, "fatal: unable to output element block params"); return 0; } /* Output the connectivity */ if(ex_put_elem_conn(exid_vis, bcnt+1, &tmp_connect[vis_el_blk_ptr[bcnt]]) < 0) { Gen_Error(0, "fatal: unable to output element connectivity"); return 0; } } } else { /* For nodal/element results visualization of the partioning. */ // Copy the mesh portion to the vis file. ex_copy(exid_inp, exid_vis); /* Set up the file for nodal/element results */ float time_val = 0.0; if(ex_put_time(exid_vis, 1, &time_val) < 0) { Gen_Error(0, "fatal: unable to output time to vis file"); return 0; } const char *var_names[] = {"proc"}; if(prob->type == NODAL) { /* Allocate memory for the nodal values */ std::vector<float> proc_vals(mesh->num_nodes); if(ex_put_variable_param(exid_vis, EX_NODAL, 1) < 0) { Gen_Error(0, "fatal: unable to output var params to vis file"); return 0; } if(ex_put_variable_names(exid_vis, EX_NODAL, 1, (char**)var_names) < 0) { Gen_Error(0, "fatal: unable to output variable name"); return 0; } /* Do some problem specific assignment */ for(size_t ncnt=0; ncnt < mesh->num_nodes; ncnt++) proc_vals[ncnt] = lb->vertex2proc[ncnt]; for(int pcnt=0; pcnt < machine->num_procs; pcnt++) { for(auto & elem : lb->bor_nodes[pcnt]) proc_vals[elem] = machine->num_procs + 1; } /* Output the nodal variables */ if(ex_put_nodal_var(exid_vis, 1, 1, mesh->num_nodes, TOPTR(proc_vals)) < 0) { Gen_Error(0, "fatal: unable to output nodal variables"); return 0; } } else if(prob->type == ELEMENTAL) { /* Allocate memory for the element values */ std::vector<float> proc_vals(mesh->num_elems); if(ex_put_variable_param(exid_vis, EX_ELEM_BLOCK, 1) < 0) { Gen_Error(0, "fatal: unable to output var params to vis file"); return 0; } if(ex_put_variable_names(exid_vis, EX_ELEM_BLOCK, 1, (char**)var_names) < 0) { Gen_Error(0, "fatal: unable to output variable name"); return 0; } /* Do some problem specific assignment */ for(int proc=0; proc < machine->num_procs; proc++) { for (size_t e = 0; e < lb->int_elems[proc].size(); e++) { size_t ecnt = lb->int_elems[proc][e]; proc_vals[ecnt] = proc; } for (size_t e = 0; e < lb->bor_elems[proc].size(); e++) { size_t ecnt = lb->bor_elems[proc][e]; proc_vals[ecnt] = proc; } } /* Output the element variables */ size_t offset = 0; for (size_t i=0; i < mesh->num_el_blks; i++) { if(ex_put_var(exid_vis, 1, EX_ELEM_BLOCK, 1, el_blk_ids[i], el_cnt_blk[i], &proc_vals[offset]) < 0) { Gen_Error(0, "fatal: unable to output nodal variables"); return 0; } offset += el_cnt_blk[i]; } } } return 1; } /*---------------------------End write_vis()-------------------------------*/
static int find_adjacency(int Proc, MESH_INFO_PTR mesh, int **sur_elem, int *nsurnd, int max_nsur) { /* Local declarations. */ int i, iblk, nsides, ielem, nscnt, inode, entry; int side_cnt, nnodes, sid; int side_nodes[MAX_SIDE_NODES], mirror_nodes[MAX_SIDE_NODES]; int *hold_elem, *pt_list, nhold, nelem; ELEM_INFO_PTR elements = mesh->elements; int *eb_etype; /***************************** BEGIN EXECUTION ******************************/ /* * Use face definition of adjacencies. So, one elements that are * connected by an entire face will be considered adjacent. This * is temporary, and will be expanded. This will make determining * off processor adjacencies much easier. * * Adjacency info for an element's side i will be stored in the (i-1) entry * of adj array for the element. Sides without adjacencies will have -1 * as the adj array entries for those sides. This system allows easy * identification of side ids for recomputing element comm maps after * migration. */ eb_etype = mesh->eb_etypes; /* allocate space to hold info about surounding elements */ pt_list = (int *) malloc(2 * max_nsur * sizeof(int)); if(!pt_list) { Gen_Error(0, "fatal: insufficient memory"); return 0; } hold_elem = pt_list + max_nsur; for (ielem = 0; ielem < mesh->num_elems; ielem++) { iblk = elements[ielem].elem_blk; /* exclude circle and sphere elements from graph */ if (mesh->eb_nnodes[iblk] > 1) { if ((nsides = get_elem_info(NSIDES, (E_Type) (eb_etype[iblk]), 0)) < 0) { Gen_Error(0, "fatal: could not get element information"); return 0; } elements[ielem].adj = (int *) malloc(nsides*sizeof(int)); elements[ielem].adj_proc = (int *) malloc(nsides*sizeof(int)); elements[ielem].edge_wgt = (float *) malloc(nsides*sizeof(float)); if(!(elements[ielem].adj) || !(elements[ielem].edge_wgt) || !(elements[ielem].adj_proc)) { Gen_Error(0, "fatal: insufficient memory"); return 0; } /* NOTE: nadj set in read_elem_info in case graph not generated */ elements[ielem].adj_len = nsides; /* Initialize adjacency entries to -1 for each side. */ for (nscnt = 0; nscnt < nsides; nscnt++) { elements[ielem].adj[nscnt] = -1; elements[ielem].adj_proc[nscnt] = -1; elements[ielem].edge_wgt[nscnt] = 0; } /* check each side of this element */ for (nscnt = 0; nscnt < nsides; nscnt++) { /* get the list of nodes on this side set */ side_cnt = ss_to_node_list((E_Type) (eb_etype[iblk]), elements[ielem].connect, (nscnt+1), side_nodes); /* * now I need to determine how many side set nodes I * need to use to determine if there is an element * connected to this side. * * 2-D - need two nodes, so find one intersection * 3-D - need three nodes, so find two intersections * NOTE: must check to make sure that this number is not * larger than the number of nodes on the sides (ie - SHELL). */ nnodes = mesh->num_dims; if (side_cnt < nnodes) nnodes = side_cnt; nnodes--; /* decrement to find the number of intersections */ nelem = 0; /* reset this in case no intersections are needed */ /* copy the first array into temp storage */ nhold = nsurnd[side_nodes[0]]; for (i = 0; i < nhold; i++) hold_elem[i] = sur_elem[side_nodes[0]][i]; for (inode = 0; inode < nnodes; inode++) { nelem = find_inter(hold_elem, sur_elem[side_nodes[(inode+1)]], nhold, nsurnd[side_nodes[(inode+1)]], 2, pt_list); if (nelem < 2) break; else { nhold = nelem; for (i = 0; i < nelem; i++) hold_elem[i] = hold_elem[pt_list[i]]; } } /* * if there is an element on this side of ielem, then there * will be at least two elements in the intersection (one * will be ielem) */ if (nelem > 1) { /* * now go through and check each element in the list * to see if it is different than ielem. */ for(i=0; i < nelem; i++) { entry = hold_elem[i]; if(entry != ielem) { /* * get the side id of entry. Make sure that ielem is * trying to communicate to a valid side of elem */ side_cnt = get_ss_mirror((E_Type) (eb_etype[iblk]), side_nodes, (nscnt+1), mirror_nodes); /* * in order to get the correct side order for elem, * get the mirror of the side of ielem */ sid = get_side_id((E_Type) (eb_etype[elements[entry].elem_blk]), elements[entry].connect, side_cnt, mirror_nodes); if (sid > 0) { (elements[ielem].nadj)++; /* * store the adjacency info in the entry for this side. */ elements[ielem].adj[nscnt] = entry; elements[ielem].adj_proc[nscnt] = Proc; /* * the edge weight is the number of nodes in the * connecting face */ elements[ielem].edge_wgt[nscnt] = (float) get_elem_info(NSNODES, (E_Type) (eb_etype[iblk]), (nscnt+1)); } /* End: "if (sid > 0)" */ else if (sid < 0) { Gen_Error(0, "fatal: could not find side id"); return 0; } } /* End: "if(ielem != entry)" */ } /* End: "for(i=0; i < nelem; i++)" */ } /* End: "if (nelem > 1)" */ } /* End: "for (nscnt = 0; ...)" */ } /* End: "if (nnode > 1)" */ } /* End: "for (ielem=0; ...)" */ free(pt_list); return 1; }
static int read_comm_map_info(int pexoid, int Proc, PROB_INFO_PTR prob, MESH_INFO_PTR mesh) { /* Local declarations. */ char *yo = "read_comm_map_info"; int ielem, imap, loc_elem, iblk, max_len, offset, index; int nnodei, nnodeb, nnodee, nelemi, nelemb, nncmap; int *int_elem, *bor_elem; int *proc_ids; int *gids, *my_procs, *recv_procs; int ierr, nrecv; int msg = 200; int sid; ELEM_INFO_PTR elements = mesh->elements; ZOLTAN_COMM_OBJ *comm_obj; E_Type etype; /***************************** BEGIN EXECUTION ******************************/ DEBUG_TRACE_START(Proc, yo); if (ne_get_loadbal_param(pexoid, &nnodei, &nnodeb, &nnodee, &nelemi, &nelemb, &nncmap, &(mesh->necmap), Proc) < 0) { Gen_Error(0, "fatal: Error returned from ne_get_loadbal_param"); return 0; } /* * get the list of the border elements in order to set * the border flag in the element structures */ int_elem = (int *) malloc ((nelemi + nelemb) * sizeof(int)); if (!int_elem) { Gen_Error(0, "fatal: insufficient memory"); return 0; } bor_elem = int_elem + nelemi; if (ne_get_elem_map(pexoid, int_elem, bor_elem, Proc) < 0) { Gen_Error(0, "fatal: Error returned from ne_get_elem_map"); return 0; } for (ielem = 0; ielem < nelemb; ielem++) { elements[bor_elem[ielem]-1].border = 1; } free(int_elem); /* * For now, only get the elemental communication maps, * since, in the driver, elements are only considered * adjacent if they share a face (same definition used * in element communication maps). Eventually, the ability * to consider elements that are connected by any nodes * adjacent will have to be added. When that happens, * the nodal communication maps will be needed. */ mesh->ecmap_cnt = (int *) malloc (mesh->necmap * sizeof(int)); mesh->ecmap_id = (int *) malloc(mesh->necmap * sizeof(int)); if (!mesh->ecmap_cnt || !mesh->ecmap_id) { Gen_Error(0, "fatal: insufficient memory"); return 0; } if (ne_get_cmap_params(pexoid, NULL, NULL, mesh->ecmap_id, mesh->ecmap_cnt, Proc) < 0) { Gen_Error(0, "fatal: Error returned from ne_get_cmap_params"); return 0; } max_len = 0; for (imap = 0; imap < mesh->necmap; imap++) max_len += mesh->ecmap_cnt[imap]; proc_ids = (int *) malloc(4 * max_len * sizeof(int)); gids = proc_ids + max_len; my_procs = gids + max_len; recv_procs = my_procs + max_len; mesh->ecmap_elemids = (int *) malloc(max_len * sizeof(int)); mesh->ecmap_sideids = (int *) malloc(max_len * sizeof(int)); mesh->ecmap_neighids = (int *) malloc(max_len * sizeof(int)); if (!mesh->ecmap_elemids || !mesh->ecmap_sideids || !mesh->ecmap_neighids) { Gen_Error(0, "fatal: insufficient memory"); return 0; } offset = 0; for (imap = 0; imap < mesh->necmap; imap++) { if(ne_get_elem_cmap(pexoid, mesh->ecmap_id[imap], &(mesh->ecmap_elemids[offset]), &(mesh->ecmap_sideids[offset]), &(proc_ids[offset]), Proc) < 0) { Gen_Error(0, "fatal: Error returned from ne_get_elem_cmap"); return 0; } offset += mesh->ecmap_cnt[imap]; } /* End: "for (imap = 0; imap < mesh->necmap; imap++)" */ /* * Decrement the ecmap_elemids by one for zero-based local numbering. * Convert the element ids to global ids to send to * the neighboring processor. */ for (ielem = 0; ielem < max_len; ielem++) { mesh->ecmap_elemids[ielem]--; gids[ielem] = elements[mesh->ecmap_elemids[ielem]].globalID; my_procs[ielem] = Proc; } /* * Now communicate with other processor to get global IDs * for the adjacent elements in this communication map. */ ierr = Zoltan_Comm_Create(&comm_obj, max_len, proc_ids, MPI_COMM_WORLD, msg, &nrecv); if (ierr != ZOLTAN_OK) { Gen_Error(0, "fatal: Error returned from Zoltan_Comm_Create"); return 0; } if (nrecv != max_len) { /* Sanity check; this should never happen. */ Gen_Error(0, "fatal: Error returned from Zoltan_Comm_Create"); return 0; } /* Exchange ids to neighbors. * Assuming messages will be stored in order of processor number in * ecmap_neighids. */ ierr = Zoltan_Comm_Do(comm_obj, msg+1, (char *) gids, sizeof(int), (char *) (mesh->ecmap_neighids)); /* Exchange sanity check information. * Allows to check assumption that messages are stored in order of * processor number. */ if (ierr == ZOLTAN_OK) ierr = Zoltan_Comm_Do(comm_obj, msg+2, (char *) my_procs, sizeof(int), (char *) recv_procs); if (ierr != ZOLTAN_OK) { Gen_Error(0, "fatal: Error returned from Zoltan_Comm_Do"); return 0; } ierr = Zoltan_Comm_Destroy(&comm_obj); /* Sanity check: messages stored in order of processor number. */ for (ielem = 0; ielem < max_len; ielem++) { if (proc_ids[ielem] != recv_procs[ielem]) { Gen_Error(0, "fatal: Sanity check failed; assumption wrong"); return 0; } } /* now process all of the element ids that have been received */ offset = 0; for (imap = 0; imap < mesh->necmap; imap++) { for (ielem = 0; ielem < mesh->ecmap_cnt[imap]; ielem++) { index = ielem + offset; /* translate from element id in the communication map to local elem id */ loc_elem = mesh->ecmap_elemids[index]; iblk = elements[loc_elem].elem_blk; etype = (E_Type) (mesh->eb_etypes[iblk]); (elements[loc_elem].nadj)++; if(elements[loc_elem].nadj > elements[loc_elem].adj_len) { /* Shouldn't happen as long as only side adjacencies are used. */ /* adj_len == number of sides. */ /* Space should already be allocated for the adjacencies read */ /* from the communication maps. */ Gen_Error(0, "fatal: Number of adj greater than adj_len"); return 0; } /* Store adjacency info in the adj entry corresponding to this side. */ sid = mesh->ecmap_sideids[index] - 1; elements[loc_elem].adj[sid] = mesh->ecmap_neighids[index]; elements[loc_elem].adj_proc[sid] = mesh->ecmap_id[imap]; elements[loc_elem].edge_wgt[sid] = (float) get_elem_info(NSNODES, etype, mesh->ecmap_sideids[index]); } /* End: "for (ielem = 0; ielem < mesh->ecmap_cnt[imap]; ielem++)" */ offset += mesh->ecmap_cnt[imap]; } /* End: "for for (imap = 0; imap < mesh->necmap; imap++)" */ free (proc_ids); DEBUG_TRACE_END(Proc, yo); return 1; }
/* Function get_side_id() begins: *---------------------------------------------------------------------------- * This function returns the Side ID (as used in ExodusII) given a list of * nodes on that side. * * Changed so that it is now order dependent, but independent of starting * node for 3-D sides. On 2-D sides (lines), the starting node is important. * * Now supoports degenrate faces in HEX elements. *****************************************************************************/ int get_side_id(E_Type etype, const int *connect, const int nsnodes, int side_nodes[]) { const char *func_name="get_side_id"; char err_buff[300]; int nnodes, i, j, num; int dup, location = 0; /* check if this is a degenerate face */ dup = 0; for (i = 0; i < (nsnodes - 1); i++) { for (j = (i + 1); j < nsnodes; j++) if (side_nodes[i] == side_nodes[j]) { dup = 1; location = i; /* location of duplicated node */ break; } if (dup) break; } nnodes = get_elem_info(NNODES, etype, 0); /* Find all of the side nodes in the connect table */ num = 0; for(i=0; i < nnodes; i++) { for(j=0; j < nsnodes; j++) { if(connect[i] == side_nodes[j]) { num++; break; } } if(num == nsnodes) break; } if(num != nsnodes) { Gen_Error(0, "fatal: not all side nodes in connect table for element"); return -1; } /* Find the side ID */ switch(etype) { case QUAD1: case S_QUAD2: case QUAD2: /* SIDE 1 */ if (side_nodes[0] == connect[0] && side_nodes[1] == connect[1]) return 1; /* SIDE 2 */ if (side_nodes[0] == connect[1] && side_nodes[1] == connect[2]) return 2; /* SIDE 3 */ if (side_nodes[0] == connect[2] && side_nodes[1] == connect[3]) return 3; /* SIDE 4 */ if (side_nodes[0] == connect[3] && side_nodes[1] == connect[0]) return 4; break; case TRI1: case TRI2: /* SIDE 1 */ if (side_nodes[0] == connect[0] && side_nodes[1] == connect[1]) return 1; /* SIDE 2 */ if (side_nodes[0] == connect[1] && side_nodes[1] == connect[2]) return 2; /* SIDE 3 */ if (side_nodes[0] == connect[2] && side_nodes[1] == connect[0]) return 3; break; case TET1: case TET2: /* SIDE 1 */ if((num = in_list(connect[0], nsnodes, side_nodes)) >= 0) { if (side_nodes[(1 + num) % 3] == connect[1] && side_nodes[(2 + num) % 3] == connect[3]) return 1; } /* SIDE 2 */ if((num = in_list(connect[1], nsnodes, side_nodes)) >= 0) { if (side_nodes[(1 + num) % 3] == connect[2] && side_nodes[(2 + num) % 3] == connect[3]) return 2; } /* SIDE 3 */ if((num = in_list(connect[0], nsnodes, side_nodes)) >= 0) { if (side_nodes[(1 + num) % 3] == connect[3] && side_nodes[(2 + num) % 3] == connect[2]) return 3; } /* SIDE 4 */ if((num = in_list(connect[0], nsnodes, side_nodes)) >= 0) { if (side_nodes[(1 + num) % 3] == connect[2] && side_nodes[(2 + num) % 3] == connect[1]) return 4; } break; case HEX1: case S_HEX2: case HEX2: case HEXSHELL: /* this should be the same as a HEX element */ /* SIDE 1 */ if((num = in_list(connect[0], nsnodes, side_nodes)) >= 0) { if (side_nodes[(1 + num) % 4] == connect[1] && side_nodes[(2 + num) % 4] == connect[5] && side_nodes[(3 + num) % 4] == connect[4]) return 1; /* if this is the duplicated node, then find the next occurence */ if (dup && connect[0] == side_nodes[location]) { num = in_list(connect[0], (nsnodes-num), &(side_nodes[num+1])); if (side_nodes[(1 + num) % 4] == connect[1] && side_nodes[(2 + num) % 4] == connect[5] && side_nodes[(3 + num) % 4] == connect[4]) return 1; } } /* SIDE 2 */ if((num = in_list(connect[1], nsnodes, side_nodes)) >= 0) { if (side_nodes[(1 + num) % 4] == connect[2] && side_nodes[(2 + num) % 4] == connect[6] && side_nodes[(3 + num) % 4] == connect[5]) return 2; /* if this is the duplicated node, then find the next occurence */ if (dup && connect[1] == side_nodes[location]) { num = in_list(connect[1], (nsnodes-num), &(side_nodes[num+1])); if (side_nodes[(1 + num) % 4] == connect[2] && side_nodes[(2 + num) % 4] == connect[6] && side_nodes[(3 + num) % 4] == connect[5]) return 2; } } /* SIDE 3 */ if((num = in_list(connect[2], nsnodes, side_nodes)) >= 0) { if (side_nodes[(1 + num) % 4] == connect[3] && side_nodes[(2 + num) % 4] == connect[7] && side_nodes[(3 + num) % 4] == connect[6]) return 3; /* if this is the duplicated node, then find the next occurence */ if (dup && connect[2] == side_nodes[location]) { num = in_list(connect[2], (nsnodes-num), &(side_nodes[num+1])); if (side_nodes[(1 + num) % 4] == connect[3] && side_nodes[(2 + num) % 4] == connect[7] && side_nodes[(3 + num) % 4] == connect[6]) return 3; } } /* SIDE 4 */ if((num = in_list(connect[3], nsnodes, side_nodes)) >= 0) { if (side_nodes[(1 + num) % 4] == connect[0] && side_nodes[(2 + num) % 4] == connect[4] && side_nodes[(3 + num) % 4] == connect[7]) return 4; /* if this is the duplicated node, then find the next occurence */ if (dup && connect[3] == side_nodes[location]) { num = in_list(connect[3], (nsnodes-num), &(side_nodes[num+1])); if (side_nodes[(1 + num) % 4] == connect[0] && side_nodes[(2 + num) % 4] == connect[4] && side_nodes[(3 + num) % 4] == connect[7]) return 4; } } /* SIDE 5 */ if((num = in_list(connect[0], nsnodes, side_nodes)) >= 0) { if (side_nodes[(1 + num) % 4] == connect[3] && side_nodes[(2 + num) % 4] == connect[2] && side_nodes[(3 + num) % 4] == connect[1]) return 5; /* if this is the duplicated node, then find the next occurence */ if (dup && connect[0] == side_nodes[location]) { num = in_list(connect[0], (nsnodes-num), &(side_nodes[num+1])); if (side_nodes[(1 + num) % 4] == connect[3] && side_nodes[(2 + num) % 4] == connect[2] && side_nodes[(3 + num) % 4] == connect[1]) return 5; } } /* SIDE 6 */ if((num = in_list(connect[4], nsnodes, side_nodes)) >= 0) { if (side_nodes[(1 + num) % 4] == connect[5] && side_nodes[(2 + num) % 4] == connect[6] && side_nodes[(3 + num) % 4] == connect[7]) return 6; /* if this is the duplicated node, then find the next occurence */ if (dup && connect[4] == side_nodes[location]) { num = in_list(connect[4], (nsnodes-num), &(side_nodes[num+1])); if (side_nodes[(1 + num) % 4] == connect[5] && side_nodes[(2 + num) % 4] == connect[6] && side_nodes[(3 + num) % 4] == connect[7]) return 6; } } break; case SHELL1: case SHELL2: /* 2D sides */ if(nsnodes == 2 || nsnodes == 3) { /* SIDE 3 */ if (side_nodes[0] == connect[0] && side_nodes[1] == connect[1]) return 3; /* SIDE 4 */ if (side_nodes[0] == connect[1] && side_nodes[1] == connect[2]) return 4; /* SIDE 5 */ if (side_nodes[0] == connect[2] && side_nodes[1] == connect[3]) return 5; /* SIDE 6 */ if (side_nodes[0] == connect[3] && side_nodes[1] == connect[0]) return 6; } /* 3D faces */ else if (nsnodes == 4 || nsnodes == 8) { /* SIDE 1 */ if((num = in_list(connect[0], nsnodes, side_nodes)) >= 0) { if (side_nodes[(1 + num) % 4] == connect[1] && side_nodes[(2 + num) % 4] == connect[2] && side_nodes[(3 + num) % 4] == connect[3]) return 1; } /* SIDE 2 */ if((num = in_list(connect[0], nsnodes, side_nodes)) >= 0) { if (side_nodes[(1 + num) % 4] == connect[3] && side_nodes[(2 + num) % 4] == connect[2] && side_nodes[(3 + num) % 4] == connect[1]) return 2; } } break; case WEDGE1: case WEDGE2: /* quad sides */ if (nsnodes == 4 || nsnodes == 8) { /* SIDE 1 */ if((num = in_list(connect[0], nsnodes, side_nodes)) >= 0) { if (side_nodes[(1 + num) % 4] == connect[1] && side_nodes[(2 + num) % 4] == connect[4] && side_nodes[(3 + num) % 4] == connect[3]) return 1; } /* SIDE 2 */ if((num = in_list(connect[1], nsnodes, side_nodes)) >= 0) { if (side_nodes[(1 + num) % 4] == connect[2] && side_nodes[(2 + num) % 4] == connect[5] && side_nodes[(3 + num) % 4] == connect[4]) return 2; } /* SIDE 3 */ if((num = in_list(connect[0], nsnodes, side_nodes)) >= 0) { if (side_nodes[(1 + num) % 4] == connect[3] && side_nodes[(2 + num) % 4] == connect[5] && side_nodes[(3 + num) % 4] == connect[2]) return 3; } } /* triangle sides */ else if (nsnodes == 3 || nsnodes == 6) { /* SIDE 4 */ if((num = in_list(connect[0], nsnodes, side_nodes)) >= 0) { if (side_nodes[(1 + num) % 3] == connect[2] && side_nodes[(3 + num) % 3] == connect[1]) return 4; } /* SIDE 5 */ if((num = in_list(connect[3], nsnodes, side_nodes)) >= 0) { if (side_nodes[(1 + num) % 3] == connect[4] && side_nodes[(3 + num) % 3] == connect[5]) return 5; } } break; case TSHELL1: case TSHELL2: /* 2D sides */ if(nsnodes == 2 || ((etype == TSHELL2) && (nsnodes == 3))) { /* SIDE 3 */ if (side_nodes[0] == connect[0] && side_nodes[1] == connect[1]) return 3; /* SIDE 4 */ if (side_nodes[0] == connect[1] && side_nodes[1] == connect[2]) return 4; /* SIDE 5 */ if (side_nodes[0] == connect[2] && side_nodes[1] == connect[0]) return 5; } /* 3D faces */ else if (nsnodes == 3 || nsnodes == 6) { /* SIDE 1 */ if((num = in_list(connect[0], nsnodes, side_nodes)) >= 0) { if (side_nodes[(1 + num) % 3] == connect[1] && side_nodes[(2 + num) % 3] == connect[2]) return 1; } /* SIDE 2 */ if((num = in_list(connect[0], nsnodes, side_nodes)) >= 0) { if (side_nodes[(1 + num) % 3] == connect[2] && side_nodes[(2 + num) % 3] == connect[1]) return 2; } } break; default: sprintf(err_buff, "fatal: unknown element type %d in function %s", etype, func_name); Gen_Error(0, err_buff); return -1; } /* End "switch(etype)" */ return 0; } /*---------------------------End get_side_id()-----------------------------*/