grid.c 150 KB
Newer Older
Uwe Schulzweida's avatar
Uwe Schulzweida committed
1
#ifdef HAVE_CONFIG_H
2
#include "config.h"
Uwe Schulzweida's avatar
Uwe Schulzweida committed
3
4
#endif

Thomas Jahns's avatar
Thomas Jahns committed
5
#include <assert.h>
Uwe Schulzweida's avatar
Uwe Schulzweida committed
6
#include <string.h>
7

Uwe Schulzweida's avatar
Uwe Schulzweida committed
8
9
#include "dmemory.h"
#include "cdi.h"
10
#include "cdi_cksum.h"
11
#include "cdi_int.h"
12
#include "cdi_uuid.h"
Uwe Schulzweida's avatar
Uwe Schulzweida committed
13
#include "grid.h"
14
#include "resource_handle.h"
15
#include "resource_unpack.h"
16
#include "namespace.h"
17
#include "serialize.h"
18
19
#include "vlist.h"

20
int (*proj_lonlat_to_lcc_func)() = NULL;
21
int (*proj_lcc_to_lonlat_func)() = NULL;
22
23
int (*proj_lonlat_to_stere_func)() = NULL;
int (*proj_stere_to_lonlat_func)() = NULL;
Uwe Schulzweida's avatar
Uwe Schulzweida committed
24

25
26
27
/* the value in the second pair of brackets must match the length of
 * the longest string (including terminating NUL) */
static const char Grids[][17] = {
28
29
30
  /*  0 */  "undefined",
  /*  1 */  "generic",
  /*  2 */  "gaussian",
31
  /*  3 */  "gaussian_reduced",
32
33
34
35
36
  /*  4 */  "lonlat",
  /*  5 */  "spectral",
  /*  6 */  "fourier",
  /*  7 */  "gme",
  /*  8 */  "trajectory",
37
  /*  9 */  "unstructured",
38
39
  /* 10 */  "curvilinear",
  /* 11 */  "lcc",
40
  /* 12 */  "projection",
Uwe Schulzweida's avatar
Uwe Schulzweida committed
41
  /* 13 */  "characterXY",
Uwe Schulzweida's avatar
Uwe Schulzweida committed
42
43
};

44
45
46
47
48
/* must match table below */
enum xystdname_idx {
  grid_xystdname_grid_latlon,
  grid_xystdname_latlon,
  grid_xystdname_projection,
Uwe Schulzweida's avatar
Uwe Schulzweida committed
49
  grid_xystdname_char,
50
51
52
53
54
55
56
57
};
static const char xystdname_tab[][2][24] = {
  [grid_xystdname_grid_latlon] = { "grid_longitude",
                                   "grid_latitude" },
  [grid_xystdname_latlon] = { "longitude",
                              "latitude" },
  [grid_xystdname_projection] = { "projection_x_coordinate",
                                  "projection_y_coordinate" },
Uwe Schulzweida's avatar
Uwe Schulzweida committed
58
59
  [grid_xystdname_char] = { "region",
                            "region" },
60
61
62
};


Uwe Schulzweida's avatar
Uwe Schulzweida committed
63

64
65
66
static int    gridCompareP    ( void * gridptr1, void * gridptr2 );
static void   gridDestroyP    ( void * gridptr );
static void   gridPrintP      ( void * gridptr, FILE * fp );
67
static int    gridGetPackSize ( void * gridptr, void *context);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
68
static void   gridPack        ( void * gridptr, void * buff, int size, int *position, void *context);
Deike Kleberg's avatar
minimal    
Deike Kleberg committed
69
static int    gridTxCode      ( void );
Uwe Schulzweida's avatar
Uwe Schulzweida committed
70

71
static const resOps gridOps = {
72
73
74
75
76
77
  gridCompareP,
  gridDestroyP,
  gridPrintP,
  gridGetPackSize,
  gridPack,
  gridTxCode
78
};
Uwe Schulzweida's avatar
Uwe Schulzweida committed
79

80
static int  GRID_Debug = 0;   /* If set to 1, debugging */
Uwe Schulzweida's avatar
Uwe Schulzweida committed
81

Uwe Schulzweida's avatar
Uwe Schulzweida committed
82

83
grid_t *grid_to_pointer(int gridID)
84
85
86
{
  return (grid_t *)reshGetVal(gridID, &gridOps);
}
Uwe Schulzweida's avatar
Uwe Schulzweida committed
87

88
#define gridMark4Update(gridID) reshSetStatus(gridID, &gridOps, RESH_DESYNC_IN_USE)
Uwe Schulzweida's avatar
Uwe Schulzweida committed
89

90
91
92
93
94
95
96
static
bool cdiInqAttConvertedToFloat(int gridID, int atttype, const char *attname, int attlen, double *attflt)
{
  bool status = true;

  if ( atttype == CDI_DATATYPE_INT32 )
    {
97
98
      int attint;
      int *pattint = attlen > 1 ? (int*) malloc(attlen*sizeof(int)) : &attint;
99
100
      cdiInqAttInt(gridID, CDI_GLOBAL, attname, attlen, pattint);
      for ( int i = 0; i < attlen; ++i ) attflt[i] = (double)pattint[i];
101
      if (attlen > 1) free(pattint);
102
103
104
105
106
107
108
109
110
111
112
113
114
    }
  else if ( atttype == CDI_DATATYPE_FLT32 || atttype == CDI_DATATYPE_FLT64 )
    {
      cdiInqAttFlt(gridID, CDI_GLOBAL, attname, attlen, attflt);
    }
  else
    {
      status = false;
    }

  return status;
}

