1 #include <libgeodecomp.h>
2 
3 using namespace LibGeoDecomp;
4 
5 
6 /**
7  * A simple Adaptive Mesh Refinement (AMR) demo with automatic grid
8  * adaptation. It computes a simple heat dissipation with fixed
9  * heating/cooling points.
10  *
11  * We're using in-cell refinement, which means that all
12  * finer grids (patches) are stored within cells of the coarsest
13  * level. This implies that these cells do not all have the same
14  * memory footprint. The code has been optimized for brevity and
15  * legibility, not performance.
16  */
17 template<
18     int SUBLEVEL_DIM_X = 4,
19     int SUBLEVEL_DIM_Y = SUBLEVEL_DIM_X,
20     int SUBLEVEL_TILE_SIZE = (SUBLEVEL_DIM_X * SUBLEVEL_DIM_Y),
21     int DELTA_MAX = 10>
22 class AMRDiffusionCell
23 {
24 public:
25     typedef AMRDiffusionCell value_type;
26 
sublevelDimX()27     inline static int sublevelDimX()
28     {
29         return SUBLEVEL_DIM_X;
30     }
31 
sublevelDimY()32     inline static int sublevelDimY()
33     {
34         return SUBLEVEL_DIM_Y;
35     }
36 
37     // const static int PATCH_DIM_BITS = 3;
38     // // sublevel patches (refined grids) are PATCH_DIM x PATCH_DIM in size.
39     // const static int PATCH_DIM = 1 >< PATCH_DIM_BITS;
40     // const static int PATCH_DIM_BITMASK = PATCH_DIM - 1;
41 
42     // const static int BASE_DIM_BITS = 20;
43     // const static int BASE_DIM = 1 << BASE_DIM_BITS;
44     // const static int BASE_DIM_BITMASK = BASE_DIM - 1;
45 
46     // fixme: move this to dedicated class/file
47     template<typename NEIGHBORHOOD>
48     class HoodAdapter
49     {
50     public:
HoodAdapter(const NEIGHBORHOOD & hood)51         inline HoodAdapter(const NEIGHBORHOOD& hood) :
52             hood(hood)
53         {}
54 
55         template<int X, int Y>
operator ()(FixedCoord<X,Y,0>,const AMRDiffusionCell & origin) const56         const AMRDiffusionCell *operator()(FixedCoord<X, Y, 0>, const AMRDiffusionCell& origin) const
57         {
58             Coord<2> searchPoint = origin.logicalCoord +
59                 Coord<2>(X * origin.logicalOffset.x(),
60                          Y * origin.logicalOffset.y());
61 
62             // std::cout << "looking for " << searchPoint << "\n";
63             const AMRDiffusionCell *res = 0;
64 
65             if (origin.logicalCoord == Coord<2>(16, 0)) {
66                 // std::cout << "  looking for " << searchPoint << "\n"
67                 //           << "  FixedCoord<" << X << ", " << Y << ">\n";
68             }
69 
70             // most accesses go to (0, 0)
71             res = hood[FixedCoord<0, 0>()].lookup(searchPoint);
72             if (res) {
73                 // if (origin.logicalCoord == Coord<2>(16, 0)) std::cout << "    found at A\n";
74                 return res;
75             }
76 
77             res = hood[FixedCoord<-1, -1>()].lookup(searchPoint);
78             if (res) {
79                 // if (origin.logicalCoord == Coord<2>(16, 0)) std::cout << "    found at B\n";
80                 return res;
81             }
82 
83             res = hood[FixedCoord< 0, -1>()].lookup(searchPoint);
84             if (res) {
85                 // if (origin.logicalCoord == Coord<2>(16, 0)) std::cout << "    found at C\n";
86                 return res;
87             }
88 
89             res = hood[FixedCoord< 1, -1>()].lookup(searchPoint);
90             if (res) {
91                 // if (origin.logicalCoord == Coord<2>(16, 0)) std::cout << "    found at D\n";
92                 return res;
93             }
94 
95             // same row:
96             res = hood[FixedCoord<-1,  0>()].lookup(searchPoint);
97             if (res) {
98                 // if (origin.logicalCoord == Coord<2>(16, 0)) std::cout << "    found at E\n";
99                 return res;
100             }
101 
102             res = hood[FixedCoord< 1,  0>()].lookup(searchPoint);
103             if (res) {
104                 // if (origin.logicalCoord == Coord<2>(16, 0)) std::cout << "    found at F\n";
105                 return res;
106             }
107 
108             // lower row:
109             res = hood[FixedCoord<-1,  1>()].lookup(searchPoint);
110             if (res) {
111                 // if (origin.logicalCoord == Coord<2>(16, 0)) std::cout << "    found at G\n";
112                 return res;
113             }
114 
115             res = hood[FixedCoord< 0,  1>()].lookup(searchPoint);
116             if (res) {
117                 // if (origin.logicalCoord == Coord<2>(16, 0)) std::cout << "    found at H\n";
118                 return res;
119             }
120 
121             res = hood[FixedCoord< 1,  1>()].lookup(searchPoint);
122             if (res) {
123                 // if (origin.logicalCoord == Coord<2>(16, 0)) std::cout << "    found at I\n";
124                 return res;
125             }
126 
127             // std::cout << "searchPoint: " << searchPoint << "\n"
128             //           << "origin: " << origin.logicalCoord << ", " << origin.logicalOffset << "\n";
129             throw std::logic_error("neighbor not found");
130         }
131 
132     private:
133         const NEIGHBORHOOD& hood;
134     };
135 
136     // fixme: move this to dedicated class/file
137     // fixme: ignore non-leaf nodes in size calculation and visitor pattern?
138     class Iterator
139     {
140     public:
Iterator(const AMRDiffusionCell * cell)141         Iterator(const AMRDiffusionCell *cell) :
142             cursors(1, cell)
143         {}
144 
operator *() const145         const AMRDiffusionCell& operator*() const
146         {
147             return *cursors.back();
148         }
149 
operator ++()150         void operator++()
151         {
152             // std::cout << "operator++1\n";
153             if (cursors.back()->sublevel.size() != 0) {
154                 cursors.push_back(&cursors.back()->sublevel[0][0]);
155                 // std::cout << "operator++2b\n";
156                 return;
157             }
158 
159             cursors.back()++;
160             // we'll need to pop all cursors which have reached the
161             // end of their superordinate level (but preserve the
162             // top-most cursor as it may be part of the topmost layer)
163             while ((cursors.size() > 1) &&
164                    ((*(cursors.end() - 2))->sublevel[0].end() == cursors.back())) {
165                 cursors.pop_back();
166                 cursors.back()++;
167             }
168             // std::cout << "operator++2a\n";
169         }
170 
operator ==(const Iterator & other) const171         inline bool operator==(const Iterator& other) const
172         {
173             return cursors.back() == other.cursors.back();
174         }
175 
operator !=(const Iterator & other) const176         inline bool operator!=(const Iterator& other) const
177         {
178             return cursors.back() != other.cursors.back();
179         }
180 
operator ->() const181         inline const AMRDiffusionCell* operator->() const
182         {
183             return cursors.back();
184         }
185 
186     private:
187         std::vector<const AMRDiffusionCell*> cursors;
188     };
189 
190     typedef Iterator iterator;
191     typedef Iterator const_iterator;
192 
193     // template<typename NEIGHBORHOOD>
194     // class LookupHelper
195     // {
196     // public:
197     //     LookupHelper(const NEIGHBORHOOD *hood) :
198     //         hood(hood)
199     //     {}
200 
201     //     inline
202     //     const AMRDiffusionCell& operator()(const unsigned long x, const unsigned long y) const
203     //     {
204     //         Coord<2> relativeCoord;
205     //         unsigned long baseCoordX = x & BASE_DIM_BITMASK;
206     //         unsigned long baseCoordY = y & BASE_DIM_BITMASK;
207 
208     //         if (baseCoordX < centerX) {
209     //             relativeCoord.x() = -1;
210     //         } else {
211     //             if (baseCoordX > centerX) {
212     //                 relativeCoord.x() = 1;
213     //             }
214     //         }
215 
216     //         if (baseCoordY < centerY) {
217     //             relativeCoord.y() = -1;
218     //         } else {
219     //             if (baseCoordY > centerY) {
220     //                 relativeCoord.y() = 1;
221     //             }
222     //         }
223 
224     //         return (*this)((*hood)[relativeCoord], x >> BASE_DIM_BITS, y >> BASE_DIM_BITS);
225     //     }
226 
227     //     inline
228     //     const AMRDiffusionCell& operator()(const AMRDiffusionCell& cell, const unsigned long x, const unsigned long y) const
229     //     {
230     //         if (cell.dim == Coord<2>()) {
231     //             return cell;
232     //         }
233 
234     //         // Coord<2> relativeCoord(
235     //         //     x & PATCH_DIM_BITMASK,
236     //         //     y & PATCH_DIM_BITMASK);
237     //         // return (*this)(cell[relativeCoord], x >> PATCH_DIM_BITS, y >> PATCH_DIM_BITS);
238     //         // fixme
239     //         return cell;
240     //     }
241 
242     // private:
243     //     const NEIGHBORHOOD *hood;
244     //     unsigned long centerX;
245     //     unsigned long centerY;
246     // };
247 
248 
AMRDiffusionCell(const FloatCoord<2> & pos=FloatCoord<2> (0,0),const FloatCoord<2> & dim=FloatCoord<2> (1,1),const Coord<2> & logicalCoord=Coord<2> (),const Coord<2> & logicalOffset=Coord<2> (0,0),const double value=0,const double influx=0,const int depth=0,const int maxDepth=0,const bool edgeCell=false,const double contagiousFlag=0)249     inline AMRDiffusionCell(
250         const FloatCoord<2>& pos = FloatCoord<2>(0, 0),
251         const FloatCoord<2>& dim = FloatCoord<2>(1, 1),
252         const Coord<2>& logicalCoord = Coord<2>(),
253         const Coord<2>& logicalOffset = Coord<2>(0, 0),
254         const double value = 0,
255         const double influx = 0,
256         const int depth = 0,
257         const int maxDepth = 0,
258         const bool edgeCell = false,
259         const double contagiousFlag = 0) :
260         pos(pos),
261         dim(dim),
262         logicalCoord(logicalCoord),
263         logicalOffset(logicalOffset),
264         value(value),
265         influx(influx),
266         depth(depth),
267         maxDepth(maxDepth),
268         edgeCell(edgeCell),
269         contagiousFlag(contagiousFlag)
270     {}
271 
272     class API : public APITraits::HasUnstructuredGrid
273     {};
274 
275     // fixme: use 2 nano steps to avoid refinement/coarseing to create gaps of 2 refinement levels
276 
277     template<typename NEIGHBORHOOD>
update(const NEIGHBORHOOD & hood,int)278     void update(const NEIGHBORHOOD& hood, int)
279     {
280         *this = hood[FixedCoord<0, 0>()];
281         HoodAdapter<NEIGHBORHOOD> adapter(hood);
282         actualUpdate(adapter);
283     }
284 
begin() const285     Iterator begin() const
286     {
287         return Iterator(this);
288     }
289 
end() const290     Iterator end() const
291     {
292         return Iterator(this + 1);
293     }
294 
size() const295     std::size_t size() const
296     {
297         std::size_t ret = 1;
298         if (sublevel.size()) {
299             for (const AMRDiffusionCell *i = sublevel[0].begin(); i != sublevel[0].end(); ++i) {
300                 ret += i->size();
301             }
302         }
303 
304         return ret;
305     }
306 
getPoint() const307     FloatCoord<2> getPoint() const
308     {
309         return pos;
310     }
311 
getShape() const312     std::vector<FloatCoord<2> > getShape() const
313     {
314         std::vector<FloatCoord<2> > shape;
315         shape << pos
316               << pos + FloatCoord<2>(dim[0], 0)
317               << pos + dim
318               << pos + FloatCoord<2>(0, dim[1]);
319         return shape;
320     }
321 
322 // fixme: private:
323 
324     std::vector<FixedArray<AMRDiffusionCell, SUBLEVEL_TILE_SIZE> > sublevel;
325     FloatCoord<2> pos;
326     FloatCoord<2> dim;
327     Coord<2> logicalCoord;
328     Coord<2> logicalOffset;
329     double value;
330     double influx;
331     int depth;
332     int maxDepth;
333     bool edgeCell;
334     double contagiousFlag;
335 
336 #define HOOD(X, Y) hood(FixedCoord<X, Y>(), *this)
337 
338     template<typename NEIGHBORHOOD>
actualUpdate(const NEIGHBORHOOD & hood)339     void actualUpdate(const NEIGHBORHOOD& hood)
340     {
341         // fixme
342         value =
343             influx +
344             0.25 *
345             (HOOD( 0, -1)->value +
346              HOOD(-1,  0)->value +
347              HOOD( 1,  0)->value +
348              HOOD( 0,  1)->value);
349 
350         // if (contagiousFlag == 1.0) {
351         //     std::cout << "infection at " << logicalCoord << ", " << logicalOffset << "\n";
352         // }
353 
354         // if (logicalCoord == Coord<2>(16, 0)) {
355         //     const AMRDiffusionCell *c = HOOD(-1, 0);
356         //     std::cout << "state at " << logicalCoord << " is " << contagiousFlag
357         //               << ", left neighbor is " << c->logicalCoord
358         //               << " of size " << c->logicalOffset
359         //               << " with " << c->contagiousFlag
360         //               << " and edge: " << c->edgeCell
361         //               << "\n";
362         // }
363 
364         contagiousFlag =
365             std::max(contagiousFlag,
366                      std::max(HOOD(0, -1)->contagiousFlag,
367                               std::max(HOOD(-1, 0)->contagiousFlag,
368                                        std::max(HOOD(1, 0)->contagiousFlag,
369                                                 HOOD(0, 1)->contagiousFlag))));
370 
371         if (sublevel.size()) {
372             for (int i = 0; i < sublevel[0].size(); ++i) {
373                 sublevel[0][i].actualUpdate(hood);
374             }
375         } else {
376             if (depth < maxDepth) {
377                 // check for refinement, fixme: extract into method
378                 double delta = 0;
379                 delta = std::max(delta, std::abs(value - HOOD(0, -1)->value));
380                 delta = std::max(delta, std::abs(value - HOOD(0,  1)->value));
381                 delta = std::max(delta, std::abs(value - HOOD(-1, 0)->value));
382                 delta = std::max(delta, std::abs(value - HOOD( 1, 0)->value));
383 
384                 if (delta > DELTA_MAX) {
385                     sublevel.resize(1);
386                     FloatCoord<2> sublevelDim(
387                         dim[0] / SUBLEVEL_DIM_X,
388                         dim[1] / SUBLEVEL_DIM_Y);
389 
390                     Coord<2> newLogicalOffset(
391                         logicalOffset.x() / SUBLEVEL_DIM_X,
392                         logicalOffset.y() / SUBLEVEL_DIM_Y);
393 
394                     for (int y = 0; y < SUBLEVEL_DIM_Y; ++y) {
395                         for (int x = 0; x < SUBLEVEL_DIM_X; ++x) {
396                             Coord<2> index(x, y);
397                             FloatCoord<2> newPos = pos + sublevelDim.scale(index);
398                             Coord<2> newLogicalCoord = logicalCoord + index.scale(newLogicalOffset);
399 
400                             sublevel[0] << AMRDiffusionCell(
401                                 newPos,
402                                 sublevelDim,
403                                 newLogicalCoord,
404                                 newLogicalOffset,
405                                 value,
406                                 influx,
407                                 depth + 1,
408                                 maxDepth,
409                                 edgeCell,
410                                 contagiousFlag);
411                         }
412                     }
413                 }
414             }
415         }
416     }
417 
lookup(const Coord<2> & searchPoint) const418     const AMRDiffusionCell *lookup(const Coord<2>& searchPoint) const
419     {
420         Coord<2> oppositeSide = logicalCoord + logicalOffset;
421         bool outside =
422             (searchPoint.x() <  logicalCoord.x()) ||
423             (searchPoint.x() >= oppositeSide.x()) ||
424             (searchPoint.y() <  logicalCoord.y()) ||
425             (searchPoint.y() >= oppositeSide.y());
426 
427         if (edgeCell && outside) {
428             // std::cout << "  edgeCell, searchPoint : " << searchPoint  << "\n"
429             //           << "  edgeCell, logicalCoord: " << logicalCoord << "\n"
430             //           << "  edgeCell, oppositeSide: " << oppositeSide << "\n\n";
431             return this;
432         }
433 
434         if (edgeCell || outside) {
435             // std::cout << "  outside(" << searchPoint << ")\n";
436             return 0;
437         }
438 
439         if (sublevel.size() == 0) {
440             // std::cout << "  no more sublevels\n";
441             return this;
442         }
443 
444         for (std::size_t i = 0; i < sublevel[0].size(); ++i) {
445             const AMRDiffusionCell *ret = sublevel[0][i].lookup(searchPoint);
446             if (ret) {
447                 // std::cout << "  child hit\n";
448                 return ret;
449             }
450         }
451 
452         return 0;
453     }
454 
455     // {
456     //     double delta = 0;
457     //     delta = std::max(std::abs(HOOD(0, 1) - HOOD( 0, -1)),
458     //                      std::abs(HOOD(1, 0) - HOOD(-1,  0)));
459 
460 
461     //     // do not descent if we're already on the finest level
462     //     if (sublevel.dimensions == Coord<2>(0, 0)) {
463     //         simpleUpdate(hood, delta);
464     //     } else {
465     //         hierarchicalUpdate(hood, delta);
466     //     }
467 
468     // }
469 
470     // template<typename NEIGHBORHOOD>
471     // void simpleUpdate(const NEIGHBORHOOD& hood, const double delta)
472     // {
473     //     // refine if delta of neighbors is too large
474     //     if (delta > DELTA_MAX) {
475     //         sublevel.resize(Coord<2>(PATCH_DIM, PATCH_DIM));
476     //         FloatCoord newDim(dim.x() * (1.0 / PATCH_DIM),
477     //                           dim.y() * (1.0 / PATCH_DIM));
478 
479     //         for (int y = 0; y < PATCH_DIM; ++y) {
480     //             for (int x = 0; x < PATCH_DIM; ++x) {
481     //                 sublevel[Coord<2>(x, y)] = AMRDiffusionCell(
482     //                     pos + FloatCoord<2>(newDim.x() * x, newDim.y() * y),
483     //                     newDim,
484     //                     value,
485     //                     depth + 1);
486     //             }
487     //         }
488 
489     //         return;
490     //     }
491 
492     //     value =
493     //         (HOOD( 0, -1).value +
494     //          HOOD(-1,  0).value +
495     //          HOOD( 1,  0).value +
496     //          HOOD( 0,  1).value);
497     // }
498 
499     // template<typename NEIGHBORHOOD>
500     // void hierarchicalUpdate(const NEIGHBORHOOD& hood, const double delta)
501     // {
502     //     // coarsen if delta of neighbors is small enough
503     //     if (delta <= DELTA_MAX) {
504     //         value = gatherFromSublevel();
505     //         sublevel.resize(Coord<2>(0, 0));
506 
507     //         return;
508     //     }
509 
510     //     // 1 step on coarse level equals PATCH_DIM steps on the
511     //     // finer levels.
512     //     for (int t = 0; t < PATCH_DIM; ++t) {
513     //         updateFinerLevel(hood);
514     //     }
515 
516     //     value = gatherFromSublevel();
517     // }
518 
519     // template<typename NEIGHBORHOOD>
520     // void updateFinerLevel(const NEIGHBORHOOD& hood, double delta)
521     // {
522     //     for (int y = 0; y < PATCH_DIM; ++y) {
523     //         for (int x = 0; x < PATCH_DIM; ++x) {
524     //             // fixme: use wrapper object instead of hood
525     //             sublevel[Coord<2>(x, y)].actualUpdate(hood);
526     //         }
527     //     }
528     // }
529 
530     // double gatherFromSublevel() const
531     // {
532     //     double newValue = 0;
533     //     for (int y = 0; y < PATCH_DIM; ++y) {
534     //         for (int x = 0; x < PATCH_DIM; ++x) {
535     //             newValue += sublevel[Coord(x, y)].value;
536     //         }
537     //     }
538     //     newValue *= 1.0 / (double(PATCH_DIM) * double(PATCH_DIM));
539 
540     //     return newValue;
541     // }
542 
543 #undef HOOD
544 };
545 
546 class AMRInitializer : public SimpleInitializer<AMRDiffusionCell<> >
547 {
548 public:
AMRInitializer(const Coord<2> dim,const unsigned steps)549     explicit AMRInitializer(
550         const Coord<2> dim,
551         const unsigned steps) :
552         SimpleInitializer<AMRDiffusionCell<> >(dim, steps)
553     {}
554 
grid(GridBase<AMRDiffusionCell<>,2> * ret)555     virtual void grid(GridBase<AMRDiffusionCell<>, 2> *ret)
556     {
557         CoordBox<2> box = ret->boundingBox();
558         int depth = 0;
559         int maxDepth = 2;
560 
561         Coord<2> logicalOffset(1, 1);
562         for (int j = 0; j < maxDepth; ++j) {
563             logicalOffset = logicalOffset.scale(
564                 Coord<2>(
565                     AMRDiffusionCell<>::sublevelDimX(),
566                     AMRDiffusionCell<>::sublevelDimY()));
567         }
568 
569         Coord<2> logicalEdgePos = box.origin.scale(logicalOffset);
570         Coord<2> logicalEdgeOffset = Coord<2>(
571             logicalOffset.x() * box.dimensions.x(),
572             logicalOffset.y() * box.dimensions.y());
573 
574         ret->setEdge(AMRDiffusionCell<>(
575                          // fixme:
576                          FloatCoord<2>(-1, -1),
577                          FloatCoord<2>( 2,  2),
578                          logicalEdgePos,
579                          logicalEdgeOffset,
580                          0.0,
581                          0.0,
582                          0,
583                          0,
584                          true));
585 
586         for (CoordBox<2>::Iterator i = box.begin(); i != box.end(); ++i) {
587             double influx = 0;
588             if (i->y() == 0) {
589                 influx = i->x();
590                 if (i->x() >= (box.dimensions.x() / 2)) {
591                     influx = box.dimensions.x() - influx - 1;
592                 }
593 
594                 influx *= influx;
595             }
596 
597             FloatCoord<2> dim(1, 1);
598             FloatCoord<2> pos = dim.scale(*i);
599 
600             Coord<2> logicalPos = i->scale(logicalOffset);
601 
602 
603             // fixme: kill this?
604             // // no refinement on boundary
605             // if ((i->y() == 0) || (i->y() == (box.dimensions.y() - 1)) ||
606             //     (i->x() == 0) || (i->x() == (box.dimensions.x() - 1))) {
607             //     maxDepth = 0;
608             // }
609 
610             double flag = 0;
611             if (*i == Coord<2>(0, 0)) {
612                 flag = 1;
613             }
614             AMRDiffusionCell<> cell(pos, dim, logicalPos, logicalOffset, 0, influx, depth, maxDepth, false, flag);
615             ret->set(*i, cell);
616         }
617     }
618 };
619 
620 // class FooBar
621 // {
622 // public:
623 
624 //     template<typename WRITER>
625 //     void dumpData(WRITER *writer)
626 //     {
627 //         double dim = 10;
628 
629 //         for (int i = 0; i < 10; ++i) {
630 //             for (int x = 0; x < 8; ++x) {
631 //                 for (int y = 0; y < 8; ++y) {
632 //                     writer->addQuad(FloatCoord<2>(x * dim, y * dim), FloatCoord<2>(dim, dim));
633 //                 }
634 //             }
635 
636 //             dim *= 0.125;
637 //         }
638 //     }
639 
640 //     int dummy;
641 // };
642 
643 
main(int argc,char ** argv)644 int main(int argc, char **argv)
645 {
646     // Grid<FooBar> grid(Coord<2>(1, 1));
647     // SiloWriter<FooBar> writer("foobar", 1);
648     // writer.stepFinished(grid, 14, WRITER_INITIALIZED);
649 
650     Coord<2> gridDim(15, 10);
651     int maxSteps = 1000;
652     SerialSimulator<AMRDiffusionCell<> > sim(new AMRInitializer(gridDim, maxSteps));
653 
654     SiloWriter<AMRDiffusionCell<> > *siloWriter = new SiloWriter<AMRDiffusionCell<> >("AMR", 1);
655     siloWriter->addSelectorForUnstructuredGrid(
656         &AMRDiffusionCell<>::value, "value");
657     siloWriter->addSelectorForUnstructuredGrid(
658         &AMRDiffusionCell<>::contagiousFlag, "flag");
659     sim.addWriter(siloWriter);
660 
661     sim.run();
662 
663     return 0;
664 }
665