Files @ b7792cb187f7
Branch filter:

Location: MD/arcos/src/interpol2.c

greta
Edited file README via RhodeCode
  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
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
/** @file interpol2.c
 *  @brief New interpolation routines
 *
 * We want to obtain a function \f$f(r, z)\f$ from another one \f$phi(r, z)\f$
 * which is known only in some points of a stencil, 
 *     \f$\phi(r0 + i dr, z0 + j dz), 0 <= {i, j} < q\f$.
 *
 * We approximate \f$f\f$ as
 *
 *    \f$f(r, z) = \sum_{nm}  (r - r0)^n (z - z0)^m c_{nm}\f$
 *
 * where \f$m + n < p\f$, \f$p\f$ being the order of the interpolating function
 * plus one.
 *
 * The coefficients \f$c_{nm}\f$ are calculated from \f$\phi\f$ as
 *
 *   \f$c_{nm} = \sum_{ij} b_{nm ij} phi(r0 + i L, z0 + j L)\f$
 *
 * Thus, to define an interpolation method, we only need the matrix
 * \f$b_{nm ij}\f$, which has \f$1/2 q^2 p(p+1)\f$ elements.
 *
 * HOWEVER, using this general interpolation matrix turns out to be too
 * slow.  Hence we define also specific routines that have the same
 * interface and that are called transparently.  Thus the calling program
 * does not have to worry about the internals of how the interpolation
 * is computed.
 */
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <math.h>

#include "grid.h"
#include "interpol2.h"
#include "parameters.h"
#include "proto.h"
#include "rz_array.h"
#include "species.h"

#define anm(N_, M_) this->stencil[(N_) * this->method->q + (M_)]

static double gen_apply (interpol_t *this, double r, double z);
void bilin_set_coeffs (interpol_t *this);
void quadlog_set_coeffs (interpol_t *this);
double quadlog_apply (interpol_t *this, double r, double z);

/** @brief Creates a new interpolator that uses a given method. */
interpol_t*
interpol_new_a (double Lr, double Lz, interpol_method_t *method)
{
  interpol_t *interpol;

  interpol = xmalloc (sizeof (interpol_t));
  
  interpol->method = method;
  interpol->Lr = Lr;
  interpol->Lz = Lz;

  /* The number of pairs {i, j} (i, j >= 0) such that i + j < p 
     is p (p + 1) / 2 */
  interpol->coeffs = xmalloc (sizeof(double) * 
			      method->p * (method->p + 1) / 2);
  interpol->stencil = xmalloc (sizeof(double) * method->q * method->q);

  return interpol;
}

/** @brief Frees the interpolator */
void
interpol_free (interpol_t *this)
{
  free (this->coeffs);
  free (this->stencil);
  free (this);
}

/** @brief Sets the stencil of the interpolation object.
 *
 * @a r0 and @a z0 are the coordinates of the bottom-left corner of the stencil
 * (the [0,0] point).
*/
void
interpol_set_stencil (interpol_t *this, double r0, double z0, ...)
{
  int i, j, indx;
  va_list ap;

  va_start (ap, z0);

  for (indx = 0, i = 0; i < this->method->q; i++) {
    for (j = 0; j < this->method->q; j++) {
      this->stencil[indx++] = va_arg (ap, double);
    }
  }

  this->r0 = r0;
  this->z0 = z0;

  va_end (ap);

  /* If the method has an optimized routine, use it. */
  if (this->method->set_coeffs != NULL) {
    this->method->set_coeffs (this);
  }
  else {
    interpol_set_coeffs (this);
  }
}

/** @brief Sets the stencil reading from an array.
 *
 * @a ir, @a iz give the index of the cell in the array corresponding to
 * the stencil origin.
 */