115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
static
void grid_axis_init(struct gridaxis_t *axisptr)
{
  axisptr->size        = 0;
  axisptr->vals        = NULL;
  axisptr->bounds      = NULL;
  axisptr->flag        = 0;
  axisptr->first       = 0.0;
  axisptr->last        = 0.0;
  axisptr->inc         = 0.0;
#ifndef USE_MPI
  axisptr->clength     = 0;
  axisptr->cvals       = NULL;
#endif
  axisptr->dimname[0]  = 0;
  axisptr->name[0]     = 0;
  axisptr->longname[0] = 0;
  axisptr->units[0]    = 0;
  axisptr->stdname     = NULL;
}
135

Deike Kleberg's avatar
Deike Kleberg committed
136
void grid_init(grid_t *gridptr)
Uwe Schulzweida's avatar
Uwe Schulzweida committed
137
{
138
139
140
  gridptr->self          = CDI_UNDEFID;
  gridptr->type          = CDI_UNDEFID;
  gridptr->proj          = CDI_UNDEFID;
141
  gridptr->projtype      = CDI_UNDEFID;
142
143
  gridptr->mask          = NULL;
  gridptr->mask_gme      = NULL;
144
145
146
147
148
  gridptr->size          = 0;

  grid_axis_init(&gridptr->x);
  grid_axis_init(&gridptr->y);

149
  gridptr->area          = NULL;
150
  gridptr->rowlon        = NULL;
151
  gridptr->reducedPointsSize       = 0;
152

153
154
155
156
  gridptr->gme.nd        = 0;
  gridptr->gme.ni        = 0;
  gridptr->gme.ni2       = 0;
  gridptr->gme.ni3       = 0;
157

158
159
160
161
162
  gridptr->trunc         = 0;
  gridptr->nvertex       = 0;
  gridptr->number        = 0;
  gridptr->position      = 0;
  gridptr->reference     = NULL;
163
  gridptr->datatype      = CDI_DATATYPE_FLT64;
164
165
  gridptr->np            = 0;
  gridptr->isCyclic      = CDI_UNDEFID;
Uwe Schulzweida's avatar
Uwe Schulzweida committed
166

167
  gridptr->lcomplex      = false;
168
  gridptr->hasdims       = true;
Uwe Schulzweida's avatar
Uwe Schulzweida committed
169
170
171
  gridptr->vdimname[0]   = 0;
  gridptr->mapname[0]    = 0;
  gridptr->mapping[0]    = 0;
Thomas Jahns's avatar
Thomas Jahns committed
172
  memset(gridptr->uuid, 0, CDI_UUID_SIZE);
173
174
  gridptr->name          = NULL;
  gridptr->vtable        = &cdiGridVtable;
175
  gridptr->atts.nalloc   = MAX_ATTRIBUTES;
176
  gridptr->atts.nelems   = 0;
177
178
179
180
181
182
183
184
185
186
  gridptr->iScansNegatively      = 0;
  gridptr->jScansPositively      = 1;
  gridptr->jPointsAreConsecutive = 0;
  gridptr->scanningMode          = 128*gridptr->iScansNegatively + 64*gridptr->jScansPositively + 32*gridptr->jPointsAreConsecutive;
  /* scanningMode  = 128 * iScansNegatively + 64 * jScansPositively + 32 * jPointsAreConsecutive;
               64  = 128 * 0                + 64 *        1         + 32 * 0
               00  = 128 * 0                + 64 *        0         + 32 * 0
               96  = 128 * 0                + 64 *        1         + 32 * 1
     Default / implicit scanning mode is 64:
                        i and j scan positively, i points are consecutive (row-major)        */
Uwe Schulzweida's avatar
Uwe Schulzweida committed
187
188
}

189

190
191
static
void grid_free_components(grid_t *gridptr)
Deike Kleberg's avatar
Deike Kleberg committed
192
{
193
  void *p2free[] = { gridptr->mask, gridptr->mask_gme,
194
                     gridptr->x.vals, gridptr->y.vals,
195
#ifndef USE_MPI
Uwe Schulzweida's avatar
Uwe Schulzweida committed
196
                     gridptr->x.cvals, gridptr->y.cvals,
197
#endif
198
199
200
201
                     gridptr->x.bounds, gridptr->y.bounds,
                     gridptr->rowlon, gridptr->area,
                     gridptr->reference, gridptr->name};

202
  for ( size_t i = 0; i < sizeof(p2free)/sizeof(p2free[0]); ++i )
203
    if ( p2free[i] ) Free(p2free[i]);
204
}
Deike Kleberg's avatar
Deike Kleberg committed
205

206
207
208
void grid_free(grid_t *gridptr)
{
  grid_free_components(gridptr);
Deike Kleberg's avatar
Deike Kleberg committed
209
210
211
  grid_init(gridptr);
}

212
213
static
grid_t *gridNewEntry(cdiResH resH)
214
{
215
  grid_t *gridptr = (grid_t*) Malloc(sizeof(grid_t));
216
  grid_init(gridptr);
217

218
  if ( resH == CDI_UNDEFID )
219
220
221
222
223
224
    gridptr->self = reshPut(gridptr, &gridOps);
  else
    {
      gridptr->self = resH;
      reshReplace(resH, gridptr, &gridOps);
    }
225

226
  return gridptr;
227
228
}

229
static
230
void gridInit(void)
Uwe Schulzweida's avatar
Uwe Schulzweida committed
231
{
232
  static bool gridInitialized = false;
233
  if ( gridInitialized ) return;
234
  gridInitialized = true;
Uwe Schulzweida's avatar
Uwe Schulzweida committed
235

Uwe Schulzweida's avatar
Uwe Schulzweida committed
236
  const char *env = getenv("GRID_DEBUG");
237
  if ( env ) GRID_Debug = atoi(env);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
238
239
}

240
241
static
void grid_copy_base_scalar_fields(grid_t *gridptrOrig, grid_t *gridptrDup)
Uwe Schulzweida's avatar
Uwe Schulzweida committed
242
{
Thomas Jahns's avatar
Thomas Jahns committed
243
244
  memcpy(gridptrDup, gridptrOrig, sizeof(grid_t));
  gridptrDup->self = CDI_UNDEFID;
245
  if ( gridptrOrig->reference )
Thomas Jahns's avatar
Thomas Jahns committed
246
247
248
    gridptrDup->reference = strdupx(gridptrOrig->reference);
}

