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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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