{{lastupdated_at}} by {{lastupdated_by}} h1. TRA file format The @TRA@ file format is used by MEGALib to store event information. Check the MEGALib methods @src/revan/src/MRERawEvent::ParseLine@ to better understand how to interpret the format. Note the inheritance of this class:

class MRERawEvent : public MRESE, public MRotationInterface
h2. TI Parameters: # [float] Defines the event time in seconds. Time zero corresponds to @1970-01-01T01:00:00@. Here is the relevant code in @src/revan/src/MRERawEvent::ParseLine@:

m_EventTime.Set(Line)
bool MTime::Set(const char* Line)
m_Seconds
m_NanoSeconds
}
Here a GammaLib code snippet for @TI=1465776080.278382500@ that gives the event time in UTC:

time  = gammalib.GTime('1970-01-01T01:00:00')
time += 1465776080.278382500
print(time.utc())
2016-06-13T01:00:54
h2. RX Parameters: # [float] @m_DetectorRotationXAxis[0]@ [cm] (default: 1.0) # [float] @m_DetectorRotationXAxis[1]@ [cm] (default: 0.0) # [float] @m_DetectorRotationXAxis[2]@ [cm] (default: 0.0) h2. RZ Parameters: # [float] @m_DetectorRotationZAxis[0]@ [cm] (default: 0.0) # [float] @m_DetectorRotationZAxis[1]@ [cm] (default: 0.0) # [float] @m_DetectorRotationZAxis[2]@ [cm] (default: 1.0) h2. GX Galactic longitude of detector X axis. Parameters: # [float] @Longitude@ [deg] # [float] @Latitude@ [deg]

SetGalacticPointingXAxis(Longitude, Latitude)
h2. GZ Galactic longitude of detector Z axis. Parameters: # [float] @Longitude@ [deg] # [float] @Latitude@ [deg]

SetGalacticPointingZAxis(Longitude, Latitude)
h2. HX Horizon of detector X axis. Parameters: # [float] @Azimuth@ [deg] # [float] @Altitude@ [deg] The information is set by

m_HorizonPointingXAxis.SetMagThetaPhi(1.0, (90-Altitude)*c_Rad, Azimuth*c_Rad)
which uses the @MVector::SetMagThetaPhi@ method

void MVector::SetMagThetaPhi(double Magnitude, double Theta, double Phi) 
{
  // Set in spherical coordinates

  double A = fabs(Magnitude);
  m_X = A*sin(Theta)*cos(Phi);
  m_Y = A*sin(Theta)*sin(Phi);
  m_Z = A*cos(Theta);
}
h2. HZ Horizon of detector Z axis. Parameters: # [float] @Azimuth@ [deg] # [float] @Altitude@ [deg] The same method