Uwe Schulzweida's avatar
Uwe Schulzweida committed
249

250
251
static
grid_t *grid_copy_base(grid_t *gridptrOrig)
Thomas Jahns's avatar
Thomas Jahns committed
252
253
254
255
256
{
  grid_t *gridptrDup = (grid_t *)Malloc(sizeof (*gridptrDup));
  gridptrOrig->vtable->copyScalarFields(gridptrOrig, gridptrDup);
  gridptrOrig->vtable->copyArrayFields(gridptrOrig, gridptrDup);
  return gridptrDup;
Uwe Schulzweida's avatar
Uwe Schulzweida committed
257
258
}

Uwe Schulzweida's avatar
Uwe Schulzweida committed
259

260
unsigned cdiGridCount(void)
Thomas Jahns's avatar
Thomas Jahns committed
261
{
262
  return reshCountType(&gridOps);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
263
264
}

265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
static inline
void gridSetString(char *gridstrname, const char *name, size_t len)
{
  if ( len > CDI_MAX_NAME ) len = CDI_MAX_NAME;
  strncpy(gridstrname, name, len);
  gridstrname[len - 1] = 0;
}

static inline
void gridGetString(char *name, const char *gridstrname, size_t len)
{
  if ( len > CDI_MAX_NAME ) len = CDI_MAX_NAME;
  strncpy(name, gridstrname, len);
  name[len - 1] = 0;
}

281
282
static inline
void gridSetName(char *gridstrname, const char *name)
283
{
284
285
  strncpy(gridstrname, name, CDI_MAX_NAME);
  gridstrname[CDI_MAX_NAME - 1] = 0;
286
287
}

288

289
void cdiGridTypeInit(grid_t *gridptr, int gridtype, size_t size)
290
291
292
293
{
  gridptr->type = gridtype;
  gridptr->size = size;

294
295
  if      ( gridtype == GRID_LONLAT   ) gridptr->nvertex = 2;
  else if ( gridtype == GRID_GAUSSIAN ) gridptr->nvertex = 2;
296
  else if ( gridtype == GRID_GAUSSIAN_REDUCED ) gridptr->nvertex = 2;
297
  else if ( gridtype == GRID_CURVILINEAR  ) gridptr->nvertex = 4;
298
299
  else if ( gridtype == GRID_UNSTRUCTURED ) gridptr->x.size = size;

300
301
302
303
304
305
  switch (gridtype)
    {
    case GRID_LONLAT:
    case GRID_GAUSSIAN:
    case GRID_GAUSSIAN_REDUCED:
    case GRID_TRAJECTORY:
306
307
308
    case GRID_CURVILINEAR:
    case GRID_UNSTRUCTURED:
    case GRID_GME:
309
310
311
      {
        if ( gridtype == GRID_TRAJECTORY )
          {
312
313
            if ( !gridptr->x.name[0] ) gridSetName(gridptr->x.name, "tlon");
            if ( !gridptr->y.name[0] ) gridSetName(gridptr->y.name, "tlat");
314
315
316
          }
        else
          {
317
318
            if ( !gridptr->x.name[0] ) gridSetName(gridptr->x.name, "lon");
            if ( !gridptr->y.name[0] ) gridSetName(gridptr->y.name, "lat");
319
          }
320

321
322
323
324
325
        if ( !gridptr->x.longname[0] ) gridSetName(gridptr->x.longname, "longitude");
        if ( !gridptr->y.longname[0] ) gridSetName(gridptr->y.longname, "latitude");

        if ( !gridptr->x.units[0] ) gridSetName(gridptr->x.units, "degrees_east");
        if ( !gridptr->y.units[0] ) gridSetName(gridptr->y.units, "degrees_north");
326

327
328
        gridptr->x.stdname = xystdname_tab[grid_xystdname_latlon][0];
        gridptr->y.stdname = xystdname_tab[grid_xystdname_latlon][1];
329

330
331
        break;
      }
332
#ifndef USE_MPI
Uwe Schulzweida's avatar
Uwe Schulzweida committed
333
334
    case GRID_CHARXY:
      {
335
336
337
338
        if ( gridptr->x.cvals ) gridptr->x.stdname = xystdname_tab[grid_xystdname_char][0];
        if ( gridptr->y.cvals ) gridptr->y.stdname = xystdname_tab[grid_xystdname_char][0];

        break;
339
340
      }
#endif
341
    case GRID_GENERIC:
342
    case GRID_PROJECTION:
343
      {
344
345
        if ( gridptr->x.name[0] == 0 ) gridSetName(gridptr->x.name, "x");
        if ( gridptr->y.name[0] == 0 ) gridSetName(gridptr->y.name, "y");
346
347
348
        if ( gridtype == GRID_PROJECTION )
          {
            gridSetName(gridptr->mapname, "Projection");
349

350
351
            gridptr->x.stdname = xystdname_tab[grid_xystdname_projection][0];
            gridptr->y.stdname = xystdname_tab[grid_xystdname_projection][1];
352
353
354

            if ( !gridptr->x.units[0] ) gridSetName(gridptr->x.units, "m");
            if ( !gridptr->y.units[0] ) gridSetName(gridptr->y.units, "m");
355
          }
356
357
358
359
360
361
        break;
      }
    }
}


