From 7a7467276049cb7f3993ba54e181096dc872c4bb Mon Sep 17 00:00:00 2001 From: Nicole Marsaglia Date: Thu, 22 Jun 2023 16:35:26 -0400 Subject: [PATCH 01/35] things making sense. now for raja and avoiding graph building --- .../expressions/ascent_conduit_reductions.cpp | 145 ++++++++++++++++++ .../expressions/ascent_conduit_reductions.hpp | 4 + 2 files changed, 149 insertions(+) diff --git a/src/libs/ascent/runtimes/expressions/ascent_conduit_reductions.cpp b/src/libs/ascent/runtimes/expressions/ascent_conduit_reductions.cpp index c55cf77e2..aec5c24cf 100644 --- a/src/libs/ascent/runtimes/expressions/ascent_conduit_reductions.cpp +++ b/src/libs/ascent/runtimes/expressions/ascent_conduit_reductions.cpp @@ -153,6 +153,77 @@ conduit::Node dispatch_memory(const conduit::Node &field, return res; } +//dispatch memory for a derived field (DF) +template +conduit::Node dispatch_memory_DF(const conduit::Node &l_field, + const conduit::Node &r_field, + std::string component, + const Function &func, + const Exec &exec) +{ + const std::string mem_space = Exec::memory_space; + + conduit::Node res; + if(field_is_float32(l_field)) + { + if(!field_is_float32(r_field)) + ASCENT_ERROR("Type dispatch: mismatch array types\n"<< + l_field.schema().to_string() << + "\n vs. \n" << + r_field.schema().to_string()); + MemoryInterface l_farray(l_field); + MemoryInterface r_farray(r_field); + MemoryAccessor l_accessor = l_farray.accessor(mem_space,component); + MemoryAccessor r_accessor = r_farray.accessor(mem_space,component); + func(l_accessor, r_accessor, res, exec); + } + else if(field_is_float64(l_field)) + { + if(!field_is_float64(r_field)) + ASCENT_ERROR("Type dispatch: mismatch array types\n"<< + l_field.schema().to_string() << + "\n vs. \n" << + r_field.schema().to_string()); + MemoryInterface l_farray(l_field); + MemoryInterface r_farray(r_field); + MemoryAccessor l_accessor = l_farray.accessor(mem_space,component); + MemoryAccessor r_accessor = r_farray.accessor(mem_space,component); + func(l_accessor, r_accessor, res, exec); + } + else if(field_is_int32(l_field)) + { + if(!field_is_int32(r_field)) + ASCENT_ERROR("Type dispatch: mismatch array types\n"<< + l_field.schema().to_string() << + "\n vs. \n" << + r_field.schema().to_string()); + MemoryInterface l_farray(l_field); + MemoryInterface r_farray(r_field); + MemoryAccessor l_accessor = l_farray.accessor(mem_space,component); + MemoryAccessor r_accessor = r_farray.accessor(mem_space,component); + func(l_accessor, r_accessor, res, exec); + } + else if(field_is_int64(l_field)) + { + if(!field_is_int64(r_field)) + ASCENT_ERROR("Type dispatch: mismatch array types\n"<< + l_field.schema().to_string() << + "\n vs. \n" << + r_field.schema().to_string()); + MemoryInterface l_farray(l_field); + MemoryInterface r_farray(r_field); + MemoryAccessor l_accessor = l_farray.accessor(mem_space,component); + MemoryAccessor r_accessor = r_farray.accessor(mem_space,component); + func(l_accessor, r_accessor, res, exec); + } + else + { + ASCENT_ERROR("Type dispatch: unsupported array type "<< + l_field.schema().to_string()); + } + return res; +} + template conduit::Node exec_dispatch(const conduit::Node &field, std::string component, const Function &func) @@ -195,6 +266,48 @@ exec_dispatch(const conduit::Node &field, std::string component, const Function return res; } +template +conduit::Node +exec_dispatch_DF(const conduit::Node &l_field, const conduit::Node &r_field, std::string component, const Function &func) +{ + + conduit::Node res; + const std::string exec_policy = ExecutionManager::execution_policy(); + //std::cout<<"Exec policy "< conduit::Node field_dispatch(const conduit::Node &field, const Function &func) @@ -481,6 +594,31 @@ struct SumFunctor } }; +struct DFAddFunctor +{ + template + void operator()(const MemoryAccessor l_accessor, + const MemoryAccessor r_accessor, + MemoryAccessor output, + const Exec &) const + { + const int size = accessor.m_size; + using for_policy = typename Exec::for_policy; + using reduce_policy = typename Exec::reduce_policy; + + ascent::ReduceSum sum(static_cast(0)); + ascent::forall(0, size, [=] ASCENT_LAMBDA(index_t i) + { + const T val = l_accessor[i] + r_accessor[i]; + output[i] = val; + + }); + ASCENT_DEVICE_ERROR_CHECK(); + + return; + } +}; + struct NanFunctor { template @@ -742,6 +880,13 @@ array_sum(const conduit::Node &array, return res; } + +conduit::Node +derived_field_add(const conduit::Node &l_field, const conduit::Node &r_field, const std::string &component) +{ + return detail::exec_dispatch_DF(l_field, r_field, component, detail::DFAddFunctor()); +} + //----------------------------------------------------------------------------- }; //----------------------------------------------------------------------------- diff --git a/src/libs/ascent/runtimes/expressions/ascent_conduit_reductions.hpp b/src/libs/ascent/runtimes/expressions/ascent_conduit_reductions.hpp index 13883089b..79e6dda88 100644 --- a/src/libs/ascent/runtimes/expressions/ascent_conduit_reductions.hpp +++ b/src/libs/ascent/runtimes/expressions/ascent_conduit_reductions.hpp @@ -74,6 +74,10 @@ conduit::Node ASCENT_API array_min(const conduit::Node &array, conduit::Node ASCENT_API array_sum(const conduit::Node &array, const std::string &exec_loc, const std::string &component = ""); + +conduit::Node ASCENT_API derived_field_add(const conduit::Node &l_field, + const conduit::Node &r_field, + const std::string &component = ""); }; //----------------------------------------------------------------------------- // -- end ascent::runtime::expressions-- From 24e8e427c148580ca593a11e6ce1cd4e22e81fa7 Mon Sep 17 00:00:00 2001 From: Nicole Marsaglia Date: Thu, 22 Jun 2023 16:58:34 -0400 Subject: [PATCH 02/35] brains in circles, but things still fine --- .../ascent/runtimes/expressions/ascent_blueprint_architect.hpp | 3 +++ .../ascent/runtimes/expressions/ascent_conduit_reductions.cpp | 2 +- .../ascent/runtimes/expressions/ascent_conduit_reductions.hpp | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/libs/ascent/runtimes/expressions/ascent_blueprint_architect.hpp b/src/libs/ascent/runtimes/expressions/ascent_blueprint_architect.hpp index b36fa4960..9cf51da19 100644 --- a/src/libs/ascent/runtimes/expressions/ascent_blueprint_architect.hpp +++ b/src/libs/ascent/runtimes/expressions/ascent_blueprint_architect.hpp @@ -91,6 +91,9 @@ ASCENT_API conduit::Node global_bounds(const conduit::Node &dataset, const std::string &topo_name); +ASCENT_API +conduit::Node derived_field_add(const conduit::Node &l_field, + const conduit::Node &r_field); // // NOTE: ascent_data_binning contains a RAJA version // of binning that needs more work, but should eventually diff --git a/src/libs/ascent/runtimes/expressions/ascent_conduit_reductions.cpp b/src/libs/ascent/runtimes/expressions/ascent_conduit_reductions.cpp index aec5c24cf..de603028d 100644 --- a/src/libs/ascent/runtimes/expressions/ascent_conduit_reductions.cpp +++ b/src/libs/ascent/runtimes/expressions/ascent_conduit_reductions.cpp @@ -882,7 +882,7 @@ array_sum(const conduit::Node &array, } conduit::Node -derived_field_add(const conduit::Node &l_field, const conduit::Node &r_field, const std::string &component) +derived_field_add_reduction(const conduit::Node &l_field, const conduit::Node &r_field, const std::string &component) { return detail::exec_dispatch_DF(l_field, r_field, component, detail::DFAddFunctor()); } diff --git a/src/libs/ascent/runtimes/expressions/ascent_conduit_reductions.hpp b/src/libs/ascent/runtimes/expressions/ascent_conduit_reductions.hpp index 79e6dda88..46e646561 100644 --- a/src/libs/ascent/runtimes/expressions/ascent_conduit_reductions.hpp +++ b/src/libs/ascent/runtimes/expressions/ascent_conduit_reductions.hpp @@ -75,7 +75,7 @@ conduit::Node ASCENT_API array_sum(const conduit::Node &array, const std::string &exec_loc, const std::string &component = ""); -conduit::Node ASCENT_API derived_field_add(const conduit::Node &l_field, +conduit::Node ASCENT_API derived_field_add_reduction(const conduit::Node &l_field, const conduit::Node &r_field, const std::string &component = ""); }; From 51368da2c10681d8875b8ac0180dd0ffa9ab7053 Mon Sep 17 00:00:00 2001 From: Nicole Marsaglia Date: Thu, 22 Jun 2023 18:43:13 -0400 Subject: [PATCH 03/35] change output node in functor --- .../ascent_blueprint_architect.cpp | 46 +++++++++++++++++++ .../expressions/ascent_conduit_reductions.cpp | 44 ++++++++++++++++-- 2 files changed, 86 insertions(+), 4 deletions(-) diff --git a/src/libs/ascent/runtimes/expressions/ascent_blueprint_architect.cpp b/src/libs/ascent/runtimes/expressions/ascent_blueprint_architect.cpp index fa4f31a3c..570544cfa 100644 --- a/src/libs/ascent/runtimes/expressions/ascent_blueprint_architect.cpp +++ b/src/libs/ascent/runtimes/expressions/ascent_blueprint_architect.cpp @@ -992,6 +992,52 @@ field_histogram(const conduit::Node &dataset, return res; } +//returns a node that is field1 + field2 +conduit::Node +derived_field_add(const conduit::Node &dataset, + const std::string &field1, + const std::string &field2) +{ + + conduit::Node res; + for(int i = 0; i < dataset.number_of_children(); ++i) + { + const conduit::Node &dom = dataset.child(i); + if(dom.has_path("fields/" + field1) && dom.has_path("fields/" + field2)) + { + const std::string path1 = "fields/" + field1; + const std::string path2 = "fields/" + field2; + conduit::Node values; + values = derived_field_add_reduction(dom[path1], dom[path2]); + + } + else if(dom.has_path("fields/" + field1)) //TODO + { + } + else if(dom.has_path("fields/" + field2)) //TODO + { + } + else //has neither field + continue; //? + } + +#ifdef ASCENT_MPI_ENABLED + double *global_bins = new double[num_bins]; + + MPI_Comm mpi_comm = MPI_Comm_f2c(flow::Workspace::default_mpi_comm()); + MPI_Allreduce(bins, global_bins, num_bins, MPI_DOUBLE, MPI_SUM, mpi_comm); + + delete[] bins; + bins = global_bins; +#endif + res["value"].set(bins, num_bins); + res["min_val"] = min_val; + res["max_val"] = max_val; + res["num_bins"] = num_bins; + delete[] bins; + return res; +} + // returns a Node containing the min, max and dim for x,y,z given a topology conduit::Node global_bounds(const conduit::Node &dataset, const std::string &topo_name) diff --git a/src/libs/ascent/runtimes/expressions/ascent_conduit_reductions.cpp b/src/libs/ascent/runtimes/expressions/ascent_conduit_reductions.cpp index de603028d..89f1c0bcf 100644 --- a/src/libs/ascent/runtimes/expressions/ascent_conduit_reductions.cpp +++ b/src/libs/ascent/runtimes/expressions/ascent_conduit_reductions.cpp @@ -602,19 +602,55 @@ struct DFAddFunctor MemoryAccessor output, const Exec &) const { - const int size = accessor.m_size; + const int l_size = l_accessor.m_size; + const int r_size = r_accessor.m_size; + bool diff_sizes = false; + const int size; + const int max_size; + + size = max_size = l_size; + if(l_size != r_size) + { + size = min(l_size, r_size); + max_size = max(l_size, r_size); + diff_sizes = true; + } + + double values[max_size]; + using for_policy = typename Exec::for_policy; using reduce_policy = typename Exec::reduce_policy; - ascent::ReduceSum sum(static_cast(0)); ascent::forall(0, size, [=] ASCENT_LAMBDA(index_t i) { const T val = l_accessor[i] + r_accessor[i]; - output[i] = val; - + values[i] = val; }); ASCENT_DEVICE_ERROR_CHECK(); + if(diff_sizes) + { + if(l_size > r_size) + { + ascent::forall(size, l_size, [=] ASCENT_LAMBDA(index_t i) + { + const T val = l_accessor[i]; + values[i] = val; + }); + ASCENT_DEVICE_ERROR_CHECK(); + } + else + { + ascent::forall(size, r_size, [=] ASCENT_LAMBDA(index_t i) + { + const T val = r_accessor[i]; + values[i] = val; + }); + ASCENT_DEVICE_ERROR_CHECK(); + } + } + + output["values"].set(values); return; } }; From ce6052e5f237edf876d713a3ee6099e8cf52a2a7 Mon Sep 17 00:00:00 2001 From: Nicole Marsaglia Date: Thu, 22 Jun 2023 19:31:25 -0400 Subject: [PATCH 04/35] blueprint/conduit q's. preserve domain structure? --- .../ascent_blueprint_architect.cpp | 33 ++++++++----------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/src/libs/ascent/runtimes/expressions/ascent_blueprint_architect.cpp b/src/libs/ascent/runtimes/expressions/ascent_blueprint_architect.cpp index 570544cfa..f5b4de8c1 100644 --- a/src/libs/ascent/runtimes/expressions/ascent_blueprint_architect.cpp +++ b/src/libs/ascent/runtimes/expressions/ascent_blueprint_architect.cpp @@ -996,45 +996,38 @@ field_histogram(const conduit::Node &dataset, conduit::Node derived_field_add(const conduit::Node &dataset, const std::string &field1, - const std::string &field2) + const std::string &field2, + const std::string &out_field) { conduit::Node res; for(int i = 0; i < dataset.number_of_children(); ++i) { + const std::string path1 = "fields/" + field1; + const std::string path2 = "fields/" + field2; const conduit::Node &dom = dataset.child(i); - if(dom.has_path("fields/" + field1) && dom.has_path("fields/" + field2)) + if(dom.has_path(path1) && dom.has_path(path2)) //has both { - const std::string path1 = "fields/" + field1; - const std::string path2 = "fields/" + field2; conduit::Node values; values = derived_field_add_reduction(dom[path1], dom[path2]); + double *values = values["values"].value(); + res[out_field].set(values); //need to preserve domain structure? + //save all into mega_values + //then set at end? } - else if(dom.has_path("fields/" + field1)) //TODO + else if(dom.has_path(path1)) //only has path1 { + res[out_field].set(dom[path1].value()); } - else if(dom.has_path("fields/" + field2)) //TODO + else if(dom.has_path(path2)) //only has path2 { + res[out_field].set(dom[path2].value()); } else //has neither field continue; //? } -#ifdef ASCENT_MPI_ENABLED - double *global_bins = new double[num_bins]; - - MPI_Comm mpi_comm = MPI_Comm_f2c(flow::Workspace::default_mpi_comm()); - MPI_Allreduce(bins, global_bins, num_bins, MPI_DOUBLE, MPI_SUM, mpi_comm); - - delete[] bins; - bins = global_bins; -#endif - res["value"].set(bins, num_bins); - res["min_val"] = min_val; - res["max_val"] = max_val; - res["num_bins"] = num_bins; - delete[] bins; return res; } From 2b31b681010394e3a8c6708f7d8114291db009eb Mon Sep 17 00:00:00 2001 From: Nicole Marsaglia Date: Fri, 23 Jun 2023 18:10:59 -0400 Subject: [PATCH 05/35] lots of tweaks. making this a function, not an object, hope that's correct --- .../ascent_blueprint_architect.cpp | 10 ++- .../expressions/ascent_conduit_reductions.cpp | 62 +++++++++------- .../expressions/ascent_expression_filters.cpp | 74 +++++++++++++++++++ 3 files changed, 115 insertions(+), 31 deletions(-) diff --git a/src/libs/ascent/runtimes/expressions/ascent_blueprint_architect.cpp b/src/libs/ascent/runtimes/expressions/ascent_blueprint_architect.cpp index f5b4de8c1..0d7a9da65 100644 --- a/src/libs/ascent/runtimes/expressions/ascent_blueprint_architect.cpp +++ b/src/libs/ascent/runtimes/expressions/ascent_blueprint_architect.cpp @@ -1010,19 +1010,21 @@ derived_field_add(const conduit::Node &dataset, { conduit::Node values; values = derived_field_add_reduction(dom[path1], dom[path2]); - double *values = values["values"].value(); - res[out_field].set(values); //need to preserve domain structure? + const double *values_array = values["values"].value(); + res[out_field].set(values_array); //need to preserve domain structure? //save all into mega_values //then set at end? } else if(dom.has_path(path1)) //only has path1 { - res[out_field].set(dom[path1].value()); + const double *values_array = dom[path1].value(); + res[out_field].set(values_array); } else if(dom.has_path(path2)) //only has path2 { - res[out_field].set(dom[path2].value()); + const double *values_array = dom[path2].value(); + res[out_field].set(values_array); } else //has neither field continue; //? diff --git a/src/libs/ascent/runtimes/expressions/ascent_conduit_reductions.cpp b/src/libs/ascent/runtimes/expressions/ascent_conduit_reductions.cpp index 89f1c0bcf..9f629fcc2 100644 --- a/src/libs/ascent/runtimes/expressions/ascent_conduit_reductions.cpp +++ b/src/libs/ascent/runtimes/expressions/ascent_conduit_reductions.cpp @@ -175,7 +175,7 @@ conduit::Node dispatch_memory_DF(const conduit::Node &l_field, MemoryInterface r_farray(r_field); MemoryAccessor l_accessor = l_farray.accessor(mem_space,component); MemoryAccessor r_accessor = r_farray.accessor(mem_space,component); - func(l_accessor, r_accessor, res, exec); + res = func(l_accessor, r_accessor, exec); } else if(field_is_float64(l_field)) { @@ -184,11 +184,11 @@ conduit::Node dispatch_memory_DF(const conduit::Node &l_field, l_field.schema().to_string() << "\n vs. \n" << r_field.schema().to_string()); - MemoryInterface l_farray(l_field); - MemoryInterface r_farray(r_field); - MemoryAccessor l_accessor = l_farray.accessor(mem_space,component); - MemoryAccessor r_accessor = r_farray.accessor(mem_space,component); - func(l_accessor, r_accessor, res, exec); + MemoryInterface l_farray(l_field); + MemoryInterface r_farray(r_field); + MemoryAccessor l_accessor = l_farray.accessor(mem_space,component); + MemoryAccessor r_accessor = r_farray.accessor(mem_space,component); + res = func(l_accessor, r_accessor, exec); } else if(field_is_int32(l_field)) { @@ -197,11 +197,11 @@ conduit::Node dispatch_memory_DF(const conduit::Node &l_field, l_field.schema().to_string() << "\n vs. \n" << r_field.schema().to_string()); - MemoryInterface l_farray(l_field); - MemoryInterface r_farray(r_field); - MemoryAccessor l_accessor = l_farray.accessor(mem_space,component); - MemoryAccessor r_accessor = r_farray.accessor(mem_space,component); - func(l_accessor, r_accessor, res, exec); + MemoryInterface l_farray(l_field); + MemoryInterface r_farray(r_field); + MemoryAccessor l_accessor = l_farray.accessor(mem_space,component); + MemoryAccessor r_accessor = r_farray.accessor(mem_space,component); + res = func(l_accessor, r_accessor, exec); } else if(field_is_int64(l_field)) { @@ -210,11 +210,11 @@ conduit::Node dispatch_memory_DF(const conduit::Node &l_field, l_field.schema().to_string() << "\n vs. \n" << r_field.schema().to_string()); - MemoryInterface l_farray(l_field); - MemoryInterface r_farray(r_field); - MemoryAccessor l_accessor = l_farray.accessor(mem_space,component); - MemoryAccessor r_accessor = r_farray.accessor(mem_space,component); - func(l_accessor, r_accessor, res, exec); + MemoryInterface l_farray(l_field); + MemoryInterface r_farray(r_field); + MemoryAccessor l_accessor = l_farray.accessor(mem_space,component); + MemoryAccessor r_accessor = r_farray.accessor(mem_space,component); + res = func(l_accessor, r_accessor, exec); } else { @@ -597,16 +597,15 @@ struct SumFunctor struct DFAddFunctor { template - void operator()(const MemoryAccessor l_accessor, + conduit::Node operator()(const MemoryAccessor l_accessor, const MemoryAccessor r_accessor, - MemoryAccessor output, const Exec &) const { const int l_size = l_accessor.m_size; const int r_size = r_accessor.m_size; bool diff_sizes = false; - const int size; - const int max_size; + int size; + int max_size; size = max_size = l_size; if(l_size != r_size) @@ -616,15 +615,22 @@ struct DFAddFunctor diff_sizes = true; } - double values[max_size]; + + // conduit zero initializes this array + conduit::Node res; + res["values"].set(conduit::DataType::float64(max_size)); + double *res_array = res["values"].value(); + + Array field_sums(res_array, max_size); + + double *sums_ptr = field_sums.get_ptr(Exec::memory_space); using for_policy = typename Exec::for_policy; - using reduce_policy = typename Exec::reduce_policy; ascent::forall(0, size, [=] ASCENT_LAMBDA(index_t i) { const T val = l_accessor[i] + r_accessor[i]; - values[i] = val; + sums_ptr[i] = val; }); ASCENT_DEVICE_ERROR_CHECK(); @@ -635,7 +641,7 @@ struct DFAddFunctor ascent::forall(size, l_size, [=] ASCENT_LAMBDA(index_t i) { const T val = l_accessor[i]; - values[i] = val; + sums_ptr[i] = val; }); ASCENT_DEVICE_ERROR_CHECK(); } @@ -644,14 +650,16 @@ struct DFAddFunctor ascent::forall(size, r_size, [=] ASCENT_LAMBDA(index_t i) { const T val = r_accessor[i]; - values[i] = val; + sums_ptr[i] = val; }); ASCENT_DEVICE_ERROR_CHECK(); } } - output["values"].set(values); - return; + // synch the values back to the host + (void) field_sums.get_host_ptr(); + + return res; } }; diff --git a/src/libs/ascent/runtimes/expressions/ascent_expression_filters.cpp b/src/libs/ascent/runtimes/expressions/ascent_expression_filters.cpp index 2f49179cd..8a94edef9 100644 --- a/src/libs/ascent/runtimes/expressions/ascent_expression_filters.cpp +++ b/src/libs/ascent/runtimes/expressions/ascent_expression_filters.cpp @@ -3554,6 +3554,80 @@ BinByValue::execute() set_output(output); } +//----------------------------------------------------------------------------- +AddFields::AddFields() : Filter() +{ + // empty +} + +//----------------------------------------------------------------------------- +AddFields::~AddFields() +{ + // empty +} + +//----------------------------------------------------------------------------- +void +AddFields::declare_interface(Node &i) +{ + i["type_name"] = "histogram"; + i["port_names"].append() = "arg1"; + i["port_names"].append() = "num_bins"; + i["port_names"].append() = "min_val"; + i["port_names"].append() = "max_val"; + i["output_port"] = "true"; +} + +//----------------------------------------------------------------------------- +bool +AddFields::verify_params(const conduit::Node ¶ms, conduit::Node &info) +{ + info.reset(); + bool res = true; + return res; +} + +//----------------------------------------------------------------------------- +void +AddFields::execute() +{ + std::cerr << " IN AddFields experssions_filters" << std::endl; + const conduit::Node *arg1 = input("arg1"); + + const std::vector fields = (*arg1)["value"].value(); + std::cerr << "fields size: " << fields.size()<< std::endl; + + DataObject *data_object = + graph().workspace().registry().fetch("dataset"); + const conduit::Node *const dataset = data_object->as_low_order_bp().get(); + + if(!is_scalar_field(*dataset, field)) + { + ASCENT_ERROR("AddFields: axis for histogram must be a scalar field. " + "Invalid axis field: '" + << field << "'."); + } + + conduit::Node *output = new conduit::Node(); + (*output)["type"] = "histogram"; + (*output)["attrs/value/value"] = + field_histogram(*dataset, field, min_val, max_val, num_bins)["value"]; + (*output)["attrs/value/type"] = "array"; + (*output)["attrs/min_val/value"] = min_val; + (*output)["attrs/min_val/type"] = "double"; + (*output)["attrs/max_val/value"] = max_val; + (*output)["attrs/max_val/type"] = "double"; + (*output)["attrs/num_bins/value"] = num_bins; + (*output)["attrs/num_bins/type"] = "int"; + (*output)["attrs/clamp/value"] = true; + (*output)["attrs/clamp/type"] = "bool"; + + resolve_symbol_result(graph(), output, this->name()); + set_output(output); +} + +//----------------------------------------------------------------------------- + //----------------------------------------------------------------------------- FieldSum::FieldSum() : Filter() { From 681fd81e22ab2fe632a93ef601dd6d537678ec68 Mon Sep 17 00:00:00 2001 From: Nicole Marsaglia Date: Fri, 23 Jun 2023 18:48:01 -0400 Subject: [PATCH 06/35] things built --- .../runtimes/ascent_expression_eval.cpp | 13 +++++- .../ascent_blueprint_architect.hpp | 6 ++- .../expressions/ascent_expression_filters.cpp | 42 +++++++++---------- .../expressions/ascent_expression_filters.hpp | 11 +++++ 4 files changed, 46 insertions(+), 26 deletions(-) diff --git a/src/libs/ascent/runtimes/ascent_expression_eval.cpp b/src/libs/ascent/runtimes/ascent_expression_eval.cpp index c0d1aeec1..580793dfa 100644 --- a/src/libs/ascent/runtimes/ascent_expression_eval.cpp +++ b/src/libs/ascent/runtimes/ascent_expression_eval.cpp @@ -677,6 +677,15 @@ initialize_functions() // ------------------------------------------------------------- + conduit::Node &df_add = (*functions)["add_fields"].append(); + df_add["return_type"] = "field"; + df_add["filter_name"] = "add_fields"; + df_add["args/arg1/type"] = "array"; + df_add["description"] = + "Returns the derived field from adding two or more fields of matching type"; + + // ------------------------------------------------------------- + conduit::Node &hist_sig = (*functions)["histogram"].append(); hist_sig["return_type"] = "histogram"; hist_sig["filter_name"] = "histogram"; @@ -876,7 +885,7 @@ initialize_functions() field_sig["args/component/optional"]; field_sig["args/component/description"] = "Used to specify a single component if the field is a vector field."; - field_sig["description"] = "Return a mesh field given a its name."; + field_sig["description"] = "Return a mesh field given its name."; //--------------------------------------------------------------------------- @@ -884,7 +893,7 @@ initialize_functions() topo_sig["return_type"] = "topo"; topo_sig["filter_name"] = "topo"; topo_sig["args/arg1/type"] = "string"; - topo_sig["description"] = "Return a mesh topology given a its name."; + topo_sig["description"] = "Return a mesh topology given its name."; //--------------------------------------------------------------------------- diff --git a/src/libs/ascent/runtimes/expressions/ascent_blueprint_architect.hpp b/src/libs/ascent/runtimes/expressions/ascent_blueprint_architect.hpp index 9cf51da19..9ba3a3a08 100644 --- a/src/libs/ascent/runtimes/expressions/ascent_blueprint_architect.hpp +++ b/src/libs/ascent/runtimes/expressions/ascent_blueprint_architect.hpp @@ -92,8 +92,10 @@ conduit::Node global_bounds(const conduit::Node &dataset, const std::string &topo_name); ASCENT_API -conduit::Node derived_field_add(const conduit::Node &l_field, - const conduit::Node &r_field); +conduit::Node derived_field_add(const conduit::Node &dataset, + const std::string &l_field, + const std::string &r_field, + const std::string output_field); // // NOTE: ascent_data_binning contains a RAJA version // of binning that needs more work, but should eventually diff --git a/src/libs/ascent/runtimes/expressions/ascent_expression_filters.cpp b/src/libs/ascent/runtimes/expressions/ascent_expression_filters.cpp index 8a94edef9..aaa68650e 100644 --- a/src/libs/ascent/runtimes/expressions/ascent_expression_filters.cpp +++ b/src/libs/ascent/runtimes/expressions/ascent_expression_filters.cpp @@ -3570,11 +3570,11 @@ AddFields::~AddFields() void AddFields::declare_interface(Node &i) { - i["type_name"] = "histogram"; - i["port_names"].append() = "arg1"; - i["port_names"].append() = "num_bins"; - i["port_names"].append() = "min_val"; - i["port_names"].append() = "max_val"; + i["type_name"] = "add_fields"; + i["port_names"].append() = "field1"; + i["port_names"].append() = "field2"; + i["port_names"].append() = "num_fields"; + i["port_names"].append() = "output_name"; i["output_port"] = "true"; } @@ -3592,35 +3592,33 @@ void AddFields::execute() { std::cerr << " IN AddFields experssions_filters" << std::endl; - const conduit::Node *arg1 = input("arg1"); - const std::vector fields = (*arg1)["value"].value(); - std::cerr << "fields size: " << fields.size()<< std::endl; + conduit::Node &n_fields = *input("num_fields"); + conduit::Node &n_field1 = *input("field1"); + conduit::Node &n_field2 = *input("field2"); + conduit::Node &n_output = *input("output_name"); + int32 num_fields = n_fields["value"].to_int32();; + const std::string field1 = n_field1["value"].as_string(); + const std::string field2 = n_field2["value"].as_string(); + const std::string out_field = n_output["value"].as_string(); + std::cerr << "fields size; num_fields: " << num_fields<< std::endl; DataObject *data_object = graph().workspace().registry().fetch("dataset"); const conduit::Node *const dataset = data_object->as_low_order_bp().get(); - if(!is_scalar_field(*dataset, field)) + if(!is_scalar_field(*dataset, field1) || !is_scalar_field(*dataset, field2)) { - ASCENT_ERROR("AddFields: axis for histogram must be a scalar field. " - "Invalid axis field: '" - << field << "'."); + ASCENT_ERROR("AddFields: input fields for AddFields must be scalar fields. " + "Invalid fields: '" + << field1 << " or " << field2 << "."); } conduit::Node *output = new conduit::Node(); - (*output)["type"] = "histogram"; + (*output)["type"] = "field"; (*output)["attrs/value/value"] = - field_histogram(*dataset, field, min_val, max_val, num_bins)["value"]; + derived_field_add(*dataset, field1, field2, out_field)["value"]; (*output)["attrs/value/type"] = "array"; - (*output)["attrs/min_val/value"] = min_val; - (*output)["attrs/min_val/type"] = "double"; - (*output)["attrs/max_val/value"] = max_val; - (*output)["attrs/max_val/type"] = "double"; - (*output)["attrs/num_bins/value"] = num_bins; - (*output)["attrs/num_bins/type"] = "int"; - (*output)["attrs/clamp/value"] = true; - (*output)["attrs/clamp/type"] = "bool"; resolve_symbol_result(graph(), output, this->name()); set_output(output); diff --git a/src/libs/ascent/runtimes/expressions/ascent_expression_filters.hpp b/src/libs/ascent/runtimes/expressions/ascent_expression_filters.hpp index b568d1873..452312068 100644 --- a/src/libs/ascent/runtimes/expressions/ascent_expression_filters.hpp +++ b/src/libs/ascent/runtimes/expressions/ascent_expression_filters.hpp @@ -486,6 +486,17 @@ class IfExpr : public ::flow::Filter virtual void execute(); }; +class AddFields : public ::flow::Filter +{ +public: + AddFields(); + ~AddFields(); + + virtual void declare_interface(conduit::Node &i); + virtual bool verify_params(const conduit::Node ¶ms, conduit::Node &info); + virtual void execute(); +}; + class Field : public ::flow::Filter { public: From 21d9ba1c019a99477f94b939d91e2219e03f43f5 Mon Sep 17 00:00:00 2001 From: Nicole Marsaglia Date: Fri, 23 Jun 2023 19:52:45 -0400 Subject: [PATCH 07/35] figure out undefined symbol error --- src/libs/ascent/runtimes/ascent_expression_eval.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/libs/ascent/runtimes/ascent_expression_eval.cpp b/src/libs/ascent/runtimes/ascent_expression_eval.cpp index 580793dfa..a58099664 100644 --- a/src/libs/ascent/runtimes/ascent_expression_eval.cpp +++ b/src/libs/ascent/runtimes/ascent_expression_eval.cpp @@ -255,6 +255,7 @@ register_builtin() flow::Workspace::register_filter_type(); flow::Workspace::register_filter_type(); flow::Workspace::register_filter_type(); + flow::Workspace::register_filter_type(); flow::Workspace::register_filter_type(); flow::Workspace::register_filter_type(); flow::Workspace::register_filter_type(); @@ -680,7 +681,10 @@ initialize_functions() conduit::Node &df_add = (*functions)["add_fields"].append(); df_add["return_type"] = "field"; df_add["filter_name"] = "add_fields"; - df_add["args/arg1/type"] = "array"; + df_add["args/field1/type"] = "string"; + df_add["args/field2/type"] = "string"; + df_add["args/num_fields/type"] = "int"; + df_add["args/output_name/type"] = "string"; df_add["description"] = "Returns the derived field from adding two or more fields of matching type"; From 44f14f0390fb8cdf6d83c27bcc547456f050616a Mon Sep 17 00:00:00 2001 From: Nicole Marsaglia Date: Thu, 29 Jun 2023 12:43:12 -0400 Subject: [PATCH 08/35] push current version --- .../ascent_blueprint_architect.cpp | 27 +++++++++++++------ .../ascent_blueprint_architect.hpp | 2 +- .../expressions/ascent_expression_filters.cpp | 5 ++++ 3 files changed, 25 insertions(+), 9 deletions(-) diff --git a/src/libs/ascent/runtimes/expressions/ascent_blueprint_architect.cpp b/src/libs/ascent/runtimes/expressions/ascent_blueprint_architect.cpp index 0d7a9da65..5044a102b 100644 --- a/src/libs/ascent/runtimes/expressions/ascent_blueprint_architect.cpp +++ b/src/libs/ascent/runtimes/expressions/ascent_blueprint_architect.cpp @@ -999,32 +999,43 @@ derived_field_add(const conduit::Node &dataset, const std::string &field2, const std::string &out_field) { + std::cerr << "blueprint_architect derived_field_add " << std::endl; conduit::Node res; + res.update(dataset); for(int i = 0; i < dataset.number_of_children(); ++i) { const std::string path1 = "fields/" + field1; const std::string path2 = "fields/" + field2; + const std::string output_path = "fields/" + out_field; +// std::cerr << " path1: " << path1 << " path2: " << path2 << std::endl; const conduit::Node &dom = dataset.child(i); + conduit::Node tmp; + tmp.update(dom); if(dom.has_path(path1) && dom.has_path(path2)) //has both { conduit::Node values; values = derived_field_add_reduction(dom[path1], dom[path2]); - const double *values_array = values["values"].value(); - res[out_field].set(values_array); //need to preserve domain structure? - //save all into mega_values - //then set at end? +// std::cerr << "derive_field_add_reduction output values " << std::endl; +// values.print(); + double* val_array = values["values"].value(); + tmp[output_path].set(val_array); +// std::cerr << "tmp print: " << std::endl; +// tmp.print(); + res.update(tmp); //corrupted double linked list } else if(dom.has_path(path1)) //only has path1 { - const double *values_array = dom[path1].value(); - res[out_field].set(values_array); + std::cerr << "only path 1=========" << std::endl; + tmp[output_path].set(dom[path1]); + // res.update(tmp); } else if(dom.has_path(path2)) //only has path2 { - const double *values_array = dom[path2].value(); - res[out_field].set(values_array); + std::cerr << "only path 2=========" << std::endl; + tmp[output_path].set(dom[path2]); + // res.update(tmp); } else //has neither field continue; //? diff --git a/src/libs/ascent/runtimes/expressions/ascent_blueprint_architect.hpp b/src/libs/ascent/runtimes/expressions/ascent_blueprint_architect.hpp index 9ba3a3a08..84fb979b4 100644 --- a/src/libs/ascent/runtimes/expressions/ascent_blueprint_architect.hpp +++ b/src/libs/ascent/runtimes/expressions/ascent_blueprint_architect.hpp @@ -95,7 +95,7 @@ ASCENT_API conduit::Node derived_field_add(const conduit::Node &dataset, const std::string &l_field, const std::string &r_field, - const std::string output_field); + const std::string &output_field); // // NOTE: ascent_data_binning contains a RAJA version // of binning that needs more work, but should eventually diff --git a/src/libs/ascent/runtimes/expressions/ascent_expression_filters.cpp b/src/libs/ascent/runtimes/expressions/ascent_expression_filters.cpp index aaa68650e..59d822eb0 100644 --- a/src/libs/ascent/runtimes/expressions/ascent_expression_filters.cpp +++ b/src/libs/ascent/runtimes/expressions/ascent_expression_filters.cpp @@ -1205,6 +1205,8 @@ FieldMax::execute() (*output)["attrs/element/index"] = n_max["index"]; (*output)["attrs/element/assoc"] = n_max["assoc"]; + std::cerr << "FieldMax output: " << std::endl; + output->print(); set_output(output); } @@ -3602,6 +3604,7 @@ AddFields::execute() const std::string field2 = n_field2["value"].as_string(); const std::string out_field = n_output["value"].as_string(); std::cerr << "fields size; num_fields: " << num_fields<< std::endl; + std::cerr << "field1: " << field1 << " field2: " << field2 << " output_field: " << out_field << std::endl; DataObject *data_object = graph().workspace().registry().fetch("dataset"); @@ -3621,6 +3624,8 @@ AddFields::execute() (*output)["attrs/value/type"] = "array"; resolve_symbol_result(graph(), output, this->name()); + std::cerr << "ADDFields output.print()" << std::endl; + output->print(); set_output(output); } From f954f78079394c1e646857e22df91452fd57538c Mon Sep 17 00:00:00 2001 From: Nicole Marsaglia Date: Thu, 29 Jun 2023 15:05:07 -0400 Subject: [PATCH 09/35] change dataset in place --- .../ascent_blueprint_architect.cpp | 42 ++++++++----------- .../ascent_blueprint_architect.hpp | 2 +- .../expressions/ascent_expression_filters.cpp | 16 +++---- 3 files changed, 24 insertions(+), 36 deletions(-) diff --git a/src/libs/ascent/runtimes/expressions/ascent_blueprint_architect.cpp b/src/libs/ascent/runtimes/expressions/ascent_blueprint_architect.cpp index 5044a102b..92e74442a 100644 --- a/src/libs/ascent/runtimes/expressions/ascent_blueprint_architect.cpp +++ b/src/libs/ascent/runtimes/expressions/ascent_blueprint_architect.cpp @@ -992,56 +992,48 @@ field_histogram(const conduit::Node &dataset, return res; } -//returns a node that is field1 + field2 -conduit::Node -derived_field_add(const conduit::Node &dataset, +//Adds a new field that is field1 + field2 +//TODO:Take in an array of fields +//TODO: add new field that is field1 + .. + fieldn +void +derived_field_add(conduit::Node &dataset, const std::string &field1, const std::string &field2, const std::string &out_field) { std::cerr << "blueprint_architect derived_field_add " << std::endl; - conduit::Node res; - res.update(dataset); for(int i = 0; i < dataset.number_of_children(); ++i) { const std::string path1 = "fields/" + field1; const std::string path2 = "fields/" + field2; const std::string output_path = "fields/" + out_field; -// std::cerr << " path1: " << path1 << " path2: " << path2 << std::endl; - const conduit::Node &dom = dataset.child(i); - conduit::Node tmp; - tmp.update(dom); + std::cerr << " path1: " << path1 << " path2: " << path2 << std::endl; + conduit::Node &dom = dataset.child(i); if(dom.has_path(path1) && dom.has_path(path2)) //has both { - conduit::Node values; - values = derived_field_add_reduction(dom[path1], dom[path2]); -// std::cerr << "derive_field_add_reduction output values " << std::endl; -// values.print(); - double* val_array = values["values"].value(); - tmp[output_path].set(val_array); -// std::cerr << "tmp print: " << std::endl; -// tmp.print(); - res.update(tmp); //corrupted double linked list + dom[output_path]["association"] = dom[path1]["association"]; + dom[output_path]["topology"] = dom[path1]["topology"]; + dom[output_path]["values"] = derived_field_add_reduction(dom[path1], dom[path2])["values"]; } else if(dom.has_path(path1)) //only has path1 { - std::cerr << "only path 1=========" << std::endl; - tmp[output_path].set(dom[path1]); - // res.update(tmp); + dom[output_path]["association"] = dom[path1]["association"]; + dom[output_path]["topology"] = dom[path1]["topology"]; + dom[output_path]["values"].set(dom[path1]["values"]); } else if(dom.has_path(path2)) //only has path2 { - std::cerr << "only path 2=========" << std::endl; - tmp[output_path].set(dom[path2]); - // res.update(tmp); + dom[output_path]["association"] = dom[path2]["association"]; + dom[output_path]["topology"] = dom[path2]["topology"]; + dom[output_path]["values"].set(dom[path2]["values"]); } else //has neither field continue; //? } - return res; + return; } // returns a Node containing the min, max and dim for x,y,z given a topology diff --git a/src/libs/ascent/runtimes/expressions/ascent_blueprint_architect.hpp b/src/libs/ascent/runtimes/expressions/ascent_blueprint_architect.hpp index 84fb979b4..ac2d711c9 100644 --- a/src/libs/ascent/runtimes/expressions/ascent_blueprint_architect.hpp +++ b/src/libs/ascent/runtimes/expressions/ascent_blueprint_architect.hpp @@ -92,7 +92,7 @@ conduit::Node global_bounds(const conduit::Node &dataset, const std::string &topo_name); ASCENT_API -conduit::Node derived_field_add(const conduit::Node &dataset, +void derived_field_add(conduit::Node &dataset, const std::string &l_field, const std::string &r_field, const std::string &output_field); diff --git a/src/libs/ascent/runtimes/expressions/ascent_expression_filters.cpp b/src/libs/ascent/runtimes/expressions/ascent_expression_filters.cpp index 59d822eb0..a602c86b8 100644 --- a/src/libs/ascent/runtimes/expressions/ascent_expression_filters.cpp +++ b/src/libs/ascent/runtimes/expressions/ascent_expression_filters.cpp @@ -3608,7 +3608,7 @@ AddFields::execute() DataObject *data_object = graph().workspace().registry().fetch("dataset"); - const conduit::Node *const dataset = data_object->as_low_order_bp().get(); + conduit::Node *dataset = data_object->as_low_order_bp().get(); if(!is_scalar_field(*dataset, field1) || !is_scalar_field(*dataset, field2)) { @@ -3617,16 +3617,12 @@ AddFields::execute() << field1 << " or " << field2 << "."); } - conduit::Node *output = new conduit::Node(); - (*output)["type"] = "field"; - (*output)["attrs/value/value"] = - derived_field_add(*dataset, field1, field2, out_field)["value"]; - (*output)["attrs/value/type"] = "array"; + derived_field_add(*dataset, field1, field2, out_field); + std::cerr << "dataset after add" << std::endl; + dataset->print(); - resolve_symbol_result(graph(), output, this->name()); - std::cerr << "ADDFields output.print()" << std::endl; - output->print(); - set_output(output); +// resolve_symbol_result(graph(), output, this->name()); + //set_output(output); } //----------------------------------------------------------------------------- From 9a9dcde0348606378a60a648af224f5d0b86418b Mon Sep 17 00:00:00 2001 From: Nicole Marsaglia Date: Thu, 29 Jun 2023 20:01:13 -0400 Subject: [PATCH 10/35] move addfields from expressions to filters --- .../runtimes/ascent_expression_eval.cpp | 13 --- .../ascent_blueprint_architect.cpp | 68 ++++++----- .../ascent_blueprint_architect.hpp | 3 +- .../expressions/ascent_expression_filters.cpp | 70 ----------- .../expressions/ascent_expression_filters.hpp | 11 -- .../ascent_runtime_blueprint_filters.cpp | 109 ++++++++++++++++++ .../ascent_runtime_blueprint_filters.hpp | 13 +++ .../flow_filters/ascent_runtime_filters.cpp | 1 + 8 files changed, 162 insertions(+), 126 deletions(-) diff --git a/src/libs/ascent/runtimes/ascent_expression_eval.cpp b/src/libs/ascent/runtimes/ascent_expression_eval.cpp index a58099664..687da0ce7 100644 --- a/src/libs/ascent/runtimes/ascent_expression_eval.cpp +++ b/src/libs/ascent/runtimes/ascent_expression_eval.cpp @@ -255,7 +255,6 @@ register_builtin() flow::Workspace::register_filter_type(); flow::Workspace::register_filter_type(); flow::Workspace::register_filter_type(); - flow::Workspace::register_filter_type(); flow::Workspace::register_filter_type(); flow::Workspace::register_filter_type(); flow::Workspace::register_filter_type(); @@ -678,18 +677,6 @@ initialize_functions() // ------------------------------------------------------------- - conduit::Node &df_add = (*functions)["add_fields"].append(); - df_add["return_type"] = "field"; - df_add["filter_name"] = "add_fields"; - df_add["args/field1/type"] = "string"; - df_add["args/field2/type"] = "string"; - df_add["args/num_fields/type"] = "int"; - df_add["args/output_name/type"] = "string"; - df_add["description"] = - "Returns the derived field from adding two or more fields of matching type"; - - // ------------------------------------------------------------- - conduit::Node &hist_sig = (*functions)["histogram"].append(); hist_sig["return_type"] = "histogram"; hist_sig["filter_name"] = "histogram"; diff --git a/src/libs/ascent/runtimes/expressions/ascent_blueprint_architect.cpp b/src/libs/ascent/runtimes/expressions/ascent_blueprint_architect.cpp index 92e74442a..bd19c040f 100644 --- a/src/libs/ascent/runtimes/expressions/ascent_blueprint_architect.cpp +++ b/src/libs/ascent/runtimes/expressions/ascent_blueprint_architect.cpp @@ -992,45 +992,53 @@ field_histogram(const conduit::Node &dataset, return res; } -//Adds a new field that is field1 + field2 -//TODO:Take in an array of fields -//TODO: add new field that is field1 + .. + fieldn +//Take in an array of fields +//add new field that is field1 + .. + fieldn void derived_field_add(conduit::Node &dataset, - const std::string &field1, - const std::string &field2, + const std::vector &fields, const std::string &out_field) { - std::cerr << "blueprint_architect derived_field_add " << std::endl; - + const int num_fields = fields.size(); + const std::string output_path = "fields/" + out_field; for(int i = 0; i < dataset.number_of_children(); ++i) { - const std::string path1 = "fields/" + field1; - const std::string path2 = "fields/" + field2; - const std::string output_path = "fields/" + out_field; - std::cerr << " path1: " << path1 << " path2: " << path2 << std::endl; conduit::Node &dom = dataset.child(i); - if(dom.has_path(path1) && dom.has_path(path2)) //has both - { - dom[output_path]["association"] = dom[path1]["association"]; - dom[output_path]["topology"] = dom[path1]["topology"]; - dom[output_path]["values"] = derived_field_add_reduction(dom[path1], dom[path2])["values"]; - - } - else if(dom.has_path(path1)) //only has path1 + for(int field = 0; field < num_fields; field++) { - dom[output_path]["association"] = dom[path1]["association"]; - dom[output_path]["topology"] = dom[path1]["topology"]; - dom[output_path]["values"].set(dom[path1]["values"]); - } - else if(dom.has_path(path2)) //only has path2 - { - dom[output_path]["association"] = dom[path2]["association"]; - dom[output_path]["topology"] = dom[path2]["topology"]; - dom[output_path]["values"].set(dom[path2]["values"]); + const std::string path = "fields/" + fields[field]; + if(dom.has_path(path)) //has both + { + if(!dom.has_path(output_path)) + { + dom[output_path]["association"] = dom[path]["association"]; + dom[output_path]["topology"] = dom[path]["topology"]; + dom[output_path]["values"].set(dom[path]["values"].number_of_children()); + } + else + { + std::string out_assoc = dom[output_path]["association"].to_string(); + std::string out_topo = dom[output_path]["topology"].to_string(); + std::string f_assoc = dom[path]["association"].to_string(); + std::string f_topo = dom[path]["topology"].to_string(); + if(out_assoc != f_assoc) + { + ASCENT_ERROR("Field associations do not match:\n " << + "Field " << fields[field] << " has association " << f_assoc << "\n" << + "Field " << out_field << " has association " << out_assoc << "\n"); + } + if(out_topo != f_topo) + { + ASCENT_ERROR("Field topologies do not match:\n " << + "Field " << fields[field] << " has topology " << f_topo << "\n" << + "Field " << out_field << " has topology " << out_topo << "\n"); + } + } + dom[output_path]["values"] = derived_field_add_reduction(dom[output_path], dom[path])["values"]; + } + else //does not have field + continue; } - else //has neither field - continue; //? } return; diff --git a/src/libs/ascent/runtimes/expressions/ascent_blueprint_architect.hpp b/src/libs/ascent/runtimes/expressions/ascent_blueprint_architect.hpp index ac2d711c9..e544f013a 100644 --- a/src/libs/ascent/runtimes/expressions/ascent_blueprint_architect.hpp +++ b/src/libs/ascent/runtimes/expressions/ascent_blueprint_architect.hpp @@ -93,8 +93,7 @@ conduit::Node global_bounds(const conduit::Node &dataset, ASCENT_API void derived_field_add(conduit::Node &dataset, - const std::string &l_field, - const std::string &r_field, + const std::vector &fields, const std::string &output_field); // // NOTE: ascent_data_binning contains a RAJA version diff --git a/src/libs/ascent/runtimes/expressions/ascent_expression_filters.cpp b/src/libs/ascent/runtimes/expressions/ascent_expression_filters.cpp index a602c86b8..fbac5f97b 100644 --- a/src/libs/ascent/runtimes/expressions/ascent_expression_filters.cpp +++ b/src/libs/ascent/runtimes/expressions/ascent_expression_filters.cpp @@ -3556,76 +3556,6 @@ BinByValue::execute() set_output(output); } -//----------------------------------------------------------------------------- -AddFields::AddFields() : Filter() -{ - // empty -} - -//----------------------------------------------------------------------------- -AddFields::~AddFields() -{ - // empty -} - -//----------------------------------------------------------------------------- -void -AddFields::declare_interface(Node &i) -{ - i["type_name"] = "add_fields"; - i["port_names"].append() = "field1"; - i["port_names"].append() = "field2"; - i["port_names"].append() = "num_fields"; - i["port_names"].append() = "output_name"; - i["output_port"] = "true"; -} - -//----------------------------------------------------------------------------- -bool -AddFields::verify_params(const conduit::Node ¶ms, conduit::Node &info) -{ - info.reset(); - bool res = true; - return res; -} - -//----------------------------------------------------------------------------- -void -AddFields::execute() -{ - std::cerr << " IN AddFields experssions_filters" << std::endl; - - conduit::Node &n_fields = *input("num_fields"); - conduit::Node &n_field1 = *input("field1"); - conduit::Node &n_field2 = *input("field2"); - conduit::Node &n_output = *input("output_name"); - int32 num_fields = n_fields["value"].to_int32();; - const std::string field1 = n_field1["value"].as_string(); - const std::string field2 = n_field2["value"].as_string(); - const std::string out_field = n_output["value"].as_string(); - std::cerr << "fields size; num_fields: " << num_fields<< std::endl; - std::cerr << "field1: " << field1 << " field2: " << field2 << " output_field: " << out_field << std::endl; - - DataObject *data_object = - graph().workspace().registry().fetch("dataset"); - conduit::Node *dataset = data_object->as_low_order_bp().get(); - - if(!is_scalar_field(*dataset, field1) || !is_scalar_field(*dataset, field2)) - { - ASCENT_ERROR("AddFields: input fields for AddFields must be scalar fields. " - "Invalid fields: '" - << field1 << " or " << field2 << "."); - } - - derived_field_add(*dataset, field1, field2, out_field); - std::cerr << "dataset after add" << std::endl; - dataset->print(); - -// resolve_symbol_result(graph(), output, this->name()); - //set_output(output); -} - -//----------------------------------------------------------------------------- //----------------------------------------------------------------------------- FieldSum::FieldSum() : Filter() diff --git a/src/libs/ascent/runtimes/expressions/ascent_expression_filters.hpp b/src/libs/ascent/runtimes/expressions/ascent_expression_filters.hpp index 452312068..b568d1873 100644 --- a/src/libs/ascent/runtimes/expressions/ascent_expression_filters.hpp +++ b/src/libs/ascent/runtimes/expressions/ascent_expression_filters.hpp @@ -486,17 +486,6 @@ class IfExpr : public ::flow::Filter virtual void execute(); }; -class AddFields : public ::flow::Filter -{ -public: - AddFields(); - ~AddFields(); - - virtual void declare_interface(conduit::Node &i); - virtual bool verify_params(const conduit::Node ¶ms, conduit::Node &info); - virtual void execute(); -}; - class Field : public ::flow::Filter { public: diff --git a/src/libs/ascent/runtimes/flow_filters/ascent_runtime_blueprint_filters.cpp b/src/libs/ascent/runtimes/flow_filters/ascent_runtime_blueprint_filters.cpp index ce63fe325..fa29c6c57 100644 --- a/src/libs/ascent/runtimes/flow_filters/ascent_runtime_blueprint_filters.cpp +++ b/src/libs/ascent/runtimes/flow_filters/ascent_runtime_blueprint_filters.cpp @@ -656,6 +656,115 @@ DataBinning::execute() } } +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// AddFields (derived field) +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +AddFields::AddFields() : Filter() +{ + // empty +} + +//----------------------------------------------------------------------------- +AddFields::~AddFields() +{ + // empty +} +//----------------------------------------------------------------------------- +void +AddFields::declare_interface(Node &i) +{ + i["type_name"] = "add_fields"; + i["port_names"].append() = "in"; + i["output_port"] = "true"; +} + +//----------------------------------------------------------------------------- +bool +AddFields::verify_params(const conduit::Node ¶ms, + conduit::Node &info) +{ + info.reset(); + bool res = true; + + + if(!params.has_path("output_field")) + { + res = false; + info["errors"].append() = "Missing param 'output_field'"; + } + + if(!params.has_path("fields")) + { + res = false; + info["errors"].append() = "Missing 'fields'"; + } + else if(!params["fields"].dtype().is_list()) + { + res = false; + info["errors"].append() = "fields is not a list"; + } + + std::vector valid_paths; + std::vector ignore_paths; + valid_paths.push_back("fields"); + valid_paths.push_back("output_field"); + ignore_paths.push_back("fields"); + + + std::string surprises = surprise_check(valid_paths, ignore_paths, params); + + if(surprises != "") + { + res = false; + info["errors"].append() = surprises; + } + + return res; +} + +//----------------------------------------------------------------------------- +void +AddFields::execute() +{ + if(!input(0).check_type()) + { + ASCENT_ERROR("add fields input must be a DataObject"); + } + + Node v_info; + DataObject *d_input = input(0); + std::shared_ptr n_input = d_input->as_low_order_bp(); + + std::string out_field = params()["output_field"].as_string(); + std::vector fields; + const conduit::Node &flist = params()["fields"]; + const int num_fields = flist.number_of_children(); + if(num_fields == 0) + { + ASCENT_ERROR("'fields' list must be non-empty"); + } + for(int i = 0; i < num_fields; i++) + { + const conduit::Node &f = flist.child(i); + if(!f.dtype().is_string()) + { + ASCENT_ERROR("'fields' list values must be a string"); + } + fields.push_back(f.as_string()); + } + + DataObject *d_output = new DataObject(); + d_output->reset(n_input); + expressions::derived_field_add(*n_input.get(), fields, out_field); + std::cerr << "dataset after add" << std::endl; + (*n_input.get()).print(); + set_output(d_output); + +} + +//----------------------------------------------------------------------------- //----------------------------------------------------------------------------- }; diff --git a/src/libs/ascent/runtimes/flow_filters/ascent_runtime_blueprint_filters.hpp b/src/libs/ascent/runtimes/flow_filters/ascent_runtime_blueprint_filters.hpp index f713fe4d4..acc2237b3 100644 --- a/src/libs/ascent/runtimes/flow_filters/ascent_runtime_blueprint_filters.hpp +++ b/src/libs/ascent/runtimes/flow_filters/ascent_runtime_blueprint_filters.hpp @@ -98,6 +98,19 @@ class ASCENT_API DataBinning : public ::flow::Filter virtual void execute(); }; +//----------------------------------------------------------------------------- +class ASCENT_API AddFields : public ::flow::Filter +{ +public: + AddFields(); + ~AddFields(); + + virtual void declare_interface(conduit::Node &i); + virtual bool verify_params(const conduit::Node ¶ms, + conduit::Node &info); + virtual void execute(); +}; + }; //----------------------------------------------------------------------------- // -- end ascent::runtime::filters -- 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 52e63066f..a3fd53172 100644 --- a/src/libs/ascent/runtimes/flow_filters/ascent_runtime_filters.cpp +++ b/src/libs/ascent/runtimes/flow_filters/ascent_runtime_filters.cpp @@ -99,6 +99,7 @@ register_builtin() AscentRuntime::register_filter_type("transforms","expression"); AscentRuntime::register_filter_type("transforms","binning"); AscentRuntime::register_filter_type("transforms","partition"); + AscentRuntime::register_filter_type("transforms","add_fields"); #if defined(ASCENT_VTKM_ENABLED) AscentRuntime::register_filter_type(); From 89a6e9a991e88daef016b079e5196adf66a7960a Mon Sep 17 00:00:00 2001 From: Nicole Marsaglia Date: Fri, 30 Jun 2023 17:34:21 -0400 Subject: [PATCH 11/35] device values? --- .../ascent_blueprint_architect.cpp | 31 ++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/src/libs/ascent/runtimes/expressions/ascent_blueprint_architect.cpp b/src/libs/ascent/runtimes/expressions/ascent_blueprint_architect.cpp index bd19c040f..7928b1434 100644 --- a/src/libs/ascent/runtimes/expressions/ascent_blueprint_architect.cpp +++ b/src/libs/ascent/runtimes/expressions/ascent_blueprint_architect.cpp @@ -1011,12 +1011,37 @@ derived_field_add(conduit::Node &dataset, { if(!dom.has_path(output_path)) { + std::cerr << "DOMAIN does not have OUTPUT PATH" << std::endl; + std::cerr << "setting output with initial info: " << std::endl; dom[output_path]["association"] = dom[path]["association"]; dom[output_path]["topology"] = dom[path]["topology"]; - dom[output_path]["values"].set(dom[path]["values"].number_of_children()); + if(field_is_float32(dom[path])) + { + const int vals = dom[path]["values"].dtype().number_of_elements(); + std::cerr << "num vals: " << vals << std::endl; + std::vector zeroes(vals,0.0); + std::cerr << "zeroes size: " << zeroes.size() << std::endl; + dom[output_path]["values"].set(zeroes); + } + else + { + const int vals = dom[path]["values"].dtype().number_of_elements(); + std::cerr << "num vals: " << vals << std::endl; + std::vector zeroes(vals,0.0); + dom[output_path]["values"].set(zeroes); + std::cerr << "zeroes size: " << zeroes.size() << std::endl; + std::cerr << "output path initialized to zeroes: " << std::endl; + dom[output_path]["values"].print(); + } + std::string out_assoc = dom[output_path]["association"].to_string(); + std::string out_topo = dom[output_path]["topology"].to_string(); + std::cerr << "set output topo as: " << out_topo << std::endl; + std::cerr << "set output assoc as: " << out_assoc << std::endl; + std::cerr << "number of elements: " << dom[path]["values"].dtype().number_of_elements() << std::endl; } else { + std::cerr << "DOMAIN HAS OUTPUT PATH" << std::endl; std::string out_assoc = dom[output_path]["association"].to_string(); std::string out_topo = dom[output_path]["topology"].to_string(); std::string f_assoc = dom[path]["association"].to_string(); @@ -1034,7 +1059,11 @@ derived_field_add(conduit::Node &dataset, "Field " << out_field << " has topology " << out_topo << "\n"); } } + std::cerr << "dom before add_reduction" << std::endl; + dom.print(); dom[output_path]["values"] = derived_field_add_reduction(dom[output_path], dom[path])["values"]; + std::cerr << "dom after add_reduction" << std::endl; + dom.print(); } else //does not have field continue; From b3bf81c9c4ad90a339aed3d2dfc79e3ef867b1ae Mon Sep 17 00:00:00 2001 From: Nicole Marsaglia Date: Fri, 30 Jun 2023 19:47:01 -0400 Subject: [PATCH 12/35] close but wha ha happen to the middle field? --- .../runtimes/ascent_vtkh_data_adapter.cpp | 2 +- .../ascent_blueprint_architect.cpp | 41 ++++++++----------- .../expressions/ascent_conduit_reductions.cpp | 16 +++++++- 3 files changed, 31 insertions(+), 28 deletions(-) diff --git a/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp b/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp index d7ded7b8c..8f080d7a2 100644 --- a/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp +++ b/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp @@ -2003,8 +2003,8 @@ VTKHDataAdapter::VTKmTopologyToBlueprint(conduit::Node &output, } else { - ASCENT_ERROR("Mixed explicit types not implemented"); data_set.PrintSummary(std::cout); + ASCENT_ERROR("Mixed explicit types not implemented"); MixedType cells = dyn_cells.AsCellSet(); } diff --git a/src/libs/ascent/runtimes/expressions/ascent_blueprint_architect.cpp b/src/libs/ascent/runtimes/expressions/ascent_blueprint_architect.cpp index 7928b1434..b728c9620 100644 --- a/src/libs/ascent/runtimes/expressions/ascent_blueprint_architect.cpp +++ b/src/libs/ascent/runtimes/expressions/ascent_blueprint_architect.cpp @@ -1009,37 +1009,24 @@ derived_field_add(conduit::Node &dataset, const std::string path = "fields/" + fields[field]; if(dom.has_path(path)) //has both { - if(!dom.has_path(output_path)) + if(!dom.has_path(output_path)) //setup output path { - std::cerr << "DOMAIN does not have OUTPUT PATH" << std::endl; + std::cerr << "DOMAIN does not have OUTPUT PATH for field: " << field << " " << path << std::endl; std::cerr << "setting output with initial info: " << std::endl; dom[output_path]["association"] = dom[path]["association"]; dom[output_path]["topology"] = dom[path]["topology"]; - if(field_is_float32(dom[path])) + if(field_is_float32(dom[path]))//Todo:: Ints. longs? { const int vals = dom[path]["values"].dtype().number_of_elements(); - std::cerr << "num vals: " << vals << std::endl; - std::vector zeroes(vals,0.0); - std::cerr << "zeroes size: " << zeroes.size() << std::endl; - dom[output_path]["values"].set(zeroes); + dom[output_path]["values"].set(conduit::DataType::float32(vals)); } else { const int vals = dom[path]["values"].dtype().number_of_elements(); - std::cerr << "num vals: " << vals << std::endl; - std::vector zeroes(vals,0.0); - dom[output_path]["values"].set(zeroes); - std::cerr << "zeroes size: " << zeroes.size() << std::endl; - std::cerr << "output path initialized to zeroes: " << std::endl; - dom[output_path]["values"].print(); + dom[output_path]["values"].set(conduit::DataType::float64(vals)); } - std::string out_assoc = dom[output_path]["association"].to_string(); - std::string out_topo = dom[output_path]["topology"].to_string(); - std::cerr << "set output topo as: " << out_topo << std::endl; - std::cerr << "set output assoc as: " << out_assoc << std::endl; - std::cerr << "number of elements: " << dom[path]["values"].dtype().number_of_elements() << std::endl; } - else + else //has output path already { std::cerr << "DOMAIN HAS OUTPUT PATH" << std::endl; std::string out_assoc = dom[output_path]["association"].to_string(); @@ -1059,16 +1046,20 @@ derived_field_add(conduit::Node &dataset, "Field " << out_field << " has topology " << out_topo << "\n"); } } - std::cerr << "dom before add_reduction" << std::endl; - dom.print(); - dom[output_path]["values"] = derived_field_add_reduction(dom[output_path], dom[path])["values"]; + std::cerr << "dom before add_reduction; adding " << path << std::endl; + conduit::Node tmp; + tmp[output_path] = dom[output_path]; + tmp[output_path]["values"] = derived_field_add_reduction(dom[output_path], dom[path])["values"]; + dom[output_path]["values"] = tmp[output_path]["values"]; std::cerr << "dom after add_reduction" << std::endl; - dom.print(); } else //does not have field + { + std::cerr << "DOES NOT HAVE FIELD: " << path << std::endl; continue; - } - } + } + }//fields + }//domains return; } diff --git a/src/libs/ascent/runtimes/expressions/ascent_conduit_reductions.cpp b/src/libs/ascent/runtimes/expressions/ascent_conduit_reductions.cpp index 9f629fcc2..d76cf147b 100644 --- a/src/libs/ascent/runtimes/expressions/ascent_conduit_reductions.cpp +++ b/src/libs/ascent/runtimes/expressions/ascent_conduit_reductions.cpp @@ -603,6 +603,8 @@ struct DFAddFunctor { const int l_size = l_accessor.m_size; const int r_size = r_accessor.m_size; + std::cerr << "left size: " << l_size << std::endl; + std::cerr << "right size: " << r_size << std::endl; bool diff_sizes = false; int size; int max_size; @@ -614,6 +616,7 @@ struct DFAddFunctor max_size = max(l_size, r_size); diff_sizes = true; } + std::cerr << "diff sizes: " << diff_sizes<< std::endl; // conduit zero initializes this array @@ -626,11 +629,20 @@ struct DFAddFunctor double *sums_ptr = field_sums.get_ptr(Exec::memory_space); using for_policy = typename Exec::for_policy; + using atomic_policy = typename Exec::atomic_policy; + + // init device array + ascent::forall(0, max_size, [=] ASCENT_LAMBDA(index_t i) + { + sums_ptr[i]=0.0; + }); + ASCENT_DEVICE_ERROR_CHECK(); ascent::forall(0, size, [=] ASCENT_LAMBDA(index_t i) { - const T val = l_accessor[i] + r_accessor[i]; - sums_ptr[i] = val; + const double val = l_accessor[i] + r_accessor[i]; + //sums_ptr[i] = val; + int old = ascent::atomic_add(&(sums_ptr[i]), val); }); ASCENT_DEVICE_ERROR_CHECK(); From 7d85bf5a623da25e9c71cd3f6d43b7634da828f1 Mon Sep 17 00:00:00 2001 From: Cyrus Harrison Date: Mon, 3 Jul 2023 11:52:18 -0700 Subject: [PATCH 13/35] some refactoring and added a simple test --- .../ascent_blueprint_architect.cpp | 136 ++++++------ .../ascent_blueprint_architect.hpp | 6 +- .../expressions/ascent_conduit_reductions.cpp | 198 ++++++++++-------- .../expressions/ascent_conduit_reductions.hpp | 41 ++-- .../ascent_runtime_blueprint_filters.cpp | 2 +- .../tout_df_add_fields100.png | Bin 0 -> 534225 bytes src/tests/ascent/t_ascent_derived.cpp | 68 ++++++ 7 files changed, 283 insertions(+), 168 deletions(-) create mode 100644 src/tests/_baseline_images/tout_df_add_fields100.png diff --git a/src/libs/ascent/runtimes/expressions/ascent_blueprint_architect.cpp b/src/libs/ascent/runtimes/expressions/ascent_blueprint_architect.cpp index b728c9620..5aed79fd6 100644 --- a/src/libs/ascent/runtimes/expressions/ascent_blueprint_architect.cpp +++ b/src/libs/ascent/runtimes/expressions/ascent_blueprint_architect.cpp @@ -995,71 +995,80 @@ field_histogram(const conduit::Node &dataset, //Take in an array of fields //add new field that is field1 + .. + fieldn void -derived_field_add(conduit::Node &dataset, - const std::vector &fields, - const std::string &out_field) +derived_field_add_fields(conduit::Node &dataset, + const std::vector &field_names, + const std::string &out_field_name) { - const int num_fields = fields.size(); - const std::string output_path = "fields/" + out_field; - for(int i = 0; i < dataset.number_of_children(); ++i) - { - conduit::Node &dom = dataset.child(i); - for(int field = 0; field < num_fields; field++) + const int num_fields = field_names.size(); + const std::string output_path = "fields/" + out_field_name; + for(int i = 0; i < dataset.number_of_children(); ++i) { - const std::string path = "fields/" + fields[field]; - if(dom.has_path(path)) //has both - { - if(!dom.has_path(output_path)) //setup output path - { - std::cerr << "DOMAIN does not have OUTPUT PATH for field: " << field << " " << path << std::endl; - std::cerr << "setting output with initial info: " << std::endl; - dom[output_path]["association"] = dom[path]["association"]; - dom[output_path]["topology"] = dom[path]["topology"]; - if(field_is_float32(dom[path]))//Todo:: Ints. longs? - { - const int vals = dom[path]["values"].dtype().number_of_elements(); - dom[output_path]["values"].set(conduit::DataType::float32(vals)); - } - else - { - const int vals = dom[path]["values"].dtype().number_of_elements(); - dom[output_path]["values"].set(conduit::DataType::float64(vals)); - } - } - else //has output path already - { - std::cerr << "DOMAIN HAS OUTPUT PATH" << std::endl; - std::string out_assoc = dom[output_path]["association"].to_string(); - std::string out_topo = dom[output_path]["topology"].to_string(); - std::string f_assoc = dom[path]["association"].to_string(); - std::string f_topo = dom[path]["topology"].to_string(); - if(out_assoc != f_assoc) - { - ASCENT_ERROR("Field associations do not match:\n " << - "Field " << fields[field] << " has association " << f_assoc << "\n" << - "Field " << out_field << " has association " << out_assoc << "\n"); - } - if(out_topo != f_topo) - { - ASCENT_ERROR("Field topologies do not match:\n " << - "Field " << fields[field] << " has topology " << f_topo << "\n" << - "Field " << out_field << " has topology " << out_topo << "\n"); - } - } - std::cerr << "dom before add_reduction; adding " << path << std::endl; - conduit::Node tmp; - tmp[output_path] = dom[output_path]; - tmp[output_path]["values"] = derived_field_add_reduction(dom[output_path], dom[path])["values"]; - dom[output_path]["values"] = tmp[output_path]["values"]; - std::cerr << "dom after add_reduction" << std::endl; - } - else //does not have field - { - std::cerr << "DOES NOT HAVE FIELD: " << path << std::endl; - continue; - } - }//fields - }//domains + conduit::Node &dom = dataset.child(i); + for(int field_idx = 0; field_idx < num_fields; field_idx++) + { + const std::string path = "fields/" + field_names[field_idx]; + if(dom.has_path(path)) //has both + { + if(!dom.has_path(output_path)) //setup output path + { + std::cerr << "DOMAIN does not have OUTPUT PATH for field: " << field_idx << " " << path << std::endl; + std::cerr << "setting output with initial info: " << std::endl; + + dom[output_path]["association"] = dom[path]["association"]; + dom[output_path]["topology"] = dom[path]["topology"]; + if(field_is_float32(dom[path]))//Todo:: Ints. longs? + { + const int vals = dom[path]["values"].dtype().number_of_elements(); + dom[output_path]["values"].set(conduit::DataType::float32(vals)); + } + else + { + const int vals = dom[path]["values"].dtype().number_of_elements(); + dom[output_path]["values"].set(conduit::DataType::float64(vals)); + } + } + else //has output path already + { + std::cerr << "DOMAIN HAS OUTPUT PATH" << std::endl; + // check that the field assoc and topo + std::string out_assoc = dom[output_path]["association"].to_string(); + std::string out_topo = dom[output_path]["topology"].to_string(); + std::string f_assoc = dom[path]["association"].to_string(); + std::string f_topo = dom[path]["topology"].to_string(); + if(out_assoc != f_assoc) + { + ASCENT_ERROR("Field associations do not match:\n " << + "Field " << field_names[field_idx] + << " has association " << f_assoc << "\n" << + "Field " << out_field_name + << " has association " << out_assoc << "\n"); + } + if(out_topo != f_topo) + { + ASCENT_ERROR("Field topologies do not match:\n " << + "Field " << field_names[field_idx] + << " has topology " << f_topo << "\n" << + "Field " << out_field_name << " has topology " << out_topo << "\n"); + } + } + + std::cerr << "dom before add_reduction; adding " << path << std::endl; + // execute + // add out result to next field + conduit::Node n_add_res = derived_field_binary_add(dom[output_path], dom[path]); + // replace out with new result + dom[output_path]["values"] = n_add_res["values"]; + std::cerr << "dom after add_reduction" << std::endl; + dom.print(); + } + else //does not have field + { + // some domains may not have this field, simply skip + std::cerr << "DOES NOT HAVE FIELD: " << path << std::endl; + continue; + } + }//fields + }//domains return; } @@ -1775,6 +1784,7 @@ binning(const conduit::Node &dataset, bins = global_bins; #endif + conduit::Node res; res["value"].set(conduit::DataType::c_double(num_bins)); double *res_bins = res["value"].value(); diff --git a/src/libs/ascent/runtimes/expressions/ascent_blueprint_architect.hpp b/src/libs/ascent/runtimes/expressions/ascent_blueprint_architect.hpp index e544f013a..238578e95 100644 --- a/src/libs/ascent/runtimes/expressions/ascent_blueprint_architect.hpp +++ b/src/libs/ascent/runtimes/expressions/ascent_blueprint_architect.hpp @@ -92,9 +92,9 @@ conduit::Node global_bounds(const conduit::Node &dataset, const std::string &topo_name); ASCENT_API -void derived_field_add(conduit::Node &dataset, - const std::vector &fields, - const std::string &output_field); +void derived_field_add_fields(conduit::Node &dataset, + const std::vector &field_names, + const std::string &output_field_name); // // NOTE: ascent_data_binning contains a RAJA version // of binning that needs more work, but should eventually diff --git a/src/libs/ascent/runtimes/expressions/ascent_conduit_reductions.cpp b/src/libs/ascent/runtimes/expressions/ascent_conduit_reductions.cpp index d76cf147b..094b90907 100644 --- a/src/libs/ascent/runtimes/expressions/ascent_conduit_reductions.cpp +++ b/src/libs/ascent/runtimes/expressions/ascent_conduit_reductions.cpp @@ -153,75 +153,92 @@ conduit::Node dispatch_memory(const conduit::Node &field, return res; } -//dispatch memory for a derived field (DF) +//----------------------------------------------------------------------------- +//dispatch memory for a derived field (DF) binary operation template -conduit::Node dispatch_memory_DF(const conduit::Node &l_field, - const conduit::Node &r_field, - std::string component, - const Function &func, - const Exec &exec) +conduit::Node +dispatch_memory_binary_df(const conduit::Node &l_field, + const conduit::Node &r_field, + std::string component, + const Function &func, + const Exec &exec) { - const std::string mem_space = Exec::memory_space; + const std::string mem_space = Exec::memory_space; - conduit::Node res; - if(field_is_float32(l_field)) - { - if(!field_is_float32(r_field)) - ASCENT_ERROR("Type dispatch: mismatch array types\n"<< - l_field.schema().to_string() << - "\n vs. \n" << - r_field.schema().to_string()); - MemoryInterface l_farray(l_field); - MemoryInterface r_farray(r_field); - MemoryAccessor l_accessor = l_farray.accessor(mem_space,component); - MemoryAccessor r_accessor = r_farray.accessor(mem_space,component); - res = func(l_accessor, r_accessor, exec); - } - else if(field_is_float64(l_field)) - { - if(!field_is_float64(r_field)) - ASCENT_ERROR("Type dispatch: mismatch array types\n"<< - l_field.schema().to_string() << - "\n vs. \n" << - r_field.schema().to_string()); - MemoryInterface l_farray(l_field); - MemoryInterface r_farray(r_field); - MemoryAccessor l_accessor = l_farray.accessor(mem_space,component); - MemoryAccessor r_accessor = r_farray.accessor(mem_space,component); - res = func(l_accessor, r_accessor, exec); - } - else if(field_is_int32(l_field)) - { - if(!field_is_int32(r_field)) - ASCENT_ERROR("Type dispatch: mismatch array types\n"<< - l_field.schema().to_string() << - "\n vs. \n" << - r_field.schema().to_string()); - MemoryInterface l_farray(l_field); - MemoryInterface r_farray(r_field); - MemoryAccessor l_accessor = l_farray.accessor(mem_space,component); - MemoryAccessor r_accessor = r_farray.accessor(mem_space,component); - res = func(l_accessor, r_accessor, exec); - } - else if(field_is_int64(l_field)) - { - if(!field_is_int64(r_field)) - ASCENT_ERROR("Type dispatch: mismatch array types\n"<< - l_field.schema().to_string() << - "\n vs. \n" << - r_field.schema().to_string()); - MemoryInterface l_farray(l_field); - MemoryInterface r_farray(r_field); - MemoryAccessor l_accessor = l_farray.accessor(mem_space,component); - MemoryAccessor r_accessor = r_farray.accessor(mem_space,component); - res = func(l_accessor, r_accessor, exec); - } - else - { - ASCENT_ERROR("Type dispatch: unsupported array type "<< - l_field.schema().to_string()); - } - return res; + conduit::Node res; + if(field_is_float32(l_field)) + { + if(!field_is_float32(r_field)) + { + ASCENT_ERROR("Type dispatch: mismatch array types\n"<< + l_field.schema().to_string() << + "\n vs. \n" << + r_field.schema().to_string()); + } + + MemoryInterface l_farray(l_field); + MemoryInterface r_farray(r_field); + MemoryAccessor l_accessor = l_farray.accessor(mem_space, component); + MemoryAccessor r_accessor = r_farray.accessor(mem_space, component); + res = func(l_accessor, r_accessor, exec); + + } + else if(field_is_float64(l_field)) + { + if(!field_is_float64(r_field)) + { + ASCENT_ERROR("Type dispatch: mismatch array types\n"<< + l_field.schema().to_string() << + "\n vs. \n" << + r_field.schema().to_string()); + } + + MemoryInterface l_farray(l_field); + MemoryInterface r_farray(r_field); + MemoryAccessor l_accessor = l_farray.accessor(mem_space, component); + MemoryAccessor r_accessor = r_farray.accessor(mem_space, component); + res = func(l_accessor, r_accessor, exec); + } + else if(field_is_int32(l_field)) + { + if(!field_is_int32(r_field)) + { + ASCENT_ERROR("Type dispatch: mismatch array types\n"<< + l_field.schema().to_string() << + "\n vs. \n" << + r_field.schema().to_string()); + } + + MemoryInterface l_farray(l_field); + MemoryInterface r_farray(r_field); + MemoryAccessor l_accessor = l_farray.accessor(mem_space, component); + MemoryAccessor r_accessor = r_farray.accessor(mem_space, component); + res = func(l_accessor, r_accessor, exec); + } + else if(field_is_int64(l_field)) + { + + if(!field_is_int64(r_field)) + { + ASCENT_ERROR("Type dispatch: mismatch array types\n"<< + l_field.schema().to_string() << + "\n vs. \n" << + r_field.schema().to_string()); + } + + MemoryInterface l_farray(l_field); + MemoryInterface r_farray(r_field); + MemoryAccessor l_accessor = l_farray.accessor(mem_space, component); + MemoryAccessor r_accessor = r_farray.accessor(mem_space, component); + res = func(l_accessor, r_accessor, exec); + } + else + { + ASCENT_ERROR("Type dispatch: unsupported array type "<< + l_field.schema().to_string()); + } + + return res; } template @@ -268,7 +285,10 @@ exec_dispatch(const conduit::Node &field, std::string component, const Function template conduit::Node -exec_dispatch_DF(const conduit::Node &l_field, const conduit::Node &r_field, std::string component, const Function &func) +exec_dispatch_binary_df(const conduit::Node &l_field, + const conduit::Node &r_field, + std::string component, + const Function &func) { conduit::Node res; @@ -277,27 +297,27 @@ exec_dispatch_DF(const conduit::Node &l_field, const conduit::Node &r_field, std if(exec_policy == "serial") { SerialExec exec; - res = dispatch_memory_DF(l_field, r_field, component, func, exec); + res = dispatch_memory_binary_df(l_field, r_field, component, func, exec); } #if defined(ASCENT_OPENMP_ENABLED) && defined(ASCENT_RAJA_ENABLED) else if(exec_policy == "openmp") { OpenMPExec exec; - res = dispatch_memory_DF(l_field, r_field, component, func, exec); + res = dispatch_memory_binary_df(l_field, r_field, component, func, exec); } #endif #if defined(ASCENT_CUDA_ENABLED) else if(exec_policy == "cuda") { CudaExec exec; - res = dispatch_memory_DF(l_field, r_field, component, func, exec); + res = dispatch_memory_binary_df(l_field, r_field, component, func, exec); } #endif #if defined(ASCENT_HIP_ENABLED) else if(exec_policy == "hip") { HipExec exec; - res = dispatch_memory_DF(l_field, r_field, component, func, exec); + res = dispatch_memory_binary_df(l_field, r_field, component, func, exec); } #endif else @@ -596,15 +616,17 @@ struct SumFunctor struct DFAddFunctor { - template - conduit::Node operator()(const MemoryAccessor l_accessor, - const MemoryAccessor r_accessor, - const Exec &) const - { + template + conduit::Node operator()(const MemoryAccessor l_accessor, + const MemoryAccessor r_accessor, + const Exec &) const + { + const int l_size = l_accessor.m_size; const int r_size = r_accessor.m_size; std::cerr << "left size: " << l_size << std::endl; std::cerr << "right size: " << r_size << std::endl; + bool diff_sizes = false; int size; int max_size; @@ -612,9 +634,9 @@ struct DFAddFunctor size = max_size = l_size; if(l_size != r_size) { - size = min(l_size, r_size); - max_size = max(l_size, r_size); - diff_sizes = true; + size = min(l_size, r_size); + max_size = max(l_size, r_size); + diff_sizes = true; } std::cerr << "diff sizes: " << diff_sizes<< std::endl; @@ -625,16 +647,15 @@ struct DFAddFunctor double *res_array = res["values"].value(); Array field_sums(res_array, max_size); - double *sums_ptr = field_sums.get_ptr(Exec::memory_space); using for_policy = typename Exec::for_policy; using atomic_policy = typename Exec::atomic_policy; - + // init device array ascent::forall(0, max_size, [=] ASCENT_LAMBDA(index_t i) { - sums_ptr[i]=0.0; + sums_ptr[i]=0.0; }); ASCENT_DEVICE_ERROR_CHECK(); @@ -672,7 +693,7 @@ struct DFAddFunctor (void) field_sums.get_host_ptr(); return res; - } + } }; struct NanFunctor @@ -938,9 +959,14 @@ array_sum(const conduit::Node &array, } conduit::Node -derived_field_add_reduction(const conduit::Node &l_field, const conduit::Node &r_field, const std::string &component) -{ - return detail::exec_dispatch_DF(l_field, r_field, component, detail::DFAddFunctor()); +derived_field_binary_add(const conduit::Node &l_field, + const conduit::Node &r_field, + const std::string &component) +{ + return detail::exec_dispatch_binary_df(l_field, + r_field, + component, + detail::DFAddFunctor()); } //----------------------------------------------------------------------------- diff --git a/src/libs/ascent/runtimes/expressions/ascent_conduit_reductions.hpp b/src/libs/ascent/runtimes/expressions/ascent_conduit_reductions.hpp index 46e646561..492369444 100644 --- a/src/libs/ascent/runtimes/expressions/ascent_conduit_reductions.hpp +++ b/src/libs/ascent/runtimes/expressions/ascent_conduit_reductions.hpp @@ -36,11 +36,31 @@ namespace runtime namespace expressions { +// ---------------------- +// array reductions +// ---------------------- +conduit::Node ASCENT_API array_max(const conduit::Node &array, + const std::string &exec_loc, + const std::string &component = ""); + +conduit::Node ASCENT_API array_min(const conduit::Node &array, + const std::string &exec_loc, + const std::string &component = ""); +conduit::Node ASCENT_API array_sum(const conduit::Node &array, + const std::string &exec_loc, + const std::string &component = ""); + +// ---------------------- +// derived arrays +// ---------------------- conduit::Node ASCENT_API array_gradient(const conduit::Node &y_values, const conduit::Node &dx_values, bool is_list = false); +//---------------------- +// field reductions +//---------------------- conduit::Node ASCENT_API field_reduction_max(const conduit::Node &field, const std::string &component = ""); @@ -63,21 +83,12 @@ conduit::Node ASCENT_API field_reduction_histogram(const conduit::Node &field, const int &num_bins, const std::string &component = ""); -conduit::Node ASCENT_API array_max(const conduit::Node &array, - const std::string &exec_loc, - const std::string &component = ""); - -conduit::Node ASCENT_API array_min(const conduit::Node &array, - const std::string &exec_loc, - const std::string &component = ""); - -conduit::Node ASCENT_API array_sum(const conduit::Node &array, - const std::string &exec_loc, - const std::string &component = ""); - -conduit::Node ASCENT_API derived_field_add_reduction(const conduit::Node &l_field, - const conduit::Node &r_field, - const std::string &component = ""); +//---------------------- +// derived fields +//---------------------- +conduit::Node ASCENT_API derived_field_binary_add(const conduit::Node &l_field, + const conduit::Node &r_field, + const std::string &component = ""); }; //----------------------------------------------------------------------------- // -- end ascent::runtime::expressions-- diff --git a/src/libs/ascent/runtimes/flow_filters/ascent_runtime_blueprint_filters.cpp b/src/libs/ascent/runtimes/flow_filters/ascent_runtime_blueprint_filters.cpp index fa29c6c57..19fa8cc08 100644 --- a/src/libs/ascent/runtimes/flow_filters/ascent_runtime_blueprint_filters.cpp +++ b/src/libs/ascent/runtimes/flow_filters/ascent_runtime_blueprint_filters.cpp @@ -757,7 +757,7 @@ AddFields::execute() DataObject *d_output = new DataObject(); d_output->reset(n_input); - expressions::derived_field_add(*n_input.get(), fields, out_field); + expressions::derived_field_add_fields(*n_input.get(), fields, out_field); std::cerr << "dataset after add" << std::endl; (*n_input.get()).print(); set_output(d_output); diff --git a/src/tests/_baseline_images/tout_df_add_fields100.png b/src/tests/_baseline_images/tout_df_add_fields100.png new file mode 100644 index 0000000000000000000000000000000000000000..db483967d908b6f6c879700dd8a4aa1b05fd2920 GIT binary patch literal 534225 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 z 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%0A%=v12*)mFqWF?RB!{em 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

