From 167ae7730c85bf8d88a56cca143567f2e04ad608 Mon Sep 17 00:00:00 2001 From: Jakob Maljaars <jakob.maljaars@stcorp.nl> Date: Tue, 14 Jul 2020 17:24:24 +0200 Subject: [PATCH] Resolve "Add GriddedResponse test" --- cpp/LofarMetaDataUtil.cc | 2 +- cpp/antenna.h | 4 +- cpp/beamformer.cc | 41 +++--- cpp/common/types.h | 8 +- cpp/gridded_response/griddedresponse.h | 10 +- cpp/gridded_response/lofargrid.cc | 10 +- cpp/gridded_response/lofargrid.h | 8 +- cpp/telescope/lofar.cc | 155 +------------------- cpp/test/tload_lofar.cc | 78 ++++++++-- demo/comparison-oskar/main.cpp | 3 +- {demo/comparison-oskar => external}/npy.hpp | 0 11 files changed, 121 insertions(+), 198 deletions(-) rename {demo/comparison-oskar => external}/npy.hpp (100%) diff --git a/cpp/LofarMetaDataUtil.cc b/cpp/LofarMetaDataUtil.cc index 55756c04..826467e5 100644 --- a/cpp/LofarMetaDataUtil.cc +++ b/cpp/LofarMetaDataUtil.cc @@ -219,7 +219,7 @@ BeamFormer::Ptr readAntennaField(const Table &table, unsigned int id, assert(aips_flag.shape().isEqual(IPosition(2, 2, aips_offset.ncolumn()))); TileConfig tile_config; - if (name != "LBA") readTileConfig(table, id); + if (name != "LBA") tile_config = readTileConfig(table, id); transformToFieldCoordinates(tile_config, coordinate_system.axes); for (size_t i = 0; i < aips_offset.ncolumn(); ++i) { diff --git a/cpp/antenna.h b/cpp/antenna.h index b4b8ed54..029b840e 100644 --- a/cpp/antenna.h +++ b/cpp/antenna.h @@ -135,9 +135,9 @@ class Antenna { .rotate = options.rotate, .east = TransformToLocalDirection(options.east), .north = TransformToLocalDirection(options.north)}; - matrix22c_t Response = + matrix22c_t response = LocalResponse(time, freq, local_direction, local_options); - return Response; + return response; } /** diff --git a/cpp/beamformer.cc b/cpp/beamformer.cc index f9adb94d..66e260d9 100644 --- a/cpp/beamformer.cc +++ b/cpp/beamformer.cc @@ -25,16 +25,17 @@ std::vector<std::complex<double>> BeamFormer::ComputeGeometricResponse( const double freq, const vector3r_t &direction) const { // Initialize and fill result vector by looping over antennas std::vector<std::complex<double>> result(antennas_.size()); - for (auto &antenna : antennas_) { - const double dl = direction[0] * (antenna->m_phase_reference_position[0] - - local_phase_reference_position_[0]) + - direction[1] * (antenna->m_phase_reference_position[1] - - local_phase_reference_position_[1]) + - direction[2] * (antenna->m_phase_reference_position[2] - - local_phase_reference_position_[2]); + for (std::size_t idx = 0; idx < antennas_.size(); ++idx) { + const double dl = + direction[0] * (antennas_[idx]->m_phase_reference_position[0] - + local_phase_reference_position_[0]) + + direction[1] * (antennas_[idx]->m_phase_reference_position[1] - + local_phase_reference_position_[1]) + + direction[2] * (antennas_[idx]->m_phase_reference_position[2] - + local_phase_reference_position_[2]); double phase = -2 * M_PI * dl / (common::c / freq); - result.push_back({std::sin(phase), std::cos(phase)}); + result[idx] = {std::sin(phase), std::cos(phase)}; } return result; } @@ -48,23 +49,20 @@ BeamFormer::ComputeWeights(const vector3r_t &pointing, double freq) const { double weight_sum[2] = {0.0, 0.0}; std::vector<std::pair<std::complex<double>, std::complex<double>>> result( geometric_response.size()); - for (std::size_t antenna_idx = 0; antenna_idx < antennas_.size(); - ++antenna_idx) { + for (std::size_t idx = 0; idx < antennas_.size(); ++idx) { // Compute conjugate of geometric response - auto phasor_conj = std::conj(geometric_response[antenna_idx]); + auto phasor_conj = std::conj(geometric_response[idx]); // Compute the delays in x/y direction - result.push_back( - {phasor_conj * (1.0 * antennas_[antenna_idx]->m_enabled[0]), - phasor_conj * (1.0 * antennas_[antenna_idx]->m_enabled[1])}); - weight_sum[0] += (1.0 * antennas_[antenna_idx]->m_enabled[0]); - weight_sum[1] += (1.0 * antennas_[antenna_idx]->m_enabled[1]); + result[idx] = {phasor_conj * (1.0 * antennas_[idx]->m_enabled[0]), + phasor_conj * (1.0 * antennas_[idx]->m_enabled[1])}; + weight_sum[0] += (1.0 * antennas_[idx]->m_enabled[0]); + weight_sum[1] += (1.0 * antennas_[idx]->m_enabled[1]); } // Normalize the weight by the number of antennas - for (std::size_t antenna_idx = 0; antenna_idx < antennas_.size(); - ++antenna_idx) { - result[antenna_idx].first /= weight_sum[0]; - result[antenna_idx].second /= weight_sum[1]; + for (std::size_t idx = 0; idx < antennas_.size(); ++idx) { + result[idx].first /= weight_sum[0]; + result[idx].second /= weight_sum[1]; } return result; @@ -79,7 +77,7 @@ matrix22c_t BeamFormer::LocalResponse(real_t time, real_t freq, auto geometric_response = ComputeGeometricResponse(freq, direction); matrix22c_t result = {0}; - for (unsigned int antenna_idx = 0; antenna_idx < antennas_.size(); + for (std::size_t antenna_idx = 0; antenna_idx < antennas_.size(); ++antenna_idx) { auto antenna = antennas_[antenna_idx]; auto antenna_weight = weights[antenna_idx]; @@ -87,7 +85,6 @@ matrix22c_t BeamFormer::LocalResponse(real_t time, real_t freq, matrix22c_t antenna_response = antenna->Response(time, freq, direction, options); - result[0][0] += antenna_weight.first * antenna_geometric_reponse * antenna_response[0][0]; result[0][1] += antenna_weight.first * antenna_geometric_reponse * diff --git a/cpp/common/types.h b/cpp/common/types.h index 0384b2ca..35a440fc 100644 --- a/cpp/common/types.h +++ b/cpp/common/types.h @@ -96,7 +96,13 @@ typedef std::array<vector3r_t, 16> TileConfig; template <typename T, size_t N> std::ostream &operator<<(std::ostream &out, const std::array<T, N> &obj) { - print(out, obj.begin(), obj.end()); + // print(out, obj.begin(), obj.end()); + out << "["; + for (auto it : obj) { + out << it; + if (it != *obj.rbegin()) out << ", "; + } + out << "]\n"; return out; } diff --git a/cpp/gridded_response/griddedresponse.h b/cpp/gridded_response/griddedresponse.h index 35abf9d5..b152850c 100644 --- a/cpp/gridded_response/griddedresponse.h +++ b/cpp/gridded_response/griddedresponse.h @@ -51,7 +51,8 @@ class GriddedResponse { /** * @brief Compute the Beam response for a single station * - * @param buffer Output buffer + * @param buffer Output buffer, compute and set size with + * GriddedResponse::GetBufferSize(1) * @param station_idx Station index, must be smaller than number of stations * in the Telescope * @param time Time, modified Julian date, UTC, in seconds (MJD(UTC), s). @@ -63,13 +64,18 @@ class GriddedResponse { /** * @brief Compute the Beam response for all stations in a Telescope * - * @param buffer Output buffer + * @param buffer Output buffer, compute and set size with + * GriddedResponse::GetBufferSize() * @param time Time, modified Julian date, UTC, in seconds (MJD(UTC), s). * @param frequency Frequency (Hz) */ virtual bool CalculateAllStations(std::complex<float>* buffer, double time, double frequency) = 0; + std::size_t GetBufferSize(std::size_t nstations) { + return std::size_t(nstations * width_ * height_ * 2 * 2); + } + protected: /** * @brief Construct a new Gridded Response object diff --git a/cpp/gridded_response/lofargrid.cc b/cpp/gridded_response/lofargrid.cc index e42bfc40..b6884670 100644 --- a/cpp/gridded_response/lofargrid.cc +++ b/cpp/gridded_response/lofargrid.cc @@ -4,7 +4,7 @@ #include <aocommon/imagecoordinates.h> #include <cmath> -// #include <casacore/measures/TableMeasures/ArrayMeasColumn.h> +#include <iostream> using namespace everybeam::gridded_response; @@ -41,7 +41,7 @@ bool LOFARGrid::CalculateStation(std::complex<float>* buffer, double time, } for (size_t y = 0; y != height_; ++y) { - lane.write(Job{.y = y, .antenna_idx = station_idx}); + lane.write(Job{.y = y, .antenna_idx = station_idx, .buffer_offset = 0}); } lane.write_end(); @@ -73,7 +73,8 @@ bool LOFARGrid::CalculateAllStations(std::complex<float>* buffer, double time, for (size_t y = 0; y != height_; ++y) { for (size_t antenna_idx = 0; antenna_idx != telescope_->GetNrStations(); ++antenna_idx) { - lane.write(Job{.y = y, .antenna_idx = antenna_idx}); + lane.write(Job{ + .y = y, .antenna_idx = antenna_idx, .buffer_offset = antenna_idx}); } } @@ -135,7 +136,8 @@ void LOFARGrid::CalcThread(std::complex<float>* buffer, double time, std::complex<float>* base_buffer = buffer + (x + job.y * height_) * 4; std::complex<float>* ant_buffer_ptr = - base_buffer + job.antenna_idx * values_per_ant; + base_buffer + job.buffer_offset * values_per_ant; + matrix22c_t gain_matrix = telescope_->GetStation(job.antenna_idx) ->Response(time, frequency, itrf_direction, sb_freq, station0_, tile0_); diff --git a/cpp/gridded_response/lofargrid.h b/cpp/gridded_response/lofargrid.h index ea524aa6..6ab3eb99 100644 --- a/cpp/gridded_response/lofargrid.h +++ b/cpp/gridded_response/lofargrid.h @@ -53,7 +53,8 @@ class LOFARGrid final : public GriddedResponse { /** * @brief Compute the Beam response for a single station * - * @param buffer Output buffer + * @param buffer Output buffer, compute and set size with + * GriddedResponse::GetBufferSize(1) * @param stationIdx Station index, must be smaller than number of stations * in the Telescope * @param time Time, modified Julian date, UTC, in seconds (MJD(UTC), s). @@ -65,7 +66,8 @@ class LOFARGrid final : public GriddedResponse { /** * @brief Compute the Beam response for all stations in a Telescope * - * @param buffer Output buffer + * @param buffer Output buffer, compute and set size with + * GriddedResponse::GetBufferSize() * @param time Time, modified Julian date, UTC, in seconds (MJD(UTC), s). * @param freq Frequency (Hz) */ @@ -82,7 +84,7 @@ class LOFARGrid final : public GriddedResponse { std::vector<std::thread> threads_; struct Job { - size_t y, antenna_idx; + size_t y, antenna_idx, buffer_offset; }; aocommon::Lane<Job>* lane_; diff --git a/cpp/telescope/lofar.cc b/cpp/telescope/lofar.cc index e1b02a6b..3cb55809 100644 --- a/cpp/telescope/lofar.cc +++ b/cpp/telescope/lofar.cc @@ -2,6 +2,7 @@ #include "./../gridded_response/lofargrid.h" #include "./../common/math_utils.h" #include "./../common/casa_utils.h" +#include "../LofarMetaDataUtil.h" #include <aocommon/banddata.h> #include <cassert> @@ -11,112 +12,6 @@ using namespace everybeam; using namespace everybeam::telescope; using namespace casacore; -// LOFAR Telescope specific utils -namespace { - -constexpr Antenna::CoordinateSystem::Axes lofar_antenna_orientation = { - { - -std::sqrt(.5), - -std::sqrt(.5), - 0.0, - }, - { - std::sqrt(.5), - -std::sqrt(.5), - 0.0, - }, - {0.0, 0.0, 1.0}, -}; - -// TODO: move to common utils? -vector3r_t transformToFieldCoordinates( - const vector3r_t &position, const Antenna::CoordinateSystem::Axes &axes) { - const vector3r_t result{dot(position, axes.p), dot(position, axes.q), - dot(position, axes.r)}; - return result; -} - -// // Seems LOFAR specific? -// void transformToFieldCoordinates(TileConfig &config, -// const Antenna::CoordinateSystem::Axes &axes) -// { -// for (unsigned int i = 0; i < config.size(); ++i) { -// const vector3r_t position = config[i]; -// config[i][0] = dot(position, axes.p); -// config[i][1] = dot(position, axes.q); -// config[i][2] = dot(position, axes.r); -// } -// } - -// LOFAR specific? Or is this generic for each telescope? -BeamFormer::Ptr readAntennaField(const Table &table, std::size_t id, - ElementResponse::Ptr element_response) { - Antenna::CoordinateSystem coordinate_system = - common::readCoordinateSystem(table, id); - - BeamFormer::Ptr beam_former(new BeamFormer(coordinate_system)); - - ROScalarColumn<String> c_name(table, "NAME"); - ROArrayQuantColumn<Double> c_offset(table, "ELEMENT_OFFSET", "m"); - ROArrayColumn<Bool> c_flag(table, "ELEMENT_FLAG"); - - const string &name = c_name(id); - - // Read element offsets and flags. - Matrix<Quantity> aips_offset = c_offset(id); - assert(aips_offset.shape().isEqual(IPosition(2, 3, aips_offset.ncolumn()))); - - Matrix<Bool> aips_flag = c_flag(id); - assert(aips_flag.shape().isEqual(IPosition(2, 2, aips_offset.ncolumn()))); - - // TileConfig tile_config; - // if(name != "LBA") readTileConfig(table, id); - // transformToFieldCoordinates(tile_config, coordinate_system.axes); - - for (size_t i = 0; i < aips_offset.ncolumn(); ++i) { - vector3r_t antenna_position = {aips_offset(0, i).getValue(), - aips_offset(1, i).getValue(), - aips_offset(2, i).getValue()}; - antenna_position = - transformToFieldCoordinates(antenna_position, coordinate_system.axes); - - Antenna::Ptr antenna; - Antenna::CoordinateSystem antenna_coordinate_system{ - antenna_position, lofar_antenna_orientation}; - if (name == "LBA") { - antenna = Element::Ptr( - new Element(antenna_coordinate_system, element_response, id)); - } else { - antenna = Element::Ptr( - new Element(antenna_coordinate_system, element_response, id)); - - // TODO - // HBA, HBA0, HBA1 - // antenna = make_tile(name, coordinate_system, tile_config, - // element_response); - } - - antenna->m_enabled[0] = !aips_flag(0, i); - antenna->m_enabled[1] = !aips_flag(1, i); - beam_former->AddAntenna(antenna); - } - return beam_former; -} - -vector3r_t readStationPhaseReference(const Table &table, unsigned int id) { - vector3r_t phase_reference = {0.0, 0.0, 0.0}; - const string columnName("LOFAR_PHASE_REFERENCE"); - if (common::hasColumn(table, columnName)) { - ROScalarMeasColumn<MPosition> c_reference(table, columnName); - MPosition mReference = - MPosition::Convert(c_reference(id), MPosition::ITRF)(); - MVPosition mvReference = mReference.getValue(); - phase_reference = {mvReference(0), mvReference(1), mvReference(2)}; - } - return phase_reference; -} -} // namespace - LOFAR::LOFAR(MeasurementSet &ms, const ElementResponseModel model, const Options &options) : Telescope(ms, model, options) { @@ -163,52 +58,6 @@ std::unique_ptr<gridded_response::GriddedResponse> LOFAR::GetGriddedResponse( Station::Ptr LOFAR::ReadStation(const MeasurementSet &ms, std::size_t id, const ElementResponseModel model) const { - ROMSAntennaColumns antenna(ms.antenna()); - assert(nstations_ > id && !antenna.flagRow()(id)); - - // Get station name. - const string name(antenna.name()(id)); - - // Get station position (ITRF). - MPosition mPosition = - MPosition::Convert(antenna.positionMeas()(id), MPosition::ITRF)(); - MVPosition mvPosition = mPosition.getValue(); - const vector3r_t position = {{mvPosition(0), mvPosition(1), mvPosition(2)}}; - - // Create station. - Station::Ptr station(new Station(name, position, model)); - - // Read phase reference position (if available). - station->setPhaseReference(readStationPhaseReference(ms.antenna(), id)); - - Table tab_field = common::getSubTable(ms, "LOFAR_ANTENNA_FIELD"); - tab_field = tab_field(tab_field.col("ANTENNA_ID") == static_cast<Int>(id)); - - // The Station will consist of a BeamFormer that combines the fields - // coordinate system is ITRF - // phase reference is station position - auto beam_former = BeamFormer::Ptr(new BeamFormer( - Antenna::IdentityCoordinateSystem, station->phaseReference())); - - for (std::size_t i = 0; i < tab_field.nrow(); ++i) { - beam_former->AddAntenna( - readAntennaField(tab_field, i, station->get_element_response())); - } - - // TODO - // If There is only one field, the top level beamformer is not needed - // and the station antenna can be set the the beamformer of the field - station->SetAntenna(beam_former); - - size_t field_id = 0; - size_t element_id = 0; - Antenna::CoordinateSystem coordinate_system = - common::readCoordinateSystem(tab_field, field_id); - - // TODO: rotate coordinate system for antenna - auto element = Element::Ptr(new Element( - coordinate_system, station->get_element_response(), element_id)); - station->SetElement(element); - + Station::Ptr station = readStation(ms, id); return station; } \ No newline at end of file diff --git a/cpp/test/tload_lofar.cc b/cpp/test/tload_lofar.cc index 6babadd5..d79b589d 100644 --- a/cpp/test/tload_lofar.cc +++ b/cpp/test/tload_lofar.cc @@ -4,8 +4,11 @@ #include "./../options.h" #include "./../gridded_response/lofargrid.h" #include "./../element_response.h" +#include "../../external/npy.hpp" #include "config.h" +#include <complex> +#include <cmath> using namespace everybeam; @@ -15,7 +18,8 @@ BOOST_AUTO_TEST_CASE(load_lofar) { casacore::MeasurementSet ms(LOFAR_MOCK_MS); // Load LOFAR Telescope - auto telescope = Load(ms, response_model, options); + std::unique_ptr<telescope::Telescope> telescope = + Load(ms, response_model, options); // Assert if we indeed have a LOFAR pointer BOOST_CHECK(nullptr != dynamic_cast<telescope::LOFAR*>(telescope.get())); @@ -27,14 +31,70 @@ BOOST_AUTO_TEST_CASE(load_lofar) { // Assert if GetStation(stationd_id) behaves properly BOOST_CHECK_EQUAL(telescope->GetStation(0)->name(), "CS001HBA0"); - // Get gridded response - coords::CoordinateSystem coord_system; - auto grrp = telescope->GetGriddedResponse(coord_system); + // Properties extracted from MS + double time = 4929192878.008341; + double frequency = 138476562.5; + std::size_t width(4), height(4); + double ra(2.15374123), dec(0.8415521), dl(0.5 * M_PI / 180.), + dm(0.5 * M_PI / 180.), shift_l(0.), shift_m(0.); + + coords::CoordinateSystem coord_system = {.width = width, + .height = height, + .ra = ra, + .dec = dec, + .dl = dl, + .dm = dm, + .phase_centre_dl = shift_l, + .phase_centre_dm = shift_m}; + std::unique_ptr<gridded_response::GriddedResponse> grid_response = + telescope->GetGriddedResponse(coord_system); BOOST_CHECK(nullptr != - dynamic_cast<gridded_response::LOFARGrid*>(grrp.get())); + dynamic_cast<gridded_response::LOFARGrid*>(grid_response.get())); + + // Define buffer and get gridded responses + std::vector<std::complex<float>> antenna_buffer_single( + grid_response->GetBufferSize(1)); + grid_response->CalculateStation(antenna_buffer_single.data(), time, frequency, + 23); + BOOST_CHECK_EQUAL(antenna_buffer_single.size(), + std::size_t(width * height * 2 * 2)); + + // LOFARBeam output at pixel (2,2): + std::vector<std::complex<float>> lofar_p22 = {{-0.175908, -0.000478397}, + {-0.845988, -0.00121503}, + {-0.89047, -0.00125383}, + {0.108123, -5.36076e-05}}; + + // Compare with everybeam + std::size_t offset_22 = (2 + 2 * height) * 4; + for (std::size_t i = 0; i < 4; ++i) { + BOOST_CHECK(std::abs(antenna_buffer_single[offset_22 + i] - lofar_p22[i]) < + 1e-4); + } + + // LOFARBeam output at pixel (1,3): + std::vector<std::complex<float>> lofar_p13 = {{-0.158755, -0.000749433}, + {-0.816165, -0.00272568}, + {-0.863389, -0.00283979}, + {0.0936919, 0.000110673}}; + + // Compare with everybeam + std::size_t offset_13 = (1 + 3 * height) * 4; + for (std::size_t i = 0; i < 4; ++i) { + BOOST_CHECK(std::abs(antenna_buffer_single[offset_13 + i] - lofar_p13[i]) < + 1e-4); + } + + std::vector<std::complex<float>> antenna_buffer_all( + grid_response->GetBufferSize(telescope->GetNrStations())); + grid_response->CalculateAllStations(antenna_buffer_all.data(), time, + frequency); + BOOST_CHECK_EQUAL( + antenna_buffer_all.size(), + std::size_t(telescope->GetNrStations() * width * height * 2 * 2)); - // TODO: add test - // grrp->CalculateStation(0); - // grrp->CalculateStation(); - // grrp->CalculateAllStations(); + // Print to np array + // const long unsigned leshape[] = {(long unsigned int)width, height, 2, 2}; + // npy::SaveArrayAsNumpy("station_responses.npy", false, 4, leshape, + // antenna_buffer_single); } \ No newline at end of file diff --git a/demo/comparison-oskar/main.cpp b/demo/comparison-oskar/main.cpp index e765ccf0..9f9410c1 100644 --- a/demo/comparison-oskar/main.cpp +++ b/demo/comparison-oskar/main.cpp @@ -4,7 +4,8 @@ #include <OSKARElementResponse.h> -#include "npy.hpp" // to save arrays in numpy format +#include "../../external/npy.hpp" +// #include "npy.hpp" // to save arrays in numpy format int main(int argc, char** argv){ // int main() { diff --git a/demo/comparison-oskar/npy.hpp b/external/npy.hpp similarity index 100% rename from demo/comparison-oskar/npy.hpp rename to external/npy.hpp -- GitLab