362
// used also in CDO
363
void gridGenXvals(int xsize, double xfirst, double xlast, double xinc, double *restrict xvals)
Uwe Schulzweida's avatar
Uwe Schulzweida committed
364
{
Uwe Schulzweida's avatar
Uwe Schulzweida committed
365
  if (fabs(xinc) <= 0 && xsize > 1)
Uwe Schulzweida's avatar
Uwe Schulzweida committed
366
367
    {
      if ( xfirst >= xlast )
Thomas Jahns's avatar
Thomas Jahns committed
368
369
370
371
        {
          while ( xfirst >= xlast ) xlast += 360;
          xinc = (xlast-xfirst)/(xsize);
        }
Uwe Schulzweida's avatar
Uwe Schulzweida committed
372
      else
Thomas Jahns's avatar
Thomas Jahns committed
373
374
375
        {
          xinc = (xlast-xfirst)/(xsize-1);
        }
Uwe Schulzweida's avatar
Uwe Schulzweida committed
376
377
    }

378
  for ( int i = 0; i < xsize; ++i )
Uwe Schulzweida's avatar
Uwe Schulzweida committed
379
380
381
    xvals[i] = xfirst + i*xinc;
}

382
static
383
void calc_gaussgrid(double *restrict yvals, size_t ysize, double yfirst, double ylast)
384
{
385
  double *restrict yw = (double *) Malloc(ysize * sizeof(double));
386
  gaussianLatitudes(yvals, yw, ysize);
387
  Free(yw);
388
  for (size_t i = 0; i < ysize; i++ )
389
390
391
392
    yvals[i] = asin(yvals[i])/M_PI*180.0;

  if ( yfirst < ylast && yfirst > -90.0 && ylast < 90.0 )
    {
393
      const size_t yhsize = ysize/2;
394
      for (size_t i = 0; i < yhsize; i++ )
Thomas Jahns's avatar
Thomas Jahns committed
395
        {
396
          const double ytmp = yvals[i];
Thomas Jahns's avatar
Thomas Jahns committed
397
398
399
          yvals[i] = yvals[ysize-i-1];
          yvals[ysize-i-1] = ytmp;
        }
400
401
402
    }
}

403
404
static
void gridGenYvalsGaussian(int ysize, double yfirst, double ylast, double *restrict yvals)
Uwe Schulzweida's avatar
Uwe Schulzweida committed
405
{
406
  const double deleps = 0.002;
Uwe Schulzweida's avatar
Uwe Schulzweida committed
407

408
409
410
411
412
413
414
415
416
417
  calc_gaussgrid(yvals, ysize, yfirst, ylast);

  if ( ! (IS_EQUAL(yfirst, 0) && IS_EQUAL(ylast, 0)) )
    if ( fabs(yvals[0] - yfirst) > deleps || fabs(yvals[ysize-1] - ylast) > deleps )
      {
        bool lfound = false;
        int ny = (int) (180./(fabs(ylast-yfirst)/(ysize-1)) + 0.5);
        ny -= ny%2;
        if ( ny > ysize && ny < 4096 )
          {
418
            double *ytmp = (double *) Malloc((size_t)ny * sizeof (double));
419
            calc_gaussgrid(ytmp, ny, yfirst, ylast);
420
421
422
423
424

            int i;
            for ( i = 0; i < (ny-ysize); i++ )
              if ( fabs(ytmp[i] - yfirst) < deleps ) break;
            int nstart = i;
425
426
427
428

            lfound = (nstart+ysize-1) < ny && fabs(ytmp[nstart+ysize-1] - ylast) < deleps;
            if ( lfound )
              {
429
                for (i = 0; i < ysize; i++) yvals[i] = ytmp[i+nstart];
430
              }
431
432

            if ( ytmp ) Free(ytmp);
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
          }

        if ( !lfound )
          {
            Warning("Cannot calculate gaussian latitudes for lat1 = %g latn = %g!", yfirst, ylast);
            for (int i = 0; i < ysize; i++ ) yvals[i] = 0;
            yvals[0] = yfirst;
            yvals[ysize-1] = ylast;
          }
      }
}

static
void gridGenYvalsRegular(int ysize, double yfirst, double ylast, double yinc, double *restrict yvals)
{
  if (fabs(yinc) <= 0 && ysize > 1)
    {
      if ( IS_EQUAL(yfirst, ylast) && IS_NOT_EQUAL(yfirst, 0) ) ylast *= -1;

      if ( yfirst > ylast )
        yinc = (yfirst-ylast)/(ysize-1);
      else if ( yfirst < ylast )
        yinc = (ylast-yfirst)/(ysize-1);
      else
        {
          if ( ysize%2 != 0 )
            {
              yinc = 180.0/(ysize-1);
              yfirst = -90;
            }
          else
            {
              yinc = 180.0/ysize;
              yfirst = -90 + yinc/2;
            }
        }
    }

  if ( yfirst > ylast && yinc > 0 ) yinc = -yinc;

  for (int i = 0; i < ysize; i++ )
    yvals[i] = yfirst + i*yinc;
}

// used also in CDO
void gridGenYvals(int gridtype, int ysize, double yfirst, double ylast, double yinc, double *restrict yvals)
{
Uwe Schulzweida's avatar
Uwe Schulzweida committed
480
481
482
  if ( gridtype == GRID_GAUSSIAN || gridtype == GRID_GAUSSIAN_REDUCED )
    {
      if ( ysize > 2 )
Deike Kleberg's avatar
Deike Kleberg committed
483
	{
484
          gridGenYvalsGaussian(ysize, yfirst, ylast, yvals);
Deike Kleberg's avatar
Deike Kleberg committed
485
	}
Uwe Schulzweida's avatar
Uwe Schulzweida committed
486
      else
Thomas Jahns's avatar
Thomas Jahns committed
487
488
489
490
        {
          yvals[0] = yfirst;
          yvals[ysize-1] = ylast;
        }
Uwe Schulzweida's avatar
Uwe Schulzweida committed
491
492
493
494
    }
  /*     else if ( gridtype == GRID_LONLAT || gridtype == GRID_GENERIC ) */
  else
    {
495
      gridGenYvalsRegular(ysize, yfirst, ylast, yinc, yvals);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
496
    }
497
   /*
Uwe Schulzweida's avatar
Uwe Schulzweida committed
498
    else
499
    Error("unable to calculate values for %s grid!", gridNamePtr(gridtype));
Uwe Schulzweida's avatar
Uwe Schulzweida committed
500
501
502
503
  */
}

