void build_local_element_map(RegionVector &part_mesh, std::vector<INT> &local_element_map) { size_t global = 0; size_t offset = 0; for (size_t p = 0; p < part_mesh.size(); p++) { Ioss::ElementBlockContainer ebs = part_mesh[p]->get_element_blocks(); Ioss::ElementBlockContainer::const_iterator i = ebs.begin(); while (i != ebs.end()) { Ioss::ElementBlock *eb = *i++; size_t num_elem = eb->get_property("entity_count").get_int(); if (entity_is_omitted(eb)) { // Fill local_element_map with -1 for the omitted elements. for (size_t j = 0; j < num_elem; j++) { local_element_map[offset+j] = -1; } } else { for (size_t j = 0; j < num_elem; j++) { local_element_map[offset+j] = global++; } } offset += num_elem; } } }
Block::Block(const Ioss::ElementBlock &other) { id = other.get_property("id").get_int(); elementCount = other.get_property("entity_count").get_int(); nodesPerElement = other.get_property("topology_node_count").get_int(); attributeCount = other.get_property("attribute_count").get_int(); offset_ = other.get_offset(); std::string el_type = other.get_property("topology_type").get_string(); if (other.property_exists("original_topology_type")) { el_type = other.get_property("original_topology_type").get_string(); } std::strncpy(elType, el_type.c_str(), MAX_STR_LENGTH); elType[MAX_STR_LENGTH] = 0; // Fixup an exodusII kludge. For triangular elements, the same // name is used for 2D elements and 3D shell elements. Convert // to unambiguous names for the IO Subsystem. The 2D name // stays the same, the 3D name becomes 'trishell#' // Here, we need to map back to the 'triangle' name... if (std::strncmp(elType, "trishell", 8) == 0) std::strncpy(elType, "triangle", 8); std::string io_name = other.name(); std::strncpy(name, io_name.c_str(), MAX_STR_LENGTH); name[MAX_STR_LENGTH] = 0; }
// ======================================================================== void process_elementblocks(Ioss::Region ®ion, stk::mesh::BulkData &bulk) { const Ioss::ElementBlockContainer& elem_blocks = region.get_element_blocks(); for(Ioss::ElementBlockContainer::const_iterator it = elem_blocks.begin(); it != elem_blocks.end(); ++it) { Ioss::ElementBlock *entity = *it; if (stk::io::include_entity(entity)) { const std::string &name = entity->name(); const stk::mesh::MetaData& meta = stk::mesh::MetaData::get(bulk); stk::mesh::Part* const part = meta.get_part(name); STKIORequire(part != NULL); const stk::topology topo = part->topology(); if (topo == stk::topology::INVALID_TOPOLOGY) { std::ostringstream msg ; msg << " INTERNAL_ERROR: Part " << part->name() << " returned INVALID from get_topology()"; throw std::runtime_error( msg.str() ); } std::vector<int> elem_ids ; std::vector<int> connectivity ; entity->get_field_data("ids", elem_ids); entity->get_field_data("connectivity", connectivity); size_t element_count = elem_ids.size(); int nodes_per_elem = topo.num_nodes(); stk::mesh::EntityIdVector connectivity2(nodes_per_elem); std::vector<int>::const_iterator connBegin = connectivity.begin(); std::vector<stk::mesh::Entity> elements(element_count); for(size_t i=0; i<element_count; ++i, connBegin += nodes_per_elem) { std::copy(connBegin, connBegin + nodes_per_elem, connectivity2.begin()); elements[i] = stk::mesh::declare_element(bulk, *part, elem_ids[i], connectivity2); } // For this example, we are just taking all attribute fields // found on the io database and populating fields on the // corresponding mesh part. In practice, would probably be // selective about which attributes to use... Ioss::NameList names; entity->field_describe(Ioss::Field::ATTRIBUTE, &names); for (Ioss::NameList::const_iterator I = names.begin(); I != names.end(); ++I) { if (*I == "attribute" && names.size() > 1) continue; stk::mesh::FieldBase *field = meta.get_field<stk::mesh::FieldBase>(stk::topology::ELEMENT_RANK, *I); stk::io::field_data_from_ioss(bulk, field, elements, entity, *I); } } } }
AxisAlignedBoundingBox DatabaseIO::get_bounding_box(const Ioss::ElementBlock *eb) const { if (elementBlockBoundingBoxes.empty()) { // Calculate the bounding boxes for all element blocks... std::vector<double> coordinates; Ioss::NodeBlock * nb = get_region()->get_node_blocks()[0]; nb->get_field_data("mesh_model_coordinates", coordinates); ssize_t nnode = nb->get_property("entity_count").get_int(); ssize_t ndim = nb->get_property("component_degree").get_int(); Ioss::ElementBlockContainer element_blocks = get_region()->get_element_blocks(); size_t nblock = element_blocks.size(); std::vector<double> minmax; minmax.reserve(6 * nblock); for (auto &block : element_blocks) { double xmin, ymin, zmin, xmax, ymax, zmax; if (block->get_database()->int_byte_size_api() == 8) { std::vector<int64_t> connectivity; block->get_field_data("connectivity_raw", connectivity); calc_bounding_box(ndim, nnode, coordinates, connectivity, xmin, ymin, zmin, xmax, ymax, zmax); } else { std::vector<int> connectivity; block->get_field_data("connectivity_raw", connectivity); calc_bounding_box(ndim, nnode, coordinates, connectivity, xmin, ymin, zmin, xmax, ymax, zmax); } minmax.push_back(xmin); minmax.push_back(ymin); minmax.push_back(zmin); minmax.push_back(-xmax); minmax.push_back(-ymax); minmax.push_back(-zmax); } util().global_array_minmax(minmax, Ioss::ParallelUtils::DO_MIN); for (size_t i = 0; i < element_blocks.size(); i++) { Ioss::ElementBlock * block = element_blocks[i]; const std::string & name = block->name(); AxisAlignedBoundingBox bbox(minmax[6 * i + 0], minmax[6 * i + 1], minmax[6 * i + 2], -minmax[6 * i + 3], -minmax[6 * i + 4], -minmax[6 * i + 5]); elementBlockBoundingBoxes[name] = bbox; } } return elementBlockBoundingBoxes[eb->name()]; }
// ======================================================================== void process_elementblocks(Ioss::Region ®ion, stk::mesh::MetaData &meta) { const stk::mesh::EntityRank element_rank = stk::topology::ELEMENT_RANK; const Ioss::ElementBlockContainer& elem_blocks = region.get_element_blocks(); stk::io::default_part_processing(elem_blocks, meta, element_rank); // Parts were created above, now handle element block specific // information (topology, attributes, ...); for(Ioss::ElementBlockContainer::const_iterator it = elem_blocks.begin(); it != elem_blocks.end(); ++it) { Ioss::ElementBlock *entity = *it; if (stk::io::include_entity(entity)) { stk::mesh::Part* const part = meta.get_part(entity->name()); STKIORequire(part != NULL); const stk::mesh::EntityRank part_rank = part->primary_entity_rank(); // Element Block attributes (if any)... /** \todo IMPLEMENT truly handle attribute fields... For this * case we are just defining a field for each attribute field * that is present in the mesh... */ stk::io::define_io_fields(entity, Ioss::Field::ATTRIBUTE, *part, part_rank); /** \todo IMPLEMENT truly handle fields... For this case we * are just defining a field for each transient field that is * present in the mesh... */ stk::io::define_io_fields(entity, Ioss::Field::TRANSIENT, *part, part_rank); } } }
void generate_element_ids(RegionVector &part_mesh, const std::vector<INT> &local_element_map, std::vector<INT> &global_element_map) { // Follow same logic as 'build_local_element_map' to ensure elements // are processed in same order. // Many models do not use the element number map at all, so they // will have a 1..numel map. If all parts have that, then we don't // want to do any fancy duplicate removal and other processing, just // output a 1..numel map for the output mesh... We still generate // the global_element_map, but check whether any of the part blocks // have a non-1..numel map... bool has_map = false; size_t offset = 0; for (size_t p = 0; p < part_mesh.size(); p++) { Ioss::ElementBlockContainer ebs = part_mesh[p]->get_element_blocks(); Ioss::ElementBlockContainer::const_iterator i = ebs.begin(); while (i != ebs.end()) { Ioss::ElementBlock *eb = *i++; INT num_elem = eb->get_property("entity_count").get_int(); if (!entity_is_omitted(eb)) { std::vector<INT> part_ids; eb->get_field_data("ids", part_ids); if (!has_map) { INT eb_offset = eb->get_offset(); for (INT j = 0; j < num_elem; j++) { if (part_ids[j] != eb_offset+j+1) { has_map = true; break; } } } for (INT j = 0; j < num_elem; j++) { INT gpos = local_element_map[offset+j]; if (gpos >= 0) global_element_map[gpos] = part_ids[j]; } } offset += num_elem; } } // Check for duplicates... // NOTE: Used to use an indexed sort here, but if there was a // duplicate id, it didnt really care whether part 1 or part N's // index came first which causes really screwy element maps. // Instead, lets sort a vector containing pairs of <id, index> where // the index will always? increase for increasing part numbers... std::vector<std::pair<INT,INT> > index(global_element_map.size()); for (size_t i=0; i < index.size(); i++) { index[i] = std::make_pair(global_element_map[i],(INT)i); } std::sort(index.begin(), index.end()); INT max_id = index[index.size()-1].first + 1; size_t beg = 0; for (size_t i=1; i < index.size(); i++) { if (index[beg].first == index[i].first) { // Duplicate found... Assign it a new id greater than any // existing id... (What happens if we exceed INT_MAX?) global_element_map[index[i].second] = max_id++; // Keep 'beg' the same in case multiple duplicate of this value. } else { beg = i; } } }
/// This function shows the basic calls needed to perform definition /// and input of the mesh model and definition and periodic output /// of a results database. The function is given the mesh filename /// and the output filename and goes through all steps of /// associating the filename with an Ioss::DatabaseIO object of the /// correct type ("exodusII" in this example); creating an /// Ioss::Region and then defining an stk::mesh corresponding to /// this mesh. The function also provides an example of how /// specific element blocks existing in the mesh database could be /// omitted from the analysis model. /// /// The example then shows how to define a results database /// corresponding to the analysis model and periodically output the /// results in an execute loop. /// /// A true application would have to provide additional /// functionality and robustness, but the example shows how the /// basic functionality can be provided by an application. /// /// Note that the paradigm illustrated here is different than the /// mesh input and output paradigm provided in the current /// framework. In this case, the application is responsible for the /// majority of the IO behavior and the toolkit only provides some /// helper functions to bridge between the Ioss and the stk::mesh. /// It is hoped that this paradigm will result in more functionality /// for the application with less complication and overhead. void io_example( const std::string& in_filename, const std::string& out_filename, const std::string& decomp_method) { // Initialize IO system. Registers all element types and storage // types and the exodusII default database type. Ioss::Init::Initializer init_db; std::cout << "========================================================================\n" << " Copy input mesh to output mesh. \n" << "========================================================================\n"; std::string dbtype("exodusII"); Ioss::PropertyManager properties; if (!decomp_method.empty()) { properties.add(Ioss::Property("DECOMPOSITION_METHOD", Ioss::Utils::uppercase(decomp_method))); } Ioss::DatabaseIO *dbi = Ioss::IOFactory::create(dbtype, in_filename, Ioss::READ_MODEL, MPI_COMM_WORLD, properties); if (dbi == NULL || !dbi->ok()) { std::cerr << "ERROR: Could not open database '" << in_filename << "' of type '" << dbtype << "'\n"; std::exit(EXIT_FAILURE); } std::cout << "Reading input file: " << in_filename << "\n"; // NOTE: 'in_region' owns 'dbi' pointer at this time... Ioss::Region in_region(dbi, "input_model"); // SUBSETTING PARSING/PREPROCESSING... // Just an example of how application could control whether an // entity is subsetted or not... #if 0 // Example command line in current code corresponding to behavior below: std::cout << "\nWhen processing file multi-block.g for use case 2, the blocks below will be omitted:\n"; std::cout << "\tOMIT BLOCK Cblock Eblock I1 I2\n\n"; Ioss::ElementBlock *eb = in_region.get_element_block("cblock"); if (eb != NULL) eb->property_add(Ioss::Property(std::string("omitted"), 1)); eb = in_region.get_element_block("eblock"); if (eb != NULL) eb->property_add(Ioss::Property(std::string("omitted"), 1)); eb = in_region.get_element_block("i1"); if (eb != NULL) eb->property_add(Ioss::Property(std::string("omitted"), 1)); eb = in_region.get_element_block("i2"); if (eb != NULL) eb->property_add(Ioss::Property(std::string("omitted"), 1)); #endif #if 0 // Example for subsetting -- omit "odd" blocks if (entity->type() == Ioss::ELEMENTBLOCK) { int id = entity->get_property("id").get_int(); if (id % 2) { entity->property_add(Ioss::Property(std::string("omitted"), 1)); std::cout << "Skipping " << entity->type_string() << ": " << entity->name() << "\n"; } } #endif //---------------------------------- // Process Entity Types. Subsetting is possible. static size_t spatial_dimension = in_region.get_property("spatial_dimension").get_int(); stk::mesh::MetaData fem_meta_data( spatial_dimension ); process_elementblocks(in_region, fem_meta_data); process_nodeblocks(in_region, fem_meta_data); process_sidesets(in_region, fem_meta_data); process_nodesets(in_region, fem_meta_data); //---------------------------------- // Done populating meta data, commit and create bulk data fem_meta_data.commit(); //---------------------------------- // Process Bulkdata for all Entity Types. Subsetting is possible. stk::mesh::BulkData bulk_data(fem_meta_data, MPI_COMM_WORLD); bulk_data.modification_begin(); process_elementblocks(in_region, bulk_data); process_nodeblocks(in_region, bulk_data); process_sidesets(in_region, bulk_data); process_nodesets(in_region, bulk_data); bulk_data.modification_end(); //---------------------------------- // OUTPUT...Create the output "mesh" portion std::cout << "Creating output file: " << out_filename << "\n"; Ioss::DatabaseIO *dbo = Ioss::IOFactory::create(dbtype, out_filename, Ioss::WRITE_RESULTS, MPI_COMM_WORLD); if (dbo == NULL || !dbo->ok()) { std::cerr << "ERROR: Could not open results database '" << out_filename << "' of type '" << dbtype << "'\n"; std::exit(EXIT_FAILURE); } #if 0 { // Code to test the remove_io_part_attribute functionality. // Hook this up to a command line option at some point to test nightly... const stk::mesh::PartVector & all_parts = fem_meta_data.get_parts(); for ( stk::mesh::PartVector::const_iterator ip = all_parts.begin(); ip != all_parts.end(); ++ip ) { stk::mesh::Part * const part = *ip; const stk::mesh::EntityRank part_rank = part->primary_entity_rank(); if (stk::io::is_part_io_part(*part) && part_rank == 2) { std::cout << "Removing part attribute from " << part->name() << "\n"; stk::io::remove_io_part_attribute(*part); } } } #endif // NOTE: 'out_region' owns 'dbo' pointer at this time... Ioss::Region out_region(dbo, "results_output"); stk::io::define_output_db(out_region, bulk_data, &in_region); stk::io::write_output_db(out_region, bulk_data); // ------------------------------------------------------------------------ /** \todo REFACTOR A real app would register a subset of the * fields on the mesh database as fields that the app would want * read at one or all or specified steps. In this example, all * fields existing on the input mesh database are defined on the * parts in the stk::mesh. * * The real app would also only register a subset of the stk::mesh * fields as output fields and would probably have a mapping from * the internally used name to some name picked by the user. In * this example, all Ioss::Field::TRANSIENT fields defined on the stk::mesh are * output to the results database and the internal stk::mesh field * name is used as the name on the database.... */ out_region.begin_mode(Ioss::STATE_DEFINE_TRANSIENT); // Special processing for nodeblock (all nodes in model)... stk::io::ioss_add_fields(fem_meta_data.universal_part(), stk::topology::NODE_RANK, out_region.get_node_blocks()[0], Ioss::Field::TRANSIENT); const stk::mesh::PartVector & all_parts = fem_meta_data.get_parts(); for ( stk::mesh::PartVector::const_iterator ip = all_parts.begin(); ip != all_parts.end(); ++ip ) { stk::mesh::Part * const part = *ip; const stk::mesh::EntityRank part_rank = part->primary_entity_rank(); // Check whether this part should be output to results database. if (stk::io::is_part_io_part(*part)) { // Get Ioss::GroupingEntity corresponding to this part... Ioss::GroupingEntity *entity = out_region.get_entity(part->name()); if (entity != NULL) { if (entity->type() == Ioss::SIDESET) { Ioss::SideSet *sset = dynamic_cast<Ioss::SideSet*>(entity); assert(sset != NULL); int block_count = sset->block_count(); for (int i=0; i < block_count; i++) { Ioss::SideBlock *fb = sset->get_block(i); stk::io::ioss_add_fields(*part, part_rank, fb, Ioss::Field::TRANSIENT); } } else { stk::io::ioss_add_fields(*part, part_rank, entity, Ioss::Field::TRANSIENT); } } else { /// \todo IMPLEMENT handle error... Possibly an assert since /// I think the corresponding entity should always exist... } } } out_region.end_mode(Ioss::STATE_DEFINE_TRANSIENT); // ------------------------------------------------------------------------ // Read and Write transient fields... out_region.begin_mode(Ioss::STATE_TRANSIENT); int timestep_count = in_region.get_property("state_count").get_int(); for (int step = 1; step <= timestep_count; step++) { double time = in_region.get_state_time(step); // Read data from the io input mesh database into stk::mesh fields... process_input_request(in_region, bulk_data, step); // execute() // Write data from the stk::mesh fields out to the output database.a int out_step = out_region.add_state(time); process_output_request(out_region, bulk_data, out_step); } out_region.end_mode(Ioss::STATE_TRANSIENT); }