Line data Source code
1 : /*
2 : * Copyright (c) 2009-2015: G-CSC, Goethe University Frankfurt
3 : * Author: Sebastian Reiter
4 : *
5 : * This file is part of UG4.
6 : *
7 : * UG4 is free software: you can redistribute it and/or modify it under the
8 : * terms of the GNU Lesser General Public License version 3 (as published by the
9 : * Free Software Foundation) with the following additional attribution
10 : * requirements (according to LGPL/GPL v3 §7):
11 : *
12 : * (1) The following notice must be displayed in the Appropriate Legal Notices
13 : * of covered and combined works: "Based on UG4 (www.ug4.org/license)".
14 : *
15 : * (2) The following notice must be displayed at a prominent place in the
16 : * terminal output of covered works: "Based on UG4 (www.ug4.org/license)".
17 : *
18 : * (3) The following bibliography is recommended for citation and must be
19 : * preserved in all covered files:
20 : * "Reiter, S., Vogel, A., Heppner, I., Rupp, M., and Wittum, G. A massively
21 : * parallel geometric multigrid solver on hierarchically distributed grids.
22 : * Computing and visualization in science 16, 4 (2013), 151-164"
23 : * "Vogel, A., Reiter, S., Rupp, M., Nägel, A., and Wittum, G. UG4 -- a novel
24 : * flexible software system for simulating pde based models on high performance
25 : * computers. Computing and visualization in science 16, 4 (2013), 165-179"
26 : *
27 : * This program is distributed in the hope that it will be useful,
28 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
29 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30 : * GNU Lesser General Public License for more details.
31 : */
32 :
33 : #ifndef __H__LIB_GRID__MULTI_GRID__
34 : #define __H__LIB_GRID__MULTI_GRID__
35 :
36 : #include <vector>
37 : #include <cassert>
38 : #include "grid/grid.h"
39 : #include "tools/subset_handler_grid.h"
40 : #include "common_attachments.h"
41 : #include "common/util/array_util.h"
42 : #include "multi_grid_child_info.h"
43 : #include "algorithms/attachment_util.h"
44 :
45 : namespace ug
46 : {
47 :
48 : ////////////////////////////////////////////////////////////////////////
49 : /**
50 : * Inherits from \sa Grid.
51 : *
52 : * A MultiGrid represents a grid hierarchy. Elements in a level have a
53 : * parent / child relationship to elements in lower / higher levels.
54 : * Such a hierarchy is normally created by repeated refinement of a
55 : * coarse grid.
56 : * Enhances the Grid interface by methods that work on specific levels.
57 : * The MultiGrid stores all elements in one grid.
58 : * The hierarchy is managed by a SubsetHandler.
59 : * If elements are created and hierarchical insertion is activated, then
60 : * new elements are added one layer higher than their parents.
61 : * (NULL indicates base-level).
62 : *
63 : * Whenever a a level is added or removed, a message is posted at the
64 : * associated MessageHub (MultiGrid::message_hub()). The message has the type
65 : * GridMessage_MultiGridChanged (defined in "lib_grid/lib_grid_messages.h").
66 : * You may register a callback at the grids message-hub if you want to react
67 : * on such a message.
68 : *
69 : * \ingroup lib_grid_grid
70 : */
71 : class MultiGrid : public Grid, public GridObserver
72 : {
73 : friend struct MGVertexInfo;
74 : friend struct MGEdgeInfo;
75 : friend struct MGFaceInfo;
76 : friend struct MGVolumeInfo;
77 :
78 : protected:
79 : typedef MGVertexInfo VertexInfo;
80 : typedef MGEdgeInfo EdgeInfo;
81 : typedef MGFaceInfo FaceInfo;
82 : typedef MGVolumeInfo VolumeInfo;
83 :
84 : public:
85 : // methods from Grid, that would be hidden if not explicitly
86 : // declared as required.
87 : using Grid::begin;
88 : using Grid::end;
89 : using Grid::num;
90 : using Grid::get_grid_objects;
91 : using Grid::create;
92 : using Grid::create_by_cloning;
93 :
94 :
95 : MultiGrid();
96 : /// initialises the grid with the given option.
97 : /** pass an or-combination of constants enumerated in
98 : * VertexOptions, EdgeOptions, FaceOptions, VolumeOptions and GridOptions.*/
99 : MultiGrid(uint options);
100 :
101 : virtual ~MultiGrid();
102 :
103 : void enable_hierarchical_insertion(bool bEnable);
104 0 : inline bool hierarchical_insertion_enabled() {return m_bHierarchicalInsertion;}
105 :
106 : ////////////////////////////////////////////////
107 : // element creation
108 : /// create a custom element on a specific level.
109 : /**
110 : * TGeomObj has to be a geometric object type as described in grid_base_objects.h.
111 : * This method should only be used if a geometric object has to be created
112 : * without a parent in higher levels of the hierarchy.
113 : * Use the create method derived from ug::Grid if you want to specify a parent.
114 : * \{ */
115 : template<class TGeomObj>
116 : typename geometry_traits<TGeomObj>::iterator
117 : create(size_t level);
118 :
119 : template <class TGeomObj>
120 : typename geometry_traits<TGeomObj>::iterator
121 : create(const typename geometry_traits<TGeomObj>::Descriptor& descriptor,
122 : size_t level);
123 : /** \} */
124 :
125 : /// this method creates a new vertex, which has the same type as pCloneMe.
126 : VertexIterator create_by_cloning(Vertex* pCloneMe,
127 : int level);
128 :
129 : /// this method creates a new edge, which has the same type as pCloneMe.
130 : EdgeIterator create_by_cloning(Edge* pCloneMe,
131 : const EdgeVertices& ev,
132 : int level);
133 :
134 : /// this method creates a new face, which has the same type as pCloneMe.
135 : FaceIterator create_by_cloning(Face* pCloneMe,
136 : const FaceVertices& fv,
137 : int level);
138 :
139 : /// this method creates a new volume, which has the same type as pCloneMe.
140 : VolumeIterator create_by_cloning(Volume* pCloneMe,
141 : const VolumeVertices& vv,
142 : int level);
143 :
144 : /// number of levels
145 0 : inline size_t num_levels() const {return (size_t)m_hierarchy.num_subsets();}
146 :
147 : /// index of the highest level.
148 : inline size_t top_level() const;
149 :
150 : /// creates new (empty) levels until num_levels() == lvl+1
151 : inline void level_required(int lvl);
152 :
153 : template <class TElem> inline
154 0 : size_t num(int level) const {return m_hierarchy.num<TElem>(level);}
155 :
156 : template <class TElem> inline
157 : typename geometry_traits<TElem>::iterator
158 0 : begin(int level)
159 : {
160 : //assert(level < (int)num_levels() && "ERROR in MultiGrid::begin(...): requested level too high!");
161 0 : if(level >= (int)num_levels())
162 : return end<TElem>();
163 0 : return m_hierarchy.begin<TElem>(level);
164 : }
165 :
166 : template <class TElem> inline
167 : typename geometry_traits<TElem>::iterator
168 0 : end(int level)
169 : {
170 : //assert(level < (int)num_levels() && "ERROR in MultiGrid::end(...): requested level too high!");
171 0 : if(level >= (int)num_levels())
172 : return end<TElem>();
173 0 : return m_hierarchy.end<TElem>(level);
174 : }
175 :
176 : template <class TElem> inline
177 : typename geometry_traits<TElem>::const_iterator
178 0 : begin(int level) const
179 : {
180 : //assert(level < (int)num_levels() && "ERROR in MultiGrid::begin(...): requested level too high!");
181 0 : if(level >= (int)num_levels())
182 : return end<TElem>();
183 0 : return m_hierarchy.begin<TElem>(level);
184 : }
185 :
186 : template <class TElem> inline
187 : typename geometry_traits<TElem>::const_iterator
188 0 : end(int level) const
189 : {
190 : //assert(level < (int)num_levels() && "ERROR in MultiGrid::end(...): requested level too high!");
191 0 : if(level >= (int)num_levels())
192 : return end<TElem>();
193 0 : return m_hierarchy.end<TElem>(level);
194 : }
195 :
196 : // geometric-object-collection
197 : inline GridObjectCollection
198 : get_grid_objects(int level)
199 0 : {return m_hierarchy.get_grid_objects_in_subset(level);}
200 :
201 : // multi-level-geometric-object-collection
202 0 : virtual GridObjectCollection get_grid_objects()
203 0 : {return m_hierarchy.get_grid_objects();}
204 :
205 : template <class TElem> inline
206 : int get_level(TElem* elem) const
207 0 : {return m_hierarchy.get_subset_index(elem);}
208 :
209 : GridObject* get_parent(GridObject* parent) const;
210 0 : inline GridObject* get_parent(Vertex* o) const {return get_info(o).m_pParent;}
211 0 : inline GridObject* get_parent(Edge* o) const {return get_info(o).m_pParent;}
212 0 : inline GridObject* get_parent(Face* o) const {return m_aaParentFACE[o];}
213 0 : inline GridObject* get_parent(Volume* o) const {return m_aaParentVOL[o];}
214 :
215 : // number of children
216 : template <class TElem> inline
217 0 : bool has_children(TElem* elem) const
218 0 : {return get_info(elem).has_children();}
219 :
220 : ////////////////////////////////
221 : // CHILD QUANTITIES
222 : /// returns the number of children of the given child-type
223 : /** \{ */
224 : template <class TChild, class TElem>
225 : inline size_t num_children(TElem* elem) const {return num_children(elem, TChild());}
226 :
227 : template <class TChild>
228 : size_t num_children(GridObject* elem) const;
229 : /** \} */
230 :
231 : /// returns the total number of children and grand-children.
232 : /** Only children of the same type as the given elements are regarded here.*/
233 : template <class TElem>
234 : inline size_t num_children_total(TElem* elem) const;
235 :
236 : /// Returns the number of child vertices
237 : template <class TElem>
238 0 : inline size_t num_child_vertices(TElem* elem) const {return get_info(elem).num_child_vertices();}
239 :
240 : /// Returns the number of child edges
241 : /** \{ */
242 : template <class TElem>
243 0 : inline size_t num_child_edges(TElem* elem) const {return get_info(elem).num_child_edges();}
244 : inline size_t num_child_edges(Vertex*) const {return 0;}
245 : /** \} */
246 :
247 : /// Returns the number of child faces
248 : /** \{ */
249 : template <class TElem>
250 0 : inline size_t num_child_faces(TElem* elem) const {return get_info(elem).num_child_faces();}
251 : inline size_t num_child_faces(Vertex*) const {return 0;}
252 : inline size_t num_child_faces(Edge*) const {return 0;}
253 : /** \} */
254 :
255 : /// Returns the number of child volumes
256 : /** \{ */
257 0 : inline size_t num_child_volumes(Volume* elem) const {return get_info(elem).num_child_volumes();}
258 : template <class TElem>
259 : inline size_t num_child_volumes(TElem*) const {return 0;}
260 : /** \} */
261 :
262 :
263 : ////////////////////////////////
264 : // CHILD ACCESS
265 : /// returns the i-th child of the given child-type
266 : /** \{ */
267 : template <class TChild, class TElem>
268 : inline TChild* get_child(TElem* elem, size_t ind) const {return get_child(elem, ind, TChild());}
269 :
270 : template <class TChild>
271 : TChild* get_child(GridObject* elem, size_t ind) const;
272 : /** \} */
273 :
274 : /// Returns the child vertex of the given element or NULL if there is none
275 : template <class TElem>
276 0 : inline Vertex* get_child_vertex(TElem* elem) const {return get_info(elem).child_vertex();}
277 :
278 : /// Returns the child edges of the given element or NULL if there is none
279 : /** \{ */
280 : template <class TElem>
281 0 : inline Edge* get_child_edge(TElem* elem, size_t ind) const {return get_info(elem).child_edge(ind);}
282 : inline Edge* get_child_edge(Vertex*, size_t) const {return NULL;}
283 : /** \} */
284 :
285 : /// Returns the child faces of the given element or NULL if there is none
286 : /** \{ */
287 : template <class TElem>
288 0 : inline Face* get_child_face(TElem* elem, size_t ind) const {return get_info(elem).child_face(ind);}
289 : inline Face* get_child_face(Vertex*, size_t) const {return NULL;}
290 : inline Face* get_child_face(Edge*, size_t) const {return NULL;}
291 : /** \} */
292 :
293 : /// Returns the child volumes of the given element or NULL if there is none
294 : /** \{ */
295 0 : inline Volume* get_child_volume(Volume* elem, size_t ind) const {return get_info(elem).child_volume(ind);}
296 : template <class TElem>
297 : inline Volume* get_child_volume(TElem*, size_t) const {return NULL;}
298 : /** \} */
299 :
300 : /// clears the relation between a parent and its children
301 : /** Use with care. This method should only be called if no other option exists.*/
302 : template <class TElem>
303 : void clear_child_connections(TElem* parent);
304 :
305 : /// establishes a parent child connection between the given elements
306 : /** Note that the MultGrid class automatically establishes a parent child
307 : * connection during element creation, based on the suppliend parent argument.
308 : * This method should thus only be used in the rare cases where this
309 : * automatic association is not sufficient.
310 : * Note that only elements of equal or higher dimension can be parent to a
311 : * given element.
312 : * Note that while parent may be NULL, elem has to be supplied.
313 : * The method also sets the parent type if a parent is supplied and leaves the
314 : * parent type as it is if none is supplied.*/
315 : template <class TElem>
316 : void associate_parent(TElem* elem, GridObject* parent);
317 :
318 : /// returns the object-type of the parent of a given object
319 : template <class TElem>
320 : char parent_type(TElem* elem) const;
321 :
322 : /// sets the object-type of the parent of a given object
323 : /** The parent type is normally handled internally. However, e.g. during
324 : * parallel redistribution it may have to be set from outside (e.g. if
325 : * a parent element hasn't been transfered to the same process as its children).*/
326 : template <class TElem>
327 : void set_parent_type(TElem* elem, char type);
328 :
329 : /// for debug purposes
330 : void check_edge_elem_infos(int level) const;
331 : /// for debug purposes
332 : void check_face_elem_infos(int level) const;
333 : /// for debug purposes
334 : void check_volume_elem_infos(int level) const;
335 :
336 : /// this method may be removed in future versions of the MultiGrid-class.
337 : /** You really shouldn't use this method!!!*/
338 : SubsetHandler& get_hierarchy_handler() {return m_hierarchy;}
339 :
340 : ////////////////////////////////////////////////////////////////////////
341 : // Don't invoke the following methods directly!
342 : // They are intended for internal feedback only.
343 :
344 : // grid callbacks
345 : virtual void elements_to_be_cleared(Grid* grid);
346 :
347 : /** In order to correctly register vrt in the hierarchy, we have to
348 : * replace pParent with vrt in the list of children of pParents parent.
349 : * This means that if a grid-observer registered after the multi-grid itself,
350 : * iterates over the list of children of pParents parent in its
351 : * vertex_created method, it won't find pParent.*/
352 : virtual void vertex_created(Grid* grid, Vertex* vrt,
353 : GridObject* pParent = NULL,
354 : bool replacesParent = false);
355 :
356 : /** In order to correctly register e in the hierarchy, we have to
357 : * replace pParent with e in the list of children of pParents parent.
358 : * This means that if a grid-observer registered after the multi-grid itself,
359 : * iterates over the list of children of pParents parent in its
360 : * edge_created method, it won't find pParent.*/
361 : virtual void edge_created(Grid* grid, Edge* e,
362 : GridObject* pParent = NULL,
363 : bool replacesParent = false);
364 :
365 : /** In order to correctly register f in the hierarchy, we have to
366 : * replace pParent with f in the list of children of pParents parent.
367 : * This means that if a grid-observer registered after the multi-grid itself,
368 : * iterates over the list of children of pParents parent in its
369 : * face_created method, it won't find pParent.*/
370 : virtual void face_created(Grid* grid, Face* f,
371 : GridObject* pParent = NULL,
372 : bool replacesParent = false);
373 :
374 : /** In order to correctly register vol in the hierarchy, we have to
375 : * replace pParent with vol in the list of children of pParents parent.
376 : * This means that if a grid-observer registered after the multi-grid itself,
377 : * iterates over the list of children of pParents parent in its
378 : * volume_created method, it won't find pParent.*/
379 : virtual void volume_created(Grid* grid, Volume* vol,
380 : GridObject* pParent = NULL,
381 : bool replacesParent = false);
382 :
383 : virtual void vertex_to_be_erased(Grid* grid, Vertex* vrt,
384 : Vertex* replacedBy = NULL);
385 :
386 : virtual void edge_to_be_erased(Grid* grid, Edge* e,
387 : Edge* replacedBy = NULL);
388 :
389 : virtual void face_to_be_erased(Grid* grid, Face* f,
390 : Face* replacedBy = NULL);
391 :
392 : virtual void volume_to_be_erased(Grid* grid, Volume* vol,
393 : Volume* replacedBy = NULL);
394 :
395 : protected:
396 :
397 : // Note: VertexInfo and EdgeInfo are stored directly, FaceInfo and
398 : // VolumeInfo are stored dynamically.
399 : typedef Attachment<GridObject*> AParent;
400 : typedef Attachment<VertexInfo> AVertexInfo;
401 : typedef Attachment<EdgeInfo> AEdgeInfo;
402 : typedef Attachment<FaceInfo*> AFaceInfo;
403 : typedef Attachment<VolumeInfo*> AVolumeInfo;
404 : typedef Attachment<char> AParentType;
405 :
406 :
407 : // initialization
408 : void init();
409 :
410 : // create levels
411 : void create_levels(int numLevels);
412 :
413 : // info-access
414 : inline VertexInfo& get_info(Vertex* v);
415 : inline EdgeInfo& get_info(Edge* e);
416 : inline FaceInfo& get_info(Face* f);
417 : inline VolumeInfo& get_info(Volume* v);
418 :
419 : // const info-access
420 : inline const VertexInfo& get_info(Vertex* v) const;
421 : inline const EdgeInfo& get_info(Edge* e) const;
422 : inline const FaceInfo& get_info(Face* f) const;
423 : inline const VolumeInfo& get_info(Volume* v) const;
424 :
425 : // elem creation
426 : template <class TElem>
427 0 : inline void element_created(TElem* elem) {element_created<TElem, TElem>(elem, NULL);}
428 :
429 : template <class TElem, class TParent>
430 : void element_created(TElem* elem, TParent* pParent);
431 :
432 : /// called if a newly created element shall replace an old one
433 : template <class TElem, class TParent>
434 : void element_created(TElem* elem, TParent* pParent, TElem* pReplaceMe);
435 :
436 : /// this method is called for elements that havn't got any parent.
437 : template <class TElem>
438 : void element_to_be_erased(TElem* elem);
439 :
440 : /// this method is called for elements with a parent.
441 : template <class TElem, class TParent>
442 : void element_to_be_erased(TElem* elem, TParent* pParent);
443 :
444 : //template <class TElem>
445 : //void element_to_be_replaced(TElem* elemOld, TElem* elemNew);
446 :
447 :
448 : /// returning the number of children of the type of the dummy-argument.
449 : /** \{ */
450 : template <class TElem>
451 : inline size_t num_children(TElem* elem, const Vertex&) const
452 : {return num_child_vertices(elem);}
453 :
454 : template <class TElem>
455 : inline size_t num_children(TElem* elem, const Edge&) const
456 : {return num_child_edges(elem);}
457 :
458 : template <class TElem>
459 : inline size_t num_children(TElem* elem, const Face&) const
460 : {return num_child_faces(elem);}
461 :
462 : template <class TElem>
463 : inline size_t num_children(TElem* elem, const Volume&) const
464 : {return num_child_volumes(elem);}
465 : /** \} */
466 :
467 : /// returning the i-th child of the type of the dummy-argument.
468 : /** \{ */
469 : template <class TElem>
470 : inline Vertex* get_child(TElem* elem, size_t ind, const Vertex&) const
471 : {return get_child_vertex(elem);}
472 :
473 : template <class TElem>
474 : inline Edge* get_child(TElem* elem, size_t ind, const Edge&) const
475 : {return get_child_edge(elem, ind);}
476 :
477 : template <class TElem>
478 : inline Face* get_child(TElem* elem, size_t ind, const Face&) const
479 : {return get_child_face(elem, ind);}
480 :
481 : template <class TElem>
482 : inline Volume* get_child(TElem* elem, size_t ind, const Volume&) const
483 : {return get_child_volume(elem, ind);}
484 : /** \} */
485 :
486 : /// sets the parent for the given object
487 : /** \{ */
488 0 : inline void set_parent(Vertex* o, GridObject* p) {get_info(o).m_pParent = p;}
489 0 : inline void set_parent(Edge* o, GridObject* p) {get_info(o).m_pParent = p;}
490 0 : inline void set_parent(Face* o, GridObject* p) {m_aaParentFACE[o] = p;}
491 0 : inline void set_parent(Volume* o, GridObject* p) {m_aaParentVOL[o] = p;}
492 : /** \} */
493 :
494 : /// adds a child to the given object
495 : /** \{ */
496 : template <class TParent, class TChild>
497 : void add_child(TParent* p, TChild* c);
498 :
499 : template <class TChild>
500 : void add_child(GridObject* p, TChild* c);
501 : /** \} */
502 :
503 : /// removes a child from the given object
504 : /** \{ */
505 : template <class TParent, class TChild>
506 : void remove_child(TParent* p, TChild* c);
507 :
508 : template <class TChild>
509 : void remove_child(GridObject* p, TChild* c);
510 : /** \} */
511 :
512 : /// creates the info-object for the given object (if necessary)
513 : /** \{ */
514 : inline void create_child_info(Vertex* o){}
515 : inline void create_child_info(Edge* o) {}
516 0 : inline void create_child_info(Face* o) {if(!m_aaFaceInf[o]) m_aaFaceInf[o] = new FaceInfo();}
517 0 : inline void create_child_info(Volume* o) {if(!m_aaVolInf[o]) m_aaVolInf[o] = new VolumeInfo();}
518 : /** \} */
519 :
520 : /// releases the info-object for the given object (if necessary)
521 : /** \{ */
522 : inline void release_child_info(Vertex* o) {}
523 : inline void release_child_info(Edge* o) {}
524 0 : inline void release_child_info(Face* o) {if(m_aaFaceInf[o]) delete m_aaFaceInf[o]; m_aaFaceInf[o] = NULL;}
525 0 : inline void release_child_info(Volume* o) {if(m_aaVolInf[o]) delete m_aaVolInf[o]; m_aaVolInf[o] = NULL;}
526 : /** \} */
527 :
528 :
529 : // hierarchy
530 : SubsetHandler m_hierarchy;
531 : bool m_bHierarchicalInsertion;
532 :
533 : // parent attachment
534 : AParent m_aParent;
535 :
536 : // info attachments
537 : AVertexInfo m_aVertexInfo;
538 : AEdgeInfo m_aEdgeInfo;
539 : AFaceInfo m_aFaceInfo;
540 : AVolumeInfo m_aVolumeInfo;
541 : AParentType m_aParentType;
542 :
543 : // parent access - only required for faces and volumes.
544 : Grid::FaceAttachmentAccessor<AParent> m_aaParentFACE;
545 : Grid::VolumeAttachmentAccessor<AParent> m_aaParentVOL;
546 :
547 : // element info access
548 : Grid::VertexAttachmentAccessor<AVertexInfo> m_aaVrtInf;
549 : Grid::EdgeAttachmentAccessor<AEdgeInfo> m_aaEdgeInf;
550 : Grid::FaceAttachmentAccessor<AFaceInfo> m_aaFaceInf;
551 : Grid::VolumeAttachmentAccessor<AVolumeInfo> m_aaVolInf;
552 :
553 : MultiElementAttachmentAccessor<AParentType> m_aaParentType;
554 : };
555 :
556 :
557 :
558 : ////////////////////////////////////////////////////////////////////////
559 : ////////////////////////////////////////////////////////////////////////
560 : /// wrapper that allows to write method that can operate on a ug::Grid and a ug::MultiGrid.
561 : /**
562 : * This template class is specialized for ug::Grid and ug::MultiGrid.
563 : *
564 : * The MGWrapper will most likely be enhanced with more mg-methods, like
565 : * has_child, get_child or parent.
566 : */
567 : template <class TGrid>
568 : class MGWrapper
569 : {
570 : public:
571 : MGWrapper(TGrid& grid);
572 :
573 : inline uint num_levels() const;
574 :
575 : template <class TElem> inline
576 : uint num(int level) const;
577 :
578 : template <class TElem> inline
579 : typename geometry_traits<TElem>::iterator
580 : begin(int level);
581 :
582 : template <class TElem> inline
583 : typename geometry_traits<TElem>::iterator
584 : end(int level);
585 : };
586 :
587 : }// end of namespace
588 :
589 : ////////////////////////////////
590 : // include implementation
591 : #include "multi_grid_impl.hpp"
592 :
593 : #endif
|