From b911ca6b697428a13bca5c93b3fb4a4f6d03764f Mon Sep 17 00:00:00 2001 From: Juan Ammerman <juan.ammerman@rai.usc.es> Date: Mon, 26 Apr 2021 23:59:30 +0200 Subject: [PATCH] Draft implementation of ZHS --- corsika/modules/radio/ZHS.hpp | 164 ++++++++++++++++++++++++++-------- 1 file changed, 125 insertions(+), 39 deletions(-) diff --git a/corsika/modules/radio/ZHS.hpp b/corsika/modules/radio/ZHS.hpp index 1b49c8262..4aab762dd 100644 --- a/corsika/modules/radio/ZHS.hpp +++ b/corsika/modules/radio/ZHS.hpp @@ -12,7 +12,7 @@ #include <corsika/framework/geometry/QuantityVector.hpp> #include <corsika/framework/core/PhysicalUnits.hpp> #include <corsika/modules/radio/propagators/SignalPath.hpp> -#include <bits/stdc++.h> +#include <cmath> namespace corsika { @@ -26,8 +26,8 @@ namespace corsika { using Base::detector_; public: - using ElectricFieldVector = - QuantityVector<ElectricFieldType::dimension_type>; +// using PotentialVector = QuantityVector<PotentialVectorType::dimension_type>; + using ElectricFieldVector = QuantityVector<ElectricFieldType::dimension_type>; /** * Construct a new ZHS instance. * @@ -51,55 +51,141 @@ namespace corsika { template <typename Particle, typename Track> ProcessReturn simulate(Particle& particle, Track const& track) const { - //get global simulation time for that track. (This is my best guess for now) - auto startTime_ {particle.getTime() + // TODO: think if we reuse these variables for the case of not being in the Fraunhoffer approx. + auto const startTime_ {particle.getTime() - track.getDuration()}; // time at start point of track. - auto endTime_ {particle.getTime()}; // time at end point of track. - auto midTime_ {(startTime_ + endTime_) / 2}; + auto const endTime_ {particle.getTime()}; // time at end point of track. - auto startPoint_ = track.getPosition(0); + auto const startPoint_{track.getPosition(0)}; + auto const endPoint_{track.getPosition(1)}; + LengthType trackLength_ {(startPoint_ - endPoint_).getNorm()}; // track velocity - auto trackVelocity_ {(track.getVelocity(0) + track.getVelocity(1)) / 2}; + auto const trackVelocity_ {(track.getVelocity(0) + track.getVelocity(1)) / 2}; // beta is defined as velocity / speed of light - auto beta_ { trackVelocity_ / constants::c}; + auto const beta_ { trackVelocity_ / constants::c}; // get particle charge auto const charge_ {get_charge(particle.getPID())}; + // get "mid" position of the track geometrically + auto const midVector_ { (startPoint_ - endPoint_) / 2 }; + auto const midPoint_ {Point(midVector_.getCoordinateSystem(), midVector_.getComponents().getX(), + midVector_.getComponents().getY(), midVector_.getComponents().getZ())}; + //changed if deltaT1_ > deltaT2_, so not const + auto constants {charge_/ (4 * M_PI)/ (constants::epsilonZero) / constants::c}; // we loop over each antenna in the collection for (auto& antenna : detector_.getAntennas()) { + // Probably will be reused in the case of not being in Fraunhoffer. + auto midPaths{this->propagator_.propagate(midPoint_, antenna.getLocation(), 1_m)}; + //Loop over midPaths to first check Fraunhoffer limit + for (size_t i{0}; i < midPaths.size(); i++){ + double const betaTimesK {beta_.dot(midPaths[i].emit_)/beta_.getNorm()}; + double const slitSize_ {1. - betaTimesK * betaTimesK * trackLength_ * trackLength_ /1_m /1_m}; + //Parameter that determines the limit for the Fraunhoffer limit (probably related to antenna sampling rate + double const lambda {0.1}; + double const fraunhLimit {slitSize_/midPaths[i].R_distance_/lambda * 1_m}; + if (fraunhLimit > 1.) // Checks if we are in fraunhoffer domain (maybe it should be less?) + { + ///code for dividing track and calculating field. + std::cout << "This code hasnt been implemented!" << std::endl; + } + else // Calculate vector potential of whole track + { + // paths from start of track to antenna + auto paths1{this->propagator_.propagate(startPoint_, antenna.getLocation(), 1_m)}; + + // paths from end of track to antenna + auto paths2{this->propagator_.propagate(endPoint_, antenna.getLocation(), 1_m)}; + // First implementation, need to check if there are the same number of paths, but once its working. + + //modified later if deltaT1_ > deltaT2_ + auto deltaT1_ {startTime_ + paths1[i].propagation_time_}; + auto deltaT2_ {endTime_ + paths2[i].propagation_time_}; + + long double const gridResolution_{antenna.duration_ / 1_s}; + //make deltaT1_ be the smallest time => changes step function order so + // constants is changed to account for it + if (deltaT1_ > deltaT2_) + { + deltaT1_ = {endTime_ + paths2[i].propagation_time_}; + deltaT2_ = {startTime_ + paths1[i].propagation_time_}; + constants = - constants; + } + + + long const startBin {std::floor((deltaT1_ / 1_s)/gridResolution_)}; + long const endBin {std::floor((deltaT2_ / 1_s) /gridResolution_)}; + + auto const betaPerp_ {midPaths[i].emit_.cross(beta_.cross(midPaths[i].emit_))}; + double const denominator {1 - midPaths[i].refractive_index_source_ * + beta_.dot(midPaths[i].emit_)}; + + long const numberOfBins{endBin - startBin}; + //IMPORTANT!!!!! Using ElectricFieldVector instead of PotentialVector until antenna is adapted + for (long int it{0}; it <= numberOfBins; ++it) + { + if (startBin == endBin)//track contained in bin + { +// std::cout << "Track in one bin !" << std::endl; + if (std::fabs(denominator)>1.e-15L)//if not in Cerenkov angle then + { + double const f {std::fabs((deltaT2_ - deltaT1_)/1_s)/gridResolution_}; + //should be PotentialVector const Vp_ = betaPerp_.getComponents()/denominator/ + // midPaths[i].R_distance_ * constants * f; + // but to make it compile until antenna is adapted it stays like that to test it. + ElectricFieldVector const Vp_ = betaPerp_.getComponents()/denominator/ + midPaths[i].R_distance_ * constants * f / 1_s; +// std::cout << "Vector Potential NOT in Cerenkov Angle: " << Vp_ << std::endl; +// std::cout << "Time " << deltaT2_ << std::endl; + antenna.receive(deltaT2_, betaPerp_, Vp_); + } + else//If emission in Cerenkov angle => approximation + { + double const f {(endTime_ - startTime_)/gridResolution_/1_s}; + ElectricFieldVector const Vp_ = betaPerp_.getComponents()/ + midPaths[i].R_distance_ * constants * f / 1_s; +// std::cout << "Vector Potential in Cerenkov Angle: " << Vp_ << std::endl; +// std::cout << "Time " << deltaT1_ << std::endl; + antenna.receive(deltaT2_, betaPerp_, Vp_); + }//end if Cerenkov angle approx + } + //TODO: should we check for Cerenkov angle? + else //Track is contained in more than one bin + { +// std::cout << "Track in multiple bins !" << std::endl; + if (it == 0)//start of track + { + double const f {std::fabs(startTime_/1_s / gridResolution_ - startBin)}; + ElectricFieldVector const Vp_ = betaPerp_.getComponents() * f * + constants/denominator/midPaths[i].R_distance_ / 1_s; + antenna.receive(deltaT1_, betaPerp_, Vp_); + } + else if (it == numberOfBins)//end of track + { + double const f {std::fabs(endTime_/1_s / gridResolution_ - endBin)}; + ElectricFieldVector const Vp_ = betaPerp_.getComponents() * f * + constants / denominator / midPaths[i].R_distance_ / 1_s; + antenna.receive(deltaT2_, betaPerp_, Vp_); + } + else + { + ElectricFieldVector const Vp_ = betaPerp_.getComponents() * constants / + denominator/midPaths[i].R_distance_ / 1_s; + antenna.receive(deltaT1_ + gridResolution_ * it * 1_s, betaPerp_, Vp_); + } + } + }//end loop over bins in which potential vector is not zero + + }//finish if statement of track in fraunhoffer or not + + }//end loop over mid paths + + } // END: loop over antennas + return ProcessReturn::Ok; + } //end simulate - // get the Path from the track to the antenna - // This is a SignalPathCollection - auto paths{this->propagator_.propagate(startPoint_, antenna.getLocation(), 1_nm)}; - - // now loop over the paths that we got above - // Note: for the StraightPropagator, there will only be a single - // path but other propagators may return more than one. - for (auto const& path : paths) { - - auto t1 = path.total_time_; - - QuantityVector<ElectricFieldType::dimension_type> v11{10_V / 1_m, 10_V / 1_m, 10_V / 1_m}; - - antenna.receive(t1, path.emit_, v11); - // calculate the ZHS formalism for this particle-antenna -// ElectricFieldVector EV_ = ((- constants) * trackVelocity_.dot(path.emit)) * -// ((midTime_ + path.total_time_ - (1 - path.average_refractivity_ * beta_ * -// acos(track.getDirection(0).dot(path.emit_))) * startTime_) -// - (midTime_ + path.total_time_ - (1 - path.average_refractivity_ * beta_ * -// acos(track.getDirection(0).dot(path.emit_))) * endTime_)) -// / (1 - path.average_refractivity_ * beta_ * acos(track.getDirection(0).dot(path.emit_))); -// -// // pass it to the antenna -// antenna.receive(midTime_ + path.total_time_, path.receive_, EV_); - - } // END: loop over paths - - } // END: loop over antennas - } /** * Return the maximum step length for this particle and track. -- GitLab