=DF$zACU=3!q zd3w^$9QHPceOE4U-e_TgzIz8hm+g zIHEue|FLlaM?Fm(QIKkwso#SB-WcY~8A8dLg6=*A&v~OuJ8m91q#Ceeq#(~dh+(=? zw2e|bnCvzCsdywLGP6yNy1-@A6hNh9RG+kdsI$Y?wsYr!@tES1KBKgHYmtDz^)+D8 zT!w&+u`cmF8W*qQk&%EIYPEGn%W|VqSRn-xdVMW|sEwisbPmt+c1qoqFPede?o~cc&0TS3fT3tZ^q4n2;cO-z#pxbLqfa9pNJoCW zxa2QF%kO8Djfn!|!$_LRIK>rDR|c+_znS^xdyYN2OeG}Ql<<|l%z1A8g9m*-o_f%I zc3u|DxZ_&~#Fo`(I2+Ry&$T$m*V(aw=HFT6g^R3jN(50xCHbcD;lbJV5* zSc4m)FbdK?`U0XbtGE{6*f5@Byq0FuWAGhz%^aF4zf>7N)>ciM2R!Aa$L&u_mS~aFXx>3_MaS^$>2oYf#yFC5dkYGnotc^nd zz$}RQ1)2+l_)3Fnk8`|tFEraw*`VEbkbFigQB8gJ09t(o*MsqV8+w_2lej(ch|(~O zk^hJm*jHAXS5iR$%u-E09LOCEEu zpg;eVMJ*e@_#;JY;>XL^a(?pX)a!l80-JH6`03)m`Ifgioc?2Zz3r2K?uD($*38bo z@}}vW2{ld?_BO6}=4J0h_z1bE6me%EP$qh-D5tZ)@P2m&80KaoTQc1+D2TYmQ%9o zQ-~xR|FgDo`5F+(DO9q@_2X(;Ac;pwNJPj^eF;&rWI@-oRwmHQBr!uKX4ZA;0zI4j6}1IJWBfPO*bI66Zi;ir~5wd zCn7PLmU;%oamiFHxAgFTV@rRH{?bW(%;Vvd&>j)kWElb+qy@Dg zQx>cAYYz*J_LEw&Q6QQi%OaX$Mh0VxM$Z5#m7Dosf>U{Acx?&r)Qr7IC~wd&9dv0a zd6<{#`$+`n_@W@mj*v&h0nB6d3=E~f6VOC7&T+oPZ6YNG6SO+xa8tT7Nu~~V4fe)V zN~q4&Jvp-XY_Dwmy7@_jl-)J#`(O{6%|zV?Ii@?lf6)AIS;vas2xI{(h1M%cu#Gfu zT(W6FqZP8&TN-16raeIvj1_Y2n@GF%EC3TQ(y2mycc)`znC8(QnnLKd2=>5;3b8}igqUPTRoQ*AD0j!;ITF|<29*ej3*gWgCWZP2k zMx-zYA_5844&zk@OP5sP9!$F;Jp_p@t@~gtHc}l_?*vJy9+m!>E;WZnNO1ElbWE1z z9hedwUoX8`d9q~hCB5o!Dcy)$dVCFO#NKP!ZVO7;IhQe~SSvOXXadE3QV)%Usl##T zxN3wtOtZy2R^s@85~@{G{Pvnc7#^>Z76p4?^=LDnnD}KQG(Q0%l3dD>TN!?}DlDBL zcVNWE&pRI>iV)^@?gg-*#Dq08co_(AlRQG+HAd-}P`CE(y5Wk}@%TG)jIz^kw(xn! zcb(tV+GKM!S**>OAeE$&j7T1#OY_yG6^q>uZ{T;EmG;aKAh zW4uV*<`LZ1h-J}>DbWXdZx{8-pSn~y8V8$Hn`0$9cl1O9O_GnGQo{lK;$^q{;WMH3 z0gRE}iI5uq3z*1jxscR+^Jv?60x7~Gp~9UDII-#rjlU0W5#o-x8^})|)H+e#*zjV| zH*R6Gy|vgLL9{jn6eGaCRl^>`2yR(e4-R5}B0Z*}me5j>WF!p}n&|0@3ZF3VDJq-; zVE~FUbvQ0_XYGEuH0RWMsd4|Q(VYYG|BA#^s`FK7>I^}tQ<&)onM6`aqUnSro(VT3N7$QKZ6qfeFm*S}H;jTr z15c1=Iaq>8$81VS_*MaXj%nQ(iK5@Z;e}ulM^RTJdxV-mas}=Fg5Fw#P#L30%*nEi z)hb(`Xi`wV+A(Z>D$%Zt7~NGIHSM*X7etya{# zkWF24_f2-1WoC@Bcwnt5J%rWcwj=Iy9y|XOf+o@ix6BAtV7|v zA(OAC$Ko9rpntJ~P|^Y~(E~>_78FU<=PP!OK)7uyhYer@KW!h8Ru)5 zr79~lOq36tWDlhd*KU_fw=ECW_3U0N=s)Sn;y4Z?w3(c?x9gKz_0znI zM7dZFfEv%-#7QFnPbUPpwtlY4@xnsh(uL32Mw-r+PnMs47NCQo>7Ep0T@+1@d#MQ| zw~;*lOiwdU<+TvM9U{3+LKZe|zLPI1;V&9>jI#gFN#20jA`e%-&9zwMh@?=?-T&hj z_)1$d_UTFH(Zy*~Jrb>7{4k;l3cvvnRBCfHyBRC4m1&jR_ilH` zyD?H%>K?2nqhYJF%NP41t{}?PwJ1Y!_rD>_92O9U!^bO~Y?`bX_ z*Cn;=z7#Ru%&)qj(Rnvoc+RP0s{>uMcX8(ri@306v zrm;iTCx~qq91w6;sBCegg-iP<(ZmfmT`JU<-sx@{D4=+HoNHTJ-y_RjU z+?9h#s(H^HxCg~8a-k;f)pza%uq=*sUj6V@?|+n-vco|lno4=f+e-V`qnV`YilOq$ z*fl_N2PAanm%F8Dx%RP+AD4}&W2Pp$@vqf-RJ}t6&;|SFq%{SH=Q<|O#~W_DZWox+ za#XePWG~p#W9#w6^)!>3WG9o=f;9x7C zP^(}aTp5P;Mk^xVXZ|)kgoXS{(ufOSGc(~{=FLbO#UUayNUn!4*?+)ElsEB9oP@Sx zP13v~a4Py9dKJKY6L9h)NBPq9)2^I}oJK>DH>bkW)zs(et}kE-{yHLz9}9?r3=7FEEl!Y96iPSv()M)-}mrK4>sf!m0k1dpci+SGB)m8t4M}SSra9Yi-jD1?txZ|aB!MQ)~K%(=;PlXOs$S^Ob4IP zn`gRHbDMa=`(Y#j0)k^Awvv+4IUjom{lkAEl0NzpA#hz1M#&2UiJQV6i!YfDZDd## z_<=}BMe7!qwQLN}dxUqGYgA$A)|&m(Y*M>zhfK;TTk|pxluV22;w|Z#V(+ZWkz33J z$1Dq7i9@|hDpOzZkXOcU;lOM6l^ie`4k7gm?{esz+xET}#5Cog=Lx^{5 zPojS&EsjhA_!7xaP6?wx@?dxyw+G?mcX8vOds%g2?ulF{yJ(*4_d)RS* zQqbSZNJP^}f7bJUR$y*(NC|*%M%2~Y9WVE<46cHKeMFi)!&8>vUhl%Ww}ys-ylr1_ z1i%)6-6up5_|VYtSp36h0Kfx_NKYP~vYhuNhZlVTM3Mzx76GfWv$GSdt!U~LDU1lu zSRqZ|_9LcjvaX$hL)nHWd;D6Xr=urYr=8Xr56{$D>zpZ~WpSn|*p-)?oitQcXUJ4K zv+5Su#++hDjAzsoNOGFj;H2By<7@CvmuIcLGR|zUnc|vE@sX03iTs48g(G9F{uGow z21e(Icam&NwdnJPS4xLzy}}Y!Q9blco_4I!MEr#aO>+f&!a`H4#ZGyOMGbhe2+akECa-WS(Z+c6SY&*4Fo6Qyp^MI$HmCE_SB zAu^@`K5lJ$oCfEdb?>;a{nl`5{sBD#tMcI62hB6)3+GceH-t6+>OO1^k1dJ#WDQf0 z*)ZIe5GUaNgj{a$Dbd9n`jpTUf+*m;4zcdoJErhG3$LPpaSF4bpG>S5U0ujza#~Db zlXGBTTq2A0Yk@K}k+5>!#&MTgo4Mv@WoNG*2Vlm+D%v#_?Thi4qgs3Rv)0Kam7<_M zMl_e9QSpVY(K(?Ws%ms*Irdo6l{#cBj&z!>JwE2&{H^?5>w1B5&7o#QN!dJQ(1w-8 zT5&XA=Tca#FUCg#Orx9S4s=ug0kx`)tk0)u0s}rVtdKyV!`4pl1YnOZ|2t>=3 zu=DI_AtoykR$?1iEh94#2c#xOwsMKMHi( z0;#Cx4*75SO-PS)mH9Qe-<-Hj5Kkz9ufD?ra_?2*GAXF;qQF@`y~_eEJ%Uf!58;!~ z5;QrHv^;8K3O(-xe2I!dlAgl>skc&1%_-0>G(zQ2B47M6t?N7dXhW_#DzNgHD6@!bOh^Hlgh8H)91u`o_p4R5}PO?-Pc)^JXEY<*`<{8oO?3SbqHl ztXtsm2fuD1;)R|>cjOJNQ)4H0V%{0R36vBYnZzJcGlUh>|p}uc{;LM!&Tm53Zbz(+#vKAI9B&@%JqH>o?x6y7DOqP6MBNA5HAVbwS zuM*+$CEoy92#y`&1r`*+>!@yb(dMDigGl$`M#V!3w0HwtSRD4j>x45U!hR7V2{m3T zh!O;`0UoFC%61~4bmCbp`w!WM08R&io}}$zjfWO*RwAS{tF|^tbPt!Jl|jCjjq|k* zYh8S5P1e+{ratKS{{_I@T2UuEF?|KB!IXN|W;MAO%>`}&2el&$XEF;`K`F~1Z82Cb zvC-|7PLg()D^*`ARMYN7!sK&gu-BX4C3WJ&!|ha&oBK1mxnZ%STYEC}8iZ3UV4xWv znm2U8K+w#ia;~R`b{)}Z#@&7kKh7^BZweMEA+-JU+Sr=XFT3n|HD}2E5zz>BIeTE!pH@GiG|cPjd{yqAz4~u^-HkRl4NP}r zUYt-9Yh;AkKrkZ4sKsa{y1SPS=y@uNu=%j-e5#x(U?AslGs+i+-saa7z*EagX zLT6sHAZr~7V@-IN^Ibo}C#s+?KmfkZzY4C|i{XjGeRCHPuXz+;l<@ zH;59DDoZ}cIQ}cR@;5DWH+AyywGx4b;}w_~BFPIl29zcOi%0}KKZaL>H^DidrB3f! z;tw-{r(lVnKSYMYVgMS;-A9`}usUhO7PO&wMYb_~PNhH$L55+2*FbY3PmPB#`-41% zQh@+t+Xbk6n>gLiGTI8cd)Q@j*)4mn_cj%AMam(yGbyuta(n+z(_U`}F;)JvC9l20 za^E%!Tjr-Kee;E!!uD3HuTfYht8MnOJ(kOjzA1oo_H9o7H@I1JQGT=`3E4RKLc`c1 zG}PgL+P-8y=|~-Dy3}0WjxPr`_ewlRUfs=@+8AIWliXVHKM_+OK7;m-^kBH>oFPOI zhGYglB%<76@`X&DmK$ssnG?`_w>1y9ktinGX|2=L%^H7@>YQ9NHW-kkj14)$p_z;; zBN8PweYJ6~=b;CbNwcFxI|nU|2Awyjm>a9|MqAzMsDsUGv6$Mg!w=osW9wD}>$-HP zs<_U-S^si`Rvds5#aR6)fCrd%s(~<6@DrA$rKj7IZ5pu0Tx%tnzE4{IN_FQWkurn$bf2;iX}byXn-4${Zl8>e|;u z5}3fh2-3$lr{V~tzk)deOjkxOFcolj@vQ|)-aX4P@KS`RKK1cIMW8$gAKd_+q~{qz z>XsW17Lm6+KvfEs_2a3=Eb>6zs$Yg~f6_E&*>Cwtf;T5YDXL3xCZGs{CM@&xSW)=c z?m1?T%@Yp`qN6lzL3`pCn!mYN88X5aA6p0N{x`)@Cu(aNXHf$E+B%&!d$$OuYBl<4 zS_N364l_A;z+&(aqLayExS(mIfVcDVJOb_JHWOR^F2BK~IzYgdq!)bM;F-z$1b+u# z{4mdfx8N_dD~7lOvSaf6CiQ*3TJoTQ7#t6w<9yir`EfY5uP9VG7Q_(HUeKNmrV2-! zzPYby0o;PCJNs!z+V?)L+`All$#e0F`Fnw9ZU3r&)!^t->80&^8g;IWD^(KN3Cn(C zUjw`d{^`4?>X~4NNRE@@xM-M(<&?8l4rYy>*x^{|*seFlo&^Nl^svOgw z@J)216THP%7#3U|Rl9Ts&F^1Xa6IwHrOM^xbHA@zSZ@9P(?#2k+!25S?5}E+G)!g< zOlYzix!subT@Nm=WQMEfGo2!9a;(g+Io~Q`fZQp(wCR^lR8HO=PAxIZ%@ zuIY9MKeGaPkA}ce33-4>`;{ZQqN>h}s zF{(9hTGVEe0+b1(LqO=tpk1GwS%v zvy@}m*8`t5KZWoqFbTiqYIC3(gCG0{1{Wh<7EBINCBzUT<*Fd=dIBOSR6N0eXNTOi z$QaI8MrK@L;8gUy}=$MCdP z8_pWY>P%b%A3AQCTJ){CGK1Q-9WuH%u##|-`!{f!GR)e`Hq0T0=|0i zW#ThTJ}GePi+nNUQ1b;FijYiSKWTg>xq$aSisfI#@b4~J4^z!f@!9P zbP(+QLa`9@^Y&3aMgtI50mq(5+Oj6f1jele2maPpw#7yMXP&>kF}QfshitXbHD$t@ z@7bF!S(gMGgc!So5wNjcbgDKUh^^C+HZV_JLa{YjKAgIw$7mF9okhmV2gQJSeSZ0X z{>Azi)-MouIL~8U?7{LYo5hHj5!Ps6~20#1@hkUkoX2)zUqKtl19$ zkCj|ZaHV_FJi?u+4juYLm#*J4qz+a_s4VY6L($*=vG?2eMizcQc%*kidSv$8g`C81 zfbE$zHm3yb_5pBM{SX7&L5-TZn!WYdLd*D>U~DA3#K!*HO^lW5U^mcuetr2i7f$EA zjWY&QxuyQdm|cXf`t0GXrt>164E9!VE+VL4FDKdfAy)-Ef(cv|S*DPhUVXp}E&~CVerf3N3G{(Kv$JZ!_JvkF<&{jHjxv38$49U3% zFsG2<kWTD_NZe?C@thiMSSGT zenc^>fv^GH|6>%F5{nItnsJS_wK9h%LmlV{Po2PdIXL-sW=CpC zWC|Dy;xMcL;^cidnviu&EU-0B43dLg|EsPkiZuYLYe&MS@G&;*`|#DU=_s%}7xthf zB8?vnqv771RlDB<+Zj+8A+)Ov{ZWQzhK&4b>3daL`2L&D_HEe{A9J7^&7UOiE_4 zQ*N(K$+VG(V_XpnAPR~x$q-M3dnBwFZ7rs02c@YbEBD?8+Uj1}4MLjcI%&rd%#c_@ zh{*aJ$Nos|;MW`tL})}}rC<5QrrMFsf^9$vW*pLnc)J`j1r$ zW4@PkXnGe_J#{!y^JY^ch|-3IV8J4LxzW`8Cz;h!Anc5rHr5sZZFv$KI-@mTz*)63 zI87U;y975K=e)igDTjKPULXll;^HjqQKH=Mk!S-!@a{uf-ciEt8!!vSG5mpLQ(C5e zFwAi{`OZfa=?$Q*WM8P$V$vjdBsgxBVSML>wl-5;@qtjCF0&44ixZYwULVPQz4dUT z(l>!K8!jdK5@p*tCAZn+`700V(jF0*XxrMGJeAC|xPRp267oGFiK4bp9|Ic5x zg1D>@kBkaye7MrpUtyYb*EQ5MHYj@zx43X3boK%yDaM+@La@5Ti1^<`Dc{4AW75KAZ}hl?#W`E6_7ejBa1IyDBU7$$F$SZ`zXm+>d^j zn7C58koObcsb7CA{Q!O$=9T7MU`~@v;I|Gp>X&iiK~qhtVO}%iScP~f5lEDNhAU3c zln6vEgCeVdO<2jfb@0GGI2ed}(Bt^;naCNukHnnEriZE8j-Vc$NatN`mBuyDL{50)68B^!Elrv?3Dtuxst@u#4172#C% zU^G*bE2A(&|GrR}>g*o@wg>9E*|lVowU*V^Y_^O*^*SryX$na9Lq0$*gHGwUTNYe2 zfr;kqKOa(`|1YW4^KMLq*w=lBcLN5tG^;T6X1Ge!M#grPC!HC1BxR#+=$n* z;f3d83&1Ok2FMsmU&v)5Ah1j3U|KG>+Wj-%nTZN>qalCjjH?*As?loxX$uSY-W`}2=w9LHWnchB5&YEj zj&C0!F0_c?xInpEz;U5C9z_!K1{NPI!py*CqbvgHFLFnN+zl4B(i0#`LM9L~BGg8v zosov48CzY~-kymwdjqfsbS1VYF2qKaBx~im^&QNLZ}y?s6U(r<1nh?ru?VuxU^4=P z&BhuR7}y&5Ta@0)ul)EL9M{-%b@5`_jX|QiN}c}d19i;!t<)2OS+v3;Y|}`H{4$Vu zB>AQBr`vX2DIlj35?8MJN_+J_tnqndX`s_bcyBS>ppo@kuI5i0Xb~X#_swpRMl1JG z!x*`I5q6QafiIES;Sb1L0wMw_%a4wcgdWb%)Tt{hhB1`LUld+m@l-7M%IBc-Op(*i zH#%(pQSZOoyY47kP%AMf@${V6J`(j4fAO|5xzV0l)IM^ePFFb4t8UM|ESL?ZHY{X_ z8{+-qxS(i%6W&tpK$0h}HSh9?PU7QRKyVSJAH)%u_zLGezgqPU3|Nr@X*tKjfrSMu z=pFecm=HbHNZ9wty6*e$xK$8XNMyKsHabS)yNH_SAs6oqWx01q>Uk`>xEu3>aF|G< zjVb=FkvwrIdX<&U)%jKMWo^)2x2xr$O53$xGSBT>*J%UQ{V{{a8wHNuB_<^`I?9?C zi{#~E1v)S9^@~tirC0Hffbhv$f zjWnOf+*q%9(G0_ROdSisFbXujd*s>E&^60DxL*Lf@C{rvq)|WceHZ{w@4LPa^IAY( zP74W&Jf(`73(m9@{muw%#; zT+o#yOMNjZo<7!A>gkCF8!r-y5^RtYL-tx2AV^)rta)2v!?vN|mfa85tshWcIbLST zN(vV!oplC9Av>4aC(E$(3z?2&vGf2W#Yl)@mtP4sw(fvNO<4?85+1pg;nY(v({@(g z&whu_Q~unN-@htQQ{QzLwDu;m4cxNf$^sbc3DI=;V>OZwGb5QGXsF9C62q6sh$sJO zt{Id2%*TCavR-Z(n{Xd$G^9Jz<*^S(i%RRpn4gDyvTCaEAO*P|+u5dr`Vq7O(`sMDCd;U@z(Kt2`94=ta9qW~)w z_C@rcdc`qP7$FRq6yg2>Lir}>i{Z`yi?vKq@t>e9oSBd*dj$Xm>eQIn8@x0lCaIMi-30b2;PX4()36b4%_ zrl5FZO=Runo8QU3{>CHkA8kWqS$utw+>veE(pC zu}J*RgM!|(eoAHU>bzs>9$ALIImKibwdu#EOQr@b10ci%+0k9K?a`VNm3Z69D%nk` z=DOqBko5Z03&d1RXK>964$Kv34M7KuW$FMR~Mh*+hco!fKz^0O&|89FYamtV5 z@Z1EUeBN7n-Ez!pdB;b582^QrtjQB$0rU#bKc2*Z^(CG>^wAPw>0)fA^{}`1HxXsq zZvCvJd;Y0hqV4IlO_-I5w#T-G&l%|EYa)Ck+@%|X-@A9#Wqw=dS zFC{n5dU_jRY$lQdX#e5kcwt^*h+^4ZNS!X*LH12MAJ$WRty;r8`9!YL3 z6D|8XsvL7Fr7pW}0TSe(X)h~eH2XDjX0`+AzLD5`VTq;K80}GGJ1aJ{=oYukSA2~s zdCR49*7a##SDP-0W%lG`SHcZO-a1kXte=JwNr~0ZFj_ESyN5-O;)}PIgKyoiGm|{D z{=y>pf0saeQ*3Q4zXXHN4-0>esrZPNV3IGeBfJRq-vu!H zz=ByrUMb>;bOnFWZ&~CNaMA}^u3o|Nd5jtI!`yjBqAMBjBhugmJbv(B9Oy)ntZV%h{ z?q$=mv#_&lT-T{)ET?SpU_w(Z5kt_pTnD_B7Wz>e;;=JW(na% zlMEXI-YslCh-`x9*Z89Ag6n`^<4c1v8#6N3{yPutQ5_*1(KZ$zNXmz_E7AJUS|s5I zi^yRZe3glez#A;OOKp{7SFzezz6s3goaL7EiYmUtjQPm@r_sXI=6~~CL^gX!#!f^X zT?HB23r8yk7!u3GnxbuWo}4m|TAgj^IVp&W69@NejQb@R*+{X$0lv+(x1^uvUEQq| zY8H*8#kg+#!e-*gj&!gUHO_HZzCPeH^ykc9=pu>J-(d7{>x6y(-2zud2vPQ-1sp7j zBk>pDEWACT`9)tX2}n0sgr28I64WxHgoG$-Z&0YHI?B+-e$7WaS_!l~vbOMaMLUw%W;aPy5&?!%68ZCh?=-Jr!Ew!|FG*Lb>hYEui2k? zX1(aC8yNnWkXCiWhMwQt-P4y^G!#tjZpDK24!K5~Z0+@1-Aq*K3R7~EDFHI8pV{F; zoydQD>VY1%F}1NqGvKuCk@r#%pBye@kGB}fTB6slV$U(qMB=F zG6Holt|rGhl3Oc`J4ArjMiFDf+N#YhndGt$0}t#M#$(gFpXP$e2sjT#>P17 zoC|$Bf(5c1~+!!=eNfOj^7VIqaH_d%%&P z*lpbJJNHN6G8$Xla7BM%88BjO!$Y+>I0_m3Iu_hVbjc0SuRvXigdLP){Fx^fgCV1Z z3@6BlWQ?)q%;6$+@xhD{+p~r~)x@Dt`KpI#S@0RZI`EPEP=5M9{_uBIo4oA$Cta9$ zG@Eb+R2dBXJP;eeK6rPJ4XuVZH$6dWAB{r?2c2>2rs1Y`1Uyvo7UUTGKaR>t1 zx{fL&wmO^#z-Hpa17manqp&8nC5lo+F|w{ij5sQFh%D4he=H6`z)4kGeQXV!{XhR# z%6BzgDY0!S2Y>R@|8xHT-b4R;&l!CW+o;%{ZJwmDaicED#7w7V?i9(yrM$co`5j|T z?R&Gumc@Db{CfW_E}0wHTc7I1QO*N<5C3YhZ|V>06Ia~-FLCc4)Yg5bj~`u7xky{? zE1`Zn!Wfq8MmLo0rkN4gP#Z+UMZ#cqnIj<-E>bexv~Gj{5jQQiMHgcr0yY`I76Ac2 z3js>g*fZi{iZN<^0V<&+)BR@cZPKLOP2+U`{5_u|u$?r!GbS_N9vMj(uyw?9-uHQ* z_cnCjlRMM+ac$m(*`XcJta0hZ^qHc$@6HT<>`Y#It@7;jjncVm1E21dX8j@SzH{KU zKV{!{zL}j~qAQs$uOIw&wliCorTb7<`Jpa*+EuBGpd={Z#KIS`Po(LruKs2C8?F)#oCs#Hrx^1RW8@W-KLBiSXVWU zy$r{aPa89H$CUGJ&fY)#)JCsbKqlJ$)9*QGe8Uzbk5U*-Cf z*4g&Lbjj6^oF7f&6Wt5C>!+sMbUSAsp0C$xr?XEMC1q!8E5@UfbW_^$Bv&DO42CHjV#&7Rd*Z}RUBXZ{hQkE@p|v=Z)5I&?OXR4{26u}wO;A;=3K}TMIZF>qO;aHI zvC2}#=A?P8r@-!E8Bk6}HKvg<(}z5Mb7%vo_GXy|$~IIel-yM9D+g7mOib#@F(Ll&THHCC9l5lD&G75&}BXI{MC{y zUBNW&rc208=c+uZbG@-$rE_LSgGnUD#!_mGfQ_cCR!_5P)CqR&Cr`?P=8<{tj=3&> z)IaH6((jlW&bEgB@h30WL2qh(>j%^e3qSboy6_v{ZU}n2W$U8nd1=ye74T$gFkoP5 zAToBeQCP5G7jRj8=_1c+msCjZFc@~K}F85N9xefK)lYf<6{w6(1$e!d|PHZFN+bmFHPhu*(z`NOWgi)*HI)j<}k=t(-f)gZ zd4-ts?v3gfur+*EXB?2klUY6ISTc-dK)>_hL&&d@GRjq9=83t`Td~Z$!EX*66&O1W zqiftzrd1Xwg8w0h(H86o)~wcuVU0TT7E)i7*V$C-Ble)&XsKnaQp*=G1bcL=BroSz zE#$o?7=lg83YG#sb9n24qhFQk-mlu6dE>9O&H-2WrY#zDX`Nc&N!O+;f=#kwR_7SB zk_Za+o!av@wysx0<{z}ECWCg*$K+0-oMbXyrd@El` zZD&v$UTcbnUc!2C^+l?4 zc{0kFKmc{vJ~bTT44~7^Lc3p4p+srH_ZdJJXmx(|Er&uOFKPigP&H`=fErteEPsEf&r<$Vbu??|xao_PrI<&62ATYogY@@(77$ezDf z?>!^_#<%aiuX@A(hX3#WQ`q3&v&+P2k(KoZCA`h`Maz<9SSZ_E#}s(K+cE_mJ2WNyYVpL?#n$IbF1i0%>djWCk$CCTaBj@yp?g2! z`5pLfo$F5nr=HhMZ^9yd=BjVgJ-;(^cjhDA_0L?c`kk}7N?a%1=c9GnsluYcqe&-q zQ<>VN?C}mkp98%o6x;(;k>V|Bemuu+4`{a{F)*T3nt~O>rnNqAx?OIdcuQGgiH~4RCpD6jeMgJoxoJ?c>)lO?-d6I1ywiYQAh3c_2Yl=t^yB z=)_)A`(u_)s$w>1;@sSg#JSnFh8XAO-Wp0>h-kg#Ia6B)$M!AH&o|)Rt`89)E&ZhT zx^Zdhy8E@$pI3rp5G0*`HR~DpxlLlBh&y=o!-tjc&kRK0AKbT1AWR>2yWVuc&*92G z6~XF*tOolekr4Y7x#2bYO+eISFc+~`^H3Sc(59(jWNFS6+Ek7T&@taCwuJWH75rhz z^6`%l@S9$F?OLej_Z!dMGQHC9MsLCzc842M7Mizk{cd|fm<*esP7!`N&wRpX=x z8Z{ls7SefqYCPhCrlau-R;k-qLHN?(Pv!>tuY)JS%?*57S;Cx5*42_%uaeUtP

= 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

8Zk-Vj?### zNVPA!Z|uN;sbNRhFEfQw6FU^hH)s%p(%@-eT+Qu>Wa;hq3mTia-5@Ngdbb&cV)eSd zP#cAZO}|Gx66@Wbx5gFpx!pQhBv;K2*9yUKtn)yt2b^J=pW{NDx!g(MCD)KP#wd8& zbmEa|LIuOTtgO=bi>m$fOp#=vj|BLDXY&0Qp88=BL6DFM7;uE$XY4$w)0BFsR1mG~ zN9iUjNj_1RwhF!RgseA4a}M&5qlusHV--YgE;ZgA7gy7cU$-{4&J#Sa8_{)Of-w>7 zf4#*sH6~8Mq=@23H276k9N|=l>(Q%jcSAyi5Kq=D$|NFb9ZkCgO`j@!%F(c^)1EtOTbf48Z2kC&B+rwn;1DZ$7;FM#LV(e7=VLl$y`o? z#2sMmV~Zu^fIiw;RBI!K!jQX34Ex*STC{;7f83a@!gn^qFvd_Bu@j+=7<}&?ear7P zd#A%=NxK~U=)B-;p2d0~mPIGJAsMw!r2ib6UeuT)mV{jGQi*CZBve!X3TOfX@`Y1qFASfvU+XPg{Ojr((Ze2 zz_~o*E=TVjJmDG+O^Yimltv3x8OcPU&oalU44Kmj$D>qhw8wjr+hvvvuk%pTvo1Nb zjfSjnPLaU_99>$-Ex%Yxu>Q)*?VY;B!C>m}v|6DpPr(&<_FO%JGo{gU}P; zP@oko<%XOs-3Zn z79FY`oHCX;G`M5Tn4`b4E5}T$kNN!v+?^);Ok1W1OHCss5=+dGB<`7IOGGLk9%^&7 z?Ju)haiWztVN71SDR7AcA;1G9hUF!e=c8!$m7*41jBzTf52YNb$_Ojc%wmFtR1wyR z1Hl&b$RCR5%YY|uBln%4I|Fojic19|GHlYttNB*e%EQJip1LV)1_&CbHK z1ojT!4uR?n!#-6;O|ZdMdwM-phoP`#f`wJ-j2XaaXxO)L|npqKUBuk}#u3y)&Y2QAg9gM6+A9f4e(< zy28>;=-L5i{Nuni4q^f3e~1w{CtFr#SmsHJ#l&C@gif7Xv>x?R88I0;W0!*LBDS-i5y^dc{OVsAv48L*DRJ|msd$o=DY+Kwi$Dgbgb2&~ zxGEK;n%C4t6?liUDkaq#_h@`V%KEZgzx*w{{O^GWuV|(}ycqlZzyIpo3$OiXZ$O_@ z{59?NGGE-keCpHxddTpO{@cFw*`Hcs#o4$0bzAqLZ+`Qe2fJ^(?Y(IoSBh6|zpcFN zq4q;7J5#HB-}cI`l%GBK*{3&ibNq;P`{R4(>neXXw0EH3)V7h*?mb`HT~T>*_rdX+ zgBPm~rl#hfOihh1W)iY2>DpqHwugs`MrNsP*?qO#=|P`ml*zh!u-XyS1d{SXqtQ6Q zh*T||5L~TYsI6b{pA{j*pATGNY+GZ#!%g;(&s<`;6N4G7V-nknYD+g*$ z?{#Wb$t0nQbXY7*F^S7YXau7-Gx}042%#>M1o>TENUcQA2hDOwP!q9`uycb!Z2Sb) zMm)8qpiK~MI@KT}xJ+!ax;*iUQ)B~0w7&!I)>{RVDOd};ZH<@_n7=lhq&>fYo8p!| z%bw_3354~(+{%kc;|;eqhOI@PC>nYH<2z67{E4e&`_EP?Dr<9!eloP})4MB9?K~W6 ztLbj5$DqvW_} zkD+dVVQ%g7nhMpbc6)D)9Cqd@i=wq_6+wVKYMcb zagyz1RBfV0!_Wl~zk~SaQIH*TU_=|Cxk2CQ!64MJWvz*UTbH1yC~I-3cCn05?q@9Z z(-xEu8f4Tyt3Fypg7H!Z*WTui40PqMAi!Ec(WuyrMv8D^5~q?MN5J28#TZdmZJI(0h4P6+>`Z!evC@5a;%A}y*+ zTox6XF*c?}e7q`6uQpV%Qs9*DQqe!P_M^pp?|cu;j|7z?2*)mp@d#ej5-a=?1tzp= zTMRGJg|)TuO-#wwhS+=g*u{z7i!ER7yzV*qKY4KJeNpIlBTfk83PDDF@{*a z%}?(%MrF0C6TaM1(~@DAZp75k&=$Y~NN{NMYEja#PEzVjbEK~=fEn8R$6DYH$BO`2 z!l{dch_kEgyXQ0c-|85-2ergB^pb*V(8lsz#TIlBXlP@~ouynYL6@}0zkX-y13#I_ zKYKpU3Ze)#oUm%*q#~+@U!QuR)LR-@i!R3XZ+U$0me1HYLD=)9ixW?s-2K4ClNV3! zo~Su_@vey)JDvLxq@GWNYO1nMMx3hTHa#|Fa)@d@I#e5PLRsJTFFNetpC5w}MiE)`Rh>paGrh@p;27fU}OuJdWQN=B= zmS8>!W4?=ol7L_1Sm*FQCdIk}ihMOgCv@cB_}2WTL0_(UZ@$BpUGKDdI}a46RXOzC z;xr}5Di8sV*&FLH>Qf{S+seBO+#$Mf==_uJqCcy9YO3I3K> zw!z}yRCAj4q!rQV*){dk-V2V$?!9_GPluVO{%p*=iKolRf^9Ev`7=`5!pxRw_gmrg zr5CX63-7LMDT0{ynPzPJBQ@P$-t&{}JvjrBizg$g2*+CE@|5;l@Fhhu3)t5vU^yW$ z?)2ycj$8KngheCr^3{bwn_0hXV2;A~Vi!k;Q5AjnnTg@DCHvBG!=w8>chnw0s4wbc z)!cii2H~DL)%R)`ka|k`lS5(xWIR-h!vph=FcgBy^YA@G*b9oL^h~n`G^dYJ{XjJt z@0bV4_dKp61v-&$bxMVKV7^mTYB2psd_AfKwu9k^c|RmUw7oqq&%H#SL$$u9U9G3f z3(lv7I7qAkeXprO)y<>kHcKqKIjbmzyn0FjmhHm%Tl5q~@-Jh8n~{^r+%@XiKUH zsJ@DN!da4n4L7%*@`~FPZNef%`y$aTAUiH<4NY5&dVVqwF>c#QovyB|?C5B6-I|1_ zLkDea_fOYg$AjL#XQ1q`c++ObwkOp*>CuT}{%V21P7SqQ(8%F?8pr4yXLFf48H5xL41%(hZOLP!_0|maE6JkHvz;bl$p0@D3bw)2u>^XN=_nuGGJbov{x@PeC@#8!1KD84y%Dzm6ddonB@_1D$ zrS~DdCP17B(dw*rpy+qDxw9DilIuB#mJ-4KvoYkFAY@UGZEqY;KNHTko6p!6jBCkW z8QsH2_tqT%>rdU?c4TP3=RJ=edH+aH_GI!v&(BZxpkkgXLgKVoTJNQ@ewDnXYg4$&tnow}*sA8vCA4Vr7*(Vr0E2|aojvOu zHo6GoQX_n}ANBCTHDt0J+`BK0M%GqRg>FjT?@~uG8MJ9pMJlC~IA+8KEfzq|33<+q9(a8YC_f>ZMnP~imZAqW z{vmx?#WI!^R3APgq57~L5`JE8dY!KN&`gCVc{021@Xt?r2Fft?&z>}(7BeP(gTvEJ z59>j;(`U0-*&w{VV+V59Wj*3Z=Nsm@9gs0*FP8f>gIuPDMeLwa3g+vh*!ROWj$hEA zK0>;BimZ$FnE7_LxwPB(wX>bZOclXuS{q|I?funen}6PbB!*UfNSEs+`va23NH|;e ztg0pEbh-2aJ2!Xm9Gl`OY?jZWG+Bjx4{8raFET3h)IsJw9+I&y-eL)xA}-jSv@xx^ zit|^Rx_qa1fBfnJL|t;k0|AphfnRJ;c#l<`Xg@VTZ8REJ>ZSENwlv-Xiyx-*e*W0S zgV5$iN^=L;Q%8O z=s2&9g(m%@5CKs$xG~5e_swpo>$)_ShE{8yJF=k8_w0cpzo+NXBQ?jr0{b0`*;U^Q zVzLcZ98qP_q-*0kG+CmtK^4pHL#!ex@A7FN*f(gv>-)x%)Us?LLa=voC#Q*&=j)2s zTyf2k%SN`4Wx{N8R6^%yt1QrizfYtO^X`)1N4-@nF19|*sV>{0oLC1}zt~KQI75m= zr+vr|m5m(^GsXKl;{)>%E^)}M_S@Wv?HlKjK2%{VqFWu&5U|-vSU*-`5w#Xnxlc7a zMNE2=3X?9td{a1f=bynSu>uk)i6WxXuvI|dO}^=sKybg#WS{VFyP;{^_=L^=XX_N6 zuWZ?~VX~W$4LI|4IRorr?Dc^iIoaD)REOJ`?l|0!8TR-&aCWG1I#7CghemzSAJ=|1 zKy5hCPn>KtIO;$q;|Nvbi##Y|i|qcY;|nc^FC}yjm+5XOaus*Vid@dKC$ZaauyuU` zwc}(Hm6|jlDK(Rj6B5ih1X{dt)k19Qh7xX$Z(=j~CL^OSG=_;3v=P|vv=_wvHpW5< zYX!QwRaRG|*oY~&u2i*$4~(APprn|3QJ5$`QMa5O>yWs`=>8yEbz-_fRqMlsM1D$p zNLPzZds&~t9V#{%d~S$#BEG0dS-Xlzy|C9-La3|9?xiX`GUKt?i=3XMyWMe6ppQ#d`4d15U4*kv1=;Jt( zz0WP`u-gUeIKz~AT7ylrY>GM~BdcOzEW0}Q@Da@U9qY>b=TM)JD(`oK!vAFfz1MPFc< zHQ3XfU2G|4(NGWbS`k;`5av1Uo>CVtq2>mqZX`@sJ|WJ>qD9nVi+Oj+j~TKMbBQAj zlD2^ax>T5ZZ3B;_he6*#Sm;%lTiHnO!MXhFbUk`Jt~4NQU7q`tTE3etJc*Z14(_aM z*$-pwo-bM7HJLG4Q&D*rwWtkzCIa`2HlYfV+=dcI3XKGPm|#}{cvO-mQ9dhPKw z>3waR$2sFtK}AF)8Y5~7L1+AF>!42~Sv4^fOva~D_&d{CF&5%Ir9@aj1Jy8_gbK`! zDqgoD_=~C1UI>M~Rom?fN35>e^j7nw;5s-eyo!fcn{}(=5?_m1=Qm~@J9Vc8*ZB4R)GGx1Boj zWoU0Xljo}|egg5Gnr5l4-loTtKzoxGtsApFNAJ+M6Z>1cAhk`y(70?t5qI4p2?$fs z(WyNsp9Z5mCGBIKL(tmu;f{%4f&kZZfpYblirx2unM08K%6$VH0vr<`AwyJ%AMk+^ zB3o!MJl|P~+H@Lrt{mK(CwlBicHRj|PA!~9Hs(CrXX}dhG`nV1%Q8!^C(VzuX)&3$ zxAV=VweI8nysoC5we7sB#Vse$`^LnPhCV}HK-a{2)Ye&H8}@hkwAB*RSkzM?Kid+K zseQWIdXd+z!qhLHv{AGzy8oU+W?g9u6FTW-8V`tw$4p9W2t_2bEE-Ef%(;zdyp+E4l)jHvgT8YPor+sFs#)U&i;x!OtwJyHU0OHI z=~fKZW6n}t+DTToKi;rwgB(&4FAa?*U5(6>L+;XJ%%WiDcDs3vH&qCWLQ2S;XljnI zX?a$0-e89&DD#9+tNa2Yzfg9Q7>Dpcj#sfi&Z2V_U)tR=1J~=_ zIW-eC6?f;n3&PyHYBu=#x(J^fl6XYfQL}vJXwK^*om9*70TJdJDeN}+nD${eS9cU; zG0$A7INC%SH^}^fN1KxC4!ByX$50<|3>&@zvkn7o&bg$4uk?Jar>5j45>K)t*!Qvf z(Egm}&<*-}DuYy}i({olmMV>u*28#Gj=qpKlW(EW8tS*{68T1Z#HK~nl2+zstu5tZ zBtCZ~E2Y_Ky$LTT>9hMLB%!l)r9q6F>R%=v-o&1;8`DKo+-%1mF93^t;+o11++mnC zU$QQe(Z!y>w9~iqMW_5V&nuh)cwtEIRhtp*2%r9LnimI z(i3xmTus6oKFiOd)<`3w{{;He{_$4F6Ce9a?*kt{_VFpl6IXsdHhJdg=$V%2DRQvj``Oxy)CBBcnxwx>ji5$XTs#pIRt%?@L1f(kc}Wk{}%9x(ZXR zt`3V`}RUJo4mAf91Pz zGwYwR*%x862g=6{-O|zjpAKFx(6oiG%x>Wv`IN7J}K9T9>-s@XE>`Hidw&siXeqK~=3=eYY# z{@+6Yk{N%5$J$Ze>{K;>>@|!c#CqDGTvD;sJ{!7n& zhvLWwA9}_3 z^k2OA&h;}RS8i;W&0b46?hOzSH|(LcXj@H8R+ z_{HDVefx!{KCt-@RF2#v0&gBg^%Cp=*MI%U!|x>TUf(V+J@xtTyztYCd-?kXPk-=*A1-yj@7jZ}+tW7p zk;+kTiohm#4X*Kx@BHDXKl{d$w|(&XD=&QWf#;q&arYzQ-H*Kci@!c}|6J}jzWtjQ zzIy$Yb{KH*^4zsofBE^_f4_M5hko1t_J6;6?KQ^r{md>r`|2;O)8bL5j>1T%UKo9A~&zVYC*|NP{u`EUGL z%3l-&6a*9m6a*9m6a*9m6a*9m6a*9m6a*9m6a*9m6a*9m6a*9m6a*9m6a*9m6a*9m z6a*9m6a*9m6a*9m6a*9m6a*9m6a*9m6a*9m6a*9m6a*9m6a*9m6a*9m6a*9m6a*9m z6a*9m6a*9m6a*9m6a*9m6a*9m6a*9m6a*9m6a*9m{?8*IUjK;xopOak^6UGRlKTJW z=TrFt3IYlO3IYlO3IYlO3IYlO3IYlO3IYlO3IYlO3IYlO3IYlO3IYlO3IYlO3IYlO z3IYlO3IYlO3IYlO3IYlO3IYlO3IYlO3IYlO3IYlO3IYlO3IYlO3IYlO3IYlO3IYlO z3IYlO3IYlO3IYlO3IYlO3IYlO3IYlO3IYlO3IYlO3IYlO3IYlO3IYlO3IYlO3IYlO z3IYlO3IYlO3IYlO3IYlO3IYlO3IYlO3IYlO3IYlO3IYlO3IYlO3IYlO3IYlO3IYlO z3IYlO3IYlO3IYlO3IYlO3IYlO3IYlO3IYlO3IYlO3IYlO3IYlO3IYlOe*y%=xM{z9 j8U6K3e}c=srAufho9tEJ`tCAMud;Ldy=7l75!(I>w{nU7 literal 0 HcmV?d00001 diff --git a/src/tests/ascent/t_ascent_derived.cpp b/src/tests/ascent/t_ascent_derived.cpp index 1af96bfe1..71d380de8 100644 --- a/src/tests/ascent/t_ascent_derived.cpp +++ b/src/tests/ascent/t_ascent_derived.cpp @@ -542,6 +542,74 @@ TEST(ascent_expressions, multi_topos) EXPECT_TRUE(check_test_image(output_image, 0.1)); } + +//----------------------------------------------------------------------------- +TEST(ascent_derived, df_add_fields) +{ + Node n; + ascent::about(n); + + conduit::Node data; + conduit::blueprint::mesh::examples::braid("uniform", + EXAMPLE_MESH_SIDE_DIM, + EXAMPLE_MESH_SIDE_DIM, + EXAMPLE_MESH_SIDE_DIM, + data); + + // 3 copies of braid + data["fields/braid_a"] = data["fields/braid"]; + data["fields/braid_b"] = data["fields/braid"]; + data["fields/braid_c"] = data["fields/braid"]; + + const std::string output_path = prepare_output_dir(); + + std::string output_image = + conduit::utils::join_file_path(output_path, "tout_df_add_fields"); + + conduit::Node actions; + conduit::Node &add_pipelines = actions.append(); + add_pipelines["action"] = "add_pipelines"; + conduit::Node &pipelines = add_pipelines["pipelines"]; + + // pipeline 1 + pipelines["pl1/f1/type"] = "add_fields"; + // filter knobs + conduit::Node ¶ms = pipelines["pl1/f1/params"]; + params["output_field"] = "braid_composite"; + params["fields"].append() = "braid_a"; + params["fields"].append() = "braid_b"; + params["fields"].append() = "braid_c"; + + + conduit::Node &add_plots = actions.append(); + add_plots["action"] = "add_scenes"; + conduit::Node &scenes = add_plots["scenes"]; + + scenes["s1/plots/p1/type"] = "pseudocolor"; + scenes["s1/plots/p1/field"] = "braid_composite"; + scenes["s1/plots/p1/pipeline"] = "pl1"; + scenes["s1/renders/r1/image_prefix"] = output_image; + + + // + // Run Ascent + // + + Ascent ascent; + + Node ascent_opts; + ascent_opts["ascent_info"] = "verbose"; + ascent_opts["timings"] = "true"; + ascent_opts["runtime/type"] = "ascent"; + + ascent.open(ascent_opts); + ascent.publish(data); + ascent.execute(actions); + ascent.close(); + + EXPECT_TRUE(check_test_image(output_image, 0.1)); +} + //----------------------------------------------------------------------------- int From 0e3b6eb988d4a112fa1af3b5fbcfe45c258a3ae7 Mon Sep 17 00:00:00 2001 From: Nicole Marsaglia Date: Mon, 3 Jul 2023 18:11:17 -0400 Subject: [PATCH 14/35] cleanup --- .../expressions/ascent_blueprint_architect.cpp | 14 +++++--------- .../expressions/ascent_conduit_reductions.cpp | 3 --- .../expressions/ascent_expression_filters.cpp | 2 -- .../ascent_runtime_blueprint_filters.cpp | 2 -- 4 files changed, 5 insertions(+), 16 deletions(-) diff --git a/src/libs/ascent/runtimes/expressions/ascent_blueprint_architect.cpp b/src/libs/ascent/runtimes/expressions/ascent_blueprint_architect.cpp index 5aed79fd6..452f1336c 100644 --- a/src/libs/ascent/runtimes/expressions/ascent_blueprint_architect.cpp +++ b/src/libs/ascent/runtimes/expressions/ascent_blueprint_architect.cpp @@ -1011,9 +1011,6 @@ derived_field_add_fields(conduit::Node &dataset, { if(!dom.has_path(output_path)) //setup output path { - std::cerr << "DOMAIN does not have OUTPUT PATH for field: " << field_idx << " " << path << std::endl; - std::cerr << "setting output with initial info: " << std::endl; - dom[output_path]["association"] = dom[path]["association"]; dom[output_path]["topology"] = dom[path]["topology"]; if(field_is_float32(dom[path]))//Todo:: Ints. longs? @@ -1029,7 +1026,6 @@ derived_field_add_fields(conduit::Node &dataset, } else //has output path already { - std::cerr << "DOMAIN HAS OUTPUT PATH" << std::endl; // check that the field assoc and topo std::string out_assoc = dom[output_path]["association"].to_string(); std::string out_topo = dom[output_path]["topology"].to_string(); @@ -1052,19 +1048,19 @@ derived_field_add_fields(conduit::Node &dataset, } } - std::cerr << "dom before add_reduction; adding " << path << std::endl; + //make tmp input + conduit::Node tmp_a,tmp_b; + tmp_a.set_external(dom[output_path]); + tmp_b.set_external(dom[path]); // execute // add out result to next field - conduit::Node n_add_res = derived_field_binary_add(dom[output_path], dom[path]); + conduit::Node n_add_res = derived_field_binary_add(tmp_a, tmp_b); // replace out with new result dom[output_path]["values"] = n_add_res["values"]; - std::cerr << "dom after add_reduction" << std::endl; - dom.print(); } else //does not have field { // some domains may not have this field, simply skip - std::cerr << "DOES NOT HAVE FIELD: " << path << std::endl; continue; } }//fields diff --git a/src/libs/ascent/runtimes/expressions/ascent_conduit_reductions.cpp b/src/libs/ascent/runtimes/expressions/ascent_conduit_reductions.cpp index 094b90907..d32833fb3 100644 --- a/src/libs/ascent/runtimes/expressions/ascent_conduit_reductions.cpp +++ b/src/libs/ascent/runtimes/expressions/ascent_conduit_reductions.cpp @@ -624,8 +624,6 @@ struct DFAddFunctor const int l_size = l_accessor.m_size; const int r_size = r_accessor.m_size; - std::cerr << "left size: " << l_size << std::endl; - std::cerr << "right size: " << r_size << std::endl; bool diff_sizes = false; int size; @@ -638,7 +636,6 @@ struct DFAddFunctor max_size = max(l_size, r_size); diff_sizes = true; } - std::cerr << "diff sizes: " << diff_sizes<< std::endl; // conduit zero initializes this array diff --git a/src/libs/ascent/runtimes/expressions/ascent_expression_filters.cpp b/src/libs/ascent/runtimes/expressions/ascent_expression_filters.cpp index fbac5f97b..c5efd2176 100644 --- a/src/libs/ascent/runtimes/expressions/ascent_expression_filters.cpp +++ b/src/libs/ascent/runtimes/expressions/ascent_expression_filters.cpp @@ -1205,8 +1205,6 @@ FieldMax::execute() (*output)["attrs/element/index"] = n_max["index"]; (*output)["attrs/element/assoc"] = n_max["assoc"]; - std::cerr << "FieldMax output: " << std::endl; - output->print(); set_output(output); } diff --git a/src/libs/ascent/runtimes/flow_filters/ascent_runtime_blueprint_filters.cpp b/src/libs/ascent/runtimes/flow_filters/ascent_runtime_blueprint_filters.cpp index 19fa8cc08..ab0c3ddd5 100644 --- a/src/libs/ascent/runtimes/flow_filters/ascent_runtime_blueprint_filters.cpp +++ b/src/libs/ascent/runtimes/flow_filters/ascent_runtime_blueprint_filters.cpp @@ -758,8 +758,6 @@ AddFields::execute() DataObject *d_output = new DataObject(); d_output->reset(n_input); expressions::derived_field_add_fields(*n_input.get(), fields, out_field); - std::cerr << "dataset after add" << std::endl; - (*n_input.get()).print(); set_output(d_output); } From da92297d50b5363e61cbbec93d1b5d6efcba5bcc Mon Sep 17 00:00:00 2001 From: Cyrus Harrison Date: Mon, 3 Jul 2023 15:28:45 -0700 Subject: [PATCH 15/35] add missing guard for add fields test --- src/tests/ascent/t_ascent_derived.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/tests/ascent/t_ascent_derived.cpp b/src/tests/ascent/t_ascent_derived.cpp index 71d380de8..e39429fdd 100644 --- a/src/tests/ascent/t_ascent_derived.cpp +++ b/src/tests/ascent/t_ascent_derived.cpp @@ -549,6 +549,13 @@ TEST(ascent_derived, df_add_fields) 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; + } + conduit::Node data; conduit::blueprint::mesh::examples::braid("uniform", EXAMPLE_MESH_SIDE_DIM, From c2ee648819ac127e688ae39a7bb283baa3028c1e Mon Sep 17 00:00:00 2001 From: Cyrus Harrison Date: Wed, 26 Jul 2023 15:15:29 -0700 Subject: [PATCH 16/35] wip: identify expanded case to zero copy to vtk-m --- .../runtimes/ascent_vtkh_data_adapter.cpp | 84 ++++++++++++++++--- 1 file changed, 73 insertions(+), 11 deletions(-) diff --git a/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp b/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp index d7ded7b8c..3d88aadd6 100644 --- a/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp +++ b/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp @@ -274,6 +274,7 @@ vtkm::cont::Field GetField(const conduit::Node &node, const std::string &field_name, const std::string &assoc_str, const std::string &topo_str, + index_t element_stride, bool zero_copy) { vtkm::CopyFlag copy = vtkm::CopyFlag::On; @@ -300,14 +301,29 @@ vtkm::cont::Field GetField(const conduit::Node &node, const T *values_ptr = node.value(); vtkm::cont::Field field; - field = vtkm::cont::make_Field(field_name, - vtkm_assoc, - values_ptr, - num_vals, - copy); + + // base case is naturally stride data + if(element_stride == 1) + { + field = vtkm::cont::make_Field(field_name, + vtkm_assoc, + values_ptr, + num_vals, + copy); + } + else + { + // + // TODO: + // use ArrayHandleStride to create new field + // + ASCENT_ERROR("ALMOST READY FOR THIS CASE!"); + } + return field; } + template vtkm::cont::Field GetVectorField(T *values_ptr, const int num_vals, @@ -1350,21 +1366,67 @@ VTKHDataAdapter::AddField(const std::string &field_name, { bool supported_type = false; - if(n_vals.is_compact()) + // if(n_vals.is_compact()) + // { + // // we compile vtk-h with fp types + // if(n_vals.dtype().is_float32()) + // { + // dset->AddField(detail::GetField(n_vals, field_name, assoc_str, topo_name, zero_copy)); + // supported_type = true; + // } + // else if(n_vals.dtype().is_float64()) + // { + // dset->AddField(detail::GetField(n_vals, field_name, assoc_str, topo_name, zero_copy)); + // supported_type = true; + // } + // } + + // vtk-m can stride as long as the strides are a multiple of the native stride + + // we compile vtk-h with fp types + if(n_vals.dtype().is_float32()) { - // we compile vtk-h with fp types - if(n_vals.dtype().is_float32()) + // check that the byte stride is a multiple of native stride + index_t stride = n_vals.dtype().stride(); + index_t element_stride = stride / sizeof(float32); + + // if element_stride is evenly divided by native, we are good to + // use vtk m array handles + if( stride % sizeof(float32) == 0 ) { - dset->AddField(detail::GetField(n_vals, field_name, assoc_str, topo_name, zero_copy)); + // in this case we can use a strided array handle + dset->AddField(detail::GetField(n_vals, + field_name, + assoc_str, + topo_name, + element_stride, + zero_copy)); supported_type = true; } - else if(n_vals.dtype().is_float64()) + } + else if(n_vals.dtype().is_float64()) + { + // check that the byte stride is a multiple of native stride + index_t stride = n_vals.dtype().stride(); + index_t element_stride = stride / sizeof(float64); + + // if element_stride is evenly divided by native, we are good to + // use vtk m array handles + if( stride % sizeof(float64) == 0 ) { - dset->AddField(detail::GetField(n_vals, field_name, assoc_str, topo_name, zero_copy)); + // in this case we can use a strided array handle + dset->AddField(detail::GetField(n_vals, + 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) From e947135aa903d68be24073c7fa42556e4ad1d95b Mon Sep 17 00:00:00 2001 From: Cyrus Harrison Date: Thu, 27 Jul 2023 13:59:18 -0700 Subject: [PATCH 17/35] use strided handle --- .../runtimes/ascent_vtkh_data_adapter.cpp | 27 ++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp b/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp index 3d88aadd6..1eeb0d60a 100644 --- a/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp +++ b/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp @@ -313,11 +313,25 @@ vtkm::cont::Field GetField(const conduit::Node &node, } else { + // - // TODO: // use ArrayHandleStride to create new field // - ASCENT_ERROR("ALMOST READY FOR THIS CASE!"); + + // NOTE: In this case, the num_vals, needs to be + // the full extent of the strided area3 + + int num_vals_expanded = num_vals * element_stride; + vtkm::cont::ArrayHandle source_array = vtkm::cont::make_ArrayHandle(values_ptr, + num_vals_expanded, + copy); + vtkm::cont::ArrayHandleStride stride_array(source_array, + num_vals, + element_stride, + 0); + field = vtkm::cont::Field(field_name, + vtkm_assoc, + stride_array); } return field; @@ -1389,7 +1403,10 @@ VTKHDataAdapter::AddField(const std::string &field_name, // check that the byte stride is a multiple of native stride index_t stride = n_vals.dtype().stride(); index_t element_stride = stride / sizeof(float32); - + + std::cout << "field 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 ) @@ -1409,7 +1426,9 @@ VTKHDataAdapter::AddField(const std::string &field_name, // check that the byte stride is a multiple of native stride index_t stride = n_vals.dtype().stride(); index_t element_stride = stride / sizeof(float64); - + std::cout << "field 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 ) From e7ae4cd396844bbd66cff8fd0cea275cdc01874a Mon Sep 17 00:00:00 2001 From: Nicole Marsaglia Date: Thu, 27 Jul 2023 18:34:22 -0400 Subject: [PATCH 18/35] pull in new vtkm zero copy --- .../ascent/runtimes/ascent_data_object.cpp | 21 ++++++++ .../runtimes/ascent_vtkh_data_adapter.cpp | 54 +++++++++++++++++-- .../ascent_runtime_vtkh_filters.cpp | 51 +++++++++++++++--- src/utilities/replay/replay.cpp | 50 ++++++++++++++++- 4 files changed, 164 insertions(+), 12 deletions(-) diff --git a/src/libs/ascent/runtimes/ascent_data_object.cpp b/src/libs/ascent/runtimes/ascent_data_object.cpp index 1177469cb..32b1265e3 100644 --- a/src/libs/ascent/runtimes/ascent_data_object.cpp +++ b/src/libs/ascent/runtimes/ascent_data_object.cpp @@ -10,6 +10,10 @@ /// file: ascent_data_object.hpp /// //----------------------------------------------------------------------------- +#if defined(ASCENT_MPI_ENABLED) +#include +#endif + #include "ascent_data_object.hpp" #include "ascent_metadata.hpp" @@ -303,10 +307,27 @@ std::shared_ptr DataObject::as_vtkh_collection() } } + int rank = 0; +#if defined(ASCENT_MPI_ENABLED) + MPI_Comm mpi_comm = MPI_Comm_f2c(vtkh::GetMPICommHandle()); + MPI_Comm_rank(mpi_comm, &rank); +#endif + std::stringstream log_name; + std::string log_prefix = "b_to_vh_collection_total_"; + log_name << log_prefix << rank<<".csv"; + std::ofstream stream; + stream.open(log_name.str().c_str(),std::ofstream::app); + conduit::utils::Timer b_to_v_timer; // convert to vtkh std::shared_ptr vtkh_dset(VTKHDataAdapter::BlueprintToVTKHCollection(*to_vtkh, zero_copy)); + float b_to_v_time = b_to_v_timer.elapsed(); + std::stringstream b_to_v_log; + b_to_v_log << "blueprint to vtkh collection total time: " << b_to_v_time << "\n"; + stream << b_to_v_log.str(); + stream.close(); + m_vtkh = vtkh_dset; return m_vtkh; diff --git a/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp b/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp index ebcac7fa6..276b64c39 100644 --- a/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp +++ b/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp @@ -276,6 +276,8 @@ vtkm::cont::Field GetField(const conduit::Node &node, const std::string &topo_str, index_t element_stride, bool zero_copy) + bool zero_copy, + bool compact) { vtkm::CopyFlag copy = vtkm::CopyFlag::On; if(zero_copy) @@ -301,7 +303,6 @@ vtkm::cont::Field GetField(const conduit::Node &node, const T *values_ptr = node.value(); vtkm::cont::Field field; - // base case is naturally stride data if(element_stride == 1) { @@ -538,6 +539,7 @@ VTKHDataAdapter::BlueprintToVTKHCollection(const conduit::Node &n, // We must separate different topologies into // different vtkh data sets + const int num_domains = n.number_of_children(); // if(num_domains == 0) // return nullptr; @@ -549,6 +551,19 @@ VTKHDataAdapter::BlueprintToVTKHCollection(const conduit::Node &n, std::vector allCycles; std::vector allTimes; + int rank = 0; +#if defined(ASCENT_MPI_ENABLED) + MPI_Comm mpi_comm = MPI_Comm_f2c(vtkh::GetMPICommHandle()); + MPI_Comm_rank(mpi_comm, &rank); +#endif + + std::stringstream log_name; + std::string log_prefix = "b_to_vm_dataset_total_"; + log_name << log_prefix << rank<<".csv"; + std::ofstream stream; + stream.open(log_name.str().c_str(),std::ofstream::app); + conduit::utils::Timer b_to_vtkm_ds_timer; + for(int i = 0; i < num_domains; ++i) { const conduit::Node &dom = n.child(i); @@ -572,7 +587,6 @@ VTKHDataAdapter::BlueprintToVTKHCollection(const conduit::Node &n, time = dom["state/time"].to_float64(); allTimes.push_back(time); } - for(int t = 0; t < topo_names.size(); ++t) { const std::string topo_name = topo_names[t]; @@ -580,9 +594,13 @@ VTKHDataAdapter::BlueprintToVTKHCollection(const conduit::Node &n, datasets[topo_name].AddDomain(*dset,domain_id); delete dset; } - } + float b_to_vtkm_ds_time = b_to_vtkm_ds_timer.elapsed(); + std::stringstream b_to_vtkm_ds_log; + b_to_vtkm_ds_log << "blueprint to vtkm dataset time: " << b_to_vtkm_ds_time << "\n"; + stream << b_to_vtkm_ds_log.str(); + //check to make sure there is data to grab if(num_domains > 0) { @@ -663,6 +681,16 @@ VTKHDataAdapter::BlueprintToVTKmDataSet(const Node &node, bool zero_copy, const std::string &topo_name_str) { + int rank = 0; +#if defined(ASCENT_MPI_ENABLED) + MPI_Comm mpi_comm = MPI_Comm_f2c(vtkh::GetMPICommHandle()); + MPI_Comm_rank(mpi_comm, &rank); +#endif + std::stringstream log_name; + std::string log_prefix = "convert_blueprint_to_vtkm_"; + log_name << log_prefix << rank<<".csv"; + std::ofstream stream; + stream.open(log_name.str().c_str(),std::ofstream::app); vtkm::cont::DataSet * result = NULL; std::string topo_name = topo_name_str; @@ -686,12 +714,17 @@ VTKHDataAdapter::BlueprintToVTKmDataSet(const Node &node, if( mesh_type == "uniform") { + conduit::utils::Timer uniform_timer; result = UniformBlueprintToVTKmDataSet(coords_name, n_coords, topo_name, n_topo, neles, nverts); + float uniform_time = uniform_timer.elapsed(); + std::stringstream uniform_log; + uniform_log << "uniform time: " << uniform_time << "\n"; + stream << uniform_log.str(); } else if(mesh_type == "rectilinear") { @@ -716,6 +749,7 @@ VTKHDataAdapter::BlueprintToVTKmDataSet(const Node &node, } else if( mesh_type == "unstructured") { + conduit::utils::Timer unstructured_timer; result = UnstructuredBlueprintToVTKmDataSet(coords_name, n_coords, topo_name, @@ -723,6 +757,10 @@ VTKHDataAdapter::BlueprintToVTKmDataSet(const Node &node, neles, nverts, zero_copy); + float unstructured_time = unstructured_timer.elapsed(); + std::stringstream unstructured_log; + unstructured_log << "unstructured time: " << unstructured_time << "\n"; + stream << unstructured_log.str(); } else { @@ -732,13 +770,15 @@ VTKHDataAdapter::BlueprintToVTKmDataSet(const Node &node, if(node.has_child("fields")) { + conduit::utils::Timer add_field_timer; // add all of the fields: NodeConstIterator itr = node["fields"].children(); + std::string field_name; while(itr.has_next()) { const Node &n_field = itr.next(); - std::string field_name = itr.name(); + field_name = itr.name(); if(n_field["topology"].as_string() != topo_name) { // these are not the fields we are looking for @@ -787,7 +827,12 @@ VTKHDataAdapter::BlueprintToVTKmDataSet(const Node &node, ASCENT_INFO("skipping field "< vtkm_arr; vtkm_arr.Allocate(num_vals); 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 5d50db01b..0e3f75932 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 @@ -1127,29 +1127,61 @@ VTKHGhostStripper::verify_params(const conduit::Node ¶ms, void VTKHGhostStripper::execute() { +#ifdef ASCENT_MPI_ENABLED + int rank; + MPI_Comm mpi_comm = MPI_Comm_f2c(Workspace::default_mpi_comm()); + MPI_Comm_rank(mpi_comm, &rank); + if(rank == 547) + std::cerr << "ENTERING VTKH FILTERS GHOST STRIPPER" << std::endl; +#endif if(!input(0).check_type()) { ASCENT_ERROR("VTKHGhostStripper input must be a data object"); } +#ifdef ASCENT_MPI_ENABLED + if(rank == 547) + std::cerr << "HERE1" << std::endl; +#endif DataObject *data_object = input(0); if(!data_object->is_valid()) { set_output(data_object); return; } +#ifdef ASCENT_MPI_ENABLED + if(rank == 547) + std::cerr << "HERE2" << std::endl; +#endif std::shared_ptr collection = data_object->as_vtkh_collection(); + set_output(data_object); + return; // ask what topology this field is associated with and // get the right data set std::string field_name = params()["field"].as_string(); +#ifdef ASCENT_MPI_ENABLED + if(rank == 547) + std::cerr << "HERE3, fieldname: "<< field_name << std::endl; + if(collection!=NULL && rank==547) + std::cerr << "collection is not null" << std::endl; + +#endif std::string topo_name = collection->field_topology(field_name); +#ifdef ASCENT_MPI_ENABLED + if(rank == 547) + std::cerr << "HERE4" << std::endl; +#endif bool field_exists = topo_name != ""; // Check to see of the ghost field even exists bool do_strip = field_exists; +#ifdef ASCENT_MPI_ENABLED + if(rank == 547) + std::cerr << "HERE5" << std::endl; +#endif if(do_strip) { @@ -1168,23 +1200,28 @@ VTKHGhostStripper::execute() stripper.SetMaxValue(max_val); stripper.SetMinValue(min_val); - stripper.Update(); + //stripper.Update(); - vtkh::DataSet *stripper_output = stripper.GetOutput(); + vtkh::DataSet *stripper_output = new vtkh::DataSet;// = stripper.GetOutput(); // 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(*stripper_output, topo_name); - // re wrap in data object - DataObject *res = new DataObject(new_coll); + //VTKHCollection *new_coll = collection->copy_without_topology(topo_name); + //new_coll->add(*stripper_output, topo_name); + //// re wrap in data object + //DataObject *res = new DataObject(new_coll); delete stripper_output; - set_output(res); + set_output(data_object); + //set_output(res); } else { set_output(data_object); } +#ifdef ASCENT_MPI_ENABLED + if(rank == 547) + std::cerr << "LEAVING VTKH FILTERS GHOST STRIPPER" << std::endl; +#endif } //----------------------------------------------------------------------------- diff --git a/src/utilities/replay/replay.cpp b/src/utilities/replay/replay.cpp index fd9b9b0e6..bddf8810c 100644 --- a/src/utilities/replay/replay.cpp +++ b/src/utilities/replay/replay.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -21,6 +22,50 @@ #include #endif +void copy_imporant_stuff_to_gpu(const conduit::Node &src, + conduit::index_t device_alloc_id, + conduit::Node &dest) +{ + bool has_children = src.number_of_children() > 0; + + if(has_children) + { + conduit::NodeConstIterator itr = src.children(); + while(itr.has_next()) + { + const conduit::Node &src_child = itr.next(); + std::string src_child_name = itr.name(); + if(src.dtype().is_object()) + { + // recurse + copy_imporant_stuff_to_gpu(src_child, device_alloc_id, + dest[src_child_name]); + } + else if(src.dtype().is_list()) // nameless children + { + // recurse + copy_imporant_stuff_to_gpu(src_child,device_alloc_id,dest.append()); + } + else // should be object or list! + { + // YIKES! + } + } + } + else if(src.dtype().is_number() && + src.dtype().number_of_elements() >= 42 ) // this is a crazy heuristic for bigger than small + { + dest.set_allocator(device_alloc_id); + dest.set(src); + } + else // general leaf, just copy + { + dest.set(src); + // or + // dest.set_external(src); + } +} + void usage() { std::cout<<"replay usage:\n"; @@ -217,7 +262,10 @@ int main (int argc, char *argv[]) float load_time = load.elapsed(); flow::Timer publish; - ascent.publish(replay_data); + conduit::Node gpu_replay_data; + copy_imporant_stuff_to_gpu(replay_data,ascent::AllocationManager::conduit_device_allocator_id(),gpu_replay_data); +// ascent.publish(replay_data); + ascent.publish(gpu_replay_data); #ifdef REPLAY_MPI MPI_Barrier(MPI_COMM_WORLD); #endif From 46f74f9473b7b29d68d38a3bb8fa0bcb410a81c7 Mon Sep 17 00:00:00 2001 From: Nicole Marsaglia Date: Thu, 27 Jul 2023 19:32:16 -0400 Subject: [PATCH 19/35] remove merge leftovers --- src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp b/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp index 276b64c39..92192beb8 100644 --- a/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp +++ b/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp @@ -276,8 +276,6 @@ vtkm::cont::Field GetField(const conduit::Node &node, const std::string &topo_str, index_t element_stride, bool zero_copy) - bool zero_copy, - bool compact) { vtkm::CopyFlag copy = vtkm::CopyFlag::On; if(zero_copy) From 716ef54afa9c9f2fc7cd6ff7bfc2047205dc810d Mon Sep 17 00:00:00 2001 From: Nicole Marsaglia Date: Fri, 28 Jul 2023 14:02:21 -0400 Subject: [PATCH 20/35] add ints --- .../runtimes/ascent_vtkh_data_adapter.cpp | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp b/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp index 92192beb8..c682bb395 100644 --- a/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp +++ b/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp @@ -1486,6 +1486,50 @@ VTKHDataAdapter::AddField(const std::string &field_name, supported_type = true; } } + else if(n_vals.dtype().is_int32()) + { + // check that the byte stride is a multiple of native stride + index_t stride = n_vals.dtype().stride(); + index_t element_stride = stride / sizeof(int32); + std::cout << "field 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::GetField(n_vals, + field_name, + assoc_str, + topo_name, + element_stride, + zero_copy)); + supported_type = true; + } + } + else if(n_vals.dtype().is_int64()) + { + // check that the byte stride is a multiple of native stride + index_t stride = n_vals.dtype().stride(); + index_t element_stride = stride / sizeof(int64); + std::cout << "field 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::GetField(n_vals, + field_name, + assoc_str, + topo_name, + element_stride, + zero_copy)); + supported_type = true; + } + } From ef5449c343593a00bdb87c3b1f38a610231f8006 Mon Sep 17 00:00:00 2001 From: Nicole Marsaglia Date: Fri, 28 Jul 2023 19:59:17 -0400 Subject: [PATCH 21/35] start of change explicit coord to use vtkm array handle stride --- .../runtimes/ascent_vtkh_data_adapter.cpp | 275 +++++++++++++++--- 1 file changed, 236 insertions(+), 39 deletions(-) diff --git a/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp b/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp index c682bb395..16405a5dc 100644 --- a/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp +++ b/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp @@ -131,10 +131,27 @@ vtkm::cont::CoordinateSystem GetExplicitCoordinateSystem(const conduit::Node &n_coords, const std::string &name, int &ndims, + index_t &x_element_stride, + index_t &y_element_stride, + index_t &z_element_stride, bool zero_copy) { + int rank = 0; +#if defined(ASCENT_MPI_ENABLED) + MPI_Comm mpi_comm = MPI_Comm_f2c(vtkh::GetMPICommHandle()); + MPI_Comm_rank(mpi_comm, &rank); +#endif + + std::stringstream log_name; + std::string log_prefix = "getExplicitCoordinateSystem_times_"; + log_name << log_prefix << rank<<".csv"; + std::ofstream stream; + stream.open(log_name.str().c_str(),std::ofstream::app); + conduit::utils::Timer explicit_timer; + int nverts = n_coords["values/x"].dtype().number_of_elements(); bool is_interleaved = blueprint::mcarray::is_interleaved(n_coords["values"]); + stream << "is_interleaved bool: " << is_interleaved << "\n"; // some interleaved cases aren't working // disabling this path until we find out what is going wrong. @@ -154,46 +171,146 @@ GetExplicitCoordinateSystem(const conduit::Node &n_coords, // directly use the pointer with vtk-m. // otherwise, we need to compact. - if(is_interleaved || n_coords["values/x"].is_compact()) - { - x_coords_ptr = GetNodePointer(n_coords["values/x"]); + conduit::utils::Timer compact_timer; + //if(is_interleaved || n_coords["values/x"].is_compact()) + //{ + // x_coords_ptr = GetNodePointer(n_coords["values/x"]); + //} + //else + //{ + // n_coords["values/x"].compact_to(n_coords_conv["x"]); + // x_coords_ptr = GetNodePointer(n_coords_conv["x"]); + // // since we had to copy and compact the data, we can't zero copy + // zero_copy = false; + //} + + //if(is_interleaved || n_coords["values/y"].is_compact()) + //{ + // y_coords_ptr = GetNodePointer(n_coords["values/y"]); + //} + //else + //{ + // n_coords["values/y"].compact_to(n_coords_conv["y"]); + // y_coords_ptr = GetNodePointer(n_coords_conv["y"]); + // // since we had to copy and compact the data, we can't zero copy + // zero_copy = false; + //} + + //if(n_coords.has_path("values/z")) + //{ + // ndims = 3; + // if(is_interleaved || n_coords["values/z"].is_compact()) + // { + // z_coords_ptr = GetNodePointer(n_coords["values/z"]); + // } + // else + // { + // n_coords["values/z"].compact_to(n_coords_conv["z"]); + // z_coords_ptr = GetNodePointer(n_coords_conv["z"]); + // // since we had to copy and compact the data, we can't zero copy + // zero_copy = false; + // } + //} + if(x_element_stride == 1 && y_element == 1 && z_element == 1) + { + const T *x_verts_ptr = n_coords["values/x"].value(); + const T *y_verts_ptr = n_coords["values/x"].value(); + const T *z_verts_ptr = n_coords["values/x"].value(); + vtkm::cont::ArrayHandle x_coords_handle = vtkm::cont::make_ArrayHandle(x_verts_ptr, + num_verts, + copy); + vtkm::cont::ArrayHandle y_coords_handle = vtkm::cont::make_ArrayHandle(y_verts_ptr, + num_verts, + copy); + vtkm::cont::ArrayHandle z_coords_handle = vtkm::cont::make_ArrayHandle(z_verts_ptr, + num_verts, + copy); + return vtkm::cont::CoordinateSystem(name, + make_ArrayHandleSOA(x_coords_handle, + y_coords_handle, + z_coords_handle)); } - else + else //TODO: interleaved data { - n_coords["values/x"].compact_to(n_coords_conv["x"]); - x_coords_ptr = GetNodePointer(n_coords_conv["x"]); - // since we had to copy and compact the data, we can't zero copy - zero_copy = false; + int num_verts_expanded = num_verts * x_element_stride; + const T *x_verts_ptr = n_coords["values/x"].value(); + vtkm::cont::ArrayHandle source_array = vtkm::cont::make_ArrayHandle(x_verts_ptr, + num_verts_expanded, + copy); + vtkm::cont::ArrayHandleStride stride_array(source_array, + num_verts, + x_element_stride, + 0); } - if(is_interleaved || n_coords["values/y"].is_compact()) + float compact_time = compact_timer.elapsed(); + std::stringstream compact_log; + compact_log << "compact check & get coords ptr: " << compact_time << "\n"; + stream << compact_log.str(); + + conduit::utils::Timer interleaved_timer; + if(!is_interleaved) { - y_coords_ptr = GetNodePointer(n_coords["values/y"]); + vtkm::cont::ArrayHandle x_coords_handle; + vtkm::cont::ArrayHandle y_coords_handle; + vtkm::cont::ArrayHandle z_coords_handle; + + detail::CopyArray(x_coords_handle, x_coords_ptr, nverts, zero_copy); + return vtkm::cont::CoordinateSystem(name, + make_ArrayHandleSOA(x_coords_handle, + y_coords_handle, + z_coords_handle)); } else { - n_coords["values/y"].compact_to(n_coords_conv["y"]); - y_coords_ptr = GetNodePointer(n_coords_conv["y"]); - // since we had to copy and compact the data, we can't zero copy - zero_copy = false; + int num_verts_expanded = num_verts * x_element_stride; + const T *x_verts_ptr = n_coords["values/x"].value(); + vtkm::cont::ArrayHandle source_array = vtkm::cont::make_ArrayHandle(x_verts_ptr, + num_verts_expanded, + copy); + vtkm::cont::ArrayHandleStride stride_array(source_array, + num_verts, + x_element_stride, + 0); } - if(n_coords.has_path("values/z")) + float compact_time = compact_timer.elapsed(); + std::stringstream compact_log; + compact_log << "compact check & get coords ptr: " << compact_time << "\n"; + stream << compact_log.str(); + + conduit::utils::Timer interleaved_timer; + if(!is_interleaved) { - ndims = 3; - if(is_interleaved || n_coords["values/z"].is_compact()) - { - z_coords_ptr = GetNodePointer(n_coords["values/z"]); - } - else - { - n_coords["values/z"].compact_to(n_coords_conv["z"]); - z_coords_ptr = GetNodePointer(n_coords_conv["z"]); - // since we had to copy and compact the data, we can't zero copy - zero_copy = false; - } + vtkm::cont::ArrayHandle x_coords_handle; + vtkm::cont::ArrayHandle y_coords_handle; + vtkm::cont::ArrayHandle z_coords_handle; + + detail::CopyArray(x_coords_handle, x_coords_ptr, nverts, zero_copy); + return vtkm::cont::CoordinateSystem(name, + make_ArrayHandleSOA(x_coords_handle, + y_coords_handle, + z_coords_handle)); } + else + { + int num_verts_expanded = num_verts * x_element_stride; + const T *x_verts_ptr = n_coords["values/x"].value(); + vtkm::cont::ArrayHandle source_array = vtkm::cont::make_ArrayHandle(x_verts_ptr, + num_verts_expanded, + copy); + vtkm::cont::ArrayHandleStride stride_array(source_array, + num_verts, + x_element_stride, + 0); + } + + float compact_time = compact_timer.elapsed(); + std::stringstream compact_log; + compact_log << "compact check & get coords ptr: " << compact_time << "\n"; + stream << compact_log.str(); + conduit::utils::Timer interleaved_timer; if(!is_interleaved) { vtkm::cont::ArrayHandle x_coords_handle; @@ -214,6 +331,17 @@ GetExplicitCoordinateSystem(const conduit::Node &n_coords, T *z = vtkh::GetVTKMPointer(z_coords_handle); memset(z, 0.0, nverts * sizeof(T)); } + float interleaved_time = interleaved_timer.elapsed(); + std::stringstream interleaved_log; + interleaved_log << "is_interleaved: " << interleaved_time << "\n"; + interleaved_log << "num dims: " << ndims << "\n" << std::endl; + stream << interleaved_log.str(); + + float explicit_time = explicit_timer.elapsed(); + std::stringstream explicit_log; + explicit_log << "GetExplicitCoordinateSystem [TOTAL]: " << explicit_time << "\n"; + stream << explicit_log.str(); + stream.close(); return vtkm::cont::CoordinateSystem(name, make_ArrayHandleSOA(x_coords_handle, @@ -1273,24 +1401,65 @@ VTKHDataAdapter::UnstructuredBlueprintToVTKmDataSet int &nverts, // output, number of verts bool zero_copy) // attempt to zero copy { + int rank = 0; +#if defined(ASCENT_MPI_ENABLED) + MPI_Comm mpi_comm = MPI_Comm_f2c(vtkh::GetMPICommHandle()); + MPI_Comm_rank(mpi_comm, &rank); +#endif + + std::stringstream log_name; + std::string log_prefix = "unstructured_times_"; + log_name << log_prefix << rank<<".csv"; + std::ofstream stream; + stream.open(log_name.str().c_str(),std::ofstream::app); + conduit::utils::Timer unstructured_timer; + vtkm::cont::DataSet *result = new vtkm::cont::DataSet(); nverts = n_coords["values/x"].dtype().number_of_elements(); + conduit::utils::Timer explicit_timer; int32 ndims; vtkm::cont::CoordinateSystem coords; if(n_coords["values/x"].dtype().is_float64()) { + index_t x_stride = n_coords["values/x"].dtype().stride(); + index_t x_element_stride = x_stride / sizeof(float64); + index_t y_stride = n_coords["values/y"].dtype().stride(); + index_t y_element_stride = y_stride / sizeof(float64); + index_t z_element_stride = 0.0; + if(n_coords.has_path("values/z")) + { + index_t z_stride = n_coords["values/z"].dtype().stride(); + index_t z_element_stride = z_stride / sizeof(float64); + } + //todo check stride % float64 == 0 coords = detail::GetExplicitCoordinateSystem(n_coords, coords_name, ndims, + x_element_stride, + y_element_stride, + z_element_stride, zero_copy); } else if(n_coords["values/x"].dtype().is_float32()) { + index_t x_stride = n_coords["values/x"].dtype().stride(); + index_t x_element_stride = x_stride / sizeof(float32); + index_t y_stride = n_coords["values/y"].dtype().stride(); + index_t y_element_stride = y_stride / sizeof(float32); + index_t z_element_stride = 0.0; + if(n_coords.has_path("values/z")) + { + index_t z_stride = n_coords["values/z"].dtype().stride(); + index_t z_element_stride = z_stride / sizeof(float32); + } coords = detail::GetExplicitCoordinateSystem(n_coords, coords_name, ndims, + x_element_stride, + y_element_stride, + z_element_stride, zero_copy); } else @@ -1298,7 +1467,17 @@ VTKHDataAdapter::UnstructuredBlueprintToVTKmDataSet ASCENT_ERROR("Coordinate system must be floating point values"); } + float explicit_time = explicit_timer.elapsed(); + std::stringstream explicit_log; + explicit_log << "blueprint to explicit: " << explicit_time << "\n"; + stream << explicit_log.str(); + + conduit::utils::Timer add_coords_timer; result->AddCoordinateSystem(coords); + float add_coords_time = add_coords_timer.elapsed(); + std::stringstream add_coords_log; + add_coords_log << "result add_coords: " << add_coords_time << "\n"; + stream << add_coords_log.str(); // shapes, number of indices, and connectivity. // Will have to do something different if this is a "zoo" @@ -1316,6 +1495,7 @@ VTKHDataAdapter::UnstructuredBlueprintToVTKmDataSet int conn_size = n_topo_conn.dtype().number_of_elements(); + conduit::utils::Timer connectivity_timer; if( sizeof(vtkm::Id) == 4) { if(n_topo_conn.is_compact() && n_topo_conn.dtype().is_int32()) @@ -1326,6 +1506,7 @@ VTKHDataAdapter::UnstructuredBlueprintToVTKmDataSet else { // convert to int32 + std::cerr << "INT32 unstructured conversion: non zero copy" << std::endl; connectivity.Allocate(conn_size); void *ptr = (void*) vtkh::GetVTKMPointer(connectivity); Node n_tmp; @@ -1343,6 +1524,7 @@ VTKHDataAdapter::UnstructuredBlueprintToVTKmDataSet else { // convert to int64 + std::cerr << "INT64 unstructured conversion: non zero copy" << std::endl; connectivity.Allocate(conn_size); void *ptr = (void*) vtkh::GetVTKMPointer(connectivity); Node n_tmp; @@ -1350,7 +1532,12 @@ VTKHDataAdapter::UnstructuredBlueprintToVTKmDataSet n_topo_conn.to_int64_array(n_tmp); } } + float connectivity_time = connectivity_timer.elapsed(); + std::stringstream connectivity_log; + connectivity_log << "copy connectivity: " << connectivity_time << "\n"; + stream << connectivity_log.str(); + conduit::utils::Timer misc_timer; vtkm::UInt8 shape_id; vtkm::IdComponent indices_per; detail::VTKmCellShape(ele_shape, shape_id, indices_per); @@ -1359,6 +1546,16 @@ VTKHDataAdapter::UnstructuredBlueprintToVTKmDataSet neles = cellset.GetNumberOfCells(); result->SetCellSet(cellset); + float misc_time = misc_timer.elapsed(); + std::stringstream misc_log; + misc_log << "misc as the end: " << misc_time << "\n"; + stream << misc_log.str(); + + float unstructured_time = unstructured_timer.elapsed(); + std::stringstream unstructured_log; + unstructured_log << "blueprint to unstructured [TOTAL]: " << unstructured_time << "\n"; + stream << unstructured_log.str(); + stream.close(); return result; } @@ -1447,9 +1644,9 @@ VTKHDataAdapter::AddField(const std::string &field_name, index_t stride = n_vals.dtype().stride(); index_t element_stride = stride / sizeof(float32); - std::cout << "field name: " << field_name << " " - << " byte stride: " << stride - << " element_stride: " << element_stride << std::endl; + //std::cout << "field 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 ) @@ -1469,9 +1666,9 @@ VTKHDataAdapter::AddField(const std::string &field_name, // check that the byte stride is a multiple of native stride index_t stride = n_vals.dtype().stride(); index_t element_stride = stride / sizeof(float64); - std::cout << "field name: " << field_name << " " - << " byte stride: " << stride - << " element_stride: " << element_stride << std::endl; + //std::cout << "field 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 ) @@ -1491,9 +1688,9 @@ VTKHDataAdapter::AddField(const std::string &field_name, // check that the byte stride is a multiple of native stride index_t stride = n_vals.dtype().stride(); index_t element_stride = stride / sizeof(int32); - std::cout << "field name: " << field_name << " " - << " byte stride: " << stride - << " element_stride: " << element_stride << std::endl; + //std::cout << "field 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 ) @@ -1513,9 +1710,9 @@ VTKHDataAdapter::AddField(const std::string &field_name, // check that the byte stride is a multiple of native stride index_t stride = n_vals.dtype().stride(); index_t element_stride = stride / sizeof(int64); - std::cout << "field name: " << field_name << " " - << " byte stride: " << stride - << " element_stride: " << element_stride << std::endl; + //std::cout << "field 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 ) From 07f13ad02a15a7d3e900c1810973dc6433d85042 Mon Sep 17 00:00:00 2001 From: Nicole Marsaglia Date: Mon, 31 Jul 2023 14:41:53 -0400 Subject: [PATCH 22/35] first swipe at a coords, now to test. --- .../runtimes/ascent_vtkh_data_adapter.cpp | 305 ++++++++++-------- 1 file changed, 167 insertions(+), 138 deletions(-) diff --git a/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp b/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp index 16405a5dc..cc491d6d3 100644 --- a/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp +++ b/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp @@ -30,6 +30,7 @@ // VTKm includes #define VTKM_USE_DOUBLE_PRECISION #include +#include #include #include #include @@ -136,6 +137,11 @@ GetExplicitCoordinateSystem(const conduit::Node &n_coords, index_t &z_element_stride, bool zero_copy) { + vtkm::CopyFlag copy = vtkm::CopyFlag::On; + if(zero_copy) + { + copy = vtkm::CopyFlag::Off; + } int rank = 0; #if defined(ASCENT_MPI_ENABLED) MPI_Comm mpi_comm = MPI_Comm_f2c(vtkh::GetMPICommHandle()); @@ -163,9 +169,9 @@ GetExplicitCoordinateSystem(const conduit::Node &n_coords, // non-interleaved values Node n_coords_conv; - const T* x_coords_ptr = NULL; - const T* y_coords_ptr = NULL; - const T *z_coords_ptr = NULL; + //const T* x_coords_ptr = NULL; + //const T* y_coords_ptr = NULL; + //const T *z_coords_ptr = NULL; // if we are an interleaved mcarray, or compact we can // directly use the pointer with vtk-m. @@ -211,118 +217,79 @@ GetExplicitCoordinateSystem(const conduit::Node &n_coords, // zero_copy = false; // } //} - if(x_element_stride == 1 && y_element == 1 && z_element == 1) + if(x_element_stride == 1 && y_element_stride == 1 && z_element_stride == 1) { const T *x_verts_ptr = n_coords["values/x"].value(); const T *y_verts_ptr = n_coords["values/x"].value(); const T *z_verts_ptr = n_coords["values/x"].value(); - vtkm::cont::ArrayHandle x_coords_handle = vtkm::cont::make_ArrayHandle(x_verts_ptr, - num_verts, + vtkm::cont::ArrayHandle x_coords_handle = vtkm::cont::make_ArrayHandle(x_verts_ptr, + nverts, copy); - vtkm::cont::ArrayHandle y_coords_handle = vtkm::cont::make_ArrayHandle(y_verts_ptr, - num_verts, + vtkm::cont::ArrayHandle y_coords_handle = vtkm::cont::make_ArrayHandle(y_verts_ptr, + nverts, copy); - vtkm::cont::ArrayHandle z_coords_handle = vtkm::cont::make_ArrayHandle(z_verts_ptr, - num_verts, + vtkm::cont::ArrayHandle z_coords_handle = vtkm::cont::make_ArrayHandle(z_verts_ptr, + nverts, copy); + float compact_time = compact_timer.elapsed(); + std::stringstream compact_log; + compact_log << "compact check & get coords ptr: " << compact_time << "\n"; + compact_log << "x_element_stride: " << x_element_stride << "\n"; + compact_log << "y_element_stride: " << y_element_stride << "\n"; + compact_log << "z_element_stride: " << z_element_stride << "\n"; + stream << compact_log.str(); + float explicit_time = explicit_timer.elapsed(); + std::stringstream explicit_log; + explicit_log << "GetExplicitCoordinateSystem [TOTAL]: " << explicit_time << "\n"; + stream << explicit_log.str(); + stream.close(); return vtkm::cont::CoordinateSystem(name, make_ArrayHandleSOA(x_coords_handle, y_coords_handle, z_coords_handle)); } else //TODO: interleaved data - { - int num_verts_expanded = num_verts * x_element_stride; - const T *x_verts_ptr = n_coords["values/x"].value(); - vtkm::cont::ArrayHandle source_array = vtkm::cont::make_ArrayHandle(x_verts_ptr, - num_verts_expanded, - copy); - vtkm::cont::ArrayHandleStride stride_array(source_array, - num_verts, - x_element_stride, - 0); - } - - float compact_time = compact_timer.elapsed(); - std::stringstream compact_log; - compact_log << "compact check & get coords ptr: " << compact_time << "\n"; - stream << compact_log.str(); - - conduit::utils::Timer interleaved_timer; - if(!is_interleaved) { vtkm::cont::ArrayHandle x_coords_handle; vtkm::cont::ArrayHandle y_coords_handle; vtkm::cont::ArrayHandle z_coords_handle; - detail::CopyArray(x_coords_handle, x_coords_ptr, nverts, zero_copy); - return vtkm::cont::CoordinateSystem(name, - make_ArrayHandleSOA(x_coords_handle, - y_coords_handle, - z_coords_handle)); - } - else - { - int num_verts_expanded = num_verts * x_element_stride; + int x_verts_expanded = nverts * x_element_stride; const T *x_verts_ptr = n_coords["values/x"].value(); - vtkm::cont::ArrayHandle source_array = vtkm::cont::make_ArrayHandle(x_verts_ptr, - num_verts_expanded, + vtkm::cont::ArrayHandle x_source_array = vtkm::cont::make_ArrayHandle(x_verts_ptr, + x_verts_expanded, copy); - vtkm::cont::ArrayHandleStride stride_array(source_array, - num_verts, - x_element_stride, - 0); - } - - float compact_time = compact_timer.elapsed(); - std::stringstream compact_log; - compact_log << "compact check & get coords ptr: " << compact_time << "\n"; - stream << compact_log.str(); + vtkm::cont::ArrayHandleStride x_stride_handle(x_source_array, + nverts, + x_element_stride, + 0); - conduit::utils::Timer interleaved_timer; - if(!is_interleaved) - { - vtkm::cont::ArrayHandle x_coords_handle; - vtkm::cont::ArrayHandle y_coords_handle; - vtkm::cont::ArrayHandle z_coords_handle; - - detail::CopyArray(x_coords_handle, x_coords_ptr, nverts, zero_copy); - return vtkm::cont::CoordinateSystem(name, - make_ArrayHandleSOA(x_coords_handle, - y_coords_handle, - z_coords_handle)); - } - else - { - int num_verts_expanded = num_verts * x_element_stride; - const T *x_verts_ptr = n_coords["values/x"].value(); - vtkm::cont::ArrayHandle source_array = vtkm::cont::make_ArrayHandle(x_verts_ptr, - num_verts_expanded, + vtkm::cont::Algorithm::Copy(x_stride_handle, x_coords_handle); + + int y_verts_expanded = nverts * y_element_stride; + const T *y_verts_ptr = n_coords["values/y"].value(); + vtkm::cont::ArrayHandle y_source_array = vtkm::cont::make_ArrayHandle(y_verts_ptr, + y_verts_expanded, copy); - vtkm::cont::ArrayHandleStride stride_array(source_array, - num_verts, - x_element_stride, - 0); - } - - float compact_time = compact_timer.elapsed(); - std::stringstream compact_log; - compact_log << "compact check & get coords ptr: " << compact_time << "\n"; - stream << compact_log.str(); - - conduit::utils::Timer interleaved_timer; - if(!is_interleaved) - { - vtkm::cont::ArrayHandle x_coords_handle; - vtkm::cont::ArrayHandle y_coords_handle; - vtkm::cont::ArrayHandle z_coords_handle; + vtkm::cont::ArrayHandleStride y_stride_handle(y_source_array, + nverts, + y_element_stride, + 0); - detail::CopyArray(x_coords_handle, x_coords_ptr, nverts, zero_copy); - detail::CopyArray(y_coords_handle, y_coords_ptr, nverts, zero_copy); + vtkm::cont::Algorithm::Copy(y_stride_handle, y_coords_handle); if(ndims == 3) { - detail::CopyArray(z_coords_handle, z_coords_ptr, nverts, zero_copy); + int z_verts_expanded = nverts * z_element_stride; + const T *z_verts_ptr = n_coords["values/z"].value(); + vtkm::cont::ArrayHandle z_source_array = vtkm::cont::make_ArrayHandle(z_verts_ptr, + z_verts_expanded, + copy); + vtkm::cont::ArrayHandleStride z_stride_handle(z_source_array, + nverts, + z_element_stride, + 0); + vtkm::cont::Algorithm::Copy(z_stride_handle, z_coords_handle); } else { @@ -331,69 +298,105 @@ GetExplicitCoordinateSystem(const conduit::Node &n_coords, T *z = vtkh::GetVTKMPointer(z_coords_handle); memset(z, 0.0, nverts * sizeof(T)); } - float interleaved_time = interleaved_timer.elapsed(); - std::stringstream interleaved_log; - interleaved_log << "is_interleaved: " << interleaved_time << "\n"; - interleaved_log << "num dims: " << ndims << "\n" << std::endl; - stream << interleaved_log.str(); + float compact_time = compact_timer.elapsed(); + std::stringstream compact_log; + compact_log << "compact check & get coords ptr: " << compact_time << "\n"; + compact_log << "x_element_stride: " << x_element_stride << "\n"; + compact_log << "y_element_stride: " << y_element_stride << "\n"; + compact_log << "z_element_stride: " << z_element_stride << "\n"; + stream << compact_log.str(); float explicit_time = explicit_timer.elapsed(); std::stringstream explicit_log; explicit_log << "GetExplicitCoordinateSystem [TOTAL]: " << explicit_time << "\n"; stream << explicit_log.str(); stream.close(); - return vtkm::cont::CoordinateSystem(name, make_ArrayHandleSOA(x_coords_handle, y_coords_handle, z_coords_handle)); } - else // NOTE: This case is disabled. - { - // we have interleaved coordinates x0,y0,z0,x1,y1,z1... - const T* coords_ptr = GetNodePointer(n_coords["values/x"]); - vtkm::cont::ArrayHandle> coords; - // we cannot zero copy 2D interleaved arrays into vtkm - if(ndims == 3 || true) // TODO: need way to detect 3d interleaved components that has - // only has xy in conduit - { - // this case was failing from Nyx + AMReX - // still haven't been able to reproduce with a simpler test - detail::CopyArray(coords, (vtkm::Vec*)coords_ptr, nverts, zero_copy); - } - else - { - // 2D interleaved array case - vtkm::cont::ArrayHandle x_coords_handle; - vtkm::cont::ArrayHandle y_coords_handle; - vtkm::cont::ArrayHandle z_coords_handle; - - x_coords_handle.Allocate(nverts); - y_coords_handle.Allocate(nverts); - z_coords_handle.Allocate(nverts); - auto x_portal = x_coords_handle.WritePortal(); - auto y_portal = y_coords_handle.WritePortal(); - const T* coords_ptr = GetNodePointer(n_coords["values/x"]); - - T *z = (T*) vtkh::GetVTKMPointer(z_coords_handle); - memset(z, 0.0, nverts * sizeof(T)); - - for(int i = 0; i < nverts; ++i) - { - x_portal.Set(i, coords_ptr[i*2+0]); - y_portal.Set(i, coords_ptr[i*2+1]); - } + //conduit::utils::Timer interleaved_timer; + //if(!is_interleaved) + //{ + // vtkm::cont::ArrayHandle x_coords_handle; + // vtkm::cont::ArrayHandle y_coords_handle; + // vtkm::cont::ArrayHandle z_coords_handle; + + // detail::CopyArray(x_coords_handle, x_coords_ptr, nverts, zero_copy); + // detail::CopyArray(y_coords_handle, y_coords_ptr, nverts, zero_copy); + + // if(ndims == 3) + // { + // detail::CopyArray(z_coords_handle, z_coords_ptr, nverts, zero_copy); + // } + // else + // { + // z_coords_handle.Allocate(nverts); + // // This does not get initialized to zero + // T *z = vtkh::GetVTKMPointer(z_coords_handle); + // memset(z, 0.0, nverts * sizeof(T)); + // } + // float interleaved_time = interleaved_timer.elapsed(); + // std::stringstream interleaved_log; + // interleaved_log << "is_interleaved: " << interleaved_time << "\n"; + // interleaved_log << "num dims: " << ndims << "\n" << std::endl; + // stream << interleaved_log.str(); + + + // return vtkm::cont::CoordinateSystem(name, + // make_ArrayHandleSOA(x_coords_handle, + // y_coords_handle, + // z_coords_handle)); + //} + //else // NOTE: This case is disabled. + //{ + // // we have interleaved coordinates x0,y0,z0,x1,y1,z1... + // const T* coords_ptr = GetNodePointer(n_coords["values/x"]); + // vtkm::cont::ArrayHandle> coords; + // // we cannot zero copy 2D interleaved arrays into vtkm + // if(ndims == 3 || true) // TODO: need way to detect 3d interleaved components that has + // // only has xy in conduit + // { + // // this case was failing from Nyx + AMReX + // // still haven't been able to reproduce with a simpler test + // detail::CopyArray(coords, (vtkm::Vec*)coords_ptr, nverts, zero_copy); + // } + // else + // { + // // 2D interleaved array case + // vtkm::cont::ArrayHandle x_coords_handle; + // vtkm::cont::ArrayHandle y_coords_handle; + // vtkm::cont::ArrayHandle z_coords_handle; + + // x_coords_handle.Allocate(nverts); + // y_coords_handle.Allocate(nverts); + // z_coords_handle.Allocate(nverts); + + // auto x_portal = x_coords_handle.WritePortal(); + // auto y_portal = y_coords_handle.WritePortal(); + + // const T* coords_ptr = GetNodePointer(n_coords["values/x"]); + + // T *z = (T*) vtkh::GetVTKMPointer(z_coords_handle); + // memset(z, 0.0, nverts * sizeof(T)); + + // for(int i = 0; i < nverts; ++i) + // { + // x_portal.Set(i, coords_ptr[i*2+0]); + // y_portal.Set(i, coords_ptr[i*2+1]); + // } - return vtkm::cont::CoordinateSystem(name, - make_ArrayHandleSOA(x_coords_handle, - y_coords_handle, - z_coords_handle)); - } + // return vtkm::cont::CoordinateSystem(name, + // make_ArrayHandleSOA(x_coords_handle, + // y_coords_handle, + // z_coords_handle)); + // } - return vtkm::cont::CoordinateSystem(name, coords); - } + // return vtkm::cont::CoordinateSystem(name, coords); + //} } @@ -1338,16 +1341,42 @@ VTKHDataAdapter::StructuredBlueprintToVTKmDataSet vtkm::cont::CoordinateSystem coords; if(n_coords["values/x"].dtype().is_float64()) { + index_t x_stride = n_coords["values/x"].dtype().stride(); + index_t x_element_stride = x_stride / sizeof(float64); + index_t y_stride = n_coords["values/y"].dtype().stride(); + index_t y_element_stride = y_stride / sizeof(float64); + index_t z_element_stride = 0.0; + if(n_coords.has_path("values/z")) + { + index_t z_stride = n_coords["values/z"].dtype().stride(); + z_element_stride = z_stride / sizeof(float64); + } coords = detail::GetExplicitCoordinateSystem(n_coords, coords_name, ndims, + x_element_stride, + y_element_stride, + z_element_stride, zero_copy); } else if(n_coords["values/x"].dtype().is_float32()) { + index_t x_stride = n_coords["values/x"].dtype().stride(); + index_t x_element_stride = x_stride / sizeof(float32); + index_t y_stride = n_coords["values/y"].dtype().stride(); + index_t y_element_stride = y_stride / sizeof(float32); + index_t z_element_stride = 0.0; + if(n_coords.has_path("values/z")) + { + index_t z_stride = n_coords["values/z"].dtype().stride(); + z_element_stride = z_stride / sizeof(float32); + } coords = detail::GetExplicitCoordinateSystem(n_coords, coords_name, ndims, + x_element_stride, + y_element_stride, + z_element_stride, zero_copy); } else @@ -1431,7 +1460,7 @@ VTKHDataAdapter::UnstructuredBlueprintToVTKmDataSet if(n_coords.has_path("values/z")) { index_t z_stride = n_coords["values/z"].dtype().stride(); - index_t z_element_stride = z_stride / sizeof(float64); + z_element_stride = z_stride / sizeof(float64); } //todo check stride % float64 == 0 coords = detail::GetExplicitCoordinateSystem(n_coords, From 05cea9573fd198a1486c7a81207679f645f29755 Mon Sep 17 00:00:00 2001 From: Nicole Marsaglia Date: Mon, 31 Jul 2023 19:17:32 -0400 Subject: [PATCH 23/35] ascent_vtkh_data_adapter.cpp --- .../ascent_runtime_vtkh_filters.cpp | 34 ------------------- 1 file changed, 34 deletions(-) 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 0e3f75932..512940fb7 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 @@ -1127,33 +1127,18 @@ VTKHGhostStripper::verify_params(const conduit::Node ¶ms, void VTKHGhostStripper::execute() { -#ifdef ASCENT_MPI_ENABLED - int rank; - MPI_Comm mpi_comm = MPI_Comm_f2c(Workspace::default_mpi_comm()); - MPI_Comm_rank(mpi_comm, &rank); - if(rank == 547) - std::cerr << "ENTERING VTKH FILTERS GHOST STRIPPER" << std::endl; -#endif if(!input(0).check_type()) { ASCENT_ERROR("VTKHGhostStripper input must be a data object"); } -#ifdef ASCENT_MPI_ENABLED - if(rank == 547) - std::cerr << "HERE1" << std::endl; -#endif DataObject *data_object = input(0); if(!data_object->is_valid()) { set_output(data_object); return; } -#ifdef ASCENT_MPI_ENABLED - if(rank == 547) - std::cerr << "HERE2" << std::endl; -#endif std::shared_ptr collection = data_object->as_vtkh_collection(); set_output(data_object); return; @@ -1161,27 +1146,12 @@ VTKHGhostStripper::execute() // ask what topology this field is associated with and // get the right data set std::string field_name = params()["field"].as_string(); -#ifdef ASCENT_MPI_ENABLED - if(rank == 547) - std::cerr << "HERE3, fieldname: "<< field_name << std::endl; - if(collection!=NULL && rank==547) - std::cerr << "collection is not null" << std::endl; - -#endif std::string topo_name = collection->field_topology(field_name); -#ifdef ASCENT_MPI_ENABLED - if(rank == 547) - std::cerr << "HERE4" << std::endl; -#endif bool field_exists = topo_name != ""; // Check to see of the ghost field even exists bool do_strip = field_exists; -#ifdef ASCENT_MPI_ENABLED - if(rank == 547) - std::cerr << "HERE5" << std::endl; -#endif if(do_strip) { @@ -1218,10 +1188,6 @@ VTKHGhostStripper::execute() { set_output(data_object); } -#ifdef ASCENT_MPI_ENABLED - if(rank == 547) - std::cerr << "LEAVING VTKH FILTERS GHOST STRIPPER" << std::endl; -#endif } //----------------------------------------------------------------------------- From 1a48615012b12d02bd5d72ef6c7a1cf62d4dc57f Mon Sep 17 00:00:00 2001 From: Nicole Marsaglia Date: Tue, 1 Aug 2023 16:47:24 -0400 Subject: [PATCH 24/35] back to working and clean --- .../ascent/runtimes/ascent_data_object.cpp | 17 --- .../runtimes/ascent_vtkh_data_adapter.cpp | 121 ------------------ 2 files changed, 138 deletions(-) diff --git a/src/libs/ascent/runtimes/ascent_data_object.cpp b/src/libs/ascent/runtimes/ascent_data_object.cpp index 32b1265e3..bb7bc53e9 100644 --- a/src/libs/ascent/runtimes/ascent_data_object.cpp +++ b/src/libs/ascent/runtimes/ascent_data_object.cpp @@ -307,27 +307,10 @@ std::shared_ptr DataObject::as_vtkh_collection() } } - int rank = 0; -#if defined(ASCENT_MPI_ENABLED) - MPI_Comm mpi_comm = MPI_Comm_f2c(vtkh::GetMPICommHandle()); - MPI_Comm_rank(mpi_comm, &rank); -#endif - std::stringstream log_name; - std::string log_prefix = "b_to_vh_collection_total_"; - log_name << log_prefix << rank<<".csv"; - std::ofstream stream; - stream.open(log_name.str().c_str(),std::ofstream::app); - conduit::utils::Timer b_to_v_timer; // convert to vtkh std::shared_ptr vtkh_dset(VTKHDataAdapter::BlueprintToVTKHCollection(*to_vtkh, zero_copy)); - float b_to_v_time = b_to_v_timer.elapsed(); - std::stringstream b_to_v_log; - b_to_v_log << "blueprint to vtkh collection total time: " << b_to_v_time << "\n"; - stream << b_to_v_log.str(); - stream.close(); - m_vtkh = vtkh_dset; return m_vtkh; diff --git a/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp b/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp index cc491d6d3..4adab8503 100644 --- a/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp +++ b/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp @@ -142,22 +142,9 @@ GetExplicitCoordinateSystem(const conduit::Node &n_coords, { copy = vtkm::CopyFlag::Off; } - int rank = 0; -#if defined(ASCENT_MPI_ENABLED) - MPI_Comm mpi_comm = MPI_Comm_f2c(vtkh::GetMPICommHandle()); - MPI_Comm_rank(mpi_comm, &rank); -#endif - std::stringstream log_name; - std::string log_prefix = "getExplicitCoordinateSystem_times_"; - log_name << log_prefix << rank<<".csv"; - std::ofstream stream; - stream.open(log_name.str().c_str(),std::ofstream::app); - conduit::utils::Timer explicit_timer; - int nverts = n_coords["values/x"].dtype().number_of_elements(); bool is_interleaved = blueprint::mcarray::is_interleaved(n_coords["values"]); - stream << "is_interleaved bool: " << is_interleaved << "\n"; // some interleaved cases aren't working // disabling this path until we find out what is going wrong. @@ -177,7 +164,6 @@ GetExplicitCoordinateSystem(const conduit::Node &n_coords, // directly use the pointer with vtk-m. // otherwise, we need to compact. - conduit::utils::Timer compact_timer; //if(is_interleaved || n_coords["values/x"].is_compact()) //{ // x_coords_ptr = GetNodePointer(n_coords["values/x"]); @@ -231,18 +217,6 @@ GetExplicitCoordinateSystem(const conduit::Node &n_coords, vtkm::cont::ArrayHandle z_coords_handle = vtkm::cont::make_ArrayHandle(z_verts_ptr, nverts, copy); - float compact_time = compact_timer.elapsed(); - std::stringstream compact_log; - compact_log << "compact check & get coords ptr: " << compact_time << "\n"; - compact_log << "x_element_stride: " << x_element_stride << "\n"; - compact_log << "y_element_stride: " << y_element_stride << "\n"; - compact_log << "z_element_stride: " << z_element_stride << "\n"; - stream << compact_log.str(); - float explicit_time = explicit_timer.elapsed(); - std::stringstream explicit_log; - explicit_log << "GetExplicitCoordinateSystem [TOTAL]: " << explicit_time << "\n"; - stream << explicit_log.str(); - stream.close(); return vtkm::cont::CoordinateSystem(name, make_ArrayHandleSOA(x_coords_handle, y_coords_handle, @@ -299,18 +273,6 @@ GetExplicitCoordinateSystem(const conduit::Node &n_coords, memset(z, 0.0, nverts * sizeof(T)); } - float compact_time = compact_timer.elapsed(); - std::stringstream compact_log; - compact_log << "compact check & get coords ptr: " << compact_time << "\n"; - compact_log << "x_element_stride: " << x_element_stride << "\n"; - compact_log << "y_element_stride: " << y_element_stride << "\n"; - compact_log << "z_element_stride: " << z_element_stride << "\n"; - stream << compact_log.str(); - float explicit_time = explicit_timer.elapsed(); - std::stringstream explicit_log; - explicit_log << "GetExplicitCoordinateSystem [TOTAL]: " << explicit_time << "\n"; - stream << explicit_log.str(); - stream.close(); return vtkm::cont::CoordinateSystem(name, make_ArrayHandleSOA(x_coords_handle, y_coords_handle, @@ -680,19 +642,6 @@ VTKHDataAdapter::BlueprintToVTKHCollection(const conduit::Node &n, std::vector allCycles; std::vector allTimes; - int rank = 0; -#if defined(ASCENT_MPI_ENABLED) - MPI_Comm mpi_comm = MPI_Comm_f2c(vtkh::GetMPICommHandle()); - MPI_Comm_rank(mpi_comm, &rank); -#endif - - std::stringstream log_name; - std::string log_prefix = "b_to_vm_dataset_total_"; - log_name << log_prefix << rank<<".csv"; - std::ofstream stream; - stream.open(log_name.str().c_str(),std::ofstream::app); - conduit::utils::Timer b_to_vtkm_ds_timer; - for(int i = 0; i < num_domains; ++i) { const conduit::Node &dom = n.child(i); @@ -725,11 +674,6 @@ VTKHDataAdapter::BlueprintToVTKHCollection(const conduit::Node &n, } } - float b_to_vtkm_ds_time = b_to_vtkm_ds_timer.elapsed(); - std::stringstream b_to_vtkm_ds_log; - b_to_vtkm_ds_log << "blueprint to vtkm dataset time: " << b_to_vtkm_ds_time << "\n"; - stream << b_to_vtkm_ds_log.str(); - //check to make sure there is data to grab if(num_domains > 0) { @@ -810,16 +754,6 @@ VTKHDataAdapter::BlueprintToVTKmDataSet(const Node &node, bool zero_copy, const std::string &topo_name_str) { - int rank = 0; -#if defined(ASCENT_MPI_ENABLED) - MPI_Comm mpi_comm = MPI_Comm_f2c(vtkh::GetMPICommHandle()); - MPI_Comm_rank(mpi_comm, &rank); -#endif - std::stringstream log_name; - std::string log_prefix = "convert_blueprint_to_vtkm_"; - log_name << log_prefix << rank<<".csv"; - std::ofstream stream; - stream.open(log_name.str().c_str(),std::ofstream::app); vtkm::cont::DataSet * result = NULL; std::string topo_name = topo_name_str; @@ -843,17 +777,12 @@ VTKHDataAdapter::BlueprintToVTKmDataSet(const Node &node, if( mesh_type == "uniform") { - conduit::utils::Timer uniform_timer; result = UniformBlueprintToVTKmDataSet(coords_name, n_coords, topo_name, n_topo, neles, nverts); - float uniform_time = uniform_timer.elapsed(); - std::stringstream uniform_log; - uniform_log << "uniform time: " << uniform_time << "\n"; - stream << uniform_log.str(); } else if(mesh_type == "rectilinear") { @@ -878,7 +807,6 @@ VTKHDataAdapter::BlueprintToVTKmDataSet(const Node &node, } else if( mesh_type == "unstructured") { - conduit::utils::Timer unstructured_timer; result = UnstructuredBlueprintToVTKmDataSet(coords_name, n_coords, topo_name, @@ -886,10 +814,6 @@ VTKHDataAdapter::BlueprintToVTKmDataSet(const Node &node, neles, nverts, zero_copy); - float unstructured_time = unstructured_timer.elapsed(); - std::stringstream unstructured_log; - unstructured_log << "unstructured time: " << unstructured_time << "\n"; - stream << unstructured_log.str(); } else { @@ -899,7 +823,6 @@ VTKHDataAdapter::BlueprintToVTKmDataSet(const Node &node, if(node.has_child("fields")) { - conduit::utils::Timer add_field_timer; // add all of the fields: NodeConstIterator itr = node["fields"].children(); std::string field_name; @@ -956,12 +879,7 @@ VTKHDataAdapter::BlueprintToVTKmDataSet(const Node &node, ASCENT_INFO("skipping field "<AddCoordinateSystem(coords); - float add_coords_time = add_coords_timer.elapsed(); - std::stringstream add_coords_log; - add_coords_log << "result add_coords: " << add_coords_time << "\n"; - stream << add_coords_log.str(); // shapes, number of indices, and connectivity. // Will have to do something different if this is a "zoo" @@ -1524,7 +1419,6 @@ VTKHDataAdapter::UnstructuredBlueprintToVTKmDataSet int conn_size = n_topo_conn.dtype().number_of_elements(); - conduit::utils::Timer connectivity_timer; if( sizeof(vtkm::Id) == 4) { if(n_topo_conn.is_compact() && n_topo_conn.dtype().is_int32()) @@ -1561,12 +1455,7 @@ VTKHDataAdapter::UnstructuredBlueprintToVTKmDataSet n_topo_conn.to_int64_array(n_tmp); } } - float connectivity_time = connectivity_timer.elapsed(); - std::stringstream connectivity_log; - connectivity_log << "copy connectivity: " << connectivity_time << "\n"; - stream << connectivity_log.str(); - conduit::utils::Timer misc_timer; vtkm::UInt8 shape_id; vtkm::IdComponent indices_per; detail::VTKmCellShape(ele_shape, shape_id, indices_per); @@ -1575,16 +1464,6 @@ VTKHDataAdapter::UnstructuredBlueprintToVTKmDataSet neles = cellset.GetNumberOfCells(); result->SetCellSet(cellset); - float misc_time = misc_timer.elapsed(); - std::stringstream misc_log; - misc_log << "misc as the end: " << misc_time << "\n"; - stream << misc_log.str(); - - float unstructured_time = unstructured_timer.elapsed(); - std::stringstream unstructured_log; - unstructured_log << "blueprint to unstructured [TOTAL]: " << unstructured_time << "\n"; - stream << unstructured_log.str(); - stream.close(); return result; } From 73ffef9d26cd0e30726a44940f7efd5e1ea5976d Mon Sep 17 00:00:00 2001 From: Nicole Marsaglia Date: Tue, 1 Aug 2023 17:29:25 -0400 Subject: [PATCH 25/35] 2d logic --- .../runtimes/ascent_vtkh_data_adapter.cpp | 279 ++++++------------ 1 file changed, 83 insertions(+), 196 deletions(-) diff --git a/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp b/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp index 4adab8503..399b66e06 100644 --- a/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp +++ b/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp @@ -144,90 +144,27 @@ GetExplicitCoordinateSystem(const conduit::Node &n_coords, } int nverts = n_coords["values/x"].dtype().number_of_elements(); - bool is_interleaved = blueprint::mcarray::is_interleaved(n_coords["values"]); + //bool is_interleaved = blueprint::mcarray::is_interleaved(n_coords["values"]); // some interleaved cases aren't working // disabling this path until we find out what is going wrong. - is_interleaved = false; + //is_interleaved = false; + + vtkm::cont::ArrayHandle x_coords_handle; + vtkm::cont::ArrayHandle y_coords_handle; + vtkm::cont::ArrayHandle z_coords_handle; ndims = 2; - // n_coords_conv holds contig data if we have stride-ed but - // non-interleaved values - Node n_coords_conv; - - //const T* x_coords_ptr = NULL; - //const T* y_coords_ptr = NULL; - //const T *z_coords_ptr = NULL; - - // if we are an interleaved mcarray, or compact we can - // directly use the pointer with vtk-m. - // otherwise, we need to compact. - - //if(is_interleaved || n_coords["values/x"].is_compact()) - //{ - // x_coords_ptr = GetNodePointer(n_coords["values/x"]); - //} - //else - //{ - // n_coords["values/x"].compact_to(n_coords_conv["x"]); - // x_coords_ptr = GetNodePointer(n_coords_conv["x"]); - // // since we had to copy and compact the data, we can't zero copy - // zero_copy = false; - //} - - //if(is_interleaved || n_coords["values/y"].is_compact()) - //{ - // y_coords_ptr = GetNodePointer(n_coords["values/y"]); - //} - //else - //{ - // n_coords["values/y"].compact_to(n_coords_conv["y"]); - // y_coords_ptr = GetNodePointer(n_coords_conv["y"]); - // // since we had to copy and compact the data, we can't zero copy - // zero_copy = false; - //} - - //if(n_coords.has_path("values/z")) - //{ - // ndims = 3; - // if(is_interleaved || n_coords["values/z"].is_compact()) - // { - // z_coords_ptr = GetNodePointer(n_coords["values/z"]); - // } - // else - // { - // n_coords["values/z"].compact_to(n_coords_conv["z"]); - // z_coords_ptr = GetNodePointer(n_coords_conv["z"]); - // // since we had to copy and compact the data, we can't zero copy - // zero_copy = false; - // } - //} - if(x_element_stride == 1 && y_element_stride == 1 && z_element_stride == 1) + if(x_element_stride == 1) { const T *x_verts_ptr = n_coords["values/x"].value(); - const T *y_verts_ptr = n_coords["values/x"].value(); - const T *z_verts_ptr = n_coords["values/x"].value(); - vtkm::cont::ArrayHandle x_coords_handle = vtkm::cont::make_ArrayHandle(x_verts_ptr, - nverts, - copy); - vtkm::cont::ArrayHandle y_coords_handle = vtkm::cont::make_ArrayHandle(y_verts_ptr, - nverts, - copy); - vtkm::cont::ArrayHandle z_coords_handle = vtkm::cont::make_ArrayHandle(z_verts_ptr, - nverts, - copy); - return vtkm::cont::CoordinateSystem(name, - make_ArrayHandleSOA(x_coords_handle, - y_coords_handle, - z_coords_handle)); + x_coords_handle = vtkm::cont::make_ArrayHandle(x_verts_ptr, + nverts, + copy); } - else //TODO: interleaved data + else { - vtkm::cont::ArrayHandle x_coords_handle; - vtkm::cont::ArrayHandle y_coords_handle; - vtkm::cont::ArrayHandle z_coords_handle; - int x_verts_expanded = nverts * x_element_stride; const T *x_verts_ptr = n_coords["values/x"].value(); vtkm::cont::ArrayHandle x_source_array = vtkm::cont::make_ArrayHandle(x_verts_ptr, @@ -239,7 +176,17 @@ GetExplicitCoordinateSystem(const conduit::Node &n_coords, 0); vtkm::cont::Algorithm::Copy(x_stride_handle, x_coords_handle); - + } + + if(y_element_stride == 1) + { + const T *y_verts_ptr = n_coords["values/y"].value(); + y_coords_handle = vtkm::cont::make_ArrayHandle(y_verts_ptr, + nverts, + copy); + } + else + { int y_verts_expanded = nverts * y_element_stride; const T *y_verts_ptr = n_coords["values/y"].value(); vtkm::cont::ArrayHandle y_source_array = vtkm::cont::make_ArrayHandle(y_verts_ptr, @@ -251,114 +198,43 @@ GetExplicitCoordinateSystem(const conduit::Node &n_coords, 0); vtkm::cont::Algorithm::Copy(y_stride_handle, y_coords_handle); + } - if(ndims == 3) - { - int z_verts_expanded = nverts * z_element_stride; - const T *z_verts_ptr = n_coords["values/z"].value(); - vtkm::cont::ArrayHandle z_source_array = vtkm::cont::make_ArrayHandle(z_verts_ptr, - z_verts_expanded, - copy); - vtkm::cont::ArrayHandleStride z_stride_handle(z_source_array, - nverts, - z_element_stride, - 0); - vtkm::cont::Algorithm::Copy(z_stride_handle, z_coords_handle); - } - else - { - z_coords_handle.Allocate(nverts); - // This does not get initialized to zero - T *z = vtkh::GetVTKMPointer(z_coords_handle); - memset(z, 0.0, nverts * sizeof(T)); - } + if(z_element_stride == 0) + { + z_coords_handle.Allocate(nverts); + // This does not get initialized to zero + T *z = vtkh::GetVTKMPointer(z_coords_handle); + memset(z, 0.0, nverts * sizeof(T)); + } + else if(z_element_stride == 1) + { + ndims = 3; + const T *z_verts_ptr = n_coords["values/z"].value(); + z_coords_handle = vtkm::cont::make_ArrayHandle(z_verts_ptr, + nverts, + copy); + } + else + { + ndims = 3; + int z_verts_expanded = nverts * z_element_stride; + const T *z_verts_ptr = n_coords["values/z"].value(); + vtkm::cont::ArrayHandle z_source_array = vtkm::cont::make_ArrayHandle(z_verts_ptr, + z_verts_expanded, + copy); + vtkm::cont::ArrayHandleStride z_stride_handle(z_source_array, + nverts, + z_element_stride, + 0); + + vtkm::cont::Algorithm::Copy(z_stride_handle, z_coords_handle); + } - return vtkm::cont::CoordinateSystem(name, - make_ArrayHandleSOA(x_coords_handle, - y_coords_handle, - z_coords_handle)); - } - - - //conduit::utils::Timer interleaved_timer; - //if(!is_interleaved) - //{ - // vtkm::cont::ArrayHandle x_coords_handle; - // vtkm::cont::ArrayHandle y_coords_handle; - // vtkm::cont::ArrayHandle z_coords_handle; - - // detail::CopyArray(x_coords_handle, x_coords_ptr, nverts, zero_copy); - // detail::CopyArray(y_coords_handle, y_coords_ptr, nverts, zero_copy); - - // if(ndims == 3) - // { - // detail::CopyArray(z_coords_handle, z_coords_ptr, nverts, zero_copy); - // } - // else - // { - // z_coords_handle.Allocate(nverts); - // // This does not get initialized to zero - // T *z = vtkh::GetVTKMPointer(z_coords_handle); - // memset(z, 0.0, nverts * sizeof(T)); - // } - // float interleaved_time = interleaved_timer.elapsed(); - // std::stringstream interleaved_log; - // interleaved_log << "is_interleaved: " << interleaved_time << "\n"; - // interleaved_log << "num dims: " << ndims << "\n" << std::endl; - // stream << interleaved_log.str(); - - - // return vtkm::cont::CoordinateSystem(name, - // make_ArrayHandleSOA(x_coords_handle, - // y_coords_handle, - // z_coords_handle)); - //} - //else // NOTE: This case is disabled. - //{ - // // we have interleaved coordinates x0,y0,z0,x1,y1,z1... - // const T* coords_ptr = GetNodePointer(n_coords["values/x"]); - // vtkm::cont::ArrayHandle> coords; - // // we cannot zero copy 2D interleaved arrays into vtkm - // if(ndims == 3 || true) // TODO: need way to detect 3d interleaved components that has - // // only has xy in conduit - // { - // // this case was failing from Nyx + AMReX - // // still haven't been able to reproduce with a simpler test - // detail::CopyArray(coords, (vtkm::Vec*)coords_ptr, nverts, zero_copy); - // } - // else - // { - // // 2D interleaved array case - // vtkm::cont::ArrayHandle x_coords_handle; - // vtkm::cont::ArrayHandle y_coords_handle; - // vtkm::cont::ArrayHandle z_coords_handle; - - // x_coords_handle.Allocate(nverts); - // y_coords_handle.Allocate(nverts); - // z_coords_handle.Allocate(nverts); - - // auto x_portal = x_coords_handle.WritePortal(); - // auto y_portal = y_coords_handle.WritePortal(); - - // const T* coords_ptr = GetNodePointer(n_coords["values/x"]); - - // T *z = (T*) vtkh::GetVTKMPointer(z_coords_handle); - // memset(z, 0.0, nverts * sizeof(T)); - - // for(int i = 0; i < nverts; ++i) - // { - // x_portal.Set(i, coords_ptr[i*2+0]); - // y_portal.Set(i, coords_ptr[i*2+1]); - // } - - // return vtkm::cont::CoordinateSystem(name, - // make_ArrayHandleSOA(x_coords_handle, - // y_coords_handle, - // z_coords_handle)); - // } - - // return vtkm::cont::CoordinateSystem(name, coords); - //} + return vtkm::cont::CoordinateSystem(name, + make_ArrayHandleSOA(x_coords_handle, + y_coords_handle, + z_coords_handle)); } @@ -1367,14 +1243,19 @@ VTKHDataAdapter::UnstructuredBlueprintToVTKmDataSet index_t z_stride = n_coords["values/z"].dtype().stride(); z_element_stride = z_stride / sizeof(float64); } - //todo check stride % float64 == 0 - coords = detail::GetExplicitCoordinateSystem(n_coords, - coords_name, - ndims, - x_element_stride, - y_element_stride, - z_element_stride, - zero_copy); + //TODO: + //can we assume all by checking one? + //or check ystride & zstride % float64 == 0? + if(x_stride % sizeof(float64) == 0) + { + coords = detail::GetExplicitCoordinateSystem(n_coords, + coords_name, + ndims, + x_element_stride, + y_element_stride, + z_element_stride, + zero_copy); + } } else if(n_coords["values/x"].dtype().is_float32()) { @@ -1388,13 +1269,19 @@ VTKHDataAdapter::UnstructuredBlueprintToVTKmDataSet index_t z_stride = n_coords["values/z"].dtype().stride(); index_t z_element_stride = z_stride / sizeof(float32); } - coords = detail::GetExplicitCoordinateSystem(n_coords, - coords_name, - ndims, - x_element_stride, - y_element_stride, - z_element_stride, - zero_copy); + //TODO: + //can we assume all by checking one? + //or check ystride & zstride % float64 == 0? + if(x_stride % sizeof(float32)) + { + coords = detail::GetExplicitCoordinateSystem(n_coords, + coords_name, + ndims, + x_element_stride, + y_element_stride, + z_element_stride, + zero_copy); + } } else { From fba3451f732ffb251a558143e65419c1a89e4fa6 Mon Sep 17 00:00:00 2001 From: Nicole Marsaglia Date: Tue, 1 Aug 2023 17:35:26 -0400 Subject: [PATCH 26/35] this seems more right --- .../ascent/runtimes/ascent_vtkh_data_adapter.cpp | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp b/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp index 399b66e06..482bc1b7c 100644 --- a/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp +++ b/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp @@ -159,9 +159,7 @@ GetExplicitCoordinateSystem(const conduit::Node &n_coords, if(x_element_stride == 1) { const T *x_verts_ptr = n_coords["values/x"].value(); - x_coords_handle = vtkm::cont::make_ArrayHandle(x_verts_ptr, - nverts, - copy); + detail::CopyArray(x_coords_handle, x_verts_ptr, nverts, zero_copy); } else { @@ -181,9 +179,7 @@ GetExplicitCoordinateSystem(const conduit::Node &n_coords, if(y_element_stride == 1) { const T *y_verts_ptr = n_coords["values/y"].value(); - y_coords_handle = vtkm::cont::make_ArrayHandle(y_verts_ptr, - nverts, - copy); + detail::CopyArray(y_coords_handle, y_verts_ptr, nverts, zero_copy); } else { @@ -211,9 +207,7 @@ GetExplicitCoordinateSystem(const conduit::Node &n_coords, { ndims = 3; const T *z_verts_ptr = n_coords["values/z"].value(); - z_coords_handle = vtkm::cont::make_ArrayHandle(z_verts_ptr, - nverts, - copy); + detail::CopyArray(z_coords_handle, z_verts_ptr, nverts, zero_copy); } else { From 53c2208de95a1fc7cc18be13646fe1355319005f Mon Sep 17 00:00:00 2001 From: Nicole Marsaglia Date: Tue, 8 Aug 2023 18:32:32 -0400 Subject: [PATCH 27/35] let's finish our if statement kthxbye -- fixes nyx --- src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp b/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp index 482bc1b7c..6f333a7d7 100644 --- a/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp +++ b/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp @@ -1266,7 +1266,7 @@ VTKHDataAdapter::UnstructuredBlueprintToVTKmDataSet //TODO: //can we assume all by checking one? //or check ystride & zstride % float64 == 0? - if(x_stride % sizeof(float32)) + if(x_stride % sizeof(float32) == 0) { coords = detail::GetExplicitCoordinateSystem(n_coords, coords_name, From 0581ee7a6d94e31e615d74692a836c3ceb7dbe72 Mon Sep 17 00:00:00 2001 From: Nicole Marsaglia Date: Thu, 10 Aug 2023 19:11:33 -0400 Subject: [PATCH 28/35] these need to go back to original --- .../ascent_runtime_vtkh_filters.cpp | 17 +++---- src/utilities/replay/replay.cpp | 50 +------------------ 2 files changed, 8 insertions(+), 59 deletions(-) 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 512940fb7..5d50db01b 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 @@ -1140,8 +1140,6 @@ VTKHGhostStripper::execute() return; } std::shared_ptr collection = data_object->as_vtkh_collection(); - set_output(data_object); - return; // ask what topology this field is associated with and // get the right data set @@ -1170,19 +1168,18 @@ VTKHGhostStripper::execute() stripper.SetMaxValue(max_val); stripper.SetMinValue(min_val); - //stripper.Update(); + stripper.Update(); - vtkh::DataSet *stripper_output = new vtkh::DataSet;// = stripper.GetOutput(); + vtkh::DataSet *stripper_output = stripper.GetOutput(); // 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(*stripper_output, topo_name); - //// re wrap in data object - //DataObject *res = new DataObject(new_coll); + VTKHCollection *new_coll = collection->copy_without_topology(topo_name); + new_coll->add(*stripper_output, topo_name); + // re wrap in data object + DataObject *res = new DataObject(new_coll); delete stripper_output; - set_output(data_object); - //set_output(res); + set_output(res); } else { diff --git a/src/utilities/replay/replay.cpp b/src/utilities/replay/replay.cpp index bddf8810c..fd9b9b0e6 100644 --- a/src/utilities/replay/replay.cpp +++ b/src/utilities/replay/replay.cpp @@ -13,7 +13,6 @@ #include #include #include -#include #include #include @@ -22,50 +21,6 @@ #include #endif -void copy_imporant_stuff_to_gpu(const conduit::Node &src, - conduit::index_t device_alloc_id, - conduit::Node &dest) -{ - bool has_children = src.number_of_children() > 0; - - if(has_children) - { - conduit::NodeConstIterator itr = src.children(); - while(itr.has_next()) - { - const conduit::Node &src_child = itr.next(); - std::string src_child_name = itr.name(); - if(src.dtype().is_object()) - { - // recurse - copy_imporant_stuff_to_gpu(src_child, device_alloc_id, - dest[src_child_name]); - } - else if(src.dtype().is_list()) // nameless children - { - // recurse - copy_imporant_stuff_to_gpu(src_child,device_alloc_id,dest.append()); - } - else // should be object or list! - { - // YIKES! - } - } - } - else if(src.dtype().is_number() && - src.dtype().number_of_elements() >= 42 ) // this is a crazy heuristic for bigger than small - { - dest.set_allocator(device_alloc_id); - dest.set(src); - } - else // general leaf, just copy - { - dest.set(src); - // or - // dest.set_external(src); - } -} - void usage() { std::cout<<"replay usage:\n"; @@ -262,10 +217,7 @@ int main (int argc, char *argv[]) float load_time = load.elapsed(); flow::Timer publish; - conduit::Node gpu_replay_data; - copy_imporant_stuff_to_gpu(replay_data,ascent::AllocationManager::conduit_device_allocator_id(),gpu_replay_data); -// ascent.publish(replay_data); - ascent.publish(gpu_replay_data); + ascent.publish(replay_data); #ifdef REPLAY_MPI MPI_Barrier(MPI_COMM_WORLD); #endif From 24ac645d0ca9e2ace47d5555466af2923c6e58e0 Mon Sep 17 00:00:00 2001 From: Nicole Date: Thu, 10 Aug 2023 16:13:48 -0700 Subject: [PATCH 29/35] Update ascent_data_object.cpp remove mpi header from debugging --- src/libs/ascent/runtimes/ascent_data_object.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/libs/ascent/runtimes/ascent_data_object.cpp b/src/libs/ascent/runtimes/ascent_data_object.cpp index bb7bc53e9..1177469cb 100644 --- a/src/libs/ascent/runtimes/ascent_data_object.cpp +++ b/src/libs/ascent/runtimes/ascent_data_object.cpp @@ -10,10 +10,6 @@ /// file: ascent_data_object.hpp /// //----------------------------------------------------------------------------- -#if defined(ASCENT_MPI_ENABLED) -#include -#endif - #include "ascent_data_object.hpp" #include "ascent_metadata.hpp" From b7e6bd00e014172c55c44676153b4bcaf9d96846 Mon Sep 17 00:00:00 2001 From: Cyrus Harrison Date: Tue, 7 Nov 2023 16:16:26 -0800 Subject: [PATCH 30/35] port to new interfaces --- .../ascent_blueprint_device_dispatch.hpp | 32 +++++++++---------- .../ascent_blueprint_device_reductions.cpp | 4 +-- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/libs/ascent/runtimes/expressions/ascent_blueprint_device_dispatch.hpp b/src/libs/ascent/runtimes/expressions/ascent_blueprint_device_dispatch.hpp index 50378d24f..7142fe551 100644 --- a/src/libs/ascent/runtimes/expressions/ascent_blueprint_device_dispatch.hpp +++ b/src/libs/ascent/runtimes/expressions/ascent_blueprint_device_dispatch.hpp @@ -513,10 +513,10 @@ dispatch_memory_binary_df(const conduit::Node &l_field, r_field.schema().to_string()); } - MemoryInterface l_farray(l_field); - MemoryInterface r_farray(r_field); - MemoryAccessor l_accessor = l_farray.accessor(mem_space, component); - MemoryAccessor r_accessor = r_farray.accessor(mem_space, component); + MCArray l_farray(l_field); + MCArray r_farray(r_field); + DeviceAccessor l_accessor = l_farray.accessor(mem_space, component); + DeviceAccessor r_accessor = r_farray.accessor(mem_space, component); res = func(l_accessor, r_accessor, exec); } @@ -530,10 +530,10 @@ dispatch_memory_binary_df(const conduit::Node &l_field, r_field.schema().to_string()); } - MemoryInterface l_farray(l_field); - MemoryInterface r_farray(r_field); - MemoryAccessor l_accessor = l_farray.accessor(mem_space, component); - MemoryAccessor r_accessor = r_farray.accessor(mem_space, component); + MCArray l_farray(l_field); + MCArray r_farray(r_field); + DeviceAccessor l_accessor = l_farray.accessor(mem_space, component); + DeviceAccessor r_accessor = r_farray.accessor(mem_space, component); res = func(l_accessor, r_accessor, exec); } else if(field_is_int32(l_field)) @@ -546,10 +546,10 @@ dispatch_memory_binary_df(const conduit::Node &l_field, r_field.schema().to_string()); } - MemoryInterface l_farray(l_field); - MemoryInterface r_farray(r_field); - MemoryAccessor l_accessor = l_farray.accessor(mem_space, component); - MemoryAccessor r_accessor = r_farray.accessor(mem_space, component); + MCArray l_farray(l_field); + MCArray r_farray(r_field); + DeviceAccessor l_accessor = l_farray.accessor(mem_space, component); + DeviceAccessor r_accessor = r_farray.accessor(mem_space, component); res = func(l_accessor, r_accessor, exec); } else if(field_is_int64(l_field)) @@ -563,10 +563,10 @@ dispatch_memory_binary_df(const conduit::Node &l_field, r_field.schema().to_string()); } - MemoryInterface l_farray(l_field); - MemoryInterface r_farray(r_field); - MemoryAccessor l_accessor = l_farray.accessor(mem_space, component); - MemoryAccessor r_accessor = r_farray.accessor(mem_space, component); + MCArray l_farray(l_field); + MCArray r_farray(r_field); + DeviceAccessor l_accessor = l_farray.accessor(mem_space, component); + DeviceAccessor r_accessor = r_farray.accessor(mem_space, component); res = func(l_accessor, r_accessor, exec); } else diff --git a/src/libs/ascent/runtimes/expressions/ascent_blueprint_device_reductions.cpp b/src/libs/ascent/runtimes/expressions/ascent_blueprint_device_reductions.cpp index 34cb35264..697d864f6 100644 --- a/src/libs/ascent/runtimes/expressions/ascent_blueprint_device_reductions.cpp +++ b/src/libs/ascent/runtimes/expressions/ascent_blueprint_device_reductions.cpp @@ -144,8 +144,8 @@ struct SumFunctor struct DFAddFunctor { template - conduit::Node operator()(const MemoryAccessor l_accessor, - const MemoryAccessor r_accessor, + conduit::Node operator()(const DeviceAccessor l_accessor, + const DeviceAccessor r_accessor, const Exec &) const { From 9222dea2c5fbe7aff43a8d95a765a973ba7ce6e6 Mon Sep 17 00:00:00 2001 From: Cyrus Harrison Date: Tue, 7 Nov 2023 16:59:27 -0800 Subject: [PATCH 31/35] adaptor logic update --- .../runtimes/ascent_vtkh_data_adapter.cpp | 44 +++++++++++++++---- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp b/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp index 4a7cb92a4..1eaa6cae6 100644 --- a/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp +++ b/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp @@ -132,9 +132,9 @@ vtkm::cont::CoordinateSystem GetExplicitCoordinateSystem(const conduit::Node &n_coords, const std::string &name, int &ndims, - index_t &x_element_stride, - index_t &y_element_stride, - index_t &z_element_stride, + index_t &x_element_stride, + index_t &y_element_stride, + index_t &z_element_stride, bool zero_copy) { vtkm::CopyFlag copy = vtkm::CopyFlag::On; @@ -1152,9 +1152,9 @@ VTKHDataAdapter::StructuredBlueprintToVTKmDataSet coords = detail::GetExplicitCoordinateSystem(n_coords, coords_name, ndims, - x_element_stride, - y_element_stride, - z_element_stride, + x_element_stride, + y_element_stride, + z_element_stride, zero_copy); } else if(n_coords["values/x"].dtype().is_float32()) @@ -1172,9 +1172,9 @@ VTKHDataAdapter::StructuredBlueprintToVTKmDataSet coords = detail::GetExplicitCoordinateSystem(n_coords, coords_name, ndims, - x_element_stride, - y_element_stride, - z_element_stride, + x_element_stride, + y_element_stride, + z_element_stride, zero_copy); } else @@ -1234,16 +1234,42 @@ VTKHDataAdapter::PointsImplicitBlueprintToVTKmDataSet vtkm::cont::CoordinateSystem coords; if(n_coords["values/x"].dtype().is_float64()) { + index_t x_stride = n_coords["values/x"].dtype().stride(); + index_t x_element_stride = x_stride / sizeof(float64); + index_t y_stride = n_coords["values/y"].dtype().stride(); + index_t y_element_stride = y_stride / sizeof(float64); + index_t z_element_stride = 0.0; + if(n_coords.has_path("values/z")) + { + index_t z_stride = n_coords["values/z"].dtype().stride(); + z_element_stride = z_stride / sizeof(float64); + } coords = detail::GetExplicitCoordinateSystem(n_coords, coords_name, ndims, + x_element_stride, + y_element_stride, + z_element_stride, zero_copy); } else if(n_coords["values/x"].dtype().is_float32()) { + index_t x_stride = n_coords["values/x"].dtype().stride(); + index_t x_element_stride = x_stride / sizeof(float32); + index_t y_stride = n_coords["values/y"].dtype().stride(); + index_t y_element_stride = y_stride / sizeof(float32); + index_t z_element_stride = 0.0; + if(n_coords.has_path("values/z")) + { + index_t z_stride = n_coords["values/z"].dtype().stride(); + z_element_stride = z_stride / sizeof(float32); + } coords = detail::GetExplicitCoordinateSystem(n_coords, coords_name, ndims, + x_element_stride, + y_element_stride, + z_element_stride, zero_copy); } else From d93b340dc394ba44562053aa77c4884c2bcd3a86 Mon Sep 17 00:00:00 2001 From: Cyrus Harrison Date: Wed, 8 Nov 2023 12:31:46 -0800 Subject: [PATCH 32/35] use proper node as mcarray input --- .../ascent_blueprint_device_dispatch.hpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/libs/ascent/runtimes/expressions/ascent_blueprint_device_dispatch.hpp b/src/libs/ascent/runtimes/expressions/ascent_blueprint_device_dispatch.hpp index 7142fe551..53fabec04 100644 --- a/src/libs/ascent/runtimes/expressions/ascent_blueprint_device_dispatch.hpp +++ b/src/libs/ascent/runtimes/expressions/ascent_blueprint_device_dispatch.hpp @@ -513,8 +513,8 @@ dispatch_memory_binary_df(const conduit::Node &l_field, r_field.schema().to_string()); } - MCArray l_farray(l_field); - MCArray r_farray(r_field); + MCArray l_farray(l_field["values"]); + MCArray r_farray(r_field["values"]); DeviceAccessor l_accessor = l_farray.accessor(mem_space, component); DeviceAccessor r_accessor = r_farray.accessor(mem_space, component); res = func(l_accessor, r_accessor, exec); @@ -530,8 +530,8 @@ dispatch_memory_binary_df(const conduit::Node &l_field, r_field.schema().to_string()); } - MCArray l_farray(l_field); - MCArray r_farray(r_field); + MCArray l_farray(l_field["values"]); + MCArray r_farray(r_field["values"]); DeviceAccessor l_accessor = l_farray.accessor(mem_space, component); DeviceAccessor r_accessor = r_farray.accessor(mem_space, component); res = func(l_accessor, r_accessor, exec); @@ -546,8 +546,8 @@ dispatch_memory_binary_df(const conduit::Node &l_field, r_field.schema().to_string()); } - MCArray l_farray(l_field); - MCArray r_farray(r_field); + MCArray l_farray(l_field["values"]); + MCArray r_farray(r_field["values"]); DeviceAccessor l_accessor = l_farray.accessor(mem_space, component); DeviceAccessor r_accessor = r_farray.accessor(mem_space, component); res = func(l_accessor, r_accessor, exec); @@ -563,8 +563,8 @@ dispatch_memory_binary_df(const conduit::Node &l_field, r_field.schema().to_string()); } - MCArray l_farray(l_field); - MCArray r_farray(r_field); + MCArray l_farray(l_field["values"]); + MCArray r_farray(r_field["values"]); DeviceAccessor l_accessor = l_farray.accessor(mem_space, component); DeviceAccessor r_accessor = r_farray.accessor(mem_space, component); res = func(l_accessor, r_accessor, exec); From d4b3aa2dcb20469132448a51cdeb59276b10955c Mon Sep 17 00:00:00 2001 From: Cyrus Harrison Date: Fri, 10 Nov 2023 09:39:08 -0800 Subject: [PATCH 33/35] add some more debugging output --- .../ascent/runtimes/ascent_vtkh_data_adapter.cpp | 2 +- src/tests/ascent/t_ascent_render_3d.cpp | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp b/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp index 1eaa6cae6..22b31cd06 100644 --- a/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp +++ b/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp @@ -1615,7 +1615,7 @@ VTKHDataAdapter::AddField(const std::string &field_name, // use float64 by default if(!supported_type) { - std::cerr << "WE ARE IN UNSUPPORTED DATA TYPE" << std::endl; + std::cerr << "WE ARE IN UNSUPPORTED DATA TYPE" << 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); diff --git a/src/tests/ascent/t_ascent_render_3d.cpp b/src/tests/ascent/t_ascent_render_3d.cpp index 833ef1f95..502d766c9 100644 --- a/src/tests/ascent/t_ascent_render_3d.cpp +++ b/src/tests/ascent/t_ascent_render_3d.cpp @@ -2108,6 +2108,8 @@ TEST(ascent_render_3d, test_render_3d_supported_field_dtypes) ASCENT_INFO("Testing 3D Rendering of fields with different data types"); + std::cout << std::endl; + int num_vals = data["fields/braid/values"].dtype().number_of_elements(); // // Create the actions. @@ -2132,6 +2134,7 @@ TEST(ascent_render_3d, test_render_3d_supported_field_dtypes) // int 8 { + std::cout << "braid_int8" << std::endl; string output_file = conduit::utils::join_file_path(output_path, "tout_render_3d_braid_int8"); // remove old images before rendering @@ -2152,6 +2155,7 @@ TEST(ascent_render_3d, test_render_3d_supported_field_dtypes) } // int 16 { + std::cout << "braid_int16" << std::endl; string output_file = conduit::utils::join_file_path(output_path, "tout_render_3d_braid_int16"); // remove old images before rendering @@ -2171,6 +2175,7 @@ TEST(ascent_render_3d, test_render_3d_supported_field_dtypes) // int 32 { + std::cout << "braid_int32" << std::endl; string output_file = conduit::utils::join_file_path(output_path, "tout_render_3d_braid_int32"); // remove old images before rendering @@ -2191,6 +2196,7 @@ TEST(ascent_render_3d, test_render_3d_supported_field_dtypes) } // int 64 { + std::cout << "braid_int64" << std::endl; string output_file = conduit::utils::join_file_path(output_path, "tout_render_3d_braid_int64"); // remove old images before rendering @@ -2213,6 +2219,7 @@ TEST(ascent_render_3d, test_render_3d_supported_field_dtypes) // uint 8 { + std::cout << "braid_uint8" << std::endl; string output_file = conduit::utils::join_file_path(output_path, "tout_render_3d_braid_uint8"); // remove old images before rendering @@ -2233,6 +2240,7 @@ TEST(ascent_render_3d, test_render_3d_supported_field_dtypes) } // uint 16 { + std::cout << "braid_uint16" << std::endl; string output_file = conduit::utils::join_file_path(output_path, "tout_render_3d_braid_uint16"); // remove old images before rendering @@ -2252,6 +2260,7 @@ TEST(ascent_render_3d, test_render_3d_supported_field_dtypes) // uint 32 { + std::cout << "braid_uint32" << std::endl; string output_file = conduit::utils::join_file_path(output_path, "tout_render_3d_braid_uint32"); // remove old images before rendering @@ -2272,6 +2281,7 @@ TEST(ascent_render_3d, test_render_3d_supported_field_dtypes) } // uint 64 { + std::cout << "braid_uint64" << std::endl; string output_file = conduit::utils::join_file_path(output_path, "tout_render_3d_braid_uint64"); // remove old images before rendering @@ -2294,6 +2304,7 @@ TEST(ascent_render_3d, test_render_3d_supported_field_dtypes) // float 32 { + std::cout << "braid_float32" << std::endl; string output_file = conduit::utils::join_file_path(output_path, "tout_render_3d_braid_float32"); // remove old images before rendering @@ -2314,6 +2325,7 @@ TEST(ascent_render_3d, test_render_3d_supported_field_dtypes) // float 64 { + std::cout << "braid_float64" << std::endl; string output_file = conduit::utils::join_file_path(output_path, "tout_render_3d_braid_float64"); // remove old images before rendering From 6cd6d7a6e90bc3593b14a2f11986c1a6bf2299b2 Mon Sep 17 00:00:00 2001 From: Cyrus Harrison Date: Wed, 6 Dec 2023 12:10:46 -0800 Subject: [PATCH 34/35] fix for fields vs non vtk-m supported type --- .../runtimes/ascent_vtkh_data_adapter.cpp | 145 +++++++++--------- src/tests/ascent/t_ascent_render_3d.cpp | 9 +- 2 files changed, 77 insertions(+), 77 deletions(-) diff --git a/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp b/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp index 22b31cd06..3b2065512 100644 --- a/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp +++ b/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp @@ -232,6 +232,7 @@ GetExplicitCoordinateSystem(const conduit::Node &n_coords, } + template vtkm::cont::Field GetField(const conduit::Node &node, const std::string &field_name, @@ -280,9 +281,9 @@ vtkm::cont::Field GetField(const conduit::Node &node, // use ArrayHandleStride to create new field // - // NOTE: In this case, the num_vals, needs to be + // NOTE: In this case, the num_vals, needs to be // the full extent of the strided area3 - + int num_vals_expanded = num_vals * element_stride; vtkm::cont::ArrayHandle source_array = vtkm::cont::make_ArrayHandle(values_ptr, num_vals_expanded, @@ -1402,7 +1403,7 @@ VTKHDataAdapter::UnstructuredBlueprintToVTKmDataSet else { // convert to int32 - std::cerr << "INT32 unstructured conversion: non zero copy" << std::endl; + // std::cout << "INT32 unstructured conversion: non zero copy" << std::endl; connectivity.Allocate(conn_size); void *ptr = (void*) vtkh::GetVTKMPointer(connectivity); Node n_tmp; @@ -1420,7 +1421,7 @@ VTKHDataAdapter::UnstructuredBlueprintToVTKmDataSet else { // convert to int64 - std::cerr << "INT64 unstructured conversion: non zero copy" << std::endl; + // std::cout << "INT64 unstructured conversion: non zero copy" << std::endl; connectivity.Allocate(conn_size); void *ptr = (void*) vtkh::GetVTKMPointer(connectivity); Node n_tmp; @@ -1501,21 +1502,6 @@ VTKHDataAdapter::AddField(const std::string &field_name, { bool supported_type = false; - // if(n_vals.is_compact()) - // { - // // we compile vtk-h with fp types - // if(n_vals.dtype().is_float32()) - // { - // dset->AddField(detail::GetField(n_vals, field_name, assoc_str, topo_name, zero_copy)); - // supported_type = true; - // } - // else if(n_vals.dtype().is_float64()) - // { - // dset->AddField(detail::GetField(n_vals, field_name, assoc_str, topo_name, zero_copy)); - // supported_type = true; - // } - // } - // vtk-m can stride as long as the strides are a multiple of the native stride // we compile vtk-h with fp types @@ -1524,15 +1510,15 @@ VTKHDataAdapter::AddField(const std::string &field_name, // check that the byte stride is a multiple of native stride index_t stride = n_vals.dtype().stride(); index_t element_stride = stride / sizeof(float32); - + //std::cout << "field name: " << field_name << " " // << " byte stride: " << stride // << " element_stride: " << element_stride << std::endl; - // if element_stride is evenly divided by native, we are good to + // 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 + // in this case we can use a strided array handle dset->AddField(detail::GetField(n_vals, field_name, assoc_str, @@ -1543,18 +1529,18 @@ VTKHDataAdapter::AddField(const std::string &field_name, } } else if(n_vals.dtype().is_float64()) - { + { // check that the byte stride is a multiple of native stride index_t stride = n_vals.dtype().stride(); index_t element_stride = stride / sizeof(float64); //std::cout << "field name: " << field_name << " " // << " byte stride: " << stride // << " element_stride: " << element_stride << std::endl; - // if element_stride is evenly divided by native, we are good to + // 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 + // in this case we can use a strided array handle dset->AddField(detail::GetField(n_vals, field_name, assoc_str, @@ -1564,62 +1550,72 @@ VTKHDataAdapter::AddField(const std::string &field_name, supported_type = true; } } - else if(n_vals.dtype().is_int32()) - { - // check that the byte stride is a multiple of native stride - index_t stride = n_vals.dtype().stride(); - index_t element_stride = stride / sizeof(int32); - //std::cout << "field 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::GetField(n_vals, - field_name, - assoc_str, - topo_name, - element_stride, - zero_copy)); - supported_type = true; - } - } - else if(n_vals.dtype().is_int64()) - { - // check that the byte stride is a multiple of native stride - index_t stride = n_vals.dtype().stride(); - index_t element_stride = stride / sizeof(int64); - //std::cout << "field 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::GetField(n_vals, - 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_vals.dtype().is_int32()) + // { + // // check that the byte stride is a multiple of native stride + // index_t stride = n_vals.dtype().stride(); + // index_t element_stride = stride / sizeof(int32); + // //std::cout << "field 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::GetField(n_vals, + // field_name, + // assoc_str, + // topo_name, + // element_stride, + // zero_copy)); + // supported_type = true; + // } + // } + // else if(n_vals.dtype().is_int64()) + // { + // // check that the byte stride is a multiple of native stride + // index_t stride = n_vals.dtype().stride(); + // index_t element_stride = stride / sizeof(int64); + // //std::cout << "field 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::GetField(n_vals, + // 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::cerr << "WE ARE IN UNSUPPORTED DATA TYPE" << std::endl; + // std::cout << "WE ARE IN UNSUPPORTED DATA TYPE: " + // << n_vals.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); @@ -1639,6 +1635,11 @@ VTKHDataAdapter::AddField(const std::string &field_name, vtkm_arr)); } } + // else + // { + // std::cout << "SUPPORTED DATA TYPE: " + // << n_vals.dtype().name() << std::endl; + // } } catch (vtkm::cont::Error error) { diff --git a/src/tests/ascent/t_ascent_render_3d.cpp b/src/tests/ascent/t_ascent_render_3d.cpp index 502d766c9..21eddc332 100644 --- a/src/tests/ascent/t_ascent_render_3d.cpp +++ b/src/tests/ascent/t_ascent_render_3d.cpp @@ -141,7 +141,6 @@ TEST(ascent_render_3d, test_render_3d_original_bounds) // remove old images before rendering remove_test_image(output_file); - // // Create the actions. // @@ -2643,7 +2642,7 @@ TEST(ascent_render_3d, test_render_3d_extreme_extents) Ascent ascent; ascent.open(); ascent.publish(mesh); - + scenes["s1/image_prefix"] = output_file; ascent.execute(actions); // check that we created an image @@ -2679,11 +2678,11 @@ TEST(ascent_render_3d, test_render_3d_extreme_extents) // check that we created an image // TODO: We expect this to fail until we address float64 vs float32 issues EXPECT_FALSE(check_test_image(output_file)); - + //now with unstructured: Node mesh_unstruct; - + conduit::blueprint::mesh::topology::rectilinear::to_unstructured(mesh["topologies/topo"], mesh_unstruct["topologies/topo"], mesh_unstruct["coordsets/coords"]); @@ -2712,7 +2711,7 @@ TEST(ascent_render_3d, test_render_3d_extreme_extents) // TODO: We expect this to fail until we address float64 vs float32 issues EXPECT_FALSE(check_test_image(output_file)); - + } From 728a977a8b2df38f5735fd5256aeb4c919918734 Mon Sep 17 00:00:00 2001 From: Cyrus Harrison Date: Wed, 6 Dec 2023 12:46:15 -0800 Subject: [PATCH 35/35] fix with one of the zstride coords calcs, simplify ascent render poly test cases --- .../runtimes/ascent_vtkh_data_adapter.cpp | 53 +++++++++-------- src/tests/ascent/t_ascent_render_2d_poly.cpp | 58 ++++++------------- src/tests/ascent/t_ascent_render_3d_poly.cpp | 37 ++++-------- 3 files changed, 57 insertions(+), 91 deletions(-) diff --git a/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp b/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp index 3b2065512..87f8915c6 100644 --- a/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp +++ b/src/libs/ascent/runtimes/ascent_vtkh_data_adapter.cpp @@ -166,12 +166,12 @@ GetExplicitCoordinateSystem(const conduit::Node &n_coords, int x_verts_expanded = nverts * x_element_stride; const T *x_verts_ptr = n_coords["values/x"].value(); vtkm::cont::ArrayHandle x_source_array = vtkm::cont::make_ArrayHandle(x_verts_ptr, - x_verts_expanded, - copy); + x_verts_expanded, + copy); vtkm::cont::ArrayHandleStride x_stride_handle(x_source_array, - nverts, - x_element_stride, - 0); + nverts, + x_element_stride, + 0); // offset vtkm::cont::Algorithm::Copy(x_stride_handle, x_coords_handle); } @@ -186,12 +186,12 @@ GetExplicitCoordinateSystem(const conduit::Node &n_coords, int y_verts_expanded = nverts * y_element_stride; const T *y_verts_ptr = n_coords["values/y"].value(); vtkm::cont::ArrayHandle y_source_array = vtkm::cont::make_ArrayHandle(y_verts_ptr, - y_verts_expanded, - copy); + y_verts_expanded, + copy); vtkm::cont::ArrayHandleStride y_stride_handle(y_source_array, - nverts, - y_element_stride, - 0); + nverts, + y_element_stride, + 0); // offset vtkm::cont::Algorithm::Copy(y_stride_handle, y_coords_handle); } @@ -199,9 +199,10 @@ GetExplicitCoordinateSystem(const conduit::Node &n_coords, if(z_element_stride == 0) { z_coords_handle.Allocate(nverts); + // TODO: Set on device? // This does not get initialized to zero T *z = vtkh::GetVTKMPointer(z_coords_handle); - memset(z, 0.0, nverts * sizeof(T)); + memset(z, 0, nverts * sizeof(T)); } else if(z_element_stride == 1) { @@ -215,12 +216,12 @@ GetExplicitCoordinateSystem(const conduit::Node &n_coords, int z_verts_expanded = nverts * z_element_stride; const T *z_verts_ptr = n_coords["values/z"].value(); vtkm::cont::ArrayHandle z_source_array = vtkm::cont::make_ArrayHandle(z_verts_ptr, - z_verts_expanded, - copy); + z_verts_expanded, + copy); vtkm::cont::ArrayHandleStride z_stride_handle(z_source_array, - nverts, - z_element_stride, - 0); + nverts, + z_element_stride, + 0); // offset vtkm::cont::Algorithm::Copy(z_stride_handle, z_coords_handle); } @@ -1144,12 +1145,13 @@ VTKHDataAdapter::StructuredBlueprintToVTKmDataSet index_t x_element_stride = x_stride / sizeof(float64); index_t y_stride = n_coords["values/y"].dtype().stride(); index_t y_element_stride = y_stride / sizeof(float64); - index_t z_element_stride = 0.0; + index_t z_element_stride = 0; if(n_coords.has_path("values/z")) { index_t z_stride = n_coords["values/z"].dtype().stride(); z_element_stride = z_stride / sizeof(float64); } + coords = detail::GetExplicitCoordinateSystem(n_coords, coords_name, ndims, @@ -1164,12 +1166,13 @@ VTKHDataAdapter::StructuredBlueprintToVTKmDataSet index_t x_element_stride = x_stride / sizeof(float32); index_t y_stride = n_coords["values/y"].dtype().stride(); index_t y_element_stride = y_stride / sizeof(float32); - index_t z_element_stride = 0.0; + index_t z_element_stride = 0; if(n_coords.has_path("values/z")) { index_t z_stride = n_coords["values/z"].dtype().stride(); z_element_stride = z_stride / sizeof(float32); } + coords = detail::GetExplicitCoordinateSystem(n_coords, coords_name, ndims, @@ -1239,12 +1242,13 @@ VTKHDataAdapter::PointsImplicitBlueprintToVTKmDataSet index_t x_element_stride = x_stride / sizeof(float64); index_t y_stride = n_coords["values/y"].dtype().stride(); index_t y_element_stride = y_stride / sizeof(float64); - index_t z_element_stride = 0.0; + index_t z_element_stride = 0; if(n_coords.has_path("values/z")) { index_t z_stride = n_coords["values/z"].dtype().stride(); z_element_stride = z_stride / sizeof(float64); } + coords = detail::GetExplicitCoordinateSystem(n_coords, coords_name, ndims, @@ -1259,12 +1263,13 @@ VTKHDataAdapter::PointsImplicitBlueprintToVTKmDataSet index_t x_element_stride = x_stride / sizeof(float32); index_t y_stride = n_coords["values/y"].dtype().stride(); index_t y_element_stride = y_stride / sizeof(float32); - index_t z_element_stride = 0.0; + index_t z_element_stride = 0; if(n_coords.has_path("values/z")) { index_t z_stride = n_coords["values/z"].dtype().stride(); z_element_stride = z_stride / sizeof(float32); } + coords = detail::GetExplicitCoordinateSystem(n_coords, coords_name, ndims, @@ -1324,12 +1329,13 @@ VTKHDataAdapter::UnstructuredBlueprintToVTKmDataSet index_t x_element_stride = x_stride / sizeof(float64); index_t y_stride = n_coords["values/y"].dtype().stride(); index_t y_element_stride = y_stride / sizeof(float64); - index_t z_element_stride = 0.0; + index_t z_element_stride = 0; if(n_coords.has_path("values/z")) { index_t z_stride = n_coords["values/z"].dtype().stride(); z_element_stride = z_stride / sizeof(float64); } + //TODO: //can we assume all by checking one? //or check ystride & zstride % float64 == 0? @@ -1350,12 +1356,13 @@ VTKHDataAdapter::UnstructuredBlueprintToVTKmDataSet index_t x_element_stride = x_stride / sizeof(float32); index_t y_stride = n_coords["values/y"].dtype().stride(); index_t y_element_stride = y_stride / sizeof(float32); - index_t z_element_stride = 0.0; + index_t z_element_stride = 0; if(n_coords.has_path("values/z")) { index_t z_stride = n_coords["values/z"].dtype().stride(); - index_t z_element_stride = z_stride / sizeof(float32); + z_element_stride = z_stride / sizeof(float32); } + //TODO: //can we assume all by checking one? //or check ystride & zstride % float64 == 0? diff --git a/src/tests/ascent/t_ascent_render_2d_poly.cpp b/src/tests/ascent/t_ascent_render_2d_poly.cpp index f17eb9c5c..b2ed38c87 100644 --- a/src/tests/ascent/t_ascent_render_2d_poly.cpp +++ b/src/tests/ascent/t_ascent_render_2d_poly.cpp @@ -53,15 +53,14 @@ TEST(ascent_pipeline, test_render_2d_poly) // // Create the actions. // - conduit::Node scenes; - scenes["s1/plots/p1/type"] = "pseudocolor"; - scenes["s1/plots/p1/field"] = "level"; - scenes["s1/image_prefix"] = output_file; conduit::Node actions; conduit::Node &add_plots = actions.append(); add_plots["action"] = "add_scenes"; - add_plots["scenes"] = scenes; + conduit::Node &scenes = add_plots["scenes"]; + scenes["s1/plots/p1/type"] = "pseudocolor"; + scenes["s1/plots/p1/field"] = "level"; + scenes["s1/image_prefix"] = output_file; actions.print(); // @@ -69,16 +68,9 @@ TEST(ascent_pipeline, test_render_2d_poly) // Ascent ascent; - - Node ascent_opts; - Node ascent_info; - ascent_opts["runtime/type"] = "ascent"; - ascent.open(ascent_opts); + ascent.open(); ascent.publish(data); ascent.execute(actions); - ascent.info(ascent_info); - EXPECT_EQ(ascent_info["runtime/type"].as_string(), "ascent"); - ascent_info.print(); ascent.close(); // @@ -134,32 +126,23 @@ TEST(ascent_pipeline, test_render_2d_poly_multi) // // Create the actions. // - conduit::Node scenes; - scenes["s1/plots/p1/type"] = "pseudocolor"; - scenes["s1/plots/p1/field"] = "level"; - scenes["s1/image_prefix"] = output_file; conduit::Node actions; conduit::Node &add_plots = actions.append(); add_plots["action"] = "add_scenes"; - add_plots["scenes"] = scenes; + conduit::Node &scenes = add_plots["scenes"]; + scenes["s1/plots/p1/type"] = "pseudocolor"; + scenes["s1/plots/p1/field"] = "level"; + scenes["s1/image_prefix"] = output_file; actions.print(); // // Run Ascent // - Ascent ascent; - - Node ascent_opts; - Node ascent_info; - ascent_opts["runtime/type"] = "ascent"; - ascent.open(ascent_opts); + ascent.open(); ascent.publish(root); ascent.execute(actions); - ascent.info(ascent_info); - EXPECT_EQ(ascent_info["runtime/type"].as_string(), "ascent"); - ascent_info.print(); ascent.close(); // @@ -209,18 +192,18 @@ TEST(ascent_pipeline, test_render_2d_poly_and_nonpoly) // // Create the actions. // - conduit::Node scenes; + + + conduit::Node actions; + conduit::Node &add_plots = actions.append(); + add_plots["action"] = "add_scenes"; + conduit::Node &scenes = add_plots["scenes"]; scenes["s1/plots/p1/type"] = "pseudocolor"; scenes["s1/plots/p1/field"] = "level"; scenes["s1/image_prefix"] = output_file + "polytess"; scenes["s2/plots/p1/type"] = "pseudocolor"; scenes["s2/plots/p1/field"] = "braid"; scenes["s2/image_prefix"] = output_file + "braid"; - - conduit::Node actions; - conduit::Node &add_plots = actions.append(); - add_plots["action"] = "add_scenes"; - add_plots["scenes"] = scenes; actions.print(); // @@ -228,16 +211,9 @@ TEST(ascent_pipeline, test_render_2d_poly_and_nonpoly) // Ascent ascent; - - Node ascent_opts; - Node ascent_info; - ascent_opts["runtime/type"] = "ascent"; - ascent.open(ascent_opts); + ascent.open(); ascent.publish(data); ascent.execute(actions); - ascent.info(ascent_info); - EXPECT_EQ(ascent_info["runtime/type"].as_string(), "ascent"); - ascent_info.print(); ascent.close(); // diff --git a/src/tests/ascent/t_ascent_render_3d_poly.cpp b/src/tests/ascent/t_ascent_render_3d_poly.cpp index 008b3670c..28321ff32 100644 --- a/src/tests/ascent/t_ascent_render_3d_poly.cpp +++ b/src/tests/ascent/t_ascent_render_3d_poly.cpp @@ -40,7 +40,6 @@ TEST(ascent_pipeline, test_render_3d_poly) index_t length = 10; conduit::blueprint::mesh::examples::polychain(length, data); - EXPECT_TRUE(conduit::blueprint::mesh::verify(data, verify_info)); string output_path = prepare_output_dir(); @@ -52,15 +51,14 @@ TEST(ascent_pipeline, test_render_3d_poly) // // Create the actions. // - conduit::Node scenes; + conduit::Node actions; + conduit::Node &add_plots = actions.append(); + add_plots["action"] = "add_scenes"; + conduit::Node &scenes = add_plots["scenes"]; scenes["s1/plots/p1/type"] = "pseudocolor"; scenes["s1/plots/p1/field"] = "chain"; scenes["s1/image_prefix"] = output_file; - conduit::Node actions; - conduit::Node &add_plots = actions.append(); - add_plots["action"] = "add_scenes"; - add_plots["scenes"] = scenes; actions.print(); // @@ -68,18 +66,10 @@ TEST(ascent_pipeline, test_render_3d_poly) // Ascent ascent; - - Node ascent_opts; - Node ascent_info; - ascent_opts["runtime/type"] = "ascent"; - ascent.open(ascent_opts); + ascent.open(); ascent.publish(data); ascent.execute(actions); - ascent.info(ascent_info); - EXPECT_EQ(ascent_info["runtime/type"].as_string(), "ascent"); - ascent_info.print(); ascent.close(); - // // // check that we created an image EXPECT_TRUE(check_test_image(output_file, 0.001f, "0")); @@ -134,15 +124,13 @@ TEST(ascent_pipeline, test_render_3d_poly_multi) // // Create the actions. // - conduit::Node scenes; - scenes["s1/plots/p1/type"] = "pseudocolor"; - scenes["s1/plots/p1/field"] = "level"; - scenes["s1/image_prefix"] = output_file; - conduit::Node actions; conduit::Node &add_plots = actions.append(); add_plots["action"] = "add_scenes"; - add_plots["scenes"] = scenes; + conduit::Node &scenes = add_plots["scenes"]; + scenes["s1/plots/p1/type"] = "pseudocolor"; + scenes["s1/plots/p1/field"] = "level"; + scenes["s1/image_prefix"] = output_file; actions.print(); // @@ -152,14 +140,9 @@ TEST(ascent_pipeline, test_render_3d_poly_multi) Ascent ascent; Node ascent_opts; - Node ascent_info; - ascent_opts["runtime/type"] = "ascent"; - ascent.open(ascent_opts); + ascent.open(); ascent.publish(root); ascent.execute(actions); - ascent.info(ascent_info); - EXPECT_EQ(ascent_info["runtime/type"].as_string(), "ascent"); - ascent_info.print(); ascent.close(); //