1 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
2 // Copyright (c) Lawrence Livermore National Security, LLC and other Ascent
3 // Project developers. See top-level LICENSE AND COPYRIGHT files for dates and
4 // other details. No copyright assignment is required to contribute to Ascent.
5 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
6 
7 //-----------------------------------------------------------------------------
8 ///
9 /// file: ascent_expression_filters.cpp
10 ///
11 //-----------------------------------------------------------------------------
12 
13 #include "ascent_expression_filters.hpp"
14 
15 //-----------------------------------------------------------------------------
16 // thirdparty includes
17 //-----------------------------------------------------------------------------
18 
19 // conduit includes
20 #include <conduit.hpp>
21 
22 //-----------------------------------------------------------------------------
23 // ascent includes
24 //-----------------------------------------------------------------------------
25 #include "ascent_blueprint_architect.hpp"
26 #include "ascent_conduit_reductions.hpp"
27 #include <ascent_config.h>
28 #include <ascent_logging.hpp>
29 #include <ascent_data_object.hpp>
30 #include <utils/ascent_mpi_utils.hpp>
31 #include <flow_graph.hpp>
32 #include <flow_timer.hpp>
33 #include <flow_workspace.hpp>
34 
35 #include <limits>
36 #include <math.h>
37 #include <cmath>
38 #include <typeinfo>
39 
40 #if defined(ASCENT_DRAY_ENABLED)
41 #include <dray/queries/lineout.hpp>
42 #endif
43 
44 #ifdef ASCENT_MPI_ENABLED
45 #include <mpi.h>
46 #endif
47 
48 using namespace conduit;
49 using namespace std;
50 
51 using namespace flow;
52 
53 //-----------------------------------------------------------------------------
54 // -- begin ascent:: --
55 //-----------------------------------------------------------------------------
56 namespace ascent
57 {
58 
59 //-----------------------------------------------------------------------------
60 // -- begin ascent::runtime --
61 //-----------------------------------------------------------------------------
62 namespace runtime
63 {
64 
65 //-----------------------------------------------------------------------------
66 // -- begin ascent::runtime::expressions --
67 //-----------------------------------------------------------------------------
68 namespace expressions
69 {
70 
71 namespace detail
72 {
73 // We want to allow some objects to have basic
74 // attributes like vectors, but since its a base
75 // type, its overly burdensome to always set these
76 // as the return types in every filter. Thus, do this.
fill_attrs(conduit::Node & obj)77 void fill_attrs(conduit::Node &obj)
78 {
79   const std::string type = obj["type"].as_string();
80   if(type == "vector")
81   {
82     double *vals = obj["value"].value();
83     obj["attrs/x/value"] = vals[0];
84     obj["attrs/x/type"] = "double";
85     obj["attrs/y/value"] = vals[1];
86     obj["attrs/y/type"] = "double";
87     obj["attrs/z/value"] = vals[2];
88     obj["attrs/z/type"] = "double";
89   }
90 }
91 
92 bool
is_math(const std::string & op)93 is_math(const std::string &op)
94 {
95   return op == "*" || op == "+" || op == "/" || op == "-" || op == "%";
96 }
97 
98 bool
is_logic(const std::string & op)99 is_logic(const std::string &op)
100 {
101   return op == "or" || op == "and" || op == "not";
102 }
103 
104 bool
is_scalar(const std::string & type)105 is_scalar(const std::string &type)
106 {
107   return type == "int" || type == "double" || type == "scalar";
108 }
109 
110 void
vector_op(const double lhs[3],const double rhs[3],const std::string & op,double res[3])111 vector_op(const double lhs[3],
112           const double rhs[3],
113           const std::string &op,
114           double res[3])
115 {
116 
117   if(op == "+")
118   {
119     res[0] = lhs[0] + rhs[0];
120     res[1] = lhs[1] + rhs[1];
121     res[2] = lhs[2] + rhs[2];
122   }
123   else if(op == "-")
124   {
125     res[0] = lhs[0] - rhs[0];
126     res[1] = lhs[1] - rhs[1];
127     res[2] = lhs[2] - rhs[2];
128   }
129   else
130   {
131     ASCENT_ERROR("Unsupported vector op " << op);
132   }
133 }
134 
135 template <typename T>
136 T
math_op(const T lhs,const T rhs,const std::string & op)137 math_op(const T lhs, const T rhs, const std::string &op)
138 {
139   ASCENT_ERROR("unknown type: " << typeid(T).name());
140 }
141 
142 template <>
143 double
math_op(const double lhs,const double rhs,const std::string & op)144 math_op(const double lhs, const double rhs, const std::string &op)
145 {
146   double res;
147   if(op == "+")
148   {
149     res = lhs + rhs;
150   }
151   else if(op == "-")
152   {
153     res = lhs - rhs;
154   }
155   else if(op == "*")
156   {
157     res = lhs * rhs;
158   }
159   else if(op == "/")
160   {
161     res = lhs / rhs;
162   }
163   else
164   {
165     ASCENT_ERROR("unknown math op " << op << " for type double");
166   }
167   return res;
168 }
169 
170 template <>
171 int
math_op(const int lhs,const int rhs,const std::string & op)172 math_op(const int lhs, const int rhs, const std::string &op)
173 {
174   int res;
175   if(op == "+")
176   {
177     res = lhs + rhs;
178   }
179   else if(op == "-")
180   {
181     res = lhs - rhs;
182   }
183   else if(op == "*")
184   {
185     res = lhs * rhs;
186   }
187   else if(op == "/")
188   {
189     res = lhs / rhs;
190   }
191   else if(op == "%")
192   {
193     res = lhs % rhs;
194   }
195   else
196   {
197     ASCENT_ERROR("unknown math op " << op << " for type int");
198   }
199   return res;
200 }
201 
202 bool
comp_op(const double lhs,const double rhs,const std::string & op)203 comp_op(const double lhs, const double rhs, const std::string &op)
204 {
205   int res;
206   if(op == "<")
207   {
208     res = lhs < rhs;
209   }
210   else if(op == "<=")
211   {
212     res = lhs <= rhs;
213   }
214   else if(op == ">")
215   {
216     res = lhs > rhs;
217   }
218   else if(op == ">=")
219   {
220     res = lhs >= rhs;
221   }
222   else if(op == "==")
223   {
224     res = lhs == rhs;
225   }
226   else if(op == "!=")
227   {
228     res = lhs != rhs;
229   }
230   else
231   {
232     ASCENT_ERROR("unknown comparison op " << op);
233   }
234 
235   return res;
236 }
237 
238 bool
logic_op(const bool lhs,const bool rhs,const std::string & op)239 logic_op(const bool lhs, const bool rhs, const std::string &op)
240 {
241   bool res;
242   if(op == "or")
243   {
244     res = lhs || rhs;
245   }
246   else if(op == "and")
247   {
248     res = lhs && rhs;
249   }
250   else if(op == "not")
251   {
252     // a dummy lhs is being passed
253     res = !rhs;
254   }
255   else
256   {
257     ASCENT_ERROR("unknown boolean op " << op);
258   }
259   return res;
260 }
261 
262 } // namespace detail
263 
resolve_symbol_result(flow::Graph & graph,const conduit::Node * output,const std::string filter_name)264 void resolve_symbol_result(flow::Graph &graph,
265                            const conduit::Node *output,
266                            const std::string filter_name)
267 {
268   conduit::Node *symbol_table =
269     graph.workspace().registry().fetch<conduit::Node>("symbol_table");
270   const int num_symbols = symbol_table->number_of_children();
271   for(int i = 0; i < num_symbols; ++i)
272   {
273     conduit::Node &symbol = symbol_table->child(i);
274     if(symbol["filter_name"].as_string() == filter_name)
275     {
276       symbol["value"] = output->fetch("value");
277       break;
278     }
279   }
280 }
281 //-----------------------------------------------------------------------------
NullArg()282 NullArg::NullArg() : Filter()
283 {
284   // empty
285 }
286 
287 //-----------------------------------------------------------------------------
~NullArg()288 NullArg::~NullArg()
289 {
290   // empty
291 }
292 
293 //-----------------------------------------------------------------------------
294 void
declare_interface(Node & i)295 NullArg::declare_interface(Node &i)
296 {
297   i["type_name"] = "null_arg";
298   i["port_names"] = DataType::empty();
299   i["output_port"] = "true";
300 }
301 
302 //-----------------------------------------------------------------------------
303 bool
verify_params(const conduit::Node & params,conduit::Node & info)304 NullArg::verify_params(const conduit::Node &params, conduit::Node &info)
305 {
306   info.reset();
307   bool res = true;
308   return res;
309 }
310 
311 //-----------------------------------------------------------------------------
312 void
execute()313 NullArg::execute()
314 {
315   conduit::Node *output = new conduit::Node();
316   set_output<conduit::Node>(output);
317 }
318 
319 //-----------------------------------------------------------------------------
Identifier()320 Identifier::Identifier() : Filter()
321 {
322   // empty
323 }
324 
325 //-----------------------------------------------------------------------------
~Identifier()326 Identifier::~Identifier()
327 {
328   // empty
329 }
330 
331 //-----------------------------------------------------------------------------
332 void
declare_interface(Node & i)333 Identifier::declare_interface(Node &i)
334 {
335   i["type_name"] = "expr_identifier";
336   i["port_names"] = DataType::empty();
337   i["output_port"] = "true";
338 }
339 
340 //-----------------------------------------------------------------------------
341 bool
verify_params(const conduit::Node & params,conduit::Node & info)342 Identifier::verify_params(const conduit::Node &params, conduit::Node &info)
343 {
344   info.reset();
345   bool res = true;
346   if(!params.has_path("value"))
347   {
348     info["errors"].append() = "Missing required string parameter 'value'";
349     res = false;
350   }
351   return res;
352 }
353 
354 //-----------------------------------------------------------------------------
355 void
execute()356 Identifier::execute()
357 {
358   conduit::Node *output = new conduit::Node();
359   std::string i_name = params()["value"].as_string();
360 
361   const conduit::Node *const cache =
362       graph().workspace().registry().fetch<Node>("cache");
363   if(!cache->has_path(i_name))
364   {
365     ASCENT_ERROR("Unknown expression identifier: '" << i_name << "'");
366   }
367 
368   const int entries = (*cache)[i_name].number_of_children();
369   if(entries < 1)
370   {
371     ASCENT_ERROR("Expression identifier: needs at least one entry");
372   }
373   // grab the last one calculated so we have type info
374   (*output) = (*cache)[i_name].child(entries - 1);
375   // we need to keep the name to retrieve the chache
376   // if history is called.
377   (*output)["name"] = i_name;
378 
379   resolve_symbol_result(graph(), output, this->name());
380   set_output<conduit::Node>(output);
381 }
382 
383 //-----------------------------------------------------------------------------
Boolean()384 Boolean::Boolean() : Filter()
385 {
386   // empty
387 }
388 
389 //-----------------------------------------------------------------------------
~Boolean()390 Boolean::~Boolean()
391 {
392   // empty
393 }
394 
395 //-----------------------------------------------------------------------------
396 void
declare_interface(Node & i)397 Boolean::declare_interface(Node &i)
398 {
399   i["type_name"] = "expr_bool";
400   i["port_names"] = DataType::empty();
401   i["output_port"] = "true";
402 }
403 
404 //-----------------------------------------------------------------------------
405 bool
verify_params(const conduit::Node & params,conduit::Node & info)406 Boolean::verify_params(const conduit::Node &params, conduit::Node &info)
407 {
408   info.reset();
409   bool res = true;
410   if(!params.has_path("value"))
411   {
412     info["errors"].append() = "Missing required numeric parameter 'value'";
413     res = false;
414   }
415   return res;
416 }
417 
418 //-----------------------------------------------------------------------------
419 void
execute()420 Boolean::execute()
421 {
422   conduit::Node *output = new conduit::Node();
423   (*output)["value"] = params()["value"].to_uint8();
424   (*output)["type"] = "bool";
425   set_output<conduit::Node>(output);
426 }
427 
428 //-----------------------------------------------------------------------------
429 //-----------------------------------------------------------------------------
Integer()430 Integer::Integer() : Filter()
431 {
432   // empty
433 }
434 
435 //-----------------------------------------------------------------------------
~Integer()436 Integer::~Integer()
437 {
438   // empty
439 }
440 
441 //-----------------------------------------------------------------------------
442 
443 void
declare_interface(Node & i)444 Integer::declare_interface(Node &i)
445 {
446   i["type_name"] = "expr_integer";
447   i["port_names"] = DataType::empty();
448   i["output_port"] = "true";
449 }
450 
451 //-----------------------------------------------------------------------------
452 bool
verify_params(const conduit::Node & params,conduit::Node & info)453 Integer::verify_params(const conduit::Node &params, conduit::Node &info)
454 {
455   info.reset();
456   bool res = true;
457   if(!params.has_path("value"))
458   {
459     info["errors"].append() = "Missing required numeric parameter 'value'";
460     res = false;
461   }
462   return res;
463 }
464 
465 //-----------------------------------------------------------------------------
466 void
execute()467 Integer::execute()
468 {
469   conduit::Node *output = new conduit::Node();
470   (*output)["value"] = params()["value"].to_int32();
471   (*output)["type"] = "int";
472   set_output<conduit::Node>(output);
473 }
474 
475 //-----------------------------------------------------------------------------
Double()476 Double::Double() : Filter()
477 {
478   // empty
479 }
480 
481 //-----------------------------------------------------------------------------
~Double()482 Double::~Double()
483 {
484   // empty
485 }
486 
487 //-----------------------------------------------------------------------------
488 void
declare_interface(Node & i)489 Double::declare_interface(Node &i)
490 {
491   i["type_name"] = "expr_double";
492   i["port_names"] = DataType::empty();
493   i["output_port"] = "true";
494 }
495 
496 //-----------------------------------------------------------------------------
497 bool
verify_params(const conduit::Node & params,conduit::Node & info)498 Double::verify_params(const conduit::Node &params, conduit::Node &info)
499 {
500   info.reset();
501   bool res = true;
502   if(!params.has_path("value"))
503   {
504     info["errors"].append() = "Missing required numeric parameter 'value'";
505     res = false;
506   }
507   return res;
508 }
509 
510 //-----------------------------------------------------------------------------
511 void
execute()512 Double::execute()
513 {
514   conduit::Node *output = new conduit::Node();
515   (*output)["value"] = params()["value"].to_float64();
516   (*output)["type"] = "double";
517   set_output<conduit::Node>(output);
518 }
519 
520 //-----------------------------------------------------------------------------
String()521 String::String() : Filter()
522 {
523   // empty
524 }
525 
526 //-----------------------------------------------------------------------------
~String()527 String::~String()
528 {
529   // empty
530 }
531 
532 //-----------------------------------------------------------------------------
533 void
declare_interface(Node & i)534 String::declare_interface(Node &i)
535 {
536   i["type_name"] = "expr_string";
537   i["port_names"] = DataType::empty();
538   i["output_port"] = "true";
539 }
540 
541 //-----------------------------------------------------------------------------
542 bool
verify_params(const conduit::Node & params,conduit::Node & info)543 String::verify_params(const conduit::Node &params, conduit::Node &info)
544 {
545   info.reset();
546   bool res = true;
547   if(!params.has_path("value"))
548   {
549     info["errors"].append() = "Missing required string parameter 'value'";
550     res = false;
551   }
552   return res;
553 }
554 
555 //-----------------------------------------------------------------------------
556 void
execute()557 String::execute()
558 {
559   conduit::Node *output = new conduit::Node();
560 
561   (*output)["value"] = params()["value"].as_string();
562   (*output)["type"] = "string";
563   set_output<conduit::Node>(output);
564 }
565 
566 //-----------------------------------------------------------------------------
BinaryOp()567 BinaryOp::BinaryOp() : Filter()
568 {
569   // empty
570 }
571 
572 //-----------------------------------------------------------------------------
~BinaryOp()573 BinaryOp::~BinaryOp()
574 {
575   // empty
576 }
577 
578 //-----------------------------------------------------------------------------
579 void
declare_interface(Node & i)580 BinaryOp::declare_interface(Node &i)
581 {
582   i["type_name"] = "expr_binary_op";
583   i["port_names"].append() = "lhs";
584   i["port_names"].append() = "rhs";
585   i["output_port"] = "true";
586 }
587 
588 //-----------------------------------------------------------------------------
589 bool
verify_params(const conduit::Node & params,conduit::Node & info)590 BinaryOp::verify_params(const conduit::Node &params, conduit::Node &info)
591 {
592   info.reset();
593   bool res = true;
594   if(!params.has_path("op_string"))
595   {
596     info["errors"].append() = "Missing required string parameter 'op_string'";
597     res = false;
598   }
599   return res;
600 }
601 
602 //-----------------------------------------------------------------------------
603 void
execute()604 BinaryOp::execute()
605 {
606   const conduit::Node *n_lhs = input<Node>("lhs");
607   const conduit::Node *n_rhs = input<Node>("rhs");
608 
609   const conduit::Node &lhs = (*n_lhs)["value"];
610   const conduit::Node &rhs = (*n_rhs)["value"];
611 
612 
613 
614   std::string op_str = params()["op_string"].as_string();
615   const std::string l_type = (*n_lhs)["type"].as_string();
616   const std::string r_type = (*n_rhs)["type"].as_string();
617 
618   conduit::Node *output = new conduit::Node();
619   std::stringstream msg;
620 
621   if(detail::is_math(op_str))
622   {
623     if(detail::is_scalar(l_type) && detail::is_scalar(r_type))
624     {
625       // promote to double if at least one is a double
626       if(l_type == "double" || r_type == "double")
627       {
628         double d_rhs = rhs.to_float64();
629         double d_lhs = lhs.to_float64();
630         (*output)["value"] = detail::math_op(d_lhs, d_rhs, op_str);
631         (*output)["type"] = "double";
632       }
633       else
634       {
635         int i_rhs = rhs.to_int32();
636         int i_lhs = lhs.to_int32();
637         (*output)["value"] = detail::math_op(i_lhs, i_rhs, op_str);
638         (*output)["type"] = "int";
639       }
640     }
641     else
642     {
643       if(detail::is_scalar(l_type) != detail::is_scalar(r_type))
644       {
645         msg << "' " << l_type << " " << op_str << " " << r_type << "'";
646         ASCENT_ERROR(
647             "Mixed vector and scalar quantities not implemented / supported: "
648             << msg.str());
649       }
650 
651       double res[3];
652       detail::vector_op(lhs.value(), rhs.value(), op_str, res);
653 
654       (*output)["value"].set(res, 3);
655       (*output)["type"] = "vector";
656     }
657   }
658   else if(detail::is_logic(op_str))
659   {
660 
661     bool b_lhs = lhs.to_uint8();
662     bool b_rhs = rhs.to_uint8();
663     (*output)["value"] = detail::logic_op(b_lhs, b_rhs, op_str);
664     (*output)["type"] = "bool";
665   }
666   else
667   {
668     double d_rhs = rhs.to_float64();
669     double d_lhs = lhs.to_float64();
670 
671     (*output)["value"] = detail::comp_op(d_lhs, d_rhs, op_str);
672     (*output)["type"] = "bool";
673   }
674 
675   // std::cout<<" operation "<<op_str<<"\n";
676 
677   resolve_symbol_result(graph(), output, this->name());
678   set_output<conduit::Node>(output);
679 }
680 
681 //-----------------------------------------------------------------------------
IfExpr()682 IfExpr::IfExpr() : Filter()
683 {
684   // empty
685 }
686 
687 //-----------------------------------------------------------------------------
~IfExpr()688 IfExpr::~IfExpr()
689 {
690   // empty
691 }
692 
693 //-----------------------------------------------------------------------------
694 void
declare_interface(Node & i)695 IfExpr::declare_interface(Node &i)
696 {
697   i["type_name"] = "expr_if";
698   i["port_names"].append() = "condition";
699   i["port_names"].append() = "if";
700   i["port_names"].append() = "else";
701   i["output_port"] = "true";
702 }
703 
704 //-----------------------------------------------------------------------------
705 bool
verify_params(const conduit::Node & params,conduit::Node & info)706 IfExpr::verify_params(const conduit::Node &params, conduit::Node &info)
707 {
708   info.reset();
709   bool res = true;
710   return res;
711 }
712 
713 //-----------------------------------------------------------------------------
714 void
execute()715 IfExpr::execute()
716 {
717   conduit::Node *n_condition = input<Node>("condition");
718   conduit::Node *n_if = input<Node>("if");
719   conduit::Node *n_else = input<Node>("else");
720 
721   conduit::Node *output;
722   if((*n_condition)["value"].to_uint8() == 1)
723   {
724     output = n_if;
725   }
726   else
727   {
728     output = n_else;
729   }
730 
731   resolve_symbol_result(graph(), output, this->name());
732   set_output<conduit::Node>(output);
733 }
734 
735 //-----------------------------------------------------------------------------
ArrayAccess()736 ArrayAccess::ArrayAccess() : Filter()
737 {
738   // empty
739 }
740 
741 //-----------------------------------------------------------------------------
~ArrayAccess()742 ArrayAccess::~ArrayAccess()
743 {
744   // empty
745 }
746 
747 //-----------------------------------------------------------------------------
748 void
declare_interface(Node & i)749 ArrayAccess::declare_interface(Node &i)
750 {
751   i["type_name"] = "expr_array";
752   i["port_names"].append() = "array";
753   i["port_names"].append() = "index";
754   i["output_port"] = "true";
755 }
756 
757 //-----------------------------------------------------------------------------
758 bool
verify_params(const conduit::Node & params,conduit::Node & info)759 ArrayAccess::verify_params(const conduit::Node &params, conduit::Node &info)
760 {
761   info.reset();
762   bool res = true;
763   return res;
764 }
765 
766 //-----------------------------------------------------------------------------
767 void
execute()768 ArrayAccess::execute()
769 {
770   const conduit::Node *n_array = input<Node>("array");
771   const conduit::Node *n_index = input<Node>("index");
772 
773   conduit::Node *output = new conduit::Node();
774 
775   int index = (*n_index)["value"].as_int32();
776   int length = (*n_array)["value"].dtype().number_of_elements();
777   if(index > length - 1)
778   {
779     ASCENT_ERROR("ArrayAccess: array index out of bounds: [0," << length - 1
780                                                                << "]");
781   }
782   const double *arr = (*n_array)["value"].value();
783   (*output)["value"] = arr[index];
784   (*output)["type"] = "double";
785 
786   resolve_symbol_result(graph(), output, this->name());
787   set_output<conduit::Node>(output);
788 }
789 
790 //-----------------------------------------------------------------------------
DotAccess()791 DotAccess::DotAccess() : Filter()
792 {
793   // empty
794 }
795 
796 //-----------------------------------------------------------------------------
~DotAccess()797 DotAccess::~DotAccess()
798 {
799   // empty
800 }
801 
802 //-----------------------------------------------------------------------------
803 void
declare_interface(Node & i)804 DotAccess::declare_interface(Node &i)
805 {
806   i["type_name"] = "expr_dot";
807   i["port_names"].append() = "obj";
808   i["output_port"] = "true";
809 }
810 
811 //-----------------------------------------------------------------------------
812 bool
verify_params(const conduit::Node & params,conduit::Node & info)813 DotAccess::verify_params(const conduit::Node &params, conduit::Node &info)
814 {
815   info.reset();
816   bool res = true;
817   if(!params.has_path("name"))
818   {
819     info["errors"].append() = "DotAccess: Missing required parameter 'name'";
820     res = false;
821   }
822   return res;
823 }
824 
825 //-----------------------------------------------------------------------------
826 void
execute()827 DotAccess::execute()
828 {
829   conduit::Node *n_obj = input<Node>("obj");
830   std::string name = params()["name"].as_string();
831 
832   conduit::Node *output = new conduit::Node();
833 
834   // fills attrs for basic types like vectors
835   detail::fill_attrs(*n_obj);
836 
837   // TODO test accessing non-existant attribute
838   if(!n_obj->has_path("attrs/" + name))
839   {
840     n_obj->print();
841     std::stringstream ss;
842     if(n_obj->has_path("attrs"))
843     {
844       std::string attr_yaml = (*n_obj)["attrs"].to_yaml();
845       if(attr_yaml == "")
846       {
847         ss<<" No known attribtues.";
848       }
849       else
850       {
851         ss<<" Known attributes: "<<attr_yaml;
852       }
853     }
854     else
855     {
856       ss<<" No known attributes.";
857     }
858 
859     ASCENT_ERROR("'"<<name << "' is not a valid object attribute for"
860                       <<" type '"<<(*n_obj)["type"].as_string()<<"'."
861                       <<ss.str());
862   }
863 
864   (*output) = (*n_obj)["attrs/" + name];
865 
866   resolve_symbol_result(graph(), output, this->name());
867   set_output<conduit::Node>(output);
868 }
869 
870 //-----------------------------------------------------------------------------
ArrayMin()871 ArrayMin::ArrayMin() : Filter()
872 {
873   // empty
874 }
875 
876 //-----------------------------------------------------------------------------
~ArrayMin()877 ArrayMin::~ArrayMin()
878 {
879   // empty
880 }
881 
882 //-----------------------------------------------------------------------------
883 void
declare_interface(Node & i)884 ArrayMin::declare_interface(Node &i)
885 {
886   i["type_name"] = "array_min";
887   i["port_names"].append() = "arg1";
888   i["output_port"] = "true";
889 }
890 
891 //-----------------------------------------------------------------------------
892 bool
verify_params(const conduit::Node & params,conduit::Node & info)893 ArrayMin::verify_params(const conduit::Node &params, conduit::Node &info)
894 {
895   info.reset();
896   bool res = true;
897   return res;
898 }
899 
900 //-----------------------------------------------------------------------------
901 void
execute()902 ArrayMin::execute()
903 {
904   conduit::Node *output = new conduit::Node();
905   (*output)["value"] = array_min((*input<Node>("arg1"))["value"]);
906   (*output)["type"] = "double";
907 
908   resolve_symbol_result(graph(), output, this->name());
909   set_output<conduit::Node>(output);
910 }
911 
912 //-----------------------------------------------------------------------------
ScalarMin()913 ScalarMin::ScalarMin() : Filter()
914 {
915   // empty
916 }
917 
918 //-----------------------------------------------------------------------------
~ScalarMin()919 ScalarMin::~ScalarMin()
920 {
921   // empty
922 }
923 
924 //-----------------------------------------------------------------------------
925 void
declare_interface(Node & i)926 ScalarMin::declare_interface(Node &i)
927 {
928   i["type_name"] = "scalar_min";
929   i["port_names"].append() = "arg1";
930   i["port_names"].append() = "arg2";
931   i["output_port"] = "true";
932 }
933 
934 //-----------------------------------------------------------------------------
935 bool
verify_params(const conduit::Node & params,conduit::Node & info)936 ScalarMin::verify_params(const conduit::Node &params, conduit::Node &info)
937 {
938   info.reset();
939   bool res = true;
940   return res;
941 }
942 
943 //-----------------------------------------------------------------------------
944 void
execute()945 ScalarMin::execute()
946 {
947   const conduit::Node *arg1 = input<Node>("arg1");
948   const conduit::Node *arg2 = input<Node>("arg2");
949 
950   conduit::Node *output = new conduit::Node();
951 
952   if((*arg1)["type"].as_string() == "double" ||
953      (*arg2)["type"].as_string() == "double")
954   {
955     double d_rhs = (*arg1)["value"].to_float64();
956     double d_lhs = (*arg2)["value"].to_float64();
957     (*output)["value"] = std::min(d_lhs, d_rhs);
958     (*output)["type"] = "double";
959   }
960   else
961   {
962     int i_rhs = (*arg1)["value"].to_int32();
963     int i_lhs = (*arg2)["value"].to_int32();
964     (*output)["value"] = std::min(i_lhs, i_rhs);
965     (*output)["type"] = "int";
966   }
967 
968   resolve_symbol_result(graph(), output, this->name());
969   set_output<conduit::Node>(output);
970 }
971 
972 //-----------------------------------------------------------------------------
ScalarMax()973 ScalarMax::ScalarMax() : Filter()
974 {
975   // empty
976 }
977 
978 //-----------------------------------------------------------------------------
~ScalarMax()979 ScalarMax::~ScalarMax()
980 {
981   // empty
982 }
983 
984 //-----------------------------------------------------------------------------
985 void
declare_interface(Node & i)986 ScalarMax::declare_interface(Node &i)
987 {
988   i["type_name"] = "scalar_max";
989   i["port_names"].append() = "arg1";
990   i["port_names"].append() = "arg2";
991   i["output_port"] = "true";
992 }
993 
994 //-----------------------------------------------------------------------------
995 bool
verify_params(const conduit::Node & params,conduit::Node & info)996 ScalarMax::verify_params(const conduit::Node &params, conduit::Node &info)
997 {
998   info.reset();
999   bool res = true;
1000   return res;
1001 }
1002 
1003 //-----------------------------------------------------------------------------
1004 void
execute()1005 ScalarMax::execute()
1006 {
1007 
1008   const conduit::Node *arg1 = input<Node>("arg1");
1009   const conduit::Node *arg2 = input<Node>("arg2");
1010 
1011   conduit::Node *output = new conduit::Node();
1012 
1013   if((*arg1)["type"].as_string() == "double" ||
1014      (*arg2)["type"].as_string() == "double")
1015   {
1016     double d_rhs = (*arg1)["value"].to_float64();
1017     double d_lhs = (*arg2)["value"].to_float64();
1018     (*output)["value"] = std::max(d_lhs, d_rhs);
1019     (*output)["type"] = "double";
1020   }
1021   else
1022   {
1023     int i_rhs = (*arg1)["value"].to_int32();
1024     int i_lhs = (*arg2)["value"].to_int32();
1025     (*output)["value"] = std::max(i_lhs, i_rhs);
1026     (*output)["type"] = "int";
1027   }
1028 
1029   resolve_symbol_result(graph(), output, this->name());
1030   set_output<conduit::Node>(output);
1031 }
1032 
1033 //-----------------------------------------------------------------------------
FieldMin()1034 FieldMin::FieldMin() : Filter()
1035 {
1036   // empty
1037 }
1038 
1039 //-----------------------------------------------------------------------------
~FieldMin()1040 FieldMin::~FieldMin()
1041 {
1042   // empty
1043 }
1044 
1045 //-----------------------------------------------------------------------------
1046 void
declare_interface(Node & i)1047 FieldMin::declare_interface(Node &i)
1048 {
1049   i["type_name"] = "field_min";
1050   i["port_names"].append() = "arg1";
1051   i["output_port"] = "true";
1052 }
1053 
1054 //-----------------------------------------------------------------------------
1055 bool
verify_params(const conduit::Node & params,conduit::Node & info)1056 FieldMin::verify_params(const conduit::Node &params, conduit::Node &info)
1057 {
1058   info.reset();
1059   bool res = true;
1060   return res;
1061 }
1062 
1063 //-----------------------------------------------------------------------------
1064 void
execute()1065 FieldMin::execute()
1066 {
1067   const conduit::Node *arg1 = input<Node>("arg1");
1068 
1069   const std::string field = (*arg1)["value"].as_string();
1070 
1071   conduit::Node *output = new conduit::Node();
1072 
1073   DataObject *data_object =
1074     graph().workspace().registry().fetch<DataObject>("dataset");
1075   const conduit::Node *const dataset = data_object->as_low_order_bp().get();
1076 
1077   if(!is_scalar_field(*dataset, field))
1078   {
1079     ASCENT_ERROR("FieldMin: field '" << field << "' is not a scalar field");
1080   }
1081 
1082   conduit::Node n_min = field_min(*dataset, field);
1083 
1084   (*output)["type"] = "value_position";
1085   (*output)["attrs/value/value"] = n_min["value"];
1086   (*output)["attrs/value/type"] = "double";
1087   (*output)["attrs/position/value"] = n_min["position"];
1088   (*output)["attrs/position/type"] = "vector";
1089   // information about the element/field
1090   (*output)["attrs/element/rank"] = n_min["rank"];
1091   (*output)["attrs/element/domain_index"] = n_min["domain_id"];
1092   (*output)["attrs/element/index"] = n_min["index"];
1093   (*output)["attrs/element/assoc"] = n_min["assoc"];
1094 
1095   resolve_symbol_result(graph(), output, this->name());
1096   set_output<conduit::Node>(output);
1097 }
1098 
1099 //-----------------------------------------------------------------------------
ArrayMax()1100 ArrayMax::ArrayMax() : Filter()
1101 {
1102   // empty
1103 }
1104 
1105 //-----------------------------------------------------------------------------
~ArrayMax()1106 ArrayMax::~ArrayMax()
1107 {
1108   // empty
1109 }
1110 
1111 //-----------------------------------------------------------------------------
1112 void
declare_interface(Node & i)1113 ArrayMax::declare_interface(Node &i)
1114 {
1115   i["type_name"] = "array_max";
1116   i["port_names"].append() = "arg1";
1117   i["output_port"] = "true";
1118 }
1119 
1120 //-----------------------------------------------------------------------------
1121 bool
verify_params(const conduit::Node & params,conduit::Node & info)1122 ArrayMax::verify_params(const conduit::Node &params, conduit::Node &info)
1123 {
1124   info.reset();
1125   bool res = true;
1126   return res;
1127 }
1128 
1129 //-----------------------------------------------------------------------------
1130 void
execute()1131 ArrayMax::execute()
1132 {
1133   conduit::Node *output = new conduit::Node();
1134   (*output)["value"] = array_max((*input<Node>("arg1"))["value"])["value"];
1135   (*output)["type"] = "double";
1136 
1137   resolve_symbol_result(graph(), output, this->name());
1138   set_output<conduit::Node>(output);
1139 }
1140 
1141 //-----------------------------------------------------------------------------
FieldMax()1142 FieldMax::FieldMax() : Filter()
1143 {
1144   // empty
1145 }
1146 
1147 //-----------------------------------------------------------------------------
~FieldMax()1148 FieldMax::~FieldMax()
1149 {
1150   // empty
1151 }
1152 
1153 //-----------------------------------------------------------------------------
1154 void
declare_interface(Node & i)1155 FieldMax::declare_interface(Node &i)
1156 {
1157   i["type_name"] = "field_max";
1158   i["port_names"].append() = "arg1";
1159   i["output_port"] = "true";
1160 }
1161 
1162 //-----------------------------------------------------------------------------
1163 bool
verify_params(const conduit::Node & params,conduit::Node & info)1164 FieldMax::verify_params(const conduit::Node &params, conduit::Node &info)
1165 {
1166   info.reset();
1167   bool res = true;
1168   return res;
1169 }
1170 
1171 //-----------------------------------------------------------------------------
1172 void
execute()1173 FieldMax::execute()
1174 {
1175 
1176   const conduit::Node *arg1 = input<Node>("arg1");
1177 
1178   const std::string field = (*arg1)["value"].as_string();
1179 
1180   conduit::Node *output = new conduit::Node();
1181 
1182   DataObject *data_object =
1183     graph().workspace().registry().fetch<DataObject>("dataset");
1184   const conduit::Node *const dataset = data_object->as_low_order_bp().get();
1185 
1186   if(!is_scalar_field(*dataset, field))
1187   {
1188     ASCENT_ERROR("FieldMax: field '" << field << "' is not a scalar field");
1189   }
1190 
1191   conduit::Node n_max = field_max(*dataset, field);
1192 
1193   (*output)["type"] = "value_position";
1194   (*output)["attrs/value/value"] = n_max["value"];
1195   (*output)["attrs/value/type"] = "double";
1196   (*output)["attrs/position/value"] = n_max["position"];
1197   (*output)["attrs/position/type"] = "vector";
1198   // information about the element/field
1199   (*output)["attrs/element/rank"] = n_max["rank"];
1200   (*output)["attrs/element/domain_index"] = n_max["domain_id"];
1201   (*output)["attrs/element/index"] = n_max["index"];
1202   (*output)["attrs/element/assoc"] = n_max["assoc"];
1203 
1204   set_output<conduit::Node>(output);
1205 }
1206 
1207 //-----------------------------------------------------------------------------
ArrayAvg()1208 ArrayAvg::ArrayAvg() : Filter()
1209 {
1210   // empty
1211 }
1212 
1213 //-----------------------------------------------------------------------------
~ArrayAvg()1214 ArrayAvg::~ArrayAvg()
1215 {
1216   // empty
1217 }
1218 
1219 //-----------------------------------------------------------------------------
1220 void
declare_interface(Node & i)1221 ArrayAvg::declare_interface(Node &i)
1222 {
1223   i["type_name"] = "array_avg";
1224   i["port_names"].append() = "arg1";
1225   i["output_port"] = "true";
1226 }
1227 
1228 //-----------------------------------------------------------------------------
1229 bool
verify_params(const conduit::Node & params,conduit::Node & info)1230 ArrayAvg::verify_params(const conduit::Node &params, conduit::Node &info)
1231 {
1232   info.reset();
1233   bool res = true;
1234   return res;
1235 }
1236 
1237 //-----------------------------------------------------------------------------
1238 void
execute()1239 ArrayAvg::execute()
1240 {
1241   conduit::Node *output = new conduit::Node();
1242   conduit::Node sum = array_sum((*input<Node>("arg1"))["value"]);
1243   (*output)["value"] = sum["value"].to_float64() / sum["count"].to_float64();
1244   (*output)["type"] = "double";
1245 
1246   set_output<conduit::Node>(output);
1247 }
1248 
1249 //-----------------------------------------------------------------------------
ScalarGradient()1250 ScalarGradient::ScalarGradient() : Filter()
1251 {
1252   // empty
1253 }
1254 
1255 //-----------------------------------------------------------------------------
~ScalarGradient()1256 ScalarGradient::~ScalarGradient()
1257 {
1258   // empty
1259 }
1260 
1261 //-----------------------------------------------------------------------------
1262 void
declare_interface(Node & i)1263 ScalarGradient::declare_interface(Node &i)
1264 {
1265   i["type_name"] = "scalar_gradient";
1266   i["port_names"].append() = "expr_name";
1267   i["port_names"].append() = "window_length";
1268   i["port_names"].append() = "window_length_unit";
1269   i["output_port"] = "true";
1270 }
1271 
1272 //-----------------------------------------------------------------------------
1273 bool
verify_params(const conduit::Node & params,conduit::Node & info)1274 ScalarGradient::verify_params(const conduit::Node &params, conduit::Node &info)
1275 {
1276   info.reset();
1277   bool res = true;
1278   return res;
1279 }
1280 
1281 //-----------------------------------------------------------------------------
1282 void
execute()1283 ScalarGradient::execute()
1284 {
1285   conduit::Node *output = new conduit::Node();
1286   const std::string expr_name  = (*input<Node>("expr_name"))["name"].as_string();
1287   conduit::Node &n_window_length = *input<Node>("window_length");
1288   conduit::Node &n_window_length_unit = *input<Node>("window_length_unit");
1289 
1290   const conduit::Node *const cache =
1291       graph().workspace().registry().fetch<Node>("cache");
1292 
1293   if(!cache->has_path(expr_name))
1294   {
1295     ASCENT_ERROR("ScalarGradient: unknown identifier "<<  expr_name);
1296   }
1297 
1298   // handle the optional inputs
1299   double window_length = 1;
1300   if(!n_window_length.dtype().is_empty())
1301   {
1302     window_length = n_window_length["value"].to_float64();
1303   }
1304   if(window_length < 0)
1305   {
1306      ASCENT_ERROR("ScalarGradient: window_length must non-negative." );
1307   }
1308 
1309   string units = "index";
1310   if(!n_window_length_unit.dtype().is_empty())
1311   {
1312     units = n_window_length_unit["value"].as_string();
1313   }
1314 
1315   bool execution_points = units == "index";
1316   bool time = units == "time";
1317   bool cycles = units == "cycle";
1318   int total = execution_points + time + cycles;
1319 
1320   if(total == 0 && !n_window_length_unit.dtype().is_empty())
1321   {
1322      ASCENT_ERROR("ScalarGradient: if a ``window_length_unit`` value is provided,"
1323                   <<" it must be set to either: 1). \"index\", 2). \"time\", or 3). \"cycle\"." );
1324   }
1325 
1326   if((execution_points || cycles) && window_length < 1) {
1327      ASCENT_ERROR("ScalarGradient: window_length must be at least 1 if the window length unit is \"index\" or \"cycle\"." );
1328   }
1329 
1330   const conduit::Node &history = (*cache)[expr_name];
1331 
1332   const int entries = history.number_of_children();
1333   if(entries < 2)
1334   {
1335     (*output)["value"] = -std::numeric_limits<double>::infinity();
1336     (*output)["type"] = "double";
1337     set_output<conduit::Node>(output);
1338     return;
1339   }
1340 
1341   int first_index = 0, current_index = entries - 1;
1342   if(execution_points)
1343   {
1344     //clamp the first index if the window length has gone too far
1345     if(window_length - current_index > 0)
1346     {
1347       first_index = 0;
1348       window_length = current_index;
1349     }
1350     else
1351     {
1352       first_index = current_index - window_length;
1353     }
1354   }
1355   else if(time)
1356   {
1357    string time_path = "time";
1358    if(!history.child(current_index).has_path(time_path))
1359     {
1360       ASCENT_ERROR("ScalarGradient: interal error. current time point does not have the child " + time_path);
1361     }
1362     const double current_time = history.child(current_index)[time_path].to_float64();
1363     const double first_time = current_time - window_length;
1364     double time;
1365     for(int index = 0; index < entries; index++)
1366     {
1367       if(history.child(index).has_path(time_path))
1368       {
1369         time = history.child(index)[time_path].to_float64();
1370       }
1371       else
1372       {
1373         ASCENT_ERROR("ScalarGradient: a time point in evaluation window (for the calculation at absolute index: " + to_string(index) + ") does not have the child " + time_path );
1374       }
1375       if(time >= first_time) {
1376         first_index = index;
1377         //adjust so our window length is accurate (since we may not have performed a calculation at precisely the requested time)
1378         window_length = current_time - time;
1379         break;
1380       }
1381     }
1382   }
1383   else if(cycles)
1384   {
1385     vector<string> child_names = history.child_names();
1386     if(child_names.size() != entries)
1387     {
1388       ASCENT_ERROR("ScalarGradient: internal error. number of history "
1389                    <<"entries: " << to_string(entries)
1390                    <<", but number of history child names: "
1391                    <<to_string(child_names.size()));
1392     }
1393     const unsigned long long current_cycle = stoull(child_names[current_index]);
1394     const unsigned long long first_cycle = current_cycle - window_length;
1395 
1396     unsigned long long cycle;
1397     for(int index = 0; index < entries; index++)
1398     {
1399       cycle = stoull(child_names[index]);
1400       if(cycle >= first_cycle)
1401       {
1402         first_index = index;
1403         //adjust so our window length is accurate (since we may not have performed a calculation at precisely the requested time)
1404         window_length = current_cycle - cycle;
1405         break;
1406       }
1407     }
1408   }
1409 
1410   string value_path = "";
1411   vector<string> value_paths = {"value", "attrs/value/value"};
1412   if(current_index < 0 || current_index >= entries)
1413   {
1414     ASCENT_ERROR("Scalar gradient: bad current index: "<<current_index);
1415   }
1416   for(const string &path : value_paths)
1417   {
1418     if(history.child(current_index).has_path(path))
1419     {
1420       value_path = path;
1421       break;
1422     }
1423   }
1424 
1425   if(value_path.size() == 0)
1426   {
1427     ASCENT_ERROR("ScalarGradient: interal error. current index does not "
1428                   <<"have one of the expected value paths");
1429   }
1430 
1431   if(first_index < 0 || first_index >= entries)
1432   {
1433     ASCENT_ERROR("Scalar gradient: bad first index: "<<first_index);
1434   }
1435 
1436   double first_value = history.child(first_index)[value_path].to_float64();
1437   double current_value = history.child(current_index)[value_path].to_float64();
1438 
1439   // dy / dx
1440   double gradient = (current_value - first_value) / window_length;
1441 
1442   (*output)["value"] = gradient;
1443   (*output)["type"] = "double";
1444 
1445   set_output<conduit::Node>(output);
1446 }
1447 
1448 
1449 //-----------------------------------------------------------------------------
ArrayGradient()1450 ArrayGradient::ArrayGradient() : Filter()
1451 {
1452   // empty
1453 }
1454 
1455 //-----------------------------------------------------------------------------
~ArrayGradient()1456 ArrayGradient::~ArrayGradient()
1457 {
1458   // empty
1459 }
1460 
1461 
1462 // -----------------------------------------------------------------------------
1463 void
declare_interface(Node & i)1464 ArrayGradient::declare_interface(Node &i)
1465 {
1466   i["type_name"] = "gradient_range";
1467   i["port_names"].append() = "expr_name";
1468   i["port_names"].append() = "first_absolute_index";
1469   i["port_names"].append() = "last_absolute_index";
1470   i["port_names"].append() = "first_relative_index";
1471   i["port_names"].append() = "last_relative_index";
1472   i["port_names"].append() = "first_absolute_time";
1473   i["port_names"].append() = "last_absolute_time";
1474   i["port_names"].append() = "first_absolute_cycle";
1475   i["port_names"].append() = "last_absolute_cycle";
1476   i["output_port"] = "true";
1477 }
1478 
1479 //-----------------------------------------------------------------------------
1480 bool
verify_params(const conduit::Node & params,conduit::Node & info)1481 ArrayGradient::verify_params(const conduit::Node &params, conduit::Node &info)
1482 {
1483   info.reset();
1484   bool res = true;
1485   return res;
1486 }
1487 
get_first_and_last_index(const string & operator_name,const conduit::Node & history,const int & entries,const conduit::Node * n_first_index,const conduit::Node * n_last_index,bool absolute,bool relative,bool simulation_time,bool simulation_cycle,int & first_index,int & last_index)1488 void get_first_and_last_index(const string &operator_name,
1489                               const conduit::Node &history,
1490                               const int &entries,
1491                               const conduit::Node *n_first_index,
1492                               const conduit::Node *n_last_index,
1493                               bool absolute,
1494                               bool relative,
1495                               bool simulation_time,
1496                               bool simulation_cycle,
1497                               int &first_index,
1498                               int &last_index)
1499 {
1500 
1501   if(absolute || relative)
1502   {
1503     first_index = (*n_first_index)["value"].to_int32();
1504     last_index = (*n_last_index)["value"].to_int32();
1505 
1506     if(first_index < 0 || last_index < 0)
1507     {
1508       ASCENT_ERROR(operator_name + ": the first index and last index must both be non-negative integers.");
1509     }
1510 
1511     if(first_index > last_index)
1512     {
1513       ASCENT_ERROR(operator_name + ": the first index must not be greater than the last index.");
1514     }
1515 
1516     if(relative) {
1517       int relative_first_index = first_index;
1518       int relative_last_index = last_index;
1519       //when retrieving from m to n cycles ago, where m < n, n will have a lower history index than m
1520       first_index = entries - relative_last_index - 1;
1521       last_index = entries - relative_first_index - 1;
1522       //clamp it to the first cycle
1523       if(first_index < 0) {
1524         first_index = 0;
1525       }
1526     }
1527     else
1528     {
1529       //clamp it to the last cycle
1530       if(last_index >= entries)
1531       {
1532         last_index = entries - 1;
1533       }
1534     }
1535   }
1536   else if(simulation_time)
1537   {
1538     double first_time = (*n_first_index)["value"].to_float64();
1539     double last_time = (*n_last_index)["value"].to_float64();
1540 
1541     // we might want window relative to the current time, and
1542     // if that window is larger than what we currently have,
1543     // clamp the values to 0
1544     // Another possible solution to this is to specify relative times
1545     first_time = std::max(0.0, first_time);
1546     last_time = std::max(0.0, last_time);
1547 
1548     if(first_time < 0 || last_time < 0)
1549     {
1550       ASCENT_ERROR(operator_name + ": the first_absolute_time and last_absolute_time "<<
1551                    " must both be non-negative.");
1552     }
1553 
1554     if(first_time > last_time)
1555     {
1556       ASCENT_ERROR(operator_name +
1557                    ": the first_absolute_time must not be "
1558                    <<"greater than the last_absolute_time.");
1559     }
1560 
1561     string time_path = "time";
1562 
1563     double time;
1564     last_index = 0;
1565     for(int index = 0; index < entries; index++)
1566     {
1567       if(history.child(index).has_path(time_path))
1568       {
1569         time = history.child(index)[time_path].to_float64();
1570       }
1571       else
1572       {
1573         ASCENT_ERROR(operator_name << ": internal error. missing " << time_path
1574                      << " value for time point in retrieval window (for the"
1575                      <<" calculation at absolute index: " + to_string(index) + ")." );
1576       }
1577 
1578       // I am not totally sure about this logic. Part of the problem is that we
1579       // haven't fully specified what we want this behavior to be.
1580       if(first_index == -1 && time >= first_time)
1581       {
1582         first_index = index;
1583       }
1584       if(time <= last_time)
1585       {
1586         last_index = index;
1587       }
1588       if(time > last_time)
1589       {
1590         break;
1591       }
1592     }
1593     //clamp it to the last index to at least the first index
1594     if(last_index < first_index)
1595     {
1596       last_index = first_index;
1597     }
1598   }
1599   else if(simulation_cycle)
1600   {
1601     long long first_cycle = (*n_first_index)["value"].to_int64();
1602     long long last_cycle = (*n_last_index)["value"].to_int64();
1603 
1604     if(first_cycle < 0 || last_cycle < 0)
1605     {
1606       ASCENT_ERROR(operator_name + ": the first_absolute_cycle and last_absolute_cycle must both be non-negative.");
1607     }
1608 
1609     if(first_cycle > last_cycle)
1610     {
1611       ASCENT_ERROR(operator_name + ": the first_absolute_cycle must not be greater than the last_absolute_cycle.");
1612     }
1613 
1614     vector<string> child_names = history.child_names();
1615     if(child_names.size() != entries)
1616     {
1617       ASCENT_ERROR(operator_name + ": internal error. number of history entries: "
1618                    <<to_string(entries) << ", but number of history child names: "
1619                    << to_string(child_names.size()));
1620     }
1621 
1622     unsigned long long cycle;
1623     for(int index = 0; index < entries; index++)
1624     {
1625       cycle = stoull(child_names[index]);
1626       if(first_index == -1 && cycle >= first_cycle)
1627       {
1628         first_index = index;
1629       }
1630       else if(cycle > last_cycle)
1631       {
1632         last_index = index - 1;
1633         break;
1634       }
1635     }
1636     //clamp it to the last index
1637     if(last_index == -1)
1638     {
1639       last_index = entries - 1;
1640     }
1641   }
1642 }
1643 
set_values_from_history(const string & operator_name,const conduit::Node & history,int first_index,int return_size,bool return_history_index,bool return_simulation_time,bool return_simulation_cycle,conduit::Node * output)1644 void set_values_from_history(const string &operator_name,
1645                              const conduit::Node &history,
1646                              int first_index,
1647                              int return_size,
1648                              bool return_history_index,
1649                              bool return_simulation_time,
1650                              bool return_simulation_cycle,
1651                              conduit::Node *output)
1652 {
1653 
1654   bool gradient = (return_history_index || return_simulation_time || return_simulation_cycle);
1655 
1656   string value_path = "";
1657   vector<string> value_paths = {"value", "attrs/value/value"};
1658   for(const string &path : value_paths)
1659   {
1660     if(history.child(first_index).has_path(path))
1661     {
1662       value_path = path;
1663       break;
1664     }
1665   }
1666 
1667 
1668   if(value_path.size() == 0)
1669   {
1670     ASCENT_ERROR("ScalarGradient: interal error. first index does not have one of the expected value paths");
1671   }
1672 
1673   conduit::DataType dtype = history.child(first_index)[value_path].dtype();
1674 
1675   if(dtype.is_float32())
1676   {
1677     float *array = new float[return_size];
1678     for(int i = 0; i < return_size; ++i)
1679     {
1680       array[i] = history.child(first_index+i)[value_path].to_float32();
1681     }
1682     (*output)["value"].set(array, return_size);
1683     delete[] array;
1684   }
1685   else if(dtype.is_float64())
1686   {
1687     double *array = new double[return_size];
1688     for(int i = 0; i < return_size; ++i)
1689     {
1690       array[i] = history.child(first_index+i)[value_path].to_float64();
1691     }
1692     (*output)["value"].set(array, return_size);
1693     delete[] array;
1694   }
1695   else if(dtype.is_int32())
1696   {
1697     int *array = new int[return_size];
1698     for(int i = 0; i < return_size; ++i)
1699     {
1700       array[i] = history.child(first_index+i)[value_path].to_int32();
1701     }
1702     (*output)["value"].set(array, return_size);
1703     delete[] array;
1704   }
1705   else if(dtype.is_int64())
1706   {
1707     long long *array = new long long[return_size];
1708     for(int i = 0; i < return_size; ++i)
1709     {
1710       array[i] = history.child(first_index+i)[value_path].to_int64();
1711     }
1712     (*output)["value"].set(array, return_size);
1713     delete[] array;
1714   }
1715   else
1716   {
1717     ASCENT_ERROR(operator_name + ": unsupported array type "<< dtype.to_string());
1718   }
1719   (*output)["type"] = "array";
1720 
1721   if(gradient)
1722   {
1723     if(return_history_index)
1724     {
1725       long long *index_array = new long long[return_size];
1726       for(int i = 0; i < return_size-1; ++i)
1727       {
1728           index_array[i] = 1;
1729       }
1730       (*output)["time"].set(index_array, return_size-1);
1731       delete[] index_array;
1732     }
1733     else if(return_simulation_time)
1734     {
1735       double *simulation_time_array = new double[return_size];
1736       for(int i = 0; i < return_size-1; ++i)
1737       {
1738         simulation_time_array[i]
1739           = history.child(first_index + i + 1)["time"].to_float64() - history.child(first_index + i)["time"].to_float64();
1740 
1741       }
1742       (*output)["time"].set(simulation_time_array, return_size-1);
1743       delete[] simulation_time_array;
1744     }
1745     else if(return_simulation_cycle)
1746     {
1747       vector<string> child_names = history.child_names();
1748       long long *cycle_array = new long long[return_size];
1749       for(int i = 0; i < return_size-1; ++i)
1750       {
1751           cycle_array[i] = stoll(child_names[first_index + i + 1]) - stoll(child_names[first_index + i]);
1752       }
1753       (*output)["time"].set(cycle_array, return_size-1);
1754       delete[] cycle_array;
1755     }
1756   }
1757 }
1758 
1759 
1760 conduit::Node *
range_values_helper(const conduit::Node & history,const conduit::Node * n_first_absolute_index,const conduit::Node * n_last_absolute_index,const conduit::Node * n_first_relative_index,const conduit::Node * n_last_relative_index,const conduit::Node * n_first_absolute_time,const conduit::Node * n_last_absolute_time,const conduit::Node * n_first_absolute_cycle,const conduit::Node * n_last_absolute_cycle,const string & operator_name,const std::string time_units="")1761 range_values_helper(const conduit::Node &history,
1762                     const conduit::Node *n_first_absolute_index,
1763                     const conduit::Node *n_last_absolute_index,
1764                     const conduit::Node *n_first_relative_index,
1765                     const conduit::Node *n_last_relative_index,
1766                     const conduit::Node *n_first_absolute_time,
1767                     const conduit::Node *n_last_absolute_time,
1768                     const conduit::Node *n_first_absolute_cycle,
1769                     const conduit::Node *n_last_absolute_cycle,
1770                     const string &operator_name,
1771                     const std::string time_units = "")  // 'cycle' 'index' 'time'
1772 {
1773   conduit::Node *output = new conduit::Node();
1774 
1775   bool absolute =
1776     (!n_first_absolute_index->dtype().is_empty() || !n_last_absolute_index->dtype().is_empty());
1777   bool relative =
1778     (!n_first_relative_index->dtype().is_empty() || !n_last_relative_index->dtype().is_empty());
1779   bool simulation_time =
1780     (!n_first_absolute_time->dtype().is_empty() || !n_last_absolute_time->dtype().is_empty());
1781   bool simulation_cycle =
1782     (!n_first_absolute_cycle->dtype().is_empty() || !n_last_absolute_cycle->dtype().is_empty());
1783 
1784   int count = absolute + relative + simulation_time + simulation_cycle;
1785 
1786   if(count == 0)
1787   {
1788     ASCENT_ERROR(
1789         operator_name << ": Must specify a selection range, providing either "
1790                       <<"1). first_absolute_index and last_absolute_index, "
1791                       <<"2). first_relative_index and last_relative_index, :"
1792                       <<"3). first_absolute_time and last_absolute_time, or "
1793                       <<"4). first_absolute_cycle and last_absolute_cycle.");
1794   }
1795 
1796   if(count > 1)
1797   {
1798     ASCENT_ERROR(
1799         operator_name <<": Must specify exactly one selection range, providing either "
1800                       <<"1). first_absolute_index and last_absolute_index, "
1801                       <<"2). first_relative_index and last_relative_index, "
1802                       <<"3). first_absolute_time and last_absolute_time, or "
1803                       <<"4). first_absolute_cycle and last_absolute_cycle.");
1804   }
1805 
1806   const conduit::Node *n_first_index, *n_last_index;
1807   int first_index = -1, last_index = -1;
1808   if(absolute)
1809   {
1810     n_first_index = n_first_absolute_index;
1811     n_last_index = n_last_absolute_index;
1812   }
1813   else if(relative)
1814   {
1815     n_first_index = n_first_relative_index;
1816     n_last_index = n_last_relative_index;
1817   }
1818   else if(simulation_cycle)
1819   {
1820     n_first_index = n_first_absolute_cycle;
1821     n_last_index = n_last_absolute_cycle;
1822   }
1823   else if(simulation_time)
1824   {
1825     n_first_index = n_first_absolute_time;
1826     n_last_index = n_last_absolute_time;
1827   }
1828 
1829   const int entries = history.number_of_children();
1830   if(entries <= 0)
1831   {
1832     ASCENT_ERROR(
1833         operator_name + ": no entries collected for expression.");
1834   }
1835 
1836   if(!n_first_index->has_path("value"))
1837   {
1838     ASCENT_ERROR(
1839         operator_name + ": internal error. first_index does not have child value");
1840   }
1841   if(!n_last_index->has_path("value"))
1842   {
1843     ASCENT_ERROR(
1844         operator_name + ": internal error. last_index does not have child value");
1845   }
1846 
1847   get_first_and_last_index(operator_name,
1848                           history,
1849                           entries,
1850                           n_first_index,
1851                           n_last_index,
1852                           absolute,
1853                           relative,
1854                           simulation_time,
1855                           simulation_cycle,
1856                           first_index,
1857                           last_index);
1858 
1859   //the entire range falls outside what has been recorded so far
1860   if(first_index < 0 && last_index < 0)
1861   {
1862     return output;
1863   }
1864 
1865   const int return_size = last_index - first_index + 1;
1866 
1867   bool return_history_index = time_units == "index";
1868   bool return_simulation_time = time_units == "time";
1869   bool return_simulation_cycle = time_units == "cycle";
1870 
1871   set_values_from_history(operator_name,
1872                           history,
1873                           first_index,
1874                           return_size,
1875                           return_history_index,
1876                           return_simulation_time,
1877                           return_simulation_cycle,
1878                           output);
1879 
1880   return output;
1881 }
1882 
1883 
1884 //-----------------------------------------------------------------------------
1885 void
execute()1886 ArrayGradient::execute()
1887 {
1888   string operator_name = "ArrayGradient";
1889 
1890  const std::string expr_name  = (*input<conduit::Node>("expr_name"))["name"].as_string();
1891 
1892   const conduit::Node *const cache =
1893       graph().workspace().registry().fetch<conduit::Node>("cache");
1894 
1895   if(!cache->has_path(expr_name))
1896   {
1897     ASCENT_ERROR(operator_name + ": unknown identifier "<<  expr_name);
1898   }
1899   const conduit::Node &history = (*cache)[expr_name];
1900 
1901   const conduit::Node *n_first_absolute_index = input<conduit::Node>("first_absolute_index");
1902   const conduit::Node *n_last_absolute_index = input<conduit::Node>("last_absolute_index");
1903   const conduit::Node *n_first_relative_index = input<conduit::Node>("first_relative_index");
1904   const conduit::Node *n_last_relative_index = input<conduit::Node>("last_relative_index");
1905   const conduit::Node *n_first_absolute_time = input<conduit::Node>("first_absolute_time");
1906   const conduit::Node *n_last_absolute_time = input<conduit::Node>("last_absolute_time");
1907   const conduit::Node *n_first_absolute_cycle = input<conduit::Node>("first_absolute_cycle");
1908   const conduit::Node *n_last_absolute_cycle = input<conduit::Node>("last_absolute_cycle");
1909 
1910   conduit::Node *output = range_values_helper(history,
1911                                               n_first_absolute_index,
1912                                               n_last_absolute_index,
1913                                               n_first_relative_index,
1914                                               n_last_relative_index,
1915                                               n_first_absolute_time,
1916                                               n_last_absolute_time,
1917                                               n_first_absolute_cycle,
1918                                               n_last_absolute_cycle,
1919                                               operator_name,
1920                                               "time");
1921 
1922   size_t num_array_elems = (*output)["value"].dtype().number_of_elements();
1923 
1924   if(num_array_elems < 2)
1925   {
1926     double neg_inf[1] = {-std::numeric_limits<double>::infinity()};
1927     (*output)["value"].set(neg_inf,1);
1928     (*output)["type"] = "array";
1929     set_output<conduit::Node>(output);
1930     return;
1931   }
1932 
1933   conduit::Node gradient = array_gradient((*output)["value"], (*output)["time"]);
1934 
1935 
1936   (*output)["value"] = gradient["value"];
1937   (*output)["type"] = "array";
1938 
1939   resolve_symbol_result(graph(), output, this->name());
1940   set_output<conduit::Node>(output);
1941 }
1942 
1943 //-----------------------------------------------------------------------------
FieldAvg()1944 FieldAvg::FieldAvg() : Filter()
1945 {
1946   // empty
1947 }
1948 
1949 //-----------------------------------------------------------------------------
~FieldAvg()1950 FieldAvg::~FieldAvg()
1951 {
1952   // empty
1953 }
1954 
1955 //-----------------------------------------------------------------------------
1956 void
declare_interface(Node & i)1957 FieldAvg::declare_interface(Node &i)
1958 {
1959   i["type_name"] = "field_avg";
1960   i["port_names"].append() = "arg1";
1961   i["output_port"] = "true";
1962 }
1963 
1964 //-----------------------------------------------------------------------------
1965 bool
verify_params(const conduit::Node & params,conduit::Node & info)1966 FieldAvg::verify_params(const conduit::Node &params, conduit::Node &info)
1967 {
1968   info.reset();
1969   bool res = true;
1970   return res;
1971 }
1972 
1973 //-----------------------------------------------------------------------------
1974 void
execute()1975 FieldAvg::execute()
1976 {
1977   const conduit::Node *arg1 = input<Node>("arg1");
1978 
1979   const std::string field = (*arg1)["value"].as_string();
1980 
1981   conduit::Node *output = new conduit::Node();
1982 
1983   DataObject *data_object =
1984     graph().workspace().registry().fetch<DataObject>("dataset");
1985   const conduit::Node *const dataset = data_object->as_low_order_bp().get();
1986 
1987   if(!is_scalar_field(*dataset, field))
1988   {
1989     ASCENT_ERROR("FieldAvg: field '" << field << "' is not a scalar field");
1990   }
1991 
1992   conduit::Node n_avg = field_avg(*dataset, field);
1993 
1994   (*output)["value"] = n_avg["value"];
1995   (*output)["type"] = "double";
1996 
1997   resolve_symbol_result(graph(), output, this->name());
1998   set_output<conduit::Node>(output);
1999 }
2000 
2001 //-----------------------------------------------------------------------------
Cycle()2002 Cycle::Cycle() : Filter()
2003 {
2004   // empty
2005 }
2006 
2007 //-----------------------------------------------------------------------------
~Cycle()2008 Cycle::~Cycle()
2009 {
2010   // empty
2011 }
2012 
2013 //-----------------------------------------------------------------------------
2014 void
declare_interface(Node & i)2015 Cycle::declare_interface(Node &i)
2016 {
2017   i["type_name"] = "cycle";
2018   i["port_names"] = DataType::empty();
2019   i["output_port"] = "true";
2020 }
2021 
2022 //-----------------------------------------------------------------------------
2023 bool
verify_params(const conduit::Node & params,conduit::Node & info)2024 Cycle::verify_params(const conduit::Node &params, conduit::Node &info)
2025 {
2026   info.reset();
2027   bool res = true;
2028   return res;
2029 }
2030 
2031 //-----------------------------------------------------------------------------
2032 void
execute()2033 Cycle::execute()
2034 {
2035   conduit::Node *output = new conduit::Node();
2036 
2037   DataObject *data_object =
2038     graph().workspace().registry().fetch<DataObject>("dataset");
2039   // we are just getting state so we don't care if its high or low
2040   // order
2041   const conduit::Node *const dataset = data_object->as_node().get();
2042 
2043   conduit::Node state = get_state_var(*dataset, "cycle");
2044   if(!state.dtype().is_number())
2045   {
2046     ASCENT_ERROR("Expressions: cycle() is not a number");
2047   }
2048 
2049   (*output)["type"] = "int";
2050   (*output)["value"] = state;
2051   resolve_symbol_result(graph(), output, this->name());
2052   set_output<conduit::Node>(output);
2053 }
2054 
2055 //-----------------------------------------------------------------------------
Time()2056 Time::Time() : Filter()
2057 {
2058   // empty
2059 }
2060 
2061 //-----------------------------------------------------------------------------
~Time()2062 Time::~Time()
2063 {
2064   // empty
2065 }
2066 
2067 //-----------------------------------------------------------------------------
2068 void
declare_interface(Node & i)2069 Time::declare_interface(Node &i)
2070 {
2071   i["type_name"] = "time";
2072   i["port_names"] = DataType::empty();
2073   i["output_port"] = "true";
2074 }
2075 
2076 //-----------------------------------------------------------------------------
2077 bool
verify_params(const conduit::Node & params,conduit::Node & info)2078 Time::verify_params(const conduit::Node &params, conduit::Node &info)
2079 {
2080   info.reset();
2081   bool res = true;
2082   return res;
2083 }
2084 
2085 //-----------------------------------------------------------------------------
2086 void
execute()2087 Time::execute()
2088 {
2089   conduit::Node *output = new conduit::Node();
2090 
2091   DataObject *data_object =
2092     graph().workspace().registry().fetch<DataObject>("dataset");
2093   // we are just getting state so we don't care if its high or low
2094   // order
2095   const conduit::Node *const dataset = data_object->as_node().get();
2096 
2097   conduit::Node state = get_state_var(*dataset, "time");
2098   if(!state.dtype().is_number())
2099   {
2100     ASCENT_ERROR("Expressions: time() is not a number");
2101   }
2102 
2103   (*output)["type"] = "double";
2104   (*output)["value"] = state;
2105   resolve_symbol_result(graph(), output, this->name());
2106   set_output<conduit::Node>(output);
2107 }
2108 
2109 //-----------------------------------------------------------------------------
History()2110 History::History() : Filter()
2111 {
2112   // empty
2113 }
2114 
2115 //-----------------------------------------------------------------------------
~History()2116 History::~History()
2117 {
2118   // empty
2119 }
2120 
2121 //-----------------------------------------------------------------------------
2122 void
declare_interface(Node & i)2123 History::declare_interface(Node &i)
2124 {
2125   i["type_name"] = "history";
2126   i["port_names"].append() = "expr_name";
2127   i["port_names"].append() = "absolute_index";
2128   i["port_names"].append() = "relative_index";
2129   i["output_port"] = "true";
2130 }
2131 
2132 //-----------------------------------------------------------------------------
2133 bool
verify_params(const conduit::Node & params,conduit::Node & info)2134 History::verify_params(const conduit::Node &params, conduit::Node &info)
2135 {
2136   info.reset();
2137   bool res = true;
2138   return res;
2139 }
2140 
2141 //-----------------------------------------------------------------------------
2142 void
execute()2143 History::execute()
2144 {
2145   conduit::Node *output = new conduit::Node();
2146 
2147   const std::string expr_name  = (*input<Node>("expr_name"))["name"].as_string();
2148 
2149   const conduit::Node *const cache =
2150       graph().workspace().registry().fetch<Node>("cache");
2151 
2152   if(!cache->has_path(expr_name))
2153   {
2154     ASCENT_ERROR("History: unknown identifier "<<  expr_name);
2155   }
2156   const conduit::Node &history = (*cache)[expr_name];
2157 
2158   const conduit::Node *n_absolute_index = input<Node>("absolute_index");
2159   const conduit::Node *n_relative_index = input<Node>("relative_index");
2160 
2161 
2162   if(!n_absolute_index->dtype().is_empty() &&
2163      !n_relative_index->dtype().is_empty())
2164   {
2165     ASCENT_ERROR(
2166         "History: Specify only one of relative_index or absolute_index.");
2167   }
2168 
2169 
2170   const int entries = history.number_of_children();
2171   if(!n_relative_index->dtype().is_empty())
2172   {
2173     int relative_index = (*n_relative_index)["value"].to_int32();
2174     if(relative_index >= entries)
2175     {
2176       // clamp to first if its gone too far
2177       relative_index = 0;
2178     }
2179     if(relative_index < 0)
2180     {
2181       ASCENT_ERROR("History: relative_index must be a non-negative integer.");
2182     }
2183     // grab the value from relative_index cycles ago
2184     (*output) = history.child(entries - relative_index - 1);
2185   }
2186   else
2187   {
2188     int absolute_index = 0;
2189 
2190     if(!n_absolute_index->has_path("value"))
2191     {
2192       ASCENT_ERROR(
2193           "History: internal error. absolute index does not have child value");
2194     }
2195     absolute_index = (*n_absolute_index)["value"].to_int32();
2196 
2197     if(absolute_index >= entries)
2198     {
2199       ASCENT_ERROR("History: found only " << entries
2200                                           << " entries, cannot get entry at "
2201                                           << absolute_index);
2202     }
2203     if(absolute_index < 0)
2204     {
2205       ASCENT_ERROR("History: absolute_index must be a non-negative integer.");
2206     }
2207 
2208     (*output) = history.child(absolute_index);
2209   }
2210 
2211   resolve_symbol_result(graph(), output, this->name());
2212   set_output<conduit::Node>(output);
2213 }
2214 
2215 
2216 //-----------------------------------------------------------------------------
HistoryRange()2217 HistoryRange::HistoryRange() : Filter()
2218 {
2219   // empty
2220 }
2221 
2222 //-----------------------------------------------------------------------------
~HistoryRange()2223 HistoryRange::~HistoryRange()
2224 {
2225   // empty
2226 }
2227 
2228 //-----------------------------------------------------------------------------
2229 void
declare_interface(Node & i)2230 HistoryRange::declare_interface(Node &i)
2231 {
2232   i["type_name"] = "history_range";
2233   i["port_names"].append() = "expr_name";
2234   i["port_names"].append() = "first_absolute_index";
2235   i["port_names"].append() = "last_absolute_index";
2236   i["port_names"].append() = "first_relative_index";
2237   i["port_names"].append() = "last_relative_index";
2238   i["port_names"].append() = "first_absolute_time";
2239   i["port_names"].append() = "last_absolute_time";
2240   i["port_names"].append() = "first_absolute_cycle";
2241   i["port_names"].append() = "last_absolute_cycle";
2242   i["output_port"] = "true";
2243 }
2244 
2245 //-----------------------------------------------------------------------------
2246 bool
verify_params(const conduit::Node & params,conduit::Node & info)2247 HistoryRange::verify_params(const conduit::Node &params, conduit::Node &info)
2248 {
2249   info.reset();
2250   bool res = true;
2251   return res;
2252 }
2253 
2254 
2255 //-----------------------------------------------------------------------------
2256 void
execute()2257 HistoryRange::execute()
2258 {
2259   const string operator_name = "HistoryRange";
2260   const std::string expr_name  = (*input<conduit::Node>("expr_name"))["name"].as_string();
2261 
2262   const conduit::Node *const cache =
2263       graph().workspace().registry().fetch<conduit::Node>("cache");
2264 
2265   if(!cache->has_path(expr_name))
2266   {
2267     ASCENT_ERROR(operator_name + ": unknown identifier "<<  expr_name);
2268   }
2269   const conduit::Node &history = (*cache)[expr_name];
2270 
2271   const conduit::Node *n_first_absolute_index = input<conduit::Node>("first_absolute_index");
2272   const conduit::Node *n_last_absolute_index = input<conduit::Node>("last_absolute_index");
2273   const conduit::Node *n_first_relative_index = input<conduit::Node>("first_relative_index");
2274   const conduit::Node *n_last_relative_index = input<conduit::Node>("last_relative_index");
2275   const conduit::Node *n_first_absolute_time = input<conduit::Node>("first_absolute_time");
2276   const conduit::Node *n_last_absolute_time = input<conduit::Node>("last_absolute_time");
2277   const conduit::Node *n_first_absolute_cycle = input<conduit::Node>("first_absolute_cycle");
2278   const conduit::Node *n_last_absolute_cycle = input<conduit::Node>("last_absolute_cycle");
2279 
2280   conduit::Node *output = range_values_helper(history,
2281                                               n_first_absolute_index,
2282                                               n_last_absolute_index,
2283                                               n_first_relative_index,
2284                                               n_last_relative_index,
2285                                               n_first_absolute_time,
2286                                               n_last_absolute_time,
2287                                               n_first_absolute_cycle,
2288                                               n_last_absolute_cycle,
2289                                               operator_name);
2290 
2291   set_output<conduit::Node>(output);
2292 }
2293 
2294 
2295 //-----------------------------------------------------------------------------
Vector()2296 Vector::Vector() : Filter()
2297 {
2298   // empty
2299 }
2300 
2301 //-----------------------------------------------------------------------------
~Vector()2302 Vector::~Vector()
2303 {
2304   // empty
2305 }
2306 
2307 //-----------------------------------------------------------------------------
2308 void
declare_interface(Node & i)2309 Vector::declare_interface(Node &i)
2310 {
2311   i["type_name"] = "vector";
2312   i["port_names"].append() = "arg1";
2313   i["port_names"].append() = "arg2";
2314   i["port_names"].append() = "arg3";
2315   i["output_port"] = "true";
2316 }
2317 
2318 //-----------------------------------------------------------------------------
2319 bool
verify_params(const conduit::Node & params,conduit::Node & info)2320 Vector::verify_params(const conduit::Node &params, conduit::Node &info)
2321 {
2322   info.reset();
2323   bool res = true;
2324   return res;
2325 }
2326 
2327 //-----------------------------------------------------------------------------
2328 void
execute()2329 Vector::execute()
2330 {
2331   const conduit::Node *arg1 = input<Node>("arg1");
2332   const conduit::Node *arg2 = input<Node>("arg2");
2333   const conduit::Node *arg3 = input<Node>("arg3");
2334 
2335   double vec[3] = {0., 0., 0.};
2336   vec[0] = (*arg1)["value"].to_float64();
2337   vec[1] = (*arg2)["value"].to_float64();
2338   vec[2] = (*arg3)["value"].to_float64();
2339 
2340   conduit::Node *output = new conduit::Node();
2341   (*output)["type"] = "vector";
2342   (*output)["value"].set(vec, 3);
2343 
2344   resolve_symbol_result(graph(), output, this->name());
2345   set_output<conduit::Node>(output);
2346 }
2347 
2348 //-----------------------------------------------------------------------------
Magnitude()2349 Magnitude::Magnitude() : Filter()
2350 {
2351   // empty
2352 }
2353 
2354 //-----------------------------------------------------------------------------
~Magnitude()2355 Magnitude::~Magnitude()
2356 {
2357   // empty
2358 }
2359 
2360 //-----------------------------------------------------------------------------
2361 void
declare_interface(Node & i)2362 Magnitude::declare_interface(Node &i)
2363 {
2364   i["type_name"] = "magnitude";
2365   i["port_names"].append() = "arg1";
2366   i["output_port"] = "true";
2367 }
2368 
2369 //-----------------------------------------------------------------------------
2370 bool
verify_params(const conduit::Node & params,conduit::Node & info)2371 Magnitude::verify_params(const conduit::Node &params, conduit::Node &info)
2372 {
2373   info.reset();
2374   bool res = true;
2375   return res;
2376 }
2377 
2378 //-----------------------------------------------------------------------------
2379 void
execute()2380 Magnitude::execute()
2381 {
2382   const conduit::Node *arg1 = input<Node>("arg1");
2383 
2384   double res = 0.;
2385   const double *vec = (*arg1)["value"].value();
2386   res = sqrt(vec[0] * vec[0] + vec[1] * vec[1] + vec[2] * vec[2]);
2387   conduit::Node *output = new conduit::Node();
2388   (*output)["type"] = "double";
2389   (*output)["value"] = res;
2390 
2391   resolve_symbol_result(graph(), output, this->name());
2392   set_output<conduit::Node>(output);
2393 }
2394 
2395 //-----------------------------------------------------------------------------
Abs()2396 Abs::Abs() : Filter()
2397 {
2398   // empty
2399 }
2400 
2401 //-----------------------------------------------------------------------------
~Abs()2402 Abs::~Abs()
2403 {
2404   // empty
2405 }
2406 
2407 //-----------------------------------------------------------------------------
2408 void
declare_interface(Node & i)2409 Abs::declare_interface(Node &i)
2410 {
2411   i["type_name"] = "abs";
2412   i["port_names"].append() = "arg1";
2413   i["output_port"] = "true";
2414 }
2415 
2416 //-----------------------------------------------------------------------------
2417 bool
verify_params(const conduit::Node & params,conduit::Node & info)2418 Abs::verify_params(const conduit::Node &params, conduit::Node &info)
2419 {
2420   info.reset();
2421   bool res = true;
2422   return res;
2423 }
2424 
2425 //-----------------------------------------------------------------------------
2426 void
execute()2427 Abs::execute()
2428 {
2429   const conduit::Node *arg1 = input<Node>("arg1");
2430 
2431   if((*arg1)["type"].as_string() == "double")
2432   {
2433     double res = 0.;
2434     res = abs((*arg1)["value"].to_float64());
2435     conduit::Node *output = new conduit::Node();
2436     (*output)["type"] = "double";
2437     (*output)["value"] = res;
2438 
2439     resolve_symbol_result(graph(), output, this->name());
2440     set_output<conduit::Node>(output);
2441   }
2442   else
2443   {
2444     int res = 0;
2445     res = abs((*arg1)["value"].to_int32());
2446     conduit::Node *output = new conduit::Node();
2447     (*output)["type"] = "int";
2448     (*output)["value"] = res;
2449 
2450     resolve_symbol_result(graph(), output, this->name());
2451     set_output<conduit::Node>(output);
2452   }
2453 }
2454 
2455 //-----------------------------------------------------------------------------
Exp()2456 Exp::Exp() : Filter()
2457 {
2458   // empty
2459 }
2460 
2461 //-----------------------------------------------------------------------------
~Exp()2462 Exp::~Exp()
2463 {
2464   // empty
2465 }
2466 
2467 //-----------------------------------------------------------------------------
2468 void
declare_interface(Node & i)2469 Exp::declare_interface(Node &i)
2470 {
2471   i["type_name"] = "exp";
2472   i["port_names"].append() = "arg1";
2473   i["output_port"] = "true";
2474 }
2475 
2476 //-----------------------------------------------------------------------------
2477 bool
verify_params(const conduit::Node & params,conduit::Node & info)2478 Exp::verify_params(const conduit::Node &params, conduit::Node &info)
2479 {
2480   info.reset();
2481   bool res = true;
2482   return res;
2483 }
2484 
2485 //-----------------------------------------------------------------------------
2486 void
execute()2487 Exp::execute()
2488 {
2489   const conduit::Node *arg1 = input<Node>("arg1");
2490 
2491   double res = 0.;
2492   res = exp((*arg1)["value"].to_float64());
2493   conduit::Node *output = new conduit::Node();
2494   (*output)["type"] = "double";
2495   (*output)["value"] = res;
2496 
2497   resolve_symbol_result(graph(), output, this->name());
2498   set_output<conduit::Node>(output);
2499 }
2500 
2501 //-----------------------------------------------------------------------------
Log()2502 Log::Log() : Filter()
2503 {
2504   // empty
2505 }
2506 
2507 //-----------------------------------------------------------------------------
~Log()2508 Log::~Log()
2509 {
2510   // empty
2511 }
2512 
2513 //-----------------------------------------------------------------------------
2514 void
declare_interface(Node & i)2515 Log::declare_interface(Node &i)
2516 {
2517   i["type_name"] = "log";
2518   i["port_names"].append() = "arg1";
2519   i["output_port"] = "true";
2520 }
2521 
2522 //-----------------------------------------------------------------------------
2523 bool
verify_params(const conduit::Node & params,conduit::Node & info)2524 Log::verify_params(const conduit::Node &params, conduit::Node &info)
2525 {
2526   info.reset();
2527   bool res = true;
2528   return res;
2529 }
2530 
2531 //-----------------------------------------------------------------------------
2532 void
execute()2533 Log::execute()
2534 {
2535   const conduit::Node *arg1 = input<Node>("arg1");
2536 
2537   double res = 0.;
2538   res = log((*arg1)["value"].to_float64());
2539   conduit::Node *output = new conduit::Node();
2540   (*output)["type"] = "double";
2541   (*output)["value"] = res;
2542 
2543   resolve_symbol_result(graph(), output, this->name());
2544   set_output<conduit::Node>(output);
2545 }
2546 
2547 //-----------------------------------------------------------------------------
Pow()2548 Pow::Pow() : Filter()
2549 {
2550   // empty
2551 }
2552 
2553 //-----------------------------------------------------------------------------
~Pow()2554 Pow::~Pow()
2555 {
2556   // empty
2557 }
2558 
2559 //-----------------------------------------------------------------------------
2560 void
declare_interface(Node & i)2561 Pow::declare_interface(Node &i)
2562 {
2563   i["type_name"] = "pow";
2564   i["port_names"].append() = "arg1";
2565   i["port_names"].append() = "arg2";
2566   i["output_port"] = "true";
2567 }
2568 
2569 //-----------------------------------------------------------------------------
2570 bool
verify_params(const conduit::Node & params,conduit::Node & info)2571 Pow::verify_params(const conduit::Node &params, conduit::Node &info)
2572 {
2573   info.reset();
2574   bool res = true;
2575   return res;
2576 }
2577 
2578 //-----------------------------------------------------------------------------
2579 void
execute()2580 Pow::execute()
2581 {
2582   const conduit::Node *arg1 = input<Node>("arg1");
2583   const conduit::Node *arg2 = input<Node>("arg2");
2584 
2585   double res = 0.;
2586   double base = (*arg1)["value"].to_float64();
2587   double exponent = (*arg2)["value"].to_float64();
2588   res = pow(base, exponent);
2589   conduit::Node *output = new conduit::Node();
2590   (*output)["type"] = "double";
2591   (*output)["value"] = res;
2592 
2593   resolve_symbol_result(graph(), output, this->name());
2594   set_output<conduit::Node>(output);
2595 }
2596 
2597 //-----------------------------------------------------------------------------
Field()2598 Field::Field() : Filter()
2599 {
2600   // empty
2601 }
2602 
2603 //-----------------------------------------------------------------------------
~Field()2604 Field::~Field()
2605 {
2606   // empty
2607 }
2608 
2609 //-----------------------------------------------------------------------------
2610 void
declare_interface(Node & i)2611 Field::declare_interface(Node &i)
2612 {
2613   i["type_name"] = "field";
2614   i["port_names"].append() = "field_name";
2615   i["port_names"].append() = "component";
2616   i["output_port"] = "true";
2617 }
2618 
2619 //-----------------------------------------------------------------------------
2620 bool
verify_params(const conduit::Node & params,conduit::Node & info)2621 Field::verify_params(const conduit::Node &params, conduit::Node &info)
2622 {
2623   info.reset();
2624   bool res = true;
2625   return res;
2626 }
2627 
2628 //-----------------------------------------------------------------------------
2629 void
execute()2630 Field::execute()
2631 {
2632   const conduit::Node *n_field_name = input<Node>("field_name");
2633   std::string field_name = (*n_field_name)["value"].as_string();
2634 
2635   // optional parameters
2636   const conduit::Node *n_component = input<Node>("component");
2637 
2638   if(!graph().workspace().registry().has_entry("dataset"))
2639   {
2640     ASCENT_ERROR("Field: Missing dataset");
2641   }
2642 
2643   DataObject *data_object =
2644     graph().workspace().registry().fetch<DataObject>("dataset");
2645   const conduit::Node *const dataset = data_object->as_low_order_bp().get();
2646 
2647   if(!has_field(*dataset, field_name))
2648   {
2649     std::string known;
2650     if(dataset->number_of_children() > 0 )
2651     {
2652       std::vector<std::string> names = dataset->child(0)["fields"].child_names();
2653       std::stringstream ss;
2654       ss << "[";
2655       for(size_t i = 0; i < names.size(); ++i)
2656       {
2657         ss << " '" << names[i]<<"'";
2658       }
2659       ss << "]";
2660       known = ss.str();
2661     }
2662     ASCENT_ERROR("Field: dataset does not contain field '"
2663                  << field_name << "'"
2664                  << " known = " << known);
2665   }
2666 
2667   std::string component;
2668   if(!n_component->dtype().is_empty())
2669   {
2670     component = (*n_component)["value"].as_string();
2671     if(!has_component(*dataset, field_name, component))
2672     {
2673       ASCENT_ERROR("Field variable '"
2674                    << field_name << "'"
2675                    << " does not have component '" << component << "'."
2676                    << " known components = "
2677                    << possible_components(*dataset, field_name));
2678     }
2679   }
2680 
2681   // at this point, we know that the field exists.
2682   // If the the field has only one component then we
2683   // don't require that the name be provide, but the
2684   // code will need the name.
2685 
2686 
2687   // if the field only has one component use that
2688   if(component.empty())
2689   {
2690 
2691     int num_comps = num_components(*dataset, field_name);
2692     if(num_comps == 1)
2693     {
2694       const int comp_idx = 0;
2695       component = component_name(*dataset, field_name, comp_idx);
2696     }
2697     else if(num_comps == 0)
2698     {
2699       // default name for empty path
2700       component = "";
2701     }
2702   }
2703 
2704   conduit::Node *output = new conduit::Node();
2705   (*output)["value"] = field_name;
2706   if(!component.empty())
2707   {
2708     (*output)["component"] = component;
2709   }
2710   (*output)["type"] = "field";
2711 
2712   resolve_symbol_result(graph(), output, this->name());
2713   set_output<conduit::Node>(output);
2714 }
2715 
2716 //-----------------------------------------------------------------------------
Axis()2717 Axis::Axis() : Filter()
2718 {
2719   // empty
2720 }
2721 
2722 //-----------------------------------------------------------------------------
~Axis()2723 Axis::~Axis()
2724 {
2725   // empty
2726 }
2727 
2728 //-----------------------------------------------------------------------------
2729 void
declare_interface(Node & i)2730 Axis::declare_interface(Node &i)
2731 {
2732   i["type_name"] = "axis";
2733   i["port_names"].append() = "name";
2734   i["port_names"].append() = "min_val";
2735   i["port_names"].append() = "max_val";
2736   i["port_names"].append() = "num_bins";
2737   i["port_names"].append() = "bins";
2738   i["port_names"].append() = "clamp";
2739   i["output_port"] = "true";
2740 }
2741 
2742 //-----------------------------------------------------------------------------
2743 bool
verify_params(const conduit::Node & params,conduit::Node & info)2744 Axis::verify_params(const conduit::Node &params, conduit::Node &info)
2745 {
2746   info.reset();
2747   bool res = true;
2748   return res;
2749 }
2750 
2751 //-----------------------------------------------------------------------------
2752 void
execute()2753 Axis::execute()
2754 {
2755   const std::string name = (*input<Node>("name"))["value"].as_string();
2756   // uniform binning
2757   const conduit::Node *n_min = input<Node>("min_val");
2758   const conduit::Node *n_max = input<Node>("max_val");
2759   const conduit::Node *n_num_bins = input<Node>("num_bins");
2760   // rectilinear binning
2761   const conduit::Node *n_bins_list_obj = input<Node>("bins");
2762   // clamp
2763   const conduit::Node *n_clamp = input<conduit::Node>("clamp");
2764 
2765   if(!graph().workspace().registry().has_entry("dataset"))
2766   {
2767     ASCENT_ERROR("Field: Missing dataset");
2768   }
2769 
2770   DataObject *data_object =
2771     graph().workspace().registry().fetch<DataObject>("dataset");
2772   const conduit::Node *const dataset = data_object->as_low_order_bp().get();
2773 
2774   if(!is_scalar_field(*dataset, name) && !is_xyz(name))
2775   {
2776     std::string known;
2777     if(dataset->number_of_children() > 0 )
2778     {
2779       std::vector<std::string> names = dataset->child(0)["fields"].child_names();
2780       std::stringstream ss;
2781       ss << "[";
2782       for(size_t i = 0; i < names.size(); ++i)
2783       {
2784         ss << " '" << names[i]<<"'";
2785       }
2786       ss << "]";
2787       known = ss.str();
2788     }
2789 
2790     ASCENT_ERROR("Axis: Axes must be scalar fields or x/y/z. Dataset does not "
2791                  "contain scalar field '"
2792                  << name << "'. Possible field names "<<known<<".");
2793   }
2794 
2795   conduit::Node *output;
2796   if(!n_bins_list_obj->dtype().is_empty())
2797   {
2798     const conduit::Node &n_bins_list = (*n_bins_list_obj)["value"];
2799     // ensure none of the uniform binning arguments are passed
2800     if(!n_min->dtype().is_empty() || !n_max->dtype().is_empty() ||
2801        !n_num_bins->dtype().is_empty())
2802     {
2803       ASCENT_ERROR("Axis: Only pass in arguments for uniform or rectilinear "
2804                    "binning, not both.");
2805     }
2806 
2807     int bins_len = n_bins_list.number_of_children();
2808 
2809     if(bins_len < 2)
2810     {
2811       ASCENT_ERROR("Axis: bins must have at least 2 items.");
2812     }
2813 
2814     output = new conduit::Node();
2815     (*output)["value/" + name + "/bins"].set(
2816         conduit::DataType::c_double(bins_len));
2817     double *bins = (*output)["value/" + name + "/bins"].value();
2818 
2819     for(int i = 0; i < bins_len; ++i)
2820     {
2821       const conduit::Node &bin = n_bins_list.child(i);
2822       if(!detail::is_scalar(bin["type"].as_string()))
2823       {
2824         delete output;
2825         ASCENT_ERROR("Axis: bins must be a list of scalars.");
2826       }
2827       bins[i] = bin["value"].to_float64();
2828       if(i != 0 && bins[i - 1] >= bins[i])
2829       {
2830         delete output;
2831         ASCENT_ERROR("Axis: bins of strictly increasing scalars.");
2832       }
2833     }
2834   }
2835   else
2836   {
2837     output = new conduit::Node();
2838 
2839     double min_val;
2840     bool min_found = false;
2841     if(!n_min->dtype().is_empty())
2842     {
2843       min_val = (*n_min)["value"].to_float64();
2844       (*output)["value/" + name + "/min_val"] = min_val;
2845       min_found = true;
2846     }
2847     else if(!is_xyz(name))
2848     {
2849       min_val = field_min(*dataset, name)["value"].to_float64();
2850       (*output)["value/" + name + "/min_val"] = min_val;
2851       min_found = true;
2852     }
2853 
2854     double max_val;
2855     bool max_found = false;
2856     if(!n_max->dtype().is_empty())
2857     {
2858       max_val = (*n_max)["value"].to_float64();
2859       max_found = true;
2860       (*output)["value/" + name + "/max_val"] = max_val;
2861     }
2862     else if(!is_xyz(name))
2863     {
2864       // add 1 because the last bin isn't inclusive
2865       max_val = field_max(*dataset, name)["value"].to_float64() + 1.0;
2866       (*output)["value/" + name + "/max_val"] = max_val;
2867       max_found = true;
2868     }
2869 
2870     (*output)["value/" + name + "/num_bins"] = 256;
2871     if(!n_num_bins->dtype().is_empty())
2872     {
2873       (*output)["value/" + name + "/num_bins"] =
2874           (*n_num_bins)["value"].to_int32();
2875     }
2876 
2877     if(min_found && max_found && min_val >= max_val)
2878     {
2879       delete output;
2880       ASCENT_ERROR("Axis: axis with name '"
2881                    << name << "': min_val (" << min_val
2882                    << ") must be smaller than max_val (" << max_val << ")");
2883     }
2884   }
2885 
2886   (*output)["value/" + name + "/clamp"] = false;
2887   if(!n_clamp->dtype().is_empty())
2888   {
2889     (*output)["value/" + name + "/clamp"] = (*n_clamp)["value"].to_uint8();
2890   }
2891 
2892   (*output)["value/" + name];
2893   (*output)["type"] = "axis";
2894   set_output<conduit::Node>(output);
2895 }
2896 
2897 //-----------------------------------------------------------------------------
Histogram()2898 Histogram::Histogram() : Filter()
2899 {
2900   // empty
2901 }
2902 
2903 //-----------------------------------------------------------------------------
~Histogram()2904 Histogram::~Histogram()
2905 {
2906   // empty
2907 }
2908 
2909 //-----------------------------------------------------------------------------
2910 void
declare_interface(Node & i)2911 Histogram::declare_interface(Node &i)
2912 {
2913   i["type_name"] = "histogram";
2914   i["port_names"].append() = "arg1";
2915   i["port_names"].append() = "num_bins";
2916   i["port_names"].append() = "min_val";
2917   i["port_names"].append() = "max_val";
2918   i["output_port"] = "true";
2919 }
2920 
2921 //-----------------------------------------------------------------------------
2922 bool
verify_params(const conduit::Node & params,conduit::Node & info)2923 Histogram::verify_params(const conduit::Node &params, conduit::Node &info)
2924 {
2925   info.reset();
2926   bool res = true;
2927   return res;
2928 }
2929 
2930 //-----------------------------------------------------------------------------
2931 void
execute()2932 Histogram::execute()
2933 {
2934   const conduit::Node *arg1 = input<Node>("arg1");
2935   // optional inputs
2936   const conduit::Node *n_bins = input<Node>("num_bins");
2937   const conduit::Node *n_max = input<Node>("max_val");
2938   const conduit::Node *n_min = input<Node>("min_val");
2939 
2940   const std::string field = (*arg1)["value"].as_string();
2941 
2942   DataObject *data_object =
2943     graph().workspace().registry().fetch<DataObject>("dataset");
2944   const conduit::Node *const dataset = data_object->as_low_order_bp().get();
2945 
2946   if(!is_scalar_field(*dataset, field))
2947   {
2948     ASCENT_ERROR("Histogram: axis for histogram must be a scalar field. "
2949                  "Invalid axis field: '"
2950                  << field << "'.");
2951   }
2952 
2953   // handle the optional inputs
2954   int num_bins = 256;
2955   if(!n_bins->dtype().is_empty())
2956   {
2957     num_bins = (*n_bins)["value"].as_int32();
2958   }
2959 
2960   double min_val;
2961   double max_val;
2962 
2963   if(!n_max->dtype().is_empty())
2964   {
2965     max_val = (*n_max)["value"].to_float64();
2966   }
2967   else
2968   {
2969     max_val = field_max(*dataset, field)["value"].to_float64();
2970   }
2971 
2972   if(!n_min->dtype().is_empty())
2973   {
2974     min_val = (*n_min)["value"].to_float64();
2975   }
2976   else
2977   {
2978     min_val = field_min(*dataset, field)["value"].to_float64();
2979   }
2980 
2981   if(min_val >= max_val)
2982   {
2983     ASCENT_ERROR("Histogram: min value ("
2984                  << min_val << ") must be smaller than max (" << max_val
2985                  << ")");
2986   }
2987 
2988   conduit::Node *output = new conduit::Node();
2989   (*output)["type"] = "histogram";
2990   (*output)["attrs/value/value"] =
2991       field_histogram(*dataset, field, min_val, max_val, num_bins)["value"];
2992   (*output)["attrs/value/type"] = "array";
2993   (*output)["attrs/min_val/value"] = min_val;
2994   (*output)["attrs/min_val/type"] = "double";
2995   (*output)["attrs/max_val/value"] = max_val;
2996   (*output)["attrs/max_val/type"] = "double";
2997   (*output)["attrs/num_bins/value"] = num_bins;
2998   (*output)["attrs/num_bins/type"] = "int";
2999   (*output)["attrs/clamp/value"] = true;
3000   (*output)["attrs/clamp/type"] = "bool";
3001 
3002   resolve_symbol_result(graph(), output, this->name());
3003   set_output<conduit::Node>(output);
3004 }
3005 
3006 //-----------------------------------------------------------------------------
Binning()3007 Binning::Binning() : Filter()
3008 {
3009   // empty
3010 }
3011 
3012 //-----------------------------------------------------------------------------
~Binning()3013 Binning::~Binning()
3014 {
3015   // empty
3016 }
3017 
3018 //-----------------------------------------------------------------------------
3019 void
declare_interface(Node & i)3020 Binning::declare_interface(Node &i)
3021 {
3022   i["type_name"] = "binning";
3023   i["port_names"].append() = "reduction_var";
3024   i["port_names"].append() = "reduction_op";
3025   i["port_names"].append() = "bin_axes";
3026   i["port_names"].append() = "empty_bin_val";
3027   i["port_names"].append() = "component";
3028   i["output_port"] = "true";
3029 }
3030 
3031 //-----------------------------------------------------------------------------
3032 bool
verify_params(const conduit::Node & params,conduit::Node & info)3033 Binning::verify_params(const conduit::Node &params, conduit::Node &info)
3034 {
3035   info.reset();
3036   bool res = true;
3037   return res;
3038 }
3039 
binning_interface(const std::string & reduction_var,const std::string & reduction_op,const conduit::Node & n_empty_bin_val,const conduit::Node & n_component,const conduit::Node & n_axis_list,conduit::Node & dataset,conduit::Node & n_binning,conduit::Node & n_output_axes)3040 void binning_interface(const std::string &reduction_var,
3041                        const std::string &reduction_op,
3042                        const conduit::Node &n_empty_bin_val,
3043                        const conduit::Node &n_component,
3044                        const conduit::Node &n_axis_list,
3045                        conduit::Node &dataset,
3046                        conduit::Node &n_binning,
3047                        conduit::Node &n_output_axes)
3048 {
3049   std::string component = "";
3050   if(!n_component.dtype().is_empty())
3051   {
3052     component = n_component["value"].as_string();
3053   }
3054 
3055   if(!n_axis_list.has_path("type"))
3056   {
3057     ASCENT_ERROR("Binning: axis list missing object type.");
3058   }
3059   std::string obj_type = n_axis_list["type"].as_string();
3060   if(obj_type != "list")
3061   {
3062     ASCENT_ERROR("Binning: axis list is not type 'list'."
3063                   <<" type is '"<<obj_type<<"'");
3064   }
3065   // verify n_axes_list and put the values in n_output_axes
3066   int num_axes = n_axis_list["value"].number_of_children();
3067   for(int i = 0; i < num_axes; ++i)
3068   {
3069     const conduit::Node &axis = n_axis_list["value"].child(i);
3070     if(axis["type"].as_string() != "axis")
3071     {
3072       ASCENT_ERROR("Binning: bin_axes must be a list of axis");
3073     }
3074     n_output_axes.update(axis["value"]);
3075   }
3076 
3077   // verify reduction_var
3078   if(reduction_var.empty())
3079   {
3080     if(reduction_op != "sum" && reduction_op != "pdf")
3081     {
3082       ASCENT_ERROR("Binning: reduction_var can only be left empty if "
3083                    "reduction_op is 'sum' or 'pdf'.");
3084     }
3085   }
3086   else if(!is_xyz(reduction_var))
3087   {
3088     if(!has_field(dataset, reduction_var))
3089     {
3090       std::string known;
3091       if(dataset.number_of_children() > 0 )
3092       {
3093         std::vector<std::string> names = dataset.child(0)["fields"].child_names();
3094         std::stringstream ss;
3095         ss << "[";
3096         for(size_t i = 0; i < names.size(); ++i)
3097         {
3098           ss << " '" << names[i]<<"'";
3099         }
3100         ss << "]";
3101         known = ss.str();
3102       }
3103       ASCENT_ERROR("Binning: reduction variable '"
3104                    << reduction_var
3105                    << "' must be a scalar field in the dataset or x/y/z or empty."
3106                    << " known = " << known);
3107     }
3108 
3109     bool scalar = is_scalar_field(dataset, reduction_var);
3110     if(!scalar && component == "")
3111     {
3112       ASCENT_ERROR("Binning: reduction variable '"
3113                    << reduction_var <<"'"
3114                    << " has multiple components and no 'component' is"
3115                    << " specified."
3116                    << " known components = "
3117                    << possible_components(dataset, reduction_var));
3118     }
3119     if(scalar && component != "")
3120     {
3121       ASCENT_ERROR("Binning: reduction variable '"
3122                    << reduction_var <<"'"
3123                    << " is a scalar(i.e., has not components "
3124                    << " but 'component' " << " '"<<component<<"' was"
3125                    << " specified. Remove the 'component' argument"
3126                    << " or choose a vector variable.");
3127     }
3128     if(!has_component(dataset, reduction_var, component))
3129     {
3130       ASCENT_ERROR("Binning: reduction variable '"
3131                    << reduction_var << "'"
3132                    << " does not have component '"<<component<<"'."
3133                    << " known components = "
3134                    << possible_components(dataset, reduction_var));
3135 
3136     }
3137   }
3138 
3139   // verify reduction_op
3140   if(reduction_op != "sum" && reduction_op != "min" && reduction_op != "max" &&
3141      reduction_op != "avg" && reduction_op != "pdf" && reduction_op != "std" &&
3142      reduction_op != "var" && reduction_op != "rms")
3143   {
3144     ASCENT_ERROR(
3145         "Unknown reduction_op: '"
3146         << reduction_op
3147         << "'. Known reduction operators are: cnt, sum, min, max, avg, pdf, "
3148            "std, var, rms");
3149   }
3150 
3151   double empty_bin_val = 0;
3152   if(!n_empty_bin_val.dtype().is_empty())
3153   {
3154     empty_bin_val = n_empty_bin_val["value"].to_float64();
3155   }
3156 
3157   n_binning = binning(dataset,
3158                       n_output_axes,
3159                       reduction_var,
3160                       reduction_op,
3161                       empty_bin_val,
3162                       component);
3163 
3164 }
3165 //-----------------------------------------------------------------------------
3166 void
execute()3167 Binning::execute()
3168 {
3169   DataObject *data_object =
3170     graph().workspace().registry().fetch<DataObject>("dataset");
3171 
3172   conduit::Node *dataset = data_object->as_low_order_bp().get();
3173 
3174   const std::string reduction_var =
3175       (*input<Node>("reduction_var"))["value"].as_string();
3176   const std::string reduction_op =
3177       (*input<Node>("reduction_op"))["value"].as_string();
3178   const conduit::Node *n_axes_list = input<Node>("bin_axes");
3179   // optional arguments
3180   const conduit::Node *n_empty_bin_val = input<conduit::Node>("empty_bin_val");
3181   const conduit::Node *n_component = input<conduit::Node>("component");
3182 
3183   conduit::Node n_binning;
3184   conduit::Node n_bin_axes;
3185 
3186   binning_interface(reduction_var,
3187                     reduction_op,
3188                     *n_empty_bin_val,
3189                     *n_component,
3190                     *n_axes_list,
3191                     *dataset,
3192                     n_binning,
3193                     n_bin_axes);
3194 
3195 
3196   conduit::Node *output = new conduit::Node();
3197   (*output)["type"] = "binning";
3198   (*output)["attrs/value/value"] = n_binning["value"];
3199   (*output)["attrs/value/type"] = "array";
3200   (*output)["attrs/reduction_var/value"] = reduction_var;
3201   (*output)["attrs/reduction_var/type"] = "string";
3202   (*output)["attrs/reduction_op/value"] = reduction_op;
3203   (*output)["attrs/reduction_op/type"] = "string";
3204   (*output)["attrs/bin_axes/value"] = n_bin_axes;
3205   //(*output)["attrs/bin_axes/type"] = "list";
3206   (*output)["attrs/association/value"] = n_binning["association"];
3207   (*output)["attrs/association/type"] = "string";
3208 
3209   resolve_symbol_result(graph(), output, this->name());
3210   set_output<conduit::Node>(output);
3211 
3212 }
3213 
3214 //-----------------------------------------------------------------------------
Entropy()3215 Entropy::Entropy() : Filter()
3216 {
3217   // empty
3218 }
3219 
3220 //-----------------------------------------------------------------------------
~Entropy()3221 Entropy::~Entropy()
3222 {
3223   // empty
3224 }
3225 
3226 //-----------------------------------------------------------------------------
3227 void
declare_interface(Node & i)3228 Entropy::declare_interface(Node &i)
3229 {
3230   i["type_name"] = "entropy";
3231   i["port_names"].append() = "hist";
3232   i["output_port"] = "true";
3233 }
3234 
3235 //-----------------------------------------------------------------------------
3236 bool
verify_params(const conduit::Node & params,conduit::Node & info)3237 Entropy::verify_params(const conduit::Node &params, conduit::Node &info)
3238 {
3239   info.reset();
3240   bool res = true;
3241   return res;
3242 }
3243 
3244 //-----------------------------------------------------------------------------
3245 void
execute()3246 Entropy::execute()
3247 {
3248   const conduit::Node *hist = input<conduit::Node>("hist");
3249 
3250   if((*hist)["type"].as_string() != "histogram")
3251   {
3252     ASCENT_ERROR("Entropy: hist must be a histogram");
3253   }
3254 
3255   conduit::Node *output = new conduit::Node();
3256   (*output)["value"] = field_entropy(*hist)["value"];
3257   (*output)["type"] = "double";
3258 
3259   resolve_symbol_result(graph(), output, this->name());
3260   set_output<conduit::Node>(output);
3261 }
3262 
3263 //-----------------------------------------------------------------------------
Pdf()3264 Pdf::Pdf() : Filter()
3265 {
3266   // empty
3267 }
3268 
3269 //-----------------------------------------------------------------------------
~Pdf()3270 Pdf::~Pdf()
3271 {
3272   // empty
3273 }
3274 
3275 //-----------------------------------------------------------------------------
3276 void
declare_interface(Node & i)3277 Pdf::declare_interface(Node &i)
3278 {
3279   i["type_name"] = "pdf";
3280   i["port_names"].append() = "hist";
3281   i["output_port"] = "true";
3282 }
3283 
3284 //-----------------------------------------------------------------------------
3285 bool
verify_params(const conduit::Node & params,conduit::Node & info)3286 Pdf::verify_params(const conduit::Node &params, conduit::Node &info)
3287 {
3288   info.reset();
3289   bool res = true;
3290   return res;
3291 }
3292 
3293 //-----------------------------------------------------------------------------
3294 void
execute()3295 Pdf::execute()
3296 {
3297   const conduit::Node *hist = input<conduit::Node>("hist");
3298 
3299   conduit::Node *output = new conduit::Node();
3300   (*output)["type"] = "histogram";
3301   (*output)["attrs/value/value"] = field_pdf(*hist)["value"];
3302   (*output)["attrs/value/type"] = "array";
3303   (*output)["attrs/min_val"] = (*hist)["attrs/min_val"];
3304   (*output)["attrs/max_val"] = (*hist)["attrs/max_val"];
3305   (*output)["attrs/num_bins"] = (*hist)["attrs/num_bins"];
3306 
3307   resolve_symbol_result(graph(), output, this->name());
3308   set_output<conduit::Node>(output);
3309 }
3310 
3311 //-----------------------------------------------------------------------------
Cdf()3312 Cdf::Cdf() : Filter()
3313 {
3314   // empty
3315 }
3316 
3317 //-----------------------------------------------------------------------------
~Cdf()3318 Cdf::~Cdf()
3319 {
3320   // empty
3321 }
3322 
3323 //-----------------------------------------------------------------------------
3324 void
declare_interface(Node & i)3325 Cdf::declare_interface(Node &i)
3326 {
3327   i["type_name"] = "cdf";
3328   i["port_names"].append() = "hist";
3329   i["output_port"] = "true";
3330 }
3331 
3332 //-----------------------------------------------------------------------------
3333 bool
verify_params(const conduit::Node & params,conduit::Node & info)3334 Cdf::verify_params(const conduit::Node &params, conduit::Node &info)
3335 {
3336   info.reset();
3337   bool res = true;
3338   return res;
3339 }
3340 
3341 //-----------------------------------------------------------------------------
3342 void
execute()3343 Cdf::execute()
3344 {
3345   const conduit::Node *hist = input<conduit::Node>("hist");
3346 
3347   conduit::Node *output = new conduit::Node();
3348   (*output)["type"] = "histogram";
3349   (*output)["attrs/value/value"] = field_cdf(*hist)["value"];
3350   (*output)["attrs/value/type"] = "array";
3351   (*output)["attrs/min_val"] = (*hist)["attrs/min_val"];
3352   (*output)["attrs/max_val"] = (*hist)["attrs/max_val"];
3353   (*output)["attrs/num_bins"] = (*hist)["attrs/num_bins"];
3354 
3355   resolve_symbol_result(graph(), output, this->name());
3356   set_output<conduit::Node>(output);
3357 }
3358 
3359 //-----------------------------------------------------------------------------
Quantile()3360 Quantile::Quantile() : Filter()
3361 {
3362   // empty
3363 }
3364 
3365 //-----------------------------------------------------------------------------
~Quantile()3366 Quantile::~Quantile()
3367 {
3368   // empty
3369 }
3370 
3371 //-----------------------------------------------------------------------------
3372 void
declare_interface(Node & i)3373 Quantile::declare_interface(Node &i)
3374 {
3375   i["type_name"] = "quantile";
3376   i["port_names"].append() = "cdf";
3377   i["port_names"].append() = "q";
3378   i["port_names"].append() = "interpolation";
3379   i["output_port"] = "true";
3380 }
3381 
3382 //-----------------------------------------------------------------------------
3383 bool
verify_params(const conduit::Node & params,conduit::Node & info)3384 Quantile::verify_params(const conduit::Node &params, conduit::Node &info)
3385 {
3386   info.reset();
3387   bool res = true;
3388   return res;
3389 }
3390 
3391 //-----------------------------------------------------------------------------
3392 void
execute()3393 Quantile::execute()
3394 {
3395   const conduit::Node *n_cdf = input<conduit::Node>("cdf");
3396   const conduit::Node *n_val = input<conduit::Node>("q");
3397   // optional inputs
3398   const conduit::Node *n_interpolation = input<conduit::Node>("interpolation");
3399 
3400   const double val = (*n_val)["value"].as_float64();
3401 
3402   if(val < 0 || val > 1)
3403   {
3404     ASCENT_ERROR("Quantile: val must be between 0 and 1");
3405   }
3406 
3407   // handle the optional inputs
3408   std::string interpolation = "linear";
3409   if(!n_interpolation->dtype().is_empty())
3410   {
3411     interpolation = (*n_interpolation)["value"].as_string();
3412     if(interpolation != "linear" && interpolation != "lower" &&
3413        interpolation != "higher" && interpolation != "midpoint" &&
3414        interpolation != "nearest")
3415     {
3416       ASCENT_ERROR("Known interpolation types are: linear, lower, higher, "
3417                    "midpoint, nearest");
3418     }
3419   }
3420 
3421   conduit::Node *output = new conduit::Node();
3422   (*output)["value"] = quantile(*n_cdf, val, interpolation)["value"];
3423   (*output)["type"] = "double";
3424 
3425   resolve_symbol_result(graph(), output, this->name());
3426   set_output<conduit::Node>(output);
3427 }
3428 
3429 //-----------------------------------------------------------------------------
BinByIndex()3430 BinByIndex::BinByIndex() : Filter()
3431 {
3432   // empty
3433 }
3434 
3435 //-----------------------------------------------------------------------------
~BinByIndex()3436 BinByIndex::~BinByIndex()
3437 {
3438   // empty
3439 }
3440 
3441 //-----------------------------------------------------------------------------
3442 void
declare_interface(Node & i)3443 BinByIndex::declare_interface(Node &i)
3444 {
3445   i["type_name"] = "bin_by_index";
3446   i["port_names"].append() = "hist";
3447   i["port_names"].append() = "bin";
3448   i["output_port"] = "true";
3449 }
3450 
3451 //-----------------------------------------------------------------------------
3452 bool
verify_params(const conduit::Node & params,conduit::Node & info)3453 BinByIndex::verify_params(const conduit::Node &params, conduit::Node &info)
3454 {
3455   info.reset();
3456   bool res = true;
3457   return res;
3458 }
3459 
3460 //-----------------------------------------------------------------------------
3461 void
execute()3462 BinByIndex::execute()
3463 {
3464   const conduit::Node *n_bin = input<conduit::Node>("bin");
3465   const conduit::Node *n_hist = input<conduit::Node>("hist");
3466 
3467   int num_bins = (*n_hist)["attrs/num_bins/value"].as_int32();
3468   int bin = (*n_bin)["value"].as_int32();
3469 
3470   if(bin < 0 || bin > num_bins - 1)
3471   {
3472     ASCENT_ERROR("BinByIndex: bin index must be within the bounds of hist [0, "
3473                  << num_bins - 1 << "]");
3474   }
3475 
3476   conduit::Node *output = new conduit::Node();
3477   const double *bins = (*n_hist)["attrs/value/value"].value();
3478   (*output)["value"] = bins[bin];
3479   (*output)["type"] = "double";
3480 
3481   resolve_symbol_result(graph(), output, this->name());
3482   set_output<conduit::Node>(output);
3483 }
3484 
3485 //-----------------------------------------------------------------------------
BinByValue()3486 BinByValue::BinByValue() : Filter()
3487 {
3488   // empty
3489 }
3490 
3491 //-----------------------------------------------------------------------------
~BinByValue()3492 BinByValue::~BinByValue()
3493 {
3494   // empty
3495 }
3496 
3497 //-----------------------------------------------------------------------------
3498 void
declare_interface(Node & i)3499 BinByValue::declare_interface(Node &i)
3500 {
3501   i["type_name"] = "bin_by_value";
3502   i["port_names"].append() = "hist";
3503   i["port_names"].append() = "val";
3504   i["output_port"] = "true";
3505 }
3506 
3507 //-----------------------------------------------------------------------------
3508 bool
verify_params(const conduit::Node & params,conduit::Node & info)3509 BinByValue::verify_params(const conduit::Node &params, conduit::Node &info)
3510 {
3511   info.reset();
3512   bool res = true;
3513   return res;
3514 }
3515 
3516 //-----------------------------------------------------------------------------
3517 void
execute()3518 BinByValue::execute()
3519 {
3520   const conduit::Node *n_val = input<conduit::Node>("val");
3521   const conduit::Node *n_hist = input<conduit::Node>("hist");
3522 
3523   double val = (*n_val)["value"].to_float64();
3524   double min_val = (*n_hist)["attrs/min_val/value"].to_float64();
3525   double max_val = (*n_hist)["attrs/max_val/value"].to_float64();
3526   int num_bins = (*n_hist)["attrs/num_bins/value"].as_int32();
3527 
3528   if(val < min_val || val > max_val)
3529   {
3530     ASCENT_ERROR("BinByValue: val must within the bounds of hist ["
3531                  << min_val << ", " << max_val << "]");
3532   }
3533 
3534   const double inv_delta = num_bins / (max_val - min_val);
3535   int bin = static_cast<int>((val - min_val) * inv_delta);
3536 
3537   conduit::Node *output = new conduit::Node();
3538   const double *bins = (*n_hist)["attrs/value/value"].value();
3539   (*output)["value"] = bins[bin];
3540   (*output)["type"] = "double";
3541 
3542   resolve_symbol_result(graph(), output, this->name());
3543   set_output<conduit::Node>(output);
3544 }
3545 
3546 //-----------------------------------------------------------------------------
FieldSum()3547 FieldSum::FieldSum() : Filter()
3548 {
3549   // empty
3550 }
3551 
3552 //-----------------------------------------------------------------------------
~FieldSum()3553 FieldSum::~FieldSum()
3554 {
3555   // empty
3556 }
3557 
3558 //-----------------------------------------------------------------------------
3559 void
declare_interface(Node & i)3560 FieldSum::declare_interface(Node &i)
3561 {
3562   i["type_name"] = "field_sum";
3563   i["port_names"].append() = "arg1";
3564   i["output_port"] = "true";
3565 }
3566 
3567 //-----------------------------------------------------------------------------
3568 bool
verify_params(const conduit::Node & params,conduit::Node & info)3569 FieldSum::verify_params(const conduit::Node &params, conduit::Node &info)
3570 {
3571   info.reset();
3572   bool res = true;
3573   return res;
3574 }
3575 
3576 //-----------------------------------------------------------------------------
3577 void
execute()3578 FieldSum::execute()
3579 {
3580   std::string field = (*input<Node>("arg1"))["value"].as_string();
3581 
3582   DataObject *data_object =
3583     graph().workspace().registry().fetch<DataObject>("dataset");
3584   const conduit::Node *const dataset = data_object->as_low_order_bp().get();
3585 
3586   conduit::Node *output = new conduit::Node();
3587   (*output)["value"] = field_sum(*dataset, field)["value"];
3588   (*output)["type"] = "double";
3589 
3590   resolve_symbol_result(graph(), output, this->name());
3591   set_output<conduit::Node>(output);
3592 }
3593 
3594 //-----------------------------------------------------------------------------
FieldNanCount()3595 FieldNanCount::FieldNanCount() : Filter()
3596 {
3597   // empty
3598 }
3599 
3600 //-----------------------------------------------------------------------------
~FieldNanCount()3601 FieldNanCount::~FieldNanCount()
3602 {
3603   // empty
3604 }
3605 
3606 //-----------------------------------------------------------------------------
3607 void
declare_interface(Node & i)3608 FieldNanCount::declare_interface(Node &i)
3609 {
3610   i["type_name"] = "field_nan_count";
3611   i["port_names"].append() = "arg1";
3612   i["output_port"] = "true";
3613 }
3614 
3615 //-----------------------------------------------------------------------------
3616 bool
verify_params(const conduit::Node & params,conduit::Node & info)3617 FieldNanCount::verify_params(const conduit::Node &params, conduit::Node &info)
3618 {
3619   info.reset();
3620   bool res = true;
3621   return res;
3622 }
3623 
3624 //-----------------------------------------------------------------------------
3625 void
execute()3626 FieldNanCount::execute()
3627 {
3628   std::string field = (*input<Node>("arg1"))["value"].as_string();
3629 
3630   DataObject *data_object =
3631     graph().workspace().registry().fetch<DataObject>("dataset");
3632   conduit::Node *dataset = data_object->as_low_order_bp().get();
3633 
3634   conduit::Node *output = new conduit::Node();
3635   (*output)["value"] = field_nan_count(*dataset, field)["value"];
3636   (*output)["type"] = "double";
3637 
3638   resolve_symbol_result(graph(), output, this->name());
3639   set_output<conduit::Node>(output);
3640 }
3641 
3642 //-----------------------------------------------------------------------------
FieldInfCount()3643 FieldInfCount::FieldInfCount() : Filter()
3644 {
3645   // empty
3646 }
3647 
3648 //-----------------------------------------------------------------------------
~FieldInfCount()3649 FieldInfCount::~FieldInfCount()
3650 {
3651   // empty
3652 }
3653 
3654 //-----------------------------------------------------------------------------
3655 void
declare_interface(Node & i)3656 FieldInfCount::declare_interface(Node &i)
3657 {
3658   i["type_name"] = "field_inf_count";
3659   i["port_names"].append() = "arg1";
3660   i["output_port"] = "true";
3661 }
3662 
3663 //-----------------------------------------------------------------------------
3664 bool
verify_params(const conduit::Node & params,conduit::Node & info)3665 FieldInfCount::verify_params(const conduit::Node &params, conduit::Node &info)
3666 {
3667   info.reset();
3668   bool res = true;
3669   return res;
3670 }
3671 
3672 //-----------------------------------------------------------------------------
3673 void
execute()3674 FieldInfCount::execute()
3675 {
3676   std::string field = (*input<Node>("arg1"))["value"].as_string();
3677 
3678   DataObject *data_object =
3679     graph().workspace().registry().fetch<DataObject>("dataset");
3680   conduit::Node *dataset = data_object->as_low_order_bp().get();
3681 
3682   conduit::Node *output = new conduit::Node();
3683   (*output)["value"] = field_inf_count(*dataset, field)["value"];
3684   (*output)["type"] = "double";
3685 
3686   resolve_symbol_result(graph(), output, this->name());
3687   set_output<conduit::Node>(output);
3688 }
3689 
3690 //-----------------------------------------------------------------------------
ArraySum()3691 ArraySum::ArraySum() : Filter()
3692 {
3693   // empty
3694 }
3695 
3696 //-----------------------------------------------------------------------------
~ArraySum()3697 ArraySum::~ArraySum()
3698 {
3699   // empty
3700 }
3701 
3702 //-----------------------------------------------------------------------------
3703 void
declare_interface(Node & i)3704 ArraySum::declare_interface(Node &i)
3705 {
3706   i["type_name"] = "array_sum";
3707   i["port_names"].append() = "arg1";
3708   i["output_port"] = "true";
3709 }
3710 
3711 //-----------------------------------------------------------------------------
3712 bool
verify_params(const conduit::Node & params,conduit::Node & info)3713 ArraySum::verify_params(const conduit::Node &params, conduit::Node &info)
3714 {
3715   info.reset();
3716   bool res = true;
3717   return res;
3718 }
3719 
3720 //-----------------------------------------------------------------------------
3721 void
execute()3722 ArraySum::execute()
3723 {
3724   conduit::Node *output = new conduit::Node();
3725   (*output)["value"] = array_sum((*input<Node>("arg1"))["value"])["value"];
3726   (*output)["type"] = "double";
3727 
3728   resolve_symbol_result(graph(), output, this->name());
3729   set_output<conduit::Node>(output);
3730 }
3731 //-----------------------------------------------------------------------------
PointAndAxis()3732 PointAndAxis::PointAndAxis() : Filter()
3733 {
3734   // empty
3735 }
3736 
3737 //-----------------------------------------------------------------------------
~PointAndAxis()3738 PointAndAxis::~PointAndAxis()
3739 {
3740   // empty
3741 }
3742 
3743 //-----------------------------------------------------------------------------
3744 void
declare_interface(Node & i)3745 PointAndAxis::declare_interface(Node &i)
3746 {
3747   i["type_name"] = "point_and_axis";
3748   i["port_names"].append() = "binning";
3749   i["port_names"].append() = "axis";
3750   i["port_names"].append() = "threshold";
3751   i["port_names"].append() = "point";
3752   i["port_names"].append() = "miss_value";
3753   i["port_names"].append() = "direction";
3754   i["output_port"] = "true";
3755 }
3756 
3757 //-----------------------------------------------------------------------------
3758 bool
verify_params(const conduit::Node & params,conduit::Node & info)3759 PointAndAxis::verify_params(const conduit::Node &params, conduit::Node &info)
3760 {
3761   info.reset();
3762   bool res = true;
3763   return res;
3764 }
3765 
3766 //-----------------------------------------------------------------------------
3767 void
execute()3768 PointAndAxis::execute()
3769 {
3770   conduit::Node &in_binning = *input<Node>("binning");
3771   conduit::Node &in_axis =  *input<Node>("axis");
3772   conduit::Node &in_threshold =  *input<Node>("threshold");
3773   conduit::Node &in_point =  *input<Node>("point");
3774   conduit::Node &n_miss_val =  *input<Node>("miss_value");
3775   conduit::Node &n_dir =  *input<Node>("direction");
3776   conduit::Node *output = new conduit::Node();
3777 
3778   const int num_axes = in_binning["attrs/bin_axes"].number_of_children();
3779   if(num_axes > 1)
3780   {
3781     ASCENT_ERROR("point_and_axis: only one axis is implemented");
3782   }
3783 
3784   int direction = 1;
3785   if(!n_dir.dtype().is_empty())
3786   {
3787     direction = n_dir["value"].to_int32();
3788     if(direction != 1 && direction != -1)
3789     {
3790       ASCENT_ERROR("point_and_axis: invalid direction `"<<direction<<"'."
3791                   <<" Valid directions are 1 or -1.");
3792     }
3793   }
3794 
3795   const double point = in_point["value"].to_float64();
3796   const double threshold = in_threshold["value"].to_float64();
3797 
3798   const conduit::Node &axis = in_binning["attrs/bin_axes/value"].child(0);
3799   const int num_bins = axis["num_bins"].to_int32();
3800   const double min_val = axis["min_val"].to_float64();
3801   const double max_val = axis["max_val"].to_float64();
3802   const double bin_size = (max_val - min_val) / double(num_bins);
3803 
3804   double *bins = in_binning["attrs/value/value"].value();
3805   double min_dist = std::numeric_limits<double>::max();
3806   int index = -1;
3807   for(int i = 0; i < num_bins; ++i)
3808   {
3809     double val = bins[i];
3810     if(val > threshold)
3811     {
3812       double left = min_val + double(i) * bin_size;
3813       double right = min_val + double(i+1) * bin_size;
3814       double center = left + (right-left) / 2.0;
3815       double dist = center - point;
3816       // skip if distance is behind
3817       bool behind = dist * double(direction) < 0;
3818 
3819       if(!behind && dist < min_dist)
3820       {
3821         min_dist = dist;
3822         index = i;
3823       }
3824     }
3825   }
3826 
3827   double bin_value = std::numeric_limits<double>::quiet_NaN();
3828 
3829   if(!n_miss_val.dtype().is_empty())
3830   {
3831     bin_value = n_miss_val["value"].to_float64();
3832   }
3833 
3834   // init with miss
3835   double bin_min = bin_value;
3836   double bin_max = bin_value;
3837   double bin_center = bin_value;
3838 
3839   if(index != -1)
3840   {
3841     bin_value = bins[index];
3842     bin_min = min_val + double(index) * bin_size;
3843     bin_max = min_val + double(index+1) * bin_size;
3844     bin_center = bin_min + (bin_max-bin_min) / 2.0;
3845   }
3846 
3847   (*output)["type"] = "bin";
3848 
3849   (*output)["attrs/value/value"] = bin_value;
3850   (*output)["attrs/value/type"] = "double";
3851 
3852   (*output)["attrs/min/value"] = bin_min;
3853   (*output)["attrs/min/type"] = "double";
3854 
3855   (*output)["attrs/max/value"] = bin_max;
3856   (*output)["attrs/max/type"] = "double";
3857 
3858   (*output)["attrs/center/value"] = bin_center;
3859   (*output)["attrs/center/type"] = "double";
3860 
3861   resolve_symbol_result(graph(), output, this->name());
3862   set_output<conduit::Node>(output);
3863 }
3864 
3865 //-----------------------------------------------------------------------------
MaxFromPoint()3866 MaxFromPoint::MaxFromPoint() : Filter()
3867 {
3868   // empty
3869 }
3870 
3871 //-----------------------------------------------------------------------------
~MaxFromPoint()3872 MaxFromPoint::~MaxFromPoint()
3873 {
3874   // empty
3875 }
3876 
3877 //-----------------------------------------------------------------------------
3878 void
declare_interface(Node & i)3879 MaxFromPoint::declare_interface(Node &i)
3880 {
3881   i["type_name"] = "max_from_point";
3882   i["port_names"].append() = "binning";
3883   i["port_names"].append() = "axis";
3884   i["port_names"].append() = "point";
3885   i["output_port"] = "true";
3886 }
3887 
3888 //-----------------------------------------------------------------------------
3889 bool
verify_params(const conduit::Node & params,conduit::Node & info)3890 MaxFromPoint::verify_params(const conduit::Node &params, conduit::Node &info)
3891 {
3892   info.reset();
3893   bool res = true;
3894   return res;
3895 }
3896 
3897 //-----------------------------------------------------------------------------
3898 void
execute()3899 MaxFromPoint::execute()
3900 {
3901   conduit::Node &in_binning = *input<Node>("binning");
3902   conduit::Node &in_axis =  *input<Node>("axis");
3903   conduit::Node &in_point =  *input<Node>("point");
3904   conduit::Node *output = new conduit::Node();
3905 
3906   const int num_axes = in_binning["attrs/bin_axes"].number_of_children();
3907   if(num_axes > 1)
3908   {
3909     ASCENT_ERROR("max_from_point: only one axis is implemented");
3910   }
3911 
3912   const double point = in_point["value"].to_float64();
3913 
3914   const conduit::Node &axis = in_binning["attrs/bin_axes/value"].child(0);
3915   const int num_bins = axis["num_bins"].to_int32();
3916   const double min_val = axis["min_val"].to_float64();
3917   const double max_val = axis["max_val"].to_float64();
3918   const double bin_size = (max_val - min_val) / double(num_bins);
3919 
3920   double *bins = in_binning["attrs/value/value"].value();
3921   double max_bin_val = std::numeric_limits<double>::lowest();
3922   double dist_value = 0;
3923   double min_dist = std::numeric_limits<double>::max();
3924   int index = -1;
3925   for(int i = 0; i < num_bins; ++i)
3926   {
3927     double val = bins[i];
3928     if(val >= max_bin_val)
3929     {
3930       double left = min_val + double(i) * bin_size;
3931       double right = min_val + double(i+1) * bin_size;
3932       double center = left + (right-left) / 2.0;
3933       double dist = fabs(center - point);
3934       if(val > max_bin_val ||
3935          ((dist < min_dist) && val == max_bin_val))
3936       {
3937         min_dist = dist;
3938         max_bin_val = val;
3939         dist_value = center - point;
3940         index = i;
3941       }
3942     }
3943   }
3944 
3945   double loc[3] = {0.0, 0.0, 0.0};
3946   std::string axis_str = in_axis["value"].as_string();
3947 
3948   if(axis_str == "z")
3949   {
3950     loc[2] = dist_value;
3951   }
3952   else if (axis_str == "y")
3953   {
3954     loc[1] = dist_value;
3955   }
3956   else
3957   {
3958     loc[0] = dist_value;
3959   }
3960 
3961   (*output)["type"] = "value_position";
3962   (*output)["attrs/value/value"] = max_bin_val;
3963   (*output)["attrs/value/type"] = "double";
3964   (*output)["attrs/position/value"].set(loc,3);
3965   (*output)["attrs/position/type"] = "vector";
3966 
3967   (*output)["value"] = min_dist;
3968   (*output)["type"] = "double";
3969 
3970   resolve_symbol_result(graph(), output, this->name());
3971   set_output<conduit::Node>(output);
3972 }
3973 
3974 //-----------------------------------------------------------------------------
Bin()3975 Bin::Bin() : Filter()
3976 {
3977   // empty
3978 }
3979 
3980 //-----------------------------------------------------------------------------
~Bin()3981 Bin::~Bin()
3982 {
3983   // empty
3984 }
3985 
3986 //-----------------------------------------------------------------------------
3987 void
declare_interface(Node & i)3988 Bin::declare_interface(Node &i)
3989 {
3990   i["type_name"] = "bin";
3991   i["port_names"].append() = "binning";
3992   i["port_names"].append() = "index";
3993   i["output_port"] = "true";
3994 }
3995 
3996 //-----------------------------------------------------------------------------
3997 bool
verify_params(const conduit::Node & params,conduit::Node & info)3998 Bin::verify_params(const conduit::Node &params, conduit::Node &info)
3999 {
4000   info.reset();
4001   bool res = true;
4002   return res;
4003 }
4004 
4005 //-----------------------------------------------------------------------------
4006 void
execute()4007 Bin::execute()
4008 {
4009   conduit::Node &in_binning = *input<Node>("binning");
4010   conduit::Node &in_index =  *input<Node>("index");
4011   conduit::Node *output = new conduit::Node();
4012 
4013   const int num_axes = in_binning["attrs/bin_axes"].number_of_children();
4014   if(num_axes > 1)
4015   {
4016     ASCENT_ERROR("bin: only one axis is implemented");
4017   }
4018 
4019   int bindex = in_index["value"].to_int32();
4020 
4021   const conduit::Node &axis = in_binning["attrs/bin_axes/value"].child(0);
4022   const int num_bins = axis["num_bins"].to_int32();
4023 
4024   if(bindex < 0 || bindex >= num_bins)
4025   {
4026     ASCENT_ERROR("bin: invalid bin "<<bindex<<"."
4027                 <<" Number of bins "<<num_bins);
4028   }
4029 
4030   const double min_val = axis["min_val"].to_float64();
4031   const double max_val = axis["max_val"].to_float64();
4032   const double bin_size = (max_val - min_val) / double(num_bins);
4033   double *bins = in_binning["attrs/value/value"].value();
4034 
4035   double left = min_val + double(bindex) * bin_size;
4036   double right = min_val + double(bindex+1) * bin_size;
4037   double center = left + (right-left) / 2.0;
4038   double val = bins[bindex];
4039 
4040   (*output)["type"] = "bin";
4041 
4042   (*output)["attrs/value/value"] = val;
4043   (*output)["attrs/value/type"] = "double";
4044 
4045   (*output)["attrs/min/value"] = left;
4046   (*output)["attrs/min/type"] = "double";
4047 
4048   (*output)["attrs/max/value"] = right;
4049   (*output)["attrs/max/type"] = "double";
4050 
4051   (*output)["attrs/center/value"] = center;
4052   (*output)["attrs/center/type"] = "double";
4053 
4054   resolve_symbol_result(graph(), output, this->name());
4055   set_output<conduit::Node>(output);
4056 }
4057 
4058 //-----------------------------------------------------------------------------
Lineout()4059 Lineout::Lineout() : Filter()
4060 {
4061   // empty
4062 }
4063 
4064 //-----------------------------------------------------------------------------
~Lineout()4065 Lineout::~Lineout()
4066 {
4067   // empty
4068 }
4069 
4070 //-----------------------------------------------------------------------------
4071 void
declare_interface(Node & i)4072 Lineout::declare_interface(Node &i)
4073 {
4074   i["type_name"] = "lineout";
4075   i["port_names"].append() = "samples";
4076   i["port_names"].append() = "start";
4077   i["port_names"].append() = "end";
4078   i["port_names"].append() = "fields";
4079   i["port_names"].append() = "empty_val";
4080   i["output_port"] = "true";
4081 }
4082 
4083 //-----------------------------------------------------------------------------
4084 bool
verify_params(const conduit::Node & params,conduit::Node & info)4085 Lineout::verify_params(const conduit::Node &params, conduit::Node &info)
4086 {
4087   info.reset();
4088   bool res = true;
4089   return res;
4090 }
4091 
4092 //-----------------------------------------------------------------------------
4093 void
execute()4094 Lineout::execute()
4095 {
4096 
4097 #if not defined(ASCENT_DRAY_ENABLED)
4098   ASCENT_ERROR("Lineout only supported when Devil Ray is built");
4099 #else
4100 
4101   conduit::Node &n_samples = *input<Node>("samples");
4102   int32 samples = n_samples["value"].to_int32();;
4103   if(samples < 1)
4104   {
4105     ASCENT_ERROR("Lineout: samples must be greater than zero: '"<<samples<<"'\n");
4106   }
4107 
4108   conduit::Node &n_start = *input<Node>("start");
4109   double *p_start = n_start["value"].as_float64_ptr();
4110 
4111   dray::Vec<dray::Float,3> start;
4112   start[0] = static_cast<dray::Float>(p_start[0]);
4113   start[1] = static_cast<dray::Float>(p_start[1]);
4114   start[2] = static_cast<dray::Float>(p_start[2]);
4115 
4116   conduit::Node &n_end= *input<Node>("end");
4117   double *p_end = n_end["value"].as_float64_ptr();
4118 
4119   dray::Vec<dray::Float,3> end;
4120   end[0] = static_cast<dray::Float>(p_end[0]);
4121   end[1] = static_cast<dray::Float>(p_end[1]);
4122   end[2] = static_cast<dray::Float>(p_end[2]);
4123 
4124   conduit::Node *output = new conduit::Node();
4125 
4126   DataObject *data_object =
4127     graph().workspace().registry().fetch<DataObject>("dataset");
4128   dray::Collection * collection = data_object->as_dray_collection().get();
4129 
4130   dray::Lineout lineout;
4131 
4132   lineout.samples(samples);
4133 
4134   conduit::Node &n_empty_val = *input<Node>("empty_val");
4135   if(!n_empty_val.dtype().is_empty())
4136   {
4137     double empty_val = n_empty_val["value"].to_float64();
4138     lineout.empty_val(empty_val);
4139   }
4140 
4141   // figure out the number of fields we will use
4142   conduit::Node &n_fields = (*input<Node>("fields"))["value"];
4143   const int num_fields = n_fields.number_of_children();
4144   if(num_fields > 0)
4145   {
4146     for(int i = 0; i < num_fields; ++i)
4147     {
4148       const conduit::Node &n_field = n_fields.child(i);
4149       if(n_field["type"].as_string() != "string")
4150       {
4151         ASCENT_ERROR("Lineout: field list item is not a string");
4152       }
4153 
4154       lineout.add_var(n_field["value"].as_string());
4155     }
4156   }
4157   else
4158   {
4159     std::set<std::string> field_names;
4160     // use all fields
4161     for(int i = 0; i < collection->size(); ++i)
4162     {
4163       dray::DataSet dset = collection->domain(i);
4164       std::vector<std::string> d_names = dset.fields();
4165       for(int n = 0; n < d_names.size(); ++n)
4166       {
4167         field_names.insert(d_names[n]);
4168       }
4169     }
4170     gather_strings(field_names);
4171   }
4172 
4173 
4174 
4175   lineout.add_line(start, end);
4176 
4177   dray::Lineout::Result res = lineout.execute(*collection);
4178   (*output)["type"] = "lineout";
4179   (*output)["attrs/empty_value/value"] = double(res.m_empty_val);
4180   (*output)["attrs/empty_value/type"] = "double";
4181   (*output)["attrs/samples/value"] = int(res.m_points_per_line);
4182   (*output)["attrs/samples/type"] = "int";
4183   // we only have one line so the size of points is the size of everything
4184   const int size = res.m_points.size();
4185   (*output)["attrs/coordinates/x/value"] = conduit::DataType::float64(size);
4186   (*output)["attrs/coordinates/x/type"] = "array";
4187   (*output)["attrs/coordinates/y/value"] = conduit::DataType::float64(size);
4188   (*output)["attrs/coordinates/y/type"] = "array";
4189   (*output)["attrs/coordinates/z/value"] = conduit::DataType::float64(size);
4190   (*output)["attrs/coordinates/z/type"] = "array";
4191   float64_array x = (*output)["attrs/coordinates/x/value"].value();
4192   float64_array y = (*output)["attrs/coordinates/y/value"].value();
4193   float64_array z = (*output)["attrs/coordinates/z/value"].value();
4194   for(int i = 0; i < size; ++i)
4195   {
4196     dray::Vec<dray::Float,3> p = res.m_points.get_value(i);
4197     x[i] = static_cast<double>(p[0]);
4198     y[i] = static_cast<double>(p[1]);
4199     z[i] = static_cast<double>(p[2]);
4200   }
4201 
4202   const int var_size = res.m_vars.size();
4203   for(int v = 0; v < var_size; ++v)
4204   {
4205     std::string var = res.m_vars[v];
4206     (*output)["attrs/vars/"+var+"/value"] = conduit::DataType::float64(size);
4207     (*output)["attrs/vars/"+var+"/type"] = "array";
4208     float64_array var_array = (*output)["attrs/vars/"+var+"/value"].value();
4209     for(int i = 0; i < size; ++i)
4210     {
4211       var_array[i] = static_cast<double>(res.m_values[v].get_value(i));
4212     }
4213   }
4214 
4215   resolve_symbol_result(graph(), output, this->name());
4216   set_output<conduit::Node>(output);
4217 #endif
4218 
4219 }
4220 
4221 //-----------------------------------------------------------------------------
Bounds()4222 Bounds::Bounds() : Filter()
4223 {
4224   // empty
4225 }
4226 
4227 //-----------------------------------------------------------------------------
~Bounds()4228 Bounds::~Bounds()
4229 {
4230   // empty
4231 }
4232 
4233 //-----------------------------------------------------------------------------
4234 void
declare_interface(Node & i)4235 Bounds::declare_interface(Node &i)
4236 {
4237   i["type_name"] = "bounds";
4238   i["port_names"].append() = "topology";
4239   i["output_port"] = "true";
4240 }
4241 
4242 //-----------------------------------------------------------------------------
4243 bool
verify_params(const conduit::Node & params,conduit::Node & info)4244 Bounds::verify_params(const conduit::Node &params, conduit::Node &info)
4245 {
4246   info.reset();
4247   bool res = true;
4248   return res;
4249 }
4250 
4251 //-----------------------------------------------------------------------------
4252 void
execute()4253 Bounds::execute()
4254 {
4255   conduit::Node &n_topology = *input<Node>("topology");
4256   conduit::Node *output = new conduit::Node();
4257 
4258   DataObject *data_object =
4259     graph().workspace().registry().fetch<DataObject>("dataset");
4260   const conduit::Node *const dataset = data_object->as_low_order_bp().get();
4261 
4262   std::set<std::string> topos;
4263 
4264   if(!n_topology.dtype().is_empty())
4265   {
4266     std::string topo = n_topology["value"].as_string();
4267     if(!has_topology(*dataset, topo))
4268     {
4269       std::set<std::string> names = topology_names(*dataset);
4270       std::stringstream msg;
4271       msg<<"Unknown topology: '"<<topo<<"'. Known topologies: [";
4272       for(auto &name : names)
4273       {
4274         msg<<" "<<name;
4275       }
4276       msg<<" ]";
4277       ASCENT_ERROR(msg.str());
4278     }
4279     topos.insert(topo);
4280   }
4281   else
4282   {
4283     topos = topology_names(*dataset);
4284   }
4285 
4286   double inf = std::numeric_limits<double>::infinity();
4287   double min_vec[3] = {inf, inf, inf};
4288   double max_vec[3] = {-inf, -inf, -inf};
4289   for(auto &topo_name : topos)
4290   {
4291     conduit::Node n_aabb = global_bounds(*dataset, topo_name);
4292     double *t_min = n_aabb["min_coords"].as_float64_ptr();
4293     double *t_max = n_aabb["max_coords"].as_float64_ptr();
4294     for(int i = 0; i < 3; ++i)
4295     {
4296       min_vec[i] = std::min(t_min[i],min_vec[i]);
4297       max_vec[i] = std::max(t_max[i],max_vec[i]);
4298     }
4299   }
4300 
4301   (*output)["type"] = "aabb";
4302   (*output)["attrs/min/value"].set(min_vec, 3);
4303   (*output)["attrs/min/type"] = "vector";
4304   (*output)["attrs/max/value"].set(max_vec, 3);
4305   (*output)["attrs/max/type"] = "vector";
4306 
4307   resolve_symbol_result(graph(), output, this->name());
4308   set_output<conduit::Node>(output);
4309 }
4310 
4311 //-----------------------------------------------------------------------------
Topo()4312 Topo::Topo() : Filter()
4313 {
4314   // empty
4315 }
4316 
4317 //-----------------------------------------------------------------------------
~Topo()4318 Topo::~Topo()
4319 {
4320   // empty
4321 }
4322 
4323 //-----------------------------------------------------------------------------
4324 void
declare_interface(Node & i)4325 Topo::declare_interface(Node &i)
4326 {
4327   i["type_name"] = "topo";
4328   i["port_names"].append() = "arg1";
4329   i["output_port"] = "true";
4330 }
4331 
4332 //-----------------------------------------------------------------------------
4333 bool
verify_params(const conduit::Node & params,conduit::Node & info)4334 Topo::verify_params(const conduit::Node &params, conduit::Node &info)
4335 {
4336   info.reset();
4337   bool res = true;
4338   return res;
4339 }
4340 
4341 //-----------------------------------------------------------------------------
4342 void
execute()4343 Topo::execute()
4344 {
4345   const conduit::Node *arg1 = input<Node>("arg1");
4346 
4347   const std::string topo = (*arg1)["value"].as_string();
4348 
4349   if(!graph().workspace().registry().has_entry("dataset"))
4350   {
4351     ASCENT_ERROR("Topo: Missing dataset");
4352   }
4353 
4354   DataObject *data_object =
4355     graph().workspace().registry().fetch<DataObject>("dataset");
4356   const conduit::Node *const dataset = data_object->as_low_order_bp().get();
4357 
4358   if(!has_topology(*dataset, topo))
4359   {
4360     std::set<std::string> names = topology_names(*dataset);
4361     std::stringstream msg;
4362     msg<<"Unknown topology: '"<<topo<<"'. Known topologies: [";
4363     for(auto &name : names)
4364     {
4365       msg<<" "<<name;
4366     }
4367     msg<<" ]";
4368     ASCENT_ERROR(msg.str());
4369   }
4370 
4371   conduit::Node *output = new conduit::Node();
4372   (*output)["value"] = topo;
4373   (*output)["type"] = "topo";
4374 
4375   resolve_symbol_result(graph(), output, this->name());
4376   set_output<conduit::Node>(output);
4377 }
4378 
4379 //-----------------------------------------------------------------------------
4380 
4381 //-----------------------------------------------------------------------------
Nan()4382 Nan::Nan() : Filter()
4383 {
4384   // empty
4385 }
4386 
4387 //-----------------------------------------------------------------------------
~Nan()4388 Nan::~Nan()
4389 {
4390   // empty
4391 }
4392 
4393 //-----------------------------------------------------------------------------
4394 void
declare_interface(Node & i)4395 Nan::declare_interface(Node &i)
4396 {
4397   i["type_name"] = "nan";
4398   i["port_names"] = DataType::empty();
4399   i["output_port"] = "true";
4400 }
4401 
4402 //-----------------------------------------------------------------------------
4403 bool
verify_params(const conduit::Node & params,conduit::Node & info)4404 Nan::verify_params(const conduit::Node &params, conduit::Node &info)
4405 {
4406   info.reset();
4407   bool res = true;
4408   return res;
4409 }
4410 
4411 //-----------------------------------------------------------------------------
4412 void
execute()4413 Nan::execute()
4414 {
4415   conduit::Node *output = new conduit::Node();
4416 
4417   (*output)["type"] = "double";
4418   (*output)["value"] = nan("");;
4419   set_output<conduit::Node>(output);
4420 }
4421 
4422 //-----------------------------------------------------------------------------
Replace()4423 Replace::Replace() : Filter()
4424 {
4425   // empty
4426 }
4427 
4428 //-----------------------------------------------------------------------------
~Replace()4429 Replace::~Replace()
4430 {
4431   // empty
4432 }
4433 
4434 //-----------------------------------------------------------------------------
4435 void
declare_interface(Node & i)4436 Replace::declare_interface(Node &i)
4437 {
4438   i["type_name"] = "replace";
4439   i["port_names"].append() = "arg1";
4440   i["port_names"].append() = "find";
4441   i["port_names"].append() = "replace";
4442   i["output_port"] = "true";
4443 }
4444 
4445 //-----------------------------------------------------------------------------
4446 bool
verify_params(const conduit::Node & params,conduit::Node & info)4447 Replace::verify_params(const conduit::Node &params, conduit::Node &info)
4448 {
4449   info.reset();
4450   bool res = true;
4451   return res;
4452 }
4453 
4454 //-----------------------------------------------------------------------------
4455 void
execute()4456 Replace::execute()
4457 {
4458   const conduit::Node *n_array= input<Node>("arg1");
4459   const conduit::Node *n_find = input<Node>("find");
4460   const conduit::Node *n_replace = input<Node>("replace");
4461 
4462   if(n_array->fetch_existing("type").as_string() != "array")
4463   {
4464     ASCENT_ERROR("replace is not an array");
4465   }
4466 
4467   if(n_find->fetch_existing("type").as_string() != "double")
4468   {
4469     ASCENT_ERROR("'find' is not a double");
4470   }
4471 
4472   if(n_replace->fetch_existing("type").as_string() != "double")
4473   {
4474     ASCENT_ERROR("'replace' is not a double");
4475   }
4476 
4477   conduit::Node *output = new conduit::Node();
4478   // copy the input into the ouptut
4479   *output = *n_array;
4480   conduit::Node &array = output->fetch_existing("value");
4481 
4482   if(!array.dtype().is_float64())
4483   {
4484     ASCENT_ERROR("Replace is only implemented for doubles");
4485   }
4486 
4487   const int size = array.dtype().number_of_elements();
4488   conduit::float64 *ptr =  array.as_float64_ptr();
4489   const double find = n_find->fetch_existing("value").to_float64();
4490   const double replace = n_replace->fetch_existing("value").to_float64();
4491 
4492   bool find_nan = find != find;
4493 
4494   if( !find_nan)
4495   {
4496     for(int i = 0; i < size; ++i)
4497     {
4498       if(ptr[i] == find)
4499       {
4500         ptr[i] = replace;
4501       }
4502     }
4503   }
4504   else
4505   {
4506     for(int i = 0; i < size; ++i)
4507     {
4508       if(ptr[i] != ptr[i])
4509       {
4510         ptr[i] = replace;
4511       }
4512     }
4513   }
4514 
4515   set_output<conduit::Node>(output);
4516 }
4517 
4518 
4519 };
4520 //-----------------------------------------------------------------------------
4521 // -- end ascent::runtime::expressions --
4522 //-----------------------------------------------------------------------------
4523 
4524 //-----------------------------------------------------------------------------
4525 };
4526 //-----------------------------------------------------------------------------
4527 // -- end ascent::runtime --
4528 //-----------------------------------------------------------------------------
4529 
4530 //-----------------------------------------------------------------------------
4531 };
4532 //-----------------------------------------------------------------------------
4533 // -- end ascent:: --
4534 //-----------------------------------------------------------------------------
4535 
4536