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