/*
504
@Function  gridCreate
505
@Title     Create a horizontal Grid
Uwe Schulzweida's avatar
Uwe Schulzweida committed
506

507
@Prototype int gridCreate(int gridtype, size_t size)
Uwe Schulzweida's avatar
Uwe Schulzweida committed
508
509
@Parameter
    @Item  gridtype  The type of the grid, one of the set of predefined CDI grid types.
510
                     The valid CDI grid types are @func{GRID_GENERIC}, @func{GRID_GAUSSIAN},
511
                     @func{GRID_LONLAT}, @func{GRID_PROJECTION}, @func{GRID_SPECTRAL},
512
                     @func{GRID_GME}, @func{GRID_CURVILINEAR} and @func{GRID_UNSTRUCTURED}.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
513
514
515
    @Item  size      Number of gridpoints.

@Description
516
The function @func{gridCreate} creates a horizontal Grid.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
517
518

@Result
519
@func{gridCreate} returns an identifier to the Grid.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
520
521

@Example
522
Here is an example using @func{gridCreate} to create a regular lon/lat Grid:
Uwe Schulzweida's avatar
Uwe Schulzweida committed
523
524

@Source
Uwe Schulzweida's avatar
Uwe Schulzweida committed
525
526
#include "cdi.h"
   ...
527
528
#define  nlon  12
#define  nlat   6
Uwe Schulzweida's avatar
Uwe Schulzweida committed
529
   ...
530
531
double lons[nlon] = {0, 30, 60, 90, 120, 150, 180, 210, 240, 270, 300, 330};
double lats[nlat] = {-75, -45, -15, 15, 45, 75};
Uwe Schulzweida's avatar
Uwe Schulzweida committed
532
533
int gridID;
   ...
534
535
536
gridID = gridCreate(GRID_LONLAT, nlon*nlat);
gridDefXsize(gridID, nlon);
gridDefYsize(gridID, nlat);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
537
538
539
540
gridDefXvals(gridID, lons);
gridDefYvals(gridID, lats);
   ...
@EndSource
Uwe Schulzweida's avatar
Uwe Schulzweida committed
541
542
@EndFunction
*/
543
int gridCreate(int gridtype, size_t size)
Uwe Schulzweida's avatar
Uwe Schulzweida committed
544
{
545
  if ( CDI_Debug ) Message("gridtype=%s  size=%zu", gridNamePtr(gridtype), size);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
546

547
  xassert(size);
548
  gridInit();
549

Uwe Schulzweida's avatar
Uwe Schulzweida committed
550
  grid_t *gridptr = gridNewEntry(CDI_UNDEFID);
551
  if ( ! gridptr ) Error("No memory");
Uwe Schulzweida's avatar
Uwe Schulzweida committed
552

553
  const int gridID = gridptr->self;
Uwe Schulzweida's avatar
Uwe Schulzweida committed
554

555
  if ( CDI_Debug ) Message("gridID: %d", gridID);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
556

557
  cdiGridTypeInit(gridptr, gridtype, size);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
558

Uwe Schulzweida's avatar
Uwe Schulzweida committed
559
  return gridID;
Uwe Schulzweida's avatar
Uwe Schulzweida committed
560
561
}

Uwe Schulzweida's avatar
Uwe Schulzweida committed
562
563
static
void gridDestroyKernel( grid_t * gridptr )
564
{
Deike Kleberg's avatar
Deike Kleberg committed
565
  xassert ( gridptr );
566

567
  const int id = gridptr->self;
568

569
  grid_free_components(gridptr);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
570
  Free( gridptr );
571

572
  reshRemove( id, &gridOps );
573
}
Uwe Schulzweida's avatar
Uwe Schulzweida committed
574

575
576
577
578
579
580
/*
@Function  gridDestroy
@Title     Destroy a horizontal Grid

@Prototype void gridDestroy(int gridID)
@Parameter
Deike Kleberg's avatar
Deike Kleberg committed
581
    @Item  gridID   Grid ID, from a previous call to @fref{gridCreate}.
582
583
584
585
586

@EndFunction
*/
void gridDestroy(int gridID)
{
587
  grid_t *gridptr = grid_to_pointer(gridID);
588
  gridptr->vtable->destroy(gridptr);
589
590
}

Uwe Schulzweida's avatar
Uwe Schulzweida committed
591
592
static
void gridDestroyP(void * gridptr)
593
{
594
  ((grid_t *)gridptr)->vtable->destroy((grid_t *)gridptr);
595
596
597
}


598
const char *gridNamePtr(int gridtype)
Uwe Schulzweida's avatar
Uwe Schulzweida committed
599
{
600
  const int size = (int) (sizeof(Grids)/sizeof(Grids[0]));
Uwe Schulzweida's avatar
Uwe Schulzweida committed
601

Uwe Schulzweida's avatar
Uwe Schulzweida committed
602
  const char *name = (gridtype >= 0 && gridtype < size) ? Grids[gridtype] : Grids[GRID_GENERIC];
Uwe Schulzweida's avatar
Uwe Schulzweida committed
603

Uwe Schulzweida's avatar
Uwe Schulzweida committed
604
  return name;
Uwe Schulzweida's avatar
Uwe Schulzweida committed
605
606
607
608
609
610
611
612
}


void gridName(int gridtype, char *gridname)
{
  strcpy(gridname, gridNamePtr(gridtype));
}

