IAP GITLAB

Skip to content
Snippets Groups Projects
ProcessSequence.h 12.44 KiB

/**
 * (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu
 *
 * See file AUTHORS for a list of contributors.
 *
 * This software is distributed under the terms of the GNU General Public
 * Licence version 3 (GPL Version 3). See file LICENSE for a full version of
 * the license.
 */

#ifndef _include_ProcessSequence_h_
#define _include_ProcessSequence_h_

#include <corsika/process/BaseProcess.h>
#include <corsika/process/ContinuousProcess.h>
#include <corsika/process/DiscreteProcess.h>
#include <corsika/process/ProcessReturn.h>

#include <limits>
#include <cmath>

//#include <corsika/setup/SetupTrajectory.h>
// using corsika::setup::Trajectory;
//#include <variant>

//#include <type_traits> // still needed ?

namespace corsika::process {

  // namespace detail {

  /*   /\* template<typename TT1, typename TT2, typename Type = void> *\/ */
  /*   /\*   struct CallHello { *\/ */
  /*   /\* 	static void Call(const TT1&, const TT2&) { *\/ */
  /*   /\* 	  std::cout << "normal" << std::endl; *\/ */
  /*   /\* 	} *\/ */
  /*   /\*   }; *\/ */

  /*   /\* template<typename TT1, typename TT2> *\/ */
  /*   /\*   struct CallHello<TT1, TT2, typename
   * std::enable_if<std::is_base_of<ContinuousProcess<TT2>, TT2>::value>::type> *\/ */
  /*   /\*   { *\/ */
  /*   /\* 	static void Call(const TT1&, const TT2&) { *\/ */
  /*   /\* 	  std::cout << "special" << std::endl; *\/ */
  /*   /\* 	}	 *\/ */
  /*          }; */

  /*   template<typename T1, typename T2, typename Particle, typename Trajectory, typename
   * Stack> //, typename Type = void> */
  /*     struct DoContinuous { */
  /* 	static EProcessReturn Call(const T1& A, const T2& B, Particle& p, Trajectory& t,
   * Stack& s) { */
  /* 	  EProcessReturn ret = EProcessReturn::eOk; */
  /* 	  if constexpr (!std::is_base_of<DiscreteProcess<T1>, T1>::value)  { */
  /* 	      A.DoContinuous(p, t, s); */
  /* 	    } */
  /* 	  if constexpr (!std::is_base_of<DiscreteProcess<T2>, T2>::value)  { */
  /* 	      B.DoContinuous(p, t, s); */
  /* 	    } */
  /* 	  return ret; */
  /* 	} */
  /*     }; */

  /*   /\* */
  /*   template<typename T1, typename T2, typename Particle, typename Trajectory, typename
   * Stack> */
  /*     struct DoContinuous<T1,T2,Particle,Trajectory,Stack, typename
   * std::enable_if<std::is_base_of<DiscreteProcess<T1>, T1>::value>::type> { */
  /* 	static EProcessReturn Call(const T1& A, const T2& B, Particle& p, Trajectory& t,
   * Stack& s) { */
  /* 	  EProcessReturn ret = EProcessReturn::eOk; */
  /* 	  A.DoContinuous(p, t, s); */
  /* 	  B.DoContinuous(p, t, s); */
  /* 	  return ret; */
  /* 	} */
  /*     }; */

  /*       template<typename T1, typename T2, typename Particle, typename Trajectory,
   * typename Stack> */
  /*     struct DoContinuous<T1,T2,Particle,Trajectory,Stack, typename
   * std::enable_if<std::is_base_of<DiscreteProcess<T2>, T2>::value>::type> { */
  /* 	static EProcessReturn Call(const T1& A, const T2&, Particle& p, Trajectory& t,
   * Stack& s) { */
  /* 	  EProcessReturn ret = EProcessReturn::eOk; */
  /* 	  A.DoContinuous(p, t, s); */
  /* 	  B.DoContinuous(p, t, s); */
  /* 	  return ret; */
  /* 	} */
  /*     }; */
  /*   *\/ */

  /*   template<typename T1, typename T2, typename Particle, typename Stack>//, typename
   * Type = void> */
  /*     struct DoDiscrete { */
  /* 	static EProcessReturn Call(const T1& A, const T2& B, Particle& p, Stack& s)  { */
  /* 	  if constexpr (!std::is_base_of<ContinuousProcess<T1>, T1>::value) { */
  /* 	      A.DoDiscrete(p, s); */
  /* 	    } */
  /* 	  if constexpr (!std::is_base_of<ContinuousProcess<T2>, T2>::value) { */
  /* 	      B.DoDiscrete(p, s); */
  /* 	    } */
  /* 	  return EProcessReturn::eOk; */
  /* 	} */
  /*     }; */
  /*   /\* */
  /*   template<typename T1, typename T2, typename Particle, typename Stack> */
  /*     struct DoDiscrete<T1,T2,Particle,Stack, typename
   * std::enable_if<std::is_base_of<ContinuousProcess<T1>, T1>::value>::type> { */
  /*     static EProcessReturn Call(const T1&, const T2& B, Particle& p, Stack& s) { */
  /* 	// A.DoDiscrete(p, s); */
  /*       B.DoDiscrete(p, s); */
  /*       return EProcessReturn::eOk; */
  /*     } */
  /*   }; */

