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_jit_topology.cpp
10 ///
11 //-----------------------------------------------------------------------------
12 
13 #include "ascent_jit_topology.hpp"
14 #include <ascent_logging.hpp>
15 #include "ascent_blueprint_topologies.hpp"
16 #include <math.h>
17 #include <cmath> // for std::pow()
18 
19 //-----------------------------------------------------------------------------
20 // -- begin ascent:: --
21 //-----------------------------------------------------------------------------
22 namespace ascent
23 {
24 
25 //-----------------------------------------------------------------------------
26 // -- begin ascent::runtime --
27 //-----------------------------------------------------------------------------
28 namespace runtime
29 {
30 
31 //-----------------------------------------------------------------------------
32 // -- begin ascent::runtime::expressions--
33 //-----------------------------------------------------------------------------
34 namespace expressions
35 {
36 
TopologyCode(const std::string & topo_name,const conduit::Node & domain,const ArrayCode & array_code)37 TopologyCode::TopologyCode(const std::string &topo_name,
38                            const conduit::Node &domain,
39                            const ArrayCode &array_code)
40     : topo_name(topo_name),
41       domain(domain),
42       array_code(array_code),
43       math_code()
44 {
45   const conduit::Node &n_topo = domain["topologies/" + topo_name];
46   const std::string coords_name = n_topo["coordset"].as_string();
47   this->topo_type = n_topo["type"].as_string();
48   this->num_dims = topo_dim(topo_name, domain);
49   if(topo_type == "unstructured")
50   {
51     this->shape =
52         domain["topologies/" + topo_name + "/elements/shape"].as_string();
53     if(shape == "polygonal")
54     {
55       // multiple shapes
56       this->shape_size = -1;
57     }
58     else if(shape == "polyhedral")
59     {
60       const std::string &subelement_shape =
61           domain["topologies/" + topo_name + "/subelements/shape"].as_string();
62       if(subelement_shape != "polygonal")
63       {
64         // shape_size becomes the number of vertices for the subelements
65         this->shape_size = get_num_vertices(shape);
66       }
67       else
68       {
69         this->shape_size = -1;
70       }
71     }
72     else
73     {
74       // single shape
75       this->shape_size = get_num_vertices(shape);
76     }
77   }
78   else
79   {
80     // uniform, rectilinear, structured
81     this->shape_size = static_cast<int>(std::pow(2, num_dims));
82   }
83 }
84 
85 void
element_idx(InsertionOrderedSet<std::string> & code) const86 TopologyCode::element_idx(InsertionOrderedSet<std::string> &code) const
87 {
88   if(topo_type == "uniform" || topo_type == "rectilinear" ||
89      topo_type == "structured")
90   {
91     code.insert({"int " + topo_name + "_element_idx[" +
92                      std::to_string(num_dims) + "];\n",
93                  topo_name + "_element_idx[0] = item % (" + topo_name +
94                      "_dims_i - 1);\n"});
95 
96     if(num_dims >= 2)
97     {
98       code.insert(topo_name + "_element_idx[1] = (item / (" + topo_name +
99                   "_dims_i - 1)) % (" + topo_name + "_dims_j - 1);\n");
100     }
101     if(num_dims == 3)
102     {
103       code.insert(topo_name + "_element_idx[2] = item / ((" + topo_name +
104                   "_dims_i - 1) * (" + topo_name + "_dims_j - 1));\n");
105     }
106   }
107   else
108   {
109     ASCENT_ERROR("element_idx for unstructured is not implemented.");
110   }
111 }
112 
113 // vertices are ordered in the VTK format
114 // https://vtk.org/wp-content/uploads/2015/04/file-formats.pdf
115 // I'm also assuming the x,y,z axis shown to the left of VTK_HEXAHEDRON
116 void
structured_vertices(InsertionOrderedSet<std::string> & code) const117 TopologyCode::structured_vertices(InsertionOrderedSet<std::string> &code) const
118 {
119   if(topo_type != "uniform" && topo_type != "rectilinear" &&
120      topo_type != "structured")
121   {
122     ASCENT_ERROR("The function structured_vertices only supports uniform, "
123                  "rectilinear, and structured topologies.");
124   }
125   element_idx(code);
126 
127   // vertex indices
128   code.insert("int " + topo_name + "_vertices[" + std::to_string(shape_size) +
129               "];\n");
130   if(num_dims == 1)
131   {
132     code.insert(
133         {topo_name + "_vertices[0] = " + topo_name + "_element_idx[0];\n",
134          topo_name + "_vertices[1] = " + topo_name + "_vertices[0] + 1;\n"});
135   }
136   else if(num_dims == 2)
137   {
138     code.insert(
139         {topo_name + "_vertices[0] = " + topo_name + "_element_idx[1] * " +
140              topo_name + "_dims_i + " + topo_name + "_element_idx[0];\n",
141          topo_name + "_vertices[1] = " + topo_name + "_vertices[0] + 1;\n",
142          topo_name + "_vertices[2] = " + topo_name + "_vertices[1] + " +
143              topo_name + "_dims_i;\n",
144          topo_name + "_vertices[3] = " + topo_name + "_vertices[2] - 1;\n"});
145   }
146   else if(num_dims == 3)
147   {
148     code.insert({
149         topo_name + "_vertices[0] = (" + topo_name + "_element_idx[2] * " +
150             topo_name + "_dims_j + " + topo_name + "_element_idx[1]) * " +
151             topo_name + "_dims_i + " + topo_name + "_element_idx[0];\n",
152         topo_name + "_vertices[1] = " + topo_name + "_vertices[0] + 1;\n",
153         topo_name + "_vertices[2] = " + topo_name + "_vertices[1] + " +
154             topo_name + "_dims_i;\n",
155         topo_name + "_vertices[3] = " + topo_name + "_vertices[2] - 1;\n",
156         topo_name + "_vertices[4] = " + topo_name + "_vertices[0] + " +
157             topo_name + "_dims_i * " + topo_name + "_dims_j;\n",
158         topo_name + "_vertices[5] = " + topo_name + "_vertices[4] + 1;\n",
159         topo_name + "_vertices[6] = " + topo_name + "_vertices[5] + " +
160             topo_name + "_dims_i;\n",
161         topo_name + "_vertices[7] = " + topo_name + "_vertices[6] - 1;\n",
162     });
163   }
164 }
165 
166 void
structured_vertex_locs(InsertionOrderedSet<std::string> & code) const167 TopologyCode::structured_vertex_locs(
168     InsertionOrderedSet<std::string> &code) const
169 {
170   if(topo_type != "structured")
171   {
172     ASCENT_ERROR("The function structured_vertex_locs only supports structured "
173                  "topologies.");
174   }
175   structured_vertices(code);
176   code.insert("double " + topo_name + "_vertex_locs[" +
177               std::to_string(shape_size) + "][" + std::to_string(num_dims) +
178               "];\n");
179   for(int i = 0; i < shape_size; ++i)
180   {
181     vertex_xyz(code,
182                array_code.index(topo_name + "_vertices", std::to_string(i)),
183                false,
184                array_code.index(topo_name + "_vertex_locs", std::to_string(i)),
185                false);
186   }
187 }
188 
189 void
unstructured_vertices(InsertionOrderedSet<std::string> & code,const std::string & index_name) const190 TopologyCode::unstructured_vertices(InsertionOrderedSet<std::string> &code,
191                                     const std::string &index_name) const
192 {
193   if(topo_type != "unstructured")
194   {
195     ASCENT_ERROR(
196         "The function unstructured_vertices only supports unstructured "
197         "topologies.");
198   }
199   if(shape_size == -1)
200   {
201     // TODO generate vertices array for multi-shapes case, it's variable length
202     // so might have to find the max shape size before hand and pass it in
203   }
204   else
205   {
206     // single shape
207     // inline the for-loop
208     code.insert("int " + topo_name + "_vertices[" + std::to_string(shape_size) +
209                 "];\n");
210     for(int i = 0; i < shape_size; ++i)
211     {
212       code.insert(topo_name + "_vertices[" + std::to_string(i) +
213                   "] = " + topo_name + "_connectivity[" + index_name + " * " +
214                   std::to_string(shape_size) + " + " + std::to_string(i) +
215                   "];\n");
216     }
217   }
218 }
219 
220 void
unstructured_vertex_locs(InsertionOrderedSet<std::string> & code,const std::string & index_name) const221 TopologyCode::unstructured_vertex_locs(InsertionOrderedSet<std::string> &code,
222                                        const std::string &index_name) const
223 {
224   if(topo_type != "unstructured")
225   {
226     ASCENT_ERROR(
227         "The function unstructured_vertex_locs only supports unstructured "
228         "topologies.");
229   }
230   unstructured_vertices(code, index_name);
231   if(shape_size == -1)
232   {
233     // multiple shapes
234     code.insert({"int " + topo_name + "_shape_size = " + topo_name + "_sizes[" +
235                      index_name + "];\n",
236                  "int " + topo_name + "_offset = " + topo_name + "_offsets[" +
237                      index_name + "];\n",
238                  "double " + topo_name + "_vertex_locs[" + topo_name +
239                      "_shape_size][" + std::to_string(num_dims) + "];\n"});
240 
241     InsertionOrderedSet<std::string> for_loop;
242     for_loop.insert(
243         {"for(int i = 0; i < " + topo_name + "_shape_size; ++i)\n", "{\n"});
244     vertex_xyz(for_loop,
245                array_code.index(topo_name + "_connectivity",
246                                 topo_name + "_offset + i"),
247                false,
248                array_code.index(topo_name + "_vertex_locs", "i"),
249                false);
250     for_loop.insert("}\n");
251     code.insert(for_loop.accumulate());
252   }
253   else
254   {
255     // single shape
256     code.insert("double " + topo_name + "_vertex_locs[" +
257                 std::to_string(shape_size) + "][" + std::to_string(num_dims) +
258                 "];\n");
259     for(int i = 0; i < shape_size; ++i)
260     {
261       vertex_xyz(
262           code,
263           array_code.index(topo_name + "_vertices", std::to_string(i)),
264           false,
265           array_code.index(topo_name + "_vertex_locs", std::to_string(i)),
266           false);
267     }
268   }
269 }
270 
271 void
element_coord(InsertionOrderedSet<std::string> & code,const std::string & coord,const std::string & index_name,const std::string & res_name,const bool declare) const272 TopologyCode::element_coord(InsertionOrderedSet<std::string> &code,
273                             const std::string &coord,
274                             const std::string &index_name,
275                             const std::string &res_name,
276                             const bool declare) const
277 {
278   // if the logical index is provided, don't regenerate it
279   std::string my_index_name;
280   if(index_name.empty() &&
281      (topo_type == "uniform" || topo_type == "rectilinear" ||
282       topo_type == "structured"))
283   {
284     element_idx(code);
285     my_index_name =
286         topo_name + "_element_idx[" + std::to_string(coord[0] - 'x') + "]";
287   }
288   else
289   {
290     my_index_name = index_name;
291   }
292   if(topo_type == "uniform")
293   {
294     code.insert((declare ? "const double " : "") + res_name + " = " +
295                 topo_name + "_origin_" + coord + +" + (" + my_index_name +
296                 " + 0.5) * " + topo_name + "_spacing_d" + coord + ";\n");
297   }
298   else if(topo_type == "rectilinear")
299   {
300     code.insert(
301         (declare ? "const double " : "") + res_name + " = (" +
302         array_code.index(topo_name + "_coords", my_index_name, coord) + " + " +
303         array_code.index(topo_name + "_coords", my_index_name + " + 1", coord) +
304         ") / 2.0;\n");
305   }
306   else if(topo_type == "structured")
307   {
308     structured_vertex_locs(code);
309     math_code.component_avg(
310         code, shape_size, topo_name + "_vertex_locs", coord, res_name, declare);
311   }
312   else if(topo_type == "unstructured")
313   {
314     unstructured_vertex_locs(code);
315     if(shape_size == -1)
316     {
317       // multiple shapes
318       // This will generate 3 for loops if we want to calculate element_xyz
319       // If this is an issue we can make a special case for it in
320       // element_xyz
321       InsertionOrderedSet<std::string> for_loop;
322       for_loop.insert(
323           {"for(int i = 0; i < " + topo_name + "_shape_size; ++i)\n", "{\n"});
324       math_code.component_avg(for_loop,
325                               shape_size,
326                               topo_name + "_vertex_locs",
327                               coord,
328                               res_name,
329                               declare);
330       for_loop.insert("}\n");
331       code.insert(for_loop.accumulate());
332     }
333     else
334     {
335       // single shape
336       for(int i = 0; i < shape_size; ++i)
337       {
338         math_code.component_avg(code,
339                                 shape_size,
340                                 topo_name + "_vertex_locs",
341                                 coord,
342                                 res_name,
343                                 declare);
344       }
345     }
346   }
347   else
348   {
349     ASCENT_ERROR("Cannot get element_coord for topology of type '" << topo_type
350                                                                    << "'.");
351   }
352 }
353 
354 void
element_xyz(InsertionOrderedSet<std::string> & code) const355 TopologyCode::element_xyz(InsertionOrderedSet<std::string> &code) const
356 {
357   code.insert("double " + topo_name + "_element_loc[" +
358               std::to_string(num_dims) + "];\n");
359   if(topo_type == "uniform" || topo_type == "rectilinear" ||
360      topo_type == "structured" || topo_type == "unstructured")
361   {
362     element_coord(code, "x", "", topo_name + "_element_loc[0]", false);
363     if(num_dims >= 2)
364     {
365       element_coord(code, "y", "", topo_name + "_element_loc[1]", false);
366     }
367     if(num_dims == 3)
368     {
369       element_coord(code, "z", "", topo_name + "_element_loc[2]", false);
370     }
371   }
372   else
373   {
374     ASCENT_ERROR("Cannot get element location for unstructured topology with "
375                  << num_dims << " dimensions.");
376   }
377 }
378 
379 void
vertex_idx(InsertionOrderedSet<std::string> & code) const380 TopologyCode::vertex_idx(InsertionOrderedSet<std::string> &code) const
381 {
382   if(topo_type == "uniform" || topo_type == "rectilinear" ||
383      topo_type == "structured")
384   {
385     code.insert(
386         {"int " + topo_name + "_vertex_idx[" + std::to_string(num_dims) +
387              "];\n",
388          topo_name + "_vertex_idx[0] = item % (" + topo_name + "_dims_i);\n"});
389     if(num_dims >= 2)
390     {
391       code.insert(topo_name + "_vertex_idx[1] = (item / (" + topo_name +
392                   "_dims_i)) % (" + topo_name + "_dims_j);\n");
393     }
394     if(num_dims == 3)
395     {
396       code.insert(topo_name + "_vertex_idx[2] = item / ((" + topo_name +
397                   "_dims_i) * (" + topo_name + "_dims_j));\n");
398     }
399   }
400   else
401   {
402     // vertex_idx is just item for explicit (unstructured)
403     // vertex_idx[0] = item
404     // vertex_idx[1] = item
405     // vertex_idx[2] = item
406     ASCENT_ERROR("vertex_idx does not need to be calculated for unstructured "
407                  "topologies.");
408   }
409 }
410 
411 void
vertex_coord(InsertionOrderedSet<std::string> & code,const std::string & coord,const std::string & index_name,const std::string & res_name,const bool declare) const412 TopologyCode::vertex_coord(InsertionOrderedSet<std::string> &code,
413                            const std::string &coord,
414                            const std::string &index_name,
415                            const std::string &res_name,
416                            const bool declare) const
417 {
418   std::string my_index_name;
419   if(index_name.empty())
420   {
421     if(topo_name == "uniform" || topo_name == "rectilinear")
422     {
423       vertex_idx(code);
424       my_index_name =
425           topo_name + "_vertex_idx[" + std::to_string(coord[0] - 'x') + "]";
426     }
427     else
428     {
429       my_index_name = "item";
430     }
431   }
432   else
433   {
434     my_index_name = index_name;
435   }
436   if(topo_type == "uniform")
437   {
438     code.insert((declare ? "const double " : "") + res_name + " = " +
439                 topo_name + "_origin_" + coord + " + " + my_index_name + " * " +
440                 topo_name + "_spacing_d" + coord + ";\n");
441   }
442   else
443   {
444     code.insert((declare ? "const double " : "") + res_name + " = " +
445                 array_code.index(topo_name + "_coords", my_index_name, coord) +
446                 ";\n");
447   }
448 }
449 
450 void
vertex_xyz(InsertionOrderedSet<std::string> & code,const std::string & index_name,const bool index_array,const std::string & res_name,const bool declare) const451 TopologyCode::vertex_xyz(InsertionOrderedSet<std::string> &code,
452                          const std::string &index_name,
453                          const bool index_array,
454                          const std::string &res_name,
455                          const bool declare) const
456 {
457   if(declare)
458   {
459     code.insert("double " + res_name + "[" + std::to_string(num_dims) + "];\n");
460   }
461   vertex_coord(code,
462                "x",
463                index_name + (index_array ? "[0]" : ""),
464                res_name + "[0]",
465                false);
466   if(num_dims >= 2)
467   {
468     vertex_coord(code,
469                  "y",
470                  index_name + (index_array ? "[1]" : ""),
471                  res_name + "[1]",
472                  false);
473   }
474   if(num_dims == 3)
475   {
476     vertex_coord(code,
477                  "z",
478                  index_name + (index_array ? "[2]" : ""),
479                  res_name + "[2]",
480                  false);
481   }
482 }
483 
484 void
vertex_xyz(InsertionOrderedSet<std::string> & code) const485 TopologyCode::vertex_xyz(InsertionOrderedSet<std::string> &code) const
486 {
487   if(topo_type == "uniform" || topo_type == "rectilinear")
488   {
489     vertex_idx(code);
490     vertex_xyz(
491         code, topo_name + "_vertex_idx", true, topo_name + "_vertex_loc");
492   }
493   else if(topo_type == "structured" || topo_type == "unstructured")
494   {
495     vertex_xyz(code, "item", false, topo_name + "_vertex_loc");
496   }
497 }
498 
499 // get rectilinear spacing for a cell
500 void
dxdydz(InsertionOrderedSet<std::string> & code) const501 TopologyCode::dxdydz(InsertionOrderedSet<std::string> &code) const
502 {
503   if(topo_type != "rectilinear")
504   {
505     ASCENT_ERROR("Function dxdydz only works on rectilinear topologies.");
506   }
507   element_idx(code);
508   code.insert("const double " + topo_name + "_dx = " +
509               array_code.index(topo_name + "_coords",
510                                topo_name + "_element_idx[0] + 1",
511                                "x") +
512               " - " +
513               array_code.index(
514                   topo_name + "_coords", topo_name + "_element_idx[0]", "x") +
515               ";\n");
516   if(num_dims >= 2)
517   {
518     code.insert("const double " + topo_name + "_dy = " +
519                 array_code.index(topo_name + "_coords",
520                                  topo_name + "_element_idx[1] + 1",
521                                  "y") +
522                 " - " +
523                 array_code.index(
524                     topo_name + "_coords", topo_name + "_element_idx[1]", "y") +
525                 ";\n");
526   }
527   if(num_dims == 3)
528   {
529     code.insert("const double " + topo_name + "_dz = " +
530                 array_code.index(topo_name + "_coords",
531                                  topo_name + "_element_idx[2] + 1",
532                                  "z") +
533                 " - " +
534                 array_code.index(
535                     topo_name + "_coords", topo_name + "_element_idx[2]", "z") +
536                 ";\n");
537   }
538 }
539 
540 // https://www.osti.gov/servlets/purl/632793 (14)
541 // switch vertices 2 and 3 and vertices 6 and 7 to match vtk order
542 void
hexahedral_volume(InsertionOrderedSet<std::string> & code,const std::string & vertex_locs,const std::string & res_name) const543 TopologyCode::hexahedral_volume(InsertionOrderedSet<std::string> &code,
544                                 const std::string &vertex_locs,
545                                 const std::string &res_name) const
546 {
547   math_code.vector_subtract(
548       code, vertex_locs + "[6]", vertex_locs + "[0]", res_name + "_6m0", 3);
549   math_code.vector_subtract(
550       code, vertex_locs + "[1]", vertex_locs + "[0]", res_name + "_1m0", 3);
551   math_code.vector_subtract(
552       code, vertex_locs + "[2]", vertex_locs + "[5]", res_name + "_2m5", 3);
553   math_code.vector_subtract(
554       code, vertex_locs + "[4]", vertex_locs + "[0]", res_name + "_4m0", 3);
555   math_code.vector_subtract(
556       code, vertex_locs + "[5]", vertex_locs + "[7]", res_name + "_5m7", 3);
557   math_code.vector_subtract(
558       code, vertex_locs + "[3]", vertex_locs + "[0]", res_name + "_3m0", 3);
559   math_code.vector_subtract(
560       code, vertex_locs + "[7]", vertex_locs + "[2]", res_name + "_7m2", 3);
561   // can save 4 flops if we use the fact that 6m0 is always the first column
562   // of the determinant
563   math_code.determinant_3x3(code,
564                             res_name + "_6m0",
565                             res_name + "_1m0",
566                             res_name + "_2m5",
567                             res_name + "_det0");
568   math_code.determinant_3x3(code,
569                             res_name + "_6m0",
570                             res_name + "_4m0",
571                             res_name + "_5m7",
572                             res_name + "_det1");
573   math_code.determinant_3x3(code,
574                             res_name + "_6m0",
575                             res_name + "_3m0",
576                             res_name + "_7m2",
577                             res_name + "_det2");
578   code.insert("const double " + res_name + " = (" + res_name + "_det0 + " +
579               res_name + "_det1 + " + res_name + "_det2) / 6.0;\n");
580 }
581 
582 // ||(p3-p0) (p2-p0) (p1-p0)|| / 6
583 void
tetrahedral_volume(InsertionOrderedSet<std::string> & code,const std::string & vertex_locs,const std::string & res_name) const584 TopologyCode::tetrahedral_volume(InsertionOrderedSet<std::string> &code,
585                                  const std::string &vertex_locs,
586                                  const std::string &res_name) const
587 {
588   math_code.vector_subtract(
589       code, vertex_locs + "[1]", vertex_locs + "[0]", res_name + "_1m0", 3);
590   math_code.vector_subtract(
591       code, vertex_locs + "[2]", vertex_locs + "[0]", res_name + "_2m0", 3);
592   math_code.vector_subtract(
593       code, vertex_locs + "[3]", vertex_locs + "[0]", res_name + "_3m0", 3);
594   math_code.determinant_3x3(code,
595                             res_name + "_3m0",
596                             res_name + "_2m0",
597                             res_name + "_1m0",
598                             res_name + "_det");
599   code.insert("const double " + res_name + " = " + res_name + "_det / 6.0;\n");
600 }
601 
602 void
print_vector(InsertionOrderedSet<std::string> & code,const std::string v,const int num_dims)603 print_vector(InsertionOrderedSet<std::string> &code,
604              const std::string v,
605              const int num_dims)
606 {
607   std::stringstream ss;
608   ss<<"printf(\""+v<<" ";
609   for(int i = 0; i < num_dims; ++i)
610   {
611     ss<<"%f ";
612   }
613   ss<<"\\n\", ";
614   for(int i = 0; i < num_dims; ++i)
615   {
616     ss<<v<<"["<<i<<"]";
617     if(i != num_dims-1)
618     {
619       ss<<", ";
620     }
621   }
622   ss<<");\n";
623   code.insert(ss.str());
624 }
625 
626 void
print_int_vector(InsertionOrderedSet<std::string> & code,const std::string v,const int num_dims)627 print_int_vector(InsertionOrderedSet<std::string> &code,
628              const std::string v,
629              const int num_dims)
630 {
631   std::stringstream ss;
632   ss<<"printf(\""+v<<" ";
633   for(int i = 0; i < num_dims; ++i)
634   {
635     ss<<"%d ";
636   }
637   ss<<"\\n\", ";
638   for(int i = 0; i < num_dims; ++i)
639   {
640     ss<<v<<"["<<i<<"]";
641     if(i != num_dims-1)
642     {
643       ss<<", ";
644     }
645   }
646   ss<<");\n";
647   code.insert(ss.str());
648 }
649 
650 // 1/2 * |(p2 - p0) X (p3 - p1)|
651 void
quadrilateral_area(InsertionOrderedSet<std::string> & code,const std::string & p0,const std::string & p1,const std::string & p2,const std::string & p3,const std::string & res_name) const652 TopologyCode::quadrilateral_area(InsertionOrderedSet<std::string> &code,
653                                  const std::string &p0,
654                                  const std::string &p1,
655                                  const std::string &p2,
656                                  const std::string &p3,
657                                  const std::string &res_name) const
658 {
659   math_code.vector_subtract(code, p2, p0, res_name + "_2m0", num_dims);
660   math_code.vector_subtract(code, p3, p1, res_name + "_3m1", num_dims);
661   if(num_dims == 3)
662   {
663     math_code.vector_subtract(code, p2, p0, res_name + "_2m0", num_dims);
664     math_code.vector_subtract(code, p3, p1, res_name + "_3m1", num_dims);
665     math_code.cross_product(code,
666                             res_name + "_2m0",
667                             res_name + "_3m1",
668                             res_name + "_cross",
669                             num_dims);
670     math_code.magnitude(code, res_name + "_cross", res_name + "_cross_mag", 3);
671     code.insert("const double " + res_name + " = " + res_name +
672                 "_cross_mag / 2.0;\n");
673   }
674   else if(num_dims ==2)
675   {
676     // 2d cross product is weird so just do it
677     // also, this is the signed area, so take the absolute value
678     // since I am not currently sure how to ensure the winding order
679     code.insert("const double " + res_name + " = abs((" +
680                  res_name + "_2m0[0] * " + res_name + "_3m1[1] - " +
681                  res_name + "_2m0[1] * " + res_name + "_3m1[0] ) / 2.0); \n");
682   }
683 }
684 
685 void
quadrilateral_area(InsertionOrderedSet<std::string> & code,const std::string & vertex_locs,const std::string & res_name) const686 TopologyCode::quadrilateral_area(InsertionOrderedSet<std::string> &code,
687                                  const std::string &vertex_locs,
688                                  const std::string &res_name) const
689 {
690   quadrilateral_area(code,
691                      vertex_locs + "[0]",
692                      vertex_locs + "[1]",
693                      vertex_locs + "[2]",
694                      vertex_locs + "[3]",
695                      res_name);
696 }
697 
698 // 1/2 * |(p1 - p0) X (p2 - p0)|
699 void
triangle_area(InsertionOrderedSet<std::string> & code,const std::string & p0,const std::string & p1,const std::string & p2,const std::string & res_name) const700 TopologyCode::triangle_area(InsertionOrderedSet<std::string> &code,
701                             const std::string &p0,
702                             const std::string &p1,
703                             const std::string &p2,
704                             const std::string &res_name) const
705 {
706   math_code.vector_subtract(code, p1, p0, res_name + "_1m0", num_dims);
707   math_code.vector_subtract(code, p2, p0, res_name + "_2m0", num_dims);
708   if(num_dims == 3)
709   {
710     math_code.cross_product(code,
711                             res_name + "_1m0",
712                             res_name + "_2m0",
713                             res_name + "_cross",
714                             num_dims);
715     math_code.magnitude(code, res_name + "_cross", res_name + "_cross_mag", 3);
716     code.insert("const double " + res_name + " = " + res_name +
717                 "_cross_mag / 2.0;\n");
718   }
719   else if(num_dims == 2)
720   {
721     // 2d cross product is weird so just do it
722     // also, this is the signed area, so take the absolute value
723     // since I am not currently sure how to ensure the winding order
724     code.insert("const double " + res_name + " = abs((" +
725                  res_name + "_1m0[0] * " + res_name + "_2m0[1] - " +
726                  res_name + "_1m0[1] * " + res_name + "_2m0[0] ) / 2.0); \n");
727   }
728 }
729 
730 void
triangle_area(InsertionOrderedSet<std::string> & code,const std::string & vertex_locs,const std::string & res_name) const731 TopologyCode::triangle_area(InsertionOrderedSet<std::string> &code,
732                             const std::string &vertex_locs,
733                             const std::string &res_name) const
734 {
735   triangle_area(code,
736                 vertex_locs + "[0]",
737                 vertex_locs + "[1]",
738                 vertex_locs + "[2]",
739                 res_name);
740 }
741 
742 // http://index-of.co.uk/Game-Development/Programming/Graphics%20Gems%205.pdf
743 // k is # vertices, h = (k-1)//2, l = 0 if k is odd, l = k-1 if k is even
744 // 2A = sum_{i=1}^{h - 1}((P_2i - P_0) X (P_2i+1 - P_2i-1)) +
745 // (P_2h - P_0) X (P_l - P_2h-1)
746 void
polygon_area_vec(InsertionOrderedSet<std::string> & code,const std::string & vertex_locs,const std::string & res_name) const747 TopologyCode::polygon_area_vec(InsertionOrderedSet<std::string> &code,
748                                const std::string &vertex_locs,
749                                const std::string &res_name) const
750 {
751   code.insert({"double " + res_name + "_vec[3];\n",
752                res_name + "_vec[0] = 0;\n",
753                res_name + "_vec[1] = 0;\n",
754                res_name + "_vec[2] = 0;\n",
755                "const int " + res_name + "_h = (" + topo_name +
756                    "_shape_size - 1) / 2;\n"});
757   InsertionOrderedSet<std::string> for_loop;
758   for_loop.insert({"for(int i = 1; i < " + res_name + "_h; ++i)\n", "{\n"});
759   math_code.vector_subtract(for_loop,
760                             vertex_locs + "[2 * i]",
761                             vertex_locs + "[0]",
762                             res_name + "_2im0",
763                             num_dims);
764   math_code.vector_subtract(for_loop,
765                             vertex_locs + "[2 * i + 1]",
766                             vertex_locs + "[2 * i - 1]",
767                             res_name + "_2ip1_m_2im1",
768                             num_dims);
769   math_code.cross_product(for_loop,
770                           res_name + "_2im0",
771                           res_name + "_2ip1_m_2im1",
772                           res_name + "_cross",
773                           num_dims);
774   math_code.vector_add(for_loop,
775                        res_name + "_vec",
776                        res_name + "_cross",
777                        res_name + "_vec",
778                        3,
779                        false);
780   for_loop.insert("}\n");
781   code.insert(for_loop.accumulate());
782   code.insert({"int " + res_name + "_last = ((" + topo_name +
783                "_shape_size & 1) ^ 1) * (" + topo_name +
784                "_shape_size - 1);\n"});
785   math_code.vector_subtract(code,
786                             vertex_locs + "[2 * " + res_name + "_h]",
787                             vertex_locs + "[0]",
788                             res_name + "_2hm0",
789                             num_dims);
790   math_code.vector_subtract(code,
791                             vertex_locs + "[" + res_name + "_last]",
792                             vertex_locs + "[2 * " + res_name + "_h - 1]",
793                             res_name + "_l_m_2hm1",
794                             num_dims);
795   math_code.cross_product(code,
796                           res_name + "_2hm0",
797                           res_name + "_l_m_2hm1",
798                           res_name + "_cross",
799                           num_dims);
800   math_code.vector_add(code,
801                        res_name + "_vec",
802                        res_name + "_cross",
803                        res_name + "_vec",
804                        3,
805                        false);
806 }
807 
808 void
polygon_area(InsertionOrderedSet<std::string> & code,const std::string & vertex_locs,const std::string & res_name) const809 TopologyCode::polygon_area(InsertionOrderedSet<std::string> &code,
810                            const std::string &vertex_locs,
811                            const std::string &res_name) const
812 {
813   polygon_area_vec(code, vertex_locs, res_name);
814   math_code.magnitude(code, res_name + "_vec", res_name + "_vec_mag", 3);
815   code.insert("const double " + res_name + " = " + res_name +
816               "_vec_mag / 2.0;\n");
817 }
818 
819 // TODO this doesn't work because A_j needs to point outside the polyhedron
820 // (i.e. vertices need to be ordered counter-clockwise when looking from
821 // outside)
822 // m is number of faces
823 // 1/6 * sum_{j=0}^{m-1}(P_j . 2*A_j)
824 // P_j is some point on face j A_j is the area vector of face j
825 /*
826 void
827 TopologyCode::polyhedron_volume(InsertionOrderedSet<std::string> &code,
828                                 const std::string &vertex_locs,
829                                 const std::string &res_name) const
830 {
831   code.insert({"double " + res_name + "_vec[3];\n",
832                res_name + "_vec[0] = 0;\n",
833                res_name + "_vec[1] = 0;\n",
834                res_name + "_vec[2] = 0;\n",
835                "int " + topo_name + "_polyhedral_shape_size = " + topo_name +
836                    "_polyhedral_sizes[item];\n",
837                "int " + topo_name + "_polyhedral_offset = " +
838                    array_code.index(topo_name + "_polyhedral_offsets", "item")
839 +
840                    ";\n"});
841 
842   InsertionOrderedSet<std::string> for_loop;
843   for_loop.insert(
844       {"for(int j = 0; j < " + topo_name + "_polyhedral_shape_size; ++j)\n",
845        "{\n"});
846   unstructured_vertex_locs(for_loop,
847                         array_code.index(topo_name +
848 "_polyhedral_connectivity", topo_name + "_polyhedral_offset + j"));
849   polygon_area_vec(for_loop, vertex_locs, res_name + "_face");
850   math_code.dot_product(for_loop,
851                         vertex_locs + "[4]",
852                         res_name + "_face_vec",
853                         res_name + "_dot",
854                         num_dims);
855   math_code.vector_add(for_loop,
856                        res_name + "_vec",
857                        res_name + "_dot",
858                        res_name + "_vec",
859                        num_dims,
860                        false);
861   for_loop.insert("}\n");
862   code.insert(for_loop.accumulate());
863   math_code.magnitude(code, res_name + "_vec", res_name + "_vec_mag",
864 num_dims); code.insert("double " + res_name + " = " + res_name + "_vec_mag
865 / 6.0;\n");
866 }
867 */
868 
869 void
volume(InsertionOrderedSet<std::string> & code) const870 TopologyCode::volume(InsertionOrderedSet<std::string> &code) const
871 {
872   if(topo_type == "uniform")
873   {
874     code.insert("const double " + topo_name + "_volume = " + topo_name +
875                 "_spacing_dx * " + topo_name + "_spacing_dy * " + topo_name +
876                 "_spacing_dz;\n");
877   }
878   else if(topo_type == "rectilinear")
879   {
880     dxdydz(code);
881     code.insert("const double " + topo_name + "_volume = " + topo_name +
882                 "_dx * " + topo_name + "_dy * " + topo_name + "_dz;\n");
883   }
884   else if(topo_type == "structured")
885   {
886     structured_vertex_locs(code);
887     hexahedral_volume(code, topo_name + "_vertex_locs", topo_name + "_volume");
888   }
889   else if(topo_type == "unstructured")
890   {
891     unstructured_vertex_locs(code);
892     if(shape == "hex")
893     {
894       hexahedral_volume(
895           code, topo_name + "_vertex_locs", topo_name + "_volume");
896     }
897     else if(shape == "tet")
898     {
899       tetrahedral_volume(
900           code, topo_name + "_vertex_locs", topo_name + "_volume");
901     }
902     // else if(shape == "polyhedral")
903     // {
904     //   polyhedron_volume(
905     //       code, topo_name + "_vertex_locs", topo_name + "_volume");
906     // }
907     else
908     {
909       ASCENT_ERROR("Unsupported unstructured topo_type '"
910                    <<topo_type<<"' with shape '"<<shape
911                    <<"' for volume calculation");
912     }
913   }
914   else
915   {
916     ASCENT_ERROR("Unsupported topo_type '"<<topo_type<<"' for volume calculation");
917   }
918 }
919 
920 void
hexahedral_surface_area(InsertionOrderedSet<std::string> & code,const std::string & vertex_locs,const std::string & res_name) const921 TopologyCode::hexahedral_surface_area(InsertionOrderedSet<std::string> &code,
922                                       const std::string &vertex_locs,
923                                       const std::string &res_name) const
924 {
925   // negative x face
926   quadrilateral_area(code,
927                      vertex_locs + "[4]",
928                      vertex_locs + "[0]",
929                      vertex_locs + "[3]",
930                      vertex_locs + "[7]",
931                      res_name + "_nx");
932   // positive x face
933   quadrilateral_area(code,
934                      vertex_locs + "[1]",
935                      vertex_locs + "[5]",
936                      vertex_locs + "[6]",
937                      vertex_locs + "[2]",
938                      res_name + "_px");
939   quadrilateral_area(code,
940                      vertex_locs + "[4]",
941                      vertex_locs + "[5]",
942                      vertex_locs + "[1]",
943                      vertex_locs + "[0]",
944                      res_name + "_ny");
945   quadrilateral_area(code,
946                      vertex_locs + "[3]",
947                      vertex_locs + "[2]",
948                      vertex_locs + "[6]",
949                      vertex_locs + "[7]",
950                      res_name + "_py");
951   quadrilateral_area(code,
952                      vertex_locs + "[0]",
953                      vertex_locs + "[1]",
954                      vertex_locs + "[2]",
955                      vertex_locs + "[3]",
956                      res_name + "_nz");
957   quadrilateral_area(code,
958                      vertex_locs + "[4]",
959                      vertex_locs + "[5]",
960                      vertex_locs + "[6]",
961                      vertex_locs + "[7]",
962                      res_name + "_pz");
963   code.insert("const double " + res_name + " = " + res_name + "_nx + " +
964               res_name + "_px + " + res_name + "_ny + " + res_name + "_py + " +
965               topo_name + "_area_nz + " + res_name + "_pz;\n");
966 }
967 
968 void
tetrahedral_surface_area(InsertionOrderedSet<std::string> & code,const std::string & vertex_locs,const std::string & res_name) const969 TopologyCode::tetrahedral_surface_area(InsertionOrderedSet<std::string> &code,
970                                        const std::string &vertex_locs,
971                                        const std::string &res_name) const
972 {
973   triangle_area(code,
974                 vertex_locs + "[0]",
975                 vertex_locs + "[2]",
976                 vertex_locs + "[1]",
977                 res_name + "_f0");
978   triangle_area(code,
979                 vertex_locs + "[0]",
980                 vertex_locs + "[3]",
981                 vertex_locs + "[2]",
982                 res_name + "_f1");
983   triangle_area(code,
984                 vertex_locs + "[0]",
985                 vertex_locs + "[1]",
986                 vertex_locs + "[3]",
987                 res_name + "_f2");
988   triangle_area(code,
989                 vertex_locs + "[1]",
990                 vertex_locs + "[2]",
991                 vertex_locs + "[3]",
992                 res_name + "_f3");
993   code.insert("const double " + res_name + " = " + res_name + "_f0 + " +
994               res_name + "_f1 + " + res_name + "_f2 + " + res_name + "_f3;\n");
995 }
996 
997 void
area(InsertionOrderedSet<std::string> & code) const998 TopologyCode::area(InsertionOrderedSet<std::string> &code) const
999 {
1000   if(num_dims != 2)
1001   {
1002     ASCENT_ERROR("'.area' is only defined for 2 dimensional meshes, but "
1003                  <<" the mesh has topological dims "<<num_dims);
1004   }
1005 
1006   if(topo_type == "uniform")
1007   {
1008     code.insert("const double " + topo_name + "_area = " + topo_name +
1009                   "_spacing_dx * " + topo_name + "_spacing_dy;\n");
1010     //else
1011     //{
1012     //  // this was originally returning length which I don't think area
1013     //  // should be defined for anything but 3d
1014     //  //code.insert("const double " + topo_name + "_area = " + topo_name +
1015     //  //            "_spacing_dx;\n");
1016     //}
1017   }
1018   else if(topo_type == "rectilinear")
1019   {
1020     dxdydz(code);
1021     code.insert("const double " + topo_name + "_area = " + topo_name +
1022                 "_dx * " + topo_name + "_dy;\n");
1023     //else
1024     //{
1025     //  code.insert("const double " + topo_name + "_area = " + topo_name +
1026     //              "_dx;\n");
1027     //}
1028   }
1029   else if(topo_type == "structured")
1030   {
1031     structured_vertex_locs(code);
1032     quadrilateral_area(code, topo_name + "_vertex_locs", topo_name + "_area");
1033     //else if(num_dims == 1)
1034     //{
1035     //  math_code.vector_subtract(code,
1036     //                            topo_name + "vertex_locs[1]",
1037     //                            topo_name + "vertex_locs[0]",
1038     //                            topo_name + "_area",
1039     //                            1);
1040     //}
1041   }
1042   else if(topo_type == "unstructured")
1043   {
1044     unstructured_vertex_locs(code);
1045     if(shape == "quad")
1046     {
1047       quadrilateral_area(code, topo_name + "_vertex_locs", topo_name + "_area");
1048     }
1049     else if(shape == "tri")
1050     {
1051       triangle_area(code, topo_name + "_vertex_locs", topo_name + "_area");
1052     }
1053     else if(shape == "polygonal")
1054     {
1055       polygon_area(code, topo_name + "_vertex_locs", topo_name + "_area");
1056     }
1057     else
1058     {
1059       ASCENT_ERROR("area for unstructured topology with shape '"
1060                    << shape << "' is not implemented.");
1061     }
1062   }
1063   else
1064   {
1065     ASCENT_ERROR("area for topology type '"<<topo_type
1066                  <<"' is not implemented.");
1067   }
1068 }
1069 
1070 void
surface_area(InsertionOrderedSet<std::string> & code) const1071 TopologyCode::surface_area(InsertionOrderedSet<std::string> &code) const
1072 {
1073   if(topo_type == "uniform")
1074   {
1075     code.insert("const double " + topo_name + "_area = 2.0 * (" + topo_name +
1076                 "_spacing_dx * " + topo_name + "_spacing_dy + " + topo_name +
1077                 "_spacing_dx * " + topo_name + "_spacing_dz + " + topo_name +
1078                 "_spacing_dy * " + topo_name + "_spacing_dz);\n");
1079   }
1080   else if(topo_type == "rectilinear")
1081   {
1082     dxdydz(code);
1083     code.insert("const double " + topo_name + "_area = 2.0 * (" + topo_name +
1084                 "_dx * " + topo_name + "_dy + " + topo_name + "_dx * " +
1085                 topo_name + "_dz + " + topo_name + "_dy * " + topo_name +
1086                 "_dz);\n");
1087   }
1088   else if(topo_type == "structured")
1089   {
1090     structured_vertex_locs(code);
1091     hexahedral_surface_area(
1092         code, topo_name + "_vertex_locs", topo_name + "_area");
1093   }
1094   else if(topo_type == "unstructured")
1095   {
1096     unstructured_vertex_locs(code);
1097     if(shape == "hex")
1098     {
1099       hexahedral_surface_area(
1100           code, topo_name + "_vertex_locs", topo_name + "_area");
1101     }
1102     else if(shape == "tet")
1103     {
1104       tetrahedral_surface_area(
1105           code, topo_name + "_vertex_locs", topo_name + "_area");
1106     }
1107     // else if(shape == "polyhedral")
1108     // {
1109     //   polyhedron_surface_area(
1110     //       code, topo_name + "_vertex_locs", topo_name + "_area");
1111     // }
1112     else
1113     {
1114       ASCENT_ERROR("area for unstructured topology with shape '"
1115                    << shape << "' is not implemented.");
1116     }
1117   }
1118   else
1119   {
1120     ASCENT_ERROR("surface_area for topology type '"<<topo_type
1121                  <<"' is not implemented.");
1122   }
1123 }
1124 // }}}
1125 
1126 //-----------------------------------------------------------------------------
1127 };
1128 //-----------------------------------------------------------------------------
1129 // -- end ascent::runtime::expressions--
1130 //-----------------------------------------------------------------------------
1131 
1132 //-----------------------------------------------------------------------------
1133 };
1134 //-----------------------------------------------------------------------------
1135 // -- end ascent::runtime --
1136 //-----------------------------------------------------------------------------
1137 
1138 //-----------------------------------------------------------------------------
1139 };
1140 //-----------------------------------------------------------------------------
1141 // -- end ascent:: --
1142 //-----------------------------------------------------------------------------
1143