613
static
614
void *grid_key_to_ptr(grid_t *gridptr, int key)
615
{
616
  void *keyptr = NULL;
617
618
619

  switch (key)
    {
620
621
622
623
624
625
626
627
628
    case CDI_KEY_XNAME:      keyptr = (void*)gridptr->x.name; break;
    case CDI_KEY_XLONGNAME:  keyptr = (void*)gridptr->x.longname; break;
    case CDI_KEY_XUNITS:     keyptr = (void*)gridptr->x.units; break;
    case CDI_KEY_YNAME:      keyptr = (void*)gridptr->y.name; break;
    case CDI_KEY_YLONGNAME:  keyptr = (void*)gridptr->y.longname; break;
    case CDI_KEY_YUNITS:     keyptr = (void*)gridptr->y.units; break;
    case CDI_KEY_XDIMNAME:   keyptr = (void*)gridptr->x.dimname; break;
    case CDI_KEY_YDIMNAME:   keyptr = (void*)gridptr->y.dimname; break;
    case CDI_KEY_VDIMNAME:   keyptr = (void*)gridptr->vdimname; break;
629
630
    case CDI_KEY_MAPPING:    keyptr = (void*)gridptr->mapname; break;
    case CDI_KEY_MAPNAME:    keyptr = (void*)gridptr->mapping; break;
631
632
    }

633
  return keyptr;
634
635
}

636
/*
637
@Function  cdiGridDefKeyStr
638
639
@Title     Define a CDI grid string value from a key

640
@Prototype int cdiGridDefKeyStr(int gridID, int key, int size, const char *mesg)
641
642
643
644
645
646
647
@Parameter
    @Item  gridID   Grid ID, from a previous call to @fref{gridCreate}.
    @Item  key      The key to be searched
    @Item  size     The allocated length of the string on input
    @Item  mesg     The address of a string where the data will be read

@Description
648
The function @func{cdiGridDefKeyStr} defines a CDI grid string value from a key.
649
650

@Result
651
@func{cdiGridDefKeyStr} returns 0 if OK and integer value on error.
652
653
654

@EndFunction
*/
655
int cdiGridDefKeyStr(int gridID, int key, int size, const char *mesg)
656
{
657
  if ( size < 1 || mesg == NULL || *mesg == 0 ) return -1;
658

659
  grid_t *gridptr = grid_to_pointer(gridID);
660

661
662
  char *keyptr = (char*)grid_key_to_ptr(gridptr, key);
  if ( keyptr == NULL )
663
664
665
666
667
    {
      Warning("CDI grid string key %d not supported!", key);
      return -1;
    }

668
  gridSetString(keyptr, mesg, (size_t)size);
669
670
671
672
673
674
  gridMark4Update(gridID);

  return 0;
}

/*
675
@Function  cdiGridInqKeyStr
676
677
@Title     Get a CDI grid string value from a key

678
@Prototype int cdiGridInqKeyStr(int gridID, int key, int size, char *mesg)
679
680
681
682
683
684
685
686
687
688
@Parameter
    @Item  gridID   Grid ID, from a previous call to @fref{gridCreate}.
    @Item  key      The key to be searched.
    @Item  size     The allocated length of the string on input.
    @Item  mesg     The address of a string where the data will be retrieved.
                    The caller must allocate space for the returned string.
                    The maximum possible length, in characters, of the string
                    is given by the predefined constant @func{CDI_MAX_NAME}.

@Description
689
The function @func{cdiGridInqKeyStr} return a CDI grid string value from a key.
690
691

@Result
692
@func{cdiGridInqKeyStr} returns 0 if OK and integer value on error.
693
694
695

@EndFunction
*/
696
int cdiGridInqKeyStr(int gridID, int key, int size, char *mesg)
697
{
698
  if ( size < 1 || mesg == NULL ) return -1;
699

700
  grid_t *gridptr = grid_to_pointer(gridID);
701
702
  const char *keyptr = (const char*)grid_key_to_ptr(gridptr, key);
  if ( keyptr == NULL)
703
704
705
706
707
    {
      Warning("CDI grid string key %d not supported!", key);
      return -1;
    }

708
  gridGetString(mesg, keyptr, (size_t)size);
709
710
711
712

  return 0;
}

Uwe Schulzweida's avatar
Uwe Schulzweida committed
713
714
715
716
717
718
/*
@Function  gridDefXname
@Title     Define the name of a X-axis

@Prototype void gridDefXname(int gridID, const char *name)
@Parameter
Deike Kleberg's avatar
Deike Kleberg committed
719
720
    @Item  gridID   Grid ID, from a previous call to @fref{gridCreate}.
    @Item  name     Name of the X-axis.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
721
722

@Description
723
The function @func{gridDefXname} defines the name of a X-axis.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
724
725
726

@EndFunction
*/
Uwe Schulzweida's avatar
Uwe Schulzweida committed
727
void gridDefXname(int gridID, const char *xname)
Uwe Schulzweida's avatar
Uwe Schulzweida committed
728
{
729
  (void)cdiGridDefKeyStr(gridID, CDI_KEY_XNAME, CDI_MAX_NAME, xname);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
730
731
732
733
734
735
736
737
}

/*
@Function  gridDefXlongname
@Title     Define the longname of a X-axis

@Prototype void gridDefXlongname(int gridID, const char *longname)
@Parameter
Deike Kleberg's avatar
Deike Kleberg committed
738
739
    @Item  gridID   Grid ID, from a previous call to @fref{gridCreate}.
    @Item  longname Longname of the X-axis.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
740
741

@Description
742
The function @func{gridDefXlongname} defines the longname of a X-axis.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
743
744
745

@EndFunction
*/
Uwe Schulzweida's avatar
Uwe Schulzweida committed
746
void gridDefXlongname(int gridID, const char *xlongname)
Uwe Schulzweida's avatar
Uwe Schulzweida committed
747
{
748
  (void)cdiGridDefKeyStr(gridID, CDI_KEY_XLONGNAME, CDI_MAX_NAME, xlongname);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
749
750
751
752
753
754
755
756
}

