Line data Source code
1 : /*
2 : * DataAccumulator.h
3 : *
4 : * Created on: Jan 18, 2016
5 : * Author: nakazato
6 : */
7 :
8 : #ifndef SINGLEDISH_FILLER_DATAACCUMULATOR_H_
9 : #define SINGLEDISH_FILLER_DATAACCUMULATOR_H_
10 :
11 : #include <vector>
12 : #include <cassert>
13 : #include <memory>
14 : #include <algorithm>
15 : #include <numeric>
16 : #include <functional>
17 :
18 : #include <casacore/casa/BasicSL/String.h>
19 : #include <casacore/casa/Arrays/Vector.h>
20 : #include <casacore/casa/Arrays/Matrix.h>
21 : #include <casacore/casa/Arrays/ArrayMath.h>
22 : #include <casacore/casa/IO/ArrayIO.h>
23 : #include <casacore/casa/Containers/Record.h>
24 :
25 : #include <casacore/measures/Measures/Stokes.h>
26 :
27 : #include <casacore/tables/Tables/TableRecord.h>
28 : #include <singledishfiller/Filler/DataRecord.h>
29 : #include <singledishfiller/Filler/FillerUtil.h>
30 :
31 : using namespace casacore;
32 :
33 : namespace {
34 : template<class T>
35 56696 : inline void resizeTo(T &array, casacore::IPosition const &shape) {
36 56696 : if (array.shape() != shape) {
37 368 : array.resize(shape, false);
38 : }
39 56696 : }
40 :
41 : template<class T>
42 58476 : inline void setValue1(ssize_t n, T const *src, T *dst) {
43 225175292 : for (ssize_t i = 0; i < n; ++i) {
44 225116816 : dst[i] = src[i];
45 : }
46 58476 : }
47 :
48 : template<class T>
49 57906 : inline void setValueToMatrixColumn(casacore::Vector<T> const &src,
50 : ssize_t icolumn, casacore::Matrix<T> &dst) {
51 57906 : casacore::IPosition const &shape = dst.shape();
52 57906 : ssize_t const nrow = shape[0];
53 57906 : ssize_t const ncolumn = shape[1];
54 57906 : if (icolumn >= ncolumn) {
55 0 : throw casacore::AipsError("Specified column doesn't exist.");
56 : }
57 :
58 : casacore::Bool b1, b2;
59 57906 : T *dst_p = dst.getStorage(b1);
60 57906 : T *work_p = dst_p + icolumn * nrow;
61 57906 : T const *src_p = src.getStorage(b2);
62 :
63 57906 : setValue1(nrow, src_p, work_p);
64 :
65 57906 : src.freeStorage(src_p, b2);
66 57906 : dst.putStorage(dst_p, b1);
67 57906 : }
68 :
69 : template<class T, class Executor>
70 28838 : inline void shuffleTransposeMatrix(ssize_t n, size_t offset_src,
71 : casacore::Matrix<T> const &src, casacore::Matrix<T> &dst,
72 : std::vector<size_t> src_order = { }) {
73 28838 : if (offset_src > src.ncolumn() - 1)
74 0 : throw casacore::AipsError("offset too large");
75 : casacore::Bool b1, b2;
76 28838 : T const *src_p = src.getStorage(b1);
77 28838 : T *dst_p = dst.getStorage(b2);
78 28838 : T const *wsrc_p = src_p + offset_src * n;
79 28838 : T *wdst_p = dst_p;
80 :
81 28838 : Executor::execute(n, wsrc_p, wdst_p, src_order);
82 :
83 28838 : src.freeStorage(src_p, b1);
84 28838 : dst.putStorage(dst_p, b2);
85 28838 : }
86 :
87 0 : inline void getDefaultOrder(size_t const num_order,
88 : std::vector<size_t> &order) {
89 0 : order.resize(num_order);
90 0 : std::iota(order.begin(), order.end(), 0);
91 0 : }
92 :
93 56236 : inline bool setAndCheckOrder(size_t const required_size, size_t const max_value,
94 : std::vector<size_t> &order) {
95 56236 : if (order.size() == 0)
96 0 : getDefaultOrder(required_size, order);
97 :
98 56236 : if (order.size() < required_size) {
99 0 : return false;
100 : }
101 56236 : size_t max_idx = order[0];
102 112472 : for (size_t i = 1; i < order.size(); ++i) {
103 56236 : max_idx = std::max(max_idx, order[i]);
104 : }
105 56236 : return max_idx <= max_value;
106 : }
107 :
108 : struct ExecuteMatrix1 {
109 : template<class T>
110 570 : static void execute(ssize_t n, T const *src, T *dst,
111 : std::vector<size_t> /*in_order*/) {
112 570 : setValue1(n, src, dst);
113 570 : }
114 : };
115 :
116 : struct ExecuteMatrix2 {
117 : template<class T>
118 28268 : static void execute(ssize_t n, T const *src, T *dst,
119 : std::vector<size_t> src_order) {
120 : // need to set max_value=3 for 4 pol case
121 28268 : if (!setAndCheckOrder(2, 3, src_order))
122 0 : throw casacore::AipsError("got invalid order list");
123 28268 : T const *row0_p = src + src_order[0] * n;
124 28268 : T const *row1_p = src + src_order[1] * n;
125 112583996 : for (ssize_t i = 0; i < n; ++i) {
126 112555728 : dst[2 * i] = row0_p[i];
127 112555728 : dst[2 * i + 1] = row1_p[i];
128 : }
129 28268 : }
130 : };
131 :
132 : struct ExecuteMatrix4X {
133 : template<class T>
134 : static void execute(ssize_t /*n*/, T const */*src*/, T */*dst*/,
135 : std::vector<size_t> /*in_order*/) {
136 : throw std::runtime_error("");
137 : }
138 : };
139 :
140 : // polarization order assumption (auto, cross, cross, auto)
141 : template<>
142 0 : inline void ExecuteMatrix4X::execute(ssize_t n, casacore::Bool const *src,
143 : casacore::Bool *dst, std::vector<size_t> src_order) {
144 0 : if (!setAndCheckOrder(4, 3, src_order))
145 0 : throw casacore::AipsError("got invalid order list");
146 0 : casacore::Bool const *row0_p = src + src_order[0] * n;
147 0 : casacore::Bool const *row1_p = src + src_order[1] * n;
148 0 : casacore::Bool const *row2_p = src + src_order[2] * n;
149 0 : casacore::Bool const *row3_p = src + src_order[3] * n;
150 0 : for (ssize_t i = 0; i < n; ++i) {
151 0 : dst[4 * i + 0] = row0_p[i];
152 0 : casacore::Bool b = row1_p[i] || row2_p[i];
153 0 : dst[4 * i + 1] = b;
154 0 : dst[4 * i + 2] = b;
155 0 : dst[4 * i + 3] = row3_p[i];
156 : }
157 0 : }
158 :
159 : struct ExecuteMatrix4 {
160 : template<class T>
161 0 : static void execute(ssize_t n, T const *src, T *dst,
162 : std::vector<size_t> src_order) {
163 0 : if (!setAndCheckOrder(4, 3, src_order))
164 0 : throw casacore::AipsError("got invalid order list");
165 0 : T const *row0_p = src + src_order[0] * n;
166 0 : T const *row1_p = src + src_order[1] * n;
167 0 : T const *row2_p = src + src_order[2] * n;
168 0 : T const *row3_p = src + src_order[3] * n;
169 0 : for (ssize_t i = 0; i < n; ++i) {
170 0 : dst[4 * i + 0] = row0_p[i];
171 0 : dst[4 * i + 1] = row1_p[i];
172 0 : dst[4 * i + 2] = row2_p[i];
173 0 : dst[4 * i + 3] = row3_p[i];
174 : }
175 0 : }
176 : };
177 :
178 0 : inline void shuffleTransposeMatrix4F2C(ssize_t n,
179 : casacore::Matrix<casacore::Float> const &src,
180 : casacore::Matrix<casacore::Complex> &dst,
181 : std::vector<size_t> src_order = { }) {
182 0 : if (!setAndCheckOrder(4, 3, src_order))
183 0 : throw casacore::AipsError("got invalid order list");
184 : casacore::Bool b1, b2;
185 0 : casacore::Float const *src_p = src.getStorage(b1);
186 0 : casacore::Complex *dst_p = dst.getStorage(b2);
187 :
188 : // polarization order assumption (auto, cross, cross, auto)
189 0 : casacore::Float const *row0_p = src_p + src_order[0] * n;
190 0 : casacore::Float const *row1_p = src_p + src_order[1] * n;
191 0 : casacore::Float const *row2_p = src_p + src_order[2] * n;
192 0 : casacore::Float const *row3_p = src_p + src_order[3] * n;
193 0 : for (ssize_t i = 0; i < n; ++i) {
194 0 : dst_p[4 * i].real(row0_p[i]);
195 0 : dst_p[4 * i].imag(0.0f);
196 0 : casacore::Float fr = row1_p[i];
197 0 : casacore::Float fi = row2_p[i];
198 0 : dst_p[4 * i + 1].real(fr);
199 0 : dst_p[4 * i + 1].imag(fi);
200 0 : dst_p[4 * i + 2].real(fr);
201 0 : dst_p[4 * i + 2].imag(-fi);
202 0 : dst_p[4 * i + 3].real(row3_p[i]);
203 0 : dst_p[4 * i + 3].imag(0.0f);
204 : }
205 :
206 0 : src.freeStorage(src_p, b1);
207 0 : dst.putStorage(dst_p, b2);
208 0 : }
209 :
210 : ////// in-place reorder of MatrixColumn
211 : //template<class T>
212 : //inline void suffleMatrixColumn(casacore::Matrix<T> const &src,
213 : // std::vector<size_t> order) {
214 : // ssize_t n = src.nrow();
215 : // if (checkOrder(1, src.ncolumn()-1, order)) throw AipsError("Invalid order");
216 : // casacore::Matrix<T> const temp = src;
217 : // casacore::Bool b1, b2;
218 : // T const *src_p = src.getStorage(b1);
219 : // T const *temp_p = temp.getStorage(b2);
220 : // if (src_p==temp_p) {
221 : // throw casacore::AipsError("Failed to generate temp storage");
222 : // }
223 : // for (ssize_t j = 0; j < order.size(); ++j) {
224 : // setValue1(n, temp_p[order[j]*n], src_p[j*n]);
225 : // }
226 : // src.putStorage(src_p, b1);
227 : // temp.freeStorage(temp_p, b2);
228 : //}
229 :
230 : // get a vector of indices (out_idx) to sort in_data in accending order
231 : template<typename T>
232 29942 : inline void getSortIndex(casacore::Vector<T> in_data,
233 : std::vector<size_t> &out_idx) {
234 29942 : casacore::Vector<size_t> idx_vec(in_data.size());
235 29942 : casacore::indgen(idx_vec);
236 29942 : idx_vec.tovector(out_idx);
237 :
238 29942 : std::sort(out_idx.begin(), out_idx.end(),
239 14384 : [&in_data](size_t const &i, size_t const &j) -> bool {return in_data[i] < in_data[j];});
240 29942 : }
241 : }
242 :
243 : namespace casa { //# NAMESPACE CASA - BEGIN
244 : namespace sdfiller { //# NAMESPACE SDFILLER - BEGIN
245 :
246 : class DataAccumulator;
247 :
248 : class DataChunk {
249 : public:
250 : friend DataAccumulator;
251 :
252 92 : DataChunk(casacore::String const &poltype) :
253 92 : num_pol_max_(4), num_chan_(0), data_(), flag_(), flag_row_(
254 92 : num_pol_max_, false), tsys_(), tcal_(), weight_(
255 92 : num_pol_max_, 1.0f), sigma_(weight_), poltype_(poltype), valid_pcorr_(), pcorr_type_(), get_chunk_(
256 184 : nullptr), get_num_pol_(nullptr) {
257 : POST_START;
258 :
259 92 : setPolType(poltype);
260 :
261 : POST_END;
262 92 : }
263 :
264 184 : virtual ~DataChunk() {
265 184 : }
266 :
267 14174 : casacore::String getPolType() const {
268 14174 : return poltype_;
269 : }
270 :
271 : void resetPolType(casacore::String const &poltype) {
272 : initialize(num_chan_);
273 : setPolType(poltype);
274 : }
275 :
276 : casacore::uInt getNumPol() const {
277 : return get_num_pol_();
278 : }
279 :
280 14174 : void initialize(size_t num_chan) {
281 14174 : num_chan_ = num_chan;
282 14174 : casacore::IPosition const shape(2, num_chan_, num_pol_max_);
283 14174 : ::resizeTo(data_, shape);
284 14174 : ::resizeTo(flag_, shape);
285 14174 : ::resizeTo(tsys_, shape);
286 14174 : ::resizeTo(tcal_, shape);
287 14174 : pcorr_type_.resize(0);
288 14174 : tsys_ = -1.0f;
289 14174 : tcal_ = -1.0f;
290 14174 : }
291 :
292 29942 : void clear() {
293 29942 : num_chan_ = 0;
294 29942 : pcorr_type_.resize(0);
295 29942 : }
296 :
297 : bool readyToWrite() {
298 : return true;
299 : }
300 :
301 28158 : bool accumulate(DataRecord const &record) {
302 : POST_START;
303 :
304 28158 : if (!isValidRecord(record)) {
305 0 : return false;
306 : }
307 :
308 28158 : if (num_pol_max_ < pcorr_type_.size()) {
309 0 : return false;
310 : }
311 :
312 28158 : casacore::Vector<casacore::Float> const &data = record.data;
313 28158 : if (num_chan_ == 0) {
314 14174 : size_t num_chan = data.size();
315 14174 : initialize(num_chan);
316 : }
317 :
318 : // Polarization
319 28158 : casacore::Stokes::StokesTypes pcorr = record.pol;
320 : // check for consistency between pol_type and stokes
321 28158 : bool consistent = false;
322 140790 : for (size_t i = 0; i < valid_pcorr_.size(); ++i) {
323 112632 : consistent |= (valid_pcorr_[i] == (Int) pcorr);
324 : }
325 28158 : if (!consistent) {
326 0 : throw casacore::AipsError(
327 0 : "Got inconsistent polarization with poltype");
328 : }
329 : // check if pcorr is the new corr type to be added
330 42142 : for (size_t i = 0; i < pcorr_type_.size(); ++i) {
331 13984 : if (pcorr == pcorr_type_[i]) {
332 0 : throw casacore::AipsError(
333 : "DataChunk already has polarization "
334 0 : + casacore::Stokes::name(pcorr));
335 : }
336 : }
337 : // new polarization type
338 28158 : pcorr_type_.push_back(pcorr);
339 : // Matrices in DataRecord is stacked in the order of accumulation
340 28158 : size_t const colid = pcorr_type_.size() - 1;
341 :
342 28158 : casacore::Vector<casacore::Bool> const &flag = record.flag;
343 28158 : casacore::Bool flagrow = record.flag_row;
344 :
345 28158 : if (data.shape() != flag.shape()) {
346 0 : return false;
347 : }
348 :
349 28158 : casacore::Vector<casacore::Float> tsys;
350 28158 : if (!record.tsys.empty()) {
351 : // std::cout << "tsys is not empty: " << record.tsys[0] << std::endl;
352 28158 : tsys.assign(record.tsys);
353 : }
354 28158 : casacore::Vector<casacore::Float> tcal;
355 28158 : if (!record.tcal.empty()) {
356 : // std::cout << "tcal is not empty: " << record.tcal << std::endl;
357 990 : tcal.assign(record.tcal);
358 : }
359 :
360 28158 : if (data.nelements() != num_chan_) {
361 0 : return false;
362 : }
363 :
364 : // Filling Matrices[chan, pol] in DataRecord. The Matrices are column (channel) major
365 : //data_.column(colid) = data;
366 28158 : ::setValueToMatrixColumn(data, colid, data_);
367 : //flag_.column(colid) = flag;
368 28158 : ::setValueToMatrixColumn(flag, colid, flag_);
369 28158 : flag_row_[colid] = flagrow;
370 28158 : if (tsys.size() == num_chan_) {
371 : //tsys_.column(polid) = tsys;
372 600 : ::setValueToMatrixColumn(tsys, colid, tsys_);
373 27558 : } else if (!tsys.empty()) {
374 27558 : tsys_(0, colid) = tsys[0];
375 : }
376 28158 : if (tcal.size() == num_chan_) {
377 : //tcal_.column(polid) = tcal;
378 990 : ::setValueToMatrixColumn(tcal, colid, tcal_);
379 27168 : } else if (!tcal.empty()) {
380 0 : tcal_(0, colid) = tcal[0];
381 : }
382 :
383 28158 : return true;
384 28158 : }
385 :
386 29942 : bool get(MSDataRecord &record) {
387 29942 : bool return_value = get_chunk_(record);
388 29942 : return return_value;
389 : }
390 :
391 : private:
392 : // Functions to return if accumulated corr types are conformant set, e.g.,
393 : // liner polarization should have XX and YY for dual pol.
394 : // assumption: pcorr_type_ has StokesTypes consistent with poltype_ and
395 : // not redundant (assured by accumulate())
396 29942 : bool isFullPol() const {
397 29942 : return pcorr_type_.size() == 4;
398 : }
399 29942 : bool isDualPol() const {
400 29942 : if (pcorr_type_.size() != 2)
401 15958 : return false;
402 13984 : casacore::Int pcorr0 = valid_pcorr_[0];
403 13984 : casacore::Int pcorr1 = valid_pcorr_[valid_pcorr_.size() - 1];
404 14384 : return (pcorr_type_[0] == pcorr0 && pcorr_type_[1] == pcorr1)
405 14384 : || (pcorr_type_[0] == pcorr1 && pcorr_type_[1] == pcorr0);
406 : }
407 15958 : bool isSinglePol0() const {
408 15958 : return (pcorr_type_.size() == 1
409 15958 : && valid_pcorr_[0] == (Int) pcorr_type_[0]);
410 : }
411 15768 : bool isSinglePol1() const {
412 15768 : return (pcorr_type_.size() == 1
413 15768 : && valid_pcorr_[valid_pcorr_.size() - 1] == (Int) pcorr_type_[0]);
414 : }
415 28158 : bool isValidRecord(DataRecord const &record) {
416 28158 : return !record.data.empty() && !record.flag.empty();
417 : }
418 92 : void setPolType(casacore::String const &poltype) {
419 : POST_START;
420 :
421 92 : poltype_ = poltype;
422 92 : if (poltype_ == "linear") {
423 30034 : get_chunk_ = [&](MSDataRecord &r) {return DataChunk::getLinear(r);};
424 92 : get_num_pol_ = [&]() {return DataChunk::getNumPolLinear();};
425 92 : valid_pcorr_.resize(4);
426 92 : valid_pcorr_[0] = casacore::Stokes::XX;
427 92 : valid_pcorr_[1] = casacore::Stokes::XY;
428 92 : valid_pcorr_[2] = casacore::Stokes::YX;
429 92 : valid_pcorr_[3] = casacore::Stokes::YY;
430 0 : } else if (poltype_ == "circular") {
431 0 : get_chunk_ = [&](MSDataRecord &r) {return DataChunk::getCircular(r);};
432 0 : get_num_pol_ = [&]() {return DataChunk::getNumPolCircular();};
433 0 : valid_pcorr_.resize(4);
434 0 : valid_pcorr_[0] = casacore::Stokes::RR;
435 0 : valid_pcorr_[1] = casacore::Stokes::RL;
436 0 : valid_pcorr_[2] = casacore::Stokes::LR;
437 0 : valid_pcorr_[3] = casacore::Stokes::LL;
438 0 : } else if (poltype_ == "stokes") {
439 0 : get_chunk_ = [&](MSDataRecord &r) {return DataChunk::getStokes(r);};
440 0 : get_num_pol_ = [&]() {return DataChunk::getNumPolStokes();};
441 0 : valid_pcorr_.resize(4);
442 0 : valid_pcorr_[0] = casacore::Stokes::I;
443 0 : valid_pcorr_[1] = casacore::Stokes::Q;
444 0 : valid_pcorr_[2] = casacore::Stokes::U;
445 0 : valid_pcorr_[3] = casacore::Stokes::V;
446 0 : } else if (poltype_ == "linpol") {
447 0 : get_chunk_ = [&](MSDataRecord &r) {return DataChunk::getLinpol(r);};
448 0 : get_num_pol_ = [&]() {return DataChunk::getNumPolLinpol();};
449 0 : valid_pcorr_.resize(2);
450 0 : valid_pcorr_[0] = casacore::Stokes::Plinear;
451 0 : valid_pcorr_[1] = casacore::Stokes::Pangle;
452 : } else {
453 0 : throw casacore::AipsError(
454 0 : casacore::String("Invalid poltype") + poltype);
455 : }
456 :
457 : POST_END;
458 92 : }
459 : size_t const num_pol_max_;
460 : size_t num_chan_;
461 : casacore::Matrix<casacore::Float> data_;
462 : casacore::Matrix<casacore::Bool> flag_;
463 : casacore::Vector<casacore::Bool> flag_row_;
464 : casacore::Matrix<casacore::Float> tsys_;
465 : casacore::Matrix<casacore::Float> tcal_;
466 : casacore::Vector<casacore::Float> weight_;
467 : casacore::Vector<casacore::Float> sigma_;
468 :
469 : casacore::String poltype_;
470 : casacore::Vector<casacore::Int> valid_pcorr_;
471 : std::vector<casacore::Stokes::StokesTypes> pcorr_type_;
472 : std::function<bool(MSDataRecord &)> get_chunk_;
473 : std::function<casacore::uInt()> get_num_pol_;
474 :
475 : // Tsys and Tcal assignment for 2 & 4 pols. Auto-correlation components of pol should be used.
476 13984 : void setTsys2(MSDataRecord &record, std::vector<size_t> order = { }) {
477 : // safeguard to avoid processing empty array
478 13984 : if (tsys_.empty()) {
479 0 : return;
480 : }
481 :
482 13984 : assert(tsys_.ncolumn() == num_pol_max_);
483 13984 : if (!setAndCheckOrder(2, tsys_.ncolumn() - 1, order)) {
484 0 : throw casacore::AipsError("got invalid order list");
485 : }
486 13984 : size_t apol0 = order[0], apol1 = order[order.size() - 1];
487 :
488 : // clear Tsys
489 13984 : record.setTsysSize(0, 0);
490 :
491 13984 : if (num_chan_ == 1) {
492 200 : record.setTsysSize(2, 1);
493 200 : record.tsys(0, 0) = tsys_(0, apol0);
494 200 : record.tsys(1, 0) = tsys_(0, apol1);
495 13784 : } else if (casacore::anyGT(tsys_, 0.0f)) {
496 : // valid spectral Tsys data are available
497 10376 : casacore::IPosition const startpos0(2, 1, apol0);
498 10376 : casacore::IPosition const endpos0(2, num_chan_ - 1, apol0);
499 10376 : casacore::IPosition const startpos1(2, 1, apol1);
500 10376 : casacore::IPosition const endpos1(2, num_chan_ - 1, apol1);
501 10376 : if (casacore::anyGT(tsys_(startpos0, endpos0), 0.0f)
502 10376 : || casacore::anyGT(tsys_(startpos1, endpos1), 0.0f)) {
503 : // spectral Tsys
504 100 : record.setTsysSize(2, num_chan_);
505 200 : shuffleTransposeMatrix<casacore::Float, ExecuteMatrix2>(
506 100 : num_chan_, 0, tsys_, record.tsys, { apol0, apol1 });
507 : } else {
508 : // valid Tsys data are only for channel 0
509 : // so treat as scalar Tsys
510 10276 : record.setTsysSize(2, 1);
511 10276 : record.tsys(0, 0) = tsys_(0, apol0);
512 10276 : record.tsys(1, 0) = tsys_(0, apol1);
513 : }
514 10376 : }
515 : }
516 :
517 13984 : void setTcal2(MSDataRecord &record, std::vector<size_t> order = { }) {
518 : // safeguard to avoid processing empty array
519 13984 : if (tcal_.empty()) {
520 0 : return;
521 : }
522 :
523 13984 : assert(tcal_.ncolumn() == num_pol_max_);
524 13984 : if (!setAndCheckOrder(2, tcal_.ncolumn()-1, order)) {
525 0 : throw casacore::AipsError("got invalid order list");
526 : }
527 13984 : size_t apol0 = order[0], apol1 = order[order.size() - 1];
528 :
529 : // clear Tcal
530 13984 : record.setTcalSize(0, 0);
531 :
532 13984 : if (num_chan_ == 1) {
533 200 : record.setTcalSize(2, 1);
534 200 : record.tcal(0, 0) = tcal_(0, apol0);
535 200 : record.tcal(1, 0) = tcal_(0, apol1);
536 13784 : } else if (casacore::anyGT(tcal_, 0.0f)) {
537 : // valid spectral Tcal data are available
538 200 : casacore::IPosition const startpos0(2, 1, apol0);
539 200 : casacore::IPosition const endpos0(2, num_chan_ - 1, apol0);
540 200 : casacore::IPosition const startpos1(2, 1, apol1);
541 200 : casacore::IPosition const endpos1(2, num_chan_ - 1, apol1);
542 200 : if (casacore::anyGT(tcal_(startpos0, endpos0), 0.0f)
543 200 : || casacore::anyGT(tcal_(startpos1, endpos1), 0.0f)) {
544 : // spectral Tcal
545 200 : record.setTcalSize(2, num_chan_);
546 400 : shuffleTransposeMatrix<casacore::Float, ExecuteMatrix2>(
547 200 : num_chan_, 0, tcal_, record.tcal, { apol0, apol1 });
548 : } else {
549 : // valid Tcal data are only for channel 0
550 : // so treat as scalar Tcal
551 0 : record.setTcalSize(2, 1);
552 0 : record.tcal(0, 0) = tcal_(0, apol0);
553 0 : record.tcal(1, 0) = tcal_(0, apol1);
554 : }
555 200 : }
556 : }
557 :
558 190 : void setTsys1(MSDataRecord &record, ssize_t start_src) {
559 190 : if (tsys_.empty()) {
560 : // no Tsys data. do nothing.
561 0 : return;
562 190 : } else if (num_chan_ == 1) {
563 0 : record.setTsysSize(1, 1);
564 0 : record.tsys(0, 0) = tsys_(0, start_src);
565 190 : } else if (casacore::anyGT(tsys_, 0.0f)) {
566 190 : casacore::IPosition const startpos(2, 1, start_src);
567 190 : casacore::IPosition const endpos(2, num_chan_ - 1, start_src);
568 190 : if (casacore::anyGT(tsys_(startpos, endpos), 0.0f)) {
569 : // should be spectral Tsys
570 0 : record.setTsysSize(1, num_chan_);
571 0 : shuffleTransposeMatrix<casacore::Float, ExecuteMatrix1>(num_chan_,
572 0 : start_src, tsys_, record.tsys);
573 : } else {
574 : // valid Tsys data are only for channel 0
575 : // so treat as scalar Tsys
576 190 : record.setTsysSize(1, 1);
577 190 : record.tsys(0, 0) = tsys_(0, start_src);
578 : }
579 190 : }
580 : }
581 :
582 190 : void setTcal1(MSDataRecord &record, ssize_t start_src) {
583 190 : if (tcal_.empty()) {
584 : // no Tcal data. do nothing.
585 0 : return;
586 190 : } else if (num_chan_ == 1) {
587 0 : record.setTcalSize(1, 1);
588 0 : record.tcal(0, 0) = tcal_(0, start_src);
589 190 : } else if (casacore::anyGT(tcal_, 0.0f)) {
590 190 : casacore::IPosition const startpos(2, 1, start_src);
591 190 : casacore::IPosition const endpos(2, num_chan_ - 1, start_src);
592 190 : if (casacore::anyGT(tcal_(startpos, endpos), 0.0f)) {
593 : // should be spectral Tcal
594 190 : record.setTcalSize(1, num_chan_);
595 380 : shuffleTransposeMatrix<casacore::Float, ExecuteMatrix1>(num_chan_,
596 190 : start_src, tcal_, record.tcal);
597 : } else {
598 : // valid Tcal data are only for channel 0
599 : // so treat as scalar Tcal
600 0 : record.setTcalSize(1, 1);
601 0 : record.tcal(0, 0) = tcal_(0, start_src);
602 : }
603 190 : }
604 : }
605 :
606 29942 : bool getLinear(MSDataRecord &record) {
607 : POST_START;
608 :
609 29942 : casacore::Vector<casacore::Int> const col_stokes(pcorr_type_);
610 29942 : std::vector<size_t> pol_idx_order;
611 29942 : getSortIndex(col_stokes, pol_idx_order);
612 :
613 29942 : casacore::Vector<casacore::Float> weight;
614 29942 : casacore::Vector<casacore::Float> sigma;
615 29942 : if (isFullPol()) {
616 : // POL 0, 1, 2, and 3
617 : // std::cout << "set data/flag" << std::endl;
618 0 : record.setComplex();
619 0 : record.setDataSize(4, num_chan_);
620 0 : shuffleTransposeMatrix4F2C(num_chan_, data_, record.complex_data,
621 : pol_idx_order);
622 0 : shuffleTransposeMatrix<casacore::Bool, ExecuteMatrix4X>(num_chan_,
623 0 : 0, flag_, record.flag, pol_idx_order);
624 0 : record.flag_row = anyEQ(flag_row_, true);
625 : // std::cout << "weight = " << record.weight << std::endl;
626 :
627 : // std::cout << "set tsys" << std::endl;
628 0 : setTsys2(record, pol_idx_order);
629 :
630 : // std::cout << "set tcal " << tcal_ << std::endl;
631 0 : setTcal2(record, pol_idx_order);
632 :
633 0 : record.num_pol = 4;
634 29942 : } else if (isDualPol()) {
635 : // POL 0 and 1
636 : // std::cout << "set data/flag" << std::endl;
637 13984 : record.setFloat();
638 13984 : record.setDataSize(2, num_chan_);
639 27968 : shuffleTransposeMatrix<casacore::Float, ExecuteMatrix2>(num_chan_,
640 13984 : 0, data_, record.float_data, pol_idx_order);
641 27968 : shuffleTransposeMatrix<casacore::Bool, ExecuteMatrix2>(num_chan_, 0,
642 13984 : flag_, record.flag, pol_idx_order);
643 13984 : record.flag_row = flag_row_[0] || flag_row_[1];
644 : // std::cout << "weight = " << record.weight << std::endl;
645 :
646 : // std::cout << "set tsys" << std::endl;
647 13984 : setTsys2(record, pol_idx_order);
648 :
649 : // std::cout << "set tcal " << tcal_ << std::endl;
650 13984 : setTcal2(record, pol_idx_order);
651 :
652 13984 : record.num_pol = 2;
653 15958 : } else if (isSinglePol0()) {
654 : // only POL 0
655 : // std::cout << "set data/flag (pol 0)" << std::endl;
656 190 : record.setFloat();
657 190 : record.setDataSize(1, num_chan_);
658 380 : shuffleTransposeMatrix<casacore::Float, ExecuteMatrix1>(num_chan_,
659 190 : 0, data_, record.float_data, pol_idx_order);
660 380 : shuffleTransposeMatrix<casacore::Bool, ExecuteMatrix1>(num_chan_, 0,
661 190 : flag_, record.flag, pol_idx_order);
662 190 : record.flag_row = flag_row_(0);
663 :
664 190 : setTsys1(record, 0);
665 :
666 : // std::cout << "set tcal " << tcal_ << std::endl;
667 190 : setTcal1(record, 0);
668 :
669 190 : record.num_pol = 1;
670 15768 : } else if (isSinglePol1()) {
671 : // only POL 1
672 : // std::cout << "set data/flag (pol 1)" << std::endl;
673 0 : record.setFloat();
674 0 : record.setDataSize(1, num_chan_);
675 0 : shuffleTransposeMatrix<casacore::Float, ExecuteMatrix1>(num_chan_,
676 0 : 0, data_, record.float_data, pol_idx_order);
677 0 : shuffleTransposeMatrix<casacore::Bool, ExecuteMatrix1>(num_chan_, 0,
678 0 : flag_, record.flag, pol_idx_order);
679 0 : record.flag_row = flag_row_(1);
680 :
681 0 : setTsys1(record, 0);
682 :
683 : // std::cout << "set tcal " << tcal_ << std::endl;
684 0 : setTcal1(record, 0);
685 :
686 0 : record.num_pol = 1;
687 15768 : } else if (pcorr_type_.size() == 0) {
688 : // std::cout << "DataChunk is not ready for get" << std::endl;
689 15768 : return false;
690 : } else {// means strange combination of polarization types in DataChunk
691 0 : throw AipsError("non-conforming combination of polarization accumulated");
692 : }
693 42332 : for (casacore::Int i = 0; i < record.num_pol; ++i) {
694 28158 : record.corr_type[i] = col_stokes[pol_idx_order[i]];
695 : }
696 :
697 : POST_END;
698 14174 : return true;
699 29942 : }
700 :
701 0 : bool getCircular(MSDataRecord &record) {
702 0 : return getLinear(record);
703 : }
704 :
705 0 : bool getStokes(MSDataRecord &record) {
706 : POST_START;
707 :
708 0 : casacore::Vector<casacore::Int> const col_stokes(pcorr_type_);
709 0 : std::vector<size_t> pol_idx_order;
710 0 : getSortIndex(col_stokes, pol_idx_order);
711 :
712 0 : record.setFloat();
713 0 : if (isFullPol()) {
714 0 : record.setDataSize(4, num_chan_);
715 0 : shuffleTransposeMatrix<casacore::Float, ExecuteMatrix4>(num_chan_,
716 0 : 0, data_, record.float_data, pol_idx_order);
717 0 : shuffleTransposeMatrix<casacore::Bool, ExecuteMatrix4>(num_chan_, 0,
718 0 : flag_, record.flag, pol_idx_order);
719 0 : record.flag_row = anyTrue(flag_row_);
720 :
721 0 : record.num_pol = 4;
722 0 : } else if (isSinglePol0()) {
723 0 : record.setDataSize(1, num_chan_);
724 0 : shuffleTransposeMatrix<casacore::Float, ExecuteMatrix1>(num_chan_,
725 0 : 0, data_, record.float_data, pol_idx_order);
726 0 : shuffleTransposeMatrix<casacore::Bool, ExecuteMatrix1>(num_chan_, 0,
727 0 : flag_, record.flag, pol_idx_order);
728 0 : record.flag_row = flag_row_[0];
729 :
730 0 : record.num_pol = 1;
731 0 : } else if (pcorr_type_.size() == 0) {
732 0 : return false;
733 : } else {// means strange combination of polarization types in DataChunk
734 0 : throw AipsError("non-conforming combination of polarization accumulated");
735 : }
736 0 : for (casacore::Int i = 0; i < record.num_pol; ++i) {
737 0 : record.corr_type[i] = col_stokes[pol_idx_order[i]];
738 : }
739 :
740 : POST_END;
741 0 : return true;
742 0 : }
743 :
744 0 : bool getLinpol(MSDataRecord &record) {
745 : POST_START;
746 :
747 0 : casacore::Vector<casacore::Int> const col_stokes(pcorr_type_);
748 0 : std::vector<size_t> pol_idx_order;
749 0 : getSortIndex(col_stokes, pol_idx_order);
750 :
751 0 : record.setFloat();
752 0 : if (isDualPol()) {
753 : // POL 0 and 1
754 0 : record.setDataSize(2, num_chan_);
755 0 : shuffleTransposeMatrix<casacore::Float, ExecuteMatrix2>(num_chan_,
756 0 : 0, data_, record.float_data, pol_idx_order);
757 0 : shuffleTransposeMatrix<casacore::Bool, ExecuteMatrix2>(num_chan_, 0,
758 0 : flag_, record.flag, pol_idx_order);
759 0 : record.flag_row = flag_row_[0] || flag_row_[1];
760 0 : record.num_pol = 2;
761 0 : } else if (isSinglePol0()) {
762 0 : record.setDataSize(1, num_chan_);
763 0 : shuffleTransposeMatrix<casacore::Float, ExecuteMatrix1>(num_chan_,
764 0 : 0, data_, record.float_data, pol_idx_order);
765 0 : shuffleTransposeMatrix<casacore::Bool, ExecuteMatrix1>(num_chan_, 0,
766 0 : flag_, record.flag, pol_idx_order);
767 0 : record.flag_row = flag_row_[0];
768 :
769 0 : record.num_pol = 1;
770 0 : } else if (pcorr_type_.size() == 0) {
771 0 : return false;
772 : } else {// means strange combination of polarization types in DataChunk
773 0 : throw AipsError("non-conforming combination of polarization accumulated");
774 : }
775 0 : for (casacore::Int i = 0; i < record.num_pol; ++i) {
776 0 : record.corr_type[i] = col_stokes[pol_idx_order[i]];
777 : }
778 :
779 : POST_END;
780 0 : return true;
781 0 : }
782 :
783 0 : casacore::uInt getNumPolLinear() const {
784 0 : if (isFullPol()) {
785 0 : return 4;
786 0 : } else if (isDualPol()) {
787 0 : return 2;
788 0 : } else if (isSinglePol0() || isSinglePol1()) {
789 0 : return 1;
790 : } else {
791 0 : return 0;
792 : }
793 : }
794 :
795 0 : casacore::uInt getNumPolCircular() const {
796 0 : return getNumPolLinear();
797 : }
798 :
799 0 : casacore::uInt getNumPolStokes() const {
800 0 : if (isFullPol()) {
801 0 : return 4;
802 0 : } else if (isSinglePol0()) {
803 0 : return 1;
804 : } else {
805 0 : return 0;
806 : }
807 : }
808 :
809 0 : casacore::uInt getNumPolLinpol() const {
810 0 : if (isDualPol()) {
811 0 : return 2;
812 0 : } else if (isSinglePol0()) {
813 0 : return 1;
814 : } else {
815 0 : return 0;
816 : }
817 : }
818 : };
819 :
820 : class DataAccumulator {
821 : private:
822 : struct DataAccumulatorKey {
823 : casacore::Int antenna_id;
824 : casacore::Int field_id;
825 : casacore::Int feed_id;
826 : casacore::Int spw_id;
827 : casacore::String pol_type;
828 : casacore::String intent;
829 :
830 : template<class T, class C>
831 623786 : bool comp(T const &a, T const &b, C const &c) const {
832 623786 : if (a < b) {
833 56685 : return true;
834 567101 : } else if (a == b) {
835 532744 : return c();
836 : } else {
837 34357 : return false;
838 : }
839 : }
840 :
841 147174 : bool operator()(DataAccumulatorKey const &lhs,
842 : DataAccumulatorKey const &rhs) const {
843 294348 : return comp(lhs.antenna_id, rhs.antenna_id,
844 102942 : [&]() {return comp(lhs.field_id, rhs.field_id,
845 102942 : [&]() {return comp(lhs.feed_id, rhs.feed_id,
846 102942 : [&]() {return comp(lhs.spw_id, rhs.spw_id,
847 83893 : [&]() {return comp(lhs.pol_type, rhs.pol_type,
848 560505 : [&]() {return comp(lhs.intent, rhs.intent,
849 1303704 : []() {return false;});});});});});});
850 : }
851 : };
852 :
853 : public:
854 7 : DataAccumulator() :
855 14 : pool_(), antenna_id_(), spw_id_(), field_id_(), feed_id_(), scan_(), subscan_(), intent_(), direction_(), interval_(), indexer_(), time_(
856 21 : -1.0), is_free_() {
857 7 : }
858 :
859 7 : virtual ~DataAccumulator() {
860 : POST_START;
861 :
862 99 : for (auto iter = pool_.begin(); iter != pool_.end(); ++iter) {
863 92 : delete *iter;
864 : }
865 :
866 : POST_END;
867 7 : }
868 :
869 2078 : size_t getNumberOfChunks() const {
870 2078 : return pool_.size();
871 : }
872 :
873 : size_t getNumberOfActiveChunks() const {
874 : return std::count_if(pool_.begin(), pool_.end(),
875 : [](DataChunk * const &c) {
876 : return c->getNumPol() > 0;
877 : });
878 : }
879 :
880 : bool queryForGet(DataRecord const &record) const {
881 : casacore::Double const time = record.time;
882 : bool is_ready = (0.0 <= time_) && !(time_ == time);
883 : return is_ready;
884 : }
885 :
886 28158 : bool queryForGet(casacore::Double const &time) const {
887 28158 : bool is_ready = (0.0 <= time_) && !(time_ == time);
888 28158 : return is_ready;
889 : }
890 :
891 2078 : void clear() {
892 32020 : for (auto iter = pool_.begin(); iter != pool_.end(); ++iter) {
893 29942 : (*iter)->clear();
894 : }
895 2078 : time_ = -1.0;
896 2078 : }
897 :
898 29942 : bool get(size_t ichunk, MSDataRecord &record) {
899 : POST_START;
900 :
901 29942 : if (pool_.size() == 0) {
902 0 : return false;
903 29942 : } else if (ichunk >= pool_.size()) {
904 0 : return false;
905 : }
906 29942 : bool status = pool_[ichunk]->get(record);
907 : // std::cout << "get Chunk status = " << status << std::endl;
908 29942 : if (!status) {
909 15768 : record.clear();
910 15768 : return status;
911 : }
912 14174 : record.time = time_;
913 14174 : record.pol_type = pool_[ichunk]->getPolType();
914 14174 : record.antenna_id = antenna_id_[ichunk];
915 14174 : record.spw_id = spw_id_[ichunk];
916 14174 : record.field_id = field_id_[ichunk];
917 14174 : record.feed_id = feed_id_[ichunk];
918 14174 : record.scan = scan_[ichunk];
919 14174 : record.subscan = subscan_[ichunk];
920 14174 : record.intent = intent_[ichunk];
921 14174 : record.direction = direction_[ichunk];
922 14174 : record.interval = interval_[ichunk];
923 14174 : record.temperature = temperature_[ichunk];
924 14174 : record.pressure = pressure_[ichunk];
925 14174 : record.rel_humidity = rel_humidity_[ichunk];
926 14174 : record.wind_speed = wind_speed_[ichunk];
927 14174 : record.wind_direction = wind_direction_[ichunk];
928 :
929 : POST_END;
930 14174 : return status;
931 : }
932 :
933 28158 : bool accumulate(DataRecord const &record) {
934 : POST_START;
935 :
936 28158 : if (!isValidRecord(record)) {
937 : // std::cout << "record is not a valid one" << std::endl;
938 0 : return false;
939 : }
940 :
941 28158 : casacore::Double time = record.time;
942 28158 : if (time_ < 0.0) {
943 2078 : time_ = time;
944 : }
945 28158 : if (time_ != time) {
946 : // std::cout << "timestamp mismatch" << std::endl;
947 0 : return false;
948 : }
949 28158 : casacore::Int antennaid = record.antenna_id;
950 28158 : casacore::Int spwid = record.spw_id;
951 28158 : casacore::Int fieldid = record.field_id;
952 28158 : casacore::Int feedid = record.feed_id;
953 28158 : casacore::Int scan = record.scan;
954 28158 : casacore::Int subscan = record.subscan;
955 28158 : casacore::String intent = record.intent;
956 28158 : casacore::String poltype = record.pol_type;
957 28158 : DataAccumulatorKey key;
958 28158 : key.antenna_id = record.antenna_id;
959 28158 : key.field_id = record.field_id;
960 28158 : key.feed_id = record.feed_id;
961 28158 : key.spw_id = record.spw_id;
962 28158 : key.intent = record.intent;
963 28158 : key.pol_type = record.pol_type;
964 28158 : casacore::Matrix<casacore::Double> const &direction = record.direction;
965 28158 : casacore::Double interval = record.interval;
966 28158 : casacore::Float temperature = record.temperature;
967 28158 : casacore::Float pressure = record.pressure;
968 28158 : casacore::Float rel_humidity = record.rel_humidity;
969 28158 : casacore::Float wind_speed = record.wind_speed;
970 28158 : casacore::Float wind_direction = record.wind_direction;
971 28158 : bool status = false;
972 28158 : auto iter = indexer_.find(key);
973 : // std::cout << "(ant, spw, pol, pol_type, field, feed, intent) = ("
974 : // << key.antenna_id << ", " << key.spw_id << ", " << record.pol<< ", " << key.pol_type << ", " << key.field_id << ", " << key.feed_id
975 : // << ", " << key.intent << ", "
976 : // << std::endl;
977 28158 : if (iter != indexer_.end()) {
978 28066 : casacore::uInt index = iter->second;
979 28066 : status = pool_[index]->accumulate(record);
980 28066 : if (status) {
981 28066 : antenna_id_[index] = antennaid;
982 28066 : spw_id_[index] = spwid;
983 28066 : field_id_[index] = fieldid;
984 28066 : feed_id_[index] = feedid;
985 28066 : scan_[index] = scan;
986 28066 : subscan_[index] = subscan;
987 28066 : intent_[index] = intent;
988 28066 : direction_[index].assign(direction);
989 28066 : interval_[index] = interval;
990 28066 : temperature_[index] = temperature;
991 28066 : pressure_[index] = pressure;
992 28066 : rel_humidity_[index] = rel_humidity;
993 28066 : wind_speed_[index] = wind_speed;
994 28066 : wind_direction_[index] = wind_direction;
995 : }
996 : } else {
997 92 : pool_.push_back(new DataChunk(poltype));
998 92 : antenna_id_.push_back(-1);
999 92 : spw_id_.push_back(-1);
1000 92 : field_id_.push_back(-1);
1001 92 : feed_id_.push_back(-1);
1002 92 : scan_.push_back(-1);
1003 92 : subscan_.push_back(-1);
1004 92 : intent_.push_back("");
1005 92 : direction_.push_back(casacore::Matrix<casacore::Double>());
1006 92 : interval_.push_back(-1.0);
1007 92 : temperature_.push_back(0.0f);
1008 92 : pressure_.push_back(0.0f);
1009 92 : rel_humidity_.push_back(0.0f);
1010 92 : wind_speed_.push_back(0.0f);
1011 92 : wind_direction_.push_back(0.0f);
1012 92 : casacore::uInt index = pool_.size() - 1;
1013 92 : indexer_[key] = index;
1014 92 : status = pool_[index]->accumulate(record);
1015 92 : if (status) {
1016 92 : antenna_id_[index] = antennaid;
1017 92 : spw_id_[index] = spwid;
1018 92 : field_id_[index] = fieldid;
1019 92 : feed_id_[index] = feedid;
1020 92 : scan_[index] = scan;
1021 92 : subscan_[index] = subscan;
1022 92 : intent_[index] = intent;
1023 92 : direction_[index].assign(direction);
1024 92 : interval_[index] = interval;
1025 92 : temperature_[index] = temperature;
1026 92 : pressure_[index] = pressure;
1027 92 : rel_humidity_[index] = rel_humidity;
1028 92 : wind_speed_[index] = wind_speed;
1029 92 : wind_direction_[index] = wind_direction;
1030 : }
1031 : }
1032 :
1033 : // std::cout << "status = " << status << std::endl;
1034 : // std::cout << "key (a" << key.antenna_id << ",f" << key.field_id << ",s"
1035 : // << key.spw_id << ",i" << key.intent << ",p" << key.pol_type << ",d"
1036 : // << key.feed_id << "(index " << indexer_[key] << "): TIME="
1037 : // << time_ << " INTERVAL=" << interval << " polno=" << record.pol << std::endl;
1038 : POST_END;
1039 28158 : return status;
1040 28158 : }
1041 :
1042 : casacore::String getPolType(size_t ichunk) const {
1043 : assert(ichunk < pool_.size());
1044 : return pool_[ichunk]->getPolType();
1045 : }
1046 :
1047 : casacore::uInt getNumPol(size_t ichunk) const {
1048 : assert(ichunk < pool_.size());
1049 : return pool_[ichunk]->getNumPol();
1050 : }
1051 :
1052 : private:
1053 28158 : bool isValidRecord(DataRecord const &record) {
1054 : // std::cout << record.time << " " << record.interval << " "
1055 : // << record.antenna_id << " " << record.field_id << " " << record.feed_id
1056 : // << " " << record.spw_id << " " << record.scan << " " << record.subscan
1057 : // << " " << record.direction << std::endl;
1058 28158 : return record.time > 0.0 && record.interval > 0.0
1059 28158 : && record.antenna_id >= 0 && record.field_id >= 0
1060 28158 : && record.feed_id >= 0 && record.spw_id >= 0 && record.scan >= 0
1061 56316 : && record.subscan >= 0 && !record.direction.empty();
1062 : }
1063 :
1064 : std::vector<DataChunk *> pool_;
1065 : std::vector<casacore::Int> antenna_id_;
1066 : std::vector<casacore::Int> spw_id_;
1067 : std::vector<casacore::Int> field_id_;
1068 : std::vector<casacore::Int> feed_id_;
1069 : std::vector<casacore::Int> scan_;
1070 : std::vector<casacore::Int> subscan_;
1071 : std::vector<casacore::String> intent_;
1072 : std::vector<casacore::Matrix<casacore::Double> > direction_;
1073 : std::vector<casacore::Double> interval_;
1074 : std::vector<casacore::Float> temperature_;
1075 : std::vector<casacore::Float> pressure_;
1076 : std::vector<casacore::Float> rel_humidity_;
1077 : std::vector<casacore::Float> wind_speed_;
1078 : std::vector<casacore::Float> wind_direction_;
1079 : std::map<DataAccumulatorKey, casacore::uInt, DataAccumulatorKey> indexer_;
1080 : casacore::Double time_;
1081 : std::vector<bool> is_free_;
1082 : };
1083 :
1084 : } //# NAMESPACE SDFILLER - END
1085 : } //# NAMESPACE CASA - END
1086 :
1087 : #endif /* SINGLEDISH_FILLER_DATAACCUMULATOR_H_ */
|