casacore
Loading...
Searching...
No Matches
SiscoStManColumn.h
Go to the documentation of this file.
1#ifndef CASACORE_SISCO_ST_MAN_COLUMN_H_
2#define CASACORE_SISCO_ST_MAN_COLUMN_H_
3
4#include <casacore/tables/DataMan/StManColumn.h>
5
6#include <casacore/casa/Arrays/Array.h>
7#include <casacore/casa/Arrays/IPosition.h>
8
9#include <casacore/tables/Tables/ScalarColumn.h>
10#include <casacore/tables/AlternateMans/StokesIConversions.h>
11
12#include "SiscoReader.h"
13#include "SiscoStoreMode.h"
14#include "SiscoWriter.h"
15#include "ShapesFileReader.h"
16#include "ShapesFileWriter.h"
17
18#include <cassert>
19#include <filesystem>
20#include <optional>
21
22namespace casacore {
23
24class SiscoStMan;
25
30class SiscoStManColumn final : public StManColumn {
31 public:
49 explicit SiscoStManColumn(SiscoStMan &parent, DataType dtype, SiscoStoreMode mode)
50 : StManColumn(dtype), parent_(parent), store_mode_(mode) {
51 if (dtype != casacore::TpComplex) {
52 throw std::runtime_error(
53 "Sisco storage manager column can only be used for a data column "
54 "with single precision complex values");
55 }
56 }
57
59
64 bool isWritable() const final { return true; }
65
66 bool canChangeShape() const final { return true; }
67
68 void setShape(rownr_t, const IPosition &) final {
69 // Shape is implied from the array; explicit setting of the shape is not
70 // required.
71 }
72 void setShape(unsigned, const IPosition &) final {}
73
74 bool isShapeDefined(rownr_t) final {
75 if (isFixedShape() || file_exists_) {
76 return true;
77 } else {
78 return false;
79 }
80 }
81 bool isShapeDefined(unsigned row) final {
82 return isShapeDefined(static_cast<rownr_t>(row));
83 }
84
86 void setShapeColumn(const IPosition &shape) final {
87 if (shape.size() != 2) {
88 throw std::runtime_error(
89 "Sisco storage manager is used for a column with " +
90 std::to_string(shape.size()) +
91 " dimensions, but it can only be used for "
92 "columns with exactly 2 dimensions");
93 }
95 }
96
99 IPosition shape(rownr_t row) final {
100 if (isFixedShape()) {
101 return fixed_shape_;
102 } else if (file_exists_) {
105 } else {
106 return IPosition{0, 0};
107 }
108 }
109 IPosition shape(unsigned row) final {
110 return shape(static_cast<rownr_t>(row));
111 }
112
118 void getArrayV(rownr_t row, ArrayBase &dataPtr) final {
120 static_cast<Array<std::complex<float>> &>(dataPtr);
122
123 const IPosition *shape;
124 if (isFixedShape()) {
126 } else {
129 }
130 if (shape->size() >= 2) {
131 const size_t n_channels = (*shape)[1];
132 if (n_channels) {
133 const int n_column_correlations = (*shape)[0];
134 const int n_polarizations = GetNStoredCorrelations(n_column_correlations);
135 assert(store_mode_ == SiscoStoreMode::Original || n_column_correlations == 4);
136 bool ownership;
137 Complex *storage = array.getStorage(ownership);
138 buffer_.resize(n_channels);
139 for (int polarization = 0; polarization != n_polarizations;
140 ++polarization) {
141 reader_->GetNextResult(buffer_);
142 for (size_t channel = 0; channel != n_channels; ++channel) {
143 storage[channel * n_polarizations + polarization] =
144 buffer_[channel];
145 }
146 }
148 ExpandFromStokesI(storage, n_channels);
150 ExpandFromDiagonal(storage, n_channels);
151 }
152 array.putStorage(storage, ownership);
153 }
154 }
155
158 }
159
165 void putArrayV(rownr_t row, const ArrayBase &dataPtr) final {
167 static_cast<const Array<std::complex<float>> &>(dataPtr);
168 if (!writer_ || row < current_write_row_) {
169 OpenWriter();
170 }
171 while (current_write_row_ < row) {
173 }
174
175 const int field_id = field_id_column_(current_write_row_);
176 const int data_desc_id = data_desc_id_column_(current_write_row_);
177 const int antenna1 = antenna1_column_(current_write_row_);
178 const int antenna2 = antenna2_column_(current_write_row_);
179 if (array.shape().size() >= 2) {
180 const int n_column_correlations = array.shape()[0];
181 if((store_mode_ == SiscoStoreMode::StokesI || store_mode_ == SiscoStoreMode::Diagonal) && n_column_correlations != 4)
182 {
183 throw std::runtime_error("Trying to store invalid number of correlations in Sisco column: Sisco was set to store full-correlation values as Stokes I or diagonal visibilities: can only store shape 4 values in this mode");
184 }
185 const size_t n_channels = array.shape()[1];
186
187 if (n_channels) {
188 const int n_polarizations = GetNStoredCorrelations(n_column_correlations);
189 bool ownership;
190 const std::complex<float> *storage = array.getStorage(ownership);
191 buffer_.resize(n_channels);
193 CheckIsDiagonal(storage, n_channels);
194 for (int polarization = 0; polarization != n_polarizations;
195 ++polarization) {
196 const size_t baseline_id = GetBaselineId(
197 field_id, data_desc_id, antenna1, antenna2, polarization);
199 TransformToStokesI(storage, buffer_.data(), n_channels);
201 const size_t diagonal_index = polarization * 3; // 0 or 3
202 for (size_t channel = 0; channel != n_channels; ++channel) {
203 buffer_[channel] =
204 storage[channel * 4 + diagonal_index];
205 }
206 } else {
207 for (size_t channel = 0; channel != n_channels; ++channel) {
208 buffer_[channel] =
209 storage[channel * n_column_correlations + polarization];
210 }
211 }
212 writer_->Write(baseline_id, buffer_);
213 }
214 array.freeStorage(storage, ownership);
215 }
216 }
217
219 if (!isFixedShape()) shapes_writer_->Write(array.shape());
220 }
221
222 void Prepare();
223
224 private:
225 SiscoStManColumn(const SiscoStManColumn &source) = delete;
226 void operator=(const SiscoStManColumn &source) = delete;
227
229 // if row < current_read_row_, we have to reset the reader and start from
230 // the beginning. if row < current_write_row_, we are trying to read
231 // something that was already written. Writing may have been to a temporary
232 // file, which needs to be moved back first (by resetting), and if writing
233 // was not to a temporary file, it is the same file that we now need to open
234 // for reading, and the same file can not be written and read at the same
235 // time (e.g. due to caching). Ergo, the writer needs to be reset. The
236 // consequence is that another write after this read will reset (empty) the
237 // file. This is surprising, but it shouldn't happen in streaming
238 // processing, so it is a compromise. If writing is done to a temporary
239 // file, and reading is done from data that has not yet been written (row >=
240 // current_write_row_), no reset is necessary.
241 const bool is_reading_after_writing =
243 // To check above condition, some boolean algebra gives:
244 // !is_reading_after_writing = !writer_ || (has_temporary_file_ && row >=
245 // current_write_row_) which is indeed also correct in that no writer reset
246 // is required in that case.
247 if (!reader_ || row < current_read_row_ || is_reading_after_writing) {
248 OpenReader();
249 }
250 while (current_read_row_ < row) {
251 SkipRow();
252 }
253 }
254
255 void ResetWriter() {
256 if (writer_) {
257 writer_.reset();
258 shapes_writer_.reset();
259
260 // In case has_temporary_file_ is true, the writers were initialized with
261 // a temporary filename such that reading of "old" values could take place
262 // simultaneously. These new files need to be moved over the old files.
264 const std::string filename = parent_.fileName();
265 std::filesystem::rename(filename + kTemporaryExtension, filename);
266 if (!isFixedShape()) {
267 const std::string shapes_filename = ShapesFilename();
268 std::filesystem::rename(shapes_filename + kTemporaryExtension,
269 shapes_filename);
270 }
271 has_temporary_file_ = false;
272 }
273 }
274 }
275
276 void ResetReader() {
277 reader_.reset();
278 shapes_reader_.reset();
279 }
280
281 void OpenWriter() {
282 ResetWriter();
283
284 std::string filename = parent_.fileName();
285 std::string shapes_filename = ShapesFilename();
286 if (reader_) {
287 filename = filename + kTemporaryExtension;
288 shapes_filename = shapes_filename + kTemporaryExtension;
289 has_temporary_file_ = true;
290 }
291
292 writer_.emplace(filename, parent_.PredictLevel(), parent_.DeflateLevel());
293 char header_buffer[kHeaderSize];
294 std::fill_n(header_buffer, kHeaderSize, 0);
295 std::copy_n(kMagic, kMagicSize, &header_buffer[0]);
296 uint16_t storage_mode_tag = 0;
297 switch(store_mode_) {
299 storage_mode_tag = 0x8000;
300 break;
302 storage_mode_tag = 0x4000;
303 break;
305 storage_mode_tag = 0;
306 break;
307 }
308 const uint16_t major_and_mode = kVersionMajor | storage_mode_tag;
309 std::copy_n(reinterpret_cast<const char *>(&major_and_mode), 2,
310 &header_buffer[kMagicSize]);
311 std::copy_n(reinterpret_cast<const char *>(&kVersionMinor), 2,
312 &header_buffer[kMagicSize + 2]);
313 std::span<const std::byte> header(
314 reinterpret_cast<const std::byte *>(header_buffer), kHeaderSize);
315 writer_->Open(header);
316
317 if (!isFixedShape()) shapes_writer_.emplace(shapes_filename);
318
320 baseline_ids_.clear();
321 baseline_count_ = 0;
322 }
323
324 void OpenReader() {
325 ResetReader();
326 if (!has_temporary_file_) {
327 ResetWriter();
328 }
329 reader_.emplace(parent_.fileName());
330 char header_buffer[kHeaderSize];
331 std::span<std::byte> header(reinterpret_cast<std::byte *>(header_buffer),
333 reader_->Open(header);
334 char magic_tag[kMagicSize];
335 uint16_t version_major_and_mode;
336 uint16_t version_minor;
337 std::copy_n(&header_buffer[0], kMagicSize, magic_tag);
338 std::copy_n(&header_buffer[kMagicSize], 2,
339 reinterpret_cast<char *>(&version_major_and_mode));
340 std::copy_n(&header_buffer[kMagicSize + 2], 2,
341 reinterpret_cast<char *>(&version_minor));
342 const uint16_t version_major = version_major_and_mode & 0x3FFF;
343 if (version_major != kVersionMajor) {
344 throw std::runtime_error(
345 "The file on disk is written as a Sisco version " +
346 std::to_string(version_major) +
347 " file, whereas this Casacore version supports only version " +
348 std::to_string(kVersionMajor));
349 }
350
351 const uint16_t store_mode_tag = version_major_and_mode & 0xC000;
352 if(store_mode_tag == 0) {
354 } else if(store_mode_tag == 0x4000) {
356 } else if(store_mode_tag == 0x8000) {
358 } else {
359 throw std::runtime_error("File specifies an unknown store mode");
360 }
361
363 baseline_ids_.clear();
364 baseline_count_ = 0;
366
367 const size_t n_requests = reader_->GetRequestBufferSize() / 2;
368 if (!isFixedShape()) {
370 // Always request half of the requests that fit in the buffer of
371 // SiscoReader, so that SiscoReader can preprocess requests using multiple
372 // threads. Every time a row is read/skipped, another row is requested.
373 shape_buffer_.resize(n_requests);
376 }
378 for (size_t i = 0; i != n_requests; ++i) {
380 }
381 }
382
386 const bool eof = current_requested_reading_row_ >= row_count_ ||
387 (!isFixedShape() && shapes_reader_->Eof());
388 if (!eof && shape.size() >= 2) {
390 const int data_desc_id =
394 const int n_polarizations = GetNStoredCorrelations(shape[0]);
395 const int n_channels = shape[1];
396 for (int polarization = 0; polarization != n_polarizations;
397 ++polarization) {
398 const size_t baseline_id = GetBaselineId(
399 field_id, data_desc_id, antenna1, antenna2, polarization);
400 reader_->Request(baseline_id, n_channels);
401 }
402 }
403 if (!isFixedShape()) {
407 }
409 }
410
411 size_t GetBaselineId(int field_id, int data_desc_id, int antenna1,
412 int antenna2, int polarization) {
413 const std::array<int, 5> baseline{field_id, data_desc_id, antenna1,
414 antenna2, polarization};
415 std::map<std::array<int, 5>, size_t>::const_iterator iterator =
416 baseline_ids_.find(baseline);
417 if (iterator == baseline_ids_.end()) {
418 iterator = baseline_ids_.emplace(baseline, baseline_count_).first;
420 }
421 return iterator->second;
422 }
423
424 std::string ShapesFilename() const {
425 return parent_.fileName() + kShapesExtension;
426 }
427
429 if (isFixedShape()) {
430 const int n_polarizations = GetNStoredCorrelations(fixed_shape_[0]);
431 const int n_channels = fixed_shape_[1];
432 buffer_.assign(n_channels, 0.0);
433 const int field_id = field_id_column_(current_write_row_);
434 const int data_desc_id = data_desc_id_column_(current_write_row_);
435 const int antenna1 = antenna1_column_(current_write_row_);
436 const int antenna2 = antenna2_column_(current_write_row_);
437 for (int polarization = 0; polarization != n_polarizations;
438 ++polarization) {
439 const size_t baseline_id = GetBaselineId(
440 field_id, data_desc_id, antenna1, antenna2, polarization);
441 writer_->Write(baseline_id, buffer_);
442 }
443 } else {
444 shapes_writer_->Write(IPosition{0, 0});
445 }
447 }
448
449 void SkipRow() {
451 if (isFixedShape()) {
453 } else {
456 }
457 if (shape->size() >= 2) {
458 const int n_polarizations = GetNStoredCorrelations((*shape)[0]);
459 const int n_channels = (*shape)[1];
460 if (n_channels) {
461 buffer_.resize(n_channels);
462 for (int polarization = 0; polarization != n_polarizations;
463 ++polarization) {
464 reader_->GetNextResult(buffer_);
465 }
466 }
467 }
470 }
471
472 constexpr int GetNStoredCorrelations(const size_t n_column_correlations) const {
473 switch(store_mode_) {
475 return 1;
477 return 2;
479 return n_column_correlations;
480 }
481 assert(false);
482 return 0;
483 }
484
485 static constexpr size_t kHeaderSize = 20;
486 static constexpr char kMagic[] = "Sisco\0\0\0";
487 static constexpr size_t kMagicSize = 8;
488 static constexpr uint16_t kVersionMajor = 2;
489 static constexpr uint16_t kVersionMinor = 0;
490 static constexpr char kShapesExtension[] = "-shapes";
491 static constexpr char kTemporaryExtension[] = "-tmp";
492
497
499 std::optional<sisco::SiscoWriter> writer_;
500 std::optional<sisco::SiscoReader> reader_;
501 std::optional<ShapesFileWriter> shapes_writer_;
502 std::optional<ShapesFileReader> shapes_reader_;
504 // A circular buffer to store the already read shapes
505 std::vector<IPosition> shape_buffer_;
506 // Points inside shape_buffer_ to the location corresponding to the shape for
507 // the current_read_row_.
509 // When reading a new shape from file, this is the place inside the shape
510 // buffer to write it to.
515 // When reading, this value is set to the number of rows in the measurement
516 // set.
518 // Scratch buffer. It does not have a specific state between function calls,
519 // but is stored in class scope so that can reuse its memory.
520 std::vector<std::complex<float>> buffer_;
521 std::map<std::array<int, 5>, size_t> baseline_ids_;
523 bool file_exists_ = false;
531};
532
533} // namespace casacore
534
535#include "SiscoStMan.h"
536
537namespace casacore {
538
540 Table &table = parent_.table();
541 field_id_column_ = ScalarColumn<int>(table, "FIELD_ID");
542 data_desc_id_column_ = ScalarColumn<int>(table, "DATA_DESC_ID");
543 antenna1_column_ = ScalarColumn<int>(table, "ANTENNA1");
544 antenna2_column_ = ScalarColumn<int>(table, "ANTENNA2");
545 file_exists_ = std::filesystem::exists(parent_.fileName()) &&
546 (!isFixedShape() || std::filesystem::exists(ShapesFilename()));
547}
548
549} // namespace casacore
550
551#endif
Non-templated base class for templated Array class.
Definition ArrayBase.h:71
Bool isFixedShape() const
Is this a fixed shape column?
static constexpr uint16_t kVersionMinor
std::optional< ShapesFileWriter > shapes_writer_
static constexpr char kMagic[]
std::vector< IPosition > shape_buffer_
A circular buffer to store the already read shapes.
std::optional< sisco::SiscoReader > reader_
size_t shape_write_position_
When reading a new shape from file, this is the place inside the shape buffer to write it to.
void setShapeColumn(const IPosition &shape) final
Set the dimensions of values in this column.
void putArrayV(rownr_t row, const ArrayBase &dataPtr) final
Write values into a particular row.
IPosition shape(unsigned row) final
bool canChangeShape() const final
Can the data manager handle chaging the shape of an existing array?
void PrepareReadingOfRow(rownr_t row)
static constexpr size_t kMagicSize
IPosition shape(rownr_t row) final
Get the dimensions of the values in a particular row.
static constexpr char kTemporaryExtension[]
std::optional< ShapesFileReader > shapes_reader_
bool isShapeDefined(rownr_t) final
Is the value shape defined in the given row?
bool isWritable() const final
Whether this column is writable.
constexpr int GetNStoredCorrelations(const size_t n_column_correlations) const
std::map< std::array< int, 5 >, size_t > baseline_ids_
SiscoStManColumn(const SiscoStManColumn &source)=delete
ScalarColumn< int > antenna2_column_
void operator=(const SiscoStManColumn &source)=delete
SiscoStManColumn(SiscoStMan &parent, DataType dtype, SiscoStoreMode mode)
rownr_t row_count_
When reading, this value is set to the number of rows in the measurement set.
bool isShapeDefined(unsigned row) final
void setShape(rownr_t, const IPosition &) final
Set the shape of an (variable-shaped) array in the given row.
void setShape(unsigned, const IPosition &) final
size_t shape_read_position_
Points inside shape_buffer_ to the location corresponding to the shape for the current_read_row_.
bool has_temporary_file_
If true, writing is done to a temporary file such that reading can still take place from the old file...
size_t GetBaselineId(int field_id, int data_desc_id, int antenna1, int antenna2, int polarization)
void getArrayV(rownr_t row, ArrayBase &dataPtr) final
Read the values for a particular row.
ScalarColumn< int > data_desc_id_column_
static constexpr uint16_t kVersionMajor
static constexpr char kShapesExtension[]
ScalarColumn< int > antenna1_column_
ScalarColumn< int > field_id_column_
std::optional< sisco::SiscoWriter > writer_
std::string ShapesFilename() const
static constexpr size_t kHeaderSize
std::vector< std::complex< float > > buffer_
Scratch buffer.
The Stokes I storage manager behaves like a full set of (4) polarizations but only stores the Stokes ...
Definition SiscoStMan.h:26
StManColumn(int dataType)
Default constructor.
Definition StManColumn.h:78
this file contains all the compiler specific defines
Definition mainpage.dox:28
const T * const_iterator
Definition Block.h:580
void ExpandFromDiagonal(T *data, size_t n)
Performs in-place expansion of n pair of diagonal values such that each pair becomes 4 values with ze...
T * array
The actual storage.
Definition Block.h:689
T * storage()
If you really, really, need a "raw" pointer to the beginning of the storage area this will give it to...
Definition Block.h:550
T * TransformToStokesI(const T *input, T *buffer, size_t n)
Calculates for every set of 4 input values the Stokes-I values by doing out = 0.5 * (in_pp + in_qq),...
T * iterator
Definition Block.h:579
void CheckIsDiagonal(const T *input, size_t n)
Check if every set of 4 input values contains only non-zeros on the diagonal (pp or qq).
void ExpandFromStokesI(T *data, size_t n)
Expands n values from single Stokes I values to have 4 values, in place.
uInt64 rownr_t
Define the type of a row number in a table.
Definition aipsxtype.h:44