/*
@Function  gridDefXunits
@Title     Define the units of a X-axis

@Prototype void gridDefXunits(int gridID, const char *units)
@Parameter
Deike Kleberg's avatar
Deike Kleberg committed
757
758
    @Item  gridID   Grid ID, from a previous call to @fref{gridCreate}.
    @Item  units    Units of the X-axis.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
759
760

@Description
761
The function @func{gridDefXunits} defines the units of a X-axis.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
762
763
764

@EndFunction
*/
Uwe Schulzweida's avatar
Uwe Schulzweida committed
765
void gridDefXunits(int gridID, const char *xunits)
Uwe Schulzweida's avatar
Uwe Schulzweida committed
766
{
767
  (void)cdiGridDefKeyStr(gridID, CDI_KEY_XUNITS, CDI_MAX_NAME, xunits);
768
769
}

Uwe Schulzweida's avatar
Uwe Schulzweida committed
770
771
772
773
774
775
/*
@Function  gridDefYname
@Title     Define the name of a Y-axis

@Prototype void gridDefYname(int gridID, const char *name)
@Parameter
Deike Kleberg's avatar
Deike Kleberg committed
776
777
    @Item  gridID   Grid ID, from a previous call to @fref{gridCreate}.
    @Item  name     Name of the Y-axis.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
778
779

@Description
780
The function @func{gridDefYname} defines the name of a Y-axis.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
781
782
783

@EndFunction
*/
Uwe Schulzweida's avatar
Uwe Schulzweida committed
784
void gridDefYname(int gridID, const char *yname)
Uwe Schulzweida's avatar
Uwe Schulzweida committed
785
{
786
  (void)cdiGridDefKeyStr(gridID, CDI_KEY_YNAME, CDI_MAX_NAME, yname);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
787
788
789
790
791
792
793
794
}

/*
@Function  gridDefYlongname
@Title     Define the longname of a Y-axis

@Prototype void gridDefYlongname(int gridID, const char *longname)
@Parameter
Deike Kleberg's avatar
Deike Kleberg committed
795
796
    @Item  gridID   Grid ID, from a previous call to @fref{gridCreate}.
    @Item  longname Longname of the Y-axis.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
797
798

@Description
799
The function @func{gridDefYlongname} defines the longname of a Y-axis.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
800
801
802

@EndFunction
*/
Uwe Schulzweida's avatar
Uwe Schulzweida committed
803
void gridDefYlongname(int gridID, const char *ylongname)
Uwe Schulzweida's avatar
Uwe Schulzweida committed
804
{
805
  (void)cdiGridDefKeyStr(gridID, CDI_KEY_YLONGNAME, CDI_MAX_NAME, ylongname);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
806
807
808
809
810
811
812
813
}

/*
@Function  gridDefYunits
@Title     Define the units of a Y-axis

@Prototype void gridDefYunits(int gridID, const char *units)
@Parameter
Deike Kleberg's avatar
Deike Kleberg committed
814
815
    @Item  gridID   Grid ID, from a previous call to @fref{gridCreate}.
    @Item  units    Units of the Y-axis.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
816
817

@Description
818
The function @func{gridDefYunits} defines the units of a Y-axis.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
819
820
821

@EndFunction
*/
Uwe Schulzweida's avatar
Uwe Schulzweida committed
822
void gridDefYunits(int gridID, const char *yunits)
Uwe Schulzweida's avatar
Uwe Schulzweida committed
823
{
824
  (void)cdiGridDefKeyStr(gridID, CDI_KEY_YUNITS, CDI_MAX_NAME, yunits);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
825
826
827
828
829
830
}

/*
@Function  gridInqXname
@Title     Get the name of a X-axis

Deike Kleberg's avatar
Deike Kleberg committed
831
@Prototype void gridInqXname(int gridID, char *name)
Uwe Schulzweida's avatar
Uwe Schulzweida committed
832
@Parameter
Uwe Schulzweida's avatar
Uwe Schulzweida committed
833
834
    @Item  gridID   Grid ID, from a previous call to @fref{gridCreate} or @fref{vlistInqVarGrid}.
    @Item  name     Name of the X-axis. The caller must allocate space for the
Deike Kleberg's avatar
Deike Kleberg committed
835
                    returned string. The maximum possible length, in characters, of
836
                    the string is given by the predefined constant @func{CDI_MAX_NAME}.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
837
838

@Description
839
The function @func{gridInqXname} returns the name of a X-axis.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
840
841

@Result
842
@func{gridInqXname} returns the name of the X-axis to the parameter name.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
843
844
845
846
847

@EndFunction
*/
void gridInqXname(int gridID, char *xname)
{
848
  (void)cdiGridInqKeyStr(gridID, CDI_KEY_XNAME, CDI_MAX_NAME, xname);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
849
850
851
852
853
854
}

/*
@Function  gridInqXlongname
@Title     Get the longname of a X-axis

Deike Kleberg's avatar
Deike Kleberg committed
855
@Prototype void gridInqXlongname(int gridID, char *longname)
Uwe Schulzweida's avatar
Uwe Schulzweida committed
856
@Parameter
Uwe Schulzweida's avatar
Uwe Schulzweida committed
857
858
    @Item  gridID   Grid ID, from a previous call to @fref{gridCreate} or @fref{vlistInqVarGrid}.
    @Item  longname Longname of the X-axis. The caller must allocate space for the
Deike Kleberg's avatar
Deike Kleberg committed
859
                    returned string. The maximum possible length, in characters, of
860
                    the string is given by the predefined constant @func{CDI_MAX_NAME}.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
861
862

@Description
863
The function @func{gridInqXlongname} returns the longname of a X-axis.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
864
865

@Result
866
@func{gridInqXlongname} returns the longname of the X-axis to the parameter longname.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
867
868
869
870
871

@EndFunction
*/
void gridInqXlongname(int gridID, char *xlongname)
{
872
  (void)cdiGridInqKeyStr(gridID, CDI_KEY_XLONGNAME, CDI_MAX_NAME, xlongname);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
873
874
875
876
877
878
}

