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: t_ascent_render_3d.cpp
10 ///
11 //-----------------------------------------------------------------------------
12 
13 #include "gtest/gtest.h"
14 
15 #include <ascent_expression_eval.hpp>
16 #include <ascent_hola.hpp>
17 
18 #include <cmath>
19 #include <iostream>
20 
21 #include <conduit_blueprint.hpp>
22 
23 #include "t_config.hpp"
24 #include "t_utils.hpp"
25 
26 using namespace std;
27 using namespace conduit;
28 using namespace ascent;
29 
30 index_t EXAMPLE_MESH_SIDE_DIM = 20;
31 
TEST(ascent_jit_expressions,derived_support_test)32 TEST(ascent_jit_expressions, derived_support_test)
33 {
34 
35   Node n;
36   ascent::about(n);
37   // only run this test if ascent was built with jit support
38   if(n["runtimes/ascent/jit/status"].as_string() == "disabled")
39   {
40       ASCENT_INFO("Ascent JIT support disabled, skipping test\n");
41       return;
42   }
43 
44   Node data;
45   conduit::blueprint::mesh::examples::braid("uniform",
46                                             EXAMPLE_MESH_SIDE_DIM,
47                                             EXAMPLE_MESH_SIDE_DIM,
48                                             EXAMPLE_MESH_SIDE_DIM,
49                                             data);
50 
51   // ascent normally adds this but we are doing an end around
52   data["state/domain_id"] = 0;
53   Node multi_dom;
54   blueprint::mesh::to_multi_domain(data, multi_dom);
55 
56   runtime::expressions::register_builtin();
57   runtime::expressions::ExpressionEval eval(&multi_dom);
58 
59   conduit::Node res;
60   std::string expr;
61 
62   expr = "builtin_avg = avg(sin(field('radial')))\n"
63          "num_elements = sum(derived_field(1.0, 'mesh', 'element'))\n"
64          "manual_avg = sum(sin(field('radial'))) / num_elements\n"
65          "builtin_avg == manual_avg";
66 
67   bool threw = false;
68   try
69   {
70     res = eval.evaluate(expr);
71   }
72   catch(...)
73   {
74     threw = true;
75   }
76 
77   if(n["runtimes/ascent/jit/status"].as_string() == "disabled")
78   {
79     EXPECT_TRUE(threw);
80   }
81   else
82   {
83     EXPECT_FALSE(threw);
84   }
85 }
86 
TEST(ascent_expressions,derived_simple)87 TEST(ascent_expressions, derived_simple)
88 {
89   Node n;
90   ascent::about(n);
91 
92   // only run this test if ascent was built with jit support
93   if(n["runtimes/ascent/jit/status"].as_string() == "disabled")
94   {
95       ASCENT_INFO("Ascent JIT support disabled, skipping test\n");
96       return;
97   }
98 
99   Node data;
100   conduit::blueprint::mesh::examples::braid("uniform",
101                                             EXAMPLE_MESH_SIDE_DIM,
102                                             EXAMPLE_MESH_SIDE_DIM,
103                                             EXAMPLE_MESH_SIDE_DIM,
104                                             data);
105 
106   // ascent normally adds this but we are doing an end around
107   data["state/domain_id"] = 0;
108   Node multi_dom;
109   blueprint::mesh::to_multi_domain(data, multi_dom);
110 
111   runtime::expressions::register_builtin();
112   runtime::expressions::ExpressionEval eval(&multi_dom);
113 
114   conduit::Node res;
115   std::string expr;
116 
117   expr = "avg(topo('mesh').cell.x)";
118   res = eval.evaluate(expr);
119   const double tiny = 1e-10;
120   std::cout<<std::abs(res["value"].to_float64())<<"\n";
121   EXPECT_EQ(std::abs(res["value"].to_float64()) < tiny, true);
122   EXPECT_EQ(res["type"].as_string(), "double");
123 
124   expr = "min_val = min(field('braid')).value\n"
125          "max_val = max(field('braid')).value\n"
126          "norm_field = (field('braid') - min_val) / (max_val - min_val)\n"
127          "not_between_0_1 = not (norm_field >= 0 and norm_field <= 1)\n"
128          "sum(not_between_0_1)";
129 
130 
131   res = eval.evaluate(expr, "bananas");
132   res = eval.evaluate(expr);
133   EXPECT_EQ(res["value"].to_float64(), 0);
134   EXPECT_EQ(res["type"].as_string(), "double");
135 
136   res = eval.evaluate(expr);
137 
138   expr = "builtin_avg = avg(sin(field('radial')))\n"
139          "num_elements = sum(derived_field(1.0, 'mesh', 'element'))\n"
140          "manual_avg = sum(sin(field('radial'))) / num_elements\n"
141          "builtin_avg == manual_avg";
142   res = eval.evaluate(expr);
143   EXPECT_EQ(res["type"].as_string(), "bool");
144 
145   Node last;
146   runtime::expressions::ExpressionEval::get_last(last);
147   double manual = last["manual_avg/100/value"].to_float64();
148   double builtin = last["builtin_avg/100/value"].to_float64();
149   EXPECT_NEAR(manual, builtin, 1e-8);
150 }
151 
152 //-----------------------------------------------------------------------------
TEST(ascent_expressions,basic_derived_expressions)153 TEST(ascent_expressions, basic_derived_expressions)
154 {
155   Node n;
156   ascent::about(n);
157   // only run this test if ascent was built with jit support
158   if(n["runtimes/ascent/jit/status"].as_string() == "disabled")
159   {
160       ASCENT_INFO("Ascent JIT support disabled, skipping test\n");
161       return;
162   }
163 
164   //
165   // Create an example mesh.
166   //
167   Node data;
168   conduit::blueprint::mesh::examples::braid("uniform",
169                                             EXAMPLE_MESH_SIDE_DIM,
170                                             EXAMPLE_MESH_SIDE_DIM,
171                                             EXAMPLE_MESH_SIDE_DIM,
172                                             data);
173   // ascent normally adds this but we are doing an end around
174   data["state/domain_id"] = 0;
175   Node multi_dom;
176   blueprint::mesh::to_multi_domain(data, multi_dom);
177 
178   runtime::expressions::register_builtin();
179   runtime::expressions::ExpressionEval eval(&multi_dom);
180 
181   conduit::Node res;
182   std::string expr;
183 
184   // expr = "1 + field('braid') + 1";
185   //    +
186   //  /   \
187   //  1   + return_type: jitable
188   //     / \
189   // field  1
190   //
191   // field + field + 1
192   //
193   //       +
194   //     /   \
195   // field   + return_type: jitable
196   //        / \
197   //    field  1
198   //
199   // max(field + 1)
200   //
201   //     max
202   //      |
203   //      + jitable -> field
204   //     / \
205   // field  1
206   //
207 
208   expr = "f1 = max((2.0 + 1) / 0.5 + field('braid'), 0.0)\n"
209          "sum(f1 < 0)";
210   res = eval.evaluate(expr);
211   EXPECT_EQ(res["value"].to_float64(), 0);
212   EXPECT_EQ(res["type"].as_string(), "double");
213 
214   expr = "method1 = field('braid') + 1 + field('braid') + 1\n"
215          "method2 = 2 * field('braid') + 2\n"
216          "bool_field = method1 != method2\n"
217          "sum(bool_field) == 0";
218 
219   res = eval.evaluate(expr);
220   EXPECT_EQ(res["value"].to_uint8(), 1);
221   EXPECT_EQ(res["type"].as_string(), "bool");
222 
223   // errors
224   // scalar * vector fields
225   bool threw = false;
226   try
227   {
228     expr = "field('braid') * field('vel')";
229     eval.evaluate(expr);
230   }
231   catch(...)
232   {
233     threw = true;
234   }
235   EXPECT_EQ(threw, true);
236 
237   // incompatible number of entries
238   threw = false;
239   try
240   {
241     expr = "field('braid') * field('radial')";
242     eval.evaluate(expr);
243   }
244   catch(...)
245   {
246     threw = true;
247   }
248   EXPECT_EQ(threw, true);
249 
250 }
251 
252 //-----------------------------------------------------------------------------
253 
TEST(ascent_expressions,derived_mesh_specific_paths)254 TEST(ascent_expressions, derived_mesh_specific_paths)
255 {
256   Node n;
257   ascent::about(n);
258   // only run this test if ascent was built with jit support
259   if(n["runtimes/ascent/jit/status"].as_string() == "disabled")
260   {
261       ASCENT_INFO("Ascent JIT support disabled, skipping test\n");
262       return;
263   }
264 
265   // Run on multiple meshes
266   const std::vector<long long> mesh_3d =
267       {EXAMPLE_MESH_SIDE_DIM, EXAMPLE_MESH_SIDE_DIM, EXAMPLE_MESH_SIDE_DIM};
268   const std::vector<long long> mesh_2d =
269       {EXAMPLE_MESH_SIDE_DIM, EXAMPLE_MESH_SIDE_DIM, 1};
270 
271   const std::vector<std::pair<std::string,std::vector<long long>>> meshes =
272     {
273       {"uniform", mesh_2d},
274       {"uniform", mesh_3d},
275       {"rectilinear", mesh_3d},
276       {"rectilinear", mesh_2d},
277       {"structured", mesh_3d},
278       {"structured", mesh_2d},
279       {"tets", mesh_3d},
280       {"hexs", mesh_3d},
281       {"tris", mesh_2d},
282       {"quads", mesh_2d}
283     };
284 
285   for(const auto mesh : meshes)
286   {
287     const std::string mesh_type = mesh.first;
288     const std::vector<long long> dims = mesh.second;
289 
290     std::cout<<"Running mesh type "<<mesh_type<<"\n";
291     std::cout<<"Mesh dims "<<dims[0]<<" "<<dims[1]<<" "<<dims[2]<<"\n\n";
292 
293     Node data;
294     conduit::blueprint::mesh::examples::braid(
295         mesh_type, dims[0], dims[1], dims[2], data);
296     //std::cout<<data.to_summary_string()<<"\n";
297 
298     // ascent normally adds this but we are doing an end around
299     data["state/domain_id"] = 0;
300     Node multi_dom;
301     blueprint::mesh::to_multi_domain(data, multi_dom);
302 
303     runtime::expressions::register_builtin();
304     runtime::expressions::ExpressionEval eval(&multi_dom);
305 
306     conduit::Node res;
307     std::string expr;
308 
309     expr = "min_val = min(field('braid')).value\n"
310            "max_val = max(field('braid')).value\n"
311            "norm_field = (field('braid') - min_val) / (max_val - min_val)\n"
312            "not_between_0_1 = not (norm_field >= 0 and norm_field <= 1)\n"
313            "sum(not_between_0_1)";
314     res = eval.evaluate(expr);
315     EXPECT_EQ(res["value"].to_float64(), 0);
316     EXPECT_EQ(res["type"].as_string(), "double");
317 
318     expr = "avg(topo('mesh').cell.x)";
319     res = eval.evaluate(expr);
320     const double tiny = 1e-10;
321     EXPECT_EQ(std::abs(res["value"].to_float64()) < tiny, true);
322     EXPECT_EQ(res["type"].as_string(), "double");
323 
324     expr = "builtin_avg = avg(sin(field('radial')))\n"
325            "num_elements = sum(derived_field(1.0, 'mesh', 'element'))\n"
326            "manual_avg = sum(sin(field('radial'))) / num_elements\n"
327            "builtin_avg == manual_avg";
328     res = eval.evaluate(expr);
329 
330     EXPECT_EQ(res["type"].as_string(), "bool");
331 
332     Node last;
333     runtime::expressions::ExpressionEval::get_last(last);
334     double manual = last["manual_avg/100/value"].to_float64();
335     double builtin = last["builtin_avg/100/value"].to_float64();
336     EXPECT_NEAR(manual, builtin, 1e-8);
337 
338     // apparently vel is element assoc
339     if(dims[2] != 1 && (mesh_type == "uniform" || mesh_type == "rectilinear"))
340     {
341       expr = "builtin_vort = curl(field('vel'))\n"
342              "du = gradient(field('vel', 'u'))\n"
343              "dv = gradient(field('vel', 'v'))\n"
344              "dw = gradient(field('vel', 'w'))\n"
345              "w_x = dw.y - dv.z\n"
346              "w_y = du.z - dw.x\n"
347              "w_z = dv.x - du.y\n"
348              "not_eq = not (builtin_vort.x == w_x and\n"
349              "              builtin_vort.y == w_y and\n"
350              "              builtin_vort.z == w_z)\n"
351              "sum(not_eq)";
352       res = eval.evaluate(expr);
353       EXPECT_EQ(res["value"].to_float64(), 0);
354       EXPECT_EQ(res["type"].as_string(), "double");
355     }
356 
357     // test the 2d code
358     if(dims[2] == 1 || dims[2] == 0)
359     {
360       expr = "sum(topo('mesh').cell.area)";
361       res = eval.evaluate(expr);
362       EXPECT_NEAR(res["value"].to_float64(), 400.0, 1e-8);
363 
364       expr = "topo('mesh').cell.volume";
365       EXPECT_ANY_THROW(eval.evaluate(expr));
366     }
367     else
368     {
369       expr = "sum(topo('mesh').cell.area)";
370       EXPECT_ANY_THROW(eval.evaluate(expr));
371 
372       expr = "sum(topo('mesh').cell.volume)";
373       res = eval.evaluate(expr);
374       EXPECT_NEAR(res["value"].to_float64(), 8000.0, 1e-8);
375     }
376 
377     if(mesh_type == "uniform" || mesh_type == "rectilinear" ||
378        mesh_type == "structured")
379     {
380       // element to vertex
381       expr = "recenter(field('radial') + 1)";
382       eval.evaluate(expr);
383     }
384 
385     // vertex to element
386     expr = "recenter(field('braid'))";
387     eval.evaluate(expr);
388 
389   }
390 }
391 //-----------------------------------------------------------------------------
392 
TEST(ascent_expressions,braid_sample)393 TEST(ascent_expressions, braid_sample)
394 {
395   Node n;
396   ascent::about(n);
397   // only run this test if ascent was built with jit support
398   if(n["runtimes/ascent/jit/status"].as_string() == "disabled")
399   {
400       ASCENT_INFO("Ascent JIT support disabled, skipping test\n");
401       return;
402   }
403     // only run this test if ascent was built with vtkm support
404   if(n["runtimes/ascent/vtkm/status"].as_string() == "disabled")
405   {
406       ASCENT_INFO("Ascent vtkm support disabled, skipping test");
407       return;
408   }
409 
410   conduit::Node data, multi_dom;
411   conduit::blueprint::mesh::examples::braid("structured",
412                                             EXAMPLE_MESH_SIDE_DIM,
413                                             EXAMPLE_MESH_SIDE_DIM,
414                                             EXAMPLE_MESH_SIDE_DIM,
415                                             data);
416   data["state/domain_id"] = 0;
417   blueprint::mesh::to_multi_domain(data, multi_dom);
418 
419 
420   const std::string output_path = prepare_output_dir();
421 
422   std::string output_image =
423       conduit::utils::join_file_path(output_path, "tout_half_braid_");
424 
425   conduit::Node actions;
426 
427   conduit::Node pipelines;
428   // pipeline 1
429   pipelines["pl1/f1/type"] = "expression";
430   // filter knobs
431   conduit::Node &expr_params = pipelines["pl1/f1/params"];
432   expr_params["expression"] = "field('braid') / 2.0";
433   expr_params["name"] = "half";
434 
435   conduit::Node &add_pipelines = actions.append();
436   add_pipelines["action"] = "add_pipelines";
437   add_pipelines["pipelines"] = pipelines;
438 
439   conduit::Node scenes;
440   scenes["s1/plots/p1/type"] = "pseudocolor";
441   scenes["s1/plots/p1/field"] = "half";
442   scenes["s1/plots/p1/pipeline"] = "pl1";
443   scenes["s1/renders/r1/image_prefix"] = output_image;
444 
445   conduit::Node &add_plots = actions.append();
446   add_plots["action"] = "add_scenes";
447   add_plots["scenes"] = scenes;
448 
449   //
450   // Run Ascent
451   //
452 
453   Ascent ascent;
454 
455   Node ascent_opts;
456   ascent_opts["ascent_info"] = "verbose";
457   ascent_opts["timings"] = "true";
458   ascent_opts["runtime/type"] = "ascent";
459 
460   ascent.open(ascent_opts);
461   ascent.publish(multi_dom);
462   ascent.execute(actions);
463   ascent.close();
464 
465   EXPECT_TRUE(check_test_image(output_image, 0.1));
466 }
467 
TEST(ascent_expressions,multi_topos)468 TEST(ascent_expressions, multi_topos)
469 {
470   Node n;
471   ascent::about(n);
472   if(n["runtimes/ascent/jit/status"].as_string() == "disabled")
473   {
474       ASCENT_INFO("Ascent JIT support disabled, skipping test\n");
475       return;
476   }
477     // only run this test if ascent was built with vtkm support
478   if(n["runtimes/ascent/vtkm/status"].as_string() == "disabled")
479   {
480       ASCENT_INFO("Ascent vtkm support disabled, skipping test");
481       return;
482   }
483   conduit::Node multi_dom;
484   conduit::Node &dom1 = multi_dom.append();
485   conduit::blueprint::mesh::examples::braid("structured",
486                                             EXAMPLE_MESH_SIDE_DIM,
487                                             EXAMPLE_MESH_SIDE_DIM,
488                                             EXAMPLE_MESH_SIDE_DIM,
489                                             dom1);
490 
491   conduit::Node &dom2 = multi_dom.append();
492   conduit::blueprint::mesh::examples::braid("uniform",
493                                             EXAMPLE_MESH_SIDE_DIM,
494                                             EXAMPLE_MESH_SIDE_DIM,
495                                             EXAMPLE_MESH_SIDE_DIM,
496                                             dom2);
497 
498   const std::string output_path = prepare_output_dir();
499 
500   std::string output_image =
501       conduit::utils::join_file_path(output_path, "tout_vol");
502 
503   conduit::Node actions;
504 
505   conduit::Node pipelines;
506   // pipeline 1
507   pipelines["pl1/f1/type"] = "expression";
508   // filter knobs
509   conduit::Node &expr_params = pipelines["pl1/f1/params"];
510   expr_params["expression"] = "topo('mesh').cell.volume";
511   expr_params["name"] = "vol";
512 
513   conduit::Node &add_pipelines = actions.append();
514   add_pipelines["action"] = "add_pipelines";
515   add_pipelines["pipelines"] = pipelines;
516 
517   conduit::Node scenes;
518   scenes["s1/plots/p1/type"] = "pseudocolor";
519   scenes["s1/plots/p1/field"] = "vol";
520   scenes["s1/plots/p1/pipeline"] = "pl1";
521   scenes["s1/renders/r1/image_prefix"] = output_image;
522 
523   conduit::Node &add_plots = actions.append();
524   add_plots["action"] = "add_scenes";
525   add_plots["scenes"] = scenes;
526 
527   //
528   // Run Ascent
529   //
530 
531   Ascent ascent;
532 
533   Node ascent_opts;
534   ascent_opts["ascent_info"] = "verbose";
535   ascent_opts["timings"] = "true";
536   ascent_opts["runtime/type"] = "ascent";
537 
538   ascent.open(ascent_opts);
539   ascent.publish(multi_dom);
540   ascent.execute(actions);
541   ascent.close();
542 
543   EXPECT_TRUE(check_test_image(output_image, 0.1));
544 }
545 //-----------------------------------------------------------------------------
546 
547 int
main(int argc,char * argv[])548 main(int argc, char *argv[])
549 {
550   int result = 0;
551 
552   ::testing::InitGoogleTest(&argc, argv);
553 
554   // allow override of the data size via the command line
555   if(argc == 2)
556   {
557     EXAMPLE_MESH_SIDE_DIM = atoi(argv[1]);
558   }
559 
560   result = RUN_ALL_TESTS();
561   return result;
562 }
563