  /*   template<typename T1, typename T2, typename Particle, typename Stack> */
  /*     struct DoDiscrete<T1,T2,Particle,Stack, typename
   * std::enable_if<std::is_base_of<ContinuousProcess<T2>, T2>::value>::type> { */
  /*     static EProcessReturn Call(const T1& A, const T2&, Particle& p, Stack& s) { */
  /* 	A.DoDiscrete(p, s); */
  /*       //B.DoDiscrete(p, s); */
  /*       return EProcessReturn::eOk; */
  /*     } */
  /*   }; */
  /*   *\/ */
  //} // end namespace detail

  /**
     \class ProcessSequence

     A compile time static list of processes. The compiler will
     generate a new type based on template logic containing all the
     elements.

     \comment Using CRTP pattern,
     https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern
   */

  //template <typename T1, typename T2>
  //class ProcessSequence : public BaseProcess<ProcessSequence<T1, T2> >;

  // this is a marker to track which BaseProcess is also a ProcessSequence
    template <typename T>
    struct is_process_sequence {
      static const bool value = false;
    };
    

  template <typename T1, typename T2>
  class ProcessSequence : public BaseProcess<ProcessSequence<T1, T2> > {
    
    /*    template <>
      struct BaseProcess<ProcessSequence<T1, T2> >::is_process_sequence<true> {
      static const bool value = true;
      };*/
    
  public:
    const T1& A;
    const T2& B;

    ProcessSequence(const T1& in_A, const T2& in_B)
        : A(in_A)
        , B(in_B) {}

    // example for a trait-based call:
    // void Hello() const  { detail::CallHello<T1,T2>::Call(A, B); }

    template <typename Particle, typename Track, typename Stack>
    inline EProcessReturn DoContinuous(Particle& p, Track& t, Stack& s) const {
      EProcessReturn ret = EProcessReturn::eOk;
      if constexpr (!std::is_base_of<DiscreteProcess<T1>, T1>::value) {
        // A.DoContinuous(std::forward<Particle>(p), t, std::forward<Stack>(s));
        A.DoContinuous(p, t, s);
      }
      if constexpr (!std::is_base_of<DiscreteProcess<T2>, T2>::value) {
        // B.DoContinuous(std::forward<Particle>(p), t, std::forward<Stack>(s));
        B.DoContinuous(p, t, s);
      }
      return ret;
    }

    template <typename Particle, typename Track>
    inline double MinStepLength(Particle& p, Track& track) const {
      double min_length = std::numeric_limits<double>::infinity();
      if constexpr (!std::is_base_of<DiscreteProcess<T1>, T1>::value) {
	  min_length = std::min(min_length, A.MinStepLength(p,track));
	}
      if constexpr (!std::is_base_of<DiscreteProcess<T2>, T2>::value) {
	  min_length = std::min(min_length, B.MinStepLength(p,track));
	}
      return min_length;
    }

    template <typename Particle, typename Track>
    inline double GetTotalInteractionLength(Particle& p, Track& t) const {
      return 1. / GetInverseInteractionLength(p, t);
    }

    template <typename Particle, typename Track>
    inline double GetTotalInverseInteractionLength(Particle& p, Track& t) const {
      return GetInverseInteractionLength(p, t);
    }

    template <typename Particle, typename Track>
    inline double GetInverseInteractionLength(Particle& p, Track& t) const {
      double tot = 0;
      if constexpr (!std::is_base_of<ContinuousProcess<T1>, T1>::value) {
        tot += A.GetInverseInteractionLength(p, t);
      }
      if constexpr (!std::is_base_of<ContinuousProcess<T2>, T2>::value) {
        tot += B.GetInverseInteractionLength(p, t);
      }
      return tot;
    }

    template <typename Particle, typename Stack>
    inline EProcessReturn DoDiscrete(Particle& p, Stack& s) const {
      if constexpr (!std::is_base_of<ContinuousProcess<T1>, T1>::value) {
        A.DoDiscrete(p, s);
      }
      if constexpr (!std::is_base_of<ContinuousProcess<T2>, T2>::value) {
        B.DoDiscrete(p, s);
      }
      return EProcessReturn::eOk;
    }