/*
@Function  gridInqXunits
@Title     Get the units of a X-axis

Deike Kleberg's avatar
Deike Kleberg committed
879
@Prototype void gridInqXunits(int gridID, char *units)
Uwe Schulzweida's avatar
Uwe Schulzweida committed
880
@Parameter
Uwe Schulzweida's avatar
Uwe Schulzweida committed
881
882
    @Item  gridID   Grid ID, from a previous call to @fref{gridCreate} or @fref{vlistInqVarGrid}.
    @Item  units    Units of the X-axis. The caller must allocate space for the
Deike Kleberg's avatar
Deike Kleberg committed
883
                    returned string. The maximum possible length, in characters, of
884
                    the string is given by the predefined constant @func{CDI_MAX_NAME}.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
885
886

@Description
887
The function @func{gridInqXunits} returns the units of a X-axis.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
888
889

@Result
890
@func{gridInqXunits} returns the units of the X-axis to the parameter units.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
891
892
893
894
895

@EndFunction
*/
void gridInqXunits(int gridID, char *xunits)
{
896
  (void)cdiGridInqKeyStr(gridID, CDI_KEY_XUNITS, CDI_MAX_NAME, xunits);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
897
898
899
900
901
}


void gridInqXstdname(int gridID, char *xstdname)
{
Uwe Schulzweida's avatar
Uwe Schulzweida committed
902
903
904
905
906
907
  if ( xstdname )
    {
      xstdname[0] = 0;
      grid_t *gridptr = grid_to_pointer(gridID);
      if ( gridptr->x.stdname ) strcpy(xstdname, gridptr->x.stdname);
    }
Uwe Schulzweida's avatar
Uwe Schulzweida committed
908
909
910
911
912
913
}

/*
@Function  gridInqYname
@Title     Get the name of a Y-axis

Deike Kleberg's avatar
Deike Kleberg committed
914
@Prototype void gridInqYname(int gridID, char *name)
Uwe Schulzweida's avatar
Uwe Schulzweida committed
915
@Parameter
Uwe Schulzweida's avatar
Uwe Schulzweida committed
916
917
    @Item  gridID   Grid ID, from a previous call to @fref{gridCreate} or @fref{vlistInqVarGrid}.
    @Item  name     Name of the Y-axis. The caller must allocate space for the
Deike Kleberg's avatar
Deike Kleberg committed
918
                    returned string. The maximum possible length, in characters, of
919
                    the string is given by the predefined constant @func{CDI_MAX_NAME}.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
920
921

@Description
922
The function @func{gridInqYname} returns the name of a Y-axis.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
923
924

@Result
925
@func{gridInqYname} returns the name of the Y-axis to the parameter name.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
926
927
928
929
930

@EndFunction
*/
void gridInqYname(int gridID, char *yname)
{
931
  (void)cdiGridInqKeyStr(gridID, CDI_KEY_YNAME, CDI_MAX_NAME, yname);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
932
933
934
935
936
937
}

/*
@Function  gridInqYlongname
@Title     Get the longname of a Y-axis

938
@Prototype void gridInqYlongname(int gridID, char *longname)
Uwe Schulzweida's avatar
Uwe Schulzweida committed
939
@Parameter
Uwe Schulzweida's avatar
Uwe Schulzweida committed
940
941
    @Item  gridID   Grid ID, from a previous call to @fref{gridCreate} or @fref{vlistInqVarGrid}.
    @Item  longname Longname of the Y-axis. The caller must allocate space for the
Deike Kleberg's avatar
Deike Kleberg committed
942
                    returned string. The maximum possible length, in characters, of
943
                    the string is given by the predefined constant @func{CDI_MAX_NAME}.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
944
945

@Description
946
The function @func{gridInqYlongname} returns the longname of a Y-axis.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
947
948

@Result
949
@func{gridInqYlongname} returns the longname of the Y-axis to the parameter longname.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
950
951
952
953
954

@EndFunction
*/
void gridInqYlongname(int gridID, char *ylongname)
{
955
  (void)cdiGridInqKeyStr(gridID, CDI_KEY_YLONGNAME, CDI_MAX_NAME, ylongname);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
956
957
958
959
960
961
}

/*
@Function  gridInqYunits
@Title     Get the units of a Y-axis

Deike Kleberg's avatar
Deike Kleberg committed
962
@Prototype void gridInqYunits(int gridID, char *units)
Uwe Schulzweida's avatar
Uwe Schulzweida committed
963
@Parameter
Uwe Schulzweida's avatar
Uwe Schulzweida committed
964
965
    @Item  gridID   Grid ID, from a previous call to @fref{gridCreate} or @fref{vlistInqVarGrid}.
    @Item  units    Units of the Y-axis. The caller must allocate space for the
Deike Kleberg's avatar
Deike Kleberg committed
966
                    returned string. The maximum possible length, in characters, of
967
                    the string is given by the predefined constant @func{CDI_MAX_NAME}.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
968
969

@Description
970
The function @func{gridInqYunits} returns the units of a Y-axis.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
971
972

@Result
973
@func{gridInqYunits} returns the units of the Y-axis to the parameter units.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
974
975
976
977
978

@EndFunction
*/
void gridInqYunits(int gridID, char *yunits)
{
979
  (void)cdiGridInqKeyStr(gridID, CDI_KEY_YUNITS, CDI_MAX_NAME, yunits);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
980
981
}

982

Uwe Schulzweida's avatar
Uwe Schulzweida committed
983
984
void gridInqYstdname(int gridID, char *ystdname)
{
Uwe Schulzweida's avatar
Uwe Schulzweida committed
985
986
987
988
989
990
  if ( ystdname )
    {
      ystdname[0] = 0;
      grid_t *gridptr = grid_to_pointer(gridID);
      if ( gridptr->y.stdname ) strcpy(ystdname, gridptr->y.stdname);
    }
Uwe Schulzweida's avatar
Uwe Schulzweida committed
991
992
}

993

994
void gridDefProj(int gridID, int projID)