From 4bbecd06cbba1323c081a879b4ae67148c85f488 Mon Sep 17 00:00:00 2001 From: Nicole Marsaglia Date: Thu, 9 May 2024 20:22:25 +0000 Subject: [PATCH 01/21] start a unit test --- src/tests/ascent/CMakeLists.txt | 1 + ...cent_material_inferface_reconstruction.cpp | 138 ++++++++++++++++++ 2 files changed, 139 insertions(+) create mode 100644 src/tests/ascent/t_ascent_material_inferface_reconstruction.cpp diff --git a/src/tests/ascent/CMakeLists.txt b/src/tests/ascent/CMakeLists.txt index a5f12ea9d..aca1d80ee 100644 --- a/src/tests/ascent/CMakeLists.txt +++ b/src/tests/ascent/CMakeLists.txt @@ -68,6 +68,7 @@ set(BASIC_TESTS t_ascent_smoke t_ascent_blueprint_reductions t_ascent_sampling t_ascent_uniform_grid + t_ascent_material_interface_reconstruction t_ascent_hola) set(DEVICE_TESTS t_ascent_execution_policies diff --git a/src/tests/ascent/t_ascent_material_inferface_reconstruction.cpp b/src/tests/ascent/t_ascent_material_inferface_reconstruction.cpp new file mode 100644 index 000000000..dbff96ce8 --- /dev/null +++ b/src/tests/ascent/t_ascent_material_inferface_reconstruction.cpp @@ -0,0 +1,138 @@ +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// +// Copyright (c) Lawrence Livermore National Security, LLC and other Ascent +// Project developers. See top-level LICENSE AND COPYRIGHT files for dates and +// other details. No copyright assignment is required to contribute to Ascent. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// + +//----------------------------------------------------------------------------- +/// +/// file: t_ascent_divergence.cpp +/// +//----------------------------------------------------------------------------- + + +#include "gtest/gtest.h" + +#include + +#include +#include + +#include + +#include "t_config.hpp" +#include "t_utils.hpp" + + + + +using namespace std; +using namespace conduit; +using namespace ascent; + + +index_t EXAMPLE_MESH_SIDE_DIM = 20; +float64 RADIUS = 10; + +//----------------------------------------------------------------------------- +TEST(ascent_vorticity, vel_vorticity) +{ + Node n; + ascent::about(n); + // only run this test if ascent was built with vtkm support + if(n["runtimes/ascent/vtkm/status"].as_string() == "disabled") + { + ASCENT_INFO("Ascent vtkm support disabled, skipping test"); + return; + } + + // + // Create an example mesh. + // + Node data, verify_info; + conduit::blueprint::mesh::examples::venn("full", + EXAMPLE_MESH_SIDE_DIM, + EXAMPLE_MESH_SIDE_DIM, + RADIUS, + data); + EXPECT_TRUE(conduit::blueprint::mesh::verify(data,verify_info)); + std::cerr << "VENN CONDUIT DATA: " << std::endl; + data.print(); + + ASCENT_INFO("Testing the MIR of a field"); + + + string output_path = prepare_output_dir(); + string output_file = conduit::utils::join_file_path(output_path,"tout_vorticity_vel"); + + // remove old images before rendering + remove_test_image(output_file); + + // + // Create the actions. + // + + conduit::Node pipelines; + // pipeline 1 + + pipelines["pl1/f1/type"] = "mir"; + conduit::Node ¶ms = pipelines["pl1/f1/params"]; + params["field"] = "vel_vorticity"; // name of the vector field + //params["output_name"] = "mag_vorticity"; // name of the output field + + conduit::Node scenes; + scenes["s1/plots/p1/type"] = "pseudocolor"; + scenes["s1/plots/p1/field"] = "mag_vorticity"; + scenes["s1/plots/p1/pipeline"] = "pl1"; + + scenes["s1/image_prefix"] = output_file; + + conduit::Node actions; + // add the pipeline + conduit::Node &add_pipelines = actions.append(); + add_pipelines["action"] = "add_pipelines"; + add_pipelines["pipelines"] = pipelines; + // add the scenes + conduit::Node &add_scenes= actions.append(); + add_scenes["action"] = "add_scenes"; + add_scenes["scenes"] = scenes; + + // + // Run Ascent + // + + Ascent ascent; + + Node ascent_opts; + ascent_opts["runtime/type"] = "ascent"; + ascent.open(ascent_opts); + ascent.publish(data); + ascent.execute(actions); + ascent.close(); + + // check that we created an image + EXPECT_TRUE(check_test_image(output_file)); + std::string msg = "An example of using the gradient filter " + "and plotting the magnitude."; + ASCENT_ACTIONS_DUMP(actions,output_file,msg); + +} + +//----------------------------------------------------------------------------- +int main(int argc, char* argv[]) +{ + int result = 0; + + ::testing::InitGoogleTest(&argc, argv); + + // allow override of the data size via the command line + if(argc == 2) + { + EXAMPLE_MESH_SIDE_DIM = atoi(argv[1]); + } + + result = RUN_ALL_TESTS(); + return result; +} + + From 14b1f175bc92828475982d6d32dc3025fc4db7f5 Mon Sep 17 00:00:00 2001 From: Nicole Marsaglia Date: Wed, 15 May 2024 20:39:08 +0000 Subject: [PATCH 02/21] change names --- src/tests/ascent/CMakeLists.txt | 2 +- src/tests/ascent/t_ascent_mir.cpp | 138 ++++++++++++++++++++++++++++++ 2 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 src/tests/ascent/t_ascent_mir.cpp diff --git a/src/tests/ascent/CMakeLists.txt b/src/tests/ascent/CMakeLists.txt index aca1d80ee..705baacaa 100644 --- a/src/tests/ascent/CMakeLists.txt +++ b/src/tests/ascent/CMakeLists.txt @@ -68,7 +68,7 @@ set(BASIC_TESTS t_ascent_smoke t_ascent_blueprint_reductions t_ascent_sampling t_ascent_uniform_grid - t_ascent_material_interface_reconstruction + t_ascent_mir t_ascent_hola) set(DEVICE_TESTS t_ascent_execution_policies diff --git a/src/tests/ascent/t_ascent_mir.cpp b/src/tests/ascent/t_ascent_mir.cpp new file mode 100644 index 000000000..dbff96ce8 --- /dev/null +++ b/src/tests/ascent/t_ascent_mir.cpp @@ -0,0 +1,138 @@ +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// +// Copyright (c) Lawrence Livermore National Security, LLC and other Ascent +// Project developers. See top-level LICENSE AND COPYRIGHT files for dates and +// other details. No copyright assignment is required to contribute to Ascent. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// + +//----------------------------------------------------------------------------- +/// +/// file: t_ascent_divergence.cpp +/// +//----------------------------------------------------------------------------- + + +#include "gtest/gtest.h" + +#include + +#include +#include + +#include + +#include "t_config.hpp" +#include "t_utils.hpp" + + + + +using namespace std; +using namespace conduit; +using namespace ascent; + + +index_t EXAMPLE_MESH_SIDE_DIM = 20; +float64 RADIUS = 10; + +//----------------------------------------------------------------------------- +TEST(ascent_vorticity, vel_vorticity) +{ + Node n; + ascent::about(n); + // only run this test if ascent was built with vtkm support + if(n["runtimes/ascent/vtkm/status"].as_string() == "disabled") + { + ASCENT_INFO("Ascent vtkm support disabled, skipping test"); + return; + } + + // + // Create an example mesh. + // + Node data, verify_info; + conduit::blueprint::mesh::examples::venn("full", + EXAMPLE_MESH_SIDE_DIM, + EXAMPLE_MESH_SIDE_DIM, + RADIUS, + data); + EXPECT_TRUE(conduit::blueprint::mesh::verify(data,verify_info)); + std::cerr << "VENN CONDUIT DATA: " << std::endl; + data.print(); + + ASCENT_INFO("Testing the MIR of a field"); + + + string output_path = prepare_output_dir(); + string output_file = conduit::utils::join_file_path(output_path,"tout_vorticity_vel"); + + // remove old images before rendering + remove_test_image(output_file); + + // + // Create the actions. + // + + conduit::Node pipelines; + // pipeline 1 + + pipelines["pl1/f1/type"] = "mir"; + conduit::Node ¶ms = pipelines["pl1/f1/params"]; + params["field"] = "vel_vorticity"; // name of the vector field + //params["output_name"] = "mag_vorticity"; // name of the output field + + conduit::Node scenes; + scenes["s1/plots/p1/type"] = "pseudocolor"; + scenes["s1/plots/p1/field"] = "mag_vorticity"; + scenes["s1/plots/p1/pipeline"] = "pl1"; + + scenes["s1/image_prefix"] = output_file; + + conduit::Node actions; + // add the pipeline + conduit::Node &add_pipelines = actions.append(); + add_pipelines["action"] = "add_pipelines"; + add_pipelines["pipelines"] = pipelines; + // add the scenes + conduit::Node &add_scenes= actions.append(); + add_scenes["action"] = "add_scenes"; + add_scenes["scenes"] = scenes; + + // + // Run Ascent + // + + Ascent ascent; + + Node ascent_opts; + ascent_opts["runtime/type"] = "ascent"; + ascent.open(ascent_opts); + ascent.publish(data); + ascent.execute(actions); + ascent.close(); + + // check that we created an image + EXPECT_TRUE(check_test_image(output_file)); + std::string msg = "An example of using the gradient filter " + "and plotting the magnitude."; + ASCENT_ACTIONS_DUMP(actions,output_file,msg); + +} + +//----------------------------------------------------------------------------- +int main(int argc, char* argv[]) +{ + int result = 0; + + ::testing::InitGoogleTest(&argc, argv); + + // allow override of the data size via the command line + if(argc == 2) + { + EXAMPLE_MESH_SIDE_DIM = atoi(argv[1]); + } + + result = RUN_ALL_TESTS(); + return result; +} + + From 137b584c54d6a229f1dfa001b96e583aaac528cb Mon Sep 17 00:00:00 2001 From: Nicole Marsaglia Date: Thu, 23 May 2024 23:45:07 +0000 Subject: [PATCH 03/21] start of mir --- .../runtimes/ascent_vtkh_data_adapter.cpp | 196 +++++++++++++++++- .../runtimes/ascent_vtkh_data_adapter.hpp | 7 + .../flow_filters/ascent_runtime_filters.cpp | 1 + .../ascent_runtime_vtkh_filters.cpp | 152 ++++++++++++++ .../ascent_runtime_vtkh_filters.hpp | 12 ++ src/tests/ascent/t_ascent_mir.cpp | 14 +- 6 files changed, 374 insertions(+), 8 deletions(-) diff --git a/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp b/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp index 87f8915c6..896d8b2a7 100644 --- a/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp +++ b/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp @@ -707,7 +707,7 @@ VTKHDataAdapter::BlueprintToVTKmDataSet(const Node &node, { // add all of the fields: NodeConstIterator itr = node["fields"].children(); - std::string field_name; + std::string field_name; while(itr.has_next()) { @@ -762,6 +762,31 @@ VTKHDataAdapter::BlueprintToVTKmDataSet(const Node &node, } } } + + if(node.has_child("matsets")) + { + // add all of the materials: + NodeConstIterator itr = node["matsets"].children(); + std::string matset_name; + while(itr.has_next()) + { + const Node &n_matset = itr.next(); + matset_name = itr.name(); + if(n_matset["topology"].as_string() != topo_name) + { + // these are not the materials we are looking for + continue; + } + std::cerr << "calling AddMatSets on: " << matset_name << " w/topo: " << topo_name << std::endl; + AddMatSets(matset_name, + n_matset, + topo_name, + neles, + result, + zero_copy); + + } + } return result; } @@ -1846,6 +1871,175 @@ VTKHDataAdapter::AddVectorField(const std::string &field_name, } +void +VTKHDataAdapter::AddMatSets(const std::string &matset_name, + const Node &n_matset, + const std::string &topo_name, + int neles, + vtkm::cont::DataSet *dset, + bool zero_copy) // attempt to zero copy +{ + + int num_materials = n_matset["volume_fractions"].number_of_children(); + if(num_materials == 0) + ASCENT_ERROR("No volume fractions were defined for matset: " << matset_name); + + std::string assoc_str = "element"; + NodeConstIterator itr = n_matset["volume_fractions"].children(); + std::string material_name; + while(itr.has_next()) + { + const Node &n_material = itr.next(); + material_name = itr.name(); + std::string field_name = matset_name + "_VF_" + material_name; + int num_vals = n_material.dtype().number_of_elements(); + if(num_vals != neles ) + { + ASCENT_ERROR("Number of vf values " + << num_vals + << " for material " + << material_name + << " does not equal number of cells " + << neles); + } + + try + { + bool supported_type = false; + + // we compile vtk-h with fp types + if(n_material.dtype().is_float32()) + { + index_t stride = n_material.dtype().stride(); + index_t element_stride = stride / sizeof(float32); + //std::cout << "material name: " << field_name << " " + // << " byte stride: " << stride + // << " element_stride: " << element_stride << std::endl; + // if element_stride is evenly divided by native, we are good to + // use vtk m array handles + if( stride % sizeof(float32) == 0 ) + { + // in this case we can use a strided array handle + dset->AddField(detail::GetField(n_material, + field_name, + assoc_str, + topo_name, + element_stride, + zero_copy)); + supported_type = true; + } + } + else if(n_material.dtype().is_float64()) + { + // check that the byte stride is a multiple of native stride + index_t stride = n_material.dtype().stride(); + index_t element_stride = stride / sizeof(float64); + //std::cout << "material name: " << field_name << " " + // << " byte stride: " << stride + // << " element_stride: " << element_stride << std::endl; + // if element_stride is evenly divided by native, we are good to + // use vtk m array handles + if( stride % sizeof(float64) == 0 ) + { + // in this case we can use a strided array handle + dset->AddField(detail::GetField(n_material, + field_name, + assoc_str, + topo_name, + element_stride, + zero_copy)); + supported_type = true; + } + } + // *********************************************************************** + // NOTE: TODO OUR VTK-M is not compiled with int32 and int64 support ... + // *********************************************************************** + // These cases fail and provide this error message: + // Execution failed with vtkm: Could not find appropriate cast for array in CastAndCall. + // Array: valueType=x storageType=N4vtkm4cont15StorageTagBasicE 27 values occupying 216 bytes [0 1 2 ... 24 25 26] + // TypeList: N4vtkm4ListIJfdEEE + // *********************************************************************** + // + // else if(n_material.dtype().is_int32()) + // { + // // check that the byte stride is a multiple of native stride + // index_t stride = n_material.dtype().stride(); + // index_t element_stride = stride / sizeof(int32); + // //std::cout << "material name: " << field_name << " " + // // << " byte stride: " << stride + // // << " element_stride: " << element_stride << std::endl; + // // if element_stride is evenly divided by native, we are good to + // // use vtk m array handles + // if( stride % sizeof(int32) == 0 ) + // { + // // in this case we can use a strided array handle + // dset->AddField(detail::Getmaterial(n_material, + // field_name, + // assoc_str, + // topo_name, + // element_stride, + // zero_copy)); + // supported_type = true; + // } + // } + // else if(n_material.dtype().is_int64()) + // { + // // check that the byte stride is a multiple of native stride + // index_t stride = n_material.dtype().stride(); + // index_t element_stride = stride / sizeof(int64); + // //std::cout << "material name: " << field_name << " " + // // << " byte stride: " << stride + // // << " element_stride: " << element_stride << std::endl; + // // if element_stride is evenly divided by native, we are good to + // // use vtk m array handles + // if( stride % sizeof(int64) == 0 ) + // { + // // in this case we can use a strided array handle + // dset->AddField(detail::Getmaterial(n_material, + // field_name, + // assoc_str, + // topo_name, + // element_stride, + // zero_copy)); + // supported_type = true; + // } + // } + + // vtk-m cant support zero copy for this layout or was not compiled to expose this datatype + // use float64 by default + if(!supported_type) + { + // std::cout << "WE ARE IN UNSUPPORTED DATA TYPE: " + // << n_material.dtype().name() << std::endl; + + // convert to float64, we use this as a comprise to cover the widest range + vtkm::cont::ArrayHandle vtkm_arr; + vtkm_arr.Allocate(num_vals); + + // TODO -- FUTURE: Do this conversion w/ device if on device + void *ptr = (void*) vtkh::GetVTKMPointer(vtkm_arr); + Node n_tmp; + n_tmp.set_external(DataType::float64(num_vals),ptr); + n_material.to_float64_array(n_tmp); + + // add material to dataset + dset->AddField(vtkm::cont::Field(field_name.c_str(), + vtkm::cont::Field::Association::Cells, + vtkm_arr)); + } + // else + // { + // std::cout << "SUPPORTED DATA TYPE: " + // << n_material.dtype().name() << std::endl; + // } + } + catch (vtkm::cont::Error error) + { + ASCENT_ERROR("VTKm exception:" << error.GetMessage()); + } + } +} + std::string GetBlueprintCellName(vtkm::UInt8 shape_id) { diff --git a/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.hpp b/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.hpp index ca15124b4..f458c8bcd 100644 --- a/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.hpp +++ b/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.hpp @@ -157,6 +157,13 @@ class ASCENT_API VTKHDataAdapter const int dims, bool zero_copy); + static void AddMatSets(const std::string &matset_name, + const conduit::Node &n_matset, + const std::string &topo_name, + int neles, + vtkm::cont::DataSet *dset, + bool zero_copy); + static bool VTKmTopologyToBlueprint(conduit::Node &output, const vtkm::cont::DataSet &data_set, const std::string &topo_name, diff --git a/src/libs/ascent/runtimes/flow_filters/ascent_runtime_filters.cpp b/src/libs/ascent/runtimes/flow_filters/ascent_runtime_filters.cpp index 0ffb5265e..559eca002 100644 --- a/src/libs/ascent/runtimes/flow_filters/ascent_runtime_filters.cpp +++ b/src/libs/ascent/runtimes/flow_filters/ascent_runtime_filters.cpp @@ -144,6 +144,7 @@ register_builtin() AscentRuntime::register_filter_type("transforms","warpx_streamline"); AscentRuntime::register_filter_type("transforms","uniform_grid"); AscentRuntime::register_filter_type("extracts", "vtk"); + AscentRuntime::register_filter_type("transforms","mir"); AscentRuntime::register_filter_type("extracts", "xray"); diff --git a/src/libs/ascent/runtimes/flow_filters/ascent_runtime_vtkh_filters.cpp b/src/libs/ascent/runtimes/flow_filters/ascent_runtime_vtkh_filters.cpp index 81cd64eb3..b8848d773 100644 --- a/src/libs/ascent/runtimes/flow_filters/ascent_runtime_vtkh_filters.cpp +++ b/src/libs/ascent/runtimes/flow_filters/ascent_runtime_vtkh_filters.cpp @@ -4954,6 +4954,158 @@ VTKHVTKFileExtract::execute() } +//----------------------------------------------------------------------------- + +VTKHMIR::VTKHMIR() +:Filter() +{ +// empty +} + +//----------------------------------------------------------------------------- +VTKHMIR::~VTKHMIR() +{ +// empty +} + +//----------------------------------------------------------------------------- +void +VTKHMIR::declare_interface(Node &i) +{ + i["type_name"] = "vtkh_mir"; + i["port_names"].append() = "in"; + i["output_port"] = "true"; +} + +//----------------------------------------------------------------------------- +bool +VTKHMIR::verify_params(const conduit::Node ¶ms, + conduit::Node &info) +{ + info.reset(); + + bool res = check_string("field",params, info, true); + res &= check_numeric("bins",params, info, false, true); + res &= check_numeric("sample_rate",params, info, false, true); + + std::vector valid_paths; + valid_paths.push_back("field"); + valid_paths.push_back("bins"); + valid_paths.push_back("sample_rate"); + + std::string surprises = surprise_check(valid_paths, params); + + if(surprises != "") + { + res = false; + info["errors"].append() = surprises; + } + + return res; +} + +//----------------------------------------------------------------------------- +void +VTKHMIR::execute() +{ + + if(!input(0).check_type()) + { + ASCENT_ERROR("vtkh_hist_sampling input must be a data object"); + } + + DataObject *data_object = input(0); + if(!data_object->is_valid()) + { + set_output(data_object); + return; + } + std::shared_ptr collection = data_object->as_vtkh_collection(); + + std::string field_name = params()["field"].as_string(); + if(!collection->has_field(field_name)) + { + bool throw_error = false; + detail::field_error(field_name, this->name(), collection, throw_error); + // this creates a data object with an invalid soource + set_output(new DataObject()); + return; + } + + std::string topo_name = collection->field_topology(field_name); + + vtkh::DataSet &data = collection->dataset_by_topology(topo_name); + + float sample_rate = .1f; + if(params().has_path("sample_rate")) + { + sample_rate = get_float32(params()["sample_rate"], data_object); + if(sample_rate <= 0.f || sample_rate >= 1.f) + { + ASCENT_ERROR("vtkh_hist_sampling 'sample_rate' value '"<copy_without_topology(topo_name); + new_coll->add(*hist_output, topo_name); + // re wrap in data object + DataObject *res = new DataObject(new_coll); + delete hist_output; + set_output(data_object); +} //----------------------------------------------------------------------------- }; diff --git a/src/libs/ascent/runtimes/flow_filters/ascent_runtime_vtkh_filters.hpp b/src/libs/ascent/runtimes/flow_filters/ascent_runtime_vtkh_filters.hpp index 22149b2f6..77b4a8e21 100644 --- a/src/libs/ascent/runtimes/flow_filters/ascent_runtime_vtkh_filters.hpp +++ b/src/libs/ascent/runtimes/flow_filters/ascent_runtime_vtkh_filters.hpp @@ -490,6 +490,18 @@ class ASCENT_API VTKHVTKFileExtract : public ::flow::Filter }; +//----------------------------------------------------------------------------- +class ASCENT_API VTKHMIR : public ::flow::Filter +{ +public: + VTKHMIR(); + virtual ~VTKHMIR(); + + virtual void declare_interface(conduit::Node &i); + virtual bool verify_params(const conduit::Node ¶ms, + conduit::Node &info); + virtual void execute(); +}; }; diff --git a/src/tests/ascent/t_ascent_mir.cpp b/src/tests/ascent/t_ascent_mir.cpp index dbff96ce8..489f270b1 100644 --- a/src/tests/ascent/t_ascent_mir.cpp +++ b/src/tests/ascent/t_ascent_mir.cpp @@ -93,9 +93,9 @@ TEST(ascent_vorticity, vel_vorticity) add_pipelines["action"] = "add_pipelines"; add_pipelines["pipelines"] = pipelines; // add the scenes - conduit::Node &add_scenes= actions.append(); - add_scenes["action"] = "add_scenes"; - add_scenes["scenes"] = scenes; + // conduit::Node &add_scenes= actions.append(); + // add_scenes["action"] = "add_scenes"; + // add_scenes["scenes"] = scenes; // // Run Ascent @@ -111,10 +111,10 @@ TEST(ascent_vorticity, vel_vorticity) ascent.close(); // check that we created an image - EXPECT_TRUE(check_test_image(output_file)); - std::string msg = "An example of using the gradient filter " - "and plotting the magnitude."; - ASCENT_ACTIONS_DUMP(actions,output_file,msg); +// EXPECT_TRUE(check_test_image(output_file)); +// std::string msg = "An example of using the gradient filter " +// "and plotting the magnitude."; +// ASCENT_ACTIONS_DUMP(actions,output_file,msg); } From 2b37545c1f46b19890dc0ff71dde22937ed450b4 Mon Sep 17 00:00:00 2001 From: Nicole Marsaglia Date: Fri, 24 May 2024 23:28:29 +0000 Subject: [PATCH 04/21] add materials as fields -- think about renaming --- .../ascent_runtime_vtkh_filters.cpp | 89 +++++-------------- src/libs/vtkh/filters/CMakeLists.txt | 2 + src/libs/vtkh/filters/MIR.cpp | 71 +++++++++++++++ src/libs/vtkh/filters/MIR.hpp | 27 ++++++ 4 files changed, 120 insertions(+), 69 deletions(-) create mode 100644 src/libs/vtkh/filters/MIR.cpp create mode 100644 src/libs/vtkh/filters/MIR.hpp diff --git a/src/libs/ascent/runtimes/flow_filters/ascent_runtime_vtkh_filters.cpp b/src/libs/ascent/runtimes/flow_filters/ascent_runtime_vtkh_filters.cpp index b8848d773..b9959a99a 100644 --- a/src/libs/ascent/runtimes/flow_filters/ascent_runtime_vtkh_filters.cpp +++ b/src/libs/ascent/runtimes/flow_filters/ascent_runtime_vtkh_filters.cpp @@ -4985,13 +4985,9 @@ VTKHMIR::verify_params(const conduit::Node ¶ms, info.reset(); bool res = check_string("field",params, info, true); - res &= check_numeric("bins",params, info, false, true); - res &= check_numeric("sample_rate",params, info, false, true); std::vector valid_paths; valid_paths.push_back("field"); - valid_paths.push_back("bins"); - valid_paths.push_back("sample_rate"); std::string surprises = surprise_check(valid_paths, params); @@ -5035,75 +5031,30 @@ VTKHMIR::execute() std::string topo_name = collection->field_topology(field_name); vtkh::DataSet &data = collection->dataset_by_topology(topo_name); + std::cerr << "data going into ascent_runtime_vtkh_filters: " << std::endl; + data.PrintSummary(std::cerr); - float sample_rate = .1f; - if(params().has_path("sample_rate")) - { - sample_rate = get_float32(params()["sample_rate"], data_object); - if(sample_rate <= 0.f || sample_rate >= 1.f) - { - ASCENT_ERROR("vtkh_hist_sampling 'sample_rate' value '"<copy_without_topology(topo_name); - new_coll->add(*hist_output, topo_name); - // re wrap in data object - DataObject *res = new DataObject(new_coll); - delete hist_output; + //// we need to pass through the rest of the topologies, untouched, + //// and add the result of this operation + //VTKHCollection *new_coll = collection->copy_without_topology(topo_name); + //new_coll->add(*hist_output, topo_name); + //// re wrap in data object + //DataObject *res = new DataObject(new_coll); + //delete hist_output; set_output(data_object); } diff --git a/src/libs/vtkh/filters/CMakeLists.txt b/src/libs/vtkh/filters/CMakeLists.txt index d8a51d723..efb7d56c1 100644 --- a/src/libs/vtkh/filters/CMakeLists.txt +++ b/src/libs/vtkh/filters/CMakeLists.txt @@ -19,6 +19,7 @@ set(vtkh_filters_headers Lagrangian.hpp MarchingCubes.hpp MeshQuality.hpp + MIR.hpp ParticleAdvection.hpp ParticleMerging.hpp PointAverage.hpp @@ -57,6 +58,7 @@ set(vtkh_filters_sources Lagrangian.cpp MarchingCubes.cpp MeshQuality.cpp + MIR.cpp ParticleAdvection.cpp ParticleMerging.cpp PointAverage.cpp diff --git a/src/libs/vtkh/filters/MIR.cpp b/src/libs/vtkh/filters/MIR.cpp new file mode 100644 index 000000000..fd008eb2c --- /dev/null +++ b/src/libs/vtkh/filters/MIR.cpp @@ -0,0 +1,71 @@ +#include "MIR.hpp" + +#include + +namespace vtkh +{ + +MIR::MIR() +{ + +} + +MIR::~MIR() +{ + +} + +void +MIR::SetField(const std::string field_name) +{ + m_field_name = field_name; +} + +void +MIR::PreExecute() +{ + Filter::PreExecute(); + Filter::CheckForRequiredField(m_field_name); +} + +void +MIR::PostExecute() +{ + Filter::PostExecute(); +} + +void MIR::DoExecute() +{ + + ClipField max_clip; + max_clip.SetInput(this->m_input); + max_clip.SetField(m_field_name); + max_clip.SetClipValue(m_range.Max); + max_clip.SetInvertClip(true); + max_clip.Update(); + + DataSet *clipped = max_clip.GetOutput(); + + ClipField min_clip; + min_clip.SetInput(clipped); + min_clip.SetField(m_field_name); + min_clip.SetClipValue(m_range.Min); + min_clip.Update(); + + delete clipped; + DataSet *iso = min_clip.GetOutput(); + CleanGrid cleaner; + cleaner.SetInput(iso); + cleaner.Update(); + delete iso; + this->m_output = cleaner.GetOutput(); + +} + +std::string +MIR::GetName() const +{ + return "vtkh::MIR"; +} + +} // namespace vtkh diff --git a/src/libs/vtkh/filters/MIR.hpp b/src/libs/vtkh/filters/MIR.hpp new file mode 100644 index 000000000..41942daf7 --- /dev/null +++ b/src/libs/vtkh/filters/MIR.hpp @@ -0,0 +1,27 @@ +#ifndef VTK_H_MIR_HPP +#define VTK_H_MIR_HPP + +#include +#include +#include + +namespace vtkh +{ + +class VTKH_API MIR: public Filter +{ +public: + MIR(); + virtual ~MIR(); + std::string GetName() const override; + void SetField(const std::string field_name); +protected: + void PreExecute() override; + void PostExecute() override; + void DoExecute() override; + + std::string m_field_name; +}; + +} //namespace vtkh +#endif From d08ac4bd6ad8757c35a52339fff79a84c0d5e3d3 Mon Sep 17 00:00:00 2001 From: Nicole Marsaglia Date: Wed, 29 May 2024 23:49:10 +0000 Subject: [PATCH 05/21] stop playing with vtkm data and lets live in conduit land. Add the calculated vtkm fields in the data adapter rather than filter for my sanity. --- .../runtimes/ascent_vtkh_data_adapter.cpp | 336 ++++++++++-------- .../ascent_runtime_vtkh_filters.cpp | 21 +- src/libs/vtkh/filters/MIR.cpp | 107 ++++-- src/libs/vtkh/filters/MIR.hpp | 4 +- src/tests/ascent/t_ascent_mir.cpp | 6 +- 5 files changed, 291 insertions(+), 183 deletions(-) diff --git a/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp b/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp index 896d8b2a7..fa8806949 100644 --- a/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp +++ b/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp @@ -486,6 +486,124 @@ bool allEqual(std::vector const &v) return std::adjacent_find(v.begin(), v.end(), std::not_equal_to()) == v.end(); } + + +template +void GetMatSetLength(const conduit::Node &node, //materials["matset"] + const std::string &length_name, + const std::string &offsets_name, + const std::string &topo_str, + int &total, + const int neles, + vtkm::cont::Field &length, + vtkm::cont::Field &offsets) +{ + vtkm::CopyFlag copy = vtkm::CopyFlag::On; + + vtkm::cont::Field::Association vtkm_assoc = vtkm::cont::Field::Association::Cells; + + std::vector v_length(neles,0); + std::vector v_offsets(neles,0); + for(int i = 0; i < neles; ++i) + { + NodeConstIterator itr = node["volume_fractions"].children(); + std::string material_name; + + //increase length when a material vf value > 0 + while(itr.has_next()) + { + const Node &n_material = itr.next(); + const S *data = n_material.value(); + if(data[i] > 0) + v_length[i] = v_length[i] + 1; + } + } + + //calc offset of length and total length + int l_total = 0; + for(int i = 0; i < neles-1; ++i) + { + v_offsets[i+1] = v_offsets[i] + v_length[i]; + l_total += v_length[i]; + } + l_total += v_length[neles-1]; + total = l_total; + + const T *length_ptr = v_length.data(); + + length = vtkm::cont::make_Field(length_name, + vtkm_assoc, + length_ptr, + neles, + copy); + + const T *offsets_ptr = v_offsets.data(); + + offsets = vtkm::cont::make_Field(offsets_name, + vtkm_assoc, + offsets_ptr, + neles, + copy); + +} + +template +void GetMatSetIDsAndVFs(const conduit::Node &node, //materials["matset"] + const std::string &ids_name, + const std::string &vfs_name, + const std::string &topo_str, + const int total, + const int neles, + vtkm::cont::Field &offsets, + vtkm::cont::Field &ids, + vtkm::cont::Field &vfs) +{ + vtkm::CopyFlag copy = vtkm::CopyFlag::On; + vtkm::cont::Field::Association vtkm_assoc = vtkm::cont::Field::Association::WholeDataSet; + + vtkm::cont::ArrayHandle ah_offsets; + offsets.GetData().AsArrayHandle(ah_offsets); + std::vector v_ids(total,0); + std::vector v_vfs(total,0); + + int num_materials = node["volume_fractions"].number_of_children(); + for(int i = 0; i < neles; ++i) + { + int offset = ah_offsets.ReadPortal().Get(i); + const Node &n_materials = node["volume_fractions"]; + std::string material_name; + + for(int j = 0; j < num_materials; ++j) + { + const Node &n_material = n_materials.child(j); + const S *data = n_material.value(); + if(data[i] > 0) + { + v_ids[offset] = j; + v_vfs[offset] = data[i]; + offset++; + } + } + } + + const T *ids_ptr = v_ids.data(); + + ids = vtkm::cont::make_Field(ids_name, + vtkm_assoc, + ids_ptr, + total, + copy); + + const S *vfs_ptr = v_vfs.data(); + + vfs = vtkm::cont::make_Field(vfs_name, + vtkm_assoc, + vfs_ptr, + total, + copy); + +} + }; //----------------------------------------------------------------------------- // -- end detail:: -- @@ -1887,157 +2005,85 @@ VTKHDataAdapter::AddMatSets(const std::string &matset_name, std::string assoc_str = "element"; NodeConstIterator itr = n_matset["volume_fractions"].children(); std::string material_name; - while(itr.has_next()) - { - const Node &n_material = itr.next(); - material_name = itr.name(); - std::string field_name = matset_name + "_VF_" + material_name; - int num_vals = n_material.dtype().number_of_elements(); - if(num_vals != neles ) - { - ASCENT_ERROR("Number of vf values " - << num_vals - << " for material " - << material_name - << " does not equal number of cells " - << neles); - } - - try - { - bool supported_type = false; - - // we compile vtk-h with fp types - if(n_material.dtype().is_float32()) - { - index_t stride = n_material.dtype().stride(); - index_t element_stride = stride / sizeof(float32); - //std::cout << "material name: " << field_name << " " - // << " byte stride: " << stride - // << " element_stride: " << element_stride << std::endl; - // if element_stride is evenly divided by native, we are good to - // use vtk m array handles - if( stride % sizeof(float32) == 0 ) - { - // in this case we can use a strided array handle - dset->AddField(detail::GetField(n_material, - field_name, - assoc_str, - topo_name, - element_stride, - zero_copy)); - supported_type = true; - } - } - else if(n_material.dtype().is_float64()) - { - // check that the byte stride is a multiple of native stride - index_t stride = n_material.dtype().stride(); - index_t element_stride = stride / sizeof(float64); - //std::cout << "material name: " << field_name << " " - // << " byte stride: " << stride - // << " element_stride: " << element_stride << std::endl; - // if element_stride is evenly divided by native, we are good to - // use vtk m array handles - if( stride % sizeof(float64) == 0 ) - { - // in this case we can use a strided array handle - dset->AddField(detail::GetField(n_material, - field_name, - assoc_str, - topo_name, - element_stride, - zero_copy)); - supported_type = true; - } - } - // *********************************************************************** - // NOTE: TODO OUR VTK-M is not compiled with int32 and int64 support ... - // *********************************************************************** - // These cases fail and provide this error message: - // Execution failed with vtkm: Could not find appropriate cast for array in CastAndCall. - // Array: valueType=x storageType=N4vtkm4cont15StorageTagBasicE 27 values occupying 216 bytes [0 1 2 ... 24 25 26] - // TypeList: N4vtkm4ListIJfdEEE - // *********************************************************************** - // - // else if(n_material.dtype().is_int32()) - // { - // // check that the byte stride is a multiple of native stride - // index_t stride = n_material.dtype().stride(); - // index_t element_stride = stride / sizeof(int32); - // //std::cout << "material name: " << field_name << " " - // // << " byte stride: " << stride - // // << " element_stride: " << element_stride << std::endl; - // // if element_stride is evenly divided by native, we are good to - // // use vtk m array handles - // if( stride % sizeof(int32) == 0 ) - // { - // // in this case we can use a strided array handle - // dset->AddField(detail::Getmaterial(n_material, - // field_name, - // assoc_str, - // topo_name, - // element_stride, - // zero_copy)); - // supported_type = true; - // } - // } - // else if(n_material.dtype().is_int64()) - // { - // // check that the byte stride is a multiple of native stride - // index_t stride = n_material.dtype().stride(); - // index_t element_stride = stride / sizeof(int64); - // //std::cout << "material name: " << field_name << " " - // // << " byte stride: " << stride - // // << " element_stride: " << element_stride << std::endl; - // // if element_stride is evenly divided by native, we are good to - // // use vtk m array handles - // if( stride % sizeof(int64) == 0 ) - // { - // // in this case we can use a strided array handle - // dset->AddField(detail::Getmaterial(n_material, - // field_name, - // assoc_str, - // topo_name, - // element_stride, - // zero_copy)); - // supported_type = true; - // } - // } - - // vtk-m cant support zero copy for this layout or was not compiled to expose this datatype - // use float64 by default - if(!supported_type) - { - // std::cout << "WE ARE IN UNSUPPORTED DATA TYPE: " - // << n_material.dtype().name() << std::endl; - - // convert to float64, we use this as a comprise to cover the widest range - vtkm::cont::ArrayHandle vtkm_arr; - vtkm_arr.Allocate(num_vals); - // TODO -- FUTURE: Do this conversion w/ device if on device - void *ptr = (void*) vtkh::GetVTKMPointer(vtkm_arr); - Node n_tmp; - n_tmp.set_external(DataType::float64(num_vals),ptr); - n_material.to_float64_array(n_tmp); + //add each material of specified matset to vtkm dataset + const Node &n_material = itr.next(); + material_name = itr.name(); + std::string field_name = matset_name + "_" + material_name; + int num_vals = n_material.dtype().number_of_elements(); + if(num_vals != neles ) + { + ASCENT_ERROR("Number of vf values " + << num_vals + << " for material " + << material_name + << " does not equal number of cells " + << neles); + } + + try + { + bool supported_type = false; - // add material to dataset - dset->AddField(vtkm::cont::Field(field_name.c_str(), - vtkm::cont::Field::Association::Cells, - vtkm_arr)); - } - // else - // { - // std::cout << "SUPPORTED DATA TYPE: " - // << n_material.dtype().name() << std::endl; - // } + // we compile vtk-h with fp types + if(n_material.dtype().is_float32()) + { + //add materials directly + //dset->AddField(detail::GetField(n_material, + // field_name, + // assoc_str, + // topo_name, + // element_stride, + // zero_copy)); + supported_type = true; + //add calculated material fields for vtkm + int total; + vtkm::cont::Field length, offsets, ids, vfs; + detail::GetMatSetLength(n_matset, "lengths", "offsets", topo_name, total,neles, length, offsets); + detail::GetMatSetIDsAndVFs(n_matset, "ids", "vfs", topo_name,total, neles, offsets, ids, vfs); + dset->AddField(length); + dset->AddField(offsets); + dset->AddField(ids); + dset->AddField(vfs); + //std::cerr << "total: " << total << std::endl; } - catch (vtkm::cont::Error error) + else if(n_material.dtype().is_float64()) { - ASCENT_ERROR("VTKm exception:" << error.GetMessage()); + //add materials directly + //dset->AddField(detail::GetField(n_material, + // field_name, + // assoc_str, + // topo_name, + // element_stride, + // zero_copy)); + supported_type = true; + //add calculated material fields for vtkm + int total; + vtkm::cont::Field length, offsets, ids, vfs; + detail::GetMatSetLength(n_matset, "lengths", "offsets", topo_name, total,neles, length, offsets); + detail::GetMatSetIDsAndVFs(n_matset, "ids", "vfs", topo_name,total, neles, offsets, ids, vfs); + dset->AddField(length); + dset->AddField(offsets); + dset->AddField(ids); + dset->AddField(vfs); + //std::cerr << "total: " << total << std::endl; + //std::cerr << "float64 length: " << std::endl; + //length.PrintSummary(std::cerr); + //std::cerr << "float64 offsets: " << std::endl; + //offsets.PrintSummary(std::cerr); + //std::cerr << "float64 ids: " << std::endl; + //ids.PrintSummary(std::cerr); + //std::cerr << "float64 vfs: " << std::endl; + //vfs.PrintSummary(std::cerr); + + //calculate vf and ids } } + catch (vtkm::cont::Error error) + { + ASCENT_ERROR("VTKm exception:" << error.GetMessage()); + } + } std::string diff --git a/src/libs/ascent/runtimes/flow_filters/ascent_runtime_vtkh_filters.cpp b/src/libs/ascent/runtimes/flow_filters/ascent_runtime_vtkh_filters.cpp index b9959a99a..d245d105c 100644 --- a/src/libs/ascent/runtimes/flow_filters/ascent_runtime_vtkh_filters.cpp +++ b/src/libs/ascent/runtimes/flow_filters/ascent_runtime_vtkh_filters.cpp @@ -76,6 +76,7 @@ #include #include #include +#include #include #include #include @@ -4984,9 +4985,11 @@ VTKHMIR::verify_params(const conduit::Node ¶ms, { info.reset(); - bool res = check_string("field",params, info, true); + bool res = check_string("matset",params, info, true); + res = check_string("field",params, info, true); std::vector valid_paths; + valid_paths.push_back("matset"); valid_paths.push_back("field"); std::string surprises = surprise_check(valid_paths, params); @@ -5007,7 +5010,7 @@ VTKHMIR::execute() if(!input(0).check_type()) { - ASCENT_ERROR("vtkh_hist_sampling input must be a data object"); + ASCENT_ERROR("vtkh_MIR input must be a data object"); } DataObject *data_object = input(0); @@ -5018,6 +5021,7 @@ VTKHMIR::execute() } std::shared_ptr collection = data_object->as_vtkh_collection(); + std::string matset_name = params()["matset"].as_string(); std::string field_name = params()["field"].as_string(); if(!collection->has_field(field_name)) { @@ -5034,10 +5038,10 @@ VTKHMIR::execute() std::cerr << "data going into ascent_runtime_vtkh_filters: " << std::endl; data.PrintSummary(std::cerr); - //vtkh::HistSampling hist; + vtkh::MIR mir; - //hist.SetInput(&data); - //hist.SetField(field_name); + mir.SetInput(&data); + mir.SetMatSet(matset_name); //hist.SetNumBins(bins); //hist.SetSamplingPercent(sample_rate); //if(ghost_field != "") @@ -5045,8 +5049,10 @@ VTKHMIR::execute() // hist.SetGhostField(ghost_field); //} - //hist.Update(); - //vtkh::DataSet *hist_output = hist.GetOutput(); + mir.Update(); + vtkh::DataSet *mir_output = mir.GetOutput(); + std::cerr << "vkth::dataset MIR: " << std::endl; + mir_output->PrintSummary(std::cerr); //// we need to pass through the rest of the topologies, untouched, //// and add the result of this operation @@ -5065,6 +5071,7 @@ VTKHMIR::execute() //----------------------------------------------------------------------------- + //----------------------------------------------------------------------------- }; //----------------------------------------------------------------------------- diff --git a/src/libs/vtkh/filters/MIR.cpp b/src/libs/vtkh/filters/MIR.cpp index fd008eb2c..16d36e6ee 100644 --- a/src/libs/vtkh/filters/MIR.cpp +++ b/src/libs/vtkh/filters/MIR.cpp @@ -1,10 +1,52 @@ #include "MIR.hpp" #include +#include +#include namespace vtkh { +namespace detail +{ + +bool +isMaterial(std::string matset_name, std::string field_name) +{ + + // Create the substring to search for + std::string searchString = matset_name + "_"; + + // Check if the fieldName contains the searchString + if (field_name.find(searchString) != std::string::npos) { + return true; + } + return false; +} + +class MetaDataLength : public vtkm::worklet::WorkletMapField +{ +public: + using ControlSignature = void(FieldIn, FieldOut); + + VTKM_EXEC + void operator()(const vtkm::FloatDefault& vf_data, + vtkm::Id& length) const + { + if (vf_data > vtkm::FloatDefault(0.0)) + { + length = length + 1; + //std::cerr << "length before: " << length << std::endl; + //length++; + //std::cerr << "length after: " << length << std::endl; + //std::cerr << std::endl; + } + } +}; + + +}//end detail + MIR::MIR() { @@ -16,16 +58,16 @@ MIR::~MIR() } void -MIR::SetField(const std::string field_name) +MIR::SetMatSet(const std::string matset_name) { - m_field_name = field_name; + m_matset_name = matset_name; } void MIR::PreExecute() { Filter::PreExecute(); - Filter::CheckForRequiredField(m_field_name); + //Filter::CheckForRequiredField(m_field_name); } void @@ -36,30 +78,41 @@ MIR::PostExecute() void MIR::DoExecute() { - - ClipField max_clip; - max_clip.SetInput(this->m_input); - max_clip.SetField(m_field_name); - max_clip.SetClipValue(m_range.Max); - max_clip.SetInvertClip(true); - max_clip.Update(); - - DataSet *clipped = max_clip.GetOutput(); - - ClipField min_clip; - min_clip.SetInput(clipped); - min_clip.SetField(m_field_name); - min_clip.SetClipValue(m_range.Min); - min_clip.Update(); - - delete clipped; - DataSet *iso = min_clip.GetOutput(); - CleanGrid cleaner; - cleaner.SetInput(iso); - cleaner.Update(); - delete iso; - this->m_output = cleaner.GetOutput(); - + vtkm::cont::Invoker invoker; + const int num_domains = this->m_input->GetNumberOfDomains(); + vtkm::cont::ArrayHandle length; + + for(int i = 0; i < num_domains; ++i) + { + vtkm::Id domain_id; + vtkm::cont::DataSet dom; + this->m_input->GetDomain(i, dom, domain_id); + vtkm::Id num_fields = dom.GetNumberOfFields(); + for(int j = 0; j < num_fields; ++j) + { + vtkm::cont::Field field = dom.GetField(j); + std::string field_name = field.GetName(); + bool is_material = detail::isMaterial(m_matset_name, field_name); + std::cerr << "isMaterial( " << field_name << " ): " << is_material << std::endl; + if(is_material) + { + vtkm::cont::ArrayHandle data; + field.GetDataAsDefaultFloat().AsArrayHandle(data); + vtkm::Id num_values = data.GetNumberOfValues(); + if(length.GetNumberOfValues() != num_values) + { + std::cerr << "HERE" << std::endl; + length.AllocateAndFill(num_values,0.0); + } + invoker(detail::MetaDataLength{}, data, length); + std::cerr << "length now: " << std::endl; + for(int n = 0; n < num_values; ++n) + { + std::cerr << length.ReadPortal().Get(n) << " "; + } + } + } + } } std::string diff --git a/src/libs/vtkh/filters/MIR.hpp b/src/libs/vtkh/filters/MIR.hpp index 41942daf7..a2df42f4f 100644 --- a/src/libs/vtkh/filters/MIR.hpp +++ b/src/libs/vtkh/filters/MIR.hpp @@ -14,13 +14,13 @@ class VTKH_API MIR: public Filter MIR(); virtual ~MIR(); std::string GetName() const override; - void SetField(const std::string field_name); + void SetMatSet(const std::string matset_name); protected: void PreExecute() override; void PostExecute() override; void DoExecute() override; - std::string m_field_name; + std::string m_matset_name; }; } //namespace vtkh diff --git a/src/tests/ascent/t_ascent_mir.cpp b/src/tests/ascent/t_ascent_mir.cpp index 489f270b1..eb17cebac 100644 --- a/src/tests/ascent/t_ascent_mir.cpp +++ b/src/tests/ascent/t_ascent_mir.cpp @@ -77,12 +77,14 @@ TEST(ascent_vorticity, vel_vorticity) pipelines["pl1/f1/type"] = "mir"; conduit::Node ¶ms = pipelines["pl1/f1/params"]; - params["field"] = "vel_vorticity"; // name of the vector field + params["field"] = "circle_a"; // name of the vector field + params["matset"] = "matset"; // name of the vector field //params["output_name"] = "mag_vorticity"; // name of the output field conduit::Node scenes; scenes["s1/plots/p1/type"] = "pseudocolor"; - scenes["s1/plots/p1/field"] = "mag_vorticity"; + scenes["s1/plots/p1/matset"] = "matset"; + scenes["s1/plots/p1/field"] = "field"; scenes["s1/plots/p1/pipeline"] = "pl1"; scenes["s1/image_prefix"] = output_file; From dc2ac51f1b582f58295c6a8cfca511e715d031d1 Mon Sep 17 00:00:00 2001 From: Nicole Marsaglia Date: Thu, 30 May 2024 22:06:46 +0000 Subject: [PATCH 06/21] it works. idk if it's correct --- .../runtimes/ascent_vtkh_data_adapter.cpp | 15 ++- .../ascent_runtime_vtkh_filters.cpp | 49 ++++---- src/libs/vtkh/filters/MIR.cpp | 105 +++++++++--------- src/libs/vtkh/filters/MIR.hpp | 14 +++ src/tests/ascent/t_ascent_mir.cpp | 20 ++-- 5 files changed, 117 insertions(+), 86 deletions(-) diff --git a/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp b/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp index fa8806949..20b851a98 100644 --- a/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp +++ b/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp @@ -2004,8 +2004,11 @@ VTKHDataAdapter::AddMatSets(const std::string &matset_name, std::string assoc_str = "element"; NodeConstIterator itr = n_matset["volume_fractions"].children(); - std::string material_name; - + std::string material_name, length_name, offsets_name, ids_name, vfs_name; + length_name = matset_name + "_lengths"; + offsets_name = matset_name + "_offsets"; + ids_name = matset_name + "_ids"; + vfs_name = matset_name + "_vfs"; //add each material of specified matset to vtkm dataset const Node &n_material = itr.next(); material_name = itr.name(); @@ -2039,8 +2042,8 @@ VTKHDataAdapter::AddMatSets(const std::string &matset_name, //add calculated material fields for vtkm int total; vtkm::cont::Field length, offsets, ids, vfs; - detail::GetMatSetLength(n_matset, "lengths", "offsets", topo_name, total,neles, length, offsets); - detail::GetMatSetIDsAndVFs(n_matset, "ids", "vfs", topo_name,total, neles, offsets, ids, vfs); + detail::GetMatSetLength(n_matset, length_name, offsets_name, topo_name, total,neles, length, offsets); + detail::GetMatSetIDsAndVFs(n_matset, ids_name, vfs_name, topo_name,total, neles, offsets, ids, vfs); dset->AddField(length); dset->AddField(offsets); dset->AddField(ids); @@ -2060,8 +2063,8 @@ VTKHDataAdapter::AddMatSets(const std::string &matset_name, //add calculated material fields for vtkm int total; vtkm::cont::Field length, offsets, ids, vfs; - detail::GetMatSetLength(n_matset, "lengths", "offsets", topo_name, total,neles, length, offsets); - detail::GetMatSetIDsAndVFs(n_matset, "ids", "vfs", topo_name,total, neles, offsets, ids, vfs); + detail::GetMatSetLength(n_matset, length_name, offsets_name, topo_name, total,neles, length, offsets); + detail::GetMatSetIDsAndVFs(n_matset, ids_name, vfs_name, topo_name,total, neles, offsets, ids, vfs); dset->AddField(length); dset->AddField(offsets); dset->AddField(ids); diff --git a/src/libs/ascent/runtimes/flow_filters/ascent_runtime_vtkh_filters.cpp b/src/libs/ascent/runtimes/flow_filters/ascent_runtime_vtkh_filters.cpp index d245d105c..d9a0a451c 100644 --- a/src/libs/ascent/runtimes/flow_filters/ascent_runtime_vtkh_filters.cpp +++ b/src/libs/ascent/runtimes/flow_filters/ascent_runtime_vtkh_filters.cpp @@ -4986,11 +4986,17 @@ VTKHMIR::verify_params(const conduit::Node ¶ms, info.reset(); bool res = check_string("matset",params, info, true); - res = check_string("field",params, info, true); + res &= check_numeric("error_scaling", params, info, true); + res &= check_numeric("scaling_decay", params, info, true); + res &= check_numeric("iterations", params, info, true); + res &= check_numeric("max_error", params, info, true); std::vector valid_paths; valid_paths.push_back("matset"); - valid_paths.push_back("field"); + valid_paths.push_back("error_scaling"); + valid_paths.push_back("scaling_decay"); + valid_paths.push_back("iterations"); + valid_paths.push_back("max_error"); std::string surprises = surprise_check(valid_paths, params); @@ -5022,33 +5028,34 @@ VTKHMIR::execute() std::shared_ptr collection = data_object->as_vtkh_collection(); std::string matset_name = params()["matset"].as_string(); - std::string field_name = params()["field"].as_string(); - if(!collection->has_field(field_name)) + std::string length_name = matset_name + "_lengths"; + if(!collection->has_field(length_name)) { bool throw_error = false; - detail::field_error(field_name, this->name(), collection, throw_error); + detail::field_error(length_name, this->name(), collection, throw_error); // this creates a data object with an invalid soource set_output(new DataObject()); return; } - std::string topo_name = collection->field_topology(field_name); + std::string topo_name = collection->field_topology(length_name); vtkh::DataSet &data = collection->dataset_by_topology(topo_name); std::cerr << "data going into ascent_runtime_vtkh_filters: " << std::endl; data.PrintSummary(std::cerr); - vtkh::MIR mir; + double error_scaling = params()["error_scaling"].to_float64(); + double scaling_decay = params()["scaling_decay"].to_float64(); + int iterations = params()["iterations"].to_int64(); + double max_error = params()["max_error"].to_float64(); - mir.SetInput(&data); + vtkh::MIR mir; + mir.SetErrorScaling(error_scaling); + mir.SetScalingDecay(scaling_decay); + mir.SetIterations(iterations); + mir.SetMaxError(max_error); mir.SetMatSet(matset_name); - //hist.SetNumBins(bins); - //hist.SetSamplingPercent(sample_rate); - //if(ghost_field != "") - //{ - // hist.SetGhostField(ghost_field); - //} - + mir.SetInput(&data); mir.Update(); vtkh::DataSet *mir_output = mir.GetOutput(); std::cerr << "vkth::dataset MIR: " << std::endl; @@ -5056,12 +5063,12 @@ VTKHMIR::execute() //// we need to pass through the rest of the topologies, untouched, //// and add the result of this operation - //VTKHCollection *new_coll = collection->copy_without_topology(topo_name); - //new_coll->add(*hist_output, topo_name); - //// re wrap in data object - //DataObject *res = new DataObject(new_coll); - //delete hist_output; - set_output(data_object); + VTKHCollection *new_coll = collection->copy_without_topology(topo_name); + new_coll->add(*mir_output, topo_name); + // re wrap in data object + DataObject *res = new DataObject(new_coll); + delete mir_output; + set_output(res); } //----------------------------------------------------------------------------- diff --git a/src/libs/vtkh/filters/MIR.cpp b/src/libs/vtkh/filters/MIR.cpp index 16d36e6ee..b503ed5c3 100644 --- a/src/libs/vtkh/filters/MIR.cpp +++ b/src/libs/vtkh/filters/MIR.cpp @@ -1,8 +1,6 @@ #include "MIR.hpp" -#include -#include -#include +#include namespace vtkh { @@ -24,26 +22,6 @@ isMaterial(std::string matset_name, std::string field_name) return false; } -class MetaDataLength : public vtkm::worklet::WorkletMapField -{ -public: - using ControlSignature = void(FieldIn, FieldOut); - - VTKM_EXEC - void operator()(const vtkm::FloatDefault& vf_data, - vtkm::Id& length) const - { - if (vf_data > vtkm::FloatDefault(0.0)) - { - length = length + 1; - //std::cerr << "length before: " << length << std::endl; - //length++; - //std::cerr << "length after: " << length << std::endl; - //std::cerr << std::endl; - } - } -}; - }//end detail @@ -61,13 +39,48 @@ void MIR::SetMatSet(const std::string matset_name) { m_matset_name = matset_name; + m_lengths_name = matset_name + "_lengths"; + m_offsets_name = matset_name + "_offsets"; + m_ids_name = matset_name + "_ids"; + m_vfs_name = matset_name + "_vfs"; +} + +void +MIR::SetErrorScaling(const double error_scaling) +{ + m_error_scaling = error_scaling; +} + +void +MIR::SetScalingDecay(const double scaling_decay) +{ + m_scaling_decay = scaling_decay; +} + +void +MIR::SetIterations(const int iterations) +{ + m_iterations = iterations; +} + +void +MIR::SetMaxError(const double max_error) +{ + m_max_error = max_error; } void MIR::PreExecute() { Filter::PreExecute(); - //Filter::CheckForRequiredField(m_field_name); + std::string lengths_field = m_matset_name + "_lengths"; + std::string offsets_field = m_matset_name + "_offsets"; + std::string ids_field = m_matset_name + "_ids"; + std::string vfs_field = m_matset_name + "_vfs"; + Filter::CheckForRequiredField(lengths_field); + Filter::CheckForRequiredField(offsets_field); + Filter::CheckForRequiredField(ids_field); + Filter::CheckForRequiredField(vfs_field); } void @@ -78,40 +91,30 @@ MIR::PostExecute() void MIR::DoExecute() { - vtkm::cont::Invoker invoker; + this->m_output = new DataSet(); const int num_domains = this->m_input->GetNumberOfDomains(); - vtkm::cont::ArrayHandle length; for(int i = 0; i < num_domains; ++i) { vtkm::Id domain_id; vtkm::cont::DataSet dom; this->m_input->GetDomain(i, dom, domain_id); - vtkm::Id num_fields = dom.GetNumberOfFields(); - for(int j = 0; j < num_fields; ++j) - { - vtkm::cont::Field field = dom.GetField(j); - std::string field_name = field.GetName(); - bool is_material = detail::isMaterial(m_matset_name, field_name); - std::cerr << "isMaterial( " << field_name << " ): " << is_material << std::endl; - if(is_material) - { - vtkm::cont::ArrayHandle data; - field.GetDataAsDefaultFloat().AsArrayHandle(data); - vtkm::Id num_values = data.GetNumberOfValues(); - if(length.GetNumberOfValues() != num_values) - { - std::cerr << "HERE" << std::endl; - length.AllocateAndFill(num_values,0.0); - } - invoker(detail::MetaDataLength{}, data, length); - std::cerr << "length now: " << std::endl; - for(int n = 0; n < num_values; ++n) - { - std::cerr << length.ReadPortal().Get(n) << " "; - } - } - } + vtkm::filter::contour::MIRFilter mir; + mir.SetLengthCellSetName(m_lengths_name); + mir.SetPositionCellSetName(m_offsets_name); + mir.SetIDWholeSetName(m_ids_name); + mir.SetVFWholeSetName(m_vfs_name); + mir.SetErrorScaling(vtkm::Float64(m_error_scaling)); + mir.SetScalingDecay(vtkm::Float64(m_scaling_decay)); + mir.SetMaxIterations(vtkm::IdComponent(m_iterations)); + mir.SetMaxPercentError(vtkm::Float64(m_max_error)); + vtkm::cont::DataSet output = mir.Execute(dom); + vtkm::cont::UnknownArrayHandle float_field = output.GetField("cellMat").GetDataAsDefaultFloat(); + output.GetField("cellMat").SetData(float_field); + std::cerr << "OUTPUT CELLS: " << output.GetNumberOfCells() << std::endl; + std::cerr << "output from vtkm MIR: =================" << std::endl; + output.PrintSummary(std::cerr); + this->m_output->AddDomain(output, i); } } diff --git a/src/libs/vtkh/filters/MIR.hpp b/src/libs/vtkh/filters/MIR.hpp index a2df42f4f..0a2d7c76b 100644 --- a/src/libs/vtkh/filters/MIR.hpp +++ b/src/libs/vtkh/filters/MIR.hpp @@ -15,12 +15,26 @@ class VTKH_API MIR: public Filter virtual ~MIR(); std::string GetName() const override; void SetMatSet(const std::string matset_name); + void SetErrorScaling(const double error_scaling); + void SetScalingDecay(const double scaling_decay); + void SetIterations(const int iterations); + void SetMaxError(const double max_error); + protected: void PreExecute() override; void PostExecute() override; void DoExecute() override; std::string m_matset_name; + std::string m_lengths_name; + std::string m_offsets_name; + std::string m_ids_name; + std::string m_vfs_name; + double m_error_scaling; + double m_scaling_decay; + int m_iterations; + double m_max_error; + }; } //namespace vtkh diff --git a/src/tests/ascent/t_ascent_mir.cpp b/src/tests/ascent/t_ascent_mir.cpp index eb17cebac..447a620d4 100644 --- a/src/tests/ascent/t_ascent_mir.cpp +++ b/src/tests/ascent/t_ascent_mir.cpp @@ -35,7 +35,7 @@ index_t EXAMPLE_MESH_SIDE_DIM = 20; float64 RADIUS = 10; //----------------------------------------------------------------------------- -TEST(ascent_vorticity, vel_vorticity) +TEST(ascent_mir, venn_vtkm_mir) { Node n; ascent::about(n); @@ -63,7 +63,7 @@ TEST(ascent_vorticity, vel_vorticity) string output_path = prepare_output_dir(); - string output_file = conduit::utils::join_file_path(output_path,"tout_vorticity_vel"); + string output_file = conduit::utils::join_file_path(output_path,"tout_mir_venn"); // remove old images before rendering remove_test_image(output_file); @@ -77,14 +77,18 @@ TEST(ascent_vorticity, vel_vorticity) pipelines["pl1/f1/type"] = "mir"; conduit::Node ¶ms = pipelines["pl1/f1/params"]; - params["field"] = "circle_a"; // name of the vector field + //params["field"] = "circle_a"; // name of the vector field params["matset"] = "matset"; // name of the vector field + params["error_scaling"] = 0.2; + params["scaling_decay"] = 1.0; + params["iterations"] = 21; + params["max_error"] = 0.00001; //params["output_name"] = "mag_vorticity"; // name of the output field conduit::Node scenes; scenes["s1/plots/p1/type"] = "pseudocolor"; - scenes["s1/plots/p1/matset"] = "matset"; - scenes["s1/plots/p1/field"] = "field"; +// scenes["s1/plots/p1/matset"] = "matset"; + scenes["s1/plots/p1/field"] = "cellMat"; scenes["s1/plots/p1/pipeline"] = "pl1"; scenes["s1/image_prefix"] = output_file; @@ -95,9 +99,9 @@ TEST(ascent_vorticity, vel_vorticity) add_pipelines["action"] = "add_pipelines"; add_pipelines["pipelines"] = pipelines; // add the scenes - // conduit::Node &add_scenes= actions.append(); - // add_scenes["action"] = "add_scenes"; - // add_scenes["scenes"] = scenes; + conduit::Node &add_scenes= actions.append(); + add_scenes["action"] = "add_scenes"; + add_scenes["scenes"] = scenes; // // Run Ascent From 1f03700667a9aa693d83ddadb30a3c000a1ad4af Mon Sep 17 00:00:00 2001 From: Nicole Marsaglia Date: Thu, 30 May 2024 22:07:24 +0000 Subject: [PATCH 07/21] please leave --- ...cent_material_inferface_reconstruction.cpp | 138 ------------------ 1 file changed, 138 deletions(-) delete mode 100644 src/tests/ascent/t_ascent_material_inferface_reconstruction.cpp diff --git a/src/tests/ascent/t_ascent_material_inferface_reconstruction.cpp b/src/tests/ascent/t_ascent_material_inferface_reconstruction.cpp deleted file mode 100644 index dbff96ce8..000000000 --- a/src/tests/ascent/t_ascent_material_inferface_reconstruction.cpp +++ /dev/null @@ -1,138 +0,0 @@ -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// -// Copyright (c) Lawrence Livermore National Security, LLC and other Ascent -// Project developers. See top-level LICENSE AND COPYRIGHT files for dates and -// other details. No copyright assignment is required to contribute to Ascent. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// - -//----------------------------------------------------------------------------- -/// -/// file: t_ascent_divergence.cpp -/// -//----------------------------------------------------------------------------- - - -#include "gtest/gtest.h" - -#include - -#include -#include - -#include - -#include "t_config.hpp" -#include "t_utils.hpp" - - - - -using namespace std; -using namespace conduit; -using namespace ascent; - - -index_t EXAMPLE_MESH_SIDE_DIM = 20; -float64 RADIUS = 10; - -//----------------------------------------------------------------------------- -TEST(ascent_vorticity, vel_vorticity) -{ - Node n; - ascent::about(n); - // only run this test if ascent was built with vtkm support - if(n["runtimes/ascent/vtkm/status"].as_string() == "disabled") - { - ASCENT_INFO("Ascent vtkm support disabled, skipping test"); - return; - } - - // - // Create an example mesh. - // - Node data, verify_info; - conduit::blueprint::mesh::examples::venn("full", - EXAMPLE_MESH_SIDE_DIM, - EXAMPLE_MESH_SIDE_DIM, - RADIUS, - data); - EXPECT_TRUE(conduit::blueprint::mesh::verify(data,verify_info)); - std::cerr << "VENN CONDUIT DATA: " << std::endl; - data.print(); - - ASCENT_INFO("Testing the MIR of a field"); - - - string output_path = prepare_output_dir(); - string output_file = conduit::utils::join_file_path(output_path,"tout_vorticity_vel"); - - // remove old images before rendering - remove_test_image(output_file); - - // - // Create the actions. - // - - conduit::Node pipelines; - // pipeline 1 - - pipelines["pl1/f1/type"] = "mir"; - conduit::Node ¶ms = pipelines["pl1/f1/params"]; - params["field"] = "vel_vorticity"; // name of the vector field - //params["output_name"] = "mag_vorticity"; // name of the output field - - conduit::Node scenes; - scenes["s1/plots/p1/type"] = "pseudocolor"; - scenes["s1/plots/p1/field"] = "mag_vorticity"; - scenes["s1/plots/p1/pipeline"] = "pl1"; - - scenes["s1/image_prefix"] = output_file; - - conduit::Node actions; - // add the pipeline - conduit::Node &add_pipelines = actions.append(); - add_pipelines["action"] = "add_pipelines"; - add_pipelines["pipelines"] = pipelines; - // add the scenes - conduit::Node &add_scenes= actions.append(); - add_scenes["action"] = "add_scenes"; - add_scenes["scenes"] = scenes; - - // - // Run Ascent - // - - Ascent ascent; - - Node ascent_opts; - ascent_opts["runtime/type"] = "ascent"; - ascent.open(ascent_opts); - ascent.publish(data); - ascent.execute(actions); - ascent.close(); - - // check that we created an image - EXPECT_TRUE(check_test_image(output_file)); - std::string msg = "An example of using the gradient filter " - "and plotting the magnitude."; - ASCENT_ACTIONS_DUMP(actions,output_file,msg); - -} - -//----------------------------------------------------------------------------- -int main(int argc, char* argv[]) -{ - int result = 0; - - ::testing::InitGoogleTest(&argc, argv); - - // allow override of the data size via the command line - if(argc == 2) - { - EXAMPLE_MESH_SIDE_DIM = atoi(argv[1]); - } - - result = RUN_ALL_TESTS(); - return result; -} - - From 6a0ed351f157499ba67106dfe9074526fc62da75 Mon Sep 17 00:00:00 2001 From: Nicole Marsaglia Date: Thu, 30 May 2024 22:36:03 +0000 Subject: [PATCH 08/21] start of MIR docs --- src/docs/sphinx/Actions/Pipelines.rst | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/docs/sphinx/Actions/Pipelines.rst b/src/docs/sphinx/Actions/Pipelines.rst index a613a5899..042223f4a 100644 --- a/src/docs/sphinx/Actions/Pipelines.rst +++ b/src/docs/sphinx/Actions/Pipelines.rst @@ -964,6 +964,24 @@ accurate but slower point based gradients (default). params["output_name"] = "my_q"; // (required) name of the output field params["use_cell_gradient"] = "false"; // (optional) +Material Interface Reconstruction +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The Material Interface Reconstruction (MIR) filter does something sciency that people like and want. +The user must specify the name of the material set (`matset`), as well as the error scaling (`error_scaling`), scaling decay (`scaling_decay`), maximum iterations (`iterations`), and maximum error percentage (`max_error`). +The output field of the MIR Filter is `cellMat` and can be used for rendering in scenes. + +.. code-block:: c++ + + conduit::Node pipelines; + // pipeline 1 + pipelines["pl1/f1/type"] = "mir"; + //params optional + conduit::Node ¶ms = pipelines["pl1/f1/params"]; + params["matset"] = "matset"; //required + params["error_scaling"] = 0.2; //required + params["scaling_decay"] = 1.0; //required + params["iterations"] = 8; //required + params["max_error"] = 0.00001; //required Partitioning From afe36f5b7d8c47bc391fc77a209dd5cd6a55a32d Mon Sep 17 00:00:00 2001 From: Nicole Marsaglia Date: Thu, 30 May 2024 22:44:10 +0000 Subject: [PATCH 09/21] add examples --- src/docs/sphinx/Actions/Examples.rst | 11 ++++++++ .../Actions/examples/tout_mir_venn0.png | Bin 0 -> 403854 bytes .../Actions/examples/tout_mir_venn100.yaml | 24 ++++++++++++++++++ src/tests/ascent/t_ascent_mir.cpp | 8 +++--- 4 files changed, 39 insertions(+), 4 deletions(-) create mode 100644 src/docs/sphinx/Actions/examples/tout_mir_venn0.png create mode 100644 src/docs/sphinx/Actions/examples/tout_mir_venn100.yaml diff --git a/src/docs/sphinx/Actions/Examples.rst b/src/docs/sphinx/Actions/Examples.rst index f3a5f41cc..2910d5fae 100644 --- a/src/docs/sphinx/Actions/Examples.rst +++ b/src/docs/sphinx/Actions/Examples.rst @@ -1018,3 +1018,14 @@ YAML actions: .. literalinclude:: examples/tout_render_amr_complex_ghost100.yaml + +An example of using the Material Interface Reconstruction filter. +-------------------------------------------------------------------------------------------------- + +YAML actions: + +.. literalinclude:: examples/tout_mir_venn0.yaml + +Resulting image: + +.. image:: examples/tout_mir_venn0.png \ No newline at end of file diff --git a/src/docs/sphinx/Actions/examples/tout_mir_venn0.png b/src/docs/sphinx/Actions/examples/tout_mir_venn0.png new file mode 100644 index 0000000000000000000000000000000000000000..0348d4a39e0c1d626fd93011cf439d3e8cc9bc7d GIT binary patch literal 403854 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 T*>I!4`GI>L{^z{Al3xCQp{Z~( literal 0 HcmV?d00001 diff --git a/src/docs/sphinx/Actions/examples/tout_mir_venn100.yaml b/src/docs/sphinx/Actions/examples/tout_mir_venn100.yaml new file mode 100644 index 000000000..fbfcbdf76 --- /dev/null +++ b/src/docs/sphinx/Actions/examples/tout_mir_venn100.yaml @@ -0,0 +1,24 @@ +#An example of using the MIR filter and plotting the field 'cellMat'. + +- + action: "add_pipelines" + pipelines: + pl1: + f1: + type: "mir" + params: + matset: "matset" + error_scaling: 0.2 + scaling_decay: 1.0 + iterations: 8 + max_error: 1e-05 +- + action: "add_scenes" + scenes: + s1: + plots: + p1: + type: "pseudocolor" + field: "cellMat" + pipeline: "pl1" + image_prefix: "/home/user/ascent/build/tests/_output/tout_mir_venn" diff --git a/src/tests/ascent/t_ascent_mir.cpp b/src/tests/ascent/t_ascent_mir.cpp index 447a620d4..5e4771614 100644 --- a/src/tests/ascent/t_ascent_mir.cpp +++ b/src/tests/ascent/t_ascent_mir.cpp @@ -81,7 +81,7 @@ TEST(ascent_mir, venn_vtkm_mir) params["matset"] = "matset"; // name of the vector field params["error_scaling"] = 0.2; params["scaling_decay"] = 1.0; - params["iterations"] = 21; + params["iterations"] = 8; params["max_error"] = 0.00001; //params["output_name"] = "mag_vorticity"; // name of the output field @@ -118,9 +118,9 @@ TEST(ascent_mir, venn_vtkm_mir) // check that we created an image // EXPECT_TRUE(check_test_image(output_file)); -// std::string msg = "An example of using the gradient filter " -// "and plotting the magnitude."; -// ASCENT_ACTIONS_DUMP(actions,output_file,msg); + std::string msg = "An example of using the MIR filter " + "and plotting the field 'cellMat'."; + ASCENT_ACTIONS_DUMP(actions,output_file,msg); } From 1ecb7d894361c2b111c0128b7f00fae0d81c8448 Mon Sep 17 00:00:00 2001 From: Nicole Marsaglia Date: Fri, 31 May 2024 22:07:25 +0000 Subject: [PATCH 10/21] update test and add baseline. clean up comments. fix mir ids -- can't start at 0 --- src/docs/sphinx/Actions/Pipelines.rst | 12 ++++---- .../Actions/examples/tout_mir_venn0.png | Bin 403854 -> 404174 bytes .../Actions/examples/tout_mir_venn100.yaml | 6 ++-- .../runtimes/ascent_vtkh_data_adapter.cpp | 2 +- .../ascent_runtime_vtkh_filters.cpp | 29 ++++++++++-------- src/libs/vtkh/filters/MIR.cpp | 5 ++- .../_baseline_images/tout_mir_venn100.png | Bin 0 -> 404174 bytes src/tests/ascent/t_ascent_mir.cpp | 28 ++++++++++------- 8 files changed, 45 insertions(+), 37 deletions(-) create mode 100644 src/tests/_baseline_images/tout_mir_venn100.png diff --git a/src/docs/sphinx/Actions/Pipelines.rst b/src/docs/sphinx/Actions/Pipelines.rst index 042223f4a..99ca5425a 100644 --- a/src/docs/sphinx/Actions/Pipelines.rst +++ b/src/docs/sphinx/Actions/Pipelines.rst @@ -967,8 +967,8 @@ accurate but slower point based gradients (default). Material Interface Reconstruction ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The Material Interface Reconstruction (MIR) filter does something sciency that people like and want. -The user must specify the name of the material set (`matset`), as well as the error scaling (`error_scaling`), scaling decay (`scaling_decay`), maximum iterations (`iterations`), and maximum error percentage (`max_error`). -The output field of the MIR Filter is `cellMat` and can be used for rendering in scenes. +The user must specify the name of the material set (`matset`) they wish to use. Optional parameters include error scaling (`error_scaling`), scaling decay (`scaling_decay`), maximum iterations (`iterations`), and maximum error percentage (`max_error`). +The output field of the MIR Filter is `cellMat` that can be used further in scenes or pipelines. .. code-block:: c++ @@ -978,10 +978,10 @@ The output field of the MIR Filter is `cellMat` and can be used for rendering in //params optional conduit::Node ¶ms = pipelines["pl1/f1/params"]; params["matset"] = "matset"; //required - params["error_scaling"] = 0.2; //required - params["scaling_decay"] = 1.0; //required - params["iterations"] = 8; //required - params["max_error"] = 0.00001; //required + params["error_scaling"] = 0.2; //default: 0.0 + params["scaling_decay"] = 1.0; //default: 0.0 + params["iterations"] = 8; //default: 0 + params["max_error"] = 0.00001; //default: 0.00001 Partitioning diff --git a/src/docs/sphinx/Actions/examples/tout_mir_venn0.png b/src/docs/sphinx/Actions/examples/tout_mir_venn0.png index 0348d4a39e0c1d626fd93011cf439d3e8cc9bc7d..1b36309856e4649ccfe9f0502794c63d4c59f178 100644 GIT binary patch literal 404174 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 literal 403854 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 T*>I!4`GI>L{^z{Al3xCQp{Z~( diff --git a/src/docs/sphinx/Actions/examples/tout_mir_venn100.yaml b/src/docs/sphinx/Actions/examples/tout_mir_venn100.yaml index fbfcbdf76..77790fc88 100644 --- a/src/docs/sphinx/Actions/examples/tout_mir_venn100.yaml +++ b/src/docs/sphinx/Actions/examples/tout_mir_venn100.yaml @@ -8,9 +8,9 @@ type: "mir" params: matset: "matset" - error_scaling: 0.2 - scaling_decay: 1.0 - iterations: 8 + error_scaling: 0.0 + scaling_decay: 0.0 + iterations: 0 max_error: 1e-05 - action: "add_scenes" diff --git a/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp b/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp index 20b851a98..9c47f3548 100644 --- a/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp +++ b/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp @@ -579,7 +579,7 @@ void GetMatSetIDsAndVFs(const conduit::Node &node, //materials["matset"] const S *data = n_material.value(); if(data[i] > 0) { - v_ids[offset] = j; + v_ids[offset] = j + 1; //IDs cannot start at zero v_vfs[offset] = data[i]; offset++; } diff --git a/src/libs/ascent/runtimes/flow_filters/ascent_runtime_vtkh_filters.cpp b/src/libs/ascent/runtimes/flow_filters/ascent_runtime_vtkh_filters.cpp index d9a0a451c..18a4799b5 100644 --- a/src/libs/ascent/runtimes/flow_filters/ascent_runtime_vtkh_filters.cpp +++ b/src/libs/ascent/runtimes/flow_filters/ascent_runtime_vtkh_filters.cpp @@ -4986,10 +4986,10 @@ VTKHMIR::verify_params(const conduit::Node ¶ms, info.reset(); bool res = check_string("matset",params, info, true); - res &= check_numeric("error_scaling", params, info, true); - res &= check_numeric("scaling_decay", params, info, true); - res &= check_numeric("iterations", params, info, true); - res &= check_numeric("max_error", params, info, true); + res &= check_numeric("error_scaling", params, info, false); + res &= check_numeric("scaling_decay", params, info, false); + res &= check_numeric("iterations", params, info, false); + res &= check_numeric("max_error", params, info, false); std::vector valid_paths; valid_paths.push_back("matset"); @@ -5041,13 +5041,18 @@ VTKHMIR::execute() std::string topo_name = collection->field_topology(length_name); vtkh::DataSet &data = collection->dataset_by_topology(topo_name); - std::cerr << "data going into ascent_runtime_vtkh_filters: " << std::endl; - data.PrintSummary(std::cerr); - - double error_scaling = params()["error_scaling"].to_float64(); - double scaling_decay = params()["scaling_decay"].to_float64(); - int iterations = params()["iterations"].to_int64(); - double max_error = params()["max_error"].to_float64(); + double error_scaling = 0.0; + double scaling_decay = 0.0; + double max_error = 0.00001; + int iterations = 0; + if(params().has_path("error_scaling")) + error_scaling = params()["error_scaling"].to_float64(); + if(params().has_path("scaling_decay")) + scaling_decay = params()["scaling_decay"].to_float64(); + if(params().has_path("iterations")) + iterations = params()["iterations"].to_int64(); + if(params().has_path("max_error")) + max_error = params()["max_error"].to_float64(); vtkh::MIR mir; mir.SetErrorScaling(error_scaling); @@ -5058,8 +5063,6 @@ VTKHMIR::execute() mir.SetInput(&data); mir.Update(); vtkh::DataSet *mir_output = mir.GetOutput(); - std::cerr << "vkth::dataset MIR: " << std::endl; - mir_output->PrintSummary(std::cerr); //// we need to pass through the rest of the topologies, untouched, //// and add the result of this operation diff --git a/src/libs/vtkh/filters/MIR.cpp b/src/libs/vtkh/filters/MIR.cpp index b503ed5c3..0120c5f68 100644 --- a/src/libs/vtkh/filters/MIR.cpp +++ b/src/libs/vtkh/filters/MIR.cpp @@ -109,12 +109,11 @@ void MIR::DoExecute() mir.SetMaxIterations(vtkm::IdComponent(m_iterations)); mir.SetMaxPercentError(vtkm::Float64(m_max_error)); vtkm::cont::DataSet output = mir.Execute(dom); + //cast and call error if cellMat stays as ints vtkm::cont::UnknownArrayHandle float_field = output.GetField("cellMat").GetDataAsDefaultFloat(); output.GetField("cellMat").SetData(float_field); - std::cerr << "OUTPUT CELLS: " << output.GetNumberOfCells() << std::endl; - std::cerr << "output from vtkm MIR: =================" << std::endl; - output.PrintSummary(std::cerr); this->m_output->AddDomain(output, i); +// this->m_output->AddDomain(dom, i); //original data } } diff --git a/src/tests/_baseline_images/tout_mir_venn100.png b/src/tests/_baseline_images/tout_mir_venn100.png new file mode 100644 index 0000000000000000000000000000000000000000..1b36309856e4649ccfe9f0502794c63d4c59f178 GIT binary patch literal 404174 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 literal 0 HcmV?d00001 diff --git a/src/tests/ascent/t_ascent_mir.cpp b/src/tests/ascent/t_ascent_mir.cpp index 5e4771614..7ad18afa2 100644 --- a/src/tests/ascent/t_ascent_mir.cpp +++ b/src/tests/ascent/t_ascent_mir.cpp @@ -31,8 +31,8 @@ using namespace conduit; using namespace ascent; -index_t EXAMPLE_MESH_SIDE_DIM = 20; -float64 RADIUS = 10; +index_t EXAMPLE_MESH_SIDE_DIM = 100; +float64 RADIUS = .25; //----------------------------------------------------------------------------- TEST(ascent_mir, venn_vtkm_mir) @@ -56,12 +56,10 @@ TEST(ascent_mir, venn_vtkm_mir) RADIUS, data); EXPECT_TRUE(conduit::blueprint::mesh::verify(data,verify_info)); - std::cerr << "VENN CONDUIT DATA: " << std::endl; - data.print(); ASCENT_INFO("Testing the MIR of a field"); - + data["state/cycle"] = 100; string output_path = prepare_output_dir(); string output_file = conduit::utils::join_file_path(output_path,"tout_mir_venn"); @@ -79,9 +77,9 @@ TEST(ascent_mir, venn_vtkm_mir) conduit::Node ¶ms = pipelines["pl1/f1/params"]; //params["field"] = "circle_a"; // name of the vector field params["matset"] = "matset"; // name of the vector field - params["error_scaling"] = 0.2; - params["scaling_decay"] = 1.0; - params["iterations"] = 8; + params["error_scaling"] = 0.0; + params["scaling_decay"] = 0.0; + params["iterations"] = 0; params["max_error"] = 0.00001; //params["output_name"] = "mag_vorticity"; // name of the output field @@ -89,10 +87,15 @@ TEST(ascent_mir, venn_vtkm_mir) scenes["s1/plots/p1/type"] = "pseudocolor"; // scenes["s1/plots/p1/matset"] = "matset"; scenes["s1/plots/p1/field"] = "cellMat"; +// scenes["s1/plots/p1/field"] = "circle_b"; scenes["s1/plots/p1/pipeline"] = "pl1"; - scenes["s1/image_prefix"] = output_file; + conduit::Node extracts; + extracts["e1/type"] = "relay"; + extracts["e1/params/path"] = output_file; + extracts["e1/params/protocol"] = "blueprint/mesh/hdf5"; + conduit::Node actions; // add the pipeline conduit::Node &add_pipelines = actions.append(); @@ -102,7 +105,10 @@ TEST(ascent_mir, venn_vtkm_mir) conduit::Node &add_scenes= actions.append(); add_scenes["action"] = "add_scenes"; add_scenes["scenes"] = scenes; - + // add the extracts +// conduit::Node &add_extracts = actions.append(); +// add_extracts["action"] = "add_extracts"; +// add_extracts["extracts"] = extracts; // // Run Ascent // @@ -117,7 +123,7 @@ TEST(ascent_mir, venn_vtkm_mir) ascent.close(); // check that we created an image -// EXPECT_TRUE(check_test_image(output_file)); + EXPECT_TRUE(check_test_image(output_file)); std::string msg = "An example of using the MIR filter " "and plotting the field 'cellMat'."; ASCENT_ACTIONS_DUMP(actions,output_file,msg); From 916ef2cc38026f611f6ac435d259776d8e27f0ab Mon Sep 17 00:00:00 2001 From: Nicole Marsaglia Date: Thu, 6 Jun 2024 18:05:24 +0000 Subject: [PATCH 11/21] add discrete color bar to mir rendering --- .../ascent_runtime_rendering_filters.cpp | 19 ++++++++++++++++++ .../ascent_runtime_vtkh_filters.cpp | 18 +++++++++++++---- src/libs/vtkh/filters/MIR.cpp | 6 ++++-- src/libs/vtkh/rendering/Annotator.cpp | 19 ++++++++++++++---- src/libs/vtkh/rendering/Annotator.hpp | 6 ++++-- src/libs/vtkh/rendering/Render.cpp | 8 +++++--- src/libs/vtkh/rendering/Render.hpp | 3 ++- src/libs/vtkh/rendering/Renderer.cpp | 15 +++++++++++++- src/libs/vtkh/rendering/Renderer.hpp | 3 +++ src/libs/vtkh/rendering/Scene.cpp | 7 ++++++- .../_baseline_images/tout_mir_venn100.png | Bin 404174 -> 402756 bytes 11 files changed, 86 insertions(+), 18 deletions(-) diff --git a/src/libs/ascent/runtimes/flow_filters/ascent_runtime_rendering_filters.cpp b/src/libs/ascent/runtimes/flow_filters/ascent_runtime_rendering_filters.cpp index 07bbd5474..4aa239e5e 100644 --- a/src/libs/ascent/runtimes/flow_filters/ascent_runtime_rendering_filters.cpp +++ b/src/libs/ascent/runtimes/flow_filters/ascent_runtime_rendering_filters.cpp @@ -1641,6 +1641,12 @@ CreatePlot::execute() return; } + bool *discrete_color_table = new bool(); + if(graph().workspace().registry().has_entry("discrete_color_table")) + { + discrete_color_table = graph().workspace().registry().fetch("discrete_color_table"); + } + std::cerr << "BOOL: " << *discrete_color_table << " IN CreatePlot " << std::endl; std::shared_ptr collection = data_object->as_vtkh_collection(); conduit::Node &plot_params = params(); @@ -1770,6 +1776,19 @@ CreatePlot::execute() renderer->SetField(field_name); } + + if(graph().workspace().registry().has_entry("is_discrete")) + { + + int *is_discrete; + std::cerr << "getting is_discrete" << std::endl; + is_discrete = graph().workspace().registry().fetch("is_discrete"); + std::cerr << "got is_discrete: " << *is_discrete << std::endl; + //make sure we set the discrete only for mir output field + if(is_discrete && field_name == "cellMat") + renderer->SetDiscrete(); + } + if(type == "mesh") { vtkh::MeshRenderer *mesh = dynamic_cast(renderer); diff --git a/src/libs/ascent/runtimes/flow_filters/ascent_runtime_vtkh_filters.cpp b/src/libs/ascent/runtimes/flow_filters/ascent_runtime_vtkh_filters.cpp index 18a4799b5..e179fb6e4 100644 --- a/src/libs/ascent/runtimes/flow_filters/ascent_runtime_vtkh_filters.cpp +++ b/src/libs/ascent/runtimes/flow_filters/ascent_runtime_vtkh_filters.cpp @@ -5028,19 +5028,29 @@ VTKHMIR::execute() std::shared_ptr collection = data_object->as_vtkh_collection(); std::string matset_name = params()["matset"].as_string(); - std::string length_name = matset_name + "_lengths"; - if(!collection->has_field(length_name)) + std::string ids_name = matset_name + "_ids"; + if(!collection->has_field(ids_name)) { bool throw_error = false; - detail::field_error(length_name, this->name(), collection, throw_error); + detail::field_error(ids_name, this->name(), collection, throw_error); // this creates a data object with an invalid soource set_output(new DataObject()); return; } - std::string topo_name = collection->field_topology(length_name); + std::string topo_name = collection->field_topology(ids_name); vtkh::DataSet &data = collection->dataset_by_topology(topo_name); + if(!graph().workspace().registry().has_entry("is_discrete")) + { + + int *is_discrete = new int(); + *is_discrete = 1; + std::cerr << "setting is_discrete: " << *is_discrete << std::endl; + graph().workspace().registry().add("is_discrete", + is_discrete, + -1); // TODO keep forever? + } double error_scaling = 0.0; double scaling_decay = 0.0; double max_error = 0.00001; diff --git a/src/libs/vtkh/filters/MIR.cpp b/src/libs/vtkh/filters/MIR.cpp index 0120c5f68..4c61b4d65 100644 --- a/src/libs/vtkh/filters/MIR.cpp +++ b/src/libs/vtkh/filters/MIR.cpp @@ -22,7 +22,6 @@ isMaterial(std::string matset_name, std::string field_name) return false; } - }//end detail MIR::MIR() @@ -93,7 +92,10 @@ void MIR::DoExecute() { this->m_output = new DataSet(); const int num_domains = this->m_input->GetNumberOfDomains(); - + //set fake discret color table + vtkm::Range ids_range = this->m_input->GetGlobalRange(m_ids_name).ReadPortal().Get(0); + std::cerr << "ids global range: " << ids_range.Min << " " << ids_range.Max << std::endl; + for(int i = 0; i < num_domains; ++i) { vtkm::Id domain_id; diff --git a/src/libs/vtkh/rendering/Annotator.cpp b/src/libs/vtkh/rendering/Annotator.cpp index 570c80949..84a69929a 100644 --- a/src/libs/vtkh/rendering/Annotator.cpp +++ b/src/libs/vtkh/rendering/Annotator.cpp @@ -32,7 +32,8 @@ Annotator::~Annotator() void Annotator::RenderScreenAnnotations(const std::vector &field_names, const std::vector &ranges, - const std::vector &color_tables) + const std::vector &color_tables, + const std::vector &is_discrete) { m_canvas.SetViewToScreenSpace(m_camera, true); // currently we only support 4 color bars, so grab the first 4 @@ -41,9 +42,15 @@ Annotator::RenderScreenAnnotations(const std::vector &field_names, m_world_annotator->BeginLineRenderingBatch(); for(int i = 0; i < num_bars; ++i) { - this->m_color_bar_annotation.SetRange(ranges[i], 5); + //TODO: What if we have a large range max? i.e. lots of materials + //Need to extend color bar in proportion somehow?? + if(is_discrete[i]) + this->m_color_bar_annotation.SetRange(ranges[i],ranges[i].Max); + else + this->m_color_bar_annotation.SetRange(ranges[i], 5); this->m_color_bar_annotation.SetFieldName(field_names[i]); this->m_color_bar_annotation.SetPosition(m_color_bar_pos[i]); + std::cerr << "color_bar_pos: " << m_color_bar_pos[i].X.Min << " " << m_color_bar_pos[i].X.Max << " " << m_color_bar_pos[i].Y.Min << " " << m_color_bar_pos[i].Y.Max << std::endl; this->m_color_bar_annotation.SetColorTable(color_tables[i]); this->m_color_bar_annotation.Render(m_camera, *m_world_annotator, m_canvas); } @@ -55,7 +62,8 @@ void Annotator::RenderScreenAnnotations(const std::vector &field_names, const std::vector &ranges, const std::vector &color_tables, - const std::vector &color_bar_pos) + const std::vector &color_bar_pos, + const std::vector &is_discrete) { m_canvas.SetViewToScreenSpace(m_camera, true); @@ -65,7 +73,10 @@ Annotator::RenderScreenAnnotations(const std::vector &field_names, m_world_annotator->BeginLineRenderingBatch(); for(int i = 0; i < num_bars; ++i) { - this->m_color_bar_annotation.SetRange(ranges[i], 5); + if(is_discrete[i]) + this->m_color_bar_annotation.SetRange(ranges[i],ranges[i].Max); + else + this->m_color_bar_annotation.SetRange(ranges[i], 5); this->m_color_bar_annotation.SetFieldName(field_names[i]); this->m_color_bar_annotation.SetPosition(color_bar_pos[i]); this->m_color_bar_annotation.SetColorTable(color_tables[i]); diff --git a/src/libs/vtkh/rendering/Annotator.hpp b/src/libs/vtkh/rendering/Annotator.hpp index 24d1573b7..002f09ba4 100644 --- a/src/libs/vtkh/rendering/Annotator.hpp +++ b/src/libs/vtkh/rendering/Annotator.hpp @@ -24,12 +24,14 @@ class VTKH_API Annotator void RenderWorldAnnotations(vtkm::Vec axis_scale); void RenderScreenAnnotations(const std::vector &field_names, const std::vector &ranges, - const std::vector &color_tables); + const std::vector &color_tables, + const std::vector &is_discrete); void RenderScreenAnnotations(const std::vector &field_names, const std::vector &ranges, const std::vector &color_tables, - const std::vector &color_bar_position); + const std::vector &color_bar_position, + const std::vector &is_discrete); protected: Annotator(); bool m_is_3d; diff --git a/src/libs/vtkh/rendering/Render.cpp b/src/libs/vtkh/rendering/Render.cpp index 9350d6f42..67e241d4c 100644 --- a/src/libs/vtkh/rendering/Render.cpp +++ b/src/libs/vtkh/rendering/Render.cpp @@ -205,7 +205,8 @@ Render::RenderWorldAnnotations() void Render::RenderScreenAnnotations(const std::vector &field_names, const std::vector &ranges, - const std::vector &colors) + const std::vector &colors, + const std::vector &is_discrete) { if(!m_render_annotations) return; if(!m_render_screen_annotations) return; @@ -219,11 +220,12 @@ Render::RenderScreenAnnotations(const std::vector &field_names, if(!m_render_annotations) return; Annotator annotator(m_canvas, m_camera, m_scene_bounds); if(m_color_bar_position.size() == 0) - annotator.RenderScreenAnnotations(field_names, ranges, colors); + annotator.RenderScreenAnnotations(field_names, ranges, colors, is_discrete); else - annotator.RenderScreenAnnotations(field_names, ranges, colors, m_color_bar_position); + annotator.RenderScreenAnnotations(field_names, ranges, colors, m_color_bar_position, is_discrete); } + Render Render::Copy() const { diff --git a/src/libs/vtkh/rendering/Render.hpp b/src/libs/vtkh/rendering/Render.hpp index be8303514..31154d7f0 100644 --- a/src/libs/vtkh/rendering/Render.hpp +++ b/src/libs/vtkh/rendering/Render.hpp @@ -58,7 +58,8 @@ class VTKH_API Render void RenderBackground(); void RenderScreenAnnotations(const std::vector &field_names, const std::vector &ranges, - const std::vector &colors); + const std::vector &colors, + const std::vector &is_discrete); void Save(); protected: vtkm::rendering::Camera m_camera; diff --git a/src/libs/vtkh/rendering/Renderer.cpp b/src/libs/vtkh/rendering/Renderer.cpp index c2a41aad5..fd70ba04f 100644 --- a/src/libs/vtkh/rendering/Renderer.cpp +++ b/src/libs/vtkh/rendering/Renderer.cpp @@ -12,7 +12,8 @@ Renderer::Renderer() : m_do_composite(true), m_color_table("Cool to Warm"), m_field_index(0), - m_has_color_table(true) + m_has_color_table(true), + m_is_discrete(false) { m_compositor = new Compositor(); } @@ -54,6 +55,18 @@ Renderer::GetHasColorTable() const return m_has_color_table; } +void +Renderer::SetDiscrete() +{ + m_is_discrete = true; +} + +bool +Renderer::IsDiscrete() const +{ + return m_is_discrete; +} + void Renderer::SetDoComposite(bool do_composite) { diff --git a/src/libs/vtkh/rendering/Renderer.hpp b/src/libs/vtkh/rendering/Renderer.hpp index 43e14c9a7..bf5f8a198 100644 --- a/src/libs/vtkh/rendering/Renderer.hpp +++ b/src/libs/vtkh/rendering/Renderer.hpp @@ -36,6 +36,7 @@ class VTKH_API Renderer : public Filter void SetDoComposite(bool do_composite); void SetRenders(const std::vector &renders); void SetRange(const vtkm::Range &range); + void SetDiscrete(); void DisableColorBar(); vtkm::cont::ColorTable GetColorTable() const; @@ -45,6 +46,7 @@ class VTKH_API Renderer : public Filter vtkh::DataSet *GetInput(); vtkm::Range GetRange() const; bool GetHasColorTable() const; + bool IsDiscrete() const; protected: // image related data with cinema support @@ -58,6 +60,7 @@ class VTKH_API Renderer : public Filter vtkm::Range m_range; vtkm::cont::ColorTable m_color_table; bool m_has_color_table; + bool m_is_discrete; // methods virtual void PreExecute() override; virtual void PostExecute() override; diff --git a/src/libs/vtkh/rendering/Scene.cpp b/src/libs/vtkh/rendering/Scene.cpp index 004b05654..e669ec918 100644 --- a/src/libs/vtkh/rendering/Scene.cpp +++ b/src/libs/vtkh/rendering/Scene.cpp @@ -126,6 +126,7 @@ Scene::Render() std::vector ranges; std::vector field_names; + std::vector is_ct_discrete; std::vector color_tables; bool do_once = true; @@ -223,6 +224,10 @@ Scene::Render() ranges.push_back((*plot).GetRange()); field_names.push_back((*plot).GetFieldName()); color_tables.push_back((*plot).GetColorTable()); + if((*plot).IsDiscrete()) + is_ct_discrete.push_back(1); + else + is_ct_discrete.push_back(0); } } do_once = false; @@ -232,7 +237,7 @@ Scene::Render() for(int i = 0; i < current_batch.size(); ++i) { current_batch[i].RenderWorldAnnotations(); - current_batch[i].RenderScreenAnnotations(field_names, ranges, color_tables); + current_batch[i].RenderScreenAnnotations(field_names, ranges, color_tables, is_ct_discrete); current_batch[i].RenderBackground(); current_batch[i].Save(); } diff --git a/src/tests/_baseline_images/tout_mir_venn100.png b/src/tests/_baseline_images/tout_mir_venn100.png index 1b36309856e4649ccfe9f0502794c63d4c59f178..262519215b84d50a2b9e5f814ce33592bd66e3b9 100644 GIT binary patch delta 14849 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 D2w1OZ delta 17051 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 I$3<@Ve?Y5vH2?qr From b7900156cc786dd2d8019b431ae88aad2c0fa640 Mon Sep 17 00:00:00 2001 From: Nicole Marsaglia Date: Thu, 6 Jun 2024 20:38:40 +0000 Subject: [PATCH 12/21] changelog --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 19c258e04..e536b2b12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,7 +15,9 @@ and this project aspires to adhere to [Semantic Versioning](https://semver.org/s - Added check to make sure all domain IDs are unique - Added a `vtk` extract that saves each mesh domain to a legacy vtk file grouped, with all domain data grouped by a `.visit` file. - Added WarpX Streamline filter that uses charged particles. -- Added seed population options for particle advection: point, point list, line, and box +- Added seed population options for particle advection: point, point list, line, and box. +- Added Uniform Grid Sampling filter +- Added Material Interface Reconstruction (`mir`) filter which produces `cellMat` output field. ### Changed - Changed the Data Binning filter to accept a `reduction_field` parameter (instead of `var`), and similarly the axis parameters to take `field` (instead of `var`). The `var` style parameters are still accepted, but deprecated and will be removed in a future release. From 9293cc65f11b92ac2056369af2829c8eb7e18242 Mon Sep 17 00:00:00 2001 From: Nicole Marsaglia Date: Thu, 6 Jun 2024 20:39:12 +0000 Subject: [PATCH 13/21] changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e536b2b12..363bde4e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,7 +16,7 @@ and this project aspires to adhere to [Semantic Versioning](https://semver.org/s - Added a `vtk` extract that saves each mesh domain to a legacy vtk file grouped, with all domain data grouped by a `.visit` file. - Added WarpX Streamline filter that uses charged particles. - Added seed population options for particle advection: point, point list, line, and box. -- Added Uniform Grid Sampling filter +- Added Uniform Grid Sampling filter. - Added Material Interface Reconstruction (`mir`) filter which produces `cellMat` output field. ### Changed From 2e46ff8943537fe8a6218a27e958f4e93c343a28 Mon Sep 17 00:00:00 2001 From: Nicole Marsaglia Date: Tue, 18 Jun 2024 22:19:52 +0000 Subject: [PATCH 14/21] default to conduit naming conventions for now. zero copy for sparse_by_element seg faulting in vtkm --- .../runtimes/ascent_vtkh_data_adapter.cpp | 329 +++++++++++++----- .../ascent_runtime_vtkh_filters.cpp | 2 +- src/libs/vtkh/filters/MIR.cpp | 24 +- 3 files changed, 259 insertions(+), 96 deletions(-) diff --git a/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp b/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp index 9c47f3548..417e10dda 100644 --- a/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp +++ b/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp @@ -256,6 +256,10 @@ vtkm::cont::Field GetField(const conduit::Node &node, { vtkm_assoc = vtkm::cont::Field::Association::Cells; } + else if(assoc_str == "whole") + { + vtkm_assoc = vtkm::cont::Field::Association::WholeDataSet; + } else { ASCENT_ERROR("Cannot add field association "< 0) { - v_ids[offset] = j + 1; //IDs cannot start at zero + v_ids[offset] = j + 1; //IDs cannot start at 0 + if(v_ids[offset] > 3) + { + std::cerr << "v_ids[offset] out: " << v_ids[offset] << std::endl; + std::cerr << std::endl; + } v_vfs[offset] = data[i]; offset++; } @@ -589,18 +598,18 @@ void GetMatSetIDsAndVFs(const conduit::Node &node, //materials["matset"] const T *ids_ptr = v_ids.data(); ids = vtkm::cont::make_Field(ids_name, - vtkm_assoc, - ids_ptr, - total, - copy); + vtkm_assoc, + ids_ptr, + total, + copy); const S *vfs_ptr = v_vfs.data(); vfs = vtkm::cont::make_Field(vfs_name, - vtkm_assoc, - vfs_ptr, - total, - copy); + vtkm_assoc, + vfs_ptr, + total, + copy); } @@ -1998,95 +2007,241 @@ VTKHDataAdapter::AddMatSets(const std::string &matset_name, bool zero_copy) // attempt to zero copy { - int num_materials = n_matset["volume_fractions"].number_of_children(); - if(num_materials == 0) + if(!n_matset.has_child("volume_fractions")) ASCENT_ERROR("No volume fractions were defined for matset: " << matset_name); - std::string assoc_str = "element"; - NodeConstIterator itr = n_matset["volume_fractions"].children(); - std::string material_name, length_name, offsets_name, ids_name, vfs_name; - length_name = matset_name + "_lengths"; - offsets_name = matset_name + "_offsets"; - ids_name = matset_name + "_ids"; - vfs_name = matset_name + "_vfs"; - //add each material of specified matset to vtkm dataset - const Node &n_material = itr.next(); - material_name = itr.name(); - std::string field_name = matset_name + "_" + material_name; - int num_vals = n_material.dtype().number_of_elements(); - if(num_vals != neles ) - { - ASCENT_ERROR("Number of vf values " - << num_vals - << " for material " - << material_name - << " does not equal number of cells " - << neles); - } - try + std::string assoc_str = "element"; + //fields required from VTK-m MIR filter + //std::string length_name, offsets_name, ids_name, vfs_name; + std::string length_name = "sizes";//matset_name + "_lengths"; + std::string offsets_name = "offsets";//matset_name + "_offsets"; + std::string ids_name = "material_ids";//matset_name + "_ids"; + std::string vfs_name = "volume_fractions";//matset_name + "_vfs"; + //matset is "sparse_by_element" + if(n_matset.has_child("material_map")) { - bool supported_type = false; - - // we compile vtk-h with fp types - if(n_material.dtype().is_float32()) + try { - //add materials directly - //dset->AddField(detail::GetField(n_material, - // field_name, - // assoc_str, - // topo_name, - // element_stride, - // zero_copy)); - supported_type = true; - //add calculated material fields for vtkm - int total; - vtkm::cont::Field length, offsets, ids, vfs; - detail::GetMatSetLength(n_matset, length_name, offsets_name, topo_name, total,neles, length, offsets); - detail::GetMatSetIDsAndVFs(n_matset, ids_name, vfs_name, topo_name,total, neles, offsets, ids, vfs); - dset->AddField(length); - dset->AddField(offsets); - dset->AddField(ids); - dset->AddField(vfs); - //std::cerr << "total: " << total << std::endl; + bool supported_type = false; + + // we compile vtk-h with fp types + if(n_matset["volume_fractions"].dtype().is_float32()) + { + //add materials directly + const conduit::Node n_length = n_matset["sizes"]; + dset->AddField(detail::GetField(n_length, + length_name, + assoc_str, + topo_name, + index_t(1), + zero_copy)); + const conduit::Node n_offsets = n_matset["offsets"]; + dset->AddField(detail::GetField(n_offsets, + offsets_name, + assoc_str, + topo_name, + index_t(1), + zero_copy)); + conduit::Node n_material_ids = n_matset["material_ids"]; + int num_vals = n_material_ids.dtype().number_of_elements(); + if(n_material_ids.dtype().is_int64()) + { + conduit::int64 *vec_ids = n_material_ids.as_int64_ptr(); + std::cerr << "num_vals: " << num_vals << std::endl; + for(int i = 0; i < num_vals; ++i) + { + vec_ids[i] += 1.0; + } + } + else if(n_material_ids.dtype().is_int32()) + { + conduit::int32 *vec_ids = n_material_ids.as_int32_ptr(); + std::cerr << "num_vals: " << num_vals << std::endl; + for(int i = 0; i < num_vals; ++i) + { + vec_ids[i] += 1.0; + } + } + else + { + ASCENT_ERROR("Unsupported integer type for material IDs"); + } + dset->AddField(detail::GetField(n_material_ids, + ids_name, + "whole", + topo_name, + index_t(1), + zero_copy)); + const conduit::Node n_volume_fractions = n_matset["volume_fractions"]; + dset->AddField(detail::GetField(n_volume_fractions, + vfs_name, + "whole", + topo_name, + index_t(1), + zero_copy)); + supported_type = true; + } + else if(n_matset["volume_fractions"].dtype().is_float64()) + { + //add materials directly + const conduit::Node n_length = n_matset["sizes"]; + dset->AddField(detail::GetField(n_length, + length_name, + assoc_str, + topo_name, + index_t(1), + zero_copy)); + const conduit::Node n_offsets = n_matset["offsets"]; + dset->AddField(detail::GetField(n_offsets, + offsets_name, + assoc_str, + topo_name, + index_t(1), + zero_copy)); + conduit::Node n_material_ids = n_matset["material_ids"]; + int num_vals = n_material_ids.dtype().number_of_elements(); + if(n_material_ids.dtype().is_int64()) + { + conduit::int64 *vec_ids = n_material_ids.as_int64_ptr(); + std::cerr << "num_vals: " << num_vals << std::endl; + for(int i = 0; i < num_vals; ++i) + { + vec_ids[i] += 1.0; + } + } + else if(n_material_ids.dtype().is_int32()) + { + conduit::int32 *vec_ids = n_material_ids.as_int32_ptr(); + std::cerr << "num_vals: " << num_vals << std::endl; + for(int i = 0; i < num_vals; ++i) + { + vec_ids[i] += 1.0; + } + } + else + { + ASCENT_ERROR("Unsupported integer type for material IDs"); + } + + dset->AddField(detail::GetField(n_material_ids, + ids_name, + "whole", + topo_name, + index_t(1), + zero_copy)); + std::cerr << "HERE 2" << std::endl; + const conduit::Node n_volume_fractions = n_matset["volume_fractions"]; + dset->AddField(detail::GetField(n_volume_fractions, + vfs_name, + "whole", + topo_name, + index_t(1), + zero_copy)); + std::cerr << "print data from data adaptor: " << std::endl; + dset->PrintSummary(std::cerr); + supported_type = true; + } } - else if(n_material.dtype().is_float64()) + catch (vtkm::cont::Error error) { - //add materials directly - //dset->AddField(detail::GetField(n_material, - // field_name, - // assoc_str, - // topo_name, - // element_stride, - // zero_copy)); - supported_type = true; - //add calculated material fields for vtkm - int total; - vtkm::cont::Field length, offsets, ids, vfs; - detail::GetMatSetLength(n_matset, length_name, offsets_name, topo_name, total,neles, length, offsets); - detail::GetMatSetIDsAndVFs(n_matset, ids_name, vfs_name, topo_name,total, neles, offsets, ids, vfs); - dset->AddField(length); - dset->AddField(offsets); - dset->AddField(ids); - dset->AddField(vfs); - //std::cerr << "total: " << total << std::endl; - //std::cerr << "float64 length: " << std::endl; - //length.PrintSummary(std::cerr); - //std::cerr << "float64 offsets: " << std::endl; - //offsets.PrintSummary(std::cerr); - //std::cerr << "float64 ids: " << std::endl; - //ids.PrintSummary(std::cerr); - //std::cerr << "float64 vfs: " << std::endl; - //vfs.PrintSummary(std::cerr); - - //calculate vf and ids + ASCENT_ERROR("VTKm exception:" << error.GetMessage()); } + + + } - catch (vtkm::cont::Error error) + //matset is "sparse_by_material" + else if(n_matset.has_child("element_ids")) { - ASCENT_ERROR("VTKm exception:" << error.GetMessage()); + + } - + //matset is "full" + else + { + std::cerr << "HERE 3" << std::endl; + int num_materials = n_matset["volume_fractions"].number_of_children(); + std::cerr << "num_materials: " << num_materials << std::endl; + if(num_materials == 0) + ASCENT_ERROR("No volume fractions were defined for matset: " << matset_name); + + NodeConstIterator itr = n_matset["volume_fractions"].children(); + const Node n_material = itr.next(); + std::string material_name = itr.name(); + + int num_vals = n_material.dtype().number_of_elements(); + + if(num_vals != neles ) + { + ASCENT_ERROR("Number of vf values " + << num_vals + << " for material " + << material_name + << " does not equal number of cells " + << neles); + } + try + { + bool supported_type = false; + + // we compile vtk-h with fp types + if(n_material.dtype().is_float32()) + { + //add materials directly + //dset->AddField(detail::GetField(n_material, + // field_name, + // assoc_str, + // topo_name, + // element_stride, + // zero_copy)); + supported_type = true; + //add calculated material fields for vtkm + int total; + vtkm::cont::Field length, offsets, ids, vfs; + detail::GetMatSetLength(n_matset, length_name, offsets_name, topo_name, total,neles, length, offsets); + detail::GetMatSetIDsAndVFs(n_matset, ids_name, vfs_name, topo_name,total, neles, offsets, ids, vfs); + dset->AddField(length); + dset->AddField(offsets); + dset->AddField(ids); + dset->AddField(vfs); + //std::cerr << "total: " << total << std::endl; + } + else if(n_material.dtype().is_float64()) + { + //add materials directly + //dset->AddField(detail::GetField(n_material, + // field_name, + // assoc_str, + // topo_name, + // element_stride, + // zero_copy)); + supported_type = true; + //add calculated material fields for vtkm + int total; + vtkm::cont::Field length, offsets, ids, vfs; + detail::GetMatSetLength(n_matset, length_name, offsets_name, topo_name, total,neles, length, offsets); + detail::GetMatSetIDsAndVFs(n_matset, ids_name, vfs_name, topo_name,total, neles, offsets, ids, vfs); + dset->AddField(length); + dset->AddField(offsets); + dset->AddField(ids); + dset->AddField(vfs); + //std::cerr << "total: " << total << std::endl; + //std::cerr << "float64 length: " << std::endl; + //length.PrintSummary(std::cerr); + //std::cerr << "float64 offsets: " << std::endl; + //offsets.PrintSummary(std::cerr); + //std::cerr << "float64 ids: " << std::endl; + //ids.PrintSummary(std::cerr); + //std::cerr << "float64 vfs: " << std::endl; + //vfs.PrintSummary(std::cerr); + } + } + catch (vtkm::cont::Error error) + { + ASCENT_ERROR("VTKm exception:" << error.GetMessage()); + } + } } std::string diff --git a/src/libs/ascent/runtimes/flow_filters/ascent_runtime_vtkh_filters.cpp b/src/libs/ascent/runtimes/flow_filters/ascent_runtime_vtkh_filters.cpp index e179fb6e4..dfad1c19d 100644 --- a/src/libs/ascent/runtimes/flow_filters/ascent_runtime_vtkh_filters.cpp +++ b/src/libs/ascent/runtimes/flow_filters/ascent_runtime_vtkh_filters.cpp @@ -5028,7 +5028,7 @@ VTKHMIR::execute() std::shared_ptr collection = data_object->as_vtkh_collection(); std::string matset_name = params()["matset"].as_string(); - std::string ids_name = matset_name + "_ids"; + std::string ids_name = "material_ids";//matset_name + "_ids"; if(!collection->has_field(ids_name)) { bool throw_error = false; diff --git a/src/libs/vtkh/filters/MIR.cpp b/src/libs/vtkh/filters/MIR.cpp index 4c61b4d65..a68bbfdd8 100644 --- a/src/libs/vtkh/filters/MIR.cpp +++ b/src/libs/vtkh/filters/MIR.cpp @@ -38,10 +38,10 @@ void MIR::SetMatSet(const std::string matset_name) { m_matset_name = matset_name; - m_lengths_name = matset_name + "_lengths"; - m_offsets_name = matset_name + "_offsets"; - m_ids_name = matset_name + "_ids"; - m_vfs_name = matset_name + "_vfs"; + m_lengths_name = "sizes";// matset_name + "_lengths"; + m_offsets_name = "offsets";// matset_name + "_offsets"; + m_ids_name = "material_ids";// matset_name + "_ids"; + m_vfs_name = "volume_fractions";// matset_name + "_vfs"; } void @@ -72,10 +72,12 @@ void MIR::PreExecute() { Filter::PreExecute(); - std::string lengths_field = m_matset_name + "_lengths"; - std::string offsets_field = m_matset_name + "_offsets"; - std::string ids_field = m_matset_name + "_ids"; - std::string vfs_field = m_matset_name + "_vfs"; + std::string lengths_field ="sizes";// m_matset_name + "_lengths"; + std::string offsets_field = "offsets";//m_matset_name + "_offsets"; + std::string ids_field = "material_ids";//m_matset_name + "_ids"; + std::string vfs_field = "volume_fractions";//m_matset_name + "_vfs"; + std::cerr << "IDS NAME " << ids_field << std::endl; + Filter::CheckForRequiredField(lengths_field); Filter::CheckForRequiredField(offsets_field); Filter::CheckForRequiredField(ids_field); @@ -90,9 +92,11 @@ MIR::PostExecute() void MIR::DoExecute() { + std::cerr << "HERE 4" << std::endl; this->m_output = new DataSet(); const int num_domains = this->m_input->GetNumberOfDomains(); //set fake discret color table + std::cerr << "m_ids_name: " << m_ids_name << std::endl; vtkm::Range ids_range = this->m_input->GetGlobalRange(m_ids_name).ReadPortal().Get(0); std::cerr << "ids global range: " << ids_range.Min << " " << ids_range.Max << std::endl; @@ -101,6 +105,8 @@ void MIR::DoExecute() vtkm::Id domain_id; vtkm::cont::DataSet dom; this->m_input->GetDomain(i, dom, domain_id); + std::cerr << "DATA before execute" << std::endl; + dom.PrintSummary(std::cerr); vtkm::filter::contour::MIRFilter mir; mir.SetLengthCellSetName(m_lengths_name); mir.SetPositionCellSetName(m_offsets_name); @@ -110,7 +116,9 @@ void MIR::DoExecute() mir.SetScalingDecay(vtkm::Float64(m_scaling_decay)); mir.SetMaxIterations(vtkm::IdComponent(m_iterations)); mir.SetMaxPercentError(vtkm::Float64(m_max_error)); + std::cerr << "before execute" << std::endl; vtkm::cont::DataSet output = mir.Execute(dom); + std::cerr << "after execute" << std::endl; //cast and call error if cellMat stays as ints vtkm::cont::UnknownArrayHandle float_field = output.GetField("cellMat").GetDataAsDefaultFloat(); output.GetField("cellMat").SetData(float_field); From bc02cd5ebcefdb9d284fb6efa48fab3d886a1809 Mon Sep 17 00:00:00 2001 From: Nicole Marsaglia Date: Tue, 18 Jun 2024 23:56:11 +0000 Subject: [PATCH 15/21] start of sparse_by_material. can't do zero copy but can probably combine into full's calcs --- .../runtimes/ascent_vtkh_data_adapter.cpp | 208 +++++++++++++----- 1 file changed, 153 insertions(+), 55 deletions(-) diff --git a/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp b/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp index 417e10dda..8bcb53188 100644 --- a/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp +++ b/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp @@ -500,29 +500,50 @@ void GetMatSetLength(const conduit::Node &node, //materials["matset"] int &total, const int neles, vtkm::cont::Field &length, - vtkm::cont::Field &offsets) + vtkm::cont::Field &offsets, + vtkm::cont::Field &ids, + vtkm::cont::Field &vfs) { vtkm::CopyFlag copy = vtkm::CopyFlag::On; - vtkm::cont::Field::Association vtkm_assoc = vtkm::cont::Field::Association::Cells; + vtkm::cont::Field::Association vtkm_assoc_c = vtkm::cont::Field::Association::Cells; std::vector v_length(neles,0); std::vector v_offsets(neles,0); - for(int i = 0; i < neles; ++i) + if(node.has_child("element_ids")) { - NodeConstIterator itr = node["volume_fractions"].children(); + NodeConstIterator itr = node["element_ids"].children(); std::string material_name; + while(itr.has_next()) + { - //increase length when a material vf value > 0 + const Node &n_material = itr.next(); + const int nvals = n_material.dtype().number_of_elements(); + const T *data = n_material.value(); + //increase length when a material vf value > 0 + for(int i = 0; i < nvals; ++i) + { + v_length[data[i]] += 1; + } + } + } + else + { + NodeConstIterator itr = node["volume_fractions"].children(); + std::string material_name; while(itr.has_next()) { + const Node &n_material = itr.next(); const S *data = n_material.value(); - if(data[i] > 0) - v_length[i] = v_length[i] + 1; + //increase length when a material vf value > 0 + for(int i = 0; i < neles; ++i) + { + if(data[i] > 0) + v_length[i] = v_length[i] + 1; + } } } - //calc offset of length and total length int l_total = 0; for(int i = 0; i < neles-1; ++i) @@ -536,7 +557,7 @@ void GetMatSetLength(const conduit::Node &node, //materials["matset"] const T *length_ptr = v_length.data(); length = vtkm::cont::make_Field(length_name, - vtkm_assoc, + vtkm_assoc_c, length_ptr, neles, copy); @@ -544,11 +565,55 @@ void GetMatSetLength(const conduit::Node &node, //materials["matset"] const T *offsets_ptr = v_offsets.data(); offsets = vtkm::cont::make_Field(offsets_name, - vtkm_assoc, + vtkm_assoc_c, offsets_ptr, neles, copy); +//calc vfs and mat ids + vtkm::cont::Field::Association vtkm_assoc_w = vtkm::cont::Field::Association::WholeDataSet; + std::vector v_ids(total,0); + std::vector v_vfs(total,0); + if(node.has_child("element_ids")) + { + int num_materials = node["element_ids"].number_of_children(); + } + else + { + int num_materials = node["volume_fractions"].number_of_children(); + for(int i = 0; i < num_materials; ++i) + { + const Node &n_materials = node["volume_fractions"]; + const Node &n_material = n_materials.child(i); + const S *data = n_material.value(); + + for(int j = 0; j < neles; ++j) + { + int offset = offsets[j]; + if(data[j] > 0) + { + v_ids[offset] = i + 1; //IDs cannot start at 0 + v_vfs[offset] = data[j]; + offset++; + } + } + } + } + const T *ids_ptr = v_ids.data(); + + ids = vtkm::cont::make_Field(ids_name, + vtkm_assoc_w, + ids_ptr, + total, + copy); + + const S *vfs_ptr = v_vfs.data(); + + vfs = vtkm::cont::make_Field(vfs_name, + vtkm_assoc_w, + vfs_ptr, + total, + copy); } template @@ -559,37 +624,26 @@ void GetMatSetIDsAndVFs(const conduit::Node &node, //materials["matset"] const int total, const int neles, vtkm::cont::Field &offsets, - vtkm::cont::Field &ids, - vtkm::cont::Field &vfs) { vtkm::CopyFlag copy = vtkm::CopyFlag::On; - vtkm::cont::Field::Association vtkm_assoc = vtkm::cont::Field::Association::WholeDataSet; vtkm::cont::ArrayHandle ah_offsets; offsets.GetData().AsArrayHandle(ah_offsets); - std::vector v_ids(total,0); - std::vector v_vfs(total,0); int num_materials = node["volume_fractions"].number_of_children(); - for(int i = 0; i < neles; ++i) + for(int i = 0; i < num_materials; ++i) { - int offset = ah_offsets.ReadPortal().Get(i); + int offset = ah_offsets.ReadPortal().Get(j); const Node &n_materials = node["volume_fractions"]; - std::string material_name; + const Node &n_material = n_materials.child(i); + const S *data = n_material.value(); - for(int j = 0; j < num_materials; ++j) + for(int j = 0; j < neles; ++j) { - const Node &n_material = n_materials.child(j); - const S *data = n_material.value(); - if(data[i] > 0) + if(data[j] > 0) { v_ids[offset] = j + 1; //IDs cannot start at 0 - if(v_ids[offset] > 3) - { - std::cerr << "v_ids[offset] out: " << v_ids[offset] << std::endl; - std::cerr << std::endl; - } - v_vfs[offset] = data[i]; + v_vfs[offset] = data[j]; offset++; } } @@ -2009,7 +2063,7 @@ VTKHDataAdapter::AddMatSets(const std::string &matset_name, if(!n_matset.has_child("volume_fractions")) ASCENT_ERROR("No volume fractions were defined for matset: " << matset_name); - + zero_copy = false; std::string assoc_str = "element"; //fields required from VTK-m MIR filter @@ -2154,8 +2208,76 @@ VTKHDataAdapter::AddMatSets(const std::string &matset_name, //matset is "sparse_by_material" else if(n_matset.has_child("element_ids")) { + int num_ids = n_matset["element_ids"].number_of_children(); + std::cerr << "num_ids: " << num_ids << std::endl; + if(num_ids == 0) + { + ASCENT_ERROR("No element ids were defined for matset: " << matset_name); + } + int num_materials = n_matset["volume_fractions"].number_of_children(); + std::cerr << "num_materials: " << num_materials << std::endl; + if(num_materials == 0) + { + ASCENT_ERROR("No volume fractions were defined for matset: " << matset_name); + } + + if(num_materials != num_ids) + { + ASCENT_ERROR("Number of materials (" << num_materials << + ") does not match number of elment IDs(" << num_ids << + " defined for matset: " << matset_name); + } + + const conduit::Node n_vfs = n_matset["volume_fractions"].child(0); + try + { + bool supported_type = false; + // we compile vtk-h with fp types + if(n_vfs.dtype().is_float32()) + { + supported_type = true; + //add calculated material fields for vtkm + int total; + vtkm::cont::Field length, offsets, ids, vfs; + //detail::SparseGetMatSetLength(n_matset, length_name, offsets_name, topo_name, total,neles, length, offsets); + //detail::SparseGetMatSetIDsAndVFs(n_matset, ids_name, vfs_name, topo_name,total, neles, offsets, ids, vfs); + //dset->AddField(length); + //dset->AddField(offsets); + //dset->AddField(ids); + //dset->AddField(vfs); + //std::cerr << "total: " << total << std::endl; + } + else if(n_vfs.dtype().is_float64()) + { + //NodeConstIterator itr = n_matset["volume_fractions"].children(); + std::cerr << "neles: " << neles << std::endl; + NodeConstIterator itr = n_matset["element_ids"].children(); + while(itr.has_next()) + { + const Node &n_material = itr.next(); + const std::string material_name = n_material.name(); + const int num_mat_vals = n_material.dtype().number_of_elements(); + std::cerr << "element_ids: " << material_name << " has num_vals: " << num_mat_vals << std::endl; + } + supported_type = true; + //add calculated material fields for vtkm + int total; + vtkm::cont::Field length, offsets, ids, vfs; + detail::GetMatSetLength(n_matset, length_name, offsets_name, topo_name, total,neles, length, offsets); + std::cerr << "total: " << total << std::endl; + //detail::SparseGetMatSetIDsAndVFs(n_matset, ids_name, vfs_name, topo_name,total, neles, offsets, ids, vfs); + //dset->AddField(length); + //dset->AddField(offsets); + //dset->AddField(ids); + //dset->AddField(vfs); + } + } + catch (vtkm::cont::Error error) + { + ASCENT_ERROR("VTKm exception:" << error.GetMessage()); + } } //matset is "full" else @@ -2166,9 +2288,8 @@ VTKHDataAdapter::AddMatSets(const std::string &matset_name, if(num_materials == 0) ASCENT_ERROR("No volume fractions were defined for matset: " << matset_name); - NodeConstIterator itr = n_matset["volume_fractions"].children(); - const Node n_material = itr.next(); - std::string material_name = itr.name(); + const Node n_material = n_matset["volume_fractions"].child(0); + std::string material_name = n_material.name(); int num_vals = n_material.dtype().number_of_elements(); @@ -2188,13 +2309,6 @@ VTKHDataAdapter::AddMatSets(const std::string &matset_name, // we compile vtk-h with fp types if(n_material.dtype().is_float32()) { - //add materials directly - //dset->AddField(detail::GetField(n_material, - // field_name, - // assoc_str, - // topo_name, - // element_stride, - // zero_copy)); supported_type = true; //add calculated material fields for vtkm int total; @@ -2209,13 +2323,6 @@ VTKHDataAdapter::AddMatSets(const std::string &matset_name, } else if(n_material.dtype().is_float64()) { - //add materials directly - //dset->AddField(detail::GetField(n_material, - // field_name, - // assoc_str, - // topo_name, - // element_stride, - // zero_copy)); supported_type = true; //add calculated material fields for vtkm int total; @@ -2226,15 +2333,6 @@ VTKHDataAdapter::AddMatSets(const std::string &matset_name, dset->AddField(offsets); dset->AddField(ids); dset->AddField(vfs); - //std::cerr << "total: " << total << std::endl; - //std::cerr << "float64 length: " << std::endl; - //length.PrintSummary(std::cerr); - //std::cerr << "float64 offsets: " << std::endl; - //offsets.PrintSummary(std::cerr); - //std::cerr << "float64 ids: " << std::endl; - //ids.PrintSummary(std::cerr); - //std::cerr << "float64 vfs: " << std::endl; - //vfs.PrintSummary(std::cerr); } } catch (vtkm::cont::Error error) From 47fdbf73720f8213812eb91e4c5e7a77c0ef56cd Mon Sep 17 00:00:00 2001 From: Nicole Marsaglia Date: Thu, 20 Jun 2024 19:16:38 +0000 Subject: [PATCH 16/21] fix offsetting volume fractions -- needed to encorporate length. Still need to hunt down zero-copy bug in vtkm. Otherwise done. --- .../runtimes/ascent_vtkh_data_adapter.cpp | 265 ++++++++++-------- .../ascent_runtime_rendering_filters.cpp | 13 +- .../ascent_runtime_vtkh_filters.cpp | 1 - src/libs/vtkh/filters/MIR.cpp | 8 - src/libs/vtkh/rendering/Annotator.cpp | 1 - ..._venn100.png => tout_mir_venn_full100.png} | Bin .../tout_mir_venn_sparse_by_element100.png | Bin 0 -> 402756 bytes .../tout_mir_venn_sparse_by_material100.png | Bin 0 -> 402756 bytes src/tests/ascent/t_ascent_mir.cpp | 197 ++++++++++++- 9 files changed, 346 insertions(+), 139 deletions(-) rename src/tests/_baseline_images/{tout_mir_venn100.png => tout_mir_venn_full100.png} (100%) create mode 100644 src/tests/_baseline_images/tout_mir_venn_sparse_by_element100.png create mode 100644 src/tests/_baseline_images/tout_mir_venn_sparse_by_material100.png diff --git a/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp b/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp index 8bcb53188..1490a4629 100644 --- a/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp +++ b/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp @@ -493,11 +493,12 @@ bool allEqual(std::vector const &v) template -void GetMatSetLength(const conduit::Node &node, //materials["matset"] +void GetMatSetFields(const conduit::Node &node, //materials["matset"] const std::string &length_name, const std::string &offsets_name, + const std::string &ids_name, + const std::string &vfs_name, const std::string &topo_str, - int &total, const int neles, vtkm::cont::Field &length, vtkm::cont::Field &offsets, @@ -537,22 +538,22 @@ void GetMatSetLength(const conduit::Node &node, //materials["matset"] const Node &n_material = itr.next(); const S *data = n_material.value(); //increase length when a material vf value > 0 - for(int i = 0; i < neles; ++i) + for(index_t i = 0; i < neles; ++i) { if(data[i] > 0) - v_length[i] = v_length[i] + 1; + v_length[i] += 1; } } } + //calc offset of length and total length int l_total = 0; - for(int i = 0; i < neles-1; ++i) + for(index_t i = 0; i < neles-1; ++i) { v_offsets[i+1] = v_offsets[i] + v_length[i]; l_total += v_length[i]; } l_total += v_length[neles-1]; - total = l_total; const T *length_ptr = v_length.data(); @@ -569,42 +570,65 @@ void GetMatSetLength(const conduit::Node &node, //materials["matset"] offsets_ptr, neles, copy); -//calc vfs and mat ids + //calc vfs and mat ids vtkm::cont::Field::Association vtkm_assoc_w = vtkm::cont::Field::Association::WholeDataSet; - std::vector v_ids(total,0); - std::vector v_vfs(total,0); + std::vector v_ids(l_total,0); + std::vector v_vfs(l_total,0); + if(node.has_child("element_ids")) { int num_materials = node["element_ids"].number_of_children(); + const Node &n_vol_fracs = node["volume_fractions"]; + const Node &n_ele_ids = node["element_ids"]; + + for(index_t i = 0; i < num_materials; ++i) + { + const Node &n_vol_frac = n_vol_fracs.child(i); + const Node &n_ele_id = n_ele_ids.child(i); + const S *vf_data = n_vol_frac.value(); + const T *id_data = n_ele_id.value(); + int num_vals = n_ele_id.dtype().number_of_elements(); + + for(index_t j = 0; j < num_vals; ++j) + { + v_length[id_data[j]] -= 1; + index_t offset = v_offsets[id_data[j]]; + index_t length = v_length[id_data[j]]; + v_vfs[offset + length] = vf_data[j]; + v_ids[offset + length] = i+1; //material ids can't start at 0 + } + } } else { int num_materials = node["volume_fractions"].number_of_children(); - for(int i = 0; i < num_materials; ++i) + for(index_t i = 0; i < num_materials; ++i) { const Node &n_materials = node["volume_fractions"]; const Node &n_material = n_materials.child(i); const S *data = n_material.value(); - for(int j = 0; j < neles; ++j) + for(index_t j = 0; j < neles; ++j) { - int offset = offsets[j]; + index_t offset = v_offsets[j]; if(data[j] > 0) { - v_ids[offset] = i + 1; //IDs cannot start at 0 - v_vfs[offset] = data[j]; - offset++; + v_length[j] -= 1; + index_t length = v_length[j]; + v_ids[offset + length] = i + 1; //IDs cannot start at 0 + v_vfs[offset + length] = data[j]; } } } } + const T *ids_ptr = v_ids.data(); ids = vtkm::cont::make_Field(ids_name, vtkm_assoc_w, ids_ptr, - total, + l_total, copy); const S *vfs_ptr = v_vfs.data(); @@ -612,60 +636,60 @@ void GetMatSetLength(const conduit::Node &node, //materials["matset"] vfs = vtkm::cont::make_Field(vfs_name, vtkm_assoc_w, vfs_ptr, - total, + l_total, copy); } -template -void GetMatSetIDsAndVFs(const conduit::Node &node, //materials["matset"] - const std::string &ids_name, - const std::string &vfs_name, - const std::string &topo_str, - const int total, - const int neles, - vtkm::cont::Field &offsets, -{ - vtkm::CopyFlag copy = vtkm::CopyFlag::On; - - vtkm::cont::ArrayHandle ah_offsets; - offsets.GetData().AsArrayHandle(ah_offsets); - - int num_materials = node["volume_fractions"].number_of_children(); - for(int i = 0; i < num_materials; ++i) - { - int offset = ah_offsets.ReadPortal().Get(j); - const Node &n_materials = node["volume_fractions"]; - const Node &n_material = n_materials.child(i); - const S *data = n_material.value(); - - for(int j = 0; j < neles; ++j) - { - if(data[j] > 0) - { - v_ids[offset] = j + 1; //IDs cannot start at 0 - v_vfs[offset] = data[j]; - offset++; - } - } - } - - const T *ids_ptr = v_ids.data(); - - ids = vtkm::cont::make_Field(ids_name, - vtkm_assoc, - ids_ptr, - total, - copy); - - const S *vfs_ptr = v_vfs.data(); - - vfs = vtkm::cont::make_Field(vfs_name, - vtkm_assoc, - vfs_ptr, - total, - copy); - -} +//template +//void GetMatSetIDsAndVFs(const conduit::Node &node, //materials["matset"] +// const std::string &ids_name, +// const std::string &vfs_name, +// const std::string &topo_str, +// const int total, +// const int neles, +// vtkm::cont::Field &offsets, +//{ +// vtkm::CopyFlag copy = vtkm::CopyFlag::On; +// +// vtkm::cont::ArrayHandle ah_offsets; +// offsets.GetData().AsArrayHandle(ah_offsets); +// +// int num_materials = node["volume_fractions"].number_of_children(); +// for(int i = 0; i < num_materials; ++i) +// { +// int offset = ah_offsets.ReadPortal().Get(j); +// const Node &n_materials = node["volume_fractions"]; +// const Node &n_material = n_materials.child(i); +// const S *data = n_material.value(); +// +// for(int j = 0; j < neles; ++j) +// { +// if(data[j] > 0) +// { +// v_ids[offset] = j + 1; //IDs cannot start at 0 +// v_vfs[offset] = data[j]; +// offset++; +// } +// } +// } +// +// const T *ids_ptr = v_ids.data(); +// +// ids = vtkm::cont::make_Field(ids_name, +// vtkm_assoc, +// ids_ptr, +// total, +// copy); +// +// const S *vfs_ptr = v_vfs.data(); +// +// vfs = vtkm::cont::make_Field(vfs_name, +// vtkm_assoc, +// vfs_ptr, +// total, +// copy); +// +//} }; //----------------------------------------------------------------------------- @@ -958,7 +982,6 @@ VTKHDataAdapter::BlueprintToVTKmDataSet(const Node &node, // these are not the materials we are looking for continue; } - std::cerr << "calling AddMatSets on: " << matset_name << " w/topo: " << topo_name << std::endl; AddMatSets(matset_name, n_matset, topo_name, @@ -2063,6 +2086,7 @@ VTKHDataAdapter::AddMatSets(const std::string &matset_name, if(!n_matset.has_child("volume_fractions")) ASCENT_ERROR("No volume fractions were defined for matset: " << matset_name); + //TODO: zero_copy = true segfaulting in vtkm mir filter zero_copy = false; std::string assoc_str = "element"; @@ -2102,7 +2126,6 @@ VTKHDataAdapter::AddMatSets(const std::string &matset_name, if(n_material_ids.dtype().is_int64()) { conduit::int64 *vec_ids = n_material_ids.as_int64_ptr(); - std::cerr << "num_vals: " << num_vals << std::endl; for(int i = 0; i < num_vals; ++i) { vec_ids[i] += 1.0; @@ -2111,7 +2134,6 @@ VTKHDataAdapter::AddMatSets(const std::string &matset_name, else if(n_material_ids.dtype().is_int32()) { conduit::int32 *vec_ids = n_material_ids.as_int32_ptr(); - std::cerr << "num_vals: " << num_vals << std::endl; for(int i = 0; i < num_vals; ++i) { vec_ids[i] += 1.0; @@ -2158,7 +2180,6 @@ VTKHDataAdapter::AddMatSets(const std::string &matset_name, if(n_material_ids.dtype().is_int64()) { conduit::int64 *vec_ids = n_material_ids.as_int64_ptr(); - std::cerr << "num_vals: " << num_vals << std::endl; for(int i = 0; i < num_vals; ++i) { vec_ids[i] += 1.0; @@ -2167,7 +2188,6 @@ VTKHDataAdapter::AddMatSets(const std::string &matset_name, else if(n_material_ids.dtype().is_int32()) { conduit::int32 *vec_ids = n_material_ids.as_int32_ptr(); - std::cerr << "num_vals: " << num_vals << std::endl; for(int i = 0; i < num_vals; ++i) { vec_ids[i] += 1.0; @@ -2184,7 +2204,6 @@ VTKHDataAdapter::AddMatSets(const std::string &matset_name, topo_name, index_t(1), zero_copy)); - std::cerr << "HERE 2" << std::endl; const conduit::Node n_volume_fractions = n_matset["volume_fractions"]; dset->AddField(detail::GetField(n_volume_fractions, vfs_name, @@ -2192,8 +2211,6 @@ VTKHDataAdapter::AddMatSets(const std::string &matset_name, topo_name, index_t(1), zero_copy)); - std::cerr << "print data from data adaptor: " << std::endl; - dset->PrintSummary(std::cerr); supported_type = true; } } @@ -2202,21 +2219,16 @@ VTKHDataAdapter::AddMatSets(const std::string &matset_name, ASCENT_ERROR("VTKm exception:" << error.GetMessage()); } - - } - //matset is "sparse_by_material" - else if(n_matset.has_child("element_ids")) + else if(n_matset.has_child("element_ids"))//matset is "sparse_by_material" { int num_ids = n_matset["element_ids"].number_of_children(); - std::cerr << "num_ids: " << num_ids << std::endl; if(num_ids == 0) { ASCENT_ERROR("No element ids were defined for matset: " << matset_name); } int num_materials = n_matset["volume_fractions"].number_of_children(); - std::cerr << "num_materials: " << num_materials << std::endl; if(num_materials == 0) { ASCENT_ERROR("No volume fractions were defined for matset: " << matset_name); @@ -2229,49 +2241,53 @@ VTKHDataAdapter::AddMatSets(const std::string &matset_name, " defined for matset: " << matset_name); } - const conduit::Node n_vfs = n_matset["volume_fractions"].child(0); try { bool supported_type = false; + const conduit::Node n_vfs = n_matset["volume_fractions"].child(0); // we compile vtk-h with fp types if(n_vfs.dtype().is_float32()) { supported_type = true; //add calculated material fields for vtkm - int total; vtkm::cont::Field length, offsets, ids, vfs; - //detail::SparseGetMatSetLength(n_matset, length_name, offsets_name, topo_name, total,neles, length, offsets); - //detail::SparseGetMatSetIDsAndVFs(n_matset, ids_name, vfs_name, topo_name,total, neles, offsets, ids, vfs); - //dset->AddField(length); - //dset->AddField(offsets); - //dset->AddField(ids); - //dset->AddField(vfs); - //std::cerr << "total: " << total << std::endl; + detail::GetMatSetFields(n_matset, + length_name, + offsets_name, + ids_name, + vfs_name, + topo_name, + neles, + length, + offsets, + ids, + vfs); + dset->AddField(length); + dset->AddField(offsets); + dset->AddField(ids); + dset->AddField(vfs); } else if(n_vfs.dtype().is_float64()) { - //NodeConstIterator itr = n_matset["volume_fractions"].children(); - std::cerr << "neles: " << neles << std::endl; - NodeConstIterator itr = n_matset["element_ids"].children(); - while(itr.has_next()) - { - const Node &n_material = itr.next(); - const std::string material_name = n_material.name(); - const int num_mat_vals = n_material.dtype().number_of_elements(); - std::cerr << "element_ids: " << material_name << " has num_vals: " << num_mat_vals << std::endl; - } supported_type = true; //add calculated material fields for vtkm - int total; vtkm::cont::Field length, offsets, ids, vfs; - detail::GetMatSetLength(n_matset, length_name, offsets_name, topo_name, total,neles, length, offsets); - std::cerr << "total: " << total << std::endl; - //detail::SparseGetMatSetIDsAndVFs(n_matset, ids_name, vfs_name, topo_name,total, neles, offsets, ids, vfs); - //dset->AddField(length); - //dset->AddField(offsets); - //dset->AddField(ids); - //dset->AddField(vfs); + detail::GetMatSetFields(n_matset, + length_name, + offsets_name, + ids_name, + vfs_name, + topo_name, + neles, + length, + offsets, + ids, + vfs); + dset->AddField(length); + dset->AddField(offsets); + dset->AddField(ids); + dset->AddField(vfs); } } catch (vtkm::cont::Error error) @@ -2279,12 +2295,9 @@ VTKHDataAdapter::AddMatSets(const std::string &matset_name, ASCENT_ERROR("VTKm exception:" << error.GetMessage()); } } - //matset is "full" - else + else //matset is "full" { - std::cerr << "HERE 3" << std::endl; int num_materials = n_matset["volume_fractions"].number_of_children(); - std::cerr << "num_materials: " << num_materials << std::endl; if(num_materials == 0) ASCENT_ERROR("No volume fractions were defined for matset: " << matset_name); @@ -2313,13 +2326,21 @@ VTKHDataAdapter::AddMatSets(const std::string &matset_name, //add calculated material fields for vtkm int total; vtkm::cont::Field length, offsets, ids, vfs; - detail::GetMatSetLength(n_matset, length_name, offsets_name, topo_name, total,neles, length, offsets); - detail::GetMatSetIDsAndVFs(n_matset, ids_name, vfs_name, topo_name,total, neles, offsets, ids, vfs); + detail::GetMatSetFields(n_matset, + length_name, + offsets_name, + ids_name, + vfs_name, + topo_name, + neles, + length, + offsets, + ids, + vfs); dset->AddField(length); dset->AddField(offsets); dset->AddField(ids); dset->AddField(vfs); - //std::cerr << "total: " << total << std::endl; } else if(n_material.dtype().is_float64()) { @@ -2327,8 +2348,17 @@ VTKHDataAdapter::AddMatSets(const std::string &matset_name, //add calculated material fields for vtkm int total; vtkm::cont::Field length, offsets, ids, vfs; - detail::GetMatSetLength(n_matset, length_name, offsets_name, topo_name, total,neles, length, offsets); - detail::GetMatSetIDsAndVFs(n_matset, ids_name, vfs_name, topo_name,total, neles, offsets, ids, vfs); + detail::GetMatSetFields(n_matset, + length_name, + offsets_name, + ids_name, + vfs_name, + topo_name, + neles, + length, + offsets, + ids, + vfs); dset->AddField(length); dset->AddField(offsets); dset->AddField(ids); @@ -2754,7 +2784,6 @@ VTKHDataAdapter::VTKmTopologyToBlueprint(conduit::Node &output, } else { - data_set.PrintSummary(std::cout); ASCENT_ERROR("Mixed explicit types not implemented"); MixedType cells = dyn_cells.AsCellSet(); } diff --git a/src/libs/ascent/runtimes/flow_filters/ascent_runtime_rendering_filters.cpp b/src/libs/ascent/runtimes/flow_filters/ascent_runtime_rendering_filters.cpp index 4aa239e5e..1cc67380d 100644 --- a/src/libs/ascent/runtimes/flow_filters/ascent_runtime_rendering_filters.cpp +++ b/src/libs/ascent/runtimes/flow_filters/ascent_runtime_rendering_filters.cpp @@ -1641,12 +1641,11 @@ CreatePlot::execute() return; } - bool *discrete_color_table = new bool(); - if(graph().workspace().registry().has_entry("discrete_color_table")) - { - discrete_color_table = graph().workspace().registry().fetch("discrete_color_table"); - } - std::cerr << "BOOL: " << *discrete_color_table << " IN CreatePlot " << std::endl; + bool *discrete_color_table = new bool(); + if(graph().workspace().registry().has_entry("discrete_color_table")) + { + discrete_color_table = graph().workspace().registry().fetch("discrete_color_table"); + } std::shared_ptr collection = data_object->as_vtkh_collection(); conduit::Node &plot_params = params(); @@ -1781,9 +1780,7 @@ CreatePlot::execute() { int *is_discrete; - std::cerr << "getting is_discrete" << std::endl; is_discrete = graph().workspace().registry().fetch("is_discrete"); - std::cerr << "got is_discrete: " << *is_discrete << std::endl; //make sure we set the discrete only for mir output field if(is_discrete && field_name == "cellMat") renderer->SetDiscrete(); diff --git a/src/libs/ascent/runtimes/flow_filters/ascent_runtime_vtkh_filters.cpp b/src/libs/ascent/runtimes/flow_filters/ascent_runtime_vtkh_filters.cpp index dfad1c19d..149046279 100644 --- a/src/libs/ascent/runtimes/flow_filters/ascent_runtime_vtkh_filters.cpp +++ b/src/libs/ascent/runtimes/flow_filters/ascent_runtime_vtkh_filters.cpp @@ -5046,7 +5046,6 @@ VTKHMIR::execute() int *is_discrete = new int(); *is_discrete = 1; - std::cerr << "setting is_discrete: " << *is_discrete << std::endl; graph().workspace().registry().add("is_discrete", is_discrete, -1); // TODO keep forever? diff --git a/src/libs/vtkh/filters/MIR.cpp b/src/libs/vtkh/filters/MIR.cpp index a68bbfdd8..05ae62c7f 100644 --- a/src/libs/vtkh/filters/MIR.cpp +++ b/src/libs/vtkh/filters/MIR.cpp @@ -76,7 +76,6 @@ MIR::PreExecute() std::string offsets_field = "offsets";//m_matset_name + "_offsets"; std::string ids_field = "material_ids";//m_matset_name + "_ids"; std::string vfs_field = "volume_fractions";//m_matset_name + "_vfs"; - std::cerr << "IDS NAME " << ids_field << std::endl; Filter::CheckForRequiredField(lengths_field); Filter::CheckForRequiredField(offsets_field); @@ -92,21 +91,16 @@ MIR::PostExecute() void MIR::DoExecute() { - std::cerr << "HERE 4" << std::endl; this->m_output = new DataSet(); const int num_domains = this->m_input->GetNumberOfDomains(); //set fake discret color table - std::cerr << "m_ids_name: " << m_ids_name << std::endl; vtkm::Range ids_range = this->m_input->GetGlobalRange(m_ids_name).ReadPortal().Get(0); - std::cerr << "ids global range: " << ids_range.Min << " " << ids_range.Max << std::endl; for(int i = 0; i < num_domains; ++i) { vtkm::Id domain_id; vtkm::cont::DataSet dom; this->m_input->GetDomain(i, dom, domain_id); - std::cerr << "DATA before execute" << std::endl; - dom.PrintSummary(std::cerr); vtkm::filter::contour::MIRFilter mir; mir.SetLengthCellSetName(m_lengths_name); mir.SetPositionCellSetName(m_offsets_name); @@ -116,9 +110,7 @@ void MIR::DoExecute() mir.SetScalingDecay(vtkm::Float64(m_scaling_decay)); mir.SetMaxIterations(vtkm::IdComponent(m_iterations)); mir.SetMaxPercentError(vtkm::Float64(m_max_error)); - std::cerr << "before execute" << std::endl; vtkm::cont::DataSet output = mir.Execute(dom); - std::cerr << "after execute" << std::endl; //cast and call error if cellMat stays as ints vtkm::cont::UnknownArrayHandle float_field = output.GetField("cellMat").GetDataAsDefaultFloat(); output.GetField("cellMat").SetData(float_field); diff --git a/src/libs/vtkh/rendering/Annotator.cpp b/src/libs/vtkh/rendering/Annotator.cpp index 84a69929a..10cdbaf2d 100644 --- a/src/libs/vtkh/rendering/Annotator.cpp +++ b/src/libs/vtkh/rendering/Annotator.cpp @@ -50,7 +50,6 @@ Annotator::RenderScreenAnnotations(const std::vector &field_names, this->m_color_bar_annotation.SetRange(ranges[i], 5); this->m_color_bar_annotation.SetFieldName(field_names[i]); this->m_color_bar_annotation.SetPosition(m_color_bar_pos[i]); - std::cerr << "color_bar_pos: " << m_color_bar_pos[i].X.Min << " " << m_color_bar_pos[i].X.Max << " " << m_color_bar_pos[i].Y.Min << " " << m_color_bar_pos[i].Y.Max << std::endl; this->m_color_bar_annotation.SetColorTable(color_tables[i]); this->m_color_bar_annotation.Render(m_camera, *m_world_annotator, m_canvas); } diff --git a/src/tests/_baseline_images/tout_mir_venn100.png b/src/tests/_baseline_images/tout_mir_venn_full100.png similarity index 100% rename from src/tests/_baseline_images/tout_mir_venn100.png rename to src/tests/_baseline_images/tout_mir_venn_full100.png diff --git a/src/tests/_baseline_images/tout_mir_venn_sparse_by_element100.png b/src/tests/_baseline_images/tout_mir_venn_sparse_by_element100.png new file mode 100644 index 0000000000000000000000000000000000000000..262519215b84d50a2b9e5f814ce33592bd66e3b9 GIT binary patch literal 402756 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 Date: Wed, 17 Jul 2024 23:40:32 +0000 Subject: [PATCH 17/21] check for nonzeroes. zero copy when we can --- src/docs/sphinx/Actions/Pipelines.rst | 7 +- .../runtimes/ascent_vtkh_data_adapter.cpp | 176 ++++++++++++++---- .../ascent_runtime_rendering_filters.cpp | 5 - 3 files changed, 140 insertions(+), 48 deletions(-) diff --git a/src/docs/sphinx/Actions/Pipelines.rst b/src/docs/sphinx/Actions/Pipelines.rst index 99ca5425a..267e21307 100644 --- a/src/docs/sphinx/Actions/Pipelines.rst +++ b/src/docs/sphinx/Actions/Pipelines.rst @@ -966,9 +966,10 @@ accurate but slower point based gradients (default). Material Interface Reconstruction ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The Material Interface Reconstruction (MIR) filter does something sciency that people like and want. -The user must specify the name of the material set (`matset`) they wish to use. Optional parameters include error scaling (`error_scaling`), scaling decay (`scaling_decay`), maximum iterations (`iterations`), and maximum error percentage (`max_error`). -The output field of the MIR Filter is `cellMat` that can be used further in scenes or pipelines. +The Material Interface Reconstruction (MIR) can be used to represent and track the interfaces between different materials or phases in a multiphase flow. +The user must specify the name of the material set (`matset`) they wish to use. +Optional parameters include error scaling (`error_scaling`), scaling decay (`scaling_decay`), maximum iterations (`iterations`), and maximum error percentage (`max_error`). +The output field of the MIR Filter will be the name of the material set and can be used further in scenes or pipelines. .. code-block:: c++ diff --git a/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp b/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp index 1490a4629..e574a8262 100644 --- a/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp +++ b/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp @@ -273,6 +273,10 @@ vtkm::cont::Field GetField(const conduit::Node &node, // base case is naturally stride data if(element_stride == 1) { + std::cerr << " In element_stride==1 for field: " << field_name << std::endl; + std::cerr << "vtkm_assoc: " << assoc_str << std::endl; + std::cerr << "num_vals: " << num_vals << std::endl; + std::cerr << "copy: " << zero_copy << std::endl; field = vtkm::cont::make_Field(field_name, vtkm_assoc, values_ptr, @@ -281,7 +285,7 @@ vtkm::cont::Field GetField(const conduit::Node &node, } else { - + std::cerr << "not in element_stride==1" << std::endl; // // use ArrayHandleStride to create new field // @@ -2086,8 +2090,12 @@ VTKHDataAdapter::AddMatSets(const std::string &matset_name, if(!n_matset.has_child("volume_fractions")) ASCENT_ERROR("No volume fractions were defined for matset: " << matset_name); + std::cerr << "zero copy going into AddMatSets: " << zero_copy << std::endl; //TODO: zero_copy = true segfaulting in vtkm mir filter - zero_copy = false; + //zero_copy = false; + + //std::cerr << " after setting zero copy to true or false: " << zero_copy << std::endl; + std::string assoc_str = "element"; //fields required from VTK-m MIR filter @@ -2107,49 +2115,93 @@ VTKHDataAdapter::AddMatSets(const std::string &matset_name, if(n_matset["volume_fractions"].dtype().is_float32()) { //add materials directly - const conduit::Node n_length = n_matset["sizes"]; + const conduit::Node &n_length = n_matset["sizes"]; dset->AddField(detail::GetField(n_length, length_name, assoc_str, topo_name, index_t(1), zero_copy)); - const conduit::Node n_offsets = n_matset["offsets"]; + const conduit::Node &n_offsets = n_matset["offsets"]; dset->AddField(detail::GetField(n_offsets, offsets_name, assoc_str, topo_name, index_t(1), zero_copy)); - conduit::Node n_material_ids = n_matset["material_ids"]; + const conduit::Node &n_material_ids = n_matset["material_ids"]; int num_vals = n_material_ids.dtype().number_of_elements(); - if(n_material_ids.dtype().is_int64()) + if(n_material_ids.dtype().is_int32()) { - conduit::int64 *vec_ids = n_material_ids.as_int64_ptr(); - for(int i = 0; i < num_vals; ++i) + const conduit::int32 *n_ids = n_material_ids.value(); + const vector vec_ids(n_ids, n_ids + num_vals); + bool zeroes = std::any_of(vec_ids.begin(), vec_ids.end(), [](int value) { return value<=0; }); + if(zeroes) //need to make a copy and increment all material ids { - vec_ids[i] += 1.0; + conduit::Node n_mat_ids = n_matset["material_ids"]; + conduit::int32 *tmp_vec_ids = n_mat_ids.value(); + for(index_t i = 0; i < num_vals; ++i) + { + tmp_vec_ids[i] += 1.0; + } + vtkm::cont::Field field_copy = detail::GetField(n_mat_ids, + ids_name, + "whole", + topo_name, + index_t(1), + false); + dset->AddField(field_copy); + } + else //can zero copy the material ids + { + vtkm::cont::Field field_copy = detail::GetField(n_material_ids, + ids_name, + "whole", + topo_name, + index_t(1), + zero_copy); + + dset->AddField(field_copy); } } - else if(n_material_ids.dtype().is_int32()) + else if(n_material_ids.dtype().is_int64()) { - conduit::int32 *vec_ids = n_material_ids.as_int32_ptr(); - for(int i = 0; i < num_vals; ++i) + const conduit::int64 *n_ids = n_material_ids.value(); + const vector vec_ids(n_ids, n_ids + num_vals); + bool zeroes = std::any_of(vec_ids.begin(), vec_ids.end(), [](int value) { return value<=0; }); + if(zeroes) //need to make a copy and increment all material ids { - vec_ids[i] += 1.0; + conduit::Node n_mat_ids = n_matset["material_ids"]; + conduit::int64 *tmp_vec_ids = n_mat_ids.value(); + for(index_t i = 0; i < num_vals; ++i) + { + tmp_vec_ids[i] += 1.0; + } + vtkm::cont::Field field_copy = detail::GetField(n_mat_ids, + ids_name, + "whole", + topo_name, + index_t(1), + false); + dset->AddField(field_copy); + } + else //can zero copy the material ids + { + vtkm::cont::Field field_copy = detail::GetField(n_material_ids, + ids_name, + "whole", + topo_name, + index_t(1), + zero_copy); + + dset->AddField(field_copy); } } else { ASCENT_ERROR("Unsupported integer type for material IDs"); } - dset->AddField(detail::GetField(n_material_ids, - ids_name, - "whole", - topo_name, - index_t(1), - zero_copy)); - const conduit::Node n_volume_fractions = n_matset["volume_fractions"]; + const conduit::Node &n_volume_fractions = n_matset["volume_fractions"]; dset->AddField(detail::GetField(n_volume_fractions, vfs_name, "whole", @@ -2161,50 +2213,93 @@ VTKHDataAdapter::AddMatSets(const std::string &matset_name, else if(n_matset["volume_fractions"].dtype().is_float64()) { //add materials directly - const conduit::Node n_length = n_matset["sizes"]; + const Node &n_length = n_matset["sizes"]; dset->AddField(detail::GetField(n_length, length_name, assoc_str, topo_name, index_t(1), zero_copy)); - const conduit::Node n_offsets = n_matset["offsets"]; + const conduit::Node &n_offsets = n_matset["offsets"]; dset->AddField(detail::GetField(n_offsets, offsets_name, assoc_str, topo_name, index_t(1), zero_copy)); - conduit::Node n_material_ids = n_matset["material_ids"]; - int num_vals = n_material_ids.dtype().number_of_elements(); - if(n_material_ids.dtype().is_int64()) + const conduit::Node &n_material_ids = n_matset["material_ids"]; + int num_vals = n_material_ids.dtype().number_of_elements(); + if(n_material_ids.dtype().is_int32()) { - conduit::int64 *vec_ids = n_material_ids.as_int64_ptr(); - for(int i = 0; i < num_vals; ++i) + const conduit::int32 *n_ids = n_material_ids.value(); + const vector vec_ids(n_ids, n_ids + num_vals); + bool zeroes = std::any_of(vec_ids.begin(), vec_ids.end(), [](int value) { return value<=0; }); + if(zeroes) //need to make a copy and increment all material ids + { + conduit::Node n_mat_ids = n_matset["material_ids"]; + conduit::int32 *tmp_vec_ids = n_mat_ids.value(); + for(index_t i = 0; i < num_vals; ++i) + { + tmp_vec_ids[i] += 1.0; + } + vtkm::cont::Field field_copy = detail::GetField(n_mat_ids, + ids_name, + "whole", + topo_name, + index_t(1), + false); + dset->AddField(field_copy); + } + else //can zero copy the material ids { - vec_ids[i] += 1.0; + vtkm::cont::Field field_copy = detail::GetField(n_material_ids, + ids_name, + "whole", + topo_name, + index_t(1), + zero_copy); + + dset->AddField(field_copy); } } - else if(n_material_ids.dtype().is_int32()) + else if(n_material_ids.dtype().is_int64()) { - conduit::int32 *vec_ids = n_material_ids.as_int32_ptr(); - for(int i = 0; i < num_vals; ++i) + const conduit::int64 *n_ids = n_material_ids.value(); + const vector vec_ids(n_ids, n_ids + num_vals); + bool zeroes = std::any_of(vec_ids.begin(), vec_ids.end(), [](int value) { return value<=0; }); + if(zeroes) //need to make a copy and increment all material ids + { + conduit::Node n_mat_ids = n_matset["material_ids"]; + conduit::int64 *tmp_vec_ids = n_mat_ids.value(); + for(index_t i = 0; i < num_vals; ++i) + { + tmp_vec_ids[i] += 1.0; + } + vtkm::cont::Field field_copy = detail::GetField(n_mat_ids, + ids_name, + "whole", + topo_name, + index_t(1), + false); + dset->AddField(field_copy); + } + else //can zero copy the material ids { - vec_ids[i] += 1.0; + vtkm::cont::Field field_copy = detail::GetField(n_material_ids, + ids_name, + "whole", + topo_name, + index_t(1), + zero_copy); + + dset->AddField(field_copy); } } else { ASCENT_ERROR("Unsupported integer type for material IDs"); } - - dset->AddField(detail::GetField(n_material_ids, - ids_name, - "whole", - topo_name, - index_t(1), - zero_copy)); - const conduit::Node n_volume_fractions = n_matset["volume_fractions"]; + const conduit::Node &n_volume_fractions = n_matset["volume_fractions"]; dset->AddField(detail::GetField(n_volume_fractions, vfs_name, "whole", @@ -2784,6 +2879,7 @@ VTKHDataAdapter::VTKmTopologyToBlueprint(conduit::Node &output, } else { + data_set.PrintSummary(std::cout); ASCENT_ERROR("Mixed explicit types not implemented"); MixedType cells = dyn_cells.AsCellSet(); } diff --git a/src/libs/ascent/runtimes/flow_filters/ascent_runtime_rendering_filters.cpp b/src/libs/ascent/runtimes/flow_filters/ascent_runtime_rendering_filters.cpp index 1cc67380d..92b090171 100644 --- a/src/libs/ascent/runtimes/flow_filters/ascent_runtime_rendering_filters.cpp +++ b/src/libs/ascent/runtimes/flow_filters/ascent_runtime_rendering_filters.cpp @@ -1641,11 +1641,6 @@ CreatePlot::execute() return; } - bool *discrete_color_table = new bool(); - if(graph().workspace().registry().has_entry("discrete_color_table")) - { - discrete_color_table = graph().workspace().registry().fetch("discrete_color_table"); - } std::shared_ptr collection = data_object->as_vtkh_collection(); conduit::Node &plot_params = params(); From 26bc850fd13022cf3ecdf2d46b4cf9d08d4b0ab1 Mon Sep 17 00:00:00 2001 From: Nicole Marsaglia Date: Wed, 17 Jul 2024 23:45:07 +0000 Subject: [PATCH 18/21] update docs. rename output from cellmat to matset name --- src/docs/sphinx/Actions/Pipelines.rst | 2 +- src/libs/vtkh/filters/MIR.cpp | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/docs/sphinx/Actions/Pipelines.rst b/src/docs/sphinx/Actions/Pipelines.rst index 267e21307..0ffdc85f2 100644 --- a/src/docs/sphinx/Actions/Pipelines.rst +++ b/src/docs/sphinx/Actions/Pipelines.rst @@ -966,7 +966,7 @@ accurate but slower point based gradients (default). Material Interface Reconstruction ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The Material Interface Reconstruction (MIR) can be used to represent and track the interfaces between different materials or phases in a multiphase flow. +The Material Interface Reconstruction (MIR) filter can be used to represent and track the interfaces between different materials or phases in a multiphase flow. The user must specify the name of the material set (`matset`) they wish to use. Optional parameters include error scaling (`error_scaling`), scaling decay (`scaling_decay`), maximum iterations (`iterations`), and maximum error percentage (`max_error`). The output field of the MIR Filter will be the name of the material set and can be used further in scenes or pipelines. diff --git a/src/libs/vtkh/filters/MIR.cpp b/src/libs/vtkh/filters/MIR.cpp index 05ae62c7f..340cf5a71 100644 --- a/src/libs/vtkh/filters/MIR.cpp +++ b/src/libs/vtkh/filters/MIR.cpp @@ -110,10 +110,20 @@ void MIR::DoExecute() mir.SetScalingDecay(vtkm::Float64(m_scaling_decay)); mir.SetMaxIterations(vtkm::IdComponent(m_iterations)); mir.SetMaxPercentError(vtkm::Float64(m_max_error)); + std::cerr <<"data of domain: " << i << " and domain_id: " << domain_id << std::endl; + std::cerr << "DATA START" << std::endl; + dom.PrintSummary(std::cerr); + std::cerr << "DATA END" << std::endl; + std::cerr << "DATA START AGAIN" << std::endl; + dom.PrintSummary(std::cerr); + std::cerr << "END AGAIN" << std::endl; vtkm::cont::DataSet output = mir.Execute(dom); //cast and call error if cellMat stays as ints vtkm::cont::UnknownArrayHandle float_field = output.GetField("cellMat").GetDataAsDefaultFloat(); - output.GetField("cellMat").SetData(float_field); + vtkm::cont::Field::Association field_assoc = output.GetField("cellMat").GetAssociation(); + vtkm::cont::Field matset_field(m_matset_name,field_assoc, float_field); + output.AddField(matset_field); + //output.GetField("cellMat").SetData(float_field); this->m_output->AddDomain(output, i); // this->m_output->AddDomain(dom, i); //original data } From 25e15b7bed7c764eb743cd2b04d1ffc9864c2eab Mon Sep 17 00:00:00 2001 From: Nicole Marsaglia Date: Thu, 18 Jul 2024 22:31:11 +0000 Subject: [PATCH 19/21] filter output name is matset name, fix zero copy, rework discrete to a color_table pseudocolor param --- .../runtimes/ascent_vtkh_data_adapter.cpp | 8 - .../ascent_runtime_rendering_filters.cpp | 153 +++++++++--------- src/libs/vtkh/filters/MIR.cpp | 7 - .../tout_mir_venn_full100.png | Bin 402756 -> 402795 bytes .../tout_mir_venn_sparse_by_element100.png | Bin 402756 -> 402795 bytes .../tout_mir_venn_sparse_by_material100.png | Bin 402756 -> 402795 bytes src/tests/ascent/t_ascent_mir.cpp | 13 +- 7 files changed, 85 insertions(+), 96 deletions(-) diff --git a/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp b/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp index 739460cf8..4748fd170 100644 --- a/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp +++ b/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp @@ -273,10 +273,6 @@ vtkm::cont::Field GetField(const conduit::Node &node, // base case is naturally stride data if(element_stride == 1) { - std::cerr << " In element_stride==1 for field: " << field_name << std::endl; - std::cerr << "vtkm_assoc: " << assoc_str << std::endl; - std::cerr << "num_vals: " << num_vals << std::endl; - std::cerr << "copy: " << zero_copy << std::endl; field = vtkm::cont::make_Field(field_name, vtkm_assoc, values_ptr, @@ -285,7 +281,6 @@ vtkm::cont::Field GetField(const conduit::Node &node, } else { - std::cerr << "not in element_stride==1" << std::endl; // // use ArrayHandleStride to create new field // @@ -2099,12 +2094,9 @@ VTKHDataAdapter::AddMatSets(const std::string &matset_name, if(!n_matset.has_child("volume_fractions")) ASCENT_ERROR("No volume fractions were defined for matset: " << matset_name); - std::cerr << "zero copy going into AddMatSets: " << zero_copy << std::endl; //TODO: zero_copy = true segfaulting in vtkm mir filter //zero_copy = false; - //std::cerr << " after setting zero copy to true or false: " << zero_copy << std::endl; - std::string assoc_str = "element"; //fields required from VTK-m MIR filter diff --git a/src/libs/ascent/runtimes/flow_filters/ascent_runtime_rendering_filters.cpp b/src/libs/ascent/runtimes/flow_filters/ascent_runtime_rendering_filters.cpp index 7be7b4b0b..00163e199 100644 --- a/src/libs/ascent/runtimes/flow_filters/ascent_runtime_rendering_filters.cpp +++ b/src/libs/ascent/runtimes/flow_filters/ascent_runtime_rendering_filters.cpp @@ -96,6 +96,7 @@ check_color_table_surprises(const conduit::Node &color_table) valid_paths.push_back("name"); valid_paths.push_back("reverse"); valid_paths.push_back("annotation"); + valid_paths.push_back("discrete"); std::vector ignore_paths; ignore_paths.push_back("control_points"); @@ -360,7 +361,6 @@ vtkh::Render parse_render(const conduit::Node &render_node, // render create a default camera. Now get it and check for // values that override the default view // - if(render_node.has_path("camera")) { vtkm::rendering::Camera camera = render.GetCamera(); @@ -1169,10 +1169,10 @@ DefaultRender::execute() std::string output_path = default_dir(); - if(render_node.has_path("output_path")) - { + if(render_node.has_path("output_path")) + { output_path = render_node["output_path"].as_string(); - int rank = 0; + int rank = 0; #ifdef ASCENT_MPI_ENABLED MPI_Comm mpi_comm = MPI_Comm_f2c(Workspace::default_mpi_comm()); MPI_Comm_rank(mpi_comm, &rank); @@ -1182,7 +1182,7 @@ DefaultRender::execute() { conduit::utils::create_directory(output_path); } - } + } if(!render_node.has_path("db_name")) { @@ -1243,104 +1243,106 @@ DefaultRender::execute() "'image_prefix' parameter"); } - if(render_node.has_path("dataset_bounds")) - { - float64_accessor d_bounds = render_node["dataset_bounds"].value(); - int num_bounds = d_bounds.number_of_elements(); - if(num_bounds != 6) + if(render_node.has_path("dataset_bounds")) + { + float64_accessor d_bounds = render_node["dataset_bounds"].value(); + int num_bounds = d_bounds.number_of_elements(); + + if(num_bounds != 6) { std::string render_name = renders_node.child_names()[i]; std::string fpath = filter_to_path(this->name()); ASCENT_ERROR("Render ("< d_bounds[0]) - scene_bounds.X.Min = d_bounds[0]; - if(scene_bounds.X.Max < d_bounds[1]) - scene_bounds.X.Max = d_bounds[1]; - if(scene_bounds.Y.Min > d_bounds[2]) - scene_bounds.Y.Min = d_bounds[2]; - if(scene_bounds.Y.Max < d_bounds[3]) - scene_bounds.Y.Max = d_bounds[3]; - if(scene_bounds.Z.Min > d_bounds[4]) - scene_bounds.Z.Min = d_bounds[4]; - if(scene_bounds.Z.Max < d_bounds[5]) - scene_bounds.Z.Max = d_bounds[5]; - } - - if(is_auto_camera) - { + " dataset_bounds when 6 are required:" << + " [xMin,xMax,yMin,yMax,zMin,zMax]"); + } + if(scene_bounds.X.Min > d_bounds[0]) + scene_bounds.X.Min = d_bounds[0]; + if(scene_bounds.X.Max < d_bounds[1]) + scene_bounds.X.Max = d_bounds[1]; + if(scene_bounds.Y.Min > d_bounds[2]) + scene_bounds.Y.Min = d_bounds[2]; + if(scene_bounds.Y.Max < d_bounds[3]) + scene_bounds.Y.Max = d_bounds[3]; + if(scene_bounds.Z.Min > d_bounds[4]) + scene_bounds.Z.Min = d_bounds[4]; + if(scene_bounds.Z.Max < d_bounds[5]) + scene_bounds.Z.Max = d_bounds[5]; + } + + if(is_auto_camera) + { DataObject *source - = graph().workspace().registry().fetch("source_object"); - + = graph().workspace().registry().fetch("source_object"); + std::shared_ptr collection = source->as_vtkh_collection(); - - if(!render_node.has_path("auto_camera/field")) + + if(!render_node.has_path("auto_camera/field")) ASCENT_ERROR("Auto Camera must specify a 'field'"); - if(!render_node.has_path("auto_camera/metric")) + if(!render_node.has_path("auto_camera/metric")) ASCENT_ERROR("Auto Camera must specify a 'metric'"); - if(!render_node.has_path("auto_camera/samples")) + if(!render_node.has_path("auto_camera/samples")) ASCENT_ERROR("Auto Camera must specify number of 'samples'"); std::string field_name = render_node["auto_camera/field"].as_string(); std::string metric = render_node["auto_camera/metric"].as_string(); - int samples = render_node["auto_camera/samples"].to_int64(); - + int samples = render_node["auto_camera/samples"].as_int32(); + if(!collection->has_field(field_name)) { ASCENT_ERROR("Unknown field '"<field_topology(field_name); vtkh::DataSet &dataset = collection->dataset_by_topology(topo_name); - + vtkh::AutoCamera auto_cam; - - int height = 1024; - int width = 1024; + + int height = 1024; + int width = 1024; if(render_node.has_path("auto_camera/bins")) { - int bins = render_node["auto_camera/bins"].to_int64(); + int bins = render_node["auto_camera/bins"].as_int32(); auto_cam.SetNumBins(bins); } if(render_node.has_path("auto_camera/height")) { - height = render_node["auto_camera/height"].to_int64(); + height = render_node["auto_camera/height"].as_int32(); auto_cam.SetHeight(height); } if(render_node.has_path("auto_camera/width")) { - width = render_node["auto_camera/width"].to_int64(); + width = render_node["auto_camera/width"].as_int32(); auto_cam.SetWidth(width); } - + auto_cam.SetInput(&dataset); auto_cam.SetField(field_name); auto_cam.SetMetric(metric); auto_cam.SetNumSamples(samples); auto_cam.Update(); - + vtkm::rendering::Camera *camera = new vtkm::rendering::Camera; *camera = auto_cam.GetCamera(); - vtkh::Render render = vtkh::MakeRender(width, - height, - scene_bounds, - *camera, - image_name); + vtkh::Render render = vtkh::MakeRender(width, + height, + scene_bounds, + *camera, + image_name); renders->push_back(render); - delete camera; - } - else - { + delete camera; + + } + else + { + vtkh::Render render = detail::parse_render(render_node, scene_bounds, image_name); renders->push_back(render); - } + } } } } @@ -1351,7 +1353,6 @@ DefaultRender::execute() if(params().has_path("image_name")) { image_name = params()["image_name"].as_string(); - image_name = output_dir(image_name); } else { @@ -1433,7 +1434,7 @@ VTKHBounds::execute() std::shared_ptr collection = data_object->as_vtkh_collection(); bounds->Include(collection->global_bounds()); } - + set_output(bounds); } @@ -1743,10 +1744,20 @@ CreatePlot::execute() { if(plot_params["color_table"].has_path("annotation")) { - if(plot_params["color_table/annotation"].as_string() == "false") - { - renderer->DisableColorBar(); - } + if(plot_params["color_table/annotation"].as_string() == "false") + { + renderer->DisableColorBar(); + } + } + if(type != "volume") + { + if(plot_params["color_table"].has_path("discrete")) + { + if(plot_params["color_table/discrete"].as_string() == "true") + { + renderer->SetDiscrete(); + } + } } } renderer->SetColorTable(color_table); @@ -1771,15 +1782,7 @@ CreatePlot::execute() } - if(graph().workspace().registry().has_entry("is_discrete")) - { - - int *is_discrete; - is_discrete = graph().workspace().registry().fetch("is_discrete"); - //make sure we set the discrete only for mir output field - if(is_discrete && field_name == "cellMat") - renderer->SetDiscrete(); - } + if(type == "mesh") { @@ -1918,9 +1921,6 @@ ExecScene::execute() image_data["camera/up"].set(&renders->at(i).GetCamera().GetViewUp()[0],3); image_data["camera/zoom"] = renders->at(i).GetCamera().GetZoom(); image_data["camera/fov"] = renders->at(i).GetCamera().GetFieldOfView(); - image_data["camera/near_plane"] = renders->at(i).GetCamera().GetClippingRange().Min; - image_data["camera/far_plane"] = renders->at(i).GetCamera().GetClippingRange().Max; - vtkm::Bounds bounds= renders->at(i).GetSceneBounds(); double coord_bounds [6] = {bounds.X.Min, bounds.Y.Min, @@ -1928,6 +1928,7 @@ ExecScene::execute() bounds.X.Max, bounds.Y.Max, bounds.Z.Max}; + image_data["scene_bounds"].set(coord_bounds, 6); image_list->append() = image_data; diff --git a/src/libs/vtkh/filters/MIR.cpp b/src/libs/vtkh/filters/MIR.cpp index 340cf5a71..b4c4d6e03 100644 --- a/src/libs/vtkh/filters/MIR.cpp +++ b/src/libs/vtkh/filters/MIR.cpp @@ -110,13 +110,6 @@ void MIR::DoExecute() mir.SetScalingDecay(vtkm::Float64(m_scaling_decay)); mir.SetMaxIterations(vtkm::IdComponent(m_iterations)); mir.SetMaxPercentError(vtkm::Float64(m_max_error)); - std::cerr <<"data of domain: " << i << " and domain_id: " << domain_id << std::endl; - std::cerr << "DATA START" << std::endl; - dom.PrintSummary(std::cerr); - std::cerr << "DATA END" << std::endl; - std::cerr << "DATA START AGAIN" << std::endl; - dom.PrintSummary(std::cerr); - std::cerr << "END AGAIN" << std::endl; vtkm::cont::DataSet output = mir.Execute(dom); //cast and call error if cellMat stays as ints vtkm::cont::UnknownArrayHandle float_field = output.GetField("cellMat").GetDataAsDefaultFloat(); diff --git a/src/tests/_baseline_images/tout_mir_venn_full100.png b/src/tests/_baseline_images/tout_mir_venn_full100.png index 262519215b84d50a2b9e5f814ce33592bd66e3b9..5840cef54ee9a6cbd8372fa1439860066771bac6 100644 GIT binary patch literal 402795 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 Date: Thu, 18 Jul 2024 22:53:48 +0000 Subject: [PATCH 20/21] add color table discrete option to docs. change number of colorbar ticks to diff in mat_id range --- src/docs/sphinx/Actions/Scenes.rst | 5 +++++ src/libs/vtkh/rendering/Annotator.cpp | 5 ++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/docs/sphinx/Actions/Scenes.rst b/src/docs/sphinx/Actions/Scenes.rst index 61466eb8f..348c1e4c2 100644 --- a/src/docs/sphinx/Actions/Scenes.rst +++ b/src/docs/sphinx/Actions/Scenes.rst @@ -242,6 +242,11 @@ Color in the table can be reversed through an optional parameter: scenes["s1/plots/p1/color_table/reverse"] = "true"; +The color table can be made (pseudo) discrete through an optional parameter: + +.. code-block:: c++ + + scenes["s1/plots/p1/color_table/discrete"] = "true"; Volume plots are special since ray casting blends colors together. diff --git a/src/libs/vtkh/rendering/Annotator.cpp b/src/libs/vtkh/rendering/Annotator.cpp index 10cdbaf2d..493352a1a 100644 --- a/src/libs/vtkh/rendering/Annotator.cpp +++ b/src/libs/vtkh/rendering/Annotator.cpp @@ -45,7 +45,10 @@ Annotator::RenderScreenAnnotations(const std::vector &field_names, //TODO: What if we have a large range max? i.e. lots of materials //Need to extend color bar in proportion somehow?? if(is_discrete[i]) - this->m_color_bar_annotation.SetRange(ranges[i],ranges[i].Max); + { + int num_tics = abs(ranges[i].Max - ranges[i].Min) + 1; + this->m_color_bar_annotation.SetRange(ranges[i],num_tics); + } else this->m_color_bar_annotation.SetRange(ranges[i], 5); this->m_color_bar_annotation.SetFieldName(field_names[i]); From 4b143034d961229d3a93f4fbc5047995d8032108 Mon Sep 17 00:00:00 2001 From: Nicole Marsaglia Date: Tue, 23 Jul 2024 23:18:14 +0000 Subject: [PATCH 21/21] some cleanup --- CHANGELOG.md | 2 +- .../flow_filters/ascent_runtime_vtkh_filters.cpp | 9 --------- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 67890ef38..36fa02c6f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,7 +39,7 @@ and this project aspires to adhere to [Semantic Versioning](https://semver.org/s - Added particle advection for streamline and related rendering support. - Added WarpX Streamline filter that uses charged particles. - Added Uniform Grid Sampling filter. -- Added Material Interface Reconstruction (`mir`) filter which produces `cellMat` output field. +- Added Material Interface Reconstruction (`mir`) filter which produces matset output field. - Added seed population options for particle advection: point, point list, line, and box - Added more Ascent tutorial examples - Added support for implicit points style Blueprint input meshes diff --git a/src/libs/ascent/runtimes/flow_filters/ascent_runtime_vtkh_filters.cpp b/src/libs/ascent/runtimes/flow_filters/ascent_runtime_vtkh_filters.cpp index 321f01097..72f5b5d5b 100644 --- a/src/libs/ascent/runtimes/flow_filters/ascent_runtime_vtkh_filters.cpp +++ b/src/libs/ascent/runtimes/flow_filters/ascent_runtime_vtkh_filters.cpp @@ -5333,15 +5333,6 @@ VTKHMIR::execute() std::string topo_name = collection->field_topology(ids_name); vtkh::DataSet &data = collection->dataset_by_topology(topo_name); - if(!graph().workspace().registry().has_entry("is_discrete")) - { - - int *is_discrete = new int(); - *is_discrete = 1; - graph().workspace().registry().add("is_discrete", - is_discrete, - -1); // TODO keep forever? - } double error_scaling = 0.0; double scaling_decay = 0.0; double max_error = 0.00001;