
#include <stdio.h>
#ifndef NULL
    #define NULL 0
#endif
#include <stdint.h>
#define XTRACK_MULTIPOLE_NO_SYNRAD
#define XFIELDS_BB3D_NO_BEAMSTR
#define XFIELDS_BB3D_NO_BHABHA
#define XTRACK_GLOBAL_XY_LIMIT 1.0
// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_CONSTANTS_H
#define XTRACK_CONSTANTS_H

#if !defined( C_LIGHT )
    #define   C_LIGHT ( 299792458.0 )
#endif /* !defined( C_LIGHT ) */

#if !defined( EPSILON_0 )
    #define   EPSILON_0 (8.854187817620e-12)
#endif /* !defined( EPSILON_0 ) */

#if !defined( PI )
    #define PI (3.1415926535897932384626433832795028841971693993751)
#endif /* !defined( PI ) */

#if !defined( MU_0 )
    #define MU_0 (PI*4.0e-7)
#endif /* !defined( MU_0 ) */

#if !defined( DEG2RAD )
    #define DEG2RAD (0.0174532925199432957692369076848861271344287188854)
#endif /* !defiend( DEG2RAD ) */

#if !defined( RAD2DEG )
    #define RAD2DEG (57.29577951308232087679815481410517033240547246656442)
#endif /* !defiend( RAD2DEG ) */

#if !defined( SQRT_PI )
    #define SQRT_PI (1.7724538509055160272981674833411451827975494561224)
#endif /* !defined( SQRT_PI ) */

#if !defined( QELEM )
    #define QELEM (1.60217662e-19)
#endif /* !defined( QELEM ) */

#if !defined( DBL_MAX )
    #define DBL_MAX (1.7976931348623158e+308)
#endif /* !defined( DBL_MAX ) */

#if !defined( DBL_MIN )
    #define DBL_MIN (2.2250738585072014e-308)
#endif /* !defined( DBL_MIN ) */

#if !defined( DBL_EPSILON )
    #define DBL_EPSILON (2.2204460492503131e-16)
#endif /* !defined( DBL_EPSILON ) */

#endif /* XTRACK_CONSTANTS_H */

#ifndef XOBJ_TYPEDEF_ArrNInt64
#define XOBJ_TYPEDEF_ArrNInt64
typedef   struct ArrNInt64_s * ArrNInt64;
 static inline ArrNInt64 ArrNInt64_getp(ArrNInt64  obj){
  int64_t offset=0;
  return (ArrNInt64)(( char*) obj+offset);
}
 static inline int64_t ArrNInt64_len(ArrNInt64  obj){
  int64_t offset=0;
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline int64_t ArrNInt64_get(const ArrNInt64  obj, int64_t i0){
  int64_t offset=0;
  offset+=16+i0*8;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void ArrNInt64_set(ArrNInt64  obj, int64_t i0, int64_t value){
  int64_t offset=0;
  offset+=16+i0*8;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* ArrNInt64_getp1(ArrNInt64  obj, int64_t i0){
  int64_t offset=0;
  offset+=16+i0*8;
  return ( int64_t*)(( char*) obj+offset);
}
#endif
#ifndef XOBJ_TYPEDEF_ArrNFloat64
#define XOBJ_TYPEDEF_ArrNFloat64
typedef   struct ArrNFloat64_s * ArrNFloat64;
 static inline ArrNFloat64 ArrNFloat64_getp(ArrNFloat64  obj){
  int64_t offset=0;
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t ArrNFloat64_len(ArrNFloat64  obj){
  int64_t offset=0;
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double ArrNFloat64_get(const ArrNFloat64  obj, int64_t i0){
  int64_t offset=0;
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void ArrNFloat64_set(ArrNFloat64  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* ArrNFloat64_getp1(ArrNFloat64  obj, int64_t i0){
  int64_t offset=0;
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
#endif
#ifndef XOBJ_TYPEDEF_Arr5Float64
#define XOBJ_TYPEDEF_Arr5Float64
typedef   struct Arr5Float64_s * Arr5Float64;
 static inline Arr5Float64 Arr5Float64_getp(Arr5Float64  obj){
  int64_t offset=0;
  return (Arr5Float64)(( char*) obj+offset);
}
 static inline int64_t Arr5Float64_len(Arr5Float64  obj){
  return 5;
}
 static inline double Arr5Float64_get(const Arr5Float64  obj, int64_t i0){
  int64_t offset=0;
  offset+=i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void Arr5Float64_set(Arr5Float64  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* Arr5Float64_getp1(Arr5Float64  obj, int64_t i0){
  int64_t offset=0;
  offset+=i0*8;
  return ( double*)(( char*) obj+offset);
}
#endif
#ifndef XOBJ_TYPEDEF_Arr6Float64
#define XOBJ_TYPEDEF_Arr6Float64
typedef   struct Arr6Float64_s * Arr6Float64;
 static inline Arr6Float64 Arr6Float64_getp(Arr6Float64  obj){
  int64_t offset=0;
  return (Arr6Float64)(( char*) obj+offset);
}
 static inline int64_t Arr6Float64_len(Arr6Float64  obj){
  return 6;
}
 static inline double Arr6Float64_get(const Arr6Float64  obj, int64_t i0){
  int64_t offset=0;
  offset+=i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void Arr6Float64_set(Arr6Float64  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* Arr6Float64_getp1(Arr6Float64  obj, int64_t i0){
  int64_t offset=0;
  offset+=i0*8;
  return ( double*)(( char*) obj+offset);
}
#endif
#ifndef XOBJ_TYPEDEF_Arr6x6Float64
#define XOBJ_TYPEDEF_Arr6x6Float64
typedef   struct Arr6x6Float64_s * Arr6x6Float64;
 static inline Arr6x6Float64 Arr6x6Float64_getp(Arr6x6Float64  obj){
  int64_t offset=0;
  return (Arr6x6Float64)(( char*) obj+offset);
}
 static inline int64_t Arr6x6Float64_len(Arr6x6Float64  obj){
  return 36;
}
 static inline double Arr6x6Float64_get(const Arr6x6Float64  obj, int64_t i0, int64_t i1){
  int64_t offset=0;
  offset+=i0*48+i1*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void Arr6x6Float64_set(Arr6x6Float64  obj, int64_t i0, int64_t i1, double value){
  int64_t offset=0;
  offset+=i0*48+i1*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* Arr6x6Float64_getp2(Arr6x6Float64  obj, int64_t i0, int64_t i1){
  int64_t offset=0;
  offset+=i0*48+i1*8;
  return ( double*)(( char*) obj+offset);
}
#endif
#ifndef XOBJ_TYPEDEF_Arr2Float64
#define XOBJ_TYPEDEF_Arr2Float64
typedef   struct Arr2Float64_s * Arr2Float64;
 static inline Arr2Float64 Arr2Float64_getp(Arr2Float64  obj){
  int64_t offset=0;
  return (Arr2Float64)(( char*) obj+offset);
}
 static inline int64_t Arr2Float64_len(Arr2Float64  obj){
  return 2;
}
 static inline double Arr2Float64_get(const Arr2Float64  obj, int64_t i0){
  int64_t offset=0;
  offset+=i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void Arr2Float64_set(Arr2Float64  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* Arr2Float64_getp1(Arr2Float64  obj, int64_t i0){
  int64_t offset=0;
  offset+=i0*8;
  return ( double*)(( char*) obj+offset);
}
#endif
#ifndef XOBJ_TYPEDEF_Arr1Float64
#define XOBJ_TYPEDEF_Arr1Float64
typedef   struct Arr1Float64_s * Arr1Float64;
 static inline Arr1Float64 Arr1Float64_getp(Arr1Float64  obj){
  int64_t offset=0;
  return (Arr1Float64)(( char*) obj+offset);
}
 static inline int64_t Arr1Float64_len(Arr1Float64  obj){
  return 1;
}
 static inline double Arr1Float64_get(const Arr1Float64  obj, int64_t i0){
  int64_t offset=0;
  offset+=i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void Arr1Float64_set(Arr1Float64  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* Arr1Float64_getp1(Arr1Float64  obj, int64_t i0){
  int64_t offset=0;
  offset+=i0*8;
  return ( double*)(( char*) obj+offset);
}
#endif
#ifndef XOBJ_TYPEDEF_ArrNString
#define XOBJ_TYPEDEF_ArrNString
typedef   struct ArrNString_s * ArrNString;
 static inline ArrNString ArrNString_getp(ArrNString  obj){
  int64_t offset=0;
  return (ArrNString)(( char*) obj+offset);
}
 static inline int64_t ArrNString_len(ArrNString  obj){
  int64_t offset=0;
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline  char* ArrNString_getp1(ArrNString  obj, int64_t i0){
  int64_t offset=0;
  offset=*( int64_t*)(( char*) obj+offset+16+i0*8);
  return ( char*)(( char*) obj+offset);
}
#endif
#ifndef XOBJ_TYPEDEF_ArrNUint32
#define XOBJ_TYPEDEF_ArrNUint32
typedef   struct ArrNUint32_s * ArrNUint32;
 static inline ArrNUint32 ArrNUint32_getp(ArrNUint32  obj){
  int64_t offset=0;
  return (ArrNUint32)(( char*) obj+offset);
}
 static inline int64_t ArrNUint32_len(ArrNUint32  obj){
  int64_t offset=0;
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline uint32_t ArrNUint32_get(const ArrNUint32  obj, int64_t i0){
  int64_t offset=0;
  offset+=16+i0*4;
  return *( uint32_t*)(( char*) obj+offset);
}
 static inline void ArrNUint32_set(ArrNUint32  obj, int64_t i0, uint32_t value){
  int64_t offset=0;
  offset+=16+i0*4;
  *( uint32_t*)(( char*) obj+offset)=value;
}
 static inline  uint32_t* ArrNUint32_getp1(ArrNUint32  obj, int64_t i0){
  int64_t offset=0;
  offset+=16+i0*4;
  return ( uint32_t*)(( char*) obj+offset);
}
#endif
#ifndef XOBJ_TYPEDEF_RecordIndex
#define XOBJ_TYPEDEF_RecordIndex
typedef   struct RecordIndex_s * RecordIndex;
 static inline RecordIndex RecordIndex_getp(RecordIndex  obj){
  int64_t offset=0;
  return (RecordIndex)(( char*) obj+offset);
}
 static inline int64_t RecordIndex_get_capacity(const RecordIndex  obj){
  int64_t offset=0;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void RecordIndex_set_capacity(RecordIndex  obj, int64_t value){
  int64_t offset=0;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* RecordIndex_getp_capacity(RecordIndex  obj){
  int64_t offset=0;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline uint32_t RecordIndex_get_num_recorded(const RecordIndex  obj){
  int64_t offset=0;
  offset+=8;
  return *( uint32_t*)(( char*) obj+offset);
}
 static inline void RecordIndex_set_num_recorded(RecordIndex  obj, uint32_t value){
  int64_t offset=0;
  offset+=8;
  *( uint32_t*)(( char*) obj+offset)=value;
}
 static inline  uint32_t* RecordIndex_getp_num_recorded(RecordIndex  obj){
  int64_t offset=0;
  offset+=8;
  return ( uint32_t*)(( char*) obj+offset);
}
 static inline uint32_t RecordIndex_get__dummy(const RecordIndex  obj){
  int64_t offset=0;
  offset+=16;
  return *( uint32_t*)(( char*) obj+offset);
}
 static inline void RecordIndex_set__dummy(RecordIndex  obj, uint32_t value){
  int64_t offset=0;
  offset+=16;
  *( uint32_t*)(( char*) obj+offset)=value;
}
 static inline  uint32_t* RecordIndex_getp__dummy(RecordIndex  obj){
  int64_t offset=0;
  offset+=16;
  return ( uint32_t*)(( char*) obj+offset);
}
 static inline int64_t RecordIndex_get_buffer_id(const RecordIndex  obj){
  int64_t offset=0;
  offset+=24;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void RecordIndex_set_buffer_id(RecordIndex  obj, int64_t value){
  int64_t offset=0;
  offset+=24;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* RecordIndex_getp_buffer_id(RecordIndex  obj){
  int64_t offset=0;
  offset+=24;
  return ( int64_t*)(( char*) obj+offset);
}
#endif


 static inline
int64_t RecordIndex_get_slot(RecordIndex record_index){

    if (record_index == NULL){
        return -2;
    }
    int64_t capacity = RecordIndex_get_capacity(record_index);
      uint32_t* num_recorded = RecordIndex_getp_num_recorded(record_index);

    if(*num_recorded >= capacity){
        return -1;
    }

    uint32_t slot;

//    slot = atomic_add(num_recorded, 1);   //only_for_context opencl
//    slot = atomicAdd(num_recorded, 1);    //only_for_context cuda

//    #pragma omp atomic capture            //only_for_context cpu_openmp
    slot = (*num_recorded)++;             //only_for_context cpu_serial cpu_openmp

    if (slot >= capacity){
        *num_recorded = capacity;
        return -1;
    }
    return (int64_t)slot;
}

#ifndef XOBJ_TYPEDEF_ArrNFloat32
#define XOBJ_TYPEDEF_ArrNFloat32
typedef   struct ArrNFloat32_s * ArrNFloat32;
 static inline ArrNFloat32 ArrNFloat32_getp(ArrNFloat32  obj){
  int64_t offset=0;
  return (ArrNFloat32)(( char*) obj+offset);
}
 static inline int64_t ArrNFloat32_len(ArrNFloat32  obj){
  int64_t offset=0;
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline float ArrNFloat32_get(const ArrNFloat32  obj, int64_t i0){
  int64_t offset=0;
  offset+=16+i0*4;
  return *( float*)(( char*) obj+offset);
}
 static inline void ArrNFloat32_set(ArrNFloat32  obj, int64_t i0, float value){
  int64_t offset=0;
  offset+=16+i0*4;
  *( float*)(( char*) obj+offset)=value;
}
 static inline  float* ArrNFloat32_getp1(ArrNFloat32  obj, int64_t i0){
  int64_t offset=0;
  offset+=16+i0*4;
  return ( float*)(( char*) obj+offset);
}
#endif
#ifndef XOBJ_TYPEDEF_GeneralMaterialData
#define XOBJ_TYPEDEF_GeneralMaterialData
typedef   struct GeneralMaterialData_s * GeneralMaterialData;
 static inline GeneralMaterialData GeneralMaterialData_getp(GeneralMaterialData  obj){
  int64_t offset=0;
  return (GeneralMaterialData)(( char*) obj+offset);
}
 static inline double GeneralMaterialData_get_Z(const GeneralMaterialData  obj){
  int64_t offset=0;
  offset+=8;
  return *( double*)(( char*) obj+offset);
}
 static inline void GeneralMaterialData_set_Z(GeneralMaterialData  obj, double value){
  int64_t offset=0;
  offset+=8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* GeneralMaterialData_getp_Z(GeneralMaterialData  obj){
  int64_t offset=0;
  offset+=8;
  return ( double*)(( char*) obj+offset);
}
 static inline double GeneralMaterialData_get_A(const GeneralMaterialData  obj){
  int64_t offset=0;
  offset+=16;
  return *( double*)(( char*) obj+offset);
}
 static inline void GeneralMaterialData_set_A(GeneralMaterialData  obj, double value){
  int64_t offset=0;
  offset+=16;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* GeneralMaterialData_getp_A(GeneralMaterialData  obj){
  int64_t offset=0;
  offset+=16;
  return ( double*)(( char*) obj+offset);
}
 static inline double GeneralMaterialData_get_density(const GeneralMaterialData  obj){
  int64_t offset=0;
  offset+=24;
  return *( double*)(( char*) obj+offset);
}
 static inline void GeneralMaterialData_set_density(GeneralMaterialData  obj, double value){
  int64_t offset=0;
  offset+=24;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* GeneralMaterialData_getp_density(GeneralMaterialData  obj){
  int64_t offset=0;
  offset+=24;
  return ( double*)(( char*) obj+offset);
}
 static inline double GeneralMaterialData_get_excitation_energy(const GeneralMaterialData  obj){
  int64_t offset=0;
  offset+=32;
  return *( double*)(( char*) obj+offset);
}
 static inline void GeneralMaterialData_set_excitation_energy(GeneralMaterialData  obj, double value){
  int64_t offset=0;
  offset+=32;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* GeneralMaterialData_getp_excitation_energy(GeneralMaterialData  obj){
  int64_t offset=0;
  offset+=32;
  return ( double*)(( char*) obj+offset);
}
 static inline double GeneralMaterialData_get_nuclear_radius(const GeneralMaterialData  obj){
  int64_t offset=0;
  offset+=40;
  return *( double*)(( char*) obj+offset);
}
 static inline void GeneralMaterialData_set_nuclear_radius(GeneralMaterialData  obj, double value){
  int64_t offset=0;
  offset+=40;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* GeneralMaterialData_getp_nuclear_radius(GeneralMaterialData  obj){
  int64_t offset=0;
  offset+=40;
  return ( double*)(( char*) obj+offset);
}
 static inline double GeneralMaterialData_get_nuclear_elastic_slope(const GeneralMaterialData  obj){
  int64_t offset=0;
  offset+=48;
  return *( double*)(( char*) obj+offset);
}
 static inline void GeneralMaterialData_set_nuclear_elastic_slope(GeneralMaterialData  obj, double value){
  int64_t offset=0;
  offset+=48;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* GeneralMaterialData_getp_nuclear_elastic_slope(GeneralMaterialData  obj){
  int64_t offset=0;
  offset+=48;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNFloat64 GeneralMaterialData_getp_cross_section(GeneralMaterialData  obj){
  int64_t offset=0;
  offset+=72;
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t GeneralMaterialData_len_cross_section(GeneralMaterialData  obj){
  int64_t offset=0;
  offset+=72;
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double GeneralMaterialData_get_cross_section(const GeneralMaterialData  obj, int64_t i0){
  int64_t offset=0;
  offset+=72;
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void GeneralMaterialData_set_cross_section(GeneralMaterialData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=72;
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* GeneralMaterialData_getp1_cross_section(GeneralMaterialData  obj, int64_t i0){
  int64_t offset=0;
  offset+=72;
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline double GeneralMaterialData_get_hcut(const GeneralMaterialData  obj){
  int64_t offset=0;
  offset+=56;
  return *( double*)(( char*) obj+offset);
}
 static inline void GeneralMaterialData_set_hcut(GeneralMaterialData  obj, double value){
  int64_t offset=0;
  offset+=56;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* GeneralMaterialData_getp_hcut(GeneralMaterialData  obj){
  int64_t offset=0;
  offset+=56;
  return ( double*)(( char*) obj+offset);
}
 static inline  char* GeneralMaterialData_getp_name(GeneralMaterialData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+64);
  return ( char*)(( char*) obj+offset);
}
#endif
#ifndef XOBJ_TYPEDEF_ParticlesData
#define XOBJ_TYPEDEF_ParticlesData
typedef   struct ParticlesData_s * ParticlesData;
 static inline ParticlesData ParticlesData_getp(ParticlesData  obj){
  int64_t offset=0;
  return (ParticlesData)(( char*) obj+offset);
}
 static inline int64_t ParticlesData_get__capacity(const ParticlesData  obj){
  int64_t offset=0;
  offset+=8;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void ParticlesData_set__capacity(ParticlesData  obj, int64_t value){
  int64_t offset=0;
  offset+=8;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* ParticlesData_getp__capacity(ParticlesData  obj){
  int64_t offset=0;
  offset+=8;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline int64_t ParticlesData_get__num_active_particles(const ParticlesData  obj){
  int64_t offset=0;
  offset+=16;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void ParticlesData_set__num_active_particles(ParticlesData  obj, int64_t value){
  int64_t offset=0;
  offset+=16;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* ParticlesData_getp__num_active_particles(ParticlesData  obj){
  int64_t offset=0;
  offset+=16;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline int64_t ParticlesData_get__num_lost_particles(const ParticlesData  obj){
  int64_t offset=0;
  offset+=24;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void ParticlesData_set__num_lost_particles(ParticlesData  obj, int64_t value){
  int64_t offset=0;
  offset+=24;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* ParticlesData_getp__num_lost_particles(ParticlesData  obj){
  int64_t offset=0;
  offset+=24;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline int64_t ParticlesData_get_start_tracking_at_element(const ParticlesData  obj){
  int64_t offset=0;
  offset+=32;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void ParticlesData_set_start_tracking_at_element(ParticlesData  obj, int64_t value){
  int64_t offset=0;
  offset+=32;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* ParticlesData_getp_start_tracking_at_element(ParticlesData  obj){
  int64_t offset=0;
  offset+=32;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline double ParticlesData_get_q0(const ParticlesData  obj){
  int64_t offset=0;
  offset+=40;
  return *( double*)(( char*) obj+offset);
}
 static inline void ParticlesData_set_q0(ParticlesData  obj, double value){
  int64_t offset=0;
  offset+=40;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* ParticlesData_getp_q0(ParticlesData  obj){
  int64_t offset=0;
  offset+=40;
  return ( double*)(( char*) obj+offset);
}
 static inline double ParticlesData_get_mass0(const ParticlesData  obj){
  int64_t offset=0;
  offset+=48;
  return *( double*)(( char*) obj+offset);
}
 static inline void ParticlesData_set_mass0(ParticlesData  obj, double value){
  int64_t offset=0;
  offset+=48;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* ParticlesData_getp_mass0(ParticlesData  obj){
  int64_t offset=0;
  offset+=48;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNFloat64 ParticlesData_getp_p0c(ParticlesData  obj){
  int64_t offset=0;
  offset+=248;
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t ParticlesData_len_p0c(ParticlesData  obj){
  int64_t offset=0;
  offset+=248;
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double ParticlesData_get_p0c(const ParticlesData  obj, int64_t i0){
  int64_t offset=0;
  offset+=248;
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void ParticlesData_set_p0c(ParticlesData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=248;
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* ParticlesData_getp1_p0c(ParticlesData  obj, int64_t i0){
  int64_t offset=0;
  offset+=248;
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNFloat64 ParticlesData_getp_gamma0(ParticlesData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+56);
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t ParticlesData_len_gamma0(ParticlesData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+56);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double ParticlesData_get_gamma0(const ParticlesData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+56);
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void ParticlesData_set_gamma0(ParticlesData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+56);
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* ParticlesData_getp1_gamma0(ParticlesData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+56);
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNFloat64 ParticlesData_getp_beta0(ParticlesData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+64);
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t ParticlesData_len_beta0(ParticlesData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+64);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double ParticlesData_get_beta0(const ParticlesData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+64);
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void ParticlesData_set_beta0(ParticlesData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+64);
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* ParticlesData_getp1_beta0(ParticlesData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+64);
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNFloat64 ParticlesData_getp_s(ParticlesData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+72);
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t ParticlesData_len_s(ParticlesData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+72);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double ParticlesData_get_s(const ParticlesData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+72);
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void ParticlesData_set_s(ParticlesData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+72);
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* ParticlesData_getp1_s(ParticlesData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+72);
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNFloat64 ParticlesData_getp_zeta(ParticlesData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+80);
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t ParticlesData_len_zeta(ParticlesData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+80);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double ParticlesData_get_zeta(const ParticlesData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+80);
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void ParticlesData_set_zeta(ParticlesData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+80);
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* ParticlesData_getp1_zeta(ParticlesData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+80);
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNFloat64 ParticlesData_getp_ptau(ParticlesData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+88);
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t ParticlesData_len_ptau(ParticlesData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+88);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double ParticlesData_get_ptau(const ParticlesData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+88);
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void ParticlesData_set_ptau(ParticlesData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+88);
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* ParticlesData_getp1_ptau(ParticlesData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+88);
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNFloat64 ParticlesData_getp_delta(ParticlesData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+96);
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t ParticlesData_len_delta(ParticlesData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+96);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double ParticlesData_get_delta(const ParticlesData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+96);
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void ParticlesData_set_delta(ParticlesData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+96);
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* ParticlesData_getp1_delta(ParticlesData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+96);
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNFloat64 ParticlesData_getp_rpp(ParticlesData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+104);
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t ParticlesData_len_rpp(ParticlesData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+104);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double ParticlesData_get_rpp(const ParticlesData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+104);
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void ParticlesData_set_rpp(ParticlesData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+104);
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* ParticlesData_getp1_rpp(ParticlesData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+104);
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNFloat64 ParticlesData_getp_rvv(ParticlesData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+112);
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t ParticlesData_len_rvv(ParticlesData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+112);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double ParticlesData_get_rvv(const ParticlesData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+112);
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void ParticlesData_set_rvv(ParticlesData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+112);
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* ParticlesData_getp1_rvv(ParticlesData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+112);
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNFloat64 ParticlesData_getp_chi(ParticlesData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+120);
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t ParticlesData_len_chi(ParticlesData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+120);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double ParticlesData_get_chi(const ParticlesData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+120);
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void ParticlesData_set_chi(ParticlesData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+120);
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* ParticlesData_getp1_chi(ParticlesData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+120);
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNFloat64 ParticlesData_getp_charge_ratio(ParticlesData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+128);
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t ParticlesData_len_charge_ratio(ParticlesData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+128);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double ParticlesData_get_charge_ratio(const ParticlesData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+128);
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void ParticlesData_set_charge_ratio(ParticlesData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+128);
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* ParticlesData_getp1_charge_ratio(ParticlesData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+128);
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNFloat64 ParticlesData_getp_weight(ParticlesData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+136);
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t ParticlesData_len_weight(ParticlesData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+136);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double ParticlesData_get_weight(const ParticlesData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+136);
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void ParticlesData_set_weight(ParticlesData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+136);
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* ParticlesData_getp1_weight(ParticlesData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+136);
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNInt64 ParticlesData_getp_particle_id(ParticlesData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+144);
  return (ArrNInt64)(( char*) obj+offset);
}
 static inline int64_t ParticlesData_len_particle_id(ParticlesData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+144);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline int64_t ParticlesData_get_particle_id(const ParticlesData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+144);
  offset+=16+i0*8;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void ParticlesData_set_particle_id(ParticlesData  obj, int64_t i0, int64_t value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+144);
  offset+=16+i0*8;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* ParticlesData_getp1_particle_id(ParticlesData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+144);
  offset+=16+i0*8;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline ArrNInt64 ParticlesData_getp_at_element(ParticlesData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+152);
  return (ArrNInt64)(( char*) obj+offset);
}
 static inline int64_t ParticlesData_len_at_element(ParticlesData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+152);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline int64_t ParticlesData_get_at_element(const ParticlesData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+152);
  offset+=16+i0*8;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void ParticlesData_set_at_element(ParticlesData  obj, int64_t i0, int64_t value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+152);
  offset+=16+i0*8;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* ParticlesData_getp1_at_element(ParticlesData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+152);
  offset+=16+i0*8;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline ArrNInt64 ParticlesData_getp_at_turn(ParticlesData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+160);
  return (ArrNInt64)(( char*) obj+offset);
}
 static inline int64_t ParticlesData_len_at_turn(ParticlesData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+160);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline int64_t ParticlesData_get_at_turn(const ParticlesData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+160);
  offset+=16+i0*8;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void ParticlesData_set_at_turn(ParticlesData  obj, int64_t i0, int64_t value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+160);
  offset+=16+i0*8;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* ParticlesData_getp1_at_turn(ParticlesData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+160);
  offset+=16+i0*8;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline ArrNInt64 ParticlesData_getp_state(ParticlesData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+168);
  return (ArrNInt64)(( char*) obj+offset);
}
 static inline int64_t ParticlesData_len_state(ParticlesData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+168);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline int64_t ParticlesData_get_state(const ParticlesData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+168);
  offset+=16+i0*8;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void ParticlesData_set_state(ParticlesData  obj, int64_t i0, int64_t value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+168);
  offset+=16+i0*8;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* ParticlesData_getp1_state(ParticlesData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+168);
  offset+=16+i0*8;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline ArrNInt64 ParticlesData_getp_parent_particle_id(ParticlesData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+176);
  return (ArrNInt64)(( char*) obj+offset);
}
 static inline int64_t ParticlesData_len_parent_particle_id(ParticlesData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+176);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline int64_t ParticlesData_get_parent_particle_id(const ParticlesData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+176);
  offset+=16+i0*8;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void ParticlesData_set_parent_particle_id(ParticlesData  obj, int64_t i0, int64_t value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+176);
  offset+=16+i0*8;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* ParticlesData_getp1_parent_particle_id(ParticlesData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+176);
  offset+=16+i0*8;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline ArrNUint32 ParticlesData_getp__rng_s1(ParticlesData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+184);
  return (ArrNUint32)(( char*) obj+offset);
}
 static inline int64_t ParticlesData_len__rng_s1(ParticlesData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+184);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline uint32_t ParticlesData_get__rng_s1(const ParticlesData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+184);
  offset+=16+i0*4;
  return *( uint32_t*)(( char*) obj+offset);
}
 static inline void ParticlesData_set__rng_s1(ParticlesData  obj, int64_t i0, uint32_t value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+184);
  offset+=16+i0*4;
  *( uint32_t*)(( char*) obj+offset)=value;
}
 static inline  uint32_t* ParticlesData_getp1__rng_s1(ParticlesData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+184);
  offset+=16+i0*4;
  return ( uint32_t*)(( char*) obj+offset);
}
 static inline ArrNUint32 ParticlesData_getp__rng_s2(ParticlesData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+192);
  return (ArrNUint32)(( char*) obj+offset);
}
 static inline int64_t ParticlesData_len__rng_s2(ParticlesData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+192);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline uint32_t ParticlesData_get__rng_s2(const ParticlesData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+192);
  offset+=16+i0*4;
  return *( uint32_t*)(( char*) obj+offset);
}
 static inline void ParticlesData_set__rng_s2(ParticlesData  obj, int64_t i0, uint32_t value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+192);
  offset+=16+i0*4;
  *( uint32_t*)(( char*) obj+offset)=value;
}
 static inline  uint32_t* ParticlesData_getp1__rng_s2(ParticlesData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+192);
  offset+=16+i0*4;
  return ( uint32_t*)(( char*) obj+offset);
}
 static inline ArrNUint32 ParticlesData_getp__rng_s3(ParticlesData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+200);
  return (ArrNUint32)(( char*) obj+offset);
}
 static inline int64_t ParticlesData_len__rng_s3(ParticlesData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+200);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline uint32_t ParticlesData_get__rng_s3(const ParticlesData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+200);
  offset+=16+i0*4;
  return *( uint32_t*)(( char*) obj+offset);
}
 static inline void ParticlesData_set__rng_s3(ParticlesData  obj, int64_t i0, uint32_t value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+200);
  offset+=16+i0*4;
  *( uint32_t*)(( char*) obj+offset)=value;
}
 static inline  uint32_t* ParticlesData_getp1__rng_s3(ParticlesData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+200);
  offset+=16+i0*4;
  return ( uint32_t*)(( char*) obj+offset);
}
 static inline ArrNUint32 ParticlesData_getp__rng_s4(ParticlesData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+208);
  return (ArrNUint32)(( char*) obj+offset);
}
 static inline int64_t ParticlesData_len__rng_s4(ParticlesData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+208);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline uint32_t ParticlesData_get__rng_s4(const ParticlesData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+208);
  offset+=16+i0*4;
  return *( uint32_t*)(( char*) obj+offset);
}
 static inline void ParticlesData_set__rng_s4(ParticlesData  obj, int64_t i0, uint32_t value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+208);
  offset+=16+i0*4;
  *( uint32_t*)(( char*) obj+offset)=value;
}
 static inline  uint32_t* ParticlesData_getp1__rng_s4(ParticlesData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+208);
  offset+=16+i0*4;
  return ( uint32_t*)(( char*) obj+offset);
}
 static inline ArrNFloat64 ParticlesData_getp_x(ParticlesData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+216);
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t ParticlesData_len_x(ParticlesData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+216);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double ParticlesData_get_x(const ParticlesData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+216);
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void ParticlesData_set_x(ParticlesData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+216);
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* ParticlesData_getp1_x(ParticlesData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+216);
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNFloat64 ParticlesData_getp_y(ParticlesData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+224);
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t ParticlesData_len_y(ParticlesData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+224);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double ParticlesData_get_y(const ParticlesData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+224);
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void ParticlesData_set_y(ParticlesData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+224);
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* ParticlesData_getp1_y(ParticlesData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+224);
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNFloat64 ParticlesData_getp_px(ParticlesData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+232);
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t ParticlesData_len_px(ParticlesData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+232);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double ParticlesData_get_px(const ParticlesData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+232);
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void ParticlesData_set_px(ParticlesData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+232);
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* ParticlesData_getp1_px(ParticlesData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+232);
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNFloat64 ParticlesData_getp_py(ParticlesData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+240);
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t ParticlesData_len_py(ParticlesData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+240);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double ParticlesData_get_py(const ParticlesData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+240);
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void ParticlesData_set_py(ParticlesData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+240);
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* ParticlesData_getp1_py(ParticlesData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+240);
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
#endif
// copyright ############################### //
// This file is part of the Xpart Package.   //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XPART_BASE_RNG_H
#define XPART_BASE_RNG_H

//#include <stdint.h> //only_for_context none

// Combined LCG-Thausworthe generator from (example 37-4):
// https://developer.nvidia.com/gpugems/gpugems3/part-vi-gpu-computing/chapter-37-efficient-random-number-generation-and-application
#define MASK 0xffffffffUL
#define TAUSWORTHE(s,a,b,c,d) ((((s) &c) <<d) &MASK) ^ (((((s) <<a) &MASK)^(s)) >>b)
#define LCG(s,A,C) ((((A*(s)) &MASK) + C) &MASK)


 static inline
double rng_get (uint32_t *s1, uint32_t *s2, uint32_t *s3, uint32_t *s4 ){
  *s1 = TAUSWORTHE (*s1, 13, 19, 4294967294UL, 12);  // p1=2^31-1
  *s2 = TAUSWORTHE (*s2, 2, 25, 4294967288UL, 4);    // p2=2^30-1
  *s3 = TAUSWORTHE (*s3, 3, 11, 4294967280UL, 17);   // p3=2^28-1
  *s4 = LCG(*s4, 1664525, 1013904223UL);             // p4=2^32

  // Combined period is lcm(p1,p2,p3,p4) ~ 2^121
  return ((*s1) ^ (*s2) ^ (*s3) ^ (*s4)) / 4294967296.0 ;
}

 static inline
void rng_set (uint32_t *s1, uint32_t *s2, uint32_t *s3, uint32_t *s4, uint32_t s ){
  *s1 = LCG (s, 69069, 0);
  if (*s1 < 2) *s1 += 2UL;
  *s2 = LCG (*s1, 69069, 0);
  if (*s2 < 8) *s2 += 8UL;
  *s3 = LCG (*s2, 69069, 0);
  if (*s3 < 16) *s3 += 16UL;
  *s4 = LCG (*s3, 69069, 0);

  /* "warm it up" */
  rng_get (s1, s2, s3, s4);
  rng_get (s1, s2, s3, s4);
  rng_get (s1, s2, s3, s4);
  rng_get (s1, s2, s3, s4);
  rng_get (s1, s2, s3, s4);
  rng_get (s1, s2, s3, s4);
  return;
}

#endif /* XPART_BASE_RNG_H */

 
// copyright ############################### //
// This file is part of the Xpart Package.   //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XPART_PARTICLES_RNG_H
#define XPART_PARTICLES_RNG_H

 
void Particles_initialize_rand_gen(ParticlesData particles,
      uint32_t* seeds, int n_init){

for (int ii=0; ii<n_init; ii++){ //autovectorized


    uint32_t s1, s2, s3, s4, s;
    s = seeds[ii];

    rng_set(&s1, &s2, &s3, &s4, s);

    ParticlesData_set__rng_s1(particles, ii, s1);
    ParticlesData_set__rng_s2(particles, ii, s2);
    ParticlesData_set__rng_s3(particles, ii, s3);
    ParticlesData_set__rng_s4(particles, ii, s4);

}//end autovectorized


}

#endif /* XPART_PARTICLES_RNG_H */


 typedef struct {
                 int64_t  _capacity;
                 int64_t  _num_active_particles;
                 int64_t  _num_lost_particles;
                 int64_t  start_tracking_at_element;
                 double  q0;
                 double  mass0;
      double* p0c;
      double* gamma0;
      double* beta0;
      double* s;
      double* zeta;
      double* ptau;
      double* delta;
      double* rpp;
      double* rvv;
      double* chi;
      double* charge_ratio;
      double* weight;
      int64_t* particle_id;
      int64_t* at_element;
      int64_t* at_turn;
      int64_t* state;
      int64_t* parent_particle_id;
      uint32_t* _rng_s1;
      uint32_t* _rng_s2;
      uint32_t* _rng_s3;
      uint32_t* _rng_s4;
      double* x;
      double* y;
      double* px;
      double* py;
                 int64_t ipart;
                 int64_t endpart;
      int8_t* io_buffer;
} LocalParticle;


         static inline
        void LocalParticle_add_to_p0c(LocalParticle* part, double value){
#ifndef FREEZE_VAR_p0c
  part->p0c[part->ipart] += value;
#endif
}


         static inline
        void LocalParticle_add_to_gamma0(LocalParticle* part, double value){
#ifndef FREEZE_VAR_gamma0
  part->gamma0[part->ipart] += value;
#endif
}


         static inline
        void LocalParticle_add_to_beta0(LocalParticle* part, double value){
#ifndef FREEZE_VAR_beta0
  part->beta0[part->ipart] += value;
#endif
}


         static inline
        void LocalParticle_add_to_s(LocalParticle* part, double value){
#ifndef FREEZE_VAR_s
  part->s[part->ipart] += value;
#endif
}


         static inline
        void LocalParticle_add_to_zeta(LocalParticle* part, double value){
#ifndef FREEZE_VAR_zeta
  part->zeta[part->ipart] += value;
#endif
}


         static inline
        void LocalParticle_add_to_ptau(LocalParticle* part, double value){
#ifndef FREEZE_VAR_ptau
  part->ptau[part->ipart] += value;
#endif
}


         static inline
        void LocalParticle_add_to_delta(LocalParticle* part, double value){
#ifndef FREEZE_VAR_delta
  part->delta[part->ipart] += value;
#endif
}


         static inline
        void LocalParticle_add_to_rpp(LocalParticle* part, double value){
#ifndef FREEZE_VAR_rpp
  part->rpp[part->ipart] += value;
#endif
}


         static inline
        void LocalParticle_add_to_rvv(LocalParticle* part, double value){
#ifndef FREEZE_VAR_rvv
  part->rvv[part->ipart] += value;
#endif
}


         static inline
        void LocalParticle_add_to_chi(LocalParticle* part, double value){
#ifndef FREEZE_VAR_chi
  part->chi[part->ipart] += value;
#endif
}


         static inline
        void LocalParticle_add_to_charge_ratio(LocalParticle* part, double value){
#ifndef FREEZE_VAR_charge_ratio
  part->charge_ratio[part->ipart] += value;
#endif
}


         static inline
        void LocalParticle_add_to_weight(LocalParticle* part, double value){
#ifndef FREEZE_VAR_weight
  part->weight[part->ipart] += value;
#endif
}


         static inline
        void LocalParticle_add_to_particle_id(LocalParticle* part, int64_t value){
#ifndef FREEZE_VAR_particle_id
  part->particle_id[part->ipart] += value;
#endif
}


         static inline
        void LocalParticle_add_to_at_element(LocalParticle* part, int64_t value){
#ifndef FREEZE_VAR_at_element
  part->at_element[part->ipart] += value;
#endif
}


         static inline
        void LocalParticle_add_to_at_turn(LocalParticle* part, int64_t value){
#ifndef FREEZE_VAR_at_turn
  part->at_turn[part->ipart] += value;
#endif
}


         static inline
        void LocalParticle_add_to_state(LocalParticle* part, int64_t value){
#ifndef FREEZE_VAR_state
  part->state[part->ipart] += value;
#endif
}


         static inline
        void LocalParticle_add_to_parent_particle_id(LocalParticle* part, int64_t value){
#ifndef FREEZE_VAR_parent_particle_id
  part->parent_particle_id[part->ipart] += value;
#endif
}


         static inline
        void LocalParticle_add_to__rng_s1(LocalParticle* part, uint32_t value){
#ifndef FREEZE_VAR__rng_s1
  part->_rng_s1[part->ipart] += value;
#endif
}


         static inline
        void LocalParticle_add_to__rng_s2(LocalParticle* part, uint32_t value){
#ifndef FREEZE_VAR__rng_s2
  part->_rng_s2[part->ipart] += value;
#endif
}


         static inline
        void LocalParticle_add_to__rng_s3(LocalParticle* part, uint32_t value){
#ifndef FREEZE_VAR__rng_s3
  part->_rng_s3[part->ipart] += value;
#endif
}


         static inline
        void LocalParticle_add_to__rng_s4(LocalParticle* part, uint32_t value){
#ifndef FREEZE_VAR__rng_s4
  part->_rng_s4[part->ipart] += value;
#endif
}


         static inline
        void LocalParticle_add_to_x(LocalParticle* part, double value){
#ifndef FREEZE_VAR_x
  part->x[part->ipart] += value;
#endif
}


         static inline
        void LocalParticle_add_to_y(LocalParticle* part, double value){
#ifndef FREEZE_VAR_y
  part->y[part->ipart] += value;
#endif
}


         static inline
        void LocalParticle_add_to_px(LocalParticle* part, double value){
#ifndef FREEZE_VAR_px
  part->px[part->ipart] += value;
#endif
}


         static inline
        void LocalParticle_add_to_py(LocalParticle* part, double value){
#ifndef FREEZE_VAR_py
  part->py[part->ipart] += value;
#endif
}


 static inline
int64_t LocalParticle_get__capacity(LocalParticle* part){
  return part->_capacity;
}
 static inline
int64_t LocalParticle_get__num_active_particles(LocalParticle* part){
  return part->_num_active_particles;
}
 static inline
int64_t LocalParticle_get__num_lost_particles(LocalParticle* part){
  return part->_num_lost_particles;
}
 static inline
int64_t LocalParticle_get_start_tracking_at_element(LocalParticle* part){
  return part->start_tracking_at_element;
}
 static inline
double LocalParticle_get_q0(LocalParticle* part){
  return part->q0;
}
 static inline
double LocalParticle_get_mass0(LocalParticle* part){
  return part->mass0;
}
 static inline
double LocalParticle_get_p0c(LocalParticle* part){
  return part->p0c[part->ipart];
}
 static inline
double LocalParticle_get_gamma0(LocalParticle* part){
  return part->gamma0[part->ipart];
}
 static inline
double LocalParticle_get_beta0(LocalParticle* part){
  return part->beta0[part->ipart];
}
 static inline
double LocalParticle_get_s(LocalParticle* part){
  return part->s[part->ipart];
}
 static inline
double LocalParticle_get_zeta(LocalParticle* part){
  return part->zeta[part->ipart];
}
 static inline
double LocalParticle_get_ptau(LocalParticle* part){
  return part->ptau[part->ipart];
}
 static inline
double LocalParticle_get_delta(LocalParticle* part){
  return part->delta[part->ipart];
}
 static inline
double LocalParticle_get_rpp(LocalParticle* part){
  return part->rpp[part->ipart];
}
 static inline
double LocalParticle_get_rvv(LocalParticle* part){
  return part->rvv[part->ipart];
}
 static inline
double LocalParticle_get_chi(LocalParticle* part){
  return part->chi[part->ipart];
}
 static inline
double LocalParticle_get_charge_ratio(LocalParticle* part){
  return part->charge_ratio[part->ipart];
}
 static inline
double LocalParticle_get_weight(LocalParticle* part){
  return part->weight[part->ipart];
}
 static inline
int64_t LocalParticle_get_particle_id(LocalParticle* part){
  return part->particle_id[part->ipart];
}
 static inline
int64_t LocalParticle_get_at_element(LocalParticle* part){
  return part->at_element[part->ipart];
}
 static inline
int64_t LocalParticle_get_at_turn(LocalParticle* part){
  return part->at_turn[part->ipart];
}
 static inline
int64_t LocalParticle_get_state(LocalParticle* part){
  return part->state[part->ipart];
}
 static inline
int64_t LocalParticle_get_parent_particle_id(LocalParticle* part){
  return part->parent_particle_id[part->ipart];
}
 static inline
uint32_t LocalParticle_get__rng_s1(LocalParticle* part){
  return part->_rng_s1[part->ipart];
}
 static inline
uint32_t LocalParticle_get__rng_s2(LocalParticle* part){
  return part->_rng_s2[part->ipart];
}
 static inline
uint32_t LocalParticle_get__rng_s3(LocalParticle* part){
  return part->_rng_s3[part->ipart];
}
 static inline
uint32_t LocalParticle_get__rng_s4(LocalParticle* part){
  return part->_rng_s4[part->ipart];
}
 static inline
double LocalParticle_get_x(LocalParticle* part){
  return part->x[part->ipart];
}
 static inline
double LocalParticle_get_y(LocalParticle* part){
  return part->y[part->ipart];
}
 static inline
double LocalParticle_get_px(LocalParticle* part){
  return part->px[part->ipart];
}
 static inline
double LocalParticle_get_py(LocalParticle* part){
  return part->py[part->ipart];
}


         static inline
        void LocalParticle_set_p0c(LocalParticle* part, double value){
#ifndef FREEZE_VAR_p0c
  part->p0c[part->ipart] = value;
#endif
}

         static inline
        void LocalParticle_set_gamma0(LocalParticle* part, double value){
#ifndef FREEZE_VAR_gamma0
  part->gamma0[part->ipart] = value;
#endif
}

         static inline
        void LocalParticle_set_beta0(LocalParticle* part, double value){
#ifndef FREEZE_VAR_beta0
  part->beta0[part->ipart] = value;
#endif
}

         static inline
        void LocalParticle_set_s(LocalParticle* part, double value){
#ifndef FREEZE_VAR_s
  part->s[part->ipart] = value;
#endif
}

         static inline
        void LocalParticle_set_zeta(LocalParticle* part, double value){
#ifndef FREEZE_VAR_zeta
  part->zeta[part->ipart] = value;
#endif
}

         static inline
        void LocalParticle_set_ptau(LocalParticle* part, double value){
#ifndef FREEZE_VAR_ptau
  part->ptau[part->ipart] = value;
#endif
}

         static inline
        void LocalParticle_set_delta(LocalParticle* part, double value){
#ifndef FREEZE_VAR_delta
  part->delta[part->ipart] = value;
#endif
}

         static inline
        void LocalParticle_set_rpp(LocalParticle* part, double value){
#ifndef FREEZE_VAR_rpp
  part->rpp[part->ipart] = value;
#endif
}

         static inline
        void LocalParticle_set_rvv(LocalParticle* part, double value){
#ifndef FREEZE_VAR_rvv
  part->rvv[part->ipart] = value;
#endif
}

         static inline
        void LocalParticle_set_chi(LocalParticle* part, double value){
#ifndef FREEZE_VAR_chi
  part->chi[part->ipart] = value;
#endif
}

         static inline
        void LocalParticle_set_charge_ratio(LocalParticle* part, double value){
#ifndef FREEZE_VAR_charge_ratio
  part->charge_ratio[part->ipart] = value;
#endif
}

         static inline
        void LocalParticle_set_weight(LocalParticle* part, double value){
#ifndef FREEZE_VAR_weight
  part->weight[part->ipart] = value;
#endif
}

         static inline
        void LocalParticle_set_particle_id(LocalParticle* part, int64_t value){
#ifndef FREEZE_VAR_particle_id
  part->particle_id[part->ipart] = value;
#endif
}

         static inline
        void LocalParticle_set_at_element(LocalParticle* part, int64_t value){
#ifndef FREEZE_VAR_at_element
  part->at_element[part->ipart] = value;
#endif
}

         static inline
        void LocalParticle_set_at_turn(LocalParticle* part, int64_t value){
#ifndef FREEZE_VAR_at_turn
  part->at_turn[part->ipart] = value;
#endif
}

         static inline
        void LocalParticle_set_state(LocalParticle* part, int64_t value){
#ifndef FREEZE_VAR_state
  part->state[part->ipart] = value;
#endif
}

         static inline
        void LocalParticle_set_parent_particle_id(LocalParticle* part, int64_t value){
#ifndef FREEZE_VAR_parent_particle_id
  part->parent_particle_id[part->ipart] = value;
#endif
}

         static inline
        void LocalParticle_set__rng_s1(LocalParticle* part, uint32_t value){
#ifndef FREEZE_VAR__rng_s1
  part->_rng_s1[part->ipart] = value;
#endif
}

         static inline
        void LocalParticle_set__rng_s2(LocalParticle* part, uint32_t value){
#ifndef FREEZE_VAR__rng_s2
  part->_rng_s2[part->ipart] = value;
#endif
}

         static inline
        void LocalParticle_set__rng_s3(LocalParticle* part, uint32_t value){
#ifndef FREEZE_VAR__rng_s3
  part->_rng_s3[part->ipart] = value;
#endif
}

         static inline
        void LocalParticle_set__rng_s4(LocalParticle* part, uint32_t value){
#ifndef FREEZE_VAR__rng_s4
  part->_rng_s4[part->ipart] = value;
#endif
}

         static inline
        void LocalParticle_set_x(LocalParticle* part, double value){
#ifndef FREEZE_VAR_x
  part->x[part->ipart] = value;
#endif
}

         static inline
        void LocalParticle_set_y(LocalParticle* part, double value){
#ifndef FREEZE_VAR_y
  part->y[part->ipart] = value;
#endif
}

         static inline
        void LocalParticle_set_px(LocalParticle* part, double value){
#ifndef FREEZE_VAR_px
  part->px[part->ipart] = value;
#endif
}

         static inline
        void LocalParticle_set_py(LocalParticle* part, double value){
#ifndef FREEZE_VAR_py
  part->py[part->ipart] = value;
#endif
}


         static inline
        void LocalParticle_scale_p0c(LocalParticle* part, double value){
#ifndef FREEZE_VAR_p0c
  part->p0c[part->ipart] *= value;
#endif
}


         static inline
        void LocalParticle_scale_gamma0(LocalParticle* part, double value){
#ifndef FREEZE_VAR_gamma0
  part->gamma0[part->ipart] *= value;
#endif
}


         static inline
        void LocalParticle_scale_beta0(LocalParticle* part, double value){
#ifndef FREEZE_VAR_beta0
  part->beta0[part->ipart] *= value;
#endif
}


         static inline
        void LocalParticle_scale_s(LocalParticle* part, double value){
#ifndef FREEZE_VAR_s
  part->s[part->ipart] *= value;
#endif
}


         static inline
        void LocalParticle_scale_zeta(LocalParticle* part, double value){
#ifndef FREEZE_VAR_zeta
  part->zeta[part->ipart] *= value;
#endif
}


         static inline
        void LocalParticle_scale_ptau(LocalParticle* part, double value){
#ifndef FREEZE_VAR_ptau
  part->ptau[part->ipart] *= value;
#endif
}


         static inline
        void LocalParticle_scale_delta(LocalParticle* part, double value){
#ifndef FREEZE_VAR_delta
  part->delta[part->ipart] *= value;
#endif
}


         static inline
        void LocalParticle_scale_rpp(LocalParticle* part, double value){
#ifndef FREEZE_VAR_rpp
  part->rpp[part->ipart] *= value;
#endif
}


         static inline
        void LocalParticle_scale_rvv(LocalParticle* part, double value){
#ifndef FREEZE_VAR_rvv
  part->rvv[part->ipart] *= value;
#endif
}


         static inline
        void LocalParticle_scale_chi(LocalParticle* part, double value){
#ifndef FREEZE_VAR_chi
  part->chi[part->ipart] *= value;
#endif
}


         static inline
        void LocalParticle_scale_charge_ratio(LocalParticle* part, double value){
#ifndef FREEZE_VAR_charge_ratio
  part->charge_ratio[part->ipart] *= value;
#endif
}


         static inline
        void LocalParticle_scale_weight(LocalParticle* part, double value){
#ifndef FREEZE_VAR_weight
  part->weight[part->ipart] *= value;
#endif
}


         static inline
        void LocalParticle_scale_particle_id(LocalParticle* part, int64_t value){
#ifndef FREEZE_VAR_particle_id
  part->particle_id[part->ipart] *= value;
#endif
}


         static inline
        void LocalParticle_scale_at_element(LocalParticle* part, int64_t value){
#ifndef FREEZE_VAR_at_element
  part->at_element[part->ipart] *= value;
#endif
}


         static inline
        void LocalParticle_scale_at_turn(LocalParticle* part, int64_t value){
#ifndef FREEZE_VAR_at_turn
  part->at_turn[part->ipart] *= value;
#endif
}


         static inline
        void LocalParticle_scale_state(LocalParticle* part, int64_t value){
#ifndef FREEZE_VAR_state
  part->state[part->ipart] *= value;
#endif
}


         static inline
        void LocalParticle_scale_parent_particle_id(LocalParticle* part, int64_t value){
#ifndef FREEZE_VAR_parent_particle_id
  part->parent_particle_id[part->ipart] *= value;
#endif
}


         static inline
        void LocalParticle_scale__rng_s1(LocalParticle* part, uint32_t value){
#ifndef FREEZE_VAR__rng_s1
  part->_rng_s1[part->ipart] *= value;
#endif
}


         static inline
        void LocalParticle_scale__rng_s2(LocalParticle* part, uint32_t value){
#ifndef FREEZE_VAR__rng_s2
  part->_rng_s2[part->ipart] *= value;
#endif
}


         static inline
        void LocalParticle_scale__rng_s3(LocalParticle* part, uint32_t value){
#ifndef FREEZE_VAR__rng_s3
  part->_rng_s3[part->ipart] *= value;
#endif
}


         static inline
        void LocalParticle_scale__rng_s4(LocalParticle* part, uint32_t value){
#ifndef FREEZE_VAR__rng_s4
  part->_rng_s4[part->ipart] *= value;
#endif
}


         static inline
        void LocalParticle_scale_x(LocalParticle* part, double value){
#ifndef FREEZE_VAR_x
  part->x[part->ipart] *= value;
#endif
}


         static inline
        void LocalParticle_scale_y(LocalParticle* part, double value){
#ifndef FREEZE_VAR_y
  part->y[part->ipart] *= value;
#endif
}


         static inline
        void LocalParticle_scale_px(LocalParticle* part, double value){
#ifndef FREEZE_VAR_px
  part->px[part->ipart] *= value;
#endif
}


         static inline
        void LocalParticle_scale_py(LocalParticle* part, double value){
#ifndef FREEZE_VAR_py
  part->py[part->ipart] *= value;
#endif
}



     static inline
    void LocalParticle_exchange(LocalParticle* part, int64_t i1, int64_t i2){
    
    {
    double temp = part->p0c[i2];
    part->p0c[i2] = part->p0c[i1];
    part->p0c[i1] = temp;
     }
    {
    double temp = part->gamma0[i2];
    part->gamma0[i2] = part->gamma0[i1];
    part->gamma0[i1] = temp;
     }
    {
    double temp = part->beta0[i2];
    part->beta0[i2] = part->beta0[i1];
    part->beta0[i1] = temp;
     }
    {
    double temp = part->s[i2];
    part->s[i2] = part->s[i1];
    part->s[i1] = temp;
     }
    {
    double temp = part->zeta[i2];
    part->zeta[i2] = part->zeta[i1];
    part->zeta[i1] = temp;
     }
    {
    double temp = part->ptau[i2];
    part->ptau[i2] = part->ptau[i1];
    part->ptau[i1] = temp;
     }
    {
    double temp = part->delta[i2];
    part->delta[i2] = part->delta[i1];
    part->delta[i1] = temp;
     }
    {
    double temp = part->rpp[i2];
    part->rpp[i2] = part->rpp[i1];
    part->rpp[i1] = temp;
     }
    {
    double temp = part->rvv[i2];
    part->rvv[i2] = part->rvv[i1];
    part->rvv[i1] = temp;
     }
    {
    double temp = part->chi[i2];
    part->chi[i2] = part->chi[i1];
    part->chi[i1] = temp;
     }
    {
    double temp = part->charge_ratio[i2];
    part->charge_ratio[i2] = part->charge_ratio[i1];
    part->charge_ratio[i1] = temp;
     }
    {
    double temp = part->weight[i2];
    part->weight[i2] = part->weight[i1];
    part->weight[i1] = temp;
     }
    {
    int64_t temp = part->particle_id[i2];
    part->particle_id[i2] = part->particle_id[i1];
    part->particle_id[i1] = temp;
     }
    {
    int64_t temp = part->at_element[i2];
    part->at_element[i2] = part->at_element[i1];
    part->at_element[i1] = temp;
     }
    {
    int64_t temp = part->at_turn[i2];
    part->at_turn[i2] = part->at_turn[i1];
    part->at_turn[i1] = temp;
     }
    {
    int64_t temp = part->state[i2];
    part->state[i2] = part->state[i1];
    part->state[i1] = temp;
     }
    {
    int64_t temp = part->parent_particle_id[i2];
    part->parent_particle_id[i2] = part->parent_particle_id[i1];
    part->parent_particle_id[i1] = temp;
     }
    {
    uint32_t temp = part->_rng_s1[i2];
    part->_rng_s1[i2] = part->_rng_s1[i1];
    part->_rng_s1[i1] = temp;
     }
    {
    uint32_t temp = part->_rng_s2[i2];
    part->_rng_s2[i2] = part->_rng_s2[i1];
    part->_rng_s2[i1] = temp;
     }
    {
    uint32_t temp = part->_rng_s3[i2];
    part->_rng_s3[i2] = part->_rng_s3[i1];
    part->_rng_s3[i1] = temp;
     }
    {
    uint32_t temp = part->_rng_s4[i2];
    part->_rng_s4[i2] = part->_rng_s4[i1];
    part->_rng_s4[i1] = temp;
     }
    {
    double temp = part->x[i2];
    part->x[i2] = part->x[i1];
    part->x[i1] = temp;
     }
    {
    double temp = part->y[i2];
    part->y[i2] = part->y[i1];
    part->y[i1] = temp;
     }
    {
    double temp = part->px[i2];
    part->px[i2] = part->px[i1];
    part->px[i1] = temp;
     }
    {
    double temp = part->py[i2];
    part->py[i2] = part->py[i1];
    part->py[i1] = temp;
     }}



         static inline
          int8_t* LocalParticle_get_io_buffer(LocalParticle* part){
            return part->io_buffer;
        }

        

         static inline
        void Particles_to_LocalParticle(ParticlesData source,
                                        LocalParticle* dest,
                                        int64_t id,
                                        int64_t eid){
  dest->_capacity = ParticlesData_get__capacity(source);
  dest->_num_active_particles = ParticlesData_get__num_active_particles(source);
  dest->_num_lost_particles = ParticlesData_get__num_lost_particles(source);
  dest->start_tracking_at_element = ParticlesData_get_start_tracking_at_element(source);
  dest->q0 = ParticlesData_get_q0(source);
  dest->mass0 = ParticlesData_get_mass0(source);
  dest->p0c = ParticlesData_getp1_p0c(source, 0);
  dest->gamma0 = ParticlesData_getp1_gamma0(source, 0);
  dest->beta0 = ParticlesData_getp1_beta0(source, 0);
  dest->s = ParticlesData_getp1_s(source, 0);
  dest->zeta = ParticlesData_getp1_zeta(source, 0);
  dest->ptau = ParticlesData_getp1_ptau(source, 0);
  dest->delta = ParticlesData_getp1_delta(source, 0);
  dest->rpp = ParticlesData_getp1_rpp(source, 0);
  dest->rvv = ParticlesData_getp1_rvv(source, 0);
  dest->chi = ParticlesData_getp1_chi(source, 0);
  dest->charge_ratio = ParticlesData_getp1_charge_ratio(source, 0);
  dest->weight = ParticlesData_getp1_weight(source, 0);
  dest->particle_id = ParticlesData_getp1_particle_id(source, 0);
  dest->at_element = ParticlesData_getp1_at_element(source, 0);
  dest->at_turn = ParticlesData_getp1_at_turn(source, 0);
  dest->state = ParticlesData_getp1_state(source, 0);
  dest->parent_particle_id = ParticlesData_getp1_parent_particle_id(source, 0);
  dest->_rng_s1 = ParticlesData_getp1__rng_s1(source, 0);
  dest->_rng_s2 = ParticlesData_getp1__rng_s2(source, 0);
  dest->_rng_s3 = ParticlesData_getp1__rng_s3(source, 0);
  dest->_rng_s4 = ParticlesData_getp1__rng_s4(source, 0);
  dest->x = ParticlesData_getp1_x(source, 0);
  dest->y = ParticlesData_getp1_y(source, 0);
  dest->px = ParticlesData_getp1_px(source, 0);
  dest->py = ParticlesData_getp1_py(source, 0);
  dest->ipart = id;
  dest->endpart = eid;
}


         static inline
        void LocalParticle_to_Particles(
                                        LocalParticle* source,
                                        ParticlesData dest,
                                        int64_t id,
                                        int64_t set_scalar){
if (set_scalar){
  ParticlesData_set__capacity(dest,      LocalParticle_get__capacity(source));
  ParticlesData_set__num_active_particles(dest,      LocalParticle_get__num_active_particles(source));
  ParticlesData_set__num_lost_particles(dest,      LocalParticle_get__num_lost_particles(source));
  ParticlesData_set_start_tracking_at_element(dest,      LocalParticle_get_start_tracking_at_element(source));
  ParticlesData_set_q0(dest,      LocalParticle_get_q0(source));
  ParticlesData_set_mass0(dest,      LocalParticle_get_mass0(source));
}
  ParticlesData_set_p0c(dest, id,       LocalParticle_get_p0c(source));
  ParticlesData_set_gamma0(dest, id,       LocalParticle_get_gamma0(source));
  ParticlesData_set_beta0(dest, id,       LocalParticle_get_beta0(source));
  ParticlesData_set_s(dest, id,       LocalParticle_get_s(source));
  ParticlesData_set_zeta(dest, id,       LocalParticle_get_zeta(source));
  ParticlesData_set_ptau(dest, id,       LocalParticle_get_ptau(source));
  ParticlesData_set_delta(dest, id,       LocalParticle_get_delta(source));
  ParticlesData_set_rpp(dest, id,       LocalParticle_get_rpp(source));
  ParticlesData_set_rvv(dest, id,       LocalParticle_get_rvv(source));
  ParticlesData_set_chi(dest, id,       LocalParticle_get_chi(source));
  ParticlesData_set_charge_ratio(dest, id,       LocalParticle_get_charge_ratio(source));
  ParticlesData_set_weight(dest, id,       LocalParticle_get_weight(source));
  ParticlesData_set_particle_id(dest, id,       LocalParticle_get_particle_id(source));
  ParticlesData_set_at_element(dest, id,       LocalParticle_get_at_element(source));
  ParticlesData_set_at_turn(dest, id,       LocalParticle_get_at_turn(source));
  ParticlesData_set_state(dest, id,       LocalParticle_get_state(source));
  ParticlesData_set_parent_particle_id(dest, id,       LocalParticle_get_parent_particle_id(source));
  ParticlesData_set__rng_s1(dest, id,       LocalParticle_get__rng_s1(source));
  ParticlesData_set__rng_s2(dest, id,       LocalParticle_get__rng_s2(source));
  ParticlesData_set__rng_s3(dest, id,       LocalParticle_get__rng_s3(source));
  ParticlesData_set__rng_s4(dest, id,       LocalParticle_get__rng_s4(source));
  ParticlesData_set_x(dest, id,       LocalParticle_get_x(source));
  ParticlesData_set_y(dest, id,       LocalParticle_get_y(source));
  ParticlesData_set_px(dest, id,       LocalParticle_get_px(source));
  ParticlesData_set_py(dest, id,       LocalParticle_get_py(source));
}


     static inline
    double LocalParticle_get_energy0(LocalParticle* part){

        double const p0c = LocalParticle_get_p0c(part);
        double const m0  = LocalParticle_get_mass0(part);

        return sqrt( p0c * p0c + m0 * m0 );
    }

     static inline
    void LocalParticle_update_ptau(LocalParticle* part, double new_ptau_value){

        double const beta0 = LocalParticle_get_beta0(part);

        double const ptau = new_ptau_value;

        double const irpp = sqrt(ptau*ptau + 2*ptau/beta0 +1);

        double const new_rpp = 1./irpp;
        LocalParticle_set_delta(part, irpp - 1.);

        double const new_rvv = irpp/(1 + beta0*ptau);
        LocalParticle_set_rvv(part, new_rvv);
        LocalParticle_set_ptau(part, ptau);

        LocalParticle_set_rpp(part, new_rpp );
    }

     static inline
    void LocalParticle_update_delta(LocalParticle* part, double new_delta_value){
        double const beta0 = LocalParticle_get_beta0(part);
        double const delta_beta0 = new_delta_value * beta0;
        double const ptau_beta0  = sqrt( delta_beta0 * delta_beta0 +
                                    2. * delta_beta0 * beta0 + 1. ) - 1.;

        double const one_plus_delta = 1. + new_delta_value;
        double const rvv    = ( one_plus_delta ) / ( 1. + ptau_beta0 );
        double const rpp    = 1. / one_plus_delta;
        double const ptau = ptau_beta0 / beta0;

        LocalParticle_set_delta(part, new_delta_value);

        LocalParticle_set_rvv(part, rvv );
        LocalParticle_set_rpp(part, rpp );
        LocalParticle_set_ptau(part, ptau );

    }

     static inline
    double LocalParticle_get_pzeta(LocalParticle* part){

        double const ptau = LocalParticle_get_ptau(part);
        double const beta0 = LocalParticle_get_beta0(part);

        return ptau/beta0;

    }

     static inline
    void LocalParticle_update_pzeta(LocalParticle* part, double new_pzeta_value){

        double const beta0 = LocalParticle_get_beta0(part);
        LocalParticle_update_ptau(part, beta0*new_pzeta_value);

    }

     static inline
    void increment_at_element(LocalParticle* part0, int64_t const increment){


    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

            LocalParticle_add_to_at_element(part, increment);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }


    }

     static inline
    void increment_at_turn(LocalParticle* part0, int flag_reset_s){


    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

        LocalParticle_add_to_at_turn(part, 1);
        LocalParticle_set_at_element(part, 0);
        if (flag_reset_s>0){
            LocalParticle_set_s(part, 0.);
        }

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

    }

     static inline
    void increment_at_turn_backtrack(LocalParticle* part0, int flag_reset_s,
                                     double const line_length,
                                     int64_t const num_elements){


    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

        LocalParticle_add_to_at_turn(part, -1);
        LocalParticle_set_at_element(part, num_elements);
        if (flag_reset_s>0){
            LocalParticle_set_s(part, line_length);
        }

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

    }

    // check_is_active has different implementation on CPU and GPU

    #define CPU_SERIAL_IMPLEM //only_for_context cpu_serial
//    #define CPU_OMP_IMPLEM //only_for_context cpu_openmp

    #ifdef CPU_SERIAL_IMPLEM

     static inline
    int64_t check_is_active(LocalParticle* part) {
        int64_t ipart=0;
        while (ipart < part->_num_active_particles){
            if (part->state[ipart]<1){
                LocalParticle_exchange(
                    part, ipart, part->_num_active_particles-1);
                part->_num_active_particles--;
                part->_num_lost_particles++;
            }
            else{
                ipart++;
            }
        }

        if (part->_num_active_particles==0){
            return 0;//All particles lost
        } else {
            return 1; //Some stable particles are still present
        }
    }

    #else // not CPU_SERIAL_IMPLEM
    #ifdef CPU_OMP_IMPLEM
    
     static inline
    int64_t check_is_active(LocalParticle* part) {
    #ifndef SKIP_SWAPS
        int64_t ipart = part->ipart;
        int64_t endpart = part->endpart;
        
        int64_t left = ipart;
        int64_t right = endpart - 1;
        int64_t swap_made = 0;
        int64_t has_alive = 0;
        
        if (left == right) return part->state[left] > 0;
        
        while (left < right) {
            if (part->state[left] > 0) {
                left++;
                has_alive = 1;
            }
            else if (part->state[right] <= 0) right--;
            else {
                LocalParticle_exchange(part, left, right);
                left++;
                right--;
                swap_made = 1;
            }
        }

        return swap_made || has_alive;
    #else
        return 1;
    #endif
    }
    
     static inline
    void count_reorganized_particles(LocalParticle* part) {
        int64_t num_active = 0;
        int64_t num_lost = 0;
        
        for (int64_t i = part->ipart; i < part->endpart; i++) {
            if (part->state[i] <= -999999999) break;
            else if (part->state[i] > 0) num_active++;
            else num_lost++;
        }
        
        part->_num_active_particles = 1;//num_active;
        part->_num_lost_particles = 1;//num_lost;
    }
    
    #else // not CPU_SERIAL_IMPLEM and not CPU_OMP_IMPLEM

     static inline
    int64_t check_is_active(LocalParticle* part) {
        return LocalParticle_get_state(part)>0;
    };

    #endif // CPU_OMP_IMPLEM
    #endif // CPU_SERIAL_IMPLEM

    #undef CPU_SERIAL_IMPLEM //only_for_context cpu_serial
//    #undef CPU_OMP_IMPLEM //only_for_context cpu_openmp


    
            #ifdef XTRACK_GLOBAL_XY_LIMIT
        
             static inline
            void global_aperture_check(LocalParticle* part0) {

    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

                    double const x = LocalParticle_get_x(part);
                    double const y = LocalParticle_get_y(part);
        
                int64_t const is_alive = (int64_t)(
                          (x >= -XTRACK_GLOBAL_XY_LIMIT) &&
                          (x <=  XTRACK_GLOBAL_XY_LIMIT) &&
                          (y >= -XTRACK_GLOBAL_XY_LIMIT) &&
                          (y <=  XTRACK_GLOBAL_XY_LIMIT) );
        
                // I assume that if I am in the function is because
                    if (!is_alive){
                       LocalParticle_set_state(part, -1);
                }

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

            }

            #endif
            
             static inline
            void LocalParticle_add_to_energy(LocalParticle* part, double delta_energy, int pz_only ){
                double ptau = LocalParticle_get_ptau(part);
                double const p0c = LocalParticle_get_p0c(part);
        
                ptau += delta_energy/p0c;
                double const old_rpp = LocalParticle_get_rpp(part);
        
                LocalParticle_update_ptau(part, ptau);
        
                if (!pz_only) {
                    double const new_rpp = LocalParticle_get_rpp(part);
                    double const f = old_rpp / new_rpp;
                    LocalParticle_scale_px(part, f);
                    LocalParticle_scale_py(part, f);
                }
            }
            
            
             static inline
            void LocalParticle_update_p0c(LocalParticle* part, double new_p0c_value){
        
                double const mass0 = LocalParticle_get_mass0(part);
                double const old_p0c = LocalParticle_get_p0c(part);
                double const old_delta = LocalParticle_get_delta(part);
                double const old_beta0 = LocalParticle_get_beta0(part);
        
                double const ppc = old_p0c * old_delta + old_p0c;
                double const new_delta = (ppc - new_p0c_value)/new_p0c_value;
        
                double const new_energy0 = sqrt(new_p0c_value*new_p0c_value + mass0 * mass0);
                double const new_beta0 = new_p0c_value / new_energy0;
                double const new_gamma0 = new_energy0 / mass0;
        
                LocalParticle_set_p0c(part, new_p0c_value);
                LocalParticle_set_gamma0(part, new_gamma0);
                LocalParticle_set_beta0(part, new_beta0);
        
                LocalParticle_update_delta(part, new_delta);
        
                LocalParticle_scale_px(part, old_p0c/new_p0c_value);
                LocalParticle_scale_py(part, old_p0c/new_p0c_value);
        
                LocalParticle_scale_zeta(part, new_beta0/old_beta0);
        
            }
    
             static inline
            void LocalParticle_kill_particle(LocalParticle* part, int64_t kill_state) {
                LocalParticle_set_x(part, 1e30);
                LocalParticle_set_px(part, 1e30);
                LocalParticle_set_y(part, 1e30);
                LocalParticle_set_py(part, 1e30);
                LocalParticle_set_zeta(part, 1e30);
                LocalParticle_update_delta(part, -1);  // zero energy
                LocalParticle_set_state(part, kill_state);
            }
         

#ifndef XOBJ_TYPEDEF_CollimatorImpactsData
#define XOBJ_TYPEDEF_CollimatorImpactsData
typedef   struct CollimatorImpactsData_s * CollimatorImpactsData;
 static inline CollimatorImpactsData CollimatorImpactsData_getp(CollimatorImpactsData  obj){
  int64_t offset=0;
  return (CollimatorImpactsData)(( char*) obj+offset);
}
 static inline RecordIndex CollimatorImpactsData_getp__index(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=8;
  return (RecordIndex)(( char*) obj+offset);
}
 static inline int64_t CollimatorImpactsData_get__index_capacity(const CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=8;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void CollimatorImpactsData_set__index_capacity(CollimatorImpactsData  obj, int64_t value){
  int64_t offset=0;
  offset+=8;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* CollimatorImpactsData_getp__index_capacity(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=8;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline uint32_t CollimatorImpactsData_get__index_num_recorded(const CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=16;
  return *( uint32_t*)(( char*) obj+offset);
}
 static inline void CollimatorImpactsData_set__index_num_recorded(CollimatorImpactsData  obj, uint32_t value){
  int64_t offset=0;
  offset+=16;
  *( uint32_t*)(( char*) obj+offset)=value;
}
 static inline  uint32_t* CollimatorImpactsData_getp__index_num_recorded(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=16;
  return ( uint32_t*)(( char*) obj+offset);
}
 static inline uint32_t CollimatorImpactsData_get__index__dummy(const CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=24;
  return *( uint32_t*)(( char*) obj+offset);
}
 static inline void CollimatorImpactsData_set__index__dummy(CollimatorImpactsData  obj, uint32_t value){
  int64_t offset=0;
  offset+=24;
  *( uint32_t*)(( char*) obj+offset)=value;
}
 static inline  uint32_t* CollimatorImpactsData_getp__index__dummy(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=24;
  return ( uint32_t*)(( char*) obj+offset);
}
 static inline int64_t CollimatorImpactsData_get__index_buffer_id(const CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=32;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void CollimatorImpactsData_set__index_buffer_id(CollimatorImpactsData  obj, int64_t value){
  int64_t offset=0;
  offset+=32;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* CollimatorImpactsData_getp__index_buffer_id(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=32;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline ArrNInt64 CollimatorImpactsData_getp_at_element(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=272;
  return (ArrNInt64)(( char*) obj+offset);
}
 static inline int64_t CollimatorImpactsData_len_at_element(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=272;
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline int64_t CollimatorImpactsData_get_at_element(const CollimatorImpactsData  obj, int64_t i0){
  int64_t offset=0;
  offset+=272;
  offset+=16+i0*8;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void CollimatorImpactsData_set_at_element(CollimatorImpactsData  obj, int64_t i0, int64_t value){
  int64_t offset=0;
  offset+=272;
  offset+=16+i0*8;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* CollimatorImpactsData_getp1_at_element(CollimatorImpactsData  obj, int64_t i0){
  int64_t offset=0;
  offset+=272;
  offset+=16+i0*8;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline ArrNInt64 CollimatorImpactsData_getp_at_turn(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+40);
  return (ArrNInt64)(( char*) obj+offset);
}
 static inline int64_t CollimatorImpactsData_len_at_turn(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+40);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline int64_t CollimatorImpactsData_get_at_turn(const CollimatorImpactsData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+40);
  offset+=16+i0*8;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void CollimatorImpactsData_set_at_turn(CollimatorImpactsData  obj, int64_t i0, int64_t value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+40);
  offset+=16+i0*8;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* CollimatorImpactsData_getp1_at_turn(CollimatorImpactsData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+40);
  offset+=16+i0*8;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline ArrNFloat64 CollimatorImpactsData_getp_s(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+48);
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t CollimatorImpactsData_len_s(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+48);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double CollimatorImpactsData_get_s(const CollimatorImpactsData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+48);
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void CollimatorImpactsData_set_s(CollimatorImpactsData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+48);
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* CollimatorImpactsData_getp1_s(CollimatorImpactsData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+48);
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNInt64 CollimatorImpactsData_getp_interaction_type(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+56);
  return (ArrNInt64)(( char*) obj+offset);
}
 static inline int64_t CollimatorImpactsData_len_interaction_type(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+56);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline int64_t CollimatorImpactsData_get_interaction_type(const CollimatorImpactsData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+56);
  offset+=16+i0*8;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void CollimatorImpactsData_set_interaction_type(CollimatorImpactsData  obj, int64_t i0, int64_t value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+56);
  offset+=16+i0*8;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* CollimatorImpactsData_getp1_interaction_type(CollimatorImpactsData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+56);
  offset+=16+i0*8;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline ArrNInt64 CollimatorImpactsData_getp_parent_id(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+64);
  return (ArrNInt64)(( char*) obj+offset);
}
 static inline int64_t CollimatorImpactsData_len_parent_id(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+64);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline int64_t CollimatorImpactsData_get_parent_id(const CollimatorImpactsData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+64);
  offset+=16+i0*8;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void CollimatorImpactsData_set_parent_id(CollimatorImpactsData  obj, int64_t i0, int64_t value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+64);
  offset+=16+i0*8;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* CollimatorImpactsData_getp1_parent_id(CollimatorImpactsData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+64);
  offset+=16+i0*8;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline ArrNFloat64 CollimatorImpactsData_getp_parent_x(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+72);
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t CollimatorImpactsData_len_parent_x(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+72);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double CollimatorImpactsData_get_parent_x(const CollimatorImpactsData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+72);
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void CollimatorImpactsData_set_parent_x(CollimatorImpactsData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+72);
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* CollimatorImpactsData_getp1_parent_x(CollimatorImpactsData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+72);
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNFloat64 CollimatorImpactsData_getp_parent_px(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+80);
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t CollimatorImpactsData_len_parent_px(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+80);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double CollimatorImpactsData_get_parent_px(const CollimatorImpactsData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+80);
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void CollimatorImpactsData_set_parent_px(CollimatorImpactsData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+80);
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* CollimatorImpactsData_getp1_parent_px(CollimatorImpactsData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+80);
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNFloat64 CollimatorImpactsData_getp_parent_y(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+88);
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t CollimatorImpactsData_len_parent_y(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+88);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double CollimatorImpactsData_get_parent_y(const CollimatorImpactsData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+88);
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void CollimatorImpactsData_set_parent_y(CollimatorImpactsData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+88);
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* CollimatorImpactsData_getp1_parent_y(CollimatorImpactsData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+88);
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNFloat64 CollimatorImpactsData_getp_parent_py(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+96);
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t CollimatorImpactsData_len_parent_py(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+96);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double CollimatorImpactsData_get_parent_py(const CollimatorImpactsData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+96);
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void CollimatorImpactsData_set_parent_py(CollimatorImpactsData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+96);
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* CollimatorImpactsData_getp1_parent_py(CollimatorImpactsData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+96);
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNFloat64 CollimatorImpactsData_getp_parent_zeta(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+104);
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t CollimatorImpactsData_len_parent_zeta(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+104);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double CollimatorImpactsData_get_parent_zeta(const CollimatorImpactsData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+104);
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void CollimatorImpactsData_set_parent_zeta(CollimatorImpactsData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+104);
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* CollimatorImpactsData_getp1_parent_zeta(CollimatorImpactsData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+104);
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNFloat64 CollimatorImpactsData_getp_parent_delta(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+112);
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t CollimatorImpactsData_len_parent_delta(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+112);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double CollimatorImpactsData_get_parent_delta(const CollimatorImpactsData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+112);
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void CollimatorImpactsData_set_parent_delta(CollimatorImpactsData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+112);
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* CollimatorImpactsData_getp1_parent_delta(CollimatorImpactsData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+112);
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNFloat64 CollimatorImpactsData_getp_parent_energy(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+120);
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t CollimatorImpactsData_len_parent_energy(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+120);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double CollimatorImpactsData_get_parent_energy(const CollimatorImpactsData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+120);
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void CollimatorImpactsData_set_parent_energy(CollimatorImpactsData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+120);
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* CollimatorImpactsData_getp1_parent_energy(CollimatorImpactsData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+120);
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNFloat64 CollimatorImpactsData_getp_parent_mass(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+128);
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t CollimatorImpactsData_len_parent_mass(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+128);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double CollimatorImpactsData_get_parent_mass(const CollimatorImpactsData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+128);
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void CollimatorImpactsData_set_parent_mass(CollimatorImpactsData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+128);
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* CollimatorImpactsData_getp1_parent_mass(CollimatorImpactsData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+128);
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNInt64 CollimatorImpactsData_getp_parent_charge(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+136);
  return (ArrNInt64)(( char*) obj+offset);
}
 static inline int64_t CollimatorImpactsData_len_parent_charge(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+136);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline int64_t CollimatorImpactsData_get_parent_charge(const CollimatorImpactsData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+136);
  offset+=16+i0*8;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void CollimatorImpactsData_set_parent_charge(CollimatorImpactsData  obj, int64_t i0, int64_t value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+136);
  offset+=16+i0*8;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* CollimatorImpactsData_getp1_parent_charge(CollimatorImpactsData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+136);
  offset+=16+i0*8;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline ArrNInt64 CollimatorImpactsData_getp_parent_z(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+144);
  return (ArrNInt64)(( char*) obj+offset);
}
 static inline int64_t CollimatorImpactsData_len_parent_z(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+144);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline int64_t CollimatorImpactsData_get_parent_z(const CollimatorImpactsData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+144);
  offset+=16+i0*8;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void CollimatorImpactsData_set_parent_z(CollimatorImpactsData  obj, int64_t i0, int64_t value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+144);
  offset+=16+i0*8;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* CollimatorImpactsData_getp1_parent_z(CollimatorImpactsData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+144);
  offset+=16+i0*8;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline ArrNInt64 CollimatorImpactsData_getp_parent_a(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+152);
  return (ArrNInt64)(( char*) obj+offset);
}
 static inline int64_t CollimatorImpactsData_len_parent_a(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+152);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline int64_t CollimatorImpactsData_get_parent_a(const CollimatorImpactsData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+152);
  offset+=16+i0*8;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void CollimatorImpactsData_set_parent_a(CollimatorImpactsData  obj, int64_t i0, int64_t value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+152);
  offset+=16+i0*8;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* CollimatorImpactsData_getp1_parent_a(CollimatorImpactsData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+152);
  offset+=16+i0*8;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline ArrNInt64 CollimatorImpactsData_getp_parent_pdgid(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+160);
  return (ArrNInt64)(( char*) obj+offset);
}
 static inline int64_t CollimatorImpactsData_len_parent_pdgid(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+160);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline int64_t CollimatorImpactsData_get_parent_pdgid(const CollimatorImpactsData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+160);
  offset+=16+i0*8;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void CollimatorImpactsData_set_parent_pdgid(CollimatorImpactsData  obj, int64_t i0, int64_t value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+160);
  offset+=16+i0*8;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* CollimatorImpactsData_getp1_parent_pdgid(CollimatorImpactsData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+160);
  offset+=16+i0*8;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline ArrNInt64 CollimatorImpactsData_getp_child_id(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+168);
  return (ArrNInt64)(( char*) obj+offset);
}
 static inline int64_t CollimatorImpactsData_len_child_id(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+168);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline int64_t CollimatorImpactsData_get_child_id(const CollimatorImpactsData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+168);
  offset+=16+i0*8;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void CollimatorImpactsData_set_child_id(CollimatorImpactsData  obj, int64_t i0, int64_t value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+168);
  offset+=16+i0*8;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* CollimatorImpactsData_getp1_child_id(CollimatorImpactsData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+168);
  offset+=16+i0*8;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline ArrNFloat64 CollimatorImpactsData_getp_child_x(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+176);
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t CollimatorImpactsData_len_child_x(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+176);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double CollimatorImpactsData_get_child_x(const CollimatorImpactsData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+176);
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void CollimatorImpactsData_set_child_x(CollimatorImpactsData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+176);
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* CollimatorImpactsData_getp1_child_x(CollimatorImpactsData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+176);
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNFloat64 CollimatorImpactsData_getp_child_px(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+184);
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t CollimatorImpactsData_len_child_px(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+184);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double CollimatorImpactsData_get_child_px(const CollimatorImpactsData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+184);
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void CollimatorImpactsData_set_child_px(CollimatorImpactsData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+184);
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* CollimatorImpactsData_getp1_child_px(CollimatorImpactsData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+184);
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNFloat64 CollimatorImpactsData_getp_child_y(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+192);
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t CollimatorImpactsData_len_child_y(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+192);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double CollimatorImpactsData_get_child_y(const CollimatorImpactsData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+192);
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void CollimatorImpactsData_set_child_y(CollimatorImpactsData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+192);
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* CollimatorImpactsData_getp1_child_y(CollimatorImpactsData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+192);
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNFloat64 CollimatorImpactsData_getp_child_py(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+200);
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t CollimatorImpactsData_len_child_py(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+200);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double CollimatorImpactsData_get_child_py(const CollimatorImpactsData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+200);
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void CollimatorImpactsData_set_child_py(CollimatorImpactsData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+200);
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* CollimatorImpactsData_getp1_child_py(CollimatorImpactsData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+200);
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNFloat64 CollimatorImpactsData_getp_child_zeta(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+208);
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t CollimatorImpactsData_len_child_zeta(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+208);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double CollimatorImpactsData_get_child_zeta(const CollimatorImpactsData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+208);
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void CollimatorImpactsData_set_child_zeta(CollimatorImpactsData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+208);
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* CollimatorImpactsData_getp1_child_zeta(CollimatorImpactsData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+208);
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNFloat64 CollimatorImpactsData_getp_child_delta(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+216);
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t CollimatorImpactsData_len_child_delta(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+216);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double CollimatorImpactsData_get_child_delta(const CollimatorImpactsData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+216);
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void CollimatorImpactsData_set_child_delta(CollimatorImpactsData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+216);
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* CollimatorImpactsData_getp1_child_delta(CollimatorImpactsData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+216);
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNFloat64 CollimatorImpactsData_getp_child_energy(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+224);
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t CollimatorImpactsData_len_child_energy(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+224);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double CollimatorImpactsData_get_child_energy(const CollimatorImpactsData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+224);
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void CollimatorImpactsData_set_child_energy(CollimatorImpactsData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+224);
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* CollimatorImpactsData_getp1_child_energy(CollimatorImpactsData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+224);
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNFloat64 CollimatorImpactsData_getp_child_mass(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+232);
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t CollimatorImpactsData_len_child_mass(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+232);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double CollimatorImpactsData_get_child_mass(const CollimatorImpactsData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+232);
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void CollimatorImpactsData_set_child_mass(CollimatorImpactsData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+232);
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* CollimatorImpactsData_getp1_child_mass(CollimatorImpactsData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+232);
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNInt64 CollimatorImpactsData_getp_child_charge(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+240);
  return (ArrNInt64)(( char*) obj+offset);
}
 static inline int64_t CollimatorImpactsData_len_child_charge(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+240);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline int64_t CollimatorImpactsData_get_child_charge(const CollimatorImpactsData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+240);
  offset+=16+i0*8;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void CollimatorImpactsData_set_child_charge(CollimatorImpactsData  obj, int64_t i0, int64_t value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+240);
  offset+=16+i0*8;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* CollimatorImpactsData_getp1_child_charge(CollimatorImpactsData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+240);
  offset+=16+i0*8;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline ArrNInt64 CollimatorImpactsData_getp_child_z(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+248);
  return (ArrNInt64)(( char*) obj+offset);
}
 static inline int64_t CollimatorImpactsData_len_child_z(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+248);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline int64_t CollimatorImpactsData_get_child_z(const CollimatorImpactsData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+248);
  offset+=16+i0*8;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void CollimatorImpactsData_set_child_z(CollimatorImpactsData  obj, int64_t i0, int64_t value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+248);
  offset+=16+i0*8;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* CollimatorImpactsData_getp1_child_z(CollimatorImpactsData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+248);
  offset+=16+i0*8;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline ArrNInt64 CollimatorImpactsData_getp_child_a(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+256);
  return (ArrNInt64)(( char*) obj+offset);
}
 static inline int64_t CollimatorImpactsData_len_child_a(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+256);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline int64_t CollimatorImpactsData_get_child_a(const CollimatorImpactsData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+256);
  offset+=16+i0*8;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void CollimatorImpactsData_set_child_a(CollimatorImpactsData  obj, int64_t i0, int64_t value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+256);
  offset+=16+i0*8;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* CollimatorImpactsData_getp1_child_a(CollimatorImpactsData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+256);
  offset+=16+i0*8;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline ArrNInt64 CollimatorImpactsData_getp_child_pdgid(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+264);
  return (ArrNInt64)(( char*) obj+offset);
}
 static inline int64_t CollimatorImpactsData_len_child_pdgid(CollimatorImpactsData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+264);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline int64_t CollimatorImpactsData_get_child_pdgid(const CollimatorImpactsData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+264);
  offset+=16+i0*8;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void CollimatorImpactsData_set_child_pdgid(CollimatorImpactsData  obj, int64_t i0, int64_t value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+264);
  offset+=16+i0*8;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* CollimatorImpactsData_getp1_child_pdgid(CollimatorImpactsData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+264);
  offset+=16+i0*8;
  return ( int64_t*)(( char*) obj+offset);
}
#endif
#ifndef XOBJ_TYPEDEF_SynchrotronRadiationRecordData
#define XOBJ_TYPEDEF_SynchrotronRadiationRecordData
typedef   struct SynchrotronRadiationRecordData_s * SynchrotronRadiationRecordData;
 static inline SynchrotronRadiationRecordData SynchrotronRadiationRecordData_getp(SynchrotronRadiationRecordData  obj){
  int64_t offset=0;
  return (SynchrotronRadiationRecordData)(( char*) obj+offset);
}
 static inline RecordIndex SynchrotronRadiationRecordData_getp__index(SynchrotronRadiationRecordData  obj){
  int64_t offset=0;
  offset+=8;
  return (RecordIndex)(( char*) obj+offset);
}
 static inline int64_t SynchrotronRadiationRecordData_get__index_capacity(const SynchrotronRadiationRecordData  obj){
  int64_t offset=0;
  offset+=8;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void SynchrotronRadiationRecordData_set__index_capacity(SynchrotronRadiationRecordData  obj, int64_t value){
  int64_t offset=0;
  offset+=8;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* SynchrotronRadiationRecordData_getp__index_capacity(SynchrotronRadiationRecordData  obj){
  int64_t offset=0;
  offset+=8;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline uint32_t SynchrotronRadiationRecordData_get__index_num_recorded(const SynchrotronRadiationRecordData  obj){
  int64_t offset=0;
  offset+=16;
  return *( uint32_t*)(( char*) obj+offset);
}
 static inline void SynchrotronRadiationRecordData_set__index_num_recorded(SynchrotronRadiationRecordData  obj, uint32_t value){
  int64_t offset=0;
  offset+=16;
  *( uint32_t*)(( char*) obj+offset)=value;
}
 static inline  uint32_t* SynchrotronRadiationRecordData_getp__index_num_recorded(SynchrotronRadiationRecordData  obj){
  int64_t offset=0;
  offset+=16;
  return ( uint32_t*)(( char*) obj+offset);
}
 static inline uint32_t SynchrotronRadiationRecordData_get__index__dummy(const SynchrotronRadiationRecordData  obj){
  int64_t offset=0;
  offset+=24;
  return *( uint32_t*)(( char*) obj+offset);
}
 static inline void SynchrotronRadiationRecordData_set__index__dummy(SynchrotronRadiationRecordData  obj, uint32_t value){
  int64_t offset=0;
  offset+=24;
  *( uint32_t*)(( char*) obj+offset)=value;
}
 static inline  uint32_t* SynchrotronRadiationRecordData_getp__index__dummy(SynchrotronRadiationRecordData  obj){
  int64_t offset=0;
  offset+=24;
  return ( uint32_t*)(( char*) obj+offset);
}
 static inline int64_t SynchrotronRadiationRecordData_get__index_buffer_id(const SynchrotronRadiationRecordData  obj){
  int64_t offset=0;
  offset+=32;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void SynchrotronRadiationRecordData_set__index_buffer_id(SynchrotronRadiationRecordData  obj, int64_t value){
  int64_t offset=0;
  offset+=32;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* SynchrotronRadiationRecordData_getp__index_buffer_id(SynchrotronRadiationRecordData  obj){
  int64_t offset=0;
  offset+=32;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline ArrNFloat64 SynchrotronRadiationRecordData_getp_photon_energy(SynchrotronRadiationRecordData  obj){
  int64_t offset=0;
  offset+=72;
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t SynchrotronRadiationRecordData_len_photon_energy(SynchrotronRadiationRecordData  obj){
  int64_t offset=0;
  offset+=72;
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double SynchrotronRadiationRecordData_get_photon_energy(const SynchrotronRadiationRecordData  obj, int64_t i0){
  int64_t offset=0;
  offset+=72;
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void SynchrotronRadiationRecordData_set_photon_energy(SynchrotronRadiationRecordData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=72;
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* SynchrotronRadiationRecordData_getp1_photon_energy(SynchrotronRadiationRecordData  obj, int64_t i0){
  int64_t offset=0;
  offset+=72;
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNInt64 SynchrotronRadiationRecordData_getp_at_element(SynchrotronRadiationRecordData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+40);
  return (ArrNInt64)(( char*) obj+offset);
}
 static inline int64_t SynchrotronRadiationRecordData_len_at_element(SynchrotronRadiationRecordData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+40);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline int64_t SynchrotronRadiationRecordData_get_at_element(const SynchrotronRadiationRecordData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+40);
  offset+=16+i0*8;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void SynchrotronRadiationRecordData_set_at_element(SynchrotronRadiationRecordData  obj, int64_t i0, int64_t value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+40);
  offset+=16+i0*8;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* SynchrotronRadiationRecordData_getp1_at_element(SynchrotronRadiationRecordData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+40);
  offset+=16+i0*8;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline ArrNInt64 SynchrotronRadiationRecordData_getp_at_turn(SynchrotronRadiationRecordData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+48);
  return (ArrNInt64)(( char*) obj+offset);
}
 static inline int64_t SynchrotronRadiationRecordData_len_at_turn(SynchrotronRadiationRecordData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+48);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline int64_t SynchrotronRadiationRecordData_get_at_turn(const SynchrotronRadiationRecordData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+48);
  offset+=16+i0*8;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void SynchrotronRadiationRecordData_set_at_turn(SynchrotronRadiationRecordData  obj, int64_t i0, int64_t value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+48);
  offset+=16+i0*8;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* SynchrotronRadiationRecordData_getp1_at_turn(SynchrotronRadiationRecordData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+48);
  offset+=16+i0*8;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline ArrNInt64 SynchrotronRadiationRecordData_getp_particle_id(SynchrotronRadiationRecordData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+56);
  return (ArrNInt64)(( char*) obj+offset);
}
 static inline int64_t SynchrotronRadiationRecordData_len_particle_id(SynchrotronRadiationRecordData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+56);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline int64_t SynchrotronRadiationRecordData_get_particle_id(const SynchrotronRadiationRecordData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+56);
  offset+=16+i0*8;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void SynchrotronRadiationRecordData_set_particle_id(SynchrotronRadiationRecordData  obj, int64_t i0, int64_t value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+56);
  offset+=16+i0*8;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* SynchrotronRadiationRecordData_getp1_particle_id(SynchrotronRadiationRecordData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+56);
  offset+=16+i0*8;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline ArrNFloat64 SynchrotronRadiationRecordData_getp_particle_delta(SynchrotronRadiationRecordData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+64);
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t SynchrotronRadiationRecordData_len_particle_delta(SynchrotronRadiationRecordData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+64);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double SynchrotronRadiationRecordData_get_particle_delta(const SynchrotronRadiationRecordData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+64);
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void SynchrotronRadiationRecordData_set_particle_delta(SynchrotronRadiationRecordData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+64);
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* SynchrotronRadiationRecordData_getp1_particle_delta(SynchrotronRadiationRecordData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+64);
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
#endif
#ifndef XOBJ_TYPEDEF_MaterialData
#define XOBJ_TYPEDEF_MaterialData
typedef   struct MaterialData_s * MaterialData;
 static inline MaterialData MaterialData_getp(MaterialData  obj){
  int64_t offset=0;
  return (MaterialData)(( char*) obj+offset);
}
 static inline double MaterialData_get_Z(const MaterialData  obj){
  int64_t offset=0;
  offset+=8;
  return *( double*)(( char*) obj+offset);
}
 static inline void MaterialData_set_Z(MaterialData  obj, double value){
  int64_t offset=0;
  offset+=8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* MaterialData_getp_Z(MaterialData  obj){
  int64_t offset=0;
  offset+=8;
  return ( double*)(( char*) obj+offset);
}
 static inline double MaterialData_get_A(const MaterialData  obj){
  int64_t offset=0;
  offset+=16;
  return *( double*)(( char*) obj+offset);
}
 static inline void MaterialData_set_A(MaterialData  obj, double value){
  int64_t offset=0;
  offset+=16;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* MaterialData_getp_A(MaterialData  obj){
  int64_t offset=0;
  offset+=16;
  return ( double*)(( char*) obj+offset);
}
 static inline double MaterialData_get_density(const MaterialData  obj){
  int64_t offset=0;
  offset+=24;
  return *( double*)(( char*) obj+offset);
}
 static inline void MaterialData_set_density(MaterialData  obj, double value){
  int64_t offset=0;
  offset+=24;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* MaterialData_getp_density(MaterialData  obj){
  int64_t offset=0;
  offset+=24;
  return ( double*)(( char*) obj+offset);
}
 static inline double MaterialData_get_excitation_energy(const MaterialData  obj){
  int64_t offset=0;
  offset+=32;
  return *( double*)(( char*) obj+offset);
}
 static inline void MaterialData_set_excitation_energy(MaterialData  obj, double value){
  int64_t offset=0;
  offset+=32;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* MaterialData_getp_excitation_energy(MaterialData  obj){
  int64_t offset=0;
  offset+=32;
  return ( double*)(( char*) obj+offset);
}
 static inline double MaterialData_get_nuclear_radius(const MaterialData  obj){
  int64_t offset=0;
  offset+=40;
  return *( double*)(( char*) obj+offset);
}
 static inline void MaterialData_set_nuclear_radius(MaterialData  obj, double value){
  int64_t offset=0;
  offset+=40;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* MaterialData_getp_nuclear_radius(MaterialData  obj){
  int64_t offset=0;
  offset+=40;
  return ( double*)(( char*) obj+offset);
}
 static inline double MaterialData_get_nuclear_elastic_slope(const MaterialData  obj){
  int64_t offset=0;
  offset+=48;
  return *( double*)(( char*) obj+offset);
}
 static inline void MaterialData_set_nuclear_elastic_slope(MaterialData  obj, double value){
  int64_t offset=0;
  offset+=48;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* MaterialData_getp_nuclear_elastic_slope(MaterialData  obj){
  int64_t offset=0;
  offset+=48;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNFloat64 MaterialData_getp_cross_section(MaterialData  obj){
  int64_t offset=0;
  offset+=80;
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t MaterialData_len_cross_section(MaterialData  obj){
  int64_t offset=0;
  offset+=80;
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double MaterialData_get_cross_section(const MaterialData  obj, int64_t i0){
  int64_t offset=0;
  offset+=80;
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void MaterialData_set_cross_section(MaterialData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=80;
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* MaterialData_getp1_cross_section(MaterialData  obj, int64_t i0){
  int64_t offset=0;
  offset+=80;
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline double MaterialData_get_hcut(const MaterialData  obj){
  int64_t offset=0;
  offset+=56;
  return *( double*)(( char*) obj+offset);
}
 static inline void MaterialData_set_hcut(MaterialData  obj, double value){
  int64_t offset=0;
  offset+=56;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* MaterialData_getp_hcut(MaterialData  obj){
  int64_t offset=0;
  offset+=56;
  return ( double*)(( char*) obj+offset);
}
 static inline  char* MaterialData_getp_name(MaterialData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+72);
  return ( char*)(( char*) obj+offset);
}
 static inline double MaterialData_get_radiation_length(const MaterialData  obj){
  int64_t offset=0;
  offset+=64;
  return *( double*)(( char*) obj+offset);
}
 static inline void MaterialData_set_radiation_length(MaterialData  obj, double value){
  int64_t offset=0;
  offset+=64;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* MaterialData_getp_radiation_length(MaterialData  obj){
  int64_t offset=0;
  offset+=64;
  return ( double*)(( char*) obj+offset);
}
#endif
#ifndef XOBJ_TYPEDEF_CrystalMaterialData
#define XOBJ_TYPEDEF_CrystalMaterialData
typedef   struct CrystalMaterialData_s * CrystalMaterialData;
 static inline CrystalMaterialData CrystalMaterialData_getp(CrystalMaterialData  obj){
  int64_t offset=0;
  return (CrystalMaterialData)(( char*) obj+offset);
}
 static inline double CrystalMaterialData_get_Z(const CrystalMaterialData  obj){
  int64_t offset=0;
  offset+=8;
  return *( double*)(( char*) obj+offset);
}
 static inline void CrystalMaterialData_set_Z(CrystalMaterialData  obj, double value){
  int64_t offset=0;
  offset+=8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* CrystalMaterialData_getp_Z(CrystalMaterialData  obj){
  int64_t offset=0;
  offset+=8;
  return ( double*)(( char*) obj+offset);
}
 static inline double CrystalMaterialData_get_A(const CrystalMaterialData  obj){
  int64_t offset=0;
  offset+=16;
  return *( double*)(( char*) obj+offset);
}
 static inline void CrystalMaterialData_set_A(CrystalMaterialData  obj, double value){
  int64_t offset=0;
  offset+=16;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* CrystalMaterialData_getp_A(CrystalMaterialData  obj){
  int64_t offset=0;
  offset+=16;
  return ( double*)(( char*) obj+offset);
}
 static inline double CrystalMaterialData_get_density(const CrystalMaterialData  obj){
  int64_t offset=0;
  offset+=24;
  return *( double*)(( char*) obj+offset);
}
 static inline void CrystalMaterialData_set_density(CrystalMaterialData  obj, double value){
  int64_t offset=0;
  offset+=24;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* CrystalMaterialData_getp_density(CrystalMaterialData  obj){
  int64_t offset=0;
  offset+=24;
  return ( double*)(( char*) obj+offset);
}
 static inline double CrystalMaterialData_get_excitation_energy(const CrystalMaterialData  obj){
  int64_t offset=0;
  offset+=32;
  return *( double*)(( char*) obj+offset);
}
 static inline void CrystalMaterialData_set_excitation_energy(CrystalMaterialData  obj, double value){
  int64_t offset=0;
  offset+=32;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* CrystalMaterialData_getp_excitation_energy(CrystalMaterialData  obj){
  int64_t offset=0;
  offset+=32;
  return ( double*)(( char*) obj+offset);
}
 static inline double CrystalMaterialData_get_nuclear_radius(const CrystalMaterialData  obj){
  int64_t offset=0;
  offset+=40;
  return *( double*)(( char*) obj+offset);
}
 static inline void CrystalMaterialData_set_nuclear_radius(CrystalMaterialData  obj, double value){
  int64_t offset=0;
  offset+=40;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* CrystalMaterialData_getp_nuclear_radius(CrystalMaterialData  obj){
  int64_t offset=0;
  offset+=40;
  return ( double*)(( char*) obj+offset);
}
 static inline double CrystalMaterialData_get_nuclear_elastic_slope(const CrystalMaterialData  obj){
  int64_t offset=0;
  offset+=48;
  return *( double*)(( char*) obj+offset);
}
 static inline void CrystalMaterialData_set_nuclear_elastic_slope(CrystalMaterialData  obj, double value){
  int64_t offset=0;
  offset+=48;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* CrystalMaterialData_getp_nuclear_elastic_slope(CrystalMaterialData  obj){
  int64_t offset=0;
  offset+=48;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNFloat64 CrystalMaterialData_getp_cross_section(CrystalMaterialData  obj){
  int64_t offset=0;
  offset+=112;
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t CrystalMaterialData_len_cross_section(CrystalMaterialData  obj){
  int64_t offset=0;
  offset+=112;
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double CrystalMaterialData_get_cross_section(const CrystalMaterialData  obj, int64_t i0){
  int64_t offset=0;
  offset+=112;
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void CrystalMaterialData_set_cross_section(CrystalMaterialData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=112;
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* CrystalMaterialData_getp1_cross_section(CrystalMaterialData  obj, int64_t i0){
  int64_t offset=0;
  offset+=112;
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline double CrystalMaterialData_get_hcut(const CrystalMaterialData  obj){
  int64_t offset=0;
  offset+=56;
  return *( double*)(( char*) obj+offset);
}
 static inline void CrystalMaterialData_set_hcut(CrystalMaterialData  obj, double value){
  int64_t offset=0;
  offset+=56;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* CrystalMaterialData_getp_hcut(CrystalMaterialData  obj){
  int64_t offset=0;
  offset+=56;
  return ( double*)(( char*) obj+offset);
}
 static inline  char* CrystalMaterialData_getp_name(CrystalMaterialData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+104);
  return ( char*)(( char*) obj+offset);
}
 static inline double CrystalMaterialData_get_crystal_radiation_length(const CrystalMaterialData  obj){
  int64_t offset=0;
  offset+=64;
  return *( double*)(( char*) obj+offset);
}
 static inline void CrystalMaterialData_set_crystal_radiation_length(CrystalMaterialData  obj, double value){
  int64_t offset=0;
  offset+=64;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* CrystalMaterialData_getp_crystal_radiation_length(CrystalMaterialData  obj){
  int64_t offset=0;
  offset+=64;
  return ( double*)(( char*) obj+offset);
}
 static inline double CrystalMaterialData_get_crystal_nuclear_length(const CrystalMaterialData  obj){
  int64_t offset=0;
  offset+=72;
  return *( double*)(( char*) obj+offset);
}
 static inline void CrystalMaterialData_set_crystal_nuclear_length(CrystalMaterialData  obj, double value){
  int64_t offset=0;
  offset+=72;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* CrystalMaterialData_getp_crystal_nuclear_length(CrystalMaterialData  obj){
  int64_t offset=0;
  offset+=72;
  return ( double*)(( char*) obj+offset);
}
 static inline double CrystalMaterialData_get_crystal_plane_distance(const CrystalMaterialData  obj){
  int64_t offset=0;
  offset+=80;
  return *( double*)(( char*) obj+offset);
}
 static inline void CrystalMaterialData_set_crystal_plane_distance(CrystalMaterialData  obj, double value){
  int64_t offset=0;
  offset+=80;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* CrystalMaterialData_getp_crystal_plane_distance(CrystalMaterialData  obj){
  int64_t offset=0;
  offset+=80;
  return ( double*)(( char*) obj+offset);
}
 static inline double CrystalMaterialData_get_crystal_potential(const CrystalMaterialData  obj){
  int64_t offset=0;
  offset+=88;
  return *( double*)(( char*) obj+offset);
}
 static inline void CrystalMaterialData_set_crystal_potential(CrystalMaterialData  obj, double value){
  int64_t offset=0;
  offset+=88;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* CrystalMaterialData_getp_crystal_potential(CrystalMaterialData  obj){
  int64_t offset=0;
  offset+=88;
  return ( double*)(( char*) obj+offset);
}
 static inline double CrystalMaterialData_get_nuclear_collision_length(const CrystalMaterialData  obj){
  int64_t offset=0;
  offset+=96;
  return *( double*)(( char*) obj+offset);
}
 static inline void CrystalMaterialData_set_nuclear_collision_length(CrystalMaterialData  obj, double value){
  int64_t offset=0;
  offset+=96;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* CrystalMaterialData_getp_nuclear_collision_length(CrystalMaterialData  obj){
  int64_t offset=0;
  offset+=96;
  return ( double*)(( char*) obj+offset);
}
#endif
#ifndef XOBJ_TYPEDEF_BendData
#define XOBJ_TYPEDEF_BendData
typedef   struct BendData_s * BendData;
 static inline BendData BendData_getp(BendData  obj){
  int64_t offset=0;
  return (BendData)(( char*) obj+offset);
}
 static inline double BendData_get_k0(const BendData  obj){
  int64_t offset=0;
  return *( double*)(( char*) obj+offset);
}
 static inline void BendData_set_k0(BendData  obj, double value){
  int64_t offset=0;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* BendData_getp_k0(BendData  obj){
  int64_t offset=0;
  return ( double*)(( char*) obj+offset);
}
 static inline double BendData_get_h(const BendData  obj){
  int64_t offset=0;
  offset+=8;
  return *( double*)(( char*) obj+offset);
}
 static inline void BendData_set_h(BendData  obj, double value){
  int64_t offset=0;
  offset+=8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* BendData_getp_h(BendData  obj){
  int64_t offset=0;
  offset+=8;
  return ( double*)(( char*) obj+offset);
}
 static inline double BendData_get_length(const BendData  obj){
  int64_t offset=0;
  offset+=16;
  return *( double*)(( char*) obj+offset);
}
 static inline void BendData_set_length(BendData  obj, double value){
  int64_t offset=0;
  offset+=16;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* BendData_getp_length(BendData  obj){
  int64_t offset=0;
  offset+=16;
  return ( double*)(( char*) obj+offset);
}
 static inline Arr5Float64 BendData_getp_knl(BendData  obj){
  int64_t offset=0;
  offset+=24;
  return (Arr5Float64)(( char*) obj+offset);
}
 static inline int64_t BendData_len_knl(BendData  obj){
  return 5;
}
 static inline double BendData_get_knl(const BendData  obj, int64_t i0){
  int64_t offset=0;
  offset+=24;
  offset+=i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void BendData_set_knl(BendData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=24;
  offset+=i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* BendData_getp1_knl(BendData  obj, int64_t i0){
  int64_t offset=0;
  offset+=24;
  offset+=i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline Arr5Float64 BendData_getp_ksl(BendData  obj){
  int64_t offset=0;
  offset+=64;
  return (Arr5Float64)(( char*) obj+offset);
}
 static inline int64_t BendData_len_ksl(BendData  obj){
  return 5;
}
 static inline double BendData_get_ksl(const BendData  obj, int64_t i0){
  int64_t offset=0;
  offset+=64;
  offset+=i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void BendData_set_ksl(BendData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=64;
  offset+=i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* BendData_getp1_ksl(BendData  obj, int64_t i0){
  int64_t offset=0;
  offset+=64;
  offset+=i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline int64_t BendData_get_num_multipole_kicks(const BendData  obj){
  int64_t offset=0;
  offset+=104;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void BendData_set_num_multipole_kicks(BendData  obj, int64_t value){
  int64_t offset=0;
  offset+=104;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* BendData_getp_num_multipole_kicks(BendData  obj){
  int64_t offset=0;
  offset+=104;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline int64_t BendData_get_order(const BendData  obj){
  int64_t offset=0;
  offset+=112;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void BendData_set_order(BendData  obj, int64_t value){
  int64_t offset=0;
  offset+=112;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* BendData_getp_order(BendData  obj){
  int64_t offset=0;
  offset+=112;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline double BendData_get_inv_factorial_order(const BendData  obj){
  int64_t offset=0;
  offset+=120;
  return *( double*)(( char*) obj+offset);
}
 static inline void BendData_set_inv_factorial_order(BendData  obj, double value){
  int64_t offset=0;
  offset+=120;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* BendData_getp_inv_factorial_order(BendData  obj){
  int64_t offset=0;
  offset+=120;
  return ( double*)(( char*) obj+offset);
}
 static inline int64_t BendData_get_model(const BendData  obj){
  int64_t offset=0;
  offset+=128;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void BendData_set_model(BendData  obj, int64_t value){
  int64_t offset=0;
  offset+=128;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* BendData_getp_model(BendData  obj){
  int64_t offset=0;
  offset+=128;
  return ( int64_t*)(( char*) obj+offset);
}
#endif
// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_CONSTANTS_H
#define XTRACK_CONSTANTS_H

#if !defined( C_LIGHT )
    #define   C_LIGHT ( 299792458.0 )
#endif /* !defined( C_LIGHT ) */

#if !defined( EPSILON_0 )
    #define   EPSILON_0 (8.854187817620e-12)
#endif /* !defined( EPSILON_0 ) */

#if !defined( PI )
    #define PI (3.1415926535897932384626433832795028841971693993751)
#endif /* !defined( PI ) */

#if !defined( MU_0 )
    #define MU_0 (PI*4.0e-7)
#endif /* !defined( MU_0 ) */

#if !defined( DEG2RAD )
    #define DEG2RAD (0.0174532925199432957692369076848861271344287188854)
#endif /* !defiend( DEG2RAD ) */

#if !defined( RAD2DEG )
    #define RAD2DEG (57.29577951308232087679815481410517033240547246656442)
#endif /* !defiend( RAD2DEG ) */

#if !defined( SQRT_PI )
    #define SQRT_PI (1.7724538509055160272981674833411451827975494561224)
#endif /* !defined( SQRT_PI ) */

#if !defined( QELEM )
    #define QELEM (1.60217662e-19)
#endif /* !defined( QELEM ) */

#if !defined( DBL_MAX )
    #define DBL_MAX (1.7976931348623158e+308)
#endif /* !defined( DBL_MAX ) */

#if !defined( DBL_MIN )
    #define DBL_MIN (2.2250738585072014e-308)
#endif /* !defined( DBL_MIN ) */

#if !defined( DBL_EPSILON )
    #define DBL_EPSILON (2.2204460492503131e-16)
#endif /* !defined( DBL_EPSILON ) */

#endif /* XTRACK_CONSTANTS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_FUNCTIONS_H
#define XTRACK_FUNCTIONS_H

 static inline
void kill_all_particles(LocalParticle* part0, int64_t kill_state) {

    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

        LocalParticle_kill_particle(part, kill_state);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

}


 static inline
int8_t assert_tracking(LocalParticle* part, int64_t kill_state){
    // Whenever we are not tracking, e.g. in a twiss, the particle will be at_turn < 0.
    // We test this to distinguish genuine tracking from twiss.
    if (LocalParticle_get_at_turn(part) < 0){
        LocalParticle_kill_particle(part, kill_state);
        return 0;
    }
    return 1;
}

#endif /* XTRACK_FUNCTIONS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_PARTICLE_STATES_H
#define XTRACK_PARTICLE_STATES_H

#define  XT_LOST_ON_APERTURE       0
#define  XT_LOST_ON_LONG_CUT      -2
#define  XT_LOST_ALL_E_IN_SYNRAD -10
#define  RNG_ERR_SEEDS_NOT_SET   -20
#define  RNG_ERR_INVALID_TRACK   -21
#define  RNG_ERR_RUTH_NOT_SET    -22

#endif /* XTRACK_PARTICLE_STATES_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_DRIFT_H
#define XTRACK_DRIFT_H


 static inline
void Drift_single_particle(LocalParticle* part, double length){

    #ifndef XTRACK_USE_EXACT_DRIFTS

        double const rpp    = LocalParticle_get_rpp(part);
        double const rv0v    = 1./LocalParticle_get_rvv(part);
        double const xp     = LocalParticle_get_px(part) * rpp;
        double const yp     = LocalParticle_get_py(part) * rpp;
        double const dzeta  = 1 - rv0v * ( 1. + ( xp*xp + yp*yp ) / 2. );

        LocalParticle_add_to_x(part, xp * length );
        LocalParticle_add_to_y(part, yp * length );
        LocalParticle_add_to_s(part, length);
        LocalParticle_add_to_zeta(part, length * dzeta );

    #else

        double const px = LocalParticle_get_px(part);
        double const py = LocalParticle_get_py(part);
        double const rv0v    = 1./LocalParticle_get_rvv(part);
        double const one_plus_delta = 1. + LocalParticle_get_delta(part);

        double const one_over_pz = 1./sqrt(one_plus_delta*one_plus_delta
                                           - px * px - py * py);
        double const dzeta = 1 - rv0v * one_plus_delta * one_over_pz;

        LocalParticle_add_to_x(part, px * one_over_pz * length);
        LocalParticle_add_to_y(part, py * one_over_pz * length);
        LocalParticle_add_to_zeta(part, dzeta * length);
        LocalParticle_add_to_s(part, length);

    #endif
}

#endif /* XTRACK_DRIFT_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_TRACK_THICK_CFD_H
#define XTRACK_TRACK_THICK_CFD_H

#define POW2(X) ((X)*(X))
#define NONZERO(X) ((X) != 0.0)

// From madx: https://github.com/MethodicalAcceleratorDesign/MAD-X/blob/8695bd422dc403a01aa185e9fea16603bbd5b3e1/src/trrun.f90#L4320

 static inline
void track_thick_cfd(
        LocalParticle* part,  // LocalParticle to track
        double length,        // length of the element
        const double k0_,     // normal dipole strength
        const double k1_,     // normal quadrupole strength
        const double h        // curvature
) {

    // Check if it's a drift
    if (k0_ == 0.0 && k1_ == 0.0) {
        Drift_single_particle(part, length);
        return;
    }

    const double x = LocalParticle_get_x(part);
    const double y = LocalParticle_get_y(part);
    const double px = LocalParticle_get_px(part);
    const double py = LocalParticle_get_py(part);
    const double rvv = LocalParticle_get_rvv(part);

    // In MAD-X (delta + 1) is computed:
    // const double delta_plus_1 = sqrt(pt*pt + 2.0*pt*beti + 1.0);
    const double delta_plus_1 = LocalParticle_get_delta(part) + 1;

    const double k0 = k0_ / delta_plus_1;
    const double k1 = k1_ / delta_plus_1;
    const double Kx = k0 * h + k1;
    const double Ky = -k1;

    double x_, px_, y_, py_, Sx, Sy, Cx, Cy;

    if (Kx > 0.0) {
        double sqrt_Kx = sqrt(Kx);
        Sx = sin(sqrt_Kx * length) / sqrt_Kx;
        Cx = cos(sqrt_Kx * length);
    }
    else if (Kx < 0.0) {
        double sqrt_Kx = sqrt(-Kx); // the imaginary part
        Sx = sinh(sqrt_Kx * length) / sqrt_Kx; // sin(ix) = i sinh(x)
        Cx = cosh(sqrt_Kx * length); // cos(ix) = cosh(x)
    }
    else { // Kx == 0.0
        Sx = length;
        Cx = 1.0;
    }

    if (Ky > 0.0) {
        double sqrt_Ky = sqrt(Ky);
        Sy = sin(sqrt_Ky * length) / sqrt_Ky;
        Cy = cos(sqrt_Ky * length);
    }
    else if (Ky < 0.0) {
        double sqrt_Ky = sqrt(-Ky); // the imaginary part
        Sy = sinh(sqrt_Ky * length) / sqrt_Ky; // sin(ix) = i sinh(x)
        Cy = cosh(sqrt_Ky * length);  // cos(ix) = cosh(x)
    }
    else { // Ky == 0.0
        Sy = length;
        Cy = 1.0;
    }

    // useful quantities
    const double xp = px / delta_plus_1;
    const double yp = py / delta_plus_1;
    const double A = -Kx * x - k0 + h;
    const double B = xp;
    const double C = -Ky * y;
    const double D = yp;

    // transverse map
    x_ = x * Cx + xp * Sx;
    y_ = y * Cy + yp * Sy;
    px_ = (A * Sx + B * Cx) * delta_plus_1;
    py_ = (C * Sy + D * Cy) * delta_plus_1;

    if (NONZERO(Kx))
        x_ = x_ + (k0 - h) * (Cx - 1.0) / Kx;
    else
        x_ = x_ - (k0 - h) * 0.5 * POW2(length);

    // longitudinal map
    double length_ = length; // will be the total path length traveled by the particle
    if (NONZERO(Kx)) {
        length_ -= (h * ((Cx - 1.0) * xp + Sx * A + length * (k0 - h))) / Kx;
        length_ += 0.5 * (
            - (POW2(A) * Cx * Sx) / (2.0 * Kx) \
            + (POW2(B) * Cx * Sx) / 2.0 \
            + (POW2(A) * length) / (2.0 * Kx) \
            + (POW2(B) * length) / 2.0 \
            - (A * B * POW2(Cx)) / Kx \
            + (A * B) / Kx
        );
    }
    else {
        length_ += h * length * (
            3.0 * length * xp \
            + 6.0 * x \
            - (k0 - h) * POW2(length)
        ) / 6.0;
        length_ += 0.5 * (POW2(B)) * length;
    }

    if (NONZERO(Ky)) {
        length_ += 0.5 * (
            - (POW2(C) * Cy * Sy) / (2.0 * Ky) \
            + (POW2(D) * Cy * Sy) / 2.0 \
            + (POW2(C) * length) / (2.0 * Ky) \
            + (POW2(D) * length) / 2.0 \
            - (C * D * POW2(Cy)) / Ky \
            + (C * D) / Ky
        );
    }
    else {
        length_ += 0.5 * POW2(D) * length;
    }

    const double dzeta = length - length_ / rvv;

    LocalParticle_set_x(part, x_);
    LocalParticle_set_px(part, px_);
    LocalParticle_set_y(part, y_);
    LocalParticle_set_py(part, py_);
    LocalParticle_add_to_zeta(part, dzeta);
    LocalParticle_add_to_s(part, length);
}

#endif // XTRACK_TRACK_THICK_CFD_H

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_TRACK_THICK_BEND_H
#define XTRACK_TRACK_THICK_BEND_H

#define POW2(X) ((X)*(X))
#define NONZERO(X) ((X) != 0.0)

 static inline
void track_thick_bend(
        LocalParticle* part,  // LocalParticle to track
        const double length,  // length of the element
        const double k,       // normal dipole strength
        const double h        // curvature
) {

    if(fabs(k) < 1e-8 && fabs(h) < 1e-8) {
        Drift_single_particle(part, length);
        return;
    }

    const double rvv = LocalParticle_get_rvv(part);
    // Particle coordinates
    const double x = LocalParticle_get_x(part);
    const double y = LocalParticle_get_y(part);
    const double px = LocalParticle_get_px(part);
    const double py = LocalParticle_get_py(part);
    const double s = length;

    double new_x, new_px, new_y, delta_ell;

    // Useful constants
    const double one_plus_delta = LocalParticle_get_delta(part) + 1.0;
    const double A = 1.0 / sqrt(POW2(one_plus_delta) - POW2(py));
    const double pz = sqrt(POW2(one_plus_delta) - POW2(px) - POW2(py));

    if (fabs(h) > 1e-8) {
        // The case for non-zero curvature, s is arc length
        // Useful constants
        const double C = pz - k * ((1 / h) + x);
        new_px = px * cos(s * h) + C * sin(s * h);
        double const new_pz = sqrt(POW2(one_plus_delta) - POW2(new_px) - POW2(py));
        // double const d_new_px_ds = new_px / new_pz;

        const double d_new_px_ds = C * h * cos(h * s) - h * px * sin(h * s);

        // Update particle coordinates

        new_x = (new_pz * h - d_new_px_ds - k)/(h*k);
        const double D = asin(A * px) - asin(A * new_px);
        new_y = y + ((py * s) / (k / h)) + (py / k) * D;

        delta_ell = ((one_plus_delta * s * h) / k) + (one_plus_delta / k) * D;
    }
    else {
        // The case for zero curvature -- straight bend, s is Cartesian length
        new_px = px - k * s;
        new_x = x + (sqrt(POW2(one_plus_delta) - POW2(new_px) - POW2(py)) - pz) / k;

        const double D = asin(A * px) - asin(A * new_px);
        new_y = y + (py / k) * D;

        delta_ell = (one_plus_delta / k) * D;
    }

    // Update Particles object
    LocalParticle_set_x(part, new_x);
    LocalParticle_set_px(part, new_px);
    LocalParticle_set_y(part, new_y);
    LocalParticle_add_to_zeta(part, length - delta_ell / rvv);
    LocalParticle_add_to_s(part, s);
}

#endif // XTRACK_TRACK_THICK_BEND_H

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_MULTIPOLAR_KICK_H
#define XTRACK_MULTIPOLAR_KICK_H

 static inline
void multipolar_kick(
    LocalParticle* part,
    const int order,
    const double inv_factorial_order,
      const double *knl,
      const double *ksl,
    const double weight
) {

    #ifdef XSUITE_BACKTRACK
        LocalParticle_kill_particle(part, -31);
        return;
    #else

    int64_t index = order;
    double inv_factorial = inv_factorial_order;

    double dpx = knl[index] * inv_factorial;
    double dpy = ksl[index] * inv_factorial;

    double const x   = LocalParticle_get_x(part);
    double const y   = LocalParticle_get_y(part);
    double const chi = LocalParticle_get_chi(part);

    while( index > 0 )
    {
        double const zre = dpx * x - dpy * y;
        double const zim = dpx * y + dpy * x;

        inv_factorial *= index;
        index -= 1;

        dpx = knl[index] * inv_factorial + zre;
        dpy = ksl[index] * inv_factorial + zim;
    }

    dpx = -chi * dpx; // rad
    dpy =  chi * dpy; // rad

    LocalParticle_add_to_px(part, weight * dpx);
    LocalParticle_add_to_py(part, weight * dpy);

    #endif // not XSUITE_BACKTRACK
}

#endif // XTRACK_MULTIPOLAR_KICK_H

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_BEND_H
#define XTRACK_BEND_H

 static inline
void Bend_track_local_particle(
        BendData el,
        LocalParticle* part0
) {
    double length = BendData_get_length(el);
    #ifdef XSUITE_BACKTRACK
        length = -length;
    #endif

    const double k0 = BendData_get_k0(el);
    const double h = BendData_get_h(el);

    const int64_t num_multipole_kicks = BendData_get_num_multipole_kicks(el);
    const int64_t order = BendData_get_order(el);
    const double inv_factorial_order = BendData_get_inv_factorial_order(el);

      const double *knl = BendData_getp1_knl(el, 0);
      const double *ksl = BendData_getp1_ksl(el, 0);

    const double slice_length = length / (num_multipole_kicks + 1);
    const double kick_weight = 1. / num_multipole_kicks;

    const int64_t model = BendData_get_model(el);

    if (model == 0){

    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

            track_thick_cfd(part, slice_length, k0, 0, h);

            for (int ii = 0; ii < num_multipole_kicks; ii++) {
                multipolar_kick(part, order, inv_factorial_order, knl, ksl, kick_weight);
                track_thick_cfd(part, slice_length, k0, 0, h);
            }

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

    }
    else{

    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp


            #ifdef XSUITE_BACKTRACK
                LocalParticle_kill_particle(part, -30);
            #else
            track_thick_bend(part, slice_length, k0, h);

            for (int ii = 0; ii < num_multipole_kicks; ii++) {
                multipolar_kick(part, order, inv_factorial_order, knl, ksl, kick_weight);
                track_thick_bend(part, slice_length, k0, h);
            }
            #endif

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

    }
}

#endif // XTRACK_TRUEBEND_H

             
            void Bend_track_particles(
               BendData el,

                             ParticlesData particles,

                             int64_t flag_increment_at_element,
                  int8_t* io_buffer){
//            const int num_threads = omp_get_max_threads();                                 //only_for_context cpu_openmp
//            const int64_t capacity = ParticlesData_get__capacity(particles);               //only_for_context cpu_openmp
//            const int64_t chunk_size = (capacity + num_threads - 1)/num_threads; // ceil division  //only_for_context cpu_openmp
//            #pragma omp parallel for                                                       //only_for_context cpu_openmp
//            for (int64_t batch_id = 0; batch_id < num_threads; batch_id++) {               //only_for_context cpu_openmp
                LocalParticle lpart;
                lpart.io_buffer = io_buffer;
//                int64_t part_id = batch_id * chunk_size;                                       //only_for_context cpu_openmp
//                int64_t end_id = (batch_id + 1) * chunk_size;                                  //only_for_context cpu_openmp
//                if (end_id > capacity) end_id = capacity;                                      //only_for_context cpu_openmp
    
                int64_t part_id = 0;                    //only_for_context cpu_serial
//                int64_t part_id = blockDim.x * blockIdx.x + threadIdx.x; //only_for_context cuda
//                int64_t part_id = get_global_id(0);                    //only_for_context opencl
                int64_t end_id = 0; // unused outside of openmp  //only_for_context cpu_serial cuda opencl
    
                int64_t part_capacity = ParticlesData_get__capacity(particles);
                if (part_id<part_capacity){
                    Particles_to_LocalParticle(particles, &lpart, part_id, end_id);
                    if (check_is_active(&lpart)>0){
              Bend_track_local_particle(el, &lpart);

                    }
                    if (check_is_active(&lpart)>0 && flag_increment_at_element){
                            increment_at_element(&lpart, 1);
                    }
                }
//            } //only_for_context cpu_openmp
            
            // On OpenMP we want to additionally by default reorganize all
            // the particles.
//            #ifndef XT_OMP_SKIP_REORGANIZE                             //only_for_context cpu_openmp
//            LocalParticle lpart;                                       //only_for_context cpu_openmp
//            lpart.io_buffer = io_buffer;                               //only_for_context cpu_openmp
//            Particles_to_LocalParticle(particles, &lpart, 0, capacity);//only_for_context cpu_openmp
//            check_is_active(&lpart);                                   //only_for_context cpu_openmp
//            #endif                                                     //only_for_context cpu_openmp
        }

#ifndef XOBJ_TYPEDEF_CavityData
#define XOBJ_TYPEDEF_CavityData
typedef   struct CavityData_s * CavityData;
 static inline CavityData CavityData_getp(CavityData  obj){
  int64_t offset=0;
  return (CavityData)(( char*) obj+offset);
}
 static inline double CavityData_get_voltage(const CavityData  obj){
  int64_t offset=0;
  return *( double*)(( char*) obj+offset);
}
 static inline void CavityData_set_voltage(CavityData  obj, double value){
  int64_t offset=0;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* CavityData_getp_voltage(CavityData  obj){
  int64_t offset=0;
  return ( double*)(( char*) obj+offset);
}
 static inline double CavityData_get_frequency(const CavityData  obj){
  int64_t offset=0;
  offset+=8;
  return *( double*)(( char*) obj+offset);
}
 static inline void CavityData_set_frequency(CavityData  obj, double value){
  int64_t offset=0;
  offset+=8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* CavityData_getp_frequency(CavityData  obj){
  int64_t offset=0;
  offset+=8;
  return ( double*)(( char*) obj+offset);
}
 static inline double CavityData_get_lag(const CavityData  obj){
  int64_t offset=0;
  offset+=16;
  return *( double*)(( char*) obj+offset);
}
 static inline void CavityData_set_lag(CavityData  obj, double value){
  int64_t offset=0;
  offset+=16;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* CavityData_getp_lag(CavityData  obj){
  int64_t offset=0;
  offset+=16;
  return ( double*)(( char*) obj+offset);
}
 static inline double CavityData_get_lag_taper(const CavityData  obj){
  int64_t offset=0;
  offset+=24;
  return *( double*)(( char*) obj+offset);
}
 static inline void CavityData_set_lag_taper(CavityData  obj, double value){
  int64_t offset=0;
  offset+=24;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* CavityData_getp_lag_taper(CavityData  obj){
  int64_t offset=0;
  offset+=24;
  return ( double*)(( char*) obj+offset);
}
#endif
// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_CONSTANTS_H
#define XTRACK_CONSTANTS_H

#if !defined( C_LIGHT )
    #define   C_LIGHT ( 299792458.0 )
#endif /* !defined( C_LIGHT ) */

#if !defined( EPSILON_0 )
    #define   EPSILON_0 (8.854187817620e-12)
#endif /* !defined( EPSILON_0 ) */

#if !defined( PI )
    #define PI (3.1415926535897932384626433832795028841971693993751)
#endif /* !defined( PI ) */

#if !defined( MU_0 )
    #define MU_0 (PI*4.0e-7)
#endif /* !defined( MU_0 ) */

#if !defined( DEG2RAD )
    #define DEG2RAD (0.0174532925199432957692369076848861271344287188854)
#endif /* !defiend( DEG2RAD ) */

#if !defined( RAD2DEG )
    #define RAD2DEG (57.29577951308232087679815481410517033240547246656442)
#endif /* !defiend( RAD2DEG ) */

#if !defined( SQRT_PI )
    #define SQRT_PI (1.7724538509055160272981674833411451827975494561224)
#endif /* !defined( SQRT_PI ) */

#if !defined( QELEM )
    #define QELEM (1.60217662e-19)
#endif /* !defined( QELEM ) */

#if !defined( DBL_MAX )
    #define DBL_MAX (1.7976931348623158e+308)
#endif /* !defined( DBL_MAX ) */

#if !defined( DBL_MIN )
    #define DBL_MIN (2.2250738585072014e-308)
#endif /* !defined( DBL_MIN ) */

#if !defined( DBL_EPSILON )
    #define DBL_EPSILON (2.2204460492503131e-16)
#endif /* !defined( DBL_EPSILON ) */

#endif /* XTRACK_CONSTANTS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_FUNCTIONS_H
#define XTRACK_FUNCTIONS_H

 static inline
void kill_all_particles(LocalParticle* part0, int64_t kill_state) {

    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

        LocalParticle_kill_particle(part, kill_state);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

}


 static inline
int8_t assert_tracking(LocalParticle* part, int64_t kill_state){
    // Whenever we are not tracking, e.g. in a twiss, the particle will be at_turn < 0.
    // We test this to distinguish genuine tracking from twiss.
    if (LocalParticle_get_at_turn(part) < 0){
        LocalParticle_kill_particle(part, kill_state);
        return 0;
    }
    return 1;
}

#endif /* XTRACK_FUNCTIONS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_PARTICLE_STATES_H
#define XTRACK_PARTICLE_STATES_H

#define  XT_LOST_ON_APERTURE       0
#define  XT_LOST_ON_LONG_CUT      -2
#define  XT_LOST_ALL_E_IN_SYNRAD -10
#define  RNG_ERR_SEEDS_NOT_SET   -20
#define  RNG_ERR_INVALID_TRACK   -21
#define  RNG_ERR_RUTH_NOT_SET    -22

#endif /* XTRACK_PARTICLE_STATES_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_CONSTANTS_H
#define XTRACK_CONSTANTS_H

#if !defined( C_LIGHT )
    #define   C_LIGHT ( 299792458.0 )
#endif /* !defined( C_LIGHT ) */

#if !defined( EPSILON_0 )
    #define   EPSILON_0 (8.854187817620e-12)
#endif /* !defined( EPSILON_0 ) */

#if !defined( PI )
    #define PI (3.1415926535897932384626433832795028841971693993751)
#endif /* !defined( PI ) */

#if !defined( MU_0 )
    #define MU_0 (PI*4.0e-7)
#endif /* !defined( MU_0 ) */

#if !defined( DEG2RAD )
    #define DEG2RAD (0.0174532925199432957692369076848861271344287188854)
#endif /* !defiend( DEG2RAD ) */

#if !defined( RAD2DEG )
    #define RAD2DEG (57.29577951308232087679815481410517033240547246656442)
#endif /* !defiend( RAD2DEG ) */

#if !defined( SQRT_PI )
    #define SQRT_PI (1.7724538509055160272981674833411451827975494561224)
#endif /* !defined( SQRT_PI ) */

#if !defined( QELEM )
    #define QELEM (1.60217662e-19)
#endif /* !defined( QELEM ) */

#if !defined( DBL_MAX )
    #define DBL_MAX (1.7976931348623158e+308)
#endif /* !defined( DBL_MAX ) */

#if !defined( DBL_MIN )
    #define DBL_MIN (2.2250738585072014e-308)
#endif /* !defined( DBL_MIN ) */

#if !defined( DBL_EPSILON )
    #define DBL_EPSILON (2.2204460492503131e-16)
#endif /* !defined( DBL_EPSILON ) */

#endif /* XTRACK_CONSTANTS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_CAVITY_H
#define XTRACK_CAVITY_H

 static inline
void Cavity_track_local_particle(CavityData el, LocalParticle* part0){

    #ifndef XSUITE_BACKTRACK
    double const K_FACTOR = ( ( double )2.0 *PI ) / C_LIGHT;
    #else
    double const K_FACTOR = -( ( double )2.0 *PI ) / C_LIGHT;
    #endif
    double const volt = CavityData_get_voltage(el);
    double const freq = CavityData_get_frequency(el);
    double const lag = CavityData_get_lag(el);
    double const lag_taper = CavityData_get_lag_taper(el);

    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp


        double const   beta0  = LocalParticle_get_beta0(part);
        double const   zeta   = LocalParticle_get_zeta(part);
        double const   q      = fabs(LocalParticle_get_q0(part))
                		        * LocalParticle_get_charge_ratio(part);
        double const   tau    = zeta / beta0;

        double const   phase  = DEG2RAD  * (lag + lag_taper) - K_FACTOR * freq * tau;

        double const energy   = q * volt * sin(phase);

        #ifdef XTRACK_CAVITY_PRESERVE_ANGLE
        LocalParticle_add_to_energy(part, energy, 0);
        #else
        LocalParticle_add_to_energy(part, energy, 1);
        #endif


//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

}

#endif


             
            void Cavity_track_particles(
               CavityData el,

                             ParticlesData particles,

                             int64_t flag_increment_at_element,
                  int8_t* io_buffer){
//            const int num_threads = omp_get_max_threads();                                 //only_for_context cpu_openmp
//            const int64_t capacity = ParticlesData_get__capacity(particles);               //only_for_context cpu_openmp
//            const int64_t chunk_size = (capacity + num_threads - 1)/num_threads; // ceil division  //only_for_context cpu_openmp
//            #pragma omp parallel for                                                       //only_for_context cpu_openmp
//            for (int64_t batch_id = 0; batch_id < num_threads; batch_id++) {               //only_for_context cpu_openmp
                LocalParticle lpart;
                lpart.io_buffer = io_buffer;
//                int64_t part_id = batch_id * chunk_size;                                       //only_for_context cpu_openmp
//                int64_t end_id = (batch_id + 1) * chunk_size;                                  //only_for_context cpu_openmp
//                if (end_id > capacity) end_id = capacity;                                      //only_for_context cpu_openmp
    
                int64_t part_id = 0;                    //only_for_context cpu_serial
//                int64_t part_id = blockDim.x * blockIdx.x + threadIdx.x; //only_for_context cuda
//                int64_t part_id = get_global_id(0);                    //only_for_context opencl
                int64_t end_id = 0; // unused outside of openmp  //only_for_context cpu_serial cuda opencl
    
                int64_t part_capacity = ParticlesData_get__capacity(particles);
                if (part_id<part_capacity){
                    Particles_to_LocalParticle(particles, &lpart, part_id, end_id);
                    if (check_is_active(&lpart)>0){
              Cavity_track_local_particle(el, &lpart);

                    }
                    if (check_is_active(&lpart)>0 && flag_increment_at_element){
                            increment_at_element(&lpart, 1);
                    }
                }
//            } //only_for_context cpu_openmp
            
            // On OpenMP we want to additionally by default reorganize all
            // the particles.
//            #ifndef XT_OMP_SKIP_REORGANIZE                             //only_for_context cpu_openmp
//            LocalParticle lpart;                                       //only_for_context cpu_openmp
//            lpart.io_buffer = io_buffer;                               //only_for_context cpu_openmp
//            Particles_to_LocalParticle(particles, &lpart, 0, capacity);//only_for_context cpu_openmp
//            check_is_active(&lpart);                                   //only_for_context cpu_openmp
//            #endif                                                     //only_for_context cpu_openmp
        }

#ifndef XOBJ_TYPEDEF_CombinedFunctionMagnetData
#define XOBJ_TYPEDEF_CombinedFunctionMagnetData
typedef   struct CombinedFunctionMagnetData_s * CombinedFunctionMagnetData;
 static inline CombinedFunctionMagnetData CombinedFunctionMagnetData_getp(CombinedFunctionMagnetData  obj){
  int64_t offset=0;
  return (CombinedFunctionMagnetData)(( char*) obj+offset);
}
 static inline double CombinedFunctionMagnetData_get_k0(const CombinedFunctionMagnetData  obj){
  int64_t offset=0;
  return *( double*)(( char*) obj+offset);
}
 static inline void CombinedFunctionMagnetData_set_k0(CombinedFunctionMagnetData  obj, double value){
  int64_t offset=0;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* CombinedFunctionMagnetData_getp_k0(CombinedFunctionMagnetData  obj){
  int64_t offset=0;
  return ( double*)(( char*) obj+offset);
}
 static inline double CombinedFunctionMagnetData_get_k1(const CombinedFunctionMagnetData  obj){
  int64_t offset=0;
  offset+=8;
  return *( double*)(( char*) obj+offset);
}
 static inline void CombinedFunctionMagnetData_set_k1(CombinedFunctionMagnetData  obj, double value){
  int64_t offset=0;
  offset+=8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* CombinedFunctionMagnetData_getp_k1(CombinedFunctionMagnetData  obj){
  int64_t offset=0;
  offset+=8;
  return ( double*)(( char*) obj+offset);
}
 static inline double CombinedFunctionMagnetData_get_h(const CombinedFunctionMagnetData  obj){
  int64_t offset=0;
  offset+=16;
  return *( double*)(( char*) obj+offset);
}
 static inline void CombinedFunctionMagnetData_set_h(CombinedFunctionMagnetData  obj, double value){
  int64_t offset=0;
  offset+=16;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* CombinedFunctionMagnetData_getp_h(CombinedFunctionMagnetData  obj){
  int64_t offset=0;
  offset+=16;
  return ( double*)(( char*) obj+offset);
}
 static inline double CombinedFunctionMagnetData_get_length(const CombinedFunctionMagnetData  obj){
  int64_t offset=0;
  offset+=24;
  return *( double*)(( char*) obj+offset);
}
 static inline void CombinedFunctionMagnetData_set_length(CombinedFunctionMagnetData  obj, double value){
  int64_t offset=0;
  offset+=24;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* CombinedFunctionMagnetData_getp_length(CombinedFunctionMagnetData  obj){
  int64_t offset=0;
  offset+=24;
  return ( double*)(( char*) obj+offset);
}
 static inline Arr5Float64 CombinedFunctionMagnetData_getp_knl(CombinedFunctionMagnetData  obj){
  int64_t offset=0;
  offset+=32;
  return (Arr5Float64)(( char*) obj+offset);
}
 static inline int64_t CombinedFunctionMagnetData_len_knl(CombinedFunctionMagnetData  obj){
  return 5;
}
 static inline double CombinedFunctionMagnetData_get_knl(const CombinedFunctionMagnetData  obj, int64_t i0){
  int64_t offset=0;
  offset+=32;
  offset+=i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void CombinedFunctionMagnetData_set_knl(CombinedFunctionMagnetData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=32;
  offset+=i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* CombinedFunctionMagnetData_getp1_knl(CombinedFunctionMagnetData  obj, int64_t i0){
  int64_t offset=0;
  offset+=32;
  offset+=i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline Arr5Float64 CombinedFunctionMagnetData_getp_ksl(CombinedFunctionMagnetData  obj){
  int64_t offset=0;
  offset+=72;
  return (Arr5Float64)(( char*) obj+offset);
}
 static inline int64_t CombinedFunctionMagnetData_len_ksl(CombinedFunctionMagnetData  obj){
  return 5;
}
 static inline double CombinedFunctionMagnetData_get_ksl(const CombinedFunctionMagnetData  obj, int64_t i0){
  int64_t offset=0;
  offset+=72;
  offset+=i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void CombinedFunctionMagnetData_set_ksl(CombinedFunctionMagnetData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=72;
  offset+=i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* CombinedFunctionMagnetData_getp1_ksl(CombinedFunctionMagnetData  obj, int64_t i0){
  int64_t offset=0;
  offset+=72;
  offset+=i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline int64_t CombinedFunctionMagnetData_get_num_multipole_kicks(const CombinedFunctionMagnetData  obj){
  int64_t offset=0;
  offset+=112;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void CombinedFunctionMagnetData_set_num_multipole_kicks(CombinedFunctionMagnetData  obj, int64_t value){
  int64_t offset=0;
  offset+=112;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* CombinedFunctionMagnetData_getp_num_multipole_kicks(CombinedFunctionMagnetData  obj){
  int64_t offset=0;
  offset+=112;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline int64_t CombinedFunctionMagnetData_get_order(const CombinedFunctionMagnetData  obj){
  int64_t offset=0;
  offset+=120;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void CombinedFunctionMagnetData_set_order(CombinedFunctionMagnetData  obj, int64_t value){
  int64_t offset=0;
  offset+=120;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* CombinedFunctionMagnetData_getp_order(CombinedFunctionMagnetData  obj){
  int64_t offset=0;
  offset+=120;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline double CombinedFunctionMagnetData_get_inv_factorial_order(const CombinedFunctionMagnetData  obj){
  int64_t offset=0;
  offset+=128;
  return *( double*)(( char*) obj+offset);
}
 static inline void CombinedFunctionMagnetData_set_inv_factorial_order(CombinedFunctionMagnetData  obj, double value){
  int64_t offset=0;
  offset+=128;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* CombinedFunctionMagnetData_getp_inv_factorial_order(CombinedFunctionMagnetData  obj){
  int64_t offset=0;
  offset+=128;
  return ( double*)(( char*) obj+offset);
}
#endif
// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_CONSTANTS_H
#define XTRACK_CONSTANTS_H

#if !defined( C_LIGHT )
    #define   C_LIGHT ( 299792458.0 )
#endif /* !defined( C_LIGHT ) */

#if !defined( EPSILON_0 )
    #define   EPSILON_0 (8.854187817620e-12)
#endif /* !defined( EPSILON_0 ) */

#if !defined( PI )
    #define PI (3.1415926535897932384626433832795028841971693993751)
#endif /* !defined( PI ) */

#if !defined( MU_0 )
    #define MU_0 (PI*4.0e-7)
#endif /* !defined( MU_0 ) */

#if !defined( DEG2RAD )
    #define DEG2RAD (0.0174532925199432957692369076848861271344287188854)
#endif /* !defiend( DEG2RAD ) */

#if !defined( RAD2DEG )
    #define RAD2DEG (57.29577951308232087679815481410517033240547246656442)
#endif /* !defiend( RAD2DEG ) */

#if !defined( SQRT_PI )
    #define SQRT_PI (1.7724538509055160272981674833411451827975494561224)
#endif /* !defined( SQRT_PI ) */

#if !defined( QELEM )
    #define QELEM (1.60217662e-19)
#endif /* !defined( QELEM ) */

#if !defined( DBL_MAX )
    #define DBL_MAX (1.7976931348623158e+308)
#endif /* !defined( DBL_MAX ) */

#if !defined( DBL_MIN )
    #define DBL_MIN (2.2250738585072014e-308)
#endif /* !defined( DBL_MIN ) */

#if !defined( DBL_EPSILON )
    #define DBL_EPSILON (2.2204460492503131e-16)
#endif /* !defined( DBL_EPSILON ) */

#endif /* XTRACK_CONSTANTS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_FUNCTIONS_H
#define XTRACK_FUNCTIONS_H

 static inline
void kill_all_particles(LocalParticle* part0, int64_t kill_state) {

    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

        LocalParticle_kill_particle(part, kill_state);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

}


 static inline
int8_t assert_tracking(LocalParticle* part, int64_t kill_state){
    // Whenever we are not tracking, e.g. in a twiss, the particle will be at_turn < 0.
    // We test this to distinguish genuine tracking from twiss.
    if (LocalParticle_get_at_turn(part) < 0){
        LocalParticle_kill_particle(part, kill_state);
        return 0;
    }
    return 1;
}

#endif /* XTRACK_FUNCTIONS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_PARTICLE_STATES_H
#define XTRACK_PARTICLE_STATES_H

#define  XT_LOST_ON_APERTURE       0
#define  XT_LOST_ON_LONG_CUT      -2
#define  XT_LOST_ALL_E_IN_SYNRAD -10
#define  RNG_ERR_SEEDS_NOT_SET   -20
#define  RNG_ERR_INVALID_TRACK   -21
#define  RNG_ERR_RUTH_NOT_SET    -22

#endif /* XTRACK_PARTICLE_STATES_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_DRIFT_H
#define XTRACK_DRIFT_H


 static inline
void Drift_single_particle(LocalParticle* part, double length){

    #ifndef XTRACK_USE_EXACT_DRIFTS

        double const rpp    = LocalParticle_get_rpp(part);
        double const rv0v    = 1./LocalParticle_get_rvv(part);
        double const xp     = LocalParticle_get_px(part) * rpp;
        double const yp     = LocalParticle_get_py(part) * rpp;
        double const dzeta  = 1 - rv0v * ( 1. + ( xp*xp + yp*yp ) / 2. );

        LocalParticle_add_to_x(part, xp * length );
        LocalParticle_add_to_y(part, yp * length );
        LocalParticle_add_to_s(part, length);
        LocalParticle_add_to_zeta(part, length * dzeta );

    #else

        double const px = LocalParticle_get_px(part);
        double const py = LocalParticle_get_py(part);
        double const rv0v    = 1./LocalParticle_get_rvv(part);
        double const one_plus_delta = 1. + LocalParticle_get_delta(part);

        double const one_over_pz = 1./sqrt(one_plus_delta*one_plus_delta
                                           - px * px - py * py);
        double const dzeta = 1 - rv0v * one_plus_delta * one_over_pz;

        LocalParticle_add_to_x(part, px * one_over_pz * length);
        LocalParticle_add_to_y(part, py * one_over_pz * length);
        LocalParticle_add_to_zeta(part, dzeta * length);
        LocalParticle_add_to_s(part, length);

    #endif
}

#endif /* XTRACK_DRIFT_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_TRACK_THICK_CFD_H
#define XTRACK_TRACK_THICK_CFD_H

#define POW2(X) ((X)*(X))
#define NONZERO(X) ((X) != 0.0)

// From madx: https://github.com/MethodicalAcceleratorDesign/MAD-X/blob/8695bd422dc403a01aa185e9fea16603bbd5b3e1/src/trrun.f90#L4320

 static inline
void track_thick_cfd(
        LocalParticle* part,  // LocalParticle to track
        double length,        // length of the element
        const double k0_,     // normal dipole strength
        const double k1_,     // normal quadrupole strength
        const double h        // curvature
) {

    // Check if it's a drift
    if (k0_ == 0.0 && k1_ == 0.0) {
        Drift_single_particle(part, length);
        return;
    }

    const double x = LocalParticle_get_x(part);
    const double y = LocalParticle_get_y(part);
    const double px = LocalParticle_get_px(part);
    const double py = LocalParticle_get_py(part);
    const double rvv = LocalParticle_get_rvv(part);

    // In MAD-X (delta + 1) is computed:
    // const double delta_plus_1 = sqrt(pt*pt + 2.0*pt*beti + 1.0);
    const double delta_plus_1 = LocalParticle_get_delta(part) + 1;

    const double k0 = k0_ / delta_plus_1;
    const double k1 = k1_ / delta_plus_1;
    const double Kx = k0 * h + k1;
    const double Ky = -k1;

    double x_, px_, y_, py_, Sx, Sy, Cx, Cy;

    if (Kx > 0.0) {
        double sqrt_Kx = sqrt(Kx);
        Sx = sin(sqrt_Kx * length) / sqrt_Kx;
        Cx = cos(sqrt_Kx * length);
    }
    else if (Kx < 0.0) {
        double sqrt_Kx = sqrt(-Kx); // the imaginary part
        Sx = sinh(sqrt_Kx * length) / sqrt_Kx; // sin(ix) = i sinh(x)
        Cx = cosh(sqrt_Kx * length); // cos(ix) = cosh(x)
    }
    else { // Kx == 0.0
        Sx = length;
        Cx = 1.0;
    }

    if (Ky > 0.0) {
        double sqrt_Ky = sqrt(Ky);
        Sy = sin(sqrt_Ky * length) / sqrt_Ky;
        Cy = cos(sqrt_Ky * length);
    }
    else if (Ky < 0.0) {
        double sqrt_Ky = sqrt(-Ky); // the imaginary part
        Sy = sinh(sqrt_Ky * length) / sqrt_Ky; // sin(ix) = i sinh(x)
        Cy = cosh(sqrt_Ky * length);  // cos(ix) = cosh(x)
    }
    else { // Ky == 0.0
        Sy = length;
        Cy = 1.0;
    }

    // useful quantities
    const double xp = px / delta_plus_1;
    const double yp = py / delta_plus_1;
    const double A = -Kx * x - k0 + h;
    const double B = xp;
    const double C = -Ky * y;
    const double D = yp;

    // transverse map
    x_ = x * Cx + xp * Sx;
    y_ = y * Cy + yp * Sy;
    px_ = (A * Sx + B * Cx) * delta_plus_1;
    py_ = (C * Sy + D * Cy) * delta_plus_1;

    if (NONZERO(Kx))
        x_ = x_ + (k0 - h) * (Cx - 1.0) / Kx;
    else
        x_ = x_ - (k0 - h) * 0.5 * POW2(length);

    // longitudinal map
    double length_ = length; // will be the total path length traveled by the particle
    if (NONZERO(Kx)) {
        length_ -= (h * ((Cx - 1.0) * xp + Sx * A + length * (k0 - h))) / Kx;
        length_ += 0.5 * (
            - (POW2(A) * Cx * Sx) / (2.0 * Kx) \
            + (POW2(B) * Cx * Sx) / 2.0 \
            + (POW2(A) * length) / (2.0 * Kx) \
            + (POW2(B) * length) / 2.0 \
            - (A * B * POW2(Cx)) / Kx \
            + (A * B) / Kx
        );
    }
    else {
        length_ += h * length * (
            3.0 * length * xp \
            + 6.0 * x \
            - (k0 - h) * POW2(length)
        ) / 6.0;
        length_ += 0.5 * (POW2(B)) * length;
    }

    if (NONZERO(Ky)) {
        length_ += 0.5 * (
            - (POW2(C) * Cy * Sy) / (2.0 * Ky) \
            + (POW2(D) * Cy * Sy) / 2.0 \
            + (POW2(C) * length) / (2.0 * Ky) \
            + (POW2(D) * length) / 2.0 \
            - (C * D * POW2(Cy)) / Ky \
            + (C * D) / Ky
        );
    }
    else {
        length_ += 0.5 * POW2(D) * length;
    }

    const double dzeta = length - length_ / rvv;

    LocalParticle_set_x(part, x_);
    LocalParticle_set_px(part, px_);
    LocalParticle_set_y(part, y_);
    LocalParticle_set_py(part, py_);
    LocalParticle_add_to_zeta(part, dzeta);
    LocalParticle_add_to_s(part, length);
}

#endif // XTRACK_TRACK_THICK_CFD_H

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_MULTIPOLAR_KICK_H
#define XTRACK_MULTIPOLAR_KICK_H

 static inline
void multipolar_kick(
    LocalParticle* part,
    const int order,
    const double inv_factorial_order,
      const double *knl,
      const double *ksl,
    const double weight
) {

    #ifdef XSUITE_BACKTRACK
        LocalParticle_kill_particle(part, -31);
        return;
    #else

    int64_t index = order;
    double inv_factorial = inv_factorial_order;

    double dpx = knl[index] * inv_factorial;
    double dpy = ksl[index] * inv_factorial;

    double const x   = LocalParticle_get_x(part);
    double const y   = LocalParticle_get_y(part);
    double const chi = LocalParticle_get_chi(part);

    while( index > 0 )
    {
        double const zre = dpx * x - dpy * y;
        double const zim = dpx * y + dpy * x;

        inv_factorial *= index;
        index -= 1;

        dpx = knl[index] * inv_factorial + zre;
        dpy = ksl[index] * inv_factorial + zim;
    }

    dpx = -chi * dpx; // rad
    dpy =  chi * dpy; // rad

    LocalParticle_add_to_px(part, weight * dpx);
    LocalParticle_add_to_py(part, weight * dpy);

    #endif // not XSUITE_BACKTRACK
}

#endif // XTRACK_MULTIPOLAR_KICK_H

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_THICKCOMBINEDFUNCTIONDIPOLE_H
#define XTRACK_THICKCOMBINEDFUNCTIONDIPOLE_H

 static inline
void CombinedFunctionMagnet_track_local_particle(
        CombinedFunctionMagnetData el,
        LocalParticle* part0
) {
    // Adapted from MAD-X `ttcfd' in `trrun.f90'
    double length = CombinedFunctionMagnetData_get_length(el);

    #ifdef XSUITE_BACKTRACK
        length = -length;
    #endif

    const double k0 = CombinedFunctionMagnetData_get_k0(el);
    const double k1 = CombinedFunctionMagnetData_get_k1(el);
    const double h = CombinedFunctionMagnetData_get_h(el);

    const int64_t num_multipole_kicks = CombinedFunctionMagnetData_get_num_multipole_kicks(el);
    const int64_t order = CombinedFunctionMagnetData_get_order(el);
    const double inv_factorial_order = CombinedFunctionMagnetData_get_inv_factorial_order(el);

      const double *knl = CombinedFunctionMagnetData_getp1_knl(el, 0);
      const double *ksl = CombinedFunctionMagnetData_getp1_ksl(el, 0);

    const double slice_length = length / (num_multipole_kicks + 1);
    const double kick_weight = 1. / num_multipole_kicks;


    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

        track_thick_cfd(part, slice_length, k0, k1, h);

        for (int ii = 0; ii < num_multipole_kicks; ii++) {
            multipolar_kick(part, order, inv_factorial_order, knl, ksl, kick_weight);
            track_thick_cfd(part, slice_length, k0, k1, h);
        }

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

}

#endif // XTRACK_THICKCOMBINEDFUNCTIONDIPOLE_H

             
            void CombinedFunctionMagnet_track_particles(
               CombinedFunctionMagnetData el,

                             ParticlesData particles,

                             int64_t flag_increment_at_element,
                  int8_t* io_buffer){
//            const int num_threads = omp_get_max_threads();                                 //only_for_context cpu_openmp
//            const int64_t capacity = ParticlesData_get__capacity(particles);               //only_for_context cpu_openmp
//            const int64_t chunk_size = (capacity + num_threads - 1)/num_threads; // ceil division  //only_for_context cpu_openmp
//            #pragma omp parallel for                                                       //only_for_context cpu_openmp
//            for (int64_t batch_id = 0; batch_id < num_threads; batch_id++) {               //only_for_context cpu_openmp
                LocalParticle lpart;
                lpart.io_buffer = io_buffer;
//                int64_t part_id = batch_id * chunk_size;                                       //only_for_context cpu_openmp
//                int64_t end_id = (batch_id + 1) * chunk_size;                                  //only_for_context cpu_openmp
//                if (end_id > capacity) end_id = capacity;                                      //only_for_context cpu_openmp
    
                int64_t part_id = 0;                    //only_for_context cpu_serial
//                int64_t part_id = blockDim.x * blockIdx.x + threadIdx.x; //only_for_context cuda
//                int64_t part_id = get_global_id(0);                    //only_for_context opencl
                int64_t end_id = 0; // unused outside of openmp  //only_for_context cpu_serial cuda opencl
    
                int64_t part_capacity = ParticlesData_get__capacity(particles);
                if (part_id<part_capacity){
                    Particles_to_LocalParticle(particles, &lpart, part_id, end_id);
                    if (check_is_active(&lpart)>0){
              CombinedFunctionMagnet_track_local_particle(el, &lpart);

                    }
                    if (check_is_active(&lpart)>0 && flag_increment_at_element){
                            increment_at_element(&lpart, 1);
                    }
                }
//            } //only_for_context cpu_openmp
            
            // On OpenMP we want to additionally by default reorganize all
            // the particles.
//            #ifndef XT_OMP_SKIP_REORGANIZE                             //only_for_context cpu_openmp
//            LocalParticle lpart;                                       //only_for_context cpu_openmp
//            lpart.io_buffer = io_buffer;                               //only_for_context cpu_openmp
//            Particles_to_LocalParticle(particles, &lpart, 0, capacity);//only_for_context cpu_openmp
//            check_is_active(&lpart);                                   //only_for_context cpu_openmp
//            #endif                                                     //only_for_context cpu_openmp
        }

#ifndef XOBJ_TYPEDEF_DipoleEdgeData
#define XOBJ_TYPEDEF_DipoleEdgeData
typedef   struct DipoleEdgeData_s * DipoleEdgeData;
 static inline DipoleEdgeData DipoleEdgeData_getp(DipoleEdgeData  obj){
  int64_t offset=0;
  return (DipoleEdgeData)(( char*) obj+offset);
}
 static inline double DipoleEdgeData_get_r21(const DipoleEdgeData  obj){
  int64_t offset=0;
  return *( double*)(( char*) obj+offset);
}
 static inline void DipoleEdgeData_set_r21(DipoleEdgeData  obj, double value){
  int64_t offset=0;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* DipoleEdgeData_getp_r21(DipoleEdgeData  obj){
  int64_t offset=0;
  return ( double*)(( char*) obj+offset);
}
 static inline double DipoleEdgeData_get_r43(const DipoleEdgeData  obj){
  int64_t offset=0;
  offset+=8;
  return *( double*)(( char*) obj+offset);
}
 static inline void DipoleEdgeData_set_r43(DipoleEdgeData  obj, double value){
  int64_t offset=0;
  offset+=8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* DipoleEdgeData_getp_r43(DipoleEdgeData  obj){
  int64_t offset=0;
  offset+=8;
  return ( double*)(( char*) obj+offset);
}
 static inline double DipoleEdgeData_get_hgap(const DipoleEdgeData  obj){
  int64_t offset=0;
  offset+=16;
  return *( double*)(( char*) obj+offset);
}
 static inline void DipoleEdgeData_set_hgap(DipoleEdgeData  obj, double value){
  int64_t offset=0;
  offset+=16;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* DipoleEdgeData_getp_hgap(DipoleEdgeData  obj){
  int64_t offset=0;
  offset+=16;
  return ( double*)(( char*) obj+offset);
}
 static inline double DipoleEdgeData_get_k(const DipoleEdgeData  obj){
  int64_t offset=0;
  offset+=24;
  return *( double*)(( char*) obj+offset);
}
 static inline void DipoleEdgeData_set_k(DipoleEdgeData  obj, double value){
  int64_t offset=0;
  offset+=24;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* DipoleEdgeData_getp_k(DipoleEdgeData  obj){
  int64_t offset=0;
  offset+=24;
  return ( double*)(( char*) obj+offset);
}
 static inline double DipoleEdgeData_get_e1(const DipoleEdgeData  obj){
  int64_t offset=0;
  offset+=32;
  return *( double*)(( char*) obj+offset);
}
 static inline void DipoleEdgeData_set_e1(DipoleEdgeData  obj, double value){
  int64_t offset=0;
  offset+=32;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* DipoleEdgeData_getp_e1(DipoleEdgeData  obj){
  int64_t offset=0;
  offset+=32;
  return ( double*)(( char*) obj+offset);
}
 static inline double DipoleEdgeData_get_e1_fd(const DipoleEdgeData  obj){
  int64_t offset=0;
  offset+=40;
  return *( double*)(( char*) obj+offset);
}
 static inline void DipoleEdgeData_set_e1_fd(DipoleEdgeData  obj, double value){
  int64_t offset=0;
  offset+=40;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* DipoleEdgeData_getp_e1_fd(DipoleEdgeData  obj){
  int64_t offset=0;
  offset+=40;
  return ( double*)(( char*) obj+offset);
}
 static inline double DipoleEdgeData_get_fint(const DipoleEdgeData  obj){
  int64_t offset=0;
  offset+=48;
  return *( double*)(( char*) obj+offset);
}
 static inline void DipoleEdgeData_set_fint(DipoleEdgeData  obj, double value){
  int64_t offset=0;
  offset+=48;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* DipoleEdgeData_getp_fint(DipoleEdgeData  obj){
  int64_t offset=0;
  offset+=48;
  return ( double*)(( char*) obj+offset);
}
 static inline int64_t DipoleEdgeData_get_model(const DipoleEdgeData  obj){
  int64_t offset=0;
  offset+=56;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void DipoleEdgeData_set_model(DipoleEdgeData  obj, int64_t value){
  int64_t offset=0;
  offset+=56;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* DipoleEdgeData_getp_model(DipoleEdgeData  obj){
  int64_t offset=0;
  offset+=56;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline int64_t DipoleEdgeData_get_side(const DipoleEdgeData  obj){
  int64_t offset=0;
  offset+=64;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void DipoleEdgeData_set_side(DipoleEdgeData  obj, int64_t value){
  int64_t offset=0;
  offset+=64;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* DipoleEdgeData_getp_side(DipoleEdgeData  obj){
  int64_t offset=0;
  offset+=64;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline double DipoleEdgeData_get_delta_taper(const DipoleEdgeData  obj){
  int64_t offset=0;
  offset+=72;
  return *( double*)(( char*) obj+offset);
}
 static inline void DipoleEdgeData_set_delta_taper(DipoleEdgeData  obj, double value){
  int64_t offset=0;
  offset+=72;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* DipoleEdgeData_getp_delta_taper(DipoleEdgeData  obj){
  int64_t offset=0;
  offset+=72;
  return ( double*)(( char*) obj+offset);
}
#endif
// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_CONSTANTS_H
#define XTRACK_CONSTANTS_H

#if !defined( C_LIGHT )
    #define   C_LIGHT ( 299792458.0 )
#endif /* !defined( C_LIGHT ) */

#if !defined( EPSILON_0 )
    #define   EPSILON_0 (8.854187817620e-12)
#endif /* !defined( EPSILON_0 ) */

#if !defined( PI )
    #define PI (3.1415926535897932384626433832795028841971693993751)
#endif /* !defined( PI ) */

#if !defined( MU_0 )
    #define MU_0 (PI*4.0e-7)
#endif /* !defined( MU_0 ) */

#if !defined( DEG2RAD )
    #define DEG2RAD (0.0174532925199432957692369076848861271344287188854)
#endif /* !defiend( DEG2RAD ) */

#if !defined( RAD2DEG )
    #define RAD2DEG (57.29577951308232087679815481410517033240547246656442)
#endif /* !defiend( RAD2DEG ) */

#if !defined( SQRT_PI )
    #define SQRT_PI (1.7724538509055160272981674833411451827975494561224)
#endif /* !defined( SQRT_PI ) */

#if !defined( QELEM )
    #define QELEM (1.60217662e-19)
#endif /* !defined( QELEM ) */

#if !defined( DBL_MAX )
    #define DBL_MAX (1.7976931348623158e+308)
#endif /* !defined( DBL_MAX ) */

#if !defined( DBL_MIN )
    #define DBL_MIN (2.2250738585072014e-308)
#endif /* !defined( DBL_MIN ) */

#if !defined( DBL_EPSILON )
    #define DBL_EPSILON (2.2204460492503131e-16)
#endif /* !defined( DBL_EPSILON ) */

#endif /* XTRACK_CONSTANTS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_FUNCTIONS_H
#define XTRACK_FUNCTIONS_H

 static inline
void kill_all_particles(LocalParticle* part0, int64_t kill_state) {

    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

        LocalParticle_kill_particle(part, kill_state);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

}


 static inline
int8_t assert_tracking(LocalParticle* part, int64_t kill_state){
    // Whenever we are not tracking, e.g. in a twiss, the particle will be at_turn < 0.
    // We test this to distinguish genuine tracking from twiss.
    if (LocalParticle_get_at_turn(part) < 0){
        LocalParticle_kill_particle(part, kill_state);
        return 0;
    }
    return 1;
}

#endif /* XTRACK_FUNCTIONS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_PARTICLE_STATES_H
#define XTRACK_PARTICLE_STATES_H

#define  XT_LOST_ON_APERTURE       0
#define  XT_LOST_ON_LONG_CUT      -2
#define  XT_LOST_ALL_E_IN_SYNRAD -10
#define  RNG_ERR_SEEDS_NOT_SET   -20
#define  RNG_ERR_INVALID_TRACK   -21
#define  RNG_ERR_RUTH_NOT_SET    -22

#endif /* XTRACK_PARTICLE_STATES_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_TRACK_YROTATION_H
#define XTRACK_TRACK_YROTATION_H

 static inline
void YRotation_single_particle(LocalParticle* part, double sin_angle, double cos_angle, double tan_angle){

    double const beta0 = LocalParticle_get_beta0(part);
    double const beta = LocalParticle_get_rvv(part)*beta0;
    double const x  = LocalParticle_get_x(part);
    double const y  = LocalParticle_get_y(part);
    double const px = LocalParticle_get_px(part);
    double const py = LocalParticle_get_py(part);
    double const t = LocalParticle_get_zeta(part)/beta0;
    double const pt = LocalParticle_get_pzeta(part)*beta0;

    double pz = sqrt(1.0 + 2.0*pt/beta + pt*pt - px*px - py*py);
    double ptt = 1.0 - tan_angle*px/pz;
    double x_hat = x/(cos_angle*ptt);
    double px_hat = cos_angle*px + sin_angle*pz;
    double y_hat = y + tan_angle*x*py/(pz*ptt);
    double t_hat = t - tan_angle*x*(1.0/beta+pt)/(pz*ptt);

    LocalParticle_set_x(part, x_hat);
    LocalParticle_set_px(part, px_hat);
    LocalParticle_set_y(part, y_hat);
    LocalParticle_set_zeta(part,t_hat*beta0);

}

#endif /* XTRACK_TRACK_YROTATION_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_WEDGE_TRACK_H
#define XTRACK_WEDGE_TRACK_H

#define POW2(X) ((X)*(X))

 static inline
void Wedge_single_particle(
        LocalParticle* part,  // LocalParticle to track
        const double theta,   // Angle of the wedge
        const double k0       // Dipole strength
) {
    if (fabs(k0) < 10e-10) {
        const double sin_ = sin(theta);
        const double cos_ = cos(theta);
        const double tan_ = tan(theta);
        YRotation_single_particle(part, sin_, cos_, tan_);
        return;
    }

    // Params
    const double b1 = k0;

    const double rvv = LocalParticle_get_rvv(part);
    // Particle coordinates
    const double x = LocalParticle_get_x(part);
    const double px = LocalParticle_get_px(part);
    const double py = LocalParticle_get_py(part);

    // Useful constants
    const double one_plus_delta = LocalParticle_get_delta(part) + 1.0;
    const double A = 1.0 / sqrt(POW2(one_plus_delta) - POW2(py));
    const double pz = sqrt(POW2(one_plus_delta) - POW2(px) - POW2(py));

    // Map
    const double new_px = px * cos(theta) + (pz - b1 * x) * sin(theta);

    const double new_pz = sqrt(POW2(one_plus_delta) - POW2(new_px) - POW2(py));
    const double new_x = x * cos(theta) \
        + (x * px * sin(2 * theta) + POW2(sin(theta)) * (2 * x * pz - b1 * POW2(x))) \
          / (new_pz + pz * cos(theta) - px * sin(theta));
    const double D = asin(A * px) - asin(A * new_px);
    const double delta_y = py * (theta + D) / b1;
    const double delta_ell = one_plus_delta * (theta + D) / b1;

    // Update particle coordinates
    LocalParticle_set_x(part, new_x);
    LocalParticle_add_to_y(part, delta_y);
    LocalParticle_set_px(part, new_px);
    LocalParticle_add_to_zeta(part, -delta_ell / rvv);
}

#endif
// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_FRINGE_TRACK_H
#define XTRACK_FRINGE_TRACK_H

#define POW2(X) ((X)*(X))
#define POW3(X) ((X)*(X)*(X))
#define POW4(X) ((X)*(X)*(X)*(X))


#ifndef XTRACK_FRINGE_FROM_PTC

// MAD-NG implementation
// https://github.com/MethodicalAcceleratorDesign/MAD/blob/d3cabd9cdebde62ebedb51bab61ac033b9159489/src/madl_dynmap.mad#L1864

 static inline
void Fringe_single_particle(
        LocalParticle* part,  // LocalParticle to track
        const double fint,    // Fringe field integral
        const double hgap,    // Half gap
        const double k0       // Dipole strength
) {
    if (fabs(k0) < 10e-10) {
        return;
    }

    const double beta0 = LocalParticle_get_beta0(part);

    // Particle coordinates
    const double x = LocalParticle_get_x(part);
    const double px = LocalParticle_get_px(part);
    const double y = LocalParticle_get_y(part);
    const double py = LocalParticle_get_py(part);
    const double t = LocalParticle_get_zeta(part) / beta0;
    const double pt = LocalParticle_get_ptau(part);
    const double delta = LocalParticle_get_delta(part);

    const double fh = hgap * fint;
    const double fsad = (fh > 10e-10) ? 1./(72 * fh) : 0;
    const double k0w = k0;

    const double _beta = 1. / beta0 ;
    const double b0 = k0w; // MAD does something with the charge (to be checked)

    const double dpp = POW2(1. + delta);
    const double pz = sqrt(dpp - POW2(px) - POW2(py));
    const double _pz = 1./pz;
    const double relp = 1./sqrt(dpp);
    const double tfac = -(_beta + pt);

    const double c2 = b0*fh*2;
    const double c3 = POW2(b0)*fsad*relp;

    const double xp = px/pz;
    const double yp = py/pz;
    const double xyp = xp*yp;
    const double yp2 = 1.+POW2(yp);
    const double xp2 = POW2(xp);
    const double _yp2 = 1./yp2;

    const double fi0 = atan((xp*_yp2)) - c2*(1 + xp2*(1+yp2))*pz;
    const double co2 = b0/POW2(cos(fi0));
    const double co1 = co2/(1 + POW2(xp*_yp2))*_yp2;
    const double co3 = co2*c2;

    const double fi1 =    co1          - co3*2*xp*(1+yp2)*pz;
    const double fi2 = -2*co1*xyp*_yp2 - co3*2*xp*xyp    *pz;
    const double fi3 =                 - co3*(1 + xp2*(1+yp2));

    const double kx = fi1*(1+xp2)*_pz   + fi2*xyp*_pz       - fi3*xp;
    const double ky = fi1*xyp*_pz       + fi2*yp2*_pz       - fi3*yp;
    const double kz = fi1*tfac*xp*POW2(_pz) + fi2*tfac*yp*POW2(_pz) - fi3*tfac*_pz;

    const double new_y = 2 * y / (1 + sqrt(1 - 2 * ky * y));
    const double new_x  = x  + 0.5 * kx * POW2(new_y);
    const double new_py = py - 4 * c3 * POW3(new_y) - b0 * tan(fi0) * new_y;
    const double new_t = t + 0.5 * kz * POW2(new_y) + c3 * POW4(new_y) * POW2(relp) * tfac;

    const double new_zeta = new_t * beta0;

    LocalParticle_set_x(part, new_x);
    LocalParticle_set_y(part, new_y);
    LocalParticle_set_py(part, new_py);
    LocalParticle_set_zeta(part, new_zeta);
}

#endif // no XTRACK_FRINGE_FROM_PTC


#ifdef XTRACK_FRINGE_FROM_PTC
// The following is ported from PTC:
//https://github.com/MethodicalAcceleratorDesign/MAD-X/blob/master/libs/ptc/src/Sh_def_kind.f90#L4936

 static inline
void Fringe_single_particle(
        LocalParticle* part,  // LocalParticle to track
        const double fint,    // Fringe field integral
        const double hgap,    // Half gap
        const double k0       // Dipole strength
) {
    if (fabs(k0) < 10e-10) {
        return;
    }

    const double b = k0; // PTC naming convention

    double fsad=0.0;
    if(fint*hgap != 0.){
      fsad=1./(fint*hgap*2)/36.0;
    }

    // Particle coordinates
    const double beta0 = LocalParticle_get_beta0(part);
    const double x = LocalParticle_get_x(part);
    const double px = LocalParticle_get_px(part);
    const double y = LocalParticle_get_y(part);
    const double py = LocalParticle_get_py(part);
    const double ptau = LocalParticle_get_ptau(part);
    const double delta = LocalParticle_get_delta(part);

    const double pz = sqrt((1.0 + delta)*(1.0 + delta) - px * px - py * py);
    const double time_fac = 1/beta0 + ptau;
    const double rel_p = sqrt(1. + 2*ptau/beta0 + POW2(ptau));
    const double c3 = b * b * fsad / rel_p;

    const double xp = px / pz;
    const double yp = py / pz;

    const double D_1_1 = (1.0 + xp * xp) / pz;
    const double D_2_1 =  xp * yp / pz;
    const double D_3_1 = -xp;
    const double D_1_2 = xp * yp / pz;
    const double D_2_2 = (1.0 + yp * yp)/pz;
    const double D_3_2 = -yp;
    const double D_1_3 = -time_fac * xp / (pz * pz);
    const double D_2_3 = -time_fac * yp / (pz * pz);
    const double D_3_3 =  time_fac / pz;

    double fi0 = atan((xp / (1.0 + yp * yp)))-b * fint * hgap * 2.0 * ( 1.0 + xp * xp *(2.0 + yp * yp)) * pz;
    const double co2 = b / cos(fi0) / cos(fi0);
    const double co1 = co2 / (1.0 + POW2(xp / POW2(1.0 + yp * yp)));

    const double fi_1 = co1 / (1.0 + yp*yp) - co2 * b * fint * hgap * 2.0*(2.0 * xp * (2.0 + yp * yp) * pz);
    const double fi_2 = -co1 * 2.0 * xp * yp / POW2(1.0 + yp * yp) - co2 * b * fint * hgap * 2.0 * (2.0 * xp* xp * yp) * pz;
    const double fi_3 = -co2 * b * fint * hgap * 2.0 * (1.0 + xp * xp * (2.0 + yp*yp));

    fi0 = b * tan(fi0);

    double BB = 0;
    BB = fi_1 * D_1_2 + BB;
    BB = fi_2 * D_2_2 + BB;
    BB = fi_3 * D_3_2 + BB;

    const double new_y = 2.0 * y / (1.0 + sqrt(1.0 - 2.0 * BB * y));
    double new_py = py - fi0 * new_y;

    BB = 0;
    BB = fi_1 * D_1_1 + BB;
    BB = fi_2 * D_2_1 + BB;
    BB = fi_3 * D_3_1 + BB;
    const double new_x = x + 0.5 * BB * new_y * new_y;

    BB = 0;
    BB = fi_1 * D_1_3 + BB;
    BB = fi_2 * D_2_3 + BB;
    BB = fi_3 * D_3_3 + BB;
    double d_tau = -0.5 * BB * new_y * new_y;

    new_py = new_py - 4 * c3 * POW3(new_y);
    d_tau = d_tau + c3 * POW4(new_y) / POW2(rel_p) * time_fac;

    LocalParticle_set_x(part, new_x);
    LocalParticle_set_y(part, new_y);
    LocalParticle_set_py(part, new_py);
    LocalParticle_add_to_zeta(part, -d_tau * beta0); // PTC uses tau = ct
}
#endif // XTRACK_FRINGE_FROM_PTC


// The following is derived from https://cds.cern.ch/record/2857004
// still to be checked

//  static inline
// void Fringe_single_particle_paper(
//         LocalParticle* part,  // LocalParticle to track
//         const double fint,    // Fringe field integral
//         const double hgap,    // Half gap
//         const double k0       // Dipole strength
// ) {
//
//     if (fabs(k0) < 10e-10) {
//         return;
//     }
//
//     const double rvv = LocalParticle_get_rvv(part);
//     // Particle coordinates
//     const double y = LocalParticle_get_y(part);
//     const double px = LocalParticle_get_px(part);
//     const double py = LocalParticle_get_py(part);
//     const double delta = LocalParticle_get_delta(part);

//     const double one_plus_delta = delta + 1.0;

//     const double pz_sq = POW2(one_plus_delta) - POW2(px) - POW2(py);
//     const double pz = sqrt(pz_sq);
//     const double xp = px / pz;
//     const double yp = py / pz;

//     const double dpz_dpx = -xp;
//     const double dpz_dpy = -yp;
//     const double dpz_ddelta = one_plus_delta / pz;

//     const double dxp_dpx =    -px/pz_sq * dpz_dpx     + 1/pz;
//     const double dxp_dpy =    -px/pz_sq * dpz_dpy;
//     const double dxp_ddelta = -px/pz_sq * dpz_ddelta;

//     const double dyp_dpx =    -py/pz_sq * dpz_dpx;
//     const double dyp_dpy =    -py/pz_sq * dpz_dpy     + 1/pz;
//     const double dyp_ddelta = -py/pz_sq * dpz_ddelta;

//     const double phi0 = xp / (1 + POW2(yp));
//     const double dphi0_dxp = 1 / (1 + POW2(yp));
//     const double dphi0_dyp = -2 * xp * yp / POW2(1 + POW2(yp));

//     const double phi1 = 1 + 2 * POW2(xp) + POW2(xp) * POW2(yp);
//     const double dphi1_dxp = 4 * xp + 2 * POW2(yp) * xp;
//     const double dphi1_dyp = 2 * POW2(xp) * yp;

//     const double dphi0_dpx = dphi0_dxp * dxp_dpx + dphi0_dyp * dyp_dpx;
//     const double dphi0_dpy = dphi0_dxp * dxp_dpy + dphi0_dyp * dyp_dpy;
//     const double dphi0_ddelta = dphi0_dxp * dxp_ddelta + dphi0_dyp * dyp_ddelta;

//     const double dphi1_dpx = dphi1_dxp * dxp_dpx + dphi1_dyp * dyp_dpx;
//     const double dphi1_dpy = dphi1_dxp * dxp_dpy + dphi1_dyp * dyp_dpy;
//     const double dphi1_ddelta = dphi1_dxp * dxp_ddelta + dphi1_dyp * dyp_ddelta;

//     const double g = 2 * hgap;
//     const double Phi = k0 * atan(phi0) - g * k0 * k0 * fint * pz * phi1;
//     const double dPhi_dpx = k0 * 1 / (1 + POW2(phi0)) * dphi0_dpx
//                     - g * k0 * k0 * fint * (pz * dphi1_dpx + phi1 * dpz_dpx);
//     const double dPhi_dpy = k0 * 1 / (1 + POW2(phi0)) * dphi0_dpy
//                     - g * k0 * k0 * fint * (pz * dphi1_dpy + phi1 * dpz_dpy);
//     const double dPhi_ddelta = k0 * 1 / (1 + POW2(phi0)) * dphi0_ddelta
//                     - g * k0 * k0 * fint * (pz * dphi1_ddelta + phi1 * dpz_ddelta);

//     // printf("dPhi_dpy = %e\n", dPhi_dpy);
//     // printf("Phi = %e\n", Phi);

//     const double new_y = 2 * y / (1 + sqrt(1 - 2 * dPhi_dpy * y));
//     const double delta_x = dPhi_dpx * POW2(new_y) / 2;
//     const double delta_py = -Phi * new_y;
//     const double delta_zeta = - 1 / (2 * rvv) * dPhi_ddelta * POW2(new_y);

//     LocalParticle_add_to_x(part, delta_x);
//     LocalParticle_add_to_py(part, delta_py);
//     LocalParticle_add_to_zeta(part, delta_zeta);
//     LocalParticle_set_y(part, new_y);

// }

#endif // XTRACK_FRINGE_H
// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_DIPOLEEDGE_H
#define XTRACK_DIPOLEEDGE_H

 static inline
void DipoleEdge_track_local_particle(DipoleEdgeData el, LocalParticle* part0){

    int64_t const model = DipoleEdgeData_get_model(el);

    #ifdef XTRACK_MULTIPOLE_NO_SYNRAD
    #define delta_taper (0)
    #else
        #ifndef XTRACK_DIPOLEEDGE_TAPER
        double const delta_taper = DipoleEdgeData_get_delta_taper(el);
        #endif
    #endif

    if (model == 0){
        double r21 = DipoleEdgeData_get_r21(el);
        double r43 = DipoleEdgeData_get_r43(el);

        #ifndef XTRACK_DIPOLEEDGE_TAPER
            r21 = r21 * (1 + delta_taper);
            r43 = r43 * (1 + delta_taper);
        #endif

        #ifdef XSUITE_BACKTRACK
            r21 = -r21;
            r43 = -r43;
        #endif


    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

            double const x = LocalParticle_get_x(part);
            double const y = LocalParticle_get_y(part);

            #ifdef XTRACK_DIPOLEEDGE_TAPER
                double const delta_taper = LocalParticle_get_delta(part);
                r21 = r21 * (1 + delta_taper);
                r43 = r43 * (1 + delta_taper);
            #endif

            LocalParticle_add_to_px(part, r21*x);
            LocalParticle_add_to_py(part, r43*y);


//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }


    }
    else if (model == 1){

        #ifdef XSUITE_BACKTRACK

    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

                LocalParticle_kill_particle(part, -32);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

            return;
        #else

        double const e1 = DipoleEdgeData_get_e1(el);
        double const fint = DipoleEdgeData_get_fint(el);
        double const hgap = DipoleEdgeData_get_hgap(el);
        double const k = DipoleEdgeData_get_k(el);
        int64_t const side = DipoleEdgeData_get_side(el);

        double sin_, cos_, tan_;
        if (fabs(e1) < 10e-10) {
            sin_ = -999.0; cos_ = -999.0; tan_ = -999.0;
        }
        else{
            sin_ = sin(e1); cos_ = cos(e1); tan_ = tan(e1);
        }

        if (side == 0){ // entry
            if (sin_ > -99.){

    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

                    YRotation_single_particle(part, sin_, cos_, tan_);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

            }

    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

                Fringe_single_particle(part, fint, hgap, k);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

            if (sin_ > -99.){

    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

                    Wedge_single_particle(part, -e1, k);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

            }
        }
        else if (side == 1){ // exit
            if (sin_ > -99.){

    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

                    Wedge_single_particle(part, -e1, k);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

            }

    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

                Fringe_single_particle(part, fint, hgap, -k);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

            if (sin_ > -99.){

    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

                    YRotation_single_particle(part, sin_, cos_, tan_);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

            }

        }
        #endif
    }

}

#endif


             
            void DipoleEdge_track_particles(
               DipoleEdgeData el,

                             ParticlesData particles,

                             int64_t flag_increment_at_element,
                  int8_t* io_buffer){
//            const int num_threads = omp_get_max_threads();                                 //only_for_context cpu_openmp
//            const int64_t capacity = ParticlesData_get__capacity(particles);               //only_for_context cpu_openmp
//            const int64_t chunk_size = (capacity + num_threads - 1)/num_threads; // ceil division  //only_for_context cpu_openmp
//            #pragma omp parallel for                                                       //only_for_context cpu_openmp
//            for (int64_t batch_id = 0; batch_id < num_threads; batch_id++) {               //only_for_context cpu_openmp
                LocalParticle lpart;
                lpart.io_buffer = io_buffer;
//                int64_t part_id = batch_id * chunk_size;                                       //only_for_context cpu_openmp
//                int64_t end_id = (batch_id + 1) * chunk_size;                                  //only_for_context cpu_openmp
//                if (end_id > capacity) end_id = capacity;                                      //only_for_context cpu_openmp
    
                int64_t part_id = 0;                    //only_for_context cpu_serial
//                int64_t part_id = blockDim.x * blockIdx.x + threadIdx.x; //only_for_context cuda
//                int64_t part_id = get_global_id(0);                    //only_for_context opencl
                int64_t end_id = 0; // unused outside of openmp  //only_for_context cpu_serial cuda opencl
    
                int64_t part_capacity = ParticlesData_get__capacity(particles);
                if (part_id<part_capacity){
                    Particles_to_LocalParticle(particles, &lpart, part_id, end_id);
                    if (check_is_active(&lpart)>0){
              DipoleEdge_track_local_particle(el, &lpart);

                    }
                    if (check_is_active(&lpart)>0 && flag_increment_at_element){
                            increment_at_element(&lpart, 1);
                    }
                }
//            } //only_for_context cpu_openmp
            
            // On OpenMP we want to additionally by default reorganize all
            // the particles.
//            #ifndef XT_OMP_SKIP_REORGANIZE                             //only_for_context cpu_openmp
//            LocalParticle lpart;                                       //only_for_context cpu_openmp
//            lpart.io_buffer = io_buffer;                               //only_for_context cpu_openmp
//            Particles_to_LocalParticle(particles, &lpart, 0, capacity);//only_for_context cpu_openmp
//            check_is_active(&lpart);                                   //only_for_context cpu_openmp
//            #endif                                                     //only_for_context cpu_openmp
        }

#ifndef XOBJ_TYPEDEF_DriftData
#define XOBJ_TYPEDEF_DriftData
typedef   struct DriftData_s * DriftData;
 static inline DriftData DriftData_getp(DriftData  obj){
  int64_t offset=0;
  return (DriftData)(( char*) obj+offset);
}
 static inline double DriftData_get_length(const DriftData  obj){
  int64_t offset=0;
  return *( double*)(( char*) obj+offset);
}
 static inline void DriftData_set_length(DriftData  obj, double value){
  int64_t offset=0;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* DriftData_getp_length(DriftData  obj){
  int64_t offset=0;
  return ( double*)(( char*) obj+offset);
}
#endif
// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_CONSTANTS_H
#define XTRACK_CONSTANTS_H

#if !defined( C_LIGHT )
    #define   C_LIGHT ( 299792458.0 )
#endif /* !defined( C_LIGHT ) */

#if !defined( EPSILON_0 )
    #define   EPSILON_0 (8.854187817620e-12)
#endif /* !defined( EPSILON_0 ) */

#if !defined( PI )
    #define PI (3.1415926535897932384626433832795028841971693993751)
#endif /* !defined( PI ) */

#if !defined( MU_0 )
    #define MU_0 (PI*4.0e-7)
#endif /* !defined( MU_0 ) */

#if !defined( DEG2RAD )
    #define DEG2RAD (0.0174532925199432957692369076848861271344287188854)
#endif /* !defiend( DEG2RAD ) */

#if !defined( RAD2DEG )
    #define RAD2DEG (57.29577951308232087679815481410517033240547246656442)
#endif /* !defiend( RAD2DEG ) */

#if !defined( SQRT_PI )
    #define SQRT_PI (1.7724538509055160272981674833411451827975494561224)
#endif /* !defined( SQRT_PI ) */

#if !defined( QELEM )
    #define QELEM (1.60217662e-19)
#endif /* !defined( QELEM ) */

#if !defined( DBL_MAX )
    #define DBL_MAX (1.7976931348623158e+308)
#endif /* !defined( DBL_MAX ) */

#if !defined( DBL_MIN )
    #define DBL_MIN (2.2250738585072014e-308)
#endif /* !defined( DBL_MIN ) */

#if !defined( DBL_EPSILON )
    #define DBL_EPSILON (2.2204460492503131e-16)
#endif /* !defined( DBL_EPSILON ) */

#endif /* XTRACK_CONSTANTS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_FUNCTIONS_H
#define XTRACK_FUNCTIONS_H

 static inline
void kill_all_particles(LocalParticle* part0, int64_t kill_state) {

    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

        LocalParticle_kill_particle(part, kill_state);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

}


 static inline
int8_t assert_tracking(LocalParticle* part, int64_t kill_state){
    // Whenever we are not tracking, e.g. in a twiss, the particle will be at_turn < 0.
    // We test this to distinguish genuine tracking from twiss.
    if (LocalParticle_get_at_turn(part) < 0){
        LocalParticle_kill_particle(part, kill_state);
        return 0;
    }
    return 1;
}

#endif /* XTRACK_FUNCTIONS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_PARTICLE_STATES_H
#define XTRACK_PARTICLE_STATES_H

#define  XT_LOST_ON_APERTURE       0
#define  XT_LOST_ON_LONG_CUT      -2
#define  XT_LOST_ALL_E_IN_SYNRAD -10
#define  RNG_ERR_SEEDS_NOT_SET   -20
#define  RNG_ERR_INVALID_TRACK   -21
#define  RNG_ERR_RUTH_NOT_SET    -22

#endif /* XTRACK_PARTICLE_STATES_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_DRIFT_H
#define XTRACK_DRIFT_H


 static inline
void Drift_single_particle(LocalParticle* part, double length){

    #ifndef XTRACK_USE_EXACT_DRIFTS

        double const rpp    = LocalParticle_get_rpp(part);
        double const rv0v    = 1./LocalParticle_get_rvv(part);
        double const xp     = LocalParticle_get_px(part) * rpp;
        double const yp     = LocalParticle_get_py(part) * rpp;
        double const dzeta  = 1 - rv0v * ( 1. + ( xp*xp + yp*yp ) / 2. );

        LocalParticle_add_to_x(part, xp * length );
        LocalParticle_add_to_y(part, yp * length );
        LocalParticle_add_to_s(part, length);
        LocalParticle_add_to_zeta(part, length * dzeta );

    #else

        double const px = LocalParticle_get_px(part);
        double const py = LocalParticle_get_py(part);
        double const rv0v    = 1./LocalParticle_get_rvv(part);
        double const one_plus_delta = 1. + LocalParticle_get_delta(part);

        double const one_over_pz = 1./sqrt(one_plus_delta*one_plus_delta
                                           - px * px - py * py);
        double const dzeta = 1 - rv0v * one_plus_delta * one_over_pz;

        LocalParticle_add_to_x(part, px * one_over_pz * length);
        LocalParticle_add_to_y(part, py * one_over_pz * length);
        LocalParticle_add_to_zeta(part, dzeta * length);
        LocalParticle_add_to_s(part, length);

    #endif
}

#endif /* XTRACK_DRIFT_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_DRIFT_ELEM_H
#define XTRACK_DRIFT_ELEM_H

 static inline
void Drift_track_local_particle(DriftData el, LocalParticle* part0){

    double length = DriftData_get_length(el);
    #ifdef XSUITE_BACKTRACK
        length = -length;
    #endif


    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

        Drift_single_particle(part, length);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }


}


#endif /* XTRACK_DRIFT_ELEM_H */


             
            void Drift_track_particles(
               DriftData el,

                             ParticlesData particles,

                             int64_t flag_increment_at_element,
                  int8_t* io_buffer){
//            const int num_threads = omp_get_max_threads();                                 //only_for_context cpu_openmp
//            const int64_t capacity = ParticlesData_get__capacity(particles);               //only_for_context cpu_openmp
//            const int64_t chunk_size = (capacity + num_threads - 1)/num_threads; // ceil division  //only_for_context cpu_openmp
//            #pragma omp parallel for                                                       //only_for_context cpu_openmp
//            for (int64_t batch_id = 0; batch_id < num_threads; batch_id++) {               //only_for_context cpu_openmp
                LocalParticle lpart;
                lpart.io_buffer = io_buffer;
//                int64_t part_id = batch_id * chunk_size;                                       //only_for_context cpu_openmp
//                int64_t end_id = (batch_id + 1) * chunk_size;                                  //only_for_context cpu_openmp
//                if (end_id > capacity) end_id = capacity;                                      //only_for_context cpu_openmp
    
                int64_t part_id = 0;                    //only_for_context cpu_serial
//                int64_t part_id = blockDim.x * blockIdx.x + threadIdx.x; //only_for_context cuda
//                int64_t part_id = get_global_id(0);                    //only_for_context opencl
                int64_t end_id = 0; // unused outside of openmp  //only_for_context cpu_serial cuda opencl
    
                int64_t part_capacity = ParticlesData_get__capacity(particles);
                if (part_id<part_capacity){
                    Particles_to_LocalParticle(particles, &lpart, part_id, end_id);
                    if (check_is_active(&lpart)>0){
              Drift_track_local_particle(el, &lpart);

                    }
                    if (check_is_active(&lpart)>0 && flag_increment_at_element){
                            increment_at_element(&lpart, 1);
                    }
                }
//            } //only_for_context cpu_openmp
            
            // On OpenMP we want to additionally by default reorganize all
            // the particles.
//            #ifndef XT_OMP_SKIP_REORGANIZE                             //only_for_context cpu_openmp
//            LocalParticle lpart;                                       //only_for_context cpu_openmp
//            lpart.io_buffer = io_buffer;                               //only_for_context cpu_openmp
//            Particles_to_LocalParticle(particles, &lpart, 0, capacity);//only_for_context cpu_openmp
//            check_is_active(&lpart);                                   //only_for_context cpu_openmp
//            #endif                                                     //only_for_context cpu_openmp
        }

#ifndef XOBJ_TYPEDEF_ElensData
#define XOBJ_TYPEDEF_ElensData
typedef   struct ElensData_s * ElensData;
 static inline ElensData ElensData_getp(ElensData  obj){
  int64_t offset=0;
  return (ElensData)(( char*) obj+offset);
}
 static inline double ElensData_get_current(const ElensData  obj){
  int64_t offset=0;
  offset+=8;
  return *( double*)(( char*) obj+offset);
}
 static inline void ElensData_set_current(ElensData  obj, double value){
  int64_t offset=0;
  offset+=8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* ElensData_getp_current(ElensData  obj){
  int64_t offset=0;
  offset+=8;
  return ( double*)(( char*) obj+offset);
}
 static inline double ElensData_get_inner_radius(const ElensData  obj){
  int64_t offset=0;
  offset+=16;
  return *( double*)(( char*) obj+offset);
}
 static inline void ElensData_set_inner_radius(ElensData  obj, double value){
  int64_t offset=0;
  offset+=16;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* ElensData_getp_inner_radius(ElensData  obj){
  int64_t offset=0;
  offset+=16;
  return ( double*)(( char*) obj+offset);
}
 static inline double ElensData_get_outer_radius(const ElensData  obj){
  int64_t offset=0;
  offset+=24;
  return *( double*)(( char*) obj+offset);
}
 static inline void ElensData_set_outer_radius(ElensData  obj, double value){
  int64_t offset=0;
  offset+=24;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* ElensData_getp_outer_radius(ElensData  obj){
  int64_t offset=0;
  offset+=24;
  return ( double*)(( char*) obj+offset);
}
 static inline double ElensData_get_elens_length(const ElensData  obj){
  int64_t offset=0;
  offset+=32;
  return *( double*)(( char*) obj+offset);
}
 static inline void ElensData_set_elens_length(ElensData  obj, double value){
  int64_t offset=0;
  offset+=32;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* ElensData_getp_elens_length(ElensData  obj){
  int64_t offset=0;
  offset+=32;
  return ( double*)(( char*) obj+offset);
}
 static inline double ElensData_get_voltage(const ElensData  obj){
  int64_t offset=0;
  offset+=40;
  return *( double*)(( char*) obj+offset);
}
 static inline void ElensData_set_voltage(ElensData  obj, double value){
  int64_t offset=0;
  offset+=40;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* ElensData_getp_voltage(ElensData  obj){
  int64_t offset=0;
  offset+=40;
  return ( double*)(( char*) obj+offset);
}
 static inline double ElensData_get_residual_kick_x(const ElensData  obj){
  int64_t offset=0;
  offset+=48;
  return *( double*)(( char*) obj+offset);
}
 static inline void ElensData_set_residual_kick_x(ElensData  obj, double value){
  int64_t offset=0;
  offset+=48;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* ElensData_getp_residual_kick_x(ElensData  obj){
  int64_t offset=0;
  offset+=48;
  return ( double*)(( char*) obj+offset);
}
 static inline double ElensData_get_residual_kick_y(const ElensData  obj){
  int64_t offset=0;
  offset+=56;
  return *( double*)(( char*) obj+offset);
}
 static inline void ElensData_set_residual_kick_y(ElensData  obj, double value){
  int64_t offset=0;
  offset+=56;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* ElensData_getp_residual_kick_y(ElensData  obj){
  int64_t offset=0;
  offset+=56;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNFloat64 ElensData_getp_coefficients_polynomial(ElensData  obj){
  int64_t offset=0;
  offset+=72;
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t ElensData_len_coefficients_polynomial(ElensData  obj){
  int64_t offset=0;
  offset+=72;
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double ElensData_get_coefficients_polynomial(const ElensData  obj, int64_t i0){
  int64_t offset=0;
  offset+=72;
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void ElensData_set_coefficients_polynomial(ElensData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=72;
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* ElensData_getp1_coefficients_polynomial(ElensData  obj, int64_t i0){
  int64_t offset=0;
  offset+=72;
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline double ElensData_get_polynomial_order(const ElensData  obj){
  int64_t offset=0;
  offset+=64;
  return *( double*)(( char*) obj+offset);
}
 static inline void ElensData_set_polynomial_order(ElensData  obj, double value){
  int64_t offset=0;
  offset+=64;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* ElensData_getp_polynomial_order(ElensData  obj){
  int64_t offset=0;
  offset+=64;
  return ( double*)(( char*) obj+offset);
}
#endif
// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_CONSTANTS_H
#define XTRACK_CONSTANTS_H

#if !defined( C_LIGHT )
    #define   C_LIGHT ( 299792458.0 )
#endif /* !defined( C_LIGHT ) */

#if !defined( EPSILON_0 )
    #define   EPSILON_0 (8.854187817620e-12)
#endif /* !defined( EPSILON_0 ) */

#if !defined( PI )
    #define PI (3.1415926535897932384626433832795028841971693993751)
#endif /* !defined( PI ) */

#if !defined( MU_0 )
    #define MU_0 (PI*4.0e-7)
#endif /* !defined( MU_0 ) */

#if !defined( DEG2RAD )
    #define DEG2RAD (0.0174532925199432957692369076848861271344287188854)
#endif /* !defiend( DEG2RAD ) */

#if !defined( RAD2DEG )
    #define RAD2DEG (57.29577951308232087679815481410517033240547246656442)
#endif /* !defiend( RAD2DEG ) */

#if !defined( SQRT_PI )
    #define SQRT_PI (1.7724538509055160272981674833411451827975494561224)
#endif /* !defined( SQRT_PI ) */

#if !defined( QELEM )
    #define QELEM (1.60217662e-19)
#endif /* !defined( QELEM ) */

#if !defined( DBL_MAX )
    #define DBL_MAX (1.7976931348623158e+308)
#endif /* !defined( DBL_MAX ) */

#if !defined( DBL_MIN )
    #define DBL_MIN (2.2250738585072014e-308)
#endif /* !defined( DBL_MIN ) */

#if !defined( DBL_EPSILON )
    #define DBL_EPSILON (2.2204460492503131e-16)
#endif /* !defined( DBL_EPSILON ) */

#endif /* XTRACK_CONSTANTS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_FUNCTIONS_H
#define XTRACK_FUNCTIONS_H

 static inline
void kill_all_particles(LocalParticle* part0, int64_t kill_state) {

    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

        LocalParticle_kill_particle(part, kill_state);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

}


 static inline
int8_t assert_tracking(LocalParticle* part, int64_t kill_state){
    // Whenever we are not tracking, e.g. in a twiss, the particle will be at_turn < 0.
    // We test this to distinguish genuine tracking from twiss.
    if (LocalParticle_get_at_turn(part) < 0){
        LocalParticle_kill_particle(part, kill_state);
        return 0;
    }
    return 1;
}

#endif /* XTRACK_FUNCTIONS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_PARTICLE_STATES_H
#define XTRACK_PARTICLE_STATES_H

#define  XT_LOST_ON_APERTURE       0
#define  XT_LOST_ON_LONG_CUT      -2
#define  XT_LOST_ALL_E_IN_SYNRAD -10
#define  RNG_ERR_SEEDS_NOT_SET   -20
#define  RNG_ERR_INVALID_TRACK   -21
#define  RNG_ERR_RUTH_NOT_SET    -22

#endif /* XTRACK_PARTICLE_STATES_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_ELENS_H
#define XTRACK_ELENS_H

 static inline
void Elens_track_local_particle(ElensData el, LocalParticle* part0){

    double elens_length = ElensData_get_elens_length(el);

    #ifdef XSUITE_BACKTRACK
        elens_length = -elens_length;
    #endif

    double const inner_radius = ElensData_get_inner_radius(el);
    double const outer_radius = ElensData_get_outer_radius(el);
    double const current = ElensData_get_current(el);
    double const voltage = ElensData_get_voltage(el);
    double const residual_kick_x = ElensData_get_residual_kick_x(el);
    double const residual_kick_y = ElensData_get_residual_kick_y(el);

    int const polynomial_order = ElensData_get_polynomial_order(el);

      double const* coefficients_polynomial =
                                ElensData_getp1_coefficients_polynomial(el, 0);


    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp


        // electron mass
        double const EMASS  = 510998.928;
        // speed of light

        #if !defined( C_LIGHT )
            #define   C_LIGHT ( 299792458.0 )
        #endif /* !defined( C_LIGHT ) */

        #if !defined( EPSILON_0 )
            #define   EPSILON_0 (8.854187817620e-12)
        #endif /* !defined( EPSILON_0 ) */

        #if !defined( PI )
            #define PI (3.1415926535897932384626433832795028841971693993751)
        #endif /* !defined( PI ) */

        double x      = LocalParticle_get_x(part);
        double y      = LocalParticle_get_y(part);

        // delta
        // double delta  = LocalParticle_get_delta(part);
        // charge ratio: q/q0
        // double qratio = LocalParticle_get_charge_ratio(part);
        // chi = q/q0 * m0/m
        double const chi    = LocalParticle_get_chi(part);
        // reference particle momentum
        double const p0c    = LocalParticle_get_p0c(part);
        // particle momentum
        // double pc     = (1+delta)*(chi/qratio)*(p0c);
        // reference particle charge
        double const q0     = LocalParticle_get_q0(part);

        // rpp = P0/P
        double const rpp     = LocalParticle_get_rpp(part);


        // transverse radius
        double r      = sqrt(x*x + y*y);

        double rvv    = LocalParticle_get_rvv(part);
        double beta0  = LocalParticle_get_beta0(part);

        // # magnetic rigidity
        double const Brho0  = p0c/(q0*C_LIGHT);

        // # Electron properties
        // total electron energy
        double const etot_e       = voltage + EMASS;
        // // electron momentum
        double const p_e          = sqrt(etot_e*etot_e - EMASS*EMASS);
        // // relativistic beta of electron
        double const beta_e       = p_e/etot_e;
        //
        // // # relativistic beta  of protons
        double beta_p = rvv*beta0;

        // keep the formulas more compact
        double const r1 = inner_radius;
        double const r2 = outer_radius;

        // # geometric factor frr uniform distribution
        double frr = 0.;

        //
        if( r < r1 )
        {
          frr = 0.;
        }
        else if ( r > r2 )
        {
          frr = 1.;
        }
        else
        {
          // frr = ((r*r - r1*r1)/(r2*r2 - r1*r1));
          if (polynomial_order ==0)
            {
              frr = ((r*r - r1*r1)/(r2*r2 - r1*r1));
            }
          else
            {
              frr = 0;
              for(int i=0; i<(polynomial_order+1); ++i){
                frr += coefficients_polynomial[i]*pow((double)(r*1e3), (double)(polynomial_order-i));
            }

            }
        }

        // # calculate the kick at r2 (maximum kick)
        double theta_max = ((1.0/(4.0*PI*EPSILON_0)));
        theta_max = theta_max*(2*elens_length*current);

        // for the moment: e-beam in the opposite direction from proton beam
        // generalize later
        theta_max = theta_max*(1+beta_e*beta_p);

        theta_max = theta_max/(outer_radius*Brho0*beta_e*beta_p);
        theta_max = theta_max/(C_LIGHT*C_LIGHT);
        // theta max is now completed
        // theta_max = (-1)*theta_max/(rpp*chi);


        // now the actual kick the particle receives

        double theta_pxpy = 0.;
        double dpx = 0.;
        double dpy = 0.;
        //


        if ( r > r1 )
        {
          theta_pxpy = (-1)*frr*theta_max*(outer_radius/r)*(1/(rpp*chi));
          dpx        = x*theta_pxpy/r;
          dpy        = y*theta_pxpy/r;
        }
        else
        {
          // if the particle is not inside the e-beam, it will only
          // be subject to the residual kick
          dpx = residual_kick_x;
          dpy = residual_kick_y;
        }


        LocalParticle_add_to_px(part, dpx );
        LocalParticle_add_to_py(part, dpy );

        // we can update the particle properties or add to the particle properties
        // LocalParticle_add_to_px(part, dpx);
        // LocalParticle_add_to_py(part, dpy);

        // LocalParticle_set_py(part, py_hat);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

}

#endif

             
            void Elens_track_particles(
               ElensData el,

                             ParticlesData particles,

                             int64_t flag_increment_at_element,
                  int8_t* io_buffer){
//            const int num_threads = omp_get_max_threads();                                 //only_for_context cpu_openmp
//            const int64_t capacity = ParticlesData_get__capacity(particles);               //only_for_context cpu_openmp
//            const int64_t chunk_size = (capacity + num_threads - 1)/num_threads; // ceil division  //only_for_context cpu_openmp
//            #pragma omp parallel for                                                       //only_for_context cpu_openmp
//            for (int64_t batch_id = 0; batch_id < num_threads; batch_id++) {               //only_for_context cpu_openmp
                LocalParticle lpart;
                lpart.io_buffer = io_buffer;
//                int64_t part_id = batch_id * chunk_size;                                       //only_for_context cpu_openmp
//                int64_t end_id = (batch_id + 1) * chunk_size;                                  //only_for_context cpu_openmp
//                if (end_id > capacity) end_id = capacity;                                      //only_for_context cpu_openmp
    
                int64_t part_id = 0;                    //only_for_context cpu_serial
//                int64_t part_id = blockDim.x * blockIdx.x + threadIdx.x; //only_for_context cuda
//                int64_t part_id = get_global_id(0);                    //only_for_context opencl
                int64_t end_id = 0; // unused outside of openmp  //only_for_context cpu_serial cuda opencl
    
                int64_t part_capacity = ParticlesData_get__capacity(particles);
                if (part_id<part_capacity){
                    Particles_to_LocalParticle(particles, &lpart, part_id, end_id);
                    if (check_is_active(&lpart)>0){
              Elens_track_local_particle(el, &lpart);

                    }
                    if (check_is_active(&lpart)>0 && flag_increment_at_element){
                            increment_at_element(&lpart, 1);
                    }
                }
//            } //only_for_context cpu_openmp
            
            // On OpenMP we want to additionally by default reorganize all
            // the particles.
//            #ifndef XT_OMP_SKIP_REORGANIZE                             //only_for_context cpu_openmp
//            LocalParticle lpart;                                       //only_for_context cpu_openmp
//            lpart.io_buffer = io_buffer;                               //only_for_context cpu_openmp
//            Particles_to_LocalParticle(particles, &lpart, 0, capacity);//only_for_context cpu_openmp
//            check_is_active(&lpart);                                   //only_for_context cpu_openmp
//            #endif                                                     //only_for_context cpu_openmp
        }

#ifndef XOBJ_TYPEDEF_ExciterData
#define XOBJ_TYPEDEF_ExciterData
typedef   struct ExciterData_s * ExciterData;
 static inline ExciterData ExciterData_getp(ExciterData  obj){
  int64_t offset=0;
  return (ExciterData)(( char*) obj+offset);
}
 static inline int64_t ExciterData_get_order(const ExciterData  obj){
  int64_t offset=0;
  offset+=8;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void ExciterData_set_order(ExciterData  obj, int64_t value){
  int64_t offset=0;
  offset+=8;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* ExciterData_getp_order(ExciterData  obj){
  int64_t offset=0;
  offset+=8;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline int64_t ExciterData_get_nsamples(const ExciterData  obj){
  int64_t offset=0;
  offset+=16;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void ExciterData_set_nsamples(ExciterData  obj, int64_t value){
  int64_t offset=0;
  offset+=16;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* ExciterData_getp_nsamples(ExciterData  obj){
  int64_t offset=0;
  offset+=16;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline double ExciterData_get_sampling_frequency(const ExciterData  obj){
  int64_t offset=0;
  offset+=24;
  return *( double*)(( char*) obj+offset);
}
 static inline void ExciterData_set_sampling_frequency(ExciterData  obj, double value){
  int64_t offset=0;
  offset+=24;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* ExciterData_getp_sampling_frequency(ExciterData  obj){
  int64_t offset=0;
  offset+=24;
  return ( double*)(( char*) obj+offset);
}
 static inline double ExciterData_get_frev(const ExciterData  obj){
  int64_t offset=0;
  offset+=32;
  return *( double*)(( char*) obj+offset);
}
 static inline void ExciterData_set_frev(ExciterData  obj, double value){
  int64_t offset=0;
  offset+=32;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* ExciterData_getp_frev(ExciterData  obj){
  int64_t offset=0;
  offset+=32;
  return ( double*)(( char*) obj+offset);
}
 static inline int64_t ExciterData_get_start_turn(const ExciterData  obj){
  int64_t offset=0;
  offset+=40;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void ExciterData_set_start_turn(ExciterData  obj, int64_t value){
  int64_t offset=0;
  offset+=40;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* ExciterData_getp_start_turn(ExciterData  obj){
  int64_t offset=0;
  offset+=40;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline int64_t ExciterData_get_nduration(const ExciterData  obj){
  int64_t offset=0;
  offset+=48;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void ExciterData_set_nduration(ExciterData  obj, int64_t value){
  int64_t offset=0;
  offset+=48;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* ExciterData_getp_nduration(ExciterData  obj){
  int64_t offset=0;
  offset+=48;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline ArrNFloat64 ExciterData_getp_knl(ExciterData  obj){
  int64_t offset=0;
  offset+=72;
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t ExciterData_len_knl(ExciterData  obj){
  int64_t offset=0;
  offset+=72;
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double ExciterData_get_knl(const ExciterData  obj, int64_t i0){
  int64_t offset=0;
  offset+=72;
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void ExciterData_set_knl(ExciterData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=72;
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* ExciterData_getp1_knl(ExciterData  obj, int64_t i0){
  int64_t offset=0;
  offset+=72;
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNFloat64 ExciterData_getp_ksl(ExciterData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+56);
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t ExciterData_len_ksl(ExciterData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+56);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double ExciterData_get_ksl(const ExciterData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+56);
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void ExciterData_set_ksl(ExciterData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+56);
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* ExciterData_getp1_ksl(ExciterData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+56);
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNFloat32 ExciterData_getp_samples(ExciterData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+64);
  return (ArrNFloat32)(( char*) obj+offset);
}
 static inline int64_t ExciterData_len_samples(ExciterData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+64);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline float ExciterData_get_samples(const ExciterData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+64);
  offset+=16+i0*4;
  return *( float*)(( char*) obj+offset);
}
 static inline void ExciterData_set_samples(ExciterData  obj, int64_t i0, float value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+64);
  offset+=16+i0*4;
  *( float*)(( char*) obj+offset)=value;
}
 static inline  float* ExciterData_getp1_samples(ExciterData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+64);
  offset+=16+i0*4;
  return ( float*)(( char*) obj+offset);
}
#endif
// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_CONSTANTS_H
#define XTRACK_CONSTANTS_H

#if !defined( C_LIGHT )
    #define   C_LIGHT ( 299792458.0 )
#endif /* !defined( C_LIGHT ) */

#if !defined( EPSILON_0 )
    #define   EPSILON_0 (8.854187817620e-12)
#endif /* !defined( EPSILON_0 ) */

#if !defined( PI )
    #define PI (3.1415926535897932384626433832795028841971693993751)
#endif /* !defined( PI ) */

#if !defined( MU_0 )
    #define MU_0 (PI*4.0e-7)
#endif /* !defined( MU_0 ) */

#if !defined( DEG2RAD )
    #define DEG2RAD (0.0174532925199432957692369076848861271344287188854)
#endif /* !defiend( DEG2RAD ) */

#if !defined( RAD2DEG )
    #define RAD2DEG (57.29577951308232087679815481410517033240547246656442)
#endif /* !defiend( RAD2DEG ) */

#if !defined( SQRT_PI )
    #define SQRT_PI (1.7724538509055160272981674833411451827975494561224)
#endif /* !defined( SQRT_PI ) */

#if !defined( QELEM )
    #define QELEM (1.60217662e-19)
#endif /* !defined( QELEM ) */

#if !defined( DBL_MAX )
    #define DBL_MAX (1.7976931348623158e+308)
#endif /* !defined( DBL_MAX ) */

#if !defined( DBL_MIN )
    #define DBL_MIN (2.2250738585072014e-308)
#endif /* !defined( DBL_MIN ) */

#if !defined( DBL_EPSILON )
    #define DBL_EPSILON (2.2204460492503131e-16)
#endif /* !defined( DBL_EPSILON ) */

#endif /* XTRACK_CONSTANTS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_FUNCTIONS_H
#define XTRACK_FUNCTIONS_H

 static inline
void kill_all_particles(LocalParticle* part0, int64_t kill_state) {

    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

        LocalParticle_kill_particle(part, kill_state);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

}


 static inline
int8_t assert_tracking(LocalParticle* part, int64_t kill_state){
    // Whenever we are not tracking, e.g. in a twiss, the particle will be at_turn < 0.
    // We test this to distinguish genuine tracking from twiss.
    if (LocalParticle_get_at_turn(part) < 0){
        LocalParticle_kill_particle(part, kill_state);
        return 0;
    }
    return 1;
}

#endif /* XTRACK_FUNCTIONS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_PARTICLE_STATES_H
#define XTRACK_PARTICLE_STATES_H

#define  XT_LOST_ON_APERTURE       0
#define  XT_LOST_ON_LONG_CUT      -2
#define  XT_LOST_ALL_E_IN_SYNRAD -10
#define  RNG_ERR_SEEDS_NOT_SET   -20
#define  RNG_ERR_INVALID_TRACK   -21
#define  RNG_ERR_RUTH_NOT_SET    -22

#endif /* XTRACK_PARTICLE_STATES_H */

// ##################################
// Exciter element
//
// Author: Philipp Niedermayer
// Date: 11.11.2022
// ##################################

#ifndef XTRACK_EXCITER_H
#define XTRACK_EXCITER_H

#if !defined( C_LIGHT )
    #define   C_LIGHT ( 299792458.0 )
#endif /* !defined( C_LIGHT ) */

 static inline
void Exciter_track_local_particle(ExciterData el, LocalParticle* part0){

    // get parameters
    int64_t const order = ExciterData_get_order(el);
      double const* knl = ExciterData_getp1_knl(el, 0);
      double const* ksl = ExciterData_getp1_ksl(el, 0);
      float const* samples = ExciterData_getp1_samples(el, 0);
    int64_t const nsamples = ExciterData_get_nsamples(el);
	int64_t const nduration = ExciterData_get_nduration(el);
    double const sampling_frequency = ExciterData_get_sampling_frequency(el);
    double const frev = ExciterData_get_frev(el);
    int64_t const start_turn = ExciterData_get_start_turn(el);

    #ifdef XSUITE_BACKTRACK
        #define XTRACK_EXCITER_SIGN (-1)
    #else
        #define XTRACK_EXCITER_SIGN (+1)
    #endif


    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp


        // zeta is the absolute path length deviation from the reference particle: zeta = (s - beta0*c*t)
        // but without limits, i.e. it can exceed the circumference (for coasting beams)
        // as the particle falls behind or overtakes the reference particle
        double const zeta = LocalParticle_get_zeta(part);
        double const at_turn = LocalParticle_get_at_turn(part);
        double const beta0 = LocalParticle_get_beta0(part);

        // compute excitation sample index
        int64_t i = sampling_frequency * ( ( at_turn - start_turn ) / frev - zeta / beta0 / C_LIGHT );

        if (i >= 0 && i < nduration){
			if (i >= nsamples){
				i = i % nsamples;
			}

            // compute normal and skew multipole components
            double const x = LocalParticle_get_x(part);
            double const y = LocalParticle_get_y(part);
            double dpx = 0.0;
            double dpy = 0.0;
            double zre = 1.0;
            double zim = 0.0;
            double factorial = 1.0;
            for (int64_t kk = 0; kk <= order; kk++){
                if (kk>0){
                    factorial *= kk;
                }

                dpx += XTRACK_EXCITER_SIGN * (knl[kk] * zre - ksl[kk] * zim) / factorial;
                dpy += XTRACK_EXCITER_SIGN * (knl[kk] * zim + ksl[kk] * zre) / factorial;

                double const zret = zre * x - zim * y;
                zim = zim * x + zre * y;
                zre = zret;
            }

            // scale by excitation strength
            dpx *= samples[i];
            dpy *= samples[i];

            // apply kick
            double const chi = LocalParticle_get_chi(part);
            LocalParticle_add_to_px(part, - chi * dpx);
            LocalParticle_add_to_py(part, + chi * dpy);

        }
 


//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }


}

#endif


             
            void Exciter_track_particles(
               ExciterData el,

                             ParticlesData particles,

                             int64_t flag_increment_at_element,
                  int8_t* io_buffer){
//            const int num_threads = omp_get_max_threads();                                 //only_for_context cpu_openmp
//            const int64_t capacity = ParticlesData_get__capacity(particles);               //only_for_context cpu_openmp
//            const int64_t chunk_size = (capacity + num_threads - 1)/num_threads; // ceil division  //only_for_context cpu_openmp
//            #pragma omp parallel for                                                       //only_for_context cpu_openmp
//            for (int64_t batch_id = 0; batch_id < num_threads; batch_id++) {               //only_for_context cpu_openmp
                LocalParticle lpart;
                lpart.io_buffer = io_buffer;
//                int64_t part_id = batch_id * chunk_size;                                       //only_for_context cpu_openmp
//                int64_t end_id = (batch_id + 1) * chunk_size;                                  //only_for_context cpu_openmp
//                if (end_id > capacity) end_id = capacity;                                      //only_for_context cpu_openmp
    
                int64_t part_id = 0;                    //only_for_context cpu_serial
//                int64_t part_id = blockDim.x * blockIdx.x + threadIdx.x; //only_for_context cuda
//                int64_t part_id = get_global_id(0);                    //only_for_context opencl
                int64_t end_id = 0; // unused outside of openmp  //only_for_context cpu_serial cuda opencl
    
                int64_t part_capacity = ParticlesData_get__capacity(particles);
                if (part_id<part_capacity){
                    Particles_to_LocalParticle(particles, &lpart, part_id, end_id);
                    if (check_is_active(&lpart)>0){
              Exciter_track_local_particle(el, &lpart);

                    }
                    if (check_is_active(&lpart)>0 && flag_increment_at_element){
                            increment_at_element(&lpart, 1);
                    }
                }
//            } //only_for_context cpu_openmp
            
            // On OpenMP we want to additionally by default reorganize all
            // the particles.
//            #ifndef XT_OMP_SKIP_REORGANIZE                             //only_for_context cpu_openmp
//            LocalParticle lpart;                                       //only_for_context cpu_openmp
//            lpart.io_buffer = io_buffer;                               //only_for_context cpu_openmp
//            Particles_to_LocalParticle(particles, &lpart, 0, capacity);//only_for_context cpu_openmp
//            check_is_active(&lpart);                                   //only_for_context cpu_openmp
//            #endif                                                     //only_for_context cpu_openmp
        }

#ifndef XOBJ_TYPEDEF_LimitEllipseData
#define XOBJ_TYPEDEF_LimitEllipseData
typedef   struct LimitEllipseData_s * LimitEllipseData;
 static inline LimitEllipseData LimitEllipseData_getp(LimitEllipseData  obj){
  int64_t offset=0;
  return (LimitEllipseData)(( char*) obj+offset);
}
 static inline double LimitEllipseData_get_a_squ(const LimitEllipseData  obj){
  int64_t offset=0;
  return *( double*)(( char*) obj+offset);
}
 static inline void LimitEllipseData_set_a_squ(LimitEllipseData  obj, double value){
  int64_t offset=0;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LimitEllipseData_getp_a_squ(LimitEllipseData  obj){
  int64_t offset=0;
  return ( double*)(( char*) obj+offset);
}
 static inline double LimitEllipseData_get_b_squ(const LimitEllipseData  obj){
  int64_t offset=0;
  offset+=8;
  return *( double*)(( char*) obj+offset);
}
 static inline void LimitEllipseData_set_b_squ(LimitEllipseData  obj, double value){
  int64_t offset=0;
  offset+=8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LimitEllipseData_getp_b_squ(LimitEllipseData  obj){
  int64_t offset=0;
  offset+=8;
  return ( double*)(( char*) obj+offset);
}
 static inline double LimitEllipseData_get_a_b_squ(const LimitEllipseData  obj){
  int64_t offset=0;
  offset+=16;
  return *( double*)(( char*) obj+offset);
}
 static inline void LimitEllipseData_set_a_b_squ(LimitEllipseData  obj, double value){
  int64_t offset=0;
  offset+=16;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LimitEllipseData_getp_a_b_squ(LimitEllipseData  obj){
  int64_t offset=0;
  offset+=16;
  return ( double*)(( char*) obj+offset);
}
#endif
// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_CONSTANTS_H
#define XTRACK_CONSTANTS_H

#if !defined( C_LIGHT )
    #define   C_LIGHT ( 299792458.0 )
#endif /* !defined( C_LIGHT ) */

#if !defined( EPSILON_0 )
    #define   EPSILON_0 (8.854187817620e-12)
#endif /* !defined( EPSILON_0 ) */

#if !defined( PI )
    #define PI (3.1415926535897932384626433832795028841971693993751)
#endif /* !defined( PI ) */

#if !defined( MU_0 )
    #define MU_0 (PI*4.0e-7)
#endif /* !defined( MU_0 ) */

#if !defined( DEG2RAD )
    #define DEG2RAD (0.0174532925199432957692369076848861271344287188854)
#endif /* !defiend( DEG2RAD ) */

#if !defined( RAD2DEG )
    #define RAD2DEG (57.29577951308232087679815481410517033240547246656442)
#endif /* !defiend( RAD2DEG ) */

#if !defined( SQRT_PI )
    #define SQRT_PI (1.7724538509055160272981674833411451827975494561224)
#endif /* !defined( SQRT_PI ) */

#if !defined( QELEM )
    #define QELEM (1.60217662e-19)
#endif /* !defined( QELEM ) */

#if !defined( DBL_MAX )
    #define DBL_MAX (1.7976931348623158e+308)
#endif /* !defined( DBL_MAX ) */

#if !defined( DBL_MIN )
    #define DBL_MIN (2.2250738585072014e-308)
#endif /* !defined( DBL_MIN ) */

#if !defined( DBL_EPSILON )
    #define DBL_EPSILON (2.2204460492503131e-16)
#endif /* !defined( DBL_EPSILON ) */

#endif /* XTRACK_CONSTANTS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_FUNCTIONS_H
#define XTRACK_FUNCTIONS_H

 static inline
void kill_all_particles(LocalParticle* part0, int64_t kill_state) {

    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

        LocalParticle_kill_particle(part, kill_state);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

}


 static inline
int8_t assert_tracking(LocalParticle* part, int64_t kill_state){
    // Whenever we are not tracking, e.g. in a twiss, the particle will be at_turn < 0.
    // We test this to distinguish genuine tracking from twiss.
    if (LocalParticle_get_at_turn(part) < 0){
        LocalParticle_kill_particle(part, kill_state);
        return 0;
    }
    return 1;
}

#endif /* XTRACK_FUNCTIONS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_PARTICLE_STATES_H
#define XTRACK_PARTICLE_STATES_H

#define  XT_LOST_ON_APERTURE       0
#define  XT_LOST_ON_LONG_CUT      -2
#define  XT_LOST_ALL_E_IN_SYNRAD -10
#define  RNG_ERR_SEEDS_NOT_SET   -20
#define  RNG_ERR_INVALID_TRACK   -21
#define  RNG_ERR_RUTH_NOT_SET    -22

#endif /* XTRACK_PARTICLE_STATES_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_LIMITELLIPSE_H
#define XTRACK_LIMITELLIPSE_H

 static inline
void LimitEllipse_track_local_particle(LimitEllipseData el, LocalParticle* part0){


    double const a_squ = LimitEllipseData_get_a_squ(el);
    double const b_squ = LimitEllipseData_get_b_squ(el);
    double const a_b_squ = LimitEllipseData_get_a_b_squ(el);


    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

        double const x = LocalParticle_get_x(part);
        double const y = LocalParticle_get_y(part);

	double const temp = x*x*b_squ + y*y*a_squ;

	int64_t const is_alive = (int64_t)( temp <= a_b_squ );

	// I assume that if I am in the function is because
    	if (!is_alive){
           LocalParticle_set_state(part, XT_LOST_ON_APERTURE);
	}


//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }


}

#endif


             
            void LimitEllipse_track_particles(
               LimitEllipseData el,

                             ParticlesData particles,

                             int64_t flag_increment_at_element,
                  int8_t* io_buffer){
//            const int num_threads = omp_get_max_threads();                                 //only_for_context cpu_openmp
//            const int64_t capacity = ParticlesData_get__capacity(particles);               //only_for_context cpu_openmp
//            const int64_t chunk_size = (capacity + num_threads - 1)/num_threads; // ceil division  //only_for_context cpu_openmp
//            #pragma omp parallel for                                                       //only_for_context cpu_openmp
//            for (int64_t batch_id = 0; batch_id < num_threads; batch_id++) {               //only_for_context cpu_openmp
                LocalParticle lpart;
                lpart.io_buffer = io_buffer;
//                int64_t part_id = batch_id * chunk_size;                                       //only_for_context cpu_openmp
//                int64_t end_id = (batch_id + 1) * chunk_size;                                  //only_for_context cpu_openmp
//                if (end_id > capacity) end_id = capacity;                                      //only_for_context cpu_openmp
    
                int64_t part_id = 0;                    //only_for_context cpu_serial
//                int64_t part_id = blockDim.x * blockIdx.x + threadIdx.x; //only_for_context cuda
//                int64_t part_id = get_global_id(0);                    //only_for_context opencl
                int64_t end_id = 0; // unused outside of openmp  //only_for_context cpu_serial cuda opencl
    
                int64_t part_capacity = ParticlesData_get__capacity(particles);
                if (part_id<part_capacity){
                    Particles_to_LocalParticle(particles, &lpart, part_id, end_id);
                    if (check_is_active(&lpart)>0){
              LimitEllipse_track_local_particle(el, &lpart);

                    }
                    if (check_is_active(&lpart)>0 && flag_increment_at_element){
                            increment_at_element(&lpart, 1);
                    }
                }
//            } //only_for_context cpu_openmp
            
            // On OpenMP we want to additionally by default reorganize all
            // the particles.
//            #ifndef XT_OMP_SKIP_REORGANIZE                             //only_for_context cpu_openmp
//            LocalParticle lpart;                                       //only_for_context cpu_openmp
//            lpart.io_buffer = io_buffer;                               //only_for_context cpu_openmp
//            Particles_to_LocalParticle(particles, &lpart, 0, capacity);//only_for_context cpu_openmp
//            check_is_active(&lpart);                                   //only_for_context cpu_openmp
//            #endif                                                     //only_for_context cpu_openmp
        }

#ifndef XOBJ_TYPEDEF_LimitPolygonData
#define XOBJ_TYPEDEF_LimitPolygonData
typedef   struct LimitPolygonData_s * LimitPolygonData;
 static inline LimitPolygonData LimitPolygonData_getp(LimitPolygonData  obj){
  int64_t offset=0;
  return (LimitPolygonData)(( char*) obj+offset);
}
 static inline ArrNFloat64 LimitPolygonData_getp_x_vertices(LimitPolygonData  obj){
  int64_t offset=0;
  offset+=40;
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t LimitPolygonData_len_x_vertices(LimitPolygonData  obj){
  int64_t offset=0;
  offset+=40;
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double LimitPolygonData_get_x_vertices(const LimitPolygonData  obj, int64_t i0){
  int64_t offset=0;
  offset+=40;
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void LimitPolygonData_set_x_vertices(LimitPolygonData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=40;
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LimitPolygonData_getp1_x_vertices(LimitPolygonData  obj, int64_t i0){
  int64_t offset=0;
  offset+=40;
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNFloat64 LimitPolygonData_getp_y_vertices(LimitPolygonData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+16);
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t LimitPolygonData_len_y_vertices(LimitPolygonData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+16);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double LimitPolygonData_get_y_vertices(const LimitPolygonData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+16);
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void LimitPolygonData_set_y_vertices(LimitPolygonData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+16);
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LimitPolygonData_getp1_y_vertices(LimitPolygonData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+16);
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNFloat64 LimitPolygonData_getp_x_normal(LimitPolygonData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+24);
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t LimitPolygonData_len_x_normal(LimitPolygonData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+24);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double LimitPolygonData_get_x_normal(const LimitPolygonData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+24);
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void LimitPolygonData_set_x_normal(LimitPolygonData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+24);
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LimitPolygonData_getp1_x_normal(LimitPolygonData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+24);
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNFloat64 LimitPolygonData_getp_y_normal(LimitPolygonData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+32);
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t LimitPolygonData_len_y_normal(LimitPolygonData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+32);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double LimitPolygonData_get_y_normal(const LimitPolygonData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+32);
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void LimitPolygonData_set_y_normal(LimitPolygonData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+32);
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LimitPolygonData_getp1_y_normal(LimitPolygonData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+32);
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline double LimitPolygonData_get_resc_fac(const LimitPolygonData  obj){
  int64_t offset=0;
  offset+=8;
  return *( double*)(( char*) obj+offset);
}
 static inline void LimitPolygonData_set_resc_fac(LimitPolygonData  obj, double value){
  int64_t offset=0;
  offset+=8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LimitPolygonData_getp_resc_fac(LimitPolygonData  obj){
  int64_t offset=0;
  offset+=8;
  return ( double*)(( char*) obj+offset);
}
#endif
// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_CONSTANTS_H
#define XTRACK_CONSTANTS_H

#if !defined( C_LIGHT )
    #define   C_LIGHT ( 299792458.0 )
#endif /* !defined( C_LIGHT ) */

#if !defined( EPSILON_0 )
    #define   EPSILON_0 (8.854187817620e-12)
#endif /* !defined( EPSILON_0 ) */

#if !defined( PI )
    #define PI (3.1415926535897932384626433832795028841971693993751)
#endif /* !defined( PI ) */

#if !defined( MU_0 )
    #define MU_0 (PI*4.0e-7)
#endif /* !defined( MU_0 ) */

#if !defined( DEG2RAD )
    #define DEG2RAD (0.0174532925199432957692369076848861271344287188854)
#endif /* !defiend( DEG2RAD ) */

#if !defined( RAD2DEG )
    #define RAD2DEG (57.29577951308232087679815481410517033240547246656442)
#endif /* !defiend( RAD2DEG ) */

#if !defined( SQRT_PI )
    #define SQRT_PI (1.7724538509055160272981674833411451827975494561224)
#endif /* !defined( SQRT_PI ) */

#if !defined( QELEM )
    #define QELEM (1.60217662e-19)
#endif /* !defined( QELEM ) */

#if !defined( DBL_MAX )
    #define DBL_MAX (1.7976931348623158e+308)
#endif /* !defined( DBL_MAX ) */

#if !defined( DBL_MIN )
    #define DBL_MIN (2.2250738585072014e-308)
#endif /* !defined( DBL_MIN ) */

#if !defined( DBL_EPSILON )
    #define DBL_EPSILON (2.2204460492503131e-16)
#endif /* !defined( DBL_EPSILON ) */

#endif /* XTRACK_CONSTANTS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_FUNCTIONS_H
#define XTRACK_FUNCTIONS_H

 static inline
void kill_all_particles(LocalParticle* part0, int64_t kill_state) {

    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

        LocalParticle_kill_particle(part, kill_state);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

}


 static inline
int8_t assert_tracking(LocalParticle* part, int64_t kill_state){
    // Whenever we are not tracking, e.g. in a twiss, the particle will be at_turn < 0.
    // We test this to distinguish genuine tracking from twiss.
    if (LocalParticle_get_at_turn(part) < 0){
        LocalParticle_kill_particle(part, kill_state);
        return 0;
    }
    return 1;
}

#endif /* XTRACK_FUNCTIONS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_PARTICLE_STATES_H
#define XTRACK_PARTICLE_STATES_H

#define  XT_LOST_ON_APERTURE       0
#define  XT_LOST_ON_LONG_CUT      -2
#define  XT_LOST_ALL_E_IN_SYNRAD -10
#define  RNG_ERR_SEEDS_NOT_SET   -20
#define  RNG_ERR_INVALID_TRACK   -21
#define  RNG_ERR_RUTH_NOT_SET    -22

#endif /* XTRACK_PARTICLE_STATES_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_LIMITPOLYGON_H
#define XTRACK_LIMITPOLYGON_H

#ifndef NO_LIMITPOLYGON_TRACK_LOCAL_PARTICLE
 static inline
void LimitPolygon_track_local_particle(LimitPolygonData el,
		LocalParticle* part0){

    int64_t N_edg = LimitPolygonData_len_x_vertices(el);


    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp


        double const x = LocalParticle_get_x(part);
        double const y = LocalParticle_get_y(part);


	int64_t iii = 0;
        int64_t jj = N_edg-1;
	int64_t is_alive = 0;
        while (iii < N_edg){
	   const double Vx_ii = LimitPolygonData_get_x_vertices(el, iii);
	   const double Vx_jj = LimitPolygonData_get_x_vertices(el, jj);
	   const double Vy_ii = LimitPolygonData_get_y_vertices(el, iii);
	   const double Vy_jj = LimitPolygonData_get_y_vertices(el, jj);
           if (((Vy_ii>y) != (Vy_jj>y)) &&
                  (x < (Vx_jj-Vx_ii) * (y-Vy_ii)
		          / (Vy_jj-Vy_ii) + Vx_ii)){
                
		is_alive = !is_alive;
	   }
           jj = iii;
           iii ++;
	}

	// I assume that if I am in the function is because
	// the particle is alive
    	if (!is_alive){
           LocalParticle_set_state(part, XT_LOST_ON_APERTURE);
	}


//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }


}
#endif

 
void LimitPolygon_impact_point_and_normal(
		             LimitPolygonData el,
                  const double* x_in,
                  const double* y_in, 
                  const double* z_in,
                  const double* x_out,
                  const double* y_out, 
                  const double* z_out,
                             const int64_t n_impacts,
                        double* x_inters,
                        double* y_inters, 
                        double* z_inters,
                        double* Nx_inters,
                        double* Ny_inters,
                        int64_t* i_found){ 
			           
    double const tol = 1e-13;

    int64_t N_edg = LimitPolygonData_len_x_vertices(el);
      const double* Vx = LimitPolygonData_getp1_x_vertices(el, 0);
      const double* Vy = LimitPolygonData_getp1_y_vertices(el, 0);
      const double* Nx = LimitPolygonData_getp1_x_normal(el, 0);
      const double* Ny = LimitPolygonData_getp1_y_normal(el, 0);
    double resc_fac = LimitPolygonData_get_resc_fac(el);

for (int i_imp=0; i_imp<n_impacts; i_imp++){ //autovectorized


        double t_min_curr = 1.;
        int64_t i_found_curr = -1;
        double x_in_curr = x_in[i_imp];
        double y_in_curr = y_in[i_imp];
        double x_out_curr = x_out[i_imp];
        double y_out_curr = y_out[i_imp];

        for (int64_t ii=0; ii<N_edg; ii++){

	    double t_border;
	    double t_ii;
            double const den = ((y_out_curr-y_in_curr)*(Vx[ii+1]-Vx[ii])
			    +(x_in_curr-x_out_curr)*(Vy[ii+1]-Vy[ii]));
            if (den == 0.){
                // it is the case when the normal top the segment is perpendicular to the edge
                // the case case overlapping the edge is not possible (this would not allow Pin inside and Pout outside - a point on the edge is condidered outside)
                // the only case left is segment parallel to tue edge => no intersection
                t_border = -2.;
	    }
            else{
                t_border=((y_out_curr-y_in_curr)*(x_in_curr-Vx[ii])
		         +(x_in_curr-x_out_curr)*(y_in_curr-Vy[ii]))/den;
	    }

            if (t_border>=0.-tol && t_border<=1.+tol){
                t_ii = (Nx[ii]*(Vx[ii]-x_in_curr)
		       +Ny[ii]*(Vy[ii]-y_in_curr)) 
		       /(Nx[ii]*(x_out_curr-x_in_curr)
	                 +Ny[ii]*(y_out_curr-y_in_curr));
                if (t_ii>=0.-tol && t_ii<t_min_curr+tol){
                    t_min_curr=t_ii;
                    i_found_curr = ii;
		}
            }
	}

        t_min_curr=resc_fac*t_min_curr;
        x_inters[i_imp]=t_min_curr*x_out_curr+(1.-t_min_curr)*x_in_curr;
        y_inters[i_imp]=t_min_curr*y_out_curr+(1.-t_min_curr)*y_in_curr;
        z_inters[i_imp]=0;

        if (i_found_curr>=0){
            Nx_inters[i_imp] = Nx[i_found_curr];
            Ny_inters[i_imp] = Ny[i_found_curr];
	}
        i_found[i_imp] = i_found_curr;
}//end autovectorized

    
}

#endif


             
            void LimitPolygon_track_particles(
               LimitPolygonData el,

                             ParticlesData particles,

                             int64_t flag_increment_at_element,
                  int8_t* io_buffer){
//            const int num_threads = omp_get_max_threads();                                 //only_for_context cpu_openmp
//            const int64_t capacity = ParticlesData_get__capacity(particles);               //only_for_context cpu_openmp
//            const int64_t chunk_size = (capacity + num_threads - 1)/num_threads; // ceil division  //only_for_context cpu_openmp
//            #pragma omp parallel for                                                       //only_for_context cpu_openmp
//            for (int64_t batch_id = 0; batch_id < num_threads; batch_id++) {               //only_for_context cpu_openmp
                LocalParticle lpart;
                lpart.io_buffer = io_buffer;
//                int64_t part_id = batch_id * chunk_size;                                       //only_for_context cpu_openmp
//                int64_t end_id = (batch_id + 1) * chunk_size;                                  //only_for_context cpu_openmp
//                if (end_id > capacity) end_id = capacity;                                      //only_for_context cpu_openmp
    
                int64_t part_id = 0;                    //only_for_context cpu_serial
//                int64_t part_id = blockDim.x * blockIdx.x + threadIdx.x; //only_for_context cuda
//                int64_t part_id = get_global_id(0);                    //only_for_context opencl
                int64_t end_id = 0; // unused outside of openmp  //only_for_context cpu_serial cuda opencl
    
                int64_t part_capacity = ParticlesData_get__capacity(particles);
                if (part_id<part_capacity){
                    Particles_to_LocalParticle(particles, &lpart, part_id, end_id);
                    if (check_is_active(&lpart)>0){
              LimitPolygon_track_local_particle(el, &lpart);

                    }
                    if (check_is_active(&lpart)>0 && flag_increment_at_element){
                            increment_at_element(&lpart, 1);
                    }
                }
//            } //only_for_context cpu_openmp
            
            // On OpenMP we want to additionally by default reorganize all
            // the particles.
//            #ifndef XT_OMP_SKIP_REORGANIZE                             //only_for_context cpu_openmp
//            LocalParticle lpart;                                       //only_for_context cpu_openmp
//            lpart.io_buffer = io_buffer;                               //only_for_context cpu_openmp
//            Particles_to_LocalParticle(particles, &lpart, 0, capacity);//only_for_context cpu_openmp
//            check_is_active(&lpart);                                   //only_for_context cpu_openmp
//            #endif                                                     //only_for_context cpu_openmp
        }

#ifndef XOBJ_TYPEDEF_LimitRacetrackData
#define XOBJ_TYPEDEF_LimitRacetrackData
typedef   struct LimitRacetrackData_s * LimitRacetrackData;
 static inline LimitRacetrackData LimitRacetrackData_getp(LimitRacetrackData  obj){
  int64_t offset=0;
  return (LimitRacetrackData)(( char*) obj+offset);
}
 static inline double LimitRacetrackData_get_min_x(const LimitRacetrackData  obj){
  int64_t offset=0;
  return *( double*)(( char*) obj+offset);
}
 static inline void LimitRacetrackData_set_min_x(LimitRacetrackData  obj, double value){
  int64_t offset=0;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LimitRacetrackData_getp_min_x(LimitRacetrackData  obj){
  int64_t offset=0;
  return ( double*)(( char*) obj+offset);
}
 static inline double LimitRacetrackData_get_max_x(const LimitRacetrackData  obj){
  int64_t offset=0;
  offset+=8;
  return *( double*)(( char*) obj+offset);
}
 static inline void LimitRacetrackData_set_max_x(LimitRacetrackData  obj, double value){
  int64_t offset=0;
  offset+=8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LimitRacetrackData_getp_max_x(LimitRacetrackData  obj){
  int64_t offset=0;
  offset+=8;
  return ( double*)(( char*) obj+offset);
}
 static inline double LimitRacetrackData_get_min_y(const LimitRacetrackData  obj){
  int64_t offset=0;
  offset+=16;
  return *( double*)(( char*) obj+offset);
}
 static inline void LimitRacetrackData_set_min_y(LimitRacetrackData  obj, double value){
  int64_t offset=0;
  offset+=16;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LimitRacetrackData_getp_min_y(LimitRacetrackData  obj){
  int64_t offset=0;
  offset+=16;
  return ( double*)(( char*) obj+offset);
}
 static inline double LimitRacetrackData_get_max_y(const LimitRacetrackData  obj){
  int64_t offset=0;
  offset+=24;
  return *( double*)(( char*) obj+offset);
}
 static inline void LimitRacetrackData_set_max_y(LimitRacetrackData  obj, double value){
  int64_t offset=0;
  offset+=24;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LimitRacetrackData_getp_max_y(LimitRacetrackData  obj){
  int64_t offset=0;
  offset+=24;
  return ( double*)(( char*) obj+offset);
}
 static inline double LimitRacetrackData_get_a(const LimitRacetrackData  obj){
  int64_t offset=0;
  offset+=32;
  return *( double*)(( char*) obj+offset);
}
 static inline void LimitRacetrackData_set_a(LimitRacetrackData  obj, double value){
  int64_t offset=0;
  offset+=32;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LimitRacetrackData_getp_a(LimitRacetrackData  obj){
  int64_t offset=0;
  offset+=32;
  return ( double*)(( char*) obj+offset);
}
 static inline double LimitRacetrackData_get_b(const LimitRacetrackData  obj){
  int64_t offset=0;
  offset+=40;
  return *( double*)(( char*) obj+offset);
}
 static inline void LimitRacetrackData_set_b(LimitRacetrackData  obj, double value){
  int64_t offset=0;
  offset+=40;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LimitRacetrackData_getp_b(LimitRacetrackData  obj){
  int64_t offset=0;
  offset+=40;
  return ( double*)(( char*) obj+offset);
}
#endif
// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_CONSTANTS_H
#define XTRACK_CONSTANTS_H

#if !defined( C_LIGHT )
    #define   C_LIGHT ( 299792458.0 )
#endif /* !defined( C_LIGHT ) */

#if !defined( EPSILON_0 )
    #define   EPSILON_0 (8.854187817620e-12)
#endif /* !defined( EPSILON_0 ) */

#if !defined( PI )
    #define PI (3.1415926535897932384626433832795028841971693993751)
#endif /* !defined( PI ) */

#if !defined( MU_0 )
    #define MU_0 (PI*4.0e-7)
#endif /* !defined( MU_0 ) */

#if !defined( DEG2RAD )
    #define DEG2RAD (0.0174532925199432957692369076848861271344287188854)
#endif /* !defiend( DEG2RAD ) */

#if !defined( RAD2DEG )
    #define RAD2DEG (57.29577951308232087679815481410517033240547246656442)
#endif /* !defiend( RAD2DEG ) */

#if !defined( SQRT_PI )
    #define SQRT_PI (1.7724538509055160272981674833411451827975494561224)
#endif /* !defined( SQRT_PI ) */

#if !defined( QELEM )
    #define QELEM (1.60217662e-19)
#endif /* !defined( QELEM ) */

#if !defined( DBL_MAX )
    #define DBL_MAX (1.7976931348623158e+308)
#endif /* !defined( DBL_MAX ) */

#if !defined( DBL_MIN )
    #define DBL_MIN (2.2250738585072014e-308)
#endif /* !defined( DBL_MIN ) */

#if !defined( DBL_EPSILON )
    #define DBL_EPSILON (2.2204460492503131e-16)
#endif /* !defined( DBL_EPSILON ) */

#endif /* XTRACK_CONSTANTS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_FUNCTIONS_H
#define XTRACK_FUNCTIONS_H

 static inline
void kill_all_particles(LocalParticle* part0, int64_t kill_state) {

    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

        LocalParticle_kill_particle(part, kill_state);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

}


 static inline
int8_t assert_tracking(LocalParticle* part, int64_t kill_state){
    // Whenever we are not tracking, e.g. in a twiss, the particle will be at_turn < 0.
    // We test this to distinguish genuine tracking from twiss.
    if (LocalParticle_get_at_turn(part) < 0){
        LocalParticle_kill_particle(part, kill_state);
        return 0;
    }
    return 1;
}

#endif /* XTRACK_FUNCTIONS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_PARTICLE_STATES_H
#define XTRACK_PARTICLE_STATES_H

#define  XT_LOST_ON_APERTURE       0
#define  XT_LOST_ON_LONG_CUT      -2
#define  XT_LOST_ALL_E_IN_SYNRAD -10
#define  RNG_ERR_SEEDS_NOT_SET   -20
#define  RNG_ERR_INVALID_TRACK   -21
#define  RNG_ERR_RUTH_NOT_SET    -22

#endif /* XTRACK_PARTICLE_STATES_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_LIMITRACETRACK_H
#define XTRACK_LIMITRACETRACK_H


 static inline
void LimitRacetrack_track_local_particle(LimitRacetrackData el, LocalParticle* part0){

    double const min_x = LimitRacetrackData_get_min_x(el);
    double const max_x = LimitRacetrackData_get_max_x(el);
    double const min_y = LimitRacetrackData_get_min_y(el);
    double const max_y = LimitRacetrackData_get_max_y(el);
    double const a = LimitRacetrackData_get_a(el);
    double const b = LimitRacetrackData_get_b(el);


    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp


        double const x = LocalParticle_get_x(part);
        double const y = LocalParticle_get_y(part);
        double dx, dy;
        int refine;

        int64_t is_alive = (int64_t)(
                        (x >= min_x) &&
                        (x <= max_x) &&
                        (y >= min_y) &&
                        (y <= max_y) );

        // We need to correct for the roundness of the corners
        if (is_alive){

            // The internal recangle (without the rounded corners) is given by
            double const rect_min_x = min_x + a;
            double const rect_max_x = max_x - a;
            double const rect_min_y = min_y + b;
            double const rect_max_y = max_y - b;

            if ((x > rect_max_x) && (y > rect_max_y)){
                // upper-right rounded corner
                refine = 1;
                dx = x - rect_max_x;
                dy = y - rect_max_y;
            }
            else if ((x < rect_min_x) && (y > rect_max_y)){
                // upper-left rounded corner
                refine = 1;
                dx = x - rect_min_x;
                dy = y - rect_max_y;
            }
            else if ((x < rect_min_x) && (y < rect_min_y)){
                // lower-left rounded corner
                refine = 1;
                dx = x - rect_min_x;
                dy = y - rect_min_y;
            }
            else if ((x > rect_max_x) && (y < rect_min_y)){
                // lower-right rounded corner
                refine = 1;
                dx = x - rect_max_x;
                dy = y - rect_min_y;
            }
            else{
                refine = 0;
            }
            if (refine){
                double const temp = dx * dx * b * b + dy * dy * a * a;
                is_alive = (int64_t)( temp <= a*a*b*b );
            }
        }

        if (!is_alive){
           LocalParticle_set_state(part, XT_LOST_ON_APERTURE);
        }


//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

}

#endif


             
            void LimitRacetrack_track_particles(
               LimitRacetrackData el,

                             ParticlesData particles,

                             int64_t flag_increment_at_element,
                  int8_t* io_buffer){
//            const int num_threads = omp_get_max_threads();                                 //only_for_context cpu_openmp
//            const int64_t capacity = ParticlesData_get__capacity(particles);               //only_for_context cpu_openmp
//            const int64_t chunk_size = (capacity + num_threads - 1)/num_threads; // ceil division  //only_for_context cpu_openmp
//            #pragma omp parallel for                                                       //only_for_context cpu_openmp
//            for (int64_t batch_id = 0; batch_id < num_threads; batch_id++) {               //only_for_context cpu_openmp
                LocalParticle lpart;
                lpart.io_buffer = io_buffer;
//                int64_t part_id = batch_id * chunk_size;                                       //only_for_context cpu_openmp
//                int64_t end_id = (batch_id + 1) * chunk_size;                                  //only_for_context cpu_openmp
//                if (end_id > capacity) end_id = capacity;                                      //only_for_context cpu_openmp
    
                int64_t part_id = 0;                    //only_for_context cpu_serial
//                int64_t part_id = blockDim.x * blockIdx.x + threadIdx.x; //only_for_context cuda
//                int64_t part_id = get_global_id(0);                    //only_for_context opencl
                int64_t end_id = 0; // unused outside of openmp  //only_for_context cpu_serial cuda opencl
    
                int64_t part_capacity = ParticlesData_get__capacity(particles);
                if (part_id<part_capacity){
                    Particles_to_LocalParticle(particles, &lpart, part_id, end_id);
                    if (check_is_active(&lpart)>0){
              LimitRacetrack_track_local_particle(el, &lpart);

                    }
                    if (check_is_active(&lpart)>0 && flag_increment_at_element){
                            increment_at_element(&lpart, 1);
                    }
                }
//            } //only_for_context cpu_openmp
            
            // On OpenMP we want to additionally by default reorganize all
            // the particles.
//            #ifndef XT_OMP_SKIP_REORGANIZE                             //only_for_context cpu_openmp
//            LocalParticle lpart;                                       //only_for_context cpu_openmp
//            lpart.io_buffer = io_buffer;                               //only_for_context cpu_openmp
//            Particles_to_LocalParticle(particles, &lpart, 0, capacity);//only_for_context cpu_openmp
//            check_is_active(&lpart);                                   //only_for_context cpu_openmp
//            #endif                                                     //only_for_context cpu_openmp
        }

#ifndef XOBJ_TYPEDEF_LimitRectData
#define XOBJ_TYPEDEF_LimitRectData
typedef   struct LimitRectData_s * LimitRectData;
 static inline LimitRectData LimitRectData_getp(LimitRectData  obj){
  int64_t offset=0;
  return (LimitRectData)(( char*) obj+offset);
}
 static inline double LimitRectData_get_min_x(const LimitRectData  obj){
  int64_t offset=0;
  return *( double*)(( char*) obj+offset);
}
 static inline void LimitRectData_set_min_x(LimitRectData  obj, double value){
  int64_t offset=0;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LimitRectData_getp_min_x(LimitRectData  obj){
  int64_t offset=0;
  return ( double*)(( char*) obj+offset);
}
 static inline double LimitRectData_get_max_x(const LimitRectData  obj){
  int64_t offset=0;
  offset+=8;
  return *( double*)(( char*) obj+offset);
}
 static inline void LimitRectData_set_max_x(LimitRectData  obj, double value){
  int64_t offset=0;
  offset+=8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LimitRectData_getp_max_x(LimitRectData  obj){
  int64_t offset=0;
  offset+=8;
  return ( double*)(( char*) obj+offset);
}
 static inline double LimitRectData_get_min_y(const LimitRectData  obj){
  int64_t offset=0;
  offset+=16;
  return *( double*)(( char*) obj+offset);
}
 static inline void LimitRectData_set_min_y(LimitRectData  obj, double value){
  int64_t offset=0;
  offset+=16;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LimitRectData_getp_min_y(LimitRectData  obj){
  int64_t offset=0;
  offset+=16;
  return ( double*)(( char*) obj+offset);
}
 static inline double LimitRectData_get_max_y(const LimitRectData  obj){
  int64_t offset=0;
  offset+=24;
  return *( double*)(( char*) obj+offset);
}
 static inline void LimitRectData_set_max_y(LimitRectData  obj, double value){
  int64_t offset=0;
  offset+=24;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LimitRectData_getp_max_y(LimitRectData  obj){
  int64_t offset=0;
  offset+=24;
  return ( double*)(( char*) obj+offset);
}
#endif
// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_CONSTANTS_H
#define XTRACK_CONSTANTS_H

#if !defined( C_LIGHT )
    #define   C_LIGHT ( 299792458.0 )
#endif /* !defined( C_LIGHT ) */

#if !defined( EPSILON_0 )
    #define   EPSILON_0 (8.854187817620e-12)
#endif /* !defined( EPSILON_0 ) */

#if !defined( PI )
    #define PI (3.1415926535897932384626433832795028841971693993751)
#endif /* !defined( PI ) */

#if !defined( MU_0 )
    #define MU_0 (PI*4.0e-7)
#endif /* !defined( MU_0 ) */

#if !defined( DEG2RAD )
    #define DEG2RAD (0.0174532925199432957692369076848861271344287188854)
#endif /* !defiend( DEG2RAD ) */

#if !defined( RAD2DEG )
    #define RAD2DEG (57.29577951308232087679815481410517033240547246656442)
#endif /* !defiend( RAD2DEG ) */

#if !defined( SQRT_PI )
    #define SQRT_PI (1.7724538509055160272981674833411451827975494561224)
#endif /* !defined( SQRT_PI ) */

#if !defined( QELEM )
    #define QELEM (1.60217662e-19)
#endif /* !defined( QELEM ) */

#if !defined( DBL_MAX )
    #define DBL_MAX (1.7976931348623158e+308)
#endif /* !defined( DBL_MAX ) */

#if !defined( DBL_MIN )
    #define DBL_MIN (2.2250738585072014e-308)
#endif /* !defined( DBL_MIN ) */

#if !defined( DBL_EPSILON )
    #define DBL_EPSILON (2.2204460492503131e-16)
#endif /* !defined( DBL_EPSILON ) */

#endif /* XTRACK_CONSTANTS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_FUNCTIONS_H
#define XTRACK_FUNCTIONS_H

 static inline
void kill_all_particles(LocalParticle* part0, int64_t kill_state) {

    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

        LocalParticle_kill_particle(part, kill_state);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

}


 static inline
int8_t assert_tracking(LocalParticle* part, int64_t kill_state){
    // Whenever we are not tracking, e.g. in a twiss, the particle will be at_turn < 0.
    // We test this to distinguish genuine tracking from twiss.
    if (LocalParticle_get_at_turn(part) < 0){
        LocalParticle_kill_particle(part, kill_state);
        return 0;
    }
    return 1;
}

#endif /* XTRACK_FUNCTIONS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_PARTICLE_STATES_H
#define XTRACK_PARTICLE_STATES_H

#define  XT_LOST_ON_APERTURE       0
#define  XT_LOST_ON_LONG_CUT      -2
#define  XT_LOST_ALL_E_IN_SYNRAD -10
#define  RNG_ERR_SEEDS_NOT_SET   -20
#define  RNG_ERR_INVALID_TRACK   -21
#define  RNG_ERR_RUTH_NOT_SET    -22

#endif /* XTRACK_PARTICLE_STATES_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_LIMITRECT_H
#define XTRACK_LIMITRECT_H

 static inline
void LimitRect_track_local_particle(LimitRectData el, LocalParticle* part0){

    double const min_x = LimitRectData_get_min_x(el);
    double const max_x = LimitRectData_get_max_x(el);
    double const min_y = LimitRectData_get_min_y(el);
    double const max_y = LimitRectData_get_max_y(el);


    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp


        double const x = LocalParticle_get_x(part);
        double const y = LocalParticle_get_y(part);

	int64_t const is_alive = (int64_t)(
                      (x >= min_x) &&
		      (x <= max_x) &&
		      (y >= min_y) &&
		      (y <= max_y) );

	// I assume that if I am in the function is because
    	if (!is_alive){
           LocalParticle_set_state(part, XT_LOST_ON_APERTURE);
	}


//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }


}

#endif


             
            void LimitRect_track_particles(
               LimitRectData el,

                             ParticlesData particles,

                             int64_t flag_increment_at_element,
                  int8_t* io_buffer){
//            const int num_threads = omp_get_max_threads();                                 //only_for_context cpu_openmp
//            const int64_t capacity = ParticlesData_get__capacity(particles);               //only_for_context cpu_openmp
//            const int64_t chunk_size = (capacity + num_threads - 1)/num_threads; // ceil division  //only_for_context cpu_openmp
//            #pragma omp parallel for                                                       //only_for_context cpu_openmp
//            for (int64_t batch_id = 0; batch_id < num_threads; batch_id++) {               //only_for_context cpu_openmp
                LocalParticle lpart;
                lpart.io_buffer = io_buffer;
//                int64_t part_id = batch_id * chunk_size;                                       //only_for_context cpu_openmp
//                int64_t end_id = (batch_id + 1) * chunk_size;                                  //only_for_context cpu_openmp
//                if (end_id > capacity) end_id = capacity;                                      //only_for_context cpu_openmp
    
                int64_t part_id = 0;                    //only_for_context cpu_serial
//                int64_t part_id = blockDim.x * blockIdx.x + threadIdx.x; //only_for_context cuda
//                int64_t part_id = get_global_id(0);                    //only_for_context opencl
                int64_t end_id = 0; // unused outside of openmp  //only_for_context cpu_serial cuda opencl
    
                int64_t part_capacity = ParticlesData_get__capacity(particles);
                if (part_id<part_capacity){
                    Particles_to_LocalParticle(particles, &lpart, part_id, end_id);
                    if (check_is_active(&lpart)>0){
              LimitRect_track_local_particle(el, &lpart);

                    }
                    if (check_is_active(&lpart)>0 && flag_increment_at_element){
                            increment_at_element(&lpart, 1);
                    }
                }
//            } //only_for_context cpu_openmp
            
            // On OpenMP we want to additionally by default reorganize all
            // the particles.
//            #ifndef XT_OMP_SKIP_REORGANIZE                             //only_for_context cpu_openmp
//            LocalParticle lpart;                                       //only_for_context cpu_openmp
//            lpart.io_buffer = io_buffer;                               //only_for_context cpu_openmp
//            Particles_to_LocalParticle(particles, &lpart, 0, capacity);//only_for_context cpu_openmp
//            check_is_active(&lpart);                                   //only_for_context cpu_openmp
//            #endif                                                     //only_for_context cpu_openmp
        }

#ifndef XOBJ_TYPEDEF_LimitRectEllipseData
#define XOBJ_TYPEDEF_LimitRectEllipseData
typedef   struct LimitRectEllipseData_s * LimitRectEllipseData;
 static inline LimitRectEllipseData LimitRectEllipseData_getp(LimitRectEllipseData  obj){
  int64_t offset=0;
  return (LimitRectEllipseData)(( char*) obj+offset);
}
 static inline double LimitRectEllipseData_get_max_x(const LimitRectEllipseData  obj){
  int64_t offset=0;
  return *( double*)(( char*) obj+offset);
}
 static inline void LimitRectEllipseData_set_max_x(LimitRectEllipseData  obj, double value){
  int64_t offset=0;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LimitRectEllipseData_getp_max_x(LimitRectEllipseData  obj){
  int64_t offset=0;
  return ( double*)(( char*) obj+offset);
}
 static inline double LimitRectEllipseData_get_max_y(const LimitRectEllipseData  obj){
  int64_t offset=0;
  offset+=8;
  return *( double*)(( char*) obj+offset);
}
 static inline void LimitRectEllipseData_set_max_y(LimitRectEllipseData  obj, double value){
  int64_t offset=0;
  offset+=8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LimitRectEllipseData_getp_max_y(LimitRectEllipseData  obj){
  int64_t offset=0;
  offset+=8;
  return ( double*)(( char*) obj+offset);
}
 static inline double LimitRectEllipseData_get_a_squ(const LimitRectEllipseData  obj){
  int64_t offset=0;
  offset+=16;
  return *( double*)(( char*) obj+offset);
}
 static inline void LimitRectEllipseData_set_a_squ(LimitRectEllipseData  obj, double value){
  int64_t offset=0;
  offset+=16;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LimitRectEllipseData_getp_a_squ(LimitRectEllipseData  obj){
  int64_t offset=0;
  offset+=16;
  return ( double*)(( char*) obj+offset);
}
 static inline double LimitRectEllipseData_get_b_squ(const LimitRectEllipseData  obj){
  int64_t offset=0;
  offset+=24;
  return *( double*)(( char*) obj+offset);
}
 static inline void LimitRectEllipseData_set_b_squ(LimitRectEllipseData  obj, double value){
  int64_t offset=0;
  offset+=24;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LimitRectEllipseData_getp_b_squ(LimitRectEllipseData  obj){
  int64_t offset=0;
  offset+=24;
  return ( double*)(( char*) obj+offset);
}
 static inline double LimitRectEllipseData_get_a_b_squ(const LimitRectEllipseData  obj){
  int64_t offset=0;
  offset+=32;
  return *( double*)(( char*) obj+offset);
}
 static inline void LimitRectEllipseData_set_a_b_squ(LimitRectEllipseData  obj, double value){
  int64_t offset=0;
  offset+=32;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LimitRectEllipseData_getp_a_b_squ(LimitRectEllipseData  obj){
  int64_t offset=0;
  offset+=32;
  return ( double*)(( char*) obj+offset);
}
#endif
// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_CONSTANTS_H
#define XTRACK_CONSTANTS_H

#if !defined( C_LIGHT )
    #define   C_LIGHT ( 299792458.0 )
#endif /* !defined( C_LIGHT ) */

#if !defined( EPSILON_0 )
    #define   EPSILON_0 (8.854187817620e-12)
#endif /* !defined( EPSILON_0 ) */

#if !defined( PI )
    #define PI (3.1415926535897932384626433832795028841971693993751)
#endif /* !defined( PI ) */

#if !defined( MU_0 )
    #define MU_0 (PI*4.0e-7)
#endif /* !defined( MU_0 ) */

#if !defined( DEG2RAD )
    #define DEG2RAD (0.0174532925199432957692369076848861271344287188854)
#endif /* !defiend( DEG2RAD ) */

#if !defined( RAD2DEG )
    #define RAD2DEG (57.29577951308232087679815481410517033240547246656442)
#endif /* !defiend( RAD2DEG ) */

#if !defined( SQRT_PI )
    #define SQRT_PI (1.7724538509055160272981674833411451827975494561224)
#endif /* !defined( SQRT_PI ) */

#if !defined( QELEM )
    #define QELEM (1.60217662e-19)
#endif /* !defined( QELEM ) */

#if !defined( DBL_MAX )
    #define DBL_MAX (1.7976931348623158e+308)
#endif /* !defined( DBL_MAX ) */

#if !defined( DBL_MIN )
    #define DBL_MIN (2.2250738585072014e-308)
#endif /* !defined( DBL_MIN ) */

#if !defined( DBL_EPSILON )
    #define DBL_EPSILON (2.2204460492503131e-16)
#endif /* !defined( DBL_EPSILON ) */

#endif /* XTRACK_CONSTANTS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_FUNCTIONS_H
#define XTRACK_FUNCTIONS_H

 static inline
void kill_all_particles(LocalParticle* part0, int64_t kill_state) {

    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

        LocalParticle_kill_particle(part, kill_state);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

}


 static inline
int8_t assert_tracking(LocalParticle* part, int64_t kill_state){
    // Whenever we are not tracking, e.g. in a twiss, the particle will be at_turn < 0.
    // We test this to distinguish genuine tracking from twiss.
    if (LocalParticle_get_at_turn(part) < 0){
        LocalParticle_kill_particle(part, kill_state);
        return 0;
    }
    return 1;
}

#endif /* XTRACK_FUNCTIONS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_PARTICLE_STATES_H
#define XTRACK_PARTICLE_STATES_H

#define  XT_LOST_ON_APERTURE       0
#define  XT_LOST_ON_LONG_CUT      -2
#define  XT_LOST_ALL_E_IN_SYNRAD -10
#define  RNG_ERR_SEEDS_NOT_SET   -20
#define  RNG_ERR_INVALID_TRACK   -21
#define  RNG_ERR_RUTH_NOT_SET    -22

#endif /* XTRACK_PARTICLE_STATES_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_LIMITRECTELLIPSE_H
#define XTRACK_LIMITRECTELLIPSE_H

 static inline
void LimitRectEllipse_track_local_particle(LimitRectEllipseData el, LocalParticle* part0){

    double const max_x = LimitRectEllipseData_get_max_x(el);
    double const max_y = LimitRectEllipseData_get_max_y(el);
    double const a_squ = LimitRectEllipseData_get_a_squ(el);
    double const b_squ = LimitRectEllipseData_get_b_squ(el);
    double const a_b_squ = LimitRectEllipseData_get_a_b_squ(el);
    
    

    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

        double const x = LocalParticle_get_x(part);
        double const y = LocalParticle_get_y(part);

	double const temp = x*x*b_squ + y*y*a_squ;

	int64_t const is_alive = (int64_t)(
				   ( x <= max_x) &&
				   ( x >= -max_x) &&
				   ( y <=  max_y) &&
				   ( y >= -max_y) &&
				   ( temp <= a_b_squ) );

	// I assume that if I am in the function is because
    	if (!is_alive){
           LocalParticle_set_state(part, XT_LOST_ON_APERTURE);
	}


//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }


}

#endif


             
            void LimitRectEllipse_track_particles(
               LimitRectEllipseData el,

                             ParticlesData particles,

                             int64_t flag_increment_at_element,
                  int8_t* io_buffer){
//            const int num_threads = omp_get_max_threads();                                 //only_for_context cpu_openmp
//            const int64_t capacity = ParticlesData_get__capacity(particles);               //only_for_context cpu_openmp
//            const int64_t chunk_size = (capacity + num_threads - 1)/num_threads; // ceil division  //only_for_context cpu_openmp
//            #pragma omp parallel for                                                       //only_for_context cpu_openmp
//            for (int64_t batch_id = 0; batch_id < num_threads; batch_id++) {               //only_for_context cpu_openmp
                LocalParticle lpart;
                lpart.io_buffer = io_buffer;
//                int64_t part_id = batch_id * chunk_size;                                       //only_for_context cpu_openmp
//                int64_t end_id = (batch_id + 1) * chunk_size;                                  //only_for_context cpu_openmp
//                if (end_id > capacity) end_id = capacity;                                      //only_for_context cpu_openmp
    
                int64_t part_id = 0;                    //only_for_context cpu_serial
//                int64_t part_id = blockDim.x * blockIdx.x + threadIdx.x; //only_for_context cuda
//                int64_t part_id = get_global_id(0);                    //only_for_context opencl
                int64_t end_id = 0; // unused outside of openmp  //only_for_context cpu_serial cuda opencl
    
                int64_t part_capacity = ParticlesData_get__capacity(particles);
                if (part_id<part_capacity){
                    Particles_to_LocalParticle(particles, &lpart, part_id, end_id);
                    if (check_is_active(&lpart)>0){
              LimitRectEllipse_track_local_particle(el, &lpart);

                    }
                    if (check_is_active(&lpart)>0 && flag_increment_at_element){
                            increment_at_element(&lpart, 1);
                    }
                }
//            } //only_for_context cpu_openmp
            
            // On OpenMP we want to additionally by default reorganize all
            // the particles.
//            #ifndef XT_OMP_SKIP_REORGANIZE                             //only_for_context cpu_openmp
//            LocalParticle lpart;                                       //only_for_context cpu_openmp
//            lpart.io_buffer = io_buffer;                               //only_for_context cpu_openmp
//            Particles_to_LocalParticle(particles, &lpart, 0, capacity);//only_for_context cpu_openmp
//            check_is_active(&lpart);                                   //only_for_context cpu_openmp
//            #endif                                                     //only_for_context cpu_openmp
        }

#ifndef XOBJ_TYPEDEF_LongitudinalLimitRectData
#define XOBJ_TYPEDEF_LongitudinalLimitRectData
typedef   struct LongitudinalLimitRectData_s * LongitudinalLimitRectData;
 static inline LongitudinalLimitRectData LongitudinalLimitRectData_getp(LongitudinalLimitRectData  obj){
  int64_t offset=0;
  return (LongitudinalLimitRectData)(( char*) obj+offset);
}
 static inline double LongitudinalLimitRectData_get_min_zeta(const LongitudinalLimitRectData  obj){
  int64_t offset=0;
  return *( double*)(( char*) obj+offset);
}
 static inline void LongitudinalLimitRectData_set_min_zeta(LongitudinalLimitRectData  obj, double value){
  int64_t offset=0;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LongitudinalLimitRectData_getp_min_zeta(LongitudinalLimitRectData  obj){
  int64_t offset=0;
  return ( double*)(( char*) obj+offset);
}
 static inline double LongitudinalLimitRectData_get_max_zeta(const LongitudinalLimitRectData  obj){
  int64_t offset=0;
  offset+=8;
  return *( double*)(( char*) obj+offset);
}
 static inline void LongitudinalLimitRectData_set_max_zeta(LongitudinalLimitRectData  obj, double value){
  int64_t offset=0;
  offset+=8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LongitudinalLimitRectData_getp_max_zeta(LongitudinalLimitRectData  obj){
  int64_t offset=0;
  offset+=8;
  return ( double*)(( char*) obj+offset);
}
 static inline double LongitudinalLimitRectData_get_min_pzeta(const LongitudinalLimitRectData  obj){
  int64_t offset=0;
  offset+=16;
  return *( double*)(( char*) obj+offset);
}
 static inline void LongitudinalLimitRectData_set_min_pzeta(LongitudinalLimitRectData  obj, double value){
  int64_t offset=0;
  offset+=16;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LongitudinalLimitRectData_getp_min_pzeta(LongitudinalLimitRectData  obj){
  int64_t offset=0;
  offset+=16;
  return ( double*)(( char*) obj+offset);
}
 static inline double LongitudinalLimitRectData_get_max_pzeta(const LongitudinalLimitRectData  obj){
  int64_t offset=0;
  offset+=24;
  return *( double*)(( char*) obj+offset);
}
 static inline void LongitudinalLimitRectData_set_max_pzeta(LongitudinalLimitRectData  obj, double value){
  int64_t offset=0;
  offset+=24;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LongitudinalLimitRectData_getp_max_pzeta(LongitudinalLimitRectData  obj){
  int64_t offset=0;
  offset+=24;
  return ( double*)(( char*) obj+offset);
}
#endif
// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_CONSTANTS_H
#define XTRACK_CONSTANTS_H

#if !defined( C_LIGHT )
    #define   C_LIGHT ( 299792458.0 )
#endif /* !defined( C_LIGHT ) */

#if !defined( EPSILON_0 )
    #define   EPSILON_0 (8.854187817620e-12)
#endif /* !defined( EPSILON_0 ) */

#if !defined( PI )
    #define PI (3.1415926535897932384626433832795028841971693993751)
#endif /* !defined( PI ) */

#if !defined( MU_0 )
    #define MU_0 (PI*4.0e-7)
#endif /* !defined( MU_0 ) */

#if !defined( DEG2RAD )
    #define DEG2RAD (0.0174532925199432957692369076848861271344287188854)
#endif /* !defiend( DEG2RAD ) */

#if !defined( RAD2DEG )
    #define RAD2DEG (57.29577951308232087679815481410517033240547246656442)
#endif /* !defiend( RAD2DEG ) */

#if !defined( SQRT_PI )
    #define SQRT_PI (1.7724538509055160272981674833411451827975494561224)
#endif /* !defined( SQRT_PI ) */

#if !defined( QELEM )
    #define QELEM (1.60217662e-19)
#endif /* !defined( QELEM ) */

#if !defined( DBL_MAX )
    #define DBL_MAX (1.7976931348623158e+308)
#endif /* !defined( DBL_MAX ) */

#if !defined( DBL_MIN )
    #define DBL_MIN (2.2250738585072014e-308)
#endif /* !defined( DBL_MIN ) */

#if !defined( DBL_EPSILON )
    #define DBL_EPSILON (2.2204460492503131e-16)
#endif /* !defined( DBL_EPSILON ) */

#endif /* XTRACK_CONSTANTS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_FUNCTIONS_H
#define XTRACK_FUNCTIONS_H

 static inline
void kill_all_particles(LocalParticle* part0, int64_t kill_state) {

    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

        LocalParticle_kill_particle(part, kill_state);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

}


 static inline
int8_t assert_tracking(LocalParticle* part, int64_t kill_state){
    // Whenever we are not tracking, e.g. in a twiss, the particle will be at_turn < 0.
    // We test this to distinguish genuine tracking from twiss.
    if (LocalParticle_get_at_turn(part) < 0){
        LocalParticle_kill_particle(part, kill_state);
        return 0;
    }
    return 1;
}

#endif /* XTRACK_FUNCTIONS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_PARTICLE_STATES_H
#define XTRACK_PARTICLE_STATES_H

#define  XT_LOST_ON_APERTURE       0
#define  XT_LOST_ON_LONG_CUT      -2
#define  XT_LOST_ALL_E_IN_SYNRAD -10
#define  RNG_ERR_SEEDS_NOT_SET   -20
#define  RNG_ERR_INVALID_TRACK   -21
#define  RNG_ERR_RUTH_NOT_SET    -22

#endif /* XTRACK_PARTICLE_STATES_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_LONGITUDINALLIMITRECT_H
#define XTRACK_LONGITUDINALLIMITRECT_H

 static inline
void LongitudinalLimitRect_track_local_particle(LongitudinalLimitRectData el, LocalParticle* part0){

    double const min_zeta = LongitudinalLimitRectData_get_min_zeta(el);
    double const max_zeta = LongitudinalLimitRectData_get_max_zeta(el);
    double const min_pzeta = LongitudinalLimitRectData_get_min_pzeta(el);
    double const max_pzeta = LongitudinalLimitRectData_get_max_pzeta(el);


    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp


        double const zeta = LocalParticle_get_zeta(part);
        double const pzeta = LocalParticle_get_pzeta(part);

	int64_t const is_alive = (int64_t)(
                      (zeta >= min_zeta) &&
		      (zeta <= max_zeta) &&
		      (pzeta >= min_pzeta) &&
		      (pzeta <= max_pzeta) );

	// I assume that if I am in the function is because
    	if (!is_alive){
           LocalParticle_set_state(part, XT_LOST_ON_LONG_CUT);
	}


//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }


}

#endif


             
            void LongitudinalLimitRect_track_particles(
               LongitudinalLimitRectData el,

                             ParticlesData particles,

                             int64_t flag_increment_at_element,
                  int8_t* io_buffer){
//            const int num_threads = omp_get_max_threads();                                 //only_for_context cpu_openmp
//            const int64_t capacity = ParticlesData_get__capacity(particles);               //only_for_context cpu_openmp
//            const int64_t chunk_size = (capacity + num_threads - 1)/num_threads; // ceil division  //only_for_context cpu_openmp
//            #pragma omp parallel for                                                       //only_for_context cpu_openmp
//            for (int64_t batch_id = 0; batch_id < num_threads; batch_id++) {               //only_for_context cpu_openmp
                LocalParticle lpart;
                lpart.io_buffer = io_buffer;
//                int64_t part_id = batch_id * chunk_size;                                       //only_for_context cpu_openmp
//                int64_t end_id = (batch_id + 1) * chunk_size;                                  //only_for_context cpu_openmp
//                if (end_id > capacity) end_id = capacity;                                      //only_for_context cpu_openmp
    
                int64_t part_id = 0;                    //only_for_context cpu_serial
//                int64_t part_id = blockDim.x * blockIdx.x + threadIdx.x; //only_for_context cuda
//                int64_t part_id = get_global_id(0);                    //only_for_context opencl
                int64_t end_id = 0; // unused outside of openmp  //only_for_context cpu_serial cuda opencl
    
                int64_t part_capacity = ParticlesData_get__capacity(particles);
                if (part_id<part_capacity){
                    Particles_to_LocalParticle(particles, &lpart, part_id, end_id);
                    if (check_is_active(&lpart)>0){
              LongitudinalLimitRect_track_local_particle(el, &lpart);

                    }
                    if (check_is_active(&lpart)>0 && flag_increment_at_element){
                            increment_at_element(&lpart, 1);
                    }
                }
//            } //only_for_context cpu_openmp
            
            // On OpenMP we want to additionally by default reorganize all
            // the particles.
//            #ifndef XT_OMP_SKIP_REORGANIZE                             //only_for_context cpu_openmp
//            LocalParticle lpart;                                       //only_for_context cpu_openmp
//            lpart.io_buffer = io_buffer;                               //only_for_context cpu_openmp
//            Particles_to_LocalParticle(particles, &lpart, 0, capacity);//only_for_context cpu_openmp
//            check_is_active(&lpart);                                   //only_for_context cpu_openmp
//            #endif                                                     //only_for_context cpu_openmp
        }

#ifndef XOBJ_TYPEDEF_MarkerData
#define XOBJ_TYPEDEF_MarkerData
typedef   struct MarkerData_s * MarkerData;
 static inline MarkerData MarkerData_getp(MarkerData  obj){
  int64_t offset=0;
  return (MarkerData)(( char*) obj+offset);
}
 static inline int64_t MarkerData_get__dummy(const MarkerData  obj){
  int64_t offset=0;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void MarkerData_set__dummy(MarkerData  obj, int64_t value){
  int64_t offset=0;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* MarkerData_getp__dummy(MarkerData  obj){
  int64_t offset=0;
  return ( int64_t*)(( char*) obj+offset);
}
#endif
// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_CONSTANTS_H
#define XTRACK_CONSTANTS_H

#if !defined( C_LIGHT )
    #define   C_LIGHT ( 299792458.0 )
#endif /* !defined( C_LIGHT ) */

#if !defined( EPSILON_0 )
    #define   EPSILON_0 (8.854187817620e-12)
#endif /* !defined( EPSILON_0 ) */

#if !defined( PI )
    #define PI (3.1415926535897932384626433832795028841971693993751)
#endif /* !defined( PI ) */

#if !defined( MU_0 )
    #define MU_0 (PI*4.0e-7)
#endif /* !defined( MU_0 ) */

#if !defined( DEG2RAD )
    #define DEG2RAD (0.0174532925199432957692369076848861271344287188854)
#endif /* !defiend( DEG2RAD ) */

#if !defined( RAD2DEG )
    #define RAD2DEG (57.29577951308232087679815481410517033240547246656442)
#endif /* !defiend( RAD2DEG ) */

#if !defined( SQRT_PI )
    #define SQRT_PI (1.7724538509055160272981674833411451827975494561224)
#endif /* !defined( SQRT_PI ) */

#if !defined( QELEM )
    #define QELEM (1.60217662e-19)
#endif /* !defined( QELEM ) */

#if !defined( DBL_MAX )
    #define DBL_MAX (1.7976931348623158e+308)
#endif /* !defined( DBL_MAX ) */

#if !defined( DBL_MIN )
    #define DBL_MIN (2.2250738585072014e-308)
#endif /* !defined( DBL_MIN ) */

#if !defined( DBL_EPSILON )
    #define DBL_EPSILON (2.2204460492503131e-16)
#endif /* !defined( DBL_EPSILON ) */

#endif /* XTRACK_CONSTANTS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_FUNCTIONS_H
#define XTRACK_FUNCTIONS_H

 static inline
void kill_all_particles(LocalParticle* part0, int64_t kill_state) {

    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

        LocalParticle_kill_particle(part, kill_state);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

}


 static inline
int8_t assert_tracking(LocalParticle* part, int64_t kill_state){
    // Whenever we are not tracking, e.g. in a twiss, the particle will be at_turn < 0.
    // We test this to distinguish genuine tracking from twiss.
    if (LocalParticle_get_at_turn(part) < 0){
        LocalParticle_kill_particle(part, kill_state);
        return 0;
    }
    return 1;
}

#endif /* XTRACK_FUNCTIONS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_PARTICLE_STATES_H
#define XTRACK_PARTICLE_STATES_H

#define  XT_LOST_ON_APERTURE       0
#define  XT_LOST_ON_LONG_CUT      -2
#define  XT_LOST_ALL_E_IN_SYNRAD -10
#define  RNG_ERR_SEEDS_NOT_SET   -20
#define  RNG_ERR_INVALID_TRACK   -21
#define  RNG_ERR_RUTH_NOT_SET    -22

#endif /* XTRACK_PARTICLE_STATES_H */

 static inline
void Marker_track_local_particle(MarkerData el, LocalParticle* part0){}

             
            void Marker_track_particles(
               MarkerData el,

                             ParticlesData particles,

                             int64_t flag_increment_at_element,
                  int8_t* io_buffer){
//            const int num_threads = omp_get_max_threads();                                 //only_for_context cpu_openmp
//            const int64_t capacity = ParticlesData_get__capacity(particles);               //only_for_context cpu_openmp
//            const int64_t chunk_size = (capacity + num_threads - 1)/num_threads; // ceil division  //only_for_context cpu_openmp
//            #pragma omp parallel for                                                       //only_for_context cpu_openmp
//            for (int64_t batch_id = 0; batch_id < num_threads; batch_id++) {               //only_for_context cpu_openmp
                LocalParticle lpart;
                lpart.io_buffer = io_buffer;
//                int64_t part_id = batch_id * chunk_size;                                       //only_for_context cpu_openmp
//                int64_t end_id = (batch_id + 1) * chunk_size;                                  //only_for_context cpu_openmp
//                if (end_id > capacity) end_id = capacity;                                      //only_for_context cpu_openmp
    
                int64_t part_id = 0;                    //only_for_context cpu_serial
//                int64_t part_id = blockDim.x * blockIdx.x + threadIdx.x; //only_for_context cuda
//                int64_t part_id = get_global_id(0);                    //only_for_context opencl
                int64_t end_id = 0; // unused outside of openmp  //only_for_context cpu_serial cuda opencl
    
                int64_t part_capacity = ParticlesData_get__capacity(particles);
                if (part_id<part_capacity){
                    Particles_to_LocalParticle(particles, &lpart, part_id, end_id);
                    if (check_is_active(&lpart)>0){
              Marker_track_local_particle(el, &lpart);

                    }
                    if (check_is_active(&lpart)>0 && flag_increment_at_element){
                            increment_at_element(&lpart, 1);
                    }
                }
//            } //only_for_context cpu_openmp
            
            // On OpenMP we want to additionally by default reorganize all
            // the particles.
//            #ifndef XT_OMP_SKIP_REORGANIZE                             //only_for_context cpu_openmp
//            LocalParticle lpart;                                       //only_for_context cpu_openmp
//            lpart.io_buffer = io_buffer;                               //only_for_context cpu_openmp
//            Particles_to_LocalParticle(particles, &lpart, 0, capacity);//only_for_context cpu_openmp
//            check_is_active(&lpart);                                   //only_for_context cpu_openmp
//            #endif                                                     //only_for_context cpu_openmp
        }

#ifndef XOBJ_TYPEDEF_NonLinearLensData
#define XOBJ_TYPEDEF_NonLinearLensData
typedef   struct NonLinearLensData_s * NonLinearLensData;
 static inline NonLinearLensData NonLinearLensData_getp(NonLinearLensData  obj){
  int64_t offset=0;
  return (NonLinearLensData)(( char*) obj+offset);
}
 static inline double NonLinearLensData_get_knll(const NonLinearLensData  obj){
  int64_t offset=0;
  return *( double*)(( char*) obj+offset);
}
 static inline void NonLinearLensData_set_knll(NonLinearLensData  obj, double value){
  int64_t offset=0;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* NonLinearLensData_getp_knll(NonLinearLensData  obj){
  int64_t offset=0;
  return ( double*)(( char*) obj+offset);
}
 static inline double NonLinearLensData_get_cnll(const NonLinearLensData  obj){
  int64_t offset=0;
  offset+=8;
  return *( double*)(( char*) obj+offset);
}
 static inline void NonLinearLensData_set_cnll(NonLinearLensData  obj, double value){
  int64_t offset=0;
  offset+=8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* NonLinearLensData_getp_cnll(NonLinearLensData  obj){
  int64_t offset=0;
  offset+=8;
  return ( double*)(( char*) obj+offset);
}
#endif
// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_CONSTANTS_H
#define XTRACK_CONSTANTS_H

#if !defined( C_LIGHT )
    #define   C_LIGHT ( 299792458.0 )
#endif /* !defined( C_LIGHT ) */

#if !defined( EPSILON_0 )
    #define   EPSILON_0 (8.854187817620e-12)
#endif /* !defined( EPSILON_0 ) */

#if !defined( PI )
    #define PI (3.1415926535897932384626433832795028841971693993751)
#endif /* !defined( PI ) */

#if !defined( MU_0 )
    #define MU_0 (PI*4.0e-7)
#endif /* !defined( MU_0 ) */

#if !defined( DEG2RAD )
    #define DEG2RAD (0.0174532925199432957692369076848861271344287188854)
#endif /* !defiend( DEG2RAD ) */

#if !defined( RAD2DEG )
    #define RAD2DEG (57.29577951308232087679815481410517033240547246656442)
#endif /* !defiend( RAD2DEG ) */

#if !defined( SQRT_PI )
    #define SQRT_PI (1.7724538509055160272981674833411451827975494561224)
#endif /* !defined( SQRT_PI ) */

#if !defined( QELEM )
    #define QELEM (1.60217662e-19)
#endif /* !defined( QELEM ) */

#if !defined( DBL_MAX )
    #define DBL_MAX (1.7976931348623158e+308)
#endif /* !defined( DBL_MAX ) */

#if !defined( DBL_MIN )
    #define DBL_MIN (2.2250738585072014e-308)
#endif /* !defined( DBL_MIN ) */

#if !defined( DBL_EPSILON )
    #define DBL_EPSILON (2.2204460492503131e-16)
#endif /* !defined( DBL_EPSILON ) */

#endif /* XTRACK_CONSTANTS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_FUNCTIONS_H
#define XTRACK_FUNCTIONS_H

 static inline
void kill_all_particles(LocalParticle* part0, int64_t kill_state) {

    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

        LocalParticle_kill_particle(part, kill_state);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

}


 static inline
int8_t assert_tracking(LocalParticle* part, int64_t kill_state){
    // Whenever we are not tracking, e.g. in a twiss, the particle will be at_turn < 0.
    // We test this to distinguish genuine tracking from twiss.
    if (LocalParticle_get_at_turn(part) < 0){
        LocalParticle_kill_particle(part, kill_state);
        return 0;
    }
    return 1;
}

#endif /* XTRACK_FUNCTIONS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_PARTICLE_STATES_H
#define XTRACK_PARTICLE_STATES_H

#define  XT_LOST_ON_APERTURE       0
#define  XT_LOST_ON_LONG_CUT      -2
#define  XT_LOST_ALL_E_IN_SYNRAD -10
#define  RNG_ERR_SEEDS_NOT_SET   -20
#define  RNG_ERR_INVALID_TRACK   -21
#define  RNG_ERR_RUTH_NOT_SET    -22

#endif /* XTRACK_PARTICLE_STATES_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_CONSTANTS_H
#define XTRACK_CONSTANTS_H

#if !defined( C_LIGHT )
    #define   C_LIGHT ( 299792458.0 )
#endif /* !defined( C_LIGHT ) */

#if !defined( EPSILON_0 )
    #define   EPSILON_0 (8.854187817620e-12)
#endif /* !defined( EPSILON_0 ) */

#if !defined( PI )
    #define PI (3.1415926535897932384626433832795028841971693993751)
#endif /* !defined( PI ) */

#if !defined( MU_0 )
    #define MU_0 (PI*4.0e-7)
#endif /* !defined( MU_0 ) */

#if !defined( DEG2RAD )
    #define DEG2RAD (0.0174532925199432957692369076848861271344287188854)
#endif /* !defiend( DEG2RAD ) */

#if !defined( RAD2DEG )
    #define RAD2DEG (57.29577951308232087679815481410517033240547246656442)
#endif /* !defiend( RAD2DEG ) */

#if !defined( SQRT_PI )
    #define SQRT_PI (1.7724538509055160272981674833411451827975494561224)
#endif /* !defined( SQRT_PI ) */

#if !defined( QELEM )
    #define QELEM (1.60217662e-19)
#endif /* !defined( QELEM ) */

#if !defined( DBL_MAX )
    #define DBL_MAX (1.7976931348623158e+308)
#endif /* !defined( DBL_MAX ) */

#if !defined( DBL_MIN )
    #define DBL_MIN (2.2250738585072014e-308)
#endif /* !defined( DBL_MIN ) */

#if !defined( DBL_EPSILON )
    #define DBL_EPSILON (2.2204460492503131e-16)
#endif /* !defined( DBL_EPSILON ) */

#endif /* XTRACK_CONSTANTS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_NONLINEARLENS_H
#define XTRACK_NONLINEARLENS_H

#ifndef POW2
#define POW2(X) ((X)*(X))
#endif

// Implementation of a non-linear lens with elliptic potential
// (based on the corresponding element in MAD-X, reference: Danilov and Nagaitsev,
// https://journals.aps.org/prab/abstract/10.1103/PhysRevSTAB.13.084002)

 static inline
void NonLinearLens_track_local_particle(
        NonLinearLensData el,
        LocalParticle* part0
) {

    double const cnll = NonLinearLensData_get_cnll(el);
    double const knll = NonLinearLensData_get_knll(el) / cnll;


    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp


        double const x = LocalParticle_get_x(part) / cnll;
        double const y = LocalParticle_get_y(part) / cnll;

        double const u = 0.5 * sqrt(POW2(x - 1.) + POW2(y)) + 0.5 * sqrt(POW2(x + 1.) + POW2(y));
        double const v = 0.5 * sqrt(POW2(x + 1.) + POW2(y)) - 0.5 * sqrt(POW2(x - 1.) + POW2(y));

        double dd;
        if (u == 1.){
            dd = 0.;
        }
        else {
            dd = POW2(u) * log(u + sqrt(POW2(u) - 1.0)) / sqrt(POW2(u) - 1.0);
        }

        double const dUu =
            (u + log(u + sqrt(u*u - 1.0)) * sqrt(POW2(u) - 1.) + dd)/(POW2(u) - POW2(v))
            - 2. * u * (u * log(u + sqrt(u*u - 1.)) * sqrt(POW2(u) - 1.)
            + v * (acos(v) - PI / 2.) * sqrt(1. - POW2(v))) /POW2(POW2(u) - POW2(v));
        double const dUv =
            2. * v * (u * log(u + sqrt(u*u - 1.)) * sqrt(POW2(u) - 1.)
            + v * (acos(v) - PI/2.) * sqrt(1. - POW2(v))) / POW2(POW2(u) - POW2(v))
            - (v - (acos(v)- PI/2.) * sqrt(1. - POW2(v)) + POW2(v) * (acos(v) - PI / 2.) / sqrt(1. - POW2(v)))
            / (POW2(u) - POW2(v));

        double const dux = 0.5 * (x - 1.) / sqrt(POW2(x - 1.) + POW2(y)) + 0.5 * (x + 1.) / sqrt(POW2(x + 1.) + POW2(y));
        double const duy = 0.5 * y / sqrt(POW2(x - 1.) + POW2(y)) + 0.5 * y / sqrt(POW2(x + 1.) + POW2(y));
        double const dvx = 0.5 * (x + 1.) / sqrt(POW2(x + 1.) + POW2(y)) - 0.5 * (x - 1.) / sqrt(POW2(x - 1.) + POW2(y));
        double const dvy = 0.5 * y / sqrt(POW2(x + 1.) + POW2(y)) - 0.5 * y / sqrt(POW2(x - 1.) + POW2(y));

        LocalParticle_add_to_px(part, knll * (dUu * dux + dUv * dvx));
        LocalParticle_add_to_py(part, knll * (dUu * duy + dUv * dvy));


//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }


}


#endif // XTRACK_NONLINEARLENS_H

             
            void NonLinearLens_track_particles(
               NonLinearLensData el,

                             ParticlesData particles,

                             int64_t flag_increment_at_element,
                  int8_t* io_buffer){
//            const int num_threads = omp_get_max_threads();                                 //only_for_context cpu_openmp
//            const int64_t capacity = ParticlesData_get__capacity(particles);               //only_for_context cpu_openmp
//            const int64_t chunk_size = (capacity + num_threads - 1)/num_threads; // ceil division  //only_for_context cpu_openmp
//            #pragma omp parallel for                                                       //only_for_context cpu_openmp
//            for (int64_t batch_id = 0; batch_id < num_threads; batch_id++) {               //only_for_context cpu_openmp
                LocalParticle lpart;
                lpart.io_buffer = io_buffer;
//                int64_t part_id = batch_id * chunk_size;                                       //only_for_context cpu_openmp
//                int64_t end_id = (batch_id + 1) * chunk_size;                                  //only_for_context cpu_openmp
//                if (end_id > capacity) end_id = capacity;                                      //only_for_context cpu_openmp
    
                int64_t part_id = 0;                    //only_for_context cpu_serial
//                int64_t part_id = blockDim.x * blockIdx.x + threadIdx.x; //only_for_context cuda
//                int64_t part_id = get_global_id(0);                    //only_for_context opencl
                int64_t end_id = 0; // unused outside of openmp  //only_for_context cpu_serial cuda opencl
    
                int64_t part_capacity = ParticlesData_get__capacity(particles);
                if (part_id<part_capacity){
                    Particles_to_LocalParticle(particles, &lpart, part_id, end_id);
                    if (check_is_active(&lpart)>0){
              NonLinearLens_track_local_particle(el, &lpart);

                    }
                    if (check_is_active(&lpart)>0 && flag_increment_at_element){
                            increment_at_element(&lpart, 1);
                    }
                }
//            } //only_for_context cpu_openmp
            
            // On OpenMP we want to additionally by default reorganize all
            // the particles.
//            #ifndef XT_OMP_SKIP_REORGANIZE                             //only_for_context cpu_openmp
//            LocalParticle lpart;                                       //only_for_context cpu_openmp
//            lpart.io_buffer = io_buffer;                               //only_for_context cpu_openmp
//            Particles_to_LocalParticle(particles, &lpart, 0, capacity);//only_for_context cpu_openmp
//            check_is_active(&lpart);                                   //only_for_context cpu_openmp
//            #endif                                                     //only_for_context cpu_openmp
        }

#ifndef XOBJ_TYPEDEF_ParticlesMonitorData
#define XOBJ_TYPEDEF_ParticlesMonitorData
typedef   struct ParticlesMonitorData_s * ParticlesMonitorData;
 static inline ParticlesMonitorData ParticlesMonitorData_getp(ParticlesMonitorData  obj){
  int64_t offset=0;
  return (ParticlesMonitorData)(( char*) obj+offset);
}
 static inline int64_t ParticlesMonitorData_get_start_at_turn(const ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=8;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void ParticlesMonitorData_set_start_at_turn(ParticlesMonitorData  obj, int64_t value){
  int64_t offset=0;
  offset+=8;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* ParticlesMonitorData_getp_start_at_turn(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=8;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline int64_t ParticlesMonitorData_get_stop_at_turn(const ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=16;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void ParticlesMonitorData_set_stop_at_turn(ParticlesMonitorData  obj, int64_t value){
  int64_t offset=0;
  offset+=16;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* ParticlesMonitorData_getp_stop_at_turn(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=16;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline int64_t ParticlesMonitorData_get_part_id_start(const ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=24;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void ParticlesMonitorData_set_part_id_start(ParticlesMonitorData  obj, int64_t value){
  int64_t offset=0;
  offset+=24;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* ParticlesMonitorData_getp_part_id_start(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=24;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline int64_t ParticlesMonitorData_get_part_id_end(const ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=32;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void ParticlesMonitorData_set_part_id_end(ParticlesMonitorData  obj, int64_t value){
  int64_t offset=0;
  offset+=32;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* ParticlesMonitorData_getp_part_id_end(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=32;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline int64_t ParticlesMonitorData_get_ebe_mode(const ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=40;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void ParticlesMonitorData_set_ebe_mode(ParticlesMonitorData  obj, int64_t value){
  int64_t offset=0;
  offset+=40;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* ParticlesMonitorData_getp_ebe_mode(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=40;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline int64_t ParticlesMonitorData_get_n_records(const ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=48;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void ParticlesMonitorData_set_n_records(ParticlesMonitorData  obj, int64_t value){
  int64_t offset=0;
  offset+=48;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* ParticlesMonitorData_getp_n_records(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=48;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline int64_t ParticlesMonitorData_get_n_repetitions(const ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=56;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void ParticlesMonitorData_set_n_repetitions(ParticlesMonitorData  obj, int64_t value){
  int64_t offset=0;
  offset+=56;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* ParticlesMonitorData_getp_n_repetitions(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=56;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline int64_t ParticlesMonitorData_get_repetition_period(const ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=64;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void ParticlesMonitorData_set_repetition_period(ParticlesMonitorData  obj, int64_t value){
  int64_t offset=0;
  offset+=64;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* ParticlesMonitorData_getp_repetition_period(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=64;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline int64_t ParticlesMonitorData_get_flag_auto_to_numpy(const ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=72;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void ParticlesMonitorData_set_flag_auto_to_numpy(ParticlesMonitorData  obj, int64_t value){
  int64_t offset=0;
  offset+=72;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* ParticlesMonitorData_getp_flag_auto_to_numpy(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=72;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline ParticlesData ParticlesMonitorData_getp_data(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=80;
  return (ParticlesData)(( char*) obj+offset);
}
 static inline int64_t ParticlesMonitorData_get_data__capacity(const ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=88;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void ParticlesMonitorData_set_data__capacity(ParticlesMonitorData  obj, int64_t value){
  int64_t offset=0;
  offset+=88;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* ParticlesMonitorData_getp_data__capacity(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=88;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline int64_t ParticlesMonitorData_get_data__num_active_particles(const ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=96;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void ParticlesMonitorData_set_data__num_active_particles(ParticlesMonitorData  obj, int64_t value){
  int64_t offset=0;
  offset+=96;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* ParticlesMonitorData_getp_data__num_active_particles(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=96;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline int64_t ParticlesMonitorData_get_data__num_lost_particles(const ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=104;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void ParticlesMonitorData_set_data__num_lost_particles(ParticlesMonitorData  obj, int64_t value){
  int64_t offset=0;
  offset+=104;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* ParticlesMonitorData_getp_data__num_lost_particles(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=104;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline int64_t ParticlesMonitorData_get_data_start_tracking_at_element(const ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=112;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void ParticlesMonitorData_set_data_start_tracking_at_element(ParticlesMonitorData  obj, int64_t value){
  int64_t offset=0;
  offset+=112;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* ParticlesMonitorData_getp_data_start_tracking_at_element(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=112;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline double ParticlesMonitorData_get_data_q0(const ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=120;
  return *( double*)(( char*) obj+offset);
}
 static inline void ParticlesMonitorData_set_data_q0(ParticlesMonitorData  obj, double value){
  int64_t offset=0;
  offset+=120;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* ParticlesMonitorData_getp_data_q0(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=120;
  return ( double*)(( char*) obj+offset);
}
 static inline double ParticlesMonitorData_get_data_mass0(const ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=128;
  return *( double*)(( char*) obj+offset);
}
 static inline void ParticlesMonitorData_set_data_mass0(ParticlesMonitorData  obj, double value){
  int64_t offset=0;
  offset+=128;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* ParticlesMonitorData_getp_data_mass0(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=128;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNFloat64 ParticlesMonitorData_getp_data_p0c(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=328;
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t ParticlesMonitorData_len_data_p0c(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=328;
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double ParticlesMonitorData_get_data_p0c(const ParticlesMonitorData  obj, int64_t i0){
  int64_t offset=0;
  offset+=328;
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void ParticlesMonitorData_set_data_p0c(ParticlesMonitorData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=328;
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* ParticlesMonitorData_getp1_data_p0c(ParticlesMonitorData  obj, int64_t i0){
  int64_t offset=0;
  offset+=328;
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNFloat64 ParticlesMonitorData_getp_data_gamma0(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+56);
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t ParticlesMonitorData_len_data_gamma0(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+56);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double ParticlesMonitorData_get_data_gamma0(const ParticlesMonitorData  obj, int64_t i0){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+56);
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void ParticlesMonitorData_set_data_gamma0(ParticlesMonitorData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+56);
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* ParticlesMonitorData_getp1_data_gamma0(ParticlesMonitorData  obj, int64_t i0){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+56);
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNFloat64 ParticlesMonitorData_getp_data_beta0(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+64);
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t ParticlesMonitorData_len_data_beta0(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+64);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double ParticlesMonitorData_get_data_beta0(const ParticlesMonitorData  obj, int64_t i0){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+64);
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void ParticlesMonitorData_set_data_beta0(ParticlesMonitorData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+64);
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* ParticlesMonitorData_getp1_data_beta0(ParticlesMonitorData  obj, int64_t i0){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+64);
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNFloat64 ParticlesMonitorData_getp_data_s(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+72);
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t ParticlesMonitorData_len_data_s(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+72);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double ParticlesMonitorData_get_data_s(const ParticlesMonitorData  obj, int64_t i0){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+72);
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void ParticlesMonitorData_set_data_s(ParticlesMonitorData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+72);
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* ParticlesMonitorData_getp1_data_s(ParticlesMonitorData  obj, int64_t i0){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+72);
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNFloat64 ParticlesMonitorData_getp_data_zeta(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+80);
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t ParticlesMonitorData_len_data_zeta(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+80);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double ParticlesMonitorData_get_data_zeta(const ParticlesMonitorData  obj, int64_t i0){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+80);
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void ParticlesMonitorData_set_data_zeta(ParticlesMonitorData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+80);
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* ParticlesMonitorData_getp1_data_zeta(ParticlesMonitorData  obj, int64_t i0){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+80);
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNFloat64 ParticlesMonitorData_getp_data_ptau(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+88);
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t ParticlesMonitorData_len_data_ptau(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+88);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double ParticlesMonitorData_get_data_ptau(const ParticlesMonitorData  obj, int64_t i0){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+88);
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void ParticlesMonitorData_set_data_ptau(ParticlesMonitorData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+88);
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* ParticlesMonitorData_getp1_data_ptau(ParticlesMonitorData  obj, int64_t i0){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+88);
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNFloat64 ParticlesMonitorData_getp_data_delta(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+96);
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t ParticlesMonitorData_len_data_delta(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+96);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double ParticlesMonitorData_get_data_delta(const ParticlesMonitorData  obj, int64_t i0){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+96);
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void ParticlesMonitorData_set_data_delta(ParticlesMonitorData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+96);
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* ParticlesMonitorData_getp1_data_delta(ParticlesMonitorData  obj, int64_t i0){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+96);
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNFloat64 ParticlesMonitorData_getp_data_rpp(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+104);
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t ParticlesMonitorData_len_data_rpp(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+104);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double ParticlesMonitorData_get_data_rpp(const ParticlesMonitorData  obj, int64_t i0){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+104);
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void ParticlesMonitorData_set_data_rpp(ParticlesMonitorData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+104);
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* ParticlesMonitorData_getp1_data_rpp(ParticlesMonitorData  obj, int64_t i0){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+104);
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNFloat64 ParticlesMonitorData_getp_data_rvv(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+112);
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t ParticlesMonitorData_len_data_rvv(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+112);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double ParticlesMonitorData_get_data_rvv(const ParticlesMonitorData  obj, int64_t i0){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+112);
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void ParticlesMonitorData_set_data_rvv(ParticlesMonitorData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+112);
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* ParticlesMonitorData_getp1_data_rvv(ParticlesMonitorData  obj, int64_t i0){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+112);
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNFloat64 ParticlesMonitorData_getp_data_chi(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+120);
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t ParticlesMonitorData_len_data_chi(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+120);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double ParticlesMonitorData_get_data_chi(const ParticlesMonitorData  obj, int64_t i0){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+120);
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void ParticlesMonitorData_set_data_chi(ParticlesMonitorData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+120);
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* ParticlesMonitorData_getp1_data_chi(ParticlesMonitorData  obj, int64_t i0){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+120);
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNFloat64 ParticlesMonitorData_getp_data_charge_ratio(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+128);
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t ParticlesMonitorData_len_data_charge_ratio(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+128);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double ParticlesMonitorData_get_data_charge_ratio(const ParticlesMonitorData  obj, int64_t i0){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+128);
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void ParticlesMonitorData_set_data_charge_ratio(ParticlesMonitorData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+128);
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* ParticlesMonitorData_getp1_data_charge_ratio(ParticlesMonitorData  obj, int64_t i0){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+128);
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNFloat64 ParticlesMonitorData_getp_data_weight(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+136);
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t ParticlesMonitorData_len_data_weight(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+136);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double ParticlesMonitorData_get_data_weight(const ParticlesMonitorData  obj, int64_t i0){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+136);
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void ParticlesMonitorData_set_data_weight(ParticlesMonitorData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+136);
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* ParticlesMonitorData_getp1_data_weight(ParticlesMonitorData  obj, int64_t i0){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+136);
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNInt64 ParticlesMonitorData_getp_data_particle_id(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+144);
  return (ArrNInt64)(( char*) obj+offset);
}
 static inline int64_t ParticlesMonitorData_len_data_particle_id(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+144);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline int64_t ParticlesMonitorData_get_data_particle_id(const ParticlesMonitorData  obj, int64_t i0){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+144);
  offset+=16+i0*8;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void ParticlesMonitorData_set_data_particle_id(ParticlesMonitorData  obj, int64_t i0, int64_t value){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+144);
  offset+=16+i0*8;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* ParticlesMonitorData_getp1_data_particle_id(ParticlesMonitorData  obj, int64_t i0){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+144);
  offset+=16+i0*8;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline ArrNInt64 ParticlesMonitorData_getp_data_at_element(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+152);
  return (ArrNInt64)(( char*) obj+offset);
}
 static inline int64_t ParticlesMonitorData_len_data_at_element(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+152);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline int64_t ParticlesMonitorData_get_data_at_element(const ParticlesMonitorData  obj, int64_t i0){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+152);
  offset+=16+i0*8;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void ParticlesMonitorData_set_data_at_element(ParticlesMonitorData  obj, int64_t i0, int64_t value){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+152);
  offset+=16+i0*8;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* ParticlesMonitorData_getp1_data_at_element(ParticlesMonitorData  obj, int64_t i0){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+152);
  offset+=16+i0*8;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline ArrNInt64 ParticlesMonitorData_getp_data_at_turn(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+160);
  return (ArrNInt64)(( char*) obj+offset);
}
 static inline int64_t ParticlesMonitorData_len_data_at_turn(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+160);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline int64_t ParticlesMonitorData_get_data_at_turn(const ParticlesMonitorData  obj, int64_t i0){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+160);
  offset+=16+i0*8;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void ParticlesMonitorData_set_data_at_turn(ParticlesMonitorData  obj, int64_t i0, int64_t value){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+160);
  offset+=16+i0*8;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* ParticlesMonitorData_getp1_data_at_turn(ParticlesMonitorData  obj, int64_t i0){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+160);
  offset+=16+i0*8;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline ArrNInt64 ParticlesMonitorData_getp_data_state(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+168);
  return (ArrNInt64)(( char*) obj+offset);
}
 static inline int64_t ParticlesMonitorData_len_data_state(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+168);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline int64_t ParticlesMonitorData_get_data_state(const ParticlesMonitorData  obj, int64_t i0){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+168);
  offset+=16+i0*8;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void ParticlesMonitorData_set_data_state(ParticlesMonitorData  obj, int64_t i0, int64_t value){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+168);
  offset+=16+i0*8;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* ParticlesMonitorData_getp1_data_state(ParticlesMonitorData  obj, int64_t i0){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+168);
  offset+=16+i0*8;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline ArrNInt64 ParticlesMonitorData_getp_data_parent_particle_id(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+176);
  return (ArrNInt64)(( char*) obj+offset);
}
 static inline int64_t ParticlesMonitorData_len_data_parent_particle_id(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+176);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline int64_t ParticlesMonitorData_get_data_parent_particle_id(const ParticlesMonitorData  obj, int64_t i0){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+176);
  offset+=16+i0*8;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void ParticlesMonitorData_set_data_parent_particle_id(ParticlesMonitorData  obj, int64_t i0, int64_t value){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+176);
  offset+=16+i0*8;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* ParticlesMonitorData_getp1_data_parent_particle_id(ParticlesMonitorData  obj, int64_t i0){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+176);
  offset+=16+i0*8;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline ArrNUint32 ParticlesMonitorData_getp_data__rng_s1(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+184);
  return (ArrNUint32)(( char*) obj+offset);
}
 static inline int64_t ParticlesMonitorData_len_data__rng_s1(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+184);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline uint32_t ParticlesMonitorData_get_data__rng_s1(const ParticlesMonitorData  obj, int64_t i0){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+184);
  offset+=16+i0*4;
  return *( uint32_t*)(( char*) obj+offset);
}
 static inline void ParticlesMonitorData_set_data__rng_s1(ParticlesMonitorData  obj, int64_t i0, uint32_t value){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+184);
  offset+=16+i0*4;
  *( uint32_t*)(( char*) obj+offset)=value;
}
 static inline  uint32_t* ParticlesMonitorData_getp1_data__rng_s1(ParticlesMonitorData  obj, int64_t i0){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+184);
  offset+=16+i0*4;
  return ( uint32_t*)(( char*) obj+offset);
}
 static inline ArrNUint32 ParticlesMonitorData_getp_data__rng_s2(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+192);
  return (ArrNUint32)(( char*) obj+offset);
}
 static inline int64_t ParticlesMonitorData_len_data__rng_s2(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+192);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline uint32_t ParticlesMonitorData_get_data__rng_s2(const ParticlesMonitorData  obj, int64_t i0){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+192);
  offset+=16+i0*4;
  return *( uint32_t*)(( char*) obj+offset);
}
 static inline void ParticlesMonitorData_set_data__rng_s2(ParticlesMonitorData  obj, int64_t i0, uint32_t value){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+192);
  offset+=16+i0*4;
  *( uint32_t*)(( char*) obj+offset)=value;
}
 static inline  uint32_t* ParticlesMonitorData_getp1_data__rng_s2(ParticlesMonitorData  obj, int64_t i0){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+192);
  offset+=16+i0*4;
  return ( uint32_t*)(( char*) obj+offset);
}
 static inline ArrNUint32 ParticlesMonitorData_getp_data__rng_s3(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+200);
  return (ArrNUint32)(( char*) obj+offset);
}
 static inline int64_t ParticlesMonitorData_len_data__rng_s3(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+200);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline uint32_t ParticlesMonitorData_get_data__rng_s3(const ParticlesMonitorData  obj, int64_t i0){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+200);
  offset+=16+i0*4;
  return *( uint32_t*)(( char*) obj+offset);
}
 static inline void ParticlesMonitorData_set_data__rng_s3(ParticlesMonitorData  obj, int64_t i0, uint32_t value){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+200);
  offset+=16+i0*4;
  *( uint32_t*)(( char*) obj+offset)=value;
}
 static inline  uint32_t* ParticlesMonitorData_getp1_data__rng_s3(ParticlesMonitorData  obj, int64_t i0){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+200);
  offset+=16+i0*4;
  return ( uint32_t*)(( char*) obj+offset);
}
 static inline ArrNUint32 ParticlesMonitorData_getp_data__rng_s4(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+208);
  return (ArrNUint32)(( char*) obj+offset);
}
 static inline int64_t ParticlesMonitorData_len_data__rng_s4(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+208);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline uint32_t ParticlesMonitorData_get_data__rng_s4(const ParticlesMonitorData  obj, int64_t i0){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+208);
  offset+=16+i0*4;
  return *( uint32_t*)(( char*) obj+offset);
}
 static inline void ParticlesMonitorData_set_data__rng_s4(ParticlesMonitorData  obj, int64_t i0, uint32_t value){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+208);
  offset+=16+i0*4;
  *( uint32_t*)(( char*) obj+offset)=value;
}
 static inline  uint32_t* ParticlesMonitorData_getp1_data__rng_s4(ParticlesMonitorData  obj, int64_t i0){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+208);
  offset+=16+i0*4;
  return ( uint32_t*)(( char*) obj+offset);
}
 static inline ArrNFloat64 ParticlesMonitorData_getp_data_x(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+216);
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t ParticlesMonitorData_len_data_x(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+216);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double ParticlesMonitorData_get_data_x(const ParticlesMonitorData  obj, int64_t i0){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+216);
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void ParticlesMonitorData_set_data_x(ParticlesMonitorData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+216);
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* ParticlesMonitorData_getp1_data_x(ParticlesMonitorData  obj, int64_t i0){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+216);
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNFloat64 ParticlesMonitorData_getp_data_y(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+224);
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t ParticlesMonitorData_len_data_y(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+224);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double ParticlesMonitorData_get_data_y(const ParticlesMonitorData  obj, int64_t i0){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+224);
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void ParticlesMonitorData_set_data_y(ParticlesMonitorData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+224);
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* ParticlesMonitorData_getp1_data_y(ParticlesMonitorData  obj, int64_t i0){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+224);
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNFloat64 ParticlesMonitorData_getp_data_px(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+232);
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t ParticlesMonitorData_len_data_px(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+232);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double ParticlesMonitorData_get_data_px(const ParticlesMonitorData  obj, int64_t i0){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+232);
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void ParticlesMonitorData_set_data_px(ParticlesMonitorData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+232);
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* ParticlesMonitorData_getp1_data_px(ParticlesMonitorData  obj, int64_t i0){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+232);
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNFloat64 ParticlesMonitorData_getp_data_py(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+240);
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t ParticlesMonitorData_len_data_py(ParticlesMonitorData  obj){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+240);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double ParticlesMonitorData_get_data_py(const ParticlesMonitorData  obj, int64_t i0){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+240);
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void ParticlesMonitorData_set_data_py(ParticlesMonitorData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+240);
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* ParticlesMonitorData_getp1_data_py(ParticlesMonitorData  obj, int64_t i0){
  int64_t offset=0;
  offset+=80;
  offset+=*( int64_t*)(( char*) obj+offset+240);
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
#endif
// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_CONSTANTS_H
#define XTRACK_CONSTANTS_H

#if !defined( C_LIGHT )
    #define   C_LIGHT ( 299792458.0 )
#endif /* !defined( C_LIGHT ) */

#if !defined( EPSILON_0 )
    #define   EPSILON_0 (8.854187817620e-12)
#endif /* !defined( EPSILON_0 ) */

#if !defined( PI )
    #define PI (3.1415926535897932384626433832795028841971693993751)
#endif /* !defined( PI ) */

#if !defined( MU_0 )
    #define MU_0 (PI*4.0e-7)
#endif /* !defined( MU_0 ) */

#if !defined( DEG2RAD )
    #define DEG2RAD (0.0174532925199432957692369076848861271344287188854)
#endif /* !defiend( DEG2RAD ) */

#if !defined( RAD2DEG )
    #define RAD2DEG (57.29577951308232087679815481410517033240547246656442)
#endif /* !defiend( RAD2DEG ) */

#if !defined( SQRT_PI )
    #define SQRT_PI (1.7724538509055160272981674833411451827975494561224)
#endif /* !defined( SQRT_PI ) */

#if !defined( QELEM )
    #define QELEM (1.60217662e-19)
#endif /* !defined( QELEM ) */

#if !defined( DBL_MAX )
    #define DBL_MAX (1.7976931348623158e+308)
#endif /* !defined( DBL_MAX ) */

#if !defined( DBL_MIN )
    #define DBL_MIN (2.2250738585072014e-308)
#endif /* !defined( DBL_MIN ) */

#if !defined( DBL_EPSILON )
    #define DBL_EPSILON (2.2204460492503131e-16)
#endif /* !defined( DBL_EPSILON ) */

#endif /* XTRACK_CONSTANTS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_FUNCTIONS_H
#define XTRACK_FUNCTIONS_H

 static inline
void kill_all_particles(LocalParticle* part0, int64_t kill_state) {

    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

        LocalParticle_kill_particle(part, kill_state);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

}


 static inline
int8_t assert_tracking(LocalParticle* part, int64_t kill_state){
    // Whenever we are not tracking, e.g. in a twiss, the particle will be at_turn < 0.
    // We test this to distinguish genuine tracking from twiss.
    if (LocalParticle_get_at_turn(part) < 0){
        LocalParticle_kill_particle(part, kill_state);
        return 0;
    }
    return 1;
}

#endif /* XTRACK_FUNCTIONS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_PARTICLE_STATES_H
#define XTRACK_PARTICLE_STATES_H

#define  XT_LOST_ON_APERTURE       0
#define  XT_LOST_ON_LONG_CUT      -2
#define  XT_LOST_ALL_E_IN_SYNRAD -10
#define  RNG_ERR_SEEDS_NOT_SET   -20
#define  RNG_ERR_INVALID_TRACK   -21
#define  RNG_ERR_RUTH_NOT_SET    -22

#endif /* XTRACK_PARTICLE_STATES_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_MONITORS_H
#define XTRACK_MONITORS_H

 static inline
void ParticlesMonitor_track_local_particle(ParticlesMonitorData el,
                       LocalParticle* part0){

    int64_t const start_at_turn = ParticlesMonitorData_get_start_at_turn(el);
    int64_t const stop_at_turn = ParticlesMonitorData_get_stop_at_turn(el);
    int64_t const part_id_start = ParticlesMonitorData_get_part_id_start(el);
    int64_t const part_id_end= ParticlesMonitorData_get_part_id_end(el);
    int64_t const ebe_mode = ParticlesMonitorData_get_ebe_mode(el);
    int64_t const n_repetitions = ParticlesMonitorData_get_n_repetitions(el);
    int64_t const repetition_period = ParticlesMonitorData_get_repetition_period(el);
    ParticlesData data = ParticlesMonitorData_getp_data(el);

    int64_t n_turns_record = stop_at_turn - start_at_turn;


    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

    int64_t at_turn;
    if (ebe_mode){
        at_turn = LocalParticle_get_at_element(part);
    }
    else{
        #ifdef XSUITE_BACKTRACK
        return; // do not log (only ebe monitor supported for now in backtrack)
        #else
        at_turn = LocalParticle_get_at_turn(part);
        #endif
    }
    if (n_repetitions == 1){
        if (at_turn>=start_at_turn && at_turn<stop_at_turn){
            int64_t const particle_id = LocalParticle_get_particle_id(part);
            if (particle_id<part_id_end && particle_id>=part_id_start){
                int64_t const store_at =
                    n_turns_record * (particle_id - part_id_start)
                    + at_turn - start_at_turn;
                LocalParticle_to_Particles(part, data, store_at, 0);
            }
        }
    }
    else if (n_repetitions > 1){
        if (at_turn < start_at_turn){
//            return; //only_for_context cuda opencl
            break; //only_for_context cpu_serial cpu_openmp
        }
        int64_t const i_frame = (at_turn - start_at_turn) / repetition_period;
        if (i_frame < n_repetitions
                 && at_turn >= start_at_turn + i_frame*repetition_period
                 && at_turn < stop_at_turn + i_frame*repetition_period
             ){
            int64_t const particle_id = LocalParticle_get_particle_id(part);
            if (particle_id<part_id_end && particle_id>=part_id_start){
                int64_t const store_at =
                    n_turns_record * (part_id_end  - part_id_start) * i_frame
                    + n_turns_record * (particle_id - part_id_start)
                    + (at_turn - i_frame * repetition_period) - start_at_turn;
                LocalParticle_to_Particles(part, data, store_at, 0);
            }
        }
    }




//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }


}

#endif


             
            void ParticlesMonitor_track_particles(
               ParticlesMonitorData el,

                             ParticlesData particles,

                             int64_t flag_increment_at_element,
                  int8_t* io_buffer){
//            const int num_threads = omp_get_max_threads();                                 //only_for_context cpu_openmp
//            const int64_t capacity = ParticlesData_get__capacity(particles);               //only_for_context cpu_openmp
//            const int64_t chunk_size = (capacity + num_threads - 1)/num_threads; // ceil division  //only_for_context cpu_openmp
//            #pragma omp parallel for                                                       //only_for_context cpu_openmp
//            for (int64_t batch_id = 0; batch_id < num_threads; batch_id++) {               //only_for_context cpu_openmp
                LocalParticle lpart;
                lpart.io_buffer = io_buffer;
//                int64_t part_id = batch_id * chunk_size;                                       //only_for_context cpu_openmp
//                int64_t end_id = (batch_id + 1) * chunk_size;                                  //only_for_context cpu_openmp
//                if (end_id > capacity) end_id = capacity;                                      //only_for_context cpu_openmp
    
                int64_t part_id = 0;                    //only_for_context cpu_serial
//                int64_t part_id = blockDim.x * blockIdx.x + threadIdx.x; //only_for_context cuda
//                int64_t part_id = get_global_id(0);                    //only_for_context opencl
                int64_t end_id = 0; // unused outside of openmp  //only_for_context cpu_serial cuda opencl
    
                int64_t part_capacity = ParticlesData_get__capacity(particles);
                if (part_id<part_capacity){
                    Particles_to_LocalParticle(particles, &lpart, part_id, end_id);
                    if (check_is_active(&lpart)>0){
              ParticlesMonitor_track_local_particle(el, &lpart);

                    }
                    if (check_is_active(&lpart)>0 && flag_increment_at_element){
                            increment_at_element(&lpart, 1);
                    }
                }
//            } //only_for_context cpu_openmp
            
            // On OpenMP we want to additionally by default reorganize all
            // the particles.
//            #ifndef XT_OMP_SKIP_REORGANIZE                             //only_for_context cpu_openmp
//            LocalParticle lpart;                                       //only_for_context cpu_openmp
//            lpart.io_buffer = io_buffer;                               //only_for_context cpu_openmp
//            Particles_to_LocalParticle(particles, &lpart, 0, capacity);//only_for_context cpu_openmp
//            check_is_active(&lpart);                                   //only_for_context cpu_openmp
//            #endif                                                     //only_for_context cpu_openmp
        }

#ifndef XOBJ_TYPEDEF_QuadrupoleData
#define XOBJ_TYPEDEF_QuadrupoleData
typedef   struct QuadrupoleData_s * QuadrupoleData;
 static inline QuadrupoleData QuadrupoleData_getp(QuadrupoleData  obj){
  int64_t offset=0;
  return (QuadrupoleData)(( char*) obj+offset);
}
 static inline double QuadrupoleData_get_k1(const QuadrupoleData  obj){
  int64_t offset=0;
  return *( double*)(( char*) obj+offset);
}
 static inline void QuadrupoleData_set_k1(QuadrupoleData  obj, double value){
  int64_t offset=0;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* QuadrupoleData_getp_k1(QuadrupoleData  obj){
  int64_t offset=0;
  return ( double*)(( char*) obj+offset);
}
 static inline double QuadrupoleData_get_length(const QuadrupoleData  obj){
  int64_t offset=0;
  offset+=8;
  return *( double*)(( char*) obj+offset);
}
 static inline void QuadrupoleData_set_length(QuadrupoleData  obj, double value){
  int64_t offset=0;
  offset+=8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* QuadrupoleData_getp_length(QuadrupoleData  obj){
  int64_t offset=0;
  offset+=8;
  return ( double*)(( char*) obj+offset);
}
 static inline Arr5Float64 QuadrupoleData_getp_knl(QuadrupoleData  obj){
  int64_t offset=0;
  offset+=16;
  return (Arr5Float64)(( char*) obj+offset);
}
 static inline int64_t QuadrupoleData_len_knl(QuadrupoleData  obj){
  return 5;
}
 static inline double QuadrupoleData_get_knl(const QuadrupoleData  obj, int64_t i0){
  int64_t offset=0;
  offset+=16;
  offset+=i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void QuadrupoleData_set_knl(QuadrupoleData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=16;
  offset+=i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* QuadrupoleData_getp1_knl(QuadrupoleData  obj, int64_t i0){
  int64_t offset=0;
  offset+=16;
  offset+=i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline Arr5Float64 QuadrupoleData_getp_ksl(QuadrupoleData  obj){
  int64_t offset=0;
  offset+=56;
  return (Arr5Float64)(( char*) obj+offset);
}
 static inline int64_t QuadrupoleData_len_ksl(QuadrupoleData  obj){
  return 5;
}
 static inline double QuadrupoleData_get_ksl(const QuadrupoleData  obj, int64_t i0){
  int64_t offset=0;
  offset+=56;
  offset+=i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void QuadrupoleData_set_ksl(QuadrupoleData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=56;
  offset+=i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* QuadrupoleData_getp1_ksl(QuadrupoleData  obj, int64_t i0){
  int64_t offset=0;
  offset+=56;
  offset+=i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline int64_t QuadrupoleData_get_num_multipole_kicks(const QuadrupoleData  obj){
  int64_t offset=0;
  offset+=96;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void QuadrupoleData_set_num_multipole_kicks(QuadrupoleData  obj, int64_t value){
  int64_t offset=0;
  offset+=96;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* QuadrupoleData_getp_num_multipole_kicks(QuadrupoleData  obj){
  int64_t offset=0;
  offset+=96;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline int64_t QuadrupoleData_get_order(const QuadrupoleData  obj){
  int64_t offset=0;
  offset+=104;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void QuadrupoleData_set_order(QuadrupoleData  obj, int64_t value){
  int64_t offset=0;
  offset+=104;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* QuadrupoleData_getp_order(QuadrupoleData  obj){
  int64_t offset=0;
  offset+=104;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline double QuadrupoleData_get_inv_factorial_order(const QuadrupoleData  obj){
  int64_t offset=0;
  offset+=112;
  return *( double*)(( char*) obj+offset);
}
 static inline void QuadrupoleData_set_inv_factorial_order(QuadrupoleData  obj, double value){
  int64_t offset=0;
  offset+=112;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* QuadrupoleData_getp_inv_factorial_order(QuadrupoleData  obj){
  int64_t offset=0;
  offset+=112;
  return ( double*)(( char*) obj+offset);
}
#endif
// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_CONSTANTS_H
#define XTRACK_CONSTANTS_H

#if !defined( C_LIGHT )
    #define   C_LIGHT ( 299792458.0 )
#endif /* !defined( C_LIGHT ) */

#if !defined( EPSILON_0 )
    #define   EPSILON_0 (8.854187817620e-12)
#endif /* !defined( EPSILON_0 ) */

#if !defined( PI )
    #define PI (3.1415926535897932384626433832795028841971693993751)
#endif /* !defined( PI ) */

#if !defined( MU_0 )
    #define MU_0 (PI*4.0e-7)
#endif /* !defined( MU_0 ) */

#if !defined( DEG2RAD )
    #define DEG2RAD (0.0174532925199432957692369076848861271344287188854)
#endif /* !defiend( DEG2RAD ) */

#if !defined( RAD2DEG )
    #define RAD2DEG (57.29577951308232087679815481410517033240547246656442)
#endif /* !defiend( RAD2DEG ) */

#if !defined( SQRT_PI )
    #define SQRT_PI (1.7724538509055160272981674833411451827975494561224)
#endif /* !defined( SQRT_PI ) */

#if !defined( QELEM )
    #define QELEM (1.60217662e-19)
#endif /* !defined( QELEM ) */

#if !defined( DBL_MAX )
    #define DBL_MAX (1.7976931348623158e+308)
#endif /* !defined( DBL_MAX ) */

#if !defined( DBL_MIN )
    #define DBL_MIN (2.2250738585072014e-308)
#endif /* !defined( DBL_MIN ) */

#if !defined( DBL_EPSILON )
    #define DBL_EPSILON (2.2204460492503131e-16)
#endif /* !defined( DBL_EPSILON ) */

#endif /* XTRACK_CONSTANTS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_FUNCTIONS_H
#define XTRACK_FUNCTIONS_H

 static inline
void kill_all_particles(LocalParticle* part0, int64_t kill_state) {

    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

        LocalParticle_kill_particle(part, kill_state);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

}


 static inline
int8_t assert_tracking(LocalParticle* part, int64_t kill_state){
    // Whenever we are not tracking, e.g. in a twiss, the particle will be at_turn < 0.
    // We test this to distinguish genuine tracking from twiss.
    if (LocalParticle_get_at_turn(part) < 0){
        LocalParticle_kill_particle(part, kill_state);
        return 0;
    }
    return 1;
}

#endif /* XTRACK_FUNCTIONS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_PARTICLE_STATES_H
#define XTRACK_PARTICLE_STATES_H

#define  XT_LOST_ON_APERTURE       0
#define  XT_LOST_ON_LONG_CUT      -2
#define  XT_LOST_ALL_E_IN_SYNRAD -10
#define  RNG_ERR_SEEDS_NOT_SET   -20
#define  RNG_ERR_INVALID_TRACK   -21
#define  RNG_ERR_RUTH_NOT_SET    -22

#endif /* XTRACK_PARTICLE_STATES_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_DRIFT_H
#define XTRACK_DRIFT_H


 static inline
void Drift_single_particle(LocalParticle* part, double length){

    #ifndef XTRACK_USE_EXACT_DRIFTS

        double const rpp    = LocalParticle_get_rpp(part);
        double const rv0v    = 1./LocalParticle_get_rvv(part);
        double const xp     = LocalParticle_get_px(part) * rpp;
        double const yp     = LocalParticle_get_py(part) * rpp;
        double const dzeta  = 1 - rv0v * ( 1. + ( xp*xp + yp*yp ) / 2. );

        LocalParticle_add_to_x(part, xp * length );
        LocalParticle_add_to_y(part, yp * length );
        LocalParticle_add_to_s(part, length);
        LocalParticle_add_to_zeta(part, length * dzeta );

    #else

        double const px = LocalParticle_get_px(part);
        double const py = LocalParticle_get_py(part);
        double const rv0v    = 1./LocalParticle_get_rvv(part);
        double const one_plus_delta = 1. + LocalParticle_get_delta(part);

        double const one_over_pz = 1./sqrt(one_plus_delta*one_plus_delta
                                           - px * px - py * py);
        double const dzeta = 1 - rv0v * one_plus_delta * one_over_pz;

        LocalParticle_add_to_x(part, px * one_over_pz * length);
        LocalParticle_add_to_y(part, py * one_over_pz * length);
        LocalParticle_add_to_zeta(part, dzeta * length);
        LocalParticle_add_to_s(part, length);

    #endif
}

#endif /* XTRACK_DRIFT_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_TRACK_THICK_CFD_H
#define XTRACK_TRACK_THICK_CFD_H

#define POW2(X) ((X)*(X))
#define NONZERO(X) ((X) != 0.0)

// From madx: https://github.com/MethodicalAcceleratorDesign/MAD-X/blob/8695bd422dc403a01aa185e9fea16603bbd5b3e1/src/trrun.f90#L4320

 static inline
void track_thick_cfd(
        LocalParticle* part,  // LocalParticle to track
        double length,        // length of the element
        const double k0_,     // normal dipole strength
        const double k1_,     // normal quadrupole strength
        const double h        // curvature
) {

    // Check if it's a drift
    if (k0_ == 0.0 && k1_ == 0.0) {
        Drift_single_particle(part, length);
        return;
    }

    const double x = LocalParticle_get_x(part);
    const double y = LocalParticle_get_y(part);
    const double px = LocalParticle_get_px(part);
    const double py = LocalParticle_get_py(part);
    const double rvv = LocalParticle_get_rvv(part);

    // In MAD-X (delta + 1) is computed:
    // const double delta_plus_1 = sqrt(pt*pt + 2.0*pt*beti + 1.0);
    const double delta_plus_1 = LocalParticle_get_delta(part) + 1;

    const double k0 = k0_ / delta_plus_1;
    const double k1 = k1_ / delta_plus_1;
    const double Kx = k0 * h + k1;
    const double Ky = -k1;

    double x_, px_, y_, py_, Sx, Sy, Cx, Cy;

    if (Kx > 0.0) {
        double sqrt_Kx = sqrt(Kx);
        Sx = sin(sqrt_Kx * length) / sqrt_Kx;
        Cx = cos(sqrt_Kx * length);
    }
    else if (Kx < 0.0) {
        double sqrt_Kx = sqrt(-Kx); // the imaginary part
        Sx = sinh(sqrt_Kx * length) / sqrt_Kx; // sin(ix) = i sinh(x)
        Cx = cosh(sqrt_Kx * length); // cos(ix) = cosh(x)
    }
    else { // Kx == 0.0
        Sx = length;
        Cx = 1.0;
    }

    if (Ky > 0.0) {
        double sqrt_Ky = sqrt(Ky);
        Sy = sin(sqrt_Ky * length) / sqrt_Ky;
        Cy = cos(sqrt_Ky * length);
    }
    else if (Ky < 0.0) {
        double sqrt_Ky = sqrt(-Ky); // the imaginary part
        Sy = sinh(sqrt_Ky * length) / sqrt_Ky; // sin(ix) = i sinh(x)
        Cy = cosh(sqrt_Ky * length);  // cos(ix) = cosh(x)
    }
    else { // Ky == 0.0
        Sy = length;
        Cy = 1.0;
    }

    // useful quantities
    const double xp = px / delta_plus_1;
    const double yp = py / delta_plus_1;
    const double A = -Kx * x - k0 + h;
    const double B = xp;
    const double C = -Ky * y;
    const double D = yp;

    // transverse map
    x_ = x * Cx + xp * Sx;
    y_ = y * Cy + yp * Sy;
    px_ = (A * Sx + B * Cx) * delta_plus_1;
    py_ = (C * Sy + D * Cy) * delta_plus_1;

    if (NONZERO(Kx))
        x_ = x_ + (k0 - h) * (Cx - 1.0) / Kx;
    else
        x_ = x_ - (k0 - h) * 0.5 * POW2(length);

    // longitudinal map
    double length_ = length; // will be the total path length traveled by the particle
    if (NONZERO(Kx)) {
        length_ -= (h * ((Cx - 1.0) * xp + Sx * A + length * (k0 - h))) / Kx;
        length_ += 0.5 * (
            - (POW2(A) * Cx * Sx) / (2.0 * Kx) \
            + (POW2(B) * Cx * Sx) / 2.0 \
            + (POW2(A) * length) / (2.0 * Kx) \
            + (POW2(B) * length) / 2.0 \
            - (A * B * POW2(Cx)) / Kx \
            + (A * B) / Kx
        );
    }
    else {
        length_ += h * length * (
            3.0 * length * xp \
            + 6.0 * x \
            - (k0 - h) * POW2(length)
        ) / 6.0;
        length_ += 0.5 * (POW2(B)) * length;
    }

    if (NONZERO(Ky)) {
        length_ += 0.5 * (
            - (POW2(C) * Cy * Sy) / (2.0 * Ky) \
            + (POW2(D) * Cy * Sy) / 2.0 \
            + (POW2(C) * length) / (2.0 * Ky) \
            + (POW2(D) * length) / 2.0 \
            - (C * D * POW2(Cy)) / Ky \
            + (C * D) / Ky
        );
    }
    else {
        length_ += 0.5 * POW2(D) * length;
    }

    const double dzeta = length - length_ / rvv;

    LocalParticle_set_x(part, x_);
    LocalParticle_set_px(part, px_);
    LocalParticle_set_y(part, y_);
    LocalParticle_set_py(part, py_);
    LocalParticle_add_to_zeta(part, dzeta);
    LocalParticle_add_to_s(part, length);
}

#endif // XTRACK_TRACK_THICK_CFD_H

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_MULTIPOLAR_KICK_H
#define XTRACK_MULTIPOLAR_KICK_H

 static inline
void multipolar_kick(
    LocalParticle* part,
    const int order,
    const double inv_factorial_order,
      const double *knl,
      const double *ksl,
    const double weight
) {

    #ifdef XSUITE_BACKTRACK
        LocalParticle_kill_particle(part, -31);
        return;
    #else

    int64_t index = order;
    double inv_factorial = inv_factorial_order;

    double dpx = knl[index] * inv_factorial;
    double dpy = ksl[index] * inv_factorial;

    double const x   = LocalParticle_get_x(part);
    double const y   = LocalParticle_get_y(part);
    double const chi = LocalParticle_get_chi(part);

    while( index > 0 )
    {
        double const zre = dpx * x - dpy * y;
        double const zim = dpx * y + dpy * x;

        inv_factorial *= index;
        index -= 1;

        dpx = knl[index] * inv_factorial + zre;
        dpy = ksl[index] * inv_factorial + zim;
    }

    dpx = -chi * dpx; // rad
    dpy =  chi * dpy; // rad

    LocalParticle_add_to_px(part, weight * dpx);
    LocalParticle_add_to_py(part, weight * dpy);

    #endif // not XSUITE_BACKTRACK
}

#endif // XTRACK_MULTIPOLAR_KICK_H

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_QUADRUPOLE_H
#define XTRACK_QUADRUPOLE_H

 static inline
void Quadrupole_track_local_particle(
        QuadrupoleData el,
        LocalParticle* part0
) {
    double length = QuadrupoleData_get_length(el);

    #ifdef XSUITE_BACKTRACK
        length = -length;
    #endif

    const double k1 = QuadrupoleData_get_k1(el);

    const int64_t num_multipole_kicks = QuadrupoleData_get_num_multipole_kicks(el);
    const int64_t order = QuadrupoleData_get_order(el);
    const double inv_factorial_order = QuadrupoleData_get_inv_factorial_order(el);

      const double *knl = QuadrupoleData_getp1_knl(el, 0);
      const double *ksl = QuadrupoleData_getp1_ksl(el, 0);

    const double slice_length = length / (num_multipole_kicks + 1);
    const double kick_weight = 1. / num_multipole_kicks;


    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

        track_thick_cfd(part, slice_length, 0, k1, 0);

        for (int ii = 0; ii < num_multipole_kicks; ii++) {
            multipolar_kick(part, order, inv_factorial_order, knl, ksl, kick_weight);
            track_thick_cfd(part, slice_length, 0, k1, 0);
        }

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

}

#endif // XTRACK_QUADRUPOLE_H

             
            void Quadrupole_track_particles(
               QuadrupoleData el,

                             ParticlesData particles,

                             int64_t flag_increment_at_element,
                  int8_t* io_buffer){
//            const int num_threads = omp_get_max_threads();                                 //only_for_context cpu_openmp
//            const int64_t capacity = ParticlesData_get__capacity(particles);               //only_for_context cpu_openmp
//            const int64_t chunk_size = (capacity + num_threads - 1)/num_threads; // ceil division  //only_for_context cpu_openmp
//            #pragma omp parallel for                                                       //only_for_context cpu_openmp
//            for (int64_t batch_id = 0; batch_id < num_threads; batch_id++) {               //only_for_context cpu_openmp
                LocalParticle lpart;
                lpart.io_buffer = io_buffer;
//                int64_t part_id = batch_id * chunk_size;                                       //only_for_context cpu_openmp
//                int64_t end_id = (batch_id + 1) * chunk_size;                                  //only_for_context cpu_openmp
//                if (end_id > capacity) end_id = capacity;                                      //only_for_context cpu_openmp
    
                int64_t part_id = 0;                    //only_for_context cpu_serial
//                int64_t part_id = blockDim.x * blockIdx.x + threadIdx.x; //only_for_context cuda
//                int64_t part_id = get_global_id(0);                    //only_for_context opencl
                int64_t end_id = 0; // unused outside of openmp  //only_for_context cpu_serial cuda opencl
    
                int64_t part_capacity = ParticlesData_get__capacity(particles);
                if (part_id<part_capacity){
                    Particles_to_LocalParticle(particles, &lpart, part_id, end_id);
                    if (check_is_active(&lpart)>0){
              Quadrupole_track_local_particle(el, &lpart);

                    }
                    if (check_is_active(&lpart)>0 && flag_increment_at_element){
                            increment_at_element(&lpart, 1);
                    }
                }
//            } //only_for_context cpu_openmp
            
            // On OpenMP we want to additionally by default reorganize all
            // the particles.
//            #ifndef XT_OMP_SKIP_REORGANIZE                             //only_for_context cpu_openmp
//            LocalParticle lpart;                                       //only_for_context cpu_openmp
//            lpart.io_buffer = io_buffer;                               //only_for_context cpu_openmp
//            Particles_to_LocalParticle(particles, &lpart, 0, capacity);//only_for_context cpu_openmp
//            check_is_active(&lpart);                                   //only_for_context cpu_openmp
//            #endif                                                     //only_for_context cpu_openmp
        }

#ifndef XOBJ_TYPEDEF_RFMultipoleData
#define XOBJ_TYPEDEF_RFMultipoleData
typedef   struct RFMultipoleData_s * RFMultipoleData;
 static inline RFMultipoleData RFMultipoleData_getp(RFMultipoleData  obj){
  int64_t offset=0;
  return (RFMultipoleData)(( char*) obj+offset);
}
 static inline int64_t RFMultipoleData_get_order(const RFMultipoleData  obj){
  int64_t offset=0;
  offset+=8;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void RFMultipoleData_set_order(RFMultipoleData  obj, int64_t value){
  int64_t offset=0;
  offset+=8;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* RFMultipoleData_getp_order(RFMultipoleData  obj){
  int64_t offset=0;
  offset+=8;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline double RFMultipoleData_get_inv_factorial_order(const RFMultipoleData  obj){
  int64_t offset=0;
  offset+=16;
  return *( double*)(( char*) obj+offset);
}
 static inline void RFMultipoleData_set_inv_factorial_order(RFMultipoleData  obj, double value){
  int64_t offset=0;
  offset+=16;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* RFMultipoleData_getp_inv_factorial_order(RFMultipoleData  obj){
  int64_t offset=0;
  offset+=16;
  return ( double*)(( char*) obj+offset);
}
 static inline double RFMultipoleData_get_voltage(const RFMultipoleData  obj){
  int64_t offset=0;
  offset+=24;
  return *( double*)(( char*) obj+offset);
}
 static inline void RFMultipoleData_set_voltage(RFMultipoleData  obj, double value){
  int64_t offset=0;
  offset+=24;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* RFMultipoleData_getp_voltage(RFMultipoleData  obj){
  int64_t offset=0;
  offset+=24;
  return ( double*)(( char*) obj+offset);
}
 static inline double RFMultipoleData_get_frequency(const RFMultipoleData  obj){
  int64_t offset=0;
  offset+=32;
  return *( double*)(( char*) obj+offset);
}
 static inline void RFMultipoleData_set_frequency(RFMultipoleData  obj, double value){
  int64_t offset=0;
  offset+=32;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* RFMultipoleData_getp_frequency(RFMultipoleData  obj){
  int64_t offset=0;
  offset+=32;
  return ( double*)(( char*) obj+offset);
}
 static inline double RFMultipoleData_get_lag(const RFMultipoleData  obj){
  int64_t offset=0;
  offset+=40;
  return *( double*)(( char*) obj+offset);
}
 static inline void RFMultipoleData_set_lag(RFMultipoleData  obj, double value){
  int64_t offset=0;
  offset+=40;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* RFMultipoleData_getp_lag(RFMultipoleData  obj){
  int64_t offset=0;
  offset+=40;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNFloat64 RFMultipoleData_getp_knl(RFMultipoleData  obj){
  int64_t offset=0;
  offset+=72;
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t RFMultipoleData_len_knl(RFMultipoleData  obj){
  int64_t offset=0;
  offset+=72;
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double RFMultipoleData_get_knl(const RFMultipoleData  obj, int64_t i0){
  int64_t offset=0;
  offset+=72;
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void RFMultipoleData_set_knl(RFMultipoleData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=72;
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* RFMultipoleData_getp1_knl(RFMultipoleData  obj, int64_t i0){
  int64_t offset=0;
  offset+=72;
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNFloat64 RFMultipoleData_getp_ksl(RFMultipoleData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+48);
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t RFMultipoleData_len_ksl(RFMultipoleData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+48);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double RFMultipoleData_get_ksl(const RFMultipoleData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+48);
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void RFMultipoleData_set_ksl(RFMultipoleData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+48);
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* RFMultipoleData_getp1_ksl(RFMultipoleData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+48);
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNFloat64 RFMultipoleData_getp_pn(RFMultipoleData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+56);
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t RFMultipoleData_len_pn(RFMultipoleData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+56);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double RFMultipoleData_get_pn(const RFMultipoleData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+56);
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void RFMultipoleData_set_pn(RFMultipoleData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+56);
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* RFMultipoleData_getp1_pn(RFMultipoleData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+56);
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNFloat64 RFMultipoleData_getp_ps(RFMultipoleData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+64);
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t RFMultipoleData_len_ps(RFMultipoleData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+64);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double RFMultipoleData_get_ps(const RFMultipoleData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+64);
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void RFMultipoleData_set_ps(RFMultipoleData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+64);
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* RFMultipoleData_getp1_ps(RFMultipoleData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+64);
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
#endif
// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_CONSTANTS_H
#define XTRACK_CONSTANTS_H

#if !defined( C_LIGHT )
    #define   C_LIGHT ( 299792458.0 )
#endif /* !defined( C_LIGHT ) */

#if !defined( EPSILON_0 )
    #define   EPSILON_0 (8.854187817620e-12)
#endif /* !defined( EPSILON_0 ) */

#if !defined( PI )
    #define PI (3.1415926535897932384626433832795028841971693993751)
#endif /* !defined( PI ) */

#if !defined( MU_0 )
    #define MU_0 (PI*4.0e-7)
#endif /* !defined( MU_0 ) */

#if !defined( DEG2RAD )
    #define DEG2RAD (0.0174532925199432957692369076848861271344287188854)
#endif /* !defiend( DEG2RAD ) */

#if !defined( RAD2DEG )
    #define RAD2DEG (57.29577951308232087679815481410517033240547246656442)
#endif /* !defiend( RAD2DEG ) */

#if !defined( SQRT_PI )
    #define SQRT_PI (1.7724538509055160272981674833411451827975494561224)
#endif /* !defined( SQRT_PI ) */

#if !defined( QELEM )
    #define QELEM (1.60217662e-19)
#endif /* !defined( QELEM ) */

#if !defined( DBL_MAX )
    #define DBL_MAX (1.7976931348623158e+308)
#endif /* !defined( DBL_MAX ) */

#if !defined( DBL_MIN )
    #define DBL_MIN (2.2250738585072014e-308)
#endif /* !defined( DBL_MIN ) */

#if !defined( DBL_EPSILON )
    #define DBL_EPSILON (2.2204460492503131e-16)
#endif /* !defined( DBL_EPSILON ) */

#endif /* XTRACK_CONSTANTS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_FUNCTIONS_H
#define XTRACK_FUNCTIONS_H

 static inline
void kill_all_particles(LocalParticle* part0, int64_t kill_state) {

    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

        LocalParticle_kill_particle(part, kill_state);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

}


 static inline
int8_t assert_tracking(LocalParticle* part, int64_t kill_state){
    // Whenever we are not tracking, e.g. in a twiss, the particle will be at_turn < 0.
    // We test this to distinguish genuine tracking from twiss.
    if (LocalParticle_get_at_turn(part) < 0){
        LocalParticle_kill_particle(part, kill_state);
        return 0;
    }
    return 1;
}

#endif /* XTRACK_FUNCTIONS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_PARTICLE_STATES_H
#define XTRACK_PARTICLE_STATES_H

#define  XT_LOST_ON_APERTURE       0
#define  XT_LOST_ON_LONG_CUT      -2
#define  XT_LOST_ALL_E_IN_SYNRAD -10
#define  RNG_ERR_SEEDS_NOT_SET   -20
#define  RNG_ERR_INVALID_TRACK   -21
#define  RNG_ERR_RUTH_NOT_SET    -22

#endif /* XTRACK_PARTICLE_STATES_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_CONSTANTS_H
#define XTRACK_CONSTANTS_H

#if !defined( C_LIGHT )
    #define   C_LIGHT ( 299792458.0 )
#endif /* !defined( C_LIGHT ) */

#if !defined( EPSILON_0 )
    #define   EPSILON_0 (8.854187817620e-12)
#endif /* !defined( EPSILON_0 ) */

#if !defined( PI )
    #define PI (3.1415926535897932384626433832795028841971693993751)
#endif /* !defined( PI ) */

#if !defined( MU_0 )
    #define MU_0 (PI*4.0e-7)
#endif /* !defined( MU_0 ) */

#if !defined( DEG2RAD )
    #define DEG2RAD (0.0174532925199432957692369076848861271344287188854)
#endif /* !defiend( DEG2RAD ) */

#if !defined( RAD2DEG )
    #define RAD2DEG (57.29577951308232087679815481410517033240547246656442)
#endif /* !defiend( RAD2DEG ) */

#if !defined( SQRT_PI )
    #define SQRT_PI (1.7724538509055160272981674833411451827975494561224)
#endif /* !defined( SQRT_PI ) */

#if !defined( QELEM )
    #define QELEM (1.60217662e-19)
#endif /* !defined( QELEM ) */

#if !defined( DBL_MAX )
    #define DBL_MAX (1.7976931348623158e+308)
#endif /* !defined( DBL_MAX ) */

#if !defined( DBL_MIN )
    #define DBL_MIN (2.2250738585072014e-308)
#endif /* !defined( DBL_MIN ) */

#if !defined( DBL_EPSILON )
    #define DBL_EPSILON (2.2204460492503131e-16)
#endif /* !defined( DBL_EPSILON ) */

#endif /* XTRACK_CONSTANTS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_RFMULTIPOLE_H
#define XTRACK_RFMULTIPOLE_H

 static inline
void RFMultipole_track_local_particle(RFMultipoleData el, LocalParticle* part0){

      double const* knl = RFMultipoleData_getp1_knl(el, 0);
      double const* ksl = RFMultipoleData_getp1_ksl(el, 0);
      double const* pn = RFMultipoleData_getp1_pn(el, 0);
      double const* ps = RFMultipoleData_getp1_ps(el, 0);
    int64_t const order = RFMultipoleData_get_order(el);
    double const frequency = RFMultipoleData_get_frequency(el);
    double voltage = RFMultipoleData_get_voltage(el);
    double const lag = RFMultipoleData_get_lag(el);

    #ifdef XSUITE_BACKTRACK
        voltage = -voltage;
    #endif


    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

        double const k = frequency * ( 2.0 * PI / C_LIGHT);

        double const x = LocalParticle_get_x(part);
        double const y = LocalParticle_get_y(part);
        double const beta0  = LocalParticle_get_beta0(part);
        double const zeta   = LocalParticle_get_zeta(part);
        double const q      = LocalParticle_get_q0(part)
                            * LocalParticle_get_charge_ratio(part);
        double const ktau   = k * zeta / beta0;

        double dpx = 0.0;
        double dpy = 0.0;
        double dptr = 0.0;
        double zre = 1.0;
        double zim = 0.0;

        double factorial = 1.0;
        for (int64_t kk = 0; kk <= order; kk++)
        {

            if (kk>0){
                factorial *= kk;
            }

            double const pn_kk = DEG2RAD * pn[kk] - ktau;
            double const ps_kk = DEG2RAD * ps[kk] - ktau;

            double bal_n_kk = knl[kk]/factorial;
            double bal_s_kk = ksl[kk]/factorial;

            #ifdef XSUITE_BACKTRACK
                bal_n_kk = -bal_n_kk;
                bal_s_kk = -bal_s_kk;
            #endif

            double const cn = cos(pn_kk);
            double const cs = cos(ps_kk);
            double const sn = sin(pn_kk);
            double const ss = sin(ps_kk);

            dpx += cn * (bal_n_kk * zre) - cs * (bal_s_kk * zim);
            dpy += cs * (bal_s_kk * zre) + cn * (bal_n_kk * zim);

            double const zret = zre * x - zim * y;
            zim = zim * x + zre * y;
            zre = zret;

            dptr += sn * (bal_n_kk * zre) - ss * (bal_s_kk * zim);
        }

        double const cav_energy = q * voltage * sin(lag * DEG2RAD - ktau);
        double const p0c = LocalParticle_get_p0c(part);
        double const rfmultipole_energy = - q * ( (k * p0c) * dptr );

        double const chi    = LocalParticle_get_chi(part);

        double const px_kick = - chi * dpx;
        double const py_kick =   chi * dpy;
        double const energy_kick = cav_energy + rfmultipole_energy;

        LocalParticle_add_to_px(part, px_kick);
        LocalParticle_add_to_py(part, py_kick);
        LocalParticle_add_to_energy(part, energy_kick, 1);


//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }


}

#endif


             
            void RFMultipole_track_particles(
               RFMultipoleData el,

                             ParticlesData particles,

                             int64_t flag_increment_at_element,
                  int8_t* io_buffer){
//            const int num_threads = omp_get_max_threads();                                 //only_for_context cpu_openmp
//            const int64_t capacity = ParticlesData_get__capacity(particles);               //only_for_context cpu_openmp
//            const int64_t chunk_size = (capacity + num_threads - 1)/num_threads; // ceil division  //only_for_context cpu_openmp
//            #pragma omp parallel for                                                       //only_for_context cpu_openmp
//            for (int64_t batch_id = 0; batch_id < num_threads; batch_id++) {               //only_for_context cpu_openmp
                LocalParticle lpart;
                lpart.io_buffer = io_buffer;
//                int64_t part_id = batch_id * chunk_size;                                       //only_for_context cpu_openmp
//                int64_t end_id = (batch_id + 1) * chunk_size;                                  //only_for_context cpu_openmp
//                if (end_id > capacity) end_id = capacity;                                      //only_for_context cpu_openmp
    
                int64_t part_id = 0;                    //only_for_context cpu_serial
//                int64_t part_id = blockDim.x * blockIdx.x + threadIdx.x; //only_for_context cuda
//                int64_t part_id = get_global_id(0);                    //only_for_context opencl
                int64_t end_id = 0; // unused outside of openmp  //only_for_context cpu_serial cuda opencl
    
                int64_t part_capacity = ParticlesData_get__capacity(particles);
                if (part_id<part_capacity){
                    Particles_to_LocalParticle(particles, &lpart, part_id, end_id);
                    if (check_is_active(&lpart)>0){
              RFMultipole_track_local_particle(el, &lpart);

                    }
                    if (check_is_active(&lpart)>0 && flag_increment_at_element){
                            increment_at_element(&lpart, 1);
                    }
                }
//            } //only_for_context cpu_openmp
            
            // On OpenMP we want to additionally by default reorganize all
            // the particles.
//            #ifndef XT_OMP_SKIP_REORGANIZE                             //only_for_context cpu_openmp
//            LocalParticle lpart;                                       //only_for_context cpu_openmp
//            lpart.io_buffer = io_buffer;                               //only_for_context cpu_openmp
//            Particles_to_LocalParticle(particles, &lpart, 0, capacity);//only_for_context cpu_openmp
//            check_is_active(&lpart);                                   //only_for_context cpu_openmp
//            #endif                                                     //only_for_context cpu_openmp
        }

#ifndef XOBJ_TYPEDEF_ReferenceEnergyIncreaseData
#define XOBJ_TYPEDEF_ReferenceEnergyIncreaseData
typedef   struct ReferenceEnergyIncreaseData_s * ReferenceEnergyIncreaseData;
 static inline ReferenceEnergyIncreaseData ReferenceEnergyIncreaseData_getp(ReferenceEnergyIncreaseData  obj){
  int64_t offset=0;
  return (ReferenceEnergyIncreaseData)(( char*) obj+offset);
}
 static inline double ReferenceEnergyIncreaseData_get_Delta_p0c(const ReferenceEnergyIncreaseData  obj){
  int64_t offset=0;
  return *( double*)(( char*) obj+offset);
}
 static inline void ReferenceEnergyIncreaseData_set_Delta_p0c(ReferenceEnergyIncreaseData  obj, double value){
  int64_t offset=0;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* ReferenceEnergyIncreaseData_getp_Delta_p0c(ReferenceEnergyIncreaseData  obj){
  int64_t offset=0;
  return ( double*)(( char*) obj+offset);
}
#endif
// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_CONSTANTS_H
#define XTRACK_CONSTANTS_H

#if !defined( C_LIGHT )
    #define   C_LIGHT ( 299792458.0 )
#endif /* !defined( C_LIGHT ) */

#if !defined( EPSILON_0 )
    #define   EPSILON_0 (8.854187817620e-12)
#endif /* !defined( EPSILON_0 ) */

#if !defined( PI )
    #define PI (3.1415926535897932384626433832795028841971693993751)
#endif /* !defined( PI ) */

#if !defined( MU_0 )
    #define MU_0 (PI*4.0e-7)
#endif /* !defined( MU_0 ) */

#if !defined( DEG2RAD )
    #define DEG2RAD (0.0174532925199432957692369076848861271344287188854)
#endif /* !defiend( DEG2RAD ) */

#if !defined( RAD2DEG )
    #define RAD2DEG (57.29577951308232087679815481410517033240547246656442)
#endif /* !defiend( RAD2DEG ) */

#if !defined( SQRT_PI )
    #define SQRT_PI (1.7724538509055160272981674833411451827975494561224)
#endif /* !defined( SQRT_PI ) */

#if !defined( QELEM )
    #define QELEM (1.60217662e-19)
#endif /* !defined( QELEM ) */

#if !defined( DBL_MAX )
    #define DBL_MAX (1.7976931348623158e+308)
#endif /* !defined( DBL_MAX ) */

#if !defined( DBL_MIN )
    #define DBL_MIN (2.2250738585072014e-308)
#endif /* !defined( DBL_MIN ) */

#if !defined( DBL_EPSILON )
    #define DBL_EPSILON (2.2204460492503131e-16)
#endif /* !defined( DBL_EPSILON ) */

#endif /* XTRACK_CONSTANTS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_FUNCTIONS_H
#define XTRACK_FUNCTIONS_H

 static inline
void kill_all_particles(LocalParticle* part0, int64_t kill_state) {

    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

        LocalParticle_kill_particle(part, kill_state);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

}


 static inline
int8_t assert_tracking(LocalParticle* part, int64_t kill_state){
    // Whenever we are not tracking, e.g. in a twiss, the particle will be at_turn < 0.
    // We test this to distinguish genuine tracking from twiss.
    if (LocalParticle_get_at_turn(part) < 0){
        LocalParticle_kill_particle(part, kill_state);
        return 0;
    }
    return 1;
}

#endif /* XTRACK_FUNCTIONS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_PARTICLE_STATES_H
#define XTRACK_PARTICLE_STATES_H

#define  XT_LOST_ON_APERTURE       0
#define  XT_LOST_ON_LONG_CUT      -2
#define  XT_LOST_ALL_E_IN_SYNRAD -10
#define  RNG_ERR_SEEDS_NOT_SET   -20
#define  RNG_ERR_INVALID_TRACK   -21
#define  RNG_ERR_RUTH_NOT_SET    -22

#endif /* XTRACK_PARTICLE_STATES_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_REFERENCEENERGYINCREASE_H
#define XTRACK_REFERENCEENERGYINCREASE_H

 static inline
void ReferenceEnergyIncrease_track_local_particle(ReferenceEnergyIncreaseData el,
		                                  LocalParticle* part0){

    double Delta_p0c = ReferenceEnergyIncreaseData_get_Delta_p0c(el);

    #ifdef XSUITE_BACKTRACK
        Delta_p0c = -Delta_p0c;
    #endif


    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

	LocalParticle_update_p0c(part,
		LocalParticle_get_p0c(part) + Delta_p0c);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }


}
#endif


             
            void ReferenceEnergyIncrease_track_particles(
               ReferenceEnergyIncreaseData el,

                             ParticlesData particles,

                             int64_t flag_increment_at_element,
                  int8_t* io_buffer){
//            const int num_threads = omp_get_max_threads();                                 //only_for_context cpu_openmp
//            const int64_t capacity = ParticlesData_get__capacity(particles);               //only_for_context cpu_openmp
//            const int64_t chunk_size = (capacity + num_threads - 1)/num_threads; // ceil division  //only_for_context cpu_openmp
//            #pragma omp parallel for                                                       //only_for_context cpu_openmp
//            for (int64_t batch_id = 0; batch_id < num_threads; batch_id++) {               //only_for_context cpu_openmp
                LocalParticle lpart;
                lpart.io_buffer = io_buffer;
//                int64_t part_id = batch_id * chunk_size;                                       //only_for_context cpu_openmp
//                int64_t end_id = (batch_id + 1) * chunk_size;                                  //only_for_context cpu_openmp
//                if (end_id > capacity) end_id = capacity;                                      //only_for_context cpu_openmp
    
                int64_t part_id = 0;                    //only_for_context cpu_serial
//                int64_t part_id = blockDim.x * blockIdx.x + threadIdx.x; //only_for_context cuda
//                int64_t part_id = get_global_id(0);                    //only_for_context opencl
                int64_t end_id = 0; // unused outside of openmp  //only_for_context cpu_serial cuda opencl
    
                int64_t part_capacity = ParticlesData_get__capacity(particles);
                if (part_id<part_capacity){
                    Particles_to_LocalParticle(particles, &lpart, part_id, end_id);
                    if (check_is_active(&lpart)>0){
              ReferenceEnergyIncrease_track_local_particle(el, &lpart);

                    }
                    if (check_is_active(&lpart)>0 && flag_increment_at_element){
                            increment_at_element(&lpart, 1);
                    }
                }
//            } //only_for_context cpu_openmp
            
            // On OpenMP we want to additionally by default reorganize all
            // the particles.
//            #ifndef XT_OMP_SKIP_REORGANIZE                             //only_for_context cpu_openmp
//            LocalParticle lpart;                                       //only_for_context cpu_openmp
//            lpart.io_buffer = io_buffer;                               //only_for_context cpu_openmp
//            Particles_to_LocalParticle(particles, &lpart, 0, capacity);//only_for_context cpu_openmp
//            check_is_active(&lpart);                                   //only_for_context cpu_openmp
//            #endif                                                     //only_for_context cpu_openmp
        }

#ifndef XOBJ_TYPEDEF_SRotationData
#define XOBJ_TYPEDEF_SRotationData
typedef   struct SRotationData_s * SRotationData;
 static inline SRotationData SRotationData_getp(SRotationData  obj){
  int64_t offset=0;
  return (SRotationData)(( char*) obj+offset);
}
 static inline double SRotationData_get_cos_z(const SRotationData  obj){
  int64_t offset=0;
  return *( double*)(( char*) obj+offset);
}
 static inline void SRotationData_set_cos_z(SRotationData  obj, double value){
  int64_t offset=0;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* SRotationData_getp_cos_z(SRotationData  obj){
  int64_t offset=0;
  return ( double*)(( char*) obj+offset);
}
 static inline double SRotationData_get_sin_z(const SRotationData  obj){
  int64_t offset=0;
  offset+=8;
  return *( double*)(( char*) obj+offset);
}
 static inline void SRotationData_set_sin_z(SRotationData  obj, double value){
  int64_t offset=0;
  offset+=8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* SRotationData_getp_sin_z(SRotationData  obj){
  int64_t offset=0;
  offset+=8;
  return ( double*)(( char*) obj+offset);
}
#endif
// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_CONSTANTS_H
#define XTRACK_CONSTANTS_H

#if !defined( C_LIGHT )
    #define   C_LIGHT ( 299792458.0 )
#endif /* !defined( C_LIGHT ) */

#if !defined( EPSILON_0 )
    #define   EPSILON_0 (8.854187817620e-12)
#endif /* !defined( EPSILON_0 ) */

#if !defined( PI )
    #define PI (3.1415926535897932384626433832795028841971693993751)
#endif /* !defined( PI ) */

#if !defined( MU_0 )
    #define MU_0 (PI*4.0e-7)
#endif /* !defined( MU_0 ) */

#if !defined( DEG2RAD )
    #define DEG2RAD (0.0174532925199432957692369076848861271344287188854)
#endif /* !defiend( DEG2RAD ) */

#if !defined( RAD2DEG )
    #define RAD2DEG (57.29577951308232087679815481410517033240547246656442)
#endif /* !defiend( RAD2DEG ) */

#if !defined( SQRT_PI )
    #define SQRT_PI (1.7724538509055160272981674833411451827975494561224)
#endif /* !defined( SQRT_PI ) */

#if !defined( QELEM )
    #define QELEM (1.60217662e-19)
#endif /* !defined( QELEM ) */

#if !defined( DBL_MAX )
    #define DBL_MAX (1.7976931348623158e+308)
#endif /* !defined( DBL_MAX ) */

#if !defined( DBL_MIN )
    #define DBL_MIN (2.2250738585072014e-308)
#endif /* !defined( DBL_MIN ) */

#if !defined( DBL_EPSILON )
    #define DBL_EPSILON (2.2204460492503131e-16)
#endif /* !defined( DBL_EPSILON ) */

#endif /* XTRACK_CONSTANTS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_FUNCTIONS_H
#define XTRACK_FUNCTIONS_H

 static inline
void kill_all_particles(LocalParticle* part0, int64_t kill_state) {

    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

        LocalParticle_kill_particle(part, kill_state);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

}


 static inline
int8_t assert_tracking(LocalParticle* part, int64_t kill_state){
    // Whenever we are not tracking, e.g. in a twiss, the particle will be at_turn < 0.
    // We test this to distinguish genuine tracking from twiss.
    if (LocalParticle_get_at_turn(part) < 0){
        LocalParticle_kill_particle(part, kill_state);
        return 0;
    }
    return 1;
}

#endif /* XTRACK_FUNCTIONS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_PARTICLE_STATES_H
#define XTRACK_PARTICLE_STATES_H

#define  XT_LOST_ON_APERTURE       0
#define  XT_LOST_ON_LONG_CUT      -2
#define  XT_LOST_ALL_E_IN_SYNRAD -10
#define  RNG_ERR_SEEDS_NOT_SET   -20
#define  RNG_ERR_INVALID_TRACK   -21
#define  RNG_ERR_RUTH_NOT_SET    -22

#endif /* XTRACK_PARTICLE_STATES_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_SROTATION_H
#define XTRACK_SROTATION_H


 static inline
void SRotation_single_particle(LocalParticle* part, double sin_z, double cos_z){

    double const x  = LocalParticle_get_x(part);
    double const y  = LocalParticle_get_y(part);
    double const px = LocalParticle_get_px(part);
    double const py = LocalParticle_get_py(part);

    double const x_hat  =  cos_z * x  + sin_z * y;
    double const y_hat  = -sin_z * x  + cos_z * y;
    double const px_hat =  cos_z * px + sin_z * py;
    double const py_hat = -sin_z * px + cos_z * py;

    LocalParticle_set_x(part, x_hat);
    LocalParticle_set_y(part, y_hat);
    LocalParticle_set_px(part, px_hat);
    LocalParticle_set_py(part, py_hat);

}


 static inline
void SRotation_track_local_particle(SRotationData el, LocalParticle* part0){

    double sin_z = SRotationData_get_sin_z(el);
    double cos_z = SRotationData_get_cos_z(el);

    #ifdef XSUITE_BACKTRACK
        sin_z = -sin_z;
    #endif


    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

        SRotation_single_particle(part, sin_z, cos_z);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }


}


#endif /* XTRACK_SROTATION_H */


             
            void SRotation_track_particles(
               SRotationData el,

                             ParticlesData particles,

                             int64_t flag_increment_at_element,
                  int8_t* io_buffer){
//            const int num_threads = omp_get_max_threads();                                 //only_for_context cpu_openmp
//            const int64_t capacity = ParticlesData_get__capacity(particles);               //only_for_context cpu_openmp
//            const int64_t chunk_size = (capacity + num_threads - 1)/num_threads; // ceil division  //only_for_context cpu_openmp
//            #pragma omp parallel for                                                       //only_for_context cpu_openmp
//            for (int64_t batch_id = 0; batch_id < num_threads; batch_id++) {               //only_for_context cpu_openmp
                LocalParticle lpart;
                lpart.io_buffer = io_buffer;
//                int64_t part_id = batch_id * chunk_size;                                       //only_for_context cpu_openmp
//                int64_t end_id = (batch_id + 1) * chunk_size;                                  //only_for_context cpu_openmp
//                if (end_id > capacity) end_id = capacity;                                      //only_for_context cpu_openmp
    
                int64_t part_id = 0;                    //only_for_context cpu_serial
//                int64_t part_id = blockDim.x * blockIdx.x + threadIdx.x; //only_for_context cuda
//                int64_t part_id = get_global_id(0);                    //only_for_context opencl
                int64_t end_id = 0; // unused outside of openmp  //only_for_context cpu_serial cuda opencl
    
                int64_t part_capacity = ParticlesData_get__capacity(particles);
                if (part_id<part_capacity){
                    Particles_to_LocalParticle(particles, &lpart, part_id, end_id);
                    if (check_is_active(&lpart)>0){
              SRotation_track_local_particle(el, &lpart);

                    }
                    if (check_is_active(&lpart)>0 && flag_increment_at_element){
                            increment_at_element(&lpart, 1);
                    }
                }
//            } //only_for_context cpu_openmp
            
            // On OpenMP we want to additionally by default reorganize all
            // the particles.
//            #ifndef XT_OMP_SKIP_REORGANIZE                             //only_for_context cpu_openmp
//            LocalParticle lpart;                                       //only_for_context cpu_openmp
//            lpart.io_buffer = io_buffer;                               //only_for_context cpu_openmp
//            Particles_to_LocalParticle(particles, &lpart, 0, capacity);//only_for_context cpu_openmp
//            check_is_active(&lpart);                                   //only_for_context cpu_openmp
//            #endif                                                     //only_for_context cpu_openmp
        }

#ifndef XOBJ_TYPEDEF_SextupoleData
#define XOBJ_TYPEDEF_SextupoleData
typedef   struct SextupoleData_s * SextupoleData;
 static inline SextupoleData SextupoleData_getp(SextupoleData  obj){
  int64_t offset=0;
  return (SextupoleData)(( char*) obj+offset);
}
 static inline double SextupoleData_get_k2(const SextupoleData  obj){
  int64_t offset=0;
  return *( double*)(( char*) obj+offset);
}
 static inline void SextupoleData_set_k2(SextupoleData  obj, double value){
  int64_t offset=0;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* SextupoleData_getp_k2(SextupoleData  obj){
  int64_t offset=0;
  return ( double*)(( char*) obj+offset);
}
 static inline double SextupoleData_get_k2s(const SextupoleData  obj){
  int64_t offset=0;
  offset+=8;
  return *( double*)(( char*) obj+offset);
}
 static inline void SextupoleData_set_k2s(SextupoleData  obj, double value){
  int64_t offset=0;
  offset+=8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* SextupoleData_getp_k2s(SextupoleData  obj){
  int64_t offset=0;
  offset+=8;
  return ( double*)(( char*) obj+offset);
}
 static inline double SextupoleData_get_length(const SextupoleData  obj){
  int64_t offset=0;
  offset+=16;
  return *( double*)(( char*) obj+offset);
}
 static inline void SextupoleData_set_length(SextupoleData  obj, double value){
  int64_t offset=0;
  offset+=16;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* SextupoleData_getp_length(SextupoleData  obj){
  int64_t offset=0;
  offset+=16;
  return ( double*)(( char*) obj+offset);
}
#endif
// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_CONSTANTS_H
#define XTRACK_CONSTANTS_H

#if !defined( C_LIGHT )
    #define   C_LIGHT ( 299792458.0 )
#endif /* !defined( C_LIGHT ) */

#if !defined( EPSILON_0 )
    #define   EPSILON_0 (8.854187817620e-12)
#endif /* !defined( EPSILON_0 ) */

#if !defined( PI )
    #define PI (3.1415926535897932384626433832795028841971693993751)
#endif /* !defined( PI ) */

#if !defined( MU_0 )
    #define MU_0 (PI*4.0e-7)
#endif /* !defined( MU_0 ) */

#if !defined( DEG2RAD )
    #define DEG2RAD (0.0174532925199432957692369076848861271344287188854)
#endif /* !defiend( DEG2RAD ) */

#if !defined( RAD2DEG )
    #define RAD2DEG (57.29577951308232087679815481410517033240547246656442)
#endif /* !defiend( RAD2DEG ) */

#if !defined( SQRT_PI )
    #define SQRT_PI (1.7724538509055160272981674833411451827975494561224)
#endif /* !defined( SQRT_PI ) */

#if !defined( QELEM )
    #define QELEM (1.60217662e-19)
#endif /* !defined( QELEM ) */

#if !defined( DBL_MAX )
    #define DBL_MAX (1.7976931348623158e+308)
#endif /* !defined( DBL_MAX ) */

#if !defined( DBL_MIN )
    #define DBL_MIN (2.2250738585072014e-308)
#endif /* !defined( DBL_MIN ) */

#if !defined( DBL_EPSILON )
    #define DBL_EPSILON (2.2204460492503131e-16)
#endif /* !defined( DBL_EPSILON ) */

#endif /* XTRACK_CONSTANTS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_FUNCTIONS_H
#define XTRACK_FUNCTIONS_H

 static inline
void kill_all_particles(LocalParticle* part0, int64_t kill_state) {

    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

        LocalParticle_kill_particle(part, kill_state);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

}


 static inline
int8_t assert_tracking(LocalParticle* part, int64_t kill_state){
    // Whenever we are not tracking, e.g. in a twiss, the particle will be at_turn < 0.
    // We test this to distinguish genuine tracking from twiss.
    if (LocalParticle_get_at_turn(part) < 0){
        LocalParticle_kill_particle(part, kill_state);
        return 0;
    }
    return 1;
}

#endif /* XTRACK_FUNCTIONS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_PARTICLE_STATES_H
#define XTRACK_PARTICLE_STATES_H

#define  XT_LOST_ON_APERTURE       0
#define  XT_LOST_ON_LONG_CUT      -2
#define  XT_LOST_ALL_E_IN_SYNRAD -10
#define  RNG_ERR_SEEDS_NOT_SET   -20
#define  RNG_ERR_INVALID_TRACK   -21
#define  RNG_ERR_RUTH_NOT_SET    -22

#endif /* XTRACK_PARTICLE_STATES_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_DRIFT_H
#define XTRACK_DRIFT_H


 static inline
void Drift_single_particle(LocalParticle* part, double length){

    #ifndef XTRACK_USE_EXACT_DRIFTS

        double const rpp    = LocalParticle_get_rpp(part);
        double const rv0v    = 1./LocalParticle_get_rvv(part);
        double const xp     = LocalParticle_get_px(part) * rpp;
        double const yp     = LocalParticle_get_py(part) * rpp;
        double const dzeta  = 1 - rv0v * ( 1. + ( xp*xp + yp*yp ) / 2. );

        LocalParticle_add_to_x(part, xp * length );
        LocalParticle_add_to_y(part, yp * length );
        LocalParticle_add_to_s(part, length);
        LocalParticle_add_to_zeta(part, length * dzeta );

    #else

        double const px = LocalParticle_get_px(part);
        double const py = LocalParticle_get_py(part);
        double const rv0v    = 1./LocalParticle_get_rvv(part);
        double const one_plus_delta = 1. + LocalParticle_get_delta(part);

        double const one_over_pz = 1./sqrt(one_plus_delta*one_plus_delta
                                           - px * px - py * py);
        double const dzeta = 1 - rv0v * one_plus_delta * one_over_pz;

        LocalParticle_add_to_x(part, px * one_over_pz * length);
        LocalParticle_add_to_y(part, py * one_over_pz * length);
        LocalParticle_add_to_zeta(part, dzeta * length);
        LocalParticle_add_to_s(part, length);

    #endif
}

#endif /* XTRACK_DRIFT_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_SEXTUPOLE_H
#define XTRACK_SEXTUPOLE_H

 static inline
void Sextupole_track_local_particle(
        SextupoleData el,
        LocalParticle* part0
) {
    double length = SextupoleData_get_length(el);

    #ifdef XSUITE_BACKTRACK
        length = -length;
    #endif

    double const k2 = SextupoleData_get_k2(el);
    double const k2s = SextupoleData_get_k2s(el);

    double const knl2 = k2 * length;
    double const ksl2 = k2s * length;


    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp


        // Drift
        Drift_single_particle(part, length / 2.);

        // Sextupolar kick
        int64_t index = 2;
        double const inv_factorial = 0.5; // 1 / factorial(2)
        double dpx = knl2 * inv_factorial;
        double dpy = ksl2 * inv_factorial;

        double const x   = LocalParticle_get_x(part);
        double const y   = LocalParticle_get_y(part);
        double const chi = LocalParticle_get_chi(part);

        while( index > 0 )
        {
            double const zre = dpx * x - dpy * y;
            double const zim = dpx * y + dpy * x;

            index -= 1;

            dpx = zre;
            dpy = zim;
        }

        dpx = -chi * dpx; // rad
        dpy =  chi * dpy; // rad

        LocalParticle_add_to_px(part, dpx);
        LocalParticle_add_to_py(part, dpy);

        // Drift
        Drift_single_particle(part, length / 2.);



//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }



}

#endif // XTRACK_SEXTUPOLE_H

             
            void Sextupole_track_particles(
               SextupoleData el,

                             ParticlesData particles,

                             int64_t flag_increment_at_element,
                  int8_t* io_buffer){
//            const int num_threads = omp_get_max_threads();                                 //only_for_context cpu_openmp
//            const int64_t capacity = ParticlesData_get__capacity(particles);               //only_for_context cpu_openmp
//            const int64_t chunk_size = (capacity + num_threads - 1)/num_threads; // ceil division  //only_for_context cpu_openmp
//            #pragma omp parallel for                                                       //only_for_context cpu_openmp
//            for (int64_t batch_id = 0; batch_id < num_threads; batch_id++) {               //only_for_context cpu_openmp
                LocalParticle lpart;
                lpart.io_buffer = io_buffer;
//                int64_t part_id = batch_id * chunk_size;                                       //only_for_context cpu_openmp
//                int64_t end_id = (batch_id + 1) * chunk_size;                                  //only_for_context cpu_openmp
//                if (end_id > capacity) end_id = capacity;                                      //only_for_context cpu_openmp
    
                int64_t part_id = 0;                    //only_for_context cpu_serial
//                int64_t part_id = blockDim.x * blockIdx.x + threadIdx.x; //only_for_context cuda
//                int64_t part_id = get_global_id(0);                    //only_for_context opencl
                int64_t end_id = 0; // unused outside of openmp  //only_for_context cpu_serial cuda opencl
    
                int64_t part_capacity = ParticlesData_get__capacity(particles);
                if (part_id<part_capacity){
                    Particles_to_LocalParticle(particles, &lpart, part_id, end_id);
                    if (check_is_active(&lpart)>0){
              Sextupole_track_local_particle(el, &lpart);

                    }
                    if (check_is_active(&lpart)>0 && flag_increment_at_element){
                            increment_at_element(&lpart, 1);
                    }
                }
//            } //only_for_context cpu_openmp
            
            // On OpenMP we want to additionally by default reorganize all
            // the particles.
//            #ifndef XT_OMP_SKIP_REORGANIZE                             //only_for_context cpu_openmp
//            LocalParticle lpart;                                       //only_for_context cpu_openmp
//            lpart.io_buffer = io_buffer;                               //only_for_context cpu_openmp
//            Particles_to_LocalParticle(particles, &lpart, 0, capacity);//only_for_context cpu_openmp
//            check_is_active(&lpart);                                   //only_for_context cpu_openmp
//            #endif                                                     //only_for_context cpu_openmp
        }

#ifndef XOBJ_TYPEDEF_SimpleThinBendData
#define XOBJ_TYPEDEF_SimpleThinBendData
typedef   struct SimpleThinBendData_s * SimpleThinBendData;
 static inline SimpleThinBendData SimpleThinBendData_getp(SimpleThinBendData  obj){
  int64_t offset=0;
  return (SimpleThinBendData)(( char*) obj+offset);
}
 static inline Arr1Float64 SimpleThinBendData_getp_knl(SimpleThinBendData  obj){
  int64_t offset=0;
  return (Arr1Float64)(( char*) obj+offset);
}
 static inline int64_t SimpleThinBendData_len_knl(SimpleThinBendData  obj){
  return 1;
}
 static inline double SimpleThinBendData_get_knl(const SimpleThinBendData  obj, int64_t i0){
  int64_t offset=0;
  offset+=i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void SimpleThinBendData_set_knl(SimpleThinBendData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* SimpleThinBendData_getp1_knl(SimpleThinBendData  obj, int64_t i0){
  int64_t offset=0;
  offset+=i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline double SimpleThinBendData_get_hxl(const SimpleThinBendData  obj){
  int64_t offset=0;
  offset+=8;
  return *( double*)(( char*) obj+offset);
}
 static inline void SimpleThinBendData_set_hxl(SimpleThinBendData  obj, double value){
  int64_t offset=0;
  offset+=8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* SimpleThinBendData_getp_hxl(SimpleThinBendData  obj){
  int64_t offset=0;
  offset+=8;
  return ( double*)(( char*) obj+offset);
}
 static inline double SimpleThinBendData_get_length(const SimpleThinBendData  obj){
  int64_t offset=0;
  offset+=16;
  return *( double*)(( char*) obj+offset);
}
 static inline void SimpleThinBendData_set_length(SimpleThinBendData  obj, double value){
  int64_t offset=0;
  offset+=16;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* SimpleThinBendData_getp_length(SimpleThinBendData  obj){
  int64_t offset=0;
  offset+=16;
  return ( double*)(( char*) obj+offset);
}
#endif
// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_CONSTANTS_H
#define XTRACK_CONSTANTS_H

#if !defined( C_LIGHT )
    #define   C_LIGHT ( 299792458.0 )
#endif /* !defined( C_LIGHT ) */

#if !defined( EPSILON_0 )
    #define   EPSILON_0 (8.854187817620e-12)
#endif /* !defined( EPSILON_0 ) */

#if !defined( PI )
    #define PI (3.1415926535897932384626433832795028841971693993751)
#endif /* !defined( PI ) */

#if !defined( MU_0 )
    #define MU_0 (PI*4.0e-7)
#endif /* !defined( MU_0 ) */

#if !defined( DEG2RAD )
    #define DEG2RAD (0.0174532925199432957692369076848861271344287188854)
#endif /* !defiend( DEG2RAD ) */

#if !defined( RAD2DEG )
    #define RAD2DEG (57.29577951308232087679815481410517033240547246656442)
#endif /* !defiend( RAD2DEG ) */

#if !defined( SQRT_PI )
    #define SQRT_PI (1.7724538509055160272981674833411451827975494561224)
#endif /* !defined( SQRT_PI ) */

#if !defined( QELEM )
    #define QELEM (1.60217662e-19)
#endif /* !defined( QELEM ) */

#if !defined( DBL_MAX )
    #define DBL_MAX (1.7976931348623158e+308)
#endif /* !defined( DBL_MAX ) */

#if !defined( DBL_MIN )
    #define DBL_MIN (2.2250738585072014e-308)
#endif /* !defined( DBL_MIN ) */

#if !defined( DBL_EPSILON )
    #define DBL_EPSILON (2.2204460492503131e-16)
#endif /* !defined( DBL_EPSILON ) */

#endif /* XTRACK_CONSTANTS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_FUNCTIONS_H
#define XTRACK_FUNCTIONS_H

 static inline
void kill_all_particles(LocalParticle* part0, int64_t kill_state) {

    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

        LocalParticle_kill_particle(part, kill_state);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

}


 static inline
int8_t assert_tracking(LocalParticle* part, int64_t kill_state){
    // Whenever we are not tracking, e.g. in a twiss, the particle will be at_turn < 0.
    // We test this to distinguish genuine tracking from twiss.
    if (LocalParticle_get_at_turn(part) < 0){
        LocalParticle_kill_particle(part, kill_state);
        return 0;
    }
    return 1;
}

#endif /* XTRACK_FUNCTIONS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_PARTICLE_STATES_H
#define XTRACK_PARTICLE_STATES_H

#define  XT_LOST_ON_APERTURE       0
#define  XT_LOST_ON_LONG_CUT      -2
#define  XT_LOST_ALL_E_IN_SYNRAD -10
#define  RNG_ERR_SEEDS_NOT_SET   -20
#define  RNG_ERR_INVALID_TRACK   -21
#define  RNG_ERR_RUTH_NOT_SET    -22

#endif /* XTRACK_PARTICLE_STATES_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_FASTDIPOLE_H
#define XTRACK_FASTDIPOLE_H



 static inline
void SimpleThinBend_track_local_particle(SimpleThinBendData el, LocalParticle* part0){
        // Horizontal bend

        double knl0 = SimpleThinBendData_get_knl(el, 0);
        double hxl = SimpleThinBendData_get_hxl(el);
        double length = SimpleThinBendData_get_length(el); // m

        #ifdef XSUITE_BACKTRACK
            knl0 = -knl0;
            hxl = -hxl;
            length = -length;
        #endif


    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

            double const chi = LocalParticle_get_chi(part);

            double dpx = - chi * knl0;

            if((hxl > 0) || (hxl < 0))
            {
                double const delta = LocalParticle_get_delta(part);
                double const x = LocalParticle_get_x(part);

                double const hxlx = x * hxl;

                double const rv0v = 1./LocalParticle_get_rvv(part);

                LocalParticle_add_to_zeta(part, rv0v*chi * (-hxlx));

                dpx += hxl + hxl * delta;

                if( length != 0)
                {
                    double const b1l = chi * knl0;
                    dpx -= b1l * hxlx / length;
                }
            }

            LocalParticle_add_to_px(part, dpx);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

}

#endif


             
            void SimpleThinBend_track_particles(
               SimpleThinBendData el,

                             ParticlesData particles,

                             int64_t flag_increment_at_element,
                  int8_t* io_buffer){
//            const int num_threads = omp_get_max_threads();                                 //only_for_context cpu_openmp
//            const int64_t capacity = ParticlesData_get__capacity(particles);               //only_for_context cpu_openmp
//            const int64_t chunk_size = (capacity + num_threads - 1)/num_threads; // ceil division  //only_for_context cpu_openmp
//            #pragma omp parallel for                                                       //only_for_context cpu_openmp
//            for (int64_t batch_id = 0; batch_id < num_threads; batch_id++) {               //only_for_context cpu_openmp
                LocalParticle lpart;
                lpart.io_buffer = io_buffer;
//                int64_t part_id = batch_id * chunk_size;                                       //only_for_context cpu_openmp
//                int64_t end_id = (batch_id + 1) * chunk_size;                                  //only_for_context cpu_openmp
//                if (end_id > capacity) end_id = capacity;                                      //only_for_context cpu_openmp
    
                int64_t part_id = 0;                    //only_for_context cpu_serial
//                int64_t part_id = blockDim.x * blockIdx.x + threadIdx.x; //only_for_context cuda
//                int64_t part_id = get_global_id(0);                    //only_for_context opencl
                int64_t end_id = 0; // unused outside of openmp  //only_for_context cpu_serial cuda opencl
    
                int64_t part_capacity = ParticlesData_get__capacity(particles);
                if (part_id<part_capacity){
                    Particles_to_LocalParticle(particles, &lpart, part_id, end_id);
                    if (check_is_active(&lpart)>0){
              SimpleThinBend_track_local_particle(el, &lpart);

                    }
                    if (check_is_active(&lpart)>0 && flag_increment_at_element){
                            increment_at_element(&lpart, 1);
                    }
                }
//            } //only_for_context cpu_openmp
            
            // On OpenMP we want to additionally by default reorganize all
            // the particles.
//            #ifndef XT_OMP_SKIP_REORGANIZE                             //only_for_context cpu_openmp
//            LocalParticle lpart;                                       //only_for_context cpu_openmp
//            lpart.io_buffer = io_buffer;                               //only_for_context cpu_openmp
//            Particles_to_LocalParticle(particles, &lpart, 0, capacity);//only_for_context cpu_openmp
//            check_is_active(&lpart);                                   //only_for_context cpu_openmp
//            #endif                                                     //only_for_context cpu_openmp
        }

#ifndef XOBJ_TYPEDEF_SimpleThinQuadrupoleData
#define XOBJ_TYPEDEF_SimpleThinQuadrupoleData
typedef   struct SimpleThinQuadrupoleData_s * SimpleThinQuadrupoleData;
 static inline SimpleThinQuadrupoleData SimpleThinQuadrupoleData_getp(SimpleThinQuadrupoleData  obj){
  int64_t offset=0;
  return (SimpleThinQuadrupoleData)(( char*) obj+offset);
}
 static inline Arr2Float64 SimpleThinQuadrupoleData_getp_knl(SimpleThinQuadrupoleData  obj){
  int64_t offset=0;
  return (Arr2Float64)(( char*) obj+offset);
}
 static inline int64_t SimpleThinQuadrupoleData_len_knl(SimpleThinQuadrupoleData  obj){
  return 2;
}
 static inline double SimpleThinQuadrupoleData_get_knl(const SimpleThinQuadrupoleData  obj, int64_t i0){
  int64_t offset=0;
  offset+=i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void SimpleThinQuadrupoleData_set_knl(SimpleThinQuadrupoleData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* SimpleThinQuadrupoleData_getp1_knl(SimpleThinQuadrupoleData  obj, int64_t i0){
  int64_t offset=0;
  offset+=i0*8;
  return ( double*)(( char*) obj+offset);
}
#endif
// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_CONSTANTS_H
#define XTRACK_CONSTANTS_H

#if !defined( C_LIGHT )
    #define   C_LIGHT ( 299792458.0 )
#endif /* !defined( C_LIGHT ) */

#if !defined( EPSILON_0 )
    #define   EPSILON_0 (8.854187817620e-12)
#endif /* !defined( EPSILON_0 ) */

#if !defined( PI )
    #define PI (3.1415926535897932384626433832795028841971693993751)
#endif /* !defined( PI ) */

#if !defined( MU_0 )
    #define MU_0 (PI*4.0e-7)
#endif /* !defined( MU_0 ) */

#if !defined( DEG2RAD )
    #define DEG2RAD (0.0174532925199432957692369076848861271344287188854)
#endif /* !defiend( DEG2RAD ) */

#if !defined( RAD2DEG )
    #define RAD2DEG (57.29577951308232087679815481410517033240547246656442)
#endif /* !defiend( RAD2DEG ) */

#if !defined( SQRT_PI )
    #define SQRT_PI (1.7724538509055160272981674833411451827975494561224)
#endif /* !defined( SQRT_PI ) */

#if !defined( QELEM )
    #define QELEM (1.60217662e-19)
#endif /* !defined( QELEM ) */

#if !defined( DBL_MAX )
    #define DBL_MAX (1.7976931348623158e+308)
#endif /* !defined( DBL_MAX ) */

#if !defined( DBL_MIN )
    #define DBL_MIN (2.2250738585072014e-308)
#endif /* !defined( DBL_MIN ) */

#if !defined( DBL_EPSILON )
    #define DBL_EPSILON (2.2204460492503131e-16)
#endif /* !defined( DBL_EPSILON ) */

#endif /* XTRACK_CONSTANTS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_FUNCTIONS_H
#define XTRACK_FUNCTIONS_H

 static inline
void kill_all_particles(LocalParticle* part0, int64_t kill_state) {

    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

        LocalParticle_kill_particle(part, kill_state);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

}


 static inline
int8_t assert_tracking(LocalParticle* part, int64_t kill_state){
    // Whenever we are not tracking, e.g. in a twiss, the particle will be at_turn < 0.
    // We test this to distinguish genuine tracking from twiss.
    if (LocalParticle_get_at_turn(part) < 0){
        LocalParticle_kill_particle(part, kill_state);
        return 0;
    }
    return 1;
}

#endif /* XTRACK_FUNCTIONS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_PARTICLE_STATES_H
#define XTRACK_PARTICLE_STATES_H

#define  XT_LOST_ON_APERTURE       0
#define  XT_LOST_ON_LONG_CUT      -2
#define  XT_LOST_ALL_E_IN_SYNRAD -10
#define  RNG_ERR_SEEDS_NOT_SET   -20
#define  RNG_ERR_INVALID_TRACK   -21
#define  RNG_ERR_RUTH_NOT_SET    -22

#endif /* XTRACK_PARTICLE_STATES_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_FASTQUADRUPOLE_H
#define XTRACK_FASTQUADRUPOLE_H

 static inline
void SimpleThinQuadrupole_track_local_particle(SimpleThinQuadrupoleData el, LocalParticle* part0){

    double knl1 = SimpleThinQuadrupoleData_get_knl(el, 1);

    #ifdef XSUITE_BACKTRACK
        knl1 = -knl1;
    #endif


    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

        double const x = LocalParticle_get_x(part);
        double const y = LocalParticle_get_y(part);
        double const chi = LocalParticle_get_chi(part);


        double const dpx = - chi * knl1 * x;
        double const dpy = chi * knl1 * y;

        LocalParticle_add_to_px(part, dpx);
        LocalParticle_add_to_py(part, dpy);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

}

#endif


             
            void SimpleThinQuadrupole_track_particles(
               SimpleThinQuadrupoleData el,

                             ParticlesData particles,

                             int64_t flag_increment_at_element,
                  int8_t* io_buffer){
//            const int num_threads = omp_get_max_threads();                                 //only_for_context cpu_openmp
//            const int64_t capacity = ParticlesData_get__capacity(particles);               //only_for_context cpu_openmp
//            const int64_t chunk_size = (capacity + num_threads - 1)/num_threads; // ceil division  //only_for_context cpu_openmp
//            #pragma omp parallel for                                                       //only_for_context cpu_openmp
//            for (int64_t batch_id = 0; batch_id < num_threads; batch_id++) {               //only_for_context cpu_openmp
                LocalParticle lpart;
                lpart.io_buffer = io_buffer;
//                int64_t part_id = batch_id * chunk_size;                                       //only_for_context cpu_openmp
//                int64_t end_id = (batch_id + 1) * chunk_size;                                  //only_for_context cpu_openmp
//                if (end_id > capacity) end_id = capacity;                                      //only_for_context cpu_openmp
    
                int64_t part_id = 0;                    //only_for_context cpu_serial
//                int64_t part_id = blockDim.x * blockIdx.x + threadIdx.x; //only_for_context cuda
//                int64_t part_id = get_global_id(0);                    //only_for_context opencl
                int64_t end_id = 0; // unused outside of openmp  //only_for_context cpu_serial cuda opencl
    
                int64_t part_capacity = ParticlesData_get__capacity(particles);
                if (part_id<part_capacity){
                    Particles_to_LocalParticle(particles, &lpart, part_id, end_id);
                    if (check_is_active(&lpart)>0){
              SimpleThinQuadrupole_track_local_particle(el, &lpart);

                    }
                    if (check_is_active(&lpart)>0 && flag_increment_at_element){
                            increment_at_element(&lpart, 1);
                    }
                }
//            } //only_for_context cpu_openmp
            
            // On OpenMP we want to additionally by default reorganize all
            // the particles.
//            #ifndef XT_OMP_SKIP_REORGANIZE                             //only_for_context cpu_openmp
//            LocalParticle lpart;                                       //only_for_context cpu_openmp
//            lpart.io_buffer = io_buffer;                               //only_for_context cpu_openmp
//            Particles_to_LocalParticle(particles, &lpart, 0, capacity);//only_for_context cpu_openmp
//            check_is_active(&lpart);                                   //only_for_context cpu_openmp
//            #endif                                                     //only_for_context cpu_openmp
        }

#ifndef XOBJ_TYPEDEF_SolenoidData
#define XOBJ_TYPEDEF_SolenoidData
typedef   struct SolenoidData_s * SolenoidData;
 static inline SolenoidData SolenoidData_getp(SolenoidData  obj){
  int64_t offset=0;
  return (SolenoidData)(( char*) obj+offset);
}
 static inline double SolenoidData_get_length(const SolenoidData  obj){
  int64_t offset=0;
  return *( double*)(( char*) obj+offset);
}
 static inline void SolenoidData_set_length(SolenoidData  obj, double value){
  int64_t offset=0;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* SolenoidData_getp_length(SolenoidData  obj){
  int64_t offset=0;
  return ( double*)(( char*) obj+offset);
}
 static inline double SolenoidData_get_ks(const SolenoidData  obj){
  int64_t offset=0;
  offset+=8;
  return *( double*)(( char*) obj+offset);
}
 static inline void SolenoidData_set_ks(SolenoidData  obj, double value){
  int64_t offset=0;
  offset+=8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* SolenoidData_getp_ks(SolenoidData  obj){
  int64_t offset=0;
  offset+=8;
  return ( double*)(( char*) obj+offset);
}
 static inline double SolenoidData_get_ksi(const SolenoidData  obj){
  int64_t offset=0;
  offset+=16;
  return *( double*)(( char*) obj+offset);
}
 static inline void SolenoidData_set_ksi(SolenoidData  obj, double value){
  int64_t offset=0;
  offset+=16;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* SolenoidData_getp_ksi(SolenoidData  obj){
  int64_t offset=0;
  offset+=16;
  return ( double*)(( char*) obj+offset);
}
#endif
// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_CONSTANTS_H
#define XTRACK_CONSTANTS_H

#if !defined( C_LIGHT )
    #define   C_LIGHT ( 299792458.0 )
#endif /* !defined( C_LIGHT ) */

#if !defined( EPSILON_0 )
    #define   EPSILON_0 (8.854187817620e-12)
#endif /* !defined( EPSILON_0 ) */

#if !defined( PI )
    #define PI (3.1415926535897932384626433832795028841971693993751)
#endif /* !defined( PI ) */

#if !defined( MU_0 )
    #define MU_0 (PI*4.0e-7)
#endif /* !defined( MU_0 ) */

#if !defined( DEG2RAD )
    #define DEG2RAD (0.0174532925199432957692369076848861271344287188854)
#endif /* !defiend( DEG2RAD ) */

#if !defined( RAD2DEG )
    #define RAD2DEG (57.29577951308232087679815481410517033240547246656442)
#endif /* !defiend( RAD2DEG ) */

#if !defined( SQRT_PI )
    #define SQRT_PI (1.7724538509055160272981674833411451827975494561224)
#endif /* !defined( SQRT_PI ) */

#if !defined( QELEM )
    #define QELEM (1.60217662e-19)
#endif /* !defined( QELEM ) */

#if !defined( DBL_MAX )
    #define DBL_MAX (1.7976931348623158e+308)
#endif /* !defined( DBL_MAX ) */

#if !defined( DBL_MIN )
    #define DBL_MIN (2.2250738585072014e-308)
#endif /* !defined( DBL_MIN ) */

#if !defined( DBL_EPSILON )
    #define DBL_EPSILON (2.2204460492503131e-16)
#endif /* !defined( DBL_EPSILON ) */

#endif /* XTRACK_CONSTANTS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_FUNCTIONS_H
#define XTRACK_FUNCTIONS_H

 static inline
void kill_all_particles(LocalParticle* part0, int64_t kill_state) {

    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

        LocalParticle_kill_particle(part, kill_state);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

}


 static inline
int8_t assert_tracking(LocalParticle* part, int64_t kill_state){
    // Whenever we are not tracking, e.g. in a twiss, the particle will be at_turn < 0.
    // We test this to distinguish genuine tracking from twiss.
    if (LocalParticle_get_at_turn(part) < 0){
        LocalParticle_kill_particle(part, kill_state);
        return 0;
    }
    return 1;
}

#endif /* XTRACK_FUNCTIONS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_PARTICLE_STATES_H
#define XTRACK_PARTICLE_STATES_H

#define  XT_LOST_ON_APERTURE       0
#define  XT_LOST_ON_LONG_CUT      -2
#define  XT_LOST_ALL_E_IN_SYNRAD -10
#define  RNG_ERR_SEEDS_NOT_SET   -20
#define  RNG_ERR_INVALID_TRACK   -21
#define  RNG_ERR_RUTH_NOT_SET    -22

#endif /* XTRACK_PARTICLE_STATES_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_DRIFT_H
#define XTRACK_DRIFT_H


 static inline
void Drift_single_particle(LocalParticle* part, double length){

    #ifndef XTRACK_USE_EXACT_DRIFTS

        double const rpp    = LocalParticle_get_rpp(part);
        double const rv0v    = 1./LocalParticle_get_rvv(part);
        double const xp     = LocalParticle_get_px(part) * rpp;
        double const yp     = LocalParticle_get_py(part) * rpp;
        double const dzeta  = 1 - rv0v * ( 1. + ( xp*xp + yp*yp ) / 2. );

        LocalParticle_add_to_x(part, xp * length );
        LocalParticle_add_to_y(part, yp * length );
        LocalParticle_add_to_s(part, length);
        LocalParticle_add_to_zeta(part, length * dzeta );

    #else

        double const px = LocalParticle_get_px(part);
        double const py = LocalParticle_get_py(part);
        double const rv0v    = 1./LocalParticle_get_rvv(part);
        double const one_plus_delta = 1. + LocalParticle_get_delta(part);

        double const one_over_pz = 1./sqrt(one_plus_delta*one_plus_delta
                                           - px * px - py * py);
        double const dzeta = 1 - rv0v * one_plus_delta * one_over_pz;

        LocalParticle_add_to_x(part, px * one_over_pz * length);
        LocalParticle_add_to_y(part, py * one_over_pz * length);
        LocalParticle_add_to_zeta(part, dzeta * length);
        LocalParticle_add_to_s(part, length);

    #endif
}

#endif /* XTRACK_DRIFT_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_SOLENOID_H
#define XTRACK_SOLENOID_H

#define IS_ZERO(X) (fabs(X) < 1e-9)

 static inline
void Solenoid_thin_track_single_particle(LocalParticle*, double, double, double);

 static inline
void Solenoid_thick_track_single_particle(LocalParticle*, double, double);


 static inline
void Solenoid_track_local_particle(SolenoidData el, LocalParticle* part0) {
    // Parameters
    const double length = SolenoidData_get_length(el);
    const double ks = SolenoidData_get_ks(el);
    const double ksi = SolenoidData_get_ksi(el);

    if (IS_ZERO(length)) {

    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

        Solenoid_thin_track_single_particle(part, length, ks, ksi);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

    }
    else {

    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

        Solenoid_thick_track_single_particle(part, length, ks);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

    }
}


 static inline
void Solenoid_thin_track_single_particle(
    LocalParticle* part,
    double length,
    double ks,
    double ksi
) {
    const double sk = ks / 2;  // todo?: flip sign to change beam direction
    const double skl = ksi / 2;
    const double beta0 = LocalParticle_get_beta0(part);

    // Particle coordinates
    const double x = LocalParticle_get_x(part);
    const double px = LocalParticle_get_px(part);
    const double y = LocalParticle_get_y(part);
    const double py = LocalParticle_get_py(part);
    const double t = LocalParticle_get_zeta(part) / beta0;
    const double pt = LocalParticle_get_ptau(part);
    const double delta = LocalParticle_get_delta(part);

    // Useful quantities
    const double psigf = pt / beta0;
    const double betas = beta0;

    const double onedp = 1 + delta;
    const double fppsig = (1 + (betas * betas) * psigf) / onedp;

    // Set up C, S, Q, R, Z
    const double cosTh = cos(skl / onedp);
    const double sinTh = sin(skl / onedp);
    const double Q = -skl * sk / onedp;
    const double Z = fppsig / (onedp * onedp) * skl;
    const double R = Z * sk;

    const double pxf = px + x * Q;
    const double pyf = py + y * Q;
    const double sigf = t * betas - 0.5 * (x * x + y * y) * R;

    // Final angles after solenoid
    const double pxf_ =  pxf * cosTh + pyf * sinTh;
    const double pyf_ = -pxf * sinTh + pyf * cosTh;

    // Calculate new coordinates
    const double new_x = x  * cosTh  +  y  * sinTh;
    const double new_px = pxf_;
    const double new_y = -x  * sinTh  +  y  * cosTh;
    const double new_py = pyf_;
    const double new_zeta = sigf + (x * new_py - y * new_px) * Z;

    LocalParticle_set_x(part, new_x);
    LocalParticle_set_px(part, new_px);
    LocalParticle_set_y(part, new_y);
    LocalParticle_set_py(part, new_py);
    LocalParticle_set_zeta(part, new_zeta);
}


 static inline
void Solenoid_thick_track_single_particle(
    LocalParticle* part,
    double length,
    double ks
) {
    const double sk = ks / 2;  // todo?: flip sign to change beam direction

    if (IS_ZERO(sk)) {
        Drift_single_particle(part, length);
        return;
    }

    const double skl = sk * length;

    // Particle coordinates
    const double x = LocalParticle_get_x(part);
    const double px = LocalParticle_get_px(part);
    const double y = LocalParticle_get_y(part);
    const double py = LocalParticle_get_py(part);
    const double delta = LocalParticle_get_delta(part);
    const double rvv = LocalParticle_get_rvv(part);

    // set up constants
    const double pk1 = px + sk * y;
    const double pk2 = py - sk * x;
    const double ptr2 = pk1 * pk1 + pk2 * pk2;
    const double one_plus_delta = 1 + delta;
    const double one_plus_delta_sq = one_plus_delta * one_plus_delta;
    const double pz = sqrt(one_plus_delta_sq - ptr2);

    // set up constants
    const double cosTh = cos(skl / pz);
    const double sinTh = sin(skl / pz);

    const double si = sin(skl / pz) / sk;
    const double rps[4] = {
        cosTh * x + sinTh * y,
        cosTh * px + sinTh * py,
        cosTh * y - sinTh * x,
        cosTh * py - sinTh * px
    };
    const double new_x = cosTh * rps[0] + si * rps[1];
    const double new_px = cosTh * rps[1] - sk * sinTh * rps[0];
    const double new_y = cosTh * rps[2] + si * rps[3];
    const double new_py = cosTh * rps[3] - sk * sinTh * rps[2];
    const double add_to_zeta = length * (1 - one_plus_delta / (pz * rvv));

    LocalParticle_set_x(part, new_x);
    LocalParticle_set_px(part, new_px);
    LocalParticle_set_y(part, new_y);
    LocalParticle_set_py(part, new_py);
    LocalParticle_add_to_zeta(part, add_to_zeta);
    LocalParticle_add_to_s(part, length);
}

#endif // XTRACK_SOLENOID_H

             
            void Solenoid_track_particles(
               SolenoidData el,

                             ParticlesData particles,

                             int64_t flag_increment_at_element,
                  int8_t* io_buffer){
//            const int num_threads = omp_get_max_threads();                                 //only_for_context cpu_openmp
//            const int64_t capacity = ParticlesData_get__capacity(particles);               //only_for_context cpu_openmp
//            const int64_t chunk_size = (capacity + num_threads - 1)/num_threads; // ceil division  //only_for_context cpu_openmp
//            #pragma omp parallel for                                                       //only_for_context cpu_openmp
//            for (int64_t batch_id = 0; batch_id < num_threads; batch_id++) {               //only_for_context cpu_openmp
                LocalParticle lpart;
                lpart.io_buffer = io_buffer;
//                int64_t part_id = batch_id * chunk_size;                                       //only_for_context cpu_openmp
//                int64_t end_id = (batch_id + 1) * chunk_size;                                  //only_for_context cpu_openmp
//                if (end_id > capacity) end_id = capacity;                                      //only_for_context cpu_openmp
    
                int64_t part_id = 0;                    //only_for_context cpu_serial
//                int64_t part_id = blockDim.x * blockIdx.x + threadIdx.x; //only_for_context cuda
//                int64_t part_id = get_global_id(0);                    //only_for_context opencl
                int64_t end_id = 0; // unused outside of openmp  //only_for_context cpu_serial cuda opencl
    
                int64_t part_capacity = ParticlesData_get__capacity(particles);
                if (part_id<part_capacity){
                    Particles_to_LocalParticle(particles, &lpart, part_id, end_id);
                    if (check_is_active(&lpart)>0){
              Solenoid_track_local_particle(el, &lpart);

                    }
                    if (check_is_active(&lpart)>0 && flag_increment_at_element){
                            increment_at_element(&lpart, 1);
                    }
                }
//            } //only_for_context cpu_openmp
            
            // On OpenMP we want to additionally by default reorganize all
            // the particles.
//            #ifndef XT_OMP_SKIP_REORGANIZE                             //only_for_context cpu_openmp
//            LocalParticle lpart;                                       //only_for_context cpu_openmp
//            lpart.io_buffer = io_buffer;                               //only_for_context cpu_openmp
//            Particles_to_LocalParticle(particles, &lpart, 0, capacity);//only_for_context cpu_openmp
//            check_is_active(&lpart);                                   //only_for_context cpu_openmp
//            #endif                                                     //only_for_context cpu_openmp
        }

#ifndef XOBJ_TYPEDEF_WireData
#define XOBJ_TYPEDEF_WireData
typedef   struct WireData_s * WireData;
 static inline WireData WireData_getp(WireData  obj){
  int64_t offset=0;
  return (WireData)(( char*) obj+offset);
}
 static inline double WireData_get_L_phy(const WireData  obj){
  int64_t offset=0;
  return *( double*)(( char*) obj+offset);
}
 static inline void WireData_set_L_phy(WireData  obj, double value){
  int64_t offset=0;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* WireData_getp_L_phy(WireData  obj){
  int64_t offset=0;
  return ( double*)(( char*) obj+offset);
}
 static inline double WireData_get_L_int(const WireData  obj){
  int64_t offset=0;
  offset+=8;
  return *( double*)(( char*) obj+offset);
}
 static inline void WireData_set_L_int(WireData  obj, double value){
  int64_t offset=0;
  offset+=8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* WireData_getp_L_int(WireData  obj){
  int64_t offset=0;
  offset+=8;
  return ( double*)(( char*) obj+offset);
}
 static inline double WireData_get_current(const WireData  obj){
  int64_t offset=0;
  offset+=16;
  return *( double*)(( char*) obj+offset);
}
 static inline void WireData_set_current(WireData  obj, double value){
  int64_t offset=0;
  offset+=16;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* WireData_getp_current(WireData  obj){
  int64_t offset=0;
  offset+=16;
  return ( double*)(( char*) obj+offset);
}
 static inline double WireData_get_xma(const WireData  obj){
  int64_t offset=0;
  offset+=24;
  return *( double*)(( char*) obj+offset);
}
 static inline void WireData_set_xma(WireData  obj, double value){
  int64_t offset=0;
  offset+=24;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* WireData_getp_xma(WireData  obj){
  int64_t offset=0;
  offset+=24;
  return ( double*)(( char*) obj+offset);
}
 static inline double WireData_get_yma(const WireData  obj){
  int64_t offset=0;
  offset+=32;
  return *( double*)(( char*) obj+offset);
}
 static inline void WireData_set_yma(WireData  obj, double value){
  int64_t offset=0;
  offset+=32;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* WireData_getp_yma(WireData  obj){
  int64_t offset=0;
  offset+=32;
  return ( double*)(( char*) obj+offset);
}
 static inline double WireData_get_post_subtract_px(const WireData  obj){
  int64_t offset=0;
  offset+=40;
  return *( double*)(( char*) obj+offset);
}
 static inline void WireData_set_post_subtract_px(WireData  obj, double value){
  int64_t offset=0;
  offset+=40;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* WireData_getp_post_subtract_px(WireData  obj){
  int64_t offset=0;
  offset+=40;
  return ( double*)(( char*) obj+offset);
}
 static inline double WireData_get_post_subtract_py(const WireData  obj){
  int64_t offset=0;
  offset+=48;
  return *( double*)(( char*) obj+offset);
}
 static inline void WireData_set_post_subtract_py(WireData  obj, double value){
  int64_t offset=0;
  offset+=48;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* WireData_getp_post_subtract_py(WireData  obj){
  int64_t offset=0;
  offset+=48;
  return ( double*)(( char*) obj+offset);
}
#endif
// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_CONSTANTS_H
#define XTRACK_CONSTANTS_H

#if !defined( C_LIGHT )
    #define   C_LIGHT ( 299792458.0 )
#endif /* !defined( C_LIGHT ) */

#if !defined( EPSILON_0 )
    #define   EPSILON_0 (8.854187817620e-12)
#endif /* !defined( EPSILON_0 ) */

#if !defined( PI )
    #define PI (3.1415926535897932384626433832795028841971693993751)
#endif /* !defined( PI ) */

#if !defined( MU_0 )
    #define MU_0 (PI*4.0e-7)
#endif /* !defined( MU_0 ) */

#if !defined( DEG2RAD )
    #define DEG2RAD (0.0174532925199432957692369076848861271344287188854)
#endif /* !defiend( DEG2RAD ) */

#if !defined( RAD2DEG )
    #define RAD2DEG (57.29577951308232087679815481410517033240547246656442)
#endif /* !defiend( RAD2DEG ) */

#if !defined( SQRT_PI )
    #define SQRT_PI (1.7724538509055160272981674833411451827975494561224)
#endif /* !defined( SQRT_PI ) */

#if !defined( QELEM )
    #define QELEM (1.60217662e-19)
#endif /* !defined( QELEM ) */

#if !defined( DBL_MAX )
    #define DBL_MAX (1.7976931348623158e+308)
#endif /* !defined( DBL_MAX ) */

#if !defined( DBL_MIN )
    #define DBL_MIN (2.2250738585072014e-308)
#endif /* !defined( DBL_MIN ) */

#if !defined( DBL_EPSILON )
    #define DBL_EPSILON (2.2204460492503131e-16)
#endif /* !defined( DBL_EPSILON ) */

#endif /* XTRACK_CONSTANTS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_FUNCTIONS_H
#define XTRACK_FUNCTIONS_H

 static inline
void kill_all_particles(LocalParticle* part0, int64_t kill_state) {

    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

        LocalParticle_kill_particle(part, kill_state);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

}


 static inline
int8_t assert_tracking(LocalParticle* part, int64_t kill_state){
    // Whenever we are not tracking, e.g. in a twiss, the particle will be at_turn < 0.
    // We test this to distinguish genuine tracking from twiss.
    if (LocalParticle_get_at_turn(part) < 0){
        LocalParticle_kill_particle(part, kill_state);
        return 0;
    }
    return 1;
}

#endif /* XTRACK_FUNCTIONS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_PARTICLE_STATES_H
#define XTRACK_PARTICLE_STATES_H

#define  XT_LOST_ON_APERTURE       0
#define  XT_LOST_ON_LONG_CUT      -2
#define  XT_LOST_ALL_E_IN_SYNRAD -10
#define  RNG_ERR_SEEDS_NOT_SET   -20
#define  RNG_ERR_INVALID_TRACK   -21
#define  RNG_ERR_RUTH_NOT_SET    -22

#endif /* XTRACK_PARTICLE_STATES_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_CONSTANTS_H
#define XTRACK_CONSTANTS_H

#if !defined( C_LIGHT )
    #define   C_LIGHT ( 299792458.0 )
#endif /* !defined( C_LIGHT ) */

#if !defined( EPSILON_0 )
    #define   EPSILON_0 (8.854187817620e-12)
#endif /* !defined( EPSILON_0 ) */

#if !defined( PI )
    #define PI (3.1415926535897932384626433832795028841971693993751)
#endif /* !defined( PI ) */

#if !defined( MU_0 )
    #define MU_0 (PI*4.0e-7)
#endif /* !defined( MU_0 ) */

#if !defined( DEG2RAD )
    #define DEG2RAD (0.0174532925199432957692369076848861271344287188854)
#endif /* !defiend( DEG2RAD ) */

#if !defined( RAD2DEG )
    #define RAD2DEG (57.29577951308232087679815481410517033240547246656442)
#endif /* !defiend( RAD2DEG ) */

#if !defined( SQRT_PI )
    #define SQRT_PI (1.7724538509055160272981674833411451827975494561224)
#endif /* !defined( SQRT_PI ) */

#if !defined( QELEM )
    #define QELEM (1.60217662e-19)
#endif /* !defined( QELEM ) */

#if !defined( DBL_MAX )
    #define DBL_MAX (1.7976931348623158e+308)
#endif /* !defined( DBL_MAX ) */

#if !defined( DBL_MIN )
    #define DBL_MIN (2.2250738585072014e-308)
#endif /* !defined( DBL_MIN ) */

#if !defined( DBL_EPSILON )
    #define DBL_EPSILON (2.2204460492503131e-16)
#endif /* !defined( DBL_EPSILON ) */

#endif /* XTRACK_CONSTANTS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_WIRE_H
#define XTRACK_WIRE_H

 static inline
void Wire_track_local_particle(WireData el, LocalParticle* part0){

    // Data from wire
    double const L_phy = WireData_get_L_phy(el);
    double const L_int = WireData_get_L_int(el);
    double const current = WireData_get_current(el);

    double const xma = WireData_get_xma(el);
    double const yma = WireData_get_yma(el);

    double const post_subtract_px = WireData_get_post_subtract_px(el);
    double const post_subtract_py = WireData_get_post_subtract_py(el);


    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp


        // constants : EPSILON_0, MU_0, PI, C_LIGHT,

        // Data from particle
        double x      = LocalParticle_get_x(part);
        double y      = LocalParticle_get_y(part);
        double D_x    = x-xma;
        double D_y    = y-yma;
        double R2     = D_x*D_x + D_y*D_y;

        // chi = q/q0 * m0/m
        // p0c : reference particle momentum
        // q0  : reference particle charge
        //double const chi    = LocalParticle_get_chi(part);
        double const p0c    = LocalParticle_get_p0c(part);
        double const q0     = LocalParticle_get_q0(part);


        // Computing the kick
        double const L1   = L_int + L_phy;
        double const L2   = L_int - L_phy;
        double const N    = MU_0*current*q0/(4*PI*p0c/C_LIGHT);

        double dpx  =  -N*D_x*(sqrt(L1*L1 + 4.0*R2) - sqrt(L2*L2 + 4.0*R2))/R2;
        double dpy  =  -N*D_y*(sqrt(L1*L1 + 4.0*R2) - sqrt(L2*L2 + 4.0*R2))/R2;

        // Update the particle properties
        LocalParticle_add_to_px(part, dpx - post_subtract_px);
        LocalParticle_add_to_py(part, dpy - post_subtract_py);



//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

}

#endif


             
            void Wire_track_particles(
               WireData el,

                             ParticlesData particles,

                             int64_t flag_increment_at_element,
                  int8_t* io_buffer){
//            const int num_threads = omp_get_max_threads();                                 //only_for_context cpu_openmp
//            const int64_t capacity = ParticlesData_get__capacity(particles);               //only_for_context cpu_openmp
//            const int64_t chunk_size = (capacity + num_threads - 1)/num_threads; // ceil division  //only_for_context cpu_openmp
//            #pragma omp parallel for                                                       //only_for_context cpu_openmp
//            for (int64_t batch_id = 0; batch_id < num_threads; batch_id++) {               //only_for_context cpu_openmp
                LocalParticle lpart;
                lpart.io_buffer = io_buffer;
//                int64_t part_id = batch_id * chunk_size;                                       //only_for_context cpu_openmp
//                int64_t end_id = (batch_id + 1) * chunk_size;                                  //only_for_context cpu_openmp
//                if (end_id > capacity) end_id = capacity;                                      //only_for_context cpu_openmp
    
                int64_t part_id = 0;                    //only_for_context cpu_serial
//                int64_t part_id = blockDim.x * blockIdx.x + threadIdx.x; //only_for_context cuda
//                int64_t part_id = get_global_id(0);                    //only_for_context opencl
                int64_t end_id = 0; // unused outside of openmp  //only_for_context cpu_serial cuda opencl
    
                int64_t part_capacity = ParticlesData_get__capacity(particles);
                if (part_id<part_capacity){
                    Particles_to_LocalParticle(particles, &lpart, part_id, end_id);
                    if (check_is_active(&lpart)>0){
              Wire_track_local_particle(el, &lpart);

                    }
                    if (check_is_active(&lpart)>0 && flag_increment_at_element){
                            increment_at_element(&lpart, 1);
                    }
                }
//            } //only_for_context cpu_openmp
            
            // On OpenMP we want to additionally by default reorganize all
            // the particles.
//            #ifndef XT_OMP_SKIP_REORGANIZE                             //only_for_context cpu_openmp
//            LocalParticle lpart;                                       //only_for_context cpu_openmp
//            lpart.io_buffer = io_buffer;                               //only_for_context cpu_openmp
//            Particles_to_LocalParticle(particles, &lpart, 0, capacity);//only_for_context cpu_openmp
//            check_is_active(&lpart);                                   //only_for_context cpu_openmp
//            #endif                                                     //only_for_context cpu_openmp
        }

#ifndef XOBJ_TYPEDEF_XRotationData
#define XOBJ_TYPEDEF_XRotationData
typedef   struct XRotationData_s * XRotationData;
 static inline XRotationData XRotationData_getp(XRotationData  obj){
  int64_t offset=0;
  return (XRotationData)(( char*) obj+offset);
}
 static inline double XRotationData_get_sin_angle(const XRotationData  obj){
  int64_t offset=0;
  return *( double*)(( char*) obj+offset);
}
 static inline void XRotationData_set_sin_angle(XRotationData  obj, double value){
  int64_t offset=0;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* XRotationData_getp_sin_angle(XRotationData  obj){
  int64_t offset=0;
  return ( double*)(( char*) obj+offset);
}
 static inline double XRotationData_get_cos_angle(const XRotationData  obj){
  int64_t offset=0;
  offset+=8;
  return *( double*)(( char*) obj+offset);
}
 static inline void XRotationData_set_cos_angle(XRotationData  obj, double value){
  int64_t offset=0;
  offset+=8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* XRotationData_getp_cos_angle(XRotationData  obj){
  int64_t offset=0;
  offset+=8;
  return ( double*)(( char*) obj+offset);
}
 static inline double XRotationData_get_tan_angle(const XRotationData  obj){
  int64_t offset=0;
  offset+=16;
  return *( double*)(( char*) obj+offset);
}
 static inline void XRotationData_set_tan_angle(XRotationData  obj, double value){
  int64_t offset=0;
  offset+=16;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* XRotationData_getp_tan_angle(XRotationData  obj){
  int64_t offset=0;
  offset+=16;
  return ( double*)(( char*) obj+offset);
}
#endif
// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_CONSTANTS_H
#define XTRACK_CONSTANTS_H

#if !defined( C_LIGHT )
    #define   C_LIGHT ( 299792458.0 )
#endif /* !defined( C_LIGHT ) */

#if !defined( EPSILON_0 )
    #define   EPSILON_0 (8.854187817620e-12)
#endif /* !defined( EPSILON_0 ) */

#if !defined( PI )
    #define PI (3.1415926535897932384626433832795028841971693993751)
#endif /* !defined( PI ) */

#if !defined( MU_0 )
    #define MU_0 (PI*4.0e-7)
#endif /* !defined( MU_0 ) */

#if !defined( DEG2RAD )
    #define DEG2RAD (0.0174532925199432957692369076848861271344287188854)
#endif /* !defiend( DEG2RAD ) */

#if !defined( RAD2DEG )
    #define RAD2DEG (57.29577951308232087679815481410517033240547246656442)
#endif /* !defiend( RAD2DEG ) */

#if !defined( SQRT_PI )
    #define SQRT_PI (1.7724538509055160272981674833411451827975494561224)
#endif /* !defined( SQRT_PI ) */

#if !defined( QELEM )
    #define QELEM (1.60217662e-19)
#endif /* !defined( QELEM ) */

#if !defined( DBL_MAX )
    #define DBL_MAX (1.7976931348623158e+308)
#endif /* !defined( DBL_MAX ) */

#if !defined( DBL_MIN )
    #define DBL_MIN (2.2250738585072014e-308)
#endif /* !defined( DBL_MIN ) */

#if !defined( DBL_EPSILON )
    #define DBL_EPSILON (2.2204460492503131e-16)
#endif /* !defined( DBL_EPSILON ) */

#endif /* XTRACK_CONSTANTS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_FUNCTIONS_H
#define XTRACK_FUNCTIONS_H

 static inline
void kill_all_particles(LocalParticle* part0, int64_t kill_state) {

    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

        LocalParticle_kill_particle(part, kill_state);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

}


 static inline
int8_t assert_tracking(LocalParticle* part, int64_t kill_state){
    // Whenever we are not tracking, e.g. in a twiss, the particle will be at_turn < 0.
    // We test this to distinguish genuine tracking from twiss.
    if (LocalParticle_get_at_turn(part) < 0){
        LocalParticle_kill_particle(part, kill_state);
        return 0;
    }
    return 1;
}

#endif /* XTRACK_FUNCTIONS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_PARTICLE_STATES_H
#define XTRACK_PARTICLE_STATES_H

#define  XT_LOST_ON_APERTURE       0
#define  XT_LOST_ON_LONG_CUT      -2
#define  XT_LOST_ALL_E_IN_SYNRAD -10
#define  RNG_ERR_SEEDS_NOT_SET   -20
#define  RNG_ERR_INVALID_TRACK   -21
#define  RNG_ERR_RUTH_NOT_SET    -22

#endif /* XTRACK_PARTICLE_STATES_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_XROTATION_H
#define XTRACK_XROTATION_H


 static inline
void XRotation_single_particle(LocalParticle* part, double sin_angle, double cos_angle, double tan_angle){

    double const beta0 = LocalParticle_get_beta0(part);
    double const beta = LocalParticle_get_rvv(part)*beta0;
    double const x  = LocalParticle_get_x(part);
    double const y  = LocalParticle_get_y(part);
    double const px = LocalParticle_get_px(part);
    double const py = LocalParticle_get_py(part);
    double const t = LocalParticle_get_zeta(part)/beta0;
    double const pt = LocalParticle_get_pzeta(part)*beta0;

    double pz = sqrt(1.0 + 2.0*pt/beta + pt*pt - px*px - py*py);
    double ptt = 1.0 - tan_angle*py/pz;
    double x_hat = x + tan_angle*y*px/(pz*ptt);
    double y_hat = y/(cos_angle*ptt);
    double py_hat = cos_angle*py + sin_angle*pz;
    double t_hat = t - tan_angle*y*(1.0/beta+pt)/(pz*ptt);

    LocalParticle_set_x(part, x_hat);
    LocalParticle_set_y(part, y_hat);
    LocalParticle_set_py(part, py_hat);
    LocalParticle_set_zeta(part,t_hat*beta0);

}


 static inline
void XRotation_track_local_particle(XRotationData el, LocalParticle* part0){

    double sin_angle = XRotationData_get_sin_angle(el);
    double cos_angle = XRotationData_get_cos_angle(el);
    double tan_angle = XRotationData_get_tan_angle(el);

    #ifdef XSUITE_BACKTRACK
        sin_angle = -sin_angle;
        tan_angle = -tan_angle;
    #endif


    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

        XRotation_single_particle(part, sin_angle, cos_angle, tan_angle);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }


}


#endif /* XTRACK_XROTATION_H */


             
            void XRotation_track_particles(
               XRotationData el,

                             ParticlesData particles,

                             int64_t flag_increment_at_element,
                  int8_t* io_buffer){
//            const int num_threads = omp_get_max_threads();                                 //only_for_context cpu_openmp
//            const int64_t capacity = ParticlesData_get__capacity(particles);               //only_for_context cpu_openmp
//            const int64_t chunk_size = (capacity + num_threads - 1)/num_threads; // ceil division  //only_for_context cpu_openmp
//            #pragma omp parallel for                                                       //only_for_context cpu_openmp
//            for (int64_t batch_id = 0; batch_id < num_threads; batch_id++) {               //only_for_context cpu_openmp
                LocalParticle lpart;
                lpart.io_buffer = io_buffer;
//                int64_t part_id = batch_id * chunk_size;                                       //only_for_context cpu_openmp
//                int64_t end_id = (batch_id + 1) * chunk_size;                                  //only_for_context cpu_openmp
//                if (end_id > capacity) end_id = capacity;                                      //only_for_context cpu_openmp
    
                int64_t part_id = 0;                    //only_for_context cpu_serial
//                int64_t part_id = blockDim.x * blockIdx.x + threadIdx.x; //only_for_context cuda
//                int64_t part_id = get_global_id(0);                    //only_for_context opencl
                int64_t end_id = 0; // unused outside of openmp  //only_for_context cpu_serial cuda opencl
    
                int64_t part_capacity = ParticlesData_get__capacity(particles);
                if (part_id<part_capacity){
                    Particles_to_LocalParticle(particles, &lpart, part_id, end_id);
                    if (check_is_active(&lpart)>0){
              XRotation_track_local_particle(el, &lpart);

                    }
                    if (check_is_active(&lpart)>0 && flag_increment_at_element){
                            increment_at_element(&lpart, 1);
                    }
                }
//            } //only_for_context cpu_openmp
            
            // On OpenMP we want to additionally by default reorganize all
            // the particles.
//            #ifndef XT_OMP_SKIP_REORGANIZE                             //only_for_context cpu_openmp
//            LocalParticle lpart;                                       //only_for_context cpu_openmp
//            lpart.io_buffer = io_buffer;                               //only_for_context cpu_openmp
//            Particles_to_LocalParticle(particles, &lpart, 0, capacity);//only_for_context cpu_openmp
//            check_is_active(&lpart);                                   //only_for_context cpu_openmp
//            #endif                                                     //only_for_context cpu_openmp
        }

#ifndef XOBJ_TYPEDEF_XYShiftData
#define XOBJ_TYPEDEF_XYShiftData
typedef   struct XYShiftData_s * XYShiftData;
 static inline XYShiftData XYShiftData_getp(XYShiftData  obj){
  int64_t offset=0;
  return (XYShiftData)(( char*) obj+offset);
}
 static inline double XYShiftData_get_dx(const XYShiftData  obj){
  int64_t offset=0;
  return *( double*)(( char*) obj+offset);
}
 static inline void XYShiftData_set_dx(XYShiftData  obj, double value){
  int64_t offset=0;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* XYShiftData_getp_dx(XYShiftData  obj){
  int64_t offset=0;
  return ( double*)(( char*) obj+offset);
}
 static inline double XYShiftData_get_dy(const XYShiftData  obj){
  int64_t offset=0;
  offset+=8;
  return *( double*)(( char*) obj+offset);
}
 static inline void XYShiftData_set_dy(XYShiftData  obj, double value){
  int64_t offset=0;
  offset+=8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* XYShiftData_getp_dy(XYShiftData  obj){
  int64_t offset=0;
  offset+=8;
  return ( double*)(( char*) obj+offset);
}
#endif
// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_CONSTANTS_H
#define XTRACK_CONSTANTS_H

#if !defined( C_LIGHT )
    #define   C_LIGHT ( 299792458.0 )
#endif /* !defined( C_LIGHT ) */

#if !defined( EPSILON_0 )
    #define   EPSILON_0 (8.854187817620e-12)
#endif /* !defined( EPSILON_0 ) */

#if !defined( PI )
    #define PI (3.1415926535897932384626433832795028841971693993751)
#endif /* !defined( PI ) */

#if !defined( MU_0 )
    #define MU_0 (PI*4.0e-7)
#endif /* !defined( MU_0 ) */

#if !defined( DEG2RAD )
    #define DEG2RAD (0.0174532925199432957692369076848861271344287188854)
#endif /* !defiend( DEG2RAD ) */

#if !defined( RAD2DEG )
    #define RAD2DEG (57.29577951308232087679815481410517033240547246656442)
#endif /* !defiend( RAD2DEG ) */

#if !defined( SQRT_PI )
    #define SQRT_PI (1.7724538509055160272981674833411451827975494561224)
#endif /* !defined( SQRT_PI ) */

#if !defined( QELEM )
    #define QELEM (1.60217662e-19)
#endif /* !defined( QELEM ) */

#if !defined( DBL_MAX )
    #define DBL_MAX (1.7976931348623158e+308)
#endif /* !defined( DBL_MAX ) */

#if !defined( DBL_MIN )
    #define DBL_MIN (2.2250738585072014e-308)
#endif /* !defined( DBL_MIN ) */

#if !defined( DBL_EPSILON )
    #define DBL_EPSILON (2.2204460492503131e-16)
#endif /* !defined( DBL_EPSILON ) */

#endif /* XTRACK_CONSTANTS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_FUNCTIONS_H
#define XTRACK_FUNCTIONS_H

 static inline
void kill_all_particles(LocalParticle* part0, int64_t kill_state) {

    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

        LocalParticle_kill_particle(part, kill_state);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

}


 static inline
int8_t assert_tracking(LocalParticle* part, int64_t kill_state){
    // Whenever we are not tracking, e.g. in a twiss, the particle will be at_turn < 0.
    // We test this to distinguish genuine tracking from twiss.
    if (LocalParticle_get_at_turn(part) < 0){
        LocalParticle_kill_particle(part, kill_state);
        return 0;
    }
    return 1;
}

#endif /* XTRACK_FUNCTIONS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_PARTICLE_STATES_H
#define XTRACK_PARTICLE_STATES_H

#define  XT_LOST_ON_APERTURE       0
#define  XT_LOST_ON_LONG_CUT      -2
#define  XT_LOST_ALL_E_IN_SYNRAD -10
#define  RNG_ERR_SEEDS_NOT_SET   -20
#define  RNG_ERR_INVALID_TRACK   -21
#define  RNG_ERR_RUTH_NOT_SET    -22

#endif /* XTRACK_PARTICLE_STATES_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_XYSHIFT_H
#define XTRACK_XYSHIFT_H


 static inline
void XYShift_single_particle(LocalParticle* part, double dx, double dy){

    LocalParticle_add_to_x(part, -dx );
    LocalParticle_add_to_y(part, -dy );
 
}


 static inline
void XYShift_track_local_particle(XYShiftData el, LocalParticle* part0){

    double dx = XYShiftData_get_dx(el);
    double dy = XYShiftData_get_dy(el);

    #ifdef XSUITE_BACKTRACK
        dx = -dx;
        dy = -dy;
    #endif


    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

        XYShift_single_particle(part, dx, dy);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }


}


#endif /* XTRACK_XYSHIFT_H */


             
            void XYShift_track_particles(
               XYShiftData el,

                             ParticlesData particles,

                             int64_t flag_increment_at_element,
                  int8_t* io_buffer){
//            const int num_threads = omp_get_max_threads();                                 //only_for_context cpu_openmp
//            const int64_t capacity = ParticlesData_get__capacity(particles);               //only_for_context cpu_openmp
//            const int64_t chunk_size = (capacity + num_threads - 1)/num_threads; // ceil division  //only_for_context cpu_openmp
//            #pragma omp parallel for                                                       //only_for_context cpu_openmp
//            for (int64_t batch_id = 0; batch_id < num_threads; batch_id++) {               //only_for_context cpu_openmp
                LocalParticle lpart;
                lpart.io_buffer = io_buffer;
//                int64_t part_id = batch_id * chunk_size;                                       //only_for_context cpu_openmp
//                int64_t end_id = (batch_id + 1) * chunk_size;                                  //only_for_context cpu_openmp
//                if (end_id > capacity) end_id = capacity;                                      //only_for_context cpu_openmp
    
                int64_t part_id = 0;                    //only_for_context cpu_serial
//                int64_t part_id = blockDim.x * blockIdx.x + threadIdx.x; //only_for_context cuda
//                int64_t part_id = get_global_id(0);                    //only_for_context opencl
                int64_t end_id = 0; // unused outside of openmp  //only_for_context cpu_serial cuda opencl
    
                int64_t part_capacity = ParticlesData_get__capacity(particles);
                if (part_id<part_capacity){
                    Particles_to_LocalParticle(particles, &lpart, part_id, end_id);
                    if (check_is_active(&lpart)>0){
              XYShift_track_local_particle(el, &lpart);

                    }
                    if (check_is_active(&lpart)>0 && flag_increment_at_element){
                            increment_at_element(&lpart, 1);
                    }
                }
//            } //only_for_context cpu_openmp
            
            // On OpenMP we want to additionally by default reorganize all
            // the particles.
//            #ifndef XT_OMP_SKIP_REORGANIZE                             //only_for_context cpu_openmp
//            LocalParticle lpart;                                       //only_for_context cpu_openmp
//            lpart.io_buffer = io_buffer;                               //only_for_context cpu_openmp
//            Particles_to_LocalParticle(particles, &lpart, 0, capacity);//only_for_context cpu_openmp
//            check_is_active(&lpart);                                   //only_for_context cpu_openmp
//            #endif                                                     //only_for_context cpu_openmp
        }

#ifndef XOBJ_TYPEDEF_YRotationData
#define XOBJ_TYPEDEF_YRotationData
typedef   struct YRotationData_s * YRotationData;
 static inline YRotationData YRotationData_getp(YRotationData  obj){
  int64_t offset=0;
  return (YRotationData)(( char*) obj+offset);
}
 static inline double YRotationData_get_sin_angle(const YRotationData  obj){
  int64_t offset=0;
  return *( double*)(( char*) obj+offset);
}
 static inline void YRotationData_set_sin_angle(YRotationData  obj, double value){
  int64_t offset=0;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* YRotationData_getp_sin_angle(YRotationData  obj){
  int64_t offset=0;
  return ( double*)(( char*) obj+offset);
}
 static inline double YRotationData_get_cos_angle(const YRotationData  obj){
  int64_t offset=0;
  offset+=8;
  return *( double*)(( char*) obj+offset);
}
 static inline void YRotationData_set_cos_angle(YRotationData  obj, double value){
  int64_t offset=0;
  offset+=8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* YRotationData_getp_cos_angle(YRotationData  obj){
  int64_t offset=0;
  offset+=8;
  return ( double*)(( char*) obj+offset);
}
 static inline double YRotationData_get_tan_angle(const YRotationData  obj){
  int64_t offset=0;
  offset+=16;
  return *( double*)(( char*) obj+offset);
}
 static inline void YRotationData_set_tan_angle(YRotationData  obj, double value){
  int64_t offset=0;
  offset+=16;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* YRotationData_getp_tan_angle(YRotationData  obj){
  int64_t offset=0;
  offset+=16;
  return ( double*)(( char*) obj+offset);
}
#endif
// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_CONSTANTS_H
#define XTRACK_CONSTANTS_H

#if !defined( C_LIGHT )
    #define   C_LIGHT ( 299792458.0 )
#endif /* !defined( C_LIGHT ) */

#if !defined( EPSILON_0 )
    #define   EPSILON_0 (8.854187817620e-12)
#endif /* !defined( EPSILON_0 ) */

#if !defined( PI )
    #define PI (3.1415926535897932384626433832795028841971693993751)
#endif /* !defined( PI ) */

#if !defined( MU_0 )
    #define MU_0 (PI*4.0e-7)
#endif /* !defined( MU_0 ) */

#if !defined( DEG2RAD )
    #define DEG2RAD (0.0174532925199432957692369076848861271344287188854)
#endif /* !defiend( DEG2RAD ) */

#if !defined( RAD2DEG )
    #define RAD2DEG (57.29577951308232087679815481410517033240547246656442)
#endif /* !defiend( RAD2DEG ) */

#if !defined( SQRT_PI )
    #define SQRT_PI (1.7724538509055160272981674833411451827975494561224)
#endif /* !defined( SQRT_PI ) */

#if !defined( QELEM )
    #define QELEM (1.60217662e-19)
#endif /* !defined( QELEM ) */

#if !defined( DBL_MAX )
    #define DBL_MAX (1.7976931348623158e+308)
#endif /* !defined( DBL_MAX ) */

#if !defined( DBL_MIN )
    #define DBL_MIN (2.2250738585072014e-308)
#endif /* !defined( DBL_MIN ) */

#if !defined( DBL_EPSILON )
    #define DBL_EPSILON (2.2204460492503131e-16)
#endif /* !defined( DBL_EPSILON ) */

#endif /* XTRACK_CONSTANTS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_FUNCTIONS_H
#define XTRACK_FUNCTIONS_H

 static inline
void kill_all_particles(LocalParticle* part0, int64_t kill_state) {

    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

        LocalParticle_kill_particle(part, kill_state);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

}


 static inline
int8_t assert_tracking(LocalParticle* part, int64_t kill_state){
    // Whenever we are not tracking, e.g. in a twiss, the particle will be at_turn < 0.
    // We test this to distinguish genuine tracking from twiss.
    if (LocalParticle_get_at_turn(part) < 0){
        LocalParticle_kill_particle(part, kill_state);
        return 0;
    }
    return 1;
}

#endif /* XTRACK_FUNCTIONS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_PARTICLE_STATES_H
#define XTRACK_PARTICLE_STATES_H

#define  XT_LOST_ON_APERTURE       0
#define  XT_LOST_ON_LONG_CUT      -2
#define  XT_LOST_ALL_E_IN_SYNRAD -10
#define  RNG_ERR_SEEDS_NOT_SET   -20
#define  RNG_ERR_INVALID_TRACK   -21
#define  RNG_ERR_RUTH_NOT_SET    -22

#endif /* XTRACK_PARTICLE_STATES_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_TRACK_YROTATION_H
#define XTRACK_TRACK_YROTATION_H

 static inline
void YRotation_single_particle(LocalParticle* part, double sin_angle, double cos_angle, double tan_angle){

    double const beta0 = LocalParticle_get_beta0(part);
    double const beta = LocalParticle_get_rvv(part)*beta0;
    double const x  = LocalParticle_get_x(part);
    double const y  = LocalParticle_get_y(part);
    double const px = LocalParticle_get_px(part);
    double const py = LocalParticle_get_py(part);
    double const t = LocalParticle_get_zeta(part)/beta0;
    double const pt = LocalParticle_get_pzeta(part)*beta0;

    double pz = sqrt(1.0 + 2.0*pt/beta + pt*pt - px*px - py*py);
    double ptt = 1.0 - tan_angle*px/pz;
    double x_hat = x/(cos_angle*ptt);
    double px_hat = cos_angle*px + sin_angle*pz;
    double y_hat = y + tan_angle*x*py/(pz*ptt);
    double t_hat = t - tan_angle*x*(1.0/beta+pt)/(pz*ptt);

    LocalParticle_set_x(part, x_hat);
    LocalParticle_set_px(part, px_hat);
    LocalParticle_set_y(part, y_hat);
    LocalParticle_set_zeta(part,t_hat*beta0);

}

#endif /* XTRACK_TRACK_YROTATION_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_YROTATION_H
#define XTRACK_YROTATION_H

 static inline
void YRotation_track_local_particle(YRotationData el, LocalParticle* part0){

    double sin_angle = YRotationData_get_sin_angle(el);
    double cos_angle = YRotationData_get_cos_angle(el);
    double tan_angle = YRotationData_get_tan_angle(el);

    #ifdef XSUITE_BACKTRACK
        sin_angle = -sin_angle;
        tan_angle = -tan_angle;
    #endif


    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

        YRotation_single_particle(part, sin_angle, cos_angle, tan_angle);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }


}

#endif /* XTRACK_YROTATION_H */


             
            void YRotation_track_particles(
               YRotationData el,

                             ParticlesData particles,

                             int64_t flag_increment_at_element,
                  int8_t* io_buffer){
//            const int num_threads = omp_get_max_threads();                                 //only_for_context cpu_openmp
//            const int64_t capacity = ParticlesData_get__capacity(particles);               //only_for_context cpu_openmp
//            const int64_t chunk_size = (capacity + num_threads - 1)/num_threads; // ceil division  //only_for_context cpu_openmp
//            #pragma omp parallel for                                                       //only_for_context cpu_openmp
//            for (int64_t batch_id = 0; batch_id < num_threads; batch_id++) {               //only_for_context cpu_openmp
                LocalParticle lpart;
                lpart.io_buffer = io_buffer;
//                int64_t part_id = batch_id * chunk_size;                                       //only_for_context cpu_openmp
//                int64_t end_id = (batch_id + 1) * chunk_size;                                  //only_for_context cpu_openmp
//                if (end_id > capacity) end_id = capacity;                                      //only_for_context cpu_openmp
    
                int64_t part_id = 0;                    //only_for_context cpu_serial
//                int64_t part_id = blockDim.x * blockIdx.x + threadIdx.x; //only_for_context cuda
//                int64_t part_id = get_global_id(0);                    //only_for_context opencl
                int64_t end_id = 0; // unused outside of openmp  //only_for_context cpu_serial cuda opencl
    
                int64_t part_capacity = ParticlesData_get__capacity(particles);
                if (part_id<part_capacity){
                    Particles_to_LocalParticle(particles, &lpart, part_id, end_id);
                    if (check_is_active(&lpart)>0){
              YRotation_track_local_particle(el, &lpart);

                    }
                    if (check_is_active(&lpart)>0 && flag_increment_at_element){
                            increment_at_element(&lpart, 1);
                    }
                }
//            } //only_for_context cpu_openmp
            
            // On OpenMP we want to additionally by default reorganize all
            // the particles.
//            #ifndef XT_OMP_SKIP_REORGANIZE                             //only_for_context cpu_openmp
//            LocalParticle lpart;                                       //only_for_context cpu_openmp
//            lpart.io_buffer = io_buffer;                               //only_for_context cpu_openmp
//            Particles_to_LocalParticle(particles, &lpart, 0, capacity);//only_for_context cpu_openmp
//            check_is_active(&lpart);                                   //only_for_context cpu_openmp
//            #endif                                                     //only_for_context cpu_openmp
        }

#ifndef XOBJ_TYPEDEF_ZetaShiftData
#define XOBJ_TYPEDEF_ZetaShiftData
typedef   struct ZetaShiftData_s * ZetaShiftData;
 static inline ZetaShiftData ZetaShiftData_getp(ZetaShiftData  obj){
  int64_t offset=0;
  return (ZetaShiftData)(( char*) obj+offset);
}
 static inline double ZetaShiftData_get_dzeta(const ZetaShiftData  obj){
  int64_t offset=0;
  return *( double*)(( char*) obj+offset);
}
 static inline void ZetaShiftData_set_dzeta(ZetaShiftData  obj, double value){
  int64_t offset=0;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* ZetaShiftData_getp_dzeta(ZetaShiftData  obj){
  int64_t offset=0;
  return ( double*)(( char*) obj+offset);
}
#endif
// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_CONSTANTS_H
#define XTRACK_CONSTANTS_H

#if !defined( C_LIGHT )
    #define   C_LIGHT ( 299792458.0 )
#endif /* !defined( C_LIGHT ) */

#if !defined( EPSILON_0 )
    #define   EPSILON_0 (8.854187817620e-12)
#endif /* !defined( EPSILON_0 ) */

#if !defined( PI )
    #define PI (3.1415926535897932384626433832795028841971693993751)
#endif /* !defined( PI ) */

#if !defined( MU_0 )
    #define MU_0 (PI*4.0e-7)
#endif /* !defined( MU_0 ) */

#if !defined( DEG2RAD )
    #define DEG2RAD (0.0174532925199432957692369076848861271344287188854)
#endif /* !defiend( DEG2RAD ) */

#if !defined( RAD2DEG )
    #define RAD2DEG (57.29577951308232087679815481410517033240547246656442)
#endif /* !defiend( RAD2DEG ) */

#if !defined( SQRT_PI )
    #define SQRT_PI (1.7724538509055160272981674833411451827975494561224)
#endif /* !defined( SQRT_PI ) */

#if !defined( QELEM )
    #define QELEM (1.60217662e-19)
#endif /* !defined( QELEM ) */

#if !defined( DBL_MAX )
    #define DBL_MAX (1.7976931348623158e+308)
#endif /* !defined( DBL_MAX ) */

#if !defined( DBL_MIN )
    #define DBL_MIN (2.2250738585072014e-308)
#endif /* !defined( DBL_MIN ) */

#if !defined( DBL_EPSILON )
    #define DBL_EPSILON (2.2204460492503131e-16)
#endif /* !defined( DBL_EPSILON ) */

#endif /* XTRACK_CONSTANTS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_FUNCTIONS_H
#define XTRACK_FUNCTIONS_H

 static inline
void kill_all_particles(LocalParticle* part0, int64_t kill_state) {

    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

        LocalParticle_kill_particle(part, kill_state);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

}


 static inline
int8_t assert_tracking(LocalParticle* part, int64_t kill_state){
    // Whenever we are not tracking, e.g. in a twiss, the particle will be at_turn < 0.
    // We test this to distinguish genuine tracking from twiss.
    if (LocalParticle_get_at_turn(part) < 0){
        LocalParticle_kill_particle(part, kill_state);
        return 0;
    }
    return 1;
}

#endif /* XTRACK_FUNCTIONS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_PARTICLE_STATES_H
#define XTRACK_PARTICLE_STATES_H

#define  XT_LOST_ON_APERTURE       0
#define  XT_LOST_ON_LONG_CUT      -2
#define  XT_LOST_ALL_E_IN_SYNRAD -10
#define  RNG_ERR_SEEDS_NOT_SET   -20
#define  RNG_ERR_INVALID_TRACK   -21
#define  RNG_ERR_RUTH_NOT_SET    -22

#endif /* XTRACK_PARTICLE_STATES_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_ZETASHIFT_H
#define XTRACK_ZETASHIFT_H

 static inline
void ZetaShift_track_local_particle(ZetaShiftData el, LocalParticle* part0){


    double dzeta = ZetaShiftData_get_dzeta(el);
    #ifdef XSUITE_BACKTRACK
        dzeta = -dzeta;
    #endif


    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp


        LocalParticle_add_to_zeta(part, -dzeta);


//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }


}

#endif


             
            void ZetaShift_track_particles(
               ZetaShiftData el,

                             ParticlesData particles,

                             int64_t flag_increment_at_element,
                  int8_t* io_buffer){
//            const int num_threads = omp_get_max_threads();                                 //only_for_context cpu_openmp
//            const int64_t capacity = ParticlesData_get__capacity(particles);               //only_for_context cpu_openmp
//            const int64_t chunk_size = (capacity + num_threads - 1)/num_threads; // ceil division  //only_for_context cpu_openmp
//            #pragma omp parallel for                                                       //only_for_context cpu_openmp
//            for (int64_t batch_id = 0; batch_id < num_threads; batch_id++) {               //only_for_context cpu_openmp
                LocalParticle lpart;
                lpart.io_buffer = io_buffer;
//                int64_t part_id = batch_id * chunk_size;                                       //only_for_context cpu_openmp
//                int64_t end_id = (batch_id + 1) * chunk_size;                                  //only_for_context cpu_openmp
//                if (end_id > capacity) end_id = capacity;                                      //only_for_context cpu_openmp
    
                int64_t part_id = 0;                    //only_for_context cpu_serial
//                int64_t part_id = blockDim.x * blockIdx.x + threadIdx.x; //only_for_context cuda
//                int64_t part_id = get_global_id(0);                    //only_for_context opencl
                int64_t end_id = 0; // unused outside of openmp  //only_for_context cpu_serial cuda opencl
    
                int64_t part_capacity = ParticlesData_get__capacity(particles);
                if (part_id<part_capacity){
                    Particles_to_LocalParticle(particles, &lpart, part_id, end_id);
                    if (check_is_active(&lpart)>0){
              ZetaShift_track_local_particle(el, &lpart);

                    }
                    if (check_is_active(&lpart)>0 && flag_increment_at_element){
                            increment_at_element(&lpart, 1);
                    }
                }
//            } //only_for_context cpu_openmp
            
            // On OpenMP we want to additionally by default reorganize all
            // the particles.
//            #ifndef XT_OMP_SKIP_REORGANIZE                             //only_for_context cpu_openmp
//            LocalParticle lpart;                                       //only_for_context cpu_openmp
//            lpart.io_buffer = io_buffer;                               //only_for_context cpu_openmp
//            Particles_to_LocalParticle(particles, &lpart, 0, capacity);//only_for_context cpu_openmp
//            check_is_active(&lpart);                                   //only_for_context cpu_openmp
//            #endif                                                     //only_for_context cpu_openmp
        }

#ifndef XOBJ_TYPEDEF_RecordIdentifier
#define XOBJ_TYPEDEF_RecordIdentifier
typedef   struct RecordIdentifier_s * RecordIdentifier;
 static inline RecordIdentifier RecordIdentifier_getp(RecordIdentifier  obj){
  int64_t offset=0;
  return (RecordIdentifier)(( char*) obj+offset);
}
 static inline int64_t RecordIdentifier_get_buffer_id(const RecordIdentifier  obj){
  int64_t offset=0;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void RecordIdentifier_set_buffer_id(RecordIdentifier  obj, int64_t value){
  int64_t offset=0;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* RecordIdentifier_getp_buffer_id(RecordIdentifier  obj){
  int64_t offset=0;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline int64_t RecordIdentifier_get_offset(const RecordIdentifier  obj){
  int64_t offset=0;
  offset+=8;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void RecordIdentifier_set_offset(RecordIdentifier  obj, int64_t value){
  int64_t offset=0;
  offset+=8;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* RecordIdentifier_getp_offset(RecordIdentifier  obj){
  int64_t offset=0;
  offset+=8;
  return ( int64_t*)(( char*) obj+offset);
}
#endif

 static inline
  int8_t* RecordIdentifier_getp_record(RecordIdentifier record_id, LocalParticle* part){
      int8_t* io_buffer = LocalParticle_get_io_buffer(part);
    if (io_buffer == NULL){
        return NULL;
    }

    int64_t buffer_id = RecordIdentifier_get_buffer_id(record_id);
      int64_t* found_id = (  int64_t*)io_buffer;
    if (buffer_id != (*found_id)){
        printf("Error: buffer_id mismatch!\n");
        return NULL;
    }

    int64_t offset = RecordIdentifier_get_offset(record_id);

    return io_buffer + offset;
    }


#ifndef XOBJ_TYPEDEF_RandomUniformData
#define XOBJ_TYPEDEF_RandomUniformData
typedef   struct RandomUniformData_s * RandomUniformData;
 static inline RandomUniformData RandomUniformData_getp(RandomUniformData  obj){
  int64_t offset=0;
  return (RandomUniformData)(( char*) obj+offset);
}
 static inline uint8_t RandomUniformData_get__dummy(const RandomUniformData  obj){
  int64_t offset=0;
  return *(( uint8_t*) obj+offset);
}
 static inline void RandomUniformData_set__dummy(RandomUniformData  obj, uint8_t value){
  int64_t offset=0;
  *(( uint8_t*) obj+offset)=value;
}
 static inline  uint8_t* RandomUniformData_getp__dummy(RandomUniformData  obj){
  int64_t offset=0;
  return ( uint8_t*)(( char*) obj+offset);
}
#endif
// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_CONSTANTS_H
#define XTRACK_CONSTANTS_H

#if !defined( C_LIGHT )
    #define   C_LIGHT ( 299792458.0 )
#endif /* !defined( C_LIGHT ) */

#if !defined( EPSILON_0 )
    #define   EPSILON_0 (8.854187817620e-12)
#endif /* !defined( EPSILON_0 ) */

#if !defined( PI )
    #define PI (3.1415926535897932384626433832795028841971693993751)
#endif /* !defined( PI ) */

#if !defined( MU_0 )
    #define MU_0 (PI*4.0e-7)
#endif /* !defined( MU_0 ) */

#if !defined( DEG2RAD )
    #define DEG2RAD (0.0174532925199432957692369076848861271344287188854)
#endif /* !defiend( DEG2RAD ) */

#if !defined( RAD2DEG )
    #define RAD2DEG (57.29577951308232087679815481410517033240547246656442)
#endif /* !defiend( RAD2DEG ) */

#if !defined( SQRT_PI )
    #define SQRT_PI (1.7724538509055160272981674833411451827975494561224)
#endif /* !defined( SQRT_PI ) */

#if !defined( QELEM )
    #define QELEM (1.60217662e-19)
#endif /* !defined( QELEM ) */

#if !defined( DBL_MAX )
    #define DBL_MAX (1.7976931348623158e+308)
#endif /* !defined( DBL_MAX ) */

#if !defined( DBL_MIN )
    #define DBL_MIN (2.2250738585072014e-308)
#endif /* !defined( DBL_MIN ) */

#if !defined( DBL_EPSILON )
    #define DBL_EPSILON (2.2204460492503131e-16)
#endif /* !defined( DBL_EPSILON ) */

#endif /* XTRACK_CONSTANTS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_FUNCTIONS_H
#define XTRACK_FUNCTIONS_H

 static inline
void kill_all_particles(LocalParticle* part0, int64_t kill_state) {

    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

        LocalParticle_kill_particle(part, kill_state);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

}


 static inline
int8_t assert_tracking(LocalParticle* part, int64_t kill_state){
    // Whenever we are not tracking, e.g. in a twiss, the particle will be at_turn < 0.
    // We test this to distinguish genuine tracking from twiss.
    if (LocalParticle_get_at_turn(part) < 0){
        LocalParticle_kill_particle(part, kill_state);
        return 0;
    }
    return 1;
}

#endif /* XTRACK_FUNCTIONS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_PARTICLE_STATES_H
#define XTRACK_PARTICLE_STATES_H

#define  XT_LOST_ON_APERTURE       0
#define  XT_LOST_ON_LONG_CUT      -2
#define  XT_LOST_ALL_E_IN_SYNRAD -10
#define  RNG_ERR_SEEDS_NOT_SET   -20
#define  RNG_ERR_INVALID_TRACK   -21
#define  RNG_ERR_RUTH_NOT_SET    -22

#endif /* XTRACK_PARTICLE_STATES_H */

// copyright ############################### //
// This file is part of the Xpart Package.   //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XPART_BASE_RNG_H
#define XPART_BASE_RNG_H

//#include <stdint.h> //only_for_context none

// Combined LCG-Thausworthe generator from (example 37-4):
// https://developer.nvidia.com/gpugems/gpugems3/part-vi-gpu-computing/chapter-37-efficient-random-number-generation-and-application
#define MASK 0xffffffffUL
#define TAUSWORTHE(s,a,b,c,d) ((((s) &c) <<d) &MASK) ^ (((((s) <<a) &MASK)^(s)) >>b)
#define LCG(s,A,C) ((((A*(s)) &MASK) + C) &MASK)


 static inline
double rng_get (uint32_t *s1, uint32_t *s2, uint32_t *s3, uint32_t *s4 ){
  *s1 = TAUSWORTHE (*s1, 13, 19, 4294967294UL, 12);  // p1=2^31-1
  *s2 = TAUSWORTHE (*s2, 2, 25, 4294967288UL, 4);    // p2=2^30-1
  *s3 = TAUSWORTHE (*s3, 3, 11, 4294967280UL, 17);   // p3=2^28-1
  *s4 = LCG(*s4, 1664525, 1013904223UL);             // p4=2^32

  // Combined period is lcm(p1,p2,p3,p4) ~ 2^121
  return ((*s1) ^ (*s2) ^ (*s3) ^ (*s4)) / 4294967296.0 ;
}

 static inline
void rng_set (uint32_t *s1, uint32_t *s2, uint32_t *s3, uint32_t *s4, uint32_t s ){
  *s1 = LCG (s, 69069, 0);
  if (*s1 < 2) *s1 += 2UL;
  *s2 = LCG (*s1, 69069, 0);
  if (*s2 < 8) *s2 += 8UL;
  *s3 = LCG (*s2, 69069, 0);
  if (*s3 < 16) *s3 += 16UL;
  *s4 = LCG (*s3, 69069, 0);

  /* "warm it up" */
  rng_get (s1, s2, s3, s4);
  rng_get (s1, s2, s3, s4);
  rng_get (s1, s2, s3, s4);
  rng_get (s1, s2, s3, s4);
  rng_get (s1, s2, s3, s4);
  rng_get (s1, s2, s3, s4);
  return;
}

#endif /* XPART_BASE_RNG_H */

 
// copyright ############################### //
// This file is part of the Xtrack Package.   //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_UNIFORM_RNG_H
#define XTRACK_UNIFORM_RNG_H
#include <stdlib.h> //only_for_context cpu_serial cpu_openmp
#include <math.h> //only_for_context cpu_serial cpu_openmp
#include <time.h> //only_for_context cpu_serial cpu_openmp


 static inline
int8_t assert_rng_set(LocalParticle* part, int64_t kill_state){
    int64_t s1 = LocalParticle_get__rng_s1(part);
    int64_t s2 = LocalParticle_get__rng_s2(part);
    int64_t s3 = LocalParticle_get__rng_s3(part);
    int64_t s4 = LocalParticle_get__rng_s4(part);
    if (s1==0 && s2==0 && s3==0 && s4==0) {
        LocalParticle_kill_particle(part, kill_state);
        return 0;
    }
    return 1;
}


 static inline
double RandomUniform_generate(LocalParticle* part){
    uint32_t s1 = LocalParticle_get__rng_s1(part);
    uint32_t s2 = LocalParticle_get__rng_s2(part);
    uint32_t s3 = LocalParticle_get__rng_s3(part);
    uint32_t s4 = LocalParticle_get__rng_s4(part);

    if (s1==0 && s2==0 && s3==0 && s4==0) {
        LocalParticle_kill_particle(part, RNG_ERR_SEEDS_NOT_SET);
        return 0;
    }

    double r = rng_get(&s1, &s2, &s3, &s4);

    LocalParticle_set__rng_s1(part, s1);
    LocalParticle_set__rng_s2(part, s2);
    LocalParticle_set__rng_s3(part, s3);
    LocalParticle_set__rng_s4(part, s4);

    return r;
}


 static inline
void RandomUniform_sample(RandomUniformData rng, LocalParticle* part0,
                               double* samples, int64_t n_samples_per_seed){

    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

    int i;
    for (i=0; i<n_samples_per_seed; ++i){
        double val = RandomUniform_generate(part);
        samples[n_samples_per_seed*LocalParticle_get_particle_id(part) + i] = val;
    }

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

}


 static inline
void RandomUniform_track_local_particle(RandomUniformData rng, LocalParticle* part0) {
    kill_all_particles(part0, RNG_ERR_INVALID_TRACK);
}

#endif /* XTRACK_UNIFORM_RNG_H */


             
            void RandomUniform_track_particles(
               RandomUniformData el,

                             ParticlesData particles,

                             int64_t flag_increment_at_element,
                  int8_t* io_buffer){
//            const int num_threads = omp_get_max_threads();                                 //only_for_context cpu_openmp
//            const int64_t capacity = ParticlesData_get__capacity(particles);               //only_for_context cpu_openmp
//            const int64_t chunk_size = (capacity + num_threads - 1)/num_threads; // ceil division  //only_for_context cpu_openmp
//            #pragma omp parallel for                                                       //only_for_context cpu_openmp
//            for (int64_t batch_id = 0; batch_id < num_threads; batch_id++) {               //only_for_context cpu_openmp
                LocalParticle lpart;
                lpart.io_buffer = io_buffer;
//                int64_t part_id = batch_id * chunk_size;                                       //only_for_context cpu_openmp
//                int64_t end_id = (batch_id + 1) * chunk_size;                                  //only_for_context cpu_openmp
//                if (end_id > capacity) end_id = capacity;                                      //only_for_context cpu_openmp
    
                int64_t part_id = 0;                    //only_for_context cpu_serial
//                int64_t part_id = blockDim.x * blockIdx.x + threadIdx.x; //only_for_context cuda
//                int64_t part_id = get_global_id(0);                    //only_for_context opencl
                int64_t end_id = 0; // unused outside of openmp  //only_for_context cpu_serial cuda opencl
    
                int64_t part_capacity = ParticlesData_get__capacity(particles);
                if (part_id<part_capacity){
                    Particles_to_LocalParticle(particles, &lpart, part_id, end_id);
                    if (check_is_active(&lpart)>0){
              RandomUniform_track_local_particle(el, &lpart);

                    }
                    if (check_is_active(&lpart)>0 && flag_increment_at_element){
                            increment_at_element(&lpart, 1);
                    }
                }
//            } //only_for_context cpu_openmp
            
            // On OpenMP we want to additionally by default reorganize all
            // the particles.
//            #ifndef XT_OMP_SKIP_REORGANIZE                             //only_for_context cpu_openmp
//            LocalParticle lpart;                                       //only_for_context cpu_openmp
//            lpart.io_buffer = io_buffer;                               //only_for_context cpu_openmp
//            Particles_to_LocalParticle(particles, &lpart, 0, capacity);//only_for_context cpu_openmp
//            check_is_active(&lpart);                                   //only_for_context cpu_openmp
//            #endif                                                     //only_for_context cpu_openmp
        }


             
            void sample_uniform(
               RandomUniformData el,

                             ParticlesData particles,
    double* samples,  int64_t n_samples_per_seed, 
                             int64_t flag_increment_at_element,
                  int8_t* io_buffer){
//            const int num_threads = omp_get_max_threads();                                 //only_for_context cpu_openmp
//            const int64_t capacity = ParticlesData_get__capacity(particles);               //only_for_context cpu_openmp
//            const int64_t chunk_size = (capacity + num_threads - 1)/num_threads; // ceil division  //only_for_context cpu_openmp
//            #pragma omp parallel for                                                       //only_for_context cpu_openmp
//            for (int64_t batch_id = 0; batch_id < num_threads; batch_id++) {               //only_for_context cpu_openmp
                LocalParticle lpart;
                lpart.io_buffer = io_buffer;
//                int64_t part_id = batch_id * chunk_size;                                       //only_for_context cpu_openmp
//                int64_t end_id = (batch_id + 1) * chunk_size;                                  //only_for_context cpu_openmp
//                if (end_id > capacity) end_id = capacity;                                      //only_for_context cpu_openmp
    
                int64_t part_id = 0;                    //only_for_context cpu_serial
//                int64_t part_id = blockDim.x * blockIdx.x + threadIdx.x; //only_for_context cuda
//                int64_t part_id = get_global_id(0);                    //only_for_context opencl
                int64_t end_id = 0; // unused outside of openmp  //only_for_context cpu_serial cuda opencl
    
                int64_t part_capacity = ParticlesData_get__capacity(particles);
                if (part_id<part_capacity){
                    Particles_to_LocalParticle(particles, &lpart, part_id, end_id);
                    if (check_is_active(&lpart)>0){
              RandomUniform_sample(el, &lpart, samples, n_samples_per_seed);

                    }
                    if (check_is_active(&lpart)>0 && flag_increment_at_element){
                            increment_at_element(&lpart, 1);
                    }
                }
//            } //only_for_context cpu_openmp
            
            // On OpenMP we want to additionally by default reorganize all
            // the particles.
//            #ifndef XT_OMP_SKIP_REORGANIZE                             //only_for_context cpu_openmp
//            LocalParticle lpart;                                       //only_for_context cpu_openmp
//            lpart.io_buffer = io_buffer;                               //only_for_context cpu_openmp
//            Particles_to_LocalParticle(particles, &lpart, 0, capacity);//only_for_context cpu_openmp
//            check_is_active(&lpart);                                   //only_for_context cpu_openmp
//            #endif                                                     //only_for_context cpu_openmp
        }

#ifndef XOBJ_TYPEDEF_InvalidCollimatorData
#define XOBJ_TYPEDEF_InvalidCollimatorData
typedef   struct InvalidCollimatorData_s * InvalidCollimatorData;
 static inline InvalidCollimatorData InvalidCollimatorData_getp(InvalidCollimatorData  obj){
  int64_t offset=0;
  return (InvalidCollimatorData)(( char*) obj+offset);
}
 static inline double InvalidCollimatorData_get_length(const InvalidCollimatorData  obj){
  int64_t offset=0;
  return *( double*)(( char*) obj+offset);
}
 static inline void InvalidCollimatorData_set_length(InvalidCollimatorData  obj, double value){
  int64_t offset=0;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* InvalidCollimatorData_getp_length(InvalidCollimatorData  obj){
  int64_t offset=0;
  return ( double*)(( char*) obj+offset);
}
#endif
// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_CONSTANTS_H
#define XTRACK_CONSTANTS_H

#if !defined( C_LIGHT )
    #define   C_LIGHT ( 299792458.0 )
#endif /* !defined( C_LIGHT ) */

#if !defined( EPSILON_0 )
    #define   EPSILON_0 (8.854187817620e-12)
#endif /* !defined( EPSILON_0 ) */

#if !defined( PI )
    #define PI (3.1415926535897932384626433832795028841971693993751)
#endif /* !defined( PI ) */

#if !defined( MU_0 )
    #define MU_0 (PI*4.0e-7)
#endif /* !defined( MU_0 ) */

#if !defined( DEG2RAD )
    #define DEG2RAD (0.0174532925199432957692369076848861271344287188854)
#endif /* !defiend( DEG2RAD ) */

#if !defined( RAD2DEG )
    #define RAD2DEG (57.29577951308232087679815481410517033240547246656442)
#endif /* !defiend( RAD2DEG ) */

#if !defined( SQRT_PI )
    #define SQRT_PI (1.7724538509055160272981674833411451827975494561224)
#endif /* !defined( SQRT_PI ) */

#if !defined( QELEM )
    #define QELEM (1.60217662e-19)
#endif /* !defined( QELEM ) */

#if !defined( DBL_MAX )
    #define DBL_MAX (1.7976931348623158e+308)
#endif /* !defined( DBL_MAX ) */

#if !defined( DBL_MIN )
    #define DBL_MIN (2.2250738585072014e-308)
#endif /* !defined( DBL_MIN ) */

#if !defined( DBL_EPSILON )
    #define DBL_EPSILON (2.2204460492503131e-16)
#endif /* !defined( DBL_EPSILON ) */

#endif /* XTRACK_CONSTANTS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_FUNCTIONS_H
#define XTRACK_FUNCTIONS_H

 static inline
void kill_all_particles(LocalParticle* part0, int64_t kill_state) {

    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

        LocalParticle_kill_particle(part, kill_state);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

}


 static inline
int8_t assert_tracking(LocalParticle* part, int64_t kill_state){
    // Whenever we are not tracking, e.g. in a twiss, the particle will be at_turn < 0.
    // We test this to distinguish genuine tracking from twiss.
    if (LocalParticle_get_at_turn(part) < 0){
        LocalParticle_kill_particle(part, kill_state);
        return 0;
    }
    return 1;
}

#endif /* XTRACK_FUNCTIONS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_PARTICLE_STATES_H
#define XTRACK_PARTICLE_STATES_H

#define  XT_LOST_ON_APERTURE       0
#define  XT_LOST_ON_LONG_CUT      -2
#define  XT_LOST_ALL_E_IN_SYNRAD -10
#define  RNG_ERR_SEEDS_NOT_SET   -20
#define  RNG_ERR_INVALID_TRACK   -21
#define  RNG_ERR_RUTH_NOT_SET    -22

#endif /* XTRACK_PARTICLE_STATES_H */

// copyright ############################### #
// This file is part of the Xcoll Package.   #
// Copyright (c) CERN, 2023.                 #
// ######################################### #

#ifndef XCOLL_STATES_H
#define XCOLL_STATES_H

#define  XC_LOST_ON_EVEREST         -333
#define  XC_LOST_ON_EVEREST_CRYSTAL -334
#define  XC_LOST_ON_FLUKA           -335
#define  XC_LOST_ON_GEANT4          -336
#define  XC_LOST_ON_ABSORBER        -340

#define  XC_ERR_INVALID_TRACK       -390
#define  XC_ERR_NOT_IMPLEMENTED     -391
#define  XC_ERR_INVALID_XOFIELD     -392
#define  XC_ERR                     -399
    
#endif /* XCOLL_STATES_H */

// copyright ############################### #
// This file is part of the Xcoll Package.   #
// Copyright (c) CERN, 2023.                 #
// ######################################### #

#ifndef XCOLL_INVALID_H
#define XCOLL_INVALID_H


 static inline
void InvalidCollimator_track_local_particle(InvalidCollimatorData el, LocalParticle* part0) {
    kill_all_particles(part0, XC_ERR_INVALID_TRACK); // xcoll lost state error
}

#endif


             
            void InvalidCollimator_track_particles(
               InvalidCollimatorData el,

                             ParticlesData particles,

                             int64_t flag_increment_at_element,
                  int8_t* io_buffer){
//            const int num_threads = omp_get_max_threads();                                 //only_for_context cpu_openmp
//            const int64_t capacity = ParticlesData_get__capacity(particles);               //only_for_context cpu_openmp
//            const int64_t chunk_size = (capacity + num_threads - 1)/num_threads; // ceil division  //only_for_context cpu_openmp
//            #pragma omp parallel for                                                       //only_for_context cpu_openmp
//            for (int64_t batch_id = 0; batch_id < num_threads; batch_id++) {               //only_for_context cpu_openmp
                LocalParticle lpart;
                lpart.io_buffer = io_buffer;
//                int64_t part_id = batch_id * chunk_size;                                       //only_for_context cpu_openmp
//                int64_t end_id = (batch_id + 1) * chunk_size;                                  //only_for_context cpu_openmp
//                if (end_id > capacity) end_id = capacity;                                      //only_for_context cpu_openmp
    
                int64_t part_id = 0;                    //only_for_context cpu_serial
//                int64_t part_id = blockDim.x * blockIdx.x + threadIdx.x; //only_for_context cuda
//                int64_t part_id = get_global_id(0);                    //only_for_context opencl
                int64_t end_id = 0; // unused outside of openmp  //only_for_context cpu_serial cuda opencl
    
                int64_t part_capacity = ParticlesData_get__capacity(particles);
                if (part_id<part_capacity){
                    Particles_to_LocalParticle(particles, &lpart, part_id, end_id);
                    if (check_is_active(&lpart)>0){
              InvalidCollimator_track_local_particle(el, &lpart);

                    }
                    if (check_is_active(&lpart)>0 && flag_increment_at_element){
                            increment_at_element(&lpart, 1);
                    }
                }
//            } //only_for_context cpu_openmp
            
            // On OpenMP we want to additionally by default reorganize all
            // the particles.
//            #ifndef XT_OMP_SKIP_REORGANIZE                             //only_for_context cpu_openmp
//            LocalParticle lpart;                                       //only_for_context cpu_openmp
//            lpart.io_buffer = io_buffer;                               //only_for_context cpu_openmp
//            Particles_to_LocalParticle(particles, &lpart, 0, capacity);//only_for_context cpu_openmp
//            check_is_active(&lpart);                                   //only_for_context cpu_openmp
//            #endif                                                     //only_for_context cpu_openmp
        }

#ifndef XOBJ_TYPEDEF_RandomRutherfordData
#define XOBJ_TYPEDEF_RandomRutherfordData
typedef   struct RandomRutherfordData_s * RandomRutherfordData;
 static inline RandomRutherfordData RandomRutherfordData_getp(RandomRutherfordData  obj){
  int64_t offset=0;
  return (RandomRutherfordData)(( char*) obj+offset);
}
 static inline double RandomRutherfordData_get_lower_val(const RandomRutherfordData  obj){
  int64_t offset=0;
  return *( double*)(( char*) obj+offset);
}
 static inline void RandomRutherfordData_set_lower_val(RandomRutherfordData  obj, double value){
  int64_t offset=0;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* RandomRutherfordData_getp_lower_val(RandomRutherfordData  obj){
  int64_t offset=0;
  return ( double*)(( char*) obj+offset);
}
 static inline double RandomRutherfordData_get_upper_val(const RandomRutherfordData  obj){
  int64_t offset=0;
  offset+=8;
  return *( double*)(( char*) obj+offset);
}
 static inline void RandomRutherfordData_set_upper_val(RandomRutherfordData  obj, double value){
  int64_t offset=0;
  offset+=8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* RandomRutherfordData_getp_upper_val(RandomRutherfordData  obj){
  int64_t offset=0;
  offset+=8;
  return ( double*)(( char*) obj+offset);
}
 static inline double RandomRutherfordData_get_A(const RandomRutherfordData  obj){
  int64_t offset=0;
  offset+=16;
  return *( double*)(( char*) obj+offset);
}
 static inline void RandomRutherfordData_set_A(RandomRutherfordData  obj, double value){
  int64_t offset=0;
  offset+=16;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* RandomRutherfordData_getp_A(RandomRutherfordData  obj){
  int64_t offset=0;
  offset+=16;
  return ( double*)(( char*) obj+offset);
}
 static inline double RandomRutherfordData_get_B(const RandomRutherfordData  obj){
  int64_t offset=0;
  offset+=24;
  return *( double*)(( char*) obj+offset);
}
 static inline void RandomRutherfordData_set_B(RandomRutherfordData  obj, double value){
  int64_t offset=0;
  offset+=24;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* RandomRutherfordData_getp_B(RandomRutherfordData  obj){
  int64_t offset=0;
  offset+=24;
  return ( double*)(( char*) obj+offset);
}
 static inline int8_t RandomRutherfordData_get_Newton_iterations(const RandomRutherfordData  obj){
  int64_t offset=0;
  offset+=32;
  return *(( int8_t*) obj+offset);
}
 static inline void RandomRutherfordData_set_Newton_iterations(RandomRutherfordData  obj, int8_t value){
  int64_t offset=0;
  offset+=32;
  *(( int8_t*) obj+offset)=value;
}
 static inline  int8_t* RandomRutherfordData_getp_Newton_iterations(RandomRutherfordData  obj){
  int64_t offset=0;
  offset+=32;
  return ( int8_t*)(( char*) obj+offset);
}
#endif
// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_CONSTANTS_H
#define XTRACK_CONSTANTS_H

#if !defined( C_LIGHT )
    #define   C_LIGHT ( 299792458.0 )
#endif /* !defined( C_LIGHT ) */

#if !defined( EPSILON_0 )
    #define   EPSILON_0 (8.854187817620e-12)
#endif /* !defined( EPSILON_0 ) */

#if !defined( PI )
    #define PI (3.1415926535897932384626433832795028841971693993751)
#endif /* !defined( PI ) */

#if !defined( MU_0 )
    #define MU_0 (PI*4.0e-7)
#endif /* !defined( MU_0 ) */

#if !defined( DEG2RAD )
    #define DEG2RAD (0.0174532925199432957692369076848861271344287188854)
#endif /* !defiend( DEG2RAD ) */

#if !defined( RAD2DEG )
    #define RAD2DEG (57.29577951308232087679815481410517033240547246656442)
#endif /* !defiend( RAD2DEG ) */

#if !defined( SQRT_PI )
    #define SQRT_PI (1.7724538509055160272981674833411451827975494561224)
#endif /* !defined( SQRT_PI ) */

#if !defined( QELEM )
    #define QELEM (1.60217662e-19)
#endif /* !defined( QELEM ) */

#if !defined( DBL_MAX )
    #define DBL_MAX (1.7976931348623158e+308)
#endif /* !defined( DBL_MAX ) */

#if !defined( DBL_MIN )
    #define DBL_MIN (2.2250738585072014e-308)
#endif /* !defined( DBL_MIN ) */

#if !defined( DBL_EPSILON )
    #define DBL_EPSILON (2.2204460492503131e-16)
#endif /* !defined( DBL_EPSILON ) */

#endif /* XTRACK_CONSTANTS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_FUNCTIONS_H
#define XTRACK_FUNCTIONS_H

 static inline
void kill_all_particles(LocalParticle* part0, int64_t kill_state) {

    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

        LocalParticle_kill_particle(part, kill_state);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

}


 static inline
int8_t assert_tracking(LocalParticle* part, int64_t kill_state){
    // Whenever we are not tracking, e.g. in a twiss, the particle will be at_turn < 0.
    // We test this to distinguish genuine tracking from twiss.
    if (LocalParticle_get_at_turn(part) < 0){
        LocalParticle_kill_particle(part, kill_state);
        return 0;
    }
    return 1;
}

#endif /* XTRACK_FUNCTIONS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_PARTICLE_STATES_H
#define XTRACK_PARTICLE_STATES_H

#define  XT_LOST_ON_APERTURE       0
#define  XT_LOST_ON_LONG_CUT      -2
#define  XT_LOST_ALL_E_IN_SYNRAD -10
#define  RNG_ERR_SEEDS_NOT_SET   -20
#define  RNG_ERR_INVALID_TRACK   -21
#define  RNG_ERR_RUTH_NOT_SET    -22

#endif /* XTRACK_PARTICLE_STATES_H */

// Original source from http://www.mymathlib.com/functions/exponential_integrals.html

////////////////////////////////////////////////////////////////////////////////
// File: exponential_integral_Ei.c                                            //
// Routine(s):                                                                //
//    Exponential_Integral_Ei                                                 //
//    xExponential_Integral_Ei                                                //
////////////////////////////////////////////////////////////////////////////////
#ifndef XTRACK_EI_H
#define XTRACK_EI_H

#include <math.h> // required for fabs(), exp() and log() //only_for_context cpu_serial cpu_openmp
#include <float.h> // required for DBL_EPSILON, DBL_MAX //only_for_context cpu_serial cpu_openmp

//                         Internally Defined Routines                        //
 static inline double Exponential_Integral_Ei( double x );
 static inline double Continued_Fraction_Ei( double x );
 static inline double Power_Series_Ei( double x );
 static inline double Argument_Addition_Series_Ei( double x );


//                         Internally Defined Constants                       //
static const double epsilon = 10.0 * DBL_EPSILON;
static const double ei[] = {
    1.915047433355013959531e2,  4.403798995348382689974e2,
    1.037878290717089587658e3,  2.492228976241877759138e3,
    6.071406374098611507965e3,  1.495953266639752885229e4,
    3.719768849068903560439e4,  9.319251363396537129882e4,
    2.349558524907683035782e5,  5.955609986708370018502e5,
    1.516637894042516884433e6,  3.877904330597443502996e6,
    9.950907251046844760026e6,  2.561565266405658882048e7,
    6.612718635548492136250e7,  1.711446713003636684975e8,
    4.439663698302712208698e8,  1.154115391849182948287e9,
    3.005950906525548689841e9,  7.842940991898186370453e9,
    2.049649711988081236484e10, 5.364511859231469415605e10,
    1.405991957584069047340e11, 3.689732094072741970640e11,
    9.694555759683939661662e11, 2.550043566357786926147e12,
    6.714640184076497558707e12, 1.769803724411626854310e13,
    4.669055014466159544500e13, 1.232852079912097685431e14,
    3.257988998672263996790e14, 8.616388199965786544948e14,
    2.280446200301902595341e15, 6.039718263611241578359e15,
    1.600664914324504111070e16, 4.244796092136850759368e16,
    1.126348290166966760275e17, 2.990444718632336675058e17,
    7.943916035704453771510e17, 2.111342388647824195000e18,
    5.614329680810343111535e18, 1.493630213112993142255e19,
    3.975442747903744836007e19, 1.058563689713169096306e20
 };


////////////////////////////////////////////////////////////////////////////////
// double Exponential_Integral_Ei( double x )                                 //
//                                                                            //
//  Description:                                                              //
//     The exponential integral Ei(x) is the integral with integrand          //
//                             exp(t) / t                                     //
//     where the integral extends from -inf to x.                             //
//     Note that there is a singularity at t = 0.  Therefore for x > 0, the   //
//     integral is defined to be the Cauchy principal value:                  //
//          lim { I[-inf, -eta] exp(-t) dt / t + I[eta, x] exp(-t) dt / t }   //
//     in which the limit is taken as eta > 0 approaches 0 and I[a,b]         //
//     denotes the integral from a to b.                                      //
//                                                                            //
//  Arguments:                                                                //
//     double  x  The argument of the exponential integral Ei().              //
//                                                                            //
//  Return Value:                                                             //
//     The value of the exponential integral Ei evaluated at x.               //
//     If x = 0.0, then Ei is -inf and -DBL_MAX is returned.                  //
//                                                                            //
//  Example:                                                                  //
//     double y, x;                                                           //
//                                                                            //
//     ( code to initialize x )                                               //
//                                                                            //
//     y = Exponential_Integral_Ei( x );                                      //
////////////////////////////////////////////////////////////////////////////////

 static inline
double Exponential_Integral_Ei( double x ){
   if ( x < -5.0 ) return Continued_Fraction_Ei(x);
   if ( x == 0.0 ) return -DBL_MAX;
   if ( x < 6.8 )  return Power_Series_Ei(x);
   if ( x < 50.0 ) return Argument_Addition_Series_Ei(x);
   return Continued_Fraction_Ei(x);
}

////////////////////////////////////////////////////////////////////////////////
// static double Continued_Fraction_Ei( double x )                            //
//                                                                            //
//  Description:                                                              //
//     For x < -5 or x > 50, the continued fraction representation of Ei      //
//     converges fairly rapidly.                                              //
//                                                                            //
//     The continued fraction expansion of Ei(x) is:                          //
//        Ei(x) = -exp(x) { 1/(-x+1-) 1/(-x+3-) 4/(-x+5-) 9/(-x+7-) ... }.    //
//                                                                            //
//                                                                            //
//  Arguments:                                                                //
//     double  x                                                              //
//                The argument of the exponential integral Ei().              //
//                                                                            //
//  Return Value:                                                             //
//     The value of the exponential integral Ei evaluated at x.               //
////////////////////////////////////////////////////////////////////////////////

 static inline
double Continued_Fraction_Ei( double x ){
   double Am1 = 1.0;
   double A0 = 0.0;
   double Bm1 = 0.0;
   double B0 = 1.0;
   double a = exp(x);
   double b = -x + 1.0;
   double Ap1 = b * A0 + a * Am1;
   double Bp1 = b * B0 + a * Bm1;
   int j = 1;

   a = 1.0;
   while ( fabs(Ap1 * B0 - A0 * Bp1) > epsilon * fabs(A0 * Bp1) ) {
      if ( fabs(Bp1) > 1.0) {
         Am1 = A0 / Bp1;
         A0 = Ap1 / Bp1;
         Bm1 = B0 / Bp1;
         B0 = 1.0;
      } else {
         Am1 = A0;
         A0 = Ap1;
         Bm1 = B0;
         B0 = Bp1;
      }
      a = -j * j;
      b += 2.0;
      Ap1 = b * A0 + a * Am1;
      Bp1 = b * B0 + a * Bm1;
      j += 1;
   }
   return (-Ap1 / Bp1);
}


////////////////////////////////////////////////////////////////////////////////
// static double Power_Series_Ei( double x )                                  //
//                                                                            //
//  Description:                                                              //
//     For -5 < x < 6.8, the power series representation for                  //
//     (Ei(x) - gamma - ln|x|)/exp(x) is used, where gamma is Euler's gamma   //
//     constant.                                                              //
//     Note that for x = 0.0, Ei is -inf.  In which case -DBL_MAX is          //
//     returned.                                                              //
//                                                                            //
//     The power series expansion of (Ei(x) - gamma - ln|x|) / exp(x) is      //
//        - Sum(1 + 1/2 + ... + 1/j) (-x)^j / j!, where the Sum extends       //
//        from j = 1 to inf.                                                  //
//                                                                            //
//  Arguments:                                                                //
//     double  x                                                              //
//                The argument of the exponential integral Ei().              //
//                                                                            //
//  Return Value:                                                             //
//     The value of the exponential integral Ei evaluated at x.               //
////////////////////////////////////////////////////////////////////////////////

 static inline
double Power_Series_Ei( double x ){
   double xn = -x;
   double Sn = -x;
   double Sm1 = 0.0;
   double hsum = 1.0;
   double g = 0.5772156649015328606065121;
   double y = 1.0;
   double factorial = 1.0;

   if ( x == 0.0 ) return (double) -DBL_MAX;

   while ( fabs(Sn - Sm1) > epsilon * fabs(Sm1) ) {
      Sm1 = Sn;
      y += 1.0;
      xn *= (-x);
      factorial *= y;
      hsum += (1.0 / y);
      Sn += hsum * xn / factorial;
   }
   return (g + log(fabs(x)) - exp(x) * Sn);
}


////////////////////////////////////////////////////////////////////////////////
// static double Argument_Addition_Series_Ei( double x )                      //
//                                                                            //
//  Description:                                                              //
//     For 6.8 < x < 50.0, the argument addition series is used to calculate  //
//     Ei.                                                                    //
//                                                                            //
//     The argument addition series for Ei(x) is:                             //
//     Ei(x+dx) = Ei(x) + exp(x) Sum j! [exp(j) expj(-dx) - 1] / x^(j+1),     //
//     where the Sum extends from j = 0 to inf, |x| > |dx| and expj(y) is     //
//     the exponential polynomial expj(y) = Sum y^k / k!, the Sum extending   //
//     from k = 0 to k = j.                                                   //
//                                                                            //
//  Arguments:                                                                //
//     double  x                                                              //
//                The argument of the exponential integral Ei().              //
//                                                                            //
//  Return Value:                                                             //
//     The value of the exponential integral Ei evaluated at x.               //
////////////////////////////////////////////////////////////////////////////////

 static inline
double Argument_Addition_Series_Ei( double x ){
   int  k = (int) (x + 0.5);
   int  j = 0;
   double xx = (double) k;
   double dx = x - xx;
   double xxj = xx;
   double edx = exp(dx);
   double Sm = 1.0;
   double Sn = (edx - 1.0) / xxj;
   double term = DBL_MAX;
   double factorial = 1.0;
   double dxj = 1.0;

   while (fabs(term) > epsilon * fabs(Sn) ) {
      j++;
      factorial *= (double) j;
      xxj *= xx;
      dxj *= (-dx);
      Sm += (dxj / factorial);
      term = ( factorial * (edx * Sm - 1.0) ) / xxj;
      Sn += term;
   }

   return ei[k-7] + Sn * exp(xx);
}

#endif /* XTRACK_EI_H */

// copyright ############################### //
// This file is part of the Xtrack Package.   //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_RUTHERFORD_RNG_H
#define XTRACK_RUTHERFORD_RNG_H
#include <stdlib.h> //only_for_context cpu_serial cpu_openmp
#include <math.h> //only_for_context cpu_serial cpu_openmp
#include <time.h> //only_for_context cpu_serial cpu_openmp

    
 static inline
int8_t assert_rutherford_set(RandomRutherfordData rng, LocalParticle* part, int64_t kill_state){
    double A = RandomRutherfordData_get_A(rng);
    double B = RandomRutherfordData_get_B(rng);
    if (A==0. && B==0.) {
        LocalParticle_kill_particle(part, kill_state);
        return 0;
    }
    return 1;
}


// TODO: how to optimise Newton's method??

// PDF of Rutherford distribution
 static inline
double ruth_PDF(double t, double A, double B){
    return (A/pow(t, 2.))*(exp(-B*t));
}

// CDF of Rutherford distribution
 static inline
double ruth_CDF(double t, double A, double B, double t0){
   return A*B*Exponential_Integral_Ei(-B*t0) + t0*ruth_PDF(t0, A, B)
        - A*B*Exponential_Integral_Ei(-B*t)  - t*ruth_PDF(t, A, B);
}

 
void RandomRutherford_set(RandomRutherfordData rng, double A, double B, double lower_val, double upper_val){
    // Normalise PDF
    double N = ruth_CDF(upper_val, A, B, lower_val);
    RandomRutherfordData_set_A(rng, A/N);
    RandomRutherfordData_set_B(rng, B);
    RandomRutherfordData_set_lower_val(rng, lower_val);
    RandomRutherfordData_set_upper_val(rng, upper_val);
}

// Generate a random value weighted with a Rutherford distribution
 static inline
double RandomRutherford_generate(RandomRutherfordData rng, LocalParticle* part){

    // get the parameters
    double x0     = RandomRutherfordData_get_lower_val(rng);
    int8_t n_iter = RandomRutherfordData_get_Newton_iterations(rng);
    double A      = RandomRutherfordData_get_A(rng);
    double B      = RandomRutherfordData_get_B(rng);
    
    if (A==0. || B==0.){
        // Not initialised
        LocalParticle_kill_particle(part, RNG_ERR_RUTH_NOT_SET);
        return 0.;
    }

    // sample a random uniform
    double t = RandomUniform_generate(part);

    // initial estimate is the lower border
    double x = x0;

    // HACK to let iterations depend on sample to improve speed
    // based on Berylium being worst performing and hcut as in materials table
    // DOES NOT WORK
//     if (n_iter==0){
//         if (t<0.1) {
//             n_iter = 3;
//         } else if (t<0.35) {
//             n_iter = 4;
//         } else if (t<0.63) {
//             n_iter = 5;
//         } else if (t<0.8) {
//             n_iter = 6;
//         } else if (t<0.92) {
//             n_iter = 7;
//         } else if (t<0.96) {
//             n_iter = 8;
//         } else if (t<0.98) {
//             n_iter = 9;
//         } else {
//             n_iter = 10;
//         }
//     }

    // solve CDF(x) == t for x
    int8_t i = 1;
    while(i <= n_iter) {
        x = x - (ruth_CDF(x, A, B, x0)-t)/ruth_PDF(x, A, B);
        i++;
    }

    return x;
}


 static inline
void RandomRutherford_sample(RandomRutherfordData rng, LocalParticle* part0,
                               double* samples, int64_t n_samples_per_seed){

    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

    int i;
    for (i=0; i<n_samples_per_seed; ++i){
        double val = RandomRutherford_generate(rng, part);
        samples[n_samples_per_seed*LocalParticle_get_particle_id(part) + i] = val;
    }

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

}


 static inline
void RandomRutherford_track_local_particle(RandomRutherfordData rng, LocalParticle* part0) {
    kill_all_particles(part0, RNG_ERR_INVALID_TRACK);
}


#endif /* XTRACK_RUTHERFORD_RNG_H */


             
            void RandomRutherford_track_particles(
               RandomRutherfordData el,

                             ParticlesData particles,

                             int64_t flag_increment_at_element,
                  int8_t* io_buffer){
//            const int num_threads = omp_get_max_threads();                                 //only_for_context cpu_openmp
//            const int64_t capacity = ParticlesData_get__capacity(particles);               //only_for_context cpu_openmp
//            const int64_t chunk_size = (capacity + num_threads - 1)/num_threads; // ceil division  //only_for_context cpu_openmp
//            #pragma omp parallel for                                                       //only_for_context cpu_openmp
//            for (int64_t batch_id = 0; batch_id < num_threads; batch_id++) {               //only_for_context cpu_openmp
                LocalParticle lpart;
                lpart.io_buffer = io_buffer;
//                int64_t part_id = batch_id * chunk_size;                                       //only_for_context cpu_openmp
//                int64_t end_id = (batch_id + 1) * chunk_size;                                  //only_for_context cpu_openmp
//                if (end_id > capacity) end_id = capacity;                                      //only_for_context cpu_openmp
    
                int64_t part_id = 0;                    //only_for_context cpu_serial
//                int64_t part_id = blockDim.x * blockIdx.x + threadIdx.x; //only_for_context cuda
//                int64_t part_id = get_global_id(0);                    //only_for_context opencl
                int64_t end_id = 0; // unused outside of openmp  //only_for_context cpu_serial cuda opencl
    
                int64_t part_capacity = ParticlesData_get__capacity(particles);
                if (part_id<part_capacity){
                    Particles_to_LocalParticle(particles, &lpart, part_id, end_id);
                    if (check_is_active(&lpart)>0){
              RandomRutherford_track_local_particle(el, &lpart);

                    }
                    if (check_is_active(&lpart)>0 && flag_increment_at_element){
                            increment_at_element(&lpart, 1);
                    }
                }
//            } //only_for_context cpu_openmp
            
            // On OpenMP we want to additionally by default reorganize all
            // the particles.
//            #ifndef XT_OMP_SKIP_REORGANIZE                             //only_for_context cpu_openmp
//            LocalParticle lpart;                                       //only_for_context cpu_openmp
//            lpart.io_buffer = io_buffer;                               //only_for_context cpu_openmp
//            Particles_to_LocalParticle(particles, &lpart, 0, capacity);//only_for_context cpu_openmp
//            check_is_active(&lpart);                                   //only_for_context cpu_openmp
//            #endif                                                     //only_for_context cpu_openmp
        }


             
            void sample_ruth(
               RandomRutherfordData el,

                             ParticlesData particles,
    double* samples,  int64_t n_samples_per_seed, 
                             int64_t flag_increment_at_element,
                  int8_t* io_buffer){
//            const int num_threads = omp_get_max_threads();                                 //only_for_context cpu_openmp
//            const int64_t capacity = ParticlesData_get__capacity(particles);               //only_for_context cpu_openmp
//            const int64_t chunk_size = (capacity + num_threads - 1)/num_threads; // ceil division  //only_for_context cpu_openmp
//            #pragma omp parallel for                                                       //only_for_context cpu_openmp
//            for (int64_t batch_id = 0; batch_id < num_threads; batch_id++) {               //only_for_context cpu_openmp
                LocalParticle lpart;
                lpart.io_buffer = io_buffer;
//                int64_t part_id = batch_id * chunk_size;                                       //only_for_context cpu_openmp
//                int64_t end_id = (batch_id + 1) * chunk_size;                                  //only_for_context cpu_openmp
//                if (end_id > capacity) end_id = capacity;                                      //only_for_context cpu_openmp
    
                int64_t part_id = 0;                    //only_for_context cpu_serial
//                int64_t part_id = blockDim.x * blockIdx.x + threadIdx.x; //only_for_context cuda
//                int64_t part_id = get_global_id(0);                    //only_for_context opencl
                int64_t end_id = 0; // unused outside of openmp  //only_for_context cpu_serial cuda opencl
    
                int64_t part_capacity = ParticlesData_get__capacity(particles);
                if (part_id<part_capacity){
                    Particles_to_LocalParticle(particles, &lpart, part_id, end_id);
                    if (check_is_active(&lpart)>0){
              RandomRutherford_sample(el, &lpart, samples, n_samples_per_seed);

                    }
                    if (check_is_active(&lpart)>0 && flag_increment_at_element){
                            increment_at_element(&lpart, 1);
                    }
                }
//            } //only_for_context cpu_openmp
            
            // On OpenMP we want to additionally by default reorganize all
            // the particles.
//            #ifndef XT_OMP_SKIP_REORGANIZE                             //only_for_context cpu_openmp
//            LocalParticle lpart;                                       //only_for_context cpu_openmp
//            lpart.io_buffer = io_buffer;                               //only_for_context cpu_openmp
//            Particles_to_LocalParticle(particles, &lpart, 0, capacity);//only_for_context cpu_openmp
//            check_is_active(&lpart);                                   //only_for_context cpu_openmp
//            #endif                                                     //only_for_context cpu_openmp
        }

#ifndef XOBJ_TYPEDEF_RandomExponentialData
#define XOBJ_TYPEDEF_RandomExponentialData
typedef   struct RandomExponentialData_s * RandomExponentialData;
 static inline RandomExponentialData RandomExponentialData_getp(RandomExponentialData  obj){
  int64_t offset=0;
  return (RandomExponentialData)(( char*) obj+offset);
}
 static inline uint8_t RandomExponentialData_get__dummy(const RandomExponentialData  obj){
  int64_t offset=0;
  return *(( uint8_t*) obj+offset);
}
 static inline void RandomExponentialData_set__dummy(RandomExponentialData  obj, uint8_t value){
  int64_t offset=0;
  *(( uint8_t*) obj+offset)=value;
}
 static inline  uint8_t* RandomExponentialData_getp__dummy(RandomExponentialData  obj){
  int64_t offset=0;
  return ( uint8_t*)(( char*) obj+offset);
}
#endif
// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_CONSTANTS_H
#define XTRACK_CONSTANTS_H

#if !defined( C_LIGHT )
    #define   C_LIGHT ( 299792458.0 )
#endif /* !defined( C_LIGHT ) */

#if !defined( EPSILON_0 )
    #define   EPSILON_0 (8.854187817620e-12)
#endif /* !defined( EPSILON_0 ) */

#if !defined( PI )
    #define PI (3.1415926535897932384626433832795028841971693993751)
#endif /* !defined( PI ) */

#if !defined( MU_0 )
    #define MU_0 (PI*4.0e-7)
#endif /* !defined( MU_0 ) */

#if !defined( DEG2RAD )
    #define DEG2RAD (0.0174532925199432957692369076848861271344287188854)
#endif /* !defiend( DEG2RAD ) */

#if !defined( RAD2DEG )
    #define RAD2DEG (57.29577951308232087679815481410517033240547246656442)
#endif /* !defiend( RAD2DEG ) */

#if !defined( SQRT_PI )
    #define SQRT_PI (1.7724538509055160272981674833411451827975494561224)
#endif /* !defined( SQRT_PI ) */

#if !defined( QELEM )
    #define QELEM (1.60217662e-19)
#endif /* !defined( QELEM ) */

#if !defined( DBL_MAX )
    #define DBL_MAX (1.7976931348623158e+308)
#endif /* !defined( DBL_MAX ) */

#if !defined( DBL_MIN )
    #define DBL_MIN (2.2250738585072014e-308)
#endif /* !defined( DBL_MIN ) */

#if !defined( DBL_EPSILON )
    #define DBL_EPSILON (2.2204460492503131e-16)
#endif /* !defined( DBL_EPSILON ) */

#endif /* XTRACK_CONSTANTS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_FUNCTIONS_H
#define XTRACK_FUNCTIONS_H

 static inline
void kill_all_particles(LocalParticle* part0, int64_t kill_state) {

    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

        LocalParticle_kill_particle(part, kill_state);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

}


 static inline
int8_t assert_tracking(LocalParticle* part, int64_t kill_state){
    // Whenever we are not tracking, e.g. in a twiss, the particle will be at_turn < 0.
    // We test this to distinguish genuine tracking from twiss.
    if (LocalParticle_get_at_turn(part) < 0){
        LocalParticle_kill_particle(part, kill_state);
        return 0;
    }
    return 1;
}

#endif /* XTRACK_FUNCTIONS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_PARTICLE_STATES_H
#define XTRACK_PARTICLE_STATES_H

#define  XT_LOST_ON_APERTURE       0
#define  XT_LOST_ON_LONG_CUT      -2
#define  XT_LOST_ALL_E_IN_SYNRAD -10
#define  RNG_ERR_SEEDS_NOT_SET   -20
#define  RNG_ERR_INVALID_TRACK   -21
#define  RNG_ERR_RUTH_NOT_SET    -22

#endif /* XTRACK_PARTICLE_STATES_H */

// copyright ############################### //
// This file is part of the Xtrack Package.   //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_EXPONENTIAL_RNG_H
#define XTRACK_EXPONENTIAL_RNG_H
#include <stdlib.h> //only_for_context cpu_serial cpu_openmp
#include <math.h> //only_for_context cpu_serial cpu_openmp
#include <time.h> //only_for_context cpu_serial cpu_openmp


 static inline
double RandomExponential_generate(LocalParticle* part){
    double x1 = RandomUniform_generate(part);
    while(x1==0.0){
        x1 = RandomUniform_generate(part);
    }
    return -log(x1);
}


 static inline
void RandomExponential_sample(RandomExponentialData rng, LocalParticle* part0,
                               double* samples, int64_t n_samples_per_seed){

    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

    int i;
    for (i=0; i<n_samples_per_seed; ++i){
        double val = RandomExponential_generate(part);
        samples[n_samples_per_seed*LocalParticle_get_particle_id(part) + i] = val;
    }

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

}


 static inline
void RandomExponential_track_local_particle(RandomExponentialData rng, LocalParticle* part0) {
    kill_all_particles(part0, RNG_ERR_INVALID_TRACK);
}


#endif /* XTRACK_EXPONENTIAL_RNG_H */


             
            void RandomExponential_track_particles(
               RandomExponentialData el,

                             ParticlesData particles,

                             int64_t flag_increment_at_element,
                  int8_t* io_buffer){
//            const int num_threads = omp_get_max_threads();                                 //only_for_context cpu_openmp
//            const int64_t capacity = ParticlesData_get__capacity(particles);               //only_for_context cpu_openmp
//            const int64_t chunk_size = (capacity + num_threads - 1)/num_threads; // ceil division  //only_for_context cpu_openmp
//            #pragma omp parallel for                                                       //only_for_context cpu_openmp
//            for (int64_t batch_id = 0; batch_id < num_threads; batch_id++) {               //only_for_context cpu_openmp
                LocalParticle lpart;
                lpart.io_buffer = io_buffer;
//                int64_t part_id = batch_id * chunk_size;                                       //only_for_context cpu_openmp
//                int64_t end_id = (batch_id + 1) * chunk_size;                                  //only_for_context cpu_openmp
//                if (end_id > capacity) end_id = capacity;                                      //only_for_context cpu_openmp
    
                int64_t part_id = 0;                    //only_for_context cpu_serial
//                int64_t part_id = blockDim.x * blockIdx.x + threadIdx.x; //only_for_context cuda
//                int64_t part_id = get_global_id(0);                    //only_for_context opencl
                int64_t end_id = 0; // unused outside of openmp  //only_for_context cpu_serial cuda opencl
    
                int64_t part_capacity = ParticlesData_get__capacity(particles);
                if (part_id<part_capacity){
                    Particles_to_LocalParticle(particles, &lpart, part_id, end_id);
                    if (check_is_active(&lpart)>0){
              RandomExponential_track_local_particle(el, &lpart);

                    }
                    if (check_is_active(&lpart)>0 && flag_increment_at_element){
                            increment_at_element(&lpart, 1);
                    }
                }
//            } //only_for_context cpu_openmp
            
            // On OpenMP we want to additionally by default reorganize all
            // the particles.
//            #ifndef XT_OMP_SKIP_REORGANIZE                             //only_for_context cpu_openmp
//            LocalParticle lpart;                                       //only_for_context cpu_openmp
//            lpart.io_buffer = io_buffer;                               //only_for_context cpu_openmp
//            Particles_to_LocalParticle(particles, &lpart, 0, capacity);//only_for_context cpu_openmp
//            check_is_active(&lpart);                                   //only_for_context cpu_openmp
//            #endif                                                     //only_for_context cpu_openmp
        }


             
            void sample_exp(
               RandomExponentialData el,

                             ParticlesData particles,
    double* samples,  int64_t n_samples_per_seed, 
                             int64_t flag_increment_at_element,
                  int8_t* io_buffer){
//            const int num_threads = omp_get_max_threads();                                 //only_for_context cpu_openmp
//            const int64_t capacity = ParticlesData_get__capacity(particles);               //only_for_context cpu_openmp
//            const int64_t chunk_size = (capacity + num_threads - 1)/num_threads; // ceil division  //only_for_context cpu_openmp
//            #pragma omp parallel for                                                       //only_for_context cpu_openmp
//            for (int64_t batch_id = 0; batch_id < num_threads; batch_id++) {               //only_for_context cpu_openmp
                LocalParticle lpart;
                lpart.io_buffer = io_buffer;
//                int64_t part_id = batch_id * chunk_size;                                       //only_for_context cpu_openmp
//                int64_t end_id = (batch_id + 1) * chunk_size;                                  //only_for_context cpu_openmp
//                if (end_id > capacity) end_id = capacity;                                      //only_for_context cpu_openmp
    
                int64_t part_id = 0;                    //only_for_context cpu_serial
//                int64_t part_id = blockDim.x * blockIdx.x + threadIdx.x; //only_for_context cuda
//                int64_t part_id = get_global_id(0);                    //only_for_context opencl
                int64_t end_id = 0; // unused outside of openmp  //only_for_context cpu_serial cuda opencl
    
                int64_t part_capacity = ParticlesData_get__capacity(particles);
                if (part_id<part_capacity){
                    Particles_to_LocalParticle(particles, &lpart, part_id, end_id);
                    if (check_is_active(&lpart)>0){
              RandomExponential_sample(el, &lpart, samples, n_samples_per_seed);

                    }
                    if (check_is_active(&lpart)>0 && flag_increment_at_element){
                            increment_at_element(&lpart, 1);
                    }
                }
//            } //only_for_context cpu_openmp
            
            // On OpenMP we want to additionally by default reorganize all
            // the particles.
//            #ifndef XT_OMP_SKIP_REORGANIZE                             //only_for_context cpu_openmp
//            LocalParticle lpart;                                       //only_for_context cpu_openmp
//            lpart.io_buffer = io_buffer;                               //only_for_context cpu_openmp
//            Particles_to_LocalParticle(particles, &lpart, 0, capacity);//only_for_context cpu_openmp
//            check_is_active(&lpart);                                   //only_for_context cpu_openmp
//            #endif                                                     //only_for_context cpu_openmp
        }

#ifndef XOBJ_TYPEDEF_RandomNormalData
#define XOBJ_TYPEDEF_RandomNormalData
typedef   struct RandomNormalData_s * RandomNormalData;
 static inline RandomNormalData RandomNormalData_getp(RandomNormalData  obj){
  int64_t offset=0;
  return (RandomNormalData)(( char*) obj+offset);
}
 static inline uint8_t RandomNormalData_get__dummy(const RandomNormalData  obj){
  int64_t offset=0;
  return *(( uint8_t*) obj+offset);
}
 static inline void RandomNormalData_set__dummy(RandomNormalData  obj, uint8_t value){
  int64_t offset=0;
  *(( uint8_t*) obj+offset)=value;
}
 static inline  uint8_t* RandomNormalData_getp__dummy(RandomNormalData  obj){
  int64_t offset=0;
  return ( uint8_t*)(( char*) obj+offset);
}
#endif
// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_CONSTANTS_H
#define XTRACK_CONSTANTS_H

#if !defined( C_LIGHT )
    #define   C_LIGHT ( 299792458.0 )
#endif /* !defined( C_LIGHT ) */

#if !defined( EPSILON_0 )
    #define   EPSILON_0 (8.854187817620e-12)
#endif /* !defined( EPSILON_0 ) */

#if !defined( PI )
    #define PI (3.1415926535897932384626433832795028841971693993751)
#endif /* !defined( PI ) */

#if !defined( MU_0 )
    #define MU_0 (PI*4.0e-7)
#endif /* !defined( MU_0 ) */

#if !defined( DEG2RAD )
    #define DEG2RAD (0.0174532925199432957692369076848861271344287188854)
#endif /* !defiend( DEG2RAD ) */

#if !defined( RAD2DEG )
    #define RAD2DEG (57.29577951308232087679815481410517033240547246656442)
#endif /* !defiend( RAD2DEG ) */

#if !defined( SQRT_PI )
    #define SQRT_PI (1.7724538509055160272981674833411451827975494561224)
#endif /* !defined( SQRT_PI ) */

#if !defined( QELEM )
    #define QELEM (1.60217662e-19)
#endif /* !defined( QELEM ) */

#if !defined( DBL_MAX )
    #define DBL_MAX (1.7976931348623158e+308)
#endif /* !defined( DBL_MAX ) */

#if !defined( DBL_MIN )
    #define DBL_MIN (2.2250738585072014e-308)
#endif /* !defined( DBL_MIN ) */

#if !defined( DBL_EPSILON )
    #define DBL_EPSILON (2.2204460492503131e-16)
#endif /* !defined( DBL_EPSILON ) */

#endif /* XTRACK_CONSTANTS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_FUNCTIONS_H
#define XTRACK_FUNCTIONS_H

 static inline
void kill_all_particles(LocalParticle* part0, int64_t kill_state) {

    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

        LocalParticle_kill_particle(part, kill_state);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

}


 static inline
int8_t assert_tracking(LocalParticle* part, int64_t kill_state){
    // Whenever we are not tracking, e.g. in a twiss, the particle will be at_turn < 0.
    // We test this to distinguish genuine tracking from twiss.
    if (LocalParticle_get_at_turn(part) < 0){
        LocalParticle_kill_particle(part, kill_state);
        return 0;
    }
    return 1;
}

#endif /* XTRACK_FUNCTIONS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_PARTICLE_STATES_H
#define XTRACK_PARTICLE_STATES_H

#define  XT_LOST_ON_APERTURE       0
#define  XT_LOST_ON_LONG_CUT      -2
#define  XT_LOST_ALL_E_IN_SYNRAD -10
#define  RNG_ERR_SEEDS_NOT_SET   -20
#define  RNG_ERR_INVALID_TRACK   -21
#define  RNG_ERR_RUTH_NOT_SET    -22

#endif /* XTRACK_PARTICLE_STATES_H */

// copyright ############################### //
// This file is part of the Xtrack Package.   //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_NORMAL_RNG_H
#define XTRACK_NORMAL_RNG_H
#include <stdlib.h> //only_for_context cpu_serial cpu_openmp
#include <math.h> //only_for_context cpu_serial cpu_openmp
#include <time.h> //only_for_context cpu_serial cpu_openmp


 static inline
double RandomNormal_generate(LocalParticle* part){
    double x1 = RandomUniform_generate(part);
    while(x1==0.0){
        x1 = RandomUniform_generate(part);
    }
    x1 = sqrt(-2.0*log(x1));
    double x2 = RandomUniform_generate(part);
    x2 = 2.0*PI*x2;
    double r = x1*sin(x2);
    return r;
}


 static inline
void RandomNormal_sample(RandomNormalData rng, LocalParticle* part0,
                               double* samples, int64_t n_samples_per_seed){

    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

    int i;
    for (i=0; i<n_samples_per_seed; ++i){
        double val = RandomNormal_generate(part);
        samples[n_samples_per_seed*LocalParticle_get_particle_id(part) + i] = val;
    }

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

}


 static inline
void RandomNormal_track_local_particle(RandomNormalData rng, LocalParticle* part0) {
    kill_all_particles(part0, RNG_ERR_INVALID_TRACK);
}


#endif /* XTRACK_NORMAL_RNG_H */


             
            void RandomNormal_track_particles(
               RandomNormalData el,

                             ParticlesData particles,

                             int64_t flag_increment_at_element,
                  int8_t* io_buffer){
//            const int num_threads = omp_get_max_threads();                                 //only_for_context cpu_openmp
//            const int64_t capacity = ParticlesData_get__capacity(particles);               //only_for_context cpu_openmp
//            const int64_t chunk_size = (capacity + num_threads - 1)/num_threads; // ceil division  //only_for_context cpu_openmp
//            #pragma omp parallel for                                                       //only_for_context cpu_openmp
//            for (int64_t batch_id = 0; batch_id < num_threads; batch_id++) {               //only_for_context cpu_openmp
                LocalParticle lpart;
                lpart.io_buffer = io_buffer;
//                int64_t part_id = batch_id * chunk_size;                                       //only_for_context cpu_openmp
//                int64_t end_id = (batch_id + 1) * chunk_size;                                  //only_for_context cpu_openmp
//                if (end_id > capacity) end_id = capacity;                                      //only_for_context cpu_openmp
    
                int64_t part_id = 0;                    //only_for_context cpu_serial
//                int64_t part_id = blockDim.x * blockIdx.x + threadIdx.x; //only_for_context cuda
//                int64_t part_id = get_global_id(0);                    //only_for_context opencl
                int64_t end_id = 0; // unused outside of openmp  //only_for_context cpu_serial cuda opencl
    
                int64_t part_capacity = ParticlesData_get__capacity(particles);
                if (part_id<part_capacity){
                    Particles_to_LocalParticle(particles, &lpart, part_id, end_id);
                    if (check_is_active(&lpart)>0){
              RandomNormal_track_local_particle(el, &lpart);

                    }
                    if (check_is_active(&lpart)>0 && flag_increment_at_element){
                            increment_at_element(&lpart, 1);
                    }
                }
//            } //only_for_context cpu_openmp
            
            // On OpenMP we want to additionally by default reorganize all
            // the particles.
//            #ifndef XT_OMP_SKIP_REORGANIZE                             //only_for_context cpu_openmp
//            LocalParticle lpart;                                       //only_for_context cpu_openmp
//            lpart.io_buffer = io_buffer;                               //only_for_context cpu_openmp
//            Particles_to_LocalParticle(particles, &lpart, 0, capacity);//only_for_context cpu_openmp
//            check_is_active(&lpart);                                   //only_for_context cpu_openmp
//            #endif                                                     //only_for_context cpu_openmp
        }


             
            void sample_gauss(
               RandomNormalData el,

                             ParticlesData particles,
    double* samples,  int64_t n_samples_per_seed, 
                             int64_t flag_increment_at_element,
                  int8_t* io_buffer){
//            const int num_threads = omp_get_max_threads();                                 //only_for_context cpu_openmp
//            const int64_t capacity = ParticlesData_get__capacity(particles);               //only_for_context cpu_openmp
//            const int64_t chunk_size = (capacity + num_threads - 1)/num_threads; // ceil division  //only_for_context cpu_openmp
//            #pragma omp parallel for                                                       //only_for_context cpu_openmp
//            for (int64_t batch_id = 0; batch_id < num_threads; batch_id++) {               //only_for_context cpu_openmp
                LocalParticle lpart;
                lpart.io_buffer = io_buffer;
//                int64_t part_id = batch_id * chunk_size;                                       //only_for_context cpu_openmp
//                int64_t end_id = (batch_id + 1) * chunk_size;                                  //only_for_context cpu_openmp
//                if (end_id > capacity) end_id = capacity;                                      //only_for_context cpu_openmp
    
                int64_t part_id = 0;                    //only_for_context cpu_serial
//                int64_t part_id = blockDim.x * blockIdx.x + threadIdx.x; //only_for_context cuda
//                int64_t part_id = get_global_id(0);                    //only_for_context opencl
                int64_t end_id = 0; // unused outside of openmp  //only_for_context cpu_serial cuda opencl
    
                int64_t part_capacity = ParticlesData_get__capacity(particles);
                if (part_id<part_capacity){
                    Particles_to_LocalParticle(particles, &lpart, part_id, end_id);
                    if (check_is_active(&lpart)>0){
              RandomNormal_sample(el, &lpart, samples, n_samples_per_seed);

                    }
                    if (check_is_active(&lpart)>0 && flag_increment_at_element){
                            increment_at_element(&lpart, 1);
                    }
                }
//            } //only_for_context cpu_openmp
            
            // On OpenMP we want to additionally by default reorganize all
            // the particles.
//            #ifndef XT_OMP_SKIP_REORGANIZE                             //only_for_context cpu_openmp
//            LocalParticle lpart;                                       //only_for_context cpu_openmp
//            lpart.io_buffer = io_buffer;                               //only_for_context cpu_openmp
//            Particles_to_LocalParticle(particles, &lpart, 0, capacity);//only_for_context cpu_openmp
//            check_is_active(&lpart);                                   //only_for_context cpu_openmp
//            #endif                                                     //only_for_context cpu_openmp
        }

#ifndef XOBJ_TYPEDEF_BaseCollimatorData
#define XOBJ_TYPEDEF_BaseCollimatorData
typedef   struct BaseCollimatorData_s * BaseCollimatorData;
 static inline BaseCollimatorData BaseCollimatorData_getp(BaseCollimatorData  obj){
  int64_t offset=0;
  return (BaseCollimatorData)(( char*) obj+offset);
}
 static inline double BaseCollimatorData_get_inactive_front(const BaseCollimatorData  obj){
  int64_t offset=0;
  return *( double*)(( char*) obj+offset);
}
 static inline void BaseCollimatorData_set_inactive_front(BaseCollimatorData  obj, double value){
  int64_t offset=0;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* BaseCollimatorData_getp_inactive_front(BaseCollimatorData  obj){
  int64_t offset=0;
  return ( double*)(( char*) obj+offset);
}
 static inline double BaseCollimatorData_get_active_length(const BaseCollimatorData  obj){
  int64_t offset=0;
  offset+=8;
  return *( double*)(( char*) obj+offset);
}
 static inline void BaseCollimatorData_set_active_length(BaseCollimatorData  obj, double value){
  int64_t offset=0;
  offset+=8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* BaseCollimatorData_getp_active_length(BaseCollimatorData  obj){
  int64_t offset=0;
  offset+=8;
  return ( double*)(( char*) obj+offset);
}
 static inline double BaseCollimatorData_get_inactive_back(const BaseCollimatorData  obj){
  int64_t offset=0;
  offset+=16;
  return *( double*)(( char*) obj+offset);
}
 static inline void BaseCollimatorData_set_inactive_back(BaseCollimatorData  obj, double value){
  int64_t offset=0;
  offset+=16;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* BaseCollimatorData_getp_inactive_back(BaseCollimatorData  obj){
  int64_t offset=0;
  offset+=16;
  return ( double*)(( char*) obj+offset);
}
 static inline double BaseCollimatorData_get_jaw_L(const BaseCollimatorData  obj){
  int64_t offset=0;
  offset+=24;
  return *( double*)(( char*) obj+offset);
}
 static inline void BaseCollimatorData_set_jaw_L(BaseCollimatorData  obj, double value){
  int64_t offset=0;
  offset+=24;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* BaseCollimatorData_getp_jaw_L(BaseCollimatorData  obj){
  int64_t offset=0;
  offset+=24;
  return ( double*)(( char*) obj+offset);
}
 static inline double BaseCollimatorData_get_jaw_R(const BaseCollimatorData  obj){
  int64_t offset=0;
  offset+=32;
  return *( double*)(( char*) obj+offset);
}
 static inline void BaseCollimatorData_set_jaw_R(BaseCollimatorData  obj, double value){
  int64_t offset=0;
  offset+=32;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* BaseCollimatorData_getp_jaw_R(BaseCollimatorData  obj){
  int64_t offset=0;
  offset+=32;
  return ( double*)(( char*) obj+offset);
}
 static inline double BaseCollimatorData_get_ref_x(const BaseCollimatorData  obj){
  int64_t offset=0;
  offset+=40;
  return *( double*)(( char*) obj+offset);
}
 static inline void BaseCollimatorData_set_ref_x(BaseCollimatorData  obj, double value){
  int64_t offset=0;
  offset+=40;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* BaseCollimatorData_getp_ref_x(BaseCollimatorData  obj){
  int64_t offset=0;
  offset+=40;
  return ( double*)(( char*) obj+offset);
}
 static inline double BaseCollimatorData_get_ref_y(const BaseCollimatorData  obj){
  int64_t offset=0;
  offset+=48;
  return *( double*)(( char*) obj+offset);
}
 static inline void BaseCollimatorData_set_ref_y(BaseCollimatorData  obj, double value){
  int64_t offset=0;
  offset+=48;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* BaseCollimatorData_getp_ref_y(BaseCollimatorData  obj){
  int64_t offset=0;
  offset+=48;
  return ( double*)(( char*) obj+offset);
}
 static inline double BaseCollimatorData_get_sin_zL(const BaseCollimatorData  obj){
  int64_t offset=0;
  offset+=56;
  return *( double*)(( char*) obj+offset);
}
 static inline void BaseCollimatorData_set_sin_zL(BaseCollimatorData  obj, double value){
  int64_t offset=0;
  offset+=56;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* BaseCollimatorData_getp_sin_zL(BaseCollimatorData  obj){
  int64_t offset=0;
  offset+=56;
  return ( double*)(( char*) obj+offset);
}
 static inline double BaseCollimatorData_get_cos_zL(const BaseCollimatorData  obj){
  int64_t offset=0;
  offset+=64;
  return *( double*)(( char*) obj+offset);
}
 static inline void BaseCollimatorData_set_cos_zL(BaseCollimatorData  obj, double value){
  int64_t offset=0;
  offset+=64;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* BaseCollimatorData_getp_cos_zL(BaseCollimatorData  obj){
  int64_t offset=0;
  offset+=64;
  return ( double*)(( char*) obj+offset);
}
 static inline double BaseCollimatorData_get_sin_zR(const BaseCollimatorData  obj){
  int64_t offset=0;
  offset+=72;
  return *( double*)(( char*) obj+offset);
}
 static inline void BaseCollimatorData_set_sin_zR(BaseCollimatorData  obj, double value){
  int64_t offset=0;
  offset+=72;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* BaseCollimatorData_getp_sin_zR(BaseCollimatorData  obj){
  int64_t offset=0;
  offset+=72;
  return ( double*)(( char*) obj+offset);
}
 static inline double BaseCollimatorData_get_cos_zR(const BaseCollimatorData  obj){
  int64_t offset=0;
  offset+=80;
  return *( double*)(( char*) obj+offset);
}
 static inline void BaseCollimatorData_set_cos_zR(BaseCollimatorData  obj, double value){
  int64_t offset=0;
  offset+=80;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* BaseCollimatorData_getp_cos_zR(BaseCollimatorData  obj){
  int64_t offset=0;
  offset+=80;
  return ( double*)(( char*) obj+offset);
}
 static inline double BaseCollimatorData_get_sin_yL(const BaseCollimatorData  obj){
  int64_t offset=0;
  offset+=88;
  return *( double*)(( char*) obj+offset);
}
 static inline void BaseCollimatorData_set_sin_yL(BaseCollimatorData  obj, double value){
  int64_t offset=0;
  offset+=88;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* BaseCollimatorData_getp_sin_yL(BaseCollimatorData  obj){
  int64_t offset=0;
  offset+=88;
  return ( double*)(( char*) obj+offset);
}
 static inline double BaseCollimatorData_get_cos_yL(const BaseCollimatorData  obj){
  int64_t offset=0;
  offset+=96;
  return *( double*)(( char*) obj+offset);
}
 static inline void BaseCollimatorData_set_cos_yL(BaseCollimatorData  obj, double value){
  int64_t offset=0;
  offset+=96;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* BaseCollimatorData_getp_cos_yL(BaseCollimatorData  obj){
  int64_t offset=0;
  offset+=96;
  return ( double*)(( char*) obj+offset);
}
 static inline double BaseCollimatorData_get_tan_yL(const BaseCollimatorData  obj){
  int64_t offset=0;
  offset+=104;
  return *( double*)(( char*) obj+offset);
}
 static inline void BaseCollimatorData_set_tan_yL(BaseCollimatorData  obj, double value){
  int64_t offset=0;
  offset+=104;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* BaseCollimatorData_getp_tan_yL(BaseCollimatorData  obj){
  int64_t offset=0;
  offset+=104;
  return ( double*)(( char*) obj+offset);
}
 static inline double BaseCollimatorData_get_sin_yR(const BaseCollimatorData  obj){
  int64_t offset=0;
  offset+=112;
  return *( double*)(( char*) obj+offset);
}
 static inline void BaseCollimatorData_set_sin_yR(BaseCollimatorData  obj, double value){
  int64_t offset=0;
  offset+=112;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* BaseCollimatorData_getp_sin_yR(BaseCollimatorData  obj){
  int64_t offset=0;
  offset+=112;
  return ( double*)(( char*) obj+offset);
}
 static inline double BaseCollimatorData_get_cos_yR(const BaseCollimatorData  obj){
  int64_t offset=0;
  offset+=120;
  return *( double*)(( char*) obj+offset);
}
 static inline void BaseCollimatorData_set_cos_yR(BaseCollimatorData  obj, double value){
  int64_t offset=0;
  offset+=120;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* BaseCollimatorData_getp_cos_yR(BaseCollimatorData  obj){
  int64_t offset=0;
  offset+=120;
  return ( double*)(( char*) obj+offset);
}
 static inline double BaseCollimatorData_get_tan_yR(const BaseCollimatorData  obj){
  int64_t offset=0;
  offset+=128;
  return *( double*)(( char*) obj+offset);
}
 static inline void BaseCollimatorData_set_tan_yR(BaseCollimatorData  obj, double value){
  int64_t offset=0;
  offset+=128;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* BaseCollimatorData_getp_tan_yR(BaseCollimatorData  obj){
  int64_t offset=0;
  offset+=128;
  return ( double*)(( char*) obj+offset);
}
 static inline int8_t BaseCollimatorData_get__side(const BaseCollimatorData  obj){
  int64_t offset=0;
  offset+=136;
  return *(( int8_t*) obj+offset);
}
 static inline void BaseCollimatorData_set__side(BaseCollimatorData  obj, int8_t value){
  int64_t offset=0;
  offset+=136;
  *(( int8_t*) obj+offset)=value;
}
 static inline  int8_t* BaseCollimatorData_getp__side(BaseCollimatorData  obj){
  int64_t offset=0;
  offset+=136;
  return ( int8_t*)(( char*) obj+offset);
}
 static inline int8_t BaseCollimatorData_get_active(const BaseCollimatorData  obj){
  int64_t offset=0;
  offset+=144;
  return *(( int8_t*) obj+offset);
}
 static inline void BaseCollimatorData_set_active(BaseCollimatorData  obj, int8_t value){
  int64_t offset=0;
  offset+=144;
  *(( int8_t*) obj+offset)=value;
}
 static inline  int8_t* BaseCollimatorData_getp_active(BaseCollimatorData  obj){
  int64_t offset=0;
  offset+=144;
  return ( int8_t*)(( char*) obj+offset);
}
 static inline RecordIdentifier BaseCollimatorData_getp__internal_record_id(BaseCollimatorData  obj){
  int64_t offset=0;
  offset+=152;
  return (RecordIdentifier)(( char*) obj+offset);
}
 static inline int64_t BaseCollimatorData_get__internal_record_id_buffer_id(const BaseCollimatorData  obj){
  int64_t offset=0;
  offset+=152;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void BaseCollimatorData_set__internal_record_id_buffer_id(BaseCollimatorData  obj, int64_t value){
  int64_t offset=0;
  offset+=152;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* BaseCollimatorData_getp__internal_record_id_buffer_id(BaseCollimatorData  obj){
  int64_t offset=0;
  offset+=152;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline int64_t BaseCollimatorData_get__internal_record_id_offset(const BaseCollimatorData  obj){
  int64_t offset=0;
  offset+=160;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void BaseCollimatorData_set__internal_record_id_offset(BaseCollimatorData  obj, int64_t value){
  int64_t offset=0;
  offset+=160;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* BaseCollimatorData_getp__internal_record_id_offset(BaseCollimatorData  obj){
  int64_t offset=0;
  offset+=160;
  return ( int64_t*)(( char*) obj+offset);
}
#endif
// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_CONSTANTS_H
#define XTRACK_CONSTANTS_H

#if !defined( C_LIGHT )
    #define   C_LIGHT ( 299792458.0 )
#endif /* !defined( C_LIGHT ) */

#if !defined( EPSILON_0 )
    #define   EPSILON_0 (8.854187817620e-12)
#endif /* !defined( EPSILON_0 ) */

#if !defined( PI )
    #define PI (3.1415926535897932384626433832795028841971693993751)
#endif /* !defined( PI ) */

#if !defined( MU_0 )
    #define MU_0 (PI*4.0e-7)
#endif /* !defined( MU_0 ) */

#if !defined( DEG2RAD )
    #define DEG2RAD (0.0174532925199432957692369076848861271344287188854)
#endif /* !defiend( DEG2RAD ) */

#if !defined( RAD2DEG )
    #define RAD2DEG (57.29577951308232087679815481410517033240547246656442)
#endif /* !defiend( RAD2DEG ) */

#if !defined( SQRT_PI )
    #define SQRT_PI (1.7724538509055160272981674833411451827975494561224)
#endif /* !defined( SQRT_PI ) */

#if !defined( QELEM )
    #define QELEM (1.60217662e-19)
#endif /* !defined( QELEM ) */

#if !defined( DBL_MAX )
    #define DBL_MAX (1.7976931348623158e+308)
#endif /* !defined( DBL_MAX ) */

#if !defined( DBL_MIN )
    #define DBL_MIN (2.2250738585072014e-308)
#endif /* !defined( DBL_MIN ) */

#if !defined( DBL_EPSILON )
    #define DBL_EPSILON (2.2204460492503131e-16)
#endif /* !defined( DBL_EPSILON ) */

#endif /* XTRACK_CONSTANTS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_FUNCTIONS_H
#define XTRACK_FUNCTIONS_H

 static inline
void kill_all_particles(LocalParticle* part0, int64_t kill_state) {

    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

        LocalParticle_kill_particle(part, kill_state);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

}


 static inline
int8_t assert_tracking(LocalParticle* part, int64_t kill_state){
    // Whenever we are not tracking, e.g. in a twiss, the particle will be at_turn < 0.
    // We test this to distinguish genuine tracking from twiss.
    if (LocalParticle_get_at_turn(part) < 0){
        LocalParticle_kill_particle(part, kill_state);
        return 0;
    }
    return 1;
}

#endif /* XTRACK_FUNCTIONS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_PARTICLE_STATES_H
#define XTRACK_PARTICLE_STATES_H

#define  XT_LOST_ON_APERTURE       0
#define  XT_LOST_ON_LONG_CUT      -2
#define  XT_LOST_ALL_E_IN_SYNRAD -10
#define  RNG_ERR_SEEDS_NOT_SET   -20
#define  RNG_ERR_INVALID_TRACK   -21
#define  RNG_ERR_RUTH_NOT_SET    -22

#endif /* XTRACK_PARTICLE_STATES_H */


 static inline
CollimatorImpactsData BaseCollimatorData_getp_internal_record(BaseCollimatorData el, LocalParticle* part){
    RecordIdentifier record_id = BaseCollimatorData_getp__internal_record_id(el);
    if (RecordIdentifier_get_buffer_id(record_id) <= 0){
        return NULL;
    }
    else{
        return (CollimatorImpactsData) RecordIdentifier_getp_record(record_id, part);
    }
    }
    
// copyright ############################### #
// This file is part of the Xcoll Package.   #
// Copyright (c) CERN, 2023.                 #
// ######################################### #

#ifndef XCOLL_BASE_H
#define XCOLL_BASE_H

 static inline
void BaseCollimator_track_local_particle(BaseCollimatorData el, LocalParticle* part0) {
    kill_all_particles(part0, XC_ERR_INVALID_TRACK);
}

#endif /* XCOLL_BASE_H */


             
            void BaseCollimator_track_particles(
               BaseCollimatorData el,

                             ParticlesData particles,

                             int64_t flag_increment_at_element,
                  int8_t* io_buffer){
//            const int num_threads = omp_get_max_threads();                                 //only_for_context cpu_openmp
//            const int64_t capacity = ParticlesData_get__capacity(particles);               //only_for_context cpu_openmp
//            const int64_t chunk_size = (capacity + num_threads - 1)/num_threads; // ceil division  //only_for_context cpu_openmp
//            #pragma omp parallel for                                                       //only_for_context cpu_openmp
//            for (int64_t batch_id = 0; batch_id < num_threads; batch_id++) {               //only_for_context cpu_openmp
                LocalParticle lpart;
                lpart.io_buffer = io_buffer;
//                int64_t part_id = batch_id * chunk_size;                                       //only_for_context cpu_openmp
//                int64_t end_id = (batch_id + 1) * chunk_size;                                  //only_for_context cpu_openmp
//                if (end_id > capacity) end_id = capacity;                                      //only_for_context cpu_openmp
    
                int64_t part_id = 0;                    //only_for_context cpu_serial
//                int64_t part_id = blockDim.x * blockIdx.x + threadIdx.x; //only_for_context cuda
//                int64_t part_id = get_global_id(0);                    //only_for_context opencl
                int64_t end_id = 0; // unused outside of openmp  //only_for_context cpu_serial cuda opencl
    
                int64_t part_capacity = ParticlesData_get__capacity(particles);
                if (part_id<part_capacity){
                    Particles_to_LocalParticle(particles, &lpart, part_id, end_id);
                    if (check_is_active(&lpart)>0){
              BaseCollimator_track_local_particle(el, &lpart);

                    }
                    if (check_is_active(&lpart)>0 && flag_increment_at_element){
                            increment_at_element(&lpart, 1);
                    }
                }
//            } //only_for_context cpu_openmp
            
            // On OpenMP we want to additionally by default reorganize all
            // the particles.
//            #ifndef XT_OMP_SKIP_REORGANIZE                             //only_for_context cpu_openmp
//            LocalParticle lpart;                                       //only_for_context cpu_openmp
//            lpart.io_buffer = io_buffer;                               //only_for_context cpu_openmp
//            Particles_to_LocalParticle(particles, &lpart, 0, capacity);//only_for_context cpu_openmp
//            check_is_active(&lpart);                                   //only_for_context cpu_openmp
//            #endif                                                     //only_for_context cpu_openmp
        }

#ifndef XOBJ_TYPEDEF_FirstOrderTaylorMapData
#define XOBJ_TYPEDEF_FirstOrderTaylorMapData
typedef   struct FirstOrderTaylorMapData_s * FirstOrderTaylorMapData;
 static inline FirstOrderTaylorMapData FirstOrderTaylorMapData_getp(FirstOrderTaylorMapData  obj){
  int64_t offset=0;
  return (FirstOrderTaylorMapData)(( char*) obj+offset);
}
 static inline int64_t FirstOrderTaylorMapData_get_radiation_flag(const FirstOrderTaylorMapData  obj){
  int64_t offset=0;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void FirstOrderTaylorMapData_set_radiation_flag(FirstOrderTaylorMapData  obj, int64_t value){
  int64_t offset=0;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* FirstOrderTaylorMapData_getp_radiation_flag(FirstOrderTaylorMapData  obj){
  int64_t offset=0;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline double FirstOrderTaylorMapData_get_length(const FirstOrderTaylorMapData  obj){
  int64_t offset=0;
  offset+=8;
  return *( double*)(( char*) obj+offset);
}
 static inline void FirstOrderTaylorMapData_set_length(FirstOrderTaylorMapData  obj, double value){
  int64_t offset=0;
  offset+=8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* FirstOrderTaylorMapData_getp_length(FirstOrderTaylorMapData  obj){
  int64_t offset=0;
  offset+=8;
  return ( double*)(( char*) obj+offset);
}
 static inline Arr6Float64 FirstOrderTaylorMapData_getp_m0(FirstOrderTaylorMapData  obj){
  int64_t offset=0;
  offset+=16;
  return (Arr6Float64)(( char*) obj+offset);
}
 static inline int64_t FirstOrderTaylorMapData_len_m0(FirstOrderTaylorMapData  obj){
  return 6;
}
 static inline double FirstOrderTaylorMapData_get_m0(const FirstOrderTaylorMapData  obj, int64_t i0){
  int64_t offset=0;
  offset+=16;
  offset+=i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void FirstOrderTaylorMapData_set_m0(FirstOrderTaylorMapData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=16;
  offset+=i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* FirstOrderTaylorMapData_getp1_m0(FirstOrderTaylorMapData  obj, int64_t i0){
  int64_t offset=0;
  offset+=16;
  offset+=i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline Arr6x6Float64 FirstOrderTaylorMapData_getp_m1(FirstOrderTaylorMapData  obj){
  int64_t offset=0;
  offset+=64;
  return (Arr6x6Float64)(( char*) obj+offset);
}
 static inline int64_t FirstOrderTaylorMapData_len_m1(FirstOrderTaylorMapData  obj){
  return 36;
}
 static inline double FirstOrderTaylorMapData_get_m1(const FirstOrderTaylorMapData  obj, int64_t i0, int64_t i1){
  int64_t offset=0;
  offset+=64;
  offset+=i0*48+i1*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void FirstOrderTaylorMapData_set_m1(FirstOrderTaylorMapData  obj, int64_t i0, int64_t i1, double value){
  int64_t offset=0;
  offset+=64;
  offset+=i0*48+i1*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* FirstOrderTaylorMapData_getp2_m1(FirstOrderTaylorMapData  obj, int64_t i0, int64_t i1){
  int64_t offset=0;
  offset+=64;
  offset+=i0*48+i1*8;
  return ( double*)(( char*) obj+offset);
}
 static inline RecordIdentifier FirstOrderTaylorMapData_getp__internal_record_id(FirstOrderTaylorMapData  obj){
  int64_t offset=0;
  offset+=352;
  return (RecordIdentifier)(( char*) obj+offset);
}
 static inline int64_t FirstOrderTaylorMapData_get__internal_record_id_buffer_id(const FirstOrderTaylorMapData  obj){
  int64_t offset=0;
  offset+=352;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void FirstOrderTaylorMapData_set__internal_record_id_buffer_id(FirstOrderTaylorMapData  obj, int64_t value){
  int64_t offset=0;
  offset+=352;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* FirstOrderTaylorMapData_getp__internal_record_id_buffer_id(FirstOrderTaylorMapData  obj){
  int64_t offset=0;
  offset+=352;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline int64_t FirstOrderTaylorMapData_get__internal_record_id_offset(const FirstOrderTaylorMapData  obj){
  int64_t offset=0;
  offset+=360;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void FirstOrderTaylorMapData_set__internal_record_id_offset(FirstOrderTaylorMapData  obj, int64_t value){
  int64_t offset=0;
  offset+=360;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* FirstOrderTaylorMapData_getp__internal_record_id_offset(FirstOrderTaylorMapData  obj){
  int64_t offset=0;
  offset+=360;
  return ( int64_t*)(( char*) obj+offset);
}
#endif
// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_CONSTANTS_H
#define XTRACK_CONSTANTS_H

#if !defined( C_LIGHT )
    #define   C_LIGHT ( 299792458.0 )
#endif /* !defined( C_LIGHT ) */

#if !defined( EPSILON_0 )
    #define   EPSILON_0 (8.854187817620e-12)
#endif /* !defined( EPSILON_0 ) */

#if !defined( PI )
    #define PI (3.1415926535897932384626433832795028841971693993751)
#endif /* !defined( PI ) */

#if !defined( MU_0 )
    #define MU_0 (PI*4.0e-7)
#endif /* !defined( MU_0 ) */

#if !defined( DEG2RAD )
    #define DEG2RAD (0.0174532925199432957692369076848861271344287188854)
#endif /* !defiend( DEG2RAD ) */

#if !defined( RAD2DEG )
    #define RAD2DEG (57.29577951308232087679815481410517033240547246656442)
#endif /* !defiend( RAD2DEG ) */

#if !defined( SQRT_PI )
    #define SQRT_PI (1.7724538509055160272981674833411451827975494561224)
#endif /* !defined( SQRT_PI ) */

#if !defined( QELEM )
    #define QELEM (1.60217662e-19)
#endif /* !defined( QELEM ) */

#if !defined( DBL_MAX )
    #define DBL_MAX (1.7976931348623158e+308)
#endif /* !defined( DBL_MAX ) */

#if !defined( DBL_MIN )
    #define DBL_MIN (2.2250738585072014e-308)
#endif /* !defined( DBL_MIN ) */

#if !defined( DBL_EPSILON )
    #define DBL_EPSILON (2.2204460492503131e-16)
#endif /* !defined( DBL_EPSILON ) */

#endif /* XTRACK_CONSTANTS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_FUNCTIONS_H
#define XTRACK_FUNCTIONS_H

 static inline
void kill_all_particles(LocalParticle* part0, int64_t kill_state) {

    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

        LocalParticle_kill_particle(part, kill_state);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

}


 static inline
int8_t assert_tracking(LocalParticle* part, int64_t kill_state){
    // Whenever we are not tracking, e.g. in a twiss, the particle will be at_turn < 0.
    // We test this to distinguish genuine tracking from twiss.
    if (LocalParticle_get_at_turn(part) < 0){
        LocalParticle_kill_particle(part, kill_state);
        return 0;
    }
    return 1;
}

#endif /* XTRACK_FUNCTIONS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_PARTICLE_STATES_H
#define XTRACK_PARTICLE_STATES_H

#define  XT_LOST_ON_APERTURE       0
#define  XT_LOST_ON_LONG_CUT      -2
#define  XT_LOST_ALL_E_IN_SYNRAD -10
#define  RNG_ERR_SEEDS_NOT_SET   -20
#define  RNG_ERR_INVALID_TRACK   -21
#define  RNG_ERR_RUTH_NOT_SET    -22

#endif /* XTRACK_PARTICLE_STATES_H */


 static inline
SynchrotronRadiationRecordData FirstOrderTaylorMapData_getp_internal_record(FirstOrderTaylorMapData el, LocalParticle* part){
    RecordIdentifier record_id = FirstOrderTaylorMapData_getp__internal_record_id(el);
    if (RecordIdentifier_get_buffer_id(record_id) <= 0){
        return NULL;
    }
    else{
        return (SynchrotronRadiationRecordData) RecordIdentifier_getp_record(record_id, part);
    }
    }
    
// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_CONSTANTS_H
#define XTRACK_CONSTANTS_H

#if !defined( C_LIGHT )
    #define   C_LIGHT ( 299792458.0 )
#endif /* !defined( C_LIGHT ) */

#if !defined( EPSILON_0 )
    #define   EPSILON_0 (8.854187817620e-12)
#endif /* !defined( EPSILON_0 ) */

#if !defined( PI )
    #define PI (3.1415926535897932384626433832795028841971693993751)
#endif /* !defined( PI ) */

#if !defined( MU_0 )
    #define MU_0 (PI*4.0e-7)
#endif /* !defined( MU_0 ) */

#if !defined( DEG2RAD )
    #define DEG2RAD (0.0174532925199432957692369076848861271344287188854)
#endif /* !defiend( DEG2RAD ) */

#if !defined( RAD2DEG )
    #define RAD2DEG (57.29577951308232087679815481410517033240547246656442)
#endif /* !defiend( RAD2DEG ) */

#if !defined( SQRT_PI )
    #define SQRT_PI (1.7724538509055160272981674833411451827975494561224)
#endif /* !defined( SQRT_PI ) */

#if !defined( QELEM )
    #define QELEM (1.60217662e-19)
#endif /* !defined( QELEM ) */

#if !defined( DBL_MAX )
    #define DBL_MAX (1.7976931348623158e+308)
#endif /* !defined( DBL_MAX ) */

#if !defined( DBL_MIN )
    #define DBL_MIN (2.2250738585072014e-308)
#endif /* !defined( DBL_MIN ) */

#if !defined( DBL_EPSILON )
    #define DBL_EPSILON (2.2204460492503131e-16)
#endif /* !defined( DBL_EPSILON ) */

#endif /* XTRACK_CONSTANTS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_SYNRAD_SPECTRUM_H
#define XTRACK_SYNRAD_SPECTRUM_H

#define SQRT3 1.732050807568877
#define ALPHA_EM 0.0072973525693

 static inline
void synrad_average_kick(LocalParticle* part, double curv, double lpath,
                         double* dp_record, double* dpx_record, double* dpy_record
                        ){
    double const gamma0  = LocalParticle_get_gamma0(part);
    double const beta0  = LocalParticle_get_beta0(part);
    double const mass0 = LocalParticle_get_mass0(part);
    double const q0 = LocalParticle_get_q0(part);

    double const delta  = LocalParticle_get_delta(part);

    double const r = QELEM/(6*PI*EPSILON_0)
                        * q0*q0 / mass0
                        * (beta0*gamma0)*(beta0*gamma0)*(beta0*gamma0)
	                * curv*curv
                        * lpath * (1 + delta);

    double const beta = beta0 * LocalParticle_get_rvv(part);
    double f_t = sqrt(1 + r*(r-2)/(beta*beta));

    #ifdef XTRACK_SYNRAD_SCALE_SAME_AS_FIRST
    if (part -> ipart == 0){
      *dp_record = f_t;
    }
    else{
      f_t = *dp_record;
    }
    #endif

    #ifdef XTRACK_SYNRAD_KICK_SAME_AS_FIRST
    if (part -> ipart == 0){
      *dp_record = LocalParticle_get_delta(part);
      *dpx_record = LocalParticle_get_px(part);
      *dpy_record = LocalParticle_get_py(part);
    }
    else{
      f_t = 1.0;
    }
    #endif

    LocalParticle_update_delta(part, (delta+1) * f_t - 1);
    LocalParticle_scale_px(part, f_t);
    LocalParticle_scale_py(part, f_t);

    #ifdef XTRACK_SYNRAD_KICK_SAME_AS_FIRST
    if (part -> ipart == 0){
      *dp_record = LocalParticle_get_delta(part) - *dp_record;
      *dpx_record = LocalParticle_get_px(part) - *dpx_record;
      *dpy_record = LocalParticle_get_py(part) - *dpy_record;
    }
    else{
      LocalParticle_update_delta(part, LocalParticle_get_delta(part) + *dp_record);
      LocalParticle_add_to_px(part, *dpx_record);
      LocalParticle_add_to_py(part, *dpy_record);
    }
    #endif
}

 static inline
double SynRad(double x)
{
  // x :    energy normalized to the critical energy
  // returns function value _SynRadC   photon spectrum dn/dx
  // (integral of modified 1/3 order Bessel function)
  // principal: Chebyshev series see H.H.Umstaetter CERN/PS/SM/81-13 10-3-1981
  // see also my LEP Note 632 of 12/1990
  // converted to C++, H.Burkhardt 21-4-1996    */
  double synrad = 0.;
  if(x>0. && x<800.) {	// otherwise result synrad remains 0
    if(x<6.) {
      double a,b,z;
      z=x*x/16.-2.;
      b=          .00000000000000000012;
      a=z*b  +    .00000000000000000460;
      b=z*a-b+    .00000000000000031738;
      a=z*b-a+    .00000000000002004426;
      b=z*a-b+    .00000000000111455474;
      a=z*b-a+    .00000000005407460944;
      b=z*a-b+    .00000000226722011790;
      a=z*b-a+    .00000008125130371644;
      b=z*a-b+    .00000245751373955212;
      a=z*b-a+    .00006181256113829740;
      b=z*a-b+    .00127066381953661690;
      a=z*b-a+    .02091216799114667278;
      b=z*a-b+    .26880346058164526514;
      a=z*b-a+   2.61902183794862213818;
      b=z*a-b+  18.65250896865416256398;
      a=z*b-a+  92.95232665922707542088;
      b=z*a-b+ 308.15919413131586030542;
      a=z*b-a+ 644.86979658236221700714;
      double p;
      p=.5*z*a-b+  414.56543648832546975110;
      a=          .00000000000000000004;
      b=z*a+      .00000000000000000289;
      a=z*b-a+    .00000000000000019786;
      b=z*a-b+    .00000000000001196168;
      a=z*b-a+    .00000000000063427729;
      b=z*a-b+    .00000000002923635681;
      a=z*b-a+    .00000000115951672806;
      b=z*a-b+    .00000003910314748244;
      a=z*b-a+    .00000110599584794379;
      b=z*a-b+    .00002581451439721298;
      a=z*b-a+    .00048768692916240683;
      b=z*a-b+    .00728456195503504923;
      a=z*b-a+    .08357935463720537773;
      b=z*a-b+    .71031361199218887514;
      a=z*b-a+   4.26780261265492264837;
      b=z*a-b+  17.05540785795221885751;
      a=z*b-a+  41.83903486779678800040;
      double q;
      q=.5*z*a-b+28.41787374362784178164;
      double y;
      y=pow(x,2./3.);
      synrad=(p/y-q*y-1.)*1.81379936423421784215530788143;

    } else {// 6 < x < 174

      double a,b,z;
      z=20./x-2.;
      a=      .00000000000000000001;
      b=z*a  -.00000000000000000002;
      a=z*b-a+.00000000000000000006;
      b=z*a-b-.00000000000000000020;
      a=z*b-a+.00000000000000000066;
      b=z*a-b-.00000000000000000216;
      a=z*b-a+.00000000000000000721;
      b=z*a-b-.00000000000000002443;
      a=z*b-a+.00000000000000008441;
      b=z*a-b-.00000000000000029752;
      a=z*b-a+.00000000000000107116;
      b=z*a-b-.00000000000000394564;
      a=z*b-a+.00000000000001489474;
      b=z*a-b-.00000000000005773537;
      a=z*b-a+.00000000000023030657;
      b=z*a-b-.00000000000094784973;
      a=z*b-a+.00000000000403683207;
      b=z*a-b-.00000000001785432348;
      a=z*b-a+.00000000008235329314;
      b=z*a-b-.00000000039817923621;
      a=z*b-a+.00000000203088939238;
      b=z*a-b-.00000001101482369622;
      a=z*b-a+.00000006418902302372;
      b=z*a-b-.00000040756144386809;
      a=z*b-a+.00000287536465397527;
      b=z*a-b-.00002321251614543524;
      a=z*b-a+.00022505317277986004;
      b=z*a-b-.00287636803664026799;
      a=z*b-a+.06239591359332750793;
      double p;
      p=.5*z*a-b    +1.06552390798340693166;
      synrad=p*sqrt(0.5*PI/x)/exp(x);
    }
  }
  return synrad;
}

 static inline
double synrad_gen_photon_energy_normalized(LocalParticle *part)
{
  // initialize constants used in the approximate expressions
  // for SYNRAD   (integral over the modified Bessel function K5/3)
  //  xmin = 0.;
  double const xlow = 1.;
  double const a1 = 2.149528241534391; // Synrad(1.e-38)/pow(1.e-38,-2./3.);
  double const a2 = 1.770750801624037; // Synrad(xlow)/exp(-xlow);
  double const c1 = 0.; //
  double const ratio = 0.908250405131381;
  double appr, exact, result;
  do {
    if (RandomUniform_generate(part) < ratio) { // use low energy approximation
      result=c1+(1.-c1)*RandomUniform_generate(part);
      double tmp = result*result;
      result*=tmp;  	// take to 3rd power;
      exact=SynRad(result);
      appr=a1/tmp;
    } else {				// use high energy approximation
      result=xlow-log(RandomUniform_generate(part));
      exact=SynRad(result);
      appr=a2*exp(-result);
    }
  } while (exact < appr*RandomUniform_generate(part));	// reject in proportion of approx
  return result; // result now exact spectrum with unity weight
}

 static inline
double synrad_average_number_of_photons(
                          double beta0_gamma0, double curv, double lpath){
    double const kick = curv * lpath;
    return 2.5/SQRT3*ALPHA_EM*beta0_gamma0*fabs(kick);
}

 static inline
int64_t synrad_emit_photons(LocalParticle *part, double curv /* 1/m */,
                            double lpath /* m */,
                            RecordIndex record_index,
                            SynchrotronRadiationRecordData record
                            ){

    if (fabs(curv) < 1e-15)
        return 0;

    int64_t nphot = 0;

    // TODO Introduce effect of chi and mass_ratio!!!
    double const m0 = LocalParticle_get_mass0(part); // eV
    double const gamma0  = LocalParticle_get_gamma0(part);
    double const beta0  = LocalParticle_get_beta0(part);

    double const initial_energy = LocalParticle_get_energy0(part)
	                          + LocalParticle_get_ptau(part)*LocalParticle_get_p0c(part); // eV
    double energy = initial_energy;
    double gamma = energy / m0; //
    //double beta_gamma = sqrt(gamma*gamma-1); //
    double n = RandomExponential_generate(part); // path_length / mean_free_path;
    while (n < synrad_average_number_of_photons(beta0 * gamma0, curv, lpath)) {
        nphot++;
        double const c1 = 1.5 * 1.973269804593025e-07; // hbar * c = 1.973269804593025e-07 eV * m
        double const energy_critical = c1 * (gamma*gamma*gamma0) * curv; // eV
        double const energy_loss = synrad_gen_photon_energy_normalized(part) * energy_critical; // eV
        if (energy_loss >= energy) {
            energy = 0.0; // eV
            break;
        }
        energy -= energy_loss; // eV
        gamma = energy / m0; //
        // beta_gamma = sqrt(gamma*gamma-1); // that's how beta gamma is
        n += RandomExponential_generate(part);
        if (record){
          int64_t i_slot = RecordIndex_get_slot(record_index);
          // The returned slot id is negative if record is NULL or if record is full

          if (i_slot>=0){
              SynchrotronRadiationRecordData_set_photon_energy(record, i_slot,
                                                               energy_loss);
              SynchrotronRadiationRecordData_set_at_element(record, i_slot,
                                          LocalParticle_get_at_element(part));
              SynchrotronRadiationRecordData_set_at_turn(record, i_slot,
                                          LocalParticle_get_at_turn(part));
              SynchrotronRadiationRecordData_set_particle_id(record, i_slot,
                                          LocalParticle_get_particle_id(part));
              SynchrotronRadiationRecordData_set_particle_delta(record, i_slot,
                                          LocalParticle_get_delta(part));
          }
        }
    }

    if (energy == 0.0)
      LocalParticle_set_state(part, XT_LOST_ALL_E_IN_SYNRAD); // used to flag this kind of loss
    else{
      LocalParticle_add_to_energy(part, energy-initial_energy, 0);
    }

    return nphot;
}

#endif /* XTRACK_SYNRAD_SPECTRUM_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_FIRSTORDERTAYLORMAP_H
#define XTRACK_FIRSTORDERTAYLORMAP_H

 static inline
void FirstOrderTaylorMap_track_local_particle(FirstOrderTaylorMapData el, LocalParticle* part0){

    int64_t const radiation_flag = FirstOrderTaylorMapData_get_radiation_flag(el);
    double const length = FirstOrderTaylorMapData_get_length(el); // m

    double dpx_record, dpy_record, dp_record;

    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp


        double x0 = LocalParticle_get_x(part);
        double px0 = LocalParticle_get_px(part);
        double y0 = LocalParticle_get_y(part);
        double py0 = LocalParticle_get_py(part);
        double beta0 = LocalParticle_get_beta0(part);
        double tau0 = LocalParticle_get_zeta(part)/beta0;
        double ptau0 = LocalParticle_get_ptau(part);

        LocalParticle_set_x(part,FirstOrderTaylorMapData_get_m0(el,0) +
                            FirstOrderTaylorMapData_get_m1(el,0,0)*x0 +
                            FirstOrderTaylorMapData_get_m1(el,0,1)*px0 +
                            FirstOrderTaylorMapData_get_m1(el,0,2)*y0 +
                            FirstOrderTaylorMapData_get_m1(el,0,3)*py0 +
                            FirstOrderTaylorMapData_get_m1(el,0,4)*tau0 +
                            FirstOrderTaylorMapData_get_m1(el,0,5)*ptau0);
        LocalParticle_set_px(part,FirstOrderTaylorMapData_get_m0(el,1) +
                            FirstOrderTaylorMapData_get_m1(el,1,0)*x0 +
                            FirstOrderTaylorMapData_get_m1(el,1,1)*px0 +
                            FirstOrderTaylorMapData_get_m1(el,1,2)*y0 +
                            FirstOrderTaylorMapData_get_m1(el,1,3)*py0 +
                            FirstOrderTaylorMapData_get_m1(el,1,4)*tau0 +
                            FirstOrderTaylorMapData_get_m1(el,1,5)*ptau0);
        LocalParticle_set_y(part,FirstOrderTaylorMapData_get_m0(el,2) +
                            FirstOrderTaylorMapData_get_m1(el,2,0)*x0 +
                            FirstOrderTaylorMapData_get_m1(el,2,1)*px0 +
                            FirstOrderTaylorMapData_get_m1(el,2,2)*y0 +
                            FirstOrderTaylorMapData_get_m1(el,2,3)*py0 +
                            FirstOrderTaylorMapData_get_m1(el,2,4)*tau0 +
                            FirstOrderTaylorMapData_get_m1(el,2,5)*ptau0);
        LocalParticle_set_py(part,FirstOrderTaylorMapData_get_m0(el,3) +
                            FirstOrderTaylorMapData_get_m1(el,3,0)*x0 +
                            FirstOrderTaylorMapData_get_m1(el,3,1)*px0 +
                            FirstOrderTaylorMapData_get_m1(el,3,2)*y0 +
                            FirstOrderTaylorMapData_get_m1(el,3,3)*py0 +
                            FirstOrderTaylorMapData_get_m1(el,3,4)*tau0 +
                            FirstOrderTaylorMapData_get_m1(el,3,5)*ptau0);
        double tau = FirstOrderTaylorMapData_get_m0(el,4) +
                            FirstOrderTaylorMapData_get_m1(el,4,0)*x0 +
                            FirstOrderTaylorMapData_get_m1(el,4,1)*px0 +
                            FirstOrderTaylorMapData_get_m1(el,4,2)*y0 +
                            FirstOrderTaylorMapData_get_m1(el,4,3)*py0 +
                            FirstOrderTaylorMapData_get_m1(el,4,4)*tau0 +
                            FirstOrderTaylorMapData_get_m1(el,4,5)*ptau0;
        double ptau = FirstOrderTaylorMapData_get_m0(el,5) +
                            FirstOrderTaylorMapData_get_m1(el,5,0)*x0 +
                            FirstOrderTaylorMapData_get_m1(el,5,1)*px0 +
                            FirstOrderTaylorMapData_get_m1(el,5,2)*y0 +
                            FirstOrderTaylorMapData_get_m1(el,5,3)*py0 +
                            FirstOrderTaylorMapData_get_m1(el,5,4)*tau0 +
                            FirstOrderTaylorMapData_get_m1(el,5,5)*ptau0;

        LocalParticle_update_ptau(part, ptau);
        LocalParticle_set_zeta(part,tau*beta0);

        // Radiation
        if (radiation_flag > 0 && length > 0){
            double dpx = LocalParticle_get_px(part)-px0;
            double dpy = LocalParticle_get_py(part)-py0;
            double curv = sqrt(dpx*dpx+dpy*dpy);
            if (radiation_flag == 1){
                synrad_average_kick(part, curv, length,
                    &dp_record, &dpx_record, &dpy_record);
            }
            else if (radiation_flag == 2){
                synrad_emit_photons(part, curv, length, NULL, NULL);
            }
        }


//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

}

#endif


             
            void FirstOrderTaylorMap_track_particles(
               FirstOrderTaylorMapData el,

                             ParticlesData particles,

                             int64_t flag_increment_at_element,
                  int8_t* io_buffer){
//            const int num_threads = omp_get_max_threads();                                 //only_for_context cpu_openmp
//            const int64_t capacity = ParticlesData_get__capacity(particles);               //only_for_context cpu_openmp
//            const int64_t chunk_size = (capacity + num_threads - 1)/num_threads; // ceil division  //only_for_context cpu_openmp
//            #pragma omp parallel for                                                       //only_for_context cpu_openmp
//            for (int64_t batch_id = 0; batch_id < num_threads; batch_id++) {               //only_for_context cpu_openmp
                LocalParticle lpart;
                lpart.io_buffer = io_buffer;
//                int64_t part_id = batch_id * chunk_size;                                       //only_for_context cpu_openmp
//                int64_t end_id = (batch_id + 1) * chunk_size;                                  //only_for_context cpu_openmp
//                if (end_id > capacity) end_id = capacity;                                      //only_for_context cpu_openmp
    
                int64_t part_id = 0;                    //only_for_context cpu_serial
//                int64_t part_id = blockDim.x * blockIdx.x + threadIdx.x; //only_for_context cuda
//                int64_t part_id = get_global_id(0);                    //only_for_context opencl
                int64_t end_id = 0; // unused outside of openmp  //only_for_context cpu_serial cuda opencl
    
                int64_t part_capacity = ParticlesData_get__capacity(particles);
                if (part_id<part_capacity){
                    Particles_to_LocalParticle(particles, &lpart, part_id, end_id);
                    if (check_is_active(&lpart)>0){
              FirstOrderTaylorMap_track_local_particle(el, &lpart);

                    }
                    if (check_is_active(&lpart)>0 && flag_increment_at_element){
                            increment_at_element(&lpart, 1);
                    }
                }
//            } //only_for_context cpu_openmp
            
            // On OpenMP we want to additionally by default reorganize all
            // the particles.
//            #ifndef XT_OMP_SKIP_REORGANIZE                             //only_for_context cpu_openmp
//            LocalParticle lpart;                                       //only_for_context cpu_openmp
//            lpart.io_buffer = io_buffer;                               //only_for_context cpu_openmp
//            Particles_to_LocalParticle(particles, &lpart, 0, capacity);//only_for_context cpu_openmp
//            check_is_active(&lpart);                                   //only_for_context cpu_openmp
//            #endif                                                     //only_for_context cpu_openmp
        }

#ifndef XOBJ_TYPEDEF_MultipoleData
#define XOBJ_TYPEDEF_MultipoleData
typedef   struct MultipoleData_s * MultipoleData;
 static inline MultipoleData MultipoleData_getp(MultipoleData  obj){
  int64_t offset=0;
  return (MultipoleData)(( char*) obj+offset);
}
 static inline int64_t MultipoleData_get_order(const MultipoleData  obj){
  int64_t offset=0;
  offset+=8;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void MultipoleData_set_order(MultipoleData  obj, int64_t value){
  int64_t offset=0;
  offset+=8;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* MultipoleData_getp_order(MultipoleData  obj){
  int64_t offset=0;
  offset+=8;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline double MultipoleData_get_inv_factorial_order(const MultipoleData  obj){
  int64_t offset=0;
  offset+=16;
  return *( double*)(( char*) obj+offset);
}
 static inline void MultipoleData_set_inv_factorial_order(MultipoleData  obj, double value){
  int64_t offset=0;
  offset+=16;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* MultipoleData_getp_inv_factorial_order(MultipoleData  obj){
  int64_t offset=0;
  offset+=16;
  return ( double*)(( char*) obj+offset);
}
 static inline double MultipoleData_get_length(const MultipoleData  obj){
  int64_t offset=0;
  offset+=24;
  return *( double*)(( char*) obj+offset);
}
 static inline void MultipoleData_set_length(MultipoleData  obj, double value){
  int64_t offset=0;
  offset+=24;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* MultipoleData_getp_length(MultipoleData  obj){
  int64_t offset=0;
  offset+=24;
  return ( double*)(( char*) obj+offset);
}
 static inline double MultipoleData_get_hxl(const MultipoleData  obj){
  int64_t offset=0;
  offset+=32;
  return *( double*)(( char*) obj+offset);
}
 static inline void MultipoleData_set_hxl(MultipoleData  obj, double value){
  int64_t offset=0;
  offset+=32;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* MultipoleData_getp_hxl(MultipoleData  obj){
  int64_t offset=0;
  offset+=32;
  return ( double*)(( char*) obj+offset);
}
 static inline double MultipoleData_get_hyl(const MultipoleData  obj){
  int64_t offset=0;
  offset+=40;
  return *( double*)(( char*) obj+offset);
}
 static inline void MultipoleData_set_hyl(MultipoleData  obj, double value){
  int64_t offset=0;
  offset+=40;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* MultipoleData_getp_hyl(MultipoleData  obj){
  int64_t offset=0;
  offset+=40;
  return ( double*)(( char*) obj+offset);
}
 static inline int64_t MultipoleData_get_radiation_flag(const MultipoleData  obj){
  int64_t offset=0;
  offset+=48;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void MultipoleData_set_radiation_flag(MultipoleData  obj, int64_t value){
  int64_t offset=0;
  offset+=48;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* MultipoleData_getp_radiation_flag(MultipoleData  obj){
  int64_t offset=0;
  offset+=48;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline double MultipoleData_get_delta_taper(const MultipoleData  obj){
  int64_t offset=0;
  offset+=56;
  return *( double*)(( char*) obj+offset);
}
 static inline void MultipoleData_set_delta_taper(MultipoleData  obj, double value){
  int64_t offset=0;
  offset+=56;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* MultipoleData_getp_delta_taper(MultipoleData  obj){
  int64_t offset=0;
  offset+=56;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNFloat64 MultipoleData_getp_knl(MultipoleData  obj){
  int64_t offset=0;
  offset+=88;
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t MultipoleData_len_knl(MultipoleData  obj){
  int64_t offset=0;
  offset+=88;
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double MultipoleData_get_knl(const MultipoleData  obj, int64_t i0){
  int64_t offset=0;
  offset+=88;
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void MultipoleData_set_knl(MultipoleData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=88;
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* MultipoleData_getp1_knl(MultipoleData  obj, int64_t i0){
  int64_t offset=0;
  offset+=88;
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNFloat64 MultipoleData_getp_ksl(MultipoleData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+80);
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t MultipoleData_len_ksl(MultipoleData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+80);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double MultipoleData_get_ksl(const MultipoleData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+80);
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void MultipoleData_set_ksl(MultipoleData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+80);
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* MultipoleData_getp1_ksl(MultipoleData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+80);
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline RecordIdentifier MultipoleData_getp__internal_record_id(MultipoleData  obj){
  int64_t offset=0;
  offset+=64;
  return (RecordIdentifier)(( char*) obj+offset);
}
 static inline int64_t MultipoleData_get__internal_record_id_buffer_id(const MultipoleData  obj){
  int64_t offset=0;
  offset+=64;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void MultipoleData_set__internal_record_id_buffer_id(MultipoleData  obj, int64_t value){
  int64_t offset=0;
  offset+=64;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* MultipoleData_getp__internal_record_id_buffer_id(MultipoleData  obj){
  int64_t offset=0;
  offset+=64;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline int64_t MultipoleData_get__internal_record_id_offset(const MultipoleData  obj){
  int64_t offset=0;
  offset+=72;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void MultipoleData_set__internal_record_id_offset(MultipoleData  obj, int64_t value){
  int64_t offset=0;
  offset+=72;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* MultipoleData_getp__internal_record_id_offset(MultipoleData  obj){
  int64_t offset=0;
  offset+=72;
  return ( int64_t*)(( char*) obj+offset);
}
#endif
// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_CONSTANTS_H
#define XTRACK_CONSTANTS_H

#if !defined( C_LIGHT )
    #define   C_LIGHT ( 299792458.0 )
#endif /* !defined( C_LIGHT ) */

#if !defined( EPSILON_0 )
    #define   EPSILON_0 (8.854187817620e-12)
#endif /* !defined( EPSILON_0 ) */

#if !defined( PI )
    #define PI (3.1415926535897932384626433832795028841971693993751)
#endif /* !defined( PI ) */

#if !defined( MU_0 )
    #define MU_0 (PI*4.0e-7)
#endif /* !defined( MU_0 ) */

#if !defined( DEG2RAD )
    #define DEG2RAD (0.0174532925199432957692369076848861271344287188854)
#endif /* !defiend( DEG2RAD ) */

#if !defined( RAD2DEG )
    #define RAD2DEG (57.29577951308232087679815481410517033240547246656442)
#endif /* !defiend( RAD2DEG ) */

#if !defined( SQRT_PI )
    #define SQRT_PI (1.7724538509055160272981674833411451827975494561224)
#endif /* !defined( SQRT_PI ) */

#if !defined( QELEM )
    #define QELEM (1.60217662e-19)
#endif /* !defined( QELEM ) */

#if !defined( DBL_MAX )
    #define DBL_MAX (1.7976931348623158e+308)
#endif /* !defined( DBL_MAX ) */

#if !defined( DBL_MIN )
    #define DBL_MIN (2.2250738585072014e-308)
#endif /* !defined( DBL_MIN ) */

#if !defined( DBL_EPSILON )
    #define DBL_EPSILON (2.2204460492503131e-16)
#endif /* !defined( DBL_EPSILON ) */

#endif /* XTRACK_CONSTANTS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_FUNCTIONS_H
#define XTRACK_FUNCTIONS_H

 static inline
void kill_all_particles(LocalParticle* part0, int64_t kill_state) {

    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

        LocalParticle_kill_particle(part, kill_state);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

}


 static inline
int8_t assert_tracking(LocalParticle* part, int64_t kill_state){
    // Whenever we are not tracking, e.g. in a twiss, the particle will be at_turn < 0.
    // We test this to distinguish genuine tracking from twiss.
    if (LocalParticle_get_at_turn(part) < 0){
        LocalParticle_kill_particle(part, kill_state);
        return 0;
    }
    return 1;
}

#endif /* XTRACK_FUNCTIONS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_PARTICLE_STATES_H
#define XTRACK_PARTICLE_STATES_H

#define  XT_LOST_ON_APERTURE       0
#define  XT_LOST_ON_LONG_CUT      -2
#define  XT_LOST_ALL_E_IN_SYNRAD -10
#define  RNG_ERR_SEEDS_NOT_SET   -20
#define  RNG_ERR_INVALID_TRACK   -21
#define  RNG_ERR_RUTH_NOT_SET    -22

#endif /* XTRACK_PARTICLE_STATES_H */


 static inline
SynchrotronRadiationRecordData MultipoleData_getp_internal_record(MultipoleData el, LocalParticle* part){
    RecordIdentifier record_id = MultipoleData_getp__internal_record_id(el);
    if (RecordIdentifier_get_buffer_id(record_id) <= 0){
        return NULL;
    }
    else{
        return (SynchrotronRadiationRecordData) RecordIdentifier_getp_record(record_id, part);
    }
    }
    
// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_CONSTANTS_H
#define XTRACK_CONSTANTS_H

#if !defined( C_LIGHT )
    #define   C_LIGHT ( 299792458.0 )
#endif /* !defined( C_LIGHT ) */

#if !defined( EPSILON_0 )
    #define   EPSILON_0 (8.854187817620e-12)
#endif /* !defined( EPSILON_0 ) */

#if !defined( PI )
    #define PI (3.1415926535897932384626433832795028841971693993751)
#endif /* !defined( PI ) */

#if !defined( MU_0 )
    #define MU_0 (PI*4.0e-7)
#endif /* !defined( MU_0 ) */

#if !defined( DEG2RAD )
    #define DEG2RAD (0.0174532925199432957692369076848861271344287188854)
#endif /* !defiend( DEG2RAD ) */

#if !defined( RAD2DEG )
    #define RAD2DEG (57.29577951308232087679815481410517033240547246656442)
#endif /* !defiend( RAD2DEG ) */

#if !defined( SQRT_PI )
    #define SQRT_PI (1.7724538509055160272981674833411451827975494561224)
#endif /* !defined( SQRT_PI ) */

#if !defined( QELEM )
    #define QELEM (1.60217662e-19)
#endif /* !defined( QELEM ) */

#if !defined( DBL_MAX )
    #define DBL_MAX (1.7976931348623158e+308)
#endif /* !defined( DBL_MAX ) */

#if !defined( DBL_MIN )
    #define DBL_MIN (2.2250738585072014e-308)
#endif /* !defined( DBL_MIN ) */

#if !defined( DBL_EPSILON )
    #define DBL_EPSILON (2.2204460492503131e-16)
#endif /* !defined( DBL_EPSILON ) */

#endif /* XTRACK_CONSTANTS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_SYNRAD_SPECTRUM_H
#define XTRACK_SYNRAD_SPECTRUM_H

#define SQRT3 1.732050807568877
#define ALPHA_EM 0.0072973525693

 static inline
void synrad_average_kick(LocalParticle* part, double curv, double lpath,
                         double* dp_record, double* dpx_record, double* dpy_record
                        ){
    double const gamma0  = LocalParticle_get_gamma0(part);
    double const beta0  = LocalParticle_get_beta0(part);
    double const mass0 = LocalParticle_get_mass0(part);
    double const q0 = LocalParticle_get_q0(part);

    double const delta  = LocalParticle_get_delta(part);

    double const r = QELEM/(6*PI*EPSILON_0)
                        * q0*q0 / mass0
                        * (beta0*gamma0)*(beta0*gamma0)*(beta0*gamma0)
	                * curv*curv
                        * lpath * (1 + delta);

    double const beta = beta0 * LocalParticle_get_rvv(part);
    double f_t = sqrt(1 + r*(r-2)/(beta*beta));

    #ifdef XTRACK_SYNRAD_SCALE_SAME_AS_FIRST
    if (part -> ipart == 0){
      *dp_record = f_t;
    }
    else{
      f_t = *dp_record;
    }
    #endif

    #ifdef XTRACK_SYNRAD_KICK_SAME_AS_FIRST
    if (part -> ipart == 0){
      *dp_record = LocalParticle_get_delta(part);
      *dpx_record = LocalParticle_get_px(part);
      *dpy_record = LocalParticle_get_py(part);
    }
    else{
      f_t = 1.0;
    }
    #endif

    LocalParticle_update_delta(part, (delta+1) * f_t - 1);
    LocalParticle_scale_px(part, f_t);
    LocalParticle_scale_py(part, f_t);

    #ifdef XTRACK_SYNRAD_KICK_SAME_AS_FIRST
    if (part -> ipart == 0){
      *dp_record = LocalParticle_get_delta(part) - *dp_record;
      *dpx_record = LocalParticle_get_px(part) - *dpx_record;
      *dpy_record = LocalParticle_get_py(part) - *dpy_record;
    }
    else{
      LocalParticle_update_delta(part, LocalParticle_get_delta(part) + *dp_record);
      LocalParticle_add_to_px(part, *dpx_record);
      LocalParticle_add_to_py(part, *dpy_record);
    }
    #endif
}

 static inline
double SynRad(double x)
{
  // x :    energy normalized to the critical energy
  // returns function value _SynRadC   photon spectrum dn/dx
  // (integral of modified 1/3 order Bessel function)
  // principal: Chebyshev series see H.H.Umstaetter CERN/PS/SM/81-13 10-3-1981
  // see also my LEP Note 632 of 12/1990
  // converted to C++, H.Burkhardt 21-4-1996    */
  double synrad = 0.;
  if(x>0. && x<800.) {	// otherwise result synrad remains 0
    if(x<6.) {
      double a,b,z;
      z=x*x/16.-2.;
      b=          .00000000000000000012;
      a=z*b  +    .00000000000000000460;
      b=z*a-b+    .00000000000000031738;
      a=z*b-a+    .00000000000002004426;
      b=z*a-b+    .00000000000111455474;
      a=z*b-a+    .00000000005407460944;
      b=z*a-b+    .00000000226722011790;
      a=z*b-a+    .00000008125130371644;
      b=z*a-b+    .00000245751373955212;
      a=z*b-a+    .00006181256113829740;
      b=z*a-b+    .00127066381953661690;
      a=z*b-a+    .02091216799114667278;
      b=z*a-b+    .26880346058164526514;
      a=z*b-a+   2.61902183794862213818;
      b=z*a-b+  18.65250896865416256398;
      a=z*b-a+  92.95232665922707542088;
      b=z*a-b+ 308.15919413131586030542;
      a=z*b-a+ 644.86979658236221700714;
      double p;
      p=.5*z*a-b+  414.56543648832546975110;
      a=          .00000000000000000004;
      b=z*a+      .00000000000000000289;
      a=z*b-a+    .00000000000000019786;
      b=z*a-b+    .00000000000001196168;
      a=z*b-a+    .00000000000063427729;
      b=z*a-b+    .00000000002923635681;
      a=z*b-a+    .00000000115951672806;
      b=z*a-b+    .00000003910314748244;
      a=z*b-a+    .00000110599584794379;
      b=z*a-b+    .00002581451439721298;
      a=z*b-a+    .00048768692916240683;
      b=z*a-b+    .00728456195503504923;
      a=z*b-a+    .08357935463720537773;
      b=z*a-b+    .71031361199218887514;
      a=z*b-a+   4.26780261265492264837;
      b=z*a-b+  17.05540785795221885751;
      a=z*b-a+  41.83903486779678800040;
      double q;
      q=.5*z*a-b+28.41787374362784178164;
      double y;
      y=pow(x,2./3.);
      synrad=(p/y-q*y-1.)*1.81379936423421784215530788143;

    } else {// 6 < x < 174

      double a,b,z;
      z=20./x-2.;
      a=      .00000000000000000001;
      b=z*a  -.00000000000000000002;
      a=z*b-a+.00000000000000000006;
      b=z*a-b-.00000000000000000020;
      a=z*b-a+.00000000000000000066;
      b=z*a-b-.00000000000000000216;
      a=z*b-a+.00000000000000000721;
      b=z*a-b-.00000000000000002443;
      a=z*b-a+.00000000000000008441;
      b=z*a-b-.00000000000000029752;
      a=z*b-a+.00000000000000107116;
      b=z*a-b-.00000000000000394564;
      a=z*b-a+.00000000000001489474;
      b=z*a-b-.00000000000005773537;
      a=z*b-a+.00000000000023030657;
      b=z*a-b-.00000000000094784973;
      a=z*b-a+.00000000000403683207;
      b=z*a-b-.00000000001785432348;
      a=z*b-a+.00000000008235329314;
      b=z*a-b-.00000000039817923621;
      a=z*b-a+.00000000203088939238;
      b=z*a-b-.00000001101482369622;
      a=z*b-a+.00000006418902302372;
      b=z*a-b-.00000040756144386809;
      a=z*b-a+.00000287536465397527;
      b=z*a-b-.00002321251614543524;
      a=z*b-a+.00022505317277986004;
      b=z*a-b-.00287636803664026799;
      a=z*b-a+.06239591359332750793;
      double p;
      p=.5*z*a-b    +1.06552390798340693166;
      synrad=p*sqrt(0.5*PI/x)/exp(x);
    }
  }
  return synrad;
}

 static inline
double synrad_gen_photon_energy_normalized(LocalParticle *part)
{
  // initialize constants used in the approximate expressions
  // for SYNRAD   (integral over the modified Bessel function K5/3)
  //  xmin = 0.;
  double const xlow = 1.;
  double const a1 = 2.149528241534391; // Synrad(1.e-38)/pow(1.e-38,-2./3.);
  double const a2 = 1.770750801624037; // Synrad(xlow)/exp(-xlow);
  double const c1 = 0.; //
  double const ratio = 0.908250405131381;
  double appr, exact, result;
  do {
    if (RandomUniform_generate(part) < ratio) { // use low energy approximation
      result=c1+(1.-c1)*RandomUniform_generate(part);
      double tmp = result*result;
      result*=tmp;  	// take to 3rd power;
      exact=SynRad(result);
      appr=a1/tmp;
    } else {				// use high energy approximation
      result=xlow-log(RandomUniform_generate(part));
      exact=SynRad(result);
      appr=a2*exp(-result);
    }
  } while (exact < appr*RandomUniform_generate(part));	// reject in proportion of approx
  return result; // result now exact spectrum with unity weight
}

 static inline
double synrad_average_number_of_photons(
                          double beta0_gamma0, double curv, double lpath){
    double const kick = curv * lpath;
    return 2.5/SQRT3*ALPHA_EM*beta0_gamma0*fabs(kick);
}

 static inline
int64_t synrad_emit_photons(LocalParticle *part, double curv /* 1/m */,
                            double lpath /* m */,
                            RecordIndex record_index,
                            SynchrotronRadiationRecordData record
                            ){

    if (fabs(curv) < 1e-15)
        return 0;

    int64_t nphot = 0;

    // TODO Introduce effect of chi and mass_ratio!!!
    double const m0 = LocalParticle_get_mass0(part); // eV
    double const gamma0  = LocalParticle_get_gamma0(part);
    double const beta0  = LocalParticle_get_beta0(part);

    double const initial_energy = LocalParticle_get_energy0(part)
	                          + LocalParticle_get_ptau(part)*LocalParticle_get_p0c(part); // eV
    double energy = initial_energy;
    double gamma = energy / m0; //
    //double beta_gamma = sqrt(gamma*gamma-1); //
    double n = RandomExponential_generate(part); // path_length / mean_free_path;
    while (n < synrad_average_number_of_photons(beta0 * gamma0, curv, lpath)) {
        nphot++;
        double const c1 = 1.5 * 1.973269804593025e-07; // hbar * c = 1.973269804593025e-07 eV * m
        double const energy_critical = c1 * (gamma*gamma*gamma0) * curv; // eV
        double const energy_loss = synrad_gen_photon_energy_normalized(part) * energy_critical; // eV
        if (energy_loss >= energy) {
            energy = 0.0; // eV
            break;
        }
        energy -= energy_loss; // eV
        gamma = energy / m0; //
        // beta_gamma = sqrt(gamma*gamma-1); // that's how beta gamma is
        n += RandomExponential_generate(part);
        if (record){
          int64_t i_slot = RecordIndex_get_slot(record_index);
          // The returned slot id is negative if record is NULL or if record is full

          if (i_slot>=0){
              SynchrotronRadiationRecordData_set_photon_energy(record, i_slot,
                                                               energy_loss);
              SynchrotronRadiationRecordData_set_at_element(record, i_slot,
                                          LocalParticle_get_at_element(part));
              SynchrotronRadiationRecordData_set_at_turn(record, i_slot,
                                          LocalParticle_get_at_turn(part));
              SynchrotronRadiationRecordData_set_particle_id(record, i_slot,
                                          LocalParticle_get_particle_id(part));
              SynchrotronRadiationRecordData_set_particle_delta(record, i_slot,
                                          LocalParticle_get_delta(part));
          }
        }
    }

    if (energy == 0.0)
      LocalParticle_set_state(part, XT_LOST_ALL_E_IN_SYNRAD); // used to flag this kind of loss
    else{
      LocalParticle_add_to_energy(part, energy-initial_energy, 0);
    }

    return nphot;
}

#endif /* XTRACK_SYNRAD_SPECTRUM_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_MULTIPOLE_H
#define XTRACK_MULTIPOLE_H

 static inline
void Multipole_track_local_particle(MultipoleData el, LocalParticle* part0){

    #ifndef XTRACK_MULTIPOLE_NO_SYNRAD
    int64_t radiation_flag = MultipoleData_get_radiation_flag(el);

    // Extract record and record_index
    SynchrotronRadiationRecordData record = NULL;
    RecordIndex record_index = NULL;
    if (radiation_flag==2){
        record = (SynchrotronRadiationRecordData) MultipoleData_getp_internal_record(el, part0);
        if (record){
            record_index = SynchrotronRadiationRecordData_getp__index(record);
        }
    }
    double dp_record_entry = 0.;
    double dpx_record_entry = 0.;
    double dpy_record_entry = 0.;
    double dp_record_exit = 0.;
    double dpx_record_exit = 0.;
    double dpy_record_exit = 0.;
    #endif

    #ifdef XTRACK_MULTIPOLE_NO_SYNRAD
    #define delta_taper (0)
    #else
        #ifndef XTRACK_MULTIPOLE_TAPER
        double const delta_taper = MultipoleData_get_delta_taper(el);
        #endif
    #endif


    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

        int64_t order = MultipoleData_get_order(el);
        int64_t index = order;

        double inv_factorial = MultipoleData_get_inv_factorial_order(el);

        double dpx = MultipoleData_get_knl(el, index) * inv_factorial;
        double dpy = MultipoleData_get_ksl(el, index) * inv_factorial;

        #ifdef XSUITE_BACKTRACK
        dpx = -dpx;
        dpy = -dpy;
        #endif

        #ifdef XTRACK_MULTIPOLE_TAPER
        double const delta_taper = LocalParticle_get_delta(part);
        #endif
        dpx = dpx * (1 + delta_taper);
        dpy = dpy * (1 + delta_taper);

        double const x   = LocalParticle_get_x(part);
        double const y   = LocalParticle_get_y(part);
        double const chi = LocalParticle_get_chi(part);

        #ifndef XSUITE_BACKTRACK
        double const hxl = MultipoleData_get_hxl(el);
        double const hyl = MultipoleData_get_hyl(el);
        #else
        double const hxl = -MultipoleData_get_hxl(el);
        double const hyl = -MultipoleData_get_hyl(el);
        #endif

        while( index > 0 )
        {
            double const zre = dpx * x - dpy * y;
            double const zim = dpx * y + dpy * x;

            inv_factorial *= index;
            index -= 1;

            double this_knl = MultipoleData_get_knl(el, index);
            double this_ksl = MultipoleData_get_ksl(el, index);

            #ifdef XSUITE_BACKTRACK
            this_knl = -this_knl;
            this_ksl = -this_ksl;
            #endif

            this_knl = this_knl * (1 + delta_taper);
            this_ksl = this_ksl * (1 + delta_taper);

            dpx = this_knl*inv_factorial + zre;
            dpy = this_ksl*inv_factorial + zim;
        }

        #ifndef XSUITE_BACKTRACK
        double const length = MultipoleData_get_length(el); // m
        #else
        double const length = -MultipoleData_get_length(el); // m
        #endif

        #ifndef XTRACK_MULTIPOLE_NO_SYNRAD
        // Radiation at entrance
        double const curv = sqrt(dpx*dpx + dpy*dpy) / length;
        if (radiation_flag > 0 && length > 0){
            double const L_path = 0.5*length*(1 + (hxl*x - hyl*y)/length); //CHECK!!!!
            if (radiation_flag == 1){
                synrad_average_kick(part, curv, L_path,
                        &dp_record_entry, &dpx_record_entry, &dpy_record_entry);
            }
            else if (radiation_flag == 2){
                synrad_emit_photons(part, curv, L_path, record_index, record);
            }
        }
        #endif

        dpx = -chi * dpx; // rad
        dpy =  chi * dpy; // rad

        if( ( hxl > 0) || ( hyl > 0) || ( hxl < 0 ) || ( hyl < 0 ) )
        {
            double const delta  = LocalParticle_get_delta(part);

            double const hxlx   = x * hxl;
            double const hyly   = y * hyl;

            double const rv0v = 1./LocalParticle_get_rvv(part);

            LocalParticle_add_to_zeta(part, rv0v*chi * ( hyly - hxlx ) );

            dpx += hxl + hxl * delta;
            dpy -= hyl + hyl * delta;

            if( length != 0)
            {
                double b1l = chi * MultipoleData_get_knl(el, 0 );
                double a1l = chi * MultipoleData_get_ksl(el, 0 );

                #ifdef XSUITE_BACKTRACK
                b1l = -b1l;
                a1l = -a1l;
                #endif

                b1l = b1l * (1 + delta_taper);
                a1l = a1l * (1 + delta_taper);

                dpx -= b1l * hxlx / length;
                dpy -= a1l * hyly / length;
            }
        }

        LocalParticle_add_to_px(part, dpx);
        LocalParticle_add_to_py(part, dpy);

        // Radiation at exit
        #ifndef XTRACK_MULTIPOLE_NO_SYNRAD
        if (radiation_flag > 0 && length > 0){
            double const L_path = 0.5*length*(1 + (hxl*x - hyl*y)/length); //CHECK!!!!
            if (radiation_flag == 1){
                synrad_average_kick(part, curv, L_path,
                        &dp_record_exit, &dpx_record_exit, &dpy_record_exit);
            }
            else if (radiation_flag == 2){
                synrad_emit_photons(part, curv, L_path, record_index, record);
            }
        }
        #endif

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

}

#endif


             
            void Multipole_track_particles(
               MultipoleData el,

                             ParticlesData particles,

                             int64_t flag_increment_at_element,
                  int8_t* io_buffer){
//            const int num_threads = omp_get_max_threads();                                 //only_for_context cpu_openmp
//            const int64_t capacity = ParticlesData_get__capacity(particles);               //only_for_context cpu_openmp
//            const int64_t chunk_size = (capacity + num_threads - 1)/num_threads; // ceil division  //only_for_context cpu_openmp
//            #pragma omp parallel for                                                       //only_for_context cpu_openmp
//            for (int64_t batch_id = 0; batch_id < num_threads; batch_id++) {               //only_for_context cpu_openmp
                LocalParticle lpart;
                lpart.io_buffer = io_buffer;
//                int64_t part_id = batch_id * chunk_size;                                       //only_for_context cpu_openmp
//                int64_t end_id = (batch_id + 1) * chunk_size;                                  //only_for_context cpu_openmp
//                if (end_id > capacity) end_id = capacity;                                      //only_for_context cpu_openmp
    
                int64_t part_id = 0;                    //only_for_context cpu_serial
//                int64_t part_id = blockDim.x * blockIdx.x + threadIdx.x; //only_for_context cuda
//                int64_t part_id = get_global_id(0);                    //only_for_context opencl
                int64_t end_id = 0; // unused outside of openmp  //only_for_context cpu_serial cuda opencl
    
                int64_t part_capacity = ParticlesData_get__capacity(particles);
                if (part_id<part_capacity){
                    Particles_to_LocalParticle(particles, &lpart, part_id, end_id);
                    if (check_is_active(&lpart)>0){
              Multipole_track_local_particle(el, &lpart);

                    }
                    if (check_is_active(&lpart)>0 && flag_increment_at_element){
                            increment_at_element(&lpart, 1);
                    }
                }
//            } //only_for_context cpu_openmp
            
            // On OpenMP we want to additionally by default reorganize all
            // the particles.
//            #ifndef XT_OMP_SKIP_REORGANIZE                             //only_for_context cpu_openmp
//            LocalParticle lpart;                                       //only_for_context cpu_openmp
//            lpart.io_buffer = io_buffer;                               //only_for_context cpu_openmp
//            Particles_to_LocalParticle(particles, &lpart, 0, capacity);//only_for_context cpu_openmp
//            check_is_active(&lpart);                                   //only_for_context cpu_openmp
//            #endif                                                     //only_for_context cpu_openmp
        }

#ifndef XOBJ_TYPEDEF_LineSegmentMapData
#define XOBJ_TYPEDEF_LineSegmentMapData
typedef   struct LineSegmentMapData_s * LineSegmentMapData;
 static inline LineSegmentMapData LineSegmentMapData_getp(LineSegmentMapData  obj){
  int64_t offset=0;
  return (LineSegmentMapData)(( char*) obj+offset);
}
 static inline double LineSegmentMapData_get_length(const LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=8;
  return *( double*)(( char*) obj+offset);
}
 static inline void LineSegmentMapData_set_length(LineSegmentMapData  obj, double value){
  int64_t offset=0;
  offset+=8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LineSegmentMapData_getp_length(LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=8;
  return ( double*)(( char*) obj+offset);
}
 static inline double LineSegmentMapData_get_qx(const LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=16;
  return *( double*)(( char*) obj+offset);
}
 static inline void LineSegmentMapData_set_qx(LineSegmentMapData  obj, double value){
  int64_t offset=0;
  offset+=16;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LineSegmentMapData_getp_qx(LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=16;
  return ( double*)(( char*) obj+offset);
}
 static inline double LineSegmentMapData_get_qy(const LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=24;
  return *( double*)(( char*) obj+offset);
}
 static inline void LineSegmentMapData_set_qy(LineSegmentMapData  obj, double value){
  int64_t offset=0;
  offset+=24;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LineSegmentMapData_getp_qy(LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=24;
  return ( double*)(( char*) obj+offset);
}
 static inline double LineSegmentMapData_get_dqx(const LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=32;
  return *( double*)(( char*) obj+offset);
}
 static inline void LineSegmentMapData_set_dqx(LineSegmentMapData  obj, double value){
  int64_t offset=0;
  offset+=32;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LineSegmentMapData_getp_dqx(LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=32;
  return ( double*)(( char*) obj+offset);
}
 static inline double LineSegmentMapData_get_dqy(const LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=40;
  return *( double*)(( char*) obj+offset);
}
 static inline void LineSegmentMapData_set_dqy(LineSegmentMapData  obj, double value){
  int64_t offset=0;
  offset+=40;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LineSegmentMapData_getp_dqy(LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=40;
  return ( double*)(( char*) obj+offset);
}
 static inline double LineSegmentMapData_get_detx_x(const LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=48;
  return *( double*)(( char*) obj+offset);
}
 static inline void LineSegmentMapData_set_detx_x(LineSegmentMapData  obj, double value){
  int64_t offset=0;
  offset+=48;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LineSegmentMapData_getp_detx_x(LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=48;
  return ( double*)(( char*) obj+offset);
}
 static inline double LineSegmentMapData_get_detx_y(const LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=56;
  return *( double*)(( char*) obj+offset);
}
 static inline void LineSegmentMapData_set_detx_y(LineSegmentMapData  obj, double value){
  int64_t offset=0;
  offset+=56;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LineSegmentMapData_getp_detx_y(LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=56;
  return ( double*)(( char*) obj+offset);
}
 static inline double LineSegmentMapData_get_dety_y(const LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=64;
  return *( double*)(( char*) obj+offset);
}
 static inline void LineSegmentMapData_set_dety_y(LineSegmentMapData  obj, double value){
  int64_t offset=0;
  offset+=64;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LineSegmentMapData_getp_dety_y(LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=64;
  return ( double*)(( char*) obj+offset);
}
 static inline double LineSegmentMapData_get_dety_x(const LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=72;
  return *( double*)(( char*) obj+offset);
}
 static inline void LineSegmentMapData_set_dety_x(LineSegmentMapData  obj, double value){
  int64_t offset=0;
  offset+=72;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LineSegmentMapData_getp_dety_x(LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=72;
  return ( double*)(( char*) obj+offset);
}
 static inline Arr2Float64 LineSegmentMapData_getp_betx(LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=80;
  return (Arr2Float64)(( char*) obj+offset);
}
 static inline int64_t LineSegmentMapData_len_betx(LineSegmentMapData  obj){
  return 2;
}
 static inline double LineSegmentMapData_get_betx(const LineSegmentMapData  obj, int64_t i0){
  int64_t offset=0;
  offset+=80;
  offset+=i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void LineSegmentMapData_set_betx(LineSegmentMapData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=80;
  offset+=i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LineSegmentMapData_getp1_betx(LineSegmentMapData  obj, int64_t i0){
  int64_t offset=0;
  offset+=80;
  offset+=i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline Arr2Float64 LineSegmentMapData_getp_bety(LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=96;
  return (Arr2Float64)(( char*) obj+offset);
}
 static inline int64_t LineSegmentMapData_len_bety(LineSegmentMapData  obj){
  return 2;
}
 static inline double LineSegmentMapData_get_bety(const LineSegmentMapData  obj, int64_t i0){
  int64_t offset=0;
  offset+=96;
  offset+=i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void LineSegmentMapData_set_bety(LineSegmentMapData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=96;
  offset+=i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LineSegmentMapData_getp1_bety(LineSegmentMapData  obj, int64_t i0){
  int64_t offset=0;
  offset+=96;
  offset+=i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline Arr2Float64 LineSegmentMapData_getp_alfx(LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=112;
  return (Arr2Float64)(( char*) obj+offset);
}
 static inline int64_t LineSegmentMapData_len_alfx(LineSegmentMapData  obj){
  return 2;
}
 static inline double LineSegmentMapData_get_alfx(const LineSegmentMapData  obj, int64_t i0){
  int64_t offset=0;
  offset+=112;
  offset+=i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void LineSegmentMapData_set_alfx(LineSegmentMapData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=112;
  offset+=i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LineSegmentMapData_getp1_alfx(LineSegmentMapData  obj, int64_t i0){
  int64_t offset=0;
  offset+=112;
  offset+=i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline Arr2Float64 LineSegmentMapData_getp_alfy(LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=128;
  return (Arr2Float64)(( char*) obj+offset);
}
 static inline int64_t LineSegmentMapData_len_alfy(LineSegmentMapData  obj){
  return 2;
}
 static inline double LineSegmentMapData_get_alfy(const LineSegmentMapData  obj, int64_t i0){
  int64_t offset=0;
  offset+=128;
  offset+=i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void LineSegmentMapData_set_alfy(LineSegmentMapData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=128;
  offset+=i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LineSegmentMapData_getp1_alfy(LineSegmentMapData  obj, int64_t i0){
  int64_t offset=0;
  offset+=128;
  offset+=i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline Arr2Float64 LineSegmentMapData_getp_dx(LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=144;
  return (Arr2Float64)(( char*) obj+offset);
}
 static inline int64_t LineSegmentMapData_len_dx(LineSegmentMapData  obj){
  return 2;
}
 static inline double LineSegmentMapData_get_dx(const LineSegmentMapData  obj, int64_t i0){
  int64_t offset=0;
  offset+=144;
  offset+=i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void LineSegmentMapData_set_dx(LineSegmentMapData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=144;
  offset+=i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LineSegmentMapData_getp1_dx(LineSegmentMapData  obj, int64_t i0){
  int64_t offset=0;
  offset+=144;
  offset+=i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline Arr2Float64 LineSegmentMapData_getp_dpx(LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=160;
  return (Arr2Float64)(( char*) obj+offset);
}
 static inline int64_t LineSegmentMapData_len_dpx(LineSegmentMapData  obj){
  return 2;
}
 static inline double LineSegmentMapData_get_dpx(const LineSegmentMapData  obj, int64_t i0){
  int64_t offset=0;
  offset+=160;
  offset+=i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void LineSegmentMapData_set_dpx(LineSegmentMapData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=160;
  offset+=i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LineSegmentMapData_getp1_dpx(LineSegmentMapData  obj, int64_t i0){
  int64_t offset=0;
  offset+=160;
  offset+=i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline Arr2Float64 LineSegmentMapData_getp_dy(LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=176;
  return (Arr2Float64)(( char*) obj+offset);
}
 static inline int64_t LineSegmentMapData_len_dy(LineSegmentMapData  obj){
  return 2;
}
 static inline double LineSegmentMapData_get_dy(const LineSegmentMapData  obj, int64_t i0){
  int64_t offset=0;
  offset+=176;
  offset+=i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void LineSegmentMapData_set_dy(LineSegmentMapData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=176;
  offset+=i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LineSegmentMapData_getp1_dy(LineSegmentMapData  obj, int64_t i0){
  int64_t offset=0;
  offset+=176;
  offset+=i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline Arr2Float64 LineSegmentMapData_getp_dpy(LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=192;
  return (Arr2Float64)(( char*) obj+offset);
}
 static inline int64_t LineSegmentMapData_len_dpy(LineSegmentMapData  obj){
  return 2;
}
 static inline double LineSegmentMapData_get_dpy(const LineSegmentMapData  obj, int64_t i0){
  int64_t offset=0;
  offset+=192;
  offset+=i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void LineSegmentMapData_set_dpy(LineSegmentMapData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=192;
  offset+=i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LineSegmentMapData_getp1_dpy(LineSegmentMapData  obj, int64_t i0){
  int64_t offset=0;
  offset+=192;
  offset+=i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline Arr2Float64 LineSegmentMapData_getp_x_ref(LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=208;
  return (Arr2Float64)(( char*) obj+offset);
}
 static inline int64_t LineSegmentMapData_len_x_ref(LineSegmentMapData  obj){
  return 2;
}
 static inline double LineSegmentMapData_get_x_ref(const LineSegmentMapData  obj, int64_t i0){
  int64_t offset=0;
  offset+=208;
  offset+=i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void LineSegmentMapData_set_x_ref(LineSegmentMapData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=208;
  offset+=i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LineSegmentMapData_getp1_x_ref(LineSegmentMapData  obj, int64_t i0){
  int64_t offset=0;
  offset+=208;
  offset+=i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline Arr2Float64 LineSegmentMapData_getp_px_ref(LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=224;
  return (Arr2Float64)(( char*) obj+offset);
}
 static inline int64_t LineSegmentMapData_len_px_ref(LineSegmentMapData  obj){
  return 2;
}
 static inline double LineSegmentMapData_get_px_ref(const LineSegmentMapData  obj, int64_t i0){
  int64_t offset=0;
  offset+=224;
  offset+=i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void LineSegmentMapData_set_px_ref(LineSegmentMapData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=224;
  offset+=i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LineSegmentMapData_getp1_px_ref(LineSegmentMapData  obj, int64_t i0){
  int64_t offset=0;
  offset+=224;
  offset+=i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline Arr2Float64 LineSegmentMapData_getp_y_ref(LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=240;
  return (Arr2Float64)(( char*) obj+offset);
}
 static inline int64_t LineSegmentMapData_len_y_ref(LineSegmentMapData  obj){
  return 2;
}
 static inline double LineSegmentMapData_get_y_ref(const LineSegmentMapData  obj, int64_t i0){
  int64_t offset=0;
  offset+=240;
  offset+=i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void LineSegmentMapData_set_y_ref(LineSegmentMapData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=240;
  offset+=i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LineSegmentMapData_getp1_y_ref(LineSegmentMapData  obj, int64_t i0){
  int64_t offset=0;
  offset+=240;
  offset+=i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline Arr2Float64 LineSegmentMapData_getp_py_ref(LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=256;
  return (Arr2Float64)(( char*) obj+offset);
}
 static inline int64_t LineSegmentMapData_len_py_ref(LineSegmentMapData  obj){
  return 2;
}
 static inline double LineSegmentMapData_get_py_ref(const LineSegmentMapData  obj, int64_t i0){
  int64_t offset=0;
  offset+=256;
  offset+=i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void LineSegmentMapData_set_py_ref(LineSegmentMapData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=256;
  offset+=i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LineSegmentMapData_getp1_py_ref(LineSegmentMapData  obj, int64_t i0){
  int64_t offset=0;
  offset+=256;
  offset+=i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline double LineSegmentMapData_get_energy_ref_increment(const LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=272;
  return *( double*)(( char*) obj+offset);
}
 static inline void LineSegmentMapData_set_energy_ref_increment(LineSegmentMapData  obj, double value){
  int64_t offset=0;
  offset+=272;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LineSegmentMapData_getp_energy_ref_increment(LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=272;
  return ( double*)(( char*) obj+offset);
}
 static inline double LineSegmentMapData_get_energy_increment(const LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=280;
  return *( double*)(( char*) obj+offset);
}
 static inline void LineSegmentMapData_set_energy_increment(LineSegmentMapData  obj, double value){
  int64_t offset=0;
  offset+=280;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LineSegmentMapData_getp_energy_increment(LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=280;
  return ( double*)(( char*) obj+offset);
}
 static inline int64_t LineSegmentMapData_get_uncorrelated_rad_damping(const LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=288;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void LineSegmentMapData_set_uncorrelated_rad_damping(LineSegmentMapData  obj, int64_t value){
  int64_t offset=0;
  offset+=288;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* LineSegmentMapData_getp_uncorrelated_rad_damping(LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=288;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline double LineSegmentMapData_get_damping_factor_x(const LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=296;
  return *( double*)(( char*) obj+offset);
}
 static inline void LineSegmentMapData_set_damping_factor_x(LineSegmentMapData  obj, double value){
  int64_t offset=0;
  offset+=296;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LineSegmentMapData_getp_damping_factor_x(LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=296;
  return ( double*)(( char*) obj+offset);
}
 static inline double LineSegmentMapData_get_damping_factor_y(const LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=304;
  return *( double*)(( char*) obj+offset);
}
 static inline void LineSegmentMapData_set_damping_factor_y(LineSegmentMapData  obj, double value){
  int64_t offset=0;
  offset+=304;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LineSegmentMapData_getp_damping_factor_y(LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=304;
  return ( double*)(( char*) obj+offset);
}
 static inline double LineSegmentMapData_get_damping_factor_s(const LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=312;
  return *( double*)(( char*) obj+offset);
}
 static inline void LineSegmentMapData_set_damping_factor_s(LineSegmentMapData  obj, double value){
  int64_t offset=0;
  offset+=312;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LineSegmentMapData_getp_damping_factor_s(LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=312;
  return ( double*)(( char*) obj+offset);
}
 static inline int64_t LineSegmentMapData_get_uncorrelated_gauss_noise(const LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=320;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void LineSegmentMapData_set_uncorrelated_gauss_noise(LineSegmentMapData  obj, int64_t value){
  int64_t offset=0;
  offset+=320;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* LineSegmentMapData_getp_uncorrelated_gauss_noise(LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=320;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline double LineSegmentMapData_get_gauss_noise_ampl_x(const LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=328;
  return *( double*)(( char*) obj+offset);
}
 static inline void LineSegmentMapData_set_gauss_noise_ampl_x(LineSegmentMapData  obj, double value){
  int64_t offset=0;
  offset+=328;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LineSegmentMapData_getp_gauss_noise_ampl_x(LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=328;
  return ( double*)(( char*) obj+offset);
}
 static inline double LineSegmentMapData_get_gauss_noise_ampl_px(const LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=336;
  return *( double*)(( char*) obj+offset);
}
 static inline void LineSegmentMapData_set_gauss_noise_ampl_px(LineSegmentMapData  obj, double value){
  int64_t offset=0;
  offset+=336;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LineSegmentMapData_getp_gauss_noise_ampl_px(LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=336;
  return ( double*)(( char*) obj+offset);
}
 static inline double LineSegmentMapData_get_gauss_noise_ampl_y(const LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=344;
  return *( double*)(( char*) obj+offset);
}
 static inline void LineSegmentMapData_set_gauss_noise_ampl_y(LineSegmentMapData  obj, double value){
  int64_t offset=0;
  offset+=344;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LineSegmentMapData_getp_gauss_noise_ampl_y(LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=344;
  return ( double*)(( char*) obj+offset);
}
 static inline double LineSegmentMapData_get_gauss_noise_ampl_py(const LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=352;
  return *( double*)(( char*) obj+offset);
}
 static inline void LineSegmentMapData_set_gauss_noise_ampl_py(LineSegmentMapData  obj, double value){
  int64_t offset=0;
  offset+=352;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LineSegmentMapData_getp_gauss_noise_ampl_py(LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=352;
  return ( double*)(( char*) obj+offset);
}
 static inline double LineSegmentMapData_get_gauss_noise_ampl_zeta(const LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=360;
  return *( double*)(( char*) obj+offset);
}
 static inline void LineSegmentMapData_set_gauss_noise_ampl_zeta(LineSegmentMapData  obj, double value){
  int64_t offset=0;
  offset+=360;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LineSegmentMapData_getp_gauss_noise_ampl_zeta(LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=360;
  return ( double*)(( char*) obj+offset);
}
 static inline double LineSegmentMapData_get_gauss_noise_ampl_delta(const LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=368;
  return *( double*)(( char*) obj+offset);
}
 static inline void LineSegmentMapData_set_gauss_noise_ampl_delta(LineSegmentMapData  obj, double value){
  int64_t offset=0;
  offset+=368;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LineSegmentMapData_getp_gauss_noise_ampl_delta(LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=368;
  return ( double*)(( char*) obj+offset);
}
 static inline int64_t LineSegmentMapData_get_longitudinal_mode_flag(const LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=376;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void LineSegmentMapData_set_longitudinal_mode_flag(LineSegmentMapData  obj, int64_t value){
  int64_t offset=0;
  offset+=376;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* LineSegmentMapData_getp_longitudinal_mode_flag(LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=376;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline double LineSegmentMapData_get_qs(const LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=384;
  return *( double*)(( char*) obj+offset);
}
 static inline void LineSegmentMapData_set_qs(LineSegmentMapData  obj, double value){
  int64_t offset=0;
  offset+=384;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LineSegmentMapData_getp_qs(LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=384;
  return ( double*)(( char*) obj+offset);
}
 static inline double LineSegmentMapData_get_bets(const LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=392;
  return *( double*)(( char*) obj+offset);
}
 static inline void LineSegmentMapData_set_bets(LineSegmentMapData  obj, double value){
  int64_t offset=0;
  offset+=392;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LineSegmentMapData_getp_bets(LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=392;
  return ( double*)(( char*) obj+offset);
}
 static inline double LineSegmentMapData_get_momentum_compaction_factor(const LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=400;
  return *( double*)(( char*) obj+offset);
}
 static inline void LineSegmentMapData_set_momentum_compaction_factor(LineSegmentMapData  obj, double value){
  int64_t offset=0;
  offset+=400;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LineSegmentMapData_getp_momentum_compaction_factor(LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=400;
  return ( double*)(( char*) obj+offset);
}
 static inline double LineSegmentMapData_get_slippage_length(const LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=408;
  return *( double*)(( char*) obj+offset);
}
 static inline void LineSegmentMapData_set_slippage_length(LineSegmentMapData  obj, double value){
  int64_t offset=0;
  offset+=408;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LineSegmentMapData_getp_slippage_length(LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=408;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNFloat64 LineSegmentMapData_getp_voltage_rf(LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=432;
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t LineSegmentMapData_len_voltage_rf(LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=432;
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double LineSegmentMapData_get_voltage_rf(const LineSegmentMapData  obj, int64_t i0){
  int64_t offset=0;
  offset+=432;
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void LineSegmentMapData_set_voltage_rf(LineSegmentMapData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=432;
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LineSegmentMapData_getp1_voltage_rf(LineSegmentMapData  obj, int64_t i0){
  int64_t offset=0;
  offset+=432;
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNFloat64 LineSegmentMapData_getp_frequency_rf(LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+416);
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t LineSegmentMapData_len_frequency_rf(LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+416);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double LineSegmentMapData_get_frequency_rf(const LineSegmentMapData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+416);
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void LineSegmentMapData_set_frequency_rf(LineSegmentMapData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+416);
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LineSegmentMapData_getp1_frequency_rf(LineSegmentMapData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+416);
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNFloat64 LineSegmentMapData_getp_lag_rf(LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+424);
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t LineSegmentMapData_len_lag_rf(LineSegmentMapData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+424);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double LineSegmentMapData_get_lag_rf(const LineSegmentMapData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+424);
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void LineSegmentMapData_set_lag_rf(LineSegmentMapData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+424);
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* LineSegmentMapData_getp1_lag_rf(LineSegmentMapData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+424);
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
#endif
// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_CONSTANTS_H
#define XTRACK_CONSTANTS_H

#if !defined( C_LIGHT )
    #define   C_LIGHT ( 299792458.0 )
#endif /* !defined( C_LIGHT ) */

#if !defined( EPSILON_0 )
    #define   EPSILON_0 (8.854187817620e-12)
#endif /* !defined( EPSILON_0 ) */

#if !defined( PI )
    #define PI (3.1415926535897932384626433832795028841971693993751)
#endif /* !defined( PI ) */

#if !defined( MU_0 )
    #define MU_0 (PI*4.0e-7)
#endif /* !defined( MU_0 ) */

#if !defined( DEG2RAD )
    #define DEG2RAD (0.0174532925199432957692369076848861271344287188854)
#endif /* !defiend( DEG2RAD ) */

#if !defined( RAD2DEG )
    #define RAD2DEG (57.29577951308232087679815481410517033240547246656442)
#endif /* !defiend( RAD2DEG ) */

#if !defined( SQRT_PI )
    #define SQRT_PI (1.7724538509055160272981674833411451827975494561224)
#endif /* !defined( SQRT_PI ) */

#if !defined( QELEM )
    #define QELEM (1.60217662e-19)
#endif /* !defined( QELEM ) */

#if !defined( DBL_MAX )
    #define DBL_MAX (1.7976931348623158e+308)
#endif /* !defined( DBL_MAX ) */

#if !defined( DBL_MIN )
    #define DBL_MIN (2.2250738585072014e-308)
#endif /* !defined( DBL_MIN ) */

#if !defined( DBL_EPSILON )
    #define DBL_EPSILON (2.2204460492503131e-16)
#endif /* !defined( DBL_EPSILON ) */

#endif /* XTRACK_CONSTANTS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_FUNCTIONS_H
#define XTRACK_FUNCTIONS_H

 static inline
void kill_all_particles(LocalParticle* part0, int64_t kill_state) {

    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

        LocalParticle_kill_particle(part, kill_state);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

}


 static inline
int8_t assert_tracking(LocalParticle* part, int64_t kill_state){
    // Whenever we are not tracking, e.g. in a twiss, the particle will be at_turn < 0.
    // We test this to distinguish genuine tracking from twiss.
    if (LocalParticle_get_at_turn(part) < 0){
        LocalParticle_kill_particle(part, kill_state);
        return 0;
    }
    return 1;
}

#endif /* XTRACK_FUNCTIONS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_PARTICLE_STATES_H
#define XTRACK_PARTICLE_STATES_H

#define  XT_LOST_ON_APERTURE       0
#define  XT_LOST_ON_LONG_CUT      -2
#define  XT_LOST_ALL_E_IN_SYNRAD -10
#define  RNG_ERR_SEEDS_NOT_SET   -20
#define  RNG_ERR_INVALID_TRACK   -21
#define  RNG_ERR_RUTH_NOT_SET    -22

#endif /* XTRACK_PARTICLE_STATES_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_CONSTANTS_H
#define XTRACK_CONSTANTS_H

#if !defined( C_LIGHT )
    #define   C_LIGHT ( 299792458.0 )
#endif /* !defined( C_LIGHT ) */

#if !defined( EPSILON_0 )
    #define   EPSILON_0 (8.854187817620e-12)
#endif /* !defined( EPSILON_0 ) */

#if !defined( PI )
    #define PI (3.1415926535897932384626433832795028841971693993751)
#endif /* !defined( PI ) */

#if !defined( MU_0 )
    #define MU_0 (PI*4.0e-7)
#endif /* !defined( MU_0 ) */

#if !defined( DEG2RAD )
    #define DEG2RAD (0.0174532925199432957692369076848861271344287188854)
#endif /* !defiend( DEG2RAD ) */

#if !defined( RAD2DEG )
    #define RAD2DEG (57.29577951308232087679815481410517033240547246656442)
#endif /* !defiend( RAD2DEG ) */

#if !defined( SQRT_PI )
    #define SQRT_PI (1.7724538509055160272981674833411451827975494561224)
#endif /* !defined( SQRT_PI ) */

#if !defined( QELEM )
    #define QELEM (1.60217662e-19)
#endif /* !defined( QELEM ) */

#if !defined( DBL_MAX )
    #define DBL_MAX (1.7976931348623158e+308)
#endif /* !defined( DBL_MAX ) */

#if !defined( DBL_MIN )
    #define DBL_MIN (2.2250738585072014e-308)
#endif /* !defined( DBL_MIN ) */

#if !defined( DBL_EPSILON )
    #define DBL_EPSILON (2.2204460492503131e-16)
#endif /* !defined( DBL_EPSILON ) */

#endif /* XTRACK_CONSTANTS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_LINESEGMENTMAP_H
#define XTRACK_LINESEGMENTMAP_H

 static inline
void remove_closed_orbit(
    LocalParticle* part0, double const x_ref, double const px_ref,
    double const y_ref, double const py_ref){


    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

        // Remove closed orbit
        LocalParticle_add_to_x(part, -x_ref);
        LocalParticle_add_to_px(part, -px_ref);
        LocalParticle_add_to_y(part, -y_ref);
        LocalParticle_add_to_py(part, -py_ref);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }


}

 static inline
void add_closed_orbit(
    LocalParticle* part0, double const x_ref, double const px_ref,
    double const y_ref, double const py_ref){


    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

        // Add closed orbit
        LocalParticle_add_to_x(part, x_ref);
        LocalParticle_add_to_px(part, px_ref);
        LocalParticle_add_to_y(part, y_ref);
        LocalParticle_add_to_py(part, py_ref);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }


}

 static inline
void remove_dispersion(
    LocalParticle* part0, double const dx_0, double const dpx_0,
    double const dy_0, double const dpy_0){


    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp


        // Remove dispersion
        // Symplecticity correction (not working, to be investigated)
        // LocalParticle_add_to_zeta(part, (
        //     dpx_0 * LocalParticle_get_x(part)
        //     - dx_0 * LocalParticle_get_px(part)
        //     + dpy_0 * LocalParticle_get_y(part)
        //     - dy_0 * LocalParticle_get_py(part)
        //     )/LocalParticle_get_rvv(part));
        double const delta = LocalParticle_get_delta(part);
        LocalParticle_add_to_x(part, -dx_0 * delta);
        LocalParticle_add_to_px(part, -dpx_0 * delta);
        LocalParticle_add_to_y(part, -dy_0 * delta);
        LocalParticle_add_to_py(part, -dpy_0 * delta);


//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }


}

 static inline
void add_dispersion(
    LocalParticle* part0, double const dx_1, double const dpx_1,
    double const dy_1, double const dpy_1){


    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp


        // Add dispersion
        // Symplecticity correction (not working, to be investigated)
        // LocalParticle_add_to_zeta(part, (
        //     dpx_1 * LocalParticle_get_x(part)
        //     - dx_1 * LocalParticle_get_px(part)
        //     + dpy_1 * LocalParticle_get_y(part)
        //     - dy_1 * LocalParticle_get_py(part)
        //     )/LocalParticle_get_rvv(part));
        double const delta = LocalParticle_get_delta(part);
        LocalParticle_add_to_x(part, dx_1 * delta);
        LocalParticle_add_to_px(part, dpx_1 * delta);
        LocalParticle_add_to_y(part, dy_1 * delta);
        LocalParticle_add_to_py(part, dpy_1 * delta);


//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }


}

 static inline
void transverse_motion(LocalParticle *part0,
    double const qx, double const qy,
    double const dqx, double const dqy,
    double const detx_x, double const detx_y, double const dety_x, double const dety_y,
    double const alfx_0, double const betx_0, double const alfy_0, double const bety_0,
    double const alfx_1, double const betx_1, double const alfy_1, double const bety_1){

    int64_t detuning;
    double sin_x = 0;
    double cos_x = 0;
    double sin_y = 0;
    double cos_y = 0;
    if (dqx != 0.0 || dqy != 0.0 ||
        detx_x != 0.0 || detx_y != 0.0 || dety_x != 0.0 || dety_y != 0.0){
        detuning = 1;
    }
    else{
        detuning = 0;
        sin_x = sin(2 * PI * qx);
        cos_x = cos(2 * PI * qx);
        sin_y = sin(2 * PI * qy);
        cos_y = cos(2 * PI * qy);
    }

    double const sqrt_betprod_x = sqrt(betx_1 * betx_0);
    double const sqrt_betprod_y = sqrt(bety_1 * bety_0);

    double const sqrt_betratio_x = sqrt(betx_1 / betx_0);
    double const sqrt_betratio_y = sqrt(bety_1 / bety_0);


    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp


        if (detuning){
            double const J_x = 0.5 * (
                (1.0 + alfx_0 * alfx_0) / betx_0
                    * LocalParticle_get_x(part)*LocalParticle_get_x(part)
                + 2 * alfx_0
                    * LocalParticle_get_x(part)*LocalParticle_get_px(part)
                + betx_0
                    * LocalParticle_get_px(part)*LocalParticle_get_px(part));
            double const J_y = 0.5 * (
                (1.0 + alfy_0 * alfy_0) /bety_0
                    * LocalParticle_get_y(part)*LocalParticle_get_y(part)
                + 2*alfy_0
                    * LocalParticle_get_y(part)*LocalParticle_get_py(part)
                + bety_0
                    * LocalParticle_get_py(part)*LocalParticle_get_py(part));
            double phase = 2*PI*(qx + dqx * LocalParticle_get_delta(part)
                                +detx_x * J_x + detx_y * J_y);
            cos_x = cos(phase);
            sin_x = sin(phase);
            phase = 2*PI*(qy + dqy * LocalParticle_get_delta(part)
                            +dety_x * J_x + dety_y * J_y);
            cos_y = cos(phase);
            sin_y = sin(phase);
        }

        double const M00_x = sqrt_betratio_x*(cos_x+alfx_0*sin_x);
        double const M01_x = sqrt_betprod_x*sin_x;
        double const M10_x = ((alfx_0-alfx_1)*cos_x
                    -(1+alfx_0*alfx_1)*sin_x
                    )/sqrt_betprod_x;
        double const M11_x = (cos_x-alfx_1*sin_x)/sqrt_betratio_x;
        double const M00_y = sqrt_betratio_y*(cos_y+alfy_0*sin_y);
        double const M01_y = sqrt_betprod_y*sin_y;
        double const M10_y = ((alfy_0-alfy_1)*cos_y
                    -(1+alfy_0*alfy_1)*sin_y
                    )/sqrt_betprod_y;
        double const M11_y = (cos_y-alfy_1*sin_y)/sqrt_betratio_y;

        double const x_out = M00_x*LocalParticle_get_x(part) + M01_x * LocalParticle_get_px(part);
        double const px_out = M10_x*LocalParticle_get_x(part) + M11_x * LocalParticle_get_px(part);
        double const y_out = M00_y*LocalParticle_get_y(part) + M01_y * LocalParticle_get_py(part);
        double const py_out = M10_y*LocalParticle_get_y(part) + M11_y * LocalParticle_get_py(part);

        LocalParticle_set_x(part, x_out);
        LocalParticle_set_px(part, px_out);
        LocalParticle_set_y(part, y_out);
        LocalParticle_set_py(part, py_out);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }


}

 static inline
void longitudinal_motion(LocalParticle *part0,
                         LineSegmentMapData el){

    int64_t const mode_flag = LineSegmentMapData_get_longitudinal_mode_flag(el);

    if (mode_flag==1){ // linear motion fixed qs
        double const qs = LineSegmentMapData_get_qs(el);
        double const bets = LineSegmentMapData_get_bets(el);
        double const sin_s = sin(2 * PI * qs);
        double const cos_s = cos(2 * PI * qs);

    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

            // We set cos_s = 999 if long map is to be skipped
            double const new_zeta = cos_s * LocalParticle_get_zeta(part) - bets * sin_s * LocalParticle_get_pzeta(part);
            double const new_pzeta = sin_s * LocalParticle_get_zeta(part) / bets + cos_s * LocalParticle_get_pzeta(part);

            LocalParticle_set_zeta(part, new_zeta);
            LocalParticle_update_pzeta(part, new_pzeta);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }


    }
    else if (mode_flag==2){ // non-linear motion

        double const alfp =
            LineSegmentMapData_get_momentum_compaction_factor(el);
        double const slippage_length =
            LineSegmentMapData_get_slippage_length(el);


    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

            double const gamma0 = LocalParticle_get_gamma0(part);
            double const eta = alfp - 1.0 / (gamma0 * gamma0);
            LocalParticle_add_to_zeta(part,
                -0.5 * eta * slippage_length * LocalParticle_get_delta(part));

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }


        int64_t const n_rf = LineSegmentMapData_len_voltage_rf(el);
        for (int i_rf=0; i_rf<n_rf; i_rf++){

            double const v_rf = LineSegmentMapData_get_voltage_rf(el,i_rf);
            double const f_rf = LineSegmentMapData_get_frequency_rf(el,i_rf);
            double const lag_rf = LineSegmentMapData_get_lag_rf(el,i_rf);

            if (f_rf == 0) continue;


    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

                double const K_FACTOR = ( ( double )2.0 *PI ) / C_LIGHT;
                double const   beta0  = LocalParticle_get_beta0(part);
                double const   zeta   = LocalParticle_get_zeta(part);
                double const   q      = fabs(LocalParticle_get_q0(part))
                                        * LocalParticle_get_charge_ratio(part);
                double const   tau    = zeta / beta0;
                double const   phase  = DEG2RAD  * lag_rf - K_FACTOR * f_rf * tau;
                double const energy   = q * v_rf * sin(phase);
                LocalParticle_add_to_energy(part, energy, 1);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

        }


    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

            double const gamma0 = LocalParticle_get_gamma0(part);
            double const eta = alfp - 1.0 / (gamma0 * gamma0);
            LocalParticle_add_to_zeta(part,
                -0.5 * eta * slippage_length * LocalParticle_get_delta(part));

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

    }
    else if (mode_flag == 3){

        double const alfp =
            LineSegmentMapData_get_momentum_compaction_factor(el);
        double const slippage_length =
            LineSegmentMapData_get_slippage_length(el);

        // Assume there is only one RF term (checked in the Python code)
        double const v_rf = LineSegmentMapData_get_voltage_rf(el,0);
        double const f_rf = LineSegmentMapData_get_frequency_rf(el,0);


    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

            double const gamma0 = LocalParticle_get_gamma0(part);
            double const beta0 = LocalParticle_get_beta0(part);
            double const q0 = LocalParticle_get_q0(part);
            double const mass0 = LocalParticle_get_mass0(part);
            double const eta = alfp - 1.0 / (gamma0 * gamma0);
            double const E0 = mass0 * gamma0;

            double const qs = sqrt(q0 * fabs(eta) * slippage_length * f_rf * v_rf
                        / (2 * PI * beta0 * beta0 * beta0 * E0 * C_LIGHT));
            double const bets = eta * slippage_length / (2 * PI * qs);

            double const sin_s = sin(2 * PI * qs);
            double const cos_s = cos(2 * PI * qs);

            double const new_zeta = cos_s * LocalParticle_get_zeta(part) - bets * sin_s * LocalParticle_get_pzeta(part);
            double const new_pzeta = sin_s * LocalParticle_get_zeta(part) / bets + cos_s * LocalParticle_get_pzeta(part);

            LocalParticle_set_zeta(part, new_zeta);
            LocalParticle_update_pzeta(part, new_pzeta);


//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }


    }

}

 static inline
void uncorrelated_radiation_damping(LocalParticle *part0,
            double const damping_factor_x, double const damping_factor_y,
            double const damping_factor_s){


    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

        LocalParticle_scale_x(part,damping_factor_x);
        LocalParticle_scale_px(part,damping_factor_x);
        LocalParticle_scale_y(part,damping_factor_y);
        LocalParticle_scale_py(part,damping_factor_y);
        LocalParticle_scale_zeta(part,damping_factor_s);
        double delta = LocalParticle_get_delta(part);
        delta *= damping_factor_s;
        LocalParticle_update_delta(part,delta);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

}

 static inline
void energy_and_reference_increments(LocalParticle *part0,
    double const energy_increment, double const energy_ref_increment){


    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

        // Change energy without change of reference momentum
        if (energy_increment !=0){
        LocalParticle_add_to_energy(part, energy_increment, 1);
        }

        // Change energy reference
        // In the transverse plane de change is smoothed, i.e.
        // both the position and the momentum are scaled,
        // rather than only the momentum.
        if (energy_ref_increment != 0){
            double const new_energy0 = LocalParticle_get_mass0(part)
                *LocalParticle_get_gamma0(part) + energy_ref_increment;
            double const new_p0c = sqrt(new_energy0*new_energy0
            -LocalParticle_get_mass0(part)*LocalParticle_get_mass0(part));
            double const new_beta0 = new_p0c / new_energy0;
            double const new_gamma0 = new_energy0 / LocalParticle_get_mass0(part);
            double const geo_emit_factor = sqrt(LocalParticle_get_beta0(part)
                    *LocalParticle_get_gamma0(part)/new_beta0/new_gamma0);
            LocalParticle_update_p0c(part,new_p0c);
            LocalParticle_scale_x(part,geo_emit_factor);
            LocalParticle_scale_px(part,geo_emit_factor);
            LocalParticle_scale_y(part,geo_emit_factor);
            LocalParticle_scale_py(part,geo_emit_factor);
        }

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }


}

 static inline
void uncorrelated_gaussian_noise(LocalParticle *part0,
                    double const gauss_noise_ampl_x,
                    double const gauss_noise_ampl_px,
                    double const gauss_noise_ampl_y,
                    double const gauss_noise_ampl_py,
                    double const gauss_noise_ampl_zeta,
                    double const gauss_noise_ampl_delta){


    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

            double r = RandomNormal_generate(part);
            LocalParticle_add_to_x(part,r*gauss_noise_ampl_x);
            r = RandomNormal_generate(part);
            LocalParticle_add_to_px(part,r*gauss_noise_ampl_px);
            r = RandomNormal_generate(part);
            LocalParticle_add_to_y(part,r*gauss_noise_ampl_y);
            r = RandomNormal_generate(part);
            LocalParticle_add_to_py(part,r*gauss_noise_ampl_py);
            r = RandomNormal_generate(part);
            LocalParticle_add_to_zeta(part,r*gauss_noise_ampl_zeta);
            r = RandomNormal_generate(part);
            double delta = LocalParticle_get_delta(part);
            delta += r*gauss_noise_ampl_delta;
            LocalParticle_update_delta(part,delta);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }


}

 static inline
void LineSegmentMap_track_local_particle(LineSegmentMapData el, LocalParticle* part0){


    remove_closed_orbit(part0,
        LineSegmentMapData_get_x_ref(el, 0),
        LineSegmentMapData_get_px_ref(el, 0),
        LineSegmentMapData_get_y_ref(el, 0),
        LineSegmentMapData_get_py_ref(el, 0));

    remove_dispersion(part0,
        LineSegmentMapData_get_dx(el, 0),
        LineSegmentMapData_get_dpx(el, 0),
        LineSegmentMapData_get_dy(el, 0),
        LineSegmentMapData_get_dpy(el, 0));

    transverse_motion(part0,
        LineSegmentMapData_get_qx(el),
        LineSegmentMapData_get_qy(el),
        LineSegmentMapData_get_dqx(el),
        LineSegmentMapData_get_dqy(el),
        LineSegmentMapData_get_detx_x(el),
        LineSegmentMapData_get_detx_y(el),
        LineSegmentMapData_get_dety_x(el),
        LineSegmentMapData_get_dety_y(el),
        LineSegmentMapData_get_alfx(el, 0),
        LineSegmentMapData_get_betx(el, 0),
        LineSegmentMapData_get_alfy(el, 0),
        LineSegmentMapData_get_bety(el, 0),
        LineSegmentMapData_get_alfx(el, 1),
        LineSegmentMapData_get_betx(el, 1),
        LineSegmentMapData_get_alfy(el, 1),
        LineSegmentMapData_get_bety(el, 1));

    longitudinal_motion(part0, el);

    energy_and_reference_increments(part0,
        LineSegmentMapData_get_energy_increment(el),
        LineSegmentMapData_get_energy_ref_increment(el));

    if (LineSegmentMapData_get_uncorrelated_rad_damping(el) == 1){
        uncorrelated_radiation_damping(part0,
            LineSegmentMapData_get_damping_factor_x(el),
            LineSegmentMapData_get_damping_factor_y(el),
            LineSegmentMapData_get_damping_factor_s(el));
    }

    if (LineSegmentMapData_get_uncorrelated_gauss_noise(el) == 1){
        uncorrelated_gaussian_noise(part0,
            LineSegmentMapData_get_gauss_noise_ampl_x(el),
            LineSegmentMapData_get_gauss_noise_ampl_px(el),
            LineSegmentMapData_get_gauss_noise_ampl_y(el),
            LineSegmentMapData_get_gauss_noise_ampl_py(el),
            LineSegmentMapData_get_gauss_noise_ampl_zeta(el),
            LineSegmentMapData_get_gauss_noise_ampl_delta(el));
    }

    add_dispersion(part0,
        LineSegmentMapData_get_dx(el, 1),
        LineSegmentMapData_get_dpx(el, 1),
        LineSegmentMapData_get_dy(el, 1),
        LineSegmentMapData_get_dpy(el, 1));

    add_closed_orbit(part0,
        LineSegmentMapData_get_x_ref(el, 1),
        LineSegmentMapData_get_px_ref(el, 1),
        LineSegmentMapData_get_y_ref(el, 1),
        LineSegmentMapData_get_py_ref(el, 1));

    double const length = LineSegmentMapData_get_length(el);

    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

        LocalParticle_add_to_s(part, length);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

}

#endif


             
            void LineSegmentMap_track_particles(
               LineSegmentMapData el,

                             ParticlesData particles,

                             int64_t flag_increment_at_element,
                  int8_t* io_buffer){
//            const int num_threads = omp_get_max_threads();                                 //only_for_context cpu_openmp
//            const int64_t capacity = ParticlesData_get__capacity(particles);               //only_for_context cpu_openmp
//            const int64_t chunk_size = (capacity + num_threads - 1)/num_threads; // ceil division  //only_for_context cpu_openmp
//            #pragma omp parallel for                                                       //only_for_context cpu_openmp
//            for (int64_t batch_id = 0; batch_id < num_threads; batch_id++) {               //only_for_context cpu_openmp
                LocalParticle lpart;
                lpart.io_buffer = io_buffer;
//                int64_t part_id = batch_id * chunk_size;                                       //only_for_context cpu_openmp
//                int64_t end_id = (batch_id + 1) * chunk_size;                                  //only_for_context cpu_openmp
//                if (end_id > capacity) end_id = capacity;                                      //only_for_context cpu_openmp
    
                int64_t part_id = 0;                    //only_for_context cpu_serial
//                int64_t part_id = blockDim.x * blockIdx.x + threadIdx.x; //only_for_context cuda
//                int64_t part_id = get_global_id(0);                    //only_for_context opencl
                int64_t end_id = 0; // unused outside of openmp  //only_for_context cpu_serial cuda opencl
    
                int64_t part_capacity = ParticlesData_get__capacity(particles);
                if (part_id<part_capacity){
                    Particles_to_LocalParticle(particles, &lpart, part_id, end_id);
                    if (check_is_active(&lpart)>0){
              LineSegmentMap_track_local_particle(el, &lpart);

                    }
                    if (check_is_active(&lpart)>0 && flag_increment_at_element){
                            increment_at_element(&lpart, 1);
                    }
                }
//            } //only_for_context cpu_openmp
            
            // On OpenMP we want to additionally by default reorganize all
            // the particles.
//            #ifndef XT_OMP_SKIP_REORGANIZE                             //only_for_context cpu_openmp
//            LocalParticle lpart;                                       //only_for_context cpu_openmp
//            lpart.io_buffer = io_buffer;                               //only_for_context cpu_openmp
//            Particles_to_LocalParticle(particles, &lpart, 0, capacity);//only_for_context cpu_openmp
//            check_is_active(&lpart);                                   //only_for_context cpu_openmp
//            #endif                                                     //only_for_context cpu_openmp
        }

#ifndef XOBJ_TYPEDEF_EverestEngineData
#define XOBJ_TYPEDEF_EverestEngineData
typedef   struct EverestEngineData_s * EverestEngineData;
 static inline EverestEngineData EverestEngineData_getp(EverestEngineData  obj){
  int64_t offset=0;
  return (EverestEngineData)(( char*) obj+offset);
}
#endif
// copyright ############################### #
// This file is part of the Xcoll Package.   #
// Copyright (c) CERN, 2023.                 #
// ######################################### #

#ifndef XCOLL_EVEREST_ENGINE_H
#define XCOLL_EVEREST_ENGINE_H

 static inline
void Drift_single_particle_4d(LocalParticle* part, double length){
    double zeta = LocalParticle_get_zeta(part);
    Drift_single_particle(part, length);
    LocalParticle_set_zeta(part, zeta);
}

 static inline
double drift_zeta_single(double rvv, double xp, double yp, double length){
    double const rv0v = 1./rvv;
    double const dzeta = 1 - rv0v * (1. + (pow(xp,2.) + pow(yp,2.))/2.);
    return length * dzeta;
}

 
void RandomRutherford_set_by_xcoll_material(RandomRutherfordData ran, GeneralMaterialData material){
    double const zatom    = GeneralMaterialData_get_Z(material);
    double const emr      = GeneralMaterialData_get_nuclear_radius(material);
    double const hcut     = GeneralMaterialData_get_hcut(material);
    double const lcut     = 0.0009982;
    double const c = 0.8561e3; // TODO: Where tha fuck does this come from??
    double A = pow(zatom,2);
    double B = c*pow(emr,2);
    RandomRutherford_set(ran, A, B, lcut, hcut);
}

#endif /* XCOLL_EVEREST_ENGINE_H */

#ifndef XCOLL_EVEREST_SCAT_INIT_H
#define XCOLL_EVEREST_SCAT_INIT_H
#include <stdlib.h>
#include <math.h>
#include <stdio.h>


struct ScatteringParameters {
    double cprob[6];
    double xintl;
    double bn;
    double ecmsq;
    double xln15s;
    double bpp;
};


struct ScatteringParameters calculate_scattering(double p0, MaterialData material) {

    struct ScatteringParameters scat;

    // Material properties
    double const anuc  = MaterialData_get_A(material);
    double const rho   = MaterialData_get_density(material);
    double const bnref = MaterialData_get_nuclear_elastic_slope(material);
    double csref[6];
    csref[0]           = MaterialData_get_cross_section(material, 0);
    csref[1]           = MaterialData_get_cross_section(material, 1);
    csref[5]           = MaterialData_get_cross_section(material, 5);

    double csect[6];
    
    // Constants 
    double pptref = 0.04;
    double freeco = 1.618;
    double pmap   = 938.271998;
    double fnavo  = 6.02214076e23;

    scat.ecmsq = (2*(pmap*1.0e-3)) * p0;
    scat.xln15s = log(0.15*scat.ecmsq);
    // Claudia Fit from COMPETE collaboration points "arXiv:hep-ph/0206172v1 19Jun2002"
    double pptot = 0.041084 - 0.0023302*log(scat.ecmsq) + 0.00031514*pow(log(scat.ecmsq),2);
    // Claudia used the fit from TOTEM for ppel (in barn)
    double ppel = (11.7-1.59*log(scat.ecmsq)+0.134*pow(log(scat.ecmsq),2))/1e3;                      // TODO /1.e3
    // Claudia updated SD cross that cointains renormalized pomeron flux (in barn)
    double ppsd = (4.3+0.3*log(scat.ecmsq))/1e3;

    // Claudia new fit for the slope parameter with new data at sqrt(s)=7 TeV from TOTEM
    scat.bpp = 7.156 + 1.439*log(sqrt(scat.ecmsq));

    // freep: number of nucleons involved in single scattering
    double freep = freeco * pow(anuc,(1./3.));

    // compute pp and pn el+single diff contributions to cross-section
    // (both added : quasi-elastic or qel later)
    csect[3] = freep * ppel;
    csect[4] = freep * ppsd;

    // correct TOT-CSec for energy dependence of qel
    // TOT CS is here without a Coulomb contribution
    csect[0] = csref[0] + freep * (pptot - pptref);
    scat.bn = bnref * csect[0] / csref[0];

    // also correct inel-CS
    csect[1] = csref[1] * csect[0] / csref[0];

    // Nuclear Elastic is TOT-inel-qel ( see definition in RPP)
    csect[2] = csect[0] - csect[1] - csect[3] - csect[4];
    csect[5] = csref[5];

    // Now add Coulomb
    csect[0] += csect[5];

    // Interaction length in meter
    scat.xintl = (1.0e-2*anuc)/(((fnavo * rho)*csect[0])*1e-24);

    // Filling CProb with cumulated normalised Cross-sections
    int i;
    scat.cprob[0] = 0;
    for (i=1; i<5; ++i){
        scat.cprob[i] = scat.cprob[i-1] + csect[i]/csect[0];
    }
    scat.cprob[5] = 1;
    
    return scat;

}


#endif /* XCOLL_EVEREST_SCAT_INIT_H */

// copyright ############################### #
// This file is part of the Xcoll Package.   #
// Copyright (c) CERN, 2023.                 #
// ######################################### #

#ifndef XCOLL_EVEREST_JAW_H
#define XCOLL_EVEREST_JAW_H
#include <math.h>
#include <stdio.h>


 static inline
double iterat(double a, double b, double dh, double s) {
    double ds = s;
    while (1) {
        ds = ds*0.5;
        if (pow(s,3) < pow((a+b*s),2)) {
            s = s+ds;
        } else {
            s = s-ds;
        }
        if (ds < dh) {
            break;
        } else {
            continue;
        }
    }
    return s;
}


 static inline
double soln3(double a, double b, double dh, double smax) {
    double s;
    if (b == 0) {
        s = pow(a,0.6666666666666667);
        if (s > smax) {
            s = smax;
        }
        return s;
    }
    if (a == 0) {    
        if (b > 0) {
            s = pow(b,2);
        } else {
            s = 0;
        }
        if (s > smax) {
            s = smax;
        }
        return s;
    }
    if (b > 0) {
        if (pow(smax,3) <= pow((a + b*smax),2)) {
            s = smax;
            return s;
        } else {
            s = smax*0.5;
            s = iterat(a,b,dh,s);
        }
    } else {
        double c = (-1*a)/b;
        if (smax < c) {
            if ((pow(smax,3)) <= pow((a + b*smax),2)) {
                s = smax;
                return s;
            } else {
                s = smax*0.5;
                s = iterat(a,b,dh,s);
            }
        } else {
            s = c*0.5;
            s = iterat(a,b,dh,s);
        }
    }
    return s;
}


 static inline
double* scamcs(LocalParticle* part, double x0, double xp0, double s) {
    // double x0  = *xx;
    // double xp0 = *xxp;
    double r2 = 0;
    double v1 = 0;
    double v2 = 0;
    double* result = (double*)malloc(2 * sizeof(double));

    while (1) {
        v1 = 2*RandomUniform_generate(part) - 1;
        v2 = 2*RandomUniform_generate(part) - 1;
        r2 = pow(v1,2) + pow(v2,2);

        if(r2 < 1) {
            break;
        }
    }

    double a   = sqrt((-2*log(r2))/r2);
    double z1  = v1*a;
    double z2  = v2*a;
    double ss  = sqrt(s);
    double sss = 1 + 0.038*log(s);

    result[0] = x0  + s*(xp0 + ((0.5*ss)*sss)*(z2 + z1*0.577350269));
    result[1] = xp0 + (ss*z2)*sss;
    return result;
}


 static inline
double* mcs(LocalParticle* part, MaterialData material, double zlm1, double p, double x, double xp, double z, double zp) {

    double const radl     = MaterialData_get_radiation_length(material);
    double s;
    double theta = 13.6e-3/p;
    double h   = 0.001;
    double dh  = 0.0001;
    double bn0 = 0.4330127019;
    double rlen0 = zlm1/radl;
    double rlen  = rlen0;
    double* result = (double*)malloc(5 * sizeof(double));

    x     = (x/theta)/radl;
    xp    = xp/theta;
    z     = (z/theta)/radl;
    zp    = zp/theta;

    while (1) {
        double ae = bn0 * x;
        double be = bn0 * xp;
        // #######################################
        // ae = np.array(ae, dtype=np.double64)
        // be = np.array(be, dtype=np.double64)
        // dh = np.array(dh, dtype=np.double64)
        // rlen = np.array(rlen, dtype=np.double64)
        // s = np.array(s, dtype=np.double64)
        // #######################################
        s = soln3(ae,be,dh,rlen);
        if (s < h) {
            s = h;
        }
        double* res = scamcs(part, x,xp,s);
        x  = res[0];
        xp = res[1];
        free(res);
        if (x <= 0) {
            s = (rlen0-rlen)+ s;
            break; // go to 20
        }
        if ((s + dh) >= rlen) {
            s = rlen0;
            break; // go to 20
        }
        // go to 10
        rlen = rlen - s;
    }

    double* res = scamcs(part, z,zp,s);
    z  = res[0];
    zp = res[1];
    free(res);

    result[0]  = s*radl;
    result[1]  = (x*theta)*radl;
    result[2]  = xp*theta;
    result[3]  = (z*theta)*radl;
    result[4]  = zp*theta;
    return result;
}


 static inline
double* tetat(LocalParticle* part, double t, double p) {
    double teta = sqrt(t)/p;
    double va = 0;
    double vb  = 0;
    double va2 = 0;
    double vb2 = 0;
    double r2  = 0;
    double* result = (double*)malloc(2 * sizeof(double));
    
    while (1) {
        va  = 2*RandomUniform_generate(part) - 1;
        vb  = RandomUniform_generate(part);
        va2 = pow(va,2);
        vb2 = pow(vb,2);
        r2  = va2 + vb2;
        if(r2 < 1) {
            break;
        }
    }
        
    result[0] = (teta*((2*va)*vb))/r2;
    result[1]  = (teta*(va2 - vb2))/r2;
    return result;
}


 static inline
double* gettran(RandomRutherfordData rng, LocalParticle* part, double inter, double p, struct ScatteringParameters scat) {

    double* res = (double*)malloc(2 * sizeof(double));

    // Neither if-statements below have an else, so defaulting function return to zero.
    double result = 0;

    if (inter==2) { // Nuclear Elastic
        result = RandomExponential_generate(part)/scat.bn;
    } else if (inter==3) { // pp Elastic
        result = RandomExponential_generate(part)/scat.bpp;
    } else if (inter==4) { // Single Diffractive
        double xm2 = exp(RandomUniform_generate(part) * scat.xln15s);
        double bsd = 0;
        p = p * (1 - xm2/scat.ecmsq);
    
        if (xm2 < 2) {
            bsd = 2 * scat.bpp;
        } else if ((xm2 >= 2) & (xm2 <= 5)) {
            bsd = ((106.0 - 17.0*xm2)*scat.bpp)/36.0;
        } else {
            bsd = (7*scat.bpp)/12.0;
        }
        result = RandomExponential_generate(part)/bsd;
    } else if (inter==5) { // Coulomb
        result = RandomRutherford_generate(rng, part);
    }

    res[0] = result;
    res[1] = p;
    return res;
}


 static inline
double calcionloss(LocalParticle* part, double p, double rlen, MaterialData material) {

    double const zatom    = MaterialData_get_Z(material);
    double const anuc     = MaterialData_get_A(material);
    double const rho      = MaterialData_get_density(material);
    double const exenergy = MaterialData_get_excitation_energy(material);
    
    double k = 0.307075;  // Constant in front bethe-bloch [mev g^-1 cm^2]
    double pmae = 0.510998902;
    double pmap = 938.271998;

    double mom    = p*1.0e3; //[GeV/c] -> [MeV/c]
    double enr    = pow(mom*mom + pmap*pmap,0.5); //[MeV]
    double gammar = enr/pmap;
    double betar  = mom/enr;
    double bgr    = betar*gammar;
    double kine   = 2*pmae*bgr*bgr;

    // Mean excitation energy
    double exEn = exenergy*1.0e3; // [MeV]

    // tmax is max energy loss from kinematics
    double tmax = kine/(1 + (2*gammar)*(pmae/pmap) + pow((pmae/pmap),2.)); // [MeV]

    // Plasma energy - see PDG 2010 table 27.1
    double plen = pow(((rho*zatom)/anuc),0.5)*28.816e-6; // [MeV]

    // Calculate threshold energy
    // Above this threshold, the cross section for high energy loss is calculated and then
    // a random number is generated to determine if tail energy loss should be applied, or only mean from Bethe-Bloch
    // below threshold, only the standard Bethe-Bloch is used (all particles get average energy loss)

    // thl is 2*width of Landau distribution (as in fig 27.7 in PDG 2010). See Alfredo's presentation for derivation
    double thl = ((((4*(k*zatom))*rlen)*1.0e2)*rho)/(anuc*pow(betar,2)); // [MeV]

    // Bethe-Bloch mean energy loss
    double enlo = ((k*zatom)/(anuc*pow(betar,2))) * (0.5*log((kine*tmax)/(exEn*exEn)) - pow(betar,2) - log(plen/exEn) - log(bgr) + 0.5);
    enlo = ((enlo*rho)*1.0e-1)*rlen; // [GeV]

    // Threshold Tt is Bethe-Bloch + 2*width of Landau distribution
    double Tt = enlo*1.0e3 + thl; // [MeV]

    // Cross section - see Alfredo's presentation for derivation
    double cs_tail = ((k*zatom)/(anuc*pow(betar,2))) * (0.5*((1/Tt)-(1/tmax)) - (log(tmax/Tt)*pow(betar,2))/(2*tmax) + (tmax-Tt)/((4*pow(gammar,2))*pow(938.271998,2)));

    // Probability of being in tail: cross section * density * path length
    double prob_tail = ((cs_tail*rho)*rlen)*1.0e2;

    // Determine based on random number if tail energy loss occurs.
    if (RandomUniform_generate(part) < prob_tail) {
        enlo = ((k*zatom)/(anuc*pow(betar,2))) * (0.5*log((kine*tmax)/(exEn*exEn)) - pow(betar,2) - log(plen/exEn) - log(bgr) + 0.5 + pow(tmax,2)/((8*pow(gammar,2))*pow(938.271998,2)));
        enlo = (enlo*rho)*1.0e-1; // [GeV/m]
    } else {
        // If tail energy loss does not occur, just use the standard Bethe-Bloch
        enlo = enlo/rlen;  // [GeV/m]
    }  
    return enlo;
}


 static inline
int ichoix(LocalParticle* part, struct ScatteringParameters scat) {

    double aran = RandomUniform_generate(part);
    int i;
    for (i = 0; i < 5; ++i) {
        if (aran <= scat.cprob[i]) {
            break;
        }
    }
    return i;
}


 static inline
double* jaw(LocalParticle* part, MaterialData material, RandomRutherfordData rng, struct ScatteringParameters scat,
            double p, double zlm, double x, double xp, double z, double zp) {

    double* result = (double*)malloc(7 * sizeof(double));
    double s;
    double nabs = 0;
    double rlen = zlm;
    double m_dpodx = 0.;
    double t;
    double tx; 
    double tz;

    // Do a step for a point-like interaction.
    // Get monte-carlo interaction length.
    while (1) {
        double zlm1 = scat.xintl*RandomExponential_generate(part);
                        
        // If the monte-carlo interaction length is longer than the
        // remaining collimator length, then put it to the remaining
        // length, do multiple coulomb scattering and return.
        // LAST STEP IN ITERATION LOOP
        if (zlm1 > rlen) { 
            zlm1 = rlen;
            double* res = mcs(part, material, zlm1, p, x, xp, z, zp);
            s = res[0];
            x = res[1];
            xp = res[2];
            z = res[3];
            zp = res[4];
            free(res);

            s = (zlm-rlen)+s;
            m_dpodx = calcionloss(part, p, rlen, material);  // DM routine to include tail
            p = p-m_dpodx*s;
            break;
        }
        // Otherwise do multi-coulomb scattering.
        // REGULAR STEP IN ITERATION LOOP
        double* res1 = mcs(part, material, zlm1, p, x, xp, z, zp);
        s = res1[0];
        x = res1[1];
        xp = res1[2];
        z = res1[3];
        zp = res1[4];
        free(res1);

        // Check if particle is outside of collimator (X.LT.0) after
        // MCS. If yes, calculate output longitudinal position (s),
        // reduce momentum (output as dpop) and return.
        // PARTICLE LEFT COLLIMATOR BEFORE ITS END.

        if(x <= 0) {
            s = (zlm-rlen)+s;
            m_dpodx = calcionloss(part, p, rlen, material);
            p = p-m_dpodx*s;
            break;
        }

        // Check whether particle is absorbed. If yes, calculate output
        // longitudinal position (s), reduce momentum (output as dpop)
        // and return.
        // PARTICLE WAS ABSORBED INSIDE COLLIMATOR DURING MCS.

        int inter = ichoix(part, scat);
        nabs = inter;
        if (inter == 1) {
            s = (zlm-rlen)+zlm1;
            m_dpodx = calcionloss(part, p, rlen, material);
            p = p-m_dpodx*s;
            break;
        }


        // Now treat the other types of interaction, as determined by ICHOIX:

        // Nuclear-Elastic:          inter = 2
        // pp Elastic:               inter = 3
        // Single-Diffractive:       inter = 4    (changes momentum p)
        // Coulomb:                  inter = 5

        // Gettran returns some monte carlo number, that, as I believe, gives the rms transverse momentum transfer.

        double* res2 = gettran(rng, part, inter, p, scat);
        t = res2[0];
        p = res2[1];
        free(res2);

        // Tetat calculates from the rms transverse momentum transfer in
        // monte-carlo fashion the angle changes for x and z planes. The
        // angle change is proportional to SQRT(t) and 1/p, as expected.

        double* res3 = tetat(part,t,p);
        tx = res3[0]; 
        tz = res3[1];
        free(res3);

        // Apply angle changes
        xp = xp + tx;
        zp = zp + tz;

        // Treat single-diffractive scattering.
        if(inter == 4) {
            // added update for s
            s    = (zlm-rlen)+zlm1;
        }

        // Calculate the remaining interaction length and close the iteration loop.
        rlen = rlen-zlm1;
    }

    result[0] = p;
    result[1] = nabs;
    result[2] = s;
    result[3] = x;
    result[4] = xp;
    result[5] = z;
    result[6] = zp;
    return result;
}  
  

#endif /* XCOLL_EVEREST_JAW_H */

// copyright ############################### #
// This file is part of the Xcoll Package.   #
// Copyright (c) CERN, 2023.                 #
// ######################################### #

#ifndef XCOLL_EVEREST_SCAT_H
#define XCOLL_EVEREST_SCAT_H
#include <math.h>
#include <stdlib.h>
#include <stdio.h>


// TODO:
//    Use drift function from xtrack Drift element (call its C function)
//    Use rotation function from xtrack XYRotation element (call its C function)

 static inline
void scatter(LocalParticle* part, double length, MaterialData material, RandomRutherfordData rng, struct ScatteringParameters scat,
            double cRot, double sRot, double c_aperture, double c_offset, double c_tilt0, double c_tilt1, double side){

    // Store initial coordinates for updating later
    double const rpp_in  = LocalParticle_get_rpp(part);
    double const rvv_in  = LocalParticle_get_rvv(part);
    double const e0      = LocalParticle_get_energy0(part) / 1e9; // Reference energy in GeV
    double const beta0   = LocalParticle_get_beta0(part);
    double const ptau_in = LocalParticle_get_ptau(part);
    double const x_in2    = LocalParticle_get_x(part);
    double const px_in2   = LocalParticle_get_px(part);
    double const y_in2    = LocalParticle_get_y(part);
    double const py_in2   = LocalParticle_get_py(part);
    double p0 = LocalParticle_get_p0c(part) / 1e9;

    double x_in  = LocalParticle_get_x(part);
    double xp_in = LocalParticle_get_px(part)*rpp_in;
    double y_in  = LocalParticle_get_y(part);
    double yp_in = LocalParticle_get_py(part)*rpp_in;

    // TODO: missing correction due to m/m0 (but also wrong in xpart...)
    double p_in = p0*ptau_in + e0; // energy, not momentum, in GeV

    // Status flags
    int val_part_hit = 0;
    int val_part_abs = 0;
    // int val_part_impact = -1;
    // double val_part_indiv = -1.;
    // double val_part_linteract = -1.;
    // double val_nabs_type = 0;
    double val_linside = 0;

    double nhit   = 0;
    double nabs   = 0;

    double x = x_in;
    double xp = xp_in;
    double z = y_in;
    double zp = yp_in;
    double s_in = 0;   // wrt collimator entrance
    // double sp = 0;
    double tiltangle = 0.;

    double mirror = 1.;

    // Transform particle coordinates to get into collimator coordinate  system
    // First do rotation into collimator frame
    double const cRRot = cRot;
    double const sRRot = -sRot;
    x  =  x_in*cRot + sRot*y_in;
    z  =  y_in*cRot - sRot*x_in;
    xp = xp_in*cRot + sRot*yp_in;
    zp = yp_in*cRot - sRot*xp_in;

    // Output variables
    double x_out = x_in;
    double y_out = y_in;
    double xp_out = xp_in;
    double yp_out = yp_in;
    double p_out = p_in;
    double s_out = 0;


    // For one-sided collimators consider only positive X. For negative X jump to the next particle
    if (side==0 || (side==1 && x>=0.) || (side==2 && x<=0.)) {

        // Now mirror at the horizontal axis for negative X offset
        if (x < 0) {
            mirror    = -1;
            tiltangle = -1*c_tilt1;
        } else {
            mirror    = 1;
            tiltangle = c_tilt0;
        }
        x  = mirror*x;
        xp = mirror*xp;

        // Shift with opening and offset
        x = (x - c_aperture/2.) - mirror*c_offset;

        // Include collimator tilt
        if (tiltangle > 0.) {
            xp = xp - tiltangle;
        }
        if (tiltangle < 0.) {
            x  = x + sin(tiltangle) * length;
            xp = xp - tiltangle;
        }

        // particle passing above the jaw are discarded => take new event
        // entering by the face, shorten the length (zlm) and keep track of
        // entrance longitudinal coordinate (keeps) for histograms

        // The definition is that the collimator jaw is at x>=0.

        // 1) Check whether particle hits the collimator
        double zlm; 

        if (x >= 0.) {
        // Particle hits collimator and we assume interaction length ZLM equal
        // to collimator length (what if it would leave collimator after
        // small length due to angle???)
            zlm  = length;
            // val_part_impact = x;    // store these in impact table, as interaction type 'hitting collimator' or something like that. Do with correct (lab frame) coordinates, hence do stuff in geometry branch
            // val_part_indiv  = xp;
        } else if (xp <= 0.) {
        // Particle does not hit collimator. Interaction length ZLM is zero.
            zlm = 0.;
        } else {
        // Calculate s-coordinate of interaction point
            double s = -x/xp;

            if (s < length) {
                zlm = length - s;
                // val_part_impact = 0.;
                // val_part_indiv  = xp;
            } else {
                zlm = 0.;
            }

        }
        // First do the drift part
        // DRIFT PART
        double drift_length = length - zlm;
        if (drift_length > 0) {
            x  = x  + xp * drift_length;
            z  = z  + zp * drift_length;
            s_in = drift_length;
        }
        // Now do the scattering part
        if (zlm > 0.) {
            if (!val_linside) {
            // first time particle hits collimator: entering jaw
                val_linside = 1;
            }
            nhit = nhit + 1;

            double* jaw_result = jaw(part, material, rng, scat,
                            p_in,
                            zlm,
                            x,
                            xp,
                            z,
                            zp);

            p_out = jaw_result[0];
            nabs = jaw_result[1];
            s_out = jaw_result[2];
            x = jaw_result[3];
            xp = jaw_result[4];
            z = jaw_result[5];
            zp = jaw_result[6];
            free(jaw_result);

            // val_nabs_type = nabs;
            val_part_hit  = 1;

            // Writeout should be done for both inelastic and single diffractive. doing all transformations
            // in x_flk and making the set to 99.99 mm conditional for nabs=1
            if (nabs == 1 || nabs == 4) {
            // Transform back to lab system for writeout.
            // keep x,y,xp,yp unchanged for continued tracking, store lab system variables in x_flk etc

            // Finally, the actual coordinate change to 99 mm
                if (nabs == 1) {
                    x = 99.99e-3;
                    z = 99.99e-3;
                    // val_part_linteract = zlm;
                    val_part_abs = 1;
                // Collimator jaw interaction
                }
            }

            if (nabs != 1. && zlm > 0.) {
            // Do the rest drift, if particle left collimator early
                drift_length = (length-(s_out+s_in));

                if (drift_length > 1.0e-15) {
                    val_linside = 0;
                    x  = x  + xp * drift_length;
                    z  = z  + zp * drift_length;
                    s_out = s_in + drift_length;
                }
                // val_part_linteract = zlm - drift_length;
            }

        }

        // Transform back to particle coordinates with opening and offset
        // Include collimator tilt
        if (tiltangle > 0) {
            x  = x  + tiltangle*length;
            xp = xp + tiltangle;
        } else if (tiltangle < 0) {
            x  = x  + tiltangle*length;
            xp = xp + tiltangle;
            x  = x  - sin(tiltangle) * length;
        }

        // Transform back to particle coordinates with opening and offset
        x   = (x + c_aperture/2) + mirror*c_offset;

        // Now mirror at the horizontal axis for negative X offset
        x  = mirror * x;
        xp = mirror * xp;

        // Last do rotation into collimator frame
        x_out  =  x*cRRot +  z*sRRot;
        y_out  =  z*cRRot -  x*sRRot;
        xp_out = xp*cRRot + zp*sRRot;
        yp_out = zp*cRRot - xp*sRRot;

        LocalParticle_set_x(part, x_out);
        LocalParticle_set_px(part, xp_out/rpp_in);
        LocalParticle_set_y(part, y_out);
        LocalParticle_set_py(part, yp_out/rpp_in);

        if (val_part_abs==0){
            LocalParticle_add_to_s(part, length);
        }

    } else {
        Drift_single_particle_4d(part, length);
    }

    double energy_out = p_out;       //  Cannot assign energy directly to LocalParticle as it would update dependent variables, but needs to be corrected first!

    // Update energy    ---------------------------------------------------
    // Only particles that hit the jaw and survived need to be updated
    if (val_part_hit>0 && val_part_abs==0){
        double ptau_out = (energy_out - e0) / (e0 * beta0);
        LocalParticle_update_ptau(part, ptau_out);
    }

    // Update 4D coordinates    -------------------------------------------
    // Absorbed particles get their coordinates set to the entrance of collimator
    if (val_part_abs>0){
        LocalParticle_set_x(part, x_in2);
        LocalParticle_set_px(part, px_in2);
        LocalParticle_set_y(part, y_in2);
        LocalParticle_set_py(part, py_in2);
    }

    // Update longitudinal coordinate zeta    -----------------------------
    // Absorbed particles keep coordinates at the entrance of collimator, others need correcting:
    // Non-hit particles are just drifting (zeta not yet drifted in K2, so do here)
    if (val_part_hit==0){
        LocalParticle_add_to_zeta(part, drift_zeta_single(rvv_in, px_in2*rpp_in, py_in2*rpp_in, length) );
    }
    // Hit and survived particles need correcting:
    if (val_part_hit>0 && val_part_abs==0){
        double px  = LocalParticle_get_px(part);
        double py  = LocalParticle_get_py(part);
        double rvv = LocalParticle_get_rvv(part);
        double rpp = LocalParticle_get_rpp(part);
        // First we drift half the length with the old angles:
        LocalParticle_add_to_zeta(part, drift_zeta_single(rvv_in, px_in2*rpp_in, py_in2*rpp_in, length/2) );
        // then half the length with the new angles:
        LocalParticle_add_to_zeta(part, drift_zeta_single(rvv, px*rpp, py*rpp, length/2) );
    }

    // Update state    ----------------------------------------------------
    if (val_part_abs > 0){
        LocalParticle_set_state(part, XC_LOST_ON_EVEREST);
    }

    return;

}

#endif /* XCOLL_EVEREST_SCAT_H */

// copyright ############################### #
// This file is part of the Xcoll Package.   #
// Copyright (c) CERN, 2023.                 #
// ######################################### #

#ifndef XCOLL_EVEREST_CRYSTAL_INTERACT_H
#define XCOLL_EVEREST_CRYSTAL_INTERACT_H
#include <math.h>
#include <stdio.h>
#include <stdlib.h>

double const tlcut_cry = 0.0009982;

double const aTF = 0.194e-10; // Screening function [m]
double const dP  = 1.920e-10; // Distance between planes (110) [m]
double const u1  = 0.075e-10; // Thermal vibrations amplitude

// pp cross-sections and parameters for energy dependence
double const pptref_cry = 0.040;
double const freeco_cry = 1.618;

// Processes
int const proc_out         =  -1;     // Crystal not hit
int const proc_AM          =   1;     // Amorphous
int const proc_VR          =   2;     // Volume reflection
int const proc_CH          =   3;     // Channeling
int const proc_VC          =   4;     // Volume capture
int const proc_absorbed    =   5;     // Absorption
int const proc_DC          =   6;     // Dechanneling
int const proc_pne         =   7;     // Proton-neutron elastic interaction
int const proc_ppe         =   8;     // Proton-proton elastic interaction
int const proc_diff        =   9;     // Single diffractive
int const proc_ruth        =  10;     // Rutherford scattering
int const proc_ch_absorbed =  15;     // Channeling followed by absorption
int const proc_ch_pne      =  17;     // Channeling followed by proton-neutron elastic interaction
int const proc_ch_ppe      =  18;     // Channeling followed by proton-proton elastic interaction
int const proc_ch_diff     =  19;     // Channeling followed by single diffractive
int const proc_ch_ruth     =  20;     // Channeling followed by Rutherford scattering
int const proc_TRVR        = 100;     // Volume reflection in VR-AM transition region
int const proc_TRAM        = 101;     // Amorphous in VR-AM transition region


 static inline
double* movech(RandomRutherfordData rng, LocalParticle* part, double nam, double dz, double x, double xp, double yp, double pc, double r, double rc, double rho, double anuc, double zatom, double emr, double hcut, double bnref, double csref0, double csref1, double csref5, double eUm, double collnt, double iProc) {

    double* result = (double*)malloc(5 * sizeof(double));

//     // Material properties
//     double const exenergy = CrystalMaterialData_get_excitation_energy(material);
//     double const rho      = CrystalMaterialData_get_density(material);
//     double const anuc     = CrystalMaterialData_get_A(material);
//     double const zatom    = CrystalMaterialData_get_Z(material);
//     double const emr      = CrystalMaterialData_get_nuclear_radius(material);
//     double const dlri     = CrystalMaterialData_get_crystal_radiation_length(material);
//     double const dlyi     = CrystalMaterialData_get_crystal_nuclear_length(material);
//     double const ai       = CrystalMaterialData_get_crystal_plane_distance(material);
//     double const eum      = CrystalMaterialData_get_crystal_potential(material);
//     double const collnt   = CrystalMaterialData_get_nuclear_collision_length(material);
//     double const hcut     = CrystalMaterialData_get_hcut(material);
//     double const bnref    = CrystalMaterialData_get_nuclear_elastic_slope(material);
//     double const csref0   = CrystalMaterialData_get_cross_section(material, 0);
//     double const csref1   = CrystalMaterialData_get_cross_section(material, 1);
//     double const csref5   = CrystalMaterialData_get_cross_section(material, 5);

    double pmap = 938.271998;
    double pc_in = pc;

    double cs[6] = {0.0,0.0,0.0,0.0,0.0,0.0};
    double cprob[6] = {0.0,0.0,0.0,0.0,0.0,0.0};
    double xran_cry[1] = {0.0};

    //New treatment of scattering routine based on standard sixtrack routine

    //Useful calculations for cross-section and event topology calculation
    double ecmsq  = ((2.*pmap)*1.0e-3)*pc;
    double xln15s = log(0.15*ecmsq);

    //New models, see Claudia's thesis
    double pptot = (0.041084 - 0.0023302*log(ecmsq)) + 0.00031514 * pow(log(ecmsq),2.);
    double ppel  = (11.7 - 1.59*log(ecmsq) + 0.134 * pow(log(ecmsq),2.))/1.0e3;
    double ppsd  = (4.3 + 0.3*log(ecmsq))/1.0e3;
    double bpp   = 7.156 + 1.439*log(sqrt(ecmsq));

    xran_cry[0] = RandomRutherford_generate(rng, part);

    //Rescale the total and inelastic cross-section accordigly to the average density seen
    double x_i = x;
    int np  = x_i/dP;    //Calculate in which crystalline plane the particle enters
    x_i = x_i - np*dP;   //Rescale the incoming x at the left crystalline plane
    x_i = x_i - (dP/2.); //Rescale the incoming x in the middle of crystalline planes

    double pv   = pow(pc,2.)/sqrt(pow(pc,2.) + pow((pmap*1.0e-3),2.))*1.0e9;          //Calculate pv=P/E
    double Ueff = eUm*((2.*x_i)/dP)*((2.*x_i)/dP) + pv*x_i/r; //Calculate effective potential
    double Et   = (pv*pow(xp,2.))/2. + Ueff;                  //Calculate transverse energy
    double Ec   = (eUm*(1.-rc/r))*(1.-rc/r);                  //Calculate critical energy in bent crystals

    //To avoid negative Et
    double xminU = ((-pow(dP,2.)*pc)*1.0e9)/(8.*eUm*r);
    double Umin  = fabs((eUm*((2.*xminU)/dP))*((2.*xminU)/dP) + pv*xminU/r);
    Et    = Et + Umin;
    Ec    = Ec + Umin;

    //Calculate min e max of the trajectory between crystalline planes
    double x_min = (-(dP/2.)*rc)/r - (dP/2.)*sqrt(Et/Ec);
    double x_max = (-(dP/2.)*rc)/r + (dP/2.)*sqrt(Et/Ec);

    //Change ref. frame and go back with 0 on the crystalline plane on the left
    x_min = x_min - dP/2.;
    x_max = x_max - dP/2.;

    //Calculate the "normal density" in m^-3
    double N_am  = ((rho*6.022e23)*1.0e6)/anuc;

    //Calculate atomic density at min and max of the trajectory oscillation
    // erf returns the error function of complex argument
    double rho_max = ((N_am*dP)/2.)*(erf(x_max/sqrt(2*pow(u1,2.))) - erf((dP-x_max)/sqrt(2*pow(u1,2.))));
    double rho_min = ((N_am*dP)/2.)*(erf(x_min/sqrt(2*pow(u1,2.))) - erf((dP-x_min)/sqrt(2*pow(u1,2.))));

    //"zero-approximation" of average nuclear density seen along the trajectory
    double avrrho  = (rho_max - rho_min)/(x_max - x_min);
    avrrho  = (2.*avrrho)/N_am;

    double csref_tot_rsc  = csref0*avrrho; //Rescaled total ref cs
    double csref_inel_rsc = csref1*avrrho; //Rescaled inelastic ref cs

    //Cross-section calculation
    double freep = freeco_cry * pow(anuc,1./3.);

    //compute pp and pn el+single diff contributions to cross-section (both added : quasi-elastic or qel later)
    cs[3] = freep*ppel;
    cs[4] = freep*ppsd;

    //correct TOT-CSec for energy dependence of qel
    //TOT CS is here without a Coulomb contribution
    cs[0] = csref_tot_rsc + freep*(pptot - pptref_cry);

    //Also correct inel-CS
    if(csref_tot_rsc == 0) {
        cs[1] = 0;
    } else {
        cs[1] = (csref_inel_rsc*cs[0])/csref_tot_rsc;
    }
        
    //Nuclear Elastic is TOT-inel-qel ( see definition in RPP)
    cs[2] = ((cs[0] - cs[1]) - cs[3]) - cs[4];
    cs[5] = csref5;

    //Now add Coulomb
    cs[0] = cs[0] + cs[5];

    //Calculate cumulative probability
    //cprob[6] = {0.0,0.0,0.0,0.0,0.0,0.0};
    cprob[5] = 1;
    
    if (cs[0] == 0) {
        int i;
        for (i = 1; i < 5; ++i) {
            cprob[i] = cprob[i-1];
        }
    } else {
        int i;
        for (i = 1; i < 5; ++i) {
            cprob[i] = cprob[i-1] + cs[i]/cs[0];
        }
    }

    //Multiple Coulomb Scattering
    xp = xp*1.0e3;
    yp = yp*1.0e3;

    //Turn on/off nuclear interactions
    if (nam == 0) {
        result[0] = x;
        result[1] = xp;
        result[2] = yp;
        result[3] = pc;
        result[4] = iProc;
        return result;
    }
    
    double nuc_cl_l;
    //Can nuclear interaction happen?
    //Rescaled nuclear collision length
    if (avrrho == 0) {
        nuc_cl_l = 1.0e6;
    } else {
        nuc_cl_l = collnt/avrrho;
    }

    double zlm = nuc_cl_l*RandomExponential_generate(part);

    //write(889,*) x_i,pv,Ueff,Et,Ec,N_am,avrrho,csref_tot_rsc,csref_inel_rsc,nuc_cl_l

    if (zlm < dz) {

        //Choose nuclear interaction
        double aran = RandomUniform_generate(part);
        int i = 1;
        double bn;
        double xm2;
        double bsd = 0.0;
        double teta;
        double tx;
        double tz;

        while (aran > cprob[i]) {
            i=i+1;
        }
        
        int ichoix = i;

        //Do the interaction
        double t = 0 ; //default value to cover ichoix=1
        
        if (ichoix==1) {
            iProc = proc_ch_absorbed; //deep inelastic, impinging p disappeared
        } else if (ichoix==2) { //p-n elastic
            iProc = proc_ch_pne;
            bn    = (bnref*cs[0])/csref_tot_rsc;
            t     = RandomExponential_generate(part)/bn;
        } else if (ichoix==3) { //p-p elastic
            iProc = proc_ch_ppe;
            t     = RandomExponential_generate(part)/bpp;
        } else if (ichoix==4) { //Single diffractive
            iProc = proc_ch_diff;
            xm2   = exp(RandomUniform_generate(part)*xln15s);
            pc    = pc*(1 - xm2/ecmsq);

            if (xm2 < 2.) {
                bsd = 2*bpp;
            } else if (xm2 >= 2. && xm2 <= 5.) {
                bsd = ((106.0 - 17.0*xm2)*bpp)/36.0;
            } else if (xm2 > 5.) {
                bsd = (7*bpp)/12.0;
            }
            //end if
            t = RandomExponential_generate(part)/bsd;
        } else { //(ichoix==5)
            iProc      = proc_ch_ruth;
            xran_cry[0] = RandomRutherford_generate(rng, part);
            t = xran_cry[0];
        }


        //Calculate the related kick -----------
        if (ichoix == 4) {
            teta = sqrt(t)/pc_in; //DIFF has changed PC!!!
        } else {
            teta = sqrt(t)/pc;
        }

        tx = teta*RandomNormal_generate(part)*1.0e3;
        tz = teta*RandomNormal_generate(part)*1.0e3;

        //Change p angle
        xp = xp + tx;
        yp = yp + tz;
    }

    xp = xp/1.0e3;
    yp = yp/1.0e3;

    result[0] = x;
    result[1] = xp;
    result[2] = yp;
    result[3] = pc;
    result[4] = iProc;
    return result;
}


 static inline
double* moveam(RandomRutherfordData rng, LocalParticle* part, double nam, double dz, double dei, double dlr, double xp, double yp, double pc, double anuc, double zatom, double emr, double hcut, double bnref, double csref0, double csref1, double csref5, double collnt, double iProc) {

    double* result = (double*)malloc(4 * sizeof(double));

//     // Material properties
//     double const exenergy = CrystalMaterialData_get_excitation_energy(material);
//     double const rho      = CrystalMaterialData_get_density(material);
//     double const anuc     = CrystalMaterialData_get_A(material);
//     double const zatom    = CrystalMaterialData_get_Z(material);
//     double const emr      = CrystalMaterialData_get_nuclear_radius(material);
//     double const dlri     = CrystalMaterialData_get_crystal_radiation_length(material);
//     double const dlyi     = CrystalMaterialData_get_crystal_nuclear_length(material);
//     double const ai       = CrystalMaterialData_get_crystal_plane_distance(material);
//     double const eum      = CrystalMaterialData_get_crystal_potential(material);
//     double const collnt   = CrystalMaterialData_get_nuclear_collision_length(material);
//     double const hcut     = CrystalMaterialData_get_hcut(material);
//     double const bnref    = CrystalMaterialData_get_nuclear_elastic_slope(material);
//     double const csref0   = CrystalMaterialData_get_cross_section(material, 0);
//     double const csref1   = CrystalMaterialData_get_cross_section(material, 1);
//     double const csref5   = CrystalMaterialData_get_cross_section(material, 5);

    double pc_in = pc;

    double pmap = 938.271998;

    double cs[6] = {0.0,0.0,0.0,0.0,0.0,0.0};
    double cprob[6] = {0.0,0.0,0.0,0.0,0.0,0.0};

    // New treatment of scattering routine based on standard sixtrack routine
    // useful calculations for cross-section and event topology calculation
    double ecmsq  = 2.*pmap*1.0e-3*pc;
    double xln15s = log(0.15*ecmsq);

    // New models, see Claudia's thesis
    double pptot = 0.041084 - 0.0023302*log(ecmsq) + 0.00031514 * pow(log(ecmsq),2.);
    double ppel  = (11.7 - 1.59*log(ecmsq) + 0.134 * pow(log(ecmsq),2.))/1.0e3;
    double ppsd  = (4.3 + 0.3*log(ecmsq))/1.0e3;
    double bpp   = 7.156 + 1.439*log(sqrt(ecmsq));

    // Cross-section calculation
    // freep: number of nucleons involved in single scattering
    double freep = freeco_cry * pow(anuc,1./3.);

    // Compute pp and pn el+single diff contributions to cross-section (both added : quasi-elastic or qel later)
    cs[3] = freep*ppel;
    cs[4] = freep*ppsd;

    // Correct TOT-CSec for energy dependence of qel
    // TOT CS is here without a Coulomb contribution
    cs[0] = csref0 + freep*(pptot - pptref_cry);
    double bn = (bnref*cs[0])/csref0;

    // Also correct inel-CS
    cs[1] = (csref1*cs[0])/csref0;

    // Nuclear Elastic is TOT-inel-qel ( see definition in RPP)
    cs[2] = ((cs[0] - cs[1]) - cs[3]) - cs[4];
    cs[5] = csref5;

    // Now add Coulomb
    cs[0] = cs[0] + cs[5];

    // Calculate cumulative probability
    cprob[5] = 1;

    int i;
    for (i = 1; i < 5; ++i) {
        cprob[i] = cprob[i-1] + cs[i]/cs[0];
    }

    // Multiple Coulomb Scattering
    xp  = xp*1.0e3;
    yp  = yp*1.0e3;
    pc  = pc - dei*dz; // Energy lost because of ionization process[GeV]

    double dya   = (13.6/pc)*sqrt(dz/dlr); // RMS of coloumb scattering MCS (mrad)
    double kxmcs = dya*RandomNormal_generate(part);
    double kymcs = dya*RandomNormal_generate(part);

    xp = xp+kxmcs;
    yp = yp+kymcs;

    if (nam == 0) {

        result[0] = xp;
        result[1] = yp;
        result[2] = pc;
        result[3] = iProc;
        return result; // Turn on/off nuclear interactions
    }

    // Can nuclear interaction happen?
    double zlm = collnt*RandomExponential_generate(part);

    if (zlm < dz) {
        // Choose nuclear interaction
        double aran = RandomUniform_generate(part);
        int i=1;

        while (aran > cprob[i]) {
            i = i+1;
            //goto 10
        }

        int ichoix = i;

        // Do the interaction
        double t = 0 ;// default value to cover ichoix=1
        if (ichoix==1) {
        //case(1) // Deep inelastic, impinging p disappeared
            iProc = proc_absorbed;
        } else if (ichoix==2) { // p-n elastic
            iProc = proc_pne;
            t     = RandomExponential_generate(part)/bn;
        } else if (ichoix==3) { // p-p elastic
            iProc = proc_ppe;
            t     = RandomExponential_generate(part)/bpp;
        } else if (ichoix==4) { // Single diffractive
            iProc = proc_diff;
            double xm2 = exp(RandomUniform_generate(part)*xln15s);
            double bsd = 0.0;
            pc    = pc*(1 - xm2/ecmsq);

            if(xm2 < 2) {
                bsd = 2*bpp;
            } else if(xm2 >= 2 && xm2 <= 5) {
                bsd = ((106.0 - 17.0*xm2)*bpp)/36.0;
            } else if(xm2 > 5) {
                bsd = 7.0*bpp/12.0;
            }
        
            t = RandomExponential_generate(part)/bsd;
        } else { //(ichoix==5)
            iProc = proc_ruth;
            t = RandomRutherford_generate(rng, part);
        }

        // end select

        double teta;

        // Calculate the related kick
        if(ichoix == 4) {
            teta = sqrt(t)/pc_in ;// DIFF has changed PC
        } else {
            teta = sqrt(t)/pc;
        }

        double tx = teta*RandomNormal_generate(part)*1.0e3;
        double tz = teta*RandomNormal_generate(part)*1.0e3;

        // Change p angle
        xp = xp + tx;
        yp = yp + tz;
        
    }

    xp = xp/1.0e3;
    yp = yp/1.0e3;

    result[0] = xp;
    result[1] = yp;
    result[2] = pc;
    result[3] = iProc;
    return result; // Turn on/off nuclear interactions
}


 static inline
double calcionloss_cry(LocalParticle* part, double dz, double EnLo, double betar, double bgr, double gammar, double tmax, double plen, double exenergy, double zatom, double rho, double anuc) {

//     // Material properties
//     double const exenergy = CrystalMaterialData_get_excitation_energy(material);
//     double const rho      = CrystalMaterialData_get_density(material);
//     double const anuc     = CrystalMaterialData_get_A(material);
//     double const zatom    = CrystalMaterialData_get_Z(material);
//     double const emr      = CrystalMaterialData_get_nuclear_radius(material);
//     double const dlri     = CrystalMaterialData_get_crystal_radiation_length(material);
//     double const dlyi     = CrystalMaterialData_get_crystal_nuclear_length(material);
//     double const ai       = CrystalMaterialData_get_crystal_plane_distance(material);
//     double const eum      = CrystalMaterialData_get_crystal_potential(material);
//     double const collnt   = CrystalMaterialData_get_nuclear_collision_length(material);
//     double const hcut     = CrystalMaterialData_get_hcut(material);
//     double const bnref    = CrystalMaterialData_get_nuclear_elastic_slope(material);
//     double const csref0   = CrystalMaterialData_get_cross_section(material, 0);
//     double const csref1   = CrystalMaterialData_get_cross_section(material, 1);
//     double const csref5   = CrystalMaterialData_get_cross_section(material, 5);

    double k = 0.307075; // Constant in front bethe-bloch [mev g^-1 cm^2]
    double pmae = 0.51099890;
    double pmap = 938.271998;

    double thl  = (((((4.*k)*zatom)*dz)*1.0e2)*rho)/(anuc* pow(betar,2)); // [MeV]
    EnLo = ((k*zatom)/(anuc* pow(betar,2.))) * (0.5*log(((((2.*pmae)*bgr)*bgr)*tmax)/(1.0e6* pow(exenergy,2.))) -
            pow(betar,2.) - log(plen/(exenergy*1.0e3)) - log(bgr) + 0.5);
    EnLo = ((EnLo*rho)*1.0e-1)*dz; // [GeV]
    double Tt   = (EnLo*1.0e3)+thl; // [MeV]

    double cs_tail   = ((k*zatom)/(anuc* pow(betar,2.))) * ((0.5*((1/Tt)-(1./tmax))) - (log(tmax/Tt)*(pow(betar,2.))/(2.*tmax)) 
                        + ((tmax-Tt)/((4.*(pow(gammar,2.)))*(pow(pmap,2.)))));
    double prob_tail = ((cs_tail*rho)*dz)*1.0e2;

    if (RandomUniform_generate(part) < prob_tail) {
        EnLo = ((k*zatom)/(anuc*pow(betar,2.))) * (0.5*log((2*pmae*bgr*bgr*tmax)/(1.0e6*pow(exenergy,2.))) - pow(betar,2.) - 
                log(plen/(exenergy*1.0e3)) - log(bgr) + 0.5 + pow(tmax,2.)/(8.*(pow(gammar,2.))*(pow(pmap,2.))));

        EnLo = (EnLo*rho)*1.0e-1; // [GeV/m]
    } else {
        EnLo = EnLo/dz; // [GeV/m]
    }

    return EnLo;
}


 static inline
double* interact(RandomRutherfordData rng, LocalParticle* part, double x, double xp, double y, double yp, double pc, double length, double s_P, double x_P, double exenergy, double rho, double anuc, double zatom, double emr, double dlri, double dlyi, 
                double ai, double eUm, double collnt, double hcut, double csref0, double csref1, double csref5, double bnref,
                 double cry_tilt, double cry_rcurv, double cry_alayer, double cry_xmax, 
                double cry_ymax, double cry_orient, double cry_miscut, double cry_bend, double cry_cBend, double cry_sBend, double cry_cpTilt, double cry_spTilt, double cry_cnTilt, double cry_snTilt, double iProc) {

    double* result = (double*)malloc(6 * sizeof(double));

//     // Material properties
//     double const exenergy = CrystalMaterialData_get_excitation_energy(material);
//     double const rho      = CrystalMaterialData_get_density(material);
//     double const anuc     = CrystalMaterialData_get_A(material);
//     double const zatom    = CrystalMaterialData_get_Z(material);
//     double const emr      = CrystalMaterialData_get_nuclear_radius(material);
//     double const dlri     = CrystalMaterialData_get_crystal_radiation_length(material);
//     double const dlyi     = CrystalMaterialData_get_crystal_nuclear_length(material);
//     double const ai       = CrystalMaterialData_get_crystal_plane_distance(material);
//     double const eum      = CrystalMaterialData_get_crystal_potential(material);
//     double const collnt   = CrystalMaterialData_get_nuclear_collision_length(material);
//     double const hcut     = CrystalMaterialData_get_hcut(material);
//     double const bnref    = CrystalMaterialData_get_nuclear_elastic_slope(material);
//     double const csref0   = CrystalMaterialData_get_cross_section(material, 0);
//     double const csref1   = CrystalMaterialData_get_cross_section(material, 1);
//     double const csref5   = CrystalMaterialData_get_cross_section(material, 5);

    double dest = 0.;
    double pmap = 938.271998;
    double pmae = 0.51099890;
    double crade = 2.817940285e-15;

    double c_v1 =  1.7;   // Fitting coefficient
    double c_v2 = -1.5;   // Fitting coefficient

    int nam = 1; // Switch on/off the nuclear interaction (NAM) and the MCS (ZN)
    int zn  = 1;

    // dE/dX and dechanneling length calculation
    double mom    = pc*1.0e3;                // [GeV]
    double enr    = sqrt(pow(mom,2.) + pow(pmap,2.)); // [MeV]
    double gammar = enr/pmap;
    double betar  = mom/enr;
    double bgr    = betar*gammar;
    double mep    = pmae/pmap;  // Electron/proton

    double tmax = (2.*pmae*pow(bgr,2.))/(1. + 2.*gammar*mep + pow(mep,2.));  // [MeV]
    double plen = sqrt((rho*zatom)/anuc)*28.816e-6; // [MeV]

    double const_dech = ((256.0/(9*pow(M_PI,2.))) * (1./(log(((2.*pmae)*gammar)/(exenergy*1.0e3)) - 1.))) * ((aTF*dP)/(crade*pmae)); // [m/MeV]
    const_dech = const_dech*1.0e3; // [m/GeV]

    double s = 0.;
    double s_length = cry_rcurv*sin(length/cry_rcurv);
    double L_chan   = length;

    // MISCUT second step: fundamental coordinates (crystal edges and plane curvature radius)
    double s_K = cry_rcurv*sin(length/cry_rcurv);
    double x_K = cry_rcurv*(1.-cos(length/cry_rcurv));
    double s_M = (cry_rcurv-cry_xmax)*sin(length/cry_rcurv);
    double x_M = cry_xmax + (cry_rcurv-cry_xmax)*(1.-cos(length/cry_rcurv));
    double r   = sqrt(pow(s_P,2.) + pow((x-x_P),2.));

    // MISCUT third step: F coordinates (exit point) on crystal exit face
    double A_F = pow((tan(length/cry_rcurv)),2.) + 1.;
    double B_F = ((-2)*pow((tan(length/cry_rcurv)),2.))*cry_rcurv + (2.*tan(length/cry_rcurv))*s_P - 2.*x_P;
    double C_F = pow((tan(length/cry_rcurv)),2.)*(pow(cry_rcurv,2.)) - ((2.*tan(length/cry_rcurv))*s_P)*cry_rcurv + pow(s_P,2.) + pow(x_P,2.) - pow(r,2.);

    double x_F = (-B_F-sqrt(pow(B_F,2.)-4.*(A_F*C_F)))/(2.*A_F);
    double s_F = (-tan(length/cry_rcurv))*(x_F-cry_rcurv);

    if (x_F < x_K || x_F > x_M || s_F < s_M || s_F > s_K) {

        double alpha_F;
        double beta_F;
        
        if (cry_miscut == 0 && fabs(x_F-x_K) <= 1.0e-13 && fabs(s_F-s_K) <= 1.0e3) {
        // no miscut, entrance from below: exit point is K (lower edge)
            x_F = x_K;
            s_F = s_K;
        } else if (cry_miscut == 0 && fabs(x_F-x_M) <= 1.0e3 && fabs(s_F-s_M) <= 1.0e3) {
        // no miscut, entrance from above: exit point is M (upper edge)
            x_F = x_M;
            s_F = s_M;
        } else {
        // MISCUT Third step (bis): F coordinates (exit point)  on bent side
            if (cry_miscut < 0) {
            // Intersect with bottom side
                alpha_F = (cry_rcurv-x_P)/x_P;
                beta_F = -(pow(r,2.)-pow(s_P,2.)-pow(x_P,2.))/(2*s_P);
                A_F = pow(alpha_F,2.) + 1.;
                B_F = 2.*(alpha_F*beta_F) - 2.*cry_rcurv;
                C_F = pow(beta_F,2.);
            } else {
            // Intersect with top side
                alpha_F = (cry_rcurv-x_P)/s_P;
                beta_F = -(pow(r,2.)+cry_xmax*(cry_xmax-(2.*cry_rcurv))-pow(s_P,2.)-pow(x_P,2.))/(2.*s_P);
                A_F = pow(alpha_F,2.) + 1.;
                B_F = 2.*(alpha_F*beta_F) - 2.*cry_rcurv;
                C_F = pow(beta_F,2.) - cry_xmax*(cry_xmax-2.*cry_rcurv);
            }
            
            x_F = (-B_F-sqrt(pow(B_F,2.)-4.*(A_F*C_F)))/(2.*A_F);
            s_F = alpha_F*x_F + beta_F;
        }
    }

    // MISCUT 4th step: deflection and length calculation
    double a = sqrt(pow(s_F,2.)+pow((x-x_F),2.));
    double tP = acos((2.*pow(r,2.)-pow(a,2.))/(2.*pow(r,2.)));
    double tdefl = asin((s_F-s_P)/r);
    L_chan = r*tP;

    double xp_rel = xp - cry_miscut;

    double ymin = -cry_ymax/2.;
    double ymax =  cry_ymax/2.;

    // FIRST CASE: p don't interact with crystal
    if (y < ymin || y > ymax || x > cry_xmax) {
        x  = x + xp*s_length;
        y     = y + yp*s_length;
        iProc = proc_out;
        result[0] = x;
        result[1] = xp;
        result[2] = y;
        result[3] = yp;
        result[4] = pc;
        result[5] = iProc;
        return result;

    } else if (x < cry_alayer || y-ymin < cry_alayer || ymax-y < cry_alayer) {
    // SECOND CASE: p hits the amorphous layer
        double x0    = x;
        double y0    = y;
        double a_eq  = 1. + pow(xp,2.);
        double b_eq  = (2.*x)*xp - (2.*xp)*cry_rcurv;
        double c_eq  = pow(x,2.) - (2.*x)*cry_rcurv;
        double delta = pow(b_eq,2.) - (4.*a_eq)*c_eq;
        s = (-b_eq+sqrt(delta))/(2.*a_eq);
        if (s >= s_length) {
            s = s_length;
        }
        x   =  xp*s + x0;
        double len_xs = sqrt(pow((x-x0),2.) + pow(s,2.));
        double len_ys;
        if (yp >= 0 && y + yp*s <= ymax) {
            len_ys = yp*len_xs;
        } else if (yp < 0 && y + yp*s >= ymin) {
            len_ys = yp*len_xs;
        } else {
            s      = (ymax-y)/yp;
            len_ys = sqrt(pow((ymax-y),2.) + pow(s,2.));
            x   = x0 + xp*s;
            len_xs = sqrt(pow((x-x0),2.) + pow(s,2.));
        }
        
        double am_len = sqrt(pow(len_xs,2.) + pow(len_ys,2.));
        s     = s/2;
        x  = x0 + xp*s;
        y     = y0 + yp*s;
        iProc = proc_AM;

        dest = calcionloss_cry(part,s_length,dest,betar,bgr,gammar,tmax,plen,
                            exenergy,zatom,rho,anuc);

        double* result_am = moveam(rng, part,nam,am_len,dest,dlri,xp,yp,pc,anuc,zatom,emr,hcut,
                            bnref,csref0,csref1,csref5,collnt,iProc);
        xp = result_am[0];
        yp = result_am[1];
        pc = result_am[2];
        iProc = result_am[3];
        free(result_am);

        x = x + xp*(s_length-s);
        y = y + yp*(s_length-s);

        result[0] = x;
        result[1] = xp;
        result[2] = y;
        result[3] = yp;
        result[4] = pc;
        result[5] = iProc;
        return result;

    } else if (x > cry_xmax-cry_alayer && x < cry_xmax) {
        iProc = proc_AM;
        
        dest = calcionloss_cry(part,s_length,dest,betar,bgr,gammar,tmax,plen,
                            exenergy,zatom,rho,anuc);

        double* result_am = moveam(rng, part,nam,s_length,dest,dlri,xp,yp,pc,anuc,zatom,emr,hcut,bnref,csref0,
                        csref1,csref5,collnt,iProc);
        xp = result_am[0];
        yp = result_am[1];
        pc = result_am[2];
        iProc = result_am[3];
        free(result_am);

        result[0] = x;
        result[1] = xp;
        result[2] = y;
        result[3] = yp;
        result[4] = pc;
        result[5] = iProc;
        return result;
    }
    
    //THIRD CASE: the p interacts with the crystal.
    //Define typical angles/probabilities for orientation 110
    double xpcrit0 = sqrt((2.0e-9*eUm)/pc);   //Critical angle (rad) for straight crystals
    double Rcrit   = (pc/(2.0e-6*eUm))*ai; //Critical curvature radius [m]

    //If R>Rcritical=>no channeling is possible (ratio<1)
    double ratio  = cry_rcurv/Rcrit;
    double xpcrit = (xpcrit0*(cry_rcurv-Rcrit))/cry_rcurv; //Critical angle for curved crystal

    double Ang_rms;
    double Ang_avr;
    double Vcapt;
    if (ratio <= 1.) { //no possibile channeling
        Ang_rms = ((c_v1*0.42)*xpcrit0)*sin(1.4*ratio); //RMS scattering
        Ang_avr = ((c_v2*xpcrit0)*5.0e-2)*ratio;                         //Average angle reflection
        Vcapt   = 0.;                                                //Probability of VC
    } else if (ratio <= 3.) { //Strongly bent crystal
        Ang_rms = ((c_v1*0.42)*xpcrit0)*sin(0.4713*ratio + 0.85); //RMS scattering
        Ang_avr = (c_v2*xpcrit0)*(0.1972*ratio - 0.1472);                  //Average angle reflection
        Vcapt   = 7.0e-4*(ratio - 0.7)/pow(pc,2.0e-1);                           //Correction by sasha drozdin/armen
        //K=0.0007 is taken based on simulations using CATCH.f (V.Biryukov)
    } else { //Rcry >> Rcrit
        Ang_rms = (c_v1*xpcrit0)*(1./ratio);                //RMS scattering
        Ang_avr = (c_v2*xpcrit0)*(1. - 1.6667/ratio); //Average angle for VR
        Vcapt   = 7.0e-4*(ratio - 0.7)/pow(pc,2.0e-1); //Probability for VC correction by sasha drozdin/armen
        //K=0.0007 is taken based on simulations using CATCH.f (V.Biryukov)
    }
    if (cry_orient == 2) {
        Ang_avr = Ang_avr*0.93;
        Ang_rms = Ang_rms*1.05;
        xpcrit  = xpcrit*0.98;
    }
    if (fabs(xp_rel) < xpcrit) {
        double alpha  = xp_rel/xpcrit;
        double Chann  = sqrt(0.9*(1 - pow(alpha,2.)))*sqrt(1.-(1./ratio)); //Saturation at 95%
        double N_atom = 1.0e-1;

        //if they can channel: 2 options
        if (RandomUniform_generate(part) <= Chann) { //option 1:channeling
            double TLdech1 = (const_dech*pc)*pow((1.-1./ratio),2.); //Updated calculate typical dech. length(m)

            if(RandomUniform_generate(part) <= N_atom) {
                TLdech1 = ((const_dech/2.0e2)*pc)*pow((1.-1./ratio),2.);  //Updated dechanneling length (m)      
            }
            double Dechan = RandomExponential_generate(part); //Probability of dechanneling
            double Ldech  = TLdech1*Dechan;   //Actual dechan. length

            //careful: the dechanneling lentgh is along the trajectory
            //of the particle -not along the longitudinal coordinate...
            if (Ldech < L_chan) {
                iProc = proc_DC;
                double Dxp   = Ldech/r; //Change angle from channeling [mrad]
                double Sdech = Ldech*cos(cry_miscut + 0.5*Dxp);
                x  = x  + Ldech*(sin(0.5*Dxp+cry_miscut)); //Trajectory at channeling exit
                xp    = xp + Dxp + (2*(RandomUniform_generate(part)-0.5))*xpcrit;
                y     = y  + yp * Sdech;

                dest = calcionloss_cry(part,Ldech,dest,betar,bgr,gammar,tmax,plen,
                                    exenergy,zatom,rho,anuc);
                pc = pc - 0.5*dest*Ldech; //Energy loss to ionization while in CH [GeV]
                x  = x  + (0.5*(s_length-Sdech))*xp;
                y  = y  + (0.5*(s_length-Sdech))*yp;

                dest = calcionloss_cry(part,s_length-Sdech,dest,betar,bgr,gammar,tmax,plen,exenergy,zatom,rho,anuc);

                double* result_am = moveam(rng,part,nam,s_length-Sdech,dest,dlri,xp,yp,pc,anuc,zatom,emr,hcut,bnref,csref0,csref1,csref5,collnt,iProc);
                xp = result_am[0];
                yp = result_am[1];
                pc = result_am[2];
                iProc = result_am[3];
                free(result_am);

                x = x + (0.5*(s_length-Sdech))*xp;
                y = y + (0.5*(s_length-Sdech))*yp;
            } else {
                iProc = proc_CH;
                double xpin  = xp;
                double ypin  = yp;

                //check if a nuclear interaction happen while in CH
                double* result_ch = movech(rng,part,nam,L_chan,x,xp,yp,pc,cry_rcurv,Rcrit,rho,anuc,zatom,emr,hcut,bnref,
                                csref0,csref1,csref5,eUm,collnt,iProc);
                x = result_ch[0];
                xp = result_ch[1];
                yp = result_ch[2];
                pc = result_ch[3];
                iProc = result_ch[4];
                free(result_ch);

                if (iProc != proc_CH) {
                    //if an nuclear interaction happened, move until the middle with initial xp,yp:
                    //propagate until the "crystal exit" with the new xp,yp accordingly with the rest
                    //of the code in "thin lens approx"
                    x = x + (0.5*L_chan)*xpin;
                    y = y + (0.5*L_chan)*ypin;
                    x = x + (0.5*L_chan)*xp;
                    y = y + (0.5*L_chan)*yp;

                    dest = calcionloss_cry(part,length,dest,betar,bgr,gammar,tmax,plen,
                                        exenergy,zatom,rho,anuc);
                    pc = pc - dest*length; //energy loss to ionization [GeV]
                } else {
                    double Dxp = tdefl + (0.5*RandomNormal_generate(part))*xpcrit; //Change angle[rad]
                    
                    xp  = Dxp;
                    x = x + L_chan*(sin(0.5*Dxp)); //Trajectory at channeling exit
                    y   = y + s_length * yp;

                    dest = calcionloss_cry(part,length,dest,betar,bgr,gammar,tmax,plen,exenergy,zatom,rho,anuc);
                    pc = pc - (0.5*dest)*length; //energy loss to ionization [GeV]     
                } 
            }
        } else { //Option 2: VR
            //good for channeling but don't channel (1-2)
            iProc = proc_VR;

            xp = xp + (0.45*(xp_rel/xpcrit + 1))*Ang_avr;
            x  = x  + (0.5*s_length)*xp;
            y  = y  + (0.5*s_length)*yp;

            dest = calcionloss_cry(part,s_length,dest,betar,bgr,gammar,tmax,plen,exenergy,zatom,rho,anuc);
            double* result_am = moveam(rng, part,nam,s_length,dest,dlri,xp,yp,pc,anuc,zatom,emr,hcut,bnref,csref0,csref1,csref5,collnt,iProc);
            xp = result_am[0];
            yp = result_am[1];
            pc = result_am[2];
            iProc = result_am[3];
            free(result_am);

            x = x + (0.5*s_length)*xp;
            y = y + (0.5*s_length)*yp;  
        }
    } else { //case 3-2: no good for channeling. check if the  can VR
        double Lrefl = xp_rel*r; //Distance of refl. point [m]
        double Srefl = sin(xp_rel/2. + cry_miscut)*Lrefl;

        if (Lrefl > 0 && Lrefl < L_chan) { //VR point inside

        //2 options: volume capture and volume reflection

            if (RandomUniform_generate(part) > Vcapt || zn == 0) { //Option 1: VR
                iProc = proc_VR;
                x  = x + xp*Srefl;
                y     = y + yp*Srefl;
                double Dxp   = Ang_avr;
                xp    = xp + Dxp + Ang_rms*RandomNormal_generate(part);
                x  = x  + (0.5*xp)*(s_length - Srefl);
                y     = y  + (0.5*yp)*(s_length - Srefl);

                dest = calcionloss_cry(part,s_length-Srefl,dest,betar,bgr,gammar,tmax,plen,exenergy,zatom,rho,anuc);
                double* result_am = moveam(rng,part,nam,s_length-Srefl,dest,dlri,xp,yp,pc,anuc,zatom,emr,hcut,bnref,csref0,csref1,csref5,collnt,iProc);
                xp = result_am[0];
                yp = result_am[1];
                pc = result_am[2];
                iProc = result_am[3];
                free(result_am);

                x = x + (0.5*xp)*(s_length - Srefl);
                y = y + (0.5*yp)*(s_length - Srefl);
            } else { //Option 2: VC
                x = x + xp*Srefl;
                y = y + yp*Srefl;

                double TLdech2 = (const_dech/1.0e1)*pc*pow((1-1/ratio),2.) ;         //Updated typical dechanneling length(m)
                double Ldech   = TLdech2 * pow((sqrt(1.0e-2 + RandomExponential_generate(part)) - 1.0e-1),2.); //Updated DC length
                double tdech   = Ldech/cry_rcurv;
                double Sdech   = Ldech*cos(xp + 0.5*tdech);

                if (Ldech < length-Lrefl) {
                    iProc = proc_DC;
                    double Dxp   = Ldech/cry_rcurv + (0.5*RandomNormal_generate(part))*xpcrit;
                    x  = x + Ldech*(sin(0.5*Dxp+xp)); //Trajectory at channeling exit
                    y     = y + Sdech*yp;
                    xp    =  Dxp;
                    double Red_S = (s_length - Srefl) - Sdech;
                    x  = x + (0.5*xp)*Red_S;
                    y     = y + (0.5*yp)*Red_S;

                    dest = calcionloss_cry(part,Srefl,dest,betar,bgr,gammar,tmax,plen,
                                        exenergy,zatom,rho,anuc);

                    pc = pc - dest*Srefl; //"added" energy loss before capture

                    dest = calcionloss_cry(part,Sdech,dest,betar,bgr,gammar,tmax,plen,
                                        exenergy,zatom,rho,anuc);
                    pc = pc - (0.5*dest)*Sdech; //"added" energy loss while captured

                    dest = calcionloss_cry(part,Red_S,dest,betar,bgr,gammar,tmax,plen,
                                        exenergy,zatom,rho,anuc);
                    double* result_am = moveam(rng,part,nam,Red_S,dest,dlri,xp,yp,pc,anuc,zatom,emr,hcut,bnref,csref0,
                        csref1,csref5,collnt,iProc);
                    xp = result_am[0];
                    yp = result_am[1];
                    pc = result_am[2];
                    iProc = result_am[3];
                    free(result_am);
                    
                    x = x + (0.5*xp)*Red_S;
                    y = y + (0.5*yp)*Red_S;
                } else {
                    iProc   = proc_VC;
                    double Rlength = length - Lrefl;
                    double tchan   = Rlength/cry_rcurv;
                    double Red_S   = Rlength*cos(xp + 0.5*tchan);

                    dest = calcionloss_cry(part,Lrefl,dest,betar,bgr,gammar,tmax,plen,
                                        exenergy,zatom,rho,anuc);
                    pc   = pc - dest*Lrefl; //"added" energy loss before capture
                    double xpin = xp;
                    double ypin = yp;

                    //Check if a nuclear interaction happen while in ch
                    double* result_ch = movech(rng,part,nam,Rlength,x,xp,yp,pc,cry_rcurv,Rcrit,rho,anuc,zatom,emr,hcut,bnref,
                                    csref0,csref1,csref5,eUm,collnt,iProc);
                    x = result_ch[0];
                    xp = result_ch[1];
                    yp = result_ch[2];
                    pc = result_ch[3];
                    iProc = result_ch[4];
                    free(result_ch);
                                    
                    if (iProc != proc_VC) {
                        //if an nuclear interaction happened, move until the middle with initial xp,yp: propagate until
                        //the "crystal exit" with the new xp,yp aciordingly with the rest of the code in "thin lens approx"
                        x = x + (0.5*Rlength)*xpin;
                        y = y + (0.5*Rlength)*ypin;
                        x = x + (0.5*Rlength)*xp;
                        y = y + (0.5*Rlength)*yp;

                        dest = calcionloss_cry(part,Rlength,dest,betar,bgr,gammar,tmax,plen,
                                            exenergy,zatom,rho,anuc);
                        pc = pc - dest*Rlength;
                    } else {
                        double Dxp = (length-Lrefl)/cry_rcurv;
                        x = x + sin(0.5*Dxp+xp)*Rlength; //Trajectory at channeling exit
                        y   = y + Red_S*yp;
                        xp  = tdefl + (0.5*RandomNormal_generate(part))*xpcrit; //[mrad]

                        dest = calcionloss_cry(part,Rlength,dest,betar,bgr,gammar,tmax,plen,
                                            exenergy,zatom,rho,anuc);
                        pc = pc - (0.5*dest)*Rlength;  //"added" energy loss once captured
                    }
                }
            }
        } else {
            //Case 3-3: move in amorphous substance (big input angles)
            //Modified for transition vram daniele
            if (xp_rel > tdefl-cry_miscut + 2*xpcrit || xp_rel < -xpcrit) {
                iProc = proc_AM;
                x  = x + (0.5*s_length)*xp;
                y     = y + (0.5*s_length)*yp;
                if(zn > 0) {
                    dest = calcionloss_cry(part,s_length,dest,betar,bgr,gammar,tmax,plen,
                                        exenergy,zatom,rho,anuc);
                    double* result_am = moveam(rng,part,nam,s_length,dest,dlri,xp,yp,pc,anuc,zatom,emr,hcut,bnref,csref0,
                        csref1,csref5,collnt,iProc);
                    xp = result_am[0];
                    yp = result_am[1];
                    pc = result_am[2];
                    iProc = result_am[3];
                    free(result_am);
                }
            
                x = x + (0.5*s_length)*xp;
                y = y + (0.5*s_length)*yp;
            } else {
                double Pvr = (xp_rel-(tdefl-cry_miscut))/(2.*xpcrit);
                if(RandomUniform_generate(part) > Pvr) {

                    iProc = proc_TRVR;
                    x  = x + xp*Srefl;
                    y     = y + yp*Srefl;

                    double Dxp = (((-3.*Ang_rms)*xp_rel)/(2.*xpcrit) + Ang_avr) + ((3.*Ang_rms)*(tdefl-cry_miscut))/(2.*xpcrit);
                    xp  = xp + Dxp;
                    x = x + (0.5*xp)*(s_length-Srefl);
                    y   = y + (0.5*yp)*(s_length-Srefl);

                    dest = calcionloss_cry(part,s_length-Srefl,dest,betar,bgr,gammar,tmax,plen,
                                        exenergy,zatom,rho,anuc);
                    double* result_am = moveam(rng,part,nam,s_length-Srefl,dest,dlri,xp,yp,pc,anuc,zatom,emr,hcut,bnref,csref0,
                        csref1,csref5,collnt,iProc);
                    xp = result_am[0];
                    yp = result_am[1];
                    pc = result_am[2];
                    iProc = result_am[3];
                    free(result_am);

                    x = x + (0.5*xp)*(s_length - Srefl);
                    y = y + (0.5*yp)*(s_length - Srefl);
                } else {
                    iProc = proc_TRAM;
                    x = x + xp*Srefl;
                    y = y + yp*Srefl;
                    double Dxp = ((((-1.*(13.6/pc))*sqrt(s_length/dlri))*1.0e-3)*xp_rel)/(2.*xpcrit) + (((13.6/pc)*sqrt(s_length/dlri))*1.0e-3)*(1.+(tdefl-cry_miscut)/(2.*xpcrit));
                    xp = xp+Dxp;
                    x  = x + (0.5*xp)*(s_length-Srefl);
                    y  = y + (0.5*yp)*(s_length-Srefl);

                    dest = calcionloss_cry(part,s_length-Srefl,dest,betar,bgr,gammar,tmax,plen,
                                        exenergy,zatom,rho,anuc);
                    double* result_am = moveam(rng,part,nam,s_length-Srefl,dest,dlri,xp,yp,pc,anuc,zatom,emr,hcut,bnref,csref0,
                        csref1,csref5,collnt,iProc);
                    xp = result_am[0];
                    yp = result_am[1];
                    pc = result_am[2];
                    iProc = result_am[3];
                    free(result_am);

                    x = x + (0.5*xp)*(s_length - Srefl);
                    y = y + (0.5*yp)*(s_length - Srefl);
                }
            }
        }            
    }

    result[0] = x;
    result[1] = xp;
    result[2] = y;
    result[3] = yp;
    result[4] = pc;
    result[5] = iProc;
    return result;
}


 static inline
double* crystal(RandomRutherfordData rng, LocalParticle* part, double x, double xp, double z, double zp, double s, double p, 
                double x0, double xp0, double zlm, double s_imp, int isimp, double val_part_hit, double val_part_abs, 
                double val_part_impact, double val_part_indiv, double c_length, CrystalMaterialData material, double nhit, 
                double nabs, double cry_tilt, double  cry_rcurv, double  cry_bend, double  cry_alayer, double  cry_xmax, 
                double cry_ymax, double cry_orient, double cry_miscut, double iProc, 
                double n_chan, double n_VR, double n_amorphous) {

    double const exenergy = CrystalMaterialData_get_excitation_energy(material);
    double const rho      = CrystalMaterialData_get_density(material);
    double const anuc     = CrystalMaterialData_get_A(material);
    double const zatom    = CrystalMaterialData_get_Z(material);
    double const emr      = CrystalMaterialData_get_nuclear_radius(material);
    double const dlri     = CrystalMaterialData_get_crystal_radiation_length(material);
    double const dlyi     = CrystalMaterialData_get_crystal_nuclear_length(material);
    double const ai       = CrystalMaterialData_get_crystal_plane_distance(material);
    double const eum      = CrystalMaterialData_get_crystal_potential(material);
    double const collnt   = CrystalMaterialData_get_nuclear_collision_length(material);
    double const hcut     = CrystalMaterialData_get_hcut(material);
    double const bnref    = CrystalMaterialData_get_nuclear_elastic_slope(material);
    double const csref0   = CrystalMaterialData_get_cross_section(material, 0);
    double const csref1   = CrystalMaterialData_get_cross_section(material, 1);
    double const csref5   = CrystalMaterialData_get_cross_section(material, 5);

    double s_temp     = 0.;
    double s_shift    = 0.;
    double s_rot      = 0.;
    double s_int      = 0.;
    double x_temp     = 0.;
    double x_shift    = 0.;
    double x_rot      = 0.;
    double x_int      = 0.;
    double xp_temp    = 0.;
    double xp_shift   = 0.;
    double xp_rot     = 0.;
    double xp_int     = 0.;
    double xp_tangent = 0.;
    double tilt_int   = 0.;
    double shift      = 0.;
    double delta      = 0.;
    double a_eq       = 0.;
    double b_eq       = 0.;
    double c_eq       = 0.;
    double cry_length  = c_length;

    s_imp  = 0.;
    iProc  = proc_out;

    double* crystal_result = (double*)malloc(21 * sizeof(double));

    double const cry_cBend  = cos(cry_bend);
    double const cry_sBend  = sin(cry_bend);

    // Transform in the crystal reference system
    // 1st transformation: shift of the center of the reference frame
    double const cry_cpTilt = cos(cry_tilt);
    double const cry_spTilt = sin(cry_tilt);
    double const cry_cnTilt = cry_cpTilt;
    double const cry_snTilt = -cry_spTilt;
    if (cry_tilt < 0) {
        s_shift = s;
        shift   = cry_rcurv*(1 - cry_cpTilt);

        if (cry_tilt < -cry_bend) {
            shift = cry_rcurv*(cry_cnTilt - cos(cry_bend - cry_tilt));
        }
        x_shift = x - shift;
    } else {
        s_shift = s;
        x_shift = x;
    }
  
    // 2nd transformation: rotation
    s_rot  = x_shift*cry_spTilt + s_shift*cry_cpTilt;
    x_rot  = x_shift*cry_cpTilt - s_shift*cry_spTilt;
    xp_rot = xp - cry_tilt;

    // 3rd transformation: drift to the new coordinate s=0
    xp = xp_rot;
    x  = x_rot - xp_rot*s_rot;
    z  = z - zp*s_rot;
    s  = 0.;
  
    // Check that particle hit the crystal
    if (x >= 0. && x < cry_xmax) {
        // MISCUT first step: P coordinates (center of curvature of crystalline planes)
        double s_P = (cry_rcurv-cry_xmax)*sin(-cry_miscut);
        double x_P = cry_xmax + (cry_rcurv-cry_xmax)*cos(-cry_miscut);

        double* result = interact(rng,part,x,xp,z,zp,p,cry_length,s_P,x_P,exenergy,rho,anuc,zatom,emr,dlri,dlyi,
                        ai,eum,collnt,hcut,csref0,csref1,csref5,bnref,cry_tilt,
                        cry_rcurv,cry_alayer,cry_xmax,cry_ymax,cry_orient,cry_miscut,cry_bend,cry_cBend,
                        cry_sBend,cry_cpTilt,cry_spTilt,cry_cnTilt,cry_snTilt,iProc);

        x = result[0];
        xp = result[1];
        z = result[2];
        zp = result[3];
        p = result[4];
        iProc = result[5];
        free(result);

        s   = cry_rcurv*cry_sBend;
        zlm = cry_rcurv*cry_sBend;

        if (iProc != proc_out) {
            isimp    = 1;
            nhit     = nhit + 1.;
            val_part_hit     = 1.;
            val_part_impact   = x0;
            val_part_indiv    = xp0;
        }

    } else {

        if (x < 0) { // Crystal can be hit from below
            xp_tangent = sqrt((-(2.*x)*cry_rcurv + pow(x,2.))/(pow(cry_rcurv,2.)));
        } else {             // Crystal can be hit from above
            xp_tangent = asin((cry_rcurv*(1. - cry_cBend) - x)/sqrt(((2.*cry_rcurv)*(cry_rcurv - x))*(1 - cry_cBend) + pow(x,2.)));
        }
        // If the hit is below, the angle must be greater or equal than the tangent,
        // or if the hit is above, the angle must be smaller or equal than the tangent
        if ((x < 0. && xp >= xp_tangent) || (x >= 0. && xp <= xp_tangent)) {

            // If it hits the crystal, calculate in which point and apply the transformation and drift to that point
            a_eq  = 1 + pow(xp,2.);
            b_eq  = (2.*xp)*(x - cry_rcurv);
            c_eq  = -(2.*x)*cry_rcurv + pow(x,2.);
            delta = pow(b_eq,2.) - 4*(a_eq*c_eq);
            s_int = (-b_eq - sqrt(delta))/(2*a_eq);
            s_imp = s_int;

            // MISCUT first step: P coordinates (center of curvature of crystalline planes)
            double s_P_tmp = (cry_rcurv-cry_xmax)*sin(-cry_miscut);
            double x_P_tmp = cry_xmax + (cry_rcurv-cry_xmax)*cos(-cry_miscut);

            if (s_int < cry_rcurv*cry_sBend) {
                // Transform to a new reference system: shift and rotate
                x_int  = xp*s_int + x;
                xp_int = xp;
                z      = z + zp*s_int;
                x      = 0.;
                s      = 0.;

                tilt_int = s_int/cry_rcurv;
                xp    = xp-tilt_int;

                // MISCUT first step (bis): transform P in new reference system
                // Translation
                s_P_tmp = s_P_tmp - s_int;
                x_P_tmp = x_P_tmp - x_int;
                // Rotation
                double s_P = s_P_tmp*cos(tilt_int) + x_P_tmp*sin(tilt_int);
                double x_P = -s_P_tmp*sin(tilt_int) + x_P_tmp*cos(tilt_int);

                double* result = interact(rng,part,x,xp,z,zp,p,cry_length-(tilt_int*cry_rcurv),s_P,x_P,exenergy,rho,anuc,
                                zatom,emr,dlri,dlyi,ai,eum,collnt,hcut,csref0,csref1,csref5,bnref,
                                cry_tilt,cry_rcurv,cry_alayer,cry_xmax,cry_ymax,cry_orient,cry_miscut,
                                cry_bend,cry_cBend,cry_sBend,cry_cpTilt,cry_spTilt,cry_cnTilt,cry_snTilt,iProc);

                x = result[0];
                xp = result[1];
                z = result[2];
                zp = result[3];
                p = result[4];
                iProc = result[5];
                free(result);

                s   = cry_rcurv*sin(cry_bend - tilt_int);
                zlm = cry_rcurv*sin(cry_bend - tilt_int);
                
                if (iProc != proc_out) {
                    x_rot    = x_int;
                    s_rot    = s_int;
                    xp_rot   = xp_int;
                    s_shift  =  s_rot*cry_cnTilt + x_rot*cry_snTilt;
                    x_shift  = -s_rot*cry_snTilt + x_rot*cry_cnTilt;
                    xp_shift = xp_rot + cry_tilt;

                    if (cry_tilt < 0.) {
                        x0  = x_shift + shift;
                        xp0 = xp_shift;
                    }
                    else {
                        x0  = x_shift;
                        xp0 = xp_shift;
                    }
                    isimp     = 1;
                    nhit      = nhit + 1.;
                    val_part_hit    = 1.;
                    val_part_impact = x0;
                    val_part_indiv  = xp0;
                    //
                }

                // un-rotate
                x_temp  = x;
                s_temp  = s;
                xp_temp = xp;
                s       =  s_temp*cos(-tilt_int) + x_temp*sin(-tilt_int);
                x    = -s_temp*sin(-tilt_int) + x_temp*cos(-tilt_int);
                xp   = xp_temp + tilt_int;

                // 2nd: shift back the 2 axis
                x = x + x_int;
                s = s + s_int;
            } else {
                s = cry_rcurv*sin(cry_length/cry_rcurv);
                x = x + s*xp;
                z = z + s*zp;
            }
        } else {
            s = cry_rcurv*sin(cry_length/cry_rcurv);
            x = x + s*xp;
            z = z + s*zp;
        }
    }

    // transform back from the crystal to the collimator reference system
    // 1st: un-rotate the coordinates
    x_rot  = x;
    s_rot  = s;
    xp_rot = xp;

    s_shift  =  s_rot*cry_cnTilt + x_rot*cry_snTilt;
    x_shift  = -s_rot*cry_snTilt + x_rot*cry_cnTilt;
    xp_shift = xp_rot + cry_tilt;

    // 2nd: shift back the reference frame
    if (cry_tilt < 0) {
        s  = s_shift;
        x  = x_shift + shift;
        xp = xp_shift;
    } else {
        x  = x_shift;
        s  = s_shift;
        xp = xp_shift;
    }
    //

    // 3rd: shift to new S=Length position
    x = xp*(c_length - s) + x;
    z = zp*(c_length - s) + z;
    s = c_length;

    nabs = 0;
  
    if (iProc == proc_AM) {
        n_amorphous = n_amorphous + 1;
    } else if (iProc == proc_VR) {
        n_VR = n_VR + 1;
    } else if (iProc == proc_CH) {
        n_chan = n_chan + 1;
    } else if (iProc == proc_absorbed) {
        nabs = 1;   // TODO: do we need to set part_abs_pos etc?
    } else if (iProc == proc_ch_absorbed) {
        nabs = 1;
    }

    crystal_result[0] = val_part_hit;
    crystal_result[1] = val_part_abs;
    crystal_result[2] = val_part_impact;
    crystal_result[3] = val_part_indiv;
    crystal_result[4] = nhit;
    crystal_result[5] = nabs;
    crystal_result[6] = s_imp;
    crystal_result[7] = isimp;
    crystal_result[8] = s;
    crystal_result[9] = zlm;
    crystal_result[10] = x;
    crystal_result[11] = xp;
    crystal_result[12] = x0;
    crystal_result[13] = xp0;
    crystal_result[14] = z;
    crystal_result[15] = zp;
    crystal_result[16] = p;
    crystal_result[17] = iProc;
    crystal_result[18] = n_chan;
    crystal_result[19] = n_VR;
    crystal_result[20] = n_amorphous;
    return crystal_result;
}

#endif /* XCOLL_EVEREST_CRYSTAL_INTERACT_H */

// copyright ############################### #
// This file is part of the Xcoll Package.   #
// Copyright (c) CERN, 2023.                 #
// ######################################### #

#ifndef XCOLL_EVEREST_SCAT_CRY_H
#define XCOLL_EVEREST_SCAT_CRY_H
#include <math.h>
#include <stdlib.h>
#include <stdio.h>


 static inline
void scatter_cry(LocalParticle* part, double length, CrystalMaterialData material, RandomRutherfordData rng,
                 double cRot, double sRot, double c_aperture, double c_offset, int side, double cry_tilt,
                 double cry_rcurv, double cry_bend, double cry_alayer, double cry_xmax, double cry_ymax, 
                 double cry_orient, double cry_miscut, CollimatorImpactsData record, RecordIndex record_index){

    // Store initial coordinates for updating later
    double const rpp_in  = LocalParticle_get_rpp(part);
    double const rvv_in  = LocalParticle_get_rvv(part);
    double const e0      = LocalParticle_get_energy0(part) / 1e9; // Reference energy in GeV
    double const beta0   = LocalParticle_get_beta0(part);
    double const ptau_in = LocalParticle_get_ptau(part);
    double const x_in2    = LocalParticle_get_x(part);
    double const px_in2   = LocalParticle_get_px(part);
    double const y_in2    = LocalParticle_get_y(part);
    double const py_in2   = LocalParticle_get_py(part);
    double p0 = LocalParticle_get_p0c(part) / 1e9;


    double x_in  = LocalParticle_get_x(part);
    double xp_in = LocalParticle_get_px(part)*rpp_in;
    double y_in  = LocalParticle_get_y(part);
    double yp_in = LocalParticle_get_py(part)*rpp_in;
    double s_in = 0;   // s at start of collimator

    // TODO: missing correction due to m/m0 (but also wrong in xpart...)
    double p_in = p0*ptau_in + e0; // energy, not momentum, in GeV

    // Status flags
    int val_part_hit = 0;
    int val_part_abs = 0;
    int val_part_impact = -1;
    double val_part_indiv = -1.;
    // double val_part_linteract = -1.;

    p0 = e0;
    double x0     = 0;
    double xp0    = 0;
    double nhit   = 0;
    double nabs   = 0;
    double nnuc0  = 0;
    double ien0   = 0;
    double nnuc1  = 0;
    double ien1   = 0;
    double iProc  = 0;
    double n_chan = 0;
    double n_VR   = 0;
    double n_amorphous = 0;
    double s_imp  = 0;

    double x = x_in;
    double xp = xp_in;
    double z = y_in;
    double zp = yp_in;
    double p = p_in;

    double mirror = 1.;

    // TODO: use xtrack C-code for rotation element

    // Transform particle coordinates to get into collimator coordinate  system
    // First do rotation into collimator frame
    double const cRRot = cRot;
    double const sRRot = -sRot;
    x  =  x_in*cRot + sRot*y_in;
    z  =  y_in*cRot - sRot*x_in;
    xp = xp_in*cRot + sRot*yp_in;
    zp = yp_in*cRot - sRot*xp_in;

    // Output variables
    double x_out = x_in;
    double y_out = y_in;
    double xp_out = xp_in;
    double yp_out = yp_in;
    double p_out = p_in;


    // For one-sided collimators consider only positive X. For negative X jump to the next particle
    if (side==0 || (side==1 && x>=0.) || (side==2 && x<=0.)) {
        // Log input energy + nucleons as per the FLUKA coupling
        nnuc0 = nnuc0 + 1.;
        ien0 = ien0 + p_in * 1.0e3;

        // Now mirror at the horizontal axis for negative X offset
        if (x < 0) {
            mirror    = -1;
        } else {
            mirror    = 1;
        }
        x  = mirror*x;
        xp = mirror*xp;

        // Shift with opening and offset
        x = (x - c_aperture/2.) - mirror*c_offset;

        // particle passing above the jaw are discarded => take new event
        // entering by the face, shorten the length (zlm) and keep track of
        // entrance longitudinal coordinate (keeps) for histograms

        // The definition is that the collimator jaw is at x>=0.

        // 1) Check whether particle hits the collimator
        int isimp = 0;
        double s = 0.;
        double zlm = -1*length;

        double* crystal_result = crystal(rng, part, x,
                                xp,
                                z,
                                zp,
                                s,
                                p,
                                x0,
                                xp0,
                                zlm,
                                s_imp,
                                isimp,
                                val_part_hit,
                                val_part_abs,
                                val_part_impact,
                                val_part_indiv,
                                length,
                                material,
                                nhit,
                                nabs,
                                cry_tilt,
                                cry_rcurv,
                                cry_bend,
                                cry_alayer,
                                cry_xmax,
                                cry_ymax,
                                cry_orient,
                                cry_miscut,
                                iProc,
                                n_chan,
                                n_VR,
                                n_amorphous
                                );

        val_part_hit = crystal_result[0];
        val_part_abs = crystal_result[1];
        val_part_impact = crystal_result[2];
        val_part_indiv = crystal_result[3];
        nhit = crystal_result[4];
        nabs = crystal_result[5];
        s_imp = crystal_result[6];
        isimp = crystal_result[7];
        s = crystal_result[8];
        zlm = crystal_result[9];
        x = crystal_result[10];
        xp = crystal_result[11];
        x0 = crystal_result[12];
        xp0 = crystal_result[13];
        z = crystal_result[14];
        zp = crystal_result[15];
        p = crystal_result[16];
        iProc = crystal_result[17];
        n_chan = crystal_result[18];
        n_VR = crystal_result[19];
        n_amorphous = crystal_result[20];

        free(crystal_result);

        if (nabs != 0.) {
            val_part_abs = 1.;
            // val_part_linteract = zlm;
        }
        s_imp  = (s - length) + s_imp;

        // Transform back to particle coordinates with opening and offset
        x   = (x + c_aperture/2) + mirror*c_offset;

        // Now mirror at the horizontal axis for negative X offset
        x  = mirror * x;
        xp = mirror * xp;

        // Last do rotation into collimator frame
        x_out  =  x*cRRot +  z*sRRot;
        y_out  =  z*cRRot -  x*sRRot;
        xp_out = xp*cRRot + zp*sRRot;
        yp_out = zp*cRRot - xp*sRRot;

        // Log output energy + nucleons as per the FLUKA coupling
        // Do not log dead particles
        nnuc1       = nnuc1 + 1;                           // outcoming nucleons
        ien1        = ien1  + p_out * 1e3;                 // outcoming energy

        p_out = p;
        s_in = s_in + s;
    }

    LocalParticle_set_x(part, x_out);
    LocalParticle_set_px(part, xp_out/rpp_in);
    LocalParticle_set_y(part, y_out);
    LocalParticle_set_py(part, yp_out/rpp_in);

    double energy_out = p_out;       //  Cannot assign energy directly to LocalParticle as it would update dependent variables, but needs to be corrected first!

    // Update energy    ---------------------------------------------------
    // Only particles that hit the jaw and survived need to be updated
    if (val_part_hit>0 && val_part_abs==0){
        double ptau_out = (energy_out - e0) / (e0 * beta0);
        LocalParticle_update_ptau(part, ptau_out);
    }

    // Update 4D coordinates    -------------------------------------------
    // Absorbed particles get their coordinates set to the entrance of collimator
    if (val_part_abs>0){
        LocalParticle_set_x(part, x_in2);
        LocalParticle_set_px(part, px_in2);
        LocalParticle_set_y(part, y_in2);
        LocalParticle_set_py(part, py_in2);
    }

    // Update longitudinal coordinate zeta    -----------------------------
    // Absorbed particles keep coordinates at the entrance of collimator, others need correcting:
    // Non-hit particles are just drifting (zeta not yet drifted in K2, so do here)
    if (val_part_hit==0){
        LocalParticle_add_to_zeta(part, drift_zeta_single(rvv_in, px_in2*rpp_in, py_in2*rpp_in, length) );
    }
    // Hit and survived particles need correcting:
    if (val_part_hit>0 && val_part_abs==0){
        double px  = LocalParticle_get_px(part);
        double py  = LocalParticle_get_py(part);
        double rvv = LocalParticle_get_rvv(part);
        double rpp = LocalParticle_get_rpp(part);
        // First we drift half the length with the old angles:
        LocalParticle_add_to_zeta(part, drift_zeta_single(rvv_in, px_in2*rpp_in, py_in2*rpp_in, length/2) );
        // then half the length with the new angles:
        LocalParticle_add_to_zeta(part, drift_zeta_single(rvv, px*rpp, py*rpp, length/2) );
    }

    // Update s    --------------------------------------------------------
    // TODO: move absorbed particles to last impact location
    if (val_part_abs==0){
        LocalParticle_add_to_s(part, length);
    }

    // Update state    ----------------------------------------------------
    if (val_part_abs > 0){
        LocalParticle_set_state(part, XC_LOST_ON_EVEREST_CRYSTAL);
    }

    return;

}

#endif /* XCOLL_EVEREST_SCAT_CRY_H */

#ifndef XOBJ_TYPEDEF_BlackAbsorberData
#define XOBJ_TYPEDEF_BlackAbsorberData
typedef   struct BlackAbsorberData_s * BlackAbsorberData;
 static inline BlackAbsorberData BlackAbsorberData_getp(BlackAbsorberData  obj){
  int64_t offset=0;
  return (BlackAbsorberData)(( char*) obj+offset);
}
 static inline double BlackAbsorberData_get_inactive_front(const BlackAbsorberData  obj){
  int64_t offset=0;
  return *( double*)(( char*) obj+offset);
}
 static inline void BlackAbsorberData_set_inactive_front(BlackAbsorberData  obj, double value){
  int64_t offset=0;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* BlackAbsorberData_getp_inactive_front(BlackAbsorberData  obj){
  int64_t offset=0;
  return ( double*)(( char*) obj+offset);
}
 static inline double BlackAbsorberData_get_active_length(const BlackAbsorberData  obj){
  int64_t offset=0;
  offset+=8;
  return *( double*)(( char*) obj+offset);
}
 static inline void BlackAbsorberData_set_active_length(BlackAbsorberData  obj, double value){
  int64_t offset=0;
  offset+=8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* BlackAbsorberData_getp_active_length(BlackAbsorberData  obj){
  int64_t offset=0;
  offset+=8;
  return ( double*)(( char*) obj+offset);
}
 static inline double BlackAbsorberData_get_inactive_back(const BlackAbsorberData  obj){
  int64_t offset=0;
  offset+=16;
  return *( double*)(( char*) obj+offset);
}
 static inline void BlackAbsorberData_set_inactive_back(BlackAbsorberData  obj, double value){
  int64_t offset=0;
  offset+=16;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* BlackAbsorberData_getp_inactive_back(BlackAbsorberData  obj){
  int64_t offset=0;
  offset+=16;
  return ( double*)(( char*) obj+offset);
}
 static inline double BlackAbsorberData_get_jaw_L(const BlackAbsorberData  obj){
  int64_t offset=0;
  offset+=24;
  return *( double*)(( char*) obj+offset);
}
 static inline void BlackAbsorberData_set_jaw_L(BlackAbsorberData  obj, double value){
  int64_t offset=0;
  offset+=24;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* BlackAbsorberData_getp_jaw_L(BlackAbsorberData  obj){
  int64_t offset=0;
  offset+=24;
  return ( double*)(( char*) obj+offset);
}
 static inline double BlackAbsorberData_get_jaw_R(const BlackAbsorberData  obj){
  int64_t offset=0;
  offset+=32;
  return *( double*)(( char*) obj+offset);
}
 static inline void BlackAbsorberData_set_jaw_R(BlackAbsorberData  obj, double value){
  int64_t offset=0;
  offset+=32;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* BlackAbsorberData_getp_jaw_R(BlackAbsorberData  obj){
  int64_t offset=0;
  offset+=32;
  return ( double*)(( char*) obj+offset);
}
 static inline double BlackAbsorberData_get_ref_x(const BlackAbsorberData  obj){
  int64_t offset=0;
  offset+=40;
  return *( double*)(( char*) obj+offset);
}
 static inline void BlackAbsorberData_set_ref_x(BlackAbsorberData  obj, double value){
  int64_t offset=0;
  offset+=40;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* BlackAbsorberData_getp_ref_x(BlackAbsorberData  obj){
  int64_t offset=0;
  offset+=40;
  return ( double*)(( char*) obj+offset);
}
 static inline double BlackAbsorberData_get_ref_y(const BlackAbsorberData  obj){
  int64_t offset=0;
  offset+=48;
  return *( double*)(( char*) obj+offset);
}
 static inline void BlackAbsorberData_set_ref_y(BlackAbsorberData  obj, double value){
  int64_t offset=0;
  offset+=48;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* BlackAbsorberData_getp_ref_y(BlackAbsorberData  obj){
  int64_t offset=0;
  offset+=48;
  return ( double*)(( char*) obj+offset);
}
 static inline double BlackAbsorberData_get_sin_zL(const BlackAbsorberData  obj){
  int64_t offset=0;
  offset+=56;
  return *( double*)(( char*) obj+offset);
}
 static inline void BlackAbsorberData_set_sin_zL(BlackAbsorberData  obj, double value){
  int64_t offset=0;
  offset+=56;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* BlackAbsorberData_getp_sin_zL(BlackAbsorberData  obj){
  int64_t offset=0;
  offset+=56;
  return ( double*)(( char*) obj+offset);
}
 static inline double BlackAbsorberData_get_cos_zL(const BlackAbsorberData  obj){
  int64_t offset=0;
  offset+=64;
  return *( double*)(( char*) obj+offset);
}
 static inline void BlackAbsorberData_set_cos_zL(BlackAbsorberData  obj, double value){
  int64_t offset=0;
  offset+=64;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* BlackAbsorberData_getp_cos_zL(BlackAbsorberData  obj){
  int64_t offset=0;
  offset+=64;
  return ( double*)(( char*) obj+offset);
}
 static inline double BlackAbsorberData_get_sin_zR(const BlackAbsorberData  obj){
  int64_t offset=0;
  offset+=72;
  return *( double*)(( char*) obj+offset);
}
 static inline void BlackAbsorberData_set_sin_zR(BlackAbsorberData  obj, double value){
  int64_t offset=0;
  offset+=72;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* BlackAbsorberData_getp_sin_zR(BlackAbsorberData  obj){
  int64_t offset=0;
  offset+=72;
  return ( double*)(( char*) obj+offset);
}
 static inline double BlackAbsorberData_get_cos_zR(const BlackAbsorberData  obj){
  int64_t offset=0;
  offset+=80;
  return *( double*)(( char*) obj+offset);
}
 static inline void BlackAbsorberData_set_cos_zR(BlackAbsorberData  obj, double value){
  int64_t offset=0;
  offset+=80;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* BlackAbsorberData_getp_cos_zR(BlackAbsorberData  obj){
  int64_t offset=0;
  offset+=80;
  return ( double*)(( char*) obj+offset);
}
 static inline double BlackAbsorberData_get_sin_yL(const BlackAbsorberData  obj){
  int64_t offset=0;
  offset+=88;
  return *( double*)(( char*) obj+offset);
}
 static inline void BlackAbsorberData_set_sin_yL(BlackAbsorberData  obj, double value){
  int64_t offset=0;
  offset+=88;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* BlackAbsorberData_getp_sin_yL(BlackAbsorberData  obj){
  int64_t offset=0;
  offset+=88;
  return ( double*)(( char*) obj+offset);
}
 static inline double BlackAbsorberData_get_cos_yL(const BlackAbsorberData  obj){
  int64_t offset=0;
  offset+=96;
  return *( double*)(( char*) obj+offset);
}
 static inline void BlackAbsorberData_set_cos_yL(BlackAbsorberData  obj, double value){
  int64_t offset=0;
  offset+=96;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* BlackAbsorberData_getp_cos_yL(BlackAbsorberData  obj){
  int64_t offset=0;
  offset+=96;
  return ( double*)(( char*) obj+offset);
}
 static inline double BlackAbsorberData_get_tan_yL(const BlackAbsorberData  obj){
  int64_t offset=0;
  offset+=104;
  return *( double*)(( char*) obj+offset);
}
 static inline void BlackAbsorberData_set_tan_yL(BlackAbsorberData  obj, double value){
  int64_t offset=0;
  offset+=104;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* BlackAbsorberData_getp_tan_yL(BlackAbsorberData  obj){
  int64_t offset=0;
  offset+=104;
  return ( double*)(( char*) obj+offset);
}
 static inline double BlackAbsorberData_get_sin_yR(const BlackAbsorberData  obj){
  int64_t offset=0;
  offset+=112;
  return *( double*)(( char*) obj+offset);
}
 static inline void BlackAbsorberData_set_sin_yR(BlackAbsorberData  obj, double value){
  int64_t offset=0;
  offset+=112;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* BlackAbsorberData_getp_sin_yR(BlackAbsorberData  obj){
  int64_t offset=0;
  offset+=112;
  return ( double*)(( char*) obj+offset);
}
 static inline double BlackAbsorberData_get_cos_yR(const BlackAbsorberData  obj){
  int64_t offset=0;
  offset+=120;
  return *( double*)(( char*) obj+offset);
}
 static inline void BlackAbsorberData_set_cos_yR(BlackAbsorberData  obj, double value){
  int64_t offset=0;
  offset+=120;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* BlackAbsorberData_getp_cos_yR(BlackAbsorberData  obj){
  int64_t offset=0;
  offset+=120;
  return ( double*)(( char*) obj+offset);
}
 static inline double BlackAbsorberData_get_tan_yR(const BlackAbsorberData  obj){
  int64_t offset=0;
  offset+=128;
  return *( double*)(( char*) obj+offset);
}
 static inline void BlackAbsorberData_set_tan_yR(BlackAbsorberData  obj, double value){
  int64_t offset=0;
  offset+=128;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* BlackAbsorberData_getp_tan_yR(BlackAbsorberData  obj){
  int64_t offset=0;
  offset+=128;
  return ( double*)(( char*) obj+offset);
}
 static inline int8_t BlackAbsorberData_get__side(const BlackAbsorberData  obj){
  int64_t offset=0;
  offset+=136;
  return *(( int8_t*) obj+offset);
}
 static inline void BlackAbsorberData_set__side(BlackAbsorberData  obj, int8_t value){
  int64_t offset=0;
  offset+=136;
  *(( int8_t*) obj+offset)=value;
}
 static inline  int8_t* BlackAbsorberData_getp__side(BlackAbsorberData  obj){
  int64_t offset=0;
  offset+=136;
  return ( int8_t*)(( char*) obj+offset);
}
 static inline int8_t BlackAbsorberData_get_active(const BlackAbsorberData  obj){
  int64_t offset=0;
  offset+=144;
  return *(( int8_t*) obj+offset);
}
 static inline void BlackAbsorberData_set_active(BlackAbsorberData  obj, int8_t value){
  int64_t offset=0;
  offset+=144;
  *(( int8_t*) obj+offset)=value;
}
 static inline  int8_t* BlackAbsorberData_getp_active(BlackAbsorberData  obj){
  int64_t offset=0;
  offset+=144;
  return ( int8_t*)(( char*) obj+offset);
}
 static inline RecordIdentifier BlackAbsorberData_getp__internal_record_id(BlackAbsorberData  obj){
  int64_t offset=0;
  offset+=152;
  return (RecordIdentifier)(( char*) obj+offset);
}
 static inline int64_t BlackAbsorberData_get__internal_record_id_buffer_id(const BlackAbsorberData  obj){
  int64_t offset=0;
  offset+=152;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void BlackAbsorberData_set__internal_record_id_buffer_id(BlackAbsorberData  obj, int64_t value){
  int64_t offset=0;
  offset+=152;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* BlackAbsorberData_getp__internal_record_id_buffer_id(BlackAbsorberData  obj){
  int64_t offset=0;
  offset+=152;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline int64_t BlackAbsorberData_get__internal_record_id_offset(const BlackAbsorberData  obj){
  int64_t offset=0;
  offset+=160;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void BlackAbsorberData_set__internal_record_id_offset(BlackAbsorberData  obj, int64_t value){
  int64_t offset=0;
  offset+=160;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* BlackAbsorberData_getp__internal_record_id_offset(BlackAbsorberData  obj){
  int64_t offset=0;
  offset+=160;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline int8_t BlackAbsorberData_get__tracking(const BlackAbsorberData  obj){
  int64_t offset=0;
  offset+=168;
  return *(( int8_t*) obj+offset);
}
 static inline void BlackAbsorberData_set__tracking(BlackAbsorberData  obj, int8_t value){
  int64_t offset=0;
  offset+=168;
  *(( int8_t*) obj+offset)=value;
}
 static inline  int8_t* BlackAbsorberData_getp__tracking(BlackAbsorberData  obj){
  int64_t offset=0;
  offset+=168;
  return ( int8_t*)(( char*) obj+offset);
}
#endif
// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_CONSTANTS_H
#define XTRACK_CONSTANTS_H

#if !defined( C_LIGHT )
    #define   C_LIGHT ( 299792458.0 )
#endif /* !defined( C_LIGHT ) */

#if !defined( EPSILON_0 )
    #define   EPSILON_0 (8.854187817620e-12)
#endif /* !defined( EPSILON_0 ) */

#if !defined( PI )
    #define PI (3.1415926535897932384626433832795028841971693993751)
#endif /* !defined( PI ) */

#if !defined( MU_0 )
    #define MU_0 (PI*4.0e-7)
#endif /* !defined( MU_0 ) */

#if !defined( DEG2RAD )
    #define DEG2RAD (0.0174532925199432957692369076848861271344287188854)
#endif /* !defiend( DEG2RAD ) */

#if !defined( RAD2DEG )
    #define RAD2DEG (57.29577951308232087679815481410517033240547246656442)
#endif /* !defiend( RAD2DEG ) */

#if !defined( SQRT_PI )
    #define SQRT_PI (1.7724538509055160272981674833411451827975494561224)
#endif /* !defined( SQRT_PI ) */

#if !defined( QELEM )
    #define QELEM (1.60217662e-19)
#endif /* !defined( QELEM ) */

#if !defined( DBL_MAX )
    #define DBL_MAX (1.7976931348623158e+308)
#endif /* !defined( DBL_MAX ) */

#if !defined( DBL_MIN )
    #define DBL_MIN (2.2250738585072014e-308)
#endif /* !defined( DBL_MIN ) */

#if !defined( DBL_EPSILON )
    #define DBL_EPSILON (2.2204460492503131e-16)
#endif /* !defined( DBL_EPSILON ) */

#endif /* XTRACK_CONSTANTS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_FUNCTIONS_H
#define XTRACK_FUNCTIONS_H

 static inline
void kill_all_particles(LocalParticle* part0, int64_t kill_state) {

    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

        LocalParticle_kill_particle(part, kill_state);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

}


 static inline
int8_t assert_tracking(LocalParticle* part, int64_t kill_state){
    // Whenever we are not tracking, e.g. in a twiss, the particle will be at_turn < 0.
    // We test this to distinguish genuine tracking from twiss.
    if (LocalParticle_get_at_turn(part) < 0){
        LocalParticle_kill_particle(part, kill_state);
        return 0;
    }
    return 1;
}

#endif /* XTRACK_FUNCTIONS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_PARTICLE_STATES_H
#define XTRACK_PARTICLE_STATES_H

#define  XT_LOST_ON_APERTURE       0
#define  XT_LOST_ON_LONG_CUT      -2
#define  XT_LOST_ALL_E_IN_SYNRAD -10
#define  RNG_ERR_SEEDS_NOT_SET   -20
#define  RNG_ERR_INVALID_TRACK   -21
#define  RNG_ERR_RUTH_NOT_SET    -22

#endif /* XTRACK_PARTICLE_STATES_H */


 static inline
CollimatorImpactsData BlackAbsorberData_getp_internal_record(BlackAbsorberData el, LocalParticle* part){
    RecordIdentifier record_id = BlackAbsorberData_getp__internal_record_id(el);
    if (RecordIdentifier_get_buffer_id(record_id) <= 0){
        return NULL;
    }
    else{
        return (CollimatorImpactsData) RecordIdentifier_getp_record(record_id, part);
    }
    }
    
#ifndef XCOLL_ABSORBER_H
#define XCOLL_ABSORBER_H

 static inline
int64_t is_within_aperture(LocalParticle* part, double jaw_L, double jaw_R){
    double const x = LocalParticle_get_x(part);
    return (int64_t)((x > jaw_R) && (x < jaw_L) );
}

 static inline
void BlackAbsorber_track_local_particle(BlackAbsorberData el, LocalParticle* part0){

    // Collimator active and length
    int8_t is_active = BlackAbsorberData_get_active(el);
    is_active       *= BlackAbsorberData_get__tracking(el);
    double const inactive_front = BlackAbsorberData_get_inactive_front(el);
    double const inactive_back = BlackAbsorberData_get_inactive_back(el);
    double const active_length = BlackAbsorberData_get_active_length(el);
    // Collimator jaws
    double const jaw_L  = BlackAbsorberData_get_jaw_L(el);
    double const jaw_R  = BlackAbsorberData_get_jaw_R(el);
    double const sin_yL = BlackAbsorberData_get_sin_yL(el);
    double const sin_yR = BlackAbsorberData_get_sin_yR(el);
    double const jaw_LU = jaw_L - sin_yL*active_length/2.;
    double const jaw_LD = jaw_L + sin_yL*active_length/2.;
    double const jaw_RU = jaw_R - sin_yR*active_length/2.;
    double const jaw_RD = jaw_R + sin_yR*active_length/2.;
    // TODO: need shortening of active length!
    // Collimator reference frame
    double const sin_zL = BlackAbsorberData_get_sin_zL(el);
    double const cos_zL = BlackAbsorberData_get_cos_zL(el);
    double const sin_zR = BlackAbsorberData_get_sin_zR(el);
    double const cos_zR = BlackAbsorberData_get_cos_zR(el);
    if (fabs(sin_zL-sin_zR) > 1.e-10 || fabs(cos_zL-cos_zR) > 1.e-10 ){
        kill_all_particles(part0, XC_ERR_NOT_IMPLEMENTED);
    };
    double const dx = BlackAbsorberData_get_ref_x(el);
    double const dy = BlackAbsorberData_get_ref_y(el);
    // Impact table
    CollimatorImpactsData record = BlackAbsorberData_getp_internal_record(el, part0);
    RecordIndex record_index = NULL;
    if (record){
        record_index = CollimatorImpactsData_getp__index(record);
    }


    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp


        // Go to collimator reference system (centered around orbit)
        XYShift_single_particle(part, dx, dy);
        SRotation_single_particle(part, sin_zL, cos_zL);

        int64_t is_alive = 1;

        if (!is_active){

            // If collimator not active, replace with drift
            Drift_single_particle(part, inactive_front+active_length+inactive_back);

        } else {

            int8_t is_tracking = assert_tracking(part, XC_ERR_INVALID_TRACK);
            if (is_tracking) {
           
            // Drift inactive length before jaw
            Drift_single_particle(part, inactive_front);

            // Store transversal coordinates for potential backtracking later
            double x_F = LocalParticle_get_x(part);
//             double y_F = LocalParticle_get_y(part);

            // Check if hit on the collimator jaw at the front
            is_alive = is_within_aperture(part, jaw_LU, jaw_RU);

            // Continue if the particle didn't hit the collimator
            if (is_alive){

                // Drift the jaw length
                Drift_single_particle(part, active_length);

                // Check if hit on the collimator jaw at the back
                is_alive = is_within_aperture(part, jaw_LD, jaw_RD);

                // TODO: is there a performance difference with nesting the ifs or not?
                // Continue if the particle didn't hit the collimator
                if (is_alive){

                    // Drift inactive length after jaw
                    Drift_single_particle(part, inactive_back);

                } else {

                    // Backtrack to the particle position of impact
                    // This should only be done if the particle did NOT hit the front jaw
                    double x_B = LocalParticle_get_x(part);
//                     double y_B = LocalParticle_get_y(part);
                    double length;

                    if (x_B > 0){        // Left jaw
                        length = (jaw_LD - x_B) / (jaw_LD - jaw_LU - x_B + x_F) * active_length;
                    } else if (x_B < 0){ // Right jaw
                        length = (jaw_RD - x_B) / (jaw_RD - jaw_RU - x_B + x_F) * active_length;
                    // TODO: check this
//                     } else if (y_B > 0){ // Upper jaw
//                         length = (y_B - jaw_U) / (y_B - y_F) * active_length;
//                     } else if (y_B < 0){ // Lower jaw
//                         length = (y_B - jaw_D) / (y_B - y_F) * active_length;
                    } else {
                        length = 0;
                    }
                    Drift_single_particle(part, -length);

                }
            }
            }
        }

        // Move back from collimator reference system
        SRotation_single_particle(part, -sin_zL, cos_zL);
        XYShift_single_particle(part, -dx, -dy);

        // Update dead particles
        if (!is_alive){

            LocalParticle_set_state(part, XC_LOST_ON_ABSORBER);

            // Record impact data
            if (record){
                // Get a slot in the record (this is thread safe)
                int64_t i_slot = RecordIndex_get_slot(record_index);
                // The returned slot id is negative if record is NULL or if record is full

                if (i_slot>=0){
                    double mass_ratio = LocalParticle_get_charge_ratio(part) / LocalParticle_get_chi(part);
                    double energy = ( LocalParticle_get_ptau(part) + 1 / LocalParticle_get_beta0(part)
                                     ) * mass_ratio * LocalParticle_get_p0c(part);

                    CollimatorImpactsData_set_at_element(record, i_slot, LocalParticle_get_at_element(part));
                    CollimatorImpactsData_set_at_turn(record, i_slot, LocalParticle_get_at_turn(part));
                    CollimatorImpactsData_set_s(record, i_slot, LocalParticle_get_s(part));
                    CollimatorImpactsData_set_interaction_type(record, i_slot, -1);

                    CollimatorImpactsData_set_parent_id(record, i_slot, LocalParticle_get_particle_id(part));
                    CollimatorImpactsData_set_parent_x(record, i_slot, LocalParticle_get_x(part));
                    CollimatorImpactsData_set_parent_px(record, i_slot, LocalParticle_get_px(part));
                    CollimatorImpactsData_set_parent_y(record, i_slot, LocalParticle_get_y(part));
                    CollimatorImpactsData_set_parent_py(record, i_slot, LocalParticle_get_py(part));
                    CollimatorImpactsData_set_parent_zeta(record, i_slot, LocalParticle_get_zeta(part));
                    CollimatorImpactsData_set_parent_delta(record, i_slot, LocalParticle_get_delta(part));
                    CollimatorImpactsData_set_parent_energy(record, i_slot, energy);
                    // TODO: particle info
                    CollimatorImpactsData_set_parent_mass(record, i_slot, -1);
                    CollimatorImpactsData_set_parent_charge(record, i_slot, 1);
                    CollimatorImpactsData_set_parent_z(record, i_slot, -1);
                    CollimatorImpactsData_set_parent_a(record, i_slot, -1);
                    CollimatorImpactsData_set_parent_pdgid(record, i_slot, -1);

                    CollimatorImpactsData_set_child_id(record, i_slot, -1);
                    // We need to fill in child data, or the arrays will not have the same length
                    // (when secondaries are be made elsewhere in other collimators)
                    CollimatorImpactsData_set_child_x(record, i_slot, -1);
                    CollimatorImpactsData_set_child_px(record, i_slot, -1);
                    CollimatorImpactsData_set_child_y(record, i_slot, -1);
                    CollimatorImpactsData_set_child_py(record, i_slot, -1);
                    CollimatorImpactsData_set_child_zeta(record, i_slot, -1);
                    CollimatorImpactsData_set_child_delta(record, i_slot, -1);
                    CollimatorImpactsData_set_child_energy(record, i_slot, -1);
                    CollimatorImpactsData_set_child_mass(record, i_slot, -1);
                    CollimatorImpactsData_set_child_charge(record, i_slot, -1);
                    CollimatorImpactsData_set_child_z(record, i_slot, -1);
                    CollimatorImpactsData_set_child_a(record, i_slot, -1);
                    CollimatorImpactsData_set_child_pdgid(record, i_slot, -1);
                }
            }
        }


//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }


}

#endif


             
            void BlackAbsorber_track_particles(
               BlackAbsorberData el,

                             ParticlesData particles,

                             int64_t flag_increment_at_element,
                  int8_t* io_buffer){
//            const int num_threads = omp_get_max_threads();                                 //only_for_context cpu_openmp
//            const int64_t capacity = ParticlesData_get__capacity(particles);               //only_for_context cpu_openmp
//            const int64_t chunk_size = (capacity + num_threads - 1)/num_threads; // ceil division  //only_for_context cpu_openmp
//            #pragma omp parallel for                                                       //only_for_context cpu_openmp
//            for (int64_t batch_id = 0; batch_id < num_threads; batch_id++) {               //only_for_context cpu_openmp
                LocalParticle lpart;
                lpart.io_buffer = io_buffer;
//                int64_t part_id = batch_id * chunk_size;                                       //only_for_context cpu_openmp
//                int64_t end_id = (batch_id + 1) * chunk_size;                                  //only_for_context cpu_openmp
//                if (end_id > capacity) end_id = capacity;                                      //only_for_context cpu_openmp
    
                int64_t part_id = 0;                    //only_for_context cpu_serial
//                int64_t part_id = blockDim.x * blockIdx.x + threadIdx.x; //only_for_context cuda
//                int64_t part_id = get_global_id(0);                    //only_for_context opencl
                int64_t end_id = 0; // unused outside of openmp  //only_for_context cpu_serial cuda opencl
    
                int64_t part_capacity = ParticlesData_get__capacity(particles);
                if (part_id<part_capacity){
                    Particles_to_LocalParticle(particles, &lpart, part_id, end_id);
                    if (check_is_active(&lpart)>0){
              BlackAbsorber_track_local_particle(el, &lpart);

                    }
                    if (check_is_active(&lpart)>0 && flag_increment_at_element){
                            increment_at_element(&lpart, 1);
                    }
                }
//            } //only_for_context cpu_openmp
            
            // On OpenMP we want to additionally by default reorganize all
            // the particles.
//            #ifndef XT_OMP_SKIP_REORGANIZE                             //only_for_context cpu_openmp
//            LocalParticle lpart;                                       //only_for_context cpu_openmp
//            lpart.io_buffer = io_buffer;                               //only_for_context cpu_openmp
//            Particles_to_LocalParticle(particles, &lpart, 0, capacity);//only_for_context cpu_openmp
//            check_is_active(&lpart);                                   //only_for_context cpu_openmp
//            #endif                                                     //only_for_context cpu_openmp
        }

#ifndef XOBJ_TYPEDEF_EverestCollimatorData
#define XOBJ_TYPEDEF_EverestCollimatorData
typedef   struct EverestCollimatorData_s * EverestCollimatorData;
 static inline EverestCollimatorData EverestCollimatorData_getp(EverestCollimatorData  obj){
  int64_t offset=0;
  return (EverestCollimatorData)(( char*) obj+offset);
}
 static inline double EverestCollimatorData_get_inactive_front(const EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=8;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCollimatorData_set_inactive_front(EverestCollimatorData  obj, double value){
  int64_t offset=0;
  offset+=8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCollimatorData_getp_inactive_front(EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=8;
  return ( double*)(( char*) obj+offset);
}
 static inline double EverestCollimatorData_get_active_length(const EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=16;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCollimatorData_set_active_length(EverestCollimatorData  obj, double value){
  int64_t offset=0;
  offset+=16;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCollimatorData_getp_active_length(EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=16;
  return ( double*)(( char*) obj+offset);
}
 static inline double EverestCollimatorData_get_inactive_back(const EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=24;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCollimatorData_set_inactive_back(EverestCollimatorData  obj, double value){
  int64_t offset=0;
  offset+=24;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCollimatorData_getp_inactive_back(EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=24;
  return ( double*)(( char*) obj+offset);
}
 static inline double EverestCollimatorData_get_jaw_L(const EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=32;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCollimatorData_set_jaw_L(EverestCollimatorData  obj, double value){
  int64_t offset=0;
  offset+=32;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCollimatorData_getp_jaw_L(EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=32;
  return ( double*)(( char*) obj+offset);
}
 static inline double EverestCollimatorData_get_jaw_R(const EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=40;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCollimatorData_set_jaw_R(EverestCollimatorData  obj, double value){
  int64_t offset=0;
  offset+=40;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCollimatorData_getp_jaw_R(EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=40;
  return ( double*)(( char*) obj+offset);
}
 static inline double EverestCollimatorData_get_ref_x(const EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=48;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCollimatorData_set_ref_x(EverestCollimatorData  obj, double value){
  int64_t offset=0;
  offset+=48;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCollimatorData_getp_ref_x(EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=48;
  return ( double*)(( char*) obj+offset);
}
 static inline double EverestCollimatorData_get_ref_y(const EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=56;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCollimatorData_set_ref_y(EverestCollimatorData  obj, double value){
  int64_t offset=0;
  offset+=56;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCollimatorData_getp_ref_y(EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=56;
  return ( double*)(( char*) obj+offset);
}
 static inline double EverestCollimatorData_get_sin_zL(const EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=64;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCollimatorData_set_sin_zL(EverestCollimatorData  obj, double value){
  int64_t offset=0;
  offset+=64;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCollimatorData_getp_sin_zL(EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=64;
  return ( double*)(( char*) obj+offset);
}
 static inline double EverestCollimatorData_get_cos_zL(const EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=72;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCollimatorData_set_cos_zL(EverestCollimatorData  obj, double value){
  int64_t offset=0;
  offset+=72;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCollimatorData_getp_cos_zL(EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=72;
  return ( double*)(( char*) obj+offset);
}
 static inline double EverestCollimatorData_get_sin_zR(const EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=80;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCollimatorData_set_sin_zR(EverestCollimatorData  obj, double value){
  int64_t offset=0;
  offset+=80;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCollimatorData_getp_sin_zR(EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=80;
  return ( double*)(( char*) obj+offset);
}
 static inline double EverestCollimatorData_get_cos_zR(const EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=88;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCollimatorData_set_cos_zR(EverestCollimatorData  obj, double value){
  int64_t offset=0;
  offset+=88;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCollimatorData_getp_cos_zR(EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=88;
  return ( double*)(( char*) obj+offset);
}
 static inline double EverestCollimatorData_get_sin_yL(const EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=96;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCollimatorData_set_sin_yL(EverestCollimatorData  obj, double value){
  int64_t offset=0;
  offset+=96;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCollimatorData_getp_sin_yL(EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=96;
  return ( double*)(( char*) obj+offset);
}
 static inline double EverestCollimatorData_get_cos_yL(const EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=104;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCollimatorData_set_cos_yL(EverestCollimatorData  obj, double value){
  int64_t offset=0;
  offset+=104;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCollimatorData_getp_cos_yL(EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=104;
  return ( double*)(( char*) obj+offset);
}
 static inline double EverestCollimatorData_get_tan_yL(const EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=112;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCollimatorData_set_tan_yL(EverestCollimatorData  obj, double value){
  int64_t offset=0;
  offset+=112;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCollimatorData_getp_tan_yL(EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=112;
  return ( double*)(( char*) obj+offset);
}
 static inline double EverestCollimatorData_get_sin_yR(const EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=120;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCollimatorData_set_sin_yR(EverestCollimatorData  obj, double value){
  int64_t offset=0;
  offset+=120;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCollimatorData_getp_sin_yR(EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=120;
  return ( double*)(( char*) obj+offset);
}
 static inline double EverestCollimatorData_get_cos_yR(const EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=128;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCollimatorData_set_cos_yR(EverestCollimatorData  obj, double value){
  int64_t offset=0;
  offset+=128;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCollimatorData_getp_cos_yR(EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=128;
  return ( double*)(( char*) obj+offset);
}
 static inline double EverestCollimatorData_get_tan_yR(const EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=136;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCollimatorData_set_tan_yR(EverestCollimatorData  obj, double value){
  int64_t offset=0;
  offset+=136;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCollimatorData_getp_tan_yR(EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=136;
  return ( double*)(( char*) obj+offset);
}
 static inline int8_t EverestCollimatorData_get__side(const EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=144;
  return *(( int8_t*) obj+offset);
}
 static inline void EverestCollimatorData_set__side(EverestCollimatorData  obj, int8_t value){
  int64_t offset=0;
  offset+=144;
  *(( int8_t*) obj+offset)=value;
}
 static inline  int8_t* EverestCollimatorData_getp__side(EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=144;
  return ( int8_t*)(( char*) obj+offset);
}
 static inline int8_t EverestCollimatorData_get_active(const EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=152;
  return *(( int8_t*) obj+offset);
}
 static inline void EverestCollimatorData_set_active(EverestCollimatorData  obj, int8_t value){
  int64_t offset=0;
  offset+=152;
  *(( int8_t*) obj+offset)=value;
}
 static inline  int8_t* EverestCollimatorData_getp_active(EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=152;
  return ( int8_t*)(( char*) obj+offset);
}
 static inline RecordIdentifier EverestCollimatorData_getp__internal_record_id(EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=160;
  return (RecordIdentifier)(( char*) obj+offset);
}
 static inline int64_t EverestCollimatorData_get__internal_record_id_buffer_id(const EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=160;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void EverestCollimatorData_set__internal_record_id_buffer_id(EverestCollimatorData  obj, int64_t value){
  int64_t offset=0;
  offset+=160;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* EverestCollimatorData_getp__internal_record_id_buffer_id(EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=160;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline int64_t EverestCollimatorData_get__internal_record_id_offset(const EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=168;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void EverestCollimatorData_set__internal_record_id_offset(EverestCollimatorData  obj, int64_t value){
  int64_t offset=0;
  offset+=168;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* EverestCollimatorData_getp__internal_record_id_offset(EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=168;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline MaterialData EverestCollimatorData_getp__material(EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=224;
  return (MaterialData)(( char*) obj+offset);
}
 static inline double EverestCollimatorData_get__material_Z(const EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=232;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCollimatorData_set__material_Z(EverestCollimatorData  obj, double value){
  int64_t offset=0;
  offset+=232;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCollimatorData_getp__material_Z(EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=232;
  return ( double*)(( char*) obj+offset);
}
 static inline double EverestCollimatorData_get__material_A(const EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=240;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCollimatorData_set__material_A(EverestCollimatorData  obj, double value){
  int64_t offset=0;
  offset+=240;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCollimatorData_getp__material_A(EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=240;
  return ( double*)(( char*) obj+offset);
}
 static inline double EverestCollimatorData_get__material_density(const EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=248;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCollimatorData_set__material_density(EverestCollimatorData  obj, double value){
  int64_t offset=0;
  offset+=248;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCollimatorData_getp__material_density(EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=248;
  return ( double*)(( char*) obj+offset);
}
 static inline double EverestCollimatorData_get__material_excitation_energy(const EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=256;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCollimatorData_set__material_excitation_energy(EverestCollimatorData  obj, double value){
  int64_t offset=0;
  offset+=256;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCollimatorData_getp__material_excitation_energy(EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=256;
  return ( double*)(( char*) obj+offset);
}
 static inline double EverestCollimatorData_get__material_nuclear_radius(const EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=264;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCollimatorData_set__material_nuclear_radius(EverestCollimatorData  obj, double value){
  int64_t offset=0;
  offset+=264;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCollimatorData_getp__material_nuclear_radius(EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=264;
  return ( double*)(( char*) obj+offset);
}
 static inline double EverestCollimatorData_get__material_nuclear_elastic_slope(const EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=272;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCollimatorData_set__material_nuclear_elastic_slope(EverestCollimatorData  obj, double value){
  int64_t offset=0;
  offset+=272;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCollimatorData_getp__material_nuclear_elastic_slope(EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=272;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNFloat64 EverestCollimatorData_getp__material_cross_section(EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=304;
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t EverestCollimatorData_len__material_cross_section(EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=304;
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double EverestCollimatorData_get__material_cross_section(const EverestCollimatorData  obj, int64_t i0){
  int64_t offset=0;
  offset+=304;
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCollimatorData_set__material_cross_section(EverestCollimatorData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=304;
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCollimatorData_getp1__material_cross_section(EverestCollimatorData  obj, int64_t i0){
  int64_t offset=0;
  offset+=304;
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline double EverestCollimatorData_get__material_hcut(const EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=280;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCollimatorData_set__material_hcut(EverestCollimatorData  obj, double value){
  int64_t offset=0;
  offset+=280;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCollimatorData_getp__material_hcut(EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=280;
  return ( double*)(( char*) obj+offset);
}
 static inline  char* EverestCollimatorData_getp__material_name(EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=224;
  offset+=*( int64_t*)(( char*) obj+offset+72);
  return ( char*)(( char*) obj+offset);
}
 static inline double EverestCollimatorData_get__material_radiation_length(const EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=288;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCollimatorData_set__material_radiation_length(EverestCollimatorData  obj, double value){
  int64_t offset=0;
  offset+=288;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCollimatorData_getp__material_radiation_length(EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=288;
  return ( double*)(( char*) obj+offset);
}
 static inline RandomRutherfordData EverestCollimatorData_getp_rutherford_rng(EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=176;
  return (RandomRutherfordData)(( char*) obj+offset);
}
 static inline double EverestCollimatorData_get_rutherford_rng_lower_val(const EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=176;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCollimatorData_set_rutherford_rng_lower_val(EverestCollimatorData  obj, double value){
  int64_t offset=0;
  offset+=176;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCollimatorData_getp_rutherford_rng_lower_val(EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=176;
  return ( double*)(( char*) obj+offset);
}
 static inline double EverestCollimatorData_get_rutherford_rng_upper_val(const EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=184;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCollimatorData_set_rutherford_rng_upper_val(EverestCollimatorData  obj, double value){
  int64_t offset=0;
  offset+=184;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCollimatorData_getp_rutherford_rng_upper_val(EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=184;
  return ( double*)(( char*) obj+offset);
}
 static inline double EverestCollimatorData_get_rutherford_rng_A(const EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=192;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCollimatorData_set_rutherford_rng_A(EverestCollimatorData  obj, double value){
  int64_t offset=0;
  offset+=192;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCollimatorData_getp_rutherford_rng_A(EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=192;
  return ( double*)(( char*) obj+offset);
}
 static inline double EverestCollimatorData_get_rutherford_rng_B(const EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=200;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCollimatorData_set_rutherford_rng_B(EverestCollimatorData  obj, double value){
  int64_t offset=0;
  offset+=200;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCollimatorData_getp_rutherford_rng_B(EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=200;
  return ( double*)(( char*) obj+offset);
}
 static inline int8_t EverestCollimatorData_get_rutherford_rng_Newton_iterations(const EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=208;
  return *(( int8_t*) obj+offset);
}
 static inline void EverestCollimatorData_set_rutherford_rng_Newton_iterations(EverestCollimatorData  obj, int8_t value){
  int64_t offset=0;
  offset+=208;
  *(( int8_t*) obj+offset)=value;
}
 static inline  int8_t* EverestCollimatorData_getp_rutherford_rng_Newton_iterations(EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=208;
  return ( int8_t*)(( char*) obj+offset);
}
 static inline int8_t EverestCollimatorData_get__tracking(const EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=216;
  return *(( int8_t*) obj+offset);
}
 static inline void EverestCollimatorData_set__tracking(EverestCollimatorData  obj, int8_t value){
  int64_t offset=0;
  offset+=216;
  *(( int8_t*) obj+offset)=value;
}
 static inline  int8_t* EverestCollimatorData_getp__tracking(EverestCollimatorData  obj){
  int64_t offset=0;
  offset+=216;
  return ( int8_t*)(( char*) obj+offset);
}
#endif
// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_CONSTANTS_H
#define XTRACK_CONSTANTS_H

#if !defined( C_LIGHT )
    #define   C_LIGHT ( 299792458.0 )
#endif /* !defined( C_LIGHT ) */

#if !defined( EPSILON_0 )
    #define   EPSILON_0 (8.854187817620e-12)
#endif /* !defined( EPSILON_0 ) */

#if !defined( PI )
    #define PI (3.1415926535897932384626433832795028841971693993751)
#endif /* !defined( PI ) */

#if !defined( MU_0 )
    #define MU_0 (PI*4.0e-7)
#endif /* !defined( MU_0 ) */

#if !defined( DEG2RAD )
    #define DEG2RAD (0.0174532925199432957692369076848861271344287188854)
#endif /* !defiend( DEG2RAD ) */

#if !defined( RAD2DEG )
    #define RAD2DEG (57.29577951308232087679815481410517033240547246656442)
#endif /* !defiend( RAD2DEG ) */

#if !defined( SQRT_PI )
    #define SQRT_PI (1.7724538509055160272981674833411451827975494561224)
#endif /* !defined( SQRT_PI ) */

#if !defined( QELEM )
    #define QELEM (1.60217662e-19)
#endif /* !defined( QELEM ) */

#if !defined( DBL_MAX )
    #define DBL_MAX (1.7976931348623158e+308)
#endif /* !defined( DBL_MAX ) */

#if !defined( DBL_MIN )
    #define DBL_MIN (2.2250738585072014e-308)
#endif /* !defined( DBL_MIN ) */

#if !defined( DBL_EPSILON )
    #define DBL_EPSILON (2.2204460492503131e-16)
#endif /* !defined( DBL_EPSILON ) */

#endif /* XTRACK_CONSTANTS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_FUNCTIONS_H
#define XTRACK_FUNCTIONS_H

 static inline
void kill_all_particles(LocalParticle* part0, int64_t kill_state) {

    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

        LocalParticle_kill_particle(part, kill_state);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

}


 static inline
int8_t assert_tracking(LocalParticle* part, int64_t kill_state){
    // Whenever we are not tracking, e.g. in a twiss, the particle will be at_turn < 0.
    // We test this to distinguish genuine tracking from twiss.
    if (LocalParticle_get_at_turn(part) < 0){
        LocalParticle_kill_particle(part, kill_state);
        return 0;
    }
    return 1;
}

#endif /* XTRACK_FUNCTIONS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_PARTICLE_STATES_H
#define XTRACK_PARTICLE_STATES_H

#define  XT_LOST_ON_APERTURE       0
#define  XT_LOST_ON_LONG_CUT      -2
#define  XT_LOST_ALL_E_IN_SYNRAD -10
#define  RNG_ERR_SEEDS_NOT_SET   -20
#define  RNG_ERR_INVALID_TRACK   -21
#define  RNG_ERR_RUTH_NOT_SET    -22

#endif /* XTRACK_PARTICLE_STATES_H */


 static inline
CollimatorImpactsData EverestCollimatorData_getp_internal_record(EverestCollimatorData el, LocalParticle* part){
    RecordIdentifier record_id = EverestCollimatorData_getp__internal_record_id(el);
    if (RecordIdentifier_get_buffer_id(record_id) <= 0){
        return NULL;
    }
    else{
        return (CollimatorImpactsData) RecordIdentifier_getp_record(record_id, part);
    }
    }
    
// copyright ############################### #
// This file is part of the Xcoll Package.   #
// Copyright (c) CERN, 2023.                 #
// ######################################### #

#ifndef XCOLL_EVEREST_H
#define XCOLL_EVEREST_H
#include <math.h>
#include <stdio.h>

 static inline
void EverestCollimator_set_material(EverestCollimatorData el, LocalParticle* part0){
    MaterialData material = EverestCollimatorData_getp__material(el);
    RandomRutherfordData rng = EverestCollimatorData_getp_rutherford_rng(el);
    RandomRutherford_set_by_xcoll_material(rng, (GeneralMaterialData) material);
}

 static inline
void EverestCollimator_track_local_particle(EverestCollimatorData el, LocalParticle* part0) {
    int8_t active = EverestCollimatorData_get_active(el);
    active       *= EverestCollimatorData_get__tracking(el);
    double const inactive_front = EverestCollimatorData_get_inactive_front(el);
    double const active_length  = EverestCollimatorData_get_active_length(el);
    double const inactive_back  = EverestCollimatorData_get_inactive_back(el);

    MaterialData material = EverestCollimatorData_getp__material(el);
    RandomRutherfordData rng = EverestCollimatorData_getp_rutherford_rng(el);

    // Collimator properties
    double const length     = EverestCollimatorData_get_active_length(el);
    double const co_x       = EverestCollimatorData_get_ref_x(el);
    double const co_y       = EverestCollimatorData_get_ref_y(el);
    // TODO: use xtrack C-code for rotation element
    // TODO: we are ignoring the angle of the right jaw
    double const sin_zL     = EverestCollimatorData_get_sin_zL(el);
    double const cos_zL     = EverestCollimatorData_get_cos_zL(el);
    double const sin_zR     = EverestCollimatorData_get_sin_zR(el);
    double const cos_zR     = EverestCollimatorData_get_cos_zR(el);
    if (fabs(sin_zL-sin_zR) > 1.e-10 || fabs(cos_zL-cos_zR) > 1.e-10 ){
        kill_all_particles(part0, XC_ERR_NOT_IMPLEMENTED);
    };
    double const c_aperture = EverestCollimatorData_get_jaw_L(el) - EverestCollimatorData_get_jaw_R(el);
    double const c_offset   = ( EverestCollimatorData_get_jaw_L(el) + EverestCollimatorData_get_jaw_R(el) ) /2;
    double const c_tilt0    = asin(EverestCollimatorData_get_sin_yL(el));
    double const c_tilt1    = asin(EverestCollimatorData_get_sin_yR(el));
    int    const side       = EverestCollimatorData_get__side(el);


    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

        if (!active){
            // Drift full length
            Drift_single_particle(part, inactive_front+active_length+inactive_back);

        } else {
            // Check collimator initialisation
            int8_t is_tracking = assert_tracking(part, XC_ERR_INVALID_TRACK);
            int8_t rng_is_set  = assert_rng_set(part, RNG_ERR_SEEDS_NOT_SET);
            int8_t ruth_is_set = assert_rutherford_set(rng, part, RNG_ERR_RUTH_NOT_SET);

            if (is_tracking && rng_is_set && ruth_is_set) {
                // Drift inactive front
                Drift_single_particle(part, inactive_front);

                // Scattering parameters
                double const energy0 = LocalParticle_get_energy0(part) / 1e9; // Reference energy in GeV
                struct ScatteringParameters scat = calculate_scattering(energy0, material);

                // Move to closed orbit
                LocalParticle_add_to_x(part, -co_x);
                LocalParticle_add_to_y(part, -co_y);
                
                scatter(part, length, material, rng, scat, cos_zL, sin_zL, c_aperture, c_offset, c_tilt0, c_tilt1, side);

                // Return from closed orbit
                LocalParticle_add_to_x(part, co_x);
                LocalParticle_add_to_y(part, co_y);

                // Drift inactive back (only surviving particles)
                if (LocalParticle_get_state(part) > 0){
                    Drift_single_particle(part, inactive_back);
                }
            }
        }

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

}


#endif /* XCOLL_EVEREST_H */


             
            void EverestCollimator_track_particles(
               EverestCollimatorData el,

                             ParticlesData particles,

                             int64_t flag_increment_at_element,
                  int8_t* io_buffer){
//            const int num_threads = omp_get_max_threads();                                 //only_for_context cpu_openmp
//            const int64_t capacity = ParticlesData_get__capacity(particles);               //only_for_context cpu_openmp
//            const int64_t chunk_size = (capacity + num_threads - 1)/num_threads; // ceil division  //only_for_context cpu_openmp
//            #pragma omp parallel for                                                       //only_for_context cpu_openmp
//            for (int64_t batch_id = 0; batch_id < num_threads; batch_id++) {               //only_for_context cpu_openmp
                LocalParticle lpart;
                lpart.io_buffer = io_buffer;
//                int64_t part_id = batch_id * chunk_size;                                       //only_for_context cpu_openmp
//                int64_t end_id = (batch_id + 1) * chunk_size;                                  //only_for_context cpu_openmp
//                if (end_id > capacity) end_id = capacity;                                      //only_for_context cpu_openmp
    
                int64_t part_id = 0;                    //only_for_context cpu_serial
//                int64_t part_id = blockDim.x * blockIdx.x + threadIdx.x; //only_for_context cuda
//                int64_t part_id = get_global_id(0);                    //only_for_context opencl
                int64_t end_id = 0; // unused outside of openmp  //only_for_context cpu_serial cuda opencl
    
                int64_t part_capacity = ParticlesData_get__capacity(particles);
                if (part_id<part_capacity){
                    Particles_to_LocalParticle(particles, &lpart, part_id, end_id);
                    if (check_is_active(&lpart)>0){
              EverestCollimator_track_local_particle(el, &lpart);

                    }
                    if (check_is_active(&lpart)>0 && flag_increment_at_element){
                            increment_at_element(&lpart, 1);
                    }
                }
//            } //only_for_context cpu_openmp
            
            // On OpenMP we want to additionally by default reorganize all
            // the particles.
//            #ifndef XT_OMP_SKIP_REORGANIZE                             //only_for_context cpu_openmp
//            LocalParticle lpart;                                       //only_for_context cpu_openmp
//            lpart.io_buffer = io_buffer;                               //only_for_context cpu_openmp
//            Particles_to_LocalParticle(particles, &lpart, 0, capacity);//only_for_context cpu_openmp
//            check_is_active(&lpart);                                   //only_for_context cpu_openmp
//            #endif                                                     //only_for_context cpu_openmp
        }


             
            void _EverestCollimator_set_material(
               EverestCollimatorData el,

                             ParticlesData particles,

                             int64_t flag_increment_at_element,
                  int8_t* io_buffer){
//            const int num_threads = omp_get_max_threads();                                 //only_for_context cpu_openmp
//            const int64_t capacity = ParticlesData_get__capacity(particles);               //only_for_context cpu_openmp
//            const int64_t chunk_size = (capacity + num_threads - 1)/num_threads; // ceil division  //only_for_context cpu_openmp
//            #pragma omp parallel for                                                       //only_for_context cpu_openmp
//            for (int64_t batch_id = 0; batch_id < num_threads; batch_id++) {               //only_for_context cpu_openmp
                LocalParticle lpart;
                lpart.io_buffer = io_buffer;
//                int64_t part_id = batch_id * chunk_size;                                       //only_for_context cpu_openmp
//                int64_t end_id = (batch_id + 1) * chunk_size;                                  //only_for_context cpu_openmp
//                if (end_id > capacity) end_id = capacity;                                      //only_for_context cpu_openmp
    
                int64_t part_id = 0;                    //only_for_context cpu_serial
//                int64_t part_id = blockDim.x * blockIdx.x + threadIdx.x; //only_for_context cuda
//                int64_t part_id = get_global_id(0);                    //only_for_context opencl
                int64_t end_id = 0; // unused outside of openmp  //only_for_context cpu_serial cuda opencl
    
                int64_t part_capacity = ParticlesData_get__capacity(particles);
                if (part_id<part_capacity){
                    Particles_to_LocalParticle(particles, &lpart, part_id, end_id);
                    if (check_is_active(&lpart)>0){
              EverestCollimator_set_material(el, &lpart);

                    }
                    if (check_is_active(&lpart)>0 && flag_increment_at_element){
                            increment_at_element(&lpart, 1);
                    }
                }
//            } //only_for_context cpu_openmp
            
            // On OpenMP we want to additionally by default reorganize all
            // the particles.
//            #ifndef XT_OMP_SKIP_REORGANIZE                             //only_for_context cpu_openmp
//            LocalParticle lpart;                                       //only_for_context cpu_openmp
//            lpart.io_buffer = io_buffer;                               //only_for_context cpu_openmp
//            Particles_to_LocalParticle(particles, &lpart, 0, capacity);//only_for_context cpu_openmp
//            check_is_active(&lpart);                                   //only_for_context cpu_openmp
//            #endif                                                     //only_for_context cpu_openmp
        }

#ifndef XOBJ_TYPEDEF_EverestCrystalData
#define XOBJ_TYPEDEF_EverestCrystalData
typedef   struct EverestCrystalData_s * EverestCrystalData;
 static inline EverestCrystalData EverestCrystalData_getp(EverestCrystalData  obj){
  int64_t offset=0;
  return (EverestCrystalData)(( char*) obj+offset);
}
 static inline double EverestCrystalData_get_inactive_front(const EverestCrystalData  obj){
  int64_t offset=0;
  offset+=8;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCrystalData_set_inactive_front(EverestCrystalData  obj, double value){
  int64_t offset=0;
  offset+=8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCrystalData_getp_inactive_front(EverestCrystalData  obj){
  int64_t offset=0;
  offset+=8;
  return ( double*)(( char*) obj+offset);
}
 static inline double EverestCrystalData_get_active_length(const EverestCrystalData  obj){
  int64_t offset=0;
  offset+=16;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCrystalData_set_active_length(EverestCrystalData  obj, double value){
  int64_t offset=0;
  offset+=16;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCrystalData_getp_active_length(EverestCrystalData  obj){
  int64_t offset=0;
  offset+=16;
  return ( double*)(( char*) obj+offset);
}
 static inline double EverestCrystalData_get_inactive_back(const EverestCrystalData  obj){
  int64_t offset=0;
  offset+=24;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCrystalData_set_inactive_back(EverestCrystalData  obj, double value){
  int64_t offset=0;
  offset+=24;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCrystalData_getp_inactive_back(EverestCrystalData  obj){
  int64_t offset=0;
  offset+=24;
  return ( double*)(( char*) obj+offset);
}
 static inline double EverestCrystalData_get_jaw_L(const EverestCrystalData  obj){
  int64_t offset=0;
  offset+=32;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCrystalData_set_jaw_L(EverestCrystalData  obj, double value){
  int64_t offset=0;
  offset+=32;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCrystalData_getp_jaw_L(EverestCrystalData  obj){
  int64_t offset=0;
  offset+=32;
  return ( double*)(( char*) obj+offset);
}
 static inline double EverestCrystalData_get_jaw_R(const EverestCrystalData  obj){
  int64_t offset=0;
  offset+=40;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCrystalData_set_jaw_R(EverestCrystalData  obj, double value){
  int64_t offset=0;
  offset+=40;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCrystalData_getp_jaw_R(EverestCrystalData  obj){
  int64_t offset=0;
  offset+=40;
  return ( double*)(( char*) obj+offset);
}
 static inline double EverestCrystalData_get_ref_x(const EverestCrystalData  obj){
  int64_t offset=0;
  offset+=48;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCrystalData_set_ref_x(EverestCrystalData  obj, double value){
  int64_t offset=0;
  offset+=48;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCrystalData_getp_ref_x(EverestCrystalData  obj){
  int64_t offset=0;
  offset+=48;
  return ( double*)(( char*) obj+offset);
}
 static inline double EverestCrystalData_get_ref_y(const EverestCrystalData  obj){
  int64_t offset=0;
  offset+=56;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCrystalData_set_ref_y(EverestCrystalData  obj, double value){
  int64_t offset=0;
  offset+=56;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCrystalData_getp_ref_y(EverestCrystalData  obj){
  int64_t offset=0;
  offset+=56;
  return ( double*)(( char*) obj+offset);
}
 static inline double EverestCrystalData_get_sin_zL(const EverestCrystalData  obj){
  int64_t offset=0;
  offset+=64;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCrystalData_set_sin_zL(EverestCrystalData  obj, double value){
  int64_t offset=0;
  offset+=64;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCrystalData_getp_sin_zL(EverestCrystalData  obj){
  int64_t offset=0;
  offset+=64;
  return ( double*)(( char*) obj+offset);
}
 static inline double EverestCrystalData_get_cos_zL(const EverestCrystalData  obj){
  int64_t offset=0;
  offset+=72;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCrystalData_set_cos_zL(EverestCrystalData  obj, double value){
  int64_t offset=0;
  offset+=72;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCrystalData_getp_cos_zL(EverestCrystalData  obj){
  int64_t offset=0;
  offset+=72;
  return ( double*)(( char*) obj+offset);
}
 static inline double EverestCrystalData_get_sin_zR(const EverestCrystalData  obj){
  int64_t offset=0;
  offset+=80;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCrystalData_set_sin_zR(EverestCrystalData  obj, double value){
  int64_t offset=0;
  offset+=80;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCrystalData_getp_sin_zR(EverestCrystalData  obj){
  int64_t offset=0;
  offset+=80;
  return ( double*)(( char*) obj+offset);
}
 static inline double EverestCrystalData_get_cos_zR(const EverestCrystalData  obj){
  int64_t offset=0;
  offset+=88;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCrystalData_set_cos_zR(EverestCrystalData  obj, double value){
  int64_t offset=0;
  offset+=88;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCrystalData_getp_cos_zR(EverestCrystalData  obj){
  int64_t offset=0;
  offset+=88;
  return ( double*)(( char*) obj+offset);
}
 static inline double EverestCrystalData_get_sin_yL(const EverestCrystalData  obj){
  int64_t offset=0;
  offset+=96;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCrystalData_set_sin_yL(EverestCrystalData  obj, double value){
  int64_t offset=0;
  offset+=96;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCrystalData_getp_sin_yL(EverestCrystalData  obj){
  int64_t offset=0;
  offset+=96;
  return ( double*)(( char*) obj+offset);
}
 static inline double EverestCrystalData_get_cos_yL(const EverestCrystalData  obj){
  int64_t offset=0;
  offset+=104;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCrystalData_set_cos_yL(EverestCrystalData  obj, double value){
  int64_t offset=0;
  offset+=104;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCrystalData_getp_cos_yL(EverestCrystalData  obj){
  int64_t offset=0;
  offset+=104;
  return ( double*)(( char*) obj+offset);
}
 static inline double EverestCrystalData_get_tan_yL(const EverestCrystalData  obj){
  int64_t offset=0;
  offset+=112;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCrystalData_set_tan_yL(EverestCrystalData  obj, double value){
  int64_t offset=0;
  offset+=112;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCrystalData_getp_tan_yL(EverestCrystalData  obj){
  int64_t offset=0;
  offset+=112;
  return ( double*)(( char*) obj+offset);
}
 static inline double EverestCrystalData_get_sin_yR(const EverestCrystalData  obj){
  int64_t offset=0;
  offset+=120;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCrystalData_set_sin_yR(EverestCrystalData  obj, double value){
  int64_t offset=0;
  offset+=120;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCrystalData_getp_sin_yR(EverestCrystalData  obj){
  int64_t offset=0;
  offset+=120;
  return ( double*)(( char*) obj+offset);
}
 static inline double EverestCrystalData_get_cos_yR(const EverestCrystalData  obj){
  int64_t offset=0;
  offset+=128;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCrystalData_set_cos_yR(EverestCrystalData  obj, double value){
  int64_t offset=0;
  offset+=128;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCrystalData_getp_cos_yR(EverestCrystalData  obj){
  int64_t offset=0;
  offset+=128;
  return ( double*)(( char*) obj+offset);
}
 static inline double EverestCrystalData_get_tan_yR(const EverestCrystalData  obj){
  int64_t offset=0;
  offset+=136;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCrystalData_set_tan_yR(EverestCrystalData  obj, double value){
  int64_t offset=0;
  offset+=136;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCrystalData_getp_tan_yR(EverestCrystalData  obj){
  int64_t offset=0;
  offset+=136;
  return ( double*)(( char*) obj+offset);
}
 static inline int8_t EverestCrystalData_get__side(const EverestCrystalData  obj){
  int64_t offset=0;
  offset+=144;
  return *(( int8_t*) obj+offset);
}
 static inline void EverestCrystalData_set__side(EverestCrystalData  obj, int8_t value){
  int64_t offset=0;
  offset+=144;
  *(( int8_t*) obj+offset)=value;
}
 static inline  int8_t* EverestCrystalData_getp__side(EverestCrystalData  obj){
  int64_t offset=0;
  offset+=144;
  return ( int8_t*)(( char*) obj+offset);
}
 static inline int8_t EverestCrystalData_get_active(const EverestCrystalData  obj){
  int64_t offset=0;
  offset+=152;
  return *(( int8_t*) obj+offset);
}
 static inline void EverestCrystalData_set_active(EverestCrystalData  obj, int8_t value){
  int64_t offset=0;
  offset+=152;
  *(( int8_t*) obj+offset)=value;
}
 static inline  int8_t* EverestCrystalData_getp_active(EverestCrystalData  obj){
  int64_t offset=0;
  offset+=152;
  return ( int8_t*)(( char*) obj+offset);
}
 static inline RecordIdentifier EverestCrystalData_getp__internal_record_id(EverestCrystalData  obj){
  int64_t offset=0;
  offset+=160;
  return (RecordIdentifier)(( char*) obj+offset);
}
 static inline int64_t EverestCrystalData_get__internal_record_id_buffer_id(const EverestCrystalData  obj){
  int64_t offset=0;
  offset+=160;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void EverestCrystalData_set__internal_record_id_buffer_id(EverestCrystalData  obj, int64_t value){
  int64_t offset=0;
  offset+=160;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* EverestCrystalData_getp__internal_record_id_buffer_id(EverestCrystalData  obj){
  int64_t offset=0;
  offset+=160;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline int64_t EverestCrystalData_get__internal_record_id_offset(const EverestCrystalData  obj){
  int64_t offset=0;
  offset+=168;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline void EverestCrystalData_set__internal_record_id_offset(EverestCrystalData  obj, int64_t value){
  int64_t offset=0;
  offset+=168;
  *( int64_t*)(( char*) obj+offset)=value;
}
 static inline  int64_t* EverestCrystalData_getp__internal_record_id_offset(EverestCrystalData  obj){
  int64_t offset=0;
  offset+=168;
  return ( int64_t*)(( char*) obj+offset);
}
 static inline double EverestCrystalData_get_align_angle(const EverestCrystalData  obj){
  int64_t offset=0;
  offset+=176;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCrystalData_set_align_angle(EverestCrystalData  obj, double value){
  int64_t offset=0;
  offset+=176;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCrystalData_getp_align_angle(EverestCrystalData  obj){
  int64_t offset=0;
  offset+=176;
  return ( double*)(( char*) obj+offset);
}
 static inline double EverestCrystalData_get_bend(const EverestCrystalData  obj){
  int64_t offset=0;
  offset+=184;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCrystalData_set_bend(EverestCrystalData  obj, double value){
  int64_t offset=0;
  offset+=184;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCrystalData_getp_bend(EverestCrystalData  obj){
  int64_t offset=0;
  offset+=184;
  return ( double*)(( char*) obj+offset);
}
 static inline double EverestCrystalData_get_xdim(const EverestCrystalData  obj){
  int64_t offset=0;
  offset+=192;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCrystalData_set_xdim(EverestCrystalData  obj, double value){
  int64_t offset=0;
  offset+=192;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCrystalData_getp_xdim(EverestCrystalData  obj){
  int64_t offset=0;
  offset+=192;
  return ( double*)(( char*) obj+offset);
}
 static inline double EverestCrystalData_get_ydim(const EverestCrystalData  obj){
  int64_t offset=0;
  offset+=200;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCrystalData_set_ydim(EverestCrystalData  obj, double value){
  int64_t offset=0;
  offset+=200;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCrystalData_getp_ydim(EverestCrystalData  obj){
  int64_t offset=0;
  offset+=200;
  return ( double*)(( char*) obj+offset);
}
 static inline double EverestCrystalData_get_thick(const EverestCrystalData  obj){
  int64_t offset=0;
  offset+=208;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCrystalData_set_thick(EverestCrystalData  obj, double value){
  int64_t offset=0;
  offset+=208;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCrystalData_getp_thick(EverestCrystalData  obj){
  int64_t offset=0;
  offset+=208;
  return ( double*)(( char*) obj+offset);
}
 static inline double EverestCrystalData_get_miscut(const EverestCrystalData  obj){
  int64_t offset=0;
  offset+=216;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCrystalData_set_miscut(EverestCrystalData  obj, double value){
  int64_t offset=0;
  offset+=216;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCrystalData_getp_miscut(EverestCrystalData  obj){
  int64_t offset=0;
  offset+=216;
  return ( double*)(( char*) obj+offset);
}
 static inline int8_t EverestCrystalData_get__orient(const EverestCrystalData  obj){
  int64_t offset=0;
  offset+=224;
  return *(( int8_t*) obj+offset);
}
 static inline void EverestCrystalData_set__orient(EverestCrystalData  obj, int8_t value){
  int64_t offset=0;
  offset+=224;
  *(( int8_t*) obj+offset)=value;
}
 static inline  int8_t* EverestCrystalData_getp__orient(EverestCrystalData  obj){
  int64_t offset=0;
  offset+=224;
  return ( int8_t*)(( char*) obj+offset);
}
 static inline CrystalMaterialData EverestCrystalData_getp__material(EverestCrystalData  obj){
  int64_t offset=0;
  offset+=280;
  return (CrystalMaterialData)(( char*) obj+offset);
}
 static inline double EverestCrystalData_get__material_Z(const EverestCrystalData  obj){
  int64_t offset=0;
  offset+=288;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCrystalData_set__material_Z(EverestCrystalData  obj, double value){
  int64_t offset=0;
  offset+=288;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCrystalData_getp__material_Z(EverestCrystalData  obj){
  int64_t offset=0;
  offset+=288;
  return ( double*)(( char*) obj+offset);
}
 static inline double EverestCrystalData_get__material_A(const EverestCrystalData  obj){
  int64_t offset=0;
  offset+=296;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCrystalData_set__material_A(EverestCrystalData  obj, double value){
  int64_t offset=0;
  offset+=296;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCrystalData_getp__material_A(EverestCrystalData  obj){
  int64_t offset=0;
  offset+=296;
  return ( double*)(( char*) obj+offset);
}
 static inline double EverestCrystalData_get__material_density(const EverestCrystalData  obj){
  int64_t offset=0;
  offset+=304;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCrystalData_set__material_density(EverestCrystalData  obj, double value){
  int64_t offset=0;
  offset+=304;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCrystalData_getp__material_density(EverestCrystalData  obj){
  int64_t offset=0;
  offset+=304;
  return ( double*)(( char*) obj+offset);
}
 static inline double EverestCrystalData_get__material_excitation_energy(const EverestCrystalData  obj){
  int64_t offset=0;
  offset+=312;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCrystalData_set__material_excitation_energy(EverestCrystalData  obj, double value){
  int64_t offset=0;
  offset+=312;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCrystalData_getp__material_excitation_energy(EverestCrystalData  obj){
  int64_t offset=0;
  offset+=312;
  return ( double*)(( char*) obj+offset);
}
 static inline double EverestCrystalData_get__material_nuclear_radius(const EverestCrystalData  obj){
  int64_t offset=0;
  offset+=320;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCrystalData_set__material_nuclear_radius(EverestCrystalData  obj, double value){
  int64_t offset=0;
  offset+=320;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCrystalData_getp__material_nuclear_radius(EverestCrystalData  obj){
  int64_t offset=0;
  offset+=320;
  return ( double*)(( char*) obj+offset);
}
 static inline double EverestCrystalData_get__material_nuclear_elastic_slope(const EverestCrystalData  obj){
  int64_t offset=0;
  offset+=328;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCrystalData_set__material_nuclear_elastic_slope(EverestCrystalData  obj, double value){
  int64_t offset=0;
  offset+=328;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCrystalData_getp__material_nuclear_elastic_slope(EverestCrystalData  obj){
  int64_t offset=0;
  offset+=328;
  return ( double*)(( char*) obj+offset);
}
 static inline ArrNFloat64 EverestCrystalData_getp__material_cross_section(EverestCrystalData  obj){
  int64_t offset=0;
  offset+=392;
  return (ArrNFloat64)(( char*) obj+offset);
}
 static inline int64_t EverestCrystalData_len__material_cross_section(EverestCrystalData  obj){
  int64_t offset=0;
  offset+=392;
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline double EverestCrystalData_get__material_cross_section(const EverestCrystalData  obj, int64_t i0){
  int64_t offset=0;
  offset+=392;
  offset+=16+i0*8;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCrystalData_set__material_cross_section(EverestCrystalData  obj, int64_t i0, double value){
  int64_t offset=0;
  offset+=392;
  offset+=16+i0*8;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCrystalData_getp1__material_cross_section(EverestCrystalData  obj, int64_t i0){
  int64_t offset=0;
  offset+=392;
  offset+=16+i0*8;
  return ( double*)(( char*) obj+offset);
}
 static inline double EverestCrystalData_get__material_hcut(const EverestCrystalData  obj){
  int64_t offset=0;
  offset+=336;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCrystalData_set__material_hcut(EverestCrystalData  obj, double value){
  int64_t offset=0;
  offset+=336;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCrystalData_getp__material_hcut(EverestCrystalData  obj){
  int64_t offset=0;
  offset+=336;
  return ( double*)(( char*) obj+offset);
}
 static inline  char* EverestCrystalData_getp__material_name(EverestCrystalData  obj){
  int64_t offset=0;
  offset+=280;
  offset+=*( int64_t*)(( char*) obj+offset+104);
  return ( char*)(( char*) obj+offset);
}
 static inline double EverestCrystalData_get__material_crystal_radiation_length(const EverestCrystalData  obj){
  int64_t offset=0;
  offset+=344;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCrystalData_set__material_crystal_radiation_length(EverestCrystalData  obj, double value){
  int64_t offset=0;
  offset+=344;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCrystalData_getp__material_crystal_radiation_length(EverestCrystalData  obj){
  int64_t offset=0;
  offset+=344;
  return ( double*)(( char*) obj+offset);
}
 static inline double EverestCrystalData_get__material_crystal_nuclear_length(const EverestCrystalData  obj){
  int64_t offset=0;
  offset+=352;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCrystalData_set__material_crystal_nuclear_length(EverestCrystalData  obj, double value){
  int64_t offset=0;
  offset+=352;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCrystalData_getp__material_crystal_nuclear_length(EverestCrystalData  obj){
  int64_t offset=0;
  offset+=352;
  return ( double*)(( char*) obj+offset);
}
 static inline double EverestCrystalData_get__material_crystal_plane_distance(const EverestCrystalData  obj){
  int64_t offset=0;
  offset+=360;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCrystalData_set__material_crystal_plane_distance(EverestCrystalData  obj, double value){
  int64_t offset=0;
  offset+=360;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCrystalData_getp__material_crystal_plane_distance(EverestCrystalData  obj){
  int64_t offset=0;
  offset+=360;
  return ( double*)(( char*) obj+offset);
}
 static inline double EverestCrystalData_get__material_crystal_potential(const EverestCrystalData  obj){
  int64_t offset=0;
  offset+=368;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCrystalData_set__material_crystal_potential(EverestCrystalData  obj, double value){
  int64_t offset=0;
  offset+=368;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCrystalData_getp__material_crystal_potential(EverestCrystalData  obj){
  int64_t offset=0;
  offset+=368;
  return ( double*)(( char*) obj+offset);
}
 static inline double EverestCrystalData_get__material_nuclear_collision_length(const EverestCrystalData  obj){
  int64_t offset=0;
  offset+=376;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCrystalData_set__material_nuclear_collision_length(EverestCrystalData  obj, double value){
  int64_t offset=0;
  offset+=376;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCrystalData_getp__material_nuclear_collision_length(EverestCrystalData  obj){
  int64_t offset=0;
  offset+=376;
  return ( double*)(( char*) obj+offset);
}
 static inline RandomRutherfordData EverestCrystalData_getp_rutherford_rng(EverestCrystalData  obj){
  int64_t offset=0;
  offset+=232;
  return (RandomRutherfordData)(( char*) obj+offset);
}
 static inline double EverestCrystalData_get_rutherford_rng_lower_val(const EverestCrystalData  obj){
  int64_t offset=0;
  offset+=232;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCrystalData_set_rutherford_rng_lower_val(EverestCrystalData  obj, double value){
  int64_t offset=0;
  offset+=232;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCrystalData_getp_rutherford_rng_lower_val(EverestCrystalData  obj){
  int64_t offset=0;
  offset+=232;
  return ( double*)(( char*) obj+offset);
}
 static inline double EverestCrystalData_get_rutherford_rng_upper_val(const EverestCrystalData  obj){
  int64_t offset=0;
  offset+=240;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCrystalData_set_rutherford_rng_upper_val(EverestCrystalData  obj, double value){
  int64_t offset=0;
  offset+=240;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCrystalData_getp_rutherford_rng_upper_val(EverestCrystalData  obj){
  int64_t offset=0;
  offset+=240;
  return ( double*)(( char*) obj+offset);
}
 static inline double EverestCrystalData_get_rutherford_rng_A(const EverestCrystalData  obj){
  int64_t offset=0;
  offset+=248;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCrystalData_set_rutherford_rng_A(EverestCrystalData  obj, double value){
  int64_t offset=0;
  offset+=248;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCrystalData_getp_rutherford_rng_A(EverestCrystalData  obj){
  int64_t offset=0;
  offset+=248;
  return ( double*)(( char*) obj+offset);
}
 static inline double EverestCrystalData_get_rutherford_rng_B(const EverestCrystalData  obj){
  int64_t offset=0;
  offset+=256;
  return *( double*)(( char*) obj+offset);
}
 static inline void EverestCrystalData_set_rutherford_rng_B(EverestCrystalData  obj, double value){
  int64_t offset=0;
  offset+=256;
  *( double*)(( char*) obj+offset)=value;
}
 static inline  double* EverestCrystalData_getp_rutherford_rng_B(EverestCrystalData  obj){
  int64_t offset=0;
  offset+=256;
  return ( double*)(( char*) obj+offset);
}
 static inline int8_t EverestCrystalData_get_rutherford_rng_Newton_iterations(const EverestCrystalData  obj){
  int64_t offset=0;
  offset+=264;
  return *(( int8_t*) obj+offset);
}
 static inline void EverestCrystalData_set_rutherford_rng_Newton_iterations(EverestCrystalData  obj, int8_t value){
  int64_t offset=0;
  offset+=264;
  *(( int8_t*) obj+offset)=value;
}
 static inline  int8_t* EverestCrystalData_getp_rutherford_rng_Newton_iterations(EverestCrystalData  obj){
  int64_t offset=0;
  offset+=264;
  return ( int8_t*)(( char*) obj+offset);
}
 static inline int8_t EverestCrystalData_get__tracking(const EverestCrystalData  obj){
  int64_t offset=0;
  offset+=272;
  return *(( int8_t*) obj+offset);
}
 static inline void EverestCrystalData_set__tracking(EverestCrystalData  obj, int8_t value){
  int64_t offset=0;
  offset+=272;
  *(( int8_t*) obj+offset)=value;
}
 static inline  int8_t* EverestCrystalData_getp__tracking(EverestCrystalData  obj){
  int64_t offset=0;
  offset+=272;
  return ( int8_t*)(( char*) obj+offset);
}
#endif
// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2021.                 //
// ######################################### //

#ifndef XTRACK_CONSTANTS_H
#define XTRACK_CONSTANTS_H

#if !defined( C_LIGHT )
    #define   C_LIGHT ( 299792458.0 )
#endif /* !defined( C_LIGHT ) */

#if !defined( EPSILON_0 )
    #define   EPSILON_0 (8.854187817620e-12)
#endif /* !defined( EPSILON_0 ) */

#if !defined( PI )
    #define PI (3.1415926535897932384626433832795028841971693993751)
#endif /* !defined( PI ) */

#if !defined( MU_0 )
    #define MU_0 (PI*4.0e-7)
#endif /* !defined( MU_0 ) */

#if !defined( DEG2RAD )
    #define DEG2RAD (0.0174532925199432957692369076848861271344287188854)
#endif /* !defiend( DEG2RAD ) */

#if !defined( RAD2DEG )
    #define RAD2DEG (57.29577951308232087679815481410517033240547246656442)
#endif /* !defiend( RAD2DEG ) */

#if !defined( SQRT_PI )
    #define SQRT_PI (1.7724538509055160272981674833411451827975494561224)
#endif /* !defined( SQRT_PI ) */

#if !defined( QELEM )
    #define QELEM (1.60217662e-19)
#endif /* !defined( QELEM ) */

#if !defined( DBL_MAX )
    #define DBL_MAX (1.7976931348623158e+308)
#endif /* !defined( DBL_MAX ) */

#if !defined( DBL_MIN )
    #define DBL_MIN (2.2250738585072014e-308)
#endif /* !defined( DBL_MIN ) */

#if !defined( DBL_EPSILON )
    #define DBL_EPSILON (2.2204460492503131e-16)
#endif /* !defined( DBL_EPSILON ) */

#endif /* XTRACK_CONSTANTS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_FUNCTIONS_H
#define XTRACK_FUNCTIONS_H

 static inline
void kill_all_particles(LocalParticle* part0, int64_t kill_state) {

    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

        LocalParticle_kill_particle(part, kill_state);

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

}


 static inline
int8_t assert_tracking(LocalParticle* part, int64_t kill_state){
    // Whenever we are not tracking, e.g. in a twiss, the particle will be at_turn < 0.
    // We test this to distinguish genuine tracking from twiss.
    if (LocalParticle_get_at_turn(part) < 0){
        LocalParticle_kill_particle(part, kill_state);
        return 0;
    }
    return 1;
}

#endif /* XTRACK_FUNCTIONS_H */

// copyright ############################### //
// This file is part of the Xtrack Package.  //
// Copyright (c) CERN, 2023.                 //
// ######################################### //

#ifndef XTRACK_PARTICLE_STATES_H
#define XTRACK_PARTICLE_STATES_H

#define  XT_LOST_ON_APERTURE       0
#define  XT_LOST_ON_LONG_CUT      -2
#define  XT_LOST_ALL_E_IN_SYNRAD -10
#define  RNG_ERR_SEEDS_NOT_SET   -20
#define  RNG_ERR_INVALID_TRACK   -21
#define  RNG_ERR_RUTH_NOT_SET    -22

#endif /* XTRACK_PARTICLE_STATES_H */


 static inline
CollimatorImpactsData EverestCrystalData_getp_internal_record(EverestCrystalData el, LocalParticle* part){
    RecordIdentifier record_id = EverestCrystalData_getp__internal_record_id(el);
    if (RecordIdentifier_get_buffer_id(record_id) <= 0){
        return NULL;
    }
    else{
        return (CollimatorImpactsData) RecordIdentifier_getp_record(record_id, part);
    }
    }
    
// copyright ############################### #
// This file is part of the Xcoll Package.   #
// Copyright (c) CERN, 2023.                 #
// ######################################### #

#ifndef XCOLL_EVEREST_CRYSTAL_H
#define XCOLL_EVEREST_CRYSTAL_H
#include <math.h>
#include <stdio.h>



 static inline
void EverestCrystal_set_material(EverestCrystalData el, LocalParticle* part0){
    CrystalMaterialData material = EverestCrystalData_getp__material(el);
    RandomRutherfordData rng = EverestCrystalData_getp_rutherford_rng(el);
    RandomRutherford_set_by_xcoll_material(rng, (GeneralMaterialData) material);
}

 static inline
void EverestCrystal_track_local_particle(EverestCrystalData el, LocalParticle* part0) {
    int8_t active      = EverestCrystalData_get_active(el);
    active            *= EverestCrystalData_get__tracking(el);
    double const inactive_front = EverestCrystalData_get_inactive_front(el);
    double const active_length  = EverestCrystalData_get_active_length(el);
    double const inactive_back  = EverestCrystalData_get_inactive_back(el);

    CrystalMaterialData material = EverestCrystalData_getp__material(el);
    RandomRutherfordData rng = EverestCrystalData_getp_rutherford_rng(el);

    // Crystal properties
    double length  = EverestCrystalData_get_active_length(el);
    double const co_x       = EverestCrystalData_get_ref_x(el);
    double const co_y       = EverestCrystalData_get_ref_y(el);
    // TODO: use xtrack C-code for rotation element
    // TODO: we are ignoring the angle of the right jaw
    // TODO: is a crystal always one-sided...?
    double const sin_zL     = EverestCrystalData_get_sin_zL(el);
    double const cos_zL     = EverestCrystalData_get_cos_zL(el);
    double const sin_zR     = EverestCrystalData_get_sin_zR(el);
    double const cos_zR     = EverestCrystalData_get_cos_zR(el);
    if (fabs(sin_zL-sin_zR) > 1.e-10 || fabs(cos_zL-cos_zR) > 1.e-10 ){
        kill_all_particles(part0, XC_ERR_NOT_IMPLEMENTED);
    };
    double const c_aperture = EverestCrystalData_get_jaw_L(el) - EverestCrystalData_get_jaw_R(el);
    double const c_offset   = ( EverestCrystalData_get_jaw_L(el) + EverestCrystalData_get_jaw_R(el) ) /2;
    double const c_tilt0    = asin(EverestCrystalData_get_sin_yL(el));
    double const c_tilt1    = asin(EverestCrystalData_get_sin_yR(el));
    if (fabs(c_tilt1) > 1.e-10){
        kill_all_particles(part0, XC_ERR_INVALID_XOFIELD);
    };
    int    const side       = EverestCrystalData_get__side(el);
    double const bend       = EverestCrystalData_get_bend(el);
    // TODO: cry_tilt should be given by jaw positions...?
    double const cry_tilt   = EverestCrystalData_get_align_angle(el) + c_tilt0;
    double const bend_ang   = length/bend;    // temporary value
    if (cry_tilt >= -bend_ang) {
        length = bend*(sin(bend_ang + cry_tilt) - sin(cry_tilt));
    } else {
        length = bend*(sin(bend_ang - cry_tilt) + sin(cry_tilt));
    }
    double const cry_rcurv  = bend;
    double const cry_bend   = length/cry_rcurv; //final value (with corrected length) 
    double const cry_alayer = EverestCrystalData_get_thick(el);
    double const cry_xmax   = EverestCrystalData_get_xdim(el);
    double const cry_ymax   = EverestCrystalData_get_ydim(el);
    double const cry_orient = EverestCrystalData_get__orient(el);
    double const cry_miscut = EverestCrystalData_get_miscut(el);

    // Impact table
    CollimatorImpactsData record = EverestCrystalData_getp_internal_record(el, part0);
    RecordIndex record_index = NULL;
    if (record){
        record_index = CollimatorImpactsData_getp__index(record);
    }


    {
//    const int64_t start_idx = part0->ipart; //only_for_context cpu_openmp
//    const int64_t end_idx = part0->endpart; //only_for_context cpu_openmp
    
    const int64_t start_idx = 0;                                            //only_for_context cpu_serial
    const int64_t end_idx = LocalParticle_get__num_active_particles(part0); //only_for_context cpu_serial
    
    //#pragma omp simd // TODO: currently does not work, needs investigating
    for (int64_t ii=start_idx; ii<end_idx; ii++) { //only_for_context cpu_openmp cpu_serial

        LocalParticle lpart = *part0;  //only_for_context cpu_serial cpu_openmp
        LocalParticle* part = &lpart;  //only_for_context cpu_serial cpu_openmp
        part->ipart = ii;              //only_for_context cpu_serial cpu_openmp

//        LocalParticle* part = part0;   //only_for_context opencl cuda
        
//        if (LocalParticle_get_state(part) > 0) {  //only_for_context cpu_openmp

        if (!active){
            // Drift full length
            Drift_single_particle(part, inactive_front+active_length+inactive_back);

        } else {
            // Check collimator initialisation
            int8_t is_tracking = assert_tracking(part, XC_ERR_INVALID_TRACK);
            int8_t rng_is_set  = assert_rng_set(part, RNG_ERR_SEEDS_NOT_SET);
            int8_t ruth_is_set = assert_rutherford_set(rng, part, RNG_ERR_RUTH_NOT_SET);

            if (is_tracking && rng_is_set && ruth_is_set) {
                // Drift inactive front
                Drift_single_particle(part, inactive_front);

                // Scatter

                // Move to closed orbit
                LocalParticle_add_to_x(part, -co_x);
                LocalParticle_add_to_y(part, -co_y);

                scatter_cry(part, length, material, rng, cos_zL, sin_zL, c_aperture, c_offset,
                            side, cry_tilt, cry_rcurv, cry_bend, cry_alayer, cry_xmax, cry_ymax, cry_orient, 
                            cry_miscut, record, record_index);

                // Return from closed orbit
                LocalParticle_add_to_x(part, co_x);
                LocalParticle_add_to_y(part, co_y);

                // Drift inactive back (only surviving particles)
                if (LocalParticle_get_state(part) > 0){
                    Drift_single_particle(part, inactive_back);
                }
            }
        }

//        }  //only_for_context cpu_openmp
    }  //only_for_context cpu_serial cpu_openmp
    }

}


#endif /* XCOLL_EVEREST_CRYSTAL_H */


             
            void EverestCrystal_track_particles(
               EverestCrystalData el,

                             ParticlesData particles,

                             int64_t flag_increment_at_element,
                  int8_t* io_buffer){
//            const int num_threads = omp_get_max_threads();                                 //only_for_context cpu_openmp
//            const int64_t capacity = ParticlesData_get__capacity(particles);               //only_for_context cpu_openmp
//            const int64_t chunk_size = (capacity + num_threads - 1)/num_threads; // ceil division  //only_for_context cpu_openmp
//            #pragma omp parallel for                                                       //only_for_context cpu_openmp
//            for (int64_t batch_id = 0; batch_id < num_threads; batch_id++) {               //only_for_context cpu_openmp
                LocalParticle lpart;
                lpart.io_buffer = io_buffer;
//                int64_t part_id = batch_id * chunk_size;                                       //only_for_context cpu_openmp
//                int64_t end_id = (batch_id + 1) * chunk_size;                                  //only_for_context cpu_openmp
//                if (end_id > capacity) end_id = capacity;                                      //only_for_context cpu_openmp
    
                int64_t part_id = 0;                    //only_for_context cpu_serial
//                int64_t part_id = blockDim.x * blockIdx.x + threadIdx.x; //only_for_context cuda
//                int64_t part_id = get_global_id(0);                    //only_for_context opencl
                int64_t end_id = 0; // unused outside of openmp  //only_for_context cpu_serial cuda opencl
    
                int64_t part_capacity = ParticlesData_get__capacity(particles);
                if (part_id<part_capacity){
                    Particles_to_LocalParticle(particles, &lpart, part_id, end_id);
                    if (check_is_active(&lpart)>0){
              EverestCrystal_track_local_particle(el, &lpart);

                    }
                    if (check_is_active(&lpart)>0 && flag_increment_at_element){
                            increment_at_element(&lpart, 1);
                    }
                }
//            } //only_for_context cpu_openmp
            
            // On OpenMP we want to additionally by default reorganize all
            // the particles.
//            #ifndef XT_OMP_SKIP_REORGANIZE                             //only_for_context cpu_openmp
//            LocalParticle lpart;                                       //only_for_context cpu_openmp
//            lpart.io_buffer = io_buffer;                               //only_for_context cpu_openmp
//            Particles_to_LocalParticle(particles, &lpart, 0, capacity);//only_for_context cpu_openmp
//            check_is_active(&lpart);                                   //only_for_context cpu_openmp
//            #endif                                                     //only_for_context cpu_openmp
        }


             
            void _EverestCrystal_set_material(
               EverestCrystalData el,

                             ParticlesData particles,

                             int64_t flag_increment_at_element,
                  int8_t* io_buffer){
//            const int num_threads = omp_get_max_threads();                                 //only_for_context cpu_openmp
//            const int64_t capacity = ParticlesData_get__capacity(particles);               //only_for_context cpu_openmp
//            const int64_t chunk_size = (capacity + num_threads - 1)/num_threads; // ceil division  //only_for_context cpu_openmp
//            #pragma omp parallel for                                                       //only_for_context cpu_openmp
//            for (int64_t batch_id = 0; batch_id < num_threads; batch_id++) {               //only_for_context cpu_openmp
                LocalParticle lpart;
                lpart.io_buffer = io_buffer;
//                int64_t part_id = batch_id * chunk_size;                                       //only_for_context cpu_openmp
//                int64_t end_id = (batch_id + 1) * chunk_size;                                  //only_for_context cpu_openmp
//                if (end_id > capacity) end_id = capacity;                                      //only_for_context cpu_openmp
    
                int64_t part_id = 0;                    //only_for_context cpu_serial
//                int64_t part_id = blockDim.x * blockIdx.x + threadIdx.x; //only_for_context cuda
//                int64_t part_id = get_global_id(0);                    //only_for_context opencl
                int64_t end_id = 0; // unused outside of openmp  //only_for_context cpu_serial cuda opencl
    
                int64_t part_capacity = ParticlesData_get__capacity(particles);
                if (part_id<part_capacity){
                    Particles_to_LocalParticle(particles, &lpart, part_id, end_id);
                    if (check_is_active(&lpart)>0){
              EverestCrystal_set_material(el, &lpart);

                    }
                    if (check_is_active(&lpart)>0 && flag_increment_at_element){
                            increment_at_element(&lpart, 1);
                    }
                }
//            } //only_for_context cpu_openmp
            
            // On OpenMP we want to additionally by default reorganize all
            // the particles.
//            #ifndef XT_OMP_SKIP_REORGANIZE                             //only_for_context cpu_openmp
//            LocalParticle lpart;                                       //only_for_context cpu_openmp
//            lpart.io_buffer = io_buffer;                               //only_for_context cpu_openmp
//            Particles_to_LocalParticle(particles, &lpart, 0, capacity);//only_for_context cpu_openmp
//            check_is_active(&lpart);                                   //only_for_context cpu_openmp
//            #endif                                                     //only_for_context cpu_openmp
        }

#ifndef XOBJ_TYPEDEF_ElementRefClass
#define XOBJ_TYPEDEF_ElementRefClass
typedef   struct ElementRefClass_s * ElementRefClass;
enum ElementRefClass_e{ElementRefClass_BendData_t,ElementRefClass_BlackAbsorberData_t,ElementRefClass_CavityData_t,ElementRefClass_CombinedFunctionMagnetData_t,ElementRefClass_DipoleEdgeData_t,ElementRefClass_DriftData_t,ElementRefClass_ElensData_t,ElementRefClass_EverestCollimatorData_t,ElementRefClass_EverestCrystalData_t,ElementRefClass_ExciterData_t,ElementRefClass_FirstOrderTaylorMapData_t,ElementRefClass_LimitEllipseData_t,ElementRefClass_LimitPolygonData_t,ElementRefClass_LimitRacetrackData_t,ElementRefClass_LimitRectData_t,ElementRefClass_LimitRectEllipseData_t,ElementRefClass_LineSegmentMapData_t,ElementRefClass_LongitudinalLimitRectData_t,ElementRefClass_MarkerData_t,ElementRefClass_MultipoleData_t,ElementRefClass_NonLinearLensData_t,ElementRefClass_ParticlesMonitorData_t,ElementRefClass_QuadrupoleData_t,ElementRefClass_RFMultipoleData_t,ElementRefClass_ReferenceEnergyIncreaseData_t,ElementRefClass_SRotationData_t,ElementRefClass_SextupoleData_t,ElementRefClass_SimpleThinBendData_t,ElementRefClass_SimpleThinQuadrupoleData_t,ElementRefClass_SolenoidData_t,ElementRefClass_WireData_t,ElementRefClass_XRotationData_t,ElementRefClass_XYShiftData_t,ElementRefClass_YRotationData_t,ElementRefClass_ZetaShiftData_t};
 static inline ElementRefClass ElementRefClass_getp(ElementRefClass  obj){
  int64_t offset=0;
  return (ElementRefClass)(( char*) obj+offset);
}
 static inline int64_t ElementRefClass_typeid(const ElementRefClass  obj){
  int64_t offset=0;
  offset+=8;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline  void* ElementRefClass_member(const ElementRefClass  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset);
 return ( void*)(( char*) obj+offset);
}
#endif
#ifndef XOBJ_TYPEDEF_ArrNElementRefClass
#define XOBJ_TYPEDEF_ArrNElementRefClass
typedef   struct ArrNElementRefClass_s * ArrNElementRefClass;
 static inline ArrNElementRefClass ArrNElementRefClass_getp(ArrNElementRefClass  obj){
  int64_t offset=0;
  return (ArrNElementRefClass)(( char*) obj+offset);
}
 static inline int64_t ArrNElementRefClass_len(ArrNElementRefClass  obj){
  int64_t offset=0;
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline ElementRefClass ArrNElementRefClass_getp1(ArrNElementRefClass  obj, int64_t i0){
  int64_t offset=0;
  offset+=16+i0*16;
  return (ElementRefClass)(( char*) obj+offset);
}
 static inline int64_t ArrNElementRefClass_typeid(const ArrNElementRefClass  obj, int64_t i0){
  int64_t offset=0;
  offset+=16+i0*16;
  offset+=8;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline  void* ArrNElementRefClass_member(const ArrNElementRefClass  obj, int64_t i0){
  int64_t offset=0;
  offset+=16+i0*16;
  offset+=*( int64_t*)(( char*) obj+offset);
 return ( void*)(( char*) obj+offset);
}
#endif
#ifndef XOBJ_TYPEDEF_ElementRefData
#define XOBJ_TYPEDEF_ElementRefData
typedef   struct ElementRefData_s * ElementRefData;
 static inline ElementRefData ElementRefData_getp(ElementRefData  obj){
  int64_t offset=0;
  return (ElementRefData)(( char*) obj+offset);
}
 static inline ArrNElementRefClass ElementRefData_getp_elements(ElementRefData  obj){
  int64_t offset=0;
  offset+=16;
  return (ArrNElementRefClass)(( char*) obj+offset);
}
 static inline int64_t ElementRefData_len_elements(ElementRefData  obj){
  int64_t offset=0;
  offset+=16;
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline ElementRefClass ElementRefData_getp1_elements(ElementRefData  obj, int64_t i0){
  int64_t offset=0;
  offset+=16;
  offset+=16+i0*16;
  return (ElementRefClass)(( char*) obj+offset);
}
 static inline int64_t ElementRefData_typeid_elements(const ElementRefData  obj, int64_t i0){
  int64_t offset=0;
  offset+=16;
  offset+=16+i0*16;
  offset+=8;
  return *( int64_t*)(( char*) obj+offset);
}
 static inline  void* ElementRefData_member_elements(const ElementRefData  obj, int64_t i0){
  int64_t offset=0;
  offset+=16;
  offset+=16+i0*16;
  offset+=*( int64_t*)(( char*) obj+offset);
 return ( void*)(( char*) obj+offset);
}
 static inline ArrNString ElementRefData_getp_names(ElementRefData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+8);
  return (ArrNString)(( char*) obj+offset);
}
 static inline int64_t ElementRefData_len_names(ElementRefData  obj){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+8);
   int64_t* arr = ( int64_t*)(( char*) obj+offset);
  return arr[1];
}
 static inline  char* ElementRefData_getp1_names(ElementRefData  obj, int64_t i0){
  int64_t offset=0;
  offset+=*( int64_t*)(( char*) obj+offset+8);
  offset=*( int64_t*)(( char*) obj+offset+16+i0*8);
  return ( char*)(( char*) obj+offset);
}
#endif

             
            void track_line(
                  int8_t* buffer,
                             ElementRefData elem_ref_data,
                             ParticlesData particles,
                             int num_turns,
                             int ele_start,
                             int num_ele_track,
                             int flag_end_turn_actions,
                             int flag_reset_s_at_end_turn,
                             int flag_monitor,
                             int num_ele_line,
                             double line_length,
                  int8_t* buffer_tbt_monitor,
                             int64_t offset_tbt_monitor,
                  int8_t* io_buffer){

//            const int64_t capacity = ParticlesData_get__capacity(particles);               //only_for_context cpu_openmp
//            const int num_threads = omp_get_max_threads();                                 //only_for_context cpu_openmp
//            const int64_t chunk_size = (capacity + num_threads - 1)/num_threads; // ceil division  //only_for_context cpu_openmp
//            #pragma omp parallel for                                                       //only_for_context cpu_openmp
//            for (int chunk = 0; chunk < num_threads; chunk++) {                            //only_for_context cpu_openmp
//            int64_t part_id = chunk * chunk_size;                                          //only_for_context cpu_openmp
//            int64_t end_id = (chunk + 1) * chunk_size;                                     //only_for_context cpu_openmp
//            if (end_id > capacity) end_id = capacity;                                      //only_for_context cpu_openmp

            int64_t part_id = 0;                                      //only_for_context cpu_serial
//            int64_t part_id = blockDim.x * blockIdx.x + threadIdx.x;  //only_for_context cuda
//            int64_t part_id = get_global_id(0);                       //only_for_context opencl
            int64_t end_id = 0; // unused outside of openmp  //only_for_context cpu_serial cuda opencl

            LocalParticle lpart;
            lpart.io_buffer = io_buffer;

              int8_t* tbt_mon_pointer =
                            buffer_tbt_monitor + offset_tbt_monitor;
            ParticlesMonitorData tbt_monitor =
                            (ParticlesMonitorData) tbt_mon_pointer;

            int64_t part_capacity = ParticlesData_get__capacity(particles);
            if (part_id<part_capacity){
            Particles_to_LocalParticle(particles, &lpart, part_id, end_id);

            int64_t isactive = check_is_active(&lpart);

            for (int64_t iturn=0; iturn<num_turns; iturn++){

                if (!isactive){
                    break;
                }

                int64_t const ele_stop = ele_start + num_ele_track;

                #ifndef XSUITE_BACKTRACK
                if (flag_monitor==1){
                    ParticlesMonitor_track_local_particle(tbt_monitor, &lpart);
                }
                int64_t elem_idx = ele_start;
                int64_t const increm = 1;
                #else
                int64_t elem_idx = ele_stop - 1;
                int64_t const increm = -1;
                if (flag_end_turn_actions>0){
                    increment_at_turn_backtrack(&lpart, flag_reset_s_at_end_turn,
                                                line_length, num_ele_line);
                }
                #endif

                for (; ((elem_idx >= ele_start) && (elem_idx < ele_stop)); elem_idx+=increm){
                        if (flag_monitor==2){
                            ParticlesMonitor_track_local_particle(tbt_monitor, &lpart);
                        }

                        // Get the pointer to and the type id of the `elem_idx`th
                        // element in `element_ref_data.elements`:
                          void* el = ElementRefData_member_elements(elem_ref_data, elem_idx);
                        int64_t elem_type = ElementRefData_typeid_elements(elem_ref_data, elem_idx);

                        switch(elem_type){
        

                        case 0:


                            Bend_track_local_particle((BendData) el, &lpart);
                            break;

                        case 1:


                            BlackAbsorber_track_local_particle((BlackAbsorberData) el, &lpart);
                            break;

                        case 2:


                            Cavity_track_local_particle((CavityData) el, &lpart);
                            break;

                        case 3:


                            CombinedFunctionMagnet_track_local_particle((CombinedFunctionMagnetData) el, &lpart);
                            break;

                        case 4:


                            DipoleEdge_track_local_particle((DipoleEdgeData) el, &lpart);
                            break;

                        case 5:


                            #ifdef XTRACK_GLOBAL_XY_LIMIT
                            global_aperture_check(&lpart);
                            #endif

                            

                            Drift_track_local_particle((DriftData) el, &lpart);
                            break;

                        case 6:


                            Elens_track_local_particle((ElensData) el, &lpart);
                            break;

                        case 7:


                            EverestCollimator_track_local_particle((EverestCollimatorData) el, &lpart);
                            break;

                        case 8:


                            EverestCrystal_track_local_particle((EverestCrystalData) el, &lpart);
                            break;

                        case 9:


                            Exciter_track_local_particle((ExciterData) el, &lpart);
                            break;

                        case 10:


                            FirstOrderTaylorMap_track_local_particle((FirstOrderTaylorMapData) el, &lpart);
                            break;

                        case 11:


                            LimitEllipse_track_local_particle((LimitEllipseData) el, &lpart);
                            break;

                        case 12:


                            LimitPolygon_track_local_particle((LimitPolygonData) el, &lpart);
                            break;

                        case 13:


                            LimitRacetrack_track_local_particle((LimitRacetrackData) el, &lpart);
                            break;

                        case 14:


                            LimitRect_track_local_particle((LimitRectData) el, &lpart);
                            break;

                        case 15:


                            LimitRectEllipse_track_local_particle((LimitRectEllipseData) el, &lpart);
                            break;

                        case 16:


                            LineSegmentMap_track_local_particle((LineSegmentMapData) el, &lpart);
                            break;

                        case 17:


                            LongitudinalLimitRect_track_local_particle((LongitudinalLimitRectData) el, &lpart);
                            break;

                        case 18:


                            Marker_track_local_particle((MarkerData) el, &lpart);
                            break;

                        case 19:


                            Multipole_track_local_particle((MultipoleData) el, &lpart);
                            break;

                        case 20:


                            NonLinearLens_track_local_particle((NonLinearLensData) el, &lpart);
                            break;

                        case 21:


                            ParticlesMonitor_track_local_particle((ParticlesMonitorData) el, &lpart);
                            break;

                        case 22:


                            Quadrupole_track_local_particle((QuadrupoleData) el, &lpart);
                            break;

                        case 23:


                            RFMultipole_track_local_particle((RFMultipoleData) el, &lpart);
                            break;

                        case 24:


                            ReferenceEnergyIncrease_track_local_particle((ReferenceEnergyIncreaseData) el, &lpart);
                            break;

                        case 25:


                            SRotation_track_local_particle((SRotationData) el, &lpart);
                            break;

                        case 26:


                            Sextupole_track_local_particle((SextupoleData) el, &lpart);
                            break;

                        case 27:


                            SimpleThinBend_track_local_particle((SimpleThinBendData) el, &lpart);
                            break;

                        case 28:


                            SimpleThinQuadrupole_track_local_particle((SimpleThinQuadrupoleData) el, &lpart);
                            break;

                        case 29:


                            Solenoid_track_local_particle((SolenoidData) el, &lpart);
                            break;

                        case 30:


                            Wire_track_local_particle((WireData) el, &lpart);
                            break;

                        case 31:


                            XRotation_track_local_particle((XRotationData) el, &lpart);
                            break;

                        case 32:


                            XYShift_track_local_particle((XYShiftData) el, &lpart);
                            break;

                        case 33:


                            YRotation_track_local_particle((YRotationData) el, &lpart);
                            break;

                        case 34:


                            ZetaShift_track_local_particle((ZetaShiftData) el, &lpart);
                            break;

                        } //switch

                    // Setting the below flag will break particle losses
                    #ifndef DANGER_SKIP_ACTIVE_CHECK_AND_SWAPS

                    isactive = check_is_active(&lpart);
                    if (!isactive){
                        break;
                    }

                    #ifndef XSUITE_BACKTRACK
                        increment_at_element(&lpart, 1);
                    #else
                        increment_at_element(&lpart, -1);
                    #endif //XSUITE_BACKTRACK

                    #endif //DANGER_SKIP_ACTIVE_CHECK_AND_SWAPS

                } // for elements

                if (flag_monitor==2){
                    // End of turn (element-by-element mode)
                    ParticlesMonitor_track_local_particle(tbt_monitor, &lpart);
                }

                #ifndef XSUITE_BACKTRACK
                if (flag_end_turn_actions>0){
                    if (isactive){
                        increment_at_turn(&lpart, flag_reset_s_at_end_turn);
                    }
                }
                #endif //XSUITE_BACKTRACK


                #ifdef XSUITE_BACKTRACK
                if (flag_monitor==1){
                    ParticlesMonitor_track_local_particle(tbt_monitor, &lpart);
                }
                #endif //XSUITE_BACKTRACK
            } // for turns

            LocalParticle_to_Particles(&lpart, particles, part_id, 1);

            }// if partid
//            } //only_for_context cpu_openmp

            // On OpenMP we want to additionally by default reorganize all
            // the particles.
//            #ifndef XT_OMP_SKIP_REORGANIZE                             //only_for_context cpu_openmp
//            LocalParticle lpart;                                       //only_for_context cpu_openmp
//            lpart.io_buffer = io_buffer;                               //only_for_context cpu_openmp
//            Particles_to_LocalParticle(particles, &lpart, 0, capacity);//only_for_context cpu_openmp
//            check_is_active(&lpart);                                   //only_for_context cpu_openmp
//            count_reorganized_particles(&lpart);                       //only_for_context cpu_openmp
//            LocalParticle_to_Particles(&lpart, particles, 0, capacity);//only_for_context cpu_openmp
//            #endif                                                     //only_for_context cpu_openmp
        }//kernel
        
