aboutsummaryrefslogtreecommitdiff
path: root/src/molecule_posn.c
blob: 553a1765de6a0b1442f2aca60b845d24c25f57d7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
 /*@@
   @file      molecule_posn.c
   @date      23 Oct 2001
   @author    Jonathan Thornburg <jthorn@aei.mpg.de>
   @desc      Worker function to compute molecule positions for interpolator.
   @enddesc
   @version   $Id$
 @@*/

#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <math.h>

#ifndef AEILOCALINTERP_STANDALONE_TEST
  #include "cctk.h"
#endif

#include "InterpLocalUniform.h"

/* the rcs ID and its dummy function to use it */
static const char *rcsid = "$Header$";
#ifndef AEILOCALINTERP_STANDALONE_TEST
  CCTK_FILEVERSION(AEIThorns_AEILocalInterp_src_molecule_posn_c)
#endif

/******************************************************************************/
/******************************************************************************/
/******************************************************************************/

/*@@
 @routine	AEILocalInterp_molecule_posn
 @date		23 Oct 2001
 @author	Jonathan Thornburg <jthorn@aei.mpg.de>
 @desc
	Given a uniformly-spaced grid, this function computes molecule
	positions for an interpolation or similar operation:
	(1) It computes the closest grid point to the input coordinate
	(2) It does the slightly tricky odd/even computation to decide where
	    to center the molecule
	(3) It checks for the molecule falling off the edge of the grid,
	    and off-centers it as appropriate.

	For (1), we assume that grid points have floating-point
	coordinates (we refer to these as "x") which are an arbitrary
	linear function of the integer grid coordinates (we refer to
	these as "i"), x = grid_origin + i*grid_delta.

	For (2), suppose we have a data array indexed by [i], from which we
	wish to select an N-point molecule [i_lo, i_hi] centered as close as
	possible to the point  x .  The problem is just how to choose the
	centering.  The following diagram illustrates the issues:

	N  i_lo  i_hi  [i-3]  [i-2]  [i-1]   [i]   [i+1]  [i+2]  [i+3]  [i+4]
	-  ----  ----  -----  -----  -----  -----  -----  -----  -----  -----
	2  i     i+1                          *xxxxxx*
	3  i-1   i+1                   *   xxx*xxx   *
	4  i-1   i+2                   *      *xxxxxx*      *
	5  i-2   i+2            *      *   xxx*xxx   *      *
	6  i-2   i+3            *      *      *xxxxxx*      *      *
	7  i-3   i+3     *      *      *   xxx*xxx   *      *      *
	8  i-3   i+4     *      *      *      *xxxxxx*      *      *      *

	The diagram shows the range of  x  values relative to the grid,
	for which we'll choose each centering.  Thus if N is even, the
	centering is based on which grid zone contains x (take the floor
	of the floating-point i coordinate), while if N is odd, the centering
	is based on which grid point is closest to x (round the floating-
	-point i coordinate to the nearest integer).

	It's also convenient to introduce the integer molecule coordinate m;
	this is the integer grid coordinate i relative to that of the molecule
	center, i.e. the above diagram has columns labelled with [i+m].

	For (3) above, note that we describe the range of the grid
	by the *closed* interval [grid_i_min, grid_i_max].
 @enddesc

 @hdate    27.Jan.2003
 @hauthor  Jonathan Thornburg <jthorn@aei.mpg.de>
 @hdesc    Complete rewrite: now supports
             @var boundary_off_centering_tolerance_min,
             @var boundary_off_centering_tolerance_max,
             @var boundary_extrapolation_tolerance_min,
             @var boundary_extrapolation_tolerance_max,
           also change to returning status code,
           also drop returning @var min_m and @var max_m
           since they were never used.
 @endhdesc 

 @var	grid_origin
 @vdesc	The floating-point coordinate x of the grid point i=0.
 @vtype	fp grid_origin
 @endvar

 @var	grid_delta
 @vdesc	The grid spacing (in the floating-point coordinate x).
 @vtype	fp grid_delta
 @endvar

 @var	grid_i_min
 @vdesc	The minimum integer coordinate i of the grid.
 @vtype	int grid_i_min
 @endvar

 @var	grid_i_max
 @vdesc	The maximum integer coordinate i of the grid.
 @vtype	int grid_i_max
 @endvar

 @var	molecule_size
 @vdesc	The size (number of points) of the molecule.
 @vtype	int molecule_size
 @endvar

 @var	boundary_off_centering_tolerance_min,
	boundary_off_centering_tolerance_max
 @vdesc	Specifies how many grid spacings the interpolation point
	may be beyond the default-centering region before being
	declared "out of range" on the {minimum,maximum} boundaries
	of the grid respectively.
 @vtype fp boundary_off_centering_tolerance_min,
	   boundary_off_centering_tolerance_max
 @endvar

 @var	boundary_extrapolation_tolerance_min,
	boundary_extrapolation_tolerance_max
 @vdesc	Specifies how many grid spacings the interpolation point
	may be beyond the grid boundary before being declared
	"out of range" on the {minimum,maximum} boundaries
	of the grid respectively.
 @vtype fp boundary_extrapolation_tolerance_min,
	   boundary_extrapolation_tolerance_max
 @endvar

 @var	x
 @vdesc	The floating-point coordinate of the interpolation point.
 @vtype	fp x
 @endvar

 @var	debug
 @vdesc	A debugging flag (0 = no debug output, > 0 = print debug output)
 @vtype	int x
 @endvar

 @var	i_center
 @vdesc	A pointer to an value where this function should
	store the integer coordinate of the molecule center,
	or NULL to skip storing this
 @vtype	int *i_center
 @vio	pointer to out
 @endvar

 @var	x_rel
 @vdesc	A pointer to where this function should store the
	interpolation point's x coordinate relative to the
	molecule center, measured in units of the grid spacing;
	or NULL to skip storing this.
 @vtype	fp *x_rel
 @vio	pointer to out
 @endvar

 @returntype	int
 @returndesc
	This function returns 0 if the interpolation point is "in range",
	or one of the (negative) error codes defined in "InterpLocalUniform.h"
	if the interpolation point is "out of range":
	MOLECULE_POSN_ERROR_X_LT_MIN
		if  x  is out-of-range on the min end of the grid
		(i.e. x < the minimum allowable x)
	MOLECULE_POSN_ERROR_X_GT_MAX
		if  x  is out-of-range on the max end of the grid
		(i.e. x > the maximum allowable x)
	MOLECULE_POSN_ERROR_GRID_TINY
		if the grid is smaller than the molecule
	MOLECULE_POSN_ERROR_NAN
		if we encounter a NaN or other non-finite floating-point value
	MOLECULE_POSN_ERROR_DX_ZERO
		if  grid_delta == 0.0
 @endreturndesc
 @@*/