m_HorizonPointingZAxis.SetMagThetaPhi(1.0, (90-Altitude)*c_Rad, Azimuth*c_Rad)
is used (see HX) h2. CE Energy of Compton event, sets members of the @MComptonEvent@ class. Parameters: # [float] @m_Eg@ [keV] # [float] @m_dEg@ [keV] # [float] @m_Ee@ [keV] # [float] @m_dEe@ [keV] h2. CD Direction of Compton events, sets members of the @MComptonEvent@ class. Parameters: # [float] @m_C1[0]@ [cm] # [float] @m_C1[1]@ [cm] # [float] @m_C1[2]@ [dm] # [float] @m_dC1[0]@ [cm] # [float] @m_dC1[1]@ [cm] # [float] @m_dC1[2]@ [dm] # [float] @m_C2[0]@ [cm] # [float] @m_C2[1]@ [cm] # [float] @m_C2[2]@ [dm] # [float] @m_dC2[0]@ [cm] # [float] @m_dC2[1]@ [cm] # [float] @m_dC2[2]@ [dm] # [float] @m_Ce[0]@ [cm] # [float] @m_Ce[1]@ [cm] # [float] @m_Ce[2]@ [dm] # [float] @m_dCe[0]@ [cm] # [float] @m_dCe[1]@ [cm] # [float] @m_dCe[2]@ [dm] h2. TL Track length, sets member of @MComptonEvent@ class. Parameters: # [float] @m_TrackLength@ [cm] h2. TE Track initial deposit, sets member of @MComptonEvent@ class. Parameters: # [float] @m_TrackInitialDeposit@ [keV] h2. LA Lever arm, sets member of @MComptonEvent@ class. Parameters: # [float] @m_LeverArm@ [cm] h2. SQ Sequence length, sets member of @MComptonEvent@ class. Parameters: # [int] @m_SequenceLength@ h2. PQ Clustering quality factor, sets member of @MComptonEvent@ class. Parameters: # [float] @m_ClusteringQualityFactor@ h2. CT Compton quality factor, sets member of @MComptonEvent@ class. Parameters: # [float] @m_ComptonQualityFactor1@ # [float] @m_ComptonQualityFactor2@ (optional, default: 1.0) h2. Computation of Chi and Psi In the detector system, the event scatter direction is defined by Chi and Psi, where Chi is an azimuth angle and Psi is a zenith angle. The computation of Chi and Psi is done in MEGAlib in @src/response/src/MResponseImagingBinnedMode::Analyze@:

  // Get the data space information
  MRotation Rotation = Compton->GetDetectorRotationMatrix();

  double Phi = Compton->Phi()*c_Deg;
  MVector Dg = -Compton->Dg();
  Dg = Rotation*Dg;
  double Chi = Dg.Phi()*c_Deg;
  while (Chi < -180) Chi += 360.0;
  while (Chi > +180) Chi -= 360.0;
  double Psi = Dg.Theta()*c_Deg;
where

  // Direction of the second gamma-ray:
  m_Dg = (m_C2 - m_C1).Unit();
is the direction of the second gamma-ray, defined in @src/misc/src/MComptonEvent::Validate@. h2. Computation of Phi The Compton scatter angle is computed in MEGAlib in @src/global/misc/src/MComptonEvent::CalculatePhi@

bool MComptonEvent::CalculatePhi()
{
  // Compute the compton scatter angle due to the standard equation
  // i.e neglect the movement of the electron,
  // which would lead to a Doppler-broadening
  //
  // Attention, make sure to test before you call this method,
  //            that the Compton-kinematics is correct

  double Value = 1 - c_E0 * (1/m_Eg - 1/(m_Ee + m_Eg));

  if (Value <= -1 || Value >= 1) {
    return false;
  }

  m_Phi = acos(Value);

  return true;
}
h2. Computation of the Earth horizon angle In MEGAlib, an intersection test with the Earth horizon is done in @src/mimrec/src/MEarthHorizon::IsEventFromEarthByIntersectionTest@