    template <typename Particle, typename Stack>
      inline EProcessReturn SelectDiscrete(Particle& p, Stack& s,
					   const double lambda_inv_tot,
					   const double rndm_select,
					   double& lambda_inv_count) const {
      if constexpr (is_process_sequence<T1>::value) {
	  // if A is a process sequence --> check inside
	  const EProcessReturn ret = A.SelectDiscrete(p, s, lambda_inv_count, rndm_select, lambda_inv_count);
	  // if A did suceed, stop routine
	  if (ret != EProcessReturn::eOk) {
	    return ret;
	  }
	} else if constexpr (!std::is_base_of<ContinuousProcess<T1>, T1>::value) {
	  // if this is not a ContinuousProcess --> evaluate probability
	  lambda_inv_count += A.GetInverseInteractionLength(p, s);
	  // check if we should execute THIS process and then EXIT
	  if (rndm_select<lambda_inv_count/lambda_inv_tot) {
	    A.DoDiscrete(p, s);
	    return EProcessReturn::eInteracted;
	  }
	} // end branch A
      
      if constexpr (is_process_sequence<T2>::value) {
	  // if A is a process sequence --> check inside
	  const EProcessReturn ret = B.SelectDiscrete(p, s, lambda_inv_count, rndm_select, lambda_inv_count);
	  // if A did suceed, stop routine
	  if (ret != EProcessReturn::eOk) {
	    return ret;
	  }
	} else if constexpr (!std::is_base_of<ContinuousProcess<T2>, T2>::value) {
	  // if this is not a ContinuousProcess --> evaluate probability
	  lambda_inv_count += B.GetInverseInteractionLength(p, s);
	  // check if we should execute THIS process and then EXIT
	  if (rndm_select<lambda_inv_count/lambda_inv_tot) {
	    B.DoDiscrete(p, s);
	    return EProcessReturn::eInteracted;
	  }
	} // end branch A
      return EProcessReturn::eOk;
    }

    /// TODO the const_cast is not nice, think about the constness here
    inline void Init() const {
      const_cast<T1*>(&A)->Init();
      const_cast<T2*>(&B)->Init();
    }
  };

  /// the +operator assembles many BaseProcess, ContinuousProcess, and
  /// DiscreteProcess objects into a ProcessSequence, all combinatorics
  /// must be allowed, this is why we define a macro to define all
  /// combinations here:
#define OPSEQ(C1, C2)                                                                \
  template <typename T1, typename T2>                                                \
  inline const ProcessSequence<T1, T2> operator+(const C1<T1>& A, const C2<T2>& B) { \
    return ProcessSequence<T1, T2>(A.GetRef(), B.GetRef());                          \
  }

  OPSEQ(BaseProcess, BaseProcess)
  OPSEQ(BaseProcess, DiscreteProcess)
  OPSEQ(BaseProcess, ContinuousProcess)
  OPSEQ(ContinuousProcess, BaseProcess)
  OPSEQ(ContinuousProcess, DiscreteProcess)
  OPSEQ(ContinuousProcess, ContinuousProcess)
  OPSEQ(DiscreteProcess, BaseProcess)
  OPSEQ(DiscreteProcess, DiscreteProcess)
  OPSEQ(DiscreteProcess, ContinuousProcess)

  /*
    template <typename T1>
    struct depth_lhs
    {
    static const int num = 0;
    };



    // terminating condition
    template <typename T1, typename T2>
    struct depth_lhs< Sequence<T1,T2> >
    {
    // try to expand the left node (T1) which might be a Sequence type
    static const int num = 1 + depth_lhs<T1>::num;
    };
  */

  /*
    template <typename T1>
    struct mat_ptrs
    {
    static const int num = 0;

    inline static void
    get_ptrs(const Process** ptrs, const T1& X)
    {
    ptrs[0] = reinterpret_cast<const Process*>(&X);
    }
    };


    template <typename T1, typename T2>
    struct mat_ptrs< Sequence<T1,T2> >
    {
    static const int num = 1 + mat_ptrs<T1>::num;

    inline static void
    get_ptrs(const Process** in_ptrs, const Sequence<T1,T2>& X)
    {
    // traverse the left node
    mat_ptrs<T1>::get_ptrs(in_ptrs, X.A);
    // get address of the matrix on the right node
    in_ptrs[num] = reinterpret_cast<const Process*>(&X.B);
    }
    };
  */

  /*
    template<typename T1, typename T2>
    const Process&
    Process::operator=(const Sequence<T1,T2>& X)
    {
    int N = 1 + depth_lhs< Sequence<T1,T2> >::num;
    const Process* ptrs[N];
    mat_ptrs< Sequence<T1,T2> >::get_ptrs(ptrs, X);
    int r = ptrs[0]->rows;
    int c = ptrs[0]->cols;
    // ... check that all matrices have the same size ...
    set_size(r, c);
    for(int j=0; j<r*c; ++j)
    {
    double sum = ptrs[0]->data[j];
    for(int i=1; i<N; ++i)
    {
    sum += ptrs[i]->data[j];
    }
    data[j] = sum;
    }
    return *this;
    }
  */

        template<template<typename, typename> class T, typename A, typename B>
      struct is_process_sequence< T<A,B> > 
    {
      static const bool value = true;
    };

} // namespace corsika::process

#endif