bes Updated for version 3.21.1
The Backend Server (BES) is the lower two tiers of the Hyrax data server
GridAggregationBase.cc
1
2// 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
44using libdap::Array;
45using libdap::BaseType;
46using libdap::Grid;
47
48using libdap::D4Group;
49using libdap::Constructor;
50using libdap::InternalErr;
51using libdap::D4Maps;
52using libdap::D4Map;
53
54// Local debug flags
55#define DEBUG_CHANNEL "agg_util"
56#define prolog std::string("GridAggregationBase::").append(__func__).append("() - ")
57
58namespace agg_util {
59GridAggregationBase::GridAggregationBase(const libdap::Grid& proto, const AMDList& memberDatasets, const DDSLoader& loaderProto) :
60 Grid(proto), _loader(loaderProto.getDHI()), _pSubGridProto(cloneSubGridProto(proto)), _memberDatasets(memberDatasets)
61{
62}
63
64GridAggregationBase::GridAggregationBase(const string& name, const AMDList& memberDatasets, const DDSLoader& loaderProto) :
65 Grid(name), _loader(loaderProto.getDHI()), _memberDatasets(memberDatasets)
66{
67}
68
69GridAggregationBase::GridAggregationBase(const GridAggregationBase& proto) :
70 Grid(proto), _loader(proto._loader.getDHI())
71{
72 duplicate(proto);
73}
74
75/* virtual */
76GridAggregationBase::~GridAggregationBase()
77{
78 cleanup();
79}
80
82GridAggregationBase::operator=(const GridAggregationBase& rhs)
83{
84 if (this != &rhs) {
85 cleanup();
86 Grid::operator=(rhs);
87 duplicate(rhs);
88 }
89 return *this;
90}
91
92
93void
94GridAggregationBase::transform_to_dap4(D4Group *root, Constructor *container)
95{
96 Grid::transform_to_dap4(root,container);
97
98#if 0 // I removed this method because I think the parent class implementation should work correctly.
99 BaseType *btp = array_var()->transform_to_dap4(root, container);
100 Array *coverage = static_cast<Array*>(btp);
101 if (!coverage) throw InternalErr(__FILE__, __LINE__, "Expected an Array while transforming a Grid (coverage)");
102
103 coverage->set_parent(container);
104
105 // Next find the maps; add them to the coverage and to the container,
106 // the latter only on the condition that they are not already there.
107
108 for (Map_iter i = map_begin(), e = map_end(); i != e; ++i) {
109 btp = (*i)->transform_to_dap4(root, container);
110 Array *map = static_cast<Array*>(btp);
111 if (!map) throw InternalErr(__FILE__, __LINE__, "Expected an Array while transforming a Grid (map)");
112
113 // map must be non-null (Grids cannot contain Grids in DAP2)
114 if (map) {
115 // Only add the map/array if it not already present; given the scoping rules
116 // for DAP2 and the assumption the DDS is valid, testing for the same name
117 // is good enough.
118 if (!root->var(map->name())) {
119 map->set_parent(container);
120 container->add_var_nocopy(map); // this adds the array to the container
121 }
122 D4Map *dap4_map = new D4Map(map->name(), map, coverage); // bind the 'map' to the coverage
123 coverage->maps()->add_map(dap4_map); // bind the coverage to the map
124 }
125 else {
126 throw InternalErr(__FILE__, __LINE__,
127 "transform_to_dap4() returned a null value where there can be no Grid.");
128 }
129 }
130
131 container->add_var_nocopy(coverage);
132#endif
133}
134
135void GridAggregationBase::setShapeFrom(const libdap::Grid& constProtoSubGrid, bool addMaps)
136{
137 // calls used are semantically const, but not syntactically.
138 Grid& protoSubGrid = const_cast<Grid&>(constProtoSubGrid);
139
140 // Save a clone of the template for read() to use.
141 // We always use these maps...
142 _pSubGridProto = unique_ptr<Grid>(cloneSubGridProto(protoSubGrid));
143
144 // Pass in the data array and maps from the proto by hand.
145 Array* pDataArrayTemplate = protoSubGrid.get_array();
146 VALID_PTR(pDataArrayTemplate);
147 set_array(static_cast<Array*>(pDataArrayTemplate->ptr_duplicate()));
148
149 // Now the maps in order if asked
150 if (addMaps) {
151 Grid::Map_iter endIt = protoSubGrid.map_end();
152 for (Grid::Map_iter it = protoSubGrid.map_begin(); it != endIt; ++it) {
153 // have to case, the iter is for some reason BaseType*
154 Array* pMap = dynamic_cast<Array*>(*it);
155 VALID_PTR(pMap);
156 add_map(pMap, true); // add as a copy
157 }
158 }
159}
160
161/* virtual */
162const AMDList&
164{
165 return _memberDatasets;
166}
167
168/* virtual */
170{
171 BESDEBUG_FUNC(DEBUG_CHANNEL, prolog << "Function entered..." << endl);
172
173 if (read_p()) {
174 BESDEBUG_FUNC(DEBUG_CHANNEL, prolog << "read_p() set, early exit!");
175 return true;
176 }
177
178 // Call the subclass hook methods to do this work properly
180
181 // Now make the read call on the data array.
182 // The aggregation subclass will do the right thing.
183 Array* pAggArray = get_array();
184 VALID_PTR(pAggArray);
185
186 // Only do this portion if the array part is supposed to serialize!
187 if (pAggArray->send_p() || pAggArray->is_in_selection()) {
188 pAggArray->read();
189 }
190
191 // Set the cache bit.
192 set_read_p(true);
193 return true;
194}
195
196#define PIPELINING 1
197
218bool
219GridAggregationBase::serialize(libdap::ConstraintEvaluator &eval, libdap::DDS &dds, libdap::Marshaller &m,
220 bool ce_eval)
221{
222 BES_STOPWATCH_START(DEBUG_CHANNEL, prolog + "Timer");
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, prolog << "Calling read() on prototype map vector name=" << pOutMap->name() << " and calling transfer constraints..." << endl);
280
281 // Make sure the protogrid maps were properly read
282 NCML_ASSERT_MSG(pProtoGridMap->read_p(), "Expected the prototype map to have been read but it wasn't.");
283
284 // Make sure the lengths match to be sure we're not gonna blow memory up
285 NCML_ASSERT_MSG(pOutMap->length() == pProtoGridMap->length(),
286 "Expected the prototype and output maps to have same size() after transfer of constraints, but they were not so we can't copy the data!");
287
288 // The dimensions will have been set up correctly now so size() is correct...
289 // We assume the pProtoGridMap matches at this point as well.
290 // So we can use this call to copy from one vector to the other
291 // so we don't use temp storage in between
292#if PIPELINING
293 pProtoGridMap->serialize(eval, dds, m, ce_eval);
294#else
295 pOutMap->reserve_value_capacity(); // reserves mem for length
296 pOutMap->set_value_slice_from_row_major_vector(*pProtoGridMap, 0);
297#endif
298 pOutMap->set_read_p(true);
299 }
300
301 // Set the cache bit.
302 set_read_p(true);
303
304#if PIPELINING
305 status = true;
306#else
307 status = libdap::Grid::serialize(eval, dds, m, ce_eval);
308#endif
309 }
310 else {
311 status = libdap::Grid::serialize(eval, dds, m, ce_eval);
312 }
313
314 return status;
315}
316
319
320Grid*
322{
323 return _pSubGridProto.get();
324}
325
326void GridAggregationBase::duplicate(const GridAggregationBase& rhs)
327{
328 _loader = DDSLoader(rhs._loader.getDHI());
329
330 _pSubGridProto.reset((rhs._pSubGridProto.get()) ? (static_cast<Grid*>(rhs._pSubGridProto->ptr_duplicate())) : nullptr);
331
332 _memberDatasets = rhs._memberDatasets;
333}
334
335void GridAggregationBase::cleanup()
336{
337 _loader.cleanup();
338
339 _memberDatasets.clear();
340 _memberDatasets.resize(0);
341}
342
343/* virtual */
345{
346 // Transfers constraints to the proto grid and reads it
348
349 // Copy the read-in, constrained maps from the proto grid
350 // into our output maps.
352}
353
354/* static */
355libdap::Grid*
356GridAggregationBase::cloneSubGridProto(const libdap::Grid& proto)
357{
358 return static_cast<Grid*>(const_cast<Grid&>(proto).ptr_duplicate());
359}
360
361void GridAggregationBase::printConstraints(const Array& fromArray)
362{
363 ostringstream oss;
364 AggregationUtil::printConstraints(oss, fromArray);
365 BESDEBUG("ncml:2", prolog << "Constraints for Grid: " << name() << ": " << oss.str() << endl);
366}
367
369{
370 Grid* pSubGridTemplate = getSubGridTemplate();
371 VALID_PTR(pSubGridTemplate);
372
373 // Call the specialized subclass constraint transfer method
374 transferConstraintsToSubGridHook(pSubGridTemplate);
375
376 // Pass it the values for the aggregated grid...
377 pSubGridTemplate->set_send_p(send_p());
378 pSubGridTemplate->set_in_selection(is_in_selection());
379
380 // Those settings will be used by read.
381 pSubGridTemplate->read();
382
383 // For some reason, some handlers only set read_p for the parts, not the whole!!
384 pSubGridTemplate->set_read_p(true);
385}
386
388{
389 Grid* pSubGridTemplate = getSubGridTemplate();
390 VALID_PTR(pSubGridTemplate);
391
392 Map_iter mapIt;
393 Map_iter mapEndIt = map_end();
394 for (mapIt = map_begin(); mapIt != mapEndIt; ++mapIt) {
395 Array* pOutMap = static_cast<Array*>(*mapIt);
396 VALID_PTR(pOutMap);
397
398 // If it isn't getting dumped, then don't bother with it
399 if (!(pOutMap->send_p() || pOutMap->is_in_selection())) {
400 continue;
401 }
402
403 // We don't want to touch the aggregation dimension since it's
404 // handled specially.
405 if (pOutMap->name() == aggDim.name) {
406 // Make sure it's read with these constraints.
407 pOutMap->read();
408 continue;
409 }
410
411 // Otherwise, find the map in the protogrid and copy it's data into this.
412 Array* pProtoGridMap = const_cast<Array*>(AggregationUtil::findMapByName(*pSubGridTemplate, pOutMap->name()));
413 NCML_ASSERT_MSG(pProtoGridMap, "Couldn't find map in prototype grid for map name=" + pOutMap->name());
414 BESDEBUG_FUNC(DEBUG_CHANNEL, prolog << "Calling read() on prototype map vector name=" << pOutMap->name() << " and calling transfer constraints..." << endl);
415
416 // Make sure the protogrid maps were properly read
417 NCML_ASSERT_MSG(pProtoGridMap->read_p(), "Expected the prototype map to have been read but it wasn't.");
418
419 // Make sure the lengths match to be sure we're not gonna blow memory up
420 NCML_ASSERT_MSG(pOutMap->length() == pProtoGridMap->length(),
421 "Expected the prototype and output maps to have same size() "
422 "after transfer of constraints, but they were not so we can't "
423 "copy the data!");
424
425 // The dimensions will have been set up correctly now so size() is correct...
426 // We assume the pProtoGridMap matches at this point as well.
427 // So we can use this call to copy from one vector to the other
428 // so we don't use temp storage in between
429 pOutMap->reserve_value_capacity(); // reserves mem for length
430 pOutMap->set_value_slice_from_row_major_vector(*pProtoGridMap, 0);
431 pOutMap->set_read_p(true);
432 }
433}
434
435/* virtual */
437{
438 THROW_NCML_INTERNAL_ERROR("Impl me!");
439}
440
441}
static const libdap::Array * findMapByName(const libdap::Grid &inGrid, const std::string &findName)
static void printConstraints(std::ostream &os, const libdap::Array &fromArray)
void cleanup()
restore dhi to clean state
Definition DDSLoader.cc:252
BESDataHandlerInterface & getDHI() const
Definition DDSLoader.h:119
virtual const Dimension & getAggregationDimension() const =0
bool serialize(libdap::ConstraintEvaluator &eval, libdap::DDS &dds, libdap::Marshaller &m, bool ce_eval) override
void copyProtoMapsIntoThisGrid(const Dimension &aggDim)
void setShapeFrom(const libdap::Grid &protoSubGrid, bool addMaps)
virtual const AMDList & getDatasetList() const
virtual void transferConstraintsToSubGridHook(Grid *pSubGrid)
STL class.
STL class.
Helper class for temporarily hijacking an existing dhi to load a DDX response for one particular file...