int AEILocalInterp_molecule_posn(fp grid_origin, fp grid_delta,
				 int grid_i_min, int grid_i_max,
				 int molecule_size,
				 fp boundary_off_centering_tolerance_min,
				 fp boundary_off_centering_tolerance_max,
				 fp boundary_extrapolation_tolerance_min,
				 fp boundary_extrapolation_tolerance_max,
				 fp x,
				 int debug,
				 int* i_center, fp* x_rel)
{
/*
 * ***** IMPORTANT *****
 *
 * This code is ++tricky.  (More accurately, the basic algorithm is
 * fairly simple, but there are lots of corner cases.)  There's a
 * fairly rigorous test driver for it in the file  test_molecule_posn.c
 * in this directory.  This is a standalone test driver, which can be
 * compiled via
 *	gmake -f Makefile.standalone
 * in this directory.  If you make any changes to this code, please
 * verify that all the tests still pass!
 */

if (debug >= 8)
   then {
	printf("AEILocalInterp_molecule_posn():\n");
	printf("   grid_origin=%g grid_delta=%g\n",
	       (double) grid_origin, (double) grid_delta);
	printf("   grid_i_[min,max]=[%d,%d] molecule_size=%d\n",
	       grid_i_min, grid_i_max, molecule_size);
	printf("   boundary_off_centering_tolerance_[min,max]=[%g,%g]\n",
	       (double) boundary_off_centering_tolerance_min,
	       (double) boundary_off_centering_tolerance_max);
	printf("   boundary_extrapolation_tolerance_[min,max]=[%g,%g]\n",
	       (double) boundary_extrapolation_tolerance_min,
	       (double) boundary_extrapolation_tolerance_max);
	printf("   x=%g\n", (double) x);
	}

/*
 * basic sanity checks
 */

/* is the molecule larger than the grid? */
if (molecule_size > HOW_MANY_IN_RANGE(grid_i_min,grid_i_max))
   then return MOLECULE_POSN_ERROR_GRID_TINY;		/*** ERROR RETURN ***/

/* has someone passed us NaNs for coordinates etc? */
if (!finite(grid_origin) || !finite(grid_delta) || !finite(x))
   then return MOLECULE_POSN_ERROR_NAN;
if (    !finite(boundary_off_centering_tolerance_min)
     || !finite(boundary_off_centering_tolerance_max)
     || !finite(boundary_extrapolation_tolerance_min)
     || !finite(boundary_extrapolation_tolerance_max)    )
   then return MOLECULE_POSN_ERROR_NAN;

/* is the grid spacing zero (we'll need to divide by it)? */
if (grid_delta == 0.0)
   then return MOLECULE_POSN_ERROR_DX_ZERO;


/* molecule radia (inherently positive) in +/- directions */
const int mr_plus  = (molecule_size >> 1);
const int mr_minus = molecule_size - mr_plus - 1;

/* range of x_rel for which this molecule is centered, */
/* cf. diagram in header comment */
const fp centered_min_x_rel = IS_EVEN(molecule_size) ? 0.0 : -0.5;
const fp centered_max_x_rel = IS_EVEN(molecule_size) ? 1.0 : +0.5;

/* range of i where a centered molecule would fit within the grid, */
/* as floating-point numbers */
const fp fp_centered_min_possible_i = grid_i_min + mr_minus
						 + centered_min_x_rel;
const fp fp_centered_max_possible_i = grid_i_max - mr_plus
						 + centered_max_x_rel;

/* integer coordinate i of interpolation point, as a floating-point number */
const fp fp_i = (x - grid_origin) / grid_delta;
assert(finite(fp_i));

if (debug > 8)
   then {
	printf("   mr_{plus,minus}={%d,%d}\n", mr_plus, mr_minus);
	printf("   centered_[min,max]_x_rel=[%g,%g]\n",
	       (double) centered_min_x_rel, (double) centered_max_x_rel);
	printf("   fp_centered_[min,max]_possible_i=[%g,%g]\n",
	       (double) fp_centered_min_possible_i,
	       (double) fp_centered_max_possible_i);
	printf("   fp_i=%g\n", (double) fp_i);
	}

/* is interpolation point x beyond the extrapolation tolerance? */
if (fp_i < (fp)grid_i_min - boundary_extrapolation_tolerance_min)
   then return MOLECULE_POSN_ERROR_X_LT_MIN;		/*** ERROR RETURN ***/
if (fp_i > (fp)grid_i_max + boundary_extrapolation_tolerance_max)
   then return MOLECULE_POSN_ERROR_X_GT_MAX;		/*** ERROR RETURN ***/

/* is interpolation point x beyond the off-centering tolerance? */
if (fp_i < fp_centered_min_possible_i - boundary_off_centering_tolerance_min)
   then return MOLECULE_POSN_ERROR_X_LT_MIN;		/*** ERROR RETURN ***/
if (fp_i > fp_centered_max_possible_i + boundary_off_centering_tolerance_max)
   then return MOLECULE_POSN_ERROR_X_GT_MAX;		/*** ERROR RETURN ***/

/* now choose the actual molecule position/centering: */
/* first set up a centered molecule */
  {
/* compute as a floating point number */
/* (because that's what the C math library provides), */
/* but this value is guaranteed to be integral */
fp fp_i_center = IS_EVEN(molecule_size)
		 ? floor(fp_i)
		 : ROUND_TO_INTEGER__RESULT_IS_FP(fp_i);
assert(finite(fp_i_center));
int int_i_center = (int) fp_i_center;	/* convert to integer */
if (debug > 8)
   then printf("   initial: fp_i_center=%g int_i_center=%d\n",
	       (double) fp_i_center, int_i_center);

/* clamp the molecule at the grid boundaries */
if (int_i_center < grid_i_min + mr_minus)
   then {
	int_i_center = grid_i_min + mr_minus;
	fp_i_center = (fp) int_i_center;
	}
if (int_i_center > grid_i_max - mr_plus)
   then {
	int_i_center = grid_i_max - mr_plus;
	fp_i_center = (fp) int_i_center;
	}

if (debug > 8)
   then printf(
	"   final result after clamping: fp_i_center=%g int_i_center=%d\n",
	       (double) fp_i_center, int_i_center);

/* store the results */
if (i_center != NULL)
   then *i_center = int_i_center;
if (x_rel != NULL)
   then *x_rel = fp_i - fp_i_center;

return 0;						/*** NORMAL RETURN ***/
  }
}