bool MEarthHorizon::IsEventFromEarthByIntersectionTest(MPhysicalEvent* Event, bool DumpOutput) const
{
  if (Event->GetType() == MPhysicalEvent::c_Compton) {
    MComptonEvent* C = dynamic_cast(Event);

    double Phi = C->Phi();

    MVector ConeAxis = -C->Dg();
    
    // Rotate the ConeAxis into the Earth system:
    ConeAxis = C->GetHorizonPointingRotationMatrix()*ConeAxis;

    
    // Distance between the Earth cone axis and the Compton cone axis:
    double AxisDist = m_PositionEarth.Angle(ConeAxis);
     
    if (fabs(AxisDist - Phi) > m_HorizonAngle) {
      return false;
    } else {
      if (DumpOutput == true) {
        mout<<"ID "<GetId()<<": Cone intersects Earth: "<GetType() == MPhysicalEvent::c_Pair) {
    MPairEvent* P = dynamic_cast(Event); 
    double AxisDist = m_PositionEarth.Angle(P->GetOrigin());
    
    if (AxisDist < m_HorizonAngle) {
      if (DumpOutput == true) {
        mout<<"ID "<GetId()<<": Origin inside Earth: "<
Note that

MRotation MRotationInterface::GetHorizonPointingRotationMatrix() const
{
  // Return the rotation matrix of this event

  // Verify that x and z axis are at right angle:
  if (fabs(m_HorizonPointingXAxis.Angle(m_HorizonPointingZAxis) - c_Pi/2.0)*c_Deg > 0.1) {
    cout<<"Event "<
and

double MVector::Angle(const MVector& V) const 
{
  // Calculate the angle between two vectors:
  // cos Angle = (v dot w) / (|v| x |w|)

  // Protect against division by zero:
  double Denom = Mag()*V.Mag();
  if (Denom == 0) {
    return 0.0;
  } else {
    double Value = Dot(V)/Denom;
    if (Value >  1.0) Value =  1.0;
    if (Value < -1.0) Value = -1.0;
    return acos(Value);
  }
}
and

m_PositionEarth = MVector(0, 0, -1);
which is the position of Earth (center) in detector coordinates and

m_HorizonAngle = 90*c_Rad;
is the (azimuth-) angle from the Earth position to Earth horizon (the values indicate the default values of the @MEarthHorizon@ constructor). The values are set using

bool MEarthHorizon::SetEarthHorizon(const MVector& PositionEarth, 
                                    const double HorizonAngle)
{
  m_PositionEarth = PositionEarth.Unit();
  m_HorizonAngle = HorizonAngle;

  return true;
}
which is called in @src/mimrec/src/MEventSelector.cxx@. Alternatively, a probabilistic evaluation is done using

bool MEarthHorizon::IsEventFromEarthByProbabilityTest(MPhysicalEvent* Event, bool DumpOutput) const
{
  massert(Event != 0);

  if (Event->GetType() == MPhysicalEvent::c_Compton) {
    MComptonEvent* C = dynamic_cast(Event);

    // Take care of scatter angles larger than 90 deg:
    double Phi = C->Phi();
    MVector ConeAxis = C->Dg();
    // Rotate the ConeAxis into the Earth system:
    ConeAxis = C->GetHorizonPointingRotationMatrix()*ConeAxis;

    MVector Origin = C->DiOnCone();

    // That's the trick, but I don't remember what it means...
    if (Phi > c_Pi/2.0) {
      Phi = c_Pi - Phi;
    } else {
      ConeAxis *= -1;
    }

    // Distance between the Earth cone axis and the Compton cone axis:
    double EarthConeaxisDist = m_PositionEarth.Angle(ConeAxis);

    // Now determine both angles between the cone and Earth (simpler now since phi always <= 90)
    // First the one towards the Earth -- if it is smaller than 0 just use fabs
    double AngleEarthCone1 = EarthConeaxisDist - Phi;
    if (AngleEarthCone1 < 0) AngleEarthCone1 = fabs(AngleEarthCone1); // please don't simplify
    // Then the one away from Earth -- if it is > 180 deg then use the smaller angle
    double AngleEarthCone2 = EarthConeaxisDist + Phi;
    if (AngleEarthCone2 > c_Pi) AngleEarthCone2 -= 2*(AngleEarthCone2 - c_Pi);
    
    // Case a: Cone is completely inside Earth
    if (AngleEarthCone1 < m_HorizonAngle && AngleEarthCone2 < m_HorizonAngle) {
      mdebug<<"EHC: Cone is completely inside Earth"<GetId()<<": Cone inside Earth"< m_HorizonAngle && AngleEarthCone2 > m_HorizonAngle) {
      mdebug<<"EHC: Cone is completely outside Earth"<GetId()<HasTrack() == true && m_ValidComptonResponse == true) {
        // Now we have to determine the distance to the origin on the cone:

        double ConeaxisOriginDist = ConeAxis.Angle(Origin);
        double EarthOriginDist = m_PositionEarth.Angle(Origin);

        if (sin(ConeaxisOriginDist) == 0) {
          merr<<"Numerical boundary: The photon's origin is identical with the cone axis...!"
              <<" Nevertheless, I am not rejecting this event "<GetId()<Ee()) +
              m_ComptonResponse.GetInterpolated((EarthConeaxisIntersectionAngle + EarthConeaxisOriginAngle)*c_Deg, C->Ee());
            Probability = 0.5*Probability;

            massert(EarthConeaxisIntersectionAngle - EarthConeaxisOriginAngle >= 0);
            massert(EarthConeaxisIntersectionAngle - EarthConeaxisOriginAngle <= c_Pi);
            massert(EarthConeaxisIntersectionAngle + EarthConeaxisOriginAngle >= 0);
            massert(EarthConeaxisIntersectionAngle + EarthConeaxisOriginAngle <= c_Pi);

          } 
          // otherwise:
          else {
            Probability = m_ComptonResponse.GetInterpolated((2*c_Pi - EarthConeaxisIntersectionAngle - EarthConeaxisOriginAngle)*c_Deg, C->Ee()) -
              m_ComptonResponse.GetInterpolated((EarthConeaxisIntersectionAngle - EarthConeaxisOriginAngle)*c_Deg, C->Ee());
            Probability = 1-0.5*Probability;
          }
        } 
        // Case B: "Maximum" on cone (not necessarily origin) is from *outside* Earth:
        else {
          // If the intersection are in different hemispheres Origin-Coneaxis-Hemispheres:
          if (EarthConeaxisOriginAngle + EarthConeaxisIntersectionAngle > c_Pi) {
            Probability = m_ComptonResponse.GetInterpolated((EarthConeaxisOriginAngle - EarthConeaxisIntersectionAngle)*c_Deg, C->Ee()) +
              m_ComptonResponse.GetInterpolated((c_Pi + EarthConeaxisIntersectionAngle - EarthConeaxisOriginAngle)*c_Deg, C->Ee());
            Probability = 1-0.5*Probability;
          } 
          // otherwise:
          else {
            Probability = m_ComptonResponse.GetInterpolated((EarthConeaxisOriginAngle + EarthConeaxisIntersectionAngle)*c_Deg, C->Ee()) -
              m_ComptonResponse.GetInterpolated((EarthConeaxisOriginAngle - EarthConeaxisIntersectionAngle)*c_Deg, C->Ee());
            Probability = 0.5*Probability;

          } 
        }

        if (Probability > m_MaxProbability) {
          if (DumpOutput == true) {
            mout<<"ID "<GetId()<<": Probability higher max probability: "< "< m_MaxProbability) {
          if (DumpOutput == true) {
            mout<<"ID "<GetId()<<": Probability higher max probability: "< "<GetType() == MPhysicalEvent::c_Pair) {
    MPairEvent* P = dynamic_cast(Event); 
    double AxisDist = m_PositionEarth.Angle(P->GetOrigin());
    
    if (AxisDist < m_HorizonAngle) {
      if (DumpOutput == true) {
        mout<<"ID "<GetId()<<": Origin inside Earth: "<
h2. MEGAlib constants The following constants are used in MEGAlib:

const double c_Pi = 3.14159265358979311600;
const double c_TwoPi = 2*c_Pi;
const double c_Sqrt2Pi = 2.506628274631;
const double c_Rad = c_Pi / 180.0;
const double c_Deg = 180.0 / c_Pi;
const double c_SpeedOfLight = 29.9792458E+9; // cm/s
const double c_E0 = 510.999; // keV
const double c_FarAway = 1E20; // cm
const double c_LargestEnergy = 0.999*numeric_limits::max();
const MVector c_NullVector(0.0, 0.0, 0.0);