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