From 84f853574add7db82b7d3923671bd5cd82bbb86b Mon Sep 17 00:00:00 2001
From: Nikos Karastathis <n.karastathis@kit.edu>
Date: Wed, 24 Feb 2021 19:23:09 +0100
Subject: [PATCH] added ZHS-like approximation to CoREAS + general updates

---
 corsika/modules/radio/CoREAS.hpp | 123 ++++++++++++++++++++++++++-----
 1 file changed, 103 insertions(+), 20 deletions(-)

diff --git a/corsika/modules/radio/CoREAS.hpp b/corsika/modules/radio/CoREAS.hpp
index ad15692f6..a9f8f9c05 100755
--- a/corsika/modules/radio/CoREAS.hpp
+++ b/corsika/modules/radio/CoREAS.hpp
@@ -11,6 +11,7 @@
 #include <corsika/modules/radio/propagators/StraightPropagator.hpp>
 #include <corsika/framework/geometry/QuantityVector.hpp>
 #include <corsika/framework/core/PhysicalUnits.hpp>
+#include <cmath>
 
 namespace corsika {
 
@@ -49,6 +50,9 @@ namespace corsika {
     template <typename Particle, typename Track>
     ProcessReturn simulate(Particle& particle, Track const& track) const {
 
+      // set threshold for application of ZHS-like approximation
+      const double approxThreshold_ {1.0e-3};
+
       //get global simulation time for that track. (This is my best guess for now)
       auto startTime_ {particle.getTime()
                       - track.getDuration()}; // time at start point of track.
@@ -57,6 +61,10 @@ namespace corsika {
       // beta is defined as velocity / speed of light
       auto startBeta_ {track.getVelocity(0) / constants::c};
       auto endBeta_ {track.getVelocity(1) / constants::c};
+      //TODO: check if they are different!!! They shouldn't!!!
+//      auto startBeta_ {(track.getPosition(0) / (particle.getTime()
+//                                                - track.getDuration()) ) / constants::c};
+//      auto endBeta_ {(track.getPosition(1) / particle.getTime() ) / constants::c};
 
       // calculate gamma factor using beta (the proper way would be with energy over mass)
       auto startGamma_ {1. / sqrt(1. - (startBeta_ * startBeta_))};
@@ -72,44 +80,119 @@ namespace corsika {
       // we loop over each antenna in the collection
       for (auto& antenna : detector_.getAntennas()) {
 
+        ElectricFieldVector EV1_ {0_V / 0_m};
+        ElectricFieldVector EV2_ {0_V / 0_m};
+        ElectricFieldVector EV3_ {0_V / 0_m};
+
+        auto startPointReceiveTime_ {0_ns};
+        auto endPointReceiveTime_ {0_ns};
+
         // get the Path (path1) from the start "endpoint" to the antenna.
         // This is a SignalPathCollection
         auto paths1{this->propagator_.propagate(startPoint_, antenna.getLocation())};
-        auto R1_ {(startPoint_ - antenna.getLocation()).getNorm()};
 
-        // now loop over the paths that we got above
+        // set postDoppler to approximate threshold in advance and check after loop
+        // if we need to perform ZHS-like approximation
+        auto preDoppler_ {approxThreshold_};
+        // now loop over the paths for startpoint that we got above
         for (auto const& path : paths1) {
-          auto startPointReceiveTime_ {path.total_time_ + startTime_}; // might do it on the fly
 
-          // CoREAS calculation -> get ElectricFieldVector1
-          ElectricFieldVector EV1_ {(charge_ / constants::c) *
-                                   path.receive_.cross(path.receive_.cross(startBeta_)) /
-                                        (R1_ * (1 - path.average_refractivity_ *
-                                                       startBeta_ * path.receive_))};
+          preDoppler_ = 1. - path.average_refractive_index_ *
+                               startBeta_ * path.emit_;
 
-          // pass it to the antenna
-          antenna.receive(startPointReceiveTime_, path.receive_, EV1_);
+          if (preDoppler_ > approxThreshold_) {
+            startPointReceiveTime_ = path.total_time_ +
+                                              startTime_ ; // might do it on the fly
 
-        } // END: loop over paths
+            // CoREAS calculation -> get ElectricFieldVector1
+            EV1_= (charge_ / constants::c) *
+                path.receive_.cross(path.receive_.cross(startBeta_)) /
+                (path.R_distance_ * preDoppler_);
+
+            // pass it to the antenna
+            antenna.receive(startPointReceiveTime_, path.receive_, EV1_);
+          } else {
+            continue;
+          } // END preDoppler check
+
+        } // END: loop over paths for startpoint
 
         // get the Path (path2) from the end "endpoint" to the antenna.
         // This is a SignalPathCollection
         auto paths2{this->propagator_.propagate(endPoint_, antenna.getLocation())};
-        auto R2_ {(endPoint_ - antenna.getLocation()).getNorm()};
 
+        // set postDoppler to approximate threshold in advance and check after loop
+        // if we need to perform ZHS-like approximation
+        auto postDoppler_ {approxThreshold_};
+        // now loop over the paths for endpoint that we got above
         for (auto const& path : paths2) {
-          auto endPointReceiveTime_ {path.total_time + endTime_}; // might do it on the fly
+          postDoppler_ = 1. - path.average_refractive_index_ *
+                                endBeta_ * path.emit_;
 
-          //CoREAS calculation -> get ElectricFieldVector2
-          ElectricFieldVector EV2_ {(charge_ / constants::c) *
-                                    path.receive_.cross(path.receive_.cross(endBeta_)) /
-                                    (R2_ * (1 - path.average_refractivity_ *
-                                                   endBeta_ * path.receive_))};
+          if (preDoppler_ > approxThreshold_) {
+            auto endPointReceiveTime_ = path.total_time + endTime_ ; // might do it on the fly
+
+            //CoREAS calculation -> get ElectricFieldVector2
+          EV2_ = (charge_ / constants::c) *
+                   path.receive_.cross(path.receive_.cross(endBeta_)) /
+                   (path.R_distance_ * postDoppler_);
 
           // pass it to the antenna
           antenna.receive(endPointReceiveTime_, path.receive_, EV2_);
-
-        } // END: loop over paths
+          } else {
+            continue;
+          } // END postDoppler check
+
+        } // END: loop over paths for endpoint
+
+        // perform ZHS-like calculation close to Cherenkov angle
+        if (fabs(preDoppler_) <= approxThreshold_ || fabs(postDoppler_) <= approxThreshold_) {
+            // get global simulation time for the middle point of that track. (This is my best guess for now)
+            auto midTime_{particle.getTime() - (track.getDuration() / 2)};
+
+            // beta is defined as velocity / speed of light
+            auto midBeta_{track.getVelocity(0.5) / constants::c};
+            //      auto midBeta_ {(track.getPosition(0.5) / (particle.getTime()
+            //                                        - (track.getDuration() / 2) ) / constants::c};
+
+            // calculate gamma factor using beta (the proper way would be with energy over mass)
+            //          auto midGamma_ {1. / sqrt(1. - (midBeta_ * midBeta_))};
+
+            // get start and end position of the track
+            auto midPoint_{track.getPosition(0.5)};
+
+            // get the Path (path3) from the middle "endpoint" to the antenna.
+            // This is a SignalPathCollection
+            auto paths3{this->propagator_.propagate(midPoint_, antenna.getLocation())};
+
+            // now loop over the paths for endpoint that we got above
+            for (auto const& path : paths3) {
+              midDoppler_ = 1. - path.average_refractive_index_ * midBeta_ * path.emit_;
+
+              auto const midPointReceiveTime_{path.total_time_ +
+                                              midTime_}; // might do it on the fly
+
+              // CoREAS calculation -> get ElectricFieldVector3 for "midPoint"
+              EV3_ = (charge_ / constants::c) *
+                     path.receive_.cross(path.receive_.cross(midBeta_)) /
+                     (path.R_distance_ * midDoppler_);
+
+              // pass it to the antenna but first check if "start" or "end" are double counted!!!
+              if (fabs(preDoppler_) > approxThreshold_ &&
+                  fabs(postDoppler_) <= approxThreshold_) {
+
+                antenna.receive(startPointReceiveTime_, path.receive_, -EV1_);
+                antenna.receive(midPointReceiveTime_, path.receive_, EV3_);
+              } else if (fabs(preDoppler_) <= approxThreshold_ &&
+                         fabs(postDoppler_) > approxThreshold_) {
+                antenna.receive(endPointReceiveTime_, path.receive_, -EV2_);
+                antenna.receive(midPointReceiveTime_, path.receive_, EV3_);
+
+              } else {
+                antenna.receive(midPointReceiveTime_, path.receive_, EV3_);
+              } // end deleting double-counted values
+            }
+          } // end of ZHS-like approximation
 
       } // END: loop over antennas
     }
-- 
GitLab