OPeNDAP Hyrax Back End Server (BES)  Updated for version 3.8.3
FONcArray.cc
Go to the documentation of this file.
1 // FONcArray.cc
2 
3 // This file is part of BES Netcdf File Out Module
4 
5 // Copyright (c) 2004,2005 University Corporation for Atmospheric Research
6 // Author: Patrick West <pwest@ucar.edu> and Jose Garcia <jgarcia@ucar.edu>
7 //
8 // This library is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU Lesser General Public
10 // License as published by the Free Software Foundation; either
11 // version 2.1 of the License, or (at your option) any later version.
12 //
13 // This library is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 // Lesser General Public License for more details.
17 //
18 // You should have received a copy of the GNU Lesser General Public
19 // License along with this library; if not, write to the Free Software
20 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 //
22 // You can contact University Corporation for Atmospheric Research at
23 // 3080 Center Green Drive, Boulder, CO 80301
24 
25 // (c) COPYRIGHT University Corporation for Atmospheric Research 2004-2005
26 // Please read the full copyright statement in the file COPYRIGHT_UCAR.
27 //
28 // Authors:
29 // pwest Patrick West <pwest@ucar.edu>
30 // jgarcia Jose Garcia <jgarcia@ucar.edu>
31 
32 #include <BESInternalError.h>
33 #include <BESDebug.h>
34 
35 #include "FONcArray.h"
36 #include "FONcDim.h"
37 #include "FONcGrid.h"
38 #include "FONcMap.h"
39 #include "FONcUtils.h"
40 #include "FONcAttributes.h"
41 
42 vector<FONcDim *> FONcArray::Dimensions;
43 
44 const int MAX_CHUNK_SIZE = 1024;
45 
54 FONcArray::FONcArray(BaseType *b) :
55  FONcBaseType(), _a(0), _array_type(NC_NAT), _ndims(0), _actual_ndims(0), _nelements(1), _dim_ids(0), _dim_sizes(
56  0), _str_data(0), _dont_use_it(false), _chunksizes(0), _grid_maps(0)
57 {
58  _a = dynamic_cast<Array *>(b);
59  if (!_a) {
60  string s = (string) "File out netcdf, FONcArray was passed a " + "variable that is not a DAP Array";
61  throw BESInternalError(s, __FILE__, __LINE__);
62  }
63 }
64 
76 {
77 #if 0
78  // No need to use erase. jhrg 8/28/13
79  bool done = false;
80  while (!done) {
81  vector<FONcDim *>::iterator i = _dims.begin();
82  vector<FONcDim *>::iterator e = _dims.end();
83  if (i == e) {
84  done = true;
85  }
86  else {
87  // These are the FONc types, not the actual ones
88  FONcDim *d = (*i);
89  d->decref();
90  _dims.erase(i);
91  }
92  }
93 #endif
94 
95  // Added jhrg 8/28/13
96  vector<FONcDim*>::iterator d = _dims.begin();
97  while (d != _dims.end()) {
98  (*d)->decref();
99  ++d;
100  }
101 
102  // Added jhrg 8/28/13
103  vector<FONcMap*>::iterator i = _grid_maps.begin();
104  while (i != _grid_maps.end()) {
105  (*i)->decref();
106  ++i;
107  }
108 
109  // Added jhrg 8/28/13
110  delete[] _dim_ids;
111  delete[] _dim_sizes;
112  delete[] _str_data;
113  delete[] _chunksizes;
114 }
115 
130 void FONcArray::convert(vector<string> embed)
131 {
132  FONcBaseType::convert(embed);
134  BESDEBUG("fonc", "FONcArray::convert - converting array " << _varname << endl);
135 
136  _array_type = FONcUtils::get_nc_type(_a->var());
137  _ndims = _a->dimensions();
138  _actual_ndims = _ndims;
139  if (_array_type == NC_CHAR) {
140  // if we have array of strings then we need to add the string length
141  // dimension, so add one more to ndims
142  _ndims++;
143  }
144 
145  _dim_ids = new int[_ndims];
146  _dim_sizes = new size_t[_ndims];
147 
148  _chunksizes = new size_t[_ndims];
149 
150  Array::Dim_iter di = _a->dim_begin();
151  Array::Dim_iter de = _a->dim_end();
152  int dimnum = 0;
153  for (; di != de; di++) {
154  int size = _a->dimension_size(di, true);
155  _dim_sizes[dimnum] = size;
156  _nelements *= size;
157 
158  // Set COMPRESSION CHUNK SIZE for each dimension.
159  if (size <= MAX_CHUNK_SIZE) {
160  _chunksizes[dimnum] = size;
161  }
162  else {
163  _chunksizes[dimnum] = MAX_CHUNK_SIZE;
164  }
165 
166  BESDEBUG("fonc",
167  "FONcArray::convert - dimension size: " << size << " chunksize: " << _chunksizes[dimnum] << endl << *this << endl);
168 
169  // See if this dimension has already been defined. If it has the
170  // same name and same size as another dimension, then it is a
171  // shared dimension. Create it only once and share the FONcDim
172  FONcDim *use_dim = find_dim(embed, _a->dimension_name(di), size);
173  _dims.push_back(use_dim);
174  dimnum++;
175  }
176 
177  // if this array is a string array, then add the length dimension
178  if (_array_type == NC_CHAR) {
179  // get the data from the dap array
180  int array_length = _a->length();
181  _str_data = new string[array_length];
182  _a->buf2val((void**) &_str_data);
183 
184  // determine the max length of the strings
185  size_t max_length = 0;
186  for (int i = 0; i < array_length; i++) {
187  if (_str_data[i].length() > max_length) {
188  max_length = _str_data[i].length();
189  }
190  }
191  max_length++;
192  vector<string> empty_embed;
193  string lendim_name = _varname + "_len";
194 
195  FONcDim *use_dim = find_dim(empty_embed, lendim_name, max_length, true);
196  // Added static_cast to supress warning. 12.27.2011 jhrg
197  if (use_dim->size() < static_cast<int>(max_length)) {
198  use_dim->update_size(max_length);
199  }
200 
201  _dim_sizes[_ndims - 1] = use_dim->size();
202  _dim_ids[_ndims - 1] = use_dim->dimid();
203  _dims.push_back(use_dim);
204  }
205 
206  // If this array has a single dimension, and the name of the array
207  // and the name of that dimension are the same, then this array
208  // might be used as a map for a grid defined elsewhere.
209  if (!FONcGrid::InGrid && _actual_ndims == 1 && _a->name() == _a->dimension_name(_a->dim_begin())) {
210  // is it already in there?
211  FONcMap *map = FONcGrid::InMaps(_a);
212  if (!map) {
213  // This memory is leaked. jhrg 8/28/13
214  FONcMap *new_map = new FONcMap(this);
215  _grid_maps.push_back(new_map); // save it here so we can free it later. jhrg 8/28/13
216  FONcGrid::Maps.push_back(new_map);
217  }
218  else {
219  _dont_use_it = true;
220  }
221  }
222 
223  BESDEBUG("fonc", "FONcArray::convert - done converting array " << _varname << endl << *this << endl);
224 }
225 
239 FONcDim *
240 FONcArray::find_dim(vector<string> &embed, const string &name, int size, bool ignore_size)
241 {
242  string oname;
243  string ename = FONcUtils::gen_name(embed, name, oname);
244  FONcDim *ret_dim = 0;
245  vector<FONcDim *>::iterator i = FONcArray::Dimensions.begin();
246  vector<FONcDim *>::iterator e = FONcArray::Dimensions.end();
247  for (; i != e && !ret_dim; i++) {
248  if (!((*i)->name().empty()) && ((*i)->name() == name)) {
249  if (ignore_size) {
250  ret_dim = (*i);
251  }
252  else if ((*i)->size() == size) {
253  ret_dim = (*i);
254  }
255  else {
256  if (embed.size() > 0) {
257  vector<string> tmp;
258  return find_dim(tmp, ename, size);
259  }
260  string err = (string) "fileout_netcdf:dimension found " + "with the same name, but different size";
261  throw BESInternalError(err, __FILE__, __LINE__);
262  }
263  }
264  }
265  if (!ret_dim) {
266  ret_dim = new FONcDim(name, size);
267  FONcArray::Dimensions.push_back(ret_dim);
268  }
269  else {
270  ret_dim->incref();
271  }
272  return ret_dim;
273 }
274 
289 void FONcArray::define(int ncid)
290 {
291  BESDEBUG("fonc", "FONcArray::define - defining array " << _varname << endl);
292 
293  if (!_defined && !_dont_use_it) {
294  vector<FONcDim *>::iterator i = _dims.begin();
295  vector<FONcDim *>::iterator e = _dims.end();
296  int dimnum = 0;
297  for (; i != e; i++) {
298  FONcDim *fd = *i;
299  fd->define(ncid);
300  _dim_ids[dimnum] = fd->dimid();
301  BESDEBUG("fonc", "FONcArray::dim_id " << fd->dimid() << " size:" << fd->size() << endl);
302  dimnum++;
303  }
304 
305  int stax = nc_def_var(ncid, _varname.c_str(), _array_type, _ndims, _dim_ids, &_varid);
306  if (stax != NC_NOERR) {
307  string err = (string) "fileout.netcdf - " + "Failed to define variable " + _varname;
308  FONcUtils::handle_error(stax, err, __FILE__, __LINE__);
309  }
310 
311  if (isNetCDF4()) {
312  stax = nc_def_var_chunking(ncid, _varid, NC_CHUNKED, _chunksizes);
313 
314  if (stax != NC_NOERR) {
315  string err = (string) "fileout.netcdf - " + "Failed to define chunking for variable " + _varname;
316  FONcUtils::handle_error(stax, err, __FILE__, __LINE__);
317  }
318 
319  int shuffle = 0;
320  int deflate = 1;
321  int deflate_level = 4;
322  stax = nc_def_var_deflate(ncid, _varid, shuffle, deflate, deflate_level);
323 
324  if (stax != NC_NOERR) {
325  string err = (string) "fileout.netcdf - " + "Failed to define compression deflation level for variable "
326  + _varname;
327  FONcUtils::handle_error(stax, err, __FILE__, __LINE__);
328  }
329  }
330 
331  FONcAttributes::add_attributes(ncid, _varid, _a);
333 
334  _defined = true;
335  }
336  else {
337  if (_defined) {
338  BESDEBUG("fonc", " variable " << _varname << " is already defined" << endl);
339  }
340  if (_dont_use_it) {
341  BESDEBUG("fonc", " variable " << _varname << " is not being used" << endl);
342  }
343  }
344 
345  BESDEBUG("fonc", "FONcArray::define - done defining array " << _varname << endl);
346 }
347 
357 void FONcArray::write(int ncid)
358 {
359  BESDEBUG("fonc", "FONcArray::write for var " << _varname << endl);
360 
361  if (_dont_use_it) {
362  BESDEBUG("fonc", "FONcTransform::write not using variable " << _varname << endl);
363  return;
364  }
365 
366  ncopts = NC_VERBOSE;
367  int stax = NC_NOERR;
368 
369  if (_array_type != NC_CHAR) {
370  string var_type = _a->var()->type_name();
371 
372  // create array to hold data hyperslab
373  switch (_array_type) {
374  case NC_BYTE: {
375  unsigned char *data = new unsigned char[_nelements];
376  _a->buf2val((void**) &data);
377  stax = nc_put_var_uchar(ncid, _varid, data);
378  delete[] data;
379 
380  if (stax != NC_NOERR) {
381  string err = "fileout.netcdf - Failed to create array of bytes for " + _varname;
382  FONcUtils::handle_error(stax, err, __FILE__, __LINE__);
383  }
384  break;
385  }
386 
387  case NC_SHORT: {
388  short *data = new short[_nelements];
389 
390  // Given Byte/UInt8/Char will always be unsigned they must map
391  // to a NetCDF type that will support unsigned bytes. This
392  // detects the original variable was of type Byte and typecasts
393  // each data value to a short.
394  if (var_type == "Byte") {
395 
396  unsigned char *orig_data = new unsigned char[_nelements];
397  _a->buf2val((void**) &orig_data);
398 
399  for (int d_i = 0; d_i < _nelements; d_i++)
400  data[d_i] = orig_data[d_i];
401 
402  delete[] orig_data;
403  }
404  else {
405  _a->buf2val((void**) &data);
406  }
407  int stax = nc_put_var_short(ncid, _varid, data);
408  delete[] data;
409 
410  if (stax != NC_NOERR) {
411  string err = (string) "fileout.netcdf - Failed to create array of shorts for " + _varname;
412  FONcUtils::handle_error(stax, err, __FILE__, __LINE__);
413  }
414  break;
415  }
416 
417  case NC_INT: {
418  int *data = new int[_nelements];
419  // Since UInt16 also maps to NC_INT, we need to obtain the data correctly
420  // KY 2012-10-25
421  if (var_type == "UInt16") {
422  unsigned short *orig_data = new unsigned short[_nelements];
423  _a->buf2val((void**) &orig_data);
424 
425  for (int d_i = 0; d_i < _nelements; d_i++)
426  data[d_i] = orig_data[d_i];
427 
428  delete[] orig_data;
429  }
430  else {
431  _a->buf2val((void**) &data);
432  }
433 
434  int stax = nc_put_var_int(ncid, _varid, data);
435  delete[] data;
436 
437  if (stax != NC_NOERR) {
438  string err = (string) "fileout.netcdf - Failed to create array of ints for " + _varname;
439  FONcUtils::handle_error(stax, err, __FILE__, __LINE__);
440  }
441  break;
442  }
443 
444  case NC_FLOAT: {
445  float *data = new float[_nelements];
446  _a->buf2val((void**) &data);
447  int stax = nc_put_var_float(ncid, _varid, data);
448  delete[] data;
449 
450  if (stax != NC_NOERR) {
451  string err = (string) "fileout.netcdf - Failed to create array of floats for " + _varname;
452  FONcUtils::handle_error(stax, err, __FILE__, __LINE__);
453  }
454  break;
455  }
456 
457  case NC_DOUBLE: {
458  double *data = new double[_nelements];
459  _a->buf2val((void**) &data);
460  int stax = nc_put_var_double(ncid, _varid, data);
461  delete[] data;
462 
463  if (stax != NC_NOERR) {
464  string err = (string) "fileout.netcdf - Failed to create array of doubles for " + _varname;
465  FONcUtils::handle_error(stax, err, __FILE__, __LINE__);
466  }
467  break;
468  }
469 
470  default:
471  string err = (string) "Failed to transform array of unknown type in file out netcdf";
472  throw BESInternalError(err, __FILE__, __LINE__);
473  }
474  }
475  else {
476  // special case for string data. Could have put this in the
477  // switch, but it's pretty big
478  size_t var_count[_ndims];
479  size_t var_start[_ndims];
480  int dim = 0;
481  for (dim = 0; dim < _ndims; dim++) {
482  // the count for each of the dimensions will always be 1 except
483  // for the string length dimension
484  var_count[dim] = 1;
485 
486  // the start for each of the dimensions will start at 0. We will
487  // bump this up in the while loop below
488  var_start[dim] = 0;
489  }
490 
491  for (int element = 0; element < _nelements; element++) {
492  var_count[_ndims - 1] = _str_data[element].size() + 1;
493  var_start[_ndims - 1] = 0;
494 
495  // write out the string
496  int stax = nc_put_vara_text(ncid, _varid, var_start, var_count, _str_data[element].c_str());
497  if (stax != NC_NOERR) {
498  string err = (string) "fileout.netcdf - Failed to create array of strings for " + _varname;
499  FONcUtils::handle_error(stax, err, __FILE__, __LINE__);
500  }
501 
502  // bump up the start.
503  if (element + 1 < _nelements) {
504  bool done = false;
505  dim = _ndims - 2;
506  while (!done) {
507  var_start[dim] = var_start[dim] + 1;
508  if (var_start[dim] == _dim_sizes[dim]) {
509  var_start[dim] = 0;
510  dim--;
511  }
512  else {
513  done = true;
514  }
515  }
516  }
517  }
518  delete[] _str_data;
519  _str_data = 0; // prevent double delete by the dtor
520  }
521 
522  BESDEBUG("fonc", "FONcTransform::write done for " << _varname << endl);
523 }
524 
530 {
531  return _a->name();
532 }
533 
542 void FONcArray::dump(ostream &strm) const
543 {
544  strm << BESIndent::LMarg << "FONcArray::dump - (" << (void *) this << ")" << endl;
546  strm << BESIndent::LMarg << "name = " << _varname << endl;
547  strm << BESIndent::LMarg << "ndims = " << _ndims << endl;
548  strm << BESIndent::LMarg << "actual ndims = " << _actual_ndims << endl;
549  strm << BESIndent::LMarg << "nelements = " << _nelements << endl;
550  if (_dims.size()) {
551  strm << BESIndent::LMarg << "dimensions:" << endl;
553  vector<FONcDim *>::const_iterator i = _dims.begin();
554  vector<FONcDim *>::const_iterator e = _dims.end();
555  for (; i != e; i++) {
556  (*i)->dump(strm);
557  }
559  }
560  else {
561  strm << BESIndent::LMarg << "dimensions: none" << endl;
562  }
564 }
A map of a DAP Grid with file out netcdf information included.
Definition: FONcMap.h:46
exception thrown if inernal error encountered
virtual bool isNetCDF4()
Returns true if NetCDF4 features will be required.
virtual int size()
Definition: FONcDim.h:61
FONcArray(BaseType *b)
Constructor for FONcArray that takes a DAP Array.
Definition: FONcArray.cc:54
virtual void write(int ncid)
Write the array out to the netcdf file.
Definition: FONcArray.cc:357
static bool InGrid
tells whether we are converting or defining a grid.
Definition: FONcGrid.h:77
static vector< FONcDim * > Dimensions
Definition: FONcArray.h:97
static void Indent()
Definition: BESIndent.cc:38
static void add_original_name(int ncid, int varid, const string &var_name, const string &orig)
Adds an attribute for the variable if the variable name had to be modified in any way...
virtual ~FONcArray()
Destructor that cleans up the array.
Definition: FONcArray.cc:75
string _orig_varname
Definition: FONcBaseType.h:53
virtual void decref()
Decrement the reference count for this dimension.
Definition: FONcDim.cc:62
string _varname
Definition: FONcBaseType.h:52
static FONcMap * InMaps(Array *array)
Definition: FONcGrid.cc:316
virtual void update_size(int newsize)
Definition: FONcDim.h:62
static ostream & LMarg(ostream &strm)
Definition: BESIndent.cc:73
static nc_type get_nc_type(BaseType *element)
translate the OPeNDAP data type to a netcdf data type
Definition: FONcUtils.cc:103
A class that represents the dimension of an array.
Definition: FONcDim.h:44
virtual void define(int ncid)
define the DAP Array in the netcdf file
Definition: FONcArray.cc:289
static void handle_error(int stax, string &err, const string &file, int line)
handle any netcdf errors
Definition: FONcUtils.cc:238
virtual void incref()
Definition: FONcDim.h:55
virtual void convert(vector< string > embed)
Converts the DAP Array to a FONcArray.
Definition: FONcArray.cc:130
A DAP BaseType with file out netcdf information included.
Definition: FONcBaseType.h:48
virtual void define(int ncid)
define the DAP dimension in the netcdf file
Definition: FONcDim.cc:81
virtual int dimid()
Definition: FONcDim.h:63
static string gen_name(const vector< string > &embed, const string &name, string &original)
generate a new name for the embedded variable
Definition: FONcUtils.cc:150
#define BESDEBUG(x, y)
macro used to send debug information to the debug stream
Definition: BESDebug.h:64
static void UnIndent()
Definition: BESIndent.cc:44
const int MAX_CHUNK_SIZE
Definition: FONcArray.cc:44
virtual string name()
returns the name of the DAP Array
Definition: FONcArray.cc:529
virtual void convert(vector< string > embed)
Definition: FONcBaseType.cc:41
virtual void dump(ostream &strm) const
dumps information about this object for debugging purposes
Definition: FONcArray.cc:542
static vector< FONcMap * > Maps
global list of maps that could be shared amongst the different grids
Definition: FONcGrid.h:75