@@ -31,6 +31,34 @@ struct LookupTableExp {
3131 }
3232};
3333
34+ // Neighbours of a point of a lookup table, as they are found by the search performed by Get:
35+ // idx[n] is the index of the left neighbour along the dimension n, and delta[n] is the elementary
36+ // ratio used to weight the two neighbours of that dimension.
37+ // Get (and GetHost) fill this structure when it is given as an argument, and GetNeighbours and
38+ // GetNeighboursIndx then reuse the search it contains instead of performing it a second time
39+ // (which is only done when they are called with the same coordinates: they search the table again
40+ // as usual when a different x is requested).
41+ // This structure is kept by the caller, so that it is thread-private when it is declared inside
42+ // an idefix_for loop. The lookup table itself is shared by all of the threads of a loop, and can
43+ // therefore not be used to store anything of the sort.
44+ template <const int kDim >
45+ struct LookupTableNeighbours {
46+ real x[kDim ]; // coordinates for which the neighbours below were computed
47+ int idx[kDim ]; // index of the left neighbour along each dimension
48+ real delta[kDim ]; // elementary ratio between the two neighbours of each dimension
49+ bool valid{false }; // whether the neighbours above have been successfully computed
50+
51+ // Check whether this structure already holds the neighbours of the coordinates xIn
52+ KOKKOS_INLINE_FUNCTION
53+ bool Matches (const real xIn[kDim ]) const {
54+ if (!valid) return (false );
55+ for (int n = 0 ; n < kDim ; n++) {
56+ if (x[n] != xIn[n]) return (false );
57+ }
58+ return (true );
59+ }
60+ };
61+
3462template <const int kDim , class TFunc = LookupTableLog, class TInvFunc = LookupTableExp>
3563class LookupTable {
3664 public:
@@ -276,6 +304,59 @@ class LookupTable {
276304 return (value);
277305 }
278306
307+ // Fill "neighbours" with the neighbours of x, unless it already holds them (in which case the
308+ // table is not searched again). This is what allows Get, GetNeighbours and GetNeighboursIndx to
309+ // share a single search when they are called successively with the same coordinates.
310+ template <typename Tint, typename Treal>
311+ KOKKOS_INLINE_FUNCTION
312+ void SearchNeighbours (const real x[kDim ], Tint &dimensions, Tint &offset, Treal &xin,
313+ LookupTableNeighbours<kDim > &neighbours) const {
314+ // Nothing to do if the neighbours of these very coordinates are already known
315+ if (neighbours.Matches (x)) return ;
316+
317+ neighbours.valid = GetIndices (x, dimensions, offset, xin, neighbours.idx , neighbours.delta );
318+ for (int n = 0 ; n < kDim ; n++) {
319+ neighbours.x [n] = x[n];
320+ }
321+ }
322+
323+ // Generic getter which stores in "neighbours" the elements of the table it used, so that a
324+ // subsequent call to GetNeighbours or GetNeighboursIndx with the same coordinates does not
325+ // search the table again
326+ template <typename Tint, typename Treal>
327+ KOKKOS_INLINE_FUNCTION
328+ real Get (const real x[kDim ], Tint &dimensions, Tint &offset, Treal &xin, Treal &data,
329+ LookupTableNeighbours<kDim > &neighbours) const {
330+ SearchNeighbours (x, dimensions, offset, xin, neighbours);
331+
332+ if (!neighbours.valid ) return (NAN );
333+
334+ // Do a linear interpolation from the neightbouring points to get our value.
335+ real value = 0 ;
336+
337+ // loop on all of the vertices of the neighbours
338+ for (unsigned int n = 0 ; n < (1 << kDim ) ; n++) {
339+ real weight = 1.0 ;
340+ for (unsigned int m = 0 ; m < kDim ; m++) {
341+ unsigned int myBit = 1 << m;
342+ // If bit is set, we're doing the right vertex, otherwise we're doing the left vertex
343+ if ((n & myBit) > 0 ) {
344+ // We're on the right
345+ weight = weight*neighbours.delta [m];
346+ } else {
347+ // We're on the left
348+ weight = weight*(1 -neighbours.delta [m]);
349+ }
350+ }
351+ value = value + weight*data (GetDataIndex (dimensions, neighbours.idx , n));
352+ }
353+
354+ // The interpolation was performed on func(data), so we transform the result back
355+ if (interpolateInFuncSpace) value = invFunc (value);
356+
357+ return (value);
358+ }
359+
279360 // Generic getter for the neighbours used by the interpolation, for all kinds of input arrays.
280361 // On output, xN[2*n] and xN[2*n+1] are the coordinates bracketing x[n] along the dimension n,
281362 // and dataN[v] is the data at the vertex v of these neighbours (see GetDataIndex for the
@@ -286,19 +367,30 @@ class LookupTable {
286367 KOKKOS_INLINE_FUNCTION
287368 void GetNeighbours (const real x[kDim ], Tint &dimensions, Tint &offset, Treal &xin, Treal &data,
288369 real xN[2 *kDim ], real dataN[1 << kDim ]) const {
289- int idx[kDim ];
290- real delta[kDim ];
370+ LookupTableNeighbours<kDim > neighbours;
371+ GetNeighbours (x, dimensions, offset, xin, data, neighbours, xN, dataN);
372+ }
373+
374+ // Same as above, but the search is stored in (and reused from) "neighbours": the table is only
375+ // searched again when "neighbours" does not already hold the neighbours of x, e.g. because it
376+ // was filled by a previous call to Get with these very same coordinates.
377+ template <typename Tint, typename Treal>
378+ KOKKOS_INLINE_FUNCTION
379+ void GetNeighbours (const real x[kDim ], Tint &dimensions, Tint &offset, Treal &xin, Treal &data,
380+ LookupTableNeighbours<kDim > &neighbours,
381+ real xN[2 *kDim ], real dataN[1 << kDim ]) const {
382+ SearchNeighbours (x, dimensions, offset, xin, neighbours);
291383
292- if (!GetIndices (x, dimensions, offset, xin, idx, delta) ) {
384+ if (!neighbours. valid ) {
293385 for (int n = 0 ; n < 2 *kDim ; n++) xN[n] = NAN ;
294386 for (unsigned int n = 0 ; n < (1 << kDim ) ; n++) dataN[n] = NAN ;
295387 return ;
296388 }
297389
298390 // Coordinates of the neighbours along each dimension
299391 for (int n = 0 ; n < kDim ; n++) {
300- xN[2 *n] = xin (offset (n) + idx[n]);
301- xN[2 *n+1 ] = xin (offset (n) + idx[n]+1 );
392+ xN[2 *n] = xin (offset (n) + neighbours. idx [n]);
393+ xN[2 *n+1 ] = xin (offset (n) + neighbours. idx [n]+1 );
302394 if (interpolateInFuncSpace) {
303395 xN[2 *n] = invFunc (xN[2 *n]);
304396 xN[2 *n+1 ] = invFunc (xN[2 *n+1 ]);
@@ -307,7 +399,7 @@ class LookupTable {
307399
308400 // Data on each vertex of the neighbours
309401 for (unsigned int n = 0 ; n < (1 << kDim ) ; n++) {
310- dataN[n] = data (GetDataIndex (dimensions, idx, n));
402+ dataN[n] = data (GetDataIndex (dimensions, neighbours. idx , n));
311403 if (interpolateInFuncSpace) dataN[n] = invFunc (dataN[n]);
312404 }
313405 }
@@ -321,16 +413,30 @@ class LookupTable {
321413 KOKKOS_INLINE_FUNCTION
322414 void GetNeighboursIndx (const real x[kDim ], Tint &dimensions, Tint &offset, Treal &xin,
323415 int idx[kDim ], int dataIdx[1 << kDim ]) const {
324- real delta[kDim ];
416+ LookupTableNeighbours<kDim > neighbours;
417+ GetNeighboursIndx (x, dimensions, offset, xin, neighbours, idx, dataIdx);
418+ }
419+
420+ // Same as above, but the search is stored in (and reused from) "neighbours", exactly like the
421+ // GetNeighbours variant above
422+ template <typename Tint, typename Treal>
423+ KOKKOS_INLINE_FUNCTION
424+ void GetNeighboursIndx (const real x[kDim ], Tint &dimensions, Tint &offset, Treal &xin,
425+ LookupTableNeighbours<kDim > &neighbours,
426+ int idx[kDim ], int dataIdx[1 << kDim ]) const {
427+ SearchNeighbours (x, dimensions, offset, xin, neighbours);
325428
326- if (!GetIndices (x, dimensions, offset, xin, idx, delta) ) {
429+ if (!neighbours. valid ) {
327430 for (int n = 0 ; n < kDim ; n++) idx[n] = -1 ;
328431 for (unsigned int n = 0 ; n < (1 << kDim ) ; n++) dataIdx[n] = -1 ;
329432 return ;
330433 }
331434
435+ for (int n = 0 ; n < kDim ; n++) {
436+ idx[n] = neighbours.idx [n];
437+ }
332438 for (unsigned int n = 0 ; n < (1 << kDim ) ; n++) {
333- dataIdx[n] = GetDataIndex (dimensions, idx, n);
439+ dataIdx[n] = GetDataIndex (dimensions, neighbours. idx , n);
334440 }
335441 }
336442
@@ -369,6 +475,51 @@ class LookupTable {
369475 void GetNeighboursIndxHost (const real x[kDim ], int idx[kDim ], int dataIdx[1 << kDim ]) const {
370476 GetNeighboursIndx (x, dimensionsHost, offsetHost, xinHost, idx, dataIdx);
371477 }
478+
479+ // Getter on device, which stores the neighbours it used in "neighbours". Giving that same
480+ // structure to GetNeighbours or GetNeighboursIndx below then avoids searching the table twice.
481+ KOKKOS_INLINE_FUNCTION
482+ real Get (const real x[kDim ], LookupTableNeighbours<kDim > &neighbours) const {
483+ return (Get (x, dimensionsDev, offsetDev, xinDev, dataDev, neighbours));
484+ }
485+
486+ // Getter on Host, which stores the neighbours it used in "neighbours"
487+ KOKKOS_INLINE_FUNCTION
488+ real GetHost (const real x[kDim ], LookupTableNeighbours<kDim > &neighbours) const {
489+ return (Get (x, dimensionsHost, offsetHost, xinHost, dataHost, neighbours));
490+ }
491+
492+ // Getter for the neighbours used by the interpolation, on device, reusing the search stored in
493+ // "neighbours" when it was performed for these very same coordinates
494+ KOKKOS_INLINE_FUNCTION
495+ void GetNeighbours (const real x[kDim ], LookupTableNeighbours<kDim > &neighbours,
496+ real xN[2 *kDim ], real dataN[1 << kDim ]) const {
497+ GetNeighbours (x, dimensionsDev, offsetDev, xinDev, dataDev, neighbours, xN, dataN);
498+ }
499+
500+ // Getter for the neighbours used by the interpolation, on Host, reusing the search stored in
501+ // "neighbours" when it was performed for these very same coordinates
502+ KOKKOS_INLINE_FUNCTION
503+ void GetNeighboursHost (const real x[kDim ], LookupTableNeighbours<kDim > &neighbours,
504+ real xN[2 *kDim ], real dataN[1 << kDim ]) const {
505+ GetNeighbours (x, dimensionsHost, offsetHost, xinHost, dataHost, neighbours, xN, dataN);
506+ }
507+
508+ // Getter for the indices of the neighbours, on device, reusing the search stored in
509+ // "neighbours" when it was performed for these very same coordinates
510+ KOKKOS_INLINE_FUNCTION
511+ void GetNeighboursIndx (const real x[kDim ], LookupTableNeighbours<kDim > &neighbours,
512+ int idx[kDim ], int dataIdx[1 << kDim ]) const {
513+ GetNeighboursIndx (x, dimensionsDev, offsetDev, xinDev, neighbours, idx, dataIdx);
514+ }
515+
516+ // Getter for the indices of the neighbours, on Host, reusing the search stored in
517+ // "neighbours" when it was performed for these very same coordinates
518+ KOKKOS_INLINE_FUNCTION
519+ void GetNeighboursIndxHost (const real x[kDim ], LookupTableNeighbours<kDim > &neighbours,
520+ int idx[kDim ], int dataIdx[1 << kDim ]) const {
521+ GetNeighboursIndx (x, dimensionsHost, offsetHost, xinHost, neighbours, idx, dataIdx);
522+ }
372523};
373524
374525template <int kDim , class TFunc , class TInvFunc >
0 commit comments