void
interpol_set_stencil_at (grid_t *grid, interpol_t *this, double r0, double z0, 
			 rz_array_t *ar,
			 int ir, int iz, int itheta)
{
  int indx, i, j;
  REAL *sp, *ap;

  assert (ar->ntheta > itheta);

  sp = this->stencil;
  for (indx = 0, i = 0; i < this->method->q; i++) {
     double r;
     if (this->method->masses) {
       r = cyl_r_at (ir + i - this->method->ir0, grid->level);
     } else {
       r = 1;
     }

    ap = RZTP (ar, ir + i - this->method->ir0, 
               iz - this->method->iz0, itheta);

    for (j = 0; j < this->method->q; j++, ap += ar->strides[Z_INDX]) {
    /* The case ir = -1 does not give any problem, since we set
       up the correct boundaries. */
       *sp = r * (*ap);
       sp++;
    }
  }

  this->r0 = r0;
  this->z0 = z0;

  /* If the method has an optimized routine, use it. */
  if (this->method->set_coeffs != NULL) {
      this->method->set_coeffs (this);
  }
  else {
    interpol_set_coeffs (this);
  }
}

/** @brief Calculates the coefficients of the interpolating polynomial.
 *
 * Has to be called after interpol_set_stencil. */
void 
interpol_set_coeffs (interpol_t *this)
{
  double Lrn[MAX_ORDER], Lzn[MAX_ORDER];
  double *matrix, sum;
  int n, ip, j;
  int pindx, mindx;

  matrix = this->method->matrix;

  Lrn[0] = 1.0;
  Lzn[0] = 1.0;

  for (ip = 0, pindx = 0, mindx = 0; ip < this->method->p; ip++) {
    Lrn[ip + 1] = Lrn[ip] * this->Lr;
    Lzn[ip + 1] = Lzn[ip] * this->Lz;
    for (n = 0; n <= ip; n++) {
      sum = 0;

      /* This is the coefficient of r^n z^m, with m + n = ip */
      for (j = 0; j < this->method->q * this->method->q; j++) 
	sum += 	matrix[mindx++] * this->stencil[j];

      this->coeffs[pindx++] = sum / Lrn[n] / Lzn[ip - n];
    }
  }
}

/** @brief Evaluates the interpolating polynomial. */
double
interpol_apply (interpol_t *this, double r, double z)
{
  double result;

  /* If the method has an optimized routine, call it and return */
  if (this->method->apply != NULL) {
    result = this->method->apply (this, r, z);
  } else {
  /* Else, apply the general method. */
    result = gen_apply (this, r, z);
  }

  if (this->method->masses) {
    return result / cyl_q(r);
  } else {
    return result;
  }
}

/** @brief gen_apply ?????. */
static double
gen_apply (interpol_t *this, double r, double z)
{
  double deltar, deltaz, sum;

  deltar = r - this->r0;
  deltaz = z - this->z0;

  int ipmax=this->method->p;
  if (ipmax < 3) {
     fprintf(stdout,"ipmax=%d\n",ipmax);
     fatal("interpol2: order interpolation not equal to 3\n");
  } 

  if (deltar==deltaz) {
    sum = this->coeffs[0] +
          deltar * (this->coeffs[1] +
                    this->coeffs[2] +
                    deltar * (this->coeffs[3] +
                              this->coeffs[4] +
                              this->coeffs[5] ));

  } else
  {
    sum = this->coeffs[0] +
          deltaz * (this->coeffs[1] + deltaz * this->coeffs[3]) +
          deltar * (this->coeffs[2] + 
                    deltaz * this->coeffs[4] +
                    deltar * this->coeffs[5] );
  }
  return sum;
}   

/** You are not supposed to understand these numbers.  
 *  Refer to InterpolArrays.nb
 */
double matrix_zero[] = {1.0};

interpol_method_t interpol_zero = {matrix_zero, 1, 1, 0, 0, FALSE,
				   interpol_set_coeffs, 
				   gen_apply};

interpol_method_t interpol_zero_masses = {matrix_zero, 1, 1, 0, 0, TRUE,
					  interpol_set_coeffs, 
					  gen_apply};

/* You are not supposed to understand these numbers.  
   Refer to InterpolArrays.nb */

