Skip to content

DRAFT: Changes/fixes for adjset generation. #1434

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 8 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,12 @@ and this project aspires to adhere to [Semantic Versioning](https://semver.org/s
### Added
- Added support for unstructured topologies with `mixed` shape type in the `conduit::blueprint::mesh::partition()` and `conduit::blueprint::mesh::utils::topology::iterate_elements()` functions. They work with mixed shape types, including polyhedra.
- Enhanced the `conduit::blueprint::mesh::examples::tiled()` function so it can accept mesh tiles containing `mixed` shape types for generating 2D tiled output.
- Added `conduit::blueprint::mesh::utils::topology::compute_mesh_info()` function to compute basic information about a mesh such as extents and edge lengths.
- Added `conduit::blueprint::mesh::utils::topology::Quantizer` class to compute a quantized index for a point.

### Changed
- The `conduit::blueprint::mesh::topology::dims()` function was changed so it will return the maximum topological dimension of the shape types from the `shape_map` for a `mixed` shape.
- Adjacency set construction was altered so points are sorted spatially using the `Quantizer` class.

## [0.9.4] - Released 2025-04-03

Expand Down
184 changes: 79 additions & 105 deletions src/libs/blueprint/conduit_blueprint_mesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,26 +57,6 @@ typedef bputils::TopologyMetadata TopologyMetadata;
//-----------------------------------------------------------------------------
// -- begin internal helpers --
//-----------------------------------------------------------------------------
// TODO(JRC): Consider moving an improved version of this type to a more accessible location.
struct ffloat64
{
conduit::float64 data;

ffloat64(conduit::float64 input = 0.0)
{
data = input;
}

operator conduit::float64() const
{
return this->data;
}

bool operator<(const ffloat64 &other) const
{
return this->data < other.data && std::abs(this->data - other.data) > CONDUIT_EPSILON;
}
};

// access conduit blueprint mesh utilities
namespace bputils = conduit::blueprint::mesh::utils;
Expand All @@ -85,7 +65,7 @@ namespace o2mrelation = conduit::blueprint::o2mrelation;

// typedefs for verbose but commonly used types
typedef std::tuple<conduit::Node*, conduit::Node*, conduit::Node*> DomMapsTuple;
typedef std::tuple<ffloat64, ffloat64, ffloat64> PointTuple;

// typedefs to enable passing around function pointers
typedef void (*GenDerivedFun)(const conduit::Node&, conduit::Node&, conduit::Node&, conduit::Node&);
typedef void (*GenDecomposedFun)(const conduit::Node&, conduit::Node&, conduit::Node&, conduit::Node&, conduit::Node&);
Expand Down Expand Up @@ -2874,6 +2854,10 @@ generate_derived_entities(conduit::Node &mesh,
GenDerivedFun generate_derived,
conduit::blueprint::mesh::utils::query::MatchQuery &Q)
{
namespace topoutils = conduit::blueprint::mesh::utils::topology;
using Entity = std::tuple<std::set<topoutils::Quantizer::QuantizedIndex>, index_t>;
using EntityVector = std::vector<Entity>;

CONDUIT_ANNOTATE_MARK_FUNCTION;

// loop over all domains and call generate_derived on each domain
Expand Down Expand Up @@ -3026,56 +3010,63 @@ generate_derived_entities(conduit::Node &mesh,
const Node &src_topo = *src_topo_ptr;
const Node *src_cset_ptr = bputils::find_reference_node(src_topo, "coordset");
const Node &src_cset = *src_cset_ptr;
const conduit::Node &src_adjset_groups = domain["adjsets"][src_adjset_name]["groups"];

const Node &dst_topo = domain["topologies"][dst_topo_name];

const conduit::Node &src_adjset_groups = domain["adjsets"][src_adjset_name]["groups"];
// Compute information about the dst_topo and make a Quantizer with it.
topoutils::MeshInfo dst_info;
topoutils::compute_mesh_info(dst_topo, dst_info);
topoutils::Quantizer quantizer(dst_info);

conduit::Node &dst_adjset_groups = domain["adjsets"][dst_adjset_name]["groups"];

// Use Entity Interfaces to Construct Group Entity Lists //

std::map<std::set<index_t>, std::vector<std::tuple<std::set<PointTuple>, index_t>>> group_entity_map;
std::map<std::set<index_t>, EntityVector> group_entity_map;
const auto &entity_neighbor_map = dom_entity_neighbor_map[domain_id];
for(const auto &entity_neighbor_pair : entity_neighbor_map)
{
const index_t &ei = entity_neighbor_pair.first;
const std::set<index_t> &entity_neighbors = entity_neighbor_pair.second;
std::tuple<std::set<PointTuple>, index_t> entity;

std::vector<index_t> entity_pidxs = bputils::topology::unstructured::points(dst_topo, ei);
std::set<PointTuple> &entity_points = std::get<0>(entity);
// Get point ids used in entity ei in dst_topo
const std::vector<index_t> entity_pidxs = bputils::topology::unstructured::points(dst_topo, ei);

// Make entity
Entity entity;
auto &entity_points = std::get<0>(entity);
for(const index_t &entity_pidx : entity_pidxs)
{
// Get the points for entity_pidx. NOTE: src_cset is same as new dst_topo's coordset.
const std::vector<float64> point_coords = bputils::coordset::_explicit::coords(
src_cset, entity_pidx);
entity_points.emplace(
point_coords[0],
(point_coords.size() > 1) ? point_coords[1] : 0.0,
(point_coords.size() > 2) ? point_coords[2] : 0.0);
}

index_t &entity_id = std::get<1>(entity);
entity_id = ei;
// Add a quantized point id to the entity points
entity_points.emplace(quantizer.quantize(point_coords));
}
std::get<1>(entity) = ei;

// NOTE(JRC): Inserting with this method allows this algorithm to sort new
// elements as they're generated, rather than as a separate process at the
// end (slight optimization overall).
std::vector<std::tuple<std::set<PointTuple>, index_t>> &group_entities =
group_entity_map[entity_neighbors];
// end (slight optimization overall). PointTuple and ffloat64's less than
// operator determines the insertion location.
auto &group_entities = group_entity_map[entity_neighbors];
auto entity_itr = std::upper_bound(group_entities.begin(), group_entities.end(), entity);
group_entities.insert(entity_itr, entity);
}

for(const auto &group_pair : group_entity_map)
{
const std::set<index_t> &group_nidxs = group_pair.first;
const EntityVector &group_entities = group_pair.second;

// NOTE(JRC): It's possible for the 'src_adjset_groups' node to be empty,
// so we only want to query child data types if we know there is at least
// 1 non-empty group.
const conduit::DataType src_neighbors_dtype = src_adjset_groups.child(0)["neighbors"].dtype();
const conduit::DataType src_values_dtype = src_adjset_groups.child(0)["values"].dtype();

const std::set<index_t> &group_nidxs = group_pair.first;
const std::vector<std::tuple<std::set<PointTuple>, index_t>> &group_entities = group_pair.second;
std::string group_name;
{
// NOTE(JRC): The current domain is included in the domain name so that
Expand All @@ -3099,25 +3090,18 @@ generate_derived_entities(conduit::Node &mesh,

dst_neighbors.set(DataType(src_neighbors_dtype.id(), group_nidxs.size()));
index_t ni = 0;
auto neighbors = dst_neighbors.as_index_t_accessor();
for(auto nitr = group_nidxs.begin(); nitr != group_nidxs.end(); ++nitr)
{
// TODO: USE ACCESSORS
src_data.set_external(DataType::index_t(1),
(void*)&(*nitr));
dst_data.set_external(DataType(src_neighbors_dtype.id(), 1),
(void*)dst_neighbors.element_ptr(ni++));
src_data.to_data_type(dst_data.dtype().id(), dst_data);
neighbors.set(ni++, *nitr);
}

dst_values.set(DataType(src_values_dtype.id(), group_entities.size()));
auto values = dst_values.as_index_t_accessor();
for(index_t ei = 0; ei < (index_t)group_entities.size(); ei++)
{
// TODO: USE ACCESSORS
src_data.set_external(DataType::index_t(1),
(void*)&std::get<1>(group_entities[ei]));
dst_data.set_external(DataType(src_values_dtype.id(), 1),
(void*)dst_values.element_ptr(ei));
src_data.to_data_type(dst_data.dtype().id(), dst_data);
const index_t nodeid = std::get<1>(group_entities[ei]);
values.set(ei, nodeid);
}
}
}
Expand Down Expand Up @@ -3164,6 +3148,10 @@ generate_decomposed_entities(conduit::Node &mesh,
const std::vector<index_t> &decomposed_centroid_dims,
conduit::blueprint::mesh::utils::query::PointQueryBase &query)
{
namespace topoutils = conduit::blueprint::mesh::utils::topology;
using Entity = std::tuple<topoutils::Quantizer::QuantizedIndex, index_t>;
using EntityVector = std::vector<Entity>;

CONDUIT_ANNOTATE_MARK_FUNCTION;

// Iterate over the domains and produce a "decomposed" mesh for each domain.
Expand Down Expand Up @@ -3362,6 +3350,13 @@ generate_decomposed_entities(conduit::Node &mesh,
const Node &src_adjset_groups = domain["adjsets"][src_adjset_name]["groups"];
Node &dst_adjset_groups = domain["adjsets"][dst_adjset_name]["groups"];

const Node &dst_topo = domain["topologies"][dst_topo_name];

// Compute information about the dst_topo and make a Quantizer with it.
topoutils::MeshInfo dst_info;
topoutils::compute_mesh_info(dst_topo, dst_info);
topoutils::Quantizer quantizer(dst_info);

// Use Entity Interfaces to Construct Group Entity Lists //

// Iterate over the entity_neighbor_map and build up group_entity_map.
Expand All @@ -3373,34 +3368,28 @@ generate_decomposed_entities(conduit::Node &mesh,
//
// This assumption must be made to avoid a costly coordinate->id lookup.

std::map<std::set<index_t>, std::vector<std::tuple<std::set<PointTuple>, index_t>>> group_entity_map;
std::map<std::set<index_t>, EntityVector> group_entity_map;
for(const auto &entity_neighbor_pair : entity_neighbor_map)
{
const index_t &entity_cidx = entity_neighbor_pair.first;
const std::set<index_t> &entity_neighbors = entity_neighbor_pair.second;

// Make an entity tuple and get references to its members.
std::tuple<std::set<PointTuple>, index_t> entity;
std::set<PointTuple> &entity_points = std::get<0>(entity);
index_t &entity_id = std::get<1>(entity);

// NOTE(JRC): Diff: Substitute entity for centroid point at the end here.

// Use the entity_cidx (a source topo index) to get the coordinate
// in the corner coordset.
const std::vector<float64> point_coords = bputils::coordset::_explicit::coords(
dst_cset, entity_cidx);

entity_points.emplace(
point_coords[0],
(point_coords.size() > 1) ? point_coords[1] : 0.0,
(point_coords.size() > 2) ? point_coords[2] : 0.0);

entity_id = entity_cidx;
// Make entity
Entity entity;
std::get<0>(entity) = quantizer.quantize(point_coords);
std::get<1>(entity) = entity_cidx;

// NOTE(JRC): Inserting with this method allows this algorithm to sort new
// elements as they're generated, rather than as a separate process at the
// end (slight optimization overall).
// end (slight optimization overall). PointTuple and ffloat64's less than
// operator determines the insertion location.
auto &group_entities = group_entity_map[entity_neighbors];
auto entity_itr = std::upper_bound(group_entities.begin(), group_entities.end(), entity);
group_entities.insert(entity_itr, entity);
Expand Down Expand Up @@ -3440,25 +3429,18 @@ generate_decomposed_entities(conduit::Node &mesh,

dst_neighbors.set(DataType(src_neighbors_dtype.id(), group_nidxs.size()));
index_t ni = 0;
auto neighbors = dst_neighbors.as_index_t_accessor();
for(auto nitr = group_nidxs.begin(); nitr != group_nidxs.end(); ++nitr)
{
// TODO: USE ACCESSORS
src_data.set_external(DataType::index_t(1),
(void*)&(*nitr));
dst_data.set_external(DataType(src_neighbors_dtype.id(), 1),
(void*)dst_neighbors.element_ptr(ni++));
src_data.to_data_type(dst_data.dtype().id(), dst_data);
neighbors.set(ni++, *nitr);
}

dst_values.set(DataType(src_values_dtype.id(), group_entities.size()));
auto values = dst_values.as_index_t_accessor();
for(index_t ei = 0; ei < (index_t)group_entities.size(); ei++)
{
// TODO: USE ACCESSORS
src_data.set_external(DataType::index_t(1),
(void*)&std::get<1>(group_entities[ei]));
dst_data.set_external(DataType(src_values_dtype.id(), 1),
(void*)dst_values.element_ptr(ei));
src_data.to_data_type(dst_data.dtype().id(), dst_data);
const index_t nodeid = std::get<1>(group_entities[ei]);
values.set(ei, nodeid);
}
}
}
Expand Down Expand Up @@ -7038,13 +7020,11 @@ mesh::adjset::is_maxshare(const Node &adjset)
while(group_itr.has_next() && res)
{
const Node &group = group_itr.next();
const Node &group_values = group["values"];
const auto values = group["values"].as_index_t_accessor();

for(index_t ni = 0; ni < group_values.dtype().number_of_elements(); ni++)
for(index_t vi = 0; vi < values.number_of_elements(); vi++)
{
Node temp(DataType(group_values.dtype().id(), 1),
(void*)group_values.element_ptr(ni), true);
const index_t next_id = temp.to_index_t();
const index_t next_id = values[vi];

res &= ids.find(next_id) == ids.end();
ids.insert(next_id);
Expand Down Expand Up @@ -7078,23 +7058,22 @@ mesh::adjset::to_pairwise(const Node &adjset,

std::vector<index_t> group_neighbors;
{
const Node &group_nvals = group_node["neighbors"];
for(index_t ni = 0; ni < group_nvals.dtype().number_of_elements(); ++ni)
const auto neighbors = group_node["neighbors"].as_index_t_accessor();
group_neighbors.reserve(neighbors.number_of_elements());
for(index_t ni = 0; ni < neighbors.number_of_elements(); ++ni)
{
Node temp(DataType(group_nvals.dtype().id(), 1),
(void*)group_nvals.element_ptr(ni), true);
group_neighbors.push_back(temp.to_index_t());
group_neighbors.push_back(neighbors[ni]);
}
}

std::vector<index_t> group_values;
{
const Node &group_vals = group_node["values"];
for(index_t vi = 0; vi < group_vals.dtype().number_of_elements(); ++vi)
const auto values = group_node["values"].as_index_t_accessor();
const index_t numValues = values.number_of_elements();
group_values.reserve(numValues);
for(index_t vi = 0; vi < numValues; ++vi)
{
Node temp(DataType(group_vals.dtype().id(), 1),
(void*)group_vals.element_ptr(vi), true);
group_values.push_back(temp.to_index_t());
group_values.push_back(values[vi]);
}
}

Expand Down Expand Up @@ -7158,23 +7137,20 @@ mesh::adjset::to_maxshare(const Node &adjset,

std::vector<index_t> group_neighbors;
{
const Node &group_nvals = group_node["neighbors"];
for(index_t ni = 0; ni < group_nvals.dtype().number_of_elements(); ++ni)
const auto neighbors = group_node["neighbors"].as_index_t_accessor();
group_neighbors.reserve(neighbors.number_of_elements());
for(index_t ni = 0; ni < neighbors.number_of_elements(); ++ni)
{
Node temp(DataType(group_nvals.dtype().id(), 1),
(void*)group_nvals.element_ptr(ni), true);
group_neighbors.push_back(temp.to_index_t());
group_neighbors.push_back(neighbors[ni]);
}
}

std::vector<index_t> group_values;
{
const Node &group_vals = group_node["values"];
for(index_t vi = 0; vi < group_vals.dtype().number_of_elements(); ++vi)
const auto values = group_node["values"].as_index_t_accessor();
for(index_t vi = 0; vi < values.number_of_elements(); ++vi)
{
Node temp(DataType(group_vals.dtype().id(), 1),
(void*)group_vals.element_ptr(vi), true);
group_values.push_back(temp.to_index_t());
group_values.push_back(values[vi]);
}
}

Expand Down Expand Up @@ -7219,12 +7195,10 @@ mesh::adjset::to_maxshare(const Node &adjset,
for(const std::string &group_name : adjset_group_names)
{
const Node &group_node = adjset["groups"][group_name];
const Node &group_vals = group_node["values"];
for(index_t vi = 0; vi < group_vals.dtype().number_of_elements(); ++vi)
const auto values = group_node["values"].as_index_t_accessor();
for(index_t vi = 0; vi < values.number_of_elements(); ++vi)
{
Node temp(DataType(group_vals.dtype().id(), 1),
(void*)group_vals.element_ptr(vi), true);
const index_t group_entity = temp.to_index_t();
const index_t group_entity = values[vi];

auto &groupset_pair = groupset_values_map[entity_groupset_map[group_entity]];
std::vector<index_t> &groupset_valuelist = groupset_pair.first;
Expand Down
Loading