GADGET-4
fof_nearest.cc
Go to the documentation of this file.
1/*******************************************************************************
2 * \copyright This file is part of the GADGET4 N-body/SPH code developed
3 * \copyright by Volker Springel. Copyright (C) 2014-2020 by Volker Springel
4 * \copyright (vspringel@mpa-garching.mpg.de) and all contributing authors.
5 *******************************************************************************/
6
12#include "gadgetconfig.h"
13
14#ifdef FOF
15
16#include <math.h>
17#include <mpi.h>
18#include <stdio.h>
19#include <stdlib.h>
20#include <string.h>
21
22#include "../data/allvars.h"
23#include "../data/dtypes.h"
24#include "../data/intposconvert.h"
25#include "../data/mymalloc.h"
26#include "../domain/domain.h"
27#include "../fof/fof.h"
28#include "../logs/timer.h"
29#include "../main/simulation.h"
30#include "../mpi_utils/generic_comm.h"
31#include "../ngbtree/ngbtree.h"
32#include "../system/system.h"
33
34/* local data structure for collecting particle/cell data that is sent to other processors if needed */
35
36struct fofdata_in : data_in_generic
37{
38 MyIntPosType IntPos[3];
39 MyFloat Hsml;
40};
41
42/* local data structure that holds results acquired on remote processors */
43struct fofdata_out
44{
45 MyFloat Distance;
46 MyIDStorage MinID;
47 int MinIDTask;
48#if defined(SUBFIND)
49 MyFloat DM_Hsml;
50#endif
51};
52
53template <typename T_tree, typename T_domain, typename T_partset>
54class fofdata_comm : public generic_comm<fofdata_in, fofdata_out, T_tree, T_domain, T_partset>
55{
56 public:
58 using gcomm::D;
59 using gcomm::Thread;
60 using gcomm::Tp; // This makes sure that we can access Tp from the base class without having to use "this->Tp"
61 using gcomm::Tree;
62
63 /* need to call the base class constructor explicitly */
64 fofdata_comm(T_domain *dptr, T_tree *tptr, T_partset *pptr)
65 : generic_comm<fofdata_in, fofdata_out, T_tree, T_domain, T_partset>(dptr, tptr, pptr)
66 {
67 }
68
69 /* routine that fills the relevant particle/cell data into the input structure defined above */
70 void particle2in(fofdata_in *in, int i)
71 {
72 for(int k = 0; k < 3; k++)
73 in->IntPos[k] = Tp->P[i].IntPos[k];
74
75 in->Hsml = Tp->fof_nearest_hsml[i];
76 }
77
78 /* routine to store or combine result data */
79 void out2particle(fofdata_out *out, int i, int mode)
80 {
81 if(out->Distance < Tp->fof_nearest_distance[i])
82 {
83 Tp->fof_nearest_distance[i] = out->Distance;
84 Tp->MinID[i] = out->MinID;
85 Tp->MinIDTask[i] = out->MinIDTask;
86#if defined(SUBFIND)
87 Tp->PS[i].v.DM_Hsml = out->DM_Hsml;
88#endif
89 }
90 }
91
92 int evaluate(int target, int mode, int thread_id, int action, fofdata_in *in, int numnodes, node_info *firstnode, fofdata_out &out)
93 {
94 MyIntPosType *intpos = in->IntPos;
95 double h = in->Hsml;
96 int index = -1;
97 double r2max = MAX_REAL_NUMBER;
98
99 /* Now start the actual tree-walk computation for this particle */
100
101 for(int k = 0; k < numnodes; k++)
102 {
103 int no;
104
105 if(mode == MODE_LOCAL_PARTICLES)
106 {
107 no = Tree->MaxPart; /* root node */
108 }
109 else
110 {
111 no = firstnode[k].Node;
112 no = Tree->get_nodep(no)->nextnode; /* open it */
113 }
114
115 int shmrank = Tree->TreeSharedMem_ThisTask;
116
117 while(no >= 0)
118 {
119 if(no < Tree->MaxPart) /* single particle */
120 {
121 int p = no;
122 auto P = Tree->get_Pp(no, shmrank);
123
124 no = Tree->get_nextnodep(shmrank)[no]; /* note: here shmrank cannot change */
125
126 if(shmrank != Tree->TreeSharedMem_ThisTask)
127 Terminate("this routine may not consider shared memory particles");
128
129 double dxyz[3];
130 Tp->nearest_image_intpos_to_pos(P->IntPos, intpos, dxyz); /* converts the integer distance to floating point */
131
132 double dist = h;
133
134 if(fabs(dxyz[0]) > dist)
135 continue;
136 if(fabs(dxyz[1]) > dist)
137 continue;
138 if(fabs(dxyz[2]) > dist)
139 continue;
140
141 double r2 = dxyz[0] * dxyz[0] + dxyz[1] * dxyz[1] + dxyz[2] * dxyz[2];
142
143 if(r2 < r2max && r2 < h * h)
144 {
145 index = p;
146 r2max = r2;
147 }
148 }
149 else if(no < Tree->MaxPart + Tree->MaxNodes) /* internal node */
150 {
151 if(mode == MODE_IMPORTED_PARTICLES)
152 {
153 if(no < Tree->FirstNonTopLevelNode) /* we reached a top-level node again, which means that we are done with the
154 branch */
155 break;
156 }
157
158 fofnode *current = Tree->get_nodep(no, shmrank);
159
160 if(current->level <= LEVEL_ALWAYS_OPEN)
161 {
162 /* we always open the root node (its full node length can't be stored in the integer type */
163 no = current->nextnode; /* no change in shmrank expected here */
164 shmrank = current->nextnode_shmrank;
165 continue;
166 }
167
168 int nosaved = no;
169
170 no = current->sibling; /* in case the node can be discarded */
171 shmrank = current->sibling_shmrank;
172
173 double dxyz[3];
174 Tp->nearest_image_intpos_to_pos(current->center.da, intpos,
175 dxyz); /* converts the integer distance to floating point */
176
177 double len = (((MyIntPosType)1) << (BITS_FOR_POSITIONS - current->level)) * Tp->FacIntToCoord;
178
179 double dist = h + 0.5 * len;
180
181 if(fabs(dxyz[0]) > dist)
182 continue;
183 if(fabs(dxyz[1]) > dist)
184 continue;
185 if(fabs(dxyz[2]) > dist)
186 continue;
187
188 /* now test against the minimal sphere enclosing everything */
189 dist += FACT1 * len;
190 if(dxyz[0] * dxyz[0] + dxyz[1] * dxyz[1] + dxyz[2] * dxyz[2] > dist * dist)
191 continue;
192
193 int p = current->nextnode;
194
195 /* in case the next node after opening is not a top-level node, we have either reached a leaf node or are in a local
196 * branch we need to do nothing if we would end up on different shared memory thread */
197 if(p < Tree->MaxPart || (p >= Tree->FirstNonTopLevelNode && p < Tree->MaxPart + Tree->MaxNodes))
198 {
199 if(current->nextnode_shmrank != Tree->TreeSharedMem_ThisTask)
200 {
201 int task = D->ThisTask + current->nextnode_shmrank - Tree->TreeSharedMem_ThisTask;
202
203 if(target >= 0) /* export */
204 Tree->tree_export_node_threads_by_task_and_node(task, nosaved, target, &Thread);
205
206 no = current->sibling; /* in case the node can be discarded */
207 shmrank = current->sibling_shmrank;
208 continue;
209 }
210 }
211
212 no = current->nextnode; /* ok, we need to open the node */
213 shmrank = current->nextnode_shmrank;
214 }
215 else if(no >= Tree->ImportedNodeOffset) /* point from imported nodelist */
216 {
217 Terminate("do not expect imported points here");
218 }
219 else /* pseudo particle */
220 {
221 if(mode == MODE_LOCAL_PARTICLES)
222 if(target >= 0)
223 Tree->tree_export_node_threads(no, target, &Thread);
224
225 no = Tree->get_nextnodep(shmrank)[no - Tree->MaxNodes];
226 /* note: here shmrank does not need to change */
227 }
228 }
229 }
230
231 if(index >= 0)
232 {
233 out.Distance = sqrt(r2max);
234 out.MinID = Tp->MinID[Tp->Head[index]];
235 out.MinIDTask = Tp->MinIDTask[Tp->Head[index]];
236#if defined(SUBFIND)
237 out.DM_Hsml = Tp->PS[index].v.DM_Hsml;
238#endif
239 }
240 else
241 {
242 out.Distance = MAX_REAL_NUMBER;
243 out.MinID.set(0);
244 out.MinIDTask = 0;
245#if defined(SUBFIND)
246 out.DM_Hsml = 0;
247#endif
248 }
249
250 return 0;
251 }
252};
253
254template <typename partset>
255double fof<partset>::fof_find_nearest_dmparticle(void)
256{
257#ifdef LEAN
258 return 0;
259#endif
260
261 double tstart = Logs.second();
262
263 mpi_printf("FOF: Start finding nearest dm-particle (presently allocated=%g MB)\n", Mem.getAllocatedBytesInMB());
264
265 Tp->fof_nearest_distance = (MyFloat *)Mem.mymalloc("fof_nearest_distance", sizeof(MyFloat) * Tp->NumPart);
266 Tp->fof_nearest_hsml = (MyFloat *)Mem.mymalloc("fof_nearest_hsml", sizeof(MyFloat) * Tp->NumPart);
267
268 int *TargetList = (int *)Mem.mymalloc("TargetList", Tp->NumPart * sizeof(int));
269
270 int Nsearch = 0;
271
272 for(int n = 0; n < Tp->NumPart; n++)
273 {
274 if(is_type_secondary_link_type(Tp->P[n].getType()))
275 {
276 Tp->fof_nearest_distance[n] = MAX_REAL_NUMBER;
277 if(Tp->P[n].getType() == 0 && Tp->SphP[n].Hsml > 0)
278 Tp->fof_nearest_hsml[n] = Tp->SphP[n].Hsml;
279 else
280 Tp->fof_nearest_hsml[n] = 0.1 * Tp->LinkL;
281
282 TargetList[Nsearch++] = n;
283 }
284 }
285
286 fofdata_comm<foftree<partset>, domain<partset>, partset> commpattern{FoFDomain, &FoFNgbTree, Tp};
287
288 int iter = 0;
289 long long ntot;
290 /* we will repeat the whole thing for those particles where we didn't find enough neighbours */
291 do
292 {
293 double t0 = Logs.second();
294
295 commpattern.execute(Nsearch, TargetList, MODE_DEFAULT);
296
297 /* do final operations on results */
298 int npleft = 0;
299 for(int n = 0; n < Nsearch; n++)
300 {
301 int i = TargetList[n];
302
303 if(Tp->fof_nearest_distance[i] > 1.0e29)
304 {
305 if(Tp->fof_nearest_hsml[i] < 4 * Tp->LinkL) /* we only search out to a maximum distance */
306 {
307 /* need to redo this particle */
308 TargetList[npleft++] = i;
309 Tp->fof_nearest_hsml[i] *= 2.0;
310 if(iter >= MAXITER - 10)
311 {
312 double pos[3];
313 Tp->intpos_to_pos(Tp->P[i].IntPos, pos);
314
315 printf("FOF: i=%d task=%d ID=%d P[i].Type=%d Hsml=%g LinkL=%g nearest=%g pos=(%g|%g|%g)\n", i, ThisTask,
316 (int)Tp->P[i].ID.get(), Tp->P[i].getType(), Tp->fof_nearest_hsml[i], Tp->LinkL,
317 Tp->fof_nearest_distance[i], pos[0], pos[1], pos[2]);
318 myflush(stdout);
319 }
320 }
321 else
322 {
323 Tp->fof_nearest_distance[i] = 0; /* we do not continue to search for this particle */
324 }
325 }
326 }
327
328 sumup_large_ints(1, &npleft, &ntot, Communicator);
329
330 Nsearch = npleft;
331
332 double t1 = Logs.second();
333 if(ntot > 0)
334 {
335 iter++;
336 if(iter > 0)
337 mpi_printf("FOF: fof-nearest iteration %d: need to repeat for %lld particles. (took = %g sec)\n", iter, ntot,
338 Logs.timediff(t0, t1));
339
340 if(iter > MAXITER)
341 Terminate("FOF: failed to converge in fof-nearest\n");
342 }
343 }
344 while(ntot > 0);
345
346 Mem.myfree(TargetList);
347 Mem.myfree(Tp->fof_nearest_hsml);
348 Mem.myfree(Tp->fof_nearest_distance);
349
350 mpi_printf("FOF: done finding nearest dm-particle\n");
351
352 double tend = Logs.second();
353 return Logs.timediff(tstart, tend);
354}
355
356/* now make sure that the following classes are really instantiated, otherwise we may get a linking problem */
357#include "../data/simparticles.h"
358template class fof<simparticles>;
359
360#if defined(LIGHTCONE) && defined(LIGHTCONE_PARTICLES_GROUPS)
361#include "../data/lcparticles.h"
362template class fof<lcparticles>;
363#endif
364
365#endif /* of FOF */
Definition: domain.h:31
virtual void out2particle(T_out *out, int i, int mode)=0
virtual void particle2in(T_in *in, int i)=0
virtual int evaluate(int target, int mode, int thread_id, int action, T_in *in, int numnodes, node_info *firstnode, T_out &out)=0
double timediff(double t0, double t1)
Definition: logs.cc:488
double second(void)
Definition: logs.cc:471
double getAllocatedBytesInMB(void)
Definition: mymalloc.h:71
#define MAXITER
Definition: constants.h:305
#define MODE_DEFAULT
Definition: constants.h:23
#define MODE_IMPORTED_PARTICLES
Definition: constants.h:22
#define FACT1
Definition: constants.h:435
#define MODE_LOCAL_PARTICLES
Definition: constants.h:21
#define MAX_REAL_NUMBER
Definition: constants.h:94
float MyFloat
Definition: dtypes.h:86
uint32_t MyIntPosType
Definition: dtypes.h:35
#define LEVEL_ALWAYS_OPEN
Definition: dtypes.h:383
#define BITS_FOR_POSITIONS
Definition: dtypes.h:37
logs Logs
Definition: main.cc:43
#define Terminate(...)
Definition: macros.h:15
void sumup_large_ints(int n, int *src, long long *res, MPI_Comm comm)
memory Mem
Definition: main.cc:44
half fabs(half arg)
Definition: half.hpp:2627
expr sqrt(half arg)
Definition: half.hpp:2777
unsigned char level
Definition: tree.h:64
unsigned char nextnode_shmrank
Definition: tree.h:66
int nextnode
Definition: tree.h:57
vector< MyIntPosType > center
Definition: tree.h:51
unsigned char sibling_shmrank
Definition: tree.h:65
int sibling
Definition: tree.h:53
Definition: tree.h:77
int Node
Definition: tree.h:78
void myflush(FILE *fstream)
Definition: system.cc:125