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 { return isShapeDefined(static_cast<rownr_t>(row)); }
82
84 void setShapeColumn(const IPosition &shape) final {
85 if (shape.size() != 2) {
86 throw std::runtime_error("Sisco storage manager is used for a column with " +
87 std::to_string(shape.size()) +
88 " dimensions, but it can only be used for "
89 "columns with exactly 2 dimensions");
90 }
92 }
93
96 IPosition shape(rownr_t row) final {
97 if (isFixedShape()) {
98 return fixed_shape_;
99 } else if (file_exists_) {
102 } else {
103 return IPosition{0, 0};
104 }
105 }
106 IPosition shape(unsigned row) final { return shape(static_cast<rownr_t>(row)); }
107
113 void getArrayV(rownr_t row, ArrayBase &dataPtr) final {
114 Array<std::complex<float>> &array = static_cast<Array<std::complex<float>> &>(dataPtr);
116
117 const IPosition *shape;
118 if (isFixedShape()) {
120 } else {
123 }
124 if (shape->size() >= 2) {
125 const size_t n_channels = (*shape)[1];
126 if (n_channels) {
127 const int n_column_correlations = (*shape)[0];
128 const int n_polarizations = GetNStoredCorrelations(n_column_correlations);
129 assert(store_mode_ == SiscoStoreMode::Original || n_column_correlations == 4);
130 bool ownership;
131 Complex *storage = array.getStorage(ownership);
132 buffer_.resize(n_channels);
133 for (int polarization = 0; polarization != n_polarizations; ++polarization) {
134 reader_->GetNextResult(buffer_);
135 for (size_t channel = 0; channel != n_channels; ++channel) {
136 storage[channel * n_polarizations + polarization] = buffer_[channel];
137 }
138 }
140 ExpandFromStokesI(storage, n_channels);
142 ExpandFromDiagonal(storage, n_channels);
143 }
144 array.putStorage(storage, ownership);
145 }
146 }
147
150 }
151
157 void putArrayV(rownr_t row, const ArrayBase &dataPtr) final {
159 static_cast<const Array<std::complex<float>> &>(dataPtr);
160 if (!writer_ || row < current_write_row_) {
161 OpenWriter();
162 }
163 while (current_write_row_ < row) {
165 }
166
167 const int field_id = field_id_column_(current_write_row_);
168 const int data_desc_id = data_desc_id_column_(current_write_row_);
169 const int antenna1 = antenna1_column_(current_write_row_);
170 const int antenna2 = antenna2_column_(current_write_row_);
171 if (array.shape().size() >= 2) {
172 const int n_column_correlations = array.shape()[0];
174 n_column_correlations != 4) {
175 throw std::runtime_error(
176 "Trying to store invalid number of correlations in Sisco column: Sisco was set to "
177 "store full-correlation values as Stokes I or diagonal visibilities: can only store "
178 "shape 4 values in this mode");
179 }
180 const size_t n_channels = array.shape()[1];
181
182 if (n_channels) {
183 const int n_polarizations = GetNStoredCorrelations(n_column_correlations);
184 bool ownership;
185 const std::complex<float> *storage = array.getStorage(ownership);
186 buffer_.resize(n_channels);
188 for (int polarization = 0; polarization != n_polarizations; ++polarization) {
189 const size_t baseline_id =
190 GetBaselineId(field_id, data_desc_id, antenna1, antenna2, polarization);
192 TransformToStokesI(storage, buffer_.data(), n_channels);
194 const size_t diagonal_index = polarization * 3; // 0 or 3
195 for (size_t channel = 0; channel != n_channels; ++channel) {
196 buffer_[channel] = storage[channel * 4 + diagonal_index];
197 }
198 } else {
199 for (size_t channel = 0; channel != n_channels; ++channel) {
200 buffer_[channel] = storage[channel * n_column_correlations + polarization];
201 }
202 }
203 writer_->Write(baseline_id, buffer_);
204 }
205 array.freeStorage(storage, ownership);
206 }
207 }
208
210 if (!isFixedShape()) shapes_writer_->Write(array.shape());
211 }
212
213 void Prepare();
214
215 private:
216 SiscoStManColumn(const SiscoStManColumn &source) = delete;
217 void operator=(const SiscoStManColumn &source) = delete;
218
220 // if row < current_read_row_, we have to reset the reader and start from
221 // the beginning. if row < current_write_row_, we are trying to read
222 // something that was already written. Writing may have been to a temporary
223 // file, which needs to be moved back first (by resetting), and if writing
224 // was not to a temporary file, it is the same file that we now need to open
225 // for reading, and the same file can not be written and read at the same
226 // time (e.g. due to caching). Ergo, the writer needs to be reset. The
227 // consequence is that another write after this read will reset (empty) the
228 // file. This is surprising, but it shouldn't happen in streaming
229 // processing, so it is a compromise. If writing is done to a temporary
230 // file, and reading is done from data that has not yet been written (row >=
231 // current_write_row_), no reset is necessary.
232 const bool is_reading_after_writing =
234 // To check above condition, some boolean algebra gives:
235 // !is_reading_after_writing = !writer_ || (has_temporary_file_ && row >=
236 // current_write_row_) which is indeed also correct in that no writer reset
237 // is required in that case.
238 if (!reader_ || row < current_read_row_ || is_reading_after_writing) {
239 OpenReader();
240 }
241 while (current_read_row_ < row) {
242 SkipRow();
243 }
244 }
245
246 void ResetWriter() {
247 if (writer_) {
248 writer_.reset();
249 shapes_writer_.reset();
250
251 // In case has_temporary_file_ is true, the writers were initialized with
252 // a temporary filename such that reading of "old" values could take place
253 // simultaneously. These new files need to be moved over the old files.
255 const std::string filename = parent_.fileName();
256 std::filesystem::rename(filename + kTemporaryExtension, filename);
257 if (!isFixedShape()) {
258 const std::string shapes_filename = ShapesFilename();
259 std::filesystem::rename(shapes_filename + kTemporaryExtension, shapes_filename);
260 }
261 has_temporary_file_ = false;
262 }
263 }
264 }
265
266 void ResetReader() {
267 reader_.reset();
268 shapes_reader_.reset();
269 }
270
271 void OpenWriter() {
272 ResetWriter();
273
274 std::string filename = parent_.fileName();
275 std::string shapes_filename = ShapesFilename();
276 if (reader_) {
277 filename = filename + kTemporaryExtension;
278 shapes_filename = shapes_filename + kTemporaryExtension;
279 has_temporary_file_ = true;
280 }
281
282 writer_.emplace(filename, parent_.PredictLevel(), parent_.DeflateLevel());
283 char header_buffer[kHeaderSize];
284 std::fill_n(header_buffer, kHeaderSize, 0);
285 std::copy_n(kMagic, kMagicSize, &header_buffer[0]);
286 uint16_t storage_mode_tag = 0;
287 switch (store_mode_) {
289 storage_mode_tag = 0x8000;
290 break;
292 storage_mode_tag = 0x4000;
293 break;
295 storage_mode_tag = 0;
296 break;
297 }
298 const uint16_t major_and_mode = kVersionMajor | storage_mode_tag;
299 std::copy_n(reinterpret_cast<const char *>(&major_and_mode), 2, &header_buffer[kMagicSize]);
300 std::copy_n(reinterpret_cast<const char *>(&kVersionMinor), 2, &header_buffer[kMagicSize + 2]);
301 std::span<const std::byte> header(reinterpret_cast<const std::byte *>(header_buffer),
303 writer_->Open(header);
304
305 if (!isFixedShape()) shapes_writer_.emplace(shapes_filename);
306
308 baseline_ids_.clear();
309 baseline_count_ = 0;
310 }
311
312 void OpenReader() {
313 ResetReader();
314 if (!has_temporary_file_) {
315 ResetWriter();
316 }
317 reader_.emplace(parent_.fileName());
318 char header_buffer[kHeaderSize];
319 std::span<std::byte> header(reinterpret_cast<std::byte *>(header_buffer), kHeaderSize);
320 reader_->Open(header);
321 char magic_tag[kMagicSize];
322 uint16_t version_major_and_mode;
323 uint16_t version_minor;
324 std::copy_n(&header_buffer[0], kMagicSize, magic_tag);
325 std::copy_n(&header_buffer[kMagicSize], 2, reinterpret_cast<char *>(&version_major_and_mode));
326 std::copy_n(&header_buffer[kMagicSize + 2], 2, reinterpret_cast<char *>(&version_minor));
327 const uint16_t version_major = version_major_and_mode & 0x3FFF;
328 if (version_major != kVersionMajor) {
329 throw std::runtime_error("The file on disk is written as a Sisco version " +
330 std::to_string(version_major) +
331 " file, whereas this Casacore version supports only version " +
332 std::to_string(kVersionMajor));
333 }
334
335 const uint16_t store_mode_tag = version_major_and_mode & 0xC000;
336 if (store_mode_tag == 0) {
338 } else if (store_mode_tag == 0x4000) {
340 } else if (store_mode_tag == 0x8000) {
342 } else {
343 throw std::runtime_error("File specifies an unknown store mode");
344 }
345
347 baseline_ids_.clear();
348 baseline_count_ = 0;
350
351 const size_t n_requests = reader_->GetRequestBufferSize() / 2;
352 if (!isFixedShape()) {
354 // Always request half of the requests that fit in the buffer of
355 // SiscoReader, so that SiscoReader can preprocess requests using multiple
356 // threads. Every time a row is read/skipped, another row is requested.
357 shape_buffer_.resize(n_requests);
360 }
362 for (size_t i = 0; i != n_requests; ++i) {
364 }
365 }
366
369 const bool eof =
371 if (!eof && shape.size() >= 2) {
376 const int n_polarizations = GetNStoredCorrelations(shape[0]);
377 const int n_channels = shape[1];
378 for (int polarization = 0; polarization != n_polarizations; ++polarization) {
379 const size_t baseline_id =
380 GetBaselineId(field_id, data_desc_id, antenna1, antenna2, polarization);
381 reader_->Request(baseline_id, n_channels);
382 }
383 }
384 if (!isFixedShape()) {
387 }
389 }
390
391 size_t GetBaselineId(int field_id, int data_desc_id, int antenna1, int antenna2,
392 int polarization) {
393 const std::array<int, 5> baseline{field_id, data_desc_id, antenna1, antenna2, polarization};
394 std::map<std::array<int, 5>, size_t>::const_iterator iterator = baseline_ids_.find(baseline);
395 if (iterator == baseline_ids_.end()) {
396 iterator = baseline_ids_.emplace(baseline, baseline_count_).first;
398 }
399 return iterator->second;
400 }
401
402 std::string ShapesFilename() const { return parent_.fileName() + kShapesExtension; }
403
405 if (isFixedShape()) {
406 const int n_polarizations = GetNStoredCorrelations(fixed_shape_[0]);
407 const int n_channels = fixed_shape_[1];
408 buffer_.assign(n_channels, 0.0);
409 const int field_id = field_id_column_(current_write_row_);
410 const int data_desc_id = data_desc_id_column_(current_write_row_);
411 const int antenna1 = antenna1_column_(current_write_row_);
412 const int antenna2 = antenna2_column_(current_write_row_);
413 for (int polarization = 0; polarization != n_polarizations; ++polarization) {
414 const size_t baseline_id =
415 GetBaselineId(field_id, data_desc_id, antenna1, antenna2, polarization);
416 writer_->Write(baseline_id, buffer_);
417 }
418 } else {
419 shapes_writer_->Write(IPosition{0, 0});
420 }
422 }
423
424 void SkipRow() {
426 if (isFixedShape()) {
428 } else {
431 }
432 if (shape->size() >= 2) {
433 const int n_polarizations = GetNStoredCorrelations((*shape)[0]);
434 const int n_channels = (*shape)[1];
435 if (n_channels) {
436 buffer_.resize(n_channels);
437 for (int polarization = 0; polarization != n_polarizations; ++polarization) {
438 reader_->GetNextResult(buffer_);
439 }
440 }
441 }
444 }
445
446 constexpr int GetNStoredCorrelations(const size_t n_column_correlations) const {
447 switch (store_mode_) {
449 return 1;
451 return 2;
453 return n_column_correlations;
454 }
455 assert(false);
456 return 0;
457 }
458
459 static constexpr size_t kHeaderSize = 20;
460 static constexpr char kMagic[] = "Sisco\0\0\0";
461 static constexpr size_t kMagicSize = 8;
462 static constexpr uint16_t kVersionMajor = 2;
463 static constexpr uint16_t kVersionMinor = 0;
464 static constexpr char kShapesExtension[] = "-shapes";
465 static constexpr char kTemporaryExtension[] = "-tmp";
466
471
473 std::optional<sisco::SiscoWriter> writer_;
474 std::optional<sisco::SiscoReader> reader_;
475 std::optional<ShapesFileWriter> shapes_writer_;
476 std::optional<ShapesFileReader> shapes_reader_;
478 // A circular buffer to store the already read shapes
479 std::vector<IPosition> shape_buffer_;
480 // Points inside shape_buffer_ to the location corresponding to the shape for
481 // the current_read_row_.
483 // When reading a new shape from file, this is the place inside the shape
484 // buffer to write it to.
489 // When reading, this value is set to the number of rows in the measurement
490 // set.
492 // Scratch buffer. It does not have a specific state between function calls,
493 // but is stored in class scope so that can reuse its memory.
494 std::vector<std::complex<float>> buffer_;
495 std::map<std::array<int, 5>, size_t> baseline_ids_;
497 bool file_exists_ = false;
505};
506
507} // namespace casacore
508
509#include "SiscoStMan.h"
510
511namespace casacore {
512
514 Table &table = parent_.table();
515 field_id_column_ = ScalarColumn<int>(table, "FIELD_ID");
516 data_desc_id_column_ = ScalarColumn<int>(table, "DATA_DESC_ID");
517 antenna1_column_ = ScalarColumn<int>(table, "ANTENNA1");
518 antenna2_column_ = ScalarColumn<int>(table, "ANTENNA2");
519 file_exists_ = std::filesystem::exists(parent_.fileName()) &&
520 (!isFixedShape() || std::filesystem::exists(ShapesFilename()));
521}
522
523} // namespace casacore
524
525#endif
Non-templated base class for templated Array class.
Definition ArrayBase.h:69
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:74
For temporary backward namespace compatibility, use casa as alias for casacore.
Definition mainpage.dox:28
const T * const_iterator
Definition Block.h:589
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:559
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:588
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