Peano
Loading...
Searching...
No Matches
ParticleAMR.py
Go to the documentation of this file.
1# This file is part of the Peano project. For conditions of distribution and
2# use, please see the copyright notice at www.peano-framework.org
3from peano4.solversteps.ActionSet import ActionSet
4
5
6import jinja2
7
8
10 """!
11
12 AMR criterion based upon the particle density
13
14 Most codes use this criterion in combination with a classic mesh-based criterion
15 which ensures that the mesh is not coarser than a certain maximal mesh sizes.
16
17 The algorithm relies heavily on the ParticleTreeAnalysis action set which
18 annotates each cell in the tree with the sum of the vertices associated to
19 the 2^d adjacent vertices. If this sum exceeds certain thresholds and, depending
20 on the setup, some search radii of hosted particles are small enough, the code
21 refines the tree. It is then the job of subsequent runs with
22 UpdateParticleGridAssociation to drop the particles into the newly created
23 cells.
24
25
26 As we work with cell data to guide the adaptivity yet store the particles
27 within vertices, it is clear that refinement pattern tends to smear out. We
28 get rather regularly refined areas around the particles. For most applications,
29 that's totally fine, as it also ensures that
30
31 """
32
34 self,
35 particle_set,
36 particle_tree_analysis,
37 min_particles_per_cell=1,
38 max_particles_per_cell=65536,
39 min_h=0.1,
40 max_h=0.01,
41 ):
42 """
43
44 :: Arguments
45
46
47
48 particle_set: peano4.toolbox.particles.ParticleSet
49 Instance of the particle set that is to be used as trigger. Each particle in the
50 set has a search radius and this one is used to guide the AMR.
51
52 particle_tree_analysis: peano4.toolbox.particles.ParticleTreeAnalysis
53 Tree analysis algorithm. Usually is instantiated over particle_set, but you can
54 also impose another analysis algorithm.
55
56 min_particles_per_cell: Integer (>=0)
57 The tree is not refined if there are less than min_particles_per_cell particles
58 within the cell. By default, this parameter is set to 1 and thus the code refines
59 always if there are more than two particles held in a cell. You can set the value
60 to 0. In this case, we refine whenever there's a particle in a cell and its search
61 radius would allow us to refine further. Set
62 it to a higher number to ensure that there's (approximately) a certain particle
63 counter per cell.
64
65 max_particles_per_cell: Integer
66 Forces the code to refine if there are more than max_particles_per_cell particles
67 held. Has to be bigger or equal to min_particles_per_cell.
68
69 Whenever the AMR criterion refines, it can happen that particles do not
70 drop down further, as their search radius is too small.
71
72 You can also set this argument to zero as well. In this case, the action sets
73 refines all the way down until the mesh size just fits the search radius. It
74 even refines a cell if there's only one particle in it.
75
76 """
77 super(ParticleAMR, self).__init__(descend_invocation_order=1, parallel=False)
78 if min_particles_per_cell < 0:
79 raise Exception(
80 "invalid min number of particles pre cell (has to be positive): {}".format(
81 min_particles_per_cell
82 )
83 )
84
85 if (
86 max_particles_per_cell < min_particles_per_cell
87 or max_particles_per_cell < 0
88 ):
89 raise Exception(
90 "max number of particles ({}) has to be bigger or equal to min number ({}) and it has to be positive".format(
91 max_particles_per_cell, min_particles_per_cell
92 )
93 )
94
95 self.d = {
96 "CELL_DATA_NAME": particle_tree_analysis._cell_data_name,
97 "MIN_PARTICLES_PER_CELL": min_particles_per_cell,
98 "MAX_PARTICLES_PER_CELL": max_particles_per_cell,
99 "MIN_H": min_h,
100 "MAX_H": max_h,
101 "PARTICLE_SET": particle_set.name,
102 "PARTICLE": particle_set.particle_model.name,
103 }
104 pass
105
107 return False
108
109 __Template_TouchCellLastTime = jinja2.Template(
110 """
111 /*
112
113 Erase
114 =====
115
116 If we want to erase a cell, we actually make the region that is to be
117 erased slightly bigger. While we can pick this blow-up factor arbitrarily
118 (up to a factor of three might make sense in some cases), but if we make
119 is 2.0, then an erased cell covers half of its neighbour. If we have a
120 cell which hosts a particle, this cell will then maybe not coarsen, but
121 all of its neighbours do, so the cell is eliminated. This triggers a
122 mesh oscillation. So the factor should either be smaller than 2.0, or we
123 have to veto that an area with particles is overwritten. Indeed we go
124 down the latter route and mark cells that we may not erase with a refine
125 event which describes the mesh status quo.
126
127
128 This is a tricky part. Our idea is that we mark only examine refined cells
129 one level above the finest mesh. These are cells which will be refined and
130 have been refined, and also carry only few particles such that we could
131 safely erase their children. While we only examine refined cells, we do not
132 examine refined cells which have refined children. To find this out, we
133 look at the flag getParentOfRefinedCell().
134
135 */
136 if (
137 not marker.willBeRefined()
138 and
139 fineGridCell{{CELL_DATA_NAME}}.getNumberOfParticles() > {{MAX_PARTICLES_PER_CELL}}
140 and
141 marker.h()(0)/3.0>{{MIN_H}}
142 ) {
143 const double ScaleMarker = 0.8;
144 peano4::grid::GridControlEvent newEntry;
145 newEntry.setRefinementControl( peano4::grid::GridControlEvent::RefinementControl::Refine );
146 newEntry.setOffset( marker.getOffset() + marker.h() * (1.0-ScaleMarker) / 2.0 );
147 newEntry.setWidth( marker.h() * ScaleMarker );
148 newEntry.setH( marker.h()/3.0 * 1.1 );
149 _localGridControlEvents.push_back(newEntry);
150 logDebug( "touchCellLastTime(...)", "add " << newEntry.toString() );
151 }
152
153 if (
154 marker.willBeRefined()
155 and
156 marker.hasBeenRefined()
157 and
158 fineGridCell{{CELL_DATA_NAME}}.getNumberOfParticles() <= {{MIN_PARTICLES_PER_CELL}}
159 and
160 not fineGridCell{{CELL_DATA_NAME}}.getParentOfRefinedCell()
161 and
162 marker.h()(0)<{{MAX_H}} // we will remove the suboctants
163 ) {
164 const double ScaleMarker = 1.4;
165 peano4::grid::GridControlEvent newEntry;
166 newEntry.setRefinementControl( peano4::grid::GridControlEvent::RefinementControl::Erase );
167 newEntry.setOffset( marker.getOffset() + marker.h() * (1.0-ScaleMarker) / 2.0 );
168 newEntry.setWidth( marker.h() * ScaleMarker );
169 newEntry.setH( marker.h() * ScaleMarker );
170 _localGridControlEvents.push_back(newEntry);
171 logDebug( "touchCellLastTime(...)", "add " << newEntry.toString() );
172 }
173"""
174 )
175
176 __Template_EndTraversal = jinja2.Template(
177 """
178 static tarch::multicore::BooleanSemaphore semaphore;
179 tarch::multicore::Lock lock(semaphore);
180
181 // insert and then merge immediately to reduce the
182 // event count
183 _localGridControlEvents = peano4::grid::merge( _localGridControlEvents );
184 _newGridControlEvents.insert( _newGridControlEvents.end(), _localGridControlEvents.begin(), _localGridControlEvents.end() );
185 _newGridControlEvents = peano4::grid::merge( _newGridControlEvents );
186"""
187 )
188
189 def get_body_of_operation(self, operation_name):
190 result = "\n"
191 if operation_name == ActionSet.OPERATION_BEGIN_TRAVERSAL:
192 result = """
193 _localGridControlEvents.clear();
194"""
195 if operation_name == ActionSet.OPERATION_TOUCH_CELL_LAST_TIME:
196 result = self.__Template_TouchCellLastTime.render(**self.d)
197 if operation_name == ActionSet.OPERATION_END_TRAVERSAL:
198 result = self.__Template_EndTraversal.render(**self.d)
199 return result
200
202 return """
203 return _oldGridControlEvents;
204"""
205
206 def get_attributes(self):
207 return """
208 std::vector< peano4::grid::GridControlEvent > _localGridControlEvents;
209 static std::vector< peano4::grid::GridControlEvent > _oldGridControlEvents;
210 static std::vector< peano4::grid::GridControlEvent > _newGridControlEvents;
211"""
212
214 return __name__.replace(".py", "").replace(".", "_")
215
216 def get_static_initialisations(self, full_qualified_classname):
217 return (
218 """
219std::vector< peano4::grid::GridControlEvent > """
220 + full_qualified_classname
221 + """::_oldGridControlEvents;
222std::vector< peano4::grid::GridControlEvent > """
223 + full_qualified_classname
224 + """::_newGridControlEvents;
225"""
226 )
227
228 def get_includes(self):
229 result = jinja2.Template(
230 """
231#include "tarch/multicore/Lock.h"
232#include "vertexdata/{{PARTICLE_SET}}.h"
233#include "globaldata/{{PARTICLE}}.h"
234#include "peano4/grid/grid.h"
235"""
236 )
237 return result.render(**self.d)
238
240 return """
241 #if defined(Parallel)
242 #error not implemented yet
243 #endif
244_oldGridControlEvents.clear();
245_oldGridControlEvents = _newGridControlEvents;
246_newGridControlEvents.clear();
247_oldGridControlEvents = peano4::grid::merge( _oldGridControlEvents );
248"""
Action set (reactions to events)
Definition ActionSet.py:6
AMR criterion based upon the particle density.
Definition ParticleAMR.py:9
get_static_initialisations(self, full_qualified_classname)
get_body_of_operation(self, operation_name)
Return actual C++ code snippets to be inserted into C++ code.
__init__(self, particle_set, particle_tree_analysis, min_particles_per_cell=1, max_particles_per_cell=65536, min_h=0.1, max_h=0.01)
:: Arguments
get_attributes(self)
Return attributes as copied and pasted into the generated class.
get_includes(self)
Return include statements that you need.
get_action_set_name(self)
Return unique action set name.
user_should_modify_template(self)
Is the user allowed to modify the output.