casacore
Loading...
Searching...
No Matches
UvwFile.h
Go to the documentation of this file.
1#ifndef CASACORE_UVW_FILE_H_
2#define CASACORE_UVW_FILE_H_
3
5
6#include <algorithm>
7#include <cassert>
8#include <limits>
9#include <ostream>
10#include <stdexcept>
11#include <string>
12
13namespace casacore {
14
49class UvwFile {
50 public:
51 UvwFile() noexcept = default;
52
53 UvwFile(UvwFile&& source) noexcept
54 : file_(std::move(source.file_)),
55 n_rows_(source.n_rows_),
63 source.n_rows_ = 0;
64 source.rows_per_block_ = 0;
65 source.active_block_ = 0;
66 source.reference_antenna_ = 0;
67 source.start_antenna_2_ = 0;
68 source.n_antennas_ = 0;
69 source.block_uvws_.clear();
70 source.block_is_changed_ = false;
71 }
72
73 ~UvwFile() noexcept { Close(); }
74
76 Close();
77 file_ = std::move(rhs.file_);
78 n_rows_ = rhs.n_rows_;
79 rows_per_block_ = rhs.rows_per_block_;
80 active_block_ = rhs.active_block_;
81 reference_antenna_ = rhs.reference_antenna_;
82 start_antenna_2_ = rhs.start_antenna_2_;
83 n_antennas_ = rhs.n_antennas_;
84 block_uvws_ = std::move(rhs.block_uvws_);
85 block_is_changed_ = rhs.block_is_changed_;
86 rhs.n_rows_ = 0;
87 rhs.rows_per_block_ = 0;
88 rhs.active_block_ = 0;
89 rhs.reference_antenna_ = 0;
90 rhs.start_antenna_2_ = 0;
91 rhs.n_antennas_ = 0;
92 rhs.block_uvws_.clear();
93 rhs.block_is_changed_ = false;
94 return *this;
95 }
96
100 static UvwFile CreateNew(const std::string& filename) { return UvwFile(filename); }
101
105 static UvwFile OpenExisting(const std::string& filename) { return UvwFile(filename, true); }
106
112 void WriteUvw(uint64_t row, size_t antenna1, size_t antenna2, const double* uvw) {
113 assert(file_.IsOpen());
114 if (row > n_rows_) {
115 throw std::runtime_error("Uvw data must be written in order (writing row " +
116 std::to_string(row) + ", after writing " + std::to_string(n_rows_) +
117 " rows)");
118 }
119 // The row/block is zero when there's not yet a full block written.
120 if (rows_per_block_ == 0) {
121 if (row == 0) {
122 reference_antenna_ = antenna1;
123 start_antenna_2_ = antenna2;
124 n_antennas_ = std::max(antenna1, antenna2) + 1;
126 block_uvws_[reference_antenna_] = {0.0, 0.0, 0.0};
127 } else if (antenna1 == reference_antenna_ && antenna2 == start_antenna_2_) {
128 // This baseline is the first baseline of a new block, so the block size
129 // can be determined
131 n_antennas_ = block_uvws_.size();
132 WriteHeader();
133 ActivateBlock(1);
134 } else {
135 n_antennas_ = std::max({antenna1 + 1, antenna2 + 1, n_antennas_});
137 }
138 } else {
139 const uint64_t block = row / rows_per_block_;
140 ActivateBlock(block);
141 }
142 if (antenna1 != antenna2) {
143 if (antenna1 == reference_antenna_) {
144 // baseline = a2 - a1 with a1 = 0
145 const std::array<double, 3> ant2_uvw{uvw[0], uvw[1], uvw[2]};
146 StoreOrCheck(antenna2, ant2_uvw);
147 } else if (antenna2 == reference_antenna_) {
148 // baseline = a2 - a1 with a2 = 0
149 const std::array<double, 3> ant1_uvw{-uvw[0], -uvw[1], -uvw[2]};
150 StoreOrCheck(antenna1, ant1_uvw);
151 } else if (IsSet(antenna1)) {
152 // baseline = a2 - a1. Given a1:
153 // a2 = baseline + a1
154 const std::array<double, 3> ant1_uvw = block_uvws_[antenna1];
155 const std::array<double, 3> ant2_uvw{uvw[0] + ant1_uvw[0], uvw[1] + ant1_uvw[1],
156 uvw[2] + ant1_uvw[2]};
157 StoreOrCheck(antenna2, ant2_uvw);
158 } else if (IsSet(antenna2)) {
159 // baseline = a2 - a1. Given a2:
160 // a1 = a2 - baseline
161 const std::array<double, 3> ant2_uvw = block_uvws_[antenna2];
162 const std::array<double, 3> ant1_uvw{ant2_uvw[0] - uvw[0], ant2_uvw[1] - uvw[1],
163 ant2_uvw[2] - uvw[2]};
164 StoreOrCheck(antenna1, ant1_uvw);
165 } else {
166 throw std::runtime_error(
167 "Baselines are written in a non-ordered way: they need to be "
168 "ordered either by antenna 1 or by antenna 2");
169 }
170 } else {
171 // In the special case of a file with only auto-correlations, the
172 // positions of the antennas can not be established in this case, it's
173 // also not necessary, because a zero uvw can be returned for
174 // auto-correlations. However, it will cause StoreOrCheck() to be
175 // never called, and therefore the block is never written to the file, and
176 // upon read the nr. of rows can not be determined. So, if this is an
177 // auto-correlation that has not yet been written, mark the block as
178 // changed so it gets written.
180 }
181 n_rows_ = std::max(n_rows_, row + 1);
182 }
183
189 void ReadUvw(uint64_t row, size_t antenna1, size_t antenna2, double* uvw) {
190 assert(file_.IsOpen());
191 if (row >= n_rows_ || antenna1 >= n_antennas_ || antenna2 >= n_antennas_) {
192 throw std::runtime_error("Invalid read for Uvw data: row " + std::to_string(row) +
193 ", baseline (" + std::to_string(antenna1) + ", " +
194 std::to_string(antenna2) + ") was requested. File has only " +
195 std::to_string(n_rows_) + " rows with " +
196 std::to_string(n_antennas_) + " antennas.");
197 }
198 if (rows_per_block_ != 0) {
199 const uint64_t block = row / rows_per_block_;
200 ActivateBlock(block);
201 }
202 uvw[0] = block_uvws_[antenna2][0] - block_uvws_[antenna1][0];
203 uvw[1] = block_uvws_[antenna2][1] - block_uvws_[antenna1][1];
204 uvw[2] = block_uvws_[antenna2][2] - block_uvws_[antenna1][2];
205 }
206
207 void Close() {
208 if (file_.IsOpen()) {
209 // This handles two special cases: i) if only one block of visibilities
210 // is written, no repetition of baseline would have been identified yet.
211 // In that case, we now know the block size. ii) in case only a single
212 // auto-correlation (one antenna) is written without any
213 // cross-correlations, the compressed file will remain empty. We then have
214 // to identify how many rows are really in the MS, which is done by
215 // setting rows_per_block_ to the nr of rows. When reading such an MS, the
216 // fact that n_antennas_ == 1 is then a trigger to use rows_per_block_ as
217 // nrows, instead of the size of the compressed file.
218 if (rows_per_block_ == 0) {
220 n_antennas_ = block_uvws_.size();
221 WriteHeader();
222 } else if (n_antennas_ == 1) {
224 WriteHeader();
225 }
226 if (block_is_changed_) {
228 }
229 file_.Close();
230 }
231 }
232
233 uint64_t NRows() const { return n_rows_; }
234 std::string Filename() const { return file_.Filename(); }
235
236 private:
240 UvwFile(const std::string& filename)
241 : file_(BufferedColumnarFile::CreateNew(filename, kHeaderSize, sizeof(double) * 3)) {}
242
247 UvwFile(const std::string& filename, bool /*open existing*/)
249 ReadHeader();
250 active_block_ = std::numeric_limits<uint64_t>::max();
251 if (n_antennas_ > 1) {
252 if (file_.NRows() % (n_antennas_ - 1) != 0) {
253 throw std::runtime_error("Uvw file has an incorrect number of rows (" +
254 std::to_string(file_.NRows()) + ", expecting multiple of " +
255 std::to_string(n_antennas_ - 1) + "): file corrupted?");
256 }
257 const uint64_t n_blocks = file_.NRows() / (n_antennas_ - 1);
258 n_rows_ = n_blocks * rows_per_block_;
259 } else if (n_antennas_ == 1) {
261 }
263 throw std::runtime_error(
264 "Invalid combination of values for n_antenna and reference antenna "
265 "in file: file damaged?");
266 // Setting the size of block_uvws_ here, saves an extra size check in
267 // ActivateBlock().
269 }
270
278 void StoreOrCheck(size_t antenna, const std::array<double, 3>& antenna_uvw) {
279 if (IsSet(antenna)) {
280 if (!AreNear(block_uvws_[antenna], antenna_uvw)) {
281 std::ostringstream msg;
282 msg << "Inconsistent UVW value written for antenna " << antenna << ": old value is "
283 << UvwAsString(block_uvws_[antenna]) << ", new value is " << UvwAsString(antenna_uvw)
284 << ".";
285 throw std::runtime_error(msg.str());
286 }
287 } else {
288 if (block_uvws_.size() <= antenna) block_uvws_.resize(antenna + 1, kUnsetPosition);
289 block_uvws_[antenna] = antenna_uvw;
290 block_is_changed_ = true;
291 }
292 }
293
294 void ActivateBlock(size_t block) {
295 if (block != active_block_) {
296 if (block_is_changed_) {
298 }
299
300 active_block_ = block;
302 }
303 }
304
306 const uint64_t block_start_row = (n_antennas_ - 1) * active_block_;
307 if (block_start_row < file_.NRows()) {
308 block_uvws_.clear();
309 for (size_t antenna = 0; antenna != n_antennas_; ++antenna) {
310 if (antenna != reference_antenna_) {
311 const uint64_t row = antenna < reference_antenna_ ? block_start_row + antenna
312 : block_start_row + antenna - 1;
313 std::array<double, 3>& uvw = block_uvws_.emplace_back();
314 file_.Read(row, 0, uvw.data(), 3);
315 } else {
316 block_uvws_.emplace_back(std::array<double, 3>{0.0, 0.0, 0.0});
317 }
318 }
319 } else {
320 std::fill(block_uvws_.begin(), block_uvws_.end(), kUnsetPosition);
321 block_uvws_[reference_antenna_] = {0.0, 0.0, 0.0};
322 }
323 }
324
326 if (block_uvws_.size() != n_antennas_)
327 throw std::runtime_error("Trying to write an incomplete UVW block");
328 const uint64_t block_start_row = (n_antennas_ - 1) * active_block_;
329 for (size_t antenna = 0; antenna != n_antennas_; ++antenna) {
330 if (antenna != reference_antenna_) {
331 const uint64_t row = antenna < reference_antenna_ ? block_start_row + antenna
332 : block_start_row + antenna - 1;
333 file_.Write(row, 0, block_uvws_[antenna].data(), 3);
334 }
335 }
336 block_is_changed_ = false;
337 }
338
339 void ReadHeader() {
340 unsigned char data[kHeaderSize];
341 file_.ReadHeader(data);
342 if (!std::equal(data, data + 8, kMagicHeaderTag)) {
343 throw std::runtime_error(
344 "The UVW columnar file header not have the expected tag for UVW "
345 "columns: the measurement set may be damaged");
346 }
347 rows_per_block_ = reinterpret_cast<uint64_t&>(data[8]);
348 reference_antenna_ = reinterpret_cast<uint64_t&>(data[16]);
349 n_antennas_ = reinterpret_cast<uint64_t&>(data[24]);
350 }
351
352 void WriteHeader() {
353 unsigned char data[kHeaderSize];
354 std::copy_n(kMagicHeaderTag, 8, data);
355 reinterpret_cast<uint64_t&>(data[8]) = rows_per_block_;
356 reinterpret_cast<uint64_t&>(data[16]) = reference_antenna_;
357 reinterpret_cast<uint64_t&>(data[24]) = n_antennas_;
358 file_.WriteHeader(data);
359 }
360
361 bool IsSet(size_t antenna) const {
362 return block_uvws_.size() > antenna && block_uvws_[antenna] != kUnsetPosition;
363 }
364 static bool AreNear(std::array<double, 3> a, std::array<double, 3> b) {
365 return AreNear(a[0], b[0]) && AreNear(a[1], b[1]) && AreNear(a[2], b[2]);
366 }
367 static bool AreNear(double a, double b) {
368 const double magnitude = std::max({1e-5, std::fabs(a), std::fabs(b)});
369 return (std::fabs(a - b) / magnitude) < 1e-5;
370 }
371 static std::string UvwAsString(const std::array<double, 3>& uvw) {
372 std::ostringstream str;
373 str << "[" << uvw[0] << ", " << uvw[1] << ", " << uvw[2] << "]";
374 return str.str();
375 }
376
384 constexpr static size_t kHeaderSize = 32;
385 constexpr static const char kMagicHeaderTag[8] = "Uvw-col";
386 constexpr static std::array<double, 3> kUnsetPosition = {std::numeric_limits<double>::max(),
387 std::numeric_limits<double>::max(),
388 std::numeric_limits<double>::max()};
397 uint64_t n_rows_ = 0;
408 uint64_t rows_per_block_ = 0;
409 uint64_t active_block_ = 0;
411 // This value is used to determine the first baseline in the data, which is
412 // the baseline (reference_antenna_, start_antenna_2_).
414 size_t n_antennas_ = 0;
415 // UVW for each antenna in the block
416 std::vector<std::array<double, 3>> block_uvws_;
417 bool block_is_changed_ = false;
418};
419
420} // namespace casacore
421
422#endif
std::string Filename() const
Definition UvwFile.h:234
static constexpr size_t kHeaderSize
The header: char[8] "Uvw-col\0" (=kMagicHeaderTag) uint64_t rows_per_block uint64_t reference_antenna...
Definition UvwFile.h:384
uint64_t active_block_
Definition UvwFile.h:409
static UvwFile OpenExisting(const std::string &filename)
Open an already existing UVW file from disk with the given filename.
Definition UvwFile.h:105
static constexpr const char kMagicHeaderTag[8]
Definition UvwFile.h:385
size_t n_antennas_
Definition UvwFile.h:414
static UvwFile CreateNew(const std::string &filename)
Create a new UVW file on disk with the given filename.
Definition UvwFile.h:100
uint64_t rows_per_block_
A "block" is a contiguous number of baselines that together form one timestep.
Definition UvwFile.h:408
void WriteUvw(uint64_t row, size_t antenna1, size_t antenna2, const double *uvw)
Write a single row to the column.
Definition UvwFile.h:112
bool block_is_changed_
Definition UvwFile.h:417
BufferedColumnarFile file_
Definition UvwFile.h:389
uint64_t n_rows_
Number of rows in the Uvw column.
Definition UvwFile.h:397
static bool AreNear(std::array< double, 3 > a, std::array< double, 3 > b)
Definition UvwFile.h:364
std::vector< std::array< double, 3 > > block_uvws_
UVW for each antenna in the block.
Definition UvwFile.h:416
uint64_t NRows() const
Definition UvwFile.h:233
static bool AreNear(double a, double b)
Definition UvwFile.h:367
void StoreOrCheck(size_t antenna, const std::array< double, 3 > &antenna_uvw)
If this block does not have a value for the specified antenna, store the uvw value for it.
Definition UvwFile.h:278
bool IsSet(size_t antenna) const
Definition UvwFile.h:361
void ReadHeader()
Definition UvwFile.h:339
UvwFile(const std::string &filename)
Create a new file on disk.
Definition UvwFile.h:240
static constexpr std::array< double, 3 > kUnsetPosition
Definition UvwFile.h:386
UvwFile & operator=(UvwFile &&rhs)
Definition UvwFile.h:75
size_t reference_antenna_
Definition UvwFile.h:410
static std::string UvwAsString(const std::array< double, 3 > &uvw)
Definition UvwFile.h:371
void ReadActiveBlock()
Definition UvwFile.h:305
UvwFile(const std::string &filename, bool)
Open an existing file from disk.
Definition UvwFile.h:247
UvwFile() noexcept=default
size_t start_antenna_2_
This value is used to determine the first baseline in the data, which is the baseline (reference_ante...
Definition UvwFile.h:413
void ReadUvw(uint64_t row, size_t antenna1, size_t antenna2, double *uvw)
Read a single row.
Definition UvwFile.h:189
void WriteActiveBlock()
Definition UvwFile.h:325
~UvwFile() noexcept
Definition UvwFile.h:73
void WriteHeader()
Definition UvwFile.h:352
void ActivateBlock(size_t block)
Definition UvwFile.h:294
For temporary backward namespace compatibility, use casa as alias for casacore.
Definition mainpage.dox:28
void move(TYPE *target, int npixels) const
VarBufferedColumnarFile< 100 *1024 > BufferedColumnarFile
Define real & complex conjugation for non-complex types and put comparisons into std namespace.
Definition Complex.h:344