1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887
|
#ifndef GRIDDING_VISIBILITY_MODIFIER_H_
#define GRIDDING_VISIBILITY_MODIFIER_H_
#include <stdexcept>
#ifdef HAVE_EVERYBEAM
#include <EveryBeam/beammode.h>
#include <EveryBeam/beamnormalisationmode.h>
#include <EveryBeam/pointresponse/pointresponse.h>
#endif
#include <schaapcommon/h5parm/jonesparameters.h>
#include <aocommon/constants.h>
#include <aocommon/matrix2x2.h>
#include <aocommon/multibanddata.h>
#include <aocommon/polarization.h>
#include <aocommon/vectormap.h>
#include "averagecorrection.h"
#include "gainmode.h"
namespace wsclean {
class SynchronizedMS;
struct ObservationInfo;
/**
* Control whether to apply the modifier, sum the corrections or both when
* calling methods that apply a modifier e.g. @ref ApplyConjugatedParmResponse()
*/
enum class ModifierBehaviour { kApply, kSum, kApplyAndSum };
using BeamResponseMap = aocommon::VectorMap<std::vector<aocommon::MC2x2F>>;
using SolutionsResponseMap =
aocommon::VectorMap<std::vector<std::complex<float>>>;
namespace internal {
/**
* @brief Apply gains to the visibilities.
*
* @tparam Mode Which entry or entries from the gain matrices should be
* taken into account when correcting the visibilities? See also the
* documentation of GainMode.
*/
template <GainMode Mode>
void ApplyGain(std::complex<float>* visibilities, const aocommon::MC2x2F& gain1,
const aocommon::MC2x2F& gain2) {
if constexpr (Mode == GainMode::kXX) {
*visibilities = gain1.Get(0) * (*visibilities) * std::conj(gain2.Get(0));
} else if constexpr (Mode == GainMode::kYY) {
*visibilities = gain1.Get(3) * (*visibilities) * std::conj(gain2.Get(3));
} else if constexpr (Mode == GainMode::kTrace) {
// Stokes-I. Have to calculate v' = G1 x v x G2^H:
// v' = 0.5 (V_xx + V_yy) with V = v x (G1 x G2^H)
// V_xx = v x (g1_xx g2_xx* + g1_yx g2_yx*), V_yy = v x (g1_xy g2_xy* +
// g1_yy g2_yy*). Hence v' = 0.5 * double_dot(G1, G2*)
*visibilities *= 0.5f * gain1.DoubleDot(gain2.Conjugate());
} else if constexpr (Mode == GainMode::k2VisDiagonal) {
visibilities[0] = gain1.Get(0) * visibilities[0] * std::conj(gain2.Get(0)) +
gain1.Get(1) * visibilities[1] * std::conj(gain2.Get(1));
visibilities[1] = gain1.Get(3) * visibilities[1] * std::conj(gain2.Get(3)) +
gain1.Get(2) * visibilities[0] * std::conj(gain2.Get(2));
} else if constexpr (Mode == GainMode::kFull) {
// All polarizations
const aocommon::MC2x2F visibilities_mc2x2(visibilities);
const aocommon::MC2x2F result =
gain1.Multiply(visibilities_mc2x2).MultiplyHerm(gain2);
result.AssignTo(visibilities);
}
}
/**
* @brief Apply conjugated gains to the visibilities.
*
* @tparam Mode Which entry or entries from the gain matrices should be
* taken into account when correcting the visibilities? See also the
* documentation of GainMode.
*/
template <GainMode Mode>
void ApplyConjugatedGain(std::complex<float>* visibilities,
const aocommon::MC2x2F& gain1,
const aocommon::MC2x2F& gain2) {
if constexpr (Mode == GainMode::kXX) {
*visibilities = std::conj(gain1.Get(0)) * (*visibilities) * gain2.Get(0);
} else if constexpr (Mode == GainMode::kYY) {
*visibilities = std::conj(gain1.Get(3)) * (*visibilities) * gain2.Get(3);
} else if constexpr (Mode == GainMode::kTrace) {
// See calculation in ApplyGain() for explanation of double dot.
*visibilities *= 0.5f * gain2.DoubleDot(gain1.Conjugate());
} else if constexpr (Mode == GainMode::k2VisDiagonal) {
visibilities[0] = std::conj(gain1.Get(0)) * visibilities[0] * gain2.Get(0) +
std::conj(gain1.Get(2)) * visibilities[1] * gain2.Get(2);
visibilities[1] = std::conj(gain1.Get(3)) * visibilities[1] * gain2.Get(3) +
std::conj(gain1.Get(1)) * visibilities[0] * gain2.Get(1);
} else if constexpr (Mode == GainMode::kFull) {
// All polarizations
const aocommon::MC2x2F visibilities_mc2x2(visibilities);
const aocommon::MC2x2F result =
gain1.HermThenMultiply(visibilities_mc2x2).Multiply(gain2);
result.AssignTo(visibilities);
}
}
constexpr bool ShouldApplyCorrection(ModifierBehaviour behaviour) {
return behaviour == ModifierBehaviour::kApply ||
behaviour == ModifierBehaviour::kApplyAndSum;
}
constexpr bool ShouldSumCorrection(ModifierBehaviour behaviour) {
return behaviour == ModifierBehaviour::kSum ||
behaviour == ModifierBehaviour::kApplyAndSum;
}
template <GainMode Mode, typename T>
constexpr decltype(auto) MakeDiagonalIfScalar(T& matrix) {
if constexpr (AllowScalarCorrection(Mode)) {
return Diagonal(matrix);
} else {
return (matrix);
}
}
template <size_t NParms, GainMode Mode, typename MatrixArrayType>
auto CreateMatrix2x2OrDiag(MatrixArrayType data, size_t offset) {
if constexpr (NParms == 2) {
return aocommon::MC2x2FDiag(data[offset], data[offset + 1]);
} else {
if constexpr (AllowScalarCorrection(Mode)) {
return aocommon::MC2x2FDiag(data[offset], data[offset + 3]);
} else {
return aocommon::MC2x2F(&data[offset]);
}
}
}
template <size_t NParms, typename MatrixArrayType>
auto CreateMatrix2x2OrDiag(MatrixArrayType data, size_t offset) {
if constexpr (NParms == 2) {
return aocommon::MC2x2FDiag(data[offset], data[offset + 1]);
} else {
return aocommon::MC2x2F(&data[offset]);
}
}
template <size_t NParms, typename MatrixArrayType>
auto CreateMatrix2x2(MatrixArrayType data, size_t offset) {
if constexpr (NParms == 2) {
return aocommon::MC2x2F(data[offset], 0.0f, 0.0f, data[offset + 1]);
} else {
return aocommon::MC2x2F(&data[offset]);
}
}
} // namespace internal
/**
* Cache the beam response for all timesteps in the rows of a chunk, maintain a
* row to timestep mapping so that the responses are indexable by row number.
*/
struct BeamResponseCacheChunk {
std::vector<uint32_t> offsets;
/**
* The outer vector contains an element for every timestep in the chunk. The
* elements themselves have a structure like the beam response VectorMaps in
* VisibilityModifier, see e.g. @ref
* VisibilityModifier::GetCachedBeamResponse().
*/
std::vector<BeamResponseMap> responses;
const BeamResponseMap& GetCachedBeamResponseForRow(size_t row) const {
assert(row < offsets.size());
assert(offsets[row] < responses.size());
return responses[offsets[row]];
}
};
/**
* Applies beam and h5parm solutions to visibilities.
* See the documentation for function @ref ApplyConjugatedParmResponse()
* for an overview of parameters that hold for most of these functions.
*/
class VisibilityModifier {
public:
VisibilityModifier() = default;
void InitializeBda(bool uses_bda) { uses_bda_ = uses_bda; }
void InitializePointResponse(SynchronizedMS&& ms,
double facet_beam_update_time,
const std::string& element_response,
const aocommon::MultiBandData& bands,
const std::string& data_column,
const std::string& mwa_path);
void InitializeTimeFrequencySmearing(const ObservationInfo& observation_info,
double interval);
/**
* A function that initializes this visibility modifier for testing. After
* calling this function, the Apply* functions can be called.
* @param beam_response vector of n_channels * n_stations * 4 gain elements.
* @param parm_response vector of n_channels * n_stations * 2
*/
void InitializeMockResponse(
size_t n_channels, size_t n_stations, size_t data_desc_id,
const std::vector<aocommon::MC2x2F>& beam_response,
const std::vector<std::complex<float>>& parm_response);
void SetNoPointResponse() {
#ifdef HAVE_EVERYBEAM
_pointResponse = nullptr;
_cachedBeamResponse.Clear();
#endif
}
void SetBeamInfo(std::string mode, std::string normalisation) {
#ifdef HAVE_EVERYBEAM
_beamModeString = std::move(mode);
_beamNormalisationMode = std::move(normalisation);
#endif
}
void ResetCache(size_t n_measurement_sets) {
_cachedParmResponse.clear();
_cachedMSTimes.clear();
time_offsets_.clear();
#ifdef HAVE_EVERYBEAM
beam_cache_chunks_.clear();
current_beam_cache_chunk_ = nullptr;
previous_beam_cache_chunk_ = nullptr;
#endif
}
/**
* @brief Cache the solutions from a h5 solution file
*/
void InitializeCacheParmResponse(
const std::vector<std::string>& antenna_names,
const aocommon::MultiBandData& selected_bands, size_t ms_index);
/**
* @brief Calculate how much memory cached h5 solution files are
* consuming after calling @ref InitializeCacheParmResponse()
* @return The memory consumption, in bytes.
*/
size_t GetCacheParmResponseSize() const;
/**
* Update the time associated with a cached h5 solution file.
* Cache must be initialised once per MsData before calling this
* method, by calling @ref InitializeCacheParmResponse()
*
* @param time_offset This value is incrementally updated by each call into
* cacheParmResponse and epresents an offset into @ref _cachedParmResponse
* that is needed by functions that will apply the parm response e.g. @ref
* ApplyParmResponse(). Also used internally as an offset into @ref
* _cachedMSTimes to avoid searching data already covered by previous calls.
* See @ref GetTimeOffset() for more information.
*/
void CacheParmResponse(double time, const aocommon::MultiBandData& bands,
size_t ms_index, size_t& time_offset);
const SolutionsResponseMap& GetCachedParmResponse(size_t ms_index) const {
return _cachedParmResponse.find(ms_index)->second;
}
/**
* Applies the conjugate (is backward, or imaging direction) h5parm gain
* to given data.
* @tparam Behaviour Determines whether we should apply the gain, sum the
* correction or both.
* @tparam Mode Gain application mode that defines how the gain is
* applied, the PolarizationCount is also implied/determined by the gain mode.
* @param [in,out] data Data array with n_channels x PolarizationCount
* elements.
* @param weights Array with for each data value the corresponding weight.
* @param image_weights Array of size n_channels (polarizations are assumed to
* have equal imaging weights) with the imaging weighting mode (e.g. from
* Briggs weighting).
* @param apply_forward If true, an additional (non-conjugate) forward gain is
* applied to the data. This is necessary for calculating direction-dependent
* PSFs.
*/
template <ModifierBehaviour Behaviour, GainMode Mode, size_t NParms,
bool ApplyForward>
void ApplyConjugatedParmResponseForRow(std::complex<float>* data,
const float* weights,
const float* image_weights,
size_t ms_index, size_t n_channels,
size_t data_desc_id, size_t n_antennas,
size_t antenna1, size_t antenna2) {
ApplyConjugatedParmResponseForRow<Behaviour, Mode, NParms, ApplyForward>(
data, weights, image_weights, ms_index, n_channels, data_desc_id,
n_antennas, antenna1, antenna2, GetTimeOffset(ms_index));
}
template <ModifierBehaviour Behaviour, GainMode Mode, size_t NParms,
bool ApplyForward>
void ApplyConjugatedParmResponseForRow(std::complex<float>* data,
const float* weights,
const float* image_weights,
size_t ms_index, size_t n_channels,
size_t data_desc_id, size_t n_antennas,
size_t antenna1, size_t antenna2,
size_t time_offset) {
const std::complex<float>* parm_response =
_cachedParmResponse[ms_index][data_desc_id].data();
const size_t n_visibilities = GetNVisibilities(Mode);
for (size_t n_channel = 0; n_channel < n_channels; ++n_channel) {
ApplyConjugatedParmResponse<Behaviour, Mode, NParms, ApplyForward>(
parm_response, data, weights, image_weights, n_channel, n_channels,
n_antennas, antenna1, antenna2, time_offset);
if constexpr (internal::ShouldApplyCorrection(Behaviour)) {
data += n_visibilities;
}
if constexpr (internal::ShouldSumCorrection(Behaviour)) {
weights += n_visibilities;
}
}
}
template <ModifierBehaviour Behaviour, GainMode Mode, size_t NParms,
bool ApplyForward>
void ApplyConjugatedParmResponse(const std::complex<float>* parm_response,
std::complex<float>* data,
const float* weights,
const float* image_weights, size_t n_channel,
size_t n_channels, size_t n_antennas,
size_t antenna1, size_t antenna2,
size_t time_offset);
template <GainMode GainEntry>
void ApplyParmResponse(std::complex<float>* data, size_t ms_index,
size_t n_channels, size_t data_desc_id,
size_t n_antennas, size_t antenna1, size_t antenna2) {
ApplyParmResponseWithTime<GainEntry>(data, ms_index, n_channels,
data_desc_id, n_antennas, antenna1,
antenna2, GetTimeOffset(ms_index));
}
template <GainMode GainEntry>
void ApplyParmResponseWithTime(std::complex<float>* data, size_t ms_index,
size_t n_channels, size_t data_desc_id,
size_t n_antennas, size_t antenna1,
size_t antenna2, size_t time_offset);
void ApplyTimeFrequencySmearing(std::complex<float>* data, const double* uvw,
const aocommon::BandData& band, int n,
double l, double m);
void SetMSTimes(size_t ms_index, std::shared_ptr<std::vector<double>> times) {
_cachedMSTimes[ms_index] = std::move(times);
}
void SetH5Parm(
const std::vector<schaapcommon::h5parm::H5Parm>& h5parms,
const std::vector<schaapcommon::h5parm::SolTab*>& first_solutions,
const std::vector<schaapcommon::h5parm::SolTab*>& second_solutions,
const std::vector<schaapcommon::h5parm::GainType>& gain_types) {
_h5parms = &h5parms;
_firstSolutions = &first_solutions;
_secondSolutions = &second_solutions;
_gainTypes = &gain_types;
}
bool HasH5Parm() const { return _h5parms && !_h5parms->empty(); }
/**
* Return the current time offset for the MS corresponding to `ms_index`
* The time offset is a value that is incrementally updated inside @ref
* CacheParmResponse() for every @ref ApplyCorrection() call and represents an
* offset from the start of @ref _cachedMSTimes that can be used to speed up
* subsequent searches in subsequent calls to @ref CacheParmResponse() as well
* as an index into @ref _cachedParmResponse that is needed by functions that
* apply the parm response e.g. @ref ApplyParmResponse()
*/
size_t GetTimeOffset(size_t ms_index) { return time_offsets_[ms_index]; }
void SetTimeOffset(size_t ms_index, size_t time_offset) {
time_offsets_[ms_index] = time_offset;
}
/**
* Return the beam response cache for a given procesing chunk. Internal
* references are released and ownership given to the caller.
*/
std::shared_ptr<BeamResponseCacheChunk> TakeCachedBeamResponse(size_t chunk) {
#ifdef HAVE_EVERYBEAM
assert(beam_cache_chunks_[chunk]);
assert(beam_cache_chunks_[chunk].get() != current_beam_cache_chunk_);
std::shared_ptr<BeamResponseCacheChunk> beam_response =
beam_cache_chunks_[chunk];
beam_cache_chunks_[chunk] = nullptr;
return beam_response;
#else
return nullptr;
#endif
}
/**
* Start processing a new chunk.
* @ref FinishProcessingChunk() must be called at end of processing the chunk.
* @ref FinishChunkedProcessing() must be called after processing all chunks.
*
* @param check_for_empty Should always be set to true when called externally.
* It exists for internal usage only.
*/
void StartProcessingChunk(bool check_for_empty = true) {
#ifdef HAVE_EVERYBEAM
// Temporary access is required to the previous cache to compute the first
// response in the new cache.
//
// Only add a new chunk if it will be the first chunk (check_for_empty=true)
// Internally when called from FinishChunkedProcessing() always add a chunk
// (check_for_empty=false)
// This behaviour is necessary to prevent a data race with
// TakeCachedBeamResponse() on debug builds.
if (!check_for_empty || beam_cache_chunks_.empty()) {
current_beam_cache_chunk_ =
beam_cache_chunks_
.emplace_back(std::make_shared<BeamResponseCacheChunk>())
.get();
}
#endif
}
/**
* Finalise processing of a chunk started by @ref StartProcessingChunk().
* NB! @ref FinishChunkedProcessing() must also be called after processing the
* last chunk.
*/
void FinishProcessingChunk() {
#ifdef HAVE_EVERYBEAM
assert(!beam_cache_chunks_.empty());
// Temporary access is required to the previous cache to compute the first
// response in the new cache.
previous_beam_cache_chunk_ = beam_cache_chunks_.back();
StartProcessingChunk(false);
#endif
}
/**
* Finalise any internal state after processing all data.
* Must be called once at end of processing when using @ref
* StartProcessingChunk()
*/
void FinishChunkedProcessing() {
#ifdef HAVE_EVERYBEAM
current_beam_cache_chunk_ = nullptr;
previous_beam_cache_chunk_ = nullptr;
#endif
}
/**
* Get the cached beam response data for the most recently processed timestep.
*/
BeamResponseMap& GetCachedBeamResponse() {
#ifdef HAVE_EVERYBEAM
return _cachedBeamResponse;
#else
static BeamResponseMap empty_map;
return empty_map;
#endif
}
#ifdef HAVE_EVERYBEAM
/**
* Compute and cache the beam response for the provided time; if it is not
* already cached.
*/
void CacheBeamResponse(double time, size_t field_id,
const aocommon::MultiBandData& bands,
bool cache_entire_beam) {
if (cache_entire_beam) {
CacheBeamResponseWithCacheChunk(time, field_id, bands);
} else {
UpdateCachedBeamResponseForRow(time, field_id, bands,
_cachedBeamResponse);
}
}
/**
* Compute the beam response for the provided time; if it is not already
* cached.
* Update the cache chunk with the new response or index to existing response.
*/
void CacheBeamResponseWithCacheChunk(double time, size_t field_id,
const aocommon::MultiBandData& bands) {
// Add a new response to the cache if its a new time interval.
if (UpdateCachedBeamResponseForRow(time, field_id, bands,
_cachedBeamResponse)) {
current_beam_cache_chunk_->responses.push_back(_cachedBeamResponse);
}
// If we have not entered a new time interval, but are processing the first
// row of a new chunk, there is no previous reponse to reference.
// In this specific case it becomes necessarry to retrieve/copy the last
// response of the previous chunks cache.
if (previous_beam_cache_chunk_) {
if (current_beam_cache_chunk_->responses.empty()) {
current_beam_cache_chunk_->responses.push_back(
previous_beam_cache_chunk_->responses.back());
}
previous_beam_cache_chunk_ = nullptr;
}
// Current row indexes the most recent response.
// Either the freshly generated response if this is a new time interval,
// otherwise the previously cached response.
current_beam_cache_chunk_->offsets.push_back(
current_beam_cache_chunk_->responses.size() - 1);
}
/**
* Compute the beam response for the current time if the time is in a new time
* interval. Return true if new beam response computed; otherwise false.
*/
bool UpdateCachedBeamResponseForRow(double time, size_t field_id,
const aocommon::MultiBandData& bands,
BeamResponseMap& cached_beam_response);
template <GainMode Mode>
void ApplyBeamResponse(std::complex<float>* data, size_t n_channels,
size_t data_desc_id, size_t antenna1, size_t antenna2);
template <ModifierBehaviour Behaviour, GainMode Mode, bool ApplyForward>
void ApplyConjugatedBeamResponseForRow(std::complex<float>* data,
const float* weights,
const float* image_weights,
size_t n_channels, size_t data_desc_id,
size_t antenna1, size_t antenna2) {
const size_t n_visibilities = GetNVisibilities(Mode);
const BeamResponseMap& cached_beam_response = GetCachedBeamResponse();
for (size_t n_channel = 0; n_channel < n_channels; ++n_channel) {
ApplyConjugatedBeamResponse<Behaviour, Mode, ApplyForward>(
data, weights, image_weights, n_channel, n_channels, data_desc_id,
antenna1, antenna2, cached_beam_response);
if constexpr (internal::ShouldApplyCorrection(Behaviour)) {
data += n_visibilities;
}
if constexpr (internal::ShouldSumCorrection(Behaviour)) {
weights += n_visibilities;
}
}
}
template <ModifierBehaviour Behaviour, GainMode Mode, bool ApplyForward>
void ApplyConjugatedBeamResponse(std::complex<float>* data,
const float* weights,
const float* image_weights, size_t n_channel,
size_t n_channels, size_t data_desc_id,
size_t antenna1, size_t antenna2,
const BeamResponseMap& cached_beam_response);
/**
* Correct the data for both the conjugated beam and the
* conjugated h5parm solutions.
*/
template <ModifierBehaviour Behaviour, GainMode Mode, size_t NParms>
void ApplyConjugatedDualForRow(std::complex<float>* data,
const float* weights,
const float* image_weights, size_t n_channels,
size_t data_desc_id, size_t antenna1,
size_t antenna2, size_t ms_index,
bool apply_forward) {
ApplyConjugatedDualForRowWithTime<Behaviour, Mode, NParms>(
data, weights, image_weights, n_channels, data_desc_id, antenna1,
antenna2, ms_index, apply_forward, GetTimeOffset(ms_index));
}
template <ModifierBehaviour Behaviour, GainMode Mode, size_t NParms>
void ApplyConjugatedDualForRowWithTime(std::complex<float>* data,
const float* weights,
const float* image_weights,
size_t n_channels, size_t data_desc_id,
size_t antenna1, size_t antenna2,
size_t ms_index, bool apply_forward,
size_t time_offset) {
const std::complex<float>* parm_response =
_cachedParmResponse[ms_index][data_desc_id].data();
const size_t n_visibilities = GetNVisibilities(Mode);
const BeamResponseMap& cached_beam_response = GetCachedBeamResponse();
for (size_t n_channel = 0; n_channel < n_channels; ++n_channel) {
ApplyConjugatedDual<Behaviour, Mode, NParms>(
parm_response, data, weights, image_weights, n_channel, n_channels,
data_desc_id, antenna1, antenna2, apply_forward, time_offset,
cached_beam_response);
if constexpr (internal::ShouldApplyCorrection(Behaviour)) {
data += n_visibilities;
}
if constexpr (internal::ShouldSumCorrection(Behaviour)) {
weights += n_visibilities;
}
}
}
template <ModifierBehaviour Behaviour, GainMode Mode, size_t NParms>
void ApplyConjugatedDual(const std::complex<float>* parm_response,
std::complex<float>* data, const float* weights,
const float* image_weights, size_t n_channel,
size_t n_channels, size_t data_desc_id,
size_t antenna1, size_t antenna2, bool apply_forward,
size_t time_offset,
const BeamResponseMap& cached_beam_response);
#endif
void SetFacetDirection(double ra, double dec) {
_facetDirectionRA = ra;
_facetDirectionDec = dec;
}
double FacetDirectionRA() const { return _facetDirectionRA; }
double FacetDirectionDec() const { return _facetDirectionDec; }
void ResetSums() {
correction_sum_ = AverageCorrection();
beam_correction_sum_ = AverageCorrection();
}
/**
* Sum of the full corrections applied to the visibilities. In case both
* beam and h5parm solutions are applied, this is the weighed sum over the
* (squared) product of both. Otherwise it is over the (squared) contribution
* of either the beam or solutions.
*
* It is not an average yet, because this class doesn't store the sum of
* weights. It would be a redundant calculation, because the gridder
* already calculates the sum of weights.
*/
const AverageCorrection& TotalCorrectionSum() const {
return correction_sum_;
}
/**
* In case both beam and solution gains are applied, this represents the beam
* part of the corrections. In that case, it is the weighed sum of the squared
* beam matrices. This is used in the final primary beam correction to
* (approximately) separate the beam and solution parts to be able to apply a
* smooth beam correction. If only one correction is applied, this value
* remains zero.
*
* Like @ref TotalCorrectionSum(), this should be divided by the sum of
* weights to turn it into the average correction.
*/
const AverageCorrection& BeamCorrectionSum() const {
return beam_correction_sum_;
}
/**
* Number of complex values per solution: 4 in the case of fulljones,
* otherwise 2 (scalar solutions are already converted to dual solutions
* by the h5parm reader).
*/
inline size_t NValuesPerSolution(size_t ms_index) const {
using schaapcommon::h5parm::GainType;
const size_t solution_index = (*_gainTypes).size() == 1 ? 0 : ms_index;
return (*_gainTypes)[solution_index] == GainType::kFullJones ? 4 : 2;
}
private:
#ifdef HAVE_EVERYBEAM
void GetBeamResponse(const aocommon::BandData& band, size_t field_id,
std::vector<aocommon::MC2x2F>& result) const;
// _telescope attribute needed to keep the telecope in _pointResponse alive
std::unique_ptr<everybeam::telescope::Telescope> _telescope;
std::unique_ptr<everybeam::pointresponse::PointResponse> _pointResponse;
/**
* The beam response for the currently processed timestep. The VectorMap
* is indexed by data_desc_id. The vector is of size n_channels x
* n_stations, where n_stations is the fastest changing index.
*/
BeamResponseMap _cachedBeamResponse;
/** Hold the caches for all processed chunks in memory until they are no
* longer required.
* Calling @ref TakeCachedBeamResponse() will set the corresponding entry to
* null and pass ownership to the caller via shared pointer.
* Shared pointer is used in place of unique pointer because
* `previous_beam_cache_chunk_` also references this data; while this
* reference will usually be short lived it is not impossible that it outlives
* the pointer returned by @ref TakeCachedBeamResponse(). */
std::vector<std::shared_ptr<BeamResponseCacheChunk>> beam_cache_chunks_;
BeamResponseCacheChunk* current_beam_cache_chunk_ = nullptr;
/* The previous cache is kept in memory until we have processed the first row
* of the current cache. This is because the last response of the previous
* cache needs to be copied in some instances. */
std::shared_ptr<BeamResponseCacheChunk> previous_beam_cache_chunk_ = nullptr;
everybeam::BeamMode _beamMode = everybeam::BeamMode::kNone;
#endif
/**
* This value is retrieved from the EveryBeam telescope class, and only
* available when the beam is applied.
*/
size_t n_stations_ = 0;
std::string _beamModeString;
std::string _beamNormalisationMode;
/**
* Element ms_index is a VectorMap of vectors, where an element is
* indexed by data_desc_id and contains the complex gains of
* size n_times x n_channels x n_stations x n_parameters, where n_parameters
* is the fastest changing. n_parameters is 2 (for diagonal) or
* 4 (for full jones).
*
* The ms_index may not be a consecutive index because this gridder
* may not have to grid all measurement sets that were specified to
* wsclean.
*/
std::map<size_t, SolutionsResponseMap> _cachedParmResponse;
/**
* Each element holds a vector with the measurement set times. The map
* is indexed by a (non-consecutive) ms_index.
*/
std::map<size_t, std::shared_ptr<std::vector<double>>> _cachedMSTimes;
/**
* Each element holds the current offset position into the _cachedParmResponse
* and _cachedMSTimes elements of the same ms_index. The map is indexed by a
* (non-consecutive) ms_index.
*/
std::map<size_t, size_t> time_offsets_;
/**
* Optional pointers to vectors with h5parm solution objects.
* The GriddingTaskManager, which always outlives GriddingTasks and their
* VisibilityModifier, owns the objects.
* If all measurement sets use the same solution, the vectors have one
* element. Otherwise, they have one element for each ms.
* The second solution table is optional. If both tables are used, the first
* table has the amplitude part and the second table has the phase part.
* @{
*/
const std::vector<schaapcommon::h5parm::H5Parm>* _h5parms = nullptr;
const std::vector<schaapcommon::h5parm::SolTab*>* _firstSolutions = nullptr;
const std::vector<schaapcommon::h5parm::SolTab*>* _secondSolutions = nullptr;
const std::vector<schaapcommon::h5parm::GainType>* _gainTypes = nullptr;
/** @} */
double _facetDirectionRA = 0.0;
double _facetDirectionDec = 0.0;
AverageCorrection correction_sum_;
AverageCorrection beam_correction_sum_;
double scaled_ncp_uvw_[3];
bool uses_bda_ = false;
};
#ifdef HAVE_EVERYBEAM
template <ModifierBehaviour Behaviour, GainMode Mode, bool ApplyForward>
inline void VisibilityModifier::ApplyConjugatedBeamResponse(
std::complex<float>* data, const float* weights, const float* image_weights,
size_t n_channel, size_t n_channels, size_t data_desc_id, size_t antenna1,
size_t antenna2,
const aocommon::VectorMap<std::vector<aocommon::MC2x2F>>&
cached_beam_response) {
using internal::MakeDiagonalIfScalar;
const size_t offset = n_channel * n_stations_;
const size_t offset1 = offset + antenna1;
const size_t offset2 = offset + antenna2;
const aocommon::MC2x2F& gain1(cached_beam_response[data_desc_id][offset1]);
const aocommon::MC2x2F& gain2(cached_beam_response[data_desc_id][offset2]);
if constexpr (internal::ShouldApplyCorrection(Behaviour)) {
if constexpr (ApplyForward) {
internal::ApplyGain<Mode>(data, gain1, gain2);
}
internal::ApplyConjugatedGain<Mode>(data, gain1, gain2);
}
if constexpr (internal::ShouldSumCorrection(Behaviour)) {
// This assumes that the weights of the polarizations are the same
correction_sum_.Add<Mode>(MakeDiagonalIfScalar<Mode>(gain1),
MakeDiagonalIfScalar<Mode>(gain2),
image_weights[n_channel] * weights[0]);
}
}
template <ModifierBehaviour Behaviour, GainMode Mode, size_t NParms>
inline void VisibilityModifier::ApplyConjugatedDual(
const std::complex<float>* parm_response, std::complex<float>* data,
const float* weights, const float* image_weights, size_t n_channel,
size_t n_channels, size_t data_desc_id, size_t antenna1, size_t antenna2,
bool apply_forward, size_t time_offset,
const aocommon::VectorMap<std::vector<aocommon::MC2x2F>>&
cached_beam_response) {
using internal::CreateMatrix2x2OrDiag;
using internal::MakeDiagonalIfScalar;
// Get facet beam
const size_t beam_offset = n_channel * n_stations_;
const size_t beam_offset1 = beam_offset + antenna1;
const size_t beam_offset2 = beam_offset + antenna2;
const aocommon::MC2x2F& gain_b_1(
cached_beam_response[data_desc_id][beam_offset1]);
const aocommon::MC2x2F& gain_b_2(
cached_beam_response[data_desc_id][beam_offset2]);
// Get h5 solution
// Column major indexing
const size_t h5_offset =
(time_offset * n_channels + n_channel) * n_stations_ * NParms;
const size_t h5_offset1 = h5_offset + antenna1 * NParms;
const size_t h5_offset2 = h5_offset + antenna2 * NParms;
const auto gain_h5_1 =
CreateMatrix2x2OrDiag<NParms>(parm_response, h5_offset1);
const auto gain_h5_2 =
CreateMatrix2x2OrDiag<NParms>(parm_response, h5_offset2);
// Combine H5parm and beam. The beam is applied first on the data,
// and therefore needs to be the last in the multiplication.
const aocommon::MC2x2F gain_combined_1 = gain_h5_1 * gain_b_1;
const aocommon::MC2x2F gain_combined_2 = gain_h5_2 * gain_b_2;
if constexpr (internal::ShouldApplyCorrection(Behaviour)) {
if (apply_forward) {
internal::ApplyGain<Mode>(data, gain_combined_1, gain_combined_2);
}
internal::ApplyConjugatedGain<Mode>(data, gain_combined_1, gain_combined_2);
}
if constexpr (internal::ShouldSumCorrection(Behaviour)) {
beam_correction_sum_.Add<Mode>(MakeDiagonalIfScalar<Mode>(gain_b_1),
MakeDiagonalIfScalar<Mode>(gain_b_2),
weights[0] * image_weights[n_channel]);
correction_sum_.Add<Mode>(MakeDiagonalIfScalar<Mode>(gain_combined_1),
MakeDiagonalIfScalar<Mode>(gain_combined_2),
weights[0] * image_weights[n_channel]);
}
}
#endif // HAVE_EVERYBEAM
template <ModifierBehaviour Behaviour, GainMode Mode, size_t NParms,
bool ApplyForward>
inline void VisibilityModifier::ApplyConjugatedParmResponse(
const std::complex<float>* parm_response, std::complex<float>* data,
const float* weights, const float* image_weights, size_t n_channel,
size_t n_channels, size_t n_antennas, size_t antenna1, size_t antenna2,
size_t time_offset) {
using internal::CreateMatrix2x2;
using internal::CreateMatrix2x2OrDiag;
using internal::MakeDiagonalIfScalar;
// Column major indexing
const size_t offset =
(time_offset * n_channels + n_channel) * n_antennas * NParms;
const size_t offset1 = offset + antenna1 * NParms;
const size_t offset2 = offset + antenna2 * NParms;
if constexpr (internal::ShouldApplyCorrection(Behaviour)) {
const aocommon::MC2x2F gain1 =
CreateMatrix2x2<NParms>(parm_response, offset1);
const aocommon::MC2x2F gain2 =
CreateMatrix2x2<NParms>(parm_response, offset2);
if constexpr (ApplyForward) {
internal::ApplyGain<Mode>(data, gain1, gain2);
}
internal::ApplyConjugatedGain<Mode>(data, gain1, gain2);
}
if constexpr (internal::ShouldSumCorrection(Behaviour)) {
// Assumes that the weights of the polarizations are the same
const auto gain1 =
CreateMatrix2x2OrDiag<NParms, Mode>(parm_response, offset1);
const auto gain2 =
CreateMatrix2x2OrDiag<NParms, Mode>(parm_response, offset2);
correction_sum_.Add<Mode>(gain1, gain2,
image_weights[n_channel] * weights[0]);
}
}
namespace internal {
inline double Sinc(double x) {
return std::abs(x) < 1e-6 ? 1.0 : std::sin(x) / x;
}
} // namespace internal
inline void VisibilityModifier::ApplyTimeFrequencySmearing(
std::complex<float>* data, const double* uvw,
const aocommon::BandData& band, int n_pol_per_vis, double dl, double dm) {
using internal::Sinc;
if (dl != 0.0 || dm != 0.0) {
const double dn = std::sqrt(1.0 - dl * dl - dm * dm) - 1.0;
const double smearing_factor_frequency =
Sinc(M_PI * (uvw[0] * dl + uvw[1] * dm + uvw[2] * dn) /
aocommon::kSpeedOfLight * band.ChannelWidth(0));
// Below terms with scaled_ncp_uvw_[0] are commented out because for epoch
// J2000 this element is zero. In case the initialization of
// scaled_ncp_uvw_ is modified to the current epoch,
// these terms need to be uncommented, because then they are non-zero.
const double delta_time =
M_PI *
(dl * (uvw[1] * scaled_ncp_uvw_[2] - uvw[2] * scaled_ncp_uvw_[1]) +
dm * (/* uvw[2] * scaled_ncp_uvw_[0] */ -uvw[0] * scaled_ncp_uvw_[2]) +
dn * (uvw[0] *
scaled_ncp_uvw_[1] /* - uvw[1] * scaled_ncp_uvw_[0] */)) /
aocommon::kSpeedOfLight;
const size_t n_channels = band.ChannelCount();
const size_t n = n_pol_per_vis * n_channels;
for (size_t i = 0; i < n; ++i) {
data[i] *= smearing_factor_frequency *
Sinc(delta_time * band.ChannelFrequency(i));
}
}
}
} // namespace wsclean
#endif
|