Skip to content
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

add option to also transform matset_values to silo #685

Merged
merged 6 commits into from
Feb 4, 2021
Merged
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
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@ and this project aspires to adhere to [Semantic Versioning](https://semver.org/s

#### Blueprint
- Added `conduit::blueprint::mesh::matset::to_silo()` which converts a valid blueprint matset to a node that contains arrays that follow Silo's sparse mix slot volume fraction representation.
- Added `conduit::blueprint::mesh::field::to_silo()` which converts a valid blueprint field and matset to a node that contains arrays that follow Silo's sparse mix slot volume fraction representation.
- Added `material_map` to `conduit::blueprint::mesh:matset::index`, to provide an explicit material name to id mapping.
- Added `mat_check` field to `blueprint::mesh::examples::venn`. This field encodes the material info in a scalar field and in the `matset_values` in a way that can be used to easily compare and verify proper construction in other tools.

### Fixed

#### Relay
- Fixed bug in the Relay IOHandle Basic that would create unnecessary "_json" schema files to be written to disk upon open().


## [0.6.0] - Released 2020-11-02
Expand Down
19 changes: 17 additions & 2 deletions src/docs/sphinx/blueprint_mesh.rst
Original file line number Diff line number Diff line change
Expand Up @@ -425,12 +425,12 @@ Uni-Buffer Material Sets
*********************************

A **uni-buffer** material set is one that presents all of its volume fraction data in a single data buffer.
In this case, the material set schema must include this volume fraction data buffer, a parallel buffer associating each volume with a material identifier, and an *Object* mapping of human-readable material names to each unique material identifier.
In this case, the material set schema must include this volume fraction data buffer, a parallel buffer associating each volume with a material identifier, and an *Object* that maps human-readable material names to unique integer material identifiers.
Additionally, the top-level of this schema is an **o2mrelation** that sources from the volume fraction/material identifier buffers and targets the material topology.
To conform to protocol, each ``matsets`` child of this type must be an *Object* that contains the following information:

* matsets/matset/topology: "topo"
* matsets/matset/material_map: (integer object)
* matsets/matset/material_map: (object with integer leaves)
* matsets/matset/material_ids: (integer array)
* matsets/matset/volume_fractions: (floating-point array)

Expand Down Expand Up @@ -465,11 +465,15 @@ Multi-Buffer Material Sets

A **multi-buffer** material set is a material set variant wherein the volume fraction data is split such that one buffer exists per material.
The schema for this variant dictates that each material be presented as an *Object* entry of the ``volume_fractions`` field with the material name as the entry key and the material volume fractions as the entry value.
**Multi-buffer** material sets also support an optional ``material_map``, which is an *Object* that maps human-readable material names to unique integer material identifiers.
If omitted, the map from material names to ids is inferred from the order of the material names in the ``volume_fractions`` node.

Optionally, the value for each such entry can be specified as an **o2mrelation** instead of a flat array to enable greater specification flexibility.
To conform to protocol, each ``matsets`` child of this type must be an *Object* that contains the following information:

* matsets/matset/topology: "topo"
* matsets/matset/volume_fractions: (object)
* matsets/matset/material_map: (optional, object with integer leaves)

The following diagram illustrates a simple **multi-buffer** material set example:

Expand All @@ -493,6 +497,9 @@ The following diagram illustrates a simple **multi-buffer** material set example
b:
values: [0, b0, b2, b1, 0]
indices: [1, 3, 2]
material_map: # (optional)
a: 0
b: 1


Material Set Indexing Variants
Expand Down Expand Up @@ -529,6 +536,10 @@ The following diagram illustrates a simple **element-dominant** material set exa
a: [a0, a1, 0]
b: [b0, b1, b2]
c: [0, 0, c2]
material_map: # (optional)
a: 0
b: 1
c: 2


Material-Dominant Material Sets
Expand Down Expand Up @@ -562,6 +573,10 @@ The following diagram illustrates a simple **material-dominant** material set ex
a: [0, 1]
b: [0, 1, 2]
c: [2]
material_map: # (optional)
a: 0
b: 1
c: 2


Fields
Expand Down
113 changes: 101 additions & 12 deletions src/libs/blueprint/conduit_blueprint_mesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2670,17 +2670,16 @@ mesh::generate_index(const Node &mesh,
idx_matset["topology"] = matset["topology"].as_string();

// support different flavors of valid matset protos
//
// if we have material_map (node with names to ids)
// use it in the index
if(matset.has_child("material_map"))
{
NodeConstIterator mats_itr = matset["material_map"].children();
while(mats_itr.has_next())
{
mats_itr.next();
idx_matset["materials"][mats_itr.name()];
}
idx_matset["material_map"] = matset["material_map"];
}
else if(matset.has_child("materials"))
{
// NOTE: I believe path is deprecated ...
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is correct; we either have and use the material_map provided, or we use a default ordering to create a temporary material_map for processing. Thus, this branch can be safely removed.

NodeConstIterator mats_itr = matset["materials"].children();
while(mats_itr.has_next())
{
Expand All @@ -2690,11 +2689,14 @@ mesh::generate_index(const Node &mesh,
}
else if(matset.has_child("volume_fractions"))
{
// we don't have material_map (node with names to ids)
// so mapping is implied from node order, construct
// an actual map that follows the implicit order
NodeConstIterator mats_itr = matset["volume_fractions"].children();
while(mats_itr.has_next())
{
mats_itr.next();
idx_matset["materials"][mats_itr.name()];
idx_matset["material_map"][mats_itr.name()] = mats_itr.index();
}
}
else // surprise!
Expand Down Expand Up @@ -4511,13 +4513,49 @@ mesh::topology::shape::verify(const Node &shape,
// blueprint::mesh::matset protocol interface
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// helper to verify a matset material_map
//-----------------------------------------------------------------------------
bool verify_matset_material_map(const std::string &protocol,
const conduit::Node &matset,
conduit::Node &info)
{
bool res = verify_object_field(protocol, matset, info, "material_map");

if(res)
{
// we already know we have an object, children should be
// integer scalars
NodeConstIterator itr = matset["material_map"].children();
while(itr.has_next())
{
const Node &curr_child = itr.next();
if(!curr_child.dtype().is_integer())
{
log::error(info,
protocol,
log::quote("material_map") +
"child " +
log::quote(itr.name()) +
" is not an integer leaf.");
res = false;
}
}
}

log::validation(info, res);

return res;
}

//-----------------------------------------------------------------------------
bool
mesh::matset::verify(const Node &matset,
Node &info)
{
const std::string protocol = "mesh::matset";
bool res = true, vfs_res = true;
bool mat_map_is_optional = true;
info.reset();

res &= verify_string_field(protocol, matset, info, "topology");
Expand All @@ -4535,12 +4573,11 @@ mesh::matset::verify(const Node &matset,
verify_number_field(protocol, matset, info, "volume_fractions"))
{
log::info(info, protocol, "detected uni-buffer matset");
// materials_map is not optional in this case, signal
// for opt check down the line
mat_map_is_optional = false;

vfs_res &= verify_integer_field(protocol, matset, info, "material_ids");
// TODO(JRC): Add a more in-depth verifier for 'material_map' that
// verifies that it's one level deep and that each child child houses
// an integer-style array.
vfs_res &= verify_object_field(protocol, matset, info, "material_map");
vfs_res &= blueprint::o2mrelation::verify(matset, info);

res &= vfs_res;
Expand Down Expand Up @@ -4574,6 +4611,47 @@ mesh::matset::verify(const Node &matset,
}
}

if(!mat_map_is_optional && !matset.has_child("material_map"))
{
log::error(info, protocol,
"'material_map' is missing (required for uni-buffer matsets) ");
res &= false;
}

if(matset.has_child("material_map"))
{
if(mat_map_is_optional)
{
log::optional(info, protocol, "includes material_map");
}

res &= verify_matset_material_map(protocol,matset,info);

// for cases where vfs are an object, we expect the material_map child
// names to be a subset of the volume_fractions child names
if(matset.has_child("volume_fractions") &&
matset["volume_fractions"].dtype().is_object())
{
NodeConstIterator itr = matset["material_map"].children();
while(itr.has_next())
{
itr.next();
std::string curr_name = itr.name();
if(!matset["volume_fractions"].has_child(curr_name))
{
std::ostringstream oss;
oss << "'material_map' hierarchy must be a subset of "
"'volume_fractions'. "
" 'volume_fractions' is missing child '"
<< curr_name
<<"' which exists in 'material_map`" ;
log::error(info, protocol,oss.str());
res &= false;
}
}
}
}

if(matset.has_child("element_ids"))
{
bool eids_res = true;
Expand Down Expand Up @@ -4675,7 +4753,18 @@ mesh::matset::index::verify(const Node &matset_idx,
// performed on the "materials" field.

res &= verify_string_field(protocol, matset_idx, info, "topology");
res &= verify_object_field(protocol, matset_idx, info, "materials");

// 2021-1-29 cyrush:
// prefer new "material_map" index spec, vs old "materials"
if(matset_idx.has_child("material_map"))
{
res &= verify_matset_material_map(protocol,matset_idx,info);
}
else
{
res &= verify_object_field(protocol, matset_idx, info, "materials");
}

res &= verify_string_field(protocol, matset_idx, info, "path");

log::validation(info, res);
Expand Down
15 changes: 14 additions & 1 deletion src/libs/blueprint/conduit_blueprint_mesh.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -414,7 +414,7 @@ namespace matset
// For details about the silo format, see documentation for
// 'DBPutMaterial' at:
// https://wci.llnl.gov/content/assets/docs/simulation/computer-codes/silo/LLNL-SM-654357.pdf
void CONDUIT_BLUEPRINT_API to_silo(const conduit::Node &n,
void CONDUIT_BLUEPRINT_API to_silo(const conduit::Node &matset,
conduit::Node &dest,
const float64 epsilon = CONDUIT_EPSILON);

Expand All @@ -441,6 +441,19 @@ namespace field
bool CONDUIT_BLUEPRINT_API verify(const conduit::Node &n,
conduit::Node &info);

//-------------------------------------------------------------------------
// Given a blueprint field and matset, converts the matset and the field
// values + matset_values to the silo style sparse mixed slot
// representation.
//
// For details about the silo format, see documentation for
// 'DBPutZZZVar' methods `mixvar` / `mixlen` params at:
// https://wci.llnl.gov/content/assets/docs/simulation/computer-codes/silo/LLNL-SM-654357.pdf
void CONDUIT_BLUEPRINT_API to_silo(const conduit::Node &field,
const conduit::Node &matset,
conduit::Node &dest,
const float64 epsilon = CONDUIT_EPSILON);

//-------------------------------------------------------------------------
// blueprint::mesh::field::index protocol interface
//-------------------------------------------------------------------------
Expand Down
Loading