bes Updated for version 3.21.1
The Backend Server (BES) is the lower two tiers of the Hyrax data server
FONcArrayStructureField.cc
1// FONcArrayStructureField.cc
2
3// This file is part of BES Netcdf File Out Module
4
5// // Copyright (c) The HDF Group, Inc. and OPeNDAP, Inc.
6//
7// This is free software; you can redistribute it and/or modify it under the
8// terms of the GNU Lesser General Public License as published by the Free
9// Software Foundation; either version 2.1 of the License, or (at your
10// option) any later version.
11//
12// This software is distributed in the hope that it will be useful, but
13// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
15// License for more details.
16//
17// You should have received a copy of the GNU Lesser General Public
18// License along with this library; if not, write to the Free Software
19// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20//
21// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
22// You can contact The HDF Group, Inc. at 410 E University Ave,
23// Suite 200, Champaign, IL 61820
24
25
26#include <cstring>
27#include <BESInternalError.h>
28#include <BESDebug.h>
29#include <libdap/Array.h>
30#include <libdap/Structure.h>
31#include <libdap/Byte.h>
32#include <libdap/Int8.h>
33#include <libdap/Int16.h>
34#include <libdap/Int32.h>
35#include <libdap/Int64.h>
36#include <libdap/UInt16.h>
37#include <libdap/UInt32.h>
38#include <libdap/UInt64.h>
39#include <libdap/Float32.h>
40#include <libdap/Float64.h>
41#include <libdap/Str.h>
42#include <libdap/util.h>
43#include <BESDebug.h>
44#include <BESUtil.h>
45
46#include "FONcArrayStructureField.h"
47#include "FONcDim.h"
48#include "FONcUtils.h"
49#include "FONcAttributes.h"
50
51vector<FONcDim *> FONcArrayStructureField::SDimensions;
52
62FONcArrayStructureField::FONcArrayStructureField( BaseType *b, Array* a, bool is_netCDF4_enhanced)
63 : FONcBaseType()
64{
65
66 // We only support one-layer of simple int/float array or scalar fields inside an array of structure now.
67 Type b_data_type = b->type();
68 Type supported_atomic_data_type = b_data_type;
69 if(b_data_type == libdap::dods_array_c)
70 supported_atomic_data_type = b->var()->type();
71
72 if (!is_simple_type(supported_atomic_data_type)
73 || supported_atomic_data_type == dods_url_c
74 || supported_atomic_data_type == dods_enum_c || supported_atomic_data_type ==dods_opaque_c) {
75 string s = "File out netcdf, only support one-layer of simple int/float fields inside an array of structure.";
76 throw BESInternalError(s, __FILE__, __LINE__);
77 }
78
79 d_a = a;
80 if (supported_atomic_data_type == dods_str_c)
81 handle_structure_string_field(b);
82 else {
83 d_varname = b->name();
84 if (b_data_type == libdap::dods_array_c) {
85 d_array_type = FONcUtils::get_nc_type(b->var(), is_netCDF4_enhanced);
86 auto t_b = dynamic_cast<Array *>(b);
87 d_array_type_size = (t_b->width_ll()) / (t_b->length_ll());
88 } else {
89 d_array_type = FONcUtils::get_nc_type(b, is_netCDF4_enhanced);
90 d_array_type_size = (b->width_ll()) / (b->length_ll());
91 }
92 // Need to retrieve dimension information here.
93 // In this version, we only try to get the dimension size right.
94 Array::Dim_iter di = d_a->dim_begin();
95 Array::Dim_iter de = d_a->dim_end();
96 for (; di != de; di++) {
97 int64_t size = d_a->dimension_size_ll(di, true);
98 struct_dim_sizes.push_back(size);
99 total_nelements *= size;
100 FONcDim *use_dim = find_sdim(d_a->dimension_name(di), size);
101 struct_dims.push_back(use_dim);
102 }
103 if (b_data_type == libdap::dods_array_c) {
104 auto db_a = dynamic_cast<Array *>(b);
105 if (!db_a) {
106 string s = "File out netcdf, FONcArrayStructField was passed a variable that is not a DAP Array";
107 throw BESInternalError(s, __FILE__, __LINE__);
108 }
109 Array::Dim_iter b_di = db_a->dim_begin();
110 Array::Dim_iter b_de = db_a->dim_end();
111 for (; b_di != b_de; b_di++) {
112 int64_t size = d_a->dimension_size_ll(b_di, true);
113 struct_dim_sizes.push_back(size);
114 total_nelements *= size;
115 field_nelements *= size;
116 FONcDim *use_dim = find_sdim(db_a->dimension_name(b_di), size);
117 struct_dims.push_back(use_dim);
118 }
119 }
120 }
121
122}
123
124size_t
125FONcArrayStructureField::obtain_maximum_string_length( ){
126
127 size_t asf_max_str_len = 0;
128
129 // Obtain the compound_buf; this is for gathering the data for individual fields.
130 vector<BaseType*> compound_buf = d_a->get_compound_buf();
131 for (unsigned i= 0; i<d_a->length_ll();i++) {
132 BaseType *cb = compound_buf[i];
133 if (cb->type()!=libdap::dods_structure_c){
134 throw BESInternalError("Fileout netcdf: This is not array of structure", __FILE__, __LINE__);
135 }
136 auto structure_elem = dynamic_cast<Structure *>(cb);
137 if (!structure_elem)
138 throw BESInternalError("Fileout netcdf: This is not array of structure", __FILE__, __LINE__);
139
140 for (auto &bt:structure_elem->variables()) {
141
142 if (bt->send_p()) {
143
144 if (bt->name() == d_varname) {
145 if (bt->type() == libdap::dods_array_c) {
146 auto memb_array = dynamic_cast<Array *>(bt);
147 if (!memb_array)
148 throw BESInternalError("Fileout netcdf: This structure member is not an array", __FILE__, __LINE__);
149 for (int64_t ma_i = 0; ma_i < memb_array->length_ll(); ma_i++) {
150 if (memb_array->get_str()[ma_i].size() > asf_max_str_len) {
151 asf_max_str_len = memb_array->get_str()[ma_i].size();
152 }
153 }
154
155 } else {// Need to switch to different data types
156 auto memb_str = dynamic_cast<Str *>(bt);
157 if ((size_t)(memb_str->length_ll())>asf_max_str_len)
158 asf_max_str_len = memb_str->length_ll();
159
160 }
161 }
162 }
163 }
164 }
165 asf_max_str_len++;
166 return asf_max_str_len;
167}
168
169void
170FONcArrayStructureField::handle_structure_string_field(BaseType *b){
171
172 d_varname = b->name();
173 d_array_type = NC_CHAR;
174
175 BESDEBUG("fonc","d_varname is: "<<d_varname <<endl);
176
177 // We need to obtain the dimension information.
178 Array::Dim_iter di = d_a->dim_begin();
179 Array::Dim_iter de = d_a->dim_end();
180 for (; di != de; di++) {
181 int64_t size = d_a->dimension_size_ll(di, true);
182 struct_dim_sizes.push_back(size);
183 total_nelements *= size;
184 FONcDim *use_dim = find_sdim(d_a->dimension_name(di), size);
185 struct_dims.push_back(use_dim);
186 }
187
188 // If this structure member is an array of string, we also need to retrieve its dimension.
189 if (b->type() == libdap::dods_array_c) {
190 auto db_a = dynamic_cast<Array *>(b);
191 if (!db_a) {
192 string s = "File out netcdf, FONcArrayStructField was passed a variable that is not a DAP Array";
193 throw BESInternalError(s, __FILE__, __LINE__);
194 }
195 Array::Dim_iter b_di = db_a->dim_begin();
196 Array::Dim_iter b_de = db_a->dim_end();
197 for (; b_di != b_de; b_di++) {
198 int64_t size = d_a->dimension_size_ll(b_di, true);
199 struct_dim_sizes.push_back(size);
200 total_nelements *= size;
201 field_nelements *= size;
202 FONcDim *use_dim = find_sdim(db_a->dimension_name(b_di), size);
203 struct_dims.push_back(use_dim);
204 }
205 }
206 size_t max_length = obtain_maximum_string_length();
207 struct_dim_sizes.push_back(max_length);
208 string last_dim_name = d_a->name()+"_"+b->name() +"_len";
209 FONcDim *use_dim = find_sdim(last_dim_name,max_length);
210 struct_dims.push_back(use_dim);
211
212}
213
214FONcArrayStructureField::~FONcArrayStructureField() {
215
216 for (auto &dim: struct_dims) {
217 dim->struct_decref();
218 }
219
220}
221
222void FONcArrayStructureField::convert(vector<string> embed, bool _dap4, bool is_dap4_group){
223 var_name = FONcUtils::gen_name(embed,d_varname, d_orig_varname);
224
225}
226
232void
234{
235 BESDEBUG("fonc", "FONcArray::define() - defining array '" << d_varname << "'" << endl);
236
237 if (!d_defined) {
238
239 BESDEBUG("fonc", "FONcArray::define() - defining array of structure field: " << d_varname << "'" << endl);
240 auto i = struct_dims.begin();
241 auto e = struct_dims.end();
242 for (; i != e; i++) {
243 FONcDim *fd = *i;
244 fd->define_struct(ncid);
245 d_dim_ids.push_back(fd->dimid());
246 BESDEBUG("fonc", "FONcArray::define() - dim_id: " << fd->dimid() << " size:" << fd->size() << endl);
247 }
248
249 int stax = nc_def_var(ncid, var_name.c_str(), d_array_type, (int)(struct_dims.size()), d_dim_ids.data(), &d_varid);
250 if (stax != NC_NOERR) {
251 string err = (string) "fileout.netcdf - Failed to define variable " + d_varname;
252 FONcUtils::handle_error(stax, err, __FILE__, __LINE__);
253 }
254
255 stax = nc_def_var_fill(ncid, d_varid, NC_NOFILL, nullptr );
256 if (stax != NC_NOERR) {
257 string err = (string) "fileout.netcdf - " + "Failed to clear fill value for " + d_varname;
258 FONcUtils::handle_error(stax, err, __FILE__, __LINE__);
259 }
260 }
261}
262
270void
272{
273 BESDEBUG( "fonc", "FONcArrayStructureField::write for var " << d_varname << endl ) ;
274
275 if (d_array_type == NC_CHAR) {
276 write_str(ncid);
277 return;
278 }
279
280 vector<char> data_buf;
281
282 // Obtain the total data buffer.
283 data_buf.resize(total_nelements*d_array_type_size);
284 char* data_buf_ptr = data_buf.data();
285
286 // Obtain the compound_buf; this is for gathering the data for individual fields.
287 vector<BaseType*> compound_buf = d_a->get_compound_buf();
288 for (unsigned i= 0; i<d_a->length_ll();i++) {
289 BaseType *cb = compound_buf[i];
290 if (cb->type()!=libdap::dods_structure_c){
291 throw BESInternalError("Fileout netcdf: This is not array of structure", __FILE__, __LINE__);
292 }
293 auto structure_elem = dynamic_cast<Structure *>(cb);
294 if (!structure_elem)
295 throw BESInternalError("Fileout netcdf: This is not array of structure", __FILE__, __LINE__);
296
297 for (auto &bt:structure_elem->variables()) {
298
299 if (bt->send_p()) {
300
301 if (bt->name() == d_varname) {
302 if (bt->type() == libdap::dods_array_c) {
303 auto memb_array = dynamic_cast<Array *>(bt);
304 if (!memb_array)
305 throw BESInternalError("Fileout netcdf: This structure member is not an array", __FILE__, __LINE__);
306 const char *buf = memb_array->get_buf();
307 size_t memb_array_size = memb_array->width_ll();
308
309 // fill in the data_buf.
310 memcpy(data_buf_ptr, buf, memb_array_size);
311 BESDEBUG("fonc","memb_array_length is "<<memb_array->length_ll()<<endl);
312 BESDEBUG("fonc","memb_array_type is "<<memb_array->width_ll()<<endl);
313 BESDEBUG("fonc","memb_array_size is "<<memb_array_size<<endl);
314 data_buf_ptr +=memb_array_size;
315
316 } else {// Need to switch to different data types
317 obtain_scalar_data(data_buf_ptr,bt);
318 data_buf_ptr += d_array_type_size;
319 }
320 }
321 }
322 }
323 }
324 int stax = nc_put_var(ncid, d_varid, (void*)data_buf.data());
325 if (stax != NC_NOERR) {
326 string err = "fileout.netcdf - cannot write the array of structure members " + d_varname;
327 FONcUtils::handle_error(stax , err, __FILE__, __LINE__);
328 }
329}
330
331void FONcArrayStructureField::obtain_scalar_data(char *data_buf_ptr, BaseType *b) const{
332
333 switch (b->type()) {
334
335 case dods_uint8_c:
336 case dods_byte_c: {
337 auto byte_var = dynamic_cast<Byte *>(b);
338 uint8_t byte_value = byte_var->value();
339 memcpy(data_buf_ptr,(void*)&byte_value,d_array_type_size);
340 break;
341 }
342 case dods_int8_c: {
343 auto int8_var = dynamic_cast<Int8 *>(b);
344 int8_t int8_value = int8_var->value();
345 memcpy(data_buf_ptr,(void*)&int8_value,d_array_type_size);
346 break;
347 }
348
349 case dods_uint16_c: {
350 auto uint16_var = dynamic_cast<UInt16 *>(b);
351 uint16_t uint16_value = uint16_var->value();
352 memcpy(data_buf_ptr,(void*)&uint16_value,d_array_type_size);
353 break;
354 }
355 case dods_int16_c: {
356 auto int16_var = dynamic_cast<Int16 *>(b);
357 int16_t int16_value = int16_var->value();
358 memcpy(data_buf_ptr, (void *) &int16_value, d_array_type_size);
359 break;
360 }
361 case dods_uint32_c: {
362 auto uint32_var = dynamic_cast<UInt32 *>(b);
363 uint32_t uint32_value = uint32_var->value();
364 memcpy(data_buf_ptr,(void*)&uint32_value,d_array_type_size);
365 break;
366 }
367 case dods_int32_c: {
368 auto int32_var = dynamic_cast<Int32 *>(b);
369 int32_t int32_value = int32_var->value();
370 memcpy(data_buf_ptr, (void *) &int32_value, d_array_type_size);
371 break;
372 }
373 case dods_uint64_c: {
374 auto uint64_var = dynamic_cast<UInt64 *>(b);
375 uint64_t uint64_value = uint64_var->value();
376 memcpy(data_buf_ptr,(void*)&uint64_value,d_array_type_size);
377 break;
378 }
379 case dods_int64_c: {
380 auto int64_var = dynamic_cast<Int64 *>(b);
381 int64_t int64_value = int64_var->value();
382 memcpy(data_buf_ptr,(void*)&int64_value,d_array_type_size);
383 break;
384 }
385 case dods_float32_c: {
386 auto float32_var = dynamic_cast<Float32 *>(b);
387 float float32_value = float32_var->value();
388 memcpy(data_buf_ptr,(void*)&float32_value,d_array_type_size);
389 break;
390 }
391 case dods_float64_c: {
392 auto float64_var = dynamic_cast<Float64 *>(b);
393 double float64_value = float64_var->value();
394 memcpy(data_buf_ptr,(void*)&float64_value,d_array_type_size);
395 break;
396 }
397 default:
398 string err = "file out netcdf structure array: Only support int/float types";
399 throw BESInternalError(err, __FILE__, __LINE__);
400 }
401}
402
403// Note: TODO: if the string is equal-size, we can optimize as write_equal_length_string_array() in FONcArray.cc.
404// However, for netCDF-4, the better way is to map DAP4 string to netCDF-4 string.
405
406void FONcArrayStructureField::write_str(int ncid){
407
408 size_t d_ndims = struct_dims.size();
409 vector<size_t> var_count(d_ndims);
410 vector<size_t> var_start(d_ndims);
411
412 int dim = 0;
413 for (dim = 0; dim < d_ndims; dim++) {
414 // the count for each of the dimensions will always be 1 except
415 // for the string length dimension.
416 // The size of the last dimension (var_count[d_ndims-1]) is set
417 // separately for each element below. jhrg 10/3/22
418 var_count[dim] = 1;
419
420 // the start for each of the dimensions will start at 0. We will
421 // bump this up in the while loop below
422 var_start[dim] = 0;
423 }
424
425 // Obtain the compound_buf; this is for gathering the data for individual fields.
426 vector<BaseType*> compound_buf = d_a->get_compound_buf();
427
428 for (unsigned i= 0; i<d_a->length_ll();i++) {
429 BaseType *cb = compound_buf[i];
430 if (cb->type()!=libdap::dods_structure_c){
431 throw BESInternalError("Fileout netcdf: This is not array of structure", __FILE__, __LINE__);
432 }
433 auto structure_elem = dynamic_cast<Structure *>(cb);
434 if (!structure_elem)
435 throw BESInternalError("Fileout netcdf: Dynamic cast failed. This is not array of structure", __FILE__, __LINE__);
436
437 for (auto &bt:structure_elem->variables()) {
438
439 if (bt->send_p()) {
440 if (bt->name() == d_varname) {
441 if (bt->type() == libdap::dods_array_c) {
442 auto memb_array = dynamic_cast<Array *>(bt);
443 if (!memb_array)
444 throw BESInternalError("Fileout netcdf: This structure member is not an array", __FILE__, __LINE__);
445 auto const &m_a_str = memb_array->get_str();
446 // array of string, use field_nelements.
447 for (int64_t element = 0; element < field_nelements; element++) {
448 var_count[d_ndims -1] = m_a_str[element].size() +1;
449 var_start[d_ndims - 1] = 0;
450
451 // write out the string
452 int stax = nc_put_vara_text(ncid, d_varid, var_start.data(), var_count.data(),
453 m_a_str[element].c_str());
454
455 if (stax != NC_NOERR) {
456 string err = (string) "fileout.netcdf - Failed to create array of strings in a DAP4 structure for " + d_varname ;
457 FONcUtils::handle_error(stax, err, __FILE__, __LINE__);
458 }
459
460 // bump up the start.
461 bool done = false;
462 dim = d_ndims - 2;
463
464 while (!done) {
465 var_start[dim] = var_start[dim] + 1;
466 if (var_start[dim] == struct_dim_sizes[dim]) {
467 var_start[dim] = 0;
468 dim--;
469 if (dim <0)
470 break;
471 }
472 else {
473 done = true;
474 }
475 }
476 }
477 } else {// Need to switch to different data types
478 auto memb_type= dynamic_cast<Str *>(bt);
479 if (!memb_type)
480 throw BESInternalError("Fileout netcdf: This structure member is not a string", __FILE__, __LINE__);
481 auto const & str_value = memb_type->value();
482 var_count[d_ndims -1] = str_value.size() +1;
483 var_start[d_ndims - 1] = 0;
484
485 // write out the string
486 int stax = nc_put_vara_text(ncid, d_varid, var_start.data(), var_count.data(),
487 str_value.c_str());
488
489 if (stax != NC_NOERR) {
490 string err = (string) "fileout.netcdf - Failed to create array of strings for " + d_varname;
491 FONcUtils::handle_error(stax, err, __FILE__, __LINE__);
492 }
493
494 // bump up the start.
495 if (i + 1 < d_a->length_ll()) {
496 bool done = false;
497 dim = d_ndims - 2;
498 while (!done) {
499 var_start[dim] = var_start[dim] + 1;
500 if (var_start[dim] == struct_dim_sizes[dim]) {
501 var_start[dim] = 0;
502 dim--;
503 if (dim <0)
504 break;
505 }
506 else {
507 done = true;
508 }
509 }
510 }
511 }
512 }
513 }
514 }
515 }
516
517}
522string
524{
525 return var_name ;
526}
527
532nc_type
534{
535 return d_array_type;
536}
537
538// The following method is adapted from FONcArray.cc.
539FONcDim *
540FONcArrayStructureField::find_sdim(const string &name, int64_t size) {
541
542 FONcDim *ret_dim = nullptr;
543 vector<FONcDim *>::iterator i = FONcArrayStructureField::SDimensions.begin();
544 vector<FONcDim *>::iterator e = FONcArrayStructureField::SDimensions.end();
545 for (; i != e && !ret_dim; i++) {
546 if (!((*i)->name().empty()) && ((*i)->name() == name)) {
547 if ((*i)->size() == size) {
548 ret_dim = (*i);
549 }
550 else {
551 string err = "fileout_netcdf: dimension found with the same name, but different size";
552 throw BESInternalError(err, __FILE__, __LINE__);
553 }
554 }
555 }
556
557 if (!ret_dim) {
558 ret_dim = new FONcDim(name, size);
559 FONcArrayStructureField::SDimensions.push_back(ret_dim);
560 }
561 else {
562 ret_dim->struct_incref();
563 }
564
565 return ret_dim;
566}
567
574void
576{
577 strm << BESIndent::LMarg << "FONcArrayStructureField::dump - ("
578 << (void *)this << ")" << endl ;
579 BESIndent::Indent() ;
580 strm << BESIndent::LMarg << "memb name = " << var_name << endl ;
581 for (const auto& sdim:struct_dims)
582 sdim->dump(strm);
583 BESIndent::UnIndent() ;
584}
585
exception thrown if internal error encountered
FONcArrayStructureField(libdap::BaseType *b, libdap::Array *a, bool is_netCDF4_enhanced)
Constructor for FONcArrayStructureField.
void dump(std::ostream &strm) const override
dumps information about this object for debugging purposes
std::string name() override
returns the name of the array structure field
nc_type type() override
returns the netcdf type of the DAP object
void write(int ncid) override
Write the netcdf variable data out to the netcdf file.
void define(int ncid) override
define the DAP array of structure field in the netcdf file
A class that represents the dimension of an array.
Definition FONcDim.h:45
static void handle_error(int stax, const string &err, const string &file, int line)
handle any netcdf errors
Definition FONcUtils.cc:429
static nc_type get_nc_type(libdap::BaseType *element, bool isNC4_ENHANCED)
translate the OPeNDAP data type to a netcdf data type
Definition FONcUtils.cc:115
static string gen_name(const vector< string > &embed, const string &name, string &original)
generate a new name for the embedded variable
Definition FONcUtils.cc:182
STL class.
STL class.
STL iterator class.
STL class.