//# ArrayColumnBase.cc: Base classfor access to an array table column //# Copyright (C) 2013 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library is distributed in the hope that it will be useful, but WITHOUT //# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or //# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: ArrayColumn.tcc 21354 2013-05-27 09:47:34Z gervandiepen $ #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN ArrayColumnBase::ArrayColumnBase() : TableColumn () {} ArrayColumnBase::ArrayColumnBase (const Table& tab, const String& columnName) : TableColumn (tab, columnName) {} ArrayColumnBase::ArrayColumnBase (const TableColumn& column) : TableColumn (column) {} ArrayColumnBase::ArrayColumnBase (const ArrayColumnBase& that) : TableColumn (that) {} void ArrayColumnBase::reference (const ArrayColumnBase& that) { TableColumn::reference (that); } ArrayColumnBase::~ArrayColumnBase() {} Bool ArrayColumnBase::checkShape (const IPosition& expShape, const IPosition& arrShape, Bool noSlicing, Int64 rownr, const String& where) const { if (! expShape.isEqual (arrShape)) { if (noSlicing && canChangeShape_p) { return False; } String msg(where); if (rownr >= 0) { msg += " for row " + String::toString(rownr); } throw TableArrayConformanceError(msg + " in column " + baseColPtr_p->columnDesc().name(), arrShape, expShape); } return True; } void ArrayColumnBase::adaptShape (const IPosition& shp, ArrayBase& arr, Bool resize, Int64 rownr, const String& where) const { if (! shp.isEqual (arr.shape())) { if (resize || arr.nelements() == 0) { arr.resize (shp); } else { checkShape (shp, arr.shape(), False, rownr, where); } } } void ArrayColumnBase::acbGet (rownr_t rownr, ArrayBase& arr, Bool resize) const { TABLECOLUMNCHECKROW(rownr); // Check array conformance and resize if needed and possible. adaptShape (shape(rownr), arr, resize, rownr, "ArrayColumn::get"); baseColPtr_p->getArray (rownr, arr); } void ArrayColumnBase::acbGetSlice (rownr_t rownr, const Slicer& arraySection, ArrayBase& arr, Bool resize) const { TABLECOLUMNCHECKROW(rownr); // Check array conformance and resize if needed and possible. IPosition arrayShape (shape(rownr)); IPosition blc,trc,inc; IPosition shp = arraySection.inferShapeFromSource (arrayShape, blc, trc, inc); adaptShape (shp, arr, resize, rownr, "ArrayColumn::getSlice"); // Get the slice. //# Creating a Slicer is somewhat expensive, so use the slicer //# itself if it contains no undefined values. if (arraySection.isFixed()) { baseColPtr_p->getSlice (rownr, arraySection, arr); } else { baseColPtr_p->getSlice (rownr, Slicer(blc, trc, inc, Slicer::endIsLast), arr); } } void ArrayColumnBase::acbGetSlice (rownr_t rownr, const Vector >& arraySlices, ArrayBase& arr, Bool resize) const { TABLECOLUMNCHECKROW(rownr); // Use shape of row. IPosition colShp = shape(rownr); // Check the slices. Make a copy because a Slice is inserted if empty. Vector > slices(arraySlices); Slicer slicer; IPosition shp = Slice::checkSlices (slices, slicer, colShp); // Check array conformance and resize if needed and possible. adaptShape (shp, arr, resize, rownr, "ArrayColumn::getSlice"); // Now loop through all the slices and fill the array in parts. GetCellSlices functor(*this, rownr); handleSlices (slices, functor, slicer, arr); } void ArrayColumnBase::acbGetColumn (ArrayBase& arr, Bool resize) const { rownr_t nrrow = nrow(); //# Take shape of array in first row. IPosition shp; if (nrrow > 0) { shp = shape(0); } //# Total shape is array shape plus nr of table rows. shp.append (IPosition(1,nrrow)); // Check array conformance and resize if needed and possible. adaptShape (shp, arr, resize, -1, "ArrayColumn::getColumn"); if (!arr.empty()) { //# Get the column. baseColPtr_p->getArrayColumn (arr); } } void ArrayColumnBase::acbGetColumn (const Slicer& arraySection, ArrayBase& arr, Bool resize) const { rownr_t nrrow = nrow(); //# Use shape of array in first row. IPosition shp, blc,trc,inc; if (nrrow > 0) { shp = arraySection.inferShapeFromSource (shape(0), blc, trc, inc); } //# Total shape is slice shape plus nr of table rows. shp.append (IPosition(1,nrrow)); // Check array conformance and resize if needed and possible. adaptShape (shp, arr, resize, -1, "ArrayColumn::getColumn"); if (!arr.empty()) { //# Get the column slice. Slicer defSlicer (blc, trc, inc, Slicer::endIsLast); baseColPtr_p->getColumnSlice (defSlicer, arr); } } void ArrayColumnBase::acbGetColumn (const Vector >& arraySlices, ArrayBase& arr, Bool resize) const { rownr_t nrrow = nrow(); // Get total shape. // Use shape of first row (if there) as overall array shape. IPosition colShp; if (nrrow > 0) { colShp = shape(0); } Vector > slices(arraySlices); Slicer slicer; IPosition shp = Slice::checkSlices (slices, slicer, colShp); // Total shape is slice shape plus nr of table rows. shp.append (IPosition(1,nrrow)); // Check array conformance and resize if needed and possible. adaptShape (shp, arr, resize, -1, "ArrayColumn::getColumn"); // Now loop through all the slices and fill the array in parts. GetColumnSlices functor(*this); handleSlices (slices, functor, slicer, arr); } void ArrayColumnBase::acbGetColumnRange (const Slicer& rowRange, ArrayBase& arr, Bool resize) const { rownr_t nrrow = nrow(); IPosition shp, blc, trc, inc; shp = rowRange.inferShapeFromSource (IPosition(1,nrrow), blc, trc, inc); //# If the entire column is accessed, use that function. if (blc(0) == 0 && shp(0) == Int(nrrow) && inc(0) == 1) { acbGetColumn (arr, resize); } else { acbGetColumnCells (RefRows(blc(0), trc(0), inc(0)), arr, resize); } } void ArrayColumnBase::acbGetColumnCells (const RefRows& rownrs, ArrayBase& arr, Bool resize) const { rownr_t nrrow = rownrs.nrow(); //# Take shape of array in first row. IPosition arrshp; if (nrrow > 0) { arrshp = shape(rownrs.firstRow()); } //# Total shape is array shape plus nr of table rows. arrshp.append (IPosition(1,nrrow)); // Check array conformance and resize if needed and possible. adaptShape (arrshp, arr, resize, -1, "ArrayColumn::getColumnCells"); baseColPtr_p->getArrayColumnCells (rownrs, arr); } void ArrayColumnBase::acbGetColumnRange (const Slicer& rowRange, const Slicer& arraySection, ArrayBase& arr, Bool resize) const { rownr_t nrrow = nrow(); IPosition shp, blc, trc, inc; shp = rowRange.inferShapeFromSource (IPosition(1,nrrow), blc, trc, inc); //# If the entire column is accessed, use that function. if (blc(0) == 0 && shp(0) == Int(nrrow) && inc(0) == 1) { acbGetColumn (arraySection, arr, resize); } else { acbGetColumnCells (RefRows(blc(0), trc(0), inc(0)), arraySection, arr, resize); } } void ArrayColumnBase::acbGetColumnCells (const RefRows& rownrs, const Slicer& arraySection, ArrayBase& arr, Bool resize) const { rownr_t nrrow = rownrs.nrow(); IPosition arrshp, arrblc, arrtrc, arrinc; if (nrrow > 0) { arrshp = arraySection.inferShapeFromSource (shape(rownrs.firstRow()), arrblc, arrtrc, arrinc); } //# Total shape is slice shape plus nr of table rows. arrshp.append (IPosition(1,nrrow)); // Check array conformance and resize if needed and possible. adaptShape (arrshp, arr, resize, -1, "ArrayColumn::getColumnCells"); if (!arr.empty()) { //# Get the column slice. Slicer defSlicer (arrblc, arrtrc, arrinc, Slicer::endIsLast); baseColPtr_p->getColumnSliceCells (rownrs, defSlicer, arr); } } void ArrayColumnBase::setShape (rownr_t rownr, const IPosition& shape) { checkWritable(); TABLECOLUMNCHECKROW(rownr); //# Set shape if not defined yet or if changed (if possible). //# Throw exception if already defined with a different shape. if (canChangeShape_p || !isDefined(rownr)) { baseColPtr_p->setShape (rownr, shape); } else { if (! shape.isEqual (baseColPtr_p->shape (rownr))) { throw TableInvOper ("ArrayColumn::setShape; shape cannot be changed for row " + String::toString(rownr) + " in column " + baseColPtr_p->columnDesc().name()); } } } void ArrayColumnBase::setShape (rownr_t rownr, const IPosition& shape, const IPosition& tileShape) { checkWritable(); TABLECOLUMNCHECKROW(rownr); //# Only set shape if not defined yet. //# Throw exception if already defined with a different shape. if (canChangeShape_p || !isDefined(rownr)) { baseColPtr_p->setShape (rownr, shape, tileShape); } else { if (! shape.isEqual (baseColPtr_p->shape (rownr))) { throw TableInvOper ("ArrayColumn::setShape; shape cannot be changed for row " + String::toString(rownr) + " column " + baseColPtr_p->columnDesc().name()); } } } void ArrayColumnBase::acbPut (rownr_t rownr, const ArrayBase& arr) { checkWritable(); TABLECOLUMNCHECKROW(rownr); //# Define the shape if not defined yet. //# If defined, check if shape conforms. if (!isDefined(rownr)) { baseColPtr_p->setShape (rownr, arr.shape()); }else{ if (! checkShape (baseColPtr_p->shape(rownr), arr.shape(), True, rownr, "ArrayColumn::put")) { baseColPtr_p->setShape (rownr, arr.shape()); } } baseColPtr_p->putArray (rownr, arr); } void ArrayColumnBase::acbPutSlice (rownr_t rownr, const Slicer& arraySection, const ArrayBase& arr) { checkWritable(); TABLECOLUMNCHECKROW(rownr); //# Check the array conformance. IPosition arrayShape (shape(rownr)); IPosition blc,trc,inc; IPosition shp = arraySection.inferShapeFromSource (arrayShape, blc,trc,inc); checkShape (shp, arr.shape(), False, rownr, "ArrayColumn::putSlice"); //# Put the slice. baseColPtr_p->putSlice (rownr, arraySection, arr); } void ArrayColumnBase::acbPutSlice (rownr_t rownr, const Vector >& arraySlices, const ArrayBase& arr) { checkWritable(); TABLECOLUMNCHECKROW(rownr); // Use shape of the row. IPosition colShp = shape(rownr); Vector > slices(arraySlices); Slicer slicer; IPosition shp = Slice::checkSlices (slices, slicer, colShp); checkShape (shp, arr.shape(), False, rownr, "ArrayColumn::putSlice(slices)"); // Now loop through all the slices and fill the array in parts. PutCellSlices functor(*this, rownr); handleSlices (slices, functor, slicer, arr); } void ArrayColumnBase::acbPutColumn (const ArrayBase& arr) { checkWritable(); //# First check if number of rows matches. rownr_t nrrow = nrow(); IPosition shp = arr.shape(); uInt last = shp.nelements() - 1; if (shp[last] != Int(nrrow)) { throw TableArrayConformanceError ("ArrayColumn::putColumn - column " + baseColPtr_p->columnDesc().name() + " has " + String::toString(nrrow) + ", array has " + String::toString(shp[last]) + " rows"); } //# Remove #rows from shape to get the shape of each cell. shp.resize (last); //# If the array is fixed shape, check if the shape matches. if ((columnDesc().options() & ColumnDesc::FixedShape) == ColumnDesc::FixedShape) { checkShape (shapeColumn(), shp, False, -1, "ArrayColumn::putColumn"); } else { //# Otherwise set the shape of each cell (as far as needed). for (rownr_t i=0; iputArrayColumn (arr); } void ArrayColumnBase::acbPutColumn (const Slicer& arraySection, const ArrayBase& arr) { checkWritable(); rownr_t nrrow = nrow(); //# First check if number of rows matches. IPosition arrshp = arr.shape(); uInt last = arrshp.nelements() - 1; if (arrshp(last) != Int(nrrow)) { throw TableArrayConformanceError ("ArrayColumn::putColumn(slicer) - column " + baseColPtr_p->columnDesc().name() + " has " + String::toString(nrrow) + ", but array has " + String::toString(arrshp[last]) + " rows"); } //# If the array is fixed shape, check if the shape matches. if ((columnDesc().options() & ColumnDesc::FixedShape) == ColumnDesc::FixedShape) { //# Remove #rows from shape to get the shape of each cell. arrshp.resize (last); IPosition blc,trc,inc; IPosition shp = arraySection.inferShapeFromSource (shapeColumn(), blc,trc,inc); checkShape (shp, arrshp, False, -1, "ArrayColumn::putColumn(slicer)"); } //# Put the column slice. baseColPtr_p->putColumnSlice (arraySection, arr); } void ArrayColumnBase::acbPutColumn (const Vector >& arraySlices, const ArrayBase& arr) { checkWritable(); rownr_t nrrow = nrow(); // Get total shape. // Use shape of first row (if there) as overall array shape. IPosition colShp; if (nrrow > 0) { colShp = shape(0); } Vector > slices(arraySlices); Slicer slicer; IPosition shp = Slice::checkSlices (slices, slicer, colShp); // Total shape is slice shape plus nr of table rows. shp.append (IPosition(1,nrrow)); // Check array conformance. if (! shp.isEqual (arr.shape())) { throw (TableArrayConformanceError ("ArrayColumn::putColumn for column " + baseColPtr_p->columnDesc().name())); } // Now loop through all the slices and fill the array in parts. PutColumnSlices functor(*this); handleSlices (slices, functor, slicer, arr); } void ArrayColumnBase::acbPutColumnRange (const Slicer& rowRange, const ArrayBase& arr) { rownr_t nrrow = nrow(); IPosition shp, blc, trc, inc; shp = rowRange.inferShapeFromSource (IPosition(1,nrrow), blc, trc, inc); //# If the entire column is accessed, use that function. if (blc(0) == 0 && shp(0) == Int(nrrow) && inc(0) == 1) { acbPutColumn (arr); } else { acbPutColumnCells (RefRows(blc(0), trc(0), inc(0)), arr); } } void ArrayColumnBase::acbPutColumnCells (const RefRows& rownrs, const ArrayBase& arr) { checkWritable(); //# First check if number of rows matches. rownr_t nrrow = rownrs.nrow(); IPosition arrshp = arr.shape(); uInt last = arrshp.nelements() - 1; if (arrshp(last) != Int(nrrow)) { throw (TableArrayConformanceError ("ArrayColumn::putColumnCells for column " + baseColPtr_p->columnDesc().name())); } //# Remove #rows from shape to get the shape of each cell. arrshp.resize (last); //# If the array is fixed shape, check if the shape matches. if ((columnDesc().options() & ColumnDesc::FixedShape) == ColumnDesc::FixedShape) { if (! arrshp.isEqual (shapeColumn())) { throw (TableArrayConformanceError ("ArrayColumn::putColumnCells for column " + baseColPtr_p->columnDesc().name())); } } else { //# Otherwise set the shape of each cell (as far as needed). RefRowsSliceIter iter(rownrs); while (! iter.pastEnd()) { rownr_t rownr = iter.sliceStart(); rownr_t end = iter.sliceEnd(); rownr_t incr = iter.sliceIncr(); while (rownr <= end) { setShape (rownr, arrshp); rownr += incr; } iter++; } } //# Put the entire array. baseColPtr_p->putArrayColumnCells (rownrs, arr); } void ArrayColumnBase::acbPutColumnRange (const Slicer& rowRange, const Slicer& arraySection, const ArrayBase& arr) { rownr_t nrrow = nrow(); IPosition shp, blc, trc, inc; shp = rowRange.inferShapeFromSource (IPosition(1,nrrow), blc, trc, inc); //# If the entire column is accessed, use that function. if (blc(0) == 0 && shp(0) == Int(nrrow) && inc(0) == 1) { acbPutColumn (arraySection, arr); } else { acbPutColumnCells (RefRows(blc(0), trc(0), inc(0)), arraySection, arr); } } void ArrayColumnBase::acbPutColumnCells (const RefRows& rownrs, const Slicer& arraySection, const ArrayBase& arr) { checkWritable(); //# First check if number of rows matches. rownr_t nrrow = rownrs.nrow(); IPosition arrshp = arr.shape(); uInt last = arrshp.nelements() - 1; if (arrshp(last) != Int(nrrow)) { throw (TableArrayConformanceError ("ArrayColumn::putColumnCells for column " + baseColPtr_p->columnDesc().name())); } //# If the array is fixed shape, check if the shape matches. if ((columnDesc().options() & ColumnDesc::FixedShape) == ColumnDesc::FixedShape) { //# Remove #rows from shape to get the shape of each cell. arrshp.resize (last); IPosition arrshp2,arrblc,arrtrc,arrinc; arrshp2 = arraySection.inferShapeFromSource (shapeColumn(), arrblc, arrtrc, arrinc); if (! arrshp.isEqual(arrshp2)) { throw (TableArrayConformanceError ("ArrayColumn::putColumnCells for column " + baseColPtr_p->columnDesc().name())); } } //# Put the entire array. baseColPtr_p->putColumnSliceCells (rownrs, arraySection, arr); } void ArrayColumnBase::acbPutColumnCells (const RefRows& rows, const Vector >& arraySlices, const ArrayBase& source) { checkWritable(); // Check if the nr of rows in the array matches. if (Int64(rows.nrows()) != source.shape()[source.ndim()-1]) { throw TableArrayConformanceError("ArrayColumn::putColumnCells - number of " "rows in RefRows and Array mismatches"); } // Put the destination array one row at a time. RefRowsSliceIter rowIter(rows); std::unique_ptr arrIter = source.makeIterator (source.ndim()-1); while (! rowIter.pastEnd()) { for (rownr_t rownr = rowIter.sliceStart(); rownr <= rowIter.sliceEnd(); rownr += rowIter.sliceIncr()) { acbPutSlice (rownr, arraySlices, arrIter->getArray()); arrIter->next(); } // Go to next slice of rows. rowIter++; } } void ArrayColumnBase::acbGetColumnCells (const RefRows& rows, const ColumnSlicer& columnSlicer, ArrayBase& destination, Bool resize) const { // Calculate the shape of the destination data. This will be // [s1, s2, ..., nR] where sI are the sum of the slice elements for // that axis as contained in arraySlices [i]. nR is the number of rows // in the RefRows object rows. Resize the destination array to match. const Vector& dataSlicers = columnSlicer.getDataSlicers(); const Vector& destSlicers = columnSlicer.getDestinationSlicers(); IPosition destShape (columnSlicer.shape()); destShape.append (IPosition (1, rows.nrows())); adaptShape (destShape, destination, resize, -1, "ArrayColumn::getColumnCells (rows, columnSlicer, ...)"); // Fill the destination array one row at a time. RefRowsSliceIter rowIter(rows); std::unique_ptr arrIter = destination.makeIterator (destination.ndim()-1); while (! rowIter.pastEnd()) { for (rownr_t rownr = rowIter.sliceStart(); rownr <= rowIter.sliceEnd(); rownr += rowIter.sliceIncr()) { ArrayBase& destArray = arrIter->getArray(); // Iterate through the slicers. for (uInt j=0; j destPart = destArray.getSection (*destSlicers[j]); baseGetSlice (rownr, *dataSlicers[j], *destPart); } arrIter->next(); } // Go to next slice or rows. rowIter++; } } void ArrayColumnBase::acbPutColumnCells (const RefRows& rows, const ColumnSlicer& columnSlicer, const ArrayBase& source) { // Calculate the shape of the source data. This will be // [s1, s2, ..., nR] where sI are the sum of the slice elements for // that axis as contained in arraySlices [i]. nR is the number of rows // in the RefRows object rows. const Vector& dataSlicers = columnSlicer.getDataSlicers(); const Vector& destSlicers = columnSlicer.getDestinationSlicers(); IPosition destShape (columnSlicer.shape()); destShape.append (IPosition (1, rows.nrows())); checkShape (destShape, source.shape(), False, -1, "ArrayColumn::putColumnCells (rows, columnSlicer, ...)"); // Fill the source array one row at a time. RefRowsSliceIter rowIter(rows); std::unique_ptr arrIter = source.makeIterator (source.ndim()-1); while (! rowIter.pastEnd()) { for (rownr_t rownr = rowIter.sliceStart(); rownr <= rowIter.sliceEnd(); rownr += rowIter.sliceIncr()) { ArrayBase& destArray = arrIter->getArray(); // Iterate through the slicers. for (uInt j=0; j destPart = destArray.getSection (*destSlicers[j]); basePutSlice (rownr, *dataSlicers[j], *destPart); } arrIter->next(); } // Go to next slice or rows. rowIter++; } } //# This is a very simple implementation. //# However, it does not need to be more fancy, since an array operation //# is already much more expensive than the virtual function calls //# involved in each loop iteration. void ArrayColumnBase::acbFillColumn (const ArrayBase& value) { rownr_t nrrow = nrow(); for (rownr_t i=0; icolumnDesc().name() + " (from column " + that.baseColPtr_p->columnDesc().name() + ')'); } for (rownr_t i=0; i >& slices, BaseSlicesFunctor& functor, const Slicer& slicer, const ArrayBase& arr) const { // Set start and end position in Array for first Slice. IPosition arrStart (arr.ndim(), 0); IPosition arrEnd (slicer.length() - 1); if (arrStart.size() > arrEnd.size()) { arrEnd.append (IPosition(1, nrow()-1)); // for get/putColumn funcs } IPosition colStart (slicer.start()); IPosition colLen (slicer.length()); IPosition colIncr (slicer.stride()); uInt nrdim = slicer.ndim(); IPosition pos(nrdim, 0); while (True) { CountedPtr refArr (arr.getSection (Slicer(arrStart, arrEnd, Slicer::endIsLast))); functor.apply (Slicer(colStart, colLen, colIncr), *refArr); uInt i; for (i=0; i& dataSlicers, const Vector& destinationSlicers) : dataSlicers_p (dataSlicers), destinationSlicers_p (destinationSlicers), shape_p (shape) { String message = validateParameters (); if (! message.empty()){ freeSlicers(); // Call gave them to us; set them free. throw AipsError ("ColumnSlicer (ctor):: " + message); } } ColumnSlicer::~ColumnSlicer () { freeSlicers(); } void ColumnSlicer::freeSlicers () { // The two Vectors contain pointers to objects so they need to be freed. // They should have the same length normally, but during validation it's // possible that they have different lengths. for (uInt i = 0; i < dataSlicers_p.size(); i++){ delete dataSlicers_p [i]; } for (uInt i = 0; i < destinationSlicers_p.size(); i++){ delete destinationSlicers_p [i]; } } String ColumnSlicer::validateParameters() const { // Validate the contruction parameters to see if they are consistent. if (dataSlicers_p.size() != destinationSlicers_p.size()){ return String::format ("Number of data slicers (%d) and destination slicers (%d) " "must match", dataSlicers_p.size(), destinationSlicers_p.size()); } if (dataSlicers_p.size() == 0){ return String::format ("At least one destination and one data slicer required."); } for (uInt i = 0; i < dataSlicers_p.size(); i++) { if (dataSlicers_p[i]->length() != destinationSlicers_p[i]->length()){ return String::format ("Length of data slicer[%d] (%s) and " "destination slicer [%d] (%s) must be equal", i, dataSlicers_p[i]->length().toString().c_str(), i, destinationSlicers_p[i]->length().toString().c_str()); } } return String(); } } //# NAMESPACE CASACORE - END