bes  Updated for version 3.20.13
GridAggregationBase.cc
1 // This file is part of the "NcML Module" project, a BES module designed
3 // to allow NcML files to be used to be used as a wrapper to add
4 // AIS to existing datasets of any format.
5 //
6 // Copyright (c) 2010 OPeNDAP, Inc.
7 // Author: Michael Johnson <m.johnson@opendap.org>
8 //
9 // For more information, please also see the main website: http://opendap.org/
10 //
11 // This library is free software; you can redistribute it and/or
12 // modify it under the terms of the GNU Lesser General Public
13 // License as published by the Free Software Foundation; either
14 // version 2.1 of the License, or (at your option) any later version.
15 //
16 // This library is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 // Lesser General Public License for more details.
20 //
21 // You should have received a copy of the GNU Lesser General Public
22 // License along with this library; if not, write to the Free Software
23 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
24 //
25 // Please see the files COPYING and COPYRIGHT for more information on the GLPL.
26 //
27 // You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
29 
30 
31 #include <libdap/Array.h> // libdap
32 #include <libdap/D4Group.h>
33 #include <libdap/Constructor.h>
34 #include <libdap/D4Maps.h>
35 #include <libdap/InternalErr.h>
36 
37 #include "BESStopWatch.h"
38 
39 #include "AggregationUtil.h" // agg_util
40 #include "GridAggregationBase.h" // agg_util
41 
42 #include "NCMLDebug.h"
43 
44 using libdap::Array;
45 using libdap::BaseType;
46 using libdap::Grid;
47 
48 using libdap::D4Group;
49 using libdap::Constructor;
50 using libdap::InternalErr;
51 using libdap::D4Maps;
52 using libdap::D4Map;
53 
54 // Local debug flags
55 #define DEBUG_CHANNEL "agg_util"
56 
57 namespace agg_util {
58 GridAggregationBase::GridAggregationBase(const libdap::Grid& proto, const AMDList& memberDatasets, const DDSLoader& loaderProto) :
59  Grid(proto), _loader(loaderProto.getDHI()), _pSubGridProto(cloneSubGridProto(proto)), _memberDatasets(memberDatasets)
60 {
61 }
62 
63 GridAggregationBase::GridAggregationBase(const string& name, const AMDList& memberDatasets, const DDSLoader& loaderProto) :
64  Grid(name), _loader(loaderProto.getDHI()), _memberDatasets(memberDatasets)
65 {
66 }
67 
68 GridAggregationBase::GridAggregationBase(const GridAggregationBase& proto) :
69  Grid(proto), _loader(proto._loader.getDHI())
70 {
71  duplicate(proto);
72 }
73 
74 /* virtual */
75 GridAggregationBase::~GridAggregationBase()
76 {
77  cleanup();
78 }
79 
80 GridAggregationBase&
81 GridAggregationBase::operator=(const GridAggregationBase& rhs)
82 {
83  if (this != &rhs) {
84  cleanup();
85  Grid::operator=(rhs);
86  duplicate(rhs);
87  }
88  return *this;
89 }
90 
91 
92 void
93 GridAggregationBase::transform_to_dap4(D4Group *root, Constructor *container)
94 {
95  Grid::transform_to_dap4(root,container);
96 
97 #if 0 // I removed this method because I think the parent class implementation should work correctly.
98  BaseType *btp = array_var()->transform_to_dap4(root, container);
99  Array *coverage = static_cast<Array*>(btp);
100  if (!coverage) throw InternalErr(__FILE__, __LINE__, "Expected an Array while transforming a Grid (coverage)");
101 
102  coverage->set_parent(container);
103 
104  // Next find the maps; add them to the coverage and to the container,
105  // the latter only on the condition that they are not already there.
106 
107  for (Map_iter i = map_begin(), e = map_end(); i != e; ++i) {
108  btp = (*i)->transform_to_dap4(root, container);
109  Array *map = static_cast<Array*>(btp);
110  if (!map) throw InternalErr(__FILE__, __LINE__, "Expected an Array while transforming a Grid (map)");
111 
112  // map must be non-null (Grids cannot contain Grids in DAP2)
113  if (map) {
114  // Only add the map/array if it not already present; given the scoping rules
115  // for DAP2 and the assumption the DDS is valid, testing for the same name
116  // is good enough.
117  if (!root->var(map->name())) {
118  map->set_parent(container);
119  container->add_var_nocopy(map); // this adds the array to the container
120  }
121  D4Map *dap4_map = new D4Map(map->name(), map, coverage); // bind the 'map' to the coverage
122  coverage->maps()->add_map(dap4_map); // bind the coverage to the map
123  }
124  else {
125  throw InternalErr(__FILE__, __LINE__,
126  "transform_to_dap4() returned a null value where there can be no Grid.");
127  }
128  }
129 
130  container->add_var_nocopy(coverage);
131 #endif
132 }
133 
134 void GridAggregationBase::setShapeFrom(const libdap::Grid& constProtoSubGrid, bool addMaps)
135 {
136  // calls used are semantically const, but not syntactically.
137  Grid& protoSubGrid = const_cast<Grid&>(constProtoSubGrid);
138 
139  // Save a clone of the template for read() to use.
140  // We always use these maps...
141  _pSubGridProto = unique_ptr<Grid>(cloneSubGridProto(protoSubGrid));
142 
143  // Pass in the data array and maps from the proto by hand.
144  Array* pDataArrayTemplate = protoSubGrid.get_array();
145  VALID_PTR(pDataArrayTemplate);
146  set_array(static_cast<Array*>(pDataArrayTemplate->ptr_duplicate()));
147 
148  // Now the maps in order if asked
149  if (addMaps) {
150  Grid::Map_iter endIt = protoSubGrid.map_end();
151  for (Grid::Map_iter it = protoSubGrid.map_begin(); it != endIt; ++it) {
152  // have to case, the iter is for some reason BaseType*
153  Array* pMap = dynamic_cast<Array*>(*it);
154  VALID_PTR(pMap);
155  add_map(pMap, true); // add as a copy
156  }
157  }
158 }
159 
160 /* virtual */
161 const AMDList&
163 {
164  return _memberDatasets;
165 }
166 
167 /* virtual */
169 {
170  BESDEBUG_FUNC(DEBUG_CHANNEL, "Function entered..." << endl);
171 
172  if (read_p()) {
173  BESDEBUG_FUNC(DEBUG_CHANNEL, "read_p() set, early exit!");
174  return true;
175  }
176 
177  // Call the subclass hook methods to do this work properly
179 
180  // Now make the read call on the data array.
181  // The aggregation subclass will do the right thing.
182  Array* pAggArray = get_array();
183  VALID_PTR(pAggArray);
184 
185  // Only do this portion if the array part is supposed to serialize!
186  if (pAggArray->send_p() || pAggArray->is_in_selection()) {
187  pAggArray->read();
188  }
189 
190  // Set the cache bit.
191  set_read_p(true);
192  return true;
193 }
194 
195 #define PIPELINING 1
196 
217 bool
218 GridAggregationBase::serialize(libdap::ConstraintEvaluator &eval, libdap::DDS &dds, libdap::Marshaller &m,
219  bool ce_eval)
220 {
221  BESStopWatch sw;
222  if (BESDebug::IsSet(TIMING_LOG_KEY)) sw.start("GridAggregationBase::serialize", "");
223 
224  bool status = false;
225 
226  if (!read_p()) {
227  // Call the subclass hook methods to do this work properly
228  // *** Replace Map code readAndAggregateConstrainedMapsHook();
229 
230  // Transfers constraints to the proto grid and reads it
232 
233  // Make the call to serialize the data array.
234  // The aggregation subclass will do the right thing.
235  Array* pAggArray = get_array();
236  VALID_PTR(pAggArray);
237 
238  // Only do this portion if the array part is supposed to serialize!
239  if (pAggArray->send_p() || pAggArray->is_in_selection()) {
240 #if PIPELINING
241  pAggArray->serialize(eval, dds, m, ce_eval);
242 #else
243  pAggArray->read();
244 #endif
245  }
246 
247  // Get the read-in, constrained maps from the proto grid and serialize them.
248  // *** Replace copyProtoMapsIntoThisGrid(getAggregationDimension());
249 
250  Grid* pSubGridTemplate = getSubGridTemplate();
251  VALID_PTR(pSubGridTemplate);
252 
253  Map_iter mapIt;
254  Map_iter mapEndIt = map_end();
255  for (mapIt = map_begin(); mapIt != mapEndIt; ++mapIt) {
256  Array* pOutMap = static_cast<Array*>(*mapIt);
257  VALID_PTR(pOutMap);
258 
259  // If it isn't getting dumped, then don't bother with it
260  if (!(pOutMap->send_p() || pOutMap->is_in_selection())) {
261  continue;
262  }
263 
264  // We don't want to touch the aggregation dimension since it's
265  // handled specially.
266  if (pOutMap->name() == getAggregationDimension().name) {
267  // Make sure it's read with these constraints.
268 #if PIPELINING
269  pOutMap->serialize(eval, dds, m, ce_eval);
270 #else
271  pOutMap->read();
272 #endif
273  continue;
274  }
275 
276  // Otherwise, find the map in the protogrid and copy it's data into this.
277  Array* pProtoGridMap = const_cast<Array*>(AggregationUtil::findMapByName(*pSubGridTemplate, pOutMap->name()));
278  NCML_ASSERT_MSG(pProtoGridMap, "Couldn't find map in prototype grid for map name=" + pOutMap->name());
279  BESDEBUG_FUNC(DEBUG_CHANNEL,
280  "About to call read() on prototype map vector name=" << pOutMap->name() << " and calling transfer constraints..." << endl);
281 
282  // Make sure the protogrid maps were properly read
283  NCML_ASSERT_MSG(pProtoGridMap->read_p(), "Expected the prototype map to have been read but it wasn't.");
284 
285  // Make sure the lengths match to be sure we're not gonna blow memory up
286  NCML_ASSERT_MSG(pOutMap->length() == pProtoGridMap->length(),
287  "Expected the prototype and output maps to have same length() after transfer of constraints, but they were not so we can't copy the data!");
288 
289  // The dimensions will have been set up correctly now so length() is correct...
290  // We assume the pProtoGridMap matches at this point as well.
291  // So we can use this call to copy from one vector to the other
292  // so we don't use temp storage in between
293 #if PIPELINING
294  pProtoGridMap->serialize(eval, dds, m, ce_eval);
295 #else
296  pOutMap->reserve_value_capacity(); // reserves mem for length
297  pOutMap->set_value_slice_from_row_major_vector(*pProtoGridMap, 0);
298 #endif
299  pOutMap->set_read_p(true);
300  }
301 
302  // Set the cache bit.
303  set_read_p(true);
304 
305 #if PIPELINING
306  status = true;
307 #else
308  status = libdap::Grid::serialize(eval, dds, m, ce_eval);
309 #endif
310  }
311  else {
312  status = libdap::Grid::serialize(eval, dds, m, ce_eval);
313  }
314 
315  return status;
316 }
317 
320 
321 Grid*
323 {
324  return _pSubGridProto.get();
325 }
326 
327 void GridAggregationBase::duplicate(const GridAggregationBase& rhs)
328 {
329  _loader = DDSLoader(rhs._loader.getDHI());
330 
331  _pSubGridProto.reset((rhs._pSubGridProto.get()) ? (static_cast<Grid*>(rhs._pSubGridProto->ptr_duplicate())) : nullptr);
332 
333  _memberDatasets = rhs._memberDatasets;
334 }
335 
336 void GridAggregationBase::cleanup()
337 {
338  _loader.cleanup();
339 
340  _memberDatasets.clear();
341  _memberDatasets.resize(0);
342 }
343 
344 /* virtual */
346 {
347  // Transfers constraints to the proto grid and reads it
349 
350  // Copy the read-in, constrained maps from the proto grid
351  // into our output maps.
353 }
354 
355 /* static */
356 libdap::Grid*
357 GridAggregationBase::cloneSubGridProto(const libdap::Grid& proto)
358 {
359  return static_cast<Grid*>(const_cast<Grid&>(proto).ptr_duplicate());
360 }
361 
362 void GridAggregationBase::printConstraints(const Array& fromArray)
363 {
364  ostringstream oss;
365  AggregationUtil::printConstraints(oss, fromArray);
366  BESDEBUG("ncml:2", "Constraints for Grid: " << name() << ": " << oss.str() << endl);
367 }
368 
370 {
371  Grid* pSubGridTemplate = getSubGridTemplate();
372  VALID_PTR(pSubGridTemplate);
373 
374  // Call the specialized subclass constraint transfer method
375  transferConstraintsToSubGridHook(pSubGridTemplate);
376 
377  // Pass it the values for the aggregated grid...
378  pSubGridTemplate->set_send_p(send_p());
379  pSubGridTemplate->set_in_selection(is_in_selection());
380 
381  // Those settings will be used by read.
382  pSubGridTemplate->read();
383 
384  // For some reason, some handlers only set read_p for the parts, not the whole!!
385  pSubGridTemplate->set_read_p(true);
386 }
387 
389 {
390  Grid* pSubGridTemplate = getSubGridTemplate();
391  VALID_PTR(pSubGridTemplate);
392 
393  Map_iter mapIt;
394  Map_iter mapEndIt = map_end();
395  for (mapIt = map_begin(); mapIt != mapEndIt; ++mapIt) {
396  Array* pOutMap = static_cast<Array*>(*mapIt);
397  VALID_PTR(pOutMap);
398 
399  // If it isn't getting dumped, then don't bother with it
400  if (!(pOutMap->send_p() || pOutMap->is_in_selection())) {
401  continue;
402  }
403 
404  // We don't want to touch the aggregation dimension since it's
405  // handled specially.
406  if (pOutMap->name() == aggDim.name) {
407  // Make sure it's read with these constraints.
408  pOutMap->read();
409  continue;
410  }
411 
412  // Otherwise, find the map in the protogrid and copy it's data into this.
413  Array* pProtoGridMap = const_cast<Array*>(AggregationUtil::findMapByName(*pSubGridTemplate, pOutMap->name()));
414  NCML_ASSERT_MSG(pProtoGridMap, "Couldn't find map in prototype grid for map name=" + pOutMap->name());
415  BESDEBUG_FUNC(DEBUG_CHANNEL,
416  "About to call read() on prototype map vector name=" << pOutMap->name() << " and calling transfer constraints..." << endl);
417 
418  // Make sure the protogrid maps were properly read
419  NCML_ASSERT_MSG(pProtoGridMap->read_p(), "Expected the prototype map to have been read but it wasn't.");
420 
421  // Make sure the lengths match to be sure we're not gonna blow memory up
422  NCML_ASSERT_MSG(pOutMap->length() == pProtoGridMap->length(),
423  "Expected the prototype and output maps to have same length() "
424  "after transfer of constraints, but they were not so we can't "
425  "copy the data!");
426 
427  // The dimensions will have been set up correctly now so length() is correct...
428  // We assume the pProtoGridMap matches at this point as well.
429  // So we can use this call to copy from one vector to the other
430  // so we don't use temp storage in between
431  pOutMap->reserve_value_capacity(); // reserves mem for length
432  pOutMap->set_value_slice_from_row_major_vector(*pProtoGridMap, 0);
433  pOutMap->set_read_p(true);
434  }
435 }
436 
437 /* virtual */
439 {
440  THROW_NCML_INTERNAL_ERROR("Impl me!");
441 }
442 
443 }
static bool IsSet(const std::string &flagName)
see if the debug context flagName is set to true
Definition: BESDebug.h:168
virtual bool start(std::string name)
Definition: BESStopWatch.cc:67
static const libdap::Array * findMapByName(const libdap::Grid &inGrid, const std::string &findName)
static void printConstraints(std::ostream &os, const libdap::Array &fromArray)
BESDataHandlerInterface & getDHI() const
Definition: DDSLoader.h:119
void cleanup()
restore dhi to clean state
Definition: DDSLoader.cc:256
virtual const Dimension & getAggregationDimension() const =0
bool serialize(libdap::ConstraintEvaluator &eval, libdap::DDS &dds, libdap::Marshaller &m, bool ce_eval) override
virtual void readAndAggregateConstrainedMapsHook()
void copyProtoMapsIntoThisGrid(const Dimension &aggDim)
void setShapeFrom(const libdap::Grid &protoSubGrid, bool addMaps)
virtual const AMDList & getDatasetList() const
virtual void transferConstraintsToSubGridHook(Grid *pSubGrid)
Helper class for temporarily hijacking an existing dhi to load a DDX response for one particular file...