/** Coefficients for a 9-point stencil quadratic interpolation. */
double matrix_quadratic[] = 
  {-0.1111111111111111,  0.2222222222222222, -0.1111111111111111,
    0.2222222222222222,  0.5555555555555556,  0.2222222222222222,
   -0.1111111111111111,  0.2222222222222222, -0.1111111111111111,
   -0.1666666666666667,  0                 ,  0.1666666666666667,
   -0.1666666666666667,  0                 ,  0.1666666666666667,
   -0.1666666666666667,  0                 ,  0.1666666666666667,
   -0.1666666666666667, -0.1666666666666667, -0.1666666666666667,
    0                 ,  0                 ,  0                 ,
    0.1666666666666667,  0.1666666666666667,  0.1666666666666667,
    0.1666666666666667, -0.3333333333333333,  0.1666666666666667,
    0.1666666666666667, -0.3333333333333333,  0.1666666666666667,
    0.1666666666666667, -0.3333333333333333,  0.1666666666666667,
    0.25              ,  0                 , -0.25              ,
    0.25              ,  0                 , -0.25              ,
    0                 ,  0                 ,  0                 ,
   -0.25              ,  0                 ,  0.25              ,
    0.1666666666666667,  0.1666666666666667,  0.1666666666666667,
   -0.3333333333333333, -0.3333333333333333, -0.3333333333333333,
    0.1666666666666667,  0.1666666666666667,  0.1666666666666667
  };

interpol_method_t interpol_quadratic = {matrix_quadratic, 3, 3, 1, 1, FALSE,
					interpol_set_coeffs, 
					gen_apply};

interpol_method_t interpol_quadratic_masses = {matrix_quadratic, 3, 3, 1, 1, 
					       TRUE,
					       interpol_set_coeffs, 
					       gen_apply};

/** @brief In this approach, we make sure that the interpolation error at
 * the matrix center (anm(2, 2)) is zero (this is used by J. Wackers' code) */
double matrix_wackers[] = 
  { 0                 ,  0                 ,  0                 ,
    0                 ,  1.0               ,  0                 ,
    0                 ,  0                 ,  0                 ,
   -0.1666666666666667,  0                 ,  0.1666666666666667,
   -0.1666666666666667,  0                 ,  0.1666666666666667,
   -0.1666666666666667,  0                 ,  0.1666666666666667,
   -0.1666666666666667, -0.1666666666666667, -0.1666666666666667,
    0                 ,  0                 ,  0                 ,
    0.1666666666666667,  0.1666666666666667,  0.1666666666666667,
    0.1               , -0.2               ,  0.1               ,
    0.3               , -0.6               ,  0.3               ,
    0.1               , -0.2               ,  0.1               ,
    0.25              ,  0                 , -0.25              ,
    0                 ,  0                 ,  0                 ,
   -0.25              ,  0                 ,  0.25              ,
    0.1               ,  0.3               ,  0.1               ,
   -0.2               , -0.6               , -0.2               ,
    0.1               ,  0.3               ,  0.1               };

interpol_method_t interpol_wackers = {matrix_wackers, 3, 3, 1, 1, FALSE,
				      interpol_set_coeffs, 
				      gen_apply};

interpol_method_t interpol_wackers_masses = {matrix_wackers, 3, 3, 1, 1, TRUE,
					     interpol_set_coeffs, 
					     gen_apply};


double matrix_averaged[] = 
{ -0.01880341880341880 , -0.008547008547008547, -0.01880341880341880 ,
  -0.008547008547008547,  1.109401709401709   , -0.008547008547008547,
  -0.01880341880341880 , -0.008547008547008547, -0.01880341880341880 ,
  -0.1666666666666667  ,  0                   ,  0.1666666666666667  ,
  -0.1666666666666667  ,  0                   ,  0.1666666666666667  ,
  -0.1666666666666667  ,  0                   ,  0.1666666666666667  ,
  -0.1666666666666667  , -0.1666666666666667  , -0.1666666666666667  ,
   0                   ,  0                   ,  0                   ,
   0.1666666666666667  ,  0.1666666666666667  ,  0.1666666666666667  ,
   0.1128205128205128  , -0.1987179487179487  ,  0.1128205128205128  ,
   0.3012820512820513  , -0.6564102564102564  ,  0.3012820512820513  ,
   0.1128205128205128  , -0.1987179487179487  ,  0.1128205128205128  ,
   0.25                ,  0                   , -0.25                ,
   0                   ,  0                   ,  0                   ,
  -0.25                ,  0                   ,  0.25                ,
   0.1128205128205128  ,  0.3012820512820513  ,  0.1128205128205128  ,
  -0.1987179487179487  , -0.6564102564102564  , -0.1987179487179487  ,
   0.1128205128205128  ,  0.3012820512820513  ,  0.1128205128205128   };

interpol_method_t interpol_averaged = {matrix_averaged, 3, 3, 1, 1, FALSE,
				      interpol_set_coeffs, 
				      gen_apply};

interpol_method_t interpol_averaged_masses = {matrix_averaged, 3, 3, 1, 1, TRUE,
					     interpol_set_coeffs, 
					     gen_apply};

/* 4-point bilinear interpolation. */
double matrix_bilin[] =
  { 1.0,  0.0,  0.0,  0.0, 
   -1.0,  0.0,  1.0,  0.0,
   -1.0,  1.0,  0.0,  0.0,
    0.0,  0.0,  0.0,  0.0,
    1.0, -1.0, -1.0,  1.0,
    0.0,  0.0,  0.0,  0.0};

/** @brief For bilinear interpolations, the standard fallback routines are too slow,
   so we define optimized routines to increase performance.  The results,
   however, should be the same. */
void 
bilin_set_coeffs (interpol_t *this)
{
  double Lr, Lz;
  double *c;

  c = this->coeffs;

  Lr = this->Lr;
  Lz = this->Lz;

  c[0] = anm(0, 0);
  c[1] = (anm(0, 1) - anm(0, 0)) / Lz;
  c[2] = (anm(1, 0) - anm(0, 0)) / Lr;
  c[3] = 0;  /* Never used anyway. */
  c[4] = (anm(0, 0) - anm(1, 0) - anm(0, 1) + anm(1, 1)) / Lr / Lz;
  c[5] = 0;  /* Never used anyway. */
}  

double
bilin_apply (interpol_t *this, double r, double z)
{
  double d_r, d_z;
  double *c;

  c = this->coeffs;

  d_r = r - this->r0;
  d_z = z - this->z0;

  return c[0] + d_z * (c[1] + c[4] * d_r) + c[2] * d_r; 
}


interpol_method_t interpol_bilin = {matrix_bilin, 
				    3, 2, 
				    0, 0, 
				    FALSE,
				    bilin_set_coeffs, 
				    bilin_apply};

interpol_method_t interpol_bilin_masses = {matrix_bilin, 
					   3, 2, 
					   0, 0, TRUE,
					   bilin_set_coeffs, 
					   bilin_apply};

void
quadlog_set_coeffs (interpol_t *this)
{
  int indx, i, j;

  for (indx = 0, i = 0; i < this->method->q; i++) {
    for (j = 0; j < this->method->q; j++) {
      this->stencil[indx] = log (this->stencil[indx]);
      indx++;
    }
  }
  interpol_set_coeffs (this);
}

double
quadlog_apply (interpol_t *this, double r, double z)
{
  double rlog;

  rlog = gen_apply (this, r, z);
  return exp (rlog);
}

interpol_method_t interpol_quadlog = {matrix_quadratic, 3, 3, 1, 1,
					FALSE,
				      quadlog_set_coeffs, 
				      quadlog_apply};

interpol_method_t *interpol_methods_index[MAX_INTERPOL_METHODS] = {
  &interpol_zero_masses,
  &interpol_quadratic_masses,
  &interpol_wackers_masses,
  &interpol_averaged_masses,
  &interpol_zero,
  &interpol_quadratic,
  &interpol_wackers,
  &interpol_averaged,
  &interpol_quadlog };