/* Automatically generated file. Do not edit. 
 * Format:     ANSI C source code
 * Creator:    McStas <http://www.mcstas.org>
 * Instrument: RITA-2-Kristine-instrument.instr (template_body_simple)
 * Date:       Wed Oct  4 10:48:16 2023
 * File:       ./RITA-2-Kristine-instrument.c
 * CFLAGS= -I@MCCODE_LIB@/share/ -DFUNNEL 
 */

#define MCCODE_STRING ""
#define FLAVOR        "mcstas"
#define FLAVOR_UPPER  "MCSTAS"

#define MC_USE_DEFAULT_MAIN
#define MC_TRACE_ENABLED

#include <string.h>

typedef double MCNUM;
typedef struct {MCNUM x, y, z;} Coords;
typedef MCNUM Rotation[3][3];
#define MCCODE_BASE_TYPES

#ifndef MC_NUSERVAR
#define MC_NUSERVAR 10
#endif

/* Particle JUMP control logic */
struct particle_logic_struct {
int dummy;
};

struct _struct_particle {
  double x,y,z; /* position [m] */
  double vx,vy,vz; /* velocity [m/s] */
  double sx,sy,sz; /* spin [0-1] */
  int mcgravitation; /* gravity-state */
  void *mcMagnet;    /* precession-state */
  int allow_backprop; /* allow backprop */
  unsigned long randstate[7];
  double t, p;    /* time, event weight */
  long long _uid;  /* event ID */
  long _index;     /* component index where to send this event */
  long _absorbed;  /* flag set to TRUE when this event is to be removed/ignored */
  long _scattered; /* flag set to TRUE when this event has interacted with the last component instance */
  long _restore;   /* set to true if neutron event must be restored */
  long flag_nocoordschange;   /* set to true if particle is jumping */
  struct particle_logic_struct _logic;
};
typedef struct _struct_particle _class_particle;

_class_particle _particle_global_randnbuse_var;
_class_particle* _particle = &_particle_global_randnbuse_var;

#pragma acc routine
_class_particle mcgenstate(void);
#pragma acc routine
_class_particle mcsetstate(double x, double y, double z, double vx, double vy, double vz,
			   double t, double sx, double sy, double sz, double p, int mcgravitation, void *mcMagnet, int mcallowbackprop);

extern int mcgravitation;      /* flag to enable gravitation */
#pragma acc declare create ( mcgravitation )
int mcallowbackprop;        
#pragma acc declare create ( mcallowbackprop )

_class_particle mcgenstate(void) {
  _class_particle particle = mcsetstate(0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, mcgravitation, NULL, mcallowbackprop);
  return(particle);
}
/*Generated user variable handlers:*/

#pragma acc routine
double particle_getvar(_class_particle *p, char *name, int *suc);

#ifdef OPENACC
#pragma acc routine
int str_comp(char *str1, char *str2);
#endif

double particle_getvar(_class_particle *p, char *name, int *suc){
#ifndef OPENACC
#define str_comp strcmp
#endif
  int s=1;
  double rval=0;
  if(!str_comp("x",name)){rval=p->x;s=0;}
  if(!str_comp("y",name)){rval=p->y;s=0;}
  if(!str_comp("z",name)){rval=p->z;s=0;}
  if(!str_comp("vx",name)){rval=p->vx;s=0;}
  if(!str_comp("vy",name)){rval=p->vy;s=0;}
  if(!str_comp("vz",name)){rval=p->vz;s=0;}
  if(!str_comp("sx",name)){rval=p->sx;s=0;}
  if(!str_comp("sy",name)){rval=p->sy;s=0;}
  if(!str_comp("sz",name)){rval=p->sz;s=0;}
  if(!str_comp("t",name)){rval=p->t;s=0;}
  if(!str_comp("p",name)){rval=p->p;s=0;}
  if (suc!=0x0) {*suc=s;}
  return rval;
}

#pragma acc routine
void* particle_getvar_void(_class_particle *p, char *name, int *suc);

#ifdef OPENACC
#pragma acc routine
int str_comp(char *str1, char *str2);
#endif

void* particle_getvar_void(_class_particle *p, char *name, int *suc){
#ifndef OPENACC
#define str_comp strcmp
#endif
  int s=1;
  void* rval=0;
  if(!str_comp("x",name)) {rval=(void*)&(p->x); s=0;}
  if(!str_comp("y",name)) {rval=(void*)&(p->y); s=0;}
  if(!str_comp("z",name)) {rval=(void*)&(p->z); s=0;}
  if(!str_comp("vx",name)){rval=(void*)&(p->vx);s=0;}
  if(!str_comp("vy",name)){rval=(void*)&(p->vy);s=0;}
  if(!str_comp("vz",name)){rval=(void*)&(p->vz);s=0;}
  if(!str_comp("sx",name)){rval=(void*)&(p->sx);s=0;}
  if(!str_comp("sy",name)){rval=(void*)&(p->sy);s=0;}
  if(!str_comp("sz",name)){rval=(void*)&(p->sz);s=0;}
  if(!str_comp("t",name)) {rval=(void*)&(p->t); s=0;}
  if(!str_comp("p",name)) {rval=(void*)&(p->p); s=0;}
  if (suc!=0x0) {*suc=s;}
  return rval;
}

#pragma acc routine
int particle_setvar_void(_class_particle *, char *, void*);

int particle_setvar_void(_class_particle *p, char *name, void* value){
#ifndef OPENACC
#define str_comp strcmp
#endif
  int rval=1;
  if(!str_comp("x",name)) {memcpy(&(p->x),  value, sizeof(double)); rval=0;}
  if(!str_comp("y",name)) {memcpy(&(p->y),  value, sizeof(double)); rval=0;}
  if(!str_comp("z",name)) {memcpy(&(p->z),  value, sizeof(double)); rval=0;}
  if(!str_comp("vx",name)){memcpy(&(p->vx), value, sizeof(double)); rval=0;}
  if(!str_comp("vy",name)){memcpy(&(p->vy), value, sizeof(double)); rval=0;}
  if(!str_comp("vz",name)){memcpy(&(p->vz), value, sizeof(double)); rval=0;}
  if(!str_comp("sx",name)){memcpy(&(p->sx), value, sizeof(double)); rval=0;}
  if(!str_comp("sy",name)){memcpy(&(p->sy), value, sizeof(double)); rval=0;}
  if(!str_comp("sz",name)){memcpy(&(p->sz), value, sizeof(double)); rval=0;}
  if(!str_comp("p",name)) {memcpy(&(p->p),  value, sizeof(double)); rval=0;}
  if(!str_comp("t",name)) {memcpy(&(p->t),  value, sizeof(double)); rval=0;}
  return rval;
}

#pragma acc routine
int particle_setvar_void_array(_class_particle *, char *, void*, int);

int particle_setvar_void_array(_class_particle *p, char *name, void* value, int elements){
#ifndef OPENACC
#define str_comp strcmp
#endif
  int rval=1;
  return rval;
}

#pragma acc routine
void particle_restore(_class_particle *p, _class_particle *p0);

void particle_restore(_class_particle *p, _class_particle *p0) {
  p->x  = p0->x;  p->y  = p0->y;  p->z  = p0->z;
  p->vx = p0->vx; p->vy = p0->vy; p->vz = p0->vz;
  p->sx = p0->sx; p->sy = p0->sy; p->sz = p0->sz;
  p->t = p0->t;  p->p  = p0->p;
  p->_absorbed=0; p->_restore=0;
}

#pragma acc routine
double particle_getuservar_byid(_class_particle *p, int id, int *suc){
  int s=1;
  double rval=0;
  switch(id){
  }
  if (suc!=0x0) {*suc=s;}
  return rval;
}

#pragma acc routine
void particle_uservar_init(_class_particle *p){
}

#define MC_EMBEDDED_RUNTIME
/* embedding file "mccode-r.h" */

/*******************************************************************************
*
* McCode, neutron/xray ray-tracing package
*         Copyright (C) 1997-2009, All rights reserved
*         Risoe National Laboratory, Roskilde, Denmark
*         Institut Laue Langevin, Grenoble, France
*
* Runtime: share/mccode-r.h
*
* %Identification
* Written by: KN
* Date:    Aug 29, 1997
* Release: McStas 3.3
* Version: $Revision$
*
* Runtime system header for McStas/McXtrace.
*
* In order to use this library as an external library, the following variables
* and macros must be declared (see details in the code)
*
*   struct mcinputtable_struct mcinputtable[];
*   int numipar;
*   char instrument_name[], instrument_source[];
*   int traceenabled, defaultmain;
*   extern MCNUM  mccomp_storein[];
*   extern MCNUM  mcAbsorbProp[];
*   extern MCNUM  mcScattered;
*   #define MCCODE_STRING "the McStas/McXtrace version"
*
* Usage: Automatically embbeded in the c code.
*
* $Id$
*
*******************************************************************************/

#ifndef MCCODE_R_H
#define MCCODE_R_H "$Revision$"

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <limits.h>
#include <errno.h>
#include <time.h>
#include <sys/time.h>
#include <float.h>
#include <inttypes.h>
#include <stdint.h>
#ifdef OPENACC
#include <openacc.h>
#ifndef GCCOFFLOAD
#include <accelmath.h>
#else
#include <math.h>
#endif
#pragma acc routine
int noprintf();
#pragma acc routine
size_t str_len(const char *s);
#else
#include <math.h>
#endif

/* If the runtime is embedded in the simulation program, some definitions can
   be made static. */

#ifdef MC_EMBEDDED_RUNTIME
#  define mcstatic
#else
#  define mcstatic
#endif

#ifdef __dest_os
#  if (__dest_os == __mac_os)
#    define MAC
#  endif
#endif

#ifdef __FreeBSD__
#  define NEED_STAT_H
#endif

#if defined(__APPLE__) && defined(__GNUC__)
#  define NEED_STAT_H
#endif

#ifdef NEED_STAT_H
#  include <sys/stat.h>
#endif

#ifndef MC_PATHSEP_C
#  ifdef WIN32
#    define MC_PATHSEP_C '\\'
#    define MC_PATHSEP_S "\\"
#  else  /* !WIN32 */
#    define MC_PATHSEP_C '/'
#    define MC_PATHSEP_S "/"
#  endif /* !WIN32 */
#endif /* MC_PATHSEP_C */



/* the version string is replaced when building distribution with mkdist */
#ifndef MCCODE_STRING
#  define MCCODE_STRING "McStas 3.3 - Mar. 30, 2023"
#endif

#ifndef MCCODE_DATE
#  define MCCODE_DATE "Mar. 30, 2023"
#endif

#ifndef MCCODE_VERSION
#  define MCCODE_VERSION "3.3"
#endif

#ifndef MCCODE_NAME
#  define MCCODE_NAME "McStas"
#endif

#ifndef MCCODE_PARTICLE
#  define MCCODE_PARTICLE "neutron"
#endif

#ifndef MCCODE_PARTICLE_CODE
#  define MCCODE_PARTICLE_CODE 2112
#endif

#ifndef MCCODE_LIBENV
#  define MCCODE_LIBENV "MCSTAS"
#endif

#ifndef FLAVOR_UPPER
#  define FLAVOR_UPPER MCCODE_NAME
#endif

#ifdef MC_PORTABLE
#  ifndef NOSIGNALS
#    define NOSIGNALS 1
#  endif
#endif

#ifdef MAC
#  ifndef NOSIGNALS
#    define NOSIGNALS 1
#  endif
#endif

#if (USE_MPI == 0)
#  undef USE_MPI
#endif

#ifdef USE_MPI  /* default is to disable signals with MPI, as MPICH uses them to communicate */
#  ifndef NOSIGNALS
#    define NOSIGNALS 1
#  endif
#endif

#ifdef OPENACC  /* default is to disable signals with PGI/OpenACC */
#  ifndef NOSIGNALS
#    define NOSIGNALS 1
#  endif
#endif

#ifndef OPENACC
#  ifndef USE_OFF  /* default is to enable OFF when not using PGI/OpenACC */
#    define USE_OFF
#  endif
#  ifndef CPUFUNNEL  /* allow to enable FUNNEL-mode on CPU */
#  ifdef FUNNEL      /* by default disable FUNNEL-mode when not using PGI/OpenACC */
#    undef FUNNEL
#  endif
#  endif
#endif

#if (NOSIGNALS == 0)
#  undef NOSIGNALS
#endif

/* Note: the enum instr_formal_types definition MUST be kept
   synchronized with the one in mccode.h and with the
   instr_formal_type_names array in cogen.c. */
enum instr_formal_types
  {
    instr_type_int,
    instr_type_string, instr_type_char,
    instr_type_vector, instr_type_double
  };
struct mcinputtable_struct { /* defines instrument parameters */
  char *name; /* name of parameter */
  void *par;  /* pointer to instrument parameter (variable) */
  enum instr_formal_types type;
  char *val;  /* default value */
};

#ifndef MCCODE_BASE_TYPES
typedef double MCNUM;
typedef struct {MCNUM x, y, z;} Coords;
typedef MCNUM Rotation[3][3];
#endif

/* the following variables are defined in the McStas generated C code
   but should be defined externally in case of independent library usage */
#ifndef DANSE
extern struct mcinputtable_struct mcinputtable[];         /* list of instrument parameters */
extern int    numipar;                                    /* number of instrument parameters */
extern char   instrument_name[], instrument_source[]; /* instrument name and filename */
extern char  *instrument_exe;                           /* executable path = argv[0] or NULL */
extern char   instrument_code[];                        /* contains the initial 'instr' file */

#ifndef MC_ANCIENT_COMPATIBILITY
extern int traceenabled, defaultmain;
#endif
#endif


/* Useful macros ============================================================ */


/* SECTION: Dynamic Arrays */
typedef int* IArray1d;
IArray1d create_iarr1d(int n);
void destroy_iarr1d(IArray1d a);

typedef int** IArray2d;
IArray2d create_iarr2d(int nx, int ny);
void destroy_iarr2d(IArray2d a);

typedef int*** IArray3d;
IArray3d create_iarr3d(int nx, int ny, int nz);
void destroy_iarr3d(IArray3d a);

typedef double* DArray1d;
DArray1d create_darr1d(int n);
void destroy_darr1d(DArray1d a);

typedef double** DArray2d;
DArray2d create_darr2d(int nx, int ny);
void destroy_darr2d(DArray2d a);

typedef double*** DArray3d;
DArray3d create_darr3d(int nx, int ny, int nz);
void destroy_darr3d(DArray3d a);


/* MPI stuff */
#ifdef USE_MPI
#include "mpi.h"

#ifdef OMPI_MPI_H  /* openmpi does not use signals: we may install our sighandler */
#ifndef OPENACC    /* ... but only if we are not also running on GPU */
#undef NOSIGNALS
#endif
#endif

/*
 * MPI_MASTER(i):
 * execution of i only on master node
 */
#define MPI_MASTER(statement) { \
  if(mpi_node_rank == mpi_node_root)\
  { statement; } \
}

#ifndef MPI_REDUCE_BLOCKSIZE
#define MPI_REDUCE_BLOCKSIZE 1000
#endif

int mc_MPI_Sum(double* buf, long count);
int mc_MPI_Send(void *sbuf, long count, MPI_Datatype dtype, int dest);
int mc_MPI_Recv(void *rbuf, long count, MPI_Datatype dtype, int source);

/* MPI_Finalize exits gracefully and should be preferred to MPI_Abort */
#define exit(code) do {                                   \
    MPI_Finalize();                                       \
    exit(code);                                           \
  } while(0)

#else /* !USE_MPI */
#define MPI_MASTER(instr) instr
#endif /* USE_MPI */


#ifdef USE_MPI
static int mpi_node_count;
#endif

#ifdef USE_THREADS  /* user want threads */
#error Threading (USE_THREADS) support has been removed for very poor efficiency. Use MPI/SSH grid instead.
#endif


void   mcset_ncount(unsigned long long count);    /* wrapper to get mcncount */
#pragma acc routine
unsigned long long int mcget_ncount(void);            /* wrapper to set mcncount */
unsigned long long mcget_run_num(void);           /* wrapper to get mcrun_num=0:mcncount-1 */

/* Following part is only embedded when not redundant with mccode.h ========= */

#ifndef MCCODE_H

#ifndef NOSIGNALS
#include <signal.h>
char  *mcsig_message;
#define SIG_MESSAGE(msg) mcsig_message=(char *)(msg);
#else
#define SIG_MESSAGE(...)
#endif /* !NOSIGNALS */


/* Useful macros and constants ============================================== */


#ifndef FLT_MAX
#define FLT_MAX         3.40282347E+38F /* max decimal value of a "float" */
#endif

#ifndef MIN
#define MIN(a, b)  (((a) < (b)) ? (a) : (b))
#endif
#ifndef MAX
#define MAX(a, b)  (((a) > (b)) ? (a) : (b))
#endif
#ifndef SQR
#define SQR(x) ( (x) * (x) )
#endif
#ifndef SIGN
#define SIGN(x) (((x)>0.0)?(1):(-1))
#endif


#  ifndef M_E
#    define M_E        2.71828182845904523536  // e
#  endif
#  ifndef M_LOG2E
#    define M_LOG2E    1.44269504088896340736  //  log2(e)
#  endif
#  ifndef M_LOG10E
#    define M_LOG10E   0.434294481903251827651 //  log10(e)
#  endif
#  ifndef M_LN2
#    define M_LN2      0.693147180559945309417 //  ln(2)
#  endif
#  ifndef M_LN10
#    define M_LN10     2.30258509299404568402  //  ln(10)
#  endif
#  ifndef M_PI
#    define M_PI       3.14159265358979323846  //  pi
#  endif
#  ifndef PI
#    define PI       M_PI                      //  pi - also used in some places
#  endif
#  ifndef M_PI_2
#    define M_PI_2     1.57079632679489661923  //  pi/2
#  endif
#  ifndef M_PI_4
#    define M_PI_4     0.785398163397448309616 //  pi/4
#  endif
#  ifndef M_1_PI
#    define M_1_PI     0.318309886183790671538 //  1/pi
#  endif
#  ifndef M_2_PI
#    define M_2_PI     0.636619772367581343076 //  2/pi
#  endif
#  ifndef M_2_SQRTPI
#    define M_2_SQRTPI 1.12837916709551257390  //  2/sqrt(pi)
#  endif
#  ifndef M_SQRT2
#    define M_SQRT2    1.41421356237309504880  //  sqrt(2)
#  endif
#  ifndef M_SQRT1_2
#    define M_SQRT1_2  0.707106781186547524401 //  1/sqrt(2)
#  endif

#define RAD2MIN  ((180*60)/PI)
#define MIN2RAD  (PI/(180*60))
#define DEG2RAD  (PI/180)
#define RAD2DEG  (180/PI)
#define FWHM2RMS 0.424660900144    /* Convert between full-width-half-max and */
#define RMS2FWHM 2.35482004503     /* root-mean-square (standard deviation) */
#define HBAR     1.05457168e-34    /* [Js] h bar Planck constant CODATA 2002 */
#define MNEUTRON 1.67492728e-27    /* [kg] mass of neutron CODATA 2002 */
#define GRAVITY  9.81              /* [m/s^2] gravitational acceleration */
#define NA       6.02214179e23     /* [#atoms/g .mole] Avogadro's number*/


#define UNSET nan("0x6E6F74736574")
int nans_match(double, double);
int is_unset(double);
int is_valid(double);
int is_set(double);
int all_unset(int n, ...);
int all_set(int n, ...);
int any_unset(int n, ...);
int any_set(int n, ...);


/* wrapper to get absolute and relative position of comp */
/* mccomp_posa and mccomp_posr are defined in McStas generated C code */
#define POS_A_COMP_INDEX(index) (instrument->_position_absolute[index])
#define POS_R_COMP_INDEX(index) (instrument->_position_relative[index])

/* setting parameters based COMP_GETPAR (returned as pointer)         */
/* compname must be given as a string, type and par are symbols.      */
#define COMP_GETPAR3(type, compname, par) \
    &( ((_class_ ## type ##_parameters *) _getvar_parameters(compname))->par )
/* the body of this function depends on component instances, and is cogen'd */
void* _getvar_parameters(char* compname);

int _getcomp_index(char* compname);

/* Note: The two-stage approach to COMP_GETPAR is NOT redundant; without it,
* after #define C sample, COMP_GETPAR(C,x) would refer to component C, not to
* component sample. Such are the joys of ANSI C.

* Anyway the usage of COMP_GETPAR requires that we use sometimes bare names...
* NOTE: This can ONLY be used in instrument descriptions, not components.
*/
#define COMP_GETPAR2(comp, par) (_ ## comp ## _var._parameters.par)
#define COMP_GETPAR(comp, par) COMP_GETPAR2(comp,par)

#define INSTRUMENT_GETPAR(par) (_instrument_var._parameters.par)

/* Current component name, index, position and orientation */
/* These macros work because, using class-based functions, "comp" is usually
*  the local variable of the active/current component. */
#define INDEX_CURRENT_COMP (_comp->_index)
#define NAME_CURRENT_COMP (_comp->_name)
#define TYPE_CURRENT_COMP (_comp->_type)
#define POS_A_CURRENT_COMP (_comp->_position_absolute)
#define POS_R_CURRENT_COMP (_comp->_position_relative)
#define ROT_A_CURRENT_COMP (_comp->_rotation_absolute)
#define ROT_R_CURRENT_COMP (_comp->_rotation_relative)

#define NAME_INSTRUMENT (instrument->_name)


/* MCDISPLAY/trace and debugging message sent to stdout */
#ifdef MC_TRACE_ENABLED
#define DEBUG
#endif

#ifdef DEBUG
#define DEBUG_INSTR() if(!mcdotrace); else { printf("INSTRUMENT:\n"); printf("Instrument '%s' (%s)\n", instrument_name, instrument_source); }
#define DEBUG_COMPONENT(name,c,t) if(!mcdotrace); else {\
  printf("COMPONENT: \"%s\"\n" \
         "POS: %g, %g, %g, %g, %g, %g, %g, %g, %g, %g, %g, %g\n", \
         name, c.x, c.y, c.z, t[0][0], t[0][1], t[0][2], \
         t[1][0], t[1][1], t[1][2], t[2][0], t[2][1], t[2][2]); \
  printf("Component %30s AT (%g,%g,%g)\n", name, c.x, c.y, c.z); \
  }
#define DEBUG_INSTR_END() if(!mcdotrace); else printf("INSTRUMENT END:\n");
#define DEBUG_ENTER() if(!mcdotrace); else printf("ENTER:\n");
#define DEBUG_COMP(c) if(!mcdotrace); else printf("COMP: \"%s\"\n", c);
#define DEBUG_LEAVE() if(!mcdotrace); else printf("LEAVE:\n");
#define DEBUG_ABSORB() if(!mcdotrace); else printf("ABSORB:\n");
#else
#define DEBUG_INSTR()
#define DEBUG_COMPONENT(name,c,t)
#define DEBUG_INSTR_END()
#define DEBUG_ENTER()
#define DEBUG_COMP(c)
#define DEBUG_LEAVE()
#define DEBUG_ABSORB()
#endif

// mcDEBUG_STATE and mcDEBUG_SCATTER are defined by mcstas-r.h and mcxtrace-r.h



#ifdef TEST
#define test_printf printf
#else
#define test_printf while(0) printf
#endif

/* send MCDISPLAY message to stdout to show gemoetry */
void mcdis_magnify(char *what);
void mcdis_line(double x1, double y1, double z1,
                double x2, double y2, double z2);
void mcdis_dashed_line(double x1, double y1, double z1,
		       double x2, double y2, double z2, int n);
void mcdis_multiline(int count, ...);
void mcdis_rectangle(char* plane, double x, double y, double z,
		     double width, double height);
void mcdis_box(double x, double y, double z,
	       double width, double height, double length);
void mcdis_circle(char *plane, double x, double y, double z, double r);
void mcdis_Circle(double x, double y, double z, double r, double nx, double ny, double nz);
void mcdis_cylinder( double x, double y, double z,
        double r, double height, int N, double nx, double ny, double nz);
void mcdis_sphere(double x, double y, double z, double r, int N);


/* random number generation. ================================================ */

/* available random number generators */
#define _RNG_ALG_MT         1
#define _RNG_ALG_KISS       2

/* selection of random number generator */
#ifndef RNG_ALG
#  define RNG_ALG  _RNG_ALG_KISS
#endif


#if RNG_ALG == _RNG_ALG_MT  // MT (currently not functional for gpu)
#  define MC_RAND_MAX ((unsigned long)0xffffffff)
#  define randstate_t unsigned long // this could be anything
#  define RANDSTATE_LEN 1
#  define srandom(seed) mt_srandom_empty()
#  define random() mt_random()
#  define _random() mt_random()
#elif RNG_ALG == _RNG_ALG_KISS  // KISS
#  ifndef ULONG_MAX
#    define ULONG_MAX ((unsigned long)0xffffffffffffffffUL)
#  endif
#  define MC_RAND_MAX ULONG_MAX
#  define randstate_t unsigned long
#  define RANDSTATE_LEN 7
#  define srandom(seed) kiss_srandom(_particle->randstate, seed)
#  define random() kiss_random(_particle->randstate)
#  define _random() kiss_random(state)
#endif

#pragma acc routine
double _randnorm2(randstate_t* state);


// component writers interface
#define randnorm() _randnorm2(_particle->randstate) // NOTE: can not use _randnorm on gpu
#define rand01() _rand01(_particle->randstate)
#define randpm1() _randpm1(_particle->randstate)
#define rand0max(p1) _rand0max(p1, _particle->randstate)
#define randminmax(p1, p2) _randminmax(p1, p2, _particle->randstate)
#define randtriangle() _randtriangle(_particle->randstate)

// Mersenne Twister rng
unsigned long mt_random(void);
void mt_srandom (unsigned long x);
void mt_srandom_empty();

// KISS rng
#pragma acc routine
unsigned long *kiss_srandom(unsigned long state[7], unsigned long seed);
#pragma acc routine
unsigned long kiss_random(unsigned long state[7]);

// Scrambler / hash function
#pragma acc routine seq
randstate_t _hash(randstate_t x);

// internal RNG (transforms) interface
#pragma acc routine
double _rand01(randstate_t* state);
#pragma acc routine
double _randpm1(randstate_t* state);
#pragma acc routine
double _rand0max(double max, randstate_t* state);
#pragma acc routine
double _randminmax(double min, double max, randstate_t* state);
#pragma acc routine
double _randtriangle(randstate_t* state);


#ifdef USE_OPENCL
#include "opencl-lib.h"
#include "opencl-lib.c"
#endif

#ifndef DANSE
int init(void);
int raytrace(_class_particle*);
int save(FILE *);
int finally(void);
int display(void);
#endif


/* GPU related algorithms =================================================== */

/*
*  Divide-and-conquer strategy for parallel sort absorbed last.
*/
#ifdef FUNNEL
long sort_absorb_last(_class_particle* particles, _class_particle* pbuffer, long len, long buffer_len, long flag_split, long* multiplier);
#endif
long sort_absorb_last_serial(_class_particle* particles, long len);


/* simple vector algebra ==================================================== */


#define vec_prod(x, y, z, x1, y1, z1, x2, y2, z2) \
	vec_prod_func(&x, &y, &z, x1, y1, z1, x2, y2, z2)
#pragma acc routine seq
mcstatic void vec_prod_func(double *x, double *y, double *z,
		double x1, double y1, double z1, double x2, double y2, double z2);

#pragma acc routine seq
mcstatic double scalar_prod(
		double x1, double y1, double z1, double x2, double y2, double z2);

#pragma acc routine seq
mcstatic void norm_func(double *x, double *y, double *z);
#define NORM(x,y,z)	norm_func(&x, &y, &z)

#pragma acc routine seq
void normal_vec(double *nx, double *ny, double *nz,
    double x, double y, double z);

/**
 * Rotate the vector vx,vy,vz psi radians around the vector ax,ay,az
 * and put the result in x,y,z.
 */
#define rotate(x, y, z, vx, vy, vz, phi, ax, ay, az) \
  do { \
    double mcrt_tmpx = (ax), mcrt_tmpy = (ay), mcrt_tmpz = (az); \
    double mcrt_vp, mcrt_vpx, mcrt_vpy, mcrt_vpz; \
    double mcrt_vnx, mcrt_vny, mcrt_vnz, mcrt_vn1x, mcrt_vn1y, mcrt_vn1z; \
    double mcrt_bx, mcrt_by, mcrt_bz; \
    double mcrt_cos, mcrt_sin; \
    NORM(mcrt_tmpx, mcrt_tmpy, mcrt_tmpz); \
    mcrt_vp = scalar_prod((vx), (vy), (vz), mcrt_tmpx, mcrt_tmpy, mcrt_tmpz); \
    mcrt_vpx = mcrt_vp*mcrt_tmpx; \
    mcrt_vpy = mcrt_vp*mcrt_tmpy; \
    mcrt_vpz = mcrt_vp*mcrt_tmpz; \
    mcrt_vnx = (vx) - mcrt_vpx; \
    mcrt_vny = (vy) - mcrt_vpy; \
    mcrt_vnz = (vz) - mcrt_vpz; \
    vec_prod(mcrt_bx, mcrt_by, mcrt_bz, \
             mcrt_tmpx, mcrt_tmpy, mcrt_tmpz, mcrt_vnx, mcrt_vny, mcrt_vnz); \
    mcrt_cos = cos((phi)); mcrt_sin = sin((phi)); \
    mcrt_vn1x = mcrt_vnx*mcrt_cos + mcrt_bx*mcrt_sin; \
    mcrt_vn1y = mcrt_vny*mcrt_cos + mcrt_by*mcrt_sin; \
    mcrt_vn1z = mcrt_vnz*mcrt_cos + mcrt_bz*mcrt_sin; \
    (x) = mcrt_vpx + mcrt_vn1x; \
    (y) = mcrt_vpy + mcrt_vn1y; \
    (z) = mcrt_vpz + mcrt_vn1z; \
  } while(0)

/**
 * Mirror (xyz) in the plane given by the point (rx,ry,rz) and normal (nx,ny,nz)
 *
 * TODO: This define is seemingly never used...
 */
#define mirror(x,y,z,rx,ry,rz,nx,ny,nz) \
  do { \
    double mcrt_tmpx= (nx), mcrt_tmpy = (ny), mcrt_tmpz = (nz); \
    double mcrt_tmpt; \
    NORM(mcrt_tmpx, mcrt_tmpy, mcrt_tmpz); \
    mcrt_tmpt=scalar_prod((rx),(ry),(rz),mcrt_tmpx,mcrt_tmpy,mcrt_tmpz); \
    (x) = rx -2 * mcrt_tmpt*mcrt_rmpx; \
    (y) = ry -2 * mcrt_tmpt*mcrt_rmpy; \
    (z) = rz -2 * mcrt_tmpt*mcrt_rmpz; \
  } while (0)

#pragma acc routine
Coords coords_set(MCNUM x, MCNUM y, MCNUM z);
#pragma acc routine
Coords coords_get(Coords a, MCNUM *x, MCNUM *y, MCNUM *z);
#pragma acc routine
Coords coords_add(Coords a, Coords b);
#pragma acc routine
Coords coords_sub(Coords a, Coords b);
#pragma acc routine
Coords coords_neg(Coords a);
#pragma acc routine
Coords coords_scale(Coords b, double scale);
#pragma acc routine
double coords_sp(Coords a, Coords b);
#pragma acc routine
Coords coords_xp(Coords b, Coords c);
#pragma acc routine
double coords_len(Coords a);
#pragma acc routine seq
void   coords_print(Coords a);
#pragma acc routine seq
mcstatic void coords_norm(Coords* c);

#pragma acc routine seq
void rot_set_rotation(Rotation t, double phx, double phy, double phz);
#pragma acc routine seq
int  rot_test_identity(Rotation t);
#pragma acc routine seq
void rot_mul(Rotation t1, Rotation t2, Rotation t3);
#pragma acc routine seq
void rot_copy(Rotation dest, Rotation src);
#pragma acc routine seq
void rot_transpose(Rotation src, Rotation dst);
#pragma acc routine seq
Coords rot_apply(Rotation t, Coords a);

#pragma acc routine seq
void mccoordschange(Coords a, Rotation t, _class_particle *particle);
#pragma acc routine seq
void mccoordschange_polarisation(Rotation t, double *sx, double *sy, double *sz);

double mcestimate_error(double N, double p1, double p2);
void mcreadparams(void);

/* this is now in mcstas-r.h and mcxtrace-r.h as the number of state parameters
is no longer equal */

_class_particle mcgenstate(void);

// trajectory/shape intersection routines
#pragma acc routine seq
int inside_rectangle(double, double, double, double);
#pragma acc routine seq
int box_intersect(double *dt_in, double *dt_out, double x, double y, double z,
      double vx, double vy, double vz, double dx, double dy, double dz);
#pragma acc routine seq
int cylinder_intersect(double *t0, double *t1, double x, double y, double z,
      double vx, double vy, double vz, double r, double h);
#pragma acc routine seq
int sphere_intersect(double *t0, double *t1, double x, double y, double z,
      double vx, double vy, double vz, double r);
// second order equation roots
#pragma acc routine seq
int solve_2nd_order(double *t1, double *t2,
      double A,  double B,  double C);

// random vector generation to shape
// defines silently introducing _particle as the last argument
#define randvec_target_circle(xo, yo, zo, solid_angle, xi, yi, zi, radius) \
  _randvec_target_circle(xo, yo, zo, solid_angle, xi, yi, zi, radius, _particle)
#define randvec_target_rect_angular(xo, yo, zo, solid_angle, xi, yi, zi, height, width, A) \
  _randvec_target_rect_angular(xo, yo, zo, solid_angle, xi, yi, zi, height, width, A, _particle)
#define randvec_target_rect_real(xo, yo, zo, solid_angle, xi, yi, zi, height, width, A, lx, ly, lz, order) \
  _randvec_target_rect_real(xo, yo, zo, solid_angle, xi, yi, zi, height, width, A, lx, ly, lz, order, _particle)
// defines forwarding to "inner" functions
#define randvec_target_sphere randvec_target_circle
#define randvec_target_rect(p0,p1,p2,p3,p4,p5,p6,p7,p8,p9) \
  randvec_target_rect_real(p0,p1,p2,p3,p4,p5,p6,p7,p8,p9,0,0,0,1)
// headers for randvec
#pragma acc routine seq
void _randvec_target_circle(double *xo, double *yo, double *zo,
  double *solid_angle, double xi, double yi, double zi, double radius,
  _class_particle* _particle);
#pragma acc routine seq
void _randvec_target_rect_angular(double *xo, double *yo, double *zo,
  double *solid_angle, double xi, double yi, double zi, double height,
  double width, Rotation A,
  _class_particle* _particle);
#pragma acc routine seq
void _randvec_target_rect_real(double *xo, double *yo, double *zo, double *solid_angle,
  double xi, double yi, double zi, double height, double width, Rotation A,
  double lx, double ly, double lz, int order,
  _class_particle* _particle);


// this is the main()
int mccode_main(int argc, char *argv[]);


#endif /* !MCCODE_H */

#ifndef MCCODE_R_IO_H
#define MCCODE_R_IO_H "$Revision$"

#if (USE_NEXUS == 0)
#undef USE_NEXUS
#endif

#ifndef CHAR_BUF_LENGTH
#define CHAR_BUF_LENGTH 1024
#endif


/* I/O section part ========================================================= */

/* ========================================================================== */

/*                               MCCODE_R_IO_C                                */

/* ========================================================================== */


/* main DETECTOR structure which stores most information to write to data files */
struct mcdetector_struct {
  char   filename[CHAR_BUF_LENGTH];   /* file name of monitor */
  char   position[CHAR_BUF_LENGTH];   /* position of detector component */
  char   component[CHAR_BUF_LENGTH];  /* component instance name */
  char   instrument[CHAR_BUF_LENGTH]; /* instrument name */
  char   type[CHAR_BUF_LENGTH];       /* data type, e.g. 0d, 1d, 2d, 3d */
  char   user[CHAR_BUF_LENGTH];       /* user name, e.g. HOME */
  char   date[CHAR_BUF_LENGTH];       /* date of simulation end/write time */
  char   title[CHAR_BUF_LENGTH];      /* title of detector */
  char   xlabel[CHAR_BUF_LENGTH];     /* X axis label */
  char   ylabel[CHAR_BUF_LENGTH];     /* Y axis label */
  char   zlabel[CHAR_BUF_LENGTH];     /* Z axis label */
  char   xvar[CHAR_BUF_LENGTH];       /* X variable name */
  char   yvar[CHAR_BUF_LENGTH];       /* Y variable name */
  char   zvar[CHAR_BUF_LENGTH];       /* Z variable name */
  char   ncount[CHAR_BUF_LENGTH];     /* number of events initially generated */
  char   limits[CHAR_BUF_LENGTH];     /* X Y Z limits, e.g. [xmin xmax ymin ymax zmin zmax] */
  char   variables[CHAR_BUF_LENGTH];  /* variables written into data block */
  char   statistics[CHAR_BUF_LENGTH]; /* center, mean and half width along axis */
  char   signal[CHAR_BUF_LENGTH];     /* min max and mean of signal (data block) */
  char   values[CHAR_BUF_LENGTH];     /* integrated values e.g. [I I_err N] */
  double xmin,xmax;                   /* min max of axes */
  double ymin,ymax;
  double zmin,zmax;
  double intensity;                   /* integrated values for data block */
  double error;
  double events;
  double min;                         /* statistics for data block */
  double max;
  double mean;
  double centerX;                     /* statistics for axes */
  double halfwidthX;
  double centerY;
  double halfwidthY;
  int    rank;                        /* dimensionaly of monitor, e.g. 0 1 2 3 */
  char   istransposed;                /* flag to transpose matrix for some formats */

  long   m,n,p;                       /* dimensions of data block and along axes */
  long   date_l;                      /* same as date, but in sec since 1970 */

  double *p0, *p1, *p2;               /* pointers to saved data, NULL when freed */
  char   format[CHAR_BUF_LENGTH];    /* format for file generation */
};

typedef struct mcdetector_struct MCDETECTOR;

static   char *dirname             = NULL;      /* name of output directory */
static   char *siminfo_name        = "mccode";  /* default output sim file name */
char    *mcformat                    = NULL;      /* NULL (default) or a specific format */

/* file I/O definitions and function prototypes */

#ifndef MC_EMBEDDED_RUNTIME /* the mcstatic variables (from mccode-r.c) */
extern FILE * siminfo_file;     /* handle to the output siminfo file */
extern int    mcgravitation;      /* flag to enable gravitation */
extern int    mcdotrace;          /* flag to print MCDISPLAY messages */
#else
mcstatic FILE *siminfo_file        = NULL;
#endif

/* I/O function prototypes ================================================== */

// from msysgit: https://code.google.com/p/msysgit/source/browse/compat/strcasestr.c
char *strcasestr(const char *haystack, const char *needle);

/* output functions */
MCDETECTOR mcdetector_out_0D(char *t, double p0, double p1, double p2, char *c, Coords pos);
MCDETECTOR mcdetector_out_1D(char *t, char *xl, char *yl,
                  char *xvar, double x1, double x2, long n,
                  double *p0, double *p1, double *p2, char *f, char *c, Coords pos);
MCDETECTOR mcdetector_out_2D(char *t, char *xl, char *yl,
                  double x1, double x2, double y1, double y2, long m,
                  long n, double *p0, double *p1, double *p2, char *f,
                  char *c, Coords pos);
MCDETECTOR mcdetector_out_list(char *t, char *xl, char *yl,
                  long m, long n,
                  double *p1, char *f,
                  char *c, Coords posa);

/* wrappers to output functions, that automatically set NAME and POSITION */
#define DETECTOR_OUT(p0,p1,p2) mcdetector_out_0D(NAME_CURRENT_COMP,p0,p1,p2,NAME_CURRENT_COMP,POS_A_CURRENT_COMP)
#define DETECTOR_OUT_0D(t,p0,p1,p2) mcdetector_out_0D(t,p0,p1,p2,NAME_CURRENT_COMP,POS_A_CURRENT_COMP)
#define DETECTOR_OUT_1D(t,xl,yl,xvar,x1,x2,n,p0,p1,p2,f) \
     mcdetector_out_1D(t,xl,yl,xvar,x1,x2,n,p0,p1,p2,f,NAME_CURRENT_COMP,POS_A_CURRENT_COMP)
#define DETECTOR_OUT_2D(t,xl,yl,x1,x2,y1,y2,m,n,p0,p1,p2,f) \
     mcdetector_out_2D(t,xl,yl,x1,x2,y1,y2,m,n,p0,p1,p2,f,NAME_CURRENT_COMP,POS_A_CURRENT_COMP)

#ifdef USE_NEXUS
#include "napi.h"
NXhandle nxhandle;
#endif

#endif /* ndef MCCODE_R_IO_H */

#endif /* MCCODE_R_H */
/* End of file "mccode-r.h". */

/* embedding file "mcstas-r.h" */

/*******************************************************************************
*
* McStas, neutron ray-tracing package
*         Copyright (C) 1997-2009, All rights reserved
*         Risoe National Laboratory, Roskilde, Denmark
*         Institut Laue Langevin, Grenoble, France
*
* Runtime: share/mcstas-r.h
*
* %Identification
* Written by: KN
* Date:    Aug 29, 1997
* Release: McStas X.Y
* Version: $Revision$
*
* Runtime system header for McStas.
*
* In order to use this library as an external library, the following variables
* and macros must be declared (see details in the code)
*
*   struct mcinputtable_struct mcinputtable[];
*   int mcnumipar;
*   char instrument_name[], instrument_source[];
*   int traceenabled, defaultmain;
*   extern MCNUM  mccomp_storein[];
*   extern MCNUM  instrument.counter_AbsorbProp[];
*   extern MCNUM  mcScattered;
*   #define MCCODE_STRING "the McStas version"
*
* Usage: Automatically embbeded in the c code.
*
* $Id$
*
*******************************************************************************/

#ifndef MCSTAS_R_H
#define MCSTAS_R_H "$Revision$"

/* Following part is only embedded when not redundent with mcstas.h */

#ifndef MCCODE_H

#define AA2MS    629.622368        /* Convert k[1/AA] to v[m/s] */
#define MS2AA    1.58825361e-3     /* Convert v[m/s] to k[1/AA] */
#define K2V      AA2MS
#define V2K      MS2AA
#define Q2V      AA2MS
#define V2Q      MS2AA
#define SE2V     437.393377        /* Convert sqrt(E)[meV] to v[m/s] */
#define VS2E     5.22703725e-6     /* Convert (v[m/s])**2 to E[meV] */

#define SCATTER0 do {DEBUG_SCATTER(); SCATTERED++;} while(0)
#define SCATTER SCATTER0

#define JUMPTOCOMP(comp) mcneutron->_index = INDEX_COMP(comp);

#define MAGNET_ON \
  do { \
    mcMagnet = 1; \
  } while(0)

#define MAGNET_OFF \
  do { \
    mcMagnet = 0; \
  } while(0)

#define ALLOW_BACKPROP \
  do { \
    mcallowbackprop = 1; \
  } while(0)

#define DISALLOW_BACKPROP \
  do { \
    mcallowbackprop = 0; \
  } while(0)

#define PROP_MAGNET(dt) \
  do { \
  } while (0)
    /* change coordinates from local system to magnet system */
/*    Rotation rotLM, rotTemp; \
      Coords   posLM = coords_sub(POS_A_CURRENT_COMP, mcMagnetPos); \
      rot_transpose(ROT_A_CURRENT_COMP, rotTemp); \
      rot_mul(rotTemp, mcMagnetRot, rotLM); \
      mcMagnetPrecession(x, y, z, t, vx, vy, vz, \
               &sx, &sy, &sz, dt, posLM, rotLM); \
      } while(0)
*/

#define mcPROP_DT(dt) \
  do { \
    if (mcMagnet && dt > 0) PROP_MAGNET(dt);\
    x += vx*(dt); \
    y += vy*(dt); \
    z += vz*(dt); \
    t += (dt); \
    if (isnan(p) || isinf(p)) { ABSORB; }\
  } while(0)

/* ADD: E. Farhi, Aug 6th, 2001 PROP_GRAV_DT propagation with acceleration */
#define PROP_GRAV_DT(dt, Ax, Ay, Az) \
  do { \
    if(dt < 0 && mcallowbackprop == 0) { ABSORB; }\
    if (mcMagnet) /*printf("Spin precession gravity\n")*/; \
    x  += vx*(dt) + (Ax)*(dt)*(dt)/2; \
    y  += vy*(dt) + (Ay)*(dt)*(dt)/2; \
    z  += vz*(dt) + (Az)*(dt)*(dt)/2; \
    vx += (Ax)*(dt); \
    vy += (Ay)*(dt); \
    vz += (Az)*(dt); \
    t  += (dt); \
    DISALLOW_BACKPROP;\
  } while(0)


#define PROP_DT(dt) \
  do { \
    if(dt < 0) { RESTORE=1; ABSORB; }; \
    if (mcgravitation) { Coords mcLocG; double mc_gx, mc_gy, mc_gz; \
    mcLocG = rot_apply(ROT_A_CURRENT_COMP, coords_set(0,-GRAVITY,0)); \
    coords_get(mcLocG, &mc_gx, &mc_gy, &mc_gz); \
    PROP_GRAV_DT(dt, mc_gx, mc_gy, mc_gz); } \
    else mcPROP_DT(dt); \
    DISALLOW_BACKPROP;\
  } while(0)


#define PROP_Z0 \
  do { \
    if (mcgravitation) { Coords mcLocG; int mc_ret; \
    double mc_dt, mc_gx, mc_gy, mc_gz; \
    mcLocG = rot_apply(ROT_A_CURRENT_COMP, coords_set(0,-GRAVITY,0)); \
    coords_get(mcLocG, &mc_gx, &mc_gy, &mc_gz); \
    mc_ret = solve_2nd_order(&mc_dt, NULL, -mc_gz/2, -vz, -z); \
    if (mc_ret) {PROP_GRAV_DT(mc_dt, mc_gx, mc_gy, mc_gz); z=0;}\
    else if (mcallowbackprop == 0 && mc_dt < 0) { ABSORB; }; } \
    else mcPROP_Z0; \
    DISALLOW_BACKPROP;\
  } while(0)

#define mcPROP_Z0 \
  do { \
    double mc_dt; \
    if(vz == 0) { ABSORB; }; \
    mc_dt = -z/vz; \
    if(mc_dt < 0 && mcallowbackprop == 0) { ABSORB; }; \
    mcPROP_DT(mc_dt); \
    z = 0; \
    DISALLOW_BACKPROP;\
  } while(0)

#define PROP_X0 \
  do { \
    if (mcgravitation) { Coords mcLocG; int mc_ret; \
    double mc_dt, mc_gx, mc_gy, mc_gz; \
    mcLocG = rot_apply(ROT_A_CURRENT_COMP, coords_set(0,-GRAVITY,0)); \
    coords_get(mcLocG, &mc_gx, &mc_gy, &mc_gz); \
    mc_ret = solve_2nd_order(&mc_dt, NULL, -mc_gx/2, -vx, -x); \
    if (mc_ret) {PROP_GRAV_DT(mc_dt, mc_gx, mc_gy, mc_gz); x=0;}\
    else if (mcallowbackprop == 0 && mc_dt < 0) { ABSORB; }; } \
    else mcPROP_X0; \
    DISALLOW_BACKPROP;\
  } while(0)

#define mcPROP_X0 \
  do { \
    double mc_dt; \
    if(vx == 0) { ABSORB; }; \
    mc_dt = -x/vx; \
    if(mc_dt < 0 && mcallowbackprop == 0) { ABSORB; }; \
    mcPROP_DT(mc_dt); \
    x = 0; \
    DISALLOW_BACKPROP;\
  } while(0)

#define PROP_Y0 \
  do { \
    if (mcgravitation) { Coords mcLocG; int mc_ret; \
    double mc_dt, mc_gx, mc_gy, mc_gz; \
    mcLocG = rot_apply(ROT_A_CURRENT_COMP, coords_set(0,-GRAVITY,0)); \
    coords_get(mcLocG, &mc_gx, &mc_gy, &mc_gz); \
    mc_ret = solve_2nd_order(&mc_dt, NULL, -mc_gy/2, -vy, -y); \
    if (mc_ret) {PROP_GRAV_DT(mc_dt, mc_gx, mc_gy, mc_gz); y=0;}\
    else if (mcallowbackprop == 0 && mc_dt < 0) { ABSORB; }; } \
    else mcPROP_Y0; \
    DISALLOW_BACKPROP;\
  } while(0)


#define mcPROP_Y0 \
  do { \
    double mc_dt; \
    if(vy == 0) { ABSORB; }; \
    mc_dt = -y/vy; \
    if(mc_dt < 0 && mcallowbackprop == 0) { ABSORB; }; \
    mcPROP_DT(mc_dt); \
    y = 0; \
    DISALLOW_BACKPROP; \
  } while(0)


#ifdef DEBUG

#define DEBUG_STATE() if(!mcdotrace); else \
  printf("STATE: %g, %g, %g, %g, %g, %g, %g, %g, %g, %g, %g\n", \
         x,y,z,vx,vy,vz,t,sx,sy,sz,p);
#define DEBUG_SCATTER() if(!mcdotrace); else \
  printf("SCATTER: %g, %g, %g, %g, %g, %g, %g, %g, %g, %g, %g\n", \
         x,y,z,vx,vy,vz,t,sx,sy,sz,p);

#else

#define DEBUG_STATE()
#define DEBUG_SCATTER()

#endif

#endif /* !MCCODE_H */

#endif /* MCSTAS_R_H */
/* End of file "mcstas-r.h". */

/* embedding file "mccode-r.c" */

/*******************************************************************************
*
* McCode, neutron/xray ray-tracing package
*         Copyright (C) 1997-2009, All rights reserved
*         Risoe National Laboratory, Roskilde, Denmark
*         Institut Laue Langevin, Grenoble, France
*
* Runtime: share/mccode-r.c
*
* %Identification
* Written by: KN
* Date:    Aug 29, 1997
* Release: McStas X.Y/McXtrace X.Y
* Version: $Revision$
*
* Runtime system for McStas and McXtrace.
* Embedded within instrument in runtime mode.
* Contains SECTIONS:
*   MPI handling (sum, send, recv)
*   format definitions
*   I/O
*   mcdisplay support
*   random numbers
*   coordinates handling
*   vectors math (solve 2nd order, normals, randvec...)
*   parameter handling
*   signal and main handlers
*
* Usage: Automatically embbeded in the c code whenever required.
*
* $Id$
*
*******************************************************************************/

/*******************************************************************************
* The I/O format definitions and functions
*******************************************************************************/


/** Include header files to avoid implicit declarations (not allowed on LLVM) */
#include <ctype.h>
#include <sys/types.h>

// UNIX specific headers (non-Windows)
#if defined(__unix__) || defined(__APPLE__)
#include <unistd.h>
#include <sys/stat.h>
#endif


#ifndef DANSE
#ifdef MC_ANCIENT_COMPATIBILITY
int traceenabled = 0;
int defaultmain  = 0;
#endif
/* else defined directly in the McCode generated C code */

static   long mcseed                 = 0; /* seed for random generator */
#pragma acc declare create ( mcseed )
static   long mcstartdate            = 0; /* start simulation time */
static   int  mcdisable_output_files = 0; /* --no-output-files */
mcstatic int  mcgravitation          = 0; /* use gravitation flag, for PROP macros */
mcstatic int  mcdotrace              = 0; /* flag for --trace and messages for DISPLAY */
#pragma acc declare create ( mcdotrace )
int      mcallowbackprop             = 0;         /* flag to enable negative/backprop */

/* OpenACC-related segmentation parameters: */
int vecsize = 128;
int numgangs = 7813;
long gpu_innerloop = 2147483647;

/* Monitor_nD list/buffer-size default */
long MONND_BUFSIZ = 1000000;

/* Number of particle histories to simulate. */
#ifdef NEUTRONICS
mcstatic unsigned long long int mcncount             = 1;
mcstatic unsigned long long int mcrun_num            = 0;
#else
#ifdef MCDEFAULT_NCOUNT
mcstatic unsigned long long int mcncount             = MCDEFAULT_NCOUNT;
#else
mcstatic unsigned long long int mcncount             = 1000000;
#endif
#pragma acc declare create ( mcncount )
mcstatic unsigned long long int mcrun_num            = 0;
#pragma acc declare create ( mcrun_num )
#endif /* NEUTRONICS */

#else
#include "mcstas-globals.h"
#endif /* !DANSE */

#ifndef NX_COMPRESION
#define NX_COMPRESSION NX_COMP_NONE
#endif

/* String nullification on GPU and other replacements */
#ifdef OPENACC
int noprintf() {
  return 0;
}

int str_comp(char *str1, char *str2) {
  while (*str1 && *str1 == *str2) {
    str1++;
    str2++;
  }
  return (*str1 - *str2);
}

size_t str_len(const char *s)
{
  size_t len = 0;
  if(s != NULL)
  {
    while(*s != '\0')
    {
      ++len;
      ++s;
    }
  }
  return len;
}

#endif

/* SECTION: Predefine (component) parameters ================================= */

int nans_match(double a, double b){
  return (*(uint64_t*)&a == *(uint64_t*)&b);
}
int is_unset(double x){
  return nans_match(x, UNSET);
}
int is_set(double x){
  return !nans_match(x, UNSET);
}
int is_valid(double x){
  return !isnan(x)||is_unset(x);
}
int all_unset(int n, ...){
  va_list ptr;
  va_start(ptr, n);
  int ret=1;
  for (int i=0; i<n; ++i) if(is_set(va_arg(ptr, double))) ret=0;
  va_end(ptr);
  return ret;
}
int all_set(int n, ...){
  va_list ptr;
  va_start(ptr, n);
  int ret=1;
  for (int i=0; i<n; ++i) if(is_unset(va_arg(ptr, double))) ret=0;
  va_end(ptr);
  return ret;
}
int any_unset(int n, ...){
  va_list ptr;
  va_start(ptr, n);
  int ret=0;
  for (int i=0; i<n; ++i) if(is_unset(va_arg(ptr, double))) ret=1;
  va_end(ptr);
  return ret;
}
int any_set(int n, ...){
  va_list ptr;
  va_start(ptr, n);
  int ret=0;
  for (int i=0; i<n; ++i) if(is_set(va_arg(ptr, double))) ret=1;
  va_end(ptr);
  return ret;
}


/* SECTION: Dynamic Arrays ================================================== */
IArray1d create_iarr1d(int n){
  IArray1d arr2d;
  arr2d = calloc(n, sizeof(int));
  return arr2d;
}
void destroy_iarr1d(IArray1d a){
  free(a);
}

IArray2d create_iarr2d(int nx, int ny){
  IArray2d arr2d;
  arr2d = calloc(nx, sizeof(int *));

  int *p1;
  p1 = calloc(nx*ny, sizeof(int));

  int i;
  for (i=0; i<nx; i++){
    arr2d[i] = &(p1[i*ny]);
  }
  return arr2d;
}
void destroy_iarr2d(IArray2d a){
  free(a[0]);
  free(a);
}

IArray3d create_iarr3d(int nx, int ny, int nz){
  IArray3d arr3d;
  int i, j;

  // 1d
  arr3d = calloc(nx, sizeof(int **));

  // d2
  int **p1;
  p1 = calloc(nx*ny, sizeof(int *));

  for (i=0; i<nx; i++){
    arr3d[i] = &(p1[i*ny]);
  }

  // 3d
  int *p2;
  p2 = calloc(nx*ny*nz, sizeof(int));
  for (i=0; i<nx; i++){
    for (j=0; j<ny; j++){
      arr3d[i][j] = &(p2[(i*ny+j)*nz]);
    }
  }
  return arr3d;
}

void destroy_iarr3d(IArray3d a){
  free(a[0][0]);
  free(a[0]);
  free(a);
}

DArray1d create_darr1d(int n){
  DArray1d arr2d;
  arr2d = calloc(n, sizeof(double));
  return arr2d;
}

void destroy_darr1d(DArray1d a){
  free(a);
}

DArray2d create_darr2d(int nx, int ny){
  DArray2d arr2d;
  arr2d = calloc(nx, sizeof(double *));

  double *p1;
  p1 = calloc(nx*ny, sizeof(double));

  int i;
  for (i=0; i<nx; i++){
    arr2d[i] = &(p1[i*ny]);
  }
  return arr2d;
}

void destroy_darr2d(DArray2d a){
  free(a[0]);
  free(a);
}

DArray3d create_darr3d(int nx, int ny, int nz){
  DArray3d arr3d;

  int i, j;

  // 1d
  arr3d = calloc(nx, sizeof(double **));

  // d2
  double **p1;
  p1 = calloc(nx*ny, sizeof(double *));

  for (i=0; i<nx; i++){
    arr3d[i] = &(p1[i*ny]);
  }

  // 3d
  double *p2;
  p2 = calloc(nx*ny*nz, sizeof(double));
  for (i=0; i<nx; i++){
    for (j=0; j<ny; j++){
      arr3d[i][j] = &(p2[(i*ny+j)*nz]);
    }
  }
  return arr3d;
}

void destroy_darr3d(DArray3d a){
  free(a[0][0]);
  free(a[0]);
  free(a);
}


/* SECTION: MPI handling ==================================================== */

#ifdef USE_MPI
/* MPI rank */
static int mpi_node_rank;
static int mpi_node_root = 0;


/*******************************************************************************
* mc_MPI_Reduce: Gathers arrays from MPI nodes using Reduce function.
*******************************************************************************/
int mc_MPI_Sum(double *sbuf, long count)
{
  if (!sbuf || count <= 0) return(MPI_SUCCESS); /* nothing to reduce */
  else {
    /* we must cut the buffer into blocks not exceeding the MPI max buffer size of 32000 */
    long   offset=0;
    double *rbuf=NULL;
    int    length=MPI_REDUCE_BLOCKSIZE; /* defined in mccode-r.h */
    int    i=0;
    rbuf = calloc(count, sizeof(double));
    if (!rbuf)
      exit(-fprintf(stderr, "Error: Out of memory %li (mc_MPI_Sum)\n", count*sizeof(double)));
    while (offset < count) {
      if (!length || offset+length > count-1) length=count-offset;
      else length=MPI_REDUCE_BLOCKSIZE;
      if (MPI_Allreduce((double*)(sbuf+offset), (double*)(rbuf+offset),
              length, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD) != MPI_SUCCESS)
        return MPI_ERR_COUNT;
      offset += length;
    }

    for (i=0; i<count; i++) sbuf[i] = rbuf[i];
    free(rbuf);
  }
  return MPI_SUCCESS;
} /* mc_MPI_Sum */

/*******************************************************************************
* mc_MPI_Send: Send array to MPI node by blocks to avoid buffer limit
*******************************************************************************/
int mc_MPI_Send(void *sbuf,
                  long count, MPI_Datatype dtype,
                  int dest)
{
  int dsize;
  long offset=0;
  int  tag=1;
  int  length=MPI_REDUCE_BLOCKSIZE; /* defined in mccode-r.h */

  if (!sbuf || count <= 0) return(MPI_SUCCESS); /* nothing to send */
  MPI_Type_size(dtype, &dsize);

  while (offset < count) {
    if (offset+length > count-1) length=count-offset;
    else length=MPI_REDUCE_BLOCKSIZE;
    if (MPI_Send((void*)(sbuf+offset*dsize), length, dtype, dest, tag++, MPI_COMM_WORLD) != MPI_SUCCESS)
      return MPI_ERR_COUNT;
    offset += length;
  }

  return MPI_SUCCESS;
} /* mc_MPI_Send */

/*******************************************************************************
* mc_MPI_Recv: Receives arrays from MPI nodes by blocks to avoid buffer limit
*             the buffer must have been allocated previously.
*******************************************************************************/
int mc_MPI_Recv(void *sbuf,
                  long count, MPI_Datatype dtype,
                  int source)
{
  int dsize;
  long offset=0;
  int  tag=1;
  int  length=MPI_REDUCE_BLOCKSIZE; /* defined in mccode-r.h */

  if (!sbuf || count <= 0) return(MPI_SUCCESS); /* nothing to recv */
  MPI_Type_size(dtype, &dsize);

  while (offset < count) {
    if (offset+length > count-1) length=count-offset;
    else length=MPI_REDUCE_BLOCKSIZE;
    if (MPI_Recv((void*)(sbuf+offset*dsize), length, dtype, source, tag++,
            MPI_COMM_WORLD, MPI_STATUS_IGNORE) != MPI_SUCCESS)
      return MPI_ERR_COUNT;
    offset += length;
  }

  return MPI_SUCCESS;
} /* mc_MPI_Recv */

#endif /* USE_MPI */

/* SECTION: parameters handling ============================================= */

/* Instrument input parameter type handling. */
/*******************************************************************************
* mcparm_double: extract double value from 's' into 'vptr'
*******************************************************************************/
static int
mcparm_double(char *s, void *vptr)
{
  char *p;
  double *v = (double *)vptr;

  if (!s) { *v = 0; return(1); }
  *v = strtod(s, &p);
  if(*s == '\0' || (p != NULL && *p != '\0') || errno == ERANGE)
    return 0;                        /* Failed */
  else
    return 1;                        /* Success */
}

/*******************************************************************************
* mcparminfo_double: display parameter type double
*******************************************************************************/
static char *
mcparminfo_double(char *parmname)
{
  return "double";
}

/*******************************************************************************
* mcparmerror_double: display error message when failed extract double
*******************************************************************************/
static void
mcparmerror_double(char *parm, char *val)
{
  fprintf(stderr, "Error: Invalid value '%s' for floating point parameter %s (mcparmerror_double)\n",
          val, parm);
}

/*******************************************************************************
* mcparmprinter_double: convert double to string
*******************************************************************************/
static void
mcparmprinter_double(char *f, void *vptr)
{
  double *v = (double *)vptr;
  sprintf(f, "%g", *v);
}

/*******************************************************************************
* mcparm_int: extract int value from 's' into 'vptr'
*******************************************************************************/
static int
mcparm_int(char *s, void *vptr)
{
  char *p;
  int *v = (int *)vptr;
  long x;

  if (!s) { *v = 0; return(1); }
  *v = 0;
  x = strtol(s, &p, 10);
  if(x < INT_MIN || x > INT_MAX)
    return 0;                        /* Under/overflow */
  *v = x;
  if(*s == '\0' || (p != NULL && *p != '\0') || errno == ERANGE)
    return 0;                        /* Failed */
  else
    return 1;                        /* Success */
}

/*******************************************************************************
* mcparminfo_int: display parameter type int
*******************************************************************************/
static char *
mcparminfo_int(char *parmname)
{
  return "int";
}

/*******************************************************************************
* mcparmerror_int: display error message when failed extract int
*******************************************************************************/
static void
mcparmerror_int(char *parm, char *val)
{
  fprintf(stderr, "Error: Invalid value '%s' for integer parameter %s (mcparmerror_int)\n",
          val, parm);
}

/*******************************************************************************
* mcparmprinter_int: convert int to string
*******************************************************************************/
static void
mcparmprinter_int(char *f, void *vptr)
{
  int *v = (int *)vptr;
  sprintf(f, "%d", *v);
}

/*******************************************************************************
* mcparm_string: extract char* value from 's' into 'vptr' (copy)
*******************************************************************************/
static int
mcparm_string(char *s, void *vptr)
{
  char **v = (char **)vptr;
  if (!s) { *v = NULL; return(1); }
  *v = (char *)malloc(strlen(s) + 1);
  if(*v == NULL)
  {
    exit(-fprintf(stderr, "Error: Out of memory %li (mcparm_string).\n", (long)strlen(s) + 1));
  }
  strcpy(*v, s);
  return 1;                        /* Success */
}

/*******************************************************************************
* mcparminfo_string: display parameter type string
*******************************************************************************/
static char *
mcparminfo_string(char *parmname)
{
  return "string";
}

/*******************************************************************************
* mcparmerror_string: display error message when failed extract string
*******************************************************************************/
static void
mcparmerror_string(char *parm, char *val)
{
  fprintf(stderr, "Error: Invalid value '%s' for string parameter %s (mcparmerror_string)\n",
          val, parm);
}

/*******************************************************************************
* mcparmprinter_string: convert string to string (including esc chars)
*******************************************************************************/
static void
mcparmprinter_string(char *f, void *vptr)
{
  char **v = (char **)vptr;
  char *p;

  if (!*v) { *f='\0'; return; }
  strcpy(f, "");
  for(p = *v; *p != '\0'; p++)
  {
    switch(*p)
    {
      case '\n':
        strcat(f, "\\n");
        break;
      case '\r':
        strcat(f, "\\r");
        break;
      case '"':
        strcat(f, "\\\"");
        break;
      case '\\':
        strcat(f, "\\\\");
        break;
      default:
        strncat(f, p, 1);
    }
  }
  /* strcat(f, "\""); */
} /* mcparmprinter_string */

/* now we may define the parameter structure, using previous functions */
static struct
  {
    int (*getparm)(char *, void *);
    char * (*parminfo)(char *);
    void (*error)(char *, char *);
    void (*printer)(char *, void *);
} mcinputtypes[] = {
  {
    mcparm_int, mcparminfo_int, mcparmerror_int,
    mcparmprinter_int
  }, {
    mcparm_string, mcparminfo_string, mcparmerror_string,
    mcparmprinter_string
  }, {
    mcparm_string, mcparminfo_string, mcparmerror_string,
    mcparmprinter_string
  }, {
    mcparm_double, mcparminfo_double, mcparmerror_double,
    mcparmprinter_double
  }, {
    mcparm_double, mcparminfo_double, mcparmerror_double,
    mcparmprinter_double
  }
};

/*******************************************************************************
* mcestimate_error: compute sigma from N,p,p2 in Gaussian large numbers approx
*******************************************************************************/
double mcestimate_error(double N, double p1, double p2)
{
  double pmean, n1;
  if(N <= 1)
    return p1;
  pmean = p1 / N;
  n1 = N - 1;
  /* Note: underflow may cause p2 to become zero; the fabs() below guards
     against this. */
  return sqrt((N/n1)*fabs(p2 - pmean*pmean));
}

double (*mcestimate_error_p)
  (double V2, double psum, double p2sum)=mcestimate_error;

/* ========================================================================== */

/*                               MCCODE_R_IO_C                                */

/* ========================================================================== */

#ifndef MCCODE_R_IO_C
#define MCCODE_R_IO_C "$Revision$"

/* SECTION: file i/o handling ================================================ */

#ifndef HAVE_STRCASESTR
// from msysgit: https://code.google.com/p/msysgit/source/browse/compat/strcasestr.c
char *strcasestr(const char *haystack, const char *needle)
{
  int nlen = strlen(needle);
  int hlen = strlen(haystack) - nlen + 1;
  int i;

  for (i = 0; i < hlen; i++) {
    int j;
    for (j = 0; j < nlen; j++) {
            unsigned char c1 = haystack[i+j];
            unsigned char c2 = needle[j];
            if (toupper(c1) != toupper(c2))
                    goto next;
    }
    return (char *) haystack + i;
  next:
    ;
  }
  return NULL;
}


#endif
#ifndef HAVE_STRCASECMP
int strcasecmp( const char *s1, const char *s2 )
{
  int c1, c2;
  do {
    c1 = tolower( (unsigned char) *s1++ );
    c2 = tolower( (unsigned char) *s2++ );
  } while (c1 == c2 && c1 != 0);
  return c2 > c1 ? -1 : c1 > c2;
}
#endif

#ifndef STRACPY
/* this is a replacement to strncpy, but ensures that the copy ends with NULL */
/* http://stracpy.blogspot.fr/2011/04/stracpy-strncpy-replacement.html */
#define STRACPY
char *stracpy(char *destination, const char *source, size_t amount)
{
        if (!destination || !source || !amount) return(NULL);
        while(amount--)
          if((*destination++ = *source++) == '\0') break;
        *destination = '\0';
        return destination;
}
#endif

/*******************************************************************************
* mcfull_file: allocates a full file name=dirname+file. Catenate extension if missing.
*******************************************************************************/
char *mcfull_file(char *name, char *ext)
{
  int   dirlen=0;
  char *mem   =NULL;

  dirlen = dirname ? strlen(dirname) : 0;
  mem = (char*)malloc(dirlen + strlen(name) + CHAR_BUF_LENGTH);
  if(!mem) {
    exit(-fprintf(stderr, "Error: Out of memory %li (mcfull_file)\n", (long)(dirlen + strlen(name) + 256)));
  }
  strcpy(mem, "");

  /* prepend directory name to path if name does not contain a path */
  if (dirlen > 0 && !strchr(name, MC_PATHSEP_C)) {
    strcat(mem, dirname);
    strcat(mem, MC_PATHSEP_S);
  } /* dirlen */

  strcat(mem, name);
  if (!strchr(name, '.') && ext && strlen(ext))
  { /* add extension if not in file name already */
    strcat(mem, ".");
    strcat(mem, ext);
  }
  return(mem);
} /* mcfull_file */

/*******************************************************************************
* mcnew_file: opens a new file within dirname if non NULL
*             the file is opened in "a" (append, create if does not exist)
*             the extension 'ext' is added if the file name does not include one.
*             the last argument is set to 0 if file did not exist, else to 1.
*******************************************************************************/
FILE *mcnew_file(char *name, char *ext, int *exists)
{
  char *mem;
  FILE *file=NULL;

  if (!name || strlen(name) == 0 || mcdisable_output_files) return(NULL);

  mem  = mcfull_file(name, ext); /* create dirname/name.ext */

  /* check for existence */
  file = fopen(mem, "r"); /* for reading -> fails if does not exist */
  if (file) {
    fclose(file);
    *exists=1;
  } else
    *exists=0;

  /* open the file for writing/appending */
#ifdef USE_NEXUS
  if (mcformat && strcasestr(mcformat, "NeXus")) {
    /* NXhandle nxhandle is defined in the .h with USE_NEXUS */
    NXaccess mode = (*exists ? NXACC_CREATE5 | NXACC_RDWR : NXACC_CREATE5);

    if (NXopen(mem, mode, &nxhandle) != NX_OK)
      file = NULL;
    else
      file = (FILE*)&nxhandle; /* to make it non NULL */
  } else
#endif
    file = fopen(mem, "a+");

  if(!file)
    fprintf(stderr, "Warning: could not open output file '%s' for %s (mcnew_file)\n",
      mem, *exists ? "append" : "create");
  free(mem);

  return file;
} /* mcnew_file */

/*******************************************************************************
* mcdetector_statistics: compute detector statistics, error bars, [x I I_err N] 1D
* RETURN:            updated detector structure
* Used by: detector_import
*******************************************************************************/
MCDETECTOR mcdetector_statistics(
  MCDETECTOR detector)
{

  if (!detector.p1 || !detector.m)
    return(detector);

  /* compute statistics and update MCDETECTOR structure ===================== */
  double sum_z  = 0, min_z  = 0, max_z  = 0;
  double fmon_x =0,  smon_x = 0, fmon_y =0, smon_y=0, mean_z=0;
  double Nsum=0, P2sum=0;

  double sum_xz = 0, sum_yz = 0, sum_x = 0, sum_y = 0, sum_x2z = 0, sum_y2z = 0;
  int    i,j;
  char   hasnan=0, hasinf=0;
  char   israw = ((char*)strcasestr(detector.format,"raw") != NULL);
  double *this_p1=NULL; /* new 1D McCode array [x I E N]. Freed after writing data */

  /* if McCode/PGPLOT and rank==1 we create a new m*4 data block=[x I E N] */
  if (detector.rank == 1 && strcasestr(detector.format,"McCode")) {
    this_p1 = (double *)calloc(detector.m*detector.n*detector.p*4, sizeof(double));
    if (!this_p1)
      exit(-fprintf(stderr, "Error: Out of memory creating %li 1D " MCCODE_STRING " data set for file '%s' (detector_import)\n",
        detector.m*detector.n*detector.p*4*sizeof(double*), detector.filename));
  }

  max_z = min_z = detector.p1[0];

  /* compute sum and moments (not for lists) */
  if (!strcasestr(detector.format,"list") && detector.m)
  for(j = 0; j < detector.n*detector.p; j++)
  {
    for(i = 0; i < detector.m; i++)
    {
      double x,y,z;
      double N, E;
      long   index= !detector.istransposed ? i*detector.n*detector.p + j : i+j*detector.m;
      char   hasnaninf=0;

      if (detector.m)
        x = detector.xmin + (i + 0.5)/detector.m*(detector.xmax - detector.xmin);
      else x = 0;
      if (detector.n && detector.p)
        y = detector.ymin + (j + 0.5)/detector.n/detector.p*(detector.ymax - detector.ymin);
      else y = 0;
      z = detector.p1[index];
      N = detector.p0 ? detector.p0[index] : 1;
      E = detector.p2 ? detector.p2[index] : 0;
      if (detector.p2 && !israw)
        detector.p2[index] = (*mcestimate_error_p)(detector.p0[index],detector.p1[index],detector.p2[index]); /* set sigma */

      if (detector.rank == 1 && this_p1 && strcasestr(detector.format,"McCode")) {
        /* fill-in 1D McCode array [x I E N] */
        this_p1[index*4]   = x;
        this_p1[index*4+1] = z;
        this_p1[index*4+2] = detector.p2 ? detector.p2[index] : 0;
        this_p1[index*4+3] = N;
      }

      if (isnan(z) || isnan(E) || isnan(N)) hasnaninf=hasnan=1;
      if (isinf(z) || isinf(E) || isinf(N)) hasnaninf=hasinf=1;

      /* compute stats integrals */
      if (!hasnaninf) {
        sum_xz += x*z;
        sum_yz += y*z;
        sum_x  += x;
        sum_y  += y;
        sum_z  += z;
        sum_x2z += x*x*z;
        sum_y2z += y*y*z;
        if (z > max_z) max_z = z;
        if (z < min_z) min_z = z;

        Nsum += N;
        P2sum += E;
      }

    }
  } /* for j */

  /* compute 1st and 2nd moments. For lists, sum_z=0 so this is skipped. */
  if (sum_z && detector.n*detector.m*detector.p)
  {
    fmon_x = sum_xz/sum_z;
    fmon_y = sum_yz/sum_z;
    smon_x = sum_x2z/sum_z-fmon_x*fmon_x; smon_x = smon_x > 0 ? sqrt(smon_x) : 0;
    smon_y = sum_y2z/sum_z-fmon_y*fmon_y; smon_y = smon_y > 0 ? sqrt(smon_y) : 0;
    mean_z = sum_z/detector.n/detector.m/detector.p;
  }
  /* store statistics into detector */
  detector.intensity = sum_z;
  detector.error     = Nsum ? (*mcestimate_error_p)(Nsum, sum_z, P2sum) : 0;
  detector.events    = Nsum;
  detector.min       = min_z;
  detector.max       = max_z;
  detector.mean      = mean_z;
  detector.centerX   = fmon_x;
  detector.halfwidthX= smon_x;
  detector.centerY   = fmon_y;
  detector.halfwidthY= smon_y;

  /* if McCode/PGPLOT and rank==1 replace p1 with new m*4 1D McCode and clear others */
  if (detector.rank == 1 && this_p1 && strcasestr(detector.format,"McCode")) {

    detector.p1 = this_p1;
    detector.n  = detector.m; detector.m  = 4;
    detector.p0 = detector.p2 = NULL;
    detector.istransposed = 1;
  }

  if (detector.n*detector.m*detector.p > 1)
    snprintf(detector.signal, CHAR_BUF_LENGTH,
      "Min=%g; Max=%g; Mean=%g;", detector.min, detector.max, detector.mean);
  else
    strcpy(detector.signal, "None");
  snprintf(detector.values, CHAR_BUF_LENGTH,
    "%g %g %g", detector.intensity, detector.error, detector.events);

  switch (detector.rank) {
    case 1:  snprintf(detector.statistics, CHAR_BUF_LENGTH, "X0=%g; dX=%g;",
      detector.centerX, detector.halfwidthX); break;
    case 2:
    case 3:  snprintf(detector.statistics, CHAR_BUF_LENGTH, "X0=%g; dX=%g; Y0=%g; dY=%g;",
      detector.centerX, detector.halfwidthX, detector.centerY, detector.halfwidthY);
      break;
    default: strcpy(detector.statistics, "None");
  }

  if (hasnan)
    printf("WARNING: Nan detected in component/file %s %s\n",
      detector.component, strlen(detector.filename) ? detector.filename : "");
  if (hasinf)
    printf("WARNING: Inf detected in component/file %s %s\n",
      detector.component, strlen(detector.filename) ? detector.filename : "");

  return(detector);

} /* mcdetector_statistics */

/*******************************************************************************
* detector_import: build detector structure, merge non-lists from MPI
*                    compute basic stat, write "Detector:" line
* RETURN:            detector structure. Invalid data if detector.p1 == NULL
*                    Invalid detector sets m=0 and filename=""
*                    Simulation data  sets m=0 and filename=siminfo_name
* This function is equivalent to the old 'mcdetector_out', returning a structure
*******************************************************************************/
MCDETECTOR detector_import(
  char *format,
  char *component, char *title,
  long m, long n,  long p,
  char *xlabel, char *ylabel, char *zlabel,
  char *xvar, char *yvar, char *zvar,
  double x1, double x2, double y1, double y2, double z1, double z2,
  char *filename,
  double *p0, double *p1, double *p2,
  Coords position)
{
  time_t t;       /* for detector.date */
  long   date_l;  /* date as a long number */
  char   istransposed=0;
  char   c[CHAR_BUF_LENGTH]; /* temp var for signal label */

  MCDETECTOR detector;

  /* build MCDETECTOR structure ============================================= */
  /* make sure we do not have NULL for char fields */

  /* these also apply to simfile */
  strncpy (detector.filename,  filename ? filename : "",        CHAR_BUF_LENGTH);
  strncpy (detector.format,    format   ? format   : "McCode" , CHAR_BUF_LENGTH);
  /* add extension if missing */
  if (strlen(detector.filename) && !strchr(detector.filename, '.'))
  { /* add extension if not in file name already */
    strcat(detector.filename, ".dat");
  }
  strncpy (detector.component, component ? component : MCCODE_STRING " component", CHAR_BUF_LENGTH);

  snprintf(detector.instrument, CHAR_BUF_LENGTH, "%s (%s)", instrument_name, instrument_source);
  snprintf(detector.user, CHAR_BUF_LENGTH,      "%s on %s",
        getenv("USER") ? getenv("USER") : MCCODE_NAME,
        getenv("HOST") ? getenv("HOST") : "localhost");
  time(&t);         /* get current write time */
  date_l = (long)t; /* same but as a long */
  snprintf(detector.date, CHAR_BUF_LENGTH, "%s", ctime(&t));
  if (strlen(detector.date))   detector.date[strlen(detector.date)-1] = '\0'; /* remove last \n in date */
  detector.date_l = date_l;

  if (!mcget_run_num() || mcget_run_num() >= mcget_ncount())
    snprintf(detector.ncount, CHAR_BUF_LENGTH, "%llu", mcget_ncount()
#ifdef USE_MPI
*mpi_node_count
#endif
  );
  else
    snprintf(detector.ncount, CHAR_BUF_LENGTH, "%g/%g", (double)mcget_run_num(), (double)mcget_ncount());

  detector.p0         = p0;
  detector.p1         = p1;
  detector.p2         = p2;

  /* handle transposition (not for NeXus) */
  if (!strcasestr(detector.format, "NeXus")) {
    if (m<0 || n<0 || p<0)             istransposed = !istransposed;
    if (strcasestr(detector.format, "transpose")) istransposed = !istransposed;
    if (istransposed) { /* do the swap once for all */
      long i=m; m=n; n=i;
    }
  }

  m=labs(m); n=labs(n); p=labs(p); /* make sure dimensions are positive */
  detector.istransposed = istransposed;

  /* determine detector rank (dimensionality) */
  if (!m || !n || !p || !p1) detector.rank = 4; /* invalid: exit with m=0 filename="" */
  else if (m*n*p == 1)       detector.rank = 0; /* 0D */
  else if (n == 1 || m == 1) detector.rank = 1; /* 1D */
  else if (p == 1)           detector.rank = 2; /* 2D */
  else                       detector.rank = 3; /* 3D */

  /* from rank, set type */
  switch (detector.rank) {
    case 0:  strcpy(detector.type,  "array_0d"); m=n=p=1; break;
    case 1:  snprintf(detector.type, CHAR_BUF_LENGTH, "array_1d(%ld)", m*n*p); m *= n*p; n=p=1; break;
    case 2:  snprintf(detector.type, CHAR_BUF_LENGTH, "array_2d(%ld, %ld)", m, n*p); n *= p; p=1; break;
    case 3:  snprintf(detector.type, CHAR_BUF_LENGTH, "array_3d(%ld, %ld, %ld)", m, n, p); break;
    default: m=0; strcpy(detector.type, ""); strcpy(detector.filename, "");/* invalid */
  }

  detector.m    = m;
  detector.n    = n;
  detector.p    = p;

  /* these only apply to detector files ===================================== */

  snprintf(detector.position, CHAR_BUF_LENGTH, "%g %g %g", position.x, position.y, position.z);
  /* may also store actual detector orientation in the future */

  strncpy(detector.title,      title && strlen(title) ? title : component,       CHAR_BUF_LENGTH);
  strncpy(detector.xlabel,     xlabel && strlen(xlabel) ? xlabel : "X", CHAR_BUF_LENGTH); /* axis labels */
  strncpy(detector.ylabel,     ylabel && strlen(ylabel) ? ylabel : "Y", CHAR_BUF_LENGTH);
  strncpy(detector.zlabel,     zlabel && strlen(zlabel) ? zlabel : "Z", CHAR_BUF_LENGTH);
  strncpy(detector.xvar,       xvar && strlen(xvar) ? xvar :       "x", CHAR_BUF_LENGTH); /* axis variables */
  strncpy(detector.yvar,       yvar && strlen(yvar) ? yvar :       detector.xvar, CHAR_BUF_LENGTH);
  strncpy(detector.zvar,       zvar && strlen(zvar) ? zvar :       detector.yvar, CHAR_BUF_LENGTH);

  /* set "variables" as e.g. "I I_err N" */
  strcpy(c, "I ");
  if (strlen(detector.zvar))      strncpy(c, detector.zvar,32);
  else if (strlen(detector.yvar)) strncpy(c, detector.yvar,32);
  else if (strlen(detector.xvar)) strncpy(c, detector.xvar,32);

  if (detector.rank == 1)
    snprintf(detector.variables, CHAR_BUF_LENGTH, "%s %s %s_err N", detector.xvar, c, c);
  else
    snprintf(detector.variables, CHAR_BUF_LENGTH, "%s %s_err N", c, c);

  /* limits */
  detector.xmin = x1;
  detector.xmax = x2;
  detector.ymin = y1;
  detector.ymax = y2;
  detector.zmin = z1;
  detector.zmax = z2;
  if (abs(detector.rank) == 1)
    snprintf(detector.limits, CHAR_BUF_LENGTH, "%g %g", x1, x2);
  else if (detector.rank == 2)
    snprintf(detector.limits, CHAR_BUF_LENGTH, "%g %g %g %g", x1, x2, y1, y2);
  else
    snprintf(detector.limits, CHAR_BUF_LENGTH, "%g %g %g %g %g %g", x1, x2, y1, y2, z1, z2);

  /* if MPI and nodes_nb > 1: reduce data sets when using MPI =============== */
#ifdef USE_MPI
  if (!strcasestr(detector.format,"list") && mpi_node_count > 1 && m) {
    /* we save additive data: reduce everything into mpi_node_root */
    if (p0) mc_MPI_Sum(p0, m*n*p);
    if (p1) mc_MPI_Sum(p1, m*n*p);
    if (p2) mc_MPI_Sum(p2, m*n*p);
    if (!p0) {  /* additive signal must be then divided by the number of nodes */
      int i;
      for (i=0; i<m*n*p; i++) {
        p1[i] /= mpi_node_count;
        if (p2) p2[i] /= mpi_node_count;
      }
    }
  }
#endif /* USE_MPI */

  /* compute statistics, Nsum, intensity, Error bars */
  detector = mcdetector_statistics(detector);

#ifdef USE_MPI
  /* slaves are done */
  if(mpi_node_rank != mpi_node_root) {
    return detector;
  }
#endif

  /* output "Detector:" line ================================================ */
  /* when this is a detector written by a component (not the SAVE from instrument),
     not an event lists */
  if (!m) return(detector);
  if (!strcasestr(detector.format,"list")) {
    if (!strcmp(detector.component, instrument_name)) {
      if (strlen(detector.filename))  /* we name it from its filename, or from its title */
        strncpy(c, detector.filename, CHAR_BUF_LENGTH);
      else
        snprintf(c, CHAR_BUF_LENGTH, "%s", instrument_name);
    } else
      strncpy(c, detector.component, CHAR_BUF_LENGTH);  /* usual detectors written by components */

    printf("Detector: %s_I=%g %s_ERR=%g %s_N=%g",
           c, detector.intensity,
           c, detector.error,
           c, detector.events);
    printf(" \"%s\"\n", strlen(detector.filename) ? detector.filename : detector.component);
  }


  return(detector);
} /* detector_import */

/* end MCDETECTOR import section ============================================ */

















/* ========================================================================== */

/*                               ASCII output                                 */
/*     The SIM file is YAML based, the data files have '#' headers            */

/* ========================================================================== */


/*******************************************************************************
* mcinfo_out: output instrument tags/info (only in SIM)
* Used in: siminfo_init (ascii), mcinfo(stdout)
*******************************************************************************/
static void mcinfo_out(char *pre, FILE *f)
{
  char Parameters[CHAR_BUF_LENGTH] = "";
  int  i;

  if (!f || mcdisable_output_files) return;

  /* create parameter string ================================================ */
  for(i = 0; i < numipar; i++)
  {
    char ThisParam[CHAR_BUF_LENGTH];
    if (strlen(mcinputtable[i].name) > CHAR_BUF_LENGTH) break;
    snprintf(ThisParam, CHAR_BUF_LENGTH, " %s(%s)", mcinputtable[i].name,
            (*mcinputtypes[mcinputtable[i].type].parminfo)
                (mcinputtable[i].name));
    strcat(Parameters, ThisParam);
    if (strlen(Parameters) >= CHAR_BUF_LENGTH-64) break;
  }

  /* output data ============================================================ */
  if (f != stdout)
    fprintf(f, "%sFile: %s%c%s\n",    pre, dirname, MC_PATHSEP_C, siminfo_name);
  else
    fprintf(f, "%sCreator: %s\n",     pre, MCCODE_STRING);

  fprintf(f, "%sSource: %s\n",   pre, instrument_source);
  fprintf(f, "%sParameters: %s\n",    pre, Parameters);

  fprintf(f, "%sTrace_enabled: %s\n", pre, traceenabled ? "yes" : "no");
  fprintf(f, "%sDefault_main: %s\n",  pre, defaultmain ?  "yes" : "no");
  fprintf(f, "%sEmbedded_runtime: %s\n", pre,
#ifdef MC_EMBEDDED_RUNTIME
         "yes"
#else
         "no"
#endif
         );

  fflush(f);
} /* mcinfo_out */

/*******************************************************************************
* mcruninfo_out: output simulation tags/info (both in SIM and data files)
* Used in: siminfo_init (ascii case), mcdetector_out_xD_ascii
*******************************************************************************/
static void mcruninfo_out(char *pre, FILE *f)
{
  int i;
  char Parameters[CHAR_BUF_LENGTH];

  if (!f || mcdisable_output_files) return;

  fprintf(f, "%sFormat: %s%s\n",      pre,
    mcformat && strlen(mcformat) ? mcformat : MCCODE_NAME,
    mcformat && strcasestr(mcformat,"McCode") ? " with text headers" : "");
  fprintf(f, "%sURL: %s\n",         pre, "http://www.mccode.org");
  fprintf(f, "%sCreator: %s\n",     pre, MCCODE_STRING);
  fprintf(f, "%sInstrument: %s\n", pre, instrument_source);
  fprintf(f, "%sNcount: %llu\n",        pre, mcget_ncount());
  fprintf(f, "%sTrace: %s\n",       pre, mcdotrace ? "yes" : "no");
  fprintf(f, "%sGravitation: %s\n", pre, mcgravitation ? "yes" : "no");
  snprintf(Parameters, CHAR_BUF_LENGTH, "%ld", mcseed);
  fprintf(f, "%sSeed: %s\n",        pre, Parameters);
  fprintf(f, "%sDirectory: %s\n",        pre, dirname ? dirname : ".");
#ifdef USE_MPI
  if (mpi_node_count > 1)
    fprintf(f, "%sNodes: %i\n",        pre, mpi_node_count);
#endif

  /* output parameter string ================================================ */
  for(i = 0; i < numipar; i++) {
      if (mcinputtable[i].par){
	/* Parameters with a default value */
	if(mcinputtable[i].val && strlen(mcinputtable[i].val)){
	  (*mcinputtypes[mcinputtable[i].type].printer)(Parameters, mcinputtable[i].par);
	  fprintf(f, "%sParam: %s=%s\n", pre, mcinputtable[i].name, Parameters);
        /* ... and those without */
	}else{
	  fprintf(f, "%sParam: %s=NULL\n", pre, mcinputtable[i].name);
	}
      }
  }
  fflush(f);
} /* mcruninfo_out */

/*******************************************************************************
* siminfo_out:    wrapper to fprintf(siminfo_file)
*******************************************************************************/
void siminfo_out(char *format, ...)
{
  va_list ap;

  if(siminfo_file && !mcdisable_output_files)
  {
    va_start(ap, format);
    vfprintf(siminfo_file, format, ap);
    va_end(ap);
  }
} /* siminfo_out */


/*******************************************************************************
* mcdatainfo_out: output detector header
*   mcdatainfo_out(prefix, file_handle, detector) writes info to data file
*******************************************************************************/
static void
mcdatainfo_out(char *pre, FILE *f, MCDETECTOR detector)
{
  if (!f || !detector.m || mcdisable_output_files) return;

  /* output data ============================================================ */
  fprintf(f, "%sDate: %s (%li)\n",       pre, detector.date, detector.date_l);
  fprintf(f, "%stype: %s\n",       pre, detector.type);
  fprintf(f, "%sSource: %s\n",     pre, detector.instrument);
  fprintf(f, "%scomponent: %s\n",  pre, detector.component);
  fprintf(f, "%sposition: %s\n",   pre, detector.position);

  fprintf(f, "%stitle: %s\n",      pre, detector.title);
  fprintf(f, !mcget_run_num() || mcget_run_num() >= mcget_ncount() ?
             "%sNcount: %s\n" :
             "%sratio: %s\n",  pre, detector.ncount);

  if (strlen(detector.filename)) {
    fprintf(f, "%sfilename: %s\n", pre, detector.filename);
  }

  fprintf(f, "%sstatistics: %s\n", pre, detector.statistics);
  fprintf(f, "%ssignal: %s\n",     pre, detector.signal);
  fprintf(f, "%svalues: %s\n",     pre, detector.values);

  if (detector.rank >= 1)
  {
    fprintf(f, "%sxvar: %s\n",     pre, detector.xvar);
    fprintf(f, "%syvar: %s\n",     pre, detector.yvar);
    fprintf(f, "%sxlabel: %s\n",   pre, detector.xlabel);
    fprintf(f, "%sylabel: %s\n",   pre, detector.ylabel);
    if (detector.rank > 1) {
      fprintf(f, "%szvar: %s\n",   pre, detector.zvar);
      fprintf(f, "%szlabel: %s\n", pre, detector.zlabel);
    }
  }

  fprintf(f,
    abs(detector.rank)==1 ?
             "%sxlimits: %s\n" :
             "%sxylimits: %s\n", pre, detector.limits);
  fprintf(f, "%svariables: %s\n", pre,
    strcasestr(detector.format, "list") ? detector.ylabel : detector.variables);

  fflush(f);

} /* mcdatainfo_out */

/* mcdetector_out_array_ascii: output a single array to a file
 *   m: columns
 *   n: rows
 *   p: array
 *   f: file handle (already opened)
 */
static void mcdetector_out_array_ascii(long m, long n, double *p, FILE *f, char istransposed)
{
  if(f)
  {
    int i,j;
    for(j = 0; j < n; j++)
    {
      for(i = 0; i < m; i++)
      {
          fprintf(f, "%.10g ", p[!istransposed ? i*n + j : j*m+i]);
      }
      fprintf(f,"\n");
    }
  }
} /* mcdetector_out_array_ascii */

/*******************************************************************************
* mcdetector_out_0D_ascii: called by mcdetector_out_0D for ascii output
*******************************************************************************/
MCDETECTOR mcdetector_out_0D_ascii(MCDETECTOR detector)
{
  int exists=0;
  FILE *outfile = NULL;

  /* Write data set information to simulation description file. */
  MPI_MASTER(
    siminfo_out("\nbegin data\n"); // detector.component
    mcdatainfo_out("  ", siminfo_file, detector);
    siminfo_out("end data\n");
    /* Don't write if filename is NULL: mcnew_file handles this (return NULL) */
    outfile = mcnew_file(detector.component, "dat", &exists);
    if(outfile)
    {
      /* write data file header and entry in simulation description file */
      mcruninfo_out( "# ", outfile);
      mcdatainfo_out("# ", outfile, detector);
      /* write I I_err N */
      fprintf(outfile, "%g %g %g\n",
        detector.intensity, detector.error, detector.events);
      fclose(outfile);
    }
  ); /* MPI_MASTER */
  return(detector);
} /* mcdetector_out_0D_ascii */

/*******************************************************************************
* mcdetector_out_1D_ascii: called by mcdetector_out_1D for ascii output
*******************************************************************************/
MCDETECTOR mcdetector_out_1D_ascii(MCDETECTOR detector)
{
  int exists=0;
  FILE *outfile = NULL;

  MPI_MASTER(
    /* Write data set information to simulation description file. */
    siminfo_out("\nbegin data\n"); // detector.filename
    mcdatainfo_out("  ", siminfo_file, detector);
    siminfo_out("end data\n");
    /* Loop over array elements, writing to file. */
    /* Don't write if filename is NULL: mcnew_file handles this (return NULL) */
    outfile = mcnew_file(detector.filename, "dat", &exists);
    if(outfile)
    {
      /* write data file header and entry in simulation description file */
      mcruninfo_out( "# ", outfile);
      mcdatainfo_out("# ", outfile, detector);
      /* output the 1D array columns */
      mcdetector_out_array_ascii(detector.m, detector.n, detector.p1, outfile, detector.istransposed);

      fclose(outfile);
    }
  ); /* MPI_MASTER */
  return(detector);

}  /* mcdetector_out_1D_ascii */

/*******************************************************************************
* mcdetector_out_2D_ascii: called by mcdetector_out_2D for ascii output
*******************************************************************************/
MCDETECTOR mcdetector_out_2D_ascii(MCDETECTOR detector)
{
  int exists=0;
  FILE *outfile = NULL;

  MPI_MASTER(
    /* Loop over array elements, writing to file. */
    /* Don't write if filename is NULL: mcnew_file handles this (return NULL) */
    outfile = mcnew_file(detector.filename, "dat", &exists);
    if(outfile)
    {
      /* write header only if file has just been created (not appending) */
      if (!exists) {
        /* Write data set information to simulation description file. */
        siminfo_out("\nbegin data\n"); // detector.filename
        mcdatainfo_out("  ", siminfo_file, detector);
        siminfo_out("end data\n");

        mcruninfo_out( "# ", outfile);
        mcdatainfo_out("# ", outfile,   detector);
        fprintf(outfile, "# Data [%s/%s] %s:\n", detector.component, detector.filename, detector.zvar);
      }
      mcdetector_out_array_ascii(detector.m, detector.n*detector.p, detector.p1,
        outfile, detector.istransposed);
      if (detector.p2) {
        fprintf(outfile, "# Errors [%s/%s] %s_err:\n", detector.component, detector.filename, detector.zvar);
        mcdetector_out_array_ascii(detector.m, detector.n*detector.p, detector.p2,
          outfile, detector.istransposed);
      }
      if (detector.p0) {
        fprintf(outfile, "# Events [%s/%s] N:\n", detector.component, detector.filename);
        mcdetector_out_array_ascii(detector.m, detector.n*detector.p, detector.p0,
          outfile, detector.istransposed);
      }
      fclose(outfile);

      if (!exists) {
        if (strcasestr(detector.format, "list"))
          printf("Events:   \"%s\"\n",
            strlen(detector.filename) ? detector.filename : detector.component);
      }
    } /* if outfile */
  ); /* MPI_MASTER */
#ifdef USE_MPI
  if (strcasestr(detector.format, "list") && mpi_node_count > 1) {
    int node_i=0;
    /* loop along MPI nodes to write sequentially */
    for(node_i=0; node_i<mpi_node_count; node_i++) {
      /* MPI: slaves wait for the master to write its block, then append theirs */
      MPI_Barrier(MPI_COMM_WORLD);
      if (node_i != mpi_node_root && node_i == mpi_node_rank) {
        if(strlen(detector.filename) && !mcdisable_output_files)	/* Don't write if filename is NULL */
          outfile = mcnew_file(detector.filename, "dat", &exists);
        if (!exists)
          fprintf(stderr, "Warning: [MPI node %i] file '%s' does not exist yet, "
                          "MASTER should have opened it before.\n",
            mpi_node_rank, detector.filename);
        if(outfile) {
          mcdetector_out_array_ascii(detector.m, detector.n*detector.p, detector.p1,
            outfile, detector.istransposed);
          fclose(outfile);
        }
      }
    }
  } /* if strcasestr list */
#endif
  return(detector);
} /* mcdetector_out_2D_ascii */

/*******************************************************************************
* strcpy_valid: makes a valid string for variable names.
*   copy 'original' into 'valid', replacing invalid characters by '_'
*   char arrays must be pre-allocated
*******************************************************************************/
static char *strcpy_valid(char *valid, char *original)
{
  long i;
  int  n=CHAR_BUF_LENGTH; /* max length of valid names */

  if (original == NULL || !strlen(original)) return(NULL);

  if (n > strlen(original)) n = strlen(original);
  else original += strlen(original)-n;
  strncpy(valid, original, n);

  for (i=0; i < n; i++)
  {
    if ( (valid[i] > 122)
      || (valid[i] < 32)
      || (strchr("!\"#$%&'()*+,-.:;<=>?@[\\]^`/ \n\r\t", valid[i]) != NULL) )
    {
      if (i) valid[i] = '_'; else valid[i] = 'm';
    }
  }
  valid[i] = '\0';

  return(valid);
} /* strcpy_valid */

/* end ascii output section ================================================= */







#ifdef USE_NEXUS

/* ========================================================================== */

/*                               NeXus output                                 */

/* ========================================================================== */

#define nxprintf(...)    nxstr('d', __VA_ARGS__)
#define nxprintattr(...) nxstr('a', __VA_ARGS__)

/*******************************************************************************
* nxstr: output a tag=value data set (char) in NeXus/current group
*   when 'format' is larger that 1024 chars it is used as value for the 'tag'
*   else the value is assembled with format and following arguments.
*   type='d' -> data set
*        'a' -> attribute for current data set
*******************************************************************************/
static int nxstr(char type, NXhandle *f, char *tag, char *format, ...)
{
  va_list ap;
  char value[CHAR_BUF_LENGTH];
  int  i;
  int  ret=NX_OK;

  if (!tag || !format || !strlen(tag) || !strlen(format)) return(NX_OK);

  /* assemble the value string */
  if (strlen(format) < CHAR_BUF_LENGTH) {
    va_start(ap, format);
    ret = vsnprintf(value, CHAR_BUF_LENGTH, format, ap);
    va_end(ap);

    i = strlen(value);
  } else {
    i = strlen(format);
  }

  if (type == 'd') {
    /* open/put/close data set */
    if (NXmakedata (f, tag, NX_CHAR, 1, &i) != NX_OK) return(NX_ERROR);
    NXopendata (f, tag);
    if (strlen(format) < CHAR_BUF_LENGTH)
      ret = NXputdata  (f, value);
    else
      ret = NXputdata  (f, format);
    NXclosedata(f);
  } else {
    if (strlen(format) < CHAR_BUF_LENGTH)
      ret = NXputattr  (f, tag, value, strlen(value), NX_CHAR);
    else
      ret = NXputattr  (f, tag, format, strlen(format), NX_CHAR);
  }

  return(ret);

} /* nxstr */

/*******************************************************************************
* mcinfo_readfile: read a full file into a string buffer which is allocated
*   Think to free the buffer after use.
* Used in: mcinfo_out_nexus (nexus)
*******************************************************************************/
char *mcinfo_readfile(char *filename)
{
  FILE *f = fopen(filename, "rb");
  if (!f) return(NULL);
  fseek(f, 0, SEEK_END);
  long fsize = ftell(f);
  rewind(f);
  char *string = malloc(fsize + 1);
  if (string) {
    int n = fread(string, fsize, 1, f);
    fclose(f);

    string[fsize] = 0;
  }
  return(string);
}

/*******************************************************************************
* mcinfo_out: output instrument/simulation groups in NeXus file
* Used in: siminfo_init (nexus)
*******************************************************************************/
static void mcinfo_out_nexus(NXhandle f)
{
  FILE  *fid;     /* for intrument source code/C/IDF */
  char  *buffer=NULL;
  time_t t     =time(NULL); /* for date */
  char   entry0[CHAR_BUF_LENGTH];
  int    count=0;
  char   name[CHAR_BUF_LENGTH];
  char   class[CHAR_BUF_LENGTH];

  if (!f || mcdisable_output_files) return;

  /* write NeXus NXroot attributes */
  /* automatically added: file_name, HDF5_Version, file_time, NeXus_version */
  nxprintattr(f, "creator",   "%s generated with " MCCODE_STRING, instrument_name);

  /* count the number of existing NXentry and create the next one */
  NXgetgroupinfo(f, &count, name, class);
  sprintf(entry0, "entry%i", count+1);

  /* create the main NXentry (mandatory in NeXus) */
  if (NXmakegroup(f, entry0, "NXentry") == NX_OK)
  if (NXopengroup(f, entry0, "NXentry") == NX_OK) {

    nxprintf(nxhandle, "program_name", MCCODE_STRING);
    nxprintf(f, "start_time", ctime(&t));
    nxprintf(f, "title", "%s%s%s simulation generated by instrument %s",
      dirname && strlen(dirname) ? dirname : ".", MC_PATHSEP_S, siminfo_name,
      instrument_name);
    nxprintattr(f, "program_name", MCCODE_STRING);
    nxprintattr(f, "instrument",   instrument_name);
    nxprintattr(f, "simulation",   "%s%s%s",
        dirname && strlen(dirname) ? dirname : ".", MC_PATHSEP_S, siminfo_name);

    /* write NeXus instrument group */
    if (NXmakegroup(f, "instrument", "NXinstrument") == NX_OK)
    if (NXopengroup(f, "instrument", "NXinstrument") == NX_OK) {
      int   i;
      char *string=NULL;

      /* write NeXus parameters(types) data =================================== */
      string = (char*)malloc(CHAR_BUF_LENGTH);
      if (string) {
        strcpy(string, "");
        for(i = 0; i < numipar; i++)
        {
          char ThisParam[CHAR_BUF_LENGTH];
          snprintf(ThisParam, CHAR_BUF_LENGTH, " %s(%s)", mcinputtable[i].name,
                  (*mcinputtypes[mcinputtable[i].type].parminfo)
                      (mcinputtable[i].name));
          if (strlen(string) + strlen(ThisParam) < CHAR_BUF_LENGTH)
            strcat(string, ThisParam);
        }
        nxprintattr(f, "Parameters",    string);
        free(string);
      }

      nxprintattr(f, "name",          instrument_name);
      nxprintf   (f, "name",          instrument_name);
      nxprintattr(f, "Source",        instrument_source);

      nxprintattr(f, "Trace_enabled", traceenabled ? "yes" : "no");
      nxprintattr(f, "Default_main",  defaultmain ?  "yes" : "no");
      nxprintattr(f, "Embedded_runtime",
  #ifdef MC_EMBEDDED_RUNTIME
           "yes"
  #else
           "no"
  #endif
           );

      /* add instrument source code when available */
      buffer = mcinfo_readfile(instrument_source);
      if (buffer && strlen(buffer)) {
        long length=strlen(buffer);
        nxprintf (f, "description", buffer);
        NXopendata(f,"description");
        nxprintattr(f, "file_name", instrument_source);
        nxprintattr(f, "file_size", "%li", length);
        nxprintattr(f, "MCCODE_STRING", MCCODE_STRING);
        NXclosedata(f);
        nxprintf (f,"instrument_source", "%s " MCCODE_NAME " " MCCODE_PARTICLE " Monte Carlo simulation", instrument_name);
        free(buffer);
      } else
        nxprintf (f, "description", "File %s not found (instrument description %s is missing)",
          instrument_source, instrument_name);

      /* add Mantid/IDF.xml when available */
      char *IDFfile=NULL;
      IDFfile = (char*)malloc(CHAR_BUF_LENGTH);
      sprintf(IDFfile,"%s%s",instrument_source,".xml");
      buffer = mcinfo_readfile(IDFfile);
      if (buffer && strlen(buffer)) {
        NXmakegroup (nxhandle, "instrument_xml", "NXnote");
        NXopengroup (nxhandle, "instrument_xml", "NXnote");
        nxprintf(f, "data", buffer);
        nxprintf(f, "description", "IDF.xml file found with instrument %s", instrument_source);
        nxprintf(f, "type", "text/xml");
        NXclosegroup(f); /* instrument_xml */
        free(buffer);
      }
      free(IDFfile);
      NXclosegroup(f); /* instrument */
    } /* NXinstrument */

    /* write NeXus simulation group */
    if (NXmakegroup(f, "simulation", "NXnote") == NX_OK)
    if (NXopengroup(f, "simulation", "NXnote") == NX_OK) {

      nxprintattr(f, "name",   "%s%s%s",
        dirname && strlen(dirname) ? dirname : ".", MC_PATHSEP_S, siminfo_name);

      nxprintf   (f, "name",      "%s",     siminfo_name);
      nxprintattr(f, "Format",    mcformat && strlen(mcformat) ? mcformat : MCCODE_NAME);
      nxprintattr(f, "URL",       "http://www.mccode.org");
      nxprintattr(f, "program",   MCCODE_STRING);
      nxprintattr(f, "Instrument",instrument_source);
      nxprintattr(f, "Trace",     mcdotrace ?     "yes" : "no");
      nxprintattr(f, "Gravitation",mcgravitation ? "yes" : "no");
      nxprintattr(f, "Seed",      "%li", mcseed);
      nxprintattr(f, "Directory", dirname);
    #ifdef USE_MPI
      if (mpi_node_count > 1)
        nxprintf(f, "Nodes", "%i",        mpi_node_count);
    #endif

      /* output parameter string ================================================ */
      if (NXmakegroup(f, "Param", "NXparameters") == NX_OK)
      if (NXopengroup(f, "Param", "NXparameters") == NX_OK) {
        int i;
        char string[CHAR_BUF_LENGTH];
        for(i = 0; i < numipar; i++) {
          if (mcget_run_num() || (mcinputtable[i].val && strlen(mcinputtable[i].val))) {
            if (mcinputtable[i].par == NULL)
              strncpy(string, (mcinputtable[i].val ? mcinputtable[i].val : ""), CHAR_BUF_LENGTH);
            else
              (*mcinputtypes[mcinputtable[i].type].printer)(string, mcinputtable[i].par);

            nxprintf(f,  mcinputtable[i].name, "%s", string);
            nxprintattr(f, mcinputtable[i].name, string);
          }
        }
        NXclosegroup(f); /* Param */
      } /* NXparameters */

      NXclosegroup(f); /* simulation */
    } /* NXsimulation */

    /* create a group to hold all monitors */
    NXmakegroup(f, "data", "NXdetector");

    /* leave the NXentry opened (closed at exit) */
  } /* NXentry */
} /* mcinfo_out_nexus */

/*******************************************************************************
* mcdatainfo_out_nexus: output detector header
*   mcdatainfo_out_nexus(detector) create group and write info to NeXus data file
*   open data:NXdetector then filename:NXdata and write headers/attributes
*   requires: NXentry to be opened
*******************************************************************************/
static void
mcdatainfo_out_nexus(NXhandle f, MCDETECTOR detector)
{
  char data_name[CHAR_BUF_LENGTH];
  if (!f || !detector.m || mcdisable_output_files) return;

  strcpy_valid(data_name,
    detector.filename && strlen(detector.filename) ?
      detector.filename : detector.component);

  /* the NXdetector group has been created in mcinfo_out_nexus (siminfo_init) */
  if (NXopengroup(f, "data", "NXdetector") == NX_OK) {

    /* create and open the data group */
    /* this may fail when appending to list -> ignore/skip */
    NXMDisableErrorReporting(); /* unactivate NeXus error messages, as creation may fail */

    if (NXmakegroup(f, data_name, "NXdata") == NX_OK)
    if (NXopengroup(f, data_name, "NXdata") == NX_OK) {

      /* output metadata (as attributes) ======================================== */
      nxprintattr(f, "Date",       detector.date);
      nxprintattr(f, "type",       detector.type);
      nxprintattr(f, "Source",     detector.instrument);
      nxprintattr(f, "component",  detector.component);
      nxprintattr(f, "position",   detector.position);

      nxprintattr(f, "title",      detector.title);
      nxprintattr(f, !mcget_run_num() || mcget_run_num() >= mcget_ncount() ?
                 "Ncount" :
                 "ratio",  detector.ncount);

      if (strlen(detector.filename)) {
        nxprintattr(f, "filename", detector.filename);
      }

      nxprintattr(f, "statistics", detector.statistics);
      nxprintattr(f, "signal",     detector.signal);
      nxprintattr(f, "values",     detector.values);

      if (detector.rank >= 1)
      {
        nxprintattr(f, "xvar",     detector.xvar);
        nxprintattr(f, "yvar",     detector.yvar);
        nxprintattr(f, "xlabel",   detector.xlabel);
        nxprintattr(f, "ylabel",   detector.ylabel);
        if (detector.rank > 1) {
          nxprintattr(f, "zvar",   detector.zvar);
          nxprintattr(f, "zlabel", detector.zlabel);
        }
      }

      nxprintattr(f, abs(detector.rank)==1 ?
                 "xlimits" :
                 "xylimits", detector.limits);
      nxprintattr(f, "variables",
        strcasestr(detector.format, "list") ? detector.ylabel : detector.variables);
      nxprintf(f, "distance", detector.position);
      nxprintf(f, "acquisition_mode",
        strcasestr(detector.format, "list") ? "event" : "summed");

      NXclosegroup(f);
    } /* NXdata (filename) */
    NXMEnableErrorReporting();  /* re-enable NeXus error messages */
    NXclosegroup(f);
  } /* NXdetector (data) */

} /* mcdatainfo_out_nexus */

/*******************************************************************************
* mcdetector_out_axis_nexus: write detector axis into current NXdata
*   requires: NXdata to be opened
*******************************************************************************/
int mcdetector_out_axis_nexus(NXhandle f, char *label, char *var, int rank, long length, double min, double max)
{
  if (!f || length <= 1 || mcdisable_output_files || max == min) return(NX_OK);
  else {
    double axis[length];
    char valid[CHAR_BUF_LENGTH];
    int dim=(int)length;
    int i;
    int nprimary=1;
    /* create an axis from [min:max] */
    for(i = 0; i < length; i++)
      axis[i] = min+(max-min)*(i+0.5)/length;
    /* create the data set */
    strcpy_valid(valid, label);
    NXcompmakedata(f, valid, NX_FLOAT64, 1, &dim, NX_COMPRESSION, &dim);
    /* open it */
    if (NXopendata(f, valid) != NX_OK) {
      fprintf(stderr, "Warning: could not open axis rank %i '%s' (NeXus)\n",
        rank, valid);
      return(NX_ERROR);
    }
    /* put the axis and its attributes */
    NXputdata  (f, axis);
    nxprintattr(f, "long_name",  label);
    nxprintattr(f, "short_name", var);
    NXputattr  (f, "axis",       &rank,     1, NX_INT32);
    nxprintattr(f, "units",      var);
    NXputattr  (f, "primary",    &nprimary, 1, NX_INT32);
    NXclosedata(f);

    return(NX_OK);
  }
} /* mcdetector_out_axis_nexus */

/*******************************************************************************
* mcdetector_out_array_nexus: write detector array into current NXdata (1D,2D)
*   requires: NXdata to be opened
*******************************************************************************/
int mcdetector_out_array_nexus(NXhandle f, char *part, double *data, MCDETECTOR detector)
{

  int dims[3]={detector.m,detector.n,detector.p};  /* number of elements to write */
  int fulldims[3]={detector.m,detector.n,detector.p};
  int signal=1;
  int exists=0;
  int current_dims[3]={0,0,0};
  int ret=NX_OK;

  if (!f || !data || !detector.m || mcdisable_output_files) return(NX_OK);

  /* when this is a list, we set 1st dimension to NX_UNLIMITED for creation */
  if (strcasestr(detector.format, "list")) fulldims[0] = NX_UNLIMITED;

  /* create the data set in NXdata group */
  NXMDisableErrorReporting(); /* unactivate NeXus error messages, as creation may fail */
  ret = NXcompmakedata(f, part, NX_FLOAT64, detector.rank, fulldims, NX_COMPRESSION, dims);
  if (ret != NX_OK) {
    /* failed: data set already exists */
    int datatype=0;
    int rank=0;
    exists=1;
    /* inquire current size of data set (nb of events stored) */
    NXopendata(f, part);
    NXgetinfo(f, &rank, current_dims, &datatype);
    NXclosedata(f);
  }
  NXMEnableErrorReporting();  /* re-enable NeXus error messages */

  /* open the data set */
  if (NXopendata(f, part) == NX_ERROR) {
    fprintf(stderr, "Warning: could not open DataSet %s '%s' (NeXus)\n",
      part, detector.title);
    return(NX_ERROR);
  }
  if (strcasestr(detector.format, "list")) {
    current_dims[1] = current_dims[2] = 0; /* set starting location for writing slab */
    NXputslab(f, data, current_dims, dims);
    if (!exists)
      printf("Events:   \"%s\"\n",
        strlen(detector.filename) ? detector.filename : detector.component);
    else
      printf("Append:   \"%s\"\n",
	     strlen(detector.filename) ? detector.filename : detector.component);
  } else {
    NXputdata (f, data);
  }

  if (strstr(part,"data") || strstr(part, "events")) {
    NXputattr(f, "signal", &signal, 1, NX_INT32);
    nxprintattr(f, "short_name", detector.filename && strlen(detector.filename) ?
      detector.filename : detector.component);
  }
  nxprintattr(f, "long_name", "%s '%s'", part, detector.title);
  NXclosedata(f);

  return(NX_OK);
} /* mcdetector_out_array_nexus */

/*******************************************************************************
* mcdetector_out_data_nexus: write detector axes+data into current NXdata
*   The data:NXdetector is opened, then filename:NXdata
*   requires: NXentry to be opened
*******************************************************************************/
int mcdetector_out_data_nexus(NXhandle f, MCDETECTOR detector)
{
  char data_name[CHAR_BUF_LENGTH];

  if (!f || !detector.m || mcdisable_output_files) return(NX_OK);

  strcpy_valid(data_name,
    detector.filename && strlen(detector.filename) ?
      detector.filename : detector.component);

  /* the NXdetector group has been created in mcinfo_out_nexus (siminfo_init) */
  if (NXopengroup(f, "data", "NXdetector") == NX_OK) {

    /* the NXdata group has been created in mcdatainfo_out_nexus */
    if (NXopengroup(f, data_name, "NXdata") == NX_OK) {

      /* write axes, for histogram data sets, not for lists */
      if (!strcasestr(detector.format, "list")) {
        mcdetector_out_axis_nexus(f, detector.xlabel, detector.xvar,
          1, detector.m, detector.xmin, detector.xmax);

        mcdetector_out_axis_nexus(f, detector.ylabel, detector.yvar,
          2, detector.n, detector.ymin, detector.ymax);

        mcdetector_out_axis_nexus(f, detector.zlabel, detector.zvar,
          3, detector.p, detector.zmin, detector.zmax);

      } /* !list */

      /* write the actual data (appended if already exists) */
      if (!strcasestr(detector.format, "list")) {
        mcdetector_out_array_nexus(f, "data", detector.p1, detector);
        mcdetector_out_array_nexus(f, "errors", detector.p2, detector);
        mcdetector_out_array_nexus(f, "ncount", detector.p0, detector);
      } else
        mcdetector_out_array_nexus(  f, "events", detector.p1, detector);

      NXclosegroup(f);
    } /* NXdata */
    NXclosegroup(f);
  } /* NXdetector */

  return(NX_OK);
} /* mcdetector_out_array_nexus */

#ifdef USE_MPI
/*******************************************************************************
* mcdetector_out_list_slaves: slaves send their list data to master which writes
*   requires: NXentry to be opened
* WARNING: this method has a flaw: it requires all nodes to flush the lists
*   the same number of times. In case one node is just below the buffer size
*   when finishing (e.g. monitor_nd), it may not trigger save but others may.
*   Then the number of recv/send is not constant along nodes, and simulation stalls.
*******************************************************************************/
MCDETECTOR mcdetector_out_list_slaves(MCDETECTOR detector)
{
  int     node_i=0;
  MPI_MASTER(
	     printf("\n** MPI master gathering slave node list data ** \n");
  );

  if (mpi_node_rank != mpi_node_root) {
    /* MPI slave: slaves send their data to master: 2 MPI_Send calls */
    /* m, n, p must be sent first, since all slaves do not have the same number of events */
    int mnp[3]={detector.m,detector.n,detector.p};

    if (mc_MPI_Send(mnp, 3, MPI_INT, mpi_node_root)!= MPI_SUCCESS)
      fprintf(stderr, "Warning: proc %i to master: MPI_Send mnp list error (mcdetector_out_list_slaves)\n", mpi_node_rank);
    if (!detector.p1
     || mc_MPI_Send(detector.p1, mnp[0]*mnp[1]*mnp[2], MPI_DOUBLE, mpi_node_root) != MPI_SUCCESS)
      fprintf(stderr, "Warning: proc %i to master: MPI_Send p1 list error: mnp=%i (mcdetector_out_list_slaves)\n", mpi_node_rank, abs(mnp[0]*mnp[1]*mnp[2]));
    /* slaves are done: sent mnp and p1 */
    return (detector);
  } /* end slaves */

  /* MPI master: receive data from slaves sequentially: 2 MPI_Recv calls */

  if (mpi_node_rank == mpi_node_root) {
    for(node_i=0; node_i<mpi_node_count; node_i++) {
      double *this_p1=NULL;                               /* buffer to hold the list from slaves */
      int     mnp[3]={0,0,0};  /* size of this buffer */
      if (node_i != mpi_node_root) { /* get data from slaves */
	if (mc_MPI_Recv(mnp, 3, MPI_INT, node_i) != MPI_SUCCESS)
	  fprintf(stderr, "Warning: master from proc %i: "
		  "MPI_Recv mnp list error (mcdetector_write_data)\n", node_i);
	if (mnp[0]*mnp[1]*mnp[2]) {
	  this_p1 = (double *)calloc(mnp[0]*mnp[1]*mnp[2], sizeof(double));
	  if (!this_p1 || mc_MPI_Recv(this_p1, abs(mnp[0]*mnp[1]*mnp[2]), MPI_DOUBLE, node_i)!= MPI_SUCCESS)
	    fprintf(stderr, "Warning: master from proc %i: "
		    "MPI_Recv p1 list error: mnp=%i (mcdetector_write_data)\n", node_i, mnp[0]*mnp[1]*mnp[2]);
	  else {
	    printf(". MPI master writing data for slave node %i\n",node_i);
	    detector.p1 = this_p1;
	    detector.m  = mnp[0]; detector.n  = mnp[1]; detector.p  = mnp[2];

	    mcdetector_out_data_nexus(nxhandle, detector);
	  }
	}
      } /* if not master */
    } /* for */
  MPI_MASTER(
	     printf("\n** Done ** \n");
  );
  }
}
#endif

MCDETECTOR mcdetector_out_0D_nexus(MCDETECTOR detector)
{
  /* Write data set information to NeXus file. */
  MPI_MASTER(
    mcdatainfo_out_nexus(nxhandle, detector);
  );

  return(detector);
} /* mcdetector_out_0D_ascii */

MCDETECTOR mcdetector_out_1D_nexus(MCDETECTOR detector)
{
  MPI_MASTER(
  mcdatainfo_out_nexus(nxhandle, detector);
  mcdetector_out_data_nexus(nxhandle, detector);
  );
  return(detector);
} /* mcdetector_out_1D_ascii */

MCDETECTOR mcdetector_out_2D_nexus(MCDETECTOR detector)
{
  MPI_MASTER(
  mcdatainfo_out_nexus(nxhandle, detector);
  mcdetector_out_data_nexus(nxhandle, detector);
  );

#ifdef USE_MPI // and USE_NEXUS
  /* NeXus: slave nodes have master write their lists */
  if (strcasestr(detector.format, "list") && mpi_node_count > 1) {
    mcdetector_out_list_slaves(detector);
  }
#endif /* USE_MPI */

  return(detector);
} /* mcdetector_out_2D_nexus */

#endif /* USE_NEXUS*/








/* ========================================================================== */

/*                            Main input functions                            */
/*            DETECTOR_OUT_xD function calls -> ascii or NeXus                */

/* ========================================================================== */

/*******************************************************************************
* siminfo_init:   open SIM and write header
*******************************************************************************/
FILE *siminfo_init(FILE *f)
{
  int exists=0;

  /* check format */
  if (!mcformat || !strlen(mcformat)
   || !strcasecmp(mcformat, "MCSTAS") || !strcasecmp(mcformat, "MCXTRACE")
   || !strcasecmp(mcformat, "PGPLOT") || !strcasecmp(mcformat, "GNUPLOT") || !strcasecmp(mcformat, "MCCODE")
   || !strcasecmp(mcformat, "MATLAB")) {
    mcformat="McCode";
#ifdef USE_NEXUS
  } else if (strcasestr(mcformat, "NeXus")) {
    /* Do nothing */
#endif
  } else {
    fprintf(stderr,
	    "Warning: You have requested the output format %s which is unsupported by this binary. Resetting to standard %s format.\n",mcformat ,"McCode");
    mcformat="McCode";
  }

  /* open the SIM file if not defined yet */
  if (siminfo_file || mcdisable_output_files)
    return (siminfo_file);

#ifdef USE_NEXUS
  /* only master writes NeXus header: calls NXopen(nxhandle) */
  if (mcformat && strcasestr(mcformat, "NeXus")) {
	  MPI_MASTER(
	  siminfo_file = mcnew_file(siminfo_name, "h5", &exists);
    if(!siminfo_file)
      fprintf(stderr,
	      "Warning: could not open simulation description file '%s'\n",
	      siminfo_name);
	  else
	    mcinfo_out_nexus(nxhandle);
	  );
    return(siminfo_file); /* points to nxhandle */
  }
#endif

  /* write main description file (only MASTER) */
  MPI_MASTER(

  siminfo_file = mcnew_file(siminfo_name, "sim", &exists);
  if(!siminfo_file)
    fprintf(stderr,
	    "Warning: could not open simulation description file '%s'\n",
	    siminfo_name);
  else
  {
    /* write SIM header */
    time_t t=time(NULL);
    siminfo_out("%s simulation description file for %s.\n",
      MCCODE_NAME, instrument_name);
    siminfo_out("Date:    %s", ctime(&t)); /* includes \n */
    siminfo_out("Program: %s\n\n", MCCODE_STRING);

    siminfo_out("begin instrument: %s\n", instrument_name);
    mcinfo_out(   "  ", siminfo_file);
    siminfo_out("end instrument\n");

    siminfo_out("\nbegin simulation: %s\n", dirname);
    mcruninfo_out("  ", siminfo_file);
    siminfo_out("end simulation\n");

  }
  return (siminfo_file);

  ); /* MPI_MASTER */

} /* siminfo_init */

/*******************************************************************************
*   siminfo_close:  close SIM
*******************************************************************************/
void siminfo_close()
{
#ifdef USE_MPI
  if(mpi_node_rank == mpi_node_root) {
#endif
  if(siminfo_file && !mcdisable_output_files) {
#ifdef USE_NEXUS
    if (mcformat && strcasestr(mcformat, "NeXus")) {
      time_t t=time(NULL);
      nxprintf(nxhandle, "end_time", ctime(&t));
      nxprintf(nxhandle, "duration", "%li", (long)t-mcstartdate);
      NXclosegroup(nxhandle); /* NXentry */
      NXclose(&nxhandle);
    } else {
#endif
      fclose(siminfo_file);
#ifdef USE_NEXUS
    }
#endif
#ifdef USE_MPI
  }
#endif
    siminfo_file = NULL;
  }
} /* siminfo_close */

/*******************************************************************************
* mcdetector_out_0D: wrapper for 0D (single value).
*   Output single detector/monitor data (p0, p1, p2).
*   Title is t, component name is c.
*******************************************************************************/
MCDETECTOR mcdetector_out_0D(char *t, double p0, double p1, double p2,
                         char *c, Coords posa)
{
  /* import and perform basic detector analysis (and handle MPI reduce) */
  MCDETECTOR detector = detector_import(mcformat,
    c, (t ? t : MCCODE_STRING " data"),
    1, 1, 1,
    "I", "", "",
    "I", "", "",
    0, 0, 0, 0, 0, 0, "",
    &p0, &p1, &p2, posa); /* write Detector: line */

#ifdef USE_NEXUS
  if (strcasestr(detector.format, "NeXus"))
    return(mcdetector_out_0D_nexus(detector));
  else
#endif
    return(mcdetector_out_0D_ascii(detector));

} /* mcdetector_out_0D */



/*******************************************************************************
* mcdetector_out_1D: wrapper for 1D.
*   Output 1d detector data (p0, p1, p2) for n bins linearly
*   distributed across the range x1..x2 (x1 is lower limit of first
*   bin, x2 is upper limit of last bin). Title is t, axis labels are xl
*   and yl. File name is f, component name is c.
*
*   t:    title
*   xl:   x-label
*   yl:   y-label
*   xvar: measured variable length
*   x1:   x axus min
*   x2:   x axis max
*   n:    1d data vector lenght
*   p0:   pntr to start of data block#0
*   p1:   pntr to start of data block#1
*   p2:   pntr to start of data block#2
*   f:    filename
*
*   Not included in the macro, and here forwarded to detector_import:
*   c:    ?
*   posa: ?
*******************************************************************************/
MCDETECTOR mcdetector_out_1D(char *t, char *xl, char *yl,
        char *xvar, double x1, double x2,
        long n,
        double *p0, double *p1, double *p2, char *f,
        char *c, Coords posa)
{
  /* import and perform basic detector analysis (and handle MPI_Reduce) */
  MCDETECTOR detector = detector_import(mcformat,
    c, (t ? t : MCCODE_STRING " 1D data"),
    n, 1, 1,
    xl, yl, (n > 1 ? "Signal per bin" : " Signal"),
    xvar, "(I,I_err)", "I",
    x1, x2, 0, 0, 0, 0, f,
    p0, p1, p2, posa); /* write Detector: line */
  if (!detector.p1 || !detector.m) return(detector);

#ifdef USE_NEXUS
  if (strcasestr(detector.format, "NeXus"))
    return(mcdetector_out_1D_nexus(detector));
  else
#endif
    return(mcdetector_out_1D_ascii(detector));

} /* mcdetector_out_1D */

/*******************************************************************************
* mcdetector_out_2D: wrapper for 2D.
*   Special case for list: master creates file first, then slaves append their
*   blocks without header-
*
*   t:    title
*   xl:   x-label
*   yl:   y-label
*   x1:   x axus min
*   x2:   x axis max
*   y1:   y axis min
*   y2:   y axis max
*   m:    dim 1 (x) size
*   n:    dim 2 (y) size
*   p0:   pntr to start of data block#0
*   p1:   pntr to start of data block#1
*   p2:   pntr to start of data block#2
*   f:    filename
*
*   Not included in the macro, and here forwarded to detector_import:
*   c:    ?
*   posa: ?
*******************************************************************************/
MCDETECTOR mcdetector_out_2D(char *t, char *xl, char *yl,
                  double x1, double x2, double y1, double y2,
                  long m, long n,
                  double *p0, double *p1, double *p2, char *f,
                  char *c, Coords posa)
{
  char xvar[CHAR_BUF_LENGTH];
  char yvar[CHAR_BUF_LENGTH];

  /* create short axes labels */
  if (xl && strlen(xl)) { strncpy(xvar, xl, CHAR_BUF_LENGTH); xvar[2]='\0'; }
  else strcpy(xvar, "x");
  if (yl && strlen(yl)) { strncpy(yvar, yl, CHAR_BUF_LENGTH); yvar[2]='\0'; }
  else strcpy(yvar, "y");

  MCDETECTOR detector;

  /* import and perform basic detector analysis (and handle MPI_Reduce) */
  if (labs(m) == 1) {/* n>1 on Y, m==1 on X: 1D, no X axis*/
    detector = detector_import(mcformat,
      c, (t ? t : MCCODE_STRING " 1D data"),
      n, 1, 1,
      yl, "", "Signal per bin",
      yvar, "(I,Ierr)", "I",
      y1, y2, x1, x2, 0, 0, f,
      p0, p1, p2, posa); /* write Detector: line */
  } else if (labs(n)==1) {/* m>1 on X, n==1 on Y: 1D, no Y axis*/
    detector = detector_import(mcformat,
      c, (t ? t : MCCODE_STRING " 1D data"),
      m, 1, 1,
      xl, "", "Signal per bin",
      xvar, "(I,Ierr)", "I",
      x1, x2, y1, y2, 0, 0, f,
      p0, p1, p2, posa); /* write Detector: line */
  }else {
    detector = detector_import(mcformat,
      c, (t ? t : MCCODE_STRING " 2D data"),
      m, n, 1,
      xl, yl, "Signal per bin",
      xvar, yvar, "I",
      x1, x2, y1, y2, 0, 0, f,
      p0, p1, p2, posa); /* write Detector: line */
  }

  if (!detector.p1 || !detector.m) return(detector);

#ifdef USE_NEXUS
  if (strcasestr(detector.format, "NeXus"))
    return(mcdetector_out_2D_nexus(detector));
  else
#endif
    return(mcdetector_out_2D_ascii(detector));

} /* mcdetector_out_2D */

/*******************************************************************************
* mcdetector_out_list: wrapper for list output (calls out_2D with mcformat+"list").
*   m=number of events, n=size of each event
*******************************************************************************/
MCDETECTOR mcdetector_out_list(char *t, char *xl, char *yl,
                  long m, long n,
                  double *p1, char *f,
                  char *c, Coords posa)
{
  char       format_new[CHAR_BUF_LENGTH];
  char      *format_org;
  MCDETECTOR detector;

  format_org = mcformat;
  strcpy(format_new, mcformat);
  strcat(format_new, " list");
  mcformat = format_new;

  detector = mcdetector_out_2D(t, xl, yl,
                  1,labs(m),1,labs(n),
                  m,n,
                  NULL, p1, NULL, f,
                  c, posa);

  mcformat = format_org;
  return(detector);
}

/*******************************************************************************
 * mcuse_dir: set data/sim storage directory and create it,
 * or exit with error if exists
 ******************************************************************************/
static void
mcuse_dir(char *dir)
{
  if (!dir || !strlen(dir)) return;
#ifdef MC_PORTABLE
  fprintf(stderr, "Error: "
          "Directory output cannot be used with portable simulation (mcuse_dir)\n");
  exit(1);
#else  /* !MC_PORTABLE */
  /* handle file://directory URL type */
  if (strncmp(dir, "file://", strlen("file://")))
    dirname = dir;
  else
    dirname = dir+strlen("file://");


#ifdef USE_MPI
  if(mpi_node_rank == mpi_node_root) {
#endif
    if(mkdir(dirname, 0777)) {
#ifndef DANSE
      fprintf(stderr, "Error: unable to create directory '%s' (mcuse_dir)\n", dir);
      fprintf(stderr, "(Maybe the directory already exists?)\n");
#endif
#ifdef USE_MPI
      MPI_Abort(MPI_COMM_WORLD, -1);
#endif
    exit(-1);
    }
#ifdef USE_MPI
    }
#endif

  /* remove trailing PATHSEP (if any) */
  while (strlen(dirname) && dirname[strlen(dirname) - 1] == MC_PATHSEP_C)
    dirname[strlen(dirname) - 1]='\0';
#endif /* !MC_PORTABLE */
} /* mcuse_dir */

/*******************************************************************************
* mcinfo: display instrument simulation info to stdout and exit
*******************************************************************************/
static void
mcinfo(void)
{
  fprintf(stdout, "begin instrument: %s\n", instrument_name);
  mcinfo_out("  ", stdout);
  fprintf(stdout, "end instrument\n");
  fprintf(stdout, "begin simulation: %s\n", dirname ? dirname : ".");
  mcruninfo_out("  ", stdout);
  fprintf(stdout, "end simulation\n");
  exit(0); /* includes MPI_Finalize in MPI mode */
} /* mcinfo */

#endif /* ndef MCCODE_R_IO_C */

/* end of the I/O section =================================================== */







/*******************************************************************************
* mcset_ncount: set total number of rays to generate
*******************************************************************************/
void mcset_ncount(unsigned long long int count)
{
  mcncount = count;
}

/* mcget_ncount: get total number of rays to generate */
unsigned long long int mcget_ncount(void)
{
  return mcncount;
}

/* mcget_run_num: get curent number of rays */
/* Within the TRACE scope we are now using _particle->uid directly */
unsigned long long int mcget_run_num() // shuld be (_class_particle* _particle) somehow
{
  /* This function only remains for the few cases outside TRACE where we need to know
     the number of simulated particles */
  return mcrun_num;
}

/* mcsetn_arg: get ncount from a string argument */
static void
mcsetn_arg(char *arg)
{
  mcset_ncount((long long int) strtod(arg, NULL));
}

/* mcsetseed: set the random generator seed from a string argument */
static void
mcsetseed(char *arg)
{
  mcseed = atol(arg);
  if(!mcseed) {
  //  srandom(mcseed);
  //} else {
    fprintf(stderr, "Error: seed must not be zero (mcsetseed)\n");
    exit(1);
  }
}

/* Following part is only embedded when not redundent with mccode-r.h ========= */

#ifndef MCCODE_H

/* SECTION: MCDISPLAY support. =============================================== */

/*******************************************************************************
* Just output MCDISPLAY keywords to be caught by an external plotter client.
*******************************************************************************/

void mcdis_magnify(char *what){
  // Do nothing here, better use interactive zoom from the tools
}

void mcdis_line(double x1, double y1, double z1,
                double x2, double y2, double z2){
  printf("MCDISPLAY: multiline(2,%g,%g,%g,%g,%g,%g)\n",
         x1,y1,z1,x2,y2,z2);
}

void mcdis_dashed_line(double x1, double y1, double z1,
		       double x2, double y2, double z2, int n){
  int i;
  const double dx = (x2-x1)/(2*n+1);
  const double dy = (y2-y1)/(2*n+1);
  const double dz = (z2-z1)/(2*n+1);

  for(i = 0; i < n+1; i++)
    mcdis_line(x1 + 2*i*dx,     y1 + 2*i*dy,     z1 + 2*i*dz,
	       x1 + (2*i+1)*dx, y1 + (2*i+1)*dy, z1 + (2*i+1)*dz);
}

void mcdis_multiline(int count, ...){
  va_list ap;
  double x,y,z;

  printf("MCDISPLAY: multiline(%d", count);
  va_start(ap, count);
  while(count--)
    {
    x = va_arg(ap, double);
    y = va_arg(ap, double);
    z = va_arg(ap, double);
    printf(",%g,%g,%g", x, y, z);
    }
  va_end(ap);
  printf(")\n");
}

void mcdis_rectangle(char* plane, double x, double y, double z,
		     double width, double height){
  /* draws a rectangle in the plane           */
  /* x is ALWAYS width and y is ALWAYS height */
  if (strcmp("xy", plane)==0) {
    mcdis_multiline(5,
		    x - width/2, y - height/2, z,
		    x + width/2, y - height/2, z,
		    x + width/2, y + height/2, z,
		    x - width/2, y + height/2, z,
		    x - width/2, y - height/2, z);
  } else if (strcmp("xz", plane)==0) {
    mcdis_multiline(5,
		    x - width/2, y, z - height/2,
		    x + width/2, y, z - height/2,
		    x + width/2, y, z + height/2,
		    x - width/2, y, z + height/2,
		    x - width/2, y, z - height/2);
  } else if (strcmp("yz", plane)==0) {
    mcdis_multiline(5,
		    x, y - height/2, z - width/2,
		    x, y - height/2, z + width/2,
		    x, y + height/2, z + width/2,
		    x, y + height/2, z - width/2,
		    x, y - height/2, z - width/2);
  } else {

    fprintf(stderr, "Error: Definition of plane %s unknown\n", plane);
    exit(1);
  }
}

/*  draws a box with center at (x, y, z) and
    width (deltax), height (deltay), length (deltaz) */
void mcdis_box(double x, double y, double z,
	       double width, double height, double length){

  mcdis_rectangle("xy", x, y, z-length/2, width, height);
  mcdis_rectangle("xy", x, y, z+length/2, width, height);
  mcdis_line(x-width/2, y-height/2, z-length/2,
	     x-width/2, y-height/2, z+length/2);
  mcdis_line(x-width/2, y+height/2, z-length/2,
	     x-width/2, y+height/2, z+length/2);
  mcdis_line(x+width/2, y-height/2, z-length/2,
	     x+width/2, y-height/2, z+length/2);
  mcdis_line(x+width/2, y+height/2, z-length/2,
	     x+width/2, y+height/2, z+length/2);
}

void mcdis_circle(char *plane, double x, double y, double z, double r){
  printf("MCDISPLAY: circle('%s',%g,%g,%g,%g)\n", plane, x, y, z, r);
}

/* Draws a circle with center (x,y,z), radius (r), and in the plane
 * with normal (nx,ny,nz)*/
void mcdis_Circle(double x, double y, double z, double r, double nx, double ny, double nz){
    int i;
    if(nx==0 && ny && nz==0){
        for (i=0;i<24; i++){
            mcdis_line(x+r*sin(i*2*PI/24),y,z+r*cos(i*2*PI/24),
                    x+r*sin((i+1)*2*PI/24),y,z+r*cos((i+1)*2*PI/24));
        }
    }else{
        double mx,my,mz;
        /*generate perpendicular vector using (nx,ny,nz) and (0,1,0)*/
        vec_prod(mx,my,mz, 0,1,0, nx,ny,nz);
        NORM(mx,my,mz);
        /*draw circle*/
        for (i=0;i<24; i++){
            double ux,uy,uz;
            double wx,wy,wz;
            rotate(ux,uy,uz, mx,my,mz, i*2*PI/24, nx,ny,nz);
            rotate(wx,wy,wz, mx,my,mz, (i+1)*2*PI/24, nx,ny,nz);
            mcdis_line(x+ux*r,y+uy*r,z+uz*r,
                    x+wx*r,y+wy*r,z+wz*r);
        }
    }
}

/* Draws a cylinder with center at (x,y,z) with extent (r,height).
 * The cylinder axis is along the vector nx,ny,nz.
 * N determines how many vertical lines are drawn.*/
void mcdis_cylinder( double x, double y, double z,
        double r, double height, int N, double nx, double ny, double nz){
    int i;
    /*no lines make little sense - so trigger the default*/
    if(N<=0) N=5;

    NORM(nx,ny,nz);
    double h_2=height/2.0;
    mcdis_Circle(x+nx*h_2,y+ny*h_2,z+nz*h_2,r,nx,ny,nz);
    mcdis_Circle(x-nx*h_2,y-ny*h_2,z-nz*h_2,r,nx,ny,nz);

    double mx,my,mz;
    /*generate perpendicular vector using (nx,ny,nz) and (0,1,0)*/
    if(nx==0 && ny && nz==0){
        mx=my=0;mz=1;
    }else{
        vec_prod(mx,my,mz, 0,1,0, nx,ny,nz);
        NORM(mx,my,mz);
    }
    /*draw circle*/
    for (i=0; i<24; i++){
        double ux,uy,uz;
        rotate(ux,uy,uz, mx,my,mz, i*2*PI/24, nx,ny,nz);
        mcdis_line(x+nx*h_2+ux*r, y+ny*h_2+uy*r, z+nz*h_2+uz*r,
                 x-nx*h_2+ux*r, y-ny*h_2+uy*r, z-nz*h_2+uz*r);
    }
}

/* draws a sphere with center at (x,y,z) with extent (r)
 * The sphere is drawn using N longitudes and N latitudes.*/
void mcdis_sphere(double x, double y, double z, double r, int N){
    double nx,ny,nz;
    int i;
    /*no lines make little sense - so trigger the default*/
    if(N<=0) N=5;

    nx=0;ny=0;nz=1;
    mcdis_Circle(x,y,z,r,nx,ny,nz);
    for (i=1;i<N;i++){
        rotate(nx,ny,nz, nx,ny,nz, PI/N, 0,1,0);
        mcdis_Circle(x,y,z,r,nx,ny,nz);
    }
    /*lastly draw a great circle perpendicular to all N circles*/
    //mcdis_Circle(x,y,z,radius,1,0,0);

    for (i=1;i<=N;i++){
        double yy=-r+ 2*r*((double)i/(N+1));
        mcdis_Circle(x,y+yy ,z,  sqrt(r*r-yy*yy) ,0,1,0);
    }
}

/* SECTION: coordinates handling ============================================ */

/*******************************************************************************
* Since we use a lot of geometric calculations using Cartesian coordinates,
* we collect some useful routines here. However, it is also permissible to
* work directly on the underlying struct coords whenever that is most
* convenient (that is, the type Coords is not abstract).
*
* Coordinates are also used to store rotation angles around x/y/z axis.
*
* Since coordinates are used much like a basic type (such as double), the
* structure itself is passed and returned, rather than a pointer.
*
* At compile-time, the values of the coordinates may be unknown (for example
* a motor position). Hence coordinates are general expressions and not simple
* numbers. For this we used the type Coords_exp which has three CExp
* fields. For runtime (or calculations possible at compile time), we use
* Coords which contains three double fields.
*******************************************************************************/

/* coords_set: Assign coordinates. */
Coords coords_set(MCNUM x, MCNUM y, MCNUM z)
{
  Coords a;

  a.x = x;
  a.y = y;
  a.z = z;
  return a;
}

/* coords_get: get coordinates. Required when 'x','y','z' are #defined as ray pars */
Coords coords_get(Coords a, MCNUM *x, MCNUM *y, MCNUM *z)
{
  *x = a.x;
  *y = a.y;
  *z = a.z;
  return a;
}

/* coords_add: Add two coordinates. */
Coords coords_add(Coords a, Coords b)
{
  Coords c;

  c.x = a.x + b.x;
  c.y = a.y + b.y;
  c.z = a.z + b.z;
  if (fabs(c.z) < 1e-14) c.z=0.0;
  return c;
}

/* coords_sub: Subtract two coordinates. */
Coords coords_sub(Coords a, Coords b)
{
  Coords c;

  c.x = a.x - b.x;
  c.y = a.y - b.y;
  c.z = a.z - b.z;
  if (fabs(c.z) < 1e-14) c.z=0.0;
  return c;
}

/* coords_neg: Negate coordinates. */
Coords coords_neg(Coords a)
{
  Coords b;

  b.x = -a.x;
  b.y = -a.y;
  b.z = -a.z;
  return b;
}

/* coords_scale: Scale a vector. */
Coords coords_scale(Coords b, double scale) {
  Coords a;

  a.x = b.x*scale;
  a.y = b.y*scale;
  a.z = b.z*scale;
  return a;
}

/* coords_sp: Scalar product: a . b */
double coords_sp(Coords a, Coords b) {
  double value;

  value = a.x*b.x + a.y*b.y + a.z*b.z;
  return value;
}

/* coords_xp: Cross product: a = b x c. */
Coords coords_xp(Coords b, Coords c) {
  Coords a;

  a.x = b.y*c.z - c.y*b.z;
  a.y = b.z*c.x - c.z*b.x;
  a.z = b.x*c.y - c.x*b.y;
  return a;
}

/* coords_len: Gives length of coords set. */
double coords_len(Coords a) {
  return sqrt(a.x*a.x + a.y*a.y + a.z*a.z);
}

/* coords_mirror: Mirror a in plane (through the origin) defined by normal n*/
Coords coords_mirror(Coords a, Coords n) {
  double t = scalar_prod(n.x, n.y, n.z, n.x, n.y, n.z);
  Coords b;
  if (t!=1) {
    t = sqrt(t);
    n.x /= t;
    n.y /= t;
    n.z /= t;
  }
  t=scalar_prod(a.x, a.y, a.z, n.x, n.y, n.z);
  b.x = a.x-2*t*n.x;
  b.y = a.y-2*t*n.y;
  b.z = a.z-2*t*n.z;
  return b;
}

/* coords_print: Print out vector values. */
void coords_print(Coords a) {
  #ifndef OPENACC
  fprintf(stdout, "(%f, %f, %f)\n", a.x, a.y, a.z);
  #endif
  return;
}

mcstatic void coords_norm(Coords* c) {
	double temp = coords_sp(*c,*c);

	// Skip if we will end dividing by zero
	if (temp == 0) return;

	temp = sqrt(temp);

	c->x /= temp;
	c->y /= temp;
	c->z /= temp;
}

/* coords_test_zero: check if zero vector*/
int coords_test_zero(Coords a){
  return ( a.x==0 && a.y==0 && a.z==0 );
}

/*******************************************************************************
* The Rotation type implements a rotation transformation of a coordinate
* system in the form of a double[3][3] matrix.
*
* Contrary to the Coords type in coords.c, rotations are passed by
* reference. Functions that yield new rotations do so by writing to an
* explicit result parameter; rotations are not returned from functions. The
* reason for this is that arrays cannot by returned from functions (though
* structures can; thus an alternative would have been to wrap the
* double[3][3] array up in a struct). Such are the ways of C programming.
*
* A rotation represents the tranformation of the coordinates of a vector when
* changing between coordinate systems that are rotated with respect to each
* other. For example, suppose that coordinate system Q is rotated 45 degrees
* around the Z axis with respect to coordinate system P. Let T be the
* rotation transformation representing a 45 degree rotation around Z. Then to
* get the coordinates of a vector r in system Q, apply T to the coordinates
* of r in P. If r=(1,0,0) in P, it will be (sqrt(1/2),-sqrt(1/2),0) in
* Q. Thus we should be careful when interpreting the sign of rotation angles:
* they represent the rotation of the coordinate systems, not of the
* coordinates (which has opposite sign).
*******************************************************************************/

/*******************************************************************************
* rot_set_rotation: Get transformation for rotation first phx around x axis,
* then phy around y, then phz around z.
*******************************************************************************/
void rot_set_rotation(Rotation t, double phx, double phy, double phz)
{
  if ((phx == 0) && (phy == 0) && (phz == 0)) {
    t[0][0] = 1.0;
    t[0][1] = 0.0;
    t[0][2] = 0.0;
    t[1][0] = 0.0;
    t[1][1] = 1.0;
    t[1][2] = 0.0;
    t[2][0] = 0.0;
    t[2][1] = 0.0;
    t[2][2] = 1.0;
  } else {
    double cx = cos(phx);
    double sx = sin(phx);
    double cy = cos(phy);
    double sy = sin(phy);
    double cz = cos(phz);
    double sz = sin(phz);

    t[0][0] = cy*cz;
    t[0][1] = sx*sy*cz + cx*sz;
    t[0][2] = sx*sz - cx*sy*cz;
    t[1][0] = -cy*sz;
    t[1][1] = cx*cz - sx*sy*sz;
    t[1][2] = sx*cz + cx*sy*sz;
    t[2][0] = sy;
    t[2][1] = -sx*cy;
    t[2][2] = cx*cy;
  }
}

/*******************************************************************************
* rot_test_identity: Test if rotation is identity
*******************************************************************************/
int rot_test_identity(Rotation t)
{
  return (t[0][0] + t[1][1] + t[2][2] == 3);
}

/*******************************************************************************
* rot_mul: Matrix multiplication of transformations (this corresponds to
* combining transformations). After rot_mul(T1, T2, T3), doing T3 is
* equal to doing first T2, then T1.
* Note that T3 must not alias (use the same array as) T1 or T2.
*******************************************************************************/
void rot_mul(Rotation t1, Rotation t2, Rotation t3)
{
  if (rot_test_identity(t1)) {
    rot_copy(t3, t2);
  } else if (rot_test_identity(t2)) {
    rot_copy(t3, t1);
  } else {
    int i,j;
    for(i = 0; i < 3; i++)
      for(j = 0; j < 3; j++)
	t3[i][j] = t1[i][0]*t2[0][j] + t1[i][1]*t2[1][j] + t1[i][2]*t2[2][j];
  }
}

/*******************************************************************************
* rot_copy: Copy a rotation transformation (arrays cannot be assigned in C).
*******************************************************************************/
void rot_copy(Rotation dest, Rotation src)
{
  int i,j;
  for(i = 0; i < 3; i++)
    for(j = 0; j < 3; j++)
      dest[i][j] = src[i][j];
}

/*******************************************************************************
* rot_transpose: Matrix transposition, which is inversion for Rotation matrices
*******************************************************************************/
void rot_transpose(Rotation src, Rotation dst)
{
  dst[0][0] = src[0][0];
  dst[0][1] = src[1][0];
  dst[0][2] = src[2][0];
  dst[1][0] = src[0][1];
  dst[1][1] = src[1][1];
  dst[1][2] = src[2][1];
  dst[2][0] = src[0][2];
  dst[2][1] = src[1][2];
  dst[2][2] = src[2][2];
}

/*******************************************************************************
* rot_apply: returns t*a
*******************************************************************************/
Coords rot_apply(Rotation t, Coords a)
{
  Coords b;
  if (rot_test_identity(t)) {
    return a;
  } else {
    b.x = t[0][0]*a.x + t[0][1]*a.y + t[0][2]*a.z;
    b.y = t[1][0]*a.x + t[1][1]*a.y + t[1][2]*a.z;
    b.z = t[2][0]*a.x + t[2][1]*a.y + t[2][2]*a.z;
    return b;
  }
}

/**
 * Pretty-printing of rotation matrices.
 */
void rot_print(Rotation rot) {
	printf("[ %4.2f %4.2f %4.2f ]\n",
			rot[0][0], rot[0][1], rot[0][2]);
	printf("[ %4.2f %4.2f %4.2f ]\n",
			rot[1][0], rot[1][1], rot[1][2]);
	printf("[ %4.2f %4.2f %4.2f ]\n\n",
			rot[2][0], rot[2][1], rot[2][2]);
}

/**
 * Vector product: used by vec_prod (mccode-r.h). Use coords_xp for Coords.
 */
void vec_prod_func(double *x, double *y, double *z,
		double x1, double y1, double z1,
		double x2, double y2, double z2) {
    *x = (y1)*(z2) - (y2)*(z1);
    *y = (z1)*(x2) - (z2)*(x1);
    *z = (x1)*(y2) - (x2)*(y1);
}

/**
 * Scalar product: use coords_sp for Coords.
 */
double scalar_prod(
		double x1, double y1, double z1,
		double x2, double y2, double z2) {
	return ((x1 * x2) + (y1 * y2) + (z1 * z2));
}

mcstatic void norm_func(double *x, double *y, double *z) {
	double temp = (*x * *x) + (*y * *y) + (*z * *z);
	if (temp != 0) {
		temp = sqrt(temp);
		*x /= temp;
		*y /= temp;
		*z /= temp;
	}
}


/* SECTION: GPU algorithms ================================================== */


/*
*  Divide-and-conquer strategy for parallelizing this task: Sort absorbed
*  particles last.
*
*   particles:  the particle array, required to checking _absorbed
*   pbuffer:    same-size particle buffer array required for parallel sort
*   len:        sorting area-of-interest size (e.g. from previous calls)
*   buffer_len: total array size
*   flag_split: if set, multiply live particles into absorbed slots, up to buffer_len
*   multiplier: output arg, becomes the  SPLIT multiplier if flag_split is set
*/
#ifdef FUNNEL
long sort_absorb_last(_class_particle* particles, _class_particle* pbuffer, long len, long buffer_len, long flag_split, long* multiplier) {
  #define SAL_THREADS 1024 // num parallel sections
  if (len<SAL_THREADS) return sort_absorb_last_serial(particles, len);

  if (multiplier != NULL) *multiplier = -1; // set default out value for multiplier
  long newlen = 0;
  long los[SAL_THREADS]; // target array startidxs
  long lens[SAL_THREADS]; // target array sublens
  long l = floor(len/(SAL_THREADS-1)); // subproblem_len
  long ll = len - l*(SAL_THREADS-1); // last_subproblem_len

  // TODO: The l vs ll is too simplistic, since ll can become much larger
  // than l, resulting in idling. We should distribute lengths more evenly.

  // step 1: sort sub-arrays
  #pragma acc parallel loop present(particles, pbuffer)
  for (unsigned long tidx=0; tidx<SAL_THREADS; tidx++) {
    long lo = l*tidx;
    long loclen = l;
    if (tidx==(SAL_THREADS-1)) loclen = ll; // last sub-problem special case
    long i = lo;
    long j = lo + loclen - 1;

    // write into pbuffer at i and j
    #pragma acc loop seq
    while (i < j) {
      #pragma acc loop seq
      while (!particles[i]._absorbed && i<j) {
        pbuffer[i] = particles[i];
        i++;
      }
      #pragma acc loop seq
      while (particles[j]._absorbed && i<j) {
        pbuffer[j] = particles[j];
        j--;
      }
      if (i < j) {
        pbuffer[j] = particles[i];
        pbuffer[i] = particles[j];
        i++;
        j--;
      }
    }
    // transfer edge case
    if (i==j)
      pbuffer[i] = particles[i];

    lens[tidx] = i - lo;
    if (i==j && !particles[i]._absorbed) lens[tidx]++;
  }

  // determine lo's
  long accumlen = 0;
  #pragma acc loop seq
  for (long idx=0; idx<SAL_THREADS; idx++) {
    los[idx] = accumlen;
    accumlen = accumlen + lens[idx];
  }

  // step 2: write non-absorbed sub-arrays to psorted/output from the left
  #pragma acc parallel loop present(pbuffer)
  for (unsigned long tidx=0; tidx<SAL_THREADS; tidx++) {
    long j, k;
    #pragma acc loop seq
    for (long i=0; i<lens[tidx]; i++) {
      j = i + l*tidx;
      k = i + los[tidx];
      particles[k] = pbuffer[j];
    }
  }
  //for (int ii=0;ii<accumlen;ii++) printf("%ld ", (psorted[ii]->_absorbed));

  // return (no SPLIT)
  if (flag_split != 1)
    return accumlen;

  // SPLIT - repeat the non-absorbed block N-1 times, where len % accumlen = N + R
  int mult = buffer_len / accumlen; // TODO: possibly use a new arg, bufferlen, rather than len

  // not enough space for full-block split, return
  if (mult <= 1)
    return accumlen;

  // copy non-absorbed block
  #pragma acc parallel loop present(particles)
  for (long tidx = 0; tidx < accumlen; tidx++) { // tidx: thread index
    unsigned long randstate[7];
    _class_particle sourcebuffer;
    _class_particle targetbuffer;
    // assign reduced weight to all particles
    particles[tidx].p=particles[tidx].p/mult;
    #pragma acc loop seq
    for (long bidx = 1; bidx < mult; bidx++) { // bidx: block index
      // preserve absorbed particle (for randstate)
      sourcebuffer = particles[bidx*accumlen + tidx];
      // buffer full particle struct
      targetbuffer = particles[tidx];
      // reassign previous randstate
      targetbuffer.randstate[0] = sourcebuffer.randstate[0];
      targetbuffer.randstate[1] = sourcebuffer.randstate[1];
      targetbuffer.randstate[2] = sourcebuffer.randstate[2];
      targetbuffer.randstate[3] = sourcebuffer.randstate[3];
      targetbuffer.randstate[4] = sourcebuffer.randstate[4];
      targetbuffer.randstate[5] = sourcebuffer.randstate[5];
      targetbuffer.randstate[6] = sourcebuffer.randstate[6];
      // apply
      particles[bidx*accumlen + tidx] = targetbuffer;
    }
  }

  // set out split multiplier value
  *multiplier = mult;

  // return expanded array size
  return accumlen * mult;
}

#endif

/*
*  Fallback serial version of the one above.
*/
long sort_absorb_last_serial(_class_particle* particles, long len) {
  long i = 0;
  long j = len - 1;
  _class_particle pbuffer;

  // bubble
  while (i < j) {
    while (!particles[i]._absorbed && i<j) i++;
    while (particles[j]._absorbed && i<j) j--;
    if (i < j) {
      pbuffer = particles[j];
      particles[j] = particles[i];
      particles[i] = pbuffer;
      i++;
      j--;
    }
  }

  // return new length
  if (i==j && !particles[i]._absorbed)
    return i + 1;
  else
    return i;
}

/*******************************************************************************
* mccoordschange: applies rotation to (x y z) and (vx vy vz) and Spin (sx,sy,sz)
*******************************************************************************/
void mccoordschange(Coords a, Rotation t, _class_particle *particle)
{
  Coords b, c;

  b.x = particle->x;
  b.y = particle->y;
  b.z = particle->z;
  c = rot_apply(t, b);
  b = coords_add(c, a);
  particle->x = b.x;
  particle->y = b.y;
  particle->z = b.z;

#if MCCODE_PARTICLE_CODE == 2112
    if (particle->vz != 0.0 || particle->vx != 0.0 || particle->vy != 0.0)
      mccoordschange_polarisation(t, &(particle->vx), &(particle->vy), &(particle->vz));

    if (particle->sz != 0.0 || particle->sx != 0.0 || particle->sy != 0.0)
      mccoordschange_polarisation(t, &(particle->sx), &(particle->sy), &(particle->sz));
#elif MCCODE_PARTICLE_CODE == 22
    if (particle->kz != 0.0 || particle->kx != 0.0 || particle->ky != 0.0)
      mccoordschange_polarisation(t, &(particle->kx), &(particle->ky), &(particle->kz));

    if (particle->Ez != 0.0 || particle->Ex != 0.0 || particle->Ey != 0.0)
      mccoordschange_polarisation(t, &(particle->Ex), &(particle->Ey), &(particle->Ez));
#endif
}

/*******************************************************************************
* mccoordschange_polarisation: applies rotation to vector (sx sy sz)
*******************************************************************************/
void mccoordschange_polarisation(Rotation t, double *sx, double *sy, double *sz)
{
  Coords b, c;

  b.x = *sx;
  b.y = *sy;
  b.z = *sz;
  c = rot_apply(t, b);
  *sx = c.x;
  *sy = c.y;
  *sz = c.z;
}

/* SECTION: vector math  ==================================================== */

/* normal_vec_func: Compute normal vector to (x,y,z). */
void normal_vec(double *nx, double *ny, double *nz,
                double x, double y, double z)
{
  double ax = fabs(x);
  double ay = fabs(y);
  double az = fabs(z);
  double l;
  if(x == 0 && y == 0 && z == 0)
  {
    *nx = 0;
    *ny = 0;
    *nz = 0;
    return;
  }
  if(ax < ay)
  {
    if(ax < az)
    {                           /* Use X axis */
      l = sqrt(z*z + y*y);
      *nx = 0;
      *ny = z/l;
      *nz = -y/l;
      return;
    }
  }
  else
  {
    if(ay < az)
    {                           /* Use Y axis */
      l = sqrt(z*z + x*x);
      *nx = z/l;
      *ny = 0;
      *nz = -x/l;
      return;
    }
  }
  /* Use Z axis */
  l = sqrt(y*y + x*x);
  *nx = y/l;
  *ny = -x/l;
  *nz = 0;
} /* normal_vec */

/*******************************************************************************
 * solve_2nd_order: second order equation solve: A*t^2 + B*t + C = 0
 * solve_2nd_order(&t1, NULL, A,B,C)
 *   returns 0 if no solution was found, or set 't1' to the smallest positive
 *   solution.
 * solve_2nd_order(&t1, &t2, A,B,C)
 *   same as with &t2=NULL, but also returns the second solution.
 * EXAMPLE usage for intersection of a trajectory with a plane in gravitation
 * field (gx,gy,gz):
 * The neutron starts at point r=(x,y,z) with velocityv=(vx vy vz). The plane
 * has a normal vector n=(nx,ny,nz) and contains the point W=(wx,wy,wz).
 * The problem consists in solving the 2nd order equation:
 *      1/2.n.g.t^2 + n.v.t + n.(r-W) = 0
 * so that A = 0.5 n.g; B = n.v; C = n.(r-W);
 * Without acceleration, t=-n.(r-W)/n.v
 ******************************************************************************/
int solve_2nd_order_old(double *t1, double *t2,
                  double A,  double B,  double C)
{
  int ret=0;

  if (!t1) return 0;
  *t1 = 0;
  if (t2) *t2=0;

  if (fabs(A) < 1E-10) /* approximate to linear equation: A ~ 0 */
  {
    if (B) {  *t1 = -C/B; ret=1; if (t2) *t2=*t1; }
    /* else no intersection: A=B=0 ret=0 */
  }
  else
  {
    double D;
    D = B*B - 4*A*C;
    if (D >= 0) /* Delta > 0: two solutions */
    {
      double sD, dt1, dt2;
      sD = sqrt(D);
      dt1 = (-B + sD)/2/A;
      dt2 = (-B - sD)/2/A;
      /* we identify very small values with zero */
      if (fabs(dt1) < 1e-10) dt1=0.0;
      if (fabs(dt2) < 1e-10) dt2=0.0;

      /* now we choose the smallest positive solution */
      if      (dt1<=0.0 && dt2>0.0) ret=2; /* dt2 positive */
      else if (dt2<=0.0 && dt1>0.0) ret=1; /* dt1 positive */
      else if (dt1> 0.0 && dt2>0.0)
      {  if (dt1 < dt2) ret=1; else ret=2; } /* all positive: min(dt1,dt2) */
      /* else two solutions are negative. ret=-1 */
      if (ret==1) { *t1 = dt1;  if (t2) *t2=dt2; }
      else        { *t1 = dt2;  if (t2) *t2=dt1; }
      ret=2;  /* found 2 solutions and t1 is the positive one */
    } /* else Delta <0: no intersection. ret=0 */
  }
  return(ret);
} /* solve_2nd_order */

int solve_2nd_order(double *t0, double *t1, double A, double B, double C){
  int retval=0;
  double sign=copysign(1.0,B);
  double dt0,dt1;

  dt0=0;
  dt1=0;
  if(t1){ *t1=0;}

  /*protect against rounding errors by locally equating DBL_EPSILON with 0*/
  if (fabs(A)<DBL_EPSILON){
    A=0;
  }
  if (fabs(B)<DBL_EPSILON){
    B=0;
  }
  if (fabs(C)<DBL_EPSILON){
    C=0;
  }

  /*check if coefficient are sane*/
  if( A==0  && B==0){
    retval=0;
  }else{
    if(A==0){
      /*equation is linear*/
      dt0=-C/B;
      retval=1;
    }else if (C==0){
      /*one root is 0*/
      if(sign<0){
        dt0=0;dt1=-B/A;
      }else{
        dt0=-B/A;dt1=0;
      }
      retval=2;
    }else{
      /*a regular 2nd order eq. Also works out fine for B==0.*/
      double D;
      D=B*B-4*A*C;
      if (D>=0){
        dt0=(-B - sign*sqrt(B*B-4*A*C))/(2*A);
        dt1=C/(A*dt0);
        retval=2;
      }else{
        /*no real roots*/
        retval=0;
      }
    }
    /*sort the solutions*/
    if (retval==1){
      /*put both solutions in t0 and t1*/
      *t0=dt0;
      if(t1) *t1=dt1;
    }else{
      /*we have two solutions*/
      /*swap if both are positive and t1 smaller than t0 or t1 the only positive*/
      int swap=0;
      if(dt1>0 && ( dt1<dt0 || dt0<=0) ){
        swap=1;
      }
      if (swap){
        *t0=dt1;
        if(t1) *t1=dt0;
      }else{
        *t0=dt0;
        if(t1) *t1=dt0;
      }
    }

  }
  return retval;

} /*solve_2nd_order_improved*/


/*******************************************************************************
 * randvec_target_circle: Choose random direction towards target at (x,y,z)
 * with given radius.
 * If radius is zero, choose random direction in full 4PI, no target.
 ******************************************************************************/
void _randvec_target_circle(double *xo, double *yo, double *zo, double *solid_angle,
        double xi, double yi, double zi, double radius,
        _class_particle* _particle)
{
  double l2, phi, theta, nx, ny, nz, xt, yt, zt, xu, yu, zu;

  if(radius == 0.0)
  {
    /* No target, choose uniformly a direction in full 4PI solid angle. */
    theta = acos(1 - rand0max(2));
    phi = rand0max(2 * PI);
    if(solid_angle)
      *solid_angle = 4*PI;
    nx = 1;
    ny = 0;
    nz = 0;
    yi = sqrt(xi*xi+yi*yi+zi*zi);
    zi = 0;
    xi = 0;
  }
  else
  {
    double costheta0;
    l2 = xi*xi + yi*yi + zi*zi; /* sqr Distance to target. */
    costheta0 = sqrt(l2/(radius*radius+l2));
    if (radius < 0) costheta0 *= -1;
    if(solid_angle)
    {
      /* Compute solid angle of target as seen from origin. */
        *solid_angle = 2*PI*(1 - costheta0);
    }

    /* Now choose point uniformly on circle surface within angle theta0 */
    theta = acos (1 - rand0max(1 - costheta0)); /* radius on circle */
    phi = rand0max(2 * PI); /* rotation on circle at given radius */
    /* Now, to obtain the desired vector rotate (xi,yi,zi) angle theta around a
       perpendicular axis u=i x n and then angle phi around i. */
    if(xi == 0 && zi == 0)
    {
      nx = 1;
      ny = 0;
      nz = 0;
    }
    else
    {
      nx = -zi;
      nz = xi;
      ny = 0;
    }
  }

  /* [xyz]u = [xyz]i x n[xyz] (usually vertical) */
  vec_prod(xu,  yu,  zu, xi, yi, zi,        nx, ny, nz);
  /* [xyz]t = [xyz]i rotated theta around [xyz]u */
  rotate  (xt,  yt,  zt, xi, yi, zi, theta, xu, yu, zu);
  /* [xyz]o = [xyz]t rotated phi around n[xyz] */
  rotate (*xo, *yo, *zo, xt, yt, zt, phi, xi, yi, zi);
}
/* randvec_target_circle */

/*******************************************************************************
 * randvec_target_rect_angular: Choose random direction towards target at
 * (xi,yi,zi) with given ANGULAR dimension height x width. height=phi_x=[0,PI],
 * width=phi_y=[0,2*PI] (radians)
 * If height or width is zero, choose random direction in full 4PI, no target.
 *******************************************************************************/
void _randvec_target_rect_angular(double *xo, double *yo, double *zo, double *solid_angle,
        double xi, double yi, double zi, double width, double height, Rotation A,
        _class_particle* _particle)
{
  double theta, phi, nx, ny, nz, xt, yt, zt, xu, yu, zu;
  Coords tmp;
  Rotation Ainverse;

  rot_transpose(A, Ainverse);

  if(height == 0.0 || width == 0.0)
  {
    randvec_target_circle(xo, yo, zo, solid_angle, xi, yi, zi, 0);
    return;
  }
  else
  {
    if(solid_angle)
    {
      /* Compute solid angle of target as seen from origin. */
      *solid_angle = 2*fabs(width*sin(height/2));
    }

    /* Go to global coordinate system */

    tmp = coords_set(xi, yi, zi);
    tmp = rot_apply(Ainverse, tmp);
    coords_get(tmp, &xi, &yi, &zi);

    /* Now choose point uniformly on the unit sphere segment with angle theta/phi */
    phi   = width*randpm1()/2.0;
    theta = asin(randpm1()*sin(height/2.0));
    /* Now, to obtain the desired vector rotate (xi,yi,zi) angle theta around
       n, and then phi around u. */
    if(xi == 0 && zi == 0)
    {
      nx = 1;
      ny = 0;
      nz = 0;
    }
    else
    {
      nx = -zi;
      nz = xi;
      ny = 0;
    }
  }

  /* [xyz]u = [xyz]i x n[xyz] (usually vertical) */
  vec_prod(xu,  yu,  zu, xi, yi, zi,        nx, ny, nz);
  /* [xyz]t = [xyz]i rotated theta around [xyz]u */
  rotate  (xt,  yt,  zt, xi, yi, zi, theta, nx, ny, nz);
  /* [xyz]o = [xyz]t rotated phi around n[xyz] */
  rotate (*xo, *yo, *zo, xt, yt, zt, phi, xu,  yu,  zu);

  /* Go back to local coordinate system */
  tmp = coords_set(*xo, *yo, *zo);
  tmp = rot_apply(A, tmp);
  coords_get(tmp, &*xo, &*yo, &*zo);
}
/* randvec_target_rect_angular */

/*******************************************************************************
 * randvec_target_rect_real: Choose random direction towards target at (xi,yi,zi)
 * with given dimension height x width (in meters !).
 *
 * Local emission coordinate is taken into account and corrected for 'order' times.
 * (See remarks posted to mcstas-users by George Apostolopoulus <gapost@ipta.demokritos.gr>)
 *
 * If height or width is zero, choose random direction in full 4PI, no target.
 *
 * Traditionally, this routine had the name randvec_target_rect - this is now a
 * a define (see mcstas-r.h) pointing here. If you use the old rouine, you are NOT
 * taking the local emmission coordinate into account.
*******************************************************************************/
void _randvec_target_rect_real(double *xo, double *yo, double *zo, double *solid_angle,
        double xi, double yi, double zi,
        double width, double height, Rotation A,
        double lx, double ly, double lz, int order,
        _class_particle* _particle)
{
  double dx, dy, dist, dist_p, nx, ny, nz, mx, my, mz, n_norm, m_norm;
  double cos_theta;
  Coords tmp;
  Rotation Ainverse;

  rot_transpose(A, Ainverse);

  if(height == 0.0 || width == 0.0)
  {
    randvec_target_circle(xo, yo, zo, solid_angle,
               xi, yi, zi, 0);
    return;
  }
  else
  {
    /* Now choose point uniformly on rectangle within width x height */
    dx = width*randpm1()/2.0;
    dy = height*randpm1()/2.0;

    /* Determine distance to target plane*/
    dist = sqrt(xi*xi + yi*yi + zi*zi);
    /* Go to global coordinate system */

    tmp = coords_set(xi, yi, zi);
    tmp = rot_apply(Ainverse, tmp);
    coords_get(tmp, &xi, &yi, &zi);

    /* Determine vector normal to trajectory axis (z) and gravity [0 1 0] */
    vec_prod(nx, ny, nz, xi, yi, zi, 0, 1, 0);

    /* This now defines the x-axis, normalize: */
    n_norm=sqrt(nx*nx + ny*ny + nz*nz);
    nx = nx/n_norm;
    ny = ny/n_norm;
    nz = nz/n_norm;

    /* Now, determine our y-axis (vertical in many cases...) */
    vec_prod(mx, my, mz, xi, yi, zi, nx, ny, nz);
    m_norm=sqrt(mx*mx + my*my + mz*mz);
    mx = mx/m_norm;
    my = my/m_norm;
    mz = mz/m_norm;

    /* Our output, random vector can now be defined by linear combination: */

    *xo = xi + dx * nx + dy * mx;
    *yo = yi + dx * ny + dy * my;
    *zo = zi + dx * nz + dy * mz;

    /* Go back to local coordinate system */
    tmp = coords_set(*xo, *yo, *zo);
    tmp = rot_apply(A, tmp);
    coords_get(tmp, &*xo, &*yo, &*zo);

    /* Go back to local coordinate system */
    tmp = coords_set(xi, yi, zi);
    tmp = rot_apply(A, tmp);
    coords_get(tmp, &xi, &yi, &zi);

    if (solid_angle) {
      /* Calculate vector from local point to remote random point */
      lx = *xo - lx;
      ly = *yo - ly;
      lz = *zo - lz;
      dist_p = sqrt(lx*lx + ly*ly + lz*lz);

      /* Adjust the 'solid angle' */
      /* 1/r^2 to the chosen point times cos(\theta) between the normal */
      /* vector of the target rectangle and direction vector of the chosen point. */
      cos_theta = (xi * lx + yi * ly + zi * lz) / (dist * dist_p);
      *solid_angle = width * height / (dist_p * dist_p);
      int counter;
      for (counter = 0; counter < order; counter++) {
        *solid_angle = *solid_angle * cos_theta;
      }
    }
  }
}
/* randvec_target_rect_real */


/* SECTION: random numbers ==================================================

  How to add a new RNG:

  - Use an rng with a manegable state vector, e.g. of lengt 4 or 7. The state
  will sit on the particle struct as a "randstate_t state[RANDSTATE_LEN]"
  - If the rng has a long state (as MT), set an empty "srandom" and initialize
  it explicitly using the appropriate define (RNG_ALG)
  - Add a seed and a random function (the transforms will be reused)
  - Write the proper defines in mccode-r.h, e.g. randstate_t and RANDSTATE_LEN,
  srandom and random.
  - Compile using -DRNG_ALG=<selector int value>

============================================================================= */


/* "Mersenne Twister", by Makoto Matsumoto and Takuji Nishimura. */
/* See http://www.math.keio.ac.jp/~matumoto/emt.html for original source. */
/*
   A C-program for MT19937, with initialization improved 2002/1/26.
   Coded by Takuji Nishimura and Makoto Matsumoto.

   Before using, initialize the state by using mt_srandom(seed)
   or init_by_array(init_key, key_length).

   Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
   All rights reserved.

   Redistribution and use in source and binary forms, with or without
   modification, are permitted provided that the following conditions
   are met:

     1. Redistributions of source code must retain the above copyright
        notice, this list of conditions and the following disclaimer.

     2. Redistributions in binary form must reproduce the above copyright
        notice, this list of conditions and the following disclaimer in the
        documentation and/or other materials provided with the distribution.

     3. The names of its contributors may not be used to endorse or promote
        products derived from this software without specific prior written
        permission.

   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
   A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


   Any feedback is very welcome.
   http://www.math.keio.ac.jp/matumoto/emt.html
   email: matumoto@math.keio.ac.jp
*/
#include <stdio.h>
/* Period parameters */
#define N 624
#define M 397
#define MATRIX_A 0x9908b0dfUL   /* constant vector a */
#define UPPER_MASK 0x80000000UL /* most significant w-r bits */
#define LOWER_MASK 0x7fffffffUL /* least significant r bits */

unsigned long mt[N]; /* the array for the state vector  */
int mti=N+1; /* mti==N+1 means mt[N] is not initialized */

// required for common rng alg interface (see RNG_ALG usage in mccode-r.h)
void mt_srandom_empty() {}

// initializes mt[N] with a seed
void mt_srandom(unsigned long s)
{
    mt[0]= s & 0xffffffffUL;
    for (mti=1; mti<N; mti++) {
        mt[mti] =
            (1812433253UL * (mt[mti-1] ^ (mt[mti-1] >> 30)) + mti);
        /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */
        /* In the previous versions, MSBs of the seed affect   */
        /* only MSBs of the array mt[].                        */
        /* 2002/01/09 modified by Makoto Matsumoto             */
        mt[mti] &= 0xffffffffUL;
        /* for >32 bit machines */
    }
}
/* Initialize by an array with array-length.
   Init_key is the array for initializing keys.
   key_length is its length. */
void init_by_array(unsigned long init_key[], unsigned long key_length)
{
    int i, j, k;
    mt_srandom(19650218UL);
    i=1; j=0;
    k = (N>key_length ? N : key_length);
    for (; k; k--) {
        mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1664525UL))
          + init_key[j] + j; /* non linear */
        mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */
        i++; j++;
        if (i>=N) { mt[0] = mt[N-1]; i=1; }
        if (j>=key_length) j=0;
    }
    for (k=N-1; k; k--) {
        mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1566083941UL))
          - i; /* non linear */
        mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */
        i++;
        if (i>=N) { mt[0] = mt[N-1]; i=1; }
    }

    mt[0] = 0x80000000UL; /* MSB is 1; assuring non-zero initial array */
}
/* generates a random number on [0,0xffffffff]-interval */
unsigned long mt_random(void)
{
    unsigned long y;
    unsigned long mag01[2]={0x0UL, MATRIX_A};
    /* mag01[x] = x * MATRIX_A  for x=0,1 */

    if (mti >= N) { /* generate N words at one time */
        int kk;

        if (mti == N+1)   /* if mt_srandom() has not been called, */
            mt_srandom(5489UL); /* a default initial seed is used */

        for (kk=0;kk<N-M;kk++) {
            y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
            mt[kk] = mt[kk+M] ^ (y >> 1) ^ mag01[y & 0x1UL];
        }
        for (;kk<N-1;kk++) {
            y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
            mt[kk] = mt[kk+(M-N)] ^ (y >> 1) ^ mag01[y & 0x1UL];
        }
        y = (mt[N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK);
        mt[N-1] = mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1UL];

        mti = 0;
    }

    y = mt[mti++];

    /* Tempering */
    y ^= (y >> 11);
    y ^= (y << 7) & 0x9d2c5680UL;
    y ^= (y << 15) & 0xefc60000UL;
    y ^= (y >> 18);

    return y;
}
#undef N
#undef M
#undef MATRIX_A
#undef UPPER_MASK
#undef LOWER_MASK
/* End of "Mersenne Twister". */


/*
KISS

 From: http://www.helsbreth.org/random/rng_kiss.html
 Scott Nelson 1999

 Based on Marsaglia's KISS or (KISS+SWB) <http://www.cs.yorku.ca/~oz/marsaglia-
rng.html>

 KISS - Keep it Simple Stupid PRNG

 the idea is to use simple, fast, individually promising
 generators to get a composite that will be fast, easy to code
 have a very long period and pass all the tests put to it.
 The three components of KISS are
        x(n)=a*x(n-1)+1 mod 2^32
        y(n)=y(n-1)(I+L^13)(I+R^17)(I+L^5),
        z(n)=2*z(n-1)+z(n-2) +carry mod 2^32
 The y's are a shift register sequence on 32bit binary vectors
 period 2^32-1;
 The z's are a simple multiply-with-carry sequence with period
 2^63+2^32-1.  The period of KISS is thus
      2^32*(2^32-1)*(2^63+2^32-1) > 2^127
*/

/* the KISS state is stored as a vector of 7 unsigned long        */
/*   0  1  2  3  4      5  6   */
/* [ x, y, z, w, carry, k, m ] */

unsigned long *kiss_srandom(unsigned long state[7], unsigned long seed) {
  if (seed == 0) seed = 1;
  state[0] = seed | 1; // x
  state[1] = seed | 2; // y
  state[2] = seed | 4; // z
  state[3] = seed | 8; // w
  state[4] = 0;        // carry
  return 0;
}

unsigned long kiss_random(unsigned long state[7]) {
    state[0] = state[0] * 69069 + 1;
    state[1] ^= state[1] << 13;
    state[1] ^= state[1] >> 17;
    state[1] ^= state[1] << 5;
    state[5] = (state[2] >> 2) + (state[3] >> 3) + (state[4] >> 2);
    state[6] = state[3] + state[3] + state[2] + state[4];
    state[2] = state[3];
    state[3] = state[6];
    state[4] = state[5] >> 30;
    return state[0] + state[1] + state[3];
}
/* end of "KISS" rng */


/* FAST KISS in another implementation (Hundt) */

//////////////////////////////////////////////////////////////////////////////
// fast keep it simple stupid generator
//////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////////////
// Thomas Mueller hash for initialization of rngs
// http://stackoverflow.com/questions/664014/
//        what-integer-hash-function-are-good-that-accepts-an-integer-hash-key
//////////////////////////////////////////////////////////////////////////////
randstate_t _hash(randstate_t x) {
  x = ((x >> 16) ^ x) * 0x45d9f3b;
  x = ((x >> 16) ^ x) * 0x45d9f3b;
  x = ((x >> 16) ^ x);
  return x;
}


// SECTION: random number transforms ==========================================



// generate a random number from normal law
double _randnorm(randstate_t* state)
{
  static double v1, v2, s; /* removing static breaks comparison with McStas <= 2.5 */
  static int phase = 0;
  double X, u1, u2;

  if(phase == 0)
  {
    do
    {
      u1 = _rand01(state);
      u2 = _rand01(state);
      v1 = 2*u1 - 1;
      v2 = 2*u2 - 1;
      s = v1*v1 + v2*v2;
    } while(s >= 1 || s == 0);

    X = v1*sqrt(-2*log(s)/s);
  }
  else
  {
    X = v2*sqrt(-2*log(s)/s);
  }

  phase = 1 - phase;
  return X;
}
// another one
double _randnorm2(randstate_t* state) {
  double x, y, r;
  do {
      x = 2.0 * _rand01(state) - 1.0;
      y = 2.0 * _rand01(state) - 1.0;
      r = x*x + y*y;
  } while (r == 0.0 || r >= 1.0);
  return x * sqrt((-2.0 * log(r)) / r);
}

// Generate a random number from -1 to 1 with triangle distribution
double _randtriangle(randstate_t* state) {
	double randnum = _rand01(state);
	if (randnum>0.5) return(1-sqrt(2*(randnum-0.5)));
	else return(sqrt(2*randnum)-1);
}
double _rand01(randstate_t* state) {
	double randnum;
	randnum = (double) _random();
  // TODO: can we mult instead of div?
	randnum /= (double) MC_RAND_MAX + 1;
	return randnum;
}
// Return a random number between 1 and -1
double _randpm1(randstate_t* state) {
	double randnum;
	randnum = (double) _random();
	randnum /= ((double) MC_RAND_MAX + 1) / 2;
	randnum -= 1;
	return randnum;
}
// Return a random number between 0 and max.
double _rand0max(double max, randstate_t* state) {
	double randnum;
	randnum = (double) _random();
	randnum /= ((double) MC_RAND_MAX + 1) / max;
	return randnum;
}
// Return a random number between min and max.
double _randminmax(double min, double max, randstate_t* state) {
	return _rand0max(max - min, state) + max;
}


/* SECTION: main and signal handlers ======================================== */

/*******************************************************************************
* mchelp: displays instrument executable help with possible options
*******************************************************************************/
static void
mchelp(char *pgmname)
{
  int i;

  fprintf(stderr, "%s (%s) instrument simulation, generated with " MCCODE_STRING " (" MCCODE_DATE ")\n", instrument_name, instrument_source);
  fprintf(stderr, "Usage: %s [options] [parm=value ...]\n", pgmname);
  fprintf(stderr,
"Options are:\n"
"  -s SEED   --seed=SEED      Set random seed (must be != 0)\n"
"  -n COUNT  --ncount=COUNT   Set number of particles to simulate.\n"
"  -d DIR    --dir=DIR        Put all data files in directory DIR.\n"
"  -t        --trace          Enable trace of " MCCODE_PARTICLE "s through instrument.\n"
"  -g        --gravitation    Enable gravitation for all trajectories.\n"
"  --no-output-files          Do not write any data files.\n"
"  -h        --help           Show this help message.\n"
"  -i        --info           Detailed instrument information.\n"
"  --source                   Show the instrument code which was compiled.\n"
#ifdef OPENACC
"\n"
"  --vecsize                  OpenACC vector-size (default: 128)\n"
"  --numgangs                 Number of OpenACC gangs (default: 7813)\n"
"  --gpu_innerloop            Maximum rays to process pr. OpenACC \n"
"                             kernel run (default: 2147483647)\n"
"\n"
#endif
"\n"
"  --bufsiz                   Monitor_nD list/buffer-size (default: 1000000)\n"
"  --format=FORMAT            Output data files using FORMAT="
   FLAVOR_UPPER
#ifdef USE_NEXUS
   " NEXUS"
#endif
"\n\n"
);
#ifdef USE_MPI
  fprintf(stderr,
  "This instrument has been compiled with MPI support.\n  Use 'mpirun %s [options] [parm=value ...]'.\n", pgmname);
#endif
#ifdef OPENACC
  fprintf(stderr,
  "This instrument has been compiled with NVIDIA GPU support through OpenACC.\n  Running on systems without such devices will lead to segfaults.\nFurter, fprintf, sprintf and printf have been removed from any component TRACE.\n");
#endif

  if(numipar > 0)
  {
    fprintf(stderr, "Instrument parameters are:\n");
    for(i = 0; i < numipar; i++)
      if (mcinputtable[i].val && strlen(mcinputtable[i].val))
        fprintf(stderr, "  %-16s(%s) [default='%s']\n", mcinputtable[i].name,
        (*mcinputtypes[mcinputtable[i].type].parminfo)(mcinputtable[i].name),
        mcinputtable[i].val);
      else
        fprintf(stderr, "  %-16s(%s)\n", mcinputtable[i].name,
        (*mcinputtypes[mcinputtable[i].type].parminfo)(mcinputtable[i].name));
  }

#ifndef NOSIGNALS
  fprintf(stderr, "Known signals are: "
#ifdef SIGUSR1
  "USR1 (status) "
#endif
#ifdef SIGUSR2
  "USR2 (save) "
#endif
#ifdef SIGBREAK
  "BREAK (save) "
#endif
#ifdef SIGTERM
  "TERM (save and exit)"
#endif
  "\n");
#endif /* !NOSIGNALS */
} /* mchelp */


/* mcshowhelp: show help and exit with 0 */
static void
mcshowhelp(char *pgmname)
{
  mchelp(pgmname);
  exit(0);
}

/* mcusage: display usage when error in input arguments and exit with 1 */
static void
mcusage(char *pgmname)
{
  fprintf(stderr, "Error: incorrect command line arguments\n");
  mchelp(pgmname);
  exit(1);
}

/* mcenabletrace: enable trace/mcdisplay or error if requires recompile */
static void
mcenabletrace(void)
{
 if(traceenabled) {
  mcdotrace = 1;
  #pragma acc update device ( mcdotrace )
 } else {
   fprintf(stderr,
           "Error: trace not enabled (mcenabletrace)\n"
           "Please re-run the " MCCODE_NAME " compiler "
                   "with the --trace option, or rerun the\n"
           "C compiler with the MC_TRACE_ENABLED macro defined.\n");
   exit(1);
 }
}

/*******************************************************************************
* mcreadparams: request parameters from the prompt (or use default)
*******************************************************************************/
void
mcreadparams(void)
{
  int i,j,status;
  static char buf[CHAR_BUF_LENGTH];
  char *p;
  int len;

  MPI_MASTER(printf("Instrument parameters for %s (%s)\n",
                    instrument_name, instrument_source));

  for(i = 0; mcinputtable[i].name != 0; i++)
  {
    do
    {
      MPI_MASTER(
                 if (mcinputtable[i].val && strlen(mcinputtable[i].val))
                   printf("Set value of instrument parameter %s (%s) [default='%s']:\n",
                          mcinputtable[i].name,
                          (*mcinputtypes[mcinputtable[i].type].parminfo)
                          (mcinputtable[i].name), mcinputtable[i].val);
                 else
                   printf("Set value of instrument parameter %s (%s):\n",
                          mcinputtable[i].name,
                          (*mcinputtypes[mcinputtable[i].type].parminfo)
                          (mcinputtable[i].name));
                 fflush(stdout);
                 );
#ifdef USE_MPI
      if(mpi_node_rank == mpi_node_root)
        {
          p = fgets(buf, CHAR_BUF_LENGTH, stdin);
          if(p == NULL)
            {
              fprintf(stderr, "Error: empty input for paramater %s (mcreadparams)\n", mcinputtable[i].name);
              exit(1);
            }
        }
      else
        p = buf;
      MPI_Bcast(buf, CHAR_BUF_LENGTH, MPI_CHAR, mpi_node_root, MPI_COMM_WORLD);
#else /* !USE_MPI */
      p = fgets(buf, CHAR_BUF_LENGTH, stdin);
      if(p == NULL)
        {
          fprintf(stderr, "Error: empty input for paramater %s (mcreadparams)\n", mcinputtable[i].name);
          exit(1);
        }
#endif /* USE_MPI */
      len = strlen(buf);
      if (!len || (len == 1 && (buf[0] == '\n' || buf[0] == '\r')))
      {
        if (mcinputtable[i].val && strlen(mcinputtable[i].val)) {
          strncpy(buf, mcinputtable[i].val, CHAR_BUF_LENGTH);  /* use default value */
          len = strlen(buf);
        }
      }
      for(j = 0; j < 2; j++)
      {
        if(len > 0 && (buf[len - 1] == '\n' || buf[len - 1] == '\r'))
        {
          len--;
          buf[len] = '\0';
        }
      }

      status = (*mcinputtypes[mcinputtable[i].type].getparm)
                   (buf, mcinputtable[i].par);
      if(!status)
      {
        (*mcinputtypes[mcinputtable[i].type].error)(mcinputtable[i].name, buf);
        if (!mcinputtable[i].val || strlen(mcinputtable[i].val)) {
          fprintf(stderr, "       Change %s default value in instrument definition.\n", mcinputtable[i].name);
          exit(1);
        }
      }
    } while(!status);
  }
} /* mcreadparams */

/*******************************************************************************
* mcparseoptions: parse command line arguments (options, parameters)
*******************************************************************************/
void
mcparseoptions(int argc, char *argv[])
{
  int i, j;
  char *p;
  int paramset = 0, *paramsetarray;
  char *usedir=NULL;

  /* Add one to numipar to avoid allocating zero size memory block. */
  paramsetarray = (int*)malloc((numipar + 1)*sizeof(*paramsetarray));
  if(paramsetarray == NULL)
  {
    fprintf(stderr, "Error: insufficient memory (mcparseoptions)\n");
    exit(1);
  }
  for(j = 0; j < numipar; j++)
    {
      paramsetarray[j] = 0;
      if (mcinputtable[j].val != NULL && strlen(mcinputtable[j].val))
      {
        int  status;
        char buf[CHAR_BUF_LENGTH];
        strncpy(buf, mcinputtable[j].val, CHAR_BUF_LENGTH);
        status = (*mcinputtypes[mcinputtable[j].type].getparm)
                   (buf, mcinputtable[j].par);
        if(!status) fprintf(stderr, "Invalid '%s' default value %s in instrument definition (mcparseoptions)\n", mcinputtable[j].name, buf);
        else paramsetarray[j] = 1;
      } else {
        (*mcinputtypes[mcinputtable[j].type].getparm)
          (NULL, mcinputtable[j].par);
        paramsetarray[j] = 0;
      }
    }
  for(i = 1; i < argc; i++)
  {
    if(!strcmp("-s", argv[i]) && (i + 1) < argc)
      mcsetseed(argv[++i]);
    else if(!strncmp("-s", argv[i], 2))
      mcsetseed(&argv[i][2]);
    else if(!strcmp("--seed", argv[i]) && (i + 1) < argc)
      mcsetseed(argv[++i]);
    else if(!strncmp("--seed=", argv[i], 7))
      mcsetseed(&argv[i][7]);
    else if(!strcmp("-n", argv[i]) && (i + 1) < argc)
      mcsetn_arg(argv[++i]);
    else if(!strncmp("-n", argv[i], 2))
      mcsetn_arg(&argv[i][2]);
    else if(!strcmp("--ncount", argv[i]) && (i + 1) < argc)
      mcsetn_arg(argv[++i]);
    else if(!strncmp("--ncount=", argv[i], 9))
      mcsetn_arg(&argv[i][9]);
    else if(!strcmp("-d", argv[i]) && (i + 1) < argc)
      usedir=argv[++i];  /* will create directory after parsing all arguments (end of this function) */
    else if(!strncmp("-d", argv[i], 2))
      usedir=&argv[i][2];
    else if(!strcmp("--dir", argv[i]) && (i + 1) < argc)
      usedir=argv[++i];
    else if(!strncmp("--dir=", argv[i], 6))
      usedir=&argv[i][6];
    else if(!strcmp("-h", argv[i]))
      mcshowhelp(argv[0]);
    else if(!strcmp("--help", argv[i]) || !strcmp("--version", argv[i]))
      mcshowhelp(argv[0]);
    else if(!strcmp("-i", argv[i])) {
      mcformat=FLAVOR_UPPER;
      mcinfo();
    }
    else if(!strcmp("--info", argv[i]))
      mcinfo();
    else if(!strcmp("-t", argv[i]))
      mcenabletrace();
    else if(!strcmp("--trace", argv[i]) || !strcmp("--verbose", argv[i]))
      mcenabletrace();
    else if(!strcmp("--gravitation", argv[i]))
      mcgravitation = 1;
    else if(!strcmp("-g", argv[i]))
      mcgravitation = 1;
    else if(!strncmp("--format=", argv[i], 9)) {
      mcformat=&argv[i][9];
    }
    else if(!strcmp("--format", argv[i]) && (i + 1) < argc) {
      mcformat=argv[++i];
    }
    else if(!strncmp("--vecsize=", argv[i], 10)) {
      vecsize=atoi(&argv[i][10]);
    }    
    else if(!strcmp("--vecsize", argv[i]) && (i + 1) < argc) {
      vecsize=atoi(argv[++i]);
    }
    else if(!strcmp("--bufsiz", argv[i]) && (i + 1) < argc) {
      MONND_BUFSIZ=atoi(argv[++i]);
    }
    else if(!strncmp("--numgangs=", argv[i], 11)) {
      numgangs=atoi(&argv[i][11]);
    }
    else if(!strcmp("--numgangs", argv[i]) && (i + 1) < argc) {
      numgangs=atoi(argv[++i]);
    }
    else if(!strncmp("--gpu_innerloop=", argv[i], 16)) {
      gpu_innerloop=(long)strtod(&argv[i][16], NULL);
    }
    else if(!strcmp("--gpu_innerloop", argv[i]) && (i + 1) < argc) {
      gpu_innerloop=(long)strtod(argv[++i], NULL);
    }

    else if(!strcmp("--no-output-files", argv[i]))
      mcdisable_output_files = 1;
    else if(!strcmp("--source", argv[i])) {
      printf("/* Source code %s from %s: */\n"
        "/******************************************************************************/\n"
        "%s\n"
        "/******************************************************************************/\n"
        "/* End of source code %s from %s */\n",
        instrument_name, instrument_source, instrument_code,
        instrument_name, instrument_source);
      exit(1);
    }
    else if(argv[i][0] != '-' && (p = strchr(argv[i], '=')) != NULL)
    {
      *p++ = '\0';

      for(j = 0; j < numipar; j++)
        if(!strcmp(mcinputtable[j].name, argv[i]))
        {
          int status;
          status = (*mcinputtypes[mcinputtable[j].type].getparm)(p,
                        mcinputtable[j].par);
          if(!status || !strlen(p))
          {
            (*mcinputtypes[mcinputtable[j].type].error)
              (mcinputtable[j].name, p);
            exit(1);
          }
          paramsetarray[j] = 1;
          paramset = 1;
          break;
        }
      if(j == numipar)
      {                                /* Unrecognized parameter name */
        fprintf(stderr, "Error: unrecognized parameter %s (mcparseoptions)\n", argv[i]);
        exit(1);
      }
    }
    else if(argv[i][0] == '-') {
      fprintf(stderr, "Error: unrecognized option argument %s (mcparseoptions). Ignored.\n", argv[i++]);
    }
    else {
      fprintf(stderr, "Error: unrecognized argument %s (mcparseoptions). Aborting.\n", argv[i]);
      mcusage(argv[0]);
    }
  }
  if(!paramset)
    mcreadparams();                /* Prompt for parameters if not specified. */
  else
  {
    for(j = 0; j < numipar; j++)
      if(!paramsetarray[j])
      {
        fprintf(stderr, "Error: Instrument parameter %s left unset (mcparseoptions)\n",
                mcinputtable[j].name);
        exit(1);
      }
  }
  free(paramsetarray);
#ifdef USE_MPI
  if (mcdotrace) mpi_node_count=1; /* disable threading when in trace mode */
#endif
  if (usedir && strlen(usedir) && !mcdisable_output_files) mcuse_dir(usedir);
} /* mcparseoptions */

#ifndef NOSIGNALS
/*******************************************************************************
* sighandler: signal handler that makes simulation stop, and save results
*******************************************************************************/
void sighandler(int sig)
{
  /* MOD: E. Farhi, Sep 20th 2001: give more info */
  time_t t1, t0;
#define SIG_SAVE 0
#define SIG_TERM 1
#define SIG_STAT 2
#define SIG_ABRT 3

  printf("\n# " MCCODE_STRING ": [pid %i] Signal %i detected", getpid(), sig);
#ifdef USE_MPI
  printf(" [proc %i]", mpi_node_rank);
#endif
#if defined(SIGUSR1) && defined(SIGUSR2) && defined(SIGKILL)
  if (!strcmp(mcsig_message, "sighandler") && (sig != SIGUSR1) && (sig != SIGUSR2))
  {
    printf("\n# Fatal : unrecoverable loop ! Suicide (naughty boy).\n");
    kill(0, SIGKILL); /* kill myself if error occurs within sighandler: loops */
  }
#endif
  switch (sig) {
#ifdef SIGINT
    case SIGINT : printf(" SIGINT (interrupt from terminal, Ctrl-C)"); sig = SIG_TERM; break;
#endif
#ifdef SIGILL
    case SIGILL  : printf(" SIGILL (Illegal instruction)"); sig = SIG_ABRT; break;
#endif
#ifdef SIGFPE
    case SIGFPE  : printf(" SIGFPE (Math Error)"); sig = SIG_ABRT; break;
#endif
#ifdef SIGSEGV
    case SIGSEGV : printf(" SIGSEGV (Mem Error)"); sig = SIG_ABRT; break;
#endif
#ifdef SIGTERM
    case SIGTERM : printf(" SIGTERM (Termination)"); sig = SIG_TERM; break;
#endif
#ifdef SIGABRT
    case SIGABRT : printf(" SIGABRT (Abort)"); sig = SIG_ABRT; break;
#endif
#ifdef SIGQUIT
    case SIGQUIT : printf(" SIGQUIT (Quit from terminal)"); sig = SIG_TERM; break;
#endif
#ifdef SIGTRAP
    case SIGTRAP : printf(" SIGTRAP (Trace trap)"); sig = SIG_ABRT; break;
#endif
#ifdef SIGPIPE
    case SIGPIPE : printf(" SIGPIPE (Broken pipe)"); sig = SIG_ABRT; break;
#endif
#ifdef SIGUSR1
    case SIGUSR1 : printf(" SIGUSR1 (Display info)"); sig = SIG_STAT; break;
#endif
#ifdef SIGUSR2
    case SIGUSR2 : printf(" SIGUSR2 (Save simulation)"); sig = SIG_SAVE; break;
#endif
#ifdef SIGHUP
    case SIGHUP  : printf(" SIGHUP (Hangup/update)"); sig = SIG_SAVE; break;
#endif
#ifdef SIGBUS
    case SIGBUS  : printf(" SIGBUS (Bus error)"); sig = SIG_ABRT; break;
#endif
#ifdef SIGURG
    case SIGURG  : printf(" SIGURG (Urgent socket condition)"); sig = SIG_ABRT; break;
#endif
#ifdef SIGBREAK
    case SIGBREAK: printf(" SIGBREAK (Break signal, Ctrl-Break)"); sig = SIG_SAVE; break;
#endif
    default : printf(" (look at signal list for signification)"); sig = SIG_ABRT; break;
  }
  printf("\n");
  printf("# Simulation: %s (%s) \n", instrument_name, instrument_source);
  printf("# Breakpoint: %s ", mcsig_message);
  if (strstr(mcsig_message, "Save") && (sig == SIG_SAVE))
    sig = SIG_STAT;
  SIG_MESSAGE("sighandler");
  if (mcget_ncount() == 0)
    printf("(0 %%)\n" );
  else
  {
    printf("%.2f %% (%10.1f/%10.1f)\n", 100.0*mcget_run_num()/mcget_ncount(), 1.0*mcget_run_num(), 1.0*mcget_ncount());
  }
  t0 = (time_t)mcstartdate;
  t1 = time(NULL);
  printf("# Date:      %s", ctime(&t1));
  printf("# Started:   %s", ctime(&t0));

  if (sig == SIG_STAT)
  {
    printf("# " MCCODE_STRING ": Resuming simulation (continue)\n");
    fflush(stdout);
    return;
  }
  else
  if (sig == SIG_SAVE)
  {
    printf("# " MCCODE_STRING ": Saving data and resume simulation (continue)\n");
    save(NULL);
    fflush(stdout);
    return;
  }
  else
  if (sig == SIG_TERM)
  {
    printf("# " MCCODE_STRING ": Finishing simulation (save results and exit)\n");
    finally();
    exit(0);
  }
  else
  {
    fflush(stdout);
    perror("# Last I/O Error");
    printf("# " MCCODE_STRING ": Simulation stop (abort).\n");
// This portion of the signal handling only works on UNIX
#if defined(__unix__) || defined(__APPLE__)
    signal(sig, SIG_DFL); /* force to use default sighandler now */
    kill(getpid(), sig);  /* and trigger it with the current signal */
#endif
    exit(-1);
  }
#undef SIG_SAVE
#undef SIG_TERM
#undef SIG_STAT
#undef SIG_ABRT

} /* sighandler */
#endif /* !NOSIGNALS */

#ifdef NEUTRONICS
/*Main neutronics function steers the McStas calls, initializes parameters etc */
/* Only called in case NEUTRONICS = TRUE */
void neutronics_main_(float *inx, float *iny, float *inz, float *invx, float *invy, float *invz, float *intime, float *insx, float *insy, float *insz, float *inw, float *outx, float *outy, float *outz, float *outvx, float *outvy, float *outvz, float *outtime, float *outsx, float *outsy, float *outsz, float *outwgt)
{

  extern double mcnx, mcny, mcnz, mcnvx, mcnvy, mcnvz;
  extern double mcnt, mcnsx, mcnsy, mcnsz, mcnp;

  /* External code governs iteration - McStas is iterated once per call to neutronics_main. I.e. below counter must be initiancated for each call to neutronics_main*/
  mcrun_num=0;

  time_t t;
  t = (time_t)mcstartdate;
  mcstartdate = t;  /* set start date before parsing options and creating sim file */
  init();

  /* *** parse options *** */
  SIG_MESSAGE("[" __FILE__ "] main START");
  mcformat=getenv(FLAVOR_UPPER "_FORMAT") ?
           getenv(FLAVOR_UPPER "_FORMAT") : FLAVOR_UPPER;

  /* Set neutron state based on input from neutronics code */
  mcsetstate(*inx,*iny,*inz,*invx,*invy,*invz,*intime,*insx,*insy,*insz,*inw);

  /* main neutron event loop - runs only one iteration */

  //mcstas_raytrace(&mcncount); /* prior to McStas 1.12 */

  mcallowbackprop = 1; //avoid absorbtion from negative dt
  int argc=1;
  char *argv[0];
  int dummy = mccode_main(argc, argv);

  *outx =  mcnx;
  *outy =  mcny;
  *outz =  mcnz;
  *outvx =  mcnvx;
  *outvy =  mcnvy;
  *outvz =  mcnvz;
  *outtime =  mcnt;
  *outsx =  mcnsx;
  *outsy =  mcnsy;
  *outsz =  mcnsz;
  *outwgt =  mcnp;

  return;
} /* neutronics_main */

#endif /*NEUTRONICS*/

#endif /* !MCCODE_H */
/* End of file "mccode-r.c". */
/* End of file "mccode-r.c". */

/* embedding file "mcstas-r.c" */

/*******************************************************************************
*
* McStas, neutron ray-tracing package
*         Copyright (C) 1997-2009, All rights reserved
*         Risoe National Laboratory, Roskilde, Denmark
*         Institut Laue Langevin, Grenoble, France
*
* Runtime: share/mcstas-r.c
*
* %Identification
* Written by: KN
* Date:    Aug 29, 1997
* Release: McStas X.Y
* Version: $Revision$
*
* Runtime system for McStas.
* Embedded within instrument in runtime mode.
*
* Usage: Automatically embbeded in the c code whenever required.
*
* $Id$
*
*******************************************************************************/

#ifndef MCSTAS_R_H
#include "mcstas-r.h"
#endif
#ifdef DANSE
#include "mcstas-globals.h"
#endif

/*******************************************************************************
* The I/O format definitions and functions
*******************************************************************************/

/*the magnet stack*/
#ifdef MC_POL_COMPAT
void (*mcMagnetPrecession) (double, double, double, double, double, double,
    double, double*, double*, double*, double, Coords, Rotation)=NULL;
Coords   mcMagnetPos;
Rotation mcMagnetRot;
double*  mcMagnetData                = NULL;
/* mcMagneticField(x, y, z, t, Bx, By, Bz) */
int (*mcMagneticField) (double, double, double, double,
    double*, double*, double*, void *) = NULL;
#endif

#ifndef MCSTAS_H

/*******************************************************************************
* mcsetstate: transfer parameters into global McStas variables
*******************************************************************************/
_class_particle mcsetstate(double x, double y, double z, double vx, double vy, double vz,
			   double t, double sx, double sy, double sz, double p, int mcgravitation, void *mcMagnet, int mcallowbackprop)
{
  _class_particle mcneutron;

  mcneutron.x  = x;
  mcneutron.y  = y;
  mcneutron.z  = z;
  mcneutron.vx = vx;
  mcneutron.vy = vy;
  mcneutron.vz = vz;
  mcneutron.t  = t;
  mcneutron.sx = sx;
  mcneutron.sy = sy;
  mcneutron.sz = sz;
  mcneutron.p  = p;
  mcneutron.mcgravitation = mcgravitation;
  mcneutron.mcMagnet = mcMagnet;
  mcneutron.allow_backprop = mcallowbackprop;
  mcneutron._uid       = 0;
  mcneutron._index     = 1;
  mcneutron._absorbed  = 0;
  mcneutron._restore   = 0;
  mcneutron._scattered = 0;

  return(mcneutron);
} /* mcsetstate */

/*******************************************************************************
* mcgetstate: get neutron parameters from particle structure
*******************************************************************************/
_class_particle mcgetstate(_class_particle mcneutron, double *x, double *y, double *z,
               double *vx, double *vy, double *vz, double *t,
               double *sx, double *sy, double *sz, double *p)
{
  *x  =  mcneutron.x;
  *y  =  mcneutron.y;
  *z  =  mcneutron.z;
  *vx =  mcneutron.vx;
  *vy =  mcneutron.vy;
  *vz =  mcneutron.vz;
  *t  =  mcneutron.t;
  *sx =  mcneutron.sx;
  *sy =  mcneutron.sy;
  *sz =  mcneutron.sz;
  *p  =  mcneutron.p;

  return(mcneutron);
} /* mcgetstate */


/*******************************************************************************
* mcgenstate: set default neutron parameters
*******************************************************************************/
// Moved to generated code
/* #pragma acc routine seq */
/* _class_particle mcgenstate(void) */
/* { */
/*   return(mcsetstate(0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, mcgravitation, mcMagnet, mcallowbackprop)); */
/* } */

/*******************************************************************************
* mccoordschanges: old style rotation routine rot -> (x y z) ,(vx vy vz),(sx,sy,sz)
*******************************************************************************/
void
mccoordschanges(Coords a, Rotation t, double *x, double *y, double *z,
               double *vx, double *vy, double *vz, double *sx, double *sy, double *sz)
{
  Coords b, c;

  b.x = *x;
  b.y = *y;
  b.z = *z;
  c = rot_apply(t, b);
  b = coords_add(c, a);
  *x = b.x;
  *y = b.y;
  *z = b.z;

  if ( (vz && vy  && vx) && (*vz != 0.0 || *vx != 0.0 || *vy != 0.0) )
    mccoordschange_polarisation(t, vx, vy, vz);

  if ( (sz && sy  && sx) && (*sz != 0.0 || *sx != 0.0 || *sy != 0.0) )
    mccoordschange_polarisation(t, sx, sy, sz);

}

/* intersection routines ==================================================== */

/*******************************************************************************
* inside_rectangle: Check if (x,y) is inside rectangle (xwidth, yheight)
* return 0 if outside and 1 if inside
*******************************************************************************/
int inside_rectangle(double x, double y, double xwidth, double yheight)
{
  if (x>-xwidth/2 && x<xwidth/2 && y>-yheight/2 && y<yheight/2)
    return 1;
  else
    return 0;
}

/*******************************************************************************
 * box_intersect: compute time intersection with a box
 * returns 0 when no intersection is found
 *      or 1 in case of intersection with resulting times dt_in and dt_out
 * This function written by Stine Nyborg, 1999.
 *******************************************************************************/
int box_intersect(double *dt_in, double *dt_out,
                  double x, double y, double z,
                  double vx, double vy, double vz,
                  double dx, double dy, double dz)
{
  double x_in, y_in, z_in, tt, t[6], a, b;
  int i, count, s;

      /* Calculate intersection time for each of the six box surface planes
       *  If the box surface plane is not hit, the result is zero.*/

  if(vx != 0)
   {
    tt = -(dx/2 + x)/vx;
    y_in = y + tt*vy;
    z_in = z + tt*vz;
    if( y_in > -dy/2 && y_in < dy/2 && z_in > -dz/2 && z_in < dz/2)
      t[0] = tt;
    else
      t[0] = 0;

    tt = (dx/2 - x)/vx;
    y_in = y + tt*vy;
    z_in = z + tt*vz;
    if( y_in > -dy/2 && y_in < dy/2 && z_in > -dz/2 && z_in < dz/2)
      t[1] = tt;
    else
      t[1] = 0;
   }
  else
    t[0] = t[1] = 0;

  if(vy != 0)
   {
    tt = -(dy/2 + y)/vy;
    x_in = x + tt*vx;
    z_in = z + tt*vz;
    if( x_in > -dx/2 && x_in < dx/2 && z_in > -dz/2 && z_in < dz/2)
      t[2] = tt;
    else
      t[2] = 0;

    tt = (dy/2 - y)/vy;
    x_in = x + tt*vx;
    z_in = z + tt*vz;
    if( x_in > -dx/2 && x_in < dx/2 && z_in > -dz/2 && z_in < dz/2)
      t[3] = tt;
    else
      t[3] = 0;
   }
  else
    t[2] = t[3] = 0;

  if(vz != 0)
   {
    tt = -(dz/2 + z)/vz;
    x_in = x + tt*vx;
    y_in = y + tt*vy;
    if( x_in > -dx/2 && x_in < dx/2 && y_in > -dy/2 && y_in < dy/2)
      t[4] = tt;
    else
      t[4] = 0;

    tt = (dz/2 - z)/vz;
    x_in = x + tt*vx;
    y_in = y + tt*vy;
    if( x_in > -dx/2 && x_in < dx/2 && y_in > -dy/2 && y_in < dy/2)
      t[5] = tt;
    else
      t[5] = 0;
   }
  else
    t[4] = t[5] = 0;

  /* The intersection is evaluated and *dt_in and *dt_out are assigned */

  a = b = s = 0;
  count = 0;

  for( i = 0; i < 6; i = i + 1 )
    if( t[i] == 0 )
      s = s+1;
    else if( count == 0 )
    {
      a = t[i];
      count = 1;
    }
    else
    {
      b = t[i];
      count = 2;
    }

  if ( a == 0 && b == 0 )
    return 0;
  else if( a < b )
  {
    *dt_in = a;
    *dt_out = b;
    return 1;
  }
  else
  {
    *dt_in = b;
    *dt_out = a;
    return 1;
  }

} /* box_intersect */

/*******************************************************************************
 * cylinder_intersect: compute intersection with a cylinder
 * returns 0 when no intersection is found
 *      or 2/4/8/16 bits depending on intersection,
 *     and resulting times t0 and t1
 * Written by: EM,NB,ABA 4.2.98
  *******************************************************************************/
int cylinder_intersect(double *t0, double *t1, double x, double y, double z,
                   double vx, double vy, double vz, double r, double h)
{
  double D, t_in, t_out, y_in, y_out;
  int ret=1;

  D = (2*vx*x + 2*vz*z)*(2*vx*x + 2*vz*z)
    - 4*(vx*vx + vz*vz)*(x*x + z*z - r*r);

  if (D>=0)
  {
    if (vz*vz + vx*vx) {
      t_in  = (-(2*vz*z + 2*vx*x) - sqrt(D))/(2*(vz*vz + vx*vx));
      t_out = (-(2*vz*z + 2*vx*x) + sqrt(D))/(2*(vz*vz + vx*vx));
    } else if (vy) { /* trajectory parallel to cylinder axis */
      t_in = (-h/2-y)/vy;
      t_out = (h/2-y)/vy;
      if (t_in>t_out){
        double tmp=t_in;
        t_in=t_out;t_out=tmp;
      }
    } else return 0;
    y_in = vy*t_in + y;
    y_out =vy*t_out + y;

    if ( (y_in > h/2 && y_out > h/2) || (y_in < -h/2 && y_out < -h/2) )
      return 0;
    else
    {
      if (y_in > h/2)
        { t_in = ((h/2)-y)/vy; ret += 2; }
      else if (y_in < -h/2)
        { t_in = ((-h/2)-y)/vy; ret += 4; }
      if (y_out > h/2)
        { t_out = ((h/2)-y)/vy; ret += 8; }
      else if (y_out < -h/2)
        { t_out = ((-h/2)-y)/vy; ret += 16; }
    }
    *t0 = t_in;
    *t1 = t_out;
    return ret;
  }
  else
  {
    *t0 = *t1 = 0;
    return 0;
  }
} /* cylinder_intersect */


/*******************************************************************************
 * sphere_intersect: Calculate intersection between a line and a sphere.
 * returns 0 when no intersection is found
 *      or 1 in case of intersection with resulting times t0 and t1
 *******************************************************************************/
int sphere_intersect(double *t0, double *t1, double x, double y, double z,
                 double vx, double vy, double vz, double r)
{
  double A, B, C, D, v;

  v = sqrt(vx*vx + vy*vy + vz*vz);
  A = v*v;
  B = 2*(x*vx + y*vy + z*vz);
  C = x*x + y*y + z*z - r*r;
  D = B*B - 4*A*C;
  if(D < 0)
    return 0;
  D = sqrt(D);
  *t0 = (-B - D) / (2*A);
  *t1 = (-B + D) / (2*A);
  return 1;
} /* sphere_intersect */

/*******************************************************************************
 * plane_intersect: Calculate intersection between a plane and a line.
 * returns 0 when no intersection is found (i.e. line is parallel to the plane)
 * returns 1 or -1 when intersection time is positive and negative respectively
 *******************************************************************************/
int plane_intersect(double *t, double x, double y, double z,
                 double vx, double vy, double vz, double nx, double ny, double nz, double wx, double wy, double wz)
{
  double s;
  if (fabs(s=scalar_prod(nx,ny,nz,vx,vy,vz))<FLT_EPSILON) return 0;
  *t = - scalar_prod(nx,ny,nz,x-wx,y-wy,z-wz)/s;
  if (*t<0) return -1;
  else return 1;
} /* plane_intersect */

#endif /* !MCSTAS_H */
/* End of file "mcstas-r.c". */


/* *****************************************************************************
* Start of instrument 'template_body_simple' generated code
***************************************************************************** */

#ifdef MC_TRACE_ENABLED
int traceenabled = 1;
#else
int traceenabled = 0;
#endif
#define MCSTAS "/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//"
int   defaultmain         = 1;
char  instrument_name[]   = "template_body_simple";
char  instrument_source[] = "RITA-2-Kristine-instrument.instr";
char *instrument_exe      = NULL; /* will be set to argv[0] in main */
char  instrument_code[]   = "Instrument template_body_simple source code RITA-2-Kristine-instrument.instr is not embedded in this executable.\n  Use --source option when running McStas.\n";

int main(int argc, char *argv[]){return mccode_main(argc, argv);}

/* *****************************************************************************
* instrument 'template_body_simple' and components DECLARE
***************************************************************************** */

/* Instrument parameters: structure and a table for the initialisation
   (Used in e.g. inputparse and I/O function (e.g. detector_out) */

struct _struct_instrument_parameters {
  MCNUM Ei;
  MCNUM Ef;
  MCNUM mono_focus;
  MCNUM h;
  MCNUM k;
  MCNUM col_div_1;
  MCNUM col_div_2;
  MCNUM slit_left;
  MCNUM slit_right;
  MCNUM slit_top;
  MCNUM slit_bottom;
  MCNUM sym;
  MCNUM dA3;
  MCNUM T;
};
typedef struct _struct_instrument_parameters _class_instrument_parameters;

/* instrument SPLIT and GROUP control logic */
struct instrument_logic_struct {
  long Split_a4; /* this is the SPLIT counter decremented down to 0 */
  _class_particle Split_a4_particle; /* this is the particle to duplicate */
};

struct _instrument_struct {
  char   _name[256]; /* the name of this instrument e.g. 'template_body_simple' */
/* Counters per component instance */
  double counter_AbsorbProp[43]; /* absorbed events in PROP routines */
  double counter_N[43], counter_P[43], counter_P2[43]; /* event counters after each component instance */
  _class_particle _trajectory[43]; /* current trajectory for STORE/RESTORE */
/* Components position table (absolute and relative coords) */
  Coords _position_relative[43]; /* positions of all components */
  Coords _position_absolute[43];
  _class_instrument_parameters _parameters; /* instrument parameters */
  struct instrument_logic_struct logic; /* instrument logic */
} _instrument_var;
struct _instrument_struct *instrument = & _instrument_var;
#pragma acc declare create ( _instrument_var )
#pragma acc declare create ( instrument )

int numipar = 14;
struct mcinputtable_struct mcinputtable[] = {
  "Ei", &(_instrument_var._parameters.Ei), instr_type_double, "5.0", 
  "Ef", &(_instrument_var._parameters.Ef), instr_type_double, "5.0", 
  "mono_focus", &(_instrument_var._parameters.mono_focus), instr_type_double, "1", 
  "h", &(_instrument_var._parameters.h), instr_type_double, "1", 
  "k", &(_instrument_var._parameters.k), instr_type_double, "0", 
  "col_div_1", &(_instrument_var._parameters.col_div_1), instr_type_double, "80", 
  "col_div_2", &(_instrument_var._parameters.col_div_2), instr_type_double, "80", 
  "slit_left", &(_instrument_var._parameters.slit_left), instr_type_double, "0.1", 
  "slit_right", &(_instrument_var._parameters.slit_right), instr_type_double, "0.1", 
  "slit_top", &(_instrument_var._parameters.slit_top), instr_type_double, "0.1", 
  "slit_bottom", &(_instrument_var._parameters.slit_bottom), instr_type_double, "0.1", 
  "sym", &(_instrument_var._parameters.sym), instr_type_double, "0", 
  "dA3", &(_instrument_var._parameters.dA3), instr_type_double, "0", 
  "T", &(_instrument_var._parameters.T), instr_type_double, "300", 
  NULL, NULL, instr_type_double, ""
};


/* ************************************************************************** */
/*             SHARE user declarations for all components                     */
/* ************************************************************************** */

/* Shared user declarations for all components types 'Source_Maxwell_3'. */
/* A normalised Maxwellian distribution : Integral over all l = 1 */
#pragma acc routine seq
double SM3_Maxwell(double l, double temp)
  {
    double a=949.0/temp;
    return 2*a*a*exp(-a/(l*l))/(l*l*l*l*l);
  }

/* Shared user declarations for all components types 'Guide'. */
/*******************************************************************************
*
* McStas, neutron ray-tracing package
*         Copyright 1997-2002, All rights reserved
*         Risoe National Laboratory, Roskilde, Denmark
*         Institut Laue Langevin, Grenoble, France
*
* Library: share/read_table-lib.h
*
* %Identification
* Written by: EF
* Date: Aug 28, 2002
* Origin: ILL
* Release: McStas 1.6
* Version: $Revision$
*
* This file is to be imported by components that may read data from table files
* It handles some shared functions.
*
* This library may be used directly as an external library. It has no dependency
*
* Usage: within SHARE
* %include "read_table-lib"
*
*******************************************************************************/

#ifndef READ_TABLE_LIB_H
#define READ_TABLE_LIB_H "$Revision$"

#define READ_TABLE_STEPTOL  0.04 /* tolerancy for constant step approx */

#ifndef MC_PATHSEP_C
#ifdef WIN32
#define MC_PATHSEP_C '\\'
#define MC_PATHSEP_S "\\"
#else  /* !WIN32 */
#ifdef MAC
#define MC_PATHSEP_C ':'
#define MC_PATHSEP_S ":"
#else  /* !MAC */
#define MC_PATHSEP_C '/'
#define MC_PATHSEP_S "/"
#endif /* !MAC */
#endif /* !WIN32 */
#endif /* !MC_PATHSEP_C */

#ifndef MCSTAS
#ifdef WIN32
#define MCSTAS "C:\\mcstas\\lib"
#else  /* !WIN32 */
#ifdef MAC
#define MCSTAS ":mcstas:lib" /* ToDo: What to put here? */
#else  /* !MAC */
#define MCSTAS "/usr/local/lib/mcstas"
#endif /* !MAC */
#endif /* !WIN32 */
#endif /* !MCSTAS */

#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>

  typedef struct struct_table
  {
    char    filename[1024];
    long    filesize;
    char   *header;  /* text header, e.g. comments */
    double *data;    /* vector { x[0], y[0], ... x[n-1], y[n-1]... } */
    double  min_x;   /* min value of first column */
    double  max_x;   /* max value of first column */
    double  step_x;  /* minimal step value of first column */
    long    rows;    /* number of rows in matrix block */
    long    columns; /* number of columns in matrix block */

    long    begin;   /* start fseek index of block */
    long    end;     /* stop  fseek index of block */
    long    block_number;  /* block index. 0 is catenation of all */
    long    array_length;  /* number of elements in the t_Table array */
    char    monotonic;     /* true when 1st column/vector data is monotonic */
    char    constantstep;  /* true when 1st column/vector data has constant step */
    char    method[32];    /* interpolation method: nearest, linear */
    char    quiet;   /*output level for messages to the console 0: print all messages, 1:only print some/including errors, 2: never print anything.*/
  } t_Table;

/*maximum number of rows to rebin a table = 1M*/
enum { mcread_table_rebin_maxsize = 1000000 };

typedef struct t_Read_table_file_item {
    int ref_count;
    t_Table *table_ref;
} t_Read_table_file_item;

typedef enum enum_Read_table_file_actions {STORE,FIND,GC}  t_Read_table_file_actions;

/* read_table-lib function prototypes */
/* ========================================================================= */

/* 'public' functions */
long     Table_Read              (t_Table *Table, char *File, long block_number);
long     Table_Read_Offset       (t_Table *Table, char *File, long block_number,
                                  long *offset, long max_lines);
long     Table_Read_Offset_Binary(t_Table *Table, char *File, char *Type,
                                  long *Offset, long Rows, long Columns);
long     Table_Rebin(t_Table *Table); /* rebin table with regular 1st column and interpolate all columns 2:end */
long     Table_Info (t_Table Table);
#pragma acc routine
double   Table_Index(t_Table Table,   long i, long j); /* get indexed value */
#pragma acc routine
double   Table_Value(t_Table Table, double X, long j); /* search X in 1st column and return interpolated value in j-column */
t_Table *Table_Read_Array(char *File, long *blocks);
void     Table_Free_Array(t_Table *Table);
long     Table_Info_Array(t_Table *Table);
int      Table_SetElement(t_Table *Table, long i, long j, double value);
long     Table_Init(t_Table *Table, long rows, long columns); /* create a Table */
#pragma acc routine
double   Table_Value2d(t_Table Table, double X, double Y);    /* same as Table_Index with non-integer indices and 2d interpolation */
MCDETECTOR Table_Write(t_Table Table, char*file, char*xl, char*yl, 
           double x1, double x2, double y1, double y2); /* write Table to disk */
void * Table_File_List_Handler(t_Read_table_file_actions action, void *item, void *item_modifier);
t_Table *Table_File_List_find(char *name, int block, int offset);
int Table_File_List_gc(t_Table *tab);
void *Table_File_List_store(t_Table *tab);

#define Table_ParseHeader(header, ...) \
  Table_ParseHeader_backend(header,__VA_ARGS__,NULL);

char **Table_ParseHeader_backend(char *header, ...);

/* private functions */
void Table_Free(t_Table *Table);
long Table_Read_Handle(t_Table *Table, FILE *fid, long block_number, long max_lines, char *name);
static void Table_Stat(t_Table *Table);
#pragma acc routine
double Table_Interp1d(double x, double x1, double y1, double x2, double y2);
#pragma acc routine
double Table_Interp1d_nearest(double x, double x1, double y1, double x2, double y2);
#pragma acc routine
double Table_Interp2d(double x, double y, double x1, double y1, double x2, double y2,
double z11, double z12, double z21, double z22);


#endif

/* end of read_table-lib.h */
/*******************************************************************************
*
* McStas, neutron ray-tracing package
*         Copyright (C) 1997-2009, All rights reserved
*         Risoe National Laboratory, Roskilde, Denmark
*         Institut Laue Langevin, Grenoble, France
*
* Library: share/read_table-lib.c
*
* %Identification
* Written by: EF
* Date: Aug 28, 2002
* Origin: ILL
* Release: McStas CVS_090504
* Version: $Revision$
*
* This file is to be imported by components that may read data from table files
* It handles some shared functions. Embedded within instrument in runtime mode.
*
* Usage: within SHARE
* %include "read_table-lib"
*
*******************************************************************************/

#ifndef READ_TABLE_LIB_H
#include "read_table-lib.h"
#endif


/*******************************************************************************
 * void *Table_File_List_Handler(action, item, item_modifier)
 *   ACTION: handle file entries in the read_table-lib file list. If a file is read - it is supposed to be
 *   stored in a list such that we can avoid reading the same file many times.
 *   input  action: FIND, STORE, GC. check if file exists in the list, store an item in the list, or check if it can be garbage collected.
 *   input item: depends on the action.
 *    FIND)  item is a filename, and item_modifier is the block number
 *    STORE) item is the Table to store - item_modifier is ignored
 *    GC)    item is the Table to check. If it has a ref_count >1 then this is simply decremented.
 *   return  depends on the action
 *    FIND)  return a reference to a table+ref_count item if found - NULL otherwise. I.e. NULL means the file has not been read before and must be read again.
 *    STORE) return NULL always
 *    GC)    return NULL if no garbage collection is needed, return an adress to the t_Table which should be garbage collected. 0x1 is returned if
 *           the item is not found in the list
*******************************************************************************/
void * Table_File_List_Handler(t_Read_table_file_actions action, void *item, void *item_modifier){

    /* logic here is Read_Table should include a call to FIND. If found the return value should just be used as
     * if the table had been read from disk. If not found then read the table and STORE.
     * Table_Free should include a call to GC. If this returns non-NULL then we should proceed with freeing the memory
     * associated with the table item - otherwise only decrement the reference counter since there are more references
     * that may need it.*/

    static t_Read_table_file_item read_table_file_list[1024];  
    static int read_table_file_count=0;

    t_Read_table_file_item *tr;
    switch(action){
        case FIND:
            /*interpret data item as a filename, if it is found return a pointer to the table and increment refcount.
             * if not found return the item itself*/
            tr=read_table_file_list;
            while ( tr->table_ref!=NULL ){
                int i=*((int*) item_modifier);
                int j=*( ((int*) item_modifier)+1);
                if ( !strcmp(tr->table_ref->filename,(char *) item) &&
                        tr->table_ref->block_number==i && tr->table_ref->begin==j ){
                    tr->ref_count++;
                    return (void *) tr;
                }
                tr++;
            }
            return NULL;
        case STORE:
            /*find an available slot and store references to table there*/
            tr=&(read_table_file_list[read_table_file_count++]);
            tr->table_ref = ((t_Table *) item);
            tr->ref_count++;
            return NULL;
        case GC:
            /* Should this item be garbage collected (freed) - if so scratch the entry and return the address of the item - 
             * else decrement ref_count and return NULL.
             * A non-NULL return expects the item to actually be freed afterwards.*/
            tr=read_table_file_list;
            while ( tr->table_ref!=NULL ){
                if ( tr->table_ref->data ==((t_Table *)item)->data && 
                        tr->table_ref->block_number == ((t_Table *)item)->block_number){
                    /*matching item found*/
                    if (tr->ref_count>1){
                        /*the item is found and no garbage collection needed*/
                        tr->ref_count--;
                        return NULL;
                    }else{
                        /* The item is found and the reference counter is 1.
                         * This means we should garbage collect. Move remaining list items up one slot,
                         * and return the table for garbage collection by caller*/
                        while (tr->table_ref!=NULL){
                            *tr=*(tr+1);
                            tr++;
                        }
                        read_table_file_count--;
                        return (t_Table *) item;
                    }
                }
                tr++;
            }
            /* item not found, and so should be garbage collected. This could be the case if freeing a
             * Table that has been constructed from code - not read from file. Return 0x1 to flag it for
             * collection.*/
            return (void *) 0x1 ;
    }
    /* If we arrive here, nothing worked, return NULL */
    return NULL;
}

/* Access functions to the handler*/

/********************************************
 * t_Table *Table_File_List_find(char *name, int block, int offset)
 * input name: filename to search for in the file list
 * input block: data block in the file as each file may contain more than 1 data block.
 * return a ref. to a table if it is found (you may use this pointer and skip reading the file), NULL otherwise (i.e. go ahead and read the file)
*********************************************/
t_Table *Table_File_List_find(char *name, int block, int offset){
    int vars[2]={block,offset};
    t_Read_table_file_item *item = Table_File_List_Handler(FIND,name, vars);
    if (item == NULL){
        return NULL;
    }else{
        return item->table_ref;
    }
}
/********************************************
 * int Table_File_List_gc(t_Table *tab)
 * input tab: the table to check for references.
 * return 0: no garbage collection needed
 *        1: Table's data and header (at least) should be freed.
*********************************************/
int Table_File_List_gc(t_Table *tab){
    void *rval=Table_File_List_Handler(GC,tab,0);
    if (rval==NULL) return 0;
    else return 1;
}


/*****************************************************************************
 * void *Table_File_List_store(t_Table *tab)
 * input tab: pointer to table to store.
 * return None. 
*******************************************************************************/
void *Table_File_List_store(t_Table *tab){
    return Table_File_List_Handler(STORE,tab,0);
}


/*******************************************************************************
* FILE *Open_File(char *name, char *Mode, char *path)
*   ACTION: search for a file and open it. Optionally return the opened path.
*   input   name:  file name from which table should be extracted
*           mode: "r", "w", "a" or any valid fopen mode
*           path:  NULL or a pointer to at least 1024 allocated chars
*   return  initialized file handle or NULL in case of error
*******************************************************************************/

  FILE *Open_File(char *File, const char *Mode, char *Path)
  {
    char path[1024];
    FILE *hfile = NULL;
    
    if (!File || File[0]=='\0')                     return(NULL);
    if (!strcmp(File,"NULL") || !strcmp(File,"0"))  return(NULL);
    
    /* search in current or full path */
    strncpy(path, File, 1024);
    hfile = fopen(path, Mode);
    if(!hfile)
    {
      char dir[1024];

      if (!hfile && instrument_source[0] != '\0' && strlen(instrument_source)) /* search in instrument source location */
      {
        char *path_pos   = NULL;
        /* extract path: searches for last file separator */
        path_pos    = strrchr(instrument_source, MC_PATHSEP_C);  /* last PATHSEP */
        if (path_pos) {
          long path_length = path_pos +1 - instrument_source;  /* from start to path+sep */
          if (path_length) {
            strncpy(dir, instrument_source, path_length);
            dir[path_length] = '\0';
            snprintf(path, 1024, "%s%c%s", dir, MC_PATHSEP_C, File);
            hfile = fopen(path, Mode);
          }
        }
      }
      if (!hfile && instrument_exe[0] != '\0' && strlen(instrument_exe)) /* search in PWD instrument executable location */
      {
        char *path_pos   = NULL;
        /* extract path: searches for last file separator */
        path_pos    = strrchr(instrument_exe, MC_PATHSEP_C);  /* last PATHSEP */
        if (path_pos) {
          long path_length = path_pos +1 - instrument_exe;  /* from start to path+sep */
          if (path_length) {
            strncpy(dir, instrument_exe, path_length);
            dir[path_length] = '\0';
            snprintf(path, 1024, "%s%c%s", dir, MC_PATHSEP_C, File);
            hfile = fopen(path, Mode);
          }
        }
      }
      if (!hfile) /* search in HOME or . */
      {
        strcpy(dir, getenv("HOME") ? getenv("HOME") : ".");
        snprintf(path, 1024, "%s%c%s", dir, MC_PATHSEP_C, File);
        hfile = fopen(path, Mode);
      }
      if (!hfile) /* search in MCSTAS/data */
      {
        strcpy(dir, getenv(FLAVOR_UPPER) ? getenv(FLAVOR_UPPER) : MCSTAS);
        snprintf(path, 1024, "%s%c%s%c%s", dir, MC_PATHSEP_C, "data", MC_PATHSEP_C, File);
        hfile = fopen(path, Mode);
      }
      if (!hfile) /* search in MVCSTAS/contrib */
      {
        strcpy(dir, getenv(FLAVOR_UPPER) ? getenv(FLAVOR_UPPER) : MCSTAS);
        snprintf(path, 1024, "%s%c%s%c%s", dir, MC_PATHSEP_C, "contrib", MC_PATHSEP_C, File);
        hfile = fopen(path, Mode);
      }
      if(!hfile)
      {
        fprintf(stderr, "Error: Could not open input file '%s' (Open_File)\n", File);
        return (NULL);
      }
    }
    if (Path) strncpy(Path, path, 1024);
    return(hfile);
  } /* end Open_File */

/*******************************************************************************
* long Read_Table(t_Table *Table, char *name, int block_number)
*   ACTION: read a single Table from a text file
*   input   Table: pointer to a t_Table structure
*           name:  file name from which table should be extracted
*           block_number: if the file does contain more than one
*                 data block, then indicates which one to get (from index 1)
*                 a 0 value means append/catenate all
*   return  initialized single Table t_Table structure containing data, header, ...
*           number of read elements (-1: error, 0:header only)
* The routine stores any line starting with '#', '%' and ';' into the header
* File is opened, read and closed
* Other lines are interpreted as numerical data, and stored.
* Data block should be a rectangular matrix or vector.
* Data block may be rebinned with Table_Rebin (also sort in ascending order)
*******************************************************************************/
  long Table_Read(t_Table *Table, char *File, long block_number)
  { /* reads all or a single data block from 'file' and returns a Table structure  */
    return(Table_Read_Offset(Table, File, block_number, NULL, 0));
  } /* end Table_Read */

/*******************************************************************************
* long Table_Read_Offset(t_Table *Table, char *name, int block_number, long *offset
*                        long max_rows)
*   ACTION: read a single Table from a text file, starting at offset
*     Same as Table_Read(..) except:
*   input   offset:    pointer to an offset (*offset should be 0 at start)
*           max_rows: max number of data rows to read from file (0 means all)
*   return  initialized single Table t_Table structure containing data, header, ...
*           number of read elements (-1: error, 0:header only)
*           updated *offset position (where end of reading occured)
*******************************************************************************/
  long Table_Read_Offset(t_Table *Table, char *File,
                         long block_number, long *offset,
                         long max_rows)
  { /* reads all/a data block in 'file' and returns a Table structure  */
    FILE *hfile;
    long  nelements=0;
    long  begin=0;
    long  filesize=0;
    char  name[1024];
    char  path[1024];
    struct stat stfile;

    /*Need to be able to store the pointer*/
    if (!Table) return(-1);
    
    //if (offset && *offset) snprintf(name, 1024, "%s@%li", File, *offset);
    //else                   
    strncpy(name, File, 1024);
    if(offset && *offset){
        begin=*offset;
    }
    /* Check if the table has already been read from file.
     * If so just reuse the table, if not (this is flagged by returning NULL
     * set up a new table and read the data into it */
    t_Table *tab_p= Table_File_List_find(name,block_number,begin);
    if ( tab_p!=NULL ){
        /*table was found in the Table_File_List*/
        *Table=*tab_p;
        MPI_MASTER(
            if(Table->quiet<1)
              printf("Reusing input file '%s' (Table_Read_Offset)\n", name);
            );
        return Table->rows*Table->columns;
    }

    /* open the file */
    hfile = Open_File(File, "r", path);
    if (!hfile) return(-1);
    else {
      MPI_MASTER(
          if(Table->quiet<1)
            printf("Opening input file '%s' (Table_Read_Offset)\n", path);
          );
    }
    
    /* read file state */
    stat(path,&stfile); filesize = stfile.st_size;
    if (offset && *offset) fseek(hfile, *offset, SEEK_SET);
    begin     = ftell(hfile);
    
    Table_Init(Table, 0, 0);

    /* read file content and set the Table */
    nelements = Table_Read_Handle(Table, hfile, block_number, max_rows, name);
    Table->begin = begin;
    Table->end   = ftell(hfile);
    Table->filesize = (filesize>0 ? filesize : 0);
    Table_Stat(Table);
    
    Table_File_List_store(Table);

    if (offset) *offset=Table->end;
    fclose(hfile);
    return(nelements);

  } /* end Table_Read_Offset */

/*******************************************************************************
* long Table_Read_Offset_Binary(t_Table *Table, char *File, char *type,
*                               long *offset, long rows, long columns)
*   ACTION: read a single Table from a binary file, starting at offset
*     Same as Table_Read_Offset(..) except that it handles binary files.
*   input   type: may be "float"/NULL or "double"
*           offset: pointer to an offset (*offset should be 0 at start)
*           rows   : number of rows (0 means read all)
*           columns: number of columns
*   return  initialized single Table t_Table structure containing data, header, ...
*           number of read elements (-1: error, 0:header only)
*           updated *offset position (where end of reading occured)
*******************************************************************************/
  long Table_Read_Offset_Binary(t_Table *Table, char *File, char *type,
                                long *offset, long rows, long columns)
  { /* reads all/a data block in binary 'file' and returns a Table structure  */
    long    nelements, sizeofelement;
    long    filesize;
    FILE   *hfile;
    char    path[1024];
    struct stat stfile;
    double *data;
    long    i;
    long    begin;

    if (!Table) return(-1);

    Table_Init(Table, 0, 0);
    
    /* open the file */
    hfile = Open_File(File, "r", path);
    if (!hfile) return(-1);
    else {
      MPI_MASTER(
          if(Table->quiet<1)
            printf("Opening input file '%s' (Table_Read, Binary)\n", path);
      );
    }
    
    /* read file state */
    stat(File,&stfile);
    filesize = stfile.st_size;
    Table->filesize=filesize;
    
    /* read file content */
    if (type && !strcmp(type,"double")) sizeofelement = sizeof(double);
    else  sizeofelement = sizeof(float);
    if (offset && *offset) fseek(hfile, *offset, SEEK_SET);
    begin     = ftell(hfile);
    if (rows && filesize > sizeofelement*columns*rows)
      nelements = columns*rows;
    else nelements = (long)(filesize/sizeofelement);
    if (!nelements || filesize <= *offset) return(0);
    data    = (double*)malloc(nelements*sizeofelement);
    if (!data) {
      if(!(Table->quiet>1))
        fprintf(stderr,"Error: allocating %ld elements for %s file '%s'. Too big (Table_Read_Offset_Binary).\n", nelements, type, File);
      exit(-1);
    }
    nelements = fread(data, sizeofelement, nelements, hfile);

    if (!data || !nelements)
    {
      if(!(Table->quiet>1))
        fprintf(stderr,"Error: reading %ld elements from %s file '%s' (Table_Read_Offset_Binary)\n", nelements, type, File);
      exit(-1);
    }
    Table->begin   = begin;
    Table->end     = ftell(hfile);
    if (offset) *offset=Table->end;
    fclose(hfile);
    data = (double*)realloc(data, (double)nelements*sizeofelement);
    /* copy file data into Table */
    if (type && !strcmp(type,"double")) Table->data = data;
    else {
      float  *s;
      double *dataf;
      s     = (float*)data;
      dataf = (double*)malloc(sizeof(double)*nelements);
      for (i=0; i<nelements; i++)
        dataf[i]=s[i];
      free(data);
      Table->data = dataf;
    }
    strncpy(Table->filename, File, 1024);
    Table->rows    = nelements/columns;
    Table->columns = columns;
    Table->array_length = 1;
    Table->block_number = 1;

    Table_Stat(Table);

    return(nelements);
  } /* end Table_Read_Offset_Binary */

/*******************************************************************************
* long Table_Read_Handle(t_Table *Table, FILE *fid, int block_number, long max_rows, char *name)
*   ACTION: read a single Table from a text file handle (private)
*   input   Table:pointer to a t_Table structure
*           fid:  pointer to FILE handle
*           block_number: if the file does contain more than one
*                 data block, then indicates which one to get (from index 1)
*                 a 0 value means append/catenate all
*           max_rows: if non 0, only reads that number of lines
*   return  initialized single Table t_Table structure containing data, header, ...
*           modified Table t_Table structure containing data, header, ...
*           number of read elements (-1: error, 0:header only)
* The routine stores any line starting with '#', '%' and ';' into the header
* Other lines are interpreted as numerical data, and stored.
* Data block should be a rectangular matrix or vector.
* Data block may be rebined with Table_Rebin (also sort in ascending order)
*******************************************************************************/
  long Table_Read_Handle(t_Table *Table, FILE *hfile,
                         long block_number, long max_rows, char *name)
  { /* reads all/a data block from 'file' handle and returns a Table structure  */
    double *Data;
    char *Header              = NULL;
    long  malloc_size         = CHAR_BUF_LENGTH;
    long  malloc_size_h       = 4096;
    long  Rows = 0,   Columns = 0;
    long  count_in_array      = 0;
    long  count_in_header     = 0;
    long  block_Current_index = 0;
    char  flag_End_row_loop   = 0;

    if (!Table) return(-1);
    Table_Init(Table, 0, 0);
    if (name && name[0]!='\0') strncpy(Table->filename, name, 1024);

    if(!hfile) {
       fprintf(stderr, "Error: File handle is NULL (Table_Read_Handle).\n");
       return (-1);
    }
    Header = (char*)  calloc(malloc_size_h, sizeof(char));
    Data   = (double*)calloc(malloc_size,   sizeof(double));
    if ((Header == NULL) || (Data == NULL)) {
       fprintf(stderr, "Error: Could not allocate Table and Header (Table_Read_Handle).\n");
       return (-1);
    }

    int flag_In_array = 0;
    do { /* while (!flag_End_row_loop) */
      char  line[1024*CHAR_BUF_LENGTH];
      long  back_pos=0;   /* ftell start of line */

      back_pos = ftell(hfile);
      if (fgets(line, 1024*CHAR_BUF_LENGTH, hfile) != NULL) { /* analyse line */
        /* first skip blank and tabulation characters */
        int i = strspn(line, " \t");

        /* handle comments: stored in header */
        if (NULL != strchr("#%;/", line[i]))
        { /* line is a comment */
          count_in_header += strlen(line);
          if (count_in_header >= malloc_size_h) {
            /* if succeed and in array : add (and realloc if necessary) */
            malloc_size_h = count_in_header+4096;
            Header        = (char*)realloc(Header, malloc_size_h*sizeof(char));
          }
          strncat(Header, line, 4096);
          flag_In_array=0;
          /* exit line and file if passed desired block */
          if (block_number > 0 && block_number == block_Current_index) {
            flag_End_row_loop = 1;
          }

          /* Continue with next line */
          continue;
        }

        /* get the number of columns splitting line with strtok */
        char  *lexeme;
        char  flag_End_Line = 0;
        long  block_Num_Columns = 0;
        const char seps[] = " ,;\t\n\r";

        lexeme = strtok(line, seps);
        while (!flag_End_Line) {
          if ((lexeme != NULL) && (lexeme[0] != '\0')) {
            /* reading line: the token is not empty */
            double X;
            int    count=1;
            /* test if we have 'NaN','Inf' */
            if (!strncasecmp(lexeme,"NaN",3))
              X = 0;
            else if (!strncasecmp(lexeme,"Inf",3) || !strncasecmp(lexeme,"+Inf",4))
              X = FLT_MAX;
            else if (!strncasecmp(lexeme,"-Inf",4))
              X = -FLT_MAX;
            else
              count = sscanf(lexeme,"%lg",&X);
            if (count == 1) {
              /* reading line: the token is a number in the line */
              if (!flag_In_array) {
                /* reading num: not already in a block: starts a new data block */
                block_Current_index++;
                flag_In_array    = 1;
                block_Num_Columns= 0;
                if (block_number > 0) {
                  /* initialise a new data block */
                  Rows = 0;
                  count_in_array = 0;
                } /* else append */
              }
              /* reading num: all blocks or selected block */
              if (flag_In_array && (block_number == 0 ||
                  block_number == block_Current_index)) {
                /* starting block: already the desired number of rows ? */
                if (block_Num_Columns == 0 &&
                    max_rows > 0 && Rows >= max_rows) {
                  flag_End_Line      = 1;
                  flag_End_row_loop  = 1;
                  flag_In_array      = 0;
                  /* reposition to begining of line (ignore line) */
                  fseek(hfile, back_pos, SEEK_SET);
                } else { /* store into data array */
                  if (count_in_array >= malloc_size) {
                    /* realloc data buffer if necessary */
                    malloc_size = count_in_array*1.5;
                    Data = (double*) realloc(Data, malloc_size*sizeof(double));
                    if (Data == NULL) {
                      fprintf(stderr, "Error: Can not re-allocate memory %li (Table_Read_Handle).\n",
                              malloc_size*sizeof(double));
                      return (-1);
                    }
                  }
                  if (0 == block_Num_Columns) Rows++;
                  Data[count_in_array] = X;
                  count_in_array++;
                  block_Num_Columns++;
                }
              } /* reading num: end if flag_In_array */
            } /* end reading num: end if sscanf lexeme -> numerical */
            else {
              /* reading line: the token is not numerical in that line. end block */
              if (block_Current_index == block_number) {
                flag_End_Line = 1;
                flag_End_row_loop = 1;
              } else {
                flag_In_array = 0;
                flag_End_Line = 1;
              }
            }
          }
          else {
            /* no more tokens in line */
            flag_End_Line = 1;
            if (block_Num_Columns > 0) Columns = block_Num_Columns;
          }

          // parse next token
          lexeme = strtok(NULL, seps);

        } /* while (!flag_End_Line) */
      } /* end: if fgets */
      else flag_End_row_loop = 1; /* else fgets : end of file */

    } while (!flag_End_row_loop); /* end while flag_End_row_loop */

    Table->block_number = block_number;
    Table->array_length = 1;

    // shrink header to actual size (plus terminating 0-byte)
    if (count_in_header) {
      Header = (char*)realloc(Header, count_in_header*sizeof(char) + 1);
    }
    Table->header = Header;

    if (count_in_array*Rows*Columns == 0)
    {
      Table->rows         = 0;
      Table->columns      = 0;
      free(Data);
      return (0);
    }
    if (Rows * Columns != count_in_array)
    {
      fprintf(stderr, "Warning: Read_Table :%s %s Data has %li values that should be %li x %li\n",
        (Table->filename[0] != '\0' ? Table->filename : ""),
        (!block_number ? " catenated" : ""),
        count_in_array, Rows, Columns);
      Columns = count_in_array; Rows = 1;
    }
    Data     = (double*)realloc(Data, count_in_array*sizeof(double));
    Table->data         = Data;
    Table->rows         = Rows;
    Table->columns      = Columns;

    return (count_in_array);

  } /* end Table_Read_Handle */

/*******************************************************************************
* long Table_Rebin(t_Table *Table)
*   ACTION: rebin a single Table, sorting 1st column in ascending order
*   input   Table: single table containing data.
*                  The data block is reallocated in this process
*   return  updated Table with increasing, evenly spaced first column (index 0)
*           number of data elements (-1: error, 0:empty data)
*******************************************************************************/
  long Table_Rebin(t_Table *Table)
  {
    double new_step=0;
    long   i;
    /* performs linear interpolation on X axis (0-th column) */

    if (!Table) return(-1);
    if (!Table->data 
    || Table->rows*Table->columns == 0 || !Table->step_x)
      return(0);
    Table_Stat(Table); /* recompute statitstics and minimal step */
    new_step = Table->step_x; /* minimal step in 1st column */

    if (!(Table->constantstep)) /* not already evenly spaced */
    {
      long Length_Table;
      double *New_Table;

      Length_Table = ceil(fabs(Table->max_x - Table->min_x)/new_step)+1;
      /*return early if the rebinned table will become too large*/
      if (Length_Table > mcread_table_rebin_maxsize){
        fprintf(stderr,"WARNING: (Table_Rebin): Rebinning table from %s would exceed 1M rows. Skipping.\n", Table->filename); 
        return(Table->rows*Table->columns);
      }
      New_Table    = (double*)malloc(Length_Table*Table->columns*sizeof(double));

      for (i=0; i < Length_Table; i++)
      {
        long   j;
        double X;
        X = Table->min_x + i*new_step;
        New_Table[i*Table->columns] = X;
        for (j=1; j < Table->columns; j++)
          New_Table[i*Table->columns+j]
                = Table_Value(*Table, X, j);
      } /* end for i */

      Table->rows = Length_Table;
      Table->step_x = new_step;
      Table->max_x = Table->min_x + (Length_Table-1)*new_step; 
      /*max might not be the same anymore
       * Use Length_Table -1 since the first and laset rows are the limits of the defined interval.*/
      free(Table->data);
      Table->data = New_Table;
      Table->constantstep=1;
    } /* end else (!constantstep) */
    return (Table->rows*Table->columns);
  } /* end Table_Rebin */

/*******************************************************************************
* double Table_Index(t_Table Table, long i, long j)
*   ACTION: read an element [i,j] of a single Table
*   input   Table: table containing data
*           i : index of row      (0:Rows-1)
*           j : index of column   (0:Columns-1)
*   return  Value = data[i][j]
* Returns Value from the i-th row, j-th column of Table
* Tests are performed on indexes i,j to avoid errors
*******************************************************************************/

#ifndef MIN
#define MIN(a, b)  (((a) < (b)) ? (a) : (b))
#endif
#ifndef MAX
#define MAX(a, b)  (((a) > (b)) ? (a) : (b))
#endif

double Table_Index(t_Table Table, long i, long j)
{
  long AbsIndex;

  if (Table.rows == 1 || Table.columns == 1) {
    /* vector */
    j = MIN(MAX(0, i+j), Table.columns*Table.rows - 1);
    i = 0;
  } else {
    /* matrix */
    i = MIN(MAX(0, i), Table.rows - 1);
    j = MIN(MAX(0, j), Table.columns - 1);
  }

  /* handle vectors specifically */
  AbsIndex = i*(Table.columns)+j;

  if (Table.data != NULL)
    return (Table.data[AbsIndex]);
  else
    return 0;
} /* end Table_Index */

/*******************************************************************************
* void Table_SetElement(t_Table *Table, long i, long j, double value)
*   ACTION: set an element [i,j] of a single Table
*   input   Table: table containing data
*           i : index of row      (0:Rows-1)
*           j : index of column   (0:Columns-1)
*           value = data[i][j]
* Returns 0 in case of error
* Tests are performed on indexes i,j to avoid errors
*******************************************************************************/
int Table_SetElement(t_Table *Table, long i, long j,
                     double value)
{
  long AbsIndex;

  if (Table->rows == 1 || Table->columns == 1) {
    /* vector */
    j = MIN(MAX(0, i+j), Table->columns*Table->rows - 1); i=0;
  } else {
    /* matrix */
    i = MIN(MAX(0, i), Table->rows - 1);
    j = MIN(MAX(0, j), Table->columns - 1);
  }

  AbsIndex = i*(Table->columns)+j;
  if (Table->data != NULL) {
    Table->data[AbsIndex] = value;
    return 1;
  }

  return 0;
} /* end Table_SetElement */

/*******************************************************************************
* double Table_Value(t_Table Table, double X, long j)
*   ACTION: read column [j] of a single Table at row which 1st column is X
*   input   Table: table containing data.
*           X : data value in the first column (index 0)
*           j : index of column from which is extracted the Value (0:Columns-1)
*   return  Value = data[index for X][j] with linear interpolation
* Returns Value from the j-th column of Table corresponding to the
* X value for the 1st column (index 0)
* Tests are performed (within Table_Index) on indexes i,j to avoid errors
* NOTE: data should rather be monotonic, and evenly sampled.
*******************************************************************************/
double Table_Value(t_Table Table, double X, long j)
{
  long   Index = -1;
  double X1=0, Y1=0, X2=0, Y2=0;
  double ret=0;

  if (X > Table.max_x) return Table_Index(Table,Table.rows-1  ,j);
  if (X < Table.min_x) return Table_Index(Table,0  ,j);

  // Use constant-time lookup when possible
  if(Table.constantstep) {
    Index = (long)floor(
              (X - Table.min_x) / (Table.max_x - Table.min_x) * (Table.rows-1));
    X1 = Table_Index(Table,Index-1,0);
    X2 = Table_Index(Table,Index  ,0);
  }
  // Use binary search on large, monotonic tables
  else if(Table.monotonic && Table.rows > 100) {
    long left = Table.min_x;
    long right = Table.max_x;

    while (!((X1 <= X) && (X < X2)) && (right - left > 1)) {
      Index = (left + right) / 2;

      X1 = Table_Index(Table, Index-1, 0);
      X2 = Table_Index(Table, Index,   0);

      if (X < X1) {
        right = Index;
      } else {
        left  = Index;
      }
    }
  }

  // Fall back to linear search, if no-one else has set X1, X2 correctly
  if (!((X1 <= X) && (X < X2))) {
    /* look for index surrounding X in the table -> Index */
    for (Index=1; Index <= Table.rows-1; Index++) {
        X1 = Table_Index(Table, Index-1,0);
        X2 = Table_Index(Table, Index  ,0);
        if ((X1 <= X) && (X < X2)) break;
      } /* end for Index */
  }

  Y1 = Table_Index(Table,Index-1, j);
  Y2 = Table_Index(Table,Index  , j);

#ifdef OPENACC
#define strcmp(a,b) str_comp(a,b)
#endif

  if (!strcmp(Table.method,"linear")) {
    ret = Table_Interp1d(X, X1,Y1, X2,Y2);
  }
  else if (!strcmp(Table.method,"nearest")) {
    ret = Table_Interp1d_nearest(X, X1,Y1, X2,Y2);
  }

#ifdef OPENACC
#ifdef strcmp
#undef strcmp
#endif
#endif

  return ret;
} /* end Table_Value */

/*******************************************************************************
* double Table_Value2d(t_Table Table, double X, double Y)
*   ACTION: read element [X,Y] of a matrix Table
*   input   Table: table containing data.
*           X : row index, may be non integer
*           Y : column index, may be non integer
*   return  Value = data[index X][index Y] with bi-linear interpolation
* Returns Value for the indices [X,Y]
* Tests are performed (within Table_Index) on indexes i,j to avoid errors
* NOTE: data should rather be monotonic, and evenly sampled.
*******************************************************************************/
double Table_Value2d(t_Table Table, double X, double Y)
  {
    long   x1,x2,y1,y2;
    double z11,z12,z21,z22;
    double ret=0;

    x1 = (long)floor(X);
    y1 = (long)floor(Y);

    if (x1 > Table.rows-1 || x1 < 0) {
      x2 = x1;
    } else {
      x2 = x1 + 1;
    }

    if (y1 > Table.columns-1 || y1 < 0) {
      y2 = y1;
    } else {
      y2 = y1 + 1;
    }

    z11 = Table_Index(Table, x1, y1);

    if (y2 != y1) z12=Table_Index(Table, x1, y2); else z12 = z11;
    if (x2 != x1) z21=Table_Index(Table, x2, y1); else z21 = z11;
    if (y2 != y1) z22=Table_Index(Table, x2, y2); else z22 = z21;

#ifdef OPENACC
#define strcmp(a,b) str_comp(a,b)
#endif

    if (!strcmp(Table.method,"linear"))
      ret = Table_Interp2d(X,Y, x1,y1,x2,y2, z11,z12,z21,z22);
#ifdef OPENACC
#ifdef strcmp
#undef strcmp
#endif
#endif
    else {
      if (fabs(X-x1) < fabs(X-x2)) {
        if (fabs(Y-y1) < fabs(Y-y2)) ret = z11; else ret = z12;
      } else {
        if (fabs(Y-y1) < fabs(Y-y2)) ret = z21; else ret = z22;
      }
    }
    return ret;
  } /* end Table_Value2d */


/*******************************************************************************
* void Table_Free(t_Table *Table)
*   ACTION: free a single Table. First Call Table_File_list_gc. If this returns
*   non-zero it means there are more refernces to the table, and so the table
*   should not bee freed.
*   return: empty Table
*******************************************************************************/
  void Table_Free(t_Table *Table)
  {
    if( !Table_File_List_gc(Table) ){
       return;
    } 
    if (!Table) return;
    if (Table->data   != NULL) free(Table->data);
    if (Table->header != NULL) free(Table->header);
    Table->data   = NULL;
    Table->header = NULL;
  } /* end Table_Free */

/******************************************************************************
* void Table_Info(t_Table Table)
*    ACTION: print informations about a single Table
*******************************************************************************/
  long Table_Info(t_Table Table)
  {
    char buffer[256];
    long ret=0;

    if (!Table.block_number) strcpy(buffer, "catenated");
    else sprintf(buffer, "block %li", Table.block_number);
    printf("Table from file '%s' (%s)",
        Table.filename[0] != '\0' ? Table.filename : "", buffer);
    if ((Table.data != NULL) && (Table.rows*Table.columns))
    {
      printf(" is %li x %li ", Table.rows, Table.columns);
      if (Table.rows*Table.columns > 1)
        printf("(x=%g:%g)", Table.min_x, Table.max_x);
      else printf("(x=%g) ", Table.min_x);
      ret = Table.rows*Table.columns;
      if (Table.monotonic)    printf(", monotonic");
      if (Table.constantstep) printf(", constant step");
      printf(". interpolation: %s\n", Table.method);
    }
    else printf(" is empty.\n");

    if (Table.header && strlen(Table.header)) {
      char *header;
      int  i;
      header = malloc(80);
      if (!header) return(ret);
      for (i=0; i<80; header[i++]=0);
      strncpy(header, Table.header, 75);
      if (strlen(Table.header) > 75) {
        strcat( header, " ...");
      }
      for (i=0; i<strlen(header); i++)
        if (header[i] == '\n' || header[i] == '\r') header[i] = ';';
      printf("  '%s'\n", header);
      free(header);
    }

    return(ret);
  } /* end Table_Info */

/******************************************************************************
* long Table_Init(t_Table *Table, m, n)
*   ACTION: initialise a Table to empty m by n table
*   return: empty Table
******************************************************************************/
long Table_Init(t_Table *Table, long rows, long columns)
{
  double *data=NULL;
  long   i;

  if (!Table) return(0);

  Table->header  = NULL;
  Table->filename[0]= '\0';
  Table->filesize= 0;
  Table->min_x   = 0;
  Table->max_x   = 0;
  Table->step_x  = 0;
  Table->block_number = 0;
  Table->array_length = 0;
  Table->monotonic    = 0;
  Table->constantstep = 0;
  Table->begin   = 0;
  Table->end     = 0;
  strcpy(Table->method,"linear");

  if (rows*columns >= 1) {
    data    = (double*)malloc(rows*columns*sizeof(double));
    if (data) for (i=0; i < rows*columns; data[i++]=0);
    else {
      if(Table->quiet<2)
        fprintf(stderr,"Error: allocating %ld double elements."
            "Too big (Table_Init).\n", rows*columns);
      rows = columns = 0;
    }
  }
  Table->rows    = (rows >= 1 ? rows : 0);
  Table->columns = (columns >= 1 ? columns : 0);
  Table->data    = data;
  return(Table->rows*Table->columns);
} /* end Table_Init */

/******************************************************************************
* long Table_Write(t_Table Table, char *file, x1,x2, y1,y2)
*   ACTION: write a Table to disk (ascii).
*     when x1=x2=0 or y1=y2=0, the table default limits are used.
*   return: 0=all is fine, non-0: error
*******************************************************************************/
MCDETECTOR Table_Write(t_Table Table, char *file, char *xl, char *yl, 
  double x1, double x2, double y1, double y2)
{
  MCDETECTOR detector;

  if ((Table.data == NULL) && (Table.rows*Table.columns)) {
    detector.m = 0;
    return(detector); /* Table is empty - nothing to do */
  }
  if (!x1 && !x2) {
    x1 = Table.min_x;
    x2 = Table.max_x;
  }
  if (!y1 && !y2) {
    y1 = 1;
    y2 = Table.columns;
  }

  /* transfer content of the Table into a 2D detector */
  Coords coords = { 0, 0, 0};

  if (Table.rows == 1 || Table.columns == 1) {
    detector = mcdetector_out_1D(Table.filename,
                      xl ? xl : "", yl ? yl : "",
                      "x", x1, x2,
                      Table.rows * Table.columns,
                      NULL, Table.data, NULL,
                      file, file, coords);
  } else {
    detector = mcdetector_out_2D(Table.filename,
                      xl ? xl : "", yl ? yl : "",
                      x1, x2, y1, y2,
                      Table.rows, Table.columns,
                      NULL, Table.data, NULL,
                      file, file, coords);
  }
  return(detector);
}

/******************************************************************************
* void Table_Stat(t_Table *Table)
*   ACTION: computes min/max/mean step of 1st column for a single table (private)
*   return: updated Table
*******************************************************************************/
  static void Table_Stat(t_Table *Table)
  {
    long   i;
    double max_x, min_x;
    double row=1;
    char   monotonic=1;
    char   constantstep=1;
    double step=0;
    long n;

    if (!Table) return;
    if (!Table->rows || !Table->columns) return;
    if (Table->rows == 1) row=0; // single row
    max_x = -FLT_MAX;
    min_x =  FLT_MAX;
    n     = (row ? Table->rows : Table->columns);
    /* get min and max of first column/vector */
    for (i=0; i < n; i++)
    {
      double X;
      X = (row ? Table_Index(*Table,i  ,0)
                               : Table_Index(*Table,0, i));
      if (X < min_x) min_x = X;
      if (X > max_x) max_x = X;
    } /* for */
    
    /* test for monotonicity and constant step if the table is an XY or single vector */
    if (n > 1) {
      /* mean step */
      step = (max_x - min_x)/(n-1);
      /* now test if table is monotonic on first column, and get minimal step size */
      for (i=0; i < n-1; i++) {
        double X, diff;;
        X    = (row ? Table_Index(*Table,i  ,0)
                    : Table_Index(*Table,0,  i));
        diff = (row ? Table_Index(*Table,i+1,0)
                    : Table_Index(*Table,0,  i+1)) - X;
        if (diff && fabs(diff) < fabs(step)) step = diff;
        /* change sign ? */
        if ((max_x - min_x)*diff < 0 && monotonic)
          monotonic = 0;
      } /* end for */
      
      /* now test if steps are constant within READ_TABLE_STEPTOL */
      if(!step){
        /*means there's a disconitnuity -> not constantstep*/
        constantstep=0;
      }else if (monotonic) {
        for (i=0; i < n-1; i++) {
          double X, diff;
          X    = (row ? Table_Index(*Table,i  ,0)
              : Table_Index(*Table,0,  i));
          diff = (row ? Table_Index(*Table,i+1,0)
              : Table_Index(*Table,0,  i+1)) - X;
          if ( fabs(step)*(1+READ_TABLE_STEPTOL) < fabs(diff) ||
                fabs(diff) < fabs(step)*(1-READ_TABLE_STEPTOL) )
          { constantstep = 0; break; }
        }
      }

    }
    Table->step_x= step;
    Table->max_x = max_x;
    Table->min_x = min_x;
    Table->monotonic = monotonic;
    Table->constantstep = constantstep;
  } /* end Table_Stat */

/******************************************************************************
* t_Table *Table_Read_Array(char *File, long *blocks)
*   ACTION: read as many data blocks as available, iteratively from file
*   return: initialized t_Table array, last element is an empty Table.
*           the number of extracted blocks in non NULL pointer *blocks
*******************************************************************************/
  t_Table *Table_Read_Array(char *File, long *blocks)
  {
    t_Table *Table_Array=NULL;
    long offset=0;
    long block_number=0;
    long allocated=256;
    long nelements=1;

    /* first allocate an initial empty t_Table array */
    Table_Array = (t_Table *)malloc(allocated*sizeof(t_Table));
    if (!Table_Array) {
      fprintf(stderr, "Error: Can not allocate memory %li (Table_Read_Array).\n",
         allocated*sizeof(t_Table));
      *blocks = 0;
      return (NULL);
    }

    while (nelements > 0)
    {
      t_Table Table;

      /* if ok, set t_Table block number else exit loop */
      block_number++;
      Table.block_number = block_number;
      
      /* access file at offset and get following block. Block number is from the set offset
       * hence the hardcoded 1 - i.e. the next block counted from offset.*/
      nelements = Table_Read_Offset(&Table, File, 1, &offset,0);
      /*if the block is empty - don't store it*/
      if (nelements>0){
          /* if t_Table array is not long enough, expand and realocate */
          if (block_number >= allocated-1) {
              allocated += 256;
              Table_Array = (t_Table *)realloc(Table_Array,
                      allocated*sizeof(t_Table));
              if (!Table_Array) {
                  fprintf(stderr, "Error: Can not re-allocate memory %li (Table_Read_Array).\n",
                          allocated*sizeof(t_Table));
                  *blocks = 0;
                  return (NULL);
              }
          }
          /* store it into t_Table array */
          //snprintf(Table.filename, 1024, "%s#%li", File, block_number-1);
          Table_Array[block_number-1] = Table;
      }
      /* continues until we find an empty block */
    }
    /* send back number of extracted blocks */
    if (blocks) *blocks = block_number-1;

    /* now store total number of elements in Table array */
    for (offset=0; offset < block_number;
      Table_Array[offset++].array_length = block_number-1);

    return(Table_Array);
  } /* end Table_Read_Array */
/*******************************************************************************
* void Table_Free_Array(t_Table *Table)
*   ACTION: free a Table array
*******************************************************************************/
  void Table_Free_Array(t_Table *Table)
  {
    long index;
    if (!Table) return;
    for (index=0;index < Table[0].array_length; index++){
            Table_Free(&Table[index]);
    }
    free(Table);
  } /* end Table_Free_Array */

/******************************************************************************
* long Table_Info_Array(t_Table *Table)
*    ACTION: print informations about a Table array
*    return: number of elements in the Table array
*******************************************************************************/
  long Table_Info_Array(t_Table *Table)
  {
    long index=0;

    if (!Table) return(-1);
    while (index < Table[index].array_length
       && (Table[index].data || Table[index].header)
       && (Table[index].rows*Table[index].columns) ) {
      Table_Info(Table[index]);
      index++;
    }
    printf("This Table array contains %li elements\n", index);
    return(index);
  } /* end Table_Info_Array */

/******************************************************************************
* char **Table_ParseHeader(char *header, symbol1, symbol2, ..., NULL)
*    ACTION: search for char* symbols in header and return their value or NULL
*            the search is not case sensitive.
*            Last argument MUST be NULL
*    return: array of char* with line following each symbol, or NULL if not found
*******************************************************************************/
#ifndef MyNL_ARGMAX
#define MyNL_ARGMAX 50
#endif

char **Table_ParseHeader_backend(char *header, ...){
  va_list ap;
  char exit_flag=0;
  int counter   =0;
  char **ret    =NULL;
  if (!header || header[0]=='\0') return(NULL);

  ret = (char**)calloc(MyNL_ARGMAX, sizeof(char*));
  if (!ret) {
    printf("Table_ParseHeader: Cannot allocate %i values array for Parser (Table_ParseHeader).\n",
      MyNL_ARGMAX);
    return(NULL);
  }
  for (counter=0; counter < MyNL_ARGMAX; ret[counter++] = NULL);
  counter=0;

  va_start(ap, header);
  while(!exit_flag && counter < MyNL_ARGMAX-1)
  {
    char *arg_char=NULL;
    char *pos     =NULL;
    /* get variable argument value as a char */
    arg_char = va_arg(ap, char *);
    if (!arg_char || arg_char[0]=='\0'){
      exit_flag = 1; break;
    }
    /* search for the symbol in the header */
    pos = (char*)strcasestr(header, arg_char);
    if (pos) {
      char *eol_pos;
      eol_pos = strchr(pos+strlen(arg_char), '\n');
      if (!eol_pos)
        eol_pos = strchr(pos+strlen(arg_char), '\r');
      if (!eol_pos)
        eol_pos = pos+strlen(pos)-1;
      ret[counter] = (char*)malloc(eol_pos - pos);
      if (!ret[counter]) {
        printf("Table_ParseHeader: Cannot allocate value[%i] array for Parser searching for %s (Table_ParseHeader).\n",
          counter, arg_char);
        exit_flag = 1; break;
      }
      strncpy(ret[counter], pos+strlen(arg_char), eol_pos - pos - strlen(arg_char));
      ret[counter][eol_pos - pos - strlen(arg_char)]='\0';
    }
    counter++;
  }
  va_end(ap);
  return(ret);
} /* Table_ParseHeader */

/******************************************************************************
* double Table_Interp1d(x, x1, y1, x2, y2)
*    ACTION: interpolates linearly at x between y1=f(x1) and y2=f(x2)
*    return: y=f(x) value
*******************************************************************************/
double Table_Interp1d(double x,
  double x1, double y1,
  double x2, double y2)
{
  double slope;
  if (x2 == x1) return (y1+y2)/2;
  if (y1 == y2) return  y1;
  slope = (y2 - y1)/(x2 - x1);
  return y1+slope*(x - x1);
} /* Table_Interp1d */

/******************************************************************************
* double Table_Interp1d_nearest(x, x1, y1, x2, y2)
*    ACTION: table lookup with nearest method at x between y1=f(x1) and y2=f(x2)
*    return: y=f(x) value
*******************************************************************************/
double Table_Interp1d_nearest(double x,
  double x1, double y1,
  double x2, double y2)
{
  if (fabs(x-x1) < fabs(x-x2)) return (y1);
  else return(y2);
} /* Table_Interp1d_nearest */

/******************************************************************************
* double Table_Interp2d(x,y, x1,y1, x2,y2, z11,z12,z21,z22)
*    ACTION: interpolates bi-linearly at (x,y) between z1=f(x1,y1) and z2=f(x2,y2)
*    return: z=f(x,y) value
*    x,y |   x1   x2
*    ----------------
*     y1 |   z11  z21
*     y2 |   z12  z22
*******************************************************************************/
double Table_Interp2d(double x, double y,
  double x1, double y1,
  double x2, double y2,
  double z11, double z12, double z21, double z22)
{
  double ratio_x, ratio_y;
  if (x2 == x1) return Table_Interp1d(y, y1,z11, y2,z12);
  if (y1 == y2) return Table_Interp1d(x, x1,z11, x2,z21);

  ratio_y = (y - y1)/(y2 - y1);
  ratio_x = (x - x1)/(x2 - x1);
  return (1-ratio_x)*(1-ratio_y)*z11 + ratio_x*(1-ratio_y)*z21
    + ratio_x*ratio_y*z22         + (1-ratio_x)*ratio_y*z12;
} /* Table_Interp2d */

/* end of read_table-lib.c */

/*****************************************************************************
*
* McStas, neutron ray-tracing package
*         Copyright 1997-2006, All rights reserved
*         Risoe National Laboratory, Roskilde, Denmark
*         Institut Laue Langevin, Grenoble, France
*
* Library: share/ref-lib.h
*
* %Identification
* Written by: Peter Christiansen
* Date: August, 2006
* Origin: RISOE
* Release: McStas 1.10
* Version: $Revision$
*
* Add StdDoubleReflecFunc, ExtendedReflecFunc
* Date: October, 2022
* Locale: ESS
* Release: McStas 2.7x, 3.1x
*
* Commonly used reflection functions are declared in this file which
* are used by some guide and mirror components.
*
* Depends on read_table-lib
*
* Usage: within SHARE
* %include "ref-lib"
*
****************************************************************************/


#ifndef REF_LIB_H
#define REF_LIB_H "$Revision$"

void StdReflecFunc(double, double*, double*);
void TableReflecFunc(double, t_Table*, double*);
void StdDoubleReflecFunc(double, double*, double*);
void ExtendedReflecFunc(double, double*, double*);

#endif

/* end of ref-lib.h */
/****************************************************************************
*
* McStas, neutron ray-tracing package
*         Copyright 1997-2006, All rights reserved
*         Risoe National Laboratory, Roskilde, Denmark
*         Institut Laue Langevin, Grenoble, France
*
* Library: share/ref-lib.c
*
* %Identification
* Written by: Peter Christiansen
* Date: August, 2006
* Origin: RISOE
* Release: McStas 1.10
* Version: $Revision$
*
* Add StdDoubleReflecFunc, ExtendedReflecFunc
* Date: October, 2022
* Locale: ESS
* Release: McStas 2.7.x, 3.x
*
* Commonly used reflection functions are declared in this file which
* are used by some guide and mirror components.
*
* Variable names have prefix 'mc_ref_' for 'McStas Reflection'
* to avoid conflicts
*
* Usage: within SHARE
* %include "ref-lib"
*
****************************************************************************/

#ifndef REF_LIB_H
#include "ref-lib.h"
#endif

#ifndef READ_TABLE_LIB_H
#include "read_table-lib.h"
#include "read_table-lib.c"
#endif

/****************************************************************************
* void StdReflecFunc(double q, double *par, double *r)
*
* The McStas standard analytic parametrization of the reflectivity.
* The parameters are:
* R0:      [1]    Low-angle reflectivity
* Qc:      [AA-1] Critical scattering vector
* alpha:   [AA]   Slope of reflectivity
* m:       [1]    m-value of material. Zero means completely absorbing.
* W:       [AA-1] Width of supermirror cut-off
*****************************************************************************/
#pragma acc routine seq
void StdReflecFunc(double mc_pol_q, double *mc_pol_par, double *mc_pol_r) {
    double R0    = mc_pol_par[0];
    double Qc    = mc_pol_par[1];
    double alpha = mc_pol_par[2];
    double m     = mc_pol_par[3];
    double W     = mc_pol_par[4];
    double beta  = 0;
    mc_pol_q     = fabs(mc_pol_q);
    double arg;
    double m_corr;

    /* Simpler parametrization from Henrik Jacobsen uses these values that depend on m only.
       double m_value=m*0.9853+0.1978;
       double W=-0.0002*m_value+0.0022;
       double alpha=0.2304*m_value+5.0944;
       double beta=-7.6251*m_value+68.1137;
       If W and alpha are set to 0, use Henrik's approach for estimating these parameters
       and apply the formulation:
       arg = R0*0.5*(1-tanh(arg))*(1-alpha*(q-Qc)+beta*(q-Qc)*(q-Qc));
    */
    if (W==0 && alpha==0) {
      m = m * 0.9853 + 0.1978;
      m_corr = m * 0.9853 - 0.7875;
      W = -0.0002 * m_corr + 0.0022;
      alpha = 0.2304 * m_corr + 5.0944;
      beta = -7.6251 * m_corr + 68.1137;
      if (m==3) {
	alpha = m_corr;
	beta = 0;
      }
      arg = (mc_pol_q - m*Qc)/W; // <--- here m, not m_corr!!
    }

    arg = W > 0 ? (mc_pol_q - m*Qc)/W : 11;

    if (arg > 10 || m <= 0 || Qc <=0 || R0 <= 0) {
      *mc_pol_r = 0;
      return;
    }

    if (m < 1) { Qc *= m; m=1; }

    if(mc_pol_q <= Qc) {
      *mc_pol_r = R0;
      return;
    }


    *mc_pol_r = R0*0.5*(1 - tanh(arg))*(1 - alpha*(mc_pol_q - Qc) + beta*(mc_pol_q - Qc)*(mc_pol_q - Qc));

    return;
  }

/****************************************************************************
* void TableReflecFunc(double q, t_Table *par, double *r) {
*
* Looks up the reflectivity in a table using the routines in read_table-lib.
*****************************************************************************/
#pragma acc routine seq
void TableReflecFunc(double mc_pol_q, t_Table *mc_pol_par, double *mc_pol_r) {

  *mc_pol_r = Table_Value(*mc_pol_par, mc_pol_q, 1);
  if(*mc_pol_r>1)
    *mc_pol_r = 1;
  return;
}


/****************************************************************************
* void StdDoubleReflecFunc(double q, double *par, double *r)
* 
* The McStas standard analytic parametrization of the reflectivity for 
* double-side coated supermirror.
* The parameters are:
* R0:      [1]    Low-angle reflectivity
* Qc:      [AA-1] Critical scattering vector
* alpha:   [AA]   Slope of reflectivity
* m:       [1]    m-value of material. Zero means completely absorbing.
* W:       [AA-1] Width of supermirror cut-off
*****************************************************************************/
void StdDoubleReflecFunc(double mc_pol_q, double *mc_pol_par, double *mc_pol_r) {
    double R0    = mc_pol_par[0];
    double Qc    = mc_pol_par[1];
    double alpha = mc_pol_par[2];
    double m     = mc_pol_par[3];
    double W     = mc_pol_par[4];
    double beta  = 0;
    mc_pol_q     = fabs(mc_pol_q);
    double arg;
        
    /* Simpler parametrization from Henrik Jacobsen uses these values that depend on m only.
       double m_value=m*0.9853+0.1978;
       double W=-0.0002*m_value+0.0022;
       double alpha=0.2304*m_value+5.0944;
       double beta=-7.6251*m_value+68.1137; 
       If W and alpha are set to 0, use Henrik's approach for estimating these parameters
       and apply the formulation:
       arg = R0*0.5*(1-tanh(arg))*(1-alpha*(q-Qc)+beta*(q-Qc)*(q-Qc));
    */  
    if (W==0 && alpha==0) {
      m=m*0.9853+0.1978;
      W=-0.0002*m+0.0022;
      alpha=0.2304*m+5.0944;
      beta=-7.6251*m+68.1137;
      if (m<=3) {
	alpha=m;
	beta=0;
      }
    }
    
    arg = W > 0 ? (mc_pol_q - m*Qc)/W : 11;

    if (arg > 10 || m <= 0 || Qc <=0 || R0 <= 0) {
      *mc_pol_r = 0;
      return;
    }
    
    if (m < 1) { Qc *= m; m=1; }
    
	/*
		Reflectivity R0 = single-side coated supermirror reflectivity
		double-side coated supermirror reflectivity = 1- (1-R0)^2
	*/
	
    if(mc_pol_q <= Qc) {      
      *mc_pol_r = 1- (1-R0)*(1-R0);
      return;
    }
    
    
    R0 = R0*0.5*(1 - tanh(arg))*(1 - alpha*(mc_pol_q - Qc) + beta*(mc_pol_q - Qc)*(mc_pol_q - Qc));
    *mc_pol_r = 1- (1-R0)*(1-R0);
   
    return;
  }

void ExtendedReflecFunc(double mc_pol_q, double *mc_pol_par, double *mc_pol_r) {
    double R0    = mc_pol_par[0];
    double Qc    = mc_pol_par[1];
    double alpha = mc_pol_par[2];
    double m     = mc_pol_par[3];
    double W     = mc_pol_par[4];
    double beta  = mc_pol_par[5];
    mc_pol_q     = fabs(mc_pol_q);
    double arg;

    /* Simpler parametrization from Henrik Jacobsen uses these values that depend on m only.
       double m_value=m*0.9853+0.1978;
       double W=-0.0002*m_value+0.0022;
       double alpha=0.2304*m_value+5.0944;
       double beta=-7.6251*m_value+68.1137;
       If W and alpha are set to 0, use Henrik's approach for estimating these parameters
       and apply the formulation:
       arg = R0*0.5*(1-tanh(arg))*(1-alpha*(q-Qc)+beta*(q-Qc)*(q-Qc));
    */
    if (W==0 && alpha==0) {
      m=m*0.9853+0.1978;
      W=-0.0002*m+0.0022;
      alpha=0.2304*m+5.0944;
      beta=-7.6251*m+68.1137;
      if (m<=3) {
	alpha=m;
	beta=0;
      }
    }

    arg = W > 0 ? (mc_pol_q - m*Qc)/W : 11;

    if (arg > 10 || m <= 0 || Qc <=0 || R0 <= 0) {
      *mc_pol_r = 0;
      return;
    }

    if (m < 1) { Qc *= m; m=1; }

    if(mc_pol_q <= Qc) {
      *mc_pol_r = R0;
      return;
    }


    *mc_pol_r = R0*0.5*(1 - tanh(arg))*(1 - alpha*(mc_pol_q - Qc) + beta*(mc_pol_q - Qc)*(mc_pol_q - Qc));

    return;
  }

/* end of ref-lib.c */


/* Shared user declarations for all components types 'Monochromator_flat'. */
#ifndef GAUSS
/* Define these arrays only once for all instances. */
/* Values for Gauss quadrature. Taken from Brice Carnahan, H. A. Luther and
James O Wilkes, "Applied numerical methods", Wiley, 1969, page 103.
This reference is available from the Copenhagen UB2 library */
double Gauss_X[] = {-0.987992518020485, -0.937273392400706, -0.848206583410427,
-0.724417731360170, -0.570972172608539, -0.394151347077563,
-0.201194093997435, 0, 0.201194093997435,
0.394151347077563, 0.570972172608539, 0.724417731360170,
0.848206583410427, 0.937273392400706, 0.987992518020485};
double Gauss_W[] = {0.030753241996117, 0.070366047488108, 0.107159220467172,
0.139570677926154, 0.166269205816994, 0.186161000115562,
0.198431485327111, 0.202578241925561, 0.198431485327111,
0.186161000115562, 0.166269205816994, 0.139570677926154,
0.107159220467172, 0.070366047488108, 0.030753241996117};
#pragma acc declare create ( Gauss_X )
#pragma acc declare create ( Gauss_W )

#define GAUSS(x,mean,rms) \
  (exp(-((x)-(mean))*((x)-(mean))/(2*(rms)*(rms)))/(sqrt(2*PI)*(rms)))
#endif

/* Shared user declarations for all components types 'Slit'. */
void slit_print_if(int condition, char* level, char* message, char* component){
  if (condition) fprintf(stderr, "Slit: %s: %s: %s\n", component, level, message);
} 
void slit_error_if(int condition, char* message, char* component){
  slit_print_if(condition, "Error", message, component);
  if (condition) exit(-1);
}
void slit_warning_if(int condition, char* message, char* component){
  slit_print_if(condition, "Warning", message, component);
}

/* Shared user declarations for all components types 'Union_init'. */

#ifdef Union
printf("ERROR: The Union_init component needs to be the first Union component!\n")
exit(1);
#endif

#ifndef Union
#define Union $Revision: 0.8 $

/*******************************************************************************
*
*  McStas, neutron ray-tracing package
*  Copyright(C) 2007 Risoe National Laboratory.
*
* %I
* Written by: Mads Bertelsen
* Date: 20.08.15
* Version: $Revision: 0.1 $
* Origin: University of Copenhagen
*
* Functions and structure definitons for Union components.
*
******************************************************************************/

// -------------    Definition of data structures   ---------------------------------------------
// GPU
enum shape {
  surroundings,
  box,
  sphere,
  cylinder,
  cone,
  mesh
};

enum process {
  Incoherent,
  Powder,
  Single_crystal,
  AF_HB_1D,
  PhononSimple,
  Texture,
  IncoherentPhonon,
  NCrystal,
  Template
};

struct intersection_time_table_struct {
int num_volumes;
int *calculated;
int *n_elements;
double **intersection_times;
};

struct line_segment{
//struct position point1;
//struct position point2;
Coords point1;
Coords point2;
int number_of_dashes;
};

struct pointer_to_1d_int_list {
int num_elements;
int *elements;
#pragma acc shape(elements[0:num_elements]) init_needed(num_elements)
};

struct pointer_to_1d_double_list {
int num_elements;
double *elements;
#pragma acc shape(elements[0:num_elements]) init_needed(num_elements)
};

struct pointer_to_1d_coords_list {
int num_elements;
Coords *elements;
#pragma acc shape(elements[0:num_elements]) init_needed(num_elements)
};

struct lines_to_draw{
int number_of_lines;
struct line_segment *lines;
#pragma acc shape(lines[0:number_of_lines]) init_needed(number_of_lines)
};

// 2D lists not needed anyway
// struct pointer_to_2d_int_list {
// int n_lists;
// struct pointer_to_1d_int_list *lists;
// }

// Todo: see if the union geometry_parameter_union and other geometry structs can be here
union geometry_parameter_union{
    struct sphere_storage   *p_sphere_storage;
    struct cylinder_storage *p_cylinder_storage;
    struct box_storage      *p_box_storage;
    struct cone_storage     *p_cone_storage;
    struct mesh_storage     *p_mesh_storage;
    // add as many pointers to structs as wanted, without increasing memory footprint.
};


struct rotation_struct{
double x;
double y;
double z;
};

struct focus_data_struct {
Coords Aim;
double angular_focus_width;
double angular_focus_height;
double spatial_focus_width;
double spatial_focus_height;
double spatial_focus_radius;
Rotation absolute_rotation;
// focusing_function creates a vector per selected criteria of focus_data_struct / selected focus function and returns solid angle
void (*focusing_function)(Coords*, double*, struct focus_data_struct*);
//                        v_out  , solid_a,
};

struct focus_data_array_struct
{
struct focus_data_struct *elements;
int num_elements;
#pragma acc shape(elements[0:num_elements]) init_needed(num_elements)
};

struct Detector_3D_struct {
  char title_string[256];
  char string_axis_1[256];
  char string_axis_2[256];
  char string_axis_3[256];
  char Filename[256];
  double D1min;
  double D1max;
  double D2min;
  double D2max;
  double D3min;
  double D3max;
  double bins_1; // McStas uses doubles for bin numbers for some reason
  double bins_2;
  double bins_3;
  double ***Array_N; // McStas uses doubles for number of rays in each bin for some reason
  double ***Array_p;
  double ***Array_p2;
};

struct Detector_2D_struct {
  char title_string[256];
  char string_axis_1[256];
  char string_axis_2[256];
  char Filename[256];
  double D1min;
  double D1max;
  double D2min;
  double D2max;
  double bins_1; // McStas uses doubles for bin numbers for some reason
  double bins_2;
  double **Array_N; // McStas uses doubles for number of rays in each bin for some reason
  double **Array_p;
  double **Array_p2;
};

struct Detector_1D_struct {
  char title_string[256];
  char string_axis[256];
  char string_axis_short[64];
  char string_axis_value[256];
  char Filename[256];
  double min;
  double max;
  double bins; // McStas uses doubles for bin numbers for some reason
  double *Array_N; // McStas uses doubles for number of rays in each bin for some reason
  double *Array_p;
  double *Array_p2;
};


union logger_data_union{
  struct a_2DQ_storage_struct *p_2DQ_storage;
  struct a_2DS_storage_struct *p_2DS_storage;
  struct a_3DS_storage_struct *p_3DS_storage;
  struct a_1D_storage_struct *p_1D_storage;
  struct a_2DS_t_storage_struct *p_2DS_t_storage;
  struct a_2D_kf_storage_struct *p_2D_kf_storage;
  struct a_2D_kf_t_storage_struct *p_2D_kf_t_storage;
  // Additional logger storage structs to be addedd
};

struct logger_with_data_struct {
  int used_elements;
  int allocated_elements;
  struct logger_struct **logger_pointers;
};

// logger_pointer_struct
// contains pointers to the different logger functions and it's data union
struct logger_pointer_set_struct {
  // The logger has two record functions, an active and an inactive. Normally the active one will be to permanent storage,
  //  but if a conditional has been defined, it can switch the two, making the active one recording to temporary, which
  //  can then be filtered based on the future path of the ray
  
  // function input Coords position, k[3], k_old[3], p, p_old, NV, NPV, N, logger_data_union, logger_with_data_struct
  void (*active_record_function)(Coords*, double*, double*, double, double, double, int, int, int, struct logger_struct*, struct logger_with_data_struct*);
  void (*inactive_record_function)(Coords*, double*, double*, double, double, double, int, int, int, struct logger_struct*, struct logger_with_data_struct*);
  
  // A clear temporary data function (for new ray)
  void (*clear_temp)(union logger_data_union*);
  
  // Write temporary to permanent is used when the record to temp function is active, and the condition is met.
  void (*temp_to_perm)(union logger_data_union*);
  
  // Write temporary final_p to permanent is used when the record to temp function is active, and the condition is met
  //  and the final weight is given to be used for all stored events in the logger.
  void (*temp_to_perm_final_p)(union logger_data_union*, double);
  
  // Select which temp_to_perm function to use
  int select_t_to_p; // 1: temp_to_perm, 2: temp_to_perm_final_p
  
};

union abs_logger_data_union{
  struct a_2D_abs_storage_struct *p_2D_abs_storage;
  struct a_1D_abs_storage_struct *p_1D_abs_storage;
  struct a_1D_time_abs_storage_struct *p_1D_time_abs_storage;
  struct a_1D_time_to_lambda_abs_storage_struct *p_1D_time_to_lambda_abs_storage;
  struct a_event_abs_storage_struct *p_event_abs_storage;
  struct a_1D_event_abs_storage_struct *p_1D_event_abs_storage;
  // Additional logger storage structs to be addedd
};

struct abs_logger_with_data_struct {
  int used_elements;
  int allocated_elements;
  struct abs_logger_struct **abs_logger_pointers;
};

struct abs_logger_pointer_set_struct {
  // The logger has two record functions, an active and an inactive. Normally the active one will be to permanent storage,
  //  but if a conditional has been defined, it can switch the two, making the active one recording to temporary, which
  //  can then be filtered based on the future path of the ray
  
  // function input Coords position, k[3], p, NV, N, logger_data_union, logger_with_data_struct
  void (*active_record_function)(Coords*, double*, double,  double, int, int, struct abs_logger_struct*, struct abs_logger_with_data_struct*);
  void (*inactive_record_function)(Coords*, double*, double,  double, int, int, struct abs_logger_struct*, struct abs_logger_with_data_struct*);
  
  // A clear temporary data function (for new ray)
  void (*clear_temp)(union abs_logger_data_union*);
  
  // Write temporary to permanent is used when the record to temp function is active, and the condition is met.
  void (*temp_to_perm)(union abs_logger_data_union*);
  
  // Write temporary final_p to permanent is used when the record to temp function is active, and the condition is met
  //  and the final weight is given to be used for all stored events in the logger.
  void (*temp_to_perm_final_p)(union abs_logger_data_union*, double);
  
  // Select which temp_to_perm function to use
  //int select_t_to_p; // 1: temp_to_perm, 2: temp_to_perm_final_p
  
};


struct conditional_standard_struct{
  // Data to be transfered to the conditional function
  double Emax;
  double Emin;
  int E_limit;
  
  double Tmin;
  double Tmax;
  int T_limit;
  
  int volume_index;
  
  double Total_scat_max;
  double Total_scat_min;
  int Total_scat_limit;
  
  double exit_volume_index;
  
  // Test
  Coords test_position;
  Rotation test_rotation;
  Rotation test_t_rotation;
};

struct conditional_PSD_struct{
  double PSD_half_xwidth;
  double PSD_half_yheight;
  
  double Tmin;
  double Tmax;
  int T_limit;
  
  // Position of the PSD
  Coords PSD_position;
  Rotation PSD_rotation;
  Rotation PSD_t_rotation;
};

union conditional_data_union {
  struct conditional_standard_struct *p_standard;
  struct conditional_PSD_struct *p_PSD;
  // Add more as conditional components are made
};

// General input for conditional functions: Position, Velocity/Wavevector, weight, time, total_scat, scattered_flag, scattered_flag_VP,
// Optional extras: data_union? tree base(s)? tree base for current ray?
typedef int (*conditional_function_pointer)(union conditional_data_union*,Coords*, Coords*, double*, double*, int*, int*, int*, int**);
//typedef int (**conditional_function_pointer_array)(union *conditional_data_union,Coords*, Coords*, double*, double*, int*, int*, int**);

struct conditional_list_struct{
  int num_elements;
  
  union conditional_data_union **p_data_unions;
  conditional_function_pointer *conditional_functions;
  //int (**conditional_functions)(Coords*, Coords*, double*, double*, int*, int*, int**);
};

struct logger_struct {
  char name[256];
  // Contains ponters to all the functions assosiated with this logger
  struct logger_pointer_set_struct function_pointers;
  // Contains hard copy of logger_data_union since the size is the same as a pointer.
  union logger_data_union data_union;
  
  int logger_extend_index; // Contain index conditional_extend_array defined in master that can be acsessed from extend section.
  
  struct conditional_list_struct conditional_list;
};


// To be stored in volume, a list of pointers to the relevant loggers corresponding to each process
struct logger_for_each_process_list {
  int num_elements;
  struct logger_struct **p_logger_process;
};

// List of logger_for_each_process_list
struct loggers_struct {
  int num_elements;
  struct logger_for_each_process_list *p_logger_volume;
  #pragma acc shape(p_logger_volume[0:num_elements]) init_needed(num_elements)
};


struct abs_logger_struct {
  char name[256];
  // Contains ponters to all the functions assosiated with this logger
  struct abs_logger_pointer_set_struct function_pointers;
  // Contains hard copy of logger_data_union since the size is the same as a pointer.
  union abs_logger_data_union data_union;
  
  // Position and rotation of the abs_logger
  Coords position;
  Rotation rotation;
  Rotation t_rotation;
  
  int abs_logger_extend_index; // Contain index conditional_extend_array defined in master that can be acsessed from extend section.
  
  struct conditional_list_struct conditional_list;
};

// To be stored in volume, a list of pointers to the relevant abs loggers corresponding to each process
/*
struct abs_logger_for_each_process_list {
  int num_elements;
  struct abs_logger_struct **p_abs_logger_process;
};
*/

// List of abs logger_for_each_process_list
struct abs_loggers_struct {
  int num_elements;
  //struct abs_logger_for_each_process_list *p_abs_logger_volume;
  struct abs_logger_struct **p_abs_logger;
};



struct geometry_struct
{
char shape[64];     // name of shape used (sphere, cylinder, box, off, ...)
enum shape eShape;  // enum with shape for flexible functions GPU
double priority_value;    // priority of the geometry
Coords center;      // Center position of volume, reported by components in global frame, updated to main frame in initialize
// Rotation of this volume
Rotation rotation_matrix; // rotation matrix of volume, reported by component in global frame, updated to main frame in initialize
Rotation transpose_rotation_matrix; // As above
// Array of prrotation matrixes for processes assigned to this volume (indexed by non_isotropic_rot_index in the processes)
Rotation *process_rot_matrix_array;             // matrix that transforms from main coordinate system to local process in this specific volume
Rotation *transpose_process_rot_matrix_array;   // matrix that transforms from local process in this specific volume to main coordinate system
int process_rot_allocated;  // Keeps track of allocation status of rot_matrix_array

struct rotation_struct rotation; // Not used, is the x y and z rotation angles.
int visualization_on; // If visualization_on is true, the volume will be drawn in mcdisplay, otherwise not
int is_exit_volume; // If is exit volume = 1, the ray will exit the component when it enters this volume.
int is_mask_volume; // 1 if volume itself is a mask (masking the ones in it's mask list), otherwise 0
int mask_index;
int is_masked_volume; // 1 if this volume is being masked by another volume, the volumes that mask it is in masked_by_list
int mask_mode; // ALL/ANY 1/2. In ALL mode, only parts covered by all masks is simulated, in ANY mode, area covered by just one mask is simulated
double geometry_p_interact; // fraction of rays that interact with this volume for each scattering (between 0 and 1, 0 for disable)
union geometry_parameter_union geometry_parameters; // relevant parameters for this shape
union geometry_parameter_union (*copy_geometry_parameters)(union geometry_parameter_union*);
struct focus_data_struct focus_data; // Used for focusing from this geometry

// New focus data implementation to remove the focusing bug for non-isotripic processes
struct focus_data_array_struct focus_data_array;
struct pointer_to_1d_int_list focus_array_indices; // Add 1D integer array with indecies for correct focus_data for each process


// intersect_function takes position/velocity of ray and parameters, returns time list
int (*intersect_function)(double*,int*,double*,double*,struct geometry_struct*);
//                        t_array,n_ar,r      ,v

// within_function that checks if the ray origin is within this volume
int (*within_function)(Coords,struct geometry_struct*);
//                     r,      parameters

// mcdisplay function, draws the geometry
//void (*mcdisplay_function)(struct lines_to_draw*,int,struct Volume_struct**,int);
void (*mcdisplay_function)(struct lines_to_draw*,int,struct geometry_struct**,int);
//                                lines          index             Geometries   N

void (*initialize_from_main_function)(struct geometry_struct*);

struct pointer_to_1d_coords_list (*shell_points)(struct geometry_struct*, int maximum_number_of_points);

// List of other volumes to be check when ray starts within this volume.
struct pointer_to_1d_int_list intersect_check_list;
// List of other volumes the ray may enter, if the ray intersects the volume itself.
struct pointer_to_1d_int_list destinations_list;
// The destinations list stored as a logic list which makes some tasks quicker. OBSOLETE
//struct pointer_to_1d_int_list destinations_logic_list;
// Reduced list of other volumes the ray may enter, if the ray intersects the volume itself.
struct pointer_to_1d_int_list reduced_destinations_list;
// List of other volumes that are within this volume
struct pointer_to_1d_int_list children;
// List of other volumes that are within this volume, but does not have any parents that are children of this volume
struct pointer_to_1d_int_list direct_children;
// List of next possible volumes (only used in tagging)
struct pointer_to_1d_int_list next_volume_list;
// List of volumes masked by this volume (usually empty)
struct pointer_to_1d_int_list mask_list;
// List of volumes masking this volume
struct pointer_to_1d_int_list masked_by_list;
// List of masks masking this volume (global mask indices)
struct pointer_to_1d_int_list masked_by_mask_index_list;
// Additional intersect lists dependent on mask status
//struct indexed_mask_lists_struct mask_intersect_lists;
// Simpler way of storing the mask_intersect_lists
struct pointer_to_1d_int_list mask_intersect_list;
};

struct physics_struct
{
char name[256]; // User defined material name
int interact_control;
int is_vacuum;
double my_a;
int number_of_processes;
// pointer to array of pointers to physics_sub structures that each describe a scattering process
struct scattering_process_struct *p_scattering_array;
};

union data_transfer_union{
    // List of pointers to storage structs for all supported physical processes
    struct Incoherent_physics_storage_struct  *pointer_to_a_Incoherent_physics_storage_struct;
    struct Powder_physics_storage_struct *pointer_to_a_Powder_physics_storage_struct;
    struct Single_crystal_physics_storage_struct *pointer_to_a_Single_crystal_physics_storage_struct;
    struct AF_HB_1D_physics_storage_struct *pointer_to_a_AF_HB_1D_physics_storage_struct;
    struct IncoherentPhonon_physics_storage_struct *pointer_to_a_IncoherentPhonon_physics_storage_struct;
    struct PhononSimpleNumeric_physics_storage_struct *pointer_to_a_PhononSimpleNumeric_storage_struct;
    struct PhononSimple_physics_storage_struct *pointer_to_a_PhononSimple_storage_struct;
    struct MagnonSimple_physics_storage_struct *pointer_to_a_MagnonSimple_storage_struct;
    struct Sans_spheres_physics_storage_struct *pointer_to_a_Sans_spheres_physics_storage_struct;
    struct Texture_physics_storage_struct *pointer_to_a_Texture_physics_storage_struct;
    struct NCrystal_physics_storage_struct *pointer_to_a_NCrystal_physics_storage_struct;
    struct Non_physics_storage_struct *pointer_to_a_Non_physics_storage_struct;
    struct Template_physics_storage_struct *pointer_to_a_Template_physics_storage_struct;
    // possible to add as many structs as wanted, without increasing memory footprint.
};



struct scattering_process_struct
{
char name[256];                // User defined process name
enum process eProcess;         // enum value corresponding to this process GPU
double process_p_interact;     // double between 0 and 1 that describes the fraction of events forced to undergo this process. -1 for disable
int non_isotropic_rot_index;   // -1 if process is isotrpic, otherwise is the index of the process rotation matrix in the volume
Rotation rotation_matrix;      // rotation matrix of process, reported by component in local frame, transformed and moved to volume struct in main

union data_transfer_union data_transfer; // The way to reach the storage space allocated for this process (see examples in process.comp files)

// probability_for_scattering_functions calculates this probability given k_i and parameters
int (*probability_for_scattering_function)(double*,double*,union data_transfer_union,struct focus_data_struct*, _class_particle *_particle);
//                                         prop,   k_i,   ,parameters               , focus data / function

// A scattering_function takes k_i and parameters, returns k_f
int (*scattering_function)(double*,double*,double*,union data_transfer_union,struct focus_data_struct*, _class_particle *_particle);
//                         k_f,    k_i,    weight, parameters               , focus data / function
};

struct Volume_struct
{
char name[256]; // User defined volume name
struct geometry_struct geometry;        // Geometry properties (including intersect functions, generated lists)
struct physics_struct *p_physics;       // Physical properties (list of scattering processes, absorption)
struct loggers_struct loggers;          // Loggers assosiated with this volume
struct abs_loggers_struct abs_loggers;  // Loggers assosiated with this volume
};

// example of calling a scattering process
// volume_pointer_list[3]->physics.scattering_process[5].probability_for_scattering_function(input,volume_pointer_list[3]->physics.scattering_process[5])

struct starting_lists_struct
{
struct pointer_to_1d_int_list allowed_starting_volume_logic_list;
struct pointer_to_1d_int_list reduced_start_list;
struct pointer_to_1d_int_list start_logic_list;
struct pointer_to_1d_int_list starting_destinations_list;
};

struct global_positions_to_transform_list_struct {
int num_elements;
Coords **positions;
};

struct global_rotations_to_transform_list_struct {
int num_elements;
Rotation **rotations;
};

struct global_process_element_struct
{
char name[128]; // Name of the process
int component_index;
struct scattering_process_struct *p_scattering_process;
};

struct pointer_to_global_process_list {
int num_elements;
struct global_process_element_struct *elements;
};

struct global_material_element_struct
{
char name[128];
int component_index;
struct physics_struct *physics;
};

struct pointer_to_global_material_list {
int num_elements;
struct global_material_element_struct *elements;
};

struct global_geometry_element_struct
{
char name[128];
int component_index;
int activation_counter;
int stored_copies;
int active;
struct Volume_struct *Volume;
};

struct pointer_to_global_geometry_list {
int num_elements;
struct global_geometry_element_struct *elements;
};

struct global_logger_element_struct {
char name[128];
int component_index;
struct logger_struct *logger;
};

struct pointer_to_global_logger_list {
int num_elements;
struct global_logger_element_struct *elements;
};

struct global_abs_logger_element_struct {
char name[128];
int component_index;
struct abs_logger_struct *abs_logger;
};

struct pointer_to_global_abs_logger_list {
int num_elements;
struct global_abs_logger_element_struct *elements;
};

struct global_tagging_conditional_element_struct {
struct conditional_list_struct conditional_list;
int extend_index;
char name[1024];
int use_status;
};

struct global_tagging_conditional_list_struct {
int num_elements;
int current_index;
struct global_tagging_conditional_element_struct *elements;
};


struct global_master_element_struct {
char name[128];
int component_index;
int stored_number_of_scattering_events; // TEST
struct conditional_list_struct *tagging_conditional_list_pointer;
};

struct pointer_to_global_master_list {
int num_elements;
struct global_master_element_struct *elements;
};


// -------------    Physics functions   ---------------------------------------------------------

//#include "Test_physics.c"
//#include "Incoherent_test.c"

// -------------    General functions   ---------------------------------------------------------
double distance_between(Coords position1,Coords position2) {
    return sqrt((position1.x-position2.x)*(position1.x-position2.x) +
                (position1.y-position2.y)*(position1.y-position2.y) +
                (position1.z-position2.z)*(position1.z-position2.z));
};



double length_of_3vector(double *r) {
        return sqrt(r[0]*r[0]+r[1]*r[1]+r[2]*r[2]);
    };

double length_of_position_vector(Coords point) {
        return sqrt(point.x*point.x+point.y*point.y+point.z*point.z);
    };

Coords make_position(double *r) {
    Coords temp;
    
    temp.x = r[0];temp.y = r[1];temp.z = r[2];
    return temp;
};

Coords coords_scalar_mult(Coords input,double scalar) {
    return coords_set(scalar*input.x,scalar*input.y,scalar*input.z);
};

double union_coords_dot(Coords vector1,Coords vector2) {
    return vector1.x*vector2.x + vector1.y*vector2.y + vector1.z*vector2.z;
}


int sum_int_list(struct pointer_to_1d_int_list list) {
    int iterate,sum = 0;
    for (iterate = 0;iterate < list.num_elements;iterate++) sum += list.elements[iterate];
    return sum;
    };
    
int on_int_list(struct pointer_to_1d_int_list list,int target) {
    int iterate,output=0;
    for (iterate = 0; iterate<list.num_elements ;iterate++) {
        if (list.elements[iterate] == target) output = 1;
    }
    return output;
    };

int find_on_int_list(struct pointer_to_1d_int_list list,int target) {
    int iterate;
    for (iterate = 0;iterate < list.num_elements;iterate++) {
        if (list.elements[iterate] == target) return iterate;
    }
    return -1;
    };
    
void on_both_int_lists(struct pointer_to_1d_int_list *list1, struct pointer_to_1d_int_list *list2, struct pointer_to_1d_int_list *common) {
    // Assume common.elements is allocated to a number of int's large enough to hold the resulting list
    int iterate,used_elements=0;
    for (iterate=0;iterate<list1->num_elements;iterate++) {
        if (on_int_list(*list2,list1->elements[iterate])) common->elements[used_elements++] = list1->elements[iterate];
    }
    common->num_elements = used_elements;
    };

void remove_element_in_list_by_index(struct pointer_to_1d_int_list *list,int index) {
    if (index >= list->num_elements) {
      printf("ERROR(remove_element_in_list_by_index): trying to remove an index that wasn't allocated to begin with");
      exit(1);
    }
    else {
        int iterate;
        int *temp;
        for (iterate = index;iterate < list->num_elements -1;iterate++) {
            list->elements[iterate] = list->elements[iterate+1];
        }
        list->num_elements--;
        //if (list->num_elements==0) printf("Making empty list!\n");
        temp = malloc(list->num_elements * sizeof(int));
        for (iterate = 0;iterate < list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
        free(list->elements);
        list->elements = malloc(list->num_elements * sizeof(int));
        for (iterate = 0;iterate < list->num_elements;iterate++) list->elements[iterate] = temp[iterate];
        free(temp);

    }
    };
    
void remove_element_in_list_by_value(struct pointer_to_1d_int_list *list,int value) {
    int iterate;
    for (iterate = 0;iterate < list->num_elements;iterate++) {
        if (list->elements[iterate] == value) remove_element_in_list_by_index(list,iterate);
    }
    };
    
void merge_lists(struct pointer_to_1d_int_list *result,struct pointer_to_1d_int_list *list1,struct pointer_to_1d_int_list *list2) {
    if (result->num_elements > 0) free(result->elements);
    result->num_elements = list1->num_elements + list2->num_elements;
    if (result->num_elements != 0) {
      result->elements = malloc(result->num_elements*sizeof(int));
      int iterate;
      for (iterate = 0;iterate < list1->num_elements;iterate++)
          result->elements[iterate] = list1->elements[iterate];
      for (iterate = 0;iterate < list2->num_elements;iterate++)
          result->elements[list1->num_elements+iterate] = list2->elements[iterate];
    }
    };

void add_element_to_double_list(struct pointer_to_1d_double_list *list,double value) {
    if (list->num_elements == 0) {
      list->num_elements++;
      list-> elements = malloc(list->num_elements*sizeof(double));
      list-> elements[0] = value;
    } else {
      double temp[list->num_elements];
      int iterate;
      for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
      free(list->elements);
      list->num_elements++;
      list-> elements = malloc(list->num_elements*sizeof(double));
      for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
      list->elements[list->num_elements-1] = value;
    }
    };

void add_element_to_int_list(struct pointer_to_1d_int_list *list,int value) {
    if (list->num_elements == 0) {
      list->num_elements++;
      list-> elements = malloc(list->num_elements*sizeof(int));
      list-> elements[0] = value;
    } else {
      int temp[list->num_elements];
      int iterate;
      for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
      free(list->elements);
      list->num_elements++;
      list-> elements = malloc(list->num_elements*sizeof(int));
      for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
      list->elements[list->num_elements-1] = value;
    }
    };

// Need to check if absolute_rotation is preserved correctly.
void add_element_to_focus_data_array(struct focus_data_array_struct *focus_data_array,struct focus_data_struct focus_data) {
    if (focus_data_array->num_elements == 0) {
      focus_data_array->num_elements++;
      focus_data_array-> elements = malloc(focus_data_array->num_elements*sizeof(struct focus_data_struct));
      focus_data_array-> elements[0] = focus_data;
    } else {
      struct focus_data_struct temp[focus_data_array->num_elements];
      int iterate;
      for (iterate=0;iterate<focus_data_array->num_elements;iterate++) temp[iterate] = focus_data_array->elements[iterate];
      free(focus_data_array->elements);
      focus_data_array->num_elements++;
      focus_data_array-> elements = malloc(focus_data_array->num_elements*sizeof(struct focus_data_struct));
      for (iterate=0;iterate<focus_data_array->num_elements-1;iterate++) focus_data_array->elements[iterate] = temp[iterate];
      focus_data_array->elements[focus_data_array->num_elements-1] = focus_data;
    }
    };


void add_to_logger_with_data(struct logger_with_data_struct *logger_with_data, struct logger_struct *logger) {
    // May reorder the order of the if conditions to avoid checking the == 0 for every single ray
    if (logger_with_data->allocated_elements == 0) {
        logger_with_data->allocated_elements = 5;
        logger_with_data->logger_pointers = malloc(logger_with_data->allocated_elements*sizeof(struct logger_struct*));
        logger_with_data->used_elements = 1;
        logger_with_data->logger_pointers[0] = logger;
    } else if (logger_with_data->used_elements > logger_with_data->allocated_elements-1) {
        struct logger_with_data_struct temp_logger_with_data;
        temp_logger_with_data.logger_pointers = malloc((logger_with_data->used_elements)*sizeof(struct logger_struct*));
        int iterate;
        for (iterate=0;iterate<logger_with_data->used_elements;iterate++) {
            temp_logger_with_data.logger_pointers[iterate]  = logger_with_data->logger_pointers[iterate];
        }
        free(logger_with_data->logger_pointers);
        logger_with_data->allocated_elements = logger_with_data->allocated_elements+5;
        logger_with_data->logger_pointers = malloc(logger_with_data->allocated_elements*sizeof(struct logger_struct*));
        for (iterate=0;iterate<logger_with_data->used_elements;iterate++) {
            logger_with_data->logger_pointers[iterate]  = temp_logger_with_data.logger_pointers[iterate];
        }

        logger_with_data->logger_pointers[logger_with_data->used_elements++] = logger;
        
        
    } else {
        logger_with_data->logger_pointers[logger_with_data->used_elements++] = logger;
    }
    
};

void add_to_abs_logger_with_data(struct abs_logger_with_data_struct *abs_logger_with_data, struct abs_logger_struct *abs_logger) {
    // May reorder the order of the if conditions to avoid checking the == 0 for every single ray
    if (abs_logger_with_data->allocated_elements == 0) {
        abs_logger_with_data->allocated_elements = 5;
        abs_logger_with_data->abs_logger_pointers = malloc(abs_logger_with_data->allocated_elements*sizeof(struct abs_logger_struct*));
        abs_logger_with_data->used_elements = 1;
        abs_logger_with_data->abs_logger_pointers[0] = abs_logger;
    } else if (abs_logger_with_data->used_elements > abs_logger_with_data->allocated_elements-1) {
        struct abs_logger_with_data_struct temp_abs_logger_with_data;
        temp_abs_logger_with_data.abs_logger_pointers = malloc((abs_logger_with_data->used_elements)*sizeof(struct abs_logger_struct*));
        int iterate;
        for (iterate=0;iterate<abs_logger_with_data->used_elements;iterate++) {
            temp_abs_logger_with_data.abs_logger_pointers[iterate]  = abs_logger_with_data->abs_logger_pointers[iterate];
        }
        free(abs_logger_with_data->abs_logger_pointers);
        abs_logger_with_data->allocated_elements = abs_logger_with_data->allocated_elements+5;
        abs_logger_with_data->abs_logger_pointers = malloc(abs_logger_with_data->allocated_elements*sizeof(struct abs_logger_struct*));
        for (iterate=0;iterate<abs_logger_with_data->used_elements;iterate++) {
            abs_logger_with_data->abs_logger_pointers[iterate] = temp_abs_logger_with_data.abs_logger_pointers[iterate];
        }

        abs_logger_with_data->abs_logger_pointers[abs_logger_with_data->used_elements++] = abs_logger;
        
        
    } else {
        abs_logger_with_data->abs_logger_pointers[abs_logger_with_data->used_elements++] = abs_logger;
    }
    
};


// Used typedef to avoid having to change this function later. May update others to use same phillosphy.
void add_function_to_conditional_list(struct conditional_list_struct *list,conditional_function_pointer new, union conditional_data_union *data_union) {
    if (list->num_elements == 0) {
      list->num_elements++;
      list->conditional_functions = malloc(list->num_elements*sizeof(conditional_function_pointer));
      list->p_data_unions = malloc(list->num_elements*sizeof(union conditional_data_union*));
      list->conditional_functions[0] = new;
      list->p_data_unions[0] = data_union;
    }
    else {
    conditional_function_pointer temp_fp[list->num_elements];
    union conditional_data_union *temp_du[list->num_elements];
    int iterate;
    // Could even get away with a shallow copy here instead of the loop, but it is not relevant for performance.
    for (iterate=0;iterate<list->num_elements;iterate++) {
      temp_fp[iterate] = list->conditional_functions[iterate];
      temp_du[iterate] = list->p_data_unions[iterate];
    }
    free(list->conditional_functions);
    free(list->p_data_unions);
    list->num_elements++;
    list->conditional_functions = malloc(list->num_elements*sizeof(conditional_function_pointer));
    list->p_data_unions = malloc(list->num_elements*sizeof(union conditional_data_union*));
    
    for (iterate=0;iterate<list->num_elements-1;iterate++) {
      list->conditional_functions[iterate] = temp_fp[iterate];
      list->p_data_unions[iterate] = temp_du[iterate];
    }
    list->conditional_functions[list->num_elements-1] = new;
    list->p_data_unions[list->num_elements-1] = data_union;
    }
};


// could make function that removes a element from a 1d_int_list, and have each list generation as a function that takes a copy of an overlap list

void print_1d_int_list(struct pointer_to_1d_int_list list,char *name) {
        int iterate;
        printf("LIST: ");printf("%s",name);printf(" = [");
        for (iterate = 0; iterate < list.num_elements; iterate++) {
            printf("%d",list.elements[iterate]);
            if (iterate < list.num_elements - 1) printf(",");
        }
        printf("]\n");
    };

void print_1d_double_list(struct pointer_to_1d_double_list list,char *name) {
        int iterate;
        printf("LIST: ");printf("%s",name);printf(" = [");
        for (iterate = 0; iterate < list.num_elements; iterate++) {
            printf("%f",list.elements[iterate]);
            if (iterate < list.num_elements - 1) printf(",");
        }
        printf("]\n");
    };

void print_position(Coords pos,char *name) {
    printf("POSITION: ");printf("%s",name);printf(" = (%f,%f,%f)\n",pos.x,pos.y,pos.z);
    };

void print_rotation(Rotation rot, char *name) {
    printf("ROT MATRIX: %s \n",name);
    printf("[%f %f %f]\n",rot[0][0],rot[0][1],rot[0][2]);
    printf("[%f %f %f]\n",rot[1][0],rot[1][1],rot[1][2]);
    printf("[%f %f %f]\n\n",rot[2][0],rot[2][1],rot[2][2]);
};
    
void allocate_list_from_temp(int num_elements,struct pointer_to_1d_int_list original,struct pointer_to_1d_int_list *new) {
        int iterate;
    
        new->num_elements = num_elements;
        if (num_elements > 0) {
            new->elements = malloc(num_elements*sizeof(int));
            for (iterate = 0;iterate < num_elements; iterate++) new->elements[iterate] = original.elements[iterate];
        } else new->elements = NULL;
    
    };

void allocate_logic_list_from_temp(int num_elements,struct pointer_to_1d_int_list original, struct pointer_to_1d_int_list *new) {
        // A logic list shares the same structure of a normal list, but instead of listing numbers, it is a list of yes / no (1/0)
        // Giving this function a list of [1 3 5] (and num_elements = 9) would return [0 1 0 1 0 1 0 0 0];
        int iterate;
    
        new->num_elements = num_elements;
        if (num_elements > 0) {
            new->elements = malloc(num_elements*sizeof(int));
            for (iterate = 0;iterate < num_elements;iterate++) new->elements[iterate] = 0;
            for (iterate = 0;iterate < original.num_elements;iterate++) {
                if (original.elements[iterate] < num_elements)
                    new->elements[original.elements[iterate]] = 1;
                else printf("Trying to allocate logical list without enough memory\n");
            }
        } else new->elements = NULL;
    };

/*
struct global_positions_to_transform_list_struct {
int num_elements;
Coords **positions;
}

struct global_rotations_to_transform_list_struct {
int num_elements;
Rotation **rotations;
}
*/

void add_position_pointer_to_list(struct global_positions_to_transform_list_struct *list, Coords *new_position_pointer) {
    if (list->num_elements == 0) {
      list->num_elements++;
      list->positions = malloc(list->num_elements*sizeof(Coords*));
      list->positions[0] = new_position_pointer;
    } else {
      Coords **temp;
      temp = malloc(list->num_elements*sizeof(Coords*));
      if (temp == NULL) printf("malloc failed in add_position_pointer_to_list for temp\n");
      
      int iterate;
      for (iterate=0;iterate<list->num_elements;iterate++)
        temp[iterate] = list->positions[iterate];
      free(list->positions);
      list->num_elements++;
      list->positions = malloc(list->num_elements*sizeof(Coords*));
      if (list->positions == NULL) printf("malloc failed in add_position_pointer_to_list for list->positions\n");
      
      for (iterate=0;iterate<list->num_elements-1;iterate++)
        list->positions[iterate] = temp[iterate];
      free(temp);
      list->positions[list->num_elements-1] = new_position_pointer;
    }
};

void add_rotation_pointer_to_list(struct global_rotations_to_transform_list_struct *list, Rotation *new_rotation_pointer) {
    if (list->num_elements == 0) {
      list->num_elements++;
      list->rotations = malloc(list->num_elements*sizeof(Rotation*));
      list->rotations[0] = new_rotation_pointer;
    } else {
      Rotation **temp;
      temp = malloc(list->num_elements*sizeof(Rotation*));
      int iterate;
      for (iterate=0;iterate<list->num_elements;iterate++)
        temp[iterate] = list->rotations[iterate];
      free(list->rotations);
      list->num_elements++;
      list->rotations = malloc(list->num_elements*sizeof(Rotation*));
      
      for (iterate=0;iterate<list->num_elements-1;iterate++)
        list->rotations[iterate] = temp[iterate];
      free(temp);
      list->rotations[list->num_elements-1] = new_rotation_pointer;
    }
};

void add_element_to_process_list(struct pointer_to_global_process_list *list,struct global_process_element_struct new_element) {
    if (list->num_elements == 0) {
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_process_element_struct));
    list-> elements[0] = new_element;
    }
    else {
    struct global_process_element_struct temp[list->num_elements];
    int iterate;
    for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
    free(list->elements);
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_process_element_struct));
    for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
    list->elements[list->num_elements-1] = new_element;
    }
};

void add_element_to_material_list(struct pointer_to_global_material_list *list,struct global_material_element_struct new_element) {
    if (list->num_elements == 0) {
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_material_element_struct));
    list-> elements[0] = new_element;
    }
    else {
    struct global_material_element_struct temp[list->num_elements];
    int iterate;
    for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
    free(list->elements);
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_material_element_struct));
    for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
    list->elements[list->num_elements-1] = new_element;
    }
};

void add_element_to_geometry_list(struct pointer_to_global_geometry_list *list,struct global_geometry_element_struct new_element) {
    if (list->num_elements == 0) {
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_geometry_element_struct));
    list-> elements[0] = new_element;
    }
    else {
    struct global_geometry_element_struct temp[list->num_elements];
    int iterate;
    for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
    free(list->elements);
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_geometry_element_struct));
    for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
    list->elements[list->num_elements-1] = new_element;
    }
};

void add_element_to_logger_list(struct pointer_to_global_logger_list *list,struct global_logger_element_struct new_element) {
    if (list->num_elements == 0) {
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_logger_element_struct));
    list-> elements[0] = new_element;
    }
    else {
    struct global_logger_element_struct temp[list->num_elements];
    int iterate;
    for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
    free(list->elements);
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_logger_element_struct));
    for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
    list->elements[list->num_elements-1] = new_element;
    }
};

void add_element_to_abs_logger_list(struct pointer_to_global_abs_logger_list *list, struct global_abs_logger_element_struct new_element) {
    if (list->num_elements == 0) {
      list->num_elements++;
      list->elements = malloc(list->num_elements*sizeof(struct global_abs_logger_element_struct));
      list->elements[0] = new_element;
    }
    else {
      struct global_abs_logger_element_struct temp[list->num_elements];
      int iterate;
      for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
      free(list->elements);
      list->num_elements++;
      list-> elements = malloc(list->num_elements*sizeof(struct global_abs_logger_element_struct));
      for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
      list->elements[list->num_elements-1] = new_element;
    }
};

void add_element_to_tagging_conditional_list(struct global_tagging_conditional_list_struct *list,struct global_tagging_conditional_element_struct new_element) {
    if (list->num_elements == 0) {
    list->num_elements++;
    list->elements = malloc(list->num_elements*sizeof(struct global_tagging_conditional_element_struct));
    list->elements[0] = new_element;
    }
    else {
    struct global_tagging_conditional_element_struct temp[list->num_elements];
    int iterate;
    for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
    free(list->elements);
    list->num_elements++;
    list->elements = malloc(list->num_elements*sizeof(struct global_tagging_conditional_element_struct));
    for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
    list->elements[list->num_elements-1] = new_element;
    }
};

void add_element_to_master_list(struct pointer_to_global_master_list *list,struct global_master_element_struct new_element) {
    if (list->num_elements == 0) {
    list->num_elements++;
    list->elements = malloc(list->num_elements*sizeof(struct global_master_element_struct));
    list->elements[0] = new_element;
    }
    else {
    struct global_master_element_struct temp[list->num_elements];
    int iterate;
    for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
    free(list->elements);
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_master_element_struct));
    for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
    list->elements[list->num_elements-1] = new_element;
    }
};

void add_initialized_logger_in_volume(struct loggers_struct *loggers,int number_of_processes) {
  int iterate;
  if (loggers->num_elements == 0) {
    loggers->num_elements++;
    loggers->p_logger_volume = malloc(loggers->num_elements * sizeof(struct logger_for_each_process_list));
    loggers->p_logger_volume[0].num_elements = number_of_processes;
    loggers->p_logger_volume[0].p_logger_process = malloc(number_of_processes * sizeof(struct logger_struct**));
    for (iterate=0;iterate<number_of_processes;iterate++)
      loggers->p_logger_volume[0].p_logger_process[iterate] = NULL;
  } else {
    // Already some elements, store them in temp, free main, transfer back and add newest.
    struct logger_for_each_process_list temp[loggers->num_elements];
    
    for (iterate=0;iterate<loggers->num_elements;iterate++) temp[iterate] = loggers->p_logger_volume[iterate];
    free(loggers->p_logger_volume);
    loggers->num_elements++;
    loggers->p_logger_volume = malloc(loggers->num_elements*sizeof(struct logger_for_each_process_list));
    for (iterate=0;iterate<loggers->num_elements-1;iterate++) loggers->p_logger_volume[iterate] = temp[iterate];
    loggers->p_logger_volume[loggers->num_elements-1].num_elements = number_of_processes;
    loggers->p_logger_volume[loggers->num_elements-1].p_logger_process = malloc(number_of_processes * sizeof(struct logger_struct**));
    for (iterate=0;iterate<number_of_processes;iterate++) {
      loggers->p_logger_volume[loggers->num_elements-1].p_logger_process[iterate] = NULL;
    }
  }
};

/*
void add_initialized_abs_logger_in_volume(struct abs_loggers_struct *abs_loggers, int number_of_processes) {
  int iterate;
  if (abs_loggers->num_elements == 0) {
    abs_loggers->num_elements++;
    abs_loggers->p_abs_logger_volume = malloc(abs_loggers->num_elements * sizeof(struct abs_loggers_struct));
    abs_loggers->p_abs_logger_volume[0].num_elements = number_of_processes;
    abs_loggers->p_abs_logger_volume[0].p_abs_logger_process = malloc(number_of_processes * sizeof(struct abs_logger_struct**));
    for (iterate=0;iterate<number_of_processes;iterate++)
      abs_loggers->p_abs_logger_volume[0].p_abs_logger_process[iterate] = NULL;
  } else {
    // Already some elements, store them in temp, free main, transfer back and add newest.
    struct abs_logger_for_each_process_list temp[abs_loggers->num_elements];
    
    for (iterate=0;iterate<abs_loggers->num_elements;iterate++) temp[iterate] = abs_loggers->p_abs_logger_volume[iterate];
    free(abs_loggers->p_abs_logger_volume);
    abs_loggers->num_elements++;
    abs_loggers->p_abs_logger_volume = malloc(abs_loggers->num_elements*sizeof(struct abs_logger_for_each_process_list));
    for (iterate=0;iterate<abs_loggers->num_elements-1;iterate++) abs_loggers->p_abs_logger_volume[iterate] = temp[iterate];
    abs_loggers->p_abs_logger_volume[abs_loggers->num_elements-1].num_elements = number_of_processes;
    abs_loggers->p_abs_logger_volume[abs_loggers->num_elements-1].p_abs_logger_process = malloc(number_of_processes * sizeof(struct abs_logger_struct**));
    for (iterate=0;iterate<number_of_processes;iterate++) {
      abs_loggers->p_abs_logger_volume[abs_loggers->num_elements-1].p_abs_logger_process[iterate] = NULL;
    }
  }
};
*/

void add_initialized_abs_logger_in_volume(struct abs_loggers_struct *abs_loggers) {
  int iterate;
  if (abs_loggers->num_elements == 0) {
    abs_loggers->num_elements++;
    abs_loggers->p_abs_logger = malloc(abs_loggers->num_elements * sizeof(struct abs_logger_struct*));
  } else {
    // Already some elements, store them in temp, free main, transfer back and add newest.
    struct abs_logger_struct *temp[abs_loggers->num_elements];
    
    for (iterate=0;iterate<abs_loggers->num_elements;iterate++) temp[iterate] = abs_loggers->p_abs_logger[iterate];
    free(abs_loggers->p_abs_logger);
    
    abs_loggers->num_elements++;
    abs_loggers->p_abs_logger = malloc(abs_loggers->num_elements*sizeof(struct abs_logger_struct*));
    for (iterate=0;iterate<abs_loggers->num_elements-1;iterate++) abs_loggers->p_abs_logger[iterate] = temp[iterate];
    abs_loggers->p_abs_logger[abs_loggers->num_elements-1] = NULL;
    
  }
};


// -------------    Functions used to shorten master trace    ---------------------------------------------


void update_current_mask_intersect_status(struct pointer_to_1d_int_list *current_mask_intersect_list_status, struct pointer_to_1d_int_list *mask_status_list, struct Volume_struct **Volumes, int *current_volume) {
  // This function is to be executed whenever the current volume changes, or the mask status changes
  // It updates the effective mask status for each element of a volumes mask_intersect_list
  // The effective mask takes the ALL/ANY mode assosiated with each volume into account,
  //  meaning a volume needs to be within ALL/ANY of it's masks to have a status of 1
  // In most cases this mask_intersect_list will be empty, and memory operations are avoided
  //printf("Number of elements to be check: %d \n",Volumes[*current_volume]->geometry.mask_intersect_list.num_elements);
  
  if (Volumes[*current_volume]->geometry.mask_intersect_list.num_elements > 0) {
    int iterate,this_element,*mask_start,*mask_check;
    for (iterate=0;iterate<Volumes[*current_volume]->geometry.mask_intersect_list.num_elements;iterate++) {
      this_element = Volumes[*current_volume]->geometry.mask_intersect_list.elements[iterate];
      //printf("We are investigating volume number %d from the mask_intersect_list of volume %d",this_element,*current_volume);
      if (Volumes[this_element]->geometry.mask_mode == 2) { // ANY mask mode
        //printf("We are in ANY mask mode! \n");
        current_mask_intersect_list_status->elements[iterate] = 0; // Assume the mask status is 0, but if any are 1, take that instead
        for (mask_start=mask_check=Volumes[this_element]->geometry.masked_by_mask_index_list.elements;mask_check-mask_start<Volumes[this_element]->geometry.masked_by_mask_index_list.num_elements;mask_check++) {
          //printf("Checking all the mask statuses of volumes masking %d, now mask with global mask index %d ! \n",this_element,*mask_check);
          if (mask_status_list->elements[*mask_check] == 1) {
            //printf("The status was 1, so the effective status is set to 1 and the loop is stopped");
            current_mask_intersect_list_status->elements[iterate] = 1;
            break;
          }
        }
      } else { // ALL mask mode
        //printf("We are in ALL mask mode! \n");
        current_mask_intersect_list_status->elements[iterate] = 1; // Assume the mask status is 1, but if any one is 0, take that instead
        for (mask_start=mask_check=Volumes[this_element]->geometry.masked_by_mask_index_list.elements;mask_check-mask_start<Volumes[this_element]->geometry.masked_by_mask_index_list.num_elements;mask_check++) {
          //printf("Checking all the mask statuses of volumes masking %d, now mask with global mask index %d ! \n",this_element,*mask_check);
          if (mask_status_list->elements[*mask_check] == 0) {
            //printf("The status was 0, so the effective status is set to 0 and the loop is stopped \n");
            current_mask_intersect_list_status->elements[iterate] = 0;
            break;
          }
        }
      }
    }
  }
    
  /* Original version compatible with trace scope
  for (iterate=0;iterate<Volumes[current_volume]->geometry.mask_intersect_list.num_elements;iterate++) {
    this_element = Volumes[current_volume]->geometry.mask_intersect_list.elements[iterate];
    if (Volumes[this_element]->geometry.mask_mode == 2) { // ANY mask mode
      current_mask_intersect_list_status.elements[iterate] = 0; // Assume the mask status is 0, but if any are 1, take that instead
      for (mask_start=mask_check=Volumes[this_element]->geometry.masked_by_mask_index_list.elements;mask_check-mask_start<Volumes[this_element]->geometry.masked_by_mask_index_list.num_elements;mask_check++) {
        if (mask_status_list[*mask_check] == 1) {
          current_mask_intersect_list_status.elements[iterate] = 1;
          break;
        }
      }
    } else { // ALL mask mode
      current_mask_intersect_list_status.elements[iterate] = 1; // Assume the mask status is 1, but if any one is 0, take that instead
      for (mask_start=mask_check=Volumes[this_element]->geometry.masked_by_mask_index_list.elements;mask_check-mask_start<Volumes[this_element]->geometry.masked_by_mask_index_list.num_elements;mask_check++) {
        if (mask_status_list[*mask_check] == 0) {
          current_mask_intersect_list_status.elements[iterate] = 0;
          break;
        }
      }
    }
  }
  */
}

// -------------    Tagging functions    ------------------------------------------------------------------

struct tagging_tree_node_struct {
  // Statistics:
  double intensity;
  int number_of_rays;
  // tree pointers
  struct tagging_tree_node_struct *above;   // Pointer to node above
  struct tagging_tree_node_struct **volume_branches;
  struct tagging_tree_node_struct **process_branches;
};

struct list_of_tagging_tree_node_pointers {
  struct tagging_tree_node_struct **elements;
  int num_elements;
};

struct tagging_tree_node_struct *make_tagging_tree_node(void) {
    return (struct tagging_tree_node_struct *) malloc(sizeof(struct tagging_tree_node_struct));
}


struct tagging_tree_node_struct  *simple_initialize_tagging_tree_node(struct tagging_tree_node_struct *new_node) {
    //struct tagging_tree_node_struct *dummy;
    //dummy = malloc(sizeof(struct tagging_tree_node_struct));
    //new_node = dummy;
    
    //new_node->element = (struct tagging_tree_node_struct *) malloc(sizeof(struct tagging_tree_node_struct));
    //new_node = (struct tagging_tree_node_struct *) malloc(sizeof(struct tagging_tree_node_struct));
    new_node = make_tagging_tree_node();
    
    if (new_node == NULL) printf("ERROR, Union tagging system could not allocate memory\n");
    new_node->intensity = 4.2; // (double) 4.2;
    new_node->number_of_rays = 42; //(int) 42;
    
    printf("new_node->intensity = %f, new_node->number_of_rays = %d \n",new_node->intensity,new_node->number_of_rays);
    return new_node;
};


struct tagging_tree_node_struct *initialize_tagging_tree_node(struct tagging_tree_node_struct *new_node, struct tagging_tree_node_struct *above_node, struct Volume_struct *this_volume) {
    new_node = make_tagging_tree_node();
    
    new_node->intensity = (double) 0;
    new_node->number_of_rays = (int) 0;
    new_node->above = above_node;
    
    int next_volume_list_length = this_volume->geometry.next_volume_list.num_elements;
    new_node->volume_branches = malloc(next_volume_list_length*sizeof(struct tagging_tree_node_struct*));
    int iterate;
    // Initializing pointers so that they can be checked for NULL later. Is this redundant? Does malloc return null pointers?
    for (iterate=0;iterate<next_volume_list_length;iterate++) new_node->volume_branches[iterate] = NULL;
    //new_node->volume_branches.num_elements = dest_list_length; // May be removed
    
    int number_of_processes;
    if (this_volume->p_physics == NULL) number_of_processes = 0;
    else number_of_processes = this_volume->p_physics->number_of_processes;
    
    new_node->process_branches = malloc(number_of_processes*sizeof(struct tagging_tree_node_struct*));
    // Initializing pointers so that they can be checked for NULL later. Is this redundant? Does malloc return null pointers?
    for (iterate=0;iterate<number_of_processes;iterate++) new_node->process_branches[iterate] = NULL;
    //new_node->process_branches.num_elements=number_of_processes; // May be removed
    
    return new_node;
};

struct tagging_tree_node_struct *goto_process_node(struct tagging_tree_node_struct *current_node, int process_index, struct Volume_struct *this_volume, int *stop_tagging_ray, int stop_creating_nodes) {
    // Either create a new node if it has not been created yet, or travel down the tree
    if (current_node->process_branches[process_index] == NULL) {
      if (stop_creating_nodes == 0) {
        current_node->process_branches[process_index] = initialize_tagging_tree_node(current_node->process_branches[process_index],current_node,this_volume);
        return current_node->process_branches[process_index];
      } else {
        // This stops the ray from using more goto node functions and being counted in the statistics.
        // Happens because the history limit is reached, and no new histories should be started.
        *stop_tagging_ray = 1;
        return current_node;
      }
    } else {
        current_node = current_node->process_branches[process_index];
        return current_node;
    }
    
    //return current_node;
};

struct tagging_tree_node_struct *goto_volume_node(struct tagging_tree_node_struct *current_node,int current_volume, int next_volume, struct Volume_struct **Volumes, int *stop_tagging_ray, int stop_creating_nodes) {
    // I have only allocated the number of node branches that corresponds to the current_volumes next_volume_list
    // The problem is to find where on the destination list the current volume is without doing a manual search
    
    // With the new mask system there is a risk of going from and to the same volume, which should be ignored by the tagging system
    if (current_volume == next_volume) return current_node;
    
    struct tagging_tree_node_struct *output;
    int next_volume_list_index = -1;
    // Temporary slow method for finding the correct index on the destination list
    int iterate;
    
    for (iterate=0;iterate<Volumes[current_volume]->geometry.next_volume_list.num_elements;iterate++)
        if (Volumes[current_volume]->geometry.next_volume_list.elements[iterate] == next_volume) {
            next_volume_list_index = iterate;
            break;
        }
    
    // Debug phase
    //printf("Tagging: going from volume %d to volume %d, which is index number %d on it's next volume list \n",current_volume,next_volume,next_volume_list_index);
    #ifndef OPENACC
    if (next_volume_list_index == -1) {
        printf("ERROR in Union component, tagging or destination system failed, next volume was not on next volume list\n");
        printf("current_volume = %d, next_volume = %d \n",current_volume,next_volume);
        print_1d_int_list(Volumes[current_volume]->geometry.destinations_list,"destinations_list for current volume");
        exit(1);
    }
    #endif

    // Either create a new node if it has not been created yet, or travel down the tree
    if (current_node->volume_branches[next_volume_list_index] == NULL) {
      if (stop_creating_nodes == 0) {
        current_node->volume_branches[next_volume_list_index] =  initialize_tagging_tree_node(current_node->volume_branches[next_volume_list_index],current_node,Volumes[next_volume]);
        return current_node->volume_branches[next_volume_list_index];
      } else {
        // This stops the ray from using more goto node functions and being counted in the statistics.
        // Happens because the history limit is reached, and no new histories should be started.
        *stop_tagging_ray = 1;
        return current_node;
      }
    } else {
        //current_node = current_node->volume_branches[next_volume_list_index];
        //return current_node;
        current_node = current_node->volume_branches[next_volume_list_index];
        //printf("used allocated node \n");
        return current_node;
    }
};

void add_statistics_to_node(struct tagging_tree_node_struct *current_node, Coords *r, Coords *v, double *weight, int *counter) {
    if (current_node->number_of_rays == 0) (*counter)++;
    current_node->number_of_rays = current_node->number_of_rays + 1;
    current_node->intensity = current_node->intensity + *weight;
};

struct history_node_struct {
    int volume_index;
    int process_index;
};

struct dynamic_history_list {
  struct history_node_struct *elements;
  int used_elements;
  int allocated_elements;
};

struct saved_history_struct {
    struct history_node_struct *elements;
    int used_elements;
    double intensity;
    int number_of_rays;
};

struct total_history_struct {
    struct saved_history_struct *saved_histories;
    int used_elements;
    int allocated_elements;
};


void add_to_history(struct dynamic_history_list *history, int volume_index, int process_index) {
    //printf("Adding to history[%d]: volume_index = %d, process_index = %d \n",history->used_elements,volume_index,process_index);
    if (history->allocated_elements == 0) {
        history->elements = malloc(5*sizeof(struct history_node_struct));
        history->used_elements = 1;
        history->allocated_elements = 5;
        history->elements[0].volume_index = volume_index;
        history->elements[0].process_index = process_index;
    } else if (history->used_elements > history->allocated_elements-1) {
        struct dynamic_history_list temp_history;
        temp_history.elements = malloc((history->used_elements)*sizeof(struct history_node_struct));
        int iterate;
        for (iterate=0;iterate<history->used_elements;iterate++) {
            temp_history.elements[iterate].volume_index  = history->elements[iterate].volume_index;
            temp_history.elements[iterate].process_index = history->elements[iterate].process_index;
        }
        free(history->elements);
        history->elements = malloc((history->allocated_elements+5)*sizeof(struct history_node_struct));
        for (iterate=0;iterate<history->used_elements;iterate++) {
            history->elements[iterate].volume_index  = temp_history.elements[iterate].volume_index;
            history->elements[iterate].process_index = temp_history.elements[iterate].process_index;
        }
        
        history->allocated_elements = history->allocated_elements+5;
        
        history->elements[history->used_elements].volume_index = volume_index;
        history->elements[history->used_elements].process_index = process_index;
        history->used_elements = history->used_elements+1;
        
    } else {
        history->elements[history->used_elements].volume_index = volume_index;
        history->elements[history->used_elements].process_index = process_index;
        history->used_elements++;
    }
    
};

void printf_history(struct dynamic_history_list *history) {
    int history_iterate;
          //printf("History number %d, intensity = %f, number of rays = %d:",hist_num, search_node->intensity, search_node->number_of_rays);
          for (history_iterate=0;history_iterate<history->used_elements-1;history_iterate++) {
            if (history->elements[history_iterate].process_index == -1) {
                printf(" V%d ->",history->elements[history_iterate].volume_index);
            } else {
                printf(" P%d ->",history->elements[history_iterate].process_index);
            }
          }
          if (history->elements[history_iterate].process_index == -1) {
                printf(" V%d \n",history->elements[history_iterate].volume_index);
          } else {
                printf(" P%d \n",history->elements[history_iterate].process_index);
          }
}

void fprintf_total_history(struct saved_history_struct *history, FILE *fp) {
    fprintf(fp,"%d\t N I=%E \t", history->number_of_rays, history->intensity);
        int history_iterate;
          //printf("History number %d, intensity = %f, number of rays = %d:",hist_num, search_node->intensity, search_node->number_of_rays);
          for (history_iterate=0;history_iterate<history->used_elements-1;history_iterate++) {
            if (history->elements[history_iterate].process_index == -1) {
                fprintf(fp," V%d ->",history->elements[history_iterate].volume_index);
            } else {
                fprintf(fp," P%d ->",history->elements[history_iterate].process_index);
            }
          }
          if (history->elements[history_iterate].process_index == -1) {
                fprintf(fp," V%d \n",history->elements[history_iterate].volume_index);
          } else {
                fprintf(fp," P%d \n",history->elements[history_iterate].process_index);
          }
}

/*
int Sample_compare_doubles (const void *a, const void *b) {
  const double *da = (const double *) a;
  const double *db = (const double *) b;

  return (*da > *db) - (*da < *db);
}
*/


int Sample_compare_history_intensities (const struct saved_history_struct *a, const struct saved_history_struct *b) {
  const double *da = (const double *) &(a->intensity);
  const double *db = (const double *) &(b->intensity);

  return (*da < *db) - (*da > *db);
}

void write_tagging_tree(struct list_of_tagging_tree_node_pointers *master_list, struct Volume_struct **Volumes, int total_history_counter, int number_of_volumes) {
  // Start from top of tree, go to extremeties, take results and add to disk / database, free that node
  int volume_index,done,volume_iterate,process_iterate,current_volume,next_node_found,current_number_of_processes,history_iterate,hist_num;
  
  struct tagging_tree_node_struct *search_node;
  struct tagging_tree_node_struct **kill_candidate;
  struct dynamic_history_list history_data; // Allocate the history list struct
  struct dynamic_history_list *history;     // Use this pointer in the algorithm
  
  struct total_history_struct total_history;
  total_history.saved_histories = malloc(total_history_counter * sizeof(struct saved_history_struct));
  total_history.allocated_elements = total_history_counter;
  total_history.used_elements = 0;
  
  history = &history_data;
  
  history->used_elements = 0;
  history->allocated_elements = 0;
  
  hist_num = 0;
  for (volume_index=0;volume_index<master_list->num_elements;volume_index++) {
    
    search_node = master_list->elements[volume_index];
    
    if (volume_index != 0)
        current_number_of_processes = Volumes[volume_index]->p_physics->number_of_processes;
    else
        current_number_of_processes = 0;
    
    current_volume = volume_index;
    done = 0;
    
    history->used_elements = 0;
    
    add_to_history(history,current_volume,-1);
    while(done == 0) {
        next_node_found=0;
        for (volume_iterate=0;volume_iterate<Volumes[current_volume]->geometry.next_volume_list.num_elements;volume_iterate++) {
            //printf("searc_node->volume_branches[0]->intensity = %f\n",search_node->volume_branches[0]->intensity);
            if (search_node->volume_branches[volume_iterate] != NULL) {
                current_volume = Volumes[current_volume]->geometry.next_volume_list.elements[volume_iterate];
                if (current_volume != 0)
                    current_number_of_processes = Volumes[current_volume]->p_physics->number_of_processes;
                else
                    current_number_of_processes = 0;
                
                //search_node = &(search_node->volume_branches[volume_iterate]);
                kill_candidate = &(search_node->volume_branches[volume_iterate]);
                search_node = search_node->volume_branches[volume_iterate];
                next_node_found = 1;
                add_to_history(history,current_volume,-1);
                //printf_history(history);
                break;
            }
        }
        if (next_node_found == 0) {
          //printf("doing process loop with %d steps \n",current_number_of_processes);
          for (process_iterate=0;process_iterate<current_number_of_processes;process_iterate++) {
            //if (&(search_node->process_branches[volume_iterate]) != NULL) {
            if (search_node->process_branches[process_iterate] != NULL) {
                //printf("was not NULL (process)\n");
                //search_node = &(search_node->process_branches[process_iterate]);
                kill_candidate = &(search_node->process_branches[process_iterate]);
                search_node = search_node->process_branches[process_iterate];
                next_node_found = 1;
                add_to_history(history,current_volume,process_iterate);
                //printf_history(history);
                break;
            }
          }
        }
        
        if (next_node_found == 0) {
          // write this history to disk / memory
          hist_num++;
          //printf("Reached next_node_found == 0 \n");
          if (history->used_elements > 0 && search_node->number_of_rays > 0) {
            //printf("%d rays (I=%E) with history: \t", search_node->number_of_rays, search_node->intensity);
            //printf_history(history);
            
            total_history.saved_histories[total_history.used_elements].used_elements = history->used_elements;
            total_history.saved_histories[total_history.used_elements].elements = malloc(total_history.saved_histories[total_history.used_elements].used_elements*sizeof(struct history_node_struct));
            for (history_iterate = 0;history_iterate<history->used_elements;history_iterate++) {
                total_history.saved_histories[total_history.used_elements].elements[history_iterate] = history->elements[history_iterate];
                //printf("total_history.saved_histories[total_history.used_elements].elements[%d].volume_index \n",history_iterate,total_history.saved_histories[total_history.used_elements].elements[history_iterate].volume_index);
            }
            //total_history.saved_histories[total_history.used_elements].elements = history->elements;
            total_history.saved_histories[total_history.used_elements].intensity = search_node->intensity;
            total_history.saved_histories[total_history.used_elements].number_of_rays = search_node->number_of_rays;
            total_history.used_elements++;
          }
          
          history->used_elements = 0;
          
          // end of tree, no new nodes
          if (search_node->above == NULL) {
            done = 1;
          } else {
            // reset to the root of the tree
            *kill_candidate = NULL;
            free(search_node);
            search_node = master_list->elements[volume_index];
            
            if (volume_index != 0)
              current_number_of_processes = Volumes[volume_index]->p_physics->number_of_processes;
            else
              current_number_of_processes = 0;
    
            current_volume = volume_index;
            add_to_history(history,current_volume,-1);
          }
          
        }
    }
  }
  
  if (history->allocated_elements > 0) free(history->elements);

  qsort(total_history.saved_histories,total_history.used_elements,sizeof (struct saved_history_struct), Sample_compare_history_intensities);
  
  
  
  MPI_MASTER(
  printf("\n\n");
  printf("Top 20 most common histories. Shows the index of volumes entered (VX), and the scattering processes (PX)\n");
  for (history_iterate=0;history_iterate<total_history.used_elements && history_iterate<20;history_iterate++) {
    printf("%d\t N I=%E \t", total_history.saved_histories[history_iterate].number_of_rays, total_history.saved_histories[history_iterate].intensity);
    printf_history(&total_history.saved_histories[history_iterate]);
  }
   
  FILE *fp;
  fp = fopen("union_history.dat","w");
  
  fprintf(fp,"History file written by the McStas component Union_master \n");
  fprintf(fp,"When running with MPI, the results may be from just a single thread, meaning intensities are divided by number of threads\n");
  fprintf(fp,"----- Description of the used volumes -----------------------------------------------------------------------------------\n");
  
  fprintf(fp,"V0: Surrounding vacuum \n");
  for (volume_iterate=1;volume_iterate<number_of_volumes;volume_iterate++) {
    fprintf(fp,"V%d: %s  ",volume_iterate,Volumes[volume_iterate]->name);
    fprintf(fp,"Material: %s  ",Volumes[volume_iterate]->p_physics->name);
    for (process_iterate=0;process_iterate<Volumes[volume_iterate]->p_physics->number_of_processes;process_iterate++) {
      fprintf(fp," P%d: %s",process_iterate,Volumes[volume_iterate]->p_physics->p_scattering_array[process_iterate].name);
    }
    fprintf(fp,"\n");
  }
  fprintf(fp,"----- Histories sorted after intensity ----------------------------------------------------------------------------------\n");
  
  for (history_iterate=0;history_iterate<total_history.used_elements;history_iterate++) {
    fprintf_total_history(&total_history.saved_histories[history_iterate],fp);
    // Garbage collection
    if (total_history.saved_histories[history_iterate].used_elements > 0) free(total_history.saved_histories[history_iterate].elements);
  }
  fclose(fp);
  
  )
  
  // Garbage collection
  if (total_history.allocated_elements > 0) free(total_history.saved_histories);
  
  
};


// -------------    Intersection table functions   --------------------------------------------------------
int clear_intersection_table(struct intersection_time_table_struct *intersection_time_table) {
    // Resets the intersection table when a scattering have occured.
    int iterate_volumes,iterate_solutions;
    
    // Start at one because vacuum (0) does not have a listing in the intersection table
    for (iterate_volumes = 1;iterate_volumes < intersection_time_table->num_volumes;iterate_volumes++) {
        intersection_time_table->calculated[iterate_volumes] = 0;
        // This second loop is added for safty in debugging phase, but can be removed as the information should never be accesed when calculated = 0
        for (iterate_solutions = 0;iterate_solutions < intersection_time_table->n_elements[iterate_volumes];iterate_solutions++) {
            intersection_time_table->intersection_times[iterate_volumes][iterate_solutions] = -1;
        }
    }
    
    return 1;
};

void print_intersection_table(struct intersection_time_table_struct *intersection_time_table) {
    int num_volumes,iterate,solutions;
    int max_number_of_solutions = 0;
    
    num_volumes = intersection_time_table->num_volumes;
    
    for (iterate = 0;iterate < num_volumes;iterate++) {
        if (max_number_of_solutions < intersection_time_table->n_elements[iterate])
            max_number_of_solutions = intersection_time_table->n_elements[iterate];
    }
    
    printf("------------------ INTERSECTION_TIME_TABLE -----------------");
    for (solutions = 2;solutions < max_number_of_solutions;solutions++) printf("------------");
    printf("\n");
    
    // printf("iterate      |");

    printf("           ");
    printf("| CALCULATED  |");
    for (solutions = 0;solutions < max_number_of_solutions;solutions++) {
        printf(" - SOLUTION %d - |",solutions);
    }

    printf("\n");
    for (iterate = 0;iterate < num_volumes;iterate++){
    // print iterate number
    printf("Volume %d   |",iterate);
    printf(" ---- %d ---- |",intersection_time_table->calculated[iterate]);
    
        for (solutions = 0;solutions < max_number_of_solutions;solutions++) {
          if (intersection_time_table->n_elements[iterate] > solutions && intersection_time_table->calculated[iterate] == 1)
            if (intersection_time_table->intersection_times[iterate][solutions] > 0)
             printf("   %1.8f   |",intersection_time_table->intersection_times[iterate][solutions]);
            else
             printf("   %1.7f   |",intersection_time_table->intersection_times[iterate][solutions]);
          else
            printf("                |");
        }
    printf("\n");
    }
    printf("------------------------------------------------------------");
    for (solutions = 2;solutions < max_number_of_solutions;solutions++) printf("------------");
    printf("\n");
    
    
    };
    
// -------------    Drawing functions   --------------------------------------------------------
void merge_lines_to_draw(struct lines_to_draw *lines_master,struct lines_to_draw *lines_new) {
    if (lines_master->number_of_lines == 0) {
    lines_master->number_of_lines = lines_new->number_of_lines;
    lines_master->lines = malloc(lines_master->number_of_lines*sizeof(struct line_segment));
    lines_master->lines = lines_new->lines;
    // One could free lines_new->lines;
    } else {
    int iterate;
    struct line_segment *temp_lines;
    temp_lines = malloc(lines_master->number_of_lines*sizeof(struct line_segment));
    for (iterate = 0;iterate < lines_master->number_of_lines;iterate++) temp_lines[iterate] = lines_master->lines[iterate];
    free(lines_master->lines);
    lines_master->lines = malloc((lines_master->number_of_lines+lines_new->number_of_lines)*sizeof(struct line_segment));
    for (iterate = 0;iterate < lines_master->number_of_lines;iterate++) lines_master->lines[iterate] = temp_lines[iterate];
    for (iterate = 0;iterate < lines_new->number_of_lines;iterate++) lines_master->lines[iterate+lines_master->number_of_lines] = lines_new->lines[iterate];
    lines_master->number_of_lines = lines_master->number_of_lines + lines_new->number_of_lines;
    free(temp_lines);
    }
    };

int r_has_highest_priority(Coords point,int N,struct geometry_struct **Geometries,int number_of_volumes) {
    // Function that test if a point in Volume N has the highest priority.
    // Returns 0 if another volume has higher priority at the point, and it's mask status is 1
    // Returns 1 if no other volume has higher priority at the point
    // (not active) Returns a larger integer if the volume N is a mask, and the point is not in the volume it is masking,
    //  which results in the drawing function making that number of dashes
    
    
    int mask_status,*mask_start,*mask_check;
    // If the volume is a mask, it does not have a priority, and is always on top
    if (Geometries[N]->is_mask_volume == 1) {
      return 1;
      // Can draw parts of the mask that is outside the volume it masks as dashed lines, but it just looks messy
      /*
      mask_status = 1;
      for (mask_check=mask_start=Geometries[N]->mask_list.elements;mask_check-mask_start<Geometries[N]->mask_list.num_elements;mask_check++) {
        if (Geometries[*mask_check]->within_function(point,Geometries[*mask_check]) == 1) {
          return 1; // If the point is within a volume the mask is masking, draw it as a solid line
        }
      }
      // As it was not inside any of the volumes the mask is masking, draw it as a dashed line
      return 5;
      */
    }
    
    // If the volume is a masked volume, check if the point is within it's masks
    if (Geometries[N]->is_masked_volume == 1) {
      if (Geometries[N]->mask_mode == 1) { // ALL mode, need to be within ALL masks
        for (mask_check=mask_start=Geometries[N]->masked_by_list.elements;mask_check-mask_start<Geometries[N]->masked_by_list.num_elements;mask_check++) {
          if (Geometries[*mask_check]->within_function(point,Geometries[*mask_check]) == 0) {
            return 0; // If the point is just outside one mask, the mask status is 0 and the point does not have highest priority
          }
        }
      } else { // ANY mode, need to be within at least one mask
        mask_status = 0;
        for (mask_check=mask_start=Geometries[N]->masked_by_list.elements;mask_check-mask_start<Geometries[N]->masked_by_list.num_elements;mask_check++) {
          if (Geometries[*mask_check]->within_function(point,Geometries[*mask_check]) == 1) {
            mask_status = 1;
            break;
          }
        }
        if (mask_status == 0) {
          return 0; // If it was not in a single of it's masks, the point did not have highest priority
        }
      }
    }
    
    int volume_index;
    double self_priority;
    self_priority = Geometries[N]->priority_value;
    
    
    for (volume_index = 1;volume_index<number_of_volumes;volume_index++) {
      if (volume_index != N && Geometries[volume_index]->is_mask_volume == 0) {
        if (Geometries[volume_index]->within_function(point,Geometries[volume_index])) {
          if (Geometries[volume_index]->is_masked_volume == 1) {
            // Since this volume is masked, the mask status need to be checked
            if (Geometries[volume_index]->mask_mode == 1) { //ALL mode, need to be within ALL masks
              mask_status = 1;
              for (mask_check=mask_start=Geometries[volume_index]->masked_by_list.elements;mask_check-mask_start<Geometries[volume_index]->masked_by_list.num_elements;mask_check++) {
                if (Geometries[*mask_check]->within_function(point,Geometries[*mask_check]) == 0) {
                  mask_status = 0;
                  break;
                }
              }
            } else { // ANY mode, need to be within at least one mask
              mask_status = 0;
              for (mask_check=mask_start=Geometries[volume_index]->masked_by_list.elements;mask_check-mask_start<Geometries[volume_index]->masked_by_list.num_elements;mask_check++) {
                if (Geometries[*mask_check]->within_function(point,Geometries[*mask_check]) == 1) {
                  mask_status = 1;
                  break;
                }
              }
            }
          } else mask_status = 1;
          if (Geometries[volume_index]->priority_value > self_priority && mask_status == 1) {
            // printf("Volume %d did not have highest priority at (%f,%f,%f) (Volume number %d was above)\n",N,point.x,point.y,point.z,volume_index);
            return 0;
          }
        }
      }
    }
    // printf("Volume %d did have highest priority at (%f,%f,%f) \n",N,point.x,point.y,point.z);
    return 1;
    }
    
void draw_line_positions(Coords point1,Coords point2) {
        //line(point1.x,point1.y,point1.z,point2.x,point2.y,point2.z);
        // sigh, can not use line in share. Need to save the information and pass to the mcdisplay part.
    }

int Sample_compare_doubles (const void *a, const void *b) {
  const double *da = (const double *) a;
  const double *db = (const double *) b;

  return (*da > *db) - (*da < *db);
}

struct lines_to_draw draw_line_with_highest_priority(Coords position1,Coords position2,int N,struct geometry_struct **Geometries,int number_of_volumes,int max_number_of_solutions) {
    
    int volume_index,iterate,permanent_list_length = 0;
    int number_of_solutions;
    double temp_intersection[max_number_of_solutions];
    double r1[3],r2[3],direction[3];
    struct pointer_to_1d_double_list intersection_list;
    
    intersection_list.num_elements = 0;
    
    // double *permanent_intersection_list,*storage; // old ways
    
    r1[0] = position1.x;
    r1[1] = position1.y;
    r1[2] = position1.z;
    r2[0] = position2.x;
    r2[1] = position2.y;
    r2[2] = position2.z;
    
    // printf("r1 = (%f,%f,%f) \n",r1[0],r1[1],r1[2]);
    // printf("r2 = (%f,%f,%f) \n",r2[0],r2[1],r2[2]);
    
    direction[0] = r2[0] - r1[0];
    direction[1] = r2[1] - r1[1];
    direction[2] = r2[2] - r1[2];
    int geometry_output;
    
    // Find intersections
    for (volume_index = 1;volume_index < number_of_volumes; volume_index++) {
        if (volume_index != N) {
            geometry_output = Geometries[volume_index]->intersect_function(temp_intersection,&number_of_solutions,r1,direction,Geometries[volume_index]);
             // printf("No solutions for intersection (Volume %d) with %d \n",N,volume_index);
                for (iterate=0;iterate<number_of_solutions;iterate++) {
                    // print_1d_double_list(intersection_list,"intersection_list");
                    if (temp_intersection[iterate] > 0 && temp_intersection[iterate] < 1) {
                        add_element_to_double_list(&intersection_list,temp_intersection[iterate]);
                        // printf("solution taken = %f\n",temp_intersection[iterate]);
                        }                         // printf("solution ignored = %f\n",temp_intersection[iterate]);
                    // print_1d_double_list(intersection_list,"intersection_list");
                
                }
                // printf("First (%d) solutions added to the solution stack, intersection with %d \n",number_of_solutions,volume_index);
                // printf(" Solutions: ");
                // for (iterate = 0;iterate < intersection_list.num_elements;iterate++) printf("%f ",intersection_list.elements[iterate]);
                // printf("\n");
            
        }
    }
    // Now we have a list of intersection distances between r1 and r2 and all volumes.
    // This list needs to be sorted before we continue!
    
    qsort(intersection_list.elements,intersection_list.num_elements,sizeof (double), Sample_compare_doubles);
    // print_1d_double_list(intersection_list,"after sorting");
    // printf(" Solutions (after sorting): ");
    // for (iterate = 0;iterate < intersection_list.num_elements;iterate++) printf("%f ",intersection_list.elements[iterate]);
    // printf("\n");
    
    Coords points[intersection_list.num_elements+2];
    points[0] = coords_set(r1[0],r1[1],r1[2]);
    points[intersection_list.num_elements+1] = coords_set(r2[0],r2[1],r2[2]);
    for (iterate = 1;iterate < intersection_list.num_elements+1;iterate++) {
        points[iterate].x = r1[0] + direction[0]*intersection_list.elements[iterate-1];
        points[iterate].y = r1[1] + direction[1]*intersection_list.elements[iterate-1];
        points[iterate].z = r1[2] + direction[2]*intersection_list.elements[iterate-1];
    }
    
    // printf("Points on the list:\n");
    // for (iterate = 0;iterate < intersection_list.num_elements + 2;iterate++) {
    // //         printf("(%f,%f,%f)\n",points[iterate].x,points[iterate].y,points[iterate].z);
    // }
    // printf("\n");
  
    struct line_segment lines[intersection_list.num_elements+1];
    int draw_logic[intersection_list.num_elements+1];
    //struct lines_to_draw draw_order;
    Coords midpoint;
    struct lines_to_draw draw_order;
    draw_order.number_of_lines = 0;
    
    // printf("test2 in function \n");
    int number_of_dashes;
    
    for (iterate = 0;iterate < intersection_list.num_elements + 1;iterate++) {
        lines[iterate].point1 = points[iterate];
        lines[iterate].point2 = points[iterate+1];
        midpoint.x = 0.5*(lines[iterate].point1.x + lines[iterate].point2.x);
        midpoint.y = 0.5*(lines[iterate].point1.y + lines[iterate].point2.y);
        midpoint.z = 0.5*(lines[iterate].point1.z + lines[iterate].point2.z);
        
        if ((number_of_dashes = r_has_highest_priority(midpoint,N,Geometries,number_of_volumes)) != 0) {
            draw_order.number_of_lines++;
            draw_logic[iterate] = number_of_dashes;
        } else draw_logic[iterate] = 0;
    }
   
    // printf("test3 in function \n");
    
    if (draw_order.number_of_lines > 0) {
        draw_order.lines = malloc(draw_order.number_of_lines*sizeof(struct line_segment));
        draw_order.number_of_lines = 0;
        for (iterate = 0;iterate < intersection_list.num_elements + 1;iterate++) {
            if (draw_logic[iterate] != 0) {
              lines[iterate].number_of_dashes = draw_logic[iterate];
              draw_order.lines[draw_order.number_of_lines++] = lines[iterate];
            }
        }
        if (intersection_list.num_elements > 0) free(intersection_list.elements);
    }

    // printf("function done \n");
    return draw_order;
    }

struct lines_to_draw draw_circle_with_highest_priority(Coords center,Coords vector,double radius,int N,struct geometry_struct **Geometries,int number_of_volumes,int max_number_of_solutions) {
    int number_of_positions = 100;

    // normalize vector
    double vector_length = length_of_position_vector(vector);
    // print_position(vector,"start vector");
    vector.x /= vector_length;
    vector.y /= vector_length;
    vector.z /= vector_length;
    // print_position(vector,"start vector normalized");
    
    // Create a vector from the center of the circle to a point on the circumference by cross product
    double cross_input[3] = {0,1,0};
    // In case the cross input is parallel with the vector, a new is chosen. Both can't be parallel.
    if (scalar_prod(cross_input[0],cross_input[1],cross_input[2],vector.x,vector.y,vector.z) > 0.99) {
        cross_input[0] = 1; cross_input[1] = 0; cross_input[2] = 0;
    }
    // print_position(make_position(cross_input),"cross input");
    
    double cross_product1[3] = {0,0,0};
    vec_prod(cross_product1[0],cross_product1[1],cross_product1[2],vector.x,vector.y,vector.z,cross_input[0],cross_input[1],cross_input[2]);
    
    // print_position(make_position(cross_product1),"cross_product1");
    
    double cross_length = length_of_3vector(cross_product1);
    
    // printf("cross_length = %f \n",cross_length);
    
    cross_product1[0] /= cross_length;
    cross_product1[1] /= cross_length;
    cross_product1[2] /= cross_length;
    
    cross_product1[0] *= radius;
    cross_product1[1] *= radius;
    cross_product1[2] *= radius;
    
    // printf("cross_product1 = (%f,%f,%f) \n",cross_product1[0],cross_product1[1],cross_product1[2]);
    
    int iterate;
    double rotate_angle;
    Coords radial_position,old_radial_position;
    struct lines_to_draw temp_draw_order,return_draw_order;
    return_draw_order.number_of_lines = 0;
    // Generate an array of positions by rotating this vector around the given vector, this corresponds to points on the circle
    old_radial_position.x = center.x + cross_product1[0];
    old_radial_position.y = center.y + cross_product1[1];
    old_radial_position.z = center.z + cross_product1[2];

    // printf("old_radial_position = (%f,%f,%f) \n",old_radial_position.x,old_radial_position.y,old_radial_position.z);
    
    for (iterate = 0;iterate < number_of_positions-1;iterate++) {
        rotate_angle = 2*3.14159*((double) iterate + 1.0)/((double) number_of_positions);
        rotate(radial_position.x,radial_position.y,radial_position.z,cross_product1[0],cross_product1[1],cross_product1[2],rotate_angle,vector.x,vector.y,vector.z);
        radial_position.x += center.x;
        radial_position.y += center.y;
        radial_position.z += center.z;
        
        // Use the draw_lines_with_highest_priority to draw get draw_orders for each line piece
        temp_draw_order = draw_line_with_highest_priority(radial_position,old_radial_position,N,Geometries,number_of_volumes,max_number_of_solutions);
       // Assemble these draw_orders to one large draw order
        merge_lines_to_draw(&return_draw_order,&temp_draw_order);

        old_radial_position.x = radial_position.x;
        old_radial_position.y = radial_position.y;
        old_radial_position.z = radial_position.z;
    }
    
    radial_position.x = center.x + cross_product1[0];
    radial_position.y = center.y + cross_product1[1];
    radial_position.z = center.z + cross_product1[2];
    // Use the draw_lines_with_highest_priority to draw get draw_orders for each line piece
    temp_draw_order = draw_line_with_highest_priority(radial_position,old_radial_position,N,Geometries,number_of_volumes,max_number_of_solutions);
    // Assemble these draw_orders to one large draw order
    merge_lines_to_draw(&return_draw_order,&temp_draw_order);
    
    // clean up
    if (temp_draw_order.number_of_lines > 0) free(temp_draw_order.lines);
    
    // return the large draw order.
    return return_draw_order;
    }

// -------------    Geometry functions   --------------------------------------------------------

#include "Geometry_functions.c"

// -------------    List generator functions   --------------------------------------------------


int within_which_volume(Coords pos, struct pointer_to_1d_int_list input_list, struct pointer_to_1d_int_list destinations_list, struct Volume_struct **Volumes, struct pointer_to_1d_int_list *mask_status_list, int number_of_volumes, int *volume_logic_copy, int *ListA, int *ListB) {
    // This function identifies in which of the volumes of the input list the position pos lies in.
    // pos: position for which the current volume should be found for
    // input list: list of potential volumes, reduced to the ones without parents (their children will be checked)
    // OLD VERSION: volume logic: A logic list of allowed volumes for lookup, volume 1 3 and 5 in a case of 10 volumes would be [0 1 0 1 0 1 0 0 0 0]
    // destinations_list: list of allowed destinations (the original destinations list)
    // Volumes: Main volumes array
    // volume_logic_copy: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    // ListA: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    // ListB: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    
    // Algorithm description
    // Check all of input list for pos being within them, those that have it within them are:
    //      checked against the current highest priority
    //          if higher, is the new highest priority and the new pick for volume
    //      have all their direct children added to the next list to be checked (but any volume can only be added to that list once)
    // Once a run have been made where the next list to check is empty, the answer for new pick for volume is taken.
    
    // Mask update:
    // Algorithm description
    // Check all of input list for pos being within them, those that have it within them are:
    //      checked against the current highest priority and mask status
    //          if higher, this is the new highest priority and the new pick for volume
    //      have all their direct children added to the next list to be checked (but any volume can only be added to that list once)
    // Once a run have been made where the next list to check is empty, the answer for new pick for volume is taken.
    
    // The advantage of the method is that potentially large numbers of children are skipped when their parents do not contain the position.
    // The overhead cost is low, as all the lists are prealocated.
    // Should be checked which of the two implementations is faster, as this is much more complicated than simply checking all possibilities.
    // No within_function call should be made twice, as the same volume number will not be checked twice because of the properties of the direct_children list and the volume_logic that removes duplicates on each level.
    
    
    // This function uses too much memory, the memory required for the volume logic list is n_volumes^2 ints, or for a MACS monochromator 127000 ints.
    // Instead the original destinations list must be used, and a function for quick lookup in a (sorted) destinations list made.
    // Still need a list of n_volumes length for control to avoid adding the same volume to the list twice.
    
    
    int ListA_length=0,ListB_length=0;
    int done = 0;
    int i,direct_children_index;
    int *temp_pointer;
    double max_priority=-1000000;
    int residing_volume=0; // 0 can be removed from the input list if default is 0
    int this_mask_status,mask_index,mask_global_index;
    
    // volume_logic_copy
    //for (i=0;i<volume_logic.num_elements;i++) volume_logic_copy[i] = volume_logic.elements[i];
    
    // low memory version of volume_logic_copy
    for (i=0;i<number_of_volumes;i++) volume_logic_copy[i] = 0;
    for (i=0;i<destinations_list.num_elements;i++) volume_logic_copy[destinations_list.elements[i]] = 1;
    printf("within_which_volume debug %i\n",input_list.num_elements);

    // Does one loop through the algorithm first to set up ListA instead of copying it from input_list, which takes time
    for (i=0;i<input_list.num_elements;i++) {
            if (Volumes[input_list.elements[i]]->geometry.within_function(pos,&Volumes[input_list.elements[i]]->geometry) == 1) {
                printf("The position is inside of volume %d\n",input_list.elements[i]);
                if (Volumes[input_list.elements[i]]->geometry.is_masked_volume == 1) {
                    // if the volume is masked, I need to know if it can be a destination volume from the mask_status_list.
                    // if the masked volume is in ANY mode,
                    this_mask_status=1;
                    //print_1d_int_list(*mask_status_list,"mask status list from within_which_volume");
                    for (mask_index=0;mask_index<Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.num_elements;mask_index++) {
                        //printf("Looking at the mask with global index %d \n",Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.elements[mask_index]);
                        if (mask_status_list->elements[Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.elements[mask_index]] == 1) {
                            if (Volumes[input_list.elements[i]]->geometry.mask_mode == 2) { // ANY (break if any one in)
                                this_mask_status=1;
                                break;
                            }
                            //printf("global index %d had mask status = 1 \n",Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.elements[mask_index]);
                        } else {
                          //printf("global index %d had mask status = 0 \n",Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.elements[mask_index]);
                          this_mask_status = 0;
                          if(Volumes[input_list.elements[i]]->geometry.mask_mode == 1) break; // ALL (break if any one out)
                        }
                    }
                    //printf("This volume is masked, and the mask status is %d\n",this_mask_status);
                } else this_mask_status = 1; // if the volume is not masked
            
                if (Volumes[input_list.elements[i]]->geometry.priority_value > max_priority && this_mask_status == 1) {
                    max_priority = Volumes[input_list.elements[i]]->geometry.priority_value;
                    residing_volume = input_list.elements[i];
                    //printf("residing volume set to %d\n",residing_volume);
                }
                    for (direct_children_index = 0;direct_children_index < Volumes[input_list.elements[i]]->geometry.direct_children.num_elements;direct_children_index++) {
                        if (volume_logic_copy[Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index]] == 1) {
                            ListA[ListA_length++] = Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index];
                            volume_logic_copy[Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index]] = 0;
                        }
                    }
            }
    }
    //printf("Completed first loop, continued in while loop\n");
    if (ListA_length > 0) {
        while (done == 0) {
            for (i=0;i<ListA_length;i++) {
              //printf("checking element number %d of list A which is volume number %d \n",i,ListA[i]);
                if (Volumes[ListA[i]]->geometry.within_function(pos,&Volumes[ListA[i]]->geometry) == 1) {
                  //printf("ray was inside this volume \n");
                    if (Volumes[ListA[i]]->geometry.is_masked_volume == 1) {
                      //printf("it is a mask and thus need check of mask status \n");
                        // if the volume is masked, I need to know if it can be a destination volume from the mask_status_list.
                        // if the masked volume is in ANY mode,
                        this_mask_status=1;
                        for (mask_index=0;mask_index<Volumes[ListA[i]]->geometry.masked_by_mask_index_list.num_elements;mask_index++) {
                            if (mask_status_list->elements[Volumes[ListA[i]]->geometry.masked_by_mask_index_list.elements[mask_index]] == 1) {
                                if (Volumes[ListA[i]]->geometry.mask_mode == 2) { // ANY (break if any one in)
                                    this_mask_status=1;
                                    break;
                                }
                            } else {
                              this_mask_status = 0;
                              if(Volumes[ListA[i]]->geometry.mask_mode == 1) break; // ALL (break if any one out)
                            }
                        }
                    } else this_mask_status = 1;
                    //printf("the mask status is %d \n",this_mask_status);
                    if (Volumes[ListA[i]]->geometry.priority_value > max_priority && this_mask_status == 1) {
                        max_priority = Volumes[ListA[i]]->geometry.priority_value;
                        residing_volume = ListA[i];
                    }
                    //printf("Adding direct children to list B \n");
                        for (direct_children_index = 0;direct_children_index < Volumes[ListA[i]]->geometry.direct_children.num_elements;direct_children_index++) {
                            //printf("Checking direct_child number %d which is %d \n",direct_children_index,Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index]);
                            if (volume_logic_copy[Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index]] == 1) {
                                //printf("It's volume_logic was 1, and it is thus added to listB with index %d \n",ListB_length);
                                ListB[ListB_length++] = Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index];
                                volume_logic_copy[Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index]] = 0;
                            }
                        }
                    //printf("List B is now: ");
                    //for (direct_children_index=0;direct_children_index<ListB_length;direct_children_index++) printf("%d ",ListB[direct_children_index]);
                    //printf("\n");
                    
                    
                }
            }
            if (ListB_length==0) done = 1;
            else {
                
                for (i=0;i<ListB_length;i++) ListA[i] = ListB[i];
                ListA_length = ListB_length;
                ListB_length = 0;
                
                /*
                // Could do this with pointers instead to avoid this for loop (and needless copy)
                // This code block fails on the cluster in rare circumstances
                ListA = temp_pointer;
                ListA = ListB;
                ListB = temp_pointer;
                ListA_length=ListB_length;
                ListB_length=0;
                */
            }
        }
    }
    //printf("residing volume returned %d\n",residing_volume);
    return residing_volume;
};

int within_which_volume_GPU(Coords pos, struct pointer_to_1d_int_list input_list, struct pointer_to_1d_int_list destinations_list, struct Volume_struct **Volumes, struct pointer_to_1d_int_list *mask_status_list, int number_of_volumes, int *volume_logic_copy, int *ListA, int *ListB) {
    // This function identifies in which of the volumes of the input list the position pos lies in.
    // pos: position for which the current volume should be found for
    // input list: list of potential volumes, reduced to the ones without parents (their children will be checked)
    // OLD VERSION: volume logic: A logic list of allowed volumes for lookup, volume 1 3 and 5 in a case of 10 volumes would be [0 1 0 1 0 1 0 0 0 0]
    // destinations_list: list of allowed destinations (the original destinations list)
    // Volumes: Main volumes array
    // volume_logic_copy: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    // ListA: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    // ListB: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    
    // Algorithm description
    // Check all of input list for pos being within them, those that have it within them are:
    //      checked against the current highest priority
    //          if higher, is the new highest priority and the new pick for volume
    //      have all their direct children added to the next list to be checked (but any volume can only be added to that list once)
    // Once a run have been made where the next list to check is empty, the answer for new pick for volume is taken.
    
    // Mask update:
    // Algorithm description
    // Check all of input list for pos being within them, those that have it within them are:
    //      checked against the current highest priority and mask status
    //          if higher, this is the new highest priority and the new pick for volume
    //      have all their direct children added to the next list to be checked (but any volume can only be added to that list once)
    // Once a run have been made where the next list to check is empty, the answer for new pick for volume is taken.
    
    // The advantage of the method is that potentially large numbers of children are skipped when their parents do not contain the position.
    // The overhead cost is low, as all the lists are prealocated.
    // Should be checked which of the two implementations is faster, as this is much more complicated than simply checking all possibilities.
    // No within_function call should be made twice, as the same volume number will not be checked twice because of the properties of the direct_children list and the volume_logic that removes duplicates on each level.
    
    
    // This function uses too much memory, the memory required for the volume logic list is n_volumes^2 ints, or for a MACS monochromator 127000 ints.
    // Instead the original destinations list must be used, and a function for quick lookup in a (sorted) destinations list made.
    // Still need a list of n_volumes length for control to avoid adding the same volume to the list twice.
    
    
    int ListA_length=0,ListB_length=0;
    int done = 0;
    int i,direct_children_index;
    int *temp_pointer;
    double max_priority=-1000000;
    int residing_volume=0; // 0 can be removed from the input list if default is 0
    int this_mask_status,mask_index,mask_global_index;
    
    // volume_logic_copy
    //for (i=0;i<volume_logic.num_elements;i++) volume_logic_copy[i] = volume_logic.elements[i];
    
    // low memory version of volume_logic_copy
    for (i=0;i<number_of_volumes;i++) volume_logic_copy[i] = 0;
    for (i=0;i<destinations_list.num_elements;i++) volume_logic_copy[destinations_list.elements[i]] = 1;
    //printf("within_which_volume debug\n");

    // Does one loop through the algorithm first to set up ListA instead of copying it from input_list, which takes time
    for (i=0;i<input_list.num_elements;i++) {
            if (r_within_function(pos, &Volumes[input_list.elements[i]]->geometry) == 1) {
                //printf("The position is inside of volume %d\n",input_list.elements[i]);
                if (Volumes[input_list.elements[i]]->geometry.is_masked_volume == 1) {
                    // if the volume is masked, I need to know if it can be a destination volume from the mask_status_list.
                    // if the masked volume is in ANY mode,
                    this_mask_status=1;
                    //print_1d_int_list(*mask_status_list,"mask status list from within_which_volume");
                    for (mask_index=0;mask_index<Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.num_elements;mask_index++) {
                        //printf("Looking at the mask with global index %d \n",Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.elements[mask_index]);
                        if (mask_status_list->elements[Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.elements[mask_index]] == 1) {
                            if (Volumes[input_list.elements[i]]->geometry.mask_mode == 2) { // ANY (break if any one in)
                                this_mask_status=1;
                                break;
                            }
                            //printf("global index %d had mask status = 1 \n",Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.elements[mask_index]);
                        } else {
                          //printf("global index %d had mask status = 0 \n",Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.elements[mask_index]);
                          this_mask_status = 0;
                          if(Volumes[input_list.elements[i]]->geometry.mask_mode == 1) break; // ALL (break if any one out)
                        }
                    }
                    //printf("This volume is masked, and the mask status is %d\n",this_mask_status);
                } else this_mask_status = 1; // if the volume is not masked
            
                if (Volumes[input_list.elements[i]]->geometry.priority_value > max_priority && this_mask_status == 1) {
                    max_priority = Volumes[input_list.elements[i]]->geometry.priority_value;
                    residing_volume = input_list.elements[i];
                    //printf("residing volume set to %d\n",residing_volume);
                }
                    for (direct_children_index = 0;direct_children_index < Volumes[input_list.elements[i]]->geometry.direct_children.num_elements;direct_children_index++) {
                        if (volume_logic_copy[Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index]] == 1) {
                            ListA[ListA_length++] = Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index];
                            volume_logic_copy[Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index]] = 0;
                        }
                    }
            }
    }
    //printf("Completed first loop, continued in while loop\n");
    if (ListA_length > 0) {
        while (done == 0) {
            for (i=0;i<ListA_length;i++) {
              //printf("checking element number %d of list A which is volume number %d \n",i,ListA[i]);
                if (r_within_function(pos,&Volumes[ListA[i]]->geometry) == 1) {
                  //printf("ray was inside this volume \n");
                    if (Volumes[ListA[i]]->geometry.is_masked_volume == 1) {
                      //printf("it is a mask and thus need check of mask status \n");
                        // if the volume is masked, I need to know if it can be a destination volume from the mask_status_list.
                        // if the masked volume is in ANY mode,
                        this_mask_status=1;
                        for (mask_index=0;mask_index<Volumes[ListA[i]]->geometry.masked_by_mask_index_list.num_elements;mask_index++) {
                            if (mask_status_list->elements[Volumes[ListA[i]]->geometry.masked_by_mask_index_list.elements[mask_index]] == 1) {
                                if (Volumes[ListA[i]]->geometry.mask_mode == 2) { // ANY (break if any one in)
                                    this_mask_status=1;
                                    break;
                                }
                            } else {
                              this_mask_status = 0;
                              if(Volumes[ListA[i]]->geometry.mask_mode == 1) break; // ALL (break if any one out)
                            }
                        }
                    } else this_mask_status = 1;
                    //printf("the mask status is %d \n",this_mask_status);
                    if (Volumes[ListA[i]]->geometry.priority_value > max_priority && this_mask_status == 1) {
                        max_priority = Volumes[ListA[i]]->geometry.priority_value;
                        residing_volume = ListA[i];
                    }
                    //printf("Adding direct children to list B \n");
                        for (direct_children_index = 0;direct_children_index < Volumes[ListA[i]]->geometry.direct_children.num_elements;direct_children_index++) {
                            //printf("Checking direct_child number %d which is %d \n",direct_children_index,Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index]);
                            if (volume_logic_copy[Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index]] == 1) {
                                //printf("It's volume_logic was 1, and it is thus added to listB with index %d \n",ListB_length);
                                ListB[ListB_length++] = Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index];
                                volume_logic_copy[Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index]] = 0;
                            }
                        }
                    //printf("List B is now: ");
                    //for (direct_children_index=0;direct_children_index<ListB_length;direct_children_index++) printf("%d ",ListB[direct_children_index]);
                    //printf("\n");
                    
                    
                }
            }
            if (ListB_length==0) done = 1;
            else {
                
                for (i=0;i<ListB_length;i++) ListA[i] = ListB[i];
                ListA_length = ListB_length;
                ListB_length = 0;
                
                /*
                // Could do this with pointers instead to avoid this for loop (and needless copy)
                // This code block fails on the cluster in rare circumstances
                ListA = temp_pointer;
                ListA = ListB;
                ListB = temp_pointer;
                ListA_length=ListB_length;
                ListB_length=0;
                */
            }
        }
    }
    //printf("residing volume returned %d\n",residing_volume);
    return residing_volume;
};




int within_which_volume_debug(Coords pos, struct pointer_to_1d_int_list input_list, struct pointer_to_1d_int_list volume_logic, struct Volume_struct **Volumes, int *volume_logic_copy, int *ListA, int *ListB) {
    // This function identifies in which of the volumes of the input list the position pos lies in.
    // pos: position for which the current volume should be found for
    // input list: list of potential volumes, reduced to the ones without parents (their children will be checked)
    // volume logic: A logic list of allowed volumes for lookup, volume 1 3 and 5 in a case of 10 volumes would be [0 1 0 1 0 1 0 0 0 0]
    // Volumes: Main volumes array
    // volume_logic_copy: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    // ListA: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    // ListB: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    
    // Algorithm description
    // Check all of input list for pos being within them, those that have it within them are:
    //      checked against the current highest priority
    //          if higher, is the new highest priority and the new pick for volume
    //      have all their direct children added to the next list to be checked (but any volume can only be added to that list once)
    // Once a run have been made where the next list to check is empty, the answer for new pick for volume is taken.
    
    // The advantage of the method is that potentially large numbers of children are skipped when their parents do not contain the position.
    // The overhead cost is low, as all the lists are prealocated.
    // Should be checked which of the two implementations is faster, as this is much more complicated than simply checking all possibilities.
    // No within_function call should be made twice, as the same volume number will not be checked twice because of the properties of the direct_children list and the volume_logic that removes duplicates on each level.
    
    
    int ListA_length=0,ListB_length=0;
    int done = 0;
    int i,direct_children_index;
    int *temp_pointer;
    double max_priority=-1000000;
    int residing_volume=0; // 0 can be removed from the input list if default is 0
    
    // volume_logic_copy
    for (i=0;i<volume_logic.num_elements;i++) volume_logic_copy[i] = volume_logic.elements[i];
    
    // Does one loop through the algorithm first to set up ListA instead of copying it from input_list, which takes time
    for (i=0;i<input_list.num_elements;i++) {
            if (Volumes[input_list.elements[i]]->geometry.within_function(pos,&Volumes[input_list.elements[i]]->geometry) == 1) {
                if (Volumes[input_list.elements[i]]->geometry.priority_value > max_priority) {
                    max_priority = Volumes[input_list.elements[i]]->geometry.priority_value;
                    residing_volume = input_list.elements[i];
                }
                    for (direct_children_index = 0;direct_children_index < Volumes[input_list.elements[i]]->geometry.direct_children.num_elements;direct_children_index++) {
                        if (volume_logic_copy[Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index]] == 1) {
                            ListA[ListA_length++] = Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index];
                            volume_logic_copy[Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index]] = 0;
                        }
                    }
            }
    }
    if (ListA_length > 0) {
        while (done == 0) {
        
            printf("ListA = [");
            for (i=0;i<ListA_length;i++) {
                printf("%d,",ListA[i]);
            }
            printf("]\n");
            for (i=0;i<ListA_length;i++) {
                if (Volumes[ListA[i]]->geometry.within_function(pos,&Volumes[ListA[i]]->geometry) == 1) {
                    if (Volumes[ListA[i]]->geometry.priority_value > max_priority) {
                        max_priority = Volumes[ListA[i]]->geometry.priority_value;
                        residing_volume = ListA[i];
                    }
                    //if (ListA[i]!=0) {
                        for (direct_children_index = 0;direct_children_index < Volumes[ListA[i]]->geometry.direct_children.num_elements;direct_children_index++) {
                            if (volume_logic_copy[Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index]] == 1) {
                                ListB[ListB_length++] = Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index];
                                volume_logic_copy[Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index]] = 0;
                            }
                        }
                    //}
                }
            }
            if (ListB_length==0) done = 1;
            else {
                ListA = temp_pointer;
                ListA = ListB;
                ListB = temp_pointer;
                ListA_length=ListB_length;
                ListB_length=0;
            }
        }
    }
    printf("Volume number %d had the highest priority of checked volumes\n",residing_volume);
    return residing_volume;
};

int inside_function(struct Volume_struct *parent_volume, struct Volume_struct *child_volume) {
    // Function that calls the correct within function depending on the shapes of the two volumes
    if (strcmp("sphere",parent_volume->geometry.shape) == 0 && strcmp("sphere",child_volume->geometry.shape) == 0) {
        if (sphere_within_sphere(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("cylinder",parent_volume->geometry.shape) == 0 && strcmp("cylinder",child_volume->geometry.shape) == 0) {
        if (cylinder_within_cylinder(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("box",parent_volume->geometry.shape) == 0 && strcmp("box",child_volume->geometry.shape) == 0) {
        if (box_within_box(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("cone",parent_volume->geometry.shape) == 0 && strcmp("cone",child_volume->geometry.shape) == 0) {
        if (cone_within_cone(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("mesh",parent_volume->geometry.shape) == 0 && strcmp("mesh",child_volume->geometry.shape) == 0) {
        if (mesh_within_mesh(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("box",parent_volume->geometry.shape) == 0 && strcmp("cylinder",child_volume->geometry.shape) == 0) {
        if (cylinder_within_box(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("cylinder",parent_volume->geometry.shape) == 0 && strcmp("box",child_volume->geometry.shape) == 0) {
        if (box_within_cylinder(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("box",parent_volume->geometry.shape) == 0 && strcmp("sphere",child_volume->geometry.shape) == 0) {
        if (sphere_within_box(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("sphere",parent_volume->geometry.shape) == 0 && strcmp("box",child_volume->geometry.shape) == 0) {
        if (box_within_sphere(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("sphere",parent_volume->geometry.shape) == 0 && strcmp("cylinder",child_volume->geometry.shape) == 0) {
        if (cylinder_within_sphere(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("cylinder",parent_volume->geometry.shape) == 0 && strcmp("sphere",child_volume->geometry.shape) == 0) {
        if (sphere_within_cylinder(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("cone",parent_volume->geometry.shape) == 0 && strcmp("sphere",child_volume->geometry.shape) == 0) {
        if (sphere_within_cone(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("sphere",parent_volume->geometry.shape) == 0 && strcmp("cone",child_volume->geometry.shape) == 0) {
        if (cone_within_sphere(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("cone",parent_volume->geometry.shape) == 0 && strcmp("cylinder",child_volume->geometry.shape) == 0) {
        if (cylinder_within_cone(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("cylinder",parent_volume->geometry.shape) == 0 && strcmp("cone",child_volume->geometry.shape) == 0) {
        if (cone_within_cylinder(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("cone",parent_volume->geometry.shape) == 0 && strcmp("box",child_volume->geometry.shape) == 0) {
        if (box_within_cone(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("box",parent_volume->geometry.shape) == 0 && strcmp("cone",child_volume->geometry.shape) == 0) {
        if (cone_within_box(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else {
        #ifndef OPENACC
        printf("Need within function for type: ");
        printf("%s",parent_volume->geometry.shape);
        printf(" and type: ");
        printf("%s",child_volume->geometry.shape);
        printf(".\n");
        printf("It is not yet supported to mix mesh geometries with the basic shapes, but several mesh geometries are allowed.\n");
        exit(1);
	#endif
    }
    
    return 0;
};

void generate_children_lists(struct Volume_struct **Volumes, struct pointer_to_1d_int_list **true_children_lists, int number_of_volumes, int verbal) {
  // This function generates a list of children for each volume.
  // A volume m is a child of volume n, if the entire space ocupied by volume m is inside of the space ocupied by volume n
  // A volume m is a true child of volume n, if the entire space coupied by volume m after it's masks are applied is inside the volume ocupied by volume n after it's masks are applied
  
  
  MPI_MASTER(
  if (verbal) printf("\nGenerating children lists --------------------------- \n");
  )

  // Mask update: Creating a temporary list for each volume
  struct pointer_to_1d_int_list *temporary_children_lists;
  temporary_children_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list));

  // The surrounding vacuum, volume 0, done outside of for loop.
  temporary_children_lists[0].num_elements = number_of_volumes - 1;
  temporary_children_lists[0].elements = malloc(temporary_children_lists[0].num_elements*sizeof(int));
  
  int parent;
  for (parent=1;parent<number_of_volumes;parent++) {
      temporary_children_lists[0].elements[parent-1] = parent;
  }
  
  //if (verbal) printf("did temporary children list \n");
  //print_1d_int_list(temporary_children_lists[0],"temp children list [0]");
  
  // Hardcoding that every volume is a child of the surrounding vacuum
  Volumes[0]->geometry.children.num_elements = number_of_volumes-1;
  Volumes[0]->geometry.children.elements = malloc((number_of_volumes-1)*sizeof(int));
  true_children_lists[0] = malloc(sizeof(struct pointer_to_1d_int_list));
  true_children_lists[0]->num_elements = number_of_volumes - 1;
  true_children_lists[0]->elements = malloc((number_of_volumes-1)*sizeof(int));
  
  //if (verbal) printf("allocated true children lists \n");
  
  for (parent=1;parent<number_of_volumes;parent++) {
      Volumes[0]->geometry.children.elements[parent-1] = parent;
      true_children_lists[0]->elements[parent-1] = parent;
  }
  
  char string_output[128];
  MPI_MASTER(
  if (verbal) sprintf(string_output,"Children for Volume %d",0);
  if (verbal) print_1d_int_list(Volumes[0]->geometry.children,string_output);
  )
  
  
  // Generating the children lists for all other volumes using the appropriate geometry functions
  struct pointer_to_1d_int_list temp_list_local;
  temp_list_local.num_elements = number_of_volumes;
  temp_list_local.elements = malloc(number_of_volumes*sizeof(int));
  
  struct pointer_to_1d_int_list true_temp_list_local;
  true_temp_list_local.num_elements = number_of_volumes;
  true_temp_list_local.elements = malloc(number_of_volumes*sizeof(int));
  
  
  int child,used_elements,used_elements_true;
  for (parent=1;parent<number_of_volumes;parent++) {
      used_elements = 0;used_elements_true = 0;
      for (child=1;child<number_of_volumes;child++) {
        if (child != parent) {
          // Call inside_function that selects the proper within function for the two volumes
          if (1 == inside_function(Volumes[parent],Volumes[child])) {
            temp_list_local.elements[used_elements++] = child;
            true_temp_list_local.elements[used_elements_true++] = child;
          }
        } else true_temp_list_local.elements[used_elements_true++] = child; // Needed when children list takes mask into account
      }
      // Temp test
      allocate_list_from_temp(used_elements,temp_list_local,&Volumes[parent]->geometry.children);
      // Assing the children list to a temporary list as the masks have yet to be taken into account
      temporary_children_lists[parent].num_elements=0;
      allocate_list_from_temp(used_elements_true,true_temp_list_local,&temporary_children_lists[parent]);
      
      
      MPI_MASTER(
      if (verbal) sprintf(string_output,"Children for Volume %d (temporary_list)",parent);
      if (verbal) print_1d_int_list(temporary_children_lists[parent],string_output);
      )
      
      MPI_MASTER(
      if (verbal) sprintf(string_output,"Children for Volume %d (permanent_list)",parent);
      if (verbal) print_1d_int_list(Volumes[parent]->geometry.children,string_output);
      )
      
  }
  
  // mask update:
  // The logical expression: (child c parent AND child c parent_mask) OR (child_mask c parent AND child_mask c parent_mask)
  //  needs to be evaluated for each child / parent combination in order to take the masks of each into account
  
  int logic_var1,logic_var2,logic_var_ANY,logic_var_ALL;
  int mask_index,mask_index_child,mask_index_parent;
  int volume_C,volume_P;
  // Loop that takes masks into account
  for (parent=1;parent<number_of_volumes;parent++) {
    used_elements = 0;
    for (child=1;child<number_of_volumes;child++) {
     if (child != parent && 0 == on_int_list(Volumes[parent]->geometry.masked_by_list,child)) {
        // The children list for each volume does not need to contain the volume itself
        //  And a parent masked by it's child can not have that mask as a child
      
      // Here c means within in the sense of a set being part of another set
      // Logical expression to be evaluated: (child c parent AND child c parent_mask) OR (child_mask c parent AND child_mask c parent_mask)
      logic_var1 = on_int_list(temporary_children_lists[parent],child);
      if (logic_var1 == 1 && Volumes[parent]->geometry.is_masked_volume == 1) {
        // if the parent volume is masked, the child also need to be inclosed in these masks to fulfill this side of the logical expression
        logic_var_ANY = 0;
        for (mask_index=0;mask_index<Volumes[parent]->geometry.masked_by_list.num_elements;mask_index++) {
          if (0 == on_int_list(temporary_children_lists[Volumes[parent]->geometry.masked_by_list.elements[mask_index]],child)) {
            if (Volumes[parent]->geometry.mask_mode == 1) {
              logic_var1 = 0;
              break;
            }
          } else logic_var_ANY = 1;
        }
        if (Volumes[parent]->geometry.mask_mode == 2) logic_var1 = logic_var_ANY;
      }
      
      if (logic_var1 == 1) true_temp_list_local.elements[used_elements++] = child;
      else if (Volumes[child]->geometry.is_masked_volume == 1) {
        // If the first side of the logical expression is false, evalute the other side
        // The other side is only relevant if the child volume is masked, otherwise it is ignored
        //printf("Second side of logical expression \n");
        
        logic_var1 = 1; // Assume true
          
        // child_mask c parent
        logic_var_ALL = 0;
        for (mask_index=0;mask_index<Volumes[child]->geometry.masked_by_list.num_elements;mask_index++) {
          if (0 == on_int_list(temporary_children_lists[parent],Volumes[child]->geometry.masked_by_list.elements[mask_index])){
            if (Volumes[child]->geometry.mask_mode == 2) {
              logic_var1 = 0;
              break;
            }
          } else logic_var_ALL = 1;
        }
        if (Volumes[child]->geometry.mask_mode == 1) logic_var1 = logic_var_ALL;
        
        // This line allows the second part of the logical expression to be true in cases where the parent volume is not masked
        if (logic_var1 == 1 && Volumes[parent]->geometry.is_masked_volume == 0) true_temp_list_local.elements[used_elements++] = child;
        
        // There is no reason to check the other part (child_mask c parent_mask) if the first part was not true
        if (logic_var1 == 1 && Volumes[parent]->geometry.is_masked_volume == 1) {
          // This last part requires both the child and the parent to be masked
          // Need to evaluate (child_mask c parent_mask), where both can be a be a list of volume with ALL/ANY modes
          
          if (Volumes[parent]->geometry.mask_mode == 1) {
            logic_var2 = 1; // Assume the logical expression (child_mask c parent_mask) is true
            for (mask_index_parent=0;mask_index_parent<Volumes[parent]->geometry.masked_by_list.num_elements;mask_index_parent++) {
              // As the parent is in ALL mode, the child masks must be within ALL parent masks
              logic_var_ANY = 0; // Assume not a single child is within this parent
              for (mask_index_child=0;mask_index_child<Volumes[child]->geometry.masked_by_list.num_elements;mask_index_child++) {
                volume_P = Volumes[parent]->geometry.masked_by_list.elements[mask_index_parent];
                volume_C = Volumes[child]->geometry.masked_by_list.elements[mask_index_child];
                // Is volume C inside volume P? If yes, volume C must be on volume P's temporary children list
                if (0 == on_int_list(temporary_children_lists[volume_P],volume_C)) {
                  if (Volumes[child]->geometry.mask_mode == 2) {
                    // If child is in ANY mode, any one mask outside is enough to make the expression false
                    logic_var2 = 0;
                    break;
                  }
                } else logic_var_ANY = 1;
              }
              // If child is in ALL mode, then if any one child were within this mask, the logic expression holds true
              if (Volumes[child]->geometry.mask_mode == 1) logic_var2 = logic_var_ANY;
              if (logic_var2 == 0) break; // No need to continue
            }
          } else if (Volumes[parent]->geometry.mask_mode == 2) {
            // If the parent is in ANY mode, it is enough if the child masks are within just 1 of the parent masks
            for (mask_index_parent=0;mask_index_parent<Volumes[parent]->geometry.masked_by_list.num_elements;mask_index_parent++) {
              logic_var2 = 1; // Assume the logical expression (child_mask c parent_mask) is true
              logic_var_ANY = 0; // Assume not a single child is within this parent
              for (mask_index_child=0;mask_index_child<Volumes[child]->geometry.masked_by_list.num_elements;mask_index_child++) {
                volume_P = Volumes[parent]->geometry.masked_by_list.elements[mask_index_parent];
                volume_C = Volumes[child]->geometry.masked_by_list.elements[mask_index_child];
                // Is volume C inside volume P? If yes, volume C must be on volume P's temporary children list
                if (0 == on_int_list(temporary_children_lists[volume_P],volume_C)) {
                  if (Volumes[child]->geometry.mask_mode == 2) {
                    // If child is in ANY mode, any one mask outside is enough to make the expression false
                    logic_var2 = 0;
                    break;
                  }
                } else logic_var_ANY = 1;
              }
              // If child is in ALL mode, then if any one child were within this mask, the logic expression holds true
              if (Volumes[child]->geometry.mask_mode == 1) logic_var2 = logic_var_ANY;
              if (logic_var2 == 1) break; // No need to continue
            }
          }
          
          // if this point is reached, and logic_var2 is true, volume[child] is a child of volume[parent]
          if (logic_var2 == 1) true_temp_list_local.elements[used_elements++] = child;
          
        }
      }
     }
    }
    
    true_children_lists[parent] = malloc(sizeof(struct pointer_to_1d_int_list));
    true_children_lists[parent]->num_elements = 0;
    allocate_list_from_temp(used_elements,true_temp_list_local,true_children_lists[parent]);
    
    MPI_MASTER(
      if (verbal) sprintf(string_output,"True children for Volume (post mask) %d",parent);
      if (verbal) print_1d_int_list(*true_children_lists[parent],string_output);
    )
  }
  
  
  // Clean up of dynamically allocated memory
  
  for(child=0;child<number_of_volumes;child++) free(temporary_children_lists[child].elements);
  free(temporary_children_lists);
  free(true_temp_list_local.elements);
  free(temp_list_local.elements);
};

void generate_overlap_lists(struct pointer_to_1d_int_list **true_overlap_lists, struct pointer_to_1d_int_list **raw_overlap_lists, struct Volume_struct **Volumes, int number_of_volumes, int verbal) {
  // This function generates overlap lists for each volume
  // Volume m overlaps volume n if there is some subset of space they both ocupy (this is called raw overlap)
  // the true overlap list takes mask into account, meaning that the masks are applied before searching for overlap

  MPI_MASTER(
  if (verbal) printf("\nGenerating overlap lists ---------------------------- \n");
  )
  
  // Manually create the overlap list for the surrounding Vacuum, as it overlaps all other volumes
  
  // temporary_overlap_lists are used to save the calculated overlaps to avoid evaluating the heavy functions more than once (twice) for each possible volume pair
  struct pointer_to_1d_int_list *temporary_overlap_lists;
  temporary_overlap_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list));
  
  // Overlap_lists are the final result of the function, and for the surrounding volume it can be set immediatly
  true_overlap_lists[0] = malloc(sizeof(struct pointer_to_1d_int_list));
  true_overlap_lists[0]->num_elements = number_of_volumes-1;
  true_overlap_lists[0]->elements = malloc((number_of_volumes-1)*sizeof(int));
  
  raw_overlap_lists[0] =  malloc(sizeof(struct pointer_to_1d_int_list));
  raw_overlap_lists[0]->num_elements = number_of_volumes;
  raw_overlap_lists[0]->elements = malloc(number_of_volumes*sizeof(int));
  raw_overlap_lists[0]->elements[0] = 0; // Volume 0 overlaps itself
  
  int parent;
  for (parent=1;parent<number_of_volumes;parent++) {
      true_overlap_lists[0]->elements[parent-1] = parent;
      raw_overlap_lists[0]->elements[parent] = parent;
  }
  
  char string_output[128];
  MPI_MASTER(
  if (verbal) sprintf(string_output,"Overlaps for Volume %d",0);
  if (verbal) print_1d_int_list(*true_overlap_lists[0],string_output);
  )
  
  // Generate the overlap lists for the remaining volumes
  struct pointer_to_1d_int_list temp_list_local;
  temp_list_local.num_elements = number_of_volumes;
  temp_list_local.elements = malloc(number_of_volumes*sizeof(int));
  
  int child,used_elements;
  // Create overlap for the remaining volumes
  for (parent=1;parent<number_of_volumes;parent++) {
      true_overlap_lists[parent] = malloc(sizeof(struct pointer_to_1d_int_list));
      used_elements = 0;
      temp_list_local.elements[used_elements++] = 0; // Alwasy overlaps with the surrounding vacuum.
      
      for (child=1;child<number_of_volumes;child++) {
        if (child == parent) temp_list_local.elements[used_elements++] = child;
        else {
        if (strcmp("sphere",Volumes[parent]->geometry.shape) == 0 && strcmp("sphere",Volumes[child]->geometry.shape) == 0) {
            if (sphere_overlaps_sphere(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("cylinder",Volumes[parent]->geometry.shape) == 0 && strcmp("cylinder",Volumes[child]->geometry.shape) == 0) {
            if (cylinder_overlaps_cylinder(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("box",Volumes[parent]->geometry.shape) == 0 && strcmp("box",Volumes[child]->geometry.shape) == 0) {
            if (box_overlaps_box(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("cone",Volumes[parent]->geometry.shape) == 0 && strcmp("cone",Volumes[child]->geometry.shape) == 0) {
            if (cone_overlaps_cone(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("mesh",Volumes[parent]->geometry.shape) == 0 && strcmp("mesh",Volumes[child]->geometry.shape) == 0) {
            if (mesh_overlaps_mesh(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("box",Volumes[parent]->geometry.shape) == 0 && strcmp("cylinder",Volumes[child]->geometry.shape) == 0) {
            if (box_overlaps_cylinder(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("cylinder",Volumes[parent]->geometry.shape) == 0 && strcmp("box",Volumes[child]->geometry.shape) == 0) {
            if (cylinder_overlaps_box(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("box",Volumes[parent]->geometry.shape) == 0 && strcmp("sphere",Volumes[child]->geometry.shape) == 0) {
            if (box_overlaps_sphere(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("sphere",Volumes[parent]->geometry.shape) == 0 && strcmp("box",Volumes[child]->geometry.shape) == 0) {
            if (sphere_overlaps_box(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("sphere",Volumes[parent]->geometry.shape) == 0 && strcmp("cylinder",Volumes[child]->geometry.shape) == 0) {
            if (sphere_overlaps_cylinder(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("cylinder",Volumes[parent]->geometry.shape) == 0 && strcmp("sphere",Volumes[child]->geometry.shape) == 0) {
            if (cylinder_overlaps_sphere(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("cone",Volumes[parent]->geometry.shape) == 0 && strcmp("sphere",Volumes[child]->geometry.shape) == 0) {
            if (cone_overlaps_sphere(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("sphere",Volumes[parent]->geometry.shape) == 0 && strcmp("cone",Volumes[child]->geometry.shape) == 0) {
            if (sphere_overlaps_cone(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("cone",Volumes[parent]->geometry.shape) == 0 && strcmp("cylinder",Volumes[child]->geometry.shape) == 0) {
            if (cone_overlaps_cylinder(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("cylinder",Volumes[parent]->geometry.shape) == 0 && strcmp("cone",Volumes[child]->geometry.shape) == 0) {
            if (cylinder_overlaps_cone(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("cone",Volumes[parent]->geometry.shape) == 0 && strcmp("box",Volumes[child]->geometry.shape) == 0) {
            if (cone_overlaps_box(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("box",Volumes[parent]->geometry.shape) == 0 && strcmp("cone",Volumes[child]->geometry.shape) == 0) {
            if (box_overlaps_cone(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else {
            printf("Need overlap function for type: ");
            printf("%s",Volumes[parent]->geometry.shape);
            printf(" and type: ");
            printf("%s",Volumes[child]->geometry.shape);
            printf(".\n");
            printf("It is not yet supported to mix mesh geometries with the basic shapes, but several mesh geometries are allowed.\n");
            exit(1);
        }
        }
      }
      //allocate_list_from_temp(used_elements,temp_list_local,overlap_lists[parent]);
      allocate_list_from_temp(used_elements,temp_list_local,&temporary_overlap_lists[parent]);
      
      // Save the raw overlap data to the raw_overlap_lists[parent] list
      raw_overlap_lists[parent] = malloc(sizeof(struct pointer_to_1d_int_list));
      raw_overlap_lists[parent]->num_elements = 0;
      allocate_list_from_temp(used_elements,temp_list_local,raw_overlap_lists[parent]);
      
      if (verbal) sprintf(string_output,"Overlaps for Volume (pre mask) %d",parent);
      MPI_MASTER(
      if (verbal) print_1d_int_list(temporary_overlap_lists[parent],string_output);
      )
  }
  
  
  // The temporary_overlap_lists gives the raw overlap data for all volume pairs
  // The next tasks is to take the masks into account, so that a volume is only said to overlap another if all of these statements are true:
  // The volumes overlap each other
  // The mask's of volume 1 overlap volume 2
  // The mask's of volume 2 overlap volume 1
  // The mask's of volume 1 overlap the masks of volume 2
  
  
  int logic_var;
  int overlap_ANY,overlap_ANY_p,overlap_ANY_c;
  int mask_index,mask_index_c,mask_index_p;
  int mask_volume_index,mask_volume_index_p,mask_volume_index_c;

  for (parent=1;parent<number_of_volumes;parent++) {
    used_elements = 0;
    temp_list_local.elements[used_elements++] = 0; // Alwasy overlaps with the surrounding vacuum.
    for (child=1;child<number_of_volumes;child++) {
      if (child != parent) {
        logic_var = 1; // Assume the volumes overlap, and search for evidence that they do not.
      
        // First check if the volumes overlap
        if (0 == on_int_list(temporary_overlap_lists[parent],child)) logic_var = 0;
        
      
        // Check if child overlap with parents masks
        if (logic_var == 1 && Volumes[parent]->geometry.is_masked_volume == 1) {
          overlap_ANY = 0;
          for (mask_index=0;mask_index<Volumes[parent]->geometry.masked_by_list.num_elements;mask_index++) {
            mask_volume_index = Volumes[parent]->geometry.masked_by_list.elements[mask_index];
            if (0 == on_int_list(temporary_overlap_lists[mask_volume_index],child)) {
              if (Volumes[parent]->geometry.mask_mode == 1) {
                logic_var = 0;
                break;
              }
            } else overlap_ANY = 1;
          }
          if (Volumes[parent]->geometry.mask_mode == 2) logic_var = overlap_ANY;
        }
      
        // Check if parent overlap with childs masks
        if (logic_var == 1 && Volumes[child]->geometry.is_masked_volume == 1) {
          overlap_ANY = 0;
          for (mask_index=0;mask_index<Volumes[child]->geometry.masked_by_list.num_elements;mask_index++) {
            mask_volume_index = Volumes[child]->geometry.masked_by_list.elements[mask_index];
            if (0 == on_int_list(temporary_overlap_lists[mask_volume_index],parent)) {
              if (Volumes[child]->geometry.mask_mode == 1) {
                logic_var = 0;
                break;
              }
            } else overlap_ANY = 1;
          }
          if (Volumes[child]->geometry.mask_mode == 2) logic_var = overlap_ANY;
        }
      
        // Check if parents masks overlap childrens masks
        if (logic_var == 1 && Volumes[parent]->geometry.is_masked_volume == 1 && Volumes[child]->geometry.is_masked_volume == 1) {
          overlap_ANY = 0;
          for (mask_index_p=0;mask_index_p<Volumes[parent]->geometry.masked_by_list.num_elements;mask_index_p++) {
            mask_volume_index_p = Volumes[parent]->geometry.masked_by_list.elements[mask_index_p];
            overlap_ANY_p = 1;
            overlap_ANY_c = 0;
            for (mask_index_c=0;mask_index_c<Volumes[child]->geometry.masked_by_list.num_elements;mask_index_c++) {
              mask_volume_index_c = Volumes[child]->geometry.masked_by_list.elements[mask_index_c];
              if (0 == on_int_list(temporary_overlap_lists[mask_volume_index_p],mask_volume_index_c)) {
                if (Volumes[parent]->geometry.mask_mode == 1 && Volumes[child]->geometry.mask_mode == 1) {
                  // If both are in ALL mode and just one combination of masks does not overlap, neither does the common set
                  logic_var = 0;
                  break;
                }
                if (Volumes[parent]->geometry.mask_mode == 2 && Volumes[child]->geometry.mask_mode == 1) {
                  // If the parent is in ANY mode, but the child is in ALL, any one child not overlapping this parent mask, stops the chance for this parent mask
                  overlap_ANY_p = 0;
                  break;
                }
                
              } else {
                // Here because mask_volume_index_p and mask_volume_index_c does overlap
                if (Volumes[parent]->geometry.mask_mode == 1 && Volumes[child]->geometry.mask_mode == 2) {
                  // If the parent is in ALL mode and the child is in ANY mode, stop if a single parent volume does not overlap any child
                  overlap_ANY_c = 1;
                }
                if (Volumes[parent]->geometry.mask_mode == 2 && Volumes[child]->geometry.mask_mode == 2) {
                  // If both parent and child are in any mode, any one overlap between the masks is sufficient
                  overlap_ANY = 1;
                  // Could actually just commit to the overlap list here, and stop all loops.
                }
              }
              
            }
            if (Volumes[parent]->geometry.mask_mode == 1 && Volumes[child]->geometry.mask_mode == 2) logic_var = overlap_ANY_c;
            if (Volumes[parent]->geometry.mask_mode == 2 && Volumes[child]->geometry.mask_mode == 1 && overlap_ANY_p == 1) {
              // When parent is in any mode, and child is in ALL mode, any parent mask that overlaps all children masks is enough to end the parent loop
              logic_var = 1;
              break; // Without this break, only the last parent will matter
            }
            // if (overlap_ANY == 1) break; would speed things up a bit, but only after testing has been started and then it will be repeated
          }
          if (Volumes[parent]->geometry.mask_mode == 2 && Volumes[child]->geometry.mask_mode == 2) logic_var = overlap_ANY;
          // If both volumes have the ANY mode, just one case of overlap is enough.
        }
        
        // If all of the 4 statements above evaluate to true, the two volumes parent and child do overlap and it is added to the list.
        if (logic_var == 1) temp_list_local.elements[used_elements++] = child;
        
      }
    }
    // Allocate the actual overlap list with the new temp_list_local
    allocate_list_from_temp(used_elements,temp_list_local,true_overlap_lists[parent]);
    if (verbal) sprintf(string_output,"Overlaps for Volume (post mask) %d",parent);
    MPI_MASTER(
    if (verbal) print_1d_int_list(*true_overlap_lists[parent],string_output);
    )
  }
  
  // Clean up of dynamically allocated memory
  for(child=1;child<number_of_volumes;child++) free(temporary_overlap_lists[child].elements);
  free(temporary_overlap_lists);
  free(temp_list_local.elements);
  
};

void add_to_mask_intersect_list(struct pointer_to_1d_int_list *mask_intersect_list, int given_volume_index) {
    // A bit simpler than before
    if (on_int_list(*mask_intersect_list,given_volume_index) == 0)
      add_element_to_int_list(mask_intersect_list,given_volume_index);
}


void generate_intersect_check_lists(struct pointer_to_1d_int_list **overlap_lists,struct Volume_struct **Volumes, int number_of_volumes, int verbal) {
  // Generate intersection list for each volume
  MPI_MASTER(
  if (verbal) printf("\nGenerating intersect check lists -------------------- \n");
  )
  // Take overlap list for volume n
  // Remove entries with p < p(n)
  // Remove entries with parents on the list
  
  // Mask update: Mask volumes does not have priorities, do not do step 2 for mask volumes
  // Mask update: Instead of step 3 (Remove entries with parents on the list), move these entries to a Mask intersect list if that parent is masking the entry, otherwise remove
  
  // 1) Mask update: Take overlap list for volume n
  // 2) Mask update: Remove entries with p < p(n), except mask volumes, that are moved to another list A
  // 3) Mask update: Entries with parents on the list are removed (if these parents are masked, only remove the entry if it is a child of their parents masks)
  // 4) Possible optimization: Entries with parents on the A list, are moved to the appropriate mask list for this volume
  // 5) Mask update: Entries on list A are added again, if they are not parents of volume n
  // 6) Mask update: Entries on the main list that are masked are moved to the appropriate mask list for this volume, if their mask is on list A
  
  // Justification of the top algorithm:
  // No need to check intersections with something that does not overlap this volume, so start with intersect list and remove from that.
  // Entries with p < p(n) are "beneath" this volume, and thus doesn't cut it, mask volumes are however always "above" and are treated with care (moved to list A)
  // Entries with parents still on the list is removed, as it is impossible to intersect with them without first going through the parent
  // Entries with parents on the A list can only be seen if the ray is in that particular mask (the parent on the A list), so it can be added to the appropriate mask list for this volume
  // Now all masks that cut the volume are transfered back to the main list, the only masks that overlap and does not cut are parents of this volume, and these are thus not added
  // The entries on the list that are masked are added to the appropriate mask lists of this volume, but only if their mask is on the A list, as only masks on list A can have mask status 1 which is required for this to be relevant (this was true automatically for the ones added to the appropriate lists in the optimization step).
  
  
  // The intersect check list for volume 0 is done manually
  // Declare list for holding logic data for each index in the overlap list of this volume
  struct pointer_to_1d_int_list logic_list;
  logic_list.num_elements = overlap_lists[0]->num_elements;
  logic_list.elements = malloc(logic_list.num_elements * sizeof(int));
  
  // Declare similar list for List A
  struct pointer_to_1d_int_list mask_logic_list;
  mask_logic_list.num_elements = overlap_lists[0]->num_elements;
  mask_logic_list.elements = malloc(mask_logic_list.num_elements * sizeof(int));
  
  
  int iterate;
  int overlap_index;
  
  /*
  // Thise code is an old broken version of intersection_check_list generation only for volume 0, but this case is now included in the loop.
  // Assume all volumes on overlap list should be on intersection check list, and remove the once that should not.
  for (iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 1;
  // Asuume no volumes should go on the mask_logic_list, but add the ones that do.
  for (iterate=0;iterate<mask_logic_list.num_elements;iterate++) mask_logic_list.elements[iterate] = 0;
  
  for (overlap_index = 0;overlap_index < overlap_lists[0]->num_elements;overlap_index++) {
    // No volumes to remove with lower priority, as surrounding vacuum have the lowest.
    
    // Check if this overlap_lists[0]->elements[overlap_index] is a child of another member of the overlap list
    for (iterate = 0;iterate < overlap_lists[0]->num_elements;iterate++) {
        if (iterate != overlap_index) {
            // We are now checking if Volumes[overlap_lists[0]->elements[iterate]]->geometry.children contains overlap_lists[overlap_index]
            if (on_int_list(Volumes[overlap_lists[0]->elements[iterate]]->geometry.children,overlap_lists[0]->elements[overlap_index])) logic_list.elements[overlap_index] = 0;
        }
    }
  }

  Volumes[0]->geometry.intersect_check_list.num_elements = sum_int_list(logic_list);
  Volumes[0]->geometry.intersect_check_list.elements = malloc(Volumes[0]->geometry.intersect_check_list.num_elements * sizeof(int));
  
  iterate = 0;
  for (overlap_index = 0;overlap_index < overlap_lists[0]->num_elements;overlap_index++) {
        if (logic_list.elements[overlap_index])   Volumes[0]->geometry.intersect_check_list.elements[iterate++] = overlap_lists[0]->elements[overlap_index];
  }
  if (logic_list.num_elements > 0) free(logic_list.elements);
  if (mask_logic_list.num_elements > 0) free(mask_logic_list.elements);
  
  MPI_MASTER(
  print_1d_int_list(Volumes[0]->geometry.intersect_check_list,"Intersect check list for Volume 0 (manual)");
  )
  */
  
  // The intersect check lists for the remaining volumes are generated
  int volume_index,mask_volume_number,mask_index;
  //for (volume_index = 1;volume_index < number_of_volumes;volume_index++) {
  for (volume_index = 0;volume_index < number_of_volumes;volume_index++) {
    
      // 1) Take overlap list for volume n
      logic_list.num_elements = overlap_lists[volume_index]->num_elements;
      logic_list.elements = malloc(logic_list.num_elements * sizeof(int));
      mask_logic_list.num_elements = overlap_lists[volume_index]->num_elements;
      mask_logic_list.elements = malloc(mask_logic_list.num_elements * sizeof(int));
      
      
      
      if (Volumes[volume_index]->geometry.is_mask_volume == 1) {
        // Masks do not have any entries on their intersect check list, as they are never the current volume
        for (iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 0;
        for (iterate=0;iterate<mask_logic_list.num_elements;iterate++) mask_logic_list.elements[iterate] = 0;
      } else {
        for (iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 1;
        for (iterate=0;iterate<mask_logic_list.num_elements;iterate++) mask_logic_list.elements[iterate] = 0;
      }
      
      //2) Remove entries with p < p(n), except mask volumes, that are moved to another list A
      for (overlap_index = 0;overlap_index < overlap_lists[volume_index]->num_elements;overlap_index++) {
        
        if (overlap_lists[volume_index]->elements[overlap_index] == 0) logic_list.elements[overlap_index] = 0; // The surrounding vacuum has lower priority (and is not a mask)
        else if (Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.is_mask_volume == 1) {
           logic_list.elements[overlap_index] = 0;      // Remove this volume from the main list
           mask_logic_list.elements[overlap_index] = 1; // Add mask volumes to seperat list
        }
        else if (volume_index != 0) { // Only relevant to remove elements because of priority if the volume_index is different from 0, meaning the surrounding vacuum skips this step
           if (Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.priority_value < Volumes[volume_index]->geometry.priority_value) {
           // If the investigated volume on the overlap_list have lower priority than the volume with volume_index, remove it
           logic_list.elements[overlap_index] = 0; // Remove this volume from the list
           }
        }
      }
      
      // 3) Entries with parents on the list are removed
      for (overlap_index = 0;overlap_index < overlap_lists[volume_index]->num_elements;overlap_index++) {
        // Check if this overlap_lists[0]->elements[overlap_index] is a child of another member of the overlap list
        for (iterate = 0;iterate < overlap_lists[volume_index]->num_elements;iterate++) {
             if (iterate != overlap_index) { // Only necessary if a volume determines that it has itself as child, but a nice safety.
                // We are now checking if Volumes[overlap_lists[volume_index]->elements[iterate]]->geometry.children contains overlap_lists[overlap_index]
                // The && part is needed because we do not remove children of elements that have allready been removed because of their priority
                
                // if (on_int_list(Volumes[overlap_lists[volume_index]->elements[iterate]]->geometry.children,overlap_lists[volume_index]->elements[overlap_index]) && logic_list.elements[overlap_lists[volume_index]->elements[iterate]] == 1) {logic_list.elements[overlap_index] = 0; bug fixed on 3/4 2016
                if (on_int_list(Volumes[overlap_lists[volume_index]->elements[iterate]]->geometry.children,overlap_lists[volume_index]->elements[overlap_index]) && logic_list.elements[iterate] == 1) logic_list.elements[overlap_index] = 0;
                
                /*
                Explanation with simpler notation
                
                Overlap list of volume i = O_i
                Element j of overlap list i = O_i(j)
                
                Children list of volume i = C_i
                Element j on children list i = C_i(j)
                
                Priority of volume i p(i)
                
                for i = volumes
                    for j in O_i(j) 
                        logic(j) = 1;
                        if p(i) > p(O_i(j)) logic(j) = 0; // Remove if lower priority than the currently checked
                        
                        for k in O_i(k)
                            if (O_i(j) is contained on the list C_k && logic(k) == 1) logic(j) = 0
                */
                
                // 4) Entries with parents on the A list, are moved to the appropriate mask list for this volume
                if (on_int_list(Volumes[overlap_lists[volume_index]->elements[iterate]]->geometry.children,overlap_lists[volume_index]->elements[overlap_index]) && mask_logic_list.elements[iterate] == 1) {
                   logic_list.elements[overlap_index] = 0; // Remove from main list (Not strictly needed as it will be removed already if it is on list A)
                   // Add overlap_lists[volume_index]->elements[overlap_index] to volumes mask intersect list for mask with index overlap_lists[volume_index]->elements[iterate]
                   //add_to_mask_intersect_lists(&Volumes[volume_index]->geometry.mask_intersect_lists,overlap_lists[volume_index]->elements[iterate],overlap_lists[volume_index]->elements[overlap_index]);
                   // Simpler method that just collects all the masked intersect parts on a 1d_int list instead of seperating them for each mask
                   add_to_mask_intersect_list(&Volumes[volume_index]->geometry.mask_intersect_list,overlap_lists[volume_index]->elements[overlap_index]);
                }
                
             }
        }
      }
      
      // 5) Entries on list A are added again, if they are not parents of volume n
      // Time to add mask volumes removed back to the intersect list, if they are not parents of the current volume, meaning the current volume should not be a child of the mask
      for (overlap_index = 0;overlap_index < overlap_lists[volume_index]->num_elements;overlap_index++) {
        if (mask_logic_list.elements[overlap_index] == 1) {
          mask_volume_number = overlap_lists[volume_index]->elements[overlap_index];
          if (on_int_list(Volumes[mask_volume_number]->geometry.children,volume_index) == 0) logic_list.elements[overlap_index] = 1; // Add it back to the list
        }
      }
      
      // 6) Entries on the main list that are masked are moved to the appropriate mask list for this volume, if their mask is on list A
      int mask_index,mask_mode,found_index,logic_ALL;
      for (overlap_index = 0;overlap_index < overlap_lists[volume_index]->num_elements;overlap_index++) {
        if (logic_list.elements[overlap_index] == 1 && Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.is_masked_volume == 1) {
          logic_list.elements[overlap_index] = 0; // If the volume is masked, remove it from the intersect check list
          // Could actually keep it on the intersect list if the volume's mask is a parent of the current volume, now it will just be added in all cases
          
          // When ALL setting is used, all masks should overlap the current volume, otherwise the user probably made a mistake, this should be checked in error checking
          // When ANY setting is used, at least one mask should overlap the current volume, otherwise the user probably made a mistake, this should be checked in error checking
          // mask_mode == 1 => ALL / mask_mode == 2 => ANY
          mask_mode = Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.mask_mode;
          
          if (mask_mode == 1) {
            logic_ALL = 1;
            for (iterate=0;iterate<Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.masked_by_list.num_elements;iterate++) {
              mask_index = Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.masked_by_list.elements[iterate];
              
              if (on_int_list(*overlap_lists[volume_index],mask_index) == 0) {
                // If any one of the volumes masks do not overlap with this volume, there is no reason check intersections with the volume regardless of the mask status's
                logic_ALL = 0;
                break;
              }
            }
          if (logic_ALL == 1) {
            // If all of it's masks are overlapping the current volume, add it to all relevant mask intersect lists of this volume
            for (iterate=0;iterate<Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.masked_by_list.num_elements;iterate++) {
              mask_index = Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.masked_by_list.elements[iterate];
              //add_to_mask_intersect_lists(&Volumes[volume_index]->geometry.mask_intersect_lists,mask_index,overlap_lists[volume_index]->elements[overlap_index]);
              // Adding the masked intersect list elements to another list
              add_to_mask_intersect_list(&Volumes[volume_index]->geometry.mask_intersect_list,overlap_lists[volume_index]->elements[overlap_index]);
            }
          }
            
            
          } else if (mask_mode == 2) {
          // When in ANY mode, the problem is easier, as not all masks have to overlap the volume, and we can add each one to the list
            for (iterate=0;iterate<Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.masked_by_list.num_elements;iterate++) {
              mask_index = Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.masked_by_list.elements[iterate];
            
              if (on_int_list(*overlap_lists[volume_index],mask_index) == 1) {
                //add_to_mask_intersect_lists(&Volumes[volume_index]->geometry.mask_intersect_lists,mask_index,overlap_lists[volume_index]->elements[overlap_index]);
                // Adding the masked intersect list elements to another list
                add_to_mask_intersect_list(&Volumes[volume_index]->geometry.mask_intersect_list,overlap_lists[volume_index]->elements[overlap_index]);
              }
            }
            
          }
        }
      }
      
      
      Volumes[volume_index]->geometry.intersect_check_list.num_elements = sum_int_list(logic_list);
      Volumes[volume_index]->geometry.intersect_check_list.elements = malloc(Volumes[volume_index]->geometry.intersect_check_list.num_elements * sizeof(int));
      
      iterate = 0;
      for (overlap_index = 0;overlap_index < overlap_lists[volume_index]->num_elements;overlap_index++) {
            if (logic_list.elements[overlap_index])   Volumes[volume_index]->geometry.intersect_check_list.elements[iterate++] = overlap_lists[volume_index]->elements[overlap_index];
      }
      free(logic_list.elements); // Need to be careful with names for variables to be freed, as they can collide with names in main because of automatic declaration of external variables
      free(mask_logic_list.elements);
      
      
      char string_output[128];
      MPI_MASTER(
      if (verbal) sprintf(string_output,"Intersect check list for Volume %d",volume_index);
      if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.intersect_check_list,string_output);
      if (verbal) sprintf(string_output,"Mask intersect check list for Volume %d",volume_index);
      if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.mask_intersect_list,string_output);
      /*
      if (verbal) {
        for (mask_index=0;mask_index<Volumes[volume_index]->geometry.mask_intersect_lists.number_of_lists;mask_index++) {
          sprintf(string_output," - mask intersect check list for mask %d which is volume %d",mask_index,Volumes[volume_index]->geometry.mask_intersect_lists.mask_indices.elements[mask_index]);
          print_1d_int_list(Volumes[volume_index]->geometry.mask_intersect_lists.lists[mask_index],string_output);
        }
      }
      */
      
      )
  }
};

void generate_parents_lists(struct pointer_to_1d_int_list **parents_lists, struct Volume_struct **Volumes, int number_of_volumes, int verbal, int mask_mode) {
  // Function for generating parent lists for all volumes
  // A volume m has n as a parent, if volume n has volume m as a child
  
  // if mask_mode == 0, masks are ignored, if mask_mode == 1, masks are taken into account.
  
  MPI_MASTER(
  if (verbal) {
    if (mask_mode == 1)
        printf("\nGenerating parents lists ---------------------------- \n");
    else if (mask_mode == 0)
        printf("\nGenerating parents lists (ignoring masks) ----------- \n");
    else {
        printf("Error, the function parents_lists got a non defined mask_mode");
        exit(1);
    }
  }
  
  )
  // Volume iterate has volume p as a parent, if volume p has volume iterate as child.
  
  struct pointer_to_1d_int_list temp_list_local;
  temp_list_local.num_elements = number_of_volumes;
  temp_list_local.elements = malloc(number_of_volumes*sizeof(int));
  
  // Loop over
  int iterate,parent,used_elements;
  for (iterate = 0;iterate < number_of_volumes;iterate++) {
    // clear temp list
    used_elements = 0;
    parents_lists[iterate] = malloc(sizeof(struct pointer_to_1d_int_list));
    for (parent = 0;parent < number_of_volumes;parent++) {
        if (on_int_list(Volumes[parent]->geometry.children,iterate))
          if (mask_mode == 1 || (Volumes[parent]->geometry.is_mask_volume == 0 && Volumes[iterate]->geometry.is_mask_volume == 0))
            temp_list_local.elements[used_elements++] = parent;
    }
      allocate_list_from_temp(used_elements,temp_list_local,parents_lists[iterate]);
      
      char string_output[128];
      MPI_MASTER(
      if (verbal) sprintf(string_output,"Parents for Volume %d",iterate);
      if (verbal) print_1d_int_list(*parents_lists[iterate],string_output);
      )
  }
  free(temp_list_local.elements);

};

void generate_true_parents_lists(struct pointer_to_1d_int_list **parents_lists, struct pointer_to_1d_int_list **true_children_lists, struct Volume_struct **Volumes, int number_of_volumes, int verbal, int mask_mode) {
  // Function for generating parent lists for all volumes
  // A volume m has n as a parent, if volume n has volume m as a child
  
  // if mask_mode == 0, masks are ignored, if mask_mode == 1, masks are taken into account.
  
  MPI_MASTER(
  if (verbal) {
    if (mask_mode == 1)
        printf("\nGenerating parents lists ---------------------------- \n");
    else if (mask_mode == 0)
        printf("\nGenerating parents lists (ignoring masks) ----------- \n");
    else {
        printf("Error, the function parents_lists got a non defined mask_mode");
        exit(1);
    }
  }
  
  )
  // Volume iterate has volume p as a parent, if volume p has volume iterate as child.
  
  struct pointer_to_1d_int_list temp_list_local;
  temp_list_local.num_elements = number_of_volumes;
  temp_list_local.elements = malloc(number_of_volumes*sizeof(int));
  
  // Loop over
  int iterate,parent,used_elements;
  for (iterate = 0;iterate < number_of_volumes;iterate++) {
    // clear temp list
    used_elements = 0;
    parents_lists[iterate] = malloc(sizeof(struct pointer_to_1d_int_list)); // allocate_list_from_temp allocates
    for (parent = 0;parent < number_of_volumes;parent++) {
        //if (on_int_list(Volumes[parent]->geometry.children,iterate))
        if (on_int_list(*true_children_lists[parent],iterate))
          if (mask_mode == 1 || (Volumes[parent]->geometry.is_mask_volume == 0 && Volumes[iterate]->geometry.is_mask_volume == 0))
            temp_list_local.elements[used_elements++] = parent;
    }
      allocate_list_from_temp(used_elements,temp_list_local,parents_lists[iterate]);
      
      char string_output[128];
      MPI_MASTER(
      if (verbal) sprintf(string_output,"Parents for Volume %d",iterate);
      if (verbal) print_1d_int_list(*parents_lists[iterate],string_output);
      )
  }
  free(temp_list_local.elements);

};

void generate_intersect_check_lists_experimental(struct pointer_to_1d_int_list **true_overlap_lists, struct pointer_to_1d_int_list **raw_overlap_lists, struct pointer_to_1d_int_list **parents_lists, struct pointer_to_1d_int_list **true_parents_lists , struct Volume_struct **Volumes, int number_of_volumes, int verbal) {
  // Generates the intersect_check_list and mask_intersect_list for each Volume.
  /*
  Description of needed lists for volume n:
  Children list: List of volumes that is contained within volume n (is stored in the Volumes struct)
  True Children list: List of volumes that when masked by their masks is contained within volume n when masked by it's masks
  
  Parents list: List of volumes that contains volume n
  True parents list: List of volumes that when masked by their masks contains volume n when it is masked by it's masks
  
  raw overlap list: List of volumes whos geometry overlaps the geometry of volume n
  true overlap list: List of volumes whos geometry overlaps the geometry of volume n when the masks of both volumes are applied
  
  
  
  The algorithm:
  
  1) Take the true overlap list for volume n
  2) remove parents of n (normal parent list, with masks)
  3) remove volumes that do not mask n and are true parents of n
  4) remove volumes that are not masks and have lower priority than n
  5) remove volumes that have at least one true parent on the list (in step 4) that is not a mask volume
  6) split the list into two, the intersect_check_list which is all non masked volumes still on the list, and the mask_intersect_list which is the masked volumes
  7) remove volumes on the mask_intersect_list whos mask does not overlap (standard overlap list) n
  
  In step 5 the order in which the volumes are tested and removed may matter, so it is specifically stated that it is as the list looked in step 4.
  */
  
  MPI_MASTER(
  if (verbal) printf("\nGenerating intersect check lists -------------------- \n");
  )
  
  struct pointer_to_1d_int_list work_list;
  struct pointer_to_1d_int_list logic_list;
  
  int volume_index,iterate,parent,mask_index,masked_volume_index,ANY_logic,true_parent_volume_number;
  int *mask_check,*mask_start;
  for (volume_index = 0;volume_index < number_of_volumes;volume_index++) {
    
    // 1) Take the true overlap list for volume n
    // Create copy of true_overlap_lists to work with
    
    if (Volumes[volume_index]->geometry.is_mask_volume == 1) {
      // Bug fixed on 26/11/2016, do not create intersection lists for masks as they are not used, and affects destinations lists in a problematic way
      Volumes[volume_index]->geometry.intersect_check_list.num_elements = 0;
      Volumes[volume_index]->geometry.mask_intersect_list.num_elements = 0;
      
    } else {
      work_list.num_elements = true_overlap_lists[volume_index]->num_elements;
      work_list.elements = malloc(work_list.num_elements * sizeof(int));
      for (iterate=0;iterate<work_list.num_elements;iterate++) work_list.elements[iterate] = true_overlap_lists[volume_index]->elements[iterate];
    
    //if (verbal) print_1d_int_list(work_list,"After 1)");
    
    
    //2) remove parents of n (normal parent list, with masks)
    for (iterate=work_list.num_elements-1;iterate>-1;iterate--) {
      if (on_int_list(*parents_lists[volume_index],work_list.elements[iterate]))
        remove_element_in_list_by_index(&work_list,iterate);
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 2)");
    
    //3) remove volumes that do not mask n and are true parents of n
    for (iterate=work_list.num_elements-1;iterate>-1;iterate--) {
      if (on_int_list(Volumes[volume_index]->geometry.masked_by_list,work_list.elements[iterate]) == 0 && on_int_list(*true_parents_lists[volume_index],work_list.elements[iterate]))
        remove_element_in_list_by_index(&work_list,iterate);
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 3)");
    
    //4) remove volumes that are not masks and have lower priority than n
    for (iterate=work_list.num_elements-1;iterate>-1;iterate--) {
      if (Volumes[work_list.elements[iterate]]->geometry.is_mask_volume == 0 && Volumes[work_list.elements[iterate]]->geometry.priority_value < Volumes[volume_index]->geometry.priority_value)
        remove_element_in_list_by_index(&work_list,iterate);
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 4)");
    
    //5) remove volumes that have at least one true parent on the list (in step 4) that is not a mask volume
    // Here a logic_list is used to not have the order of removal matter
    logic_list.num_elements = work_list.num_elements;
    logic_list.elements = malloc(logic_list.num_elements * sizeof(int));
    for(iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 1;
    
    for (iterate=0;iterate<work_list.num_elements;iterate++) {
      for (parent=0;parent<true_parents_lists[work_list.elements[iterate]]->num_elements;parent++) {
        true_parent_volume_number = true_parents_lists[work_list.elements[iterate]]->elements[parent];
        if (on_int_list(work_list,true_parent_volume_number) && Volumes[true_parent_volume_number]->geometry.is_mask_volume == 0) {
          // Since element number iterate on the work list have a true parent on the work list, it can be removed
          logic_list.elements[iterate] = 0;
          break;
        }
      }
    }
    
    // Now the elements marked for removal can be removed without interfering in the operation
    for (iterate=work_list.num_elements-1;iterate>-1;iterate--) {
      if (logic_list.elements[iterate] == 0) remove_element_in_list_by_index(&work_list,iterate);
    }
    free(logic_list.elements);
    
    //if (verbal) print_1d_int_list(work_list,"After 5)");
      
    //6) split the list into two, the intersect_check_list which is all non masked volumes still on the list, and the mask_intersect_list which is the masked volumes
    
    Volumes[volume_index]->geometry.intersect_check_list.num_elements = 0;
    Volumes[volume_index]->geometry.mask_intersect_list.num_elements = 0;
    
    for (iterate=0;iterate<work_list.num_elements;iterate++) {
      if (Volumes[work_list.elements[iterate]]->geometry.is_masked_volume == 1) {
        add_element_to_int_list(&Volumes[volume_index]->geometry.mask_intersect_list,work_list.elements[iterate]);
      } else {
        add_element_to_int_list(&Volumes[volume_index]->geometry.intersect_check_list,work_list.elements[iterate]);
      }
    }
    
    //if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.intersect_check_list,"After 6) intersect_check_list");
    //if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.mask_intersect_list,"After 6) mask_intersect_list");
    
    //7) remove volumes on the mask_intersect_list whos masks does not overlap (standard overlap list) n
    for (iterate=Volumes[volume_index]->geometry.mask_intersect_list.num_elements-1;iterate>-1;iterate--) {
      // Need to check if the volumes masking Volumes[volume_index]->geometry.mask_intersect_list.elements[iterate] overlaps with n
      masked_volume_index = Volumes[volume_index]->geometry.mask_intersect_list.elements[iterate];
      
      if (Volumes[masked_volume_index]->geometry.mask_mode == 1) {// All mode, if just one does not overlap, remove the element
        for (mask_start=mask_check=Volumes[masked_volume_index]->geometry.masked_by_list.elements;mask_check-mask_start<Volumes[masked_volume_index]->geometry.masked_by_list.num_elements;mask_check++) {
          if (on_int_list(*raw_overlap_lists[volume_index],*mask_check) == 0) {
            remove_element_in_list_by_index(&Volumes[volume_index]->geometry.mask_intersect_list,iterate);
            break;
          }
        }
      } else { // ANY mode, just one need to overlap in order to keep the element
        ANY_logic = 0;
        for (mask_start=mask_check=Volumes[masked_volume_index]->geometry.masked_by_list.elements;mask_check-mask_start<Volumes[masked_volume_index]->geometry.masked_by_list.num_elements;mask_check++) {
          if (on_int_list(*raw_overlap_lists[volume_index],*mask_check) == 1) {
            ANY_logic = 1;
            break;
          }
        }
        if (ANY_logic == 0) remove_element_in_list_by_index(&Volumes[volume_index]->geometry.mask_intersect_list,iterate);
      }
    }
    
    if (work_list.num_elements > 0) free(work_list.elements);
    }
    
    
    //if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.mask_intersect_list,"After 7) mask_intersect_list");
    
    char string_output[128];
    MPI_MASTER(
    if (verbal) sprintf(string_output,"Intersect check list for Volume %d",volume_index);
    if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.intersect_check_list,string_output);
    if (verbal) sprintf(string_output,"Mask intersect check list for Volume %d",volume_index);
    if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.mask_intersect_list,string_output);
    )
    
    
  }
}

void generate_grandparents_lists(struct pointer_to_1d_int_list **grandparents_lists, struct pointer_to_1d_int_list **parents_lists, int number_of_volumes, int verbal) {
  // Function for generating grandparents lists
  // Volume iterate has volume p as a grandparent, if volume p has a parent T, who has volume iterate as parent.
  // Alternertively:
  // Volume iterate has volume p as a grandparent, if volume p have a child that is volume iterate's parent.
  
  MPI_MASTER(
  if (verbal) printf("\nGenerating grandparents lists ----------------------- \n");
  )

  struct pointer_to_1d_int_list common;
  common.num_elements = number_of_volumes;
  common.elements = malloc(common.num_elements*sizeof(int)); // Maximum needed space.
  
  struct pointer_to_1d_int_list temp_list_local;
  temp_list_local.num_elements = number_of_volumes;
  temp_list_local.elements = malloc(number_of_volumes*sizeof(int));
  
  int iterate,reset_int,parent,child,used_elements;
  for (iterate = 0;iterate < number_of_volumes;iterate++) {
    // clear temp list
    used_elements = 0;

    for (reset_int=0; reset_int<number_of_volumes; reset_int++) common.elements[reset_int] = -1; // Initialize to impossible volume ids
    for (reset_int=0; reset_int<number_of_volumes; reset_int++) temp_list_local.elements[reset_int] = -1; // Initialize to impossible volume ids

    grandparents_lists[iterate] = malloc(sizeof(struct pointer_to_1d_int_list));
    
    for (parent = 0; parent<parents_lists[iterate]->num_elements; parent++) {
        // parent number p parents_lists[iterate].elements.[p] in the parent_list for iterate.
        on_both_int_lists(parents_lists[parents_lists[iterate]->elements[parent]], parents_lists[iterate], &common);
        // returns a pointer_to_1d_list, with all the elements that are in common.
        for (child = 0;child < common.num_elements;child++) {
            // Need to make sure the element is not already on the list
            if (0 == on_int_list(temp_list_local, common.elements[child])) {
              temp_list_local.elements[used_elements++] = common.elements[child];
            }
        }
    }
    allocate_list_from_temp(used_elements, temp_list_local, grandparents_lists[iterate]);
  
    char string_output[128];
    MPI_MASTER(
      if (verbal) sprintf(string_output,"Grandparents for Volume %d", iterate);
      if (verbal) print_1d_int_list(*grandparents_lists[iterate], string_output);
    )
  }
  free(temp_list_local.elements);
  free(common.elements);
};


void generate_destinations_lists_experimental(struct pointer_to_1d_int_list **true_overlap_lists, struct pointer_to_1d_int_list **true_children_lists, struct pointer_to_1d_int_list **true_parents_lists, struct pointer_to_1d_int_list **true_grandparents_lists, struct Volume_struct **Volumes, int number_of_volumes, int verbal) {
  // Generates destinations list for for all volumes
  // Current implementation uses true_parents_lists and true_grandparents_lists that are generated as if no masks were defined

  MPI_MASTER(
  if (verbal) printf("\nGenerating destinations lists ----------------------- \n");
  )
  // Volume 0 has an hardcoded empty destinations list
  Volumes[0]->geometry.destinations_list.num_elements = 0;

  struct pointer_to_1d_int_list work_list;
  int volume_index,iterate,iterate2,found_index,I_index,I_volume;
  
  for (volume_index=1;volume_index<number_of_volumes;volume_index++) {
    
    // 1) Take the true overlap list for volume n
    // Create copy of true_overlap_lists to work with
    work_list.num_elements = true_overlap_lists[volume_index]->num_elements;
    work_list.elements = malloc(work_list.num_elements * sizeof(int));
    for (iterate=0;iterate<work_list.num_elements;iterate++) work_list.elements[iterate] = true_overlap_lists[volume_index]->elements[iterate];
    
    //if (verbal) print_1d_int_list(work_list,"After 1)");
    
    // 2) Remove elements from n's intersection list
    for (iterate=work_list.num_elements-1;iterate>-1;iterate--) {
      if (on_int_list(Volumes[volume_index]->geometry.intersect_check_list,work_list.elements[iterate]))
        remove_element_in_list_by_index(&work_list,iterate);
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 2)");
  
    // 3) Remove true children of non-mask elements on n's intersection list
    for (I_index=0;I_index<Volumes[volume_index]->geometry.intersect_check_list.num_elements;I_index++) {
      I_volume = Volumes[volume_index]->geometry.intersect_check_list.elements[I_index];
      if (Volumes[I_volume]->geometry.is_mask_volume == 0) {
        for (iterate=0;iterate<true_children_lists[I_volume]->num_elements;iterate++) {
          remove_element_in_list_by_value(&work_list,true_children_lists[I_volume]->elements[iterate]);
        }
      }
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 3)");
    
    // 4) Remove elements from mask intersection list
    for (iterate=work_list.num_elements-1;iterate>-1;iterate--) {
      if (on_int_list(Volumes[volume_index]->geometry.mask_intersect_list,work_list.elements[iterate]))
        remove_element_in_list_by_index(&work_list,iterate);
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 4)");
    
    // 5) Remove true children of elements on n's mask intersection list
    for (I_index=0;I_index<Volumes[volume_index]->geometry.mask_intersect_list.num_elements;I_index++) {
      I_volume = Volumes[volume_index]->geometry.mask_intersect_list.elements[I_index];
      if (Volumes[I_volume]->geometry.is_mask_volume == 0) {
        for (iterate=0;iterate<true_children_lists[I_volume]->num_elements;iterate++) {
          remove_element_in_list_by_value(&work_list,true_children_lists[I_volume]->elements[iterate]);
        }
      }
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 5)");
    
    // 6) Remove true children of n
    for (iterate=0;iterate<true_children_lists[volume_index]->num_elements;iterate++)
      remove_element_in_list_by_value(&work_list,true_children_lists[volume_index]->elements[iterate]);
      
    //if (verbal) print_1d_int_list(work_list,"After 6)");
      
    // 7) Remove true grandparents of n
    for (iterate=0;iterate<true_grandparents_lists[volume_index]->num_elements;iterate++)
      remove_element_in_list_by_value(&work_list,true_grandparents_lists[volume_index]->elements[iterate]);
      
    //if (verbal) print_1d_int_list(work_list,"After 7)");
      
    // 8) Remove mask volumes
    for (iterate=work_list.num_elements-1;iterate>-1;iterate--) {
      if (Volumes[work_list.elements[iterate]]->geometry.is_mask_volume == 1)
        remove_element_in_list_by_index(&work_list,iterate);
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 8)");
    
    // 9) Remove true parents of n on the list that has other true parents of n on the list with higher priority
    for (iterate=work_list.num_elements-1;iterate>-1;iterate--) {
      if (on_int_list(*true_parents_lists[volume_index],work_list.elements[iterate])){
        // work_list.elements[iterate] is the volume index of a volume that is a true parent of n
        for (iterate2=0;iterate2<work_list.num_elements;iterate2++) {
          if (on_int_list(*true_parents_lists[volume_index],work_list.elements[iterate2]) && iterate != iterate2) {
            if (Volumes[work_list.elements[iterate]]->geometry.priority_value < Volumes[work_list.elements[iterate2]]->geometry.priority_value) {
              //printf("Removing element number %d (V%d) because element number %d (V%d) had higher priority \n",iterate,work_list.elements[iterate],iterate2,work_list.elements[iterate2]);
              remove_element_in_list_by_index(&work_list,iterate);
              break; // Missing break inserted on 14/9/2016
            }
          }
        }
      }
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 9)");
    
    // 10) The remaining list is the destinations_list
    Volumes[volume_index]->geometry.destinations_list.num_elements = work_list.num_elements;
    Volumes[volume_index]->geometry.destinations_list.elements = malloc(Volumes[volume_index]->geometry.destinations_list.num_elements*sizeof(int));
    
    for (iterate=0;iterate<work_list.num_elements;iterate++)
      Volumes[volume_index]->geometry.destinations_list.elements[iterate] = work_list.elements[iterate];
    
    // Clean up after work_list so the next can be allocated
    free(work_list.elements);
  
  
    char string_output[128];
    MPI_MASTER(
    if (verbal) sprintf(string_output,"Destinations list for Volume %d",volume_index);
    if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.destinations_list,string_output);
    )

  }

};

void generate_destinations_list(int N_volume,struct Volume_struct **Volumes,struct pointer_to_1d_int_list original_overlap_list,struct pointer_to_1d_int_list *parent_list, struct pointer_to_1d_int_list *grandparent_list) {
    // This function generates the destinations_list for a single volume index, N_volume
    // The destination list describes which volumes a ray can enter when leaving N_volume
    // Each of the 6 steps for the algorithm is commented individually, and debug print statements are available
    
    // Mask update: Mask volumes are to be removed from all destinations_list as they can not be the current volume
    // It is not that simple, as some volume will then get empty destination lists. Need to revise this algorithm.
    
    // 1) Start with the overlap list of volume N
    // 2) remove all Volumes from the overlap list of N, which is also on the intersect_check_list
    // 3) remove the children of volumes removed in step 2)
    // 4) remove the children of N
    // 5) remove the grandparents of N
    // 6) remove volumes with lower priority than parents of N still on the list
    // 7) The remaing list is the destinations list
    
    
    // The destination list system should run without masks altogether, meaning parent and grandparent lists should not use them,
    //  and all masks should be removed in step 2
    
    // 1) Start with the overlap list of volume N
    // 2) remove all masks on the list
    // 3) remove all Volumes from the overlap list of N, which is also on the intersect_check_list
    // 4) remove the children of volumes removed in step 2) (if the removed volume was not a mask )
    // 5) remove the children of N
    // 6) remove the grandparents of N
    // 7) remove volumes with lower priority than parents of N still on the list
    // 8) The remaing list is the destinations list
    
    

    // 1) Start with the overlap list of volume N
    struct pointer_to_1d_int_list overlap_list;
    overlap_list.num_elements = original_overlap_list.num_elements;
    overlap_list.elements = malloc(overlap_list.num_elements*sizeof(int));
    int iterate;
    for (iterate=0;iterate<overlap_list.num_elements;iterate++) overlap_list.elements[iterate] = original_overlap_list.elements[iterate];
    
    char string_output[124];
    // sprintf(string_output,"Destinations list for Volume %d step 1",N_volume);
    // print_1d_int_list(overlap_list,string_output);
    
    // 2) remove all masks
    for (iterate=overlap_list.num_elements-1;iterate > -1;iterate--) {
        if (Volumes[overlap_list.elements[iterate]]->geometry.is_mask_volume == 1) {
            remove_element_in_list_by_index(&overlap_list,iterate);
        }
    }
    
    // 3) remove all Volumes from the overlap list of N, which is also on the intersect_check_list
    struct pointer_to_1d_int_list removed_under_2;
    removed_under_2.num_elements = 0;
    int to_check;
    removed_under_2.elements = malloc( Volumes[N_volume]->geometry.intersect_check_list.num_elements * sizeof(int));
    for (iterate=0;iterate < Volumes[N_volume]->geometry.intersect_check_list.num_elements;iterate++) {
        to_check = Volumes[N_volume]->geometry.intersect_check_list.elements[iterate];
        if (on_int_list(overlap_list,to_check)) {
            removed_under_2.elements[removed_under_2.num_elements++] = to_check;
            remove_element_in_list_by_value(&overlap_list,to_check);
        }
    }
    
    // sprintf(string_output,"Destinations list for Volume %d step 2",N_volume);
    // print_1d_int_list(overlap_list,string_output);
    
    // 4) remove the children of volumes removed in step 2)
    int children;
    for (iterate=0;iterate<removed_under_2.num_elements;iterate++){
        for (children = 0;children < Volumes[removed_under_2.elements[iterate]]->geometry.children.num_elements;children++) {
          remove_element_in_list_by_value(&overlap_list,Volumes[removed_under_2.elements[iterate]]->geometry.children.elements[children]);
        }
    }
    
    // sprintf(string_output,"Destinations list for Volume %d step 3",N_volume);
    // print_1d_int_list(overlap_list,string_output);
    
    // 5) remove the children of N
    for (children = 0;children < Volumes[N_volume]->geometry.children.num_elements;children++) {
        remove_element_in_list_by_value(&overlap_list,Volumes[N_volume]->geometry.children.elements[children]);
    }
    
    // sprintf(string_output,"Destinations list for Volume %d step 4",N_volume);
    // print_1d_int_list(overlap_list,string_output);
    
    // 6) remove the grandparents of N
    int grandparent;
    for (grandparent = 0;grandparent < grandparent_list->num_elements;grandparent++) {
        remove_element_in_list_by_value(&overlap_list,grandparent_list->elements[grandparent]);
    }
    
    // sprintf(string_output,"Destinations list for Volume %d step 5",N_volume);
    // print_1d_int_list(overlap_list,string_output);
    
    // 7) remove volumes with lower priority than parents of N still on the list
    struct pointer_to_1d_int_list logic_list;
    logic_list.num_elements = overlap_list.num_elements;
    if (logic_list.num_elements>0) logic_list.elements = malloc(logic_list.num_elements*sizeof(int));
    for (iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 1;
    
    int parent;
    for (parent=0;parent<parent_list->num_elements;parent++) {
      if (on_int_list(overlap_list,parent_list->elements[parent])) {
        // Found a parent to N on the list, now check all other elements on the list
        for (iterate=0;iterate<overlap_list.num_elements;iterate++) {
          if (parent_list->elements[parent] != overlap_list.elements[iterate]) {
            // if the element iterate have lower priority than the parent, remove it from the list
            if (Volumes[overlap_list.elements[iterate]]->geometry.priority_value < Volumes[parent_list->elements[parent]]->geometry.priority_value) logic_list.elements[iterate] = 0;
          }
        }
      }
    }
    
    // 8) The remaing list is the destinations list
    Volumes[N_volume]->geometry.destinations_list.num_elements = sum_int_list(logic_list);
    Volumes[N_volume]->geometry.destinations_list.elements = malloc(Volumes[N_volume]->geometry.destinations_list.num_elements * sizeof(int));
    
    int overlap_index,used_elements=0;
    for (overlap_index=0;overlap_index < overlap_list.num_elements;overlap_index++)
      if (logic_list.elements[overlap_index] == 1)
        Volumes[N_volume]->geometry.destinations_list.elements[used_elements++] = overlap_list.elements[overlap_index];
    
    if (overlap_list.num_elements>0) free(overlap_list.elements);
    if (logic_list.num_elements>0) free(logic_list.elements);
    
    
    /*
    // Old version before rule 6 and 2 was added
    // 8) The remaing list is the destinations list
    Volumes[N_volume]->geometry.destinations_list.num_elements = overlap_list.num_elements;
    Volumes[N_volume]->geometry.destinations_list.elements = malloc(Volumes[N_volume]->geometry.destinations_list.num_elements * sizeof(int));
    
    int overlap_index;
    for (overlap_index=0;overlap_index < overlap_list.num_elements;overlap_index++)
        Volumes[N_volume]->geometry.destinations_list.elements[overlap_index] = overlap_list.elements[overlap_index];
    
    // sprintf(string_output,"Destination list for Volume %d step 6",N_volume);
    // print_1d_int_list(Volumes[N_volume]->geometry.destinations_list,string_output);
    
    if (overlap_list.num_elements>0) free(overlap_list.elements);
    */
    
    };

void generate_destinations_lists(struct pointer_to_1d_int_list **grandparents_lists, struct pointer_to_1d_int_list **parents_lists, struct pointer_to_1d_int_list **overlap_lists,struct Volume_struct **Volumes, int number_of_volumes, int verbal) {
    // Because of the complexity of the algortithm for generating the destinations list, the function is made for a single volume at the time to keep the notation simpler
    // This funtion runs the destinations list function for each volume
    MPI_MASTER(
    if (verbal) printf("\nGenerating destinations lists ----------------------- \n");
    )
    
    int volume_index;
    for (volume_index = 0;volume_index < number_of_volumes;volume_index++) {
      generate_destinations_list(volume_index,Volumes,*overlap_lists[volume_index],parents_lists[volume_index],grandparents_lists[volume_index]);
      
      char string_output[128];
      if (verbal) sprintf(string_output,"Destinations list for Volume %d",volume_index);
      MPI_MASTER(
      if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.destinations_list,string_output);
      )
    }
};

/* OBSOLETE
void generate_destinations_logic_lists(struct Volume_struct **Volumes,int number_of_volumes,int verbal) {
  // The destinations logic list is another way to store the destinations list that makes some tasks quicker
  
  MPI_MASTER(
  if (verbal) printf("\nGenerating destinations logic lists ----------------------- \n");
  )

  int volume_index;
  for (volume_index = 0;volume_index < number_of_volumes;volume_index++) {
      allocate_logic_list_from_temp(number_of_volumes,Volumes[volume_index]->geometry.destinations_list,&Volumes[volume_index]->geometry.destinations_logic_list);
      
      char string_output[128];
      MPI_MASTER(
      if (verbal) sprintf(string_output,"Destinations logic list for Volume %d",volume_index);
      if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.destinations_logic_list,string_output);
      )
  }

};
*/

void generate_reduced_destinations_lists(struct pointer_to_1d_int_list **parents_lists, struct Volume_struct **Volumes,int number_of_volumes,int verbal) {
    // The reduced destination list is the destination list of a volume, where each element that has a parent on the same destination list is removed
    // This list is to be fed to the which_volume function, as this funtion will automatically search through the direct children in a tree like manner
    // The optimization reduces the number of calculations of within functions in nested geometries.
    
    MPI_MASTER(
    if (verbal) printf("\nGenerating reduced destination lists ----------------------- \n");
    )
    
    struct pointer_to_1d_int_list logic_list;
  
    int volume_index,checked_dest_index,checked_dest_volume,rest_dest_index,rest_dest_volume,dest_index,iterate;
    for (volume_index = 0;volume_index < number_of_volumes;volume_index++) {
      
      //printf("Generating reduced destinations lists for volume %d\n",volume_index);
      logic_list.num_elements = Volumes[volume_index]->geometry.destinations_list.num_elements;
      logic_list.elements = malloc( (int) logic_list.num_elements * sizeof(int));
      //printf("Sucsessfully allocated space for %d integers in logic list %d\n",logic_list.num_elements,volume_index);
      for (iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 1;
      
      for (checked_dest_index=0;checked_dest_index<Volumes[volume_index]->geometry.destinations_list.num_elements;checked_dest_index++) {
            checked_dest_volume = Volumes[volume_index]->geometry.destinations_list.elements[checked_dest_index];
            for (rest_dest_index=0;rest_dest_index<Volumes[volume_index]->geometry.destinations_list.num_elements;rest_dest_index++) {
                rest_dest_volume = Volumes[volume_index]->geometry.destinations_list.elements[rest_dest_index];
                // As every volume has 0 as a parent, these are ignored. It would work to include this, but would add an extra trivial step to within_which_volume
                if (rest_dest_volume != 0) {
                    if (on_int_list(*parents_lists[checked_dest_volume],rest_dest_volume) == 1) {
                        // In this case, do not include element checked_dest_index on the reduced destinations list
                        // ADD mask check
                        if (Volumes[rest_dest_volume]->geometry.is_masked_volume == 0) {
                          logic_list.elements[checked_dest_index] = 0;
                        }
                    }
                }
            }
      }
      
      Volumes[volume_index]->geometry.reduced_destinations_list.num_elements = sum_int_list(logic_list);
      Volumes[volume_index]->geometry.reduced_destinations_list.elements = malloc((int)Volumes[volume_index]->geometry.reduced_destinations_list.num_elements * sizeof(int));
      
      iterate = 0;
      for (dest_index = 0;dest_index < Volumes[volume_index]->geometry.destinations_list.num_elements;dest_index++) {
            if (logic_list.elements[dest_index] == 1)   Volumes[volume_index]->geometry.reduced_destinations_list.elements[iterate++] = Volumes[volume_index]->geometry.destinations_list.elements[dest_index];
      }
      
      free(logic_list.elements);
      
      // Testing an optimization
      remove_element_in_list_by_value(&Volumes[volume_index]->geometry.reduced_destinations_list,0);
      
      char string_output[128];
      MPI_MASTER(
      if (verbal) sprintf(string_output,"Reduced destinations list for Volume %d",volume_index);
      if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.reduced_destinations_list,string_output);
      )
    }
};

void generate_direct_children_lists(struct pointer_to_1d_int_list **parents_lists, struct Volume_struct **Volumes,int number_of_volumes,int verbal) {

  MPI_MASTER(
  if (verbal) printf("\nGenerating direct children lists ----------------------- \n");
  )
  // A direct children of volume n is a volume that is a child of n, but no other child of n is its parent
  
  // Mask update: Need to check that this step does not bug out when the mask system interferes with child/parent systems
  
  struct pointer_to_1d_int_list logic_list;
  int volume_index,child,parent,iterate;
  for (volume_index = 0;volume_index < number_of_volumes;volume_index++) {
        // Temp elements is used, and its actual number of elements is edited even though the memory allocated is not changed. This is so that the list functions handles it correctly.
        // The free function will free all the allocated memory regardless of the value of the .num_elements structure field, it is just there for convinience.
      
        logic_list.num_elements = Volumes[volume_index]->geometry.children.num_elements;
        logic_list.elements = malloc(logic_list.num_elements * sizeof(int));
        for (iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 1;
      
        // Look through each child of volume n, and for each of those look through all the other children of n and search for one of them being a parent to the child
        for (child=0;child<Volumes[volume_index]->geometry.children.num_elements;child++) {
            for (parent=0;parent<parents_lists[Volumes[volume_index]->geometry.children.elements[child]]->num_elements;parent++) {
                if (on_int_list(Volumes[volume_index]->geometry.children,parents_lists[Volumes[volume_index]->geometry.children.elements[child]]->elements[parent]))
                    // If such a parent is found, remove that child from the list
                    logic_list.elements[child] = 0;
            }
        }
      
      Volumes[volume_index]->geometry.direct_children.num_elements = sum_int_list(logic_list);
      Volumes[volume_index]->geometry.direct_children.elements = malloc(Volumes[volume_index]->geometry.direct_children.num_elements*sizeof(int));
      
      iterate = 0;
      for (child = 0;child < Volumes[volume_index]->geometry.children.num_elements;child++) {
            if (logic_list.elements[child])   Volumes[volume_index]->geometry.direct_children.elements[iterate++] = Volumes[volume_index]->geometry.children.elements[child];
      }
      // Be careful with names in both main and a function, as they are automatically declared as external variables, which would then also free the main.
      free(logic_list.elements);
      
      char string_output[128];
      MPI_MASTER(
      if (verbal) sprintf(string_output,"Children list for Volume        %d",volume_index);
      if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.children,string_output);
      if (verbal) sprintf(string_output,"Direct_children list for Volume %d",volume_index);
      if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.direct_children,string_output);
      )
      
  }

};

void generate_starting_logic_list(struct starting_lists_struct *starting_lists, struct Volume_struct **Volumes, int number_of_volumes, int verbal) {
    // Function for generating logic list of volumes the ray can start in without an error.
    // Start with a list of all vacuum volumes
    // Remove all volumes that are children of non-vacuum volumes
    // It is still possible to have a volume on this list that is surrounded by non-vacuum volumes, but it is hard to detect these situations,
    //  meaning that it is ultimately partly the users responsibility to not send neutrons directly into materials.
    
    int volume_index,*start,*check;
    
    struct pointer_to_1d_int_list temp_list_local;
    temp_list_local.num_elements = number_of_volumes;
    temp_list_local.elements = malloc(number_of_volumes*sizeof(int));
    
    //if (verbal==1) printf("sucessfully allocated temp_list_local.elements, now to enter which volumes are vacuums as a logic list\n");
    temp_list_local.elements[0] = 1; // Volume 0 is a vacuum volume.
    for (volume_index = 1;volume_index < number_of_volumes;volume_index++) {
        if (Volumes[volume_index]->p_physics->is_vacuum == 1) temp_list_local.elements[volume_index] = 1;
        else temp_list_local.elements[volume_index] = 0;
    }
    // temp_list_local is now a logic list of all vacuum volumes
    //if (verbal==1) printf("list of vacuum volumes done, now to remove the children of non-vcauum volumes\n");
    for (volume_index = 1;volume_index < number_of_volumes;volume_index++) { // All volumes ...
        if (temp_list_local.elements[volume_index] == 0) { // ... that are not vacuum ...
            for (start = check = Volumes[volume_index]->geometry.children.elements;check - start < Volumes[volume_index]->geometry.children.num_elements;check++) { // ... have all their children ...
                temp_list_local.elements[*check] = 0; // .. removed from the allowed_start_logic_list
            }
        }
    }
    //if (verbal==1) printf("sucessfully removed children of non-vacuum volumes, now allocate allowed_start_logic_list\n");
    allocate_list_from_temp(number_of_volumes,temp_list_local,&starting_lists->allowed_starting_volume_logic_list);
    //if (verbal==1) printf("sucsessfully allocated allowed_start_logic_list, now freeing temp_list_local.elements. \n");
    
    free(temp_list_local.elements);
    //if (verbal==1) printf("sucessfully freed temp_list_local.elements, generate starting lists done\n");
    
    char string_output[128];
    MPI_MASTER(
    if (verbal) sprintf(string_output,"Allowed starting volume logic list");
    if (verbal) print_1d_int_list(starting_lists->allowed_starting_volume_logic_list,string_output);
    )
    
    
};

void generate_reduced_starting_destinations_list(struct starting_lists_struct *starting_lists, struct pointer_to_1d_int_list **parents_lists, struct Volume_struct **Volumes,int number_of_volumes,int verbal) {
    // The starting_destinations_list is trivial, as it contains all volumes that are not masks.


    struct pointer_to_1d_int_list logic_list;
    //printf("Generating reduced destinations lists for volume %d\n",volume_index);
    logic_list.num_elements = number_of_volumes;
    logic_list.elements = malloc( (int) logic_list.num_elements * sizeof(int));
    int iterate;
    for (iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 0;
    
    
    for (iterate=1;iterate<number_of_volumes;iterate++)
      if (Volumes[iterate]->geometry.is_mask_volume == 0) logic_list.elements[iterate] = 1;
      //else logic_list.elements[iterate] = 0;
    
    starting_lists->starting_destinations_list.num_elements = sum_int_list(logic_list);
    starting_lists->starting_destinations_list.elements = malloc(starting_lists->starting_destinations_list.num_elements*sizeof(int));
    
    int used_elements=0;
    for (iterate=1;iterate<number_of_volumes;iterate++)
      if (logic_list.elements[iterate] == 1) starting_lists->starting_destinations_list.elements[used_elements++] = iterate;
    
    MPI_MASTER(
    if (verbal) printf("\nGenerating start destinations list ------------------------------ \n");
    if (verbal) print_1d_int_list(starting_lists->starting_destinations_list,"Starting destinations list");
    )

    // The reduced starting destination list is used when a ray enters the component in the search for which volume it starts in.
    // It facilitates the same optimization as the regular destination list.
    // The start logic list is also generated, as it is very simple and does not need a seperate function
    
    // Mask update: Need to remove mask volumes from the reduced starting destination list
    
    MPI_MASTER(
    if (verbal) printf("\nGenerating reduced start destination list ----------------------- \n");
    )
    
    int checked_dest_index,checked_dest_volume,rest_dest_index,rest_dest_volume,dest_index;
    
    /* Old version before masks were introduced
    struct pointer_to_1d_int_list starting_dest_list;
    
    starting_dest_list.num_elements = number_of_volumes - 1;
    starting_dest_list.elements = malloc ( starting_dest_list.num_elements * sizeof(int));
    for (iterate=0;iterate<number_of_volumes-1;iterate++) starting_dest_list.elements[iterate] = iterate + 1; // All volumes to be checked for starting
    */
    
    logic_list.num_elements = starting_lists->starting_destinations_list.num_elements;
    free(logic_list.elements);
    logic_list.elements = malloc( (int) logic_list.num_elements * sizeof(int));
    
    //printf("Sucsessfully allocated space for %d integers in logic list %d\n",logic_list.num_elements,volume_index);
    for (iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 1;
  
    for (checked_dest_index=0;checked_dest_index<starting_lists->starting_destinations_list.num_elements;checked_dest_index++) {
        checked_dest_volume = starting_lists->starting_destinations_list.elements[checked_dest_index];
        for (rest_dest_index=0;rest_dest_index<starting_lists->starting_destinations_list.num_elements;rest_dest_index++) {
            rest_dest_volume = starting_lists->starting_destinations_list.elements[rest_dest_index];
            // As every volume has 0 as a parent, these are ignored. It would work to include this, but would add an extra trivial step to within_which_volume
            if (rest_dest_volume != 0) {
                if (on_int_list(*parents_lists[checked_dest_volume],rest_dest_volume) == 1) {
                    // In this case, do not include element checked_dest_index on the reduced destinations list
                    logic_list.elements[checked_dest_index] = 0;
                }
            }
        }
  }
  
  starting_lists->reduced_start_list.num_elements = sum_int_list(logic_list);
  starting_lists->reduced_start_list.elements = malloc((int)starting_lists->reduced_start_list.num_elements * sizeof(int));
  
  iterate = 0;
  for (dest_index = 0;dest_index < starting_lists->starting_destinations_list.num_elements;dest_index++) {
        if (logic_list.elements[dest_index] == 1) starting_lists->reduced_start_list.elements[iterate++] = starting_lists->starting_destinations_list.elements[dest_index];
  }
  
  free(logic_list.elements);
  //free(starting_dest_list.elements);
  
  MPI_MASTER(
  if (verbal) print_1d_int_list(starting_lists->reduced_start_list,"Reduced start destinations list");
  )
    
  // Making the start_logic_list.
  starting_lists->start_logic_list.num_elements = number_of_volumes;
  starting_lists->start_logic_list.elements = malloc ( starting_lists->start_logic_list.num_elements * sizeof(int));
  starting_lists->start_logic_list.elements[0] = 0;
  for (iterate=1;iterate<number_of_volumes;iterate++) starting_lists->start_logic_list.elements[iterate] = 1; // All volumes to be checked for starting volume
  MPI_MASTER(
  if (verbal) print_1d_int_list(starting_lists->start_logic_list,"Start logic list");
  )
};


void generate_next_volume_list(struct Volume_struct **Volumes, int number_of_volumes, int verbal) {
    // Generate list of volumes that can be the next volume which the ray enters. It is used for tagging, not the simulation / propagation

    // Mask update: These lists should be done as if all mask statuses are on, meaning they include all possible next volumes (will include more input to this function)
    
    // bug: next volume list is not complete and contains duplicates

    MPI_MASTER(
    if (verbal) printf("\nGenerating next volume list ------------------------------------- \n");
    )
    // Merge destinations list, intersection list and mask_intersection_list
    int volume_index,iterate,mask_index;
    struct pointer_to_1d_int_list full_intersection_list;
    full_intersection_list.num_elements=0;
    
    for (volume_index=0;volume_index<number_of_volumes;volume_index++) {
        Volumes[volume_index]->geometry.next_volume_list.num_elements = 0;
        // Before mask update
        //merge_lists(&Volumes[volume_index]->geometry.next_volume_list, &Volumes[volume_index]->geometry.destinations_list, &Volumes[volume_index]->geometry.intersect_check_list);
        
        merge_lists(&full_intersection_list, &Volumes[volume_index]->geometry.mask_intersect_list, &Volumes[volume_index]->geometry.intersect_check_list);
        merge_lists(&Volumes[volume_index]->geometry.next_volume_list,&Volumes[volume_index]->geometry.destinations_list,&full_intersection_list);
        
        /* // This complication is taken into account by adding the mask_intersect list instead
        // It is possible that the next volume is still not on this list, as when masks are encountered the next volume can be a volume they mask.
        // For each on the list, add the volumes masked by that mask (do not iterate over this, as masks can not be masked regardless)
        for (iterate=Volumes[volume_index]->geometry.next_volume_list.num_elements-1;iterate>-1;iterate--) {
          if (Volumes[Volumes[volume_index]->geometry.next_volume_list.elements[iterate]]->geometry.is_mask_volume == 1) {
            for (mask_index=0;mask_index<Volumes[Volumes[volume_index]->geometry.next_volume_list.elements[iterate]]->geometry.mask_list.num_elements;mask_index++) {
              add_element_to_int_list(&Volumes[volume_index]->geometry.next_volume_list,Volumes[Volumes[volume_index]->geometry.next_volume_list.elements[iterate]]->geometry.mask_list.elements[mask_index]);
            }
          }
        }
        */
        
        // Remove mask volumes from the next volume list, as they never occur as current_volume, and thus just create dead branches that takes up memory
        for (iterate=Volumes[volume_index]->geometry.next_volume_list.num_elements-1;iterate>-1;iterate--)
          if (Volumes[Volumes[volume_index]->geometry.next_volume_list.elements[iterate]]->geometry.is_mask_volume == 1)
            remove_element_in_list_by_index(&Volumes[volume_index]->geometry.next_volume_list,iterate);
        
        
        if (full_intersection_list.num_elements>0) free(full_intersection_list.elements);
        full_intersection_list.num_elements=0;
        
        char string_output[128];
        MPI_MASTER(
        if (verbal) sprintf(string_output,"Next volume list                %d",volume_index);
        if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.next_volume_list,string_output);
        )
    }
};

void generate_lists(struct Volume_struct **Volumes, struct starting_lists_struct *starting_lists, int number_of_volumes, int verbal) {
    // Function to control the generation of lists
    // Some lists are only needed temporary, and are thus declared here to keep them out of the main scope
    // Others are stored in the volume structs as they are needed in the trace algorithm (or tagging)
    
    
    struct pointer_to_1d_int_list **true_children_lists;
    true_children_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    // generate_children_lists both generate the normal children list for each volume, but also the true children list needed locally.
    generate_children_lists(Volumes, true_children_lists, number_of_volumes,verbal);
    
    
    struct pointer_to_1d_int_list **true_overlap_lists;
    true_overlap_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    struct pointer_to_1d_int_list **raw_overlap_lists;
    raw_overlap_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    
    generate_overlap_lists(true_overlap_lists, raw_overlap_lists, Volumes,number_of_volumes,verbal);
    
    
    //generate_intersect_check_lists(true_overlap_lists, Volumes, number_of_volumes, verbal);
    
    
    struct pointer_to_1d_int_list **parents_lists;
    parents_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    generate_parents_lists(parents_lists,Volumes,number_of_volumes,verbal,1); // The last 1 means masks are taken into account
    
    // Generate version of parent list as it would be without masks
    struct pointer_to_1d_int_list **parents_lists_no_masks;
    parents_lists_no_masks = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    generate_parents_lists(parents_lists_no_masks,Volumes,number_of_volumes,verbal,0); // The last 0 means masks are NOT taken into account
    
    // Generate version of parent list using true_children instead
    struct pointer_to_1d_int_list **true_parents_lists;
    true_parents_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    generate_true_parents_lists(true_parents_lists, true_children_lists, Volumes, number_of_volumes, verbal, 1);
    
    // Generate version of parent list no masks using true_children instead
    struct pointer_to_1d_int_list **true_parents_lists_no_masks;
    true_parents_lists_no_masks = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    generate_true_parents_lists(true_parents_lists_no_masks, true_children_lists, Volumes, number_of_volumes, verbal, 0);
    
    // New version of generate intersect lists
    generate_intersect_check_lists_experimental(true_overlap_lists, raw_overlap_lists, parents_lists, true_parents_lists, Volumes, number_of_volumes, verbal);
    
    struct pointer_to_1d_int_list **grandparents_lists;
    grandparents_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    generate_grandparents_lists(grandparents_lists,parents_lists,number_of_volumes,verbal);
    
    // Generate version of grandparents list as it would have been if no masks were defined
    struct pointer_to_1d_int_list **grandparents_lists_no_masks;
    grandparents_lists_no_masks = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    generate_grandparents_lists(grandparents_lists_no_masks,parents_lists_no_masks,number_of_volumes,verbal);
    
    // Generate true_grandparents_lists
    struct pointer_to_1d_int_list **true_grandparents_lists;
    true_grandparents_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    generate_grandparents_lists(true_grandparents_lists,true_parents_lists,number_of_volumes,verbal);
    
    struct pointer_to_1d_int_list **true_grandparents_lists_no_masks;
    true_grandparents_lists_no_masks = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    generate_grandparents_lists(true_grandparents_lists_no_masks,true_parents_lists_no_masks,number_of_volumes,verbal);
    
    // The destinations lists are generated without taking masks into account (they are removed from the overlap list in an early step)
    //generate_destinations_lists(grandparents_lists_no_masks,parents_lists_no_masks,true_overlap_lists,Volumes,number_of_volumes,verbal);
    generate_destinations_lists_experimental(true_overlap_lists, true_children_lists, true_parents_lists_no_masks, true_grandparents_lists_no_masks, Volumes, number_of_volumes, verbal);
    
    // Obsolete, found a way around them in within_which_volume, but need to test the performance difference
    // generate_destinations_logic_lists(Volumes,number_of_volumes,verbal);
    
    generate_reduced_destinations_lists(parents_lists,Volumes,number_of_volumes,verbal);
    
    generate_direct_children_lists(parents_lists,Volumes,number_of_volumes,verbal);
    
    //generate_starting_list(starting_lists,Volumes,number_of_volumes,verbal);
    generate_starting_logic_list(starting_lists,Volumes,number_of_volumes,verbal);
    
    generate_reduced_starting_destinations_list(starting_lists,parents_lists,Volumes,number_of_volumes,verbal);
    
    // This list is stored with the volumes for convinience, but is only used for tagging
    generate_next_volume_list(Volumes,number_of_volumes,verbal);
    
    // Garbage collection for temporary dynamically allocated lists. (Permanent lists freed from FINALLY)
    int iterate;
    for (iterate=0;iterate<number_of_volumes;iterate++) {
        //printf("freeing for volume nr %d\n",iterate);
        //printf("true_overlap_lists[iterate]->num_elements = %d \n",true_overlap_lists[iterate]->num_elements);
        if (true_overlap_lists[iterate]->num_elements > 0) free(true_overlap_lists[iterate]->elements);
        free(true_overlap_lists[iterate]);
        
        //printf("raw_overlap_lists[iterate]->num_elements = %d \n",raw_overlap_lists[iterate]->num_elements);
        if (raw_overlap_lists[iterate]->num_elements > 0) free(raw_overlap_lists[iterate]->elements);
        free(raw_overlap_lists[iterate]);
        
        //printf("parents_lists[iterate]->num_elements = %d \n",parents_lists[iterate]->num_elements);
        if (parents_lists[iterate]->num_elements > 0) free(parents_lists[iterate]->elements);
        free(parents_lists[iterate]);
        
        //printf("parents_lists_no_masks[iterate]->num_elements = %d \n",parents_lists_no_masks[iterate]->num_elements);
        if (parents_lists_no_masks[iterate]->num_elements > 0) free(parents_lists_no_masks[iterate]->elements);
        free(parents_lists_no_masks[iterate]);
        
        //printf("true_parents_lists[iterate]->num_elements = %d \n",true_parents_lists[iterate]->num_elements);
        if (true_parents_lists[iterate]->num_elements > 0) free(true_parents_lists[iterate]->elements);
        free(true_parents_lists[iterate]);
        
        //printf("true_parents_lists_no_masks[iterate]->num_elements = %d \n",true_parents_lists_no_masks[iterate]->num_elements);
        if (true_parents_lists_no_masks[iterate]->num_elements > 0) free(true_parents_lists_no_masks[iterate]->elements);
        free(true_parents_lists_no_masks[iterate]);
        
        //printf("grandparents_lists[iterate]->num_elements = %d \n",grandparents_lists[iterate]->num_elements);
        if (grandparents_lists[iterate]->num_elements > 0) free(grandparents_lists[iterate]->elements);
        free(grandparents_lists[iterate]);
        
        //printf("true_grandparents_lists[iterate]->num_elements = %d \n",true_grandparents_lists[iterate]->num_elements);
        if (true_grandparents_lists[iterate]->num_elements > 0) free(true_grandparents_lists[iterate]->elements);
        free(true_grandparents_lists[iterate]);
        
        //printf("grandparents_lists_no_masks[iterate]->num_elements = %d \n",grandparents_lists_no_masks[iterate]->num_elements);
        if (grandparents_lists_no_masks[iterate]->num_elements > 0) free(grandparents_lists_no_masks[iterate]->elements);
        free(grandparents_lists_no_masks[iterate]);
        
        //printf("true_grandparents_lists_no_masks[iterate]->num_elements = %d \n",true_grandparents_lists_no_masks[iterate]->num_elements);
        if (true_grandparents_lists_no_masks[iterate]->num_elements > 0) free(true_grandparents_lists_no_masks[iterate]->elements);
        free(true_grandparents_lists_no_masks[iterate]);
        
        //printf("true_children_lists[iterate]->num_elements = %d \n",true_children_lists[iterate]->num_elements);
        if (true_children_lists[iterate]->num_elements > 0) free(true_children_lists[iterate]->elements);
        free(true_children_lists[iterate]);
    }
    //printf("generate lists volume specific free completed\n");
    free(true_overlap_lists);free(raw_overlap_lists);free(parents_lists);free(true_parents_lists);free(true_parents_lists_no_masks);
    free(parents_lists_no_masks);free(true_grandparents_lists);free(grandparents_lists);free(grandparents_lists_no_masks);free(true_grandparents_lists_no_masks);
    free(true_children_lists);
    //printf("generate lists free completed\n");
};

// -------------    Focusing functions   --------------------------------------------------------

// The focusing_data structure is set up by the geometry component, and a pointer to the appropriate
//  focusing function is added to the Volume structure (for this reason the input of all the functions
//  need to be identical, at least in terms of types). In this way there are no if statements to check
//  which of these to be used in the trace, but the focus_data_struct will carry some redundant
//  information, as only the appropriate parameters are set:
// Angular focus on a rectangle (angular_focus_height / angular_focus_width)
// Spatial focus on a rectangle (spatial_focus_height / spatial_focus_width)
// Spatial focus on a disk (sptial_focus_radius)
// No focus (randvec in 4pi) (all set to zero, will select randvec circle as it is slightly faster
//
// When adding a new physical process focusing becomes very easy, as one just calls the master focusing
//  function assosiated with the volume (placed in the geometry struct), using the focus_data_struct
//  also found in the geometry struct, and the process then supports all the focusing modes. It is even
//  possible to add new focusing modes in the future by updating just the geometry components, and this
//  section.

// focus_data_struct definitioon shown here, defined at the start of this file
//struct focus_data_struct {
//Coords Aim;
//double angular_focus_width;
//double angular_focus_height;
//double spatial_focus_width;
//double spatial_focus_height;
//double spatial_focus_radius;
//Rotation absolute_rotation;
//// focusing_function creates a vector per selected criteria of focus_data_struct / selected focus function and returns solid angle
//void (*focusing_function)(Coords*, double*, struct focus_data_struct*);
////                        v_out  , solid_a,
//};

void randvec_target_rect_angular_union(Coords *v_out,double *solid_angle_out, struct focus_data_struct *focus_data) {
    // Calls the standard McStas randvec_target_rect_angular focusing function, but is with the new data input format.
    randvec_target_rect_angular(&v_out->x, &v_out->y, &v_out->z, solid_angle_out, focus_data->Aim.x,focus_data->Aim.y, focus_data->Aim.z, focus_data->angular_focus_width, focus_data->angular_focus_height,focus_data->absolute_rotation);
    //randvec_target_rect_angular(&vx, &vy, &vz, &solid_angle,aim_x, aim_y, aim_z, VarsInc.aw, VarsInc.ah, ROT_A_CURRENT_COMP);
};

void randvec_target_rect_union(Coords *v_out,double *solid_angle_out, struct focus_data_struct *focus_data) {
// Calls the standard McStas randvec_target_rect focusing function, but is with the new data input format.
    randvec_target_rect(&v_out->x, &v_out->y, &v_out->z, solid_angle_out, focus_data->Aim.x,focus_data->Aim.y, focus_data->Aim.z, focus_data->spatial_focus_width, focus_data->spatial_focus_height,focus_data->absolute_rotation);
    // randvec_target_rect(&vx, &vy, &vz, &solid_angle,aim_x, aim_y, aim_z, VarsInc.xw, VarsInc.yh, ROT_A_CURRENT_COMP);
};

void randvec_target_circle_union(Coords *v_out,double *solid_angle_out, struct focus_data_struct *focus_data) {
// Calls the standard McStas randvec_target_circle focusing function, but is with the new data input format.

    // debug input into randvec_target_circle
    //print_position(focus_data->Aim,"Aim vector input for randvec_target_circle");
    //printf("Radius input %f\n",focus_data->spatial_focus_radius);

    randvec_target_circle(&v_out->x, &v_out->y, &v_out->z, solid_angle_out, focus_data->Aim.x,focus_data->Aim.y, focus_data->Aim.z, focus_data->spatial_focus_radius);
    //randvec_target_circle(&vx, &vy, &vz, &solid_angle, aim_x, aim_y, aim_z, focus_r);
};



void focus_initialize(struct geometry_struct *geometry, Coords POS_A_TARGET, Coords POS_A_CURRENT, Rotation ROT_A_CURRENT, int target_index, double target_x, double target_y, double target_z, double angular_focus_width, double angular_focus_height, double spatial_focus_width, double spatial_focus_height, double spatial_focus_radius, char *component_name) {
    // Initialize focusing system
    // target_x/y/z needs to be double setting parameters with default value 0
    // target_index needs to be int setting parameter with default value 0
    // angular_focus_width, angular_focus_height, spatial_focus_width, spatial_focus_height, spatial_focus_radius nneds to be double setting parameters with default value 0
    // When those conditions are met, this code will identify which settings have been entered by the user and select the appropriate focusing parameters, which are loaded into the focus_data struct and the geometry struct.
    // The aim vector in the focus_data struct will be transformed from the local coordinate system of the geometry component to the coordinate system of the master component during the master component initialize

  // Input sanitation
  if (angular_focus_width < 0) {
    printf("\nERROR in Union geometry component named \"%s\", angular focus width focus_aw < 0! \n",component_name);
    exit(1);
  }
  if (angular_focus_height < 0) {
    printf("\nERROR in Union geometry component named \"%s\", angular focus width focus_ah < 0! \n",component_name);
    exit(1);
  }
  if (spatial_focus_width < 0) {
    printf("\nERROR in Union geometry component named \"%s\", spatial focus width focus_xw < 0! \n",component_name);
    exit(1);
  }
  if (spatial_focus_height < 0) {
    printf("\nERROR in Union geometry component named \"%s\", spatial focus height focus_xh < 0! \n",component_name);
    exit(1);
  }
  if (spatial_focus_radius < 0) {
    printf("\nERROR in Union geometry component named \"%s\", spatial focus radius focus_r < 0! \n",component_name);
    exit(1);
  }
  
  struct focus_data_struct focus_data;

  // Initialize focus_data_struct
  /*
  geometry->focus_data.Aim = coords_set(0,0,0);
  geometry->focus_data.angular_focus_width = 0;
  geometry->focus_data.angular_focus_height = 0;
  geometry->focus_data.spatial_focus_width = 0;
  geometry->focus_data.spatial_focus_height = 0;
  geometry->focus_data.spatial_focus_radius = 0;
  rot_copy(geometry->focus_data.absolute_rotation,ROT_A_CURRENT);
  */
  focus_data.Aim = coords_set(0,0,0);
  focus_data.angular_focus_width = 0;
  focus_data.angular_focus_height = 0;
  focus_data.spatial_focus_width = 0;
  focus_data.spatial_focus_height = 0;
  focus_data.spatial_focus_radius = 0;
  rot_copy(focus_data.absolute_rotation,ROT_A_CURRENT);

  // Built on code from Incoherent.comp by Kim Lefmann and Kristian Nielsen
  if (target_index != 0 && !target_x && !target_y && !target_z)
  {
    Coords ToTarget;
    //ToTarget = coords_sub(POS_A_COMP_INDEX(INDEX_CURRENT_COMP+target_index),POS_A_CURRENT_COMP);
    ToTarget = coords_sub(POS_A_TARGET,POS_A_CURRENT);
    //ToTarget = rot_apply(ROT_A_CURRENT_COMP, ToTarget);
    ToTarget = rot_apply(ROT_A_CURRENT, ToTarget);
    coords_get(ToTarget, &focus_data.Aim.x, &focus_data.Aim.y, &focus_data.Aim.z);
  }
  else
  { focus_data.Aim.x = target_x; focus_data.Aim.y = target_y; focus_data.Aim.z = target_z; }
  
  if (!(focus_data.Aim.x || focus_data.Aim.y || focus_data.Aim.z)) {
    // Somehow set a variable to signify scattering into 4pi
    // printf("Union %s: The target is not defined. Using scattering into 4pi.\n",NAME_CURRENT_COMP);
    focus_data.Aim.z=1; // set aim to one so that the randvec output vector has length 1 instead of 0
  }

  int focusing_model_selected = 0;
  if (angular_focus_width != 0 && angular_focus_height != 0) {
    focus_data.focusing_function = &randvec_target_rect_angular_union;
    focus_data.angular_focus_width = DEG2RAD*angular_focus_width; // Convert to radians here
    focus_data.angular_focus_height = DEG2RAD*angular_focus_height;
    focusing_model_selected = 1;
  }
  if (spatial_focus_width != 0 && spatial_focus_height != 0) {
    focus_data.focusing_function = &randvec_target_rect_union;
    focus_data.spatial_focus_width = spatial_focus_width;
    focus_data.spatial_focus_height = spatial_focus_height;
    if (focusing_model_selected) {
        printf("ERROR %s: Select either angular or spatial focusing, not both! Exiting \n",component_name);
        exit(1);
    }
    focusing_model_selected = 1;
  }
  if (spatial_focus_radius != 0) {
    focus_data.focusing_function = &randvec_target_circle_union;
    focus_data.spatial_focus_radius = spatial_focus_radius;
    if (focusing_model_selected) {
        printf("ERROR %s: Select a maximum of one focusing method (spatial rectangle or cicle, or angular rectangle! Exiting \n",component_name);
        exit(1);
    }
    focusing_model_selected = 1;
  }
  if (focusing_model_selected == 0) {
    // Select 4pi focusing
    focus_data.spatial_focus_radius = 0;
    focus_data.focusing_function = &randvec_target_circle_union;
  }
  
  // Allocate the isotropic focus_data struct
  geometry->focus_data_array.num_elements = 0;
  //geometry->focus_data_array.elements = malloc(sizeof(focus_data_struct));
  add_element_to_focus_data_array(&geometry->focus_data_array,focus_data);
};


struct abs_event{
    double time1;
    double position1[3];
    double time2;
    double position2[3];
    double weight_change;
    int volume_index;
    int neutron_id;
};

// Functions for recording absorption
void initialize_absorption_file() {
  FILE *fp;
  fp = fopen("Union_absorption.dat","w");
  fprintf(fp,"r_old x, r_old y, r_old z, old t, r x, r y, r z, new t, weight change, volume index, neutron id \n");
  
  fclose(fp);
}

void write_events_to_file(int last_index, struct abs_event *events) {

  FILE *fp;
  fp = fopen("Union_absorption.dat","a");
  
  struct abs_event *this_event;
  int iterate;
  for (iterate=0; iterate<last_index; iterate++) {
  
    this_event = &events[iterate];
    fprintf(fp,"%g, %g, %g, %g, %g, %g, %g, %g, %e, %i, %i \n",
           this_event->position1[0], this_event->position1[1], this_event->position1[2], this_event->time1,
           this_event->position2[0], this_event->position2[1], this_event->position2[2], this_event->time2,
           this_event->weight_change,
           this_event->volume_index,
           this_event->neutron_id);
  }

  fclose(fp);
}

void record_abs_to_file(double *r, double t1, double *r_old, double t2, double weight_change, int volume, int neutron_id, int *data_index, struct abs_event *events) {
  
  
  
  struct abs_event *this_event;
  
  this_event = &events[(*data_index)++];
  
  //printf("Recording something! %i\n", *data_index);
  
  this_event->position1[0] = r[0];
  this_event->position1[1] = r[1];
  this_event->position1[2] = r[2];
  this_event->time1 = t1;
  this_event->position2[0] = r_old[0];
  this_event->position2[1] = r_old[1];
  this_event->position2[2] = r_old[2];
  this_event->time2 = t2;
  this_event->weight_change = weight_change;
  this_event->volume_index = volume;
  this_event->neutron_id = neutron_id;
  
  
  if (*data_index == 999) {
    
      write_events_to_file(*data_index, events);
      *data_index = 0;

  }

};





#endif


/* Shared user declarations for all components types 'Incoherent_process'. */
#ifndef Union
#define Union $Revision: 0.8 $

/*******************************************************************************
*
*  McStas, neutron ray-tracing package
*  Copyright(C) 2007 Risoe National Laboratory.
*
* %I
* Written by: Mads Bertelsen
* Date: 20.08.15
* Version: $Revision: 0.1 $
* Origin: University of Copenhagen
*
* Functions and structure definitons for Union components.
*
******************************************************************************/

// -------------    Definition of data structures   ---------------------------------------------
// GPU
enum shape {
  surroundings,
  box,
  sphere,
  cylinder,
  cone,
  mesh
};

enum process {
  Incoherent,
  Powder,
  Single_crystal,
  AF_HB_1D,
  PhononSimple,
  Texture,
  IncoherentPhonon,
  NCrystal,
  Template
};

struct intersection_time_table_struct {
int num_volumes;
int *calculated;
int *n_elements;
double **intersection_times;
};

struct line_segment{
//struct position point1;
//struct position point2;
Coords point1;
Coords point2;
int number_of_dashes;
};

struct pointer_to_1d_int_list {
int num_elements;
int *elements;
#pragma acc shape(elements[0:num_elements]) init_needed(num_elements)
};

struct pointer_to_1d_double_list {
int num_elements;
double *elements;
#pragma acc shape(elements[0:num_elements]) init_needed(num_elements)
};

struct pointer_to_1d_coords_list {
int num_elements;
Coords *elements;
#pragma acc shape(elements[0:num_elements]) init_needed(num_elements)
};

struct lines_to_draw{
int number_of_lines;
struct line_segment *lines;
#pragma acc shape(lines[0:number_of_lines]) init_needed(number_of_lines)
};

// 2D lists not needed anyway
// struct pointer_to_2d_int_list {
// int n_lists;
// struct pointer_to_1d_int_list *lists;
// }

// Todo: see if the union geometry_parameter_union and other geometry structs can be here
union geometry_parameter_union{
    struct sphere_storage   *p_sphere_storage;
    struct cylinder_storage *p_cylinder_storage;
    struct box_storage      *p_box_storage;
    struct cone_storage     *p_cone_storage;
    struct mesh_storage     *p_mesh_storage;
    // add as many pointers to structs as wanted, without increasing memory footprint.
};


struct rotation_struct{
double x;
double y;
double z;
};

struct focus_data_struct {
Coords Aim;
double angular_focus_width;
double angular_focus_height;
double spatial_focus_width;
double spatial_focus_height;
double spatial_focus_radius;
Rotation absolute_rotation;
// focusing_function creates a vector per selected criteria of focus_data_struct / selected focus function and returns solid angle
void (*focusing_function)(Coords*, double*, struct focus_data_struct*);
//                        v_out  , solid_a,
};

struct focus_data_array_struct
{
struct focus_data_struct *elements;
int num_elements;
#pragma acc shape(elements[0:num_elements]) init_needed(num_elements)
};

struct Detector_3D_struct {
  char title_string[256];
  char string_axis_1[256];
  char string_axis_2[256];
  char string_axis_3[256];
  char Filename[256];
  double D1min;
  double D1max;
  double D2min;
  double D2max;
  double D3min;
  double D3max;
  double bins_1; // McStas uses doubles for bin numbers for some reason
  double bins_2;
  double bins_3;
  double ***Array_N; // McStas uses doubles for number of rays in each bin for some reason
  double ***Array_p;
  double ***Array_p2;
};

struct Detector_2D_struct {
  char title_string[256];
  char string_axis_1[256];
  char string_axis_2[256];
  char Filename[256];
  double D1min;
  double D1max;
  double D2min;
  double D2max;
  double bins_1; // McStas uses doubles for bin numbers for some reason
  double bins_2;
  double **Array_N; // McStas uses doubles for number of rays in each bin for some reason
  double **Array_p;
  double **Array_p2;
};

struct Detector_1D_struct {
  char title_string[256];
  char string_axis[256];
  char string_axis_short[64];
  char string_axis_value[256];
  char Filename[256];
  double min;
  double max;
  double bins; // McStas uses doubles for bin numbers for some reason
  double *Array_N; // McStas uses doubles for number of rays in each bin for some reason
  double *Array_p;
  double *Array_p2;
};


union logger_data_union{
  struct a_2DQ_storage_struct *p_2DQ_storage;
  struct a_2DS_storage_struct *p_2DS_storage;
  struct a_3DS_storage_struct *p_3DS_storage;
  struct a_1D_storage_struct *p_1D_storage;
  struct a_2DS_t_storage_struct *p_2DS_t_storage;
  struct a_2D_kf_storage_struct *p_2D_kf_storage;
  struct a_2D_kf_t_storage_struct *p_2D_kf_t_storage;
  // Additional logger storage structs to be addedd
};

struct logger_with_data_struct {
  int used_elements;
  int allocated_elements;
  struct logger_struct **logger_pointers;
};

// logger_pointer_struct
// contains pointers to the different logger functions and it's data union
struct logger_pointer_set_struct {
  // The logger has two record functions, an active and an inactive. Normally the active one will be to permanent storage,
  //  but if a conditional has been defined, it can switch the two, making the active one recording to temporary, which
  //  can then be filtered based on the future path of the ray
  
  // function input Coords position, k[3], k_old[3], p, p_old, NV, NPV, N, logger_data_union, logger_with_data_struct
  void (*active_record_function)(Coords*, double*, double*, double, double, double, int, int, int, struct logger_struct*, struct logger_with_data_struct*);
  void (*inactive_record_function)(Coords*, double*, double*, double, double, double, int, int, int, struct logger_struct*, struct logger_with_data_struct*);
  
  // A clear temporary data function (for new ray)
  void (*clear_temp)(union logger_data_union*);
  
  // Write temporary to permanent is used when the record to temp function is active, and the condition is met.
  void (*temp_to_perm)(union logger_data_union*);
  
  // Write temporary final_p to permanent is used when the record to temp function is active, and the condition is met
  //  and the final weight is given to be used for all stored events in the logger.
  void (*temp_to_perm_final_p)(union logger_data_union*, double);
  
  // Select which temp_to_perm function to use
  int select_t_to_p; // 1: temp_to_perm, 2: temp_to_perm_final_p
  
};

union abs_logger_data_union{
  struct a_2D_abs_storage_struct *p_2D_abs_storage;
  struct a_1D_abs_storage_struct *p_1D_abs_storage;
  struct a_1D_time_abs_storage_struct *p_1D_time_abs_storage;
  struct a_1D_time_to_lambda_abs_storage_struct *p_1D_time_to_lambda_abs_storage;
  struct a_event_abs_storage_struct *p_event_abs_storage;
  struct a_1D_event_abs_storage_struct *p_1D_event_abs_storage;
  // Additional logger storage structs to be addedd
};

struct abs_logger_with_data_struct {
  int used_elements;
  int allocated_elements;
  struct abs_logger_struct **abs_logger_pointers;
};

struct abs_logger_pointer_set_struct {
  // The logger has two record functions, an active and an inactive. Normally the active one will be to permanent storage,
  //  but if a conditional has been defined, it can switch the two, making the active one recording to temporary, which
  //  can then be filtered based on the future path of the ray
  
  // function input Coords position, k[3], p, NV, N, logger_data_union, logger_with_data_struct
  void (*active_record_function)(Coords*, double*, double,  double, int, int, struct abs_logger_struct*, struct abs_logger_with_data_struct*);
  void (*inactive_record_function)(Coords*, double*, double,  double, int, int, struct abs_logger_struct*, struct abs_logger_with_data_struct*);
  
  // A clear temporary data function (for new ray)
  void (*clear_temp)(union abs_logger_data_union*);
  
  // Write temporary to permanent is used when the record to temp function is active, and the condition is met.
  void (*temp_to_perm)(union abs_logger_data_union*);
  
  // Write temporary final_p to permanent is used when the record to temp function is active, and the condition is met
  //  and the final weight is given to be used for all stored events in the logger.
  void (*temp_to_perm_final_p)(union abs_logger_data_union*, double);
  
  // Select which temp_to_perm function to use
  //int select_t_to_p; // 1: temp_to_perm, 2: temp_to_perm_final_p
  
};


struct conditional_standard_struct{
  // Data to be transfered to the conditional function
  double Emax;
  double Emin;
  int E_limit;
  
  double Tmin;
  double Tmax;
  int T_limit;
  
  int volume_index;
  
  double Total_scat_max;
  double Total_scat_min;
  int Total_scat_limit;
  
  double exit_volume_index;
  
  // Test
  Coords test_position;
  Rotation test_rotation;
  Rotation test_t_rotation;
};

struct conditional_PSD_struct{
  double PSD_half_xwidth;
  double PSD_half_yheight;
  
  double Tmin;
  double Tmax;
  int T_limit;
  
  // Position of the PSD
  Coords PSD_position;
  Rotation PSD_rotation;
  Rotation PSD_t_rotation;
};

union conditional_data_union {
  struct conditional_standard_struct *p_standard;
  struct conditional_PSD_struct *p_PSD;
  // Add more as conditional components are made
};

// General input for conditional functions: Position, Velocity/Wavevector, weight, time, total_scat, scattered_flag, scattered_flag_VP,
// Optional extras: data_union? tree base(s)? tree base for current ray?
typedef int (*conditional_function_pointer)(union conditional_data_union*,Coords*, Coords*, double*, double*, int*, int*, int*, int**);
//typedef int (**conditional_function_pointer_array)(union *conditional_data_union,Coords*, Coords*, double*, double*, int*, int*, int**);

struct conditional_list_struct{
  int num_elements;
  
  union conditional_data_union **p_data_unions;
  conditional_function_pointer *conditional_functions;
  //int (**conditional_functions)(Coords*, Coords*, double*, double*, int*, int*, int**);
};

struct logger_struct {
  char name[256];
  // Contains ponters to all the functions assosiated with this logger
  struct logger_pointer_set_struct function_pointers;
  // Contains hard copy of logger_data_union since the size is the same as a pointer.
  union logger_data_union data_union;
  
  int logger_extend_index; // Contain index conditional_extend_array defined in master that can be acsessed from extend section.
  
  struct conditional_list_struct conditional_list;
};


// To be stored in volume, a list of pointers to the relevant loggers corresponding to each process
struct logger_for_each_process_list {
  int num_elements;
  struct logger_struct **p_logger_process;
};

// List of logger_for_each_process_list
struct loggers_struct {
  int num_elements;
  struct logger_for_each_process_list *p_logger_volume;
  #pragma acc shape(p_logger_volume[0:num_elements]) init_needed(num_elements)
};


struct abs_logger_struct {
  char name[256];
  // Contains ponters to all the functions assosiated with this logger
  struct abs_logger_pointer_set_struct function_pointers;
  // Contains hard copy of logger_data_union since the size is the same as a pointer.
  union abs_logger_data_union data_union;
  
  // Position and rotation of the abs_logger
  Coords position;
  Rotation rotation;
  Rotation t_rotation;
  
  int abs_logger_extend_index; // Contain index conditional_extend_array defined in master that can be acsessed from extend section.
  
  struct conditional_list_struct conditional_list;
};

// To be stored in volume, a list of pointers to the relevant abs loggers corresponding to each process
/*
struct abs_logger_for_each_process_list {
  int num_elements;
  struct abs_logger_struct **p_abs_logger_process;
};
*/

// List of abs logger_for_each_process_list
struct abs_loggers_struct {
  int num_elements;
  //struct abs_logger_for_each_process_list *p_abs_logger_volume;
  struct abs_logger_struct **p_abs_logger;
};



struct geometry_struct
{
char shape[64];     // name of shape used (sphere, cylinder, box, off, ...)
enum shape eShape;  // enum with shape for flexible functions GPU
double priority_value;    // priority of the geometry
Coords center;      // Center position of volume, reported by components in global frame, updated to main frame in initialize
// Rotation of this volume
Rotation rotation_matrix; // rotation matrix of volume, reported by component in global frame, updated to main frame in initialize
Rotation transpose_rotation_matrix; // As above
// Array of prrotation matrixes for processes assigned to this volume (indexed by non_isotropic_rot_index in the processes)
Rotation *process_rot_matrix_array;             // matrix that transforms from main coordinate system to local process in this specific volume
Rotation *transpose_process_rot_matrix_array;   // matrix that transforms from local process in this specific volume to main coordinate system
int process_rot_allocated;  // Keeps track of allocation status of rot_matrix_array

struct rotation_struct rotation; // Not used, is the x y and z rotation angles.
int visualization_on; // If visualization_on is true, the volume will be drawn in mcdisplay, otherwise not
int is_exit_volume; // If is exit volume = 1, the ray will exit the component when it enters this volume.
int is_mask_volume; // 1 if volume itself is a mask (masking the ones in it's mask list), otherwise 0
int mask_index;
int is_masked_volume; // 1 if this volume is being masked by another volume, the volumes that mask it is in masked_by_list
int mask_mode; // ALL/ANY 1/2. In ALL mode, only parts covered by all masks is simulated, in ANY mode, area covered by just one mask is simulated
double geometry_p_interact; // fraction of rays that interact with this volume for each scattering (between 0 and 1, 0 for disable)
union geometry_parameter_union geometry_parameters; // relevant parameters for this shape
union geometry_parameter_union (*copy_geometry_parameters)(union geometry_parameter_union*);
struct focus_data_struct focus_data; // Used for focusing from this geometry

// New focus data implementation to remove the focusing bug for non-isotripic processes
struct focus_data_array_struct focus_data_array;
struct pointer_to_1d_int_list focus_array_indices; // Add 1D integer array with indecies for correct focus_data for each process


// intersect_function takes position/velocity of ray and parameters, returns time list
int (*intersect_function)(double*,int*,double*,double*,struct geometry_struct*);
//                        t_array,n_ar,r      ,v

// within_function that checks if the ray origin is within this volume
int (*within_function)(Coords,struct geometry_struct*);
//                     r,      parameters

// mcdisplay function, draws the geometry
//void (*mcdisplay_function)(struct lines_to_draw*,int,struct Volume_struct**,int);
void (*mcdisplay_function)(struct lines_to_draw*,int,struct geometry_struct**,int);
//                                lines          index             Geometries   N

void (*initialize_from_main_function)(struct geometry_struct*);

struct pointer_to_1d_coords_list (*shell_points)(struct geometry_struct*, int maximum_number_of_points);

// List of other volumes to be check when ray starts within this volume.
struct pointer_to_1d_int_list intersect_check_list;
// List of other volumes the ray may enter, if the ray intersects the volume itself.
struct pointer_to_1d_int_list destinations_list;
// The destinations list stored as a logic list which makes some tasks quicker. OBSOLETE
//struct pointer_to_1d_int_list destinations_logic_list;
// Reduced list of other volumes the ray may enter, if the ray intersects the volume itself.
struct pointer_to_1d_int_list reduced_destinations_list;
// List of other volumes that are within this volume
struct pointer_to_1d_int_list children;
// List of other volumes that are within this volume, but does not have any parents that are children of this volume
struct pointer_to_1d_int_list direct_children;
// List of next possible volumes (only used in tagging)
struct pointer_to_1d_int_list next_volume_list;
// List of volumes masked by this volume (usually empty)
struct pointer_to_1d_int_list mask_list;
// List of volumes masking this volume
struct pointer_to_1d_int_list masked_by_list;
// List of masks masking this volume (global mask indices)
struct pointer_to_1d_int_list masked_by_mask_index_list;
// Additional intersect lists dependent on mask status
//struct indexed_mask_lists_struct mask_intersect_lists;
// Simpler way of storing the mask_intersect_lists
struct pointer_to_1d_int_list mask_intersect_list;
};

struct physics_struct
{
char name[256]; // User defined material name
int interact_control;
int is_vacuum;
double my_a;
int number_of_processes;
// pointer to array of pointers to physics_sub structures that each describe a scattering process
struct scattering_process_struct *p_scattering_array;
};

union data_transfer_union{
    // List of pointers to storage structs for all supported physical processes
    struct Incoherent_physics_storage_struct  *pointer_to_a_Incoherent_physics_storage_struct;
    struct Powder_physics_storage_struct *pointer_to_a_Powder_physics_storage_struct;
    struct Single_crystal_physics_storage_struct *pointer_to_a_Single_crystal_physics_storage_struct;
    struct AF_HB_1D_physics_storage_struct *pointer_to_a_AF_HB_1D_physics_storage_struct;
    struct IncoherentPhonon_physics_storage_struct *pointer_to_a_IncoherentPhonon_physics_storage_struct;
    struct PhononSimpleNumeric_physics_storage_struct *pointer_to_a_PhononSimpleNumeric_storage_struct;
    struct PhononSimple_physics_storage_struct *pointer_to_a_PhononSimple_storage_struct;
    struct MagnonSimple_physics_storage_struct *pointer_to_a_MagnonSimple_storage_struct;
    struct Sans_spheres_physics_storage_struct *pointer_to_a_Sans_spheres_physics_storage_struct;
    struct Texture_physics_storage_struct *pointer_to_a_Texture_physics_storage_struct;
    struct NCrystal_physics_storage_struct *pointer_to_a_NCrystal_physics_storage_struct;
    struct Non_physics_storage_struct *pointer_to_a_Non_physics_storage_struct;
    struct Template_physics_storage_struct *pointer_to_a_Template_physics_storage_struct;
    // possible to add as many structs as wanted, without increasing memory footprint.
};



struct scattering_process_struct
{
char name[256];                // User defined process name
enum process eProcess;         // enum value corresponding to this process GPU
double process_p_interact;     // double between 0 and 1 that describes the fraction of events forced to undergo this process. -1 for disable
int non_isotropic_rot_index;   // -1 if process is isotrpic, otherwise is the index of the process rotation matrix in the volume
Rotation rotation_matrix;      // rotation matrix of process, reported by component in local frame, transformed and moved to volume struct in main

union data_transfer_union data_transfer; // The way to reach the storage space allocated for this process (see examples in process.comp files)

// probability_for_scattering_functions calculates this probability given k_i and parameters
int (*probability_for_scattering_function)(double*,double*,union data_transfer_union,struct focus_data_struct*, _class_particle *_particle);
//                                         prop,   k_i,   ,parameters               , focus data / function

// A scattering_function takes k_i and parameters, returns k_f
int (*scattering_function)(double*,double*,double*,union data_transfer_union,struct focus_data_struct*, _class_particle *_particle);
//                         k_f,    k_i,    weight, parameters               , focus data / function
};

struct Volume_struct
{
char name[256]; // User defined volume name
struct geometry_struct geometry;        // Geometry properties (including intersect functions, generated lists)
struct physics_struct *p_physics;       // Physical properties (list of scattering processes, absorption)
struct loggers_struct loggers;          // Loggers assosiated with this volume
struct abs_loggers_struct abs_loggers;  // Loggers assosiated with this volume
};

// example of calling a scattering process
// volume_pointer_list[3]->physics.scattering_process[5].probability_for_scattering_function(input,volume_pointer_list[3]->physics.scattering_process[5])

struct starting_lists_struct
{
struct pointer_to_1d_int_list allowed_starting_volume_logic_list;
struct pointer_to_1d_int_list reduced_start_list;
struct pointer_to_1d_int_list start_logic_list;
struct pointer_to_1d_int_list starting_destinations_list;
};

struct global_positions_to_transform_list_struct {
int num_elements;
Coords **positions;
};

struct global_rotations_to_transform_list_struct {
int num_elements;
Rotation **rotations;
};

struct global_process_element_struct
{
char name[128]; // Name of the process
int component_index;
struct scattering_process_struct *p_scattering_process;
};

struct pointer_to_global_process_list {
int num_elements;
struct global_process_element_struct *elements;
};

struct global_material_element_struct
{
char name[128];
int component_index;
struct physics_struct *physics;
};

struct pointer_to_global_material_list {
int num_elements;
struct global_material_element_struct *elements;
};

struct global_geometry_element_struct
{
char name[128];
int component_index;
int activation_counter;
int stored_copies;
int active;
struct Volume_struct *Volume;
};

struct pointer_to_global_geometry_list {
int num_elements;
struct global_geometry_element_struct *elements;
};

struct global_logger_element_struct {
char name[128];
int component_index;
struct logger_struct *logger;
};

struct pointer_to_global_logger_list {
int num_elements;
struct global_logger_element_struct *elements;
};

struct global_abs_logger_element_struct {
char name[128];
int component_index;
struct abs_logger_struct *abs_logger;
};

struct pointer_to_global_abs_logger_list {
int num_elements;
struct global_abs_logger_element_struct *elements;
};

struct global_tagging_conditional_element_struct {
struct conditional_list_struct conditional_list;
int extend_index;
char name[1024];
int use_status;
};

struct global_tagging_conditional_list_struct {
int num_elements;
int current_index;
struct global_tagging_conditional_element_struct *elements;
};


struct global_master_element_struct {
char name[128];
int component_index;
int stored_number_of_scattering_events; // TEST
struct conditional_list_struct *tagging_conditional_list_pointer;
};

struct pointer_to_global_master_list {
int num_elements;
struct global_master_element_struct *elements;
};


// -------------    Physics functions   ---------------------------------------------------------

//#include "Test_physics.c"
//#include "Incoherent_test.c"

// -------------    General functions   ---------------------------------------------------------
double distance_between(Coords position1,Coords position2) {
    return sqrt((position1.x-position2.x)*(position1.x-position2.x) +
                (position1.y-position2.y)*(position1.y-position2.y) +
                (position1.z-position2.z)*(position1.z-position2.z));
};



double length_of_3vector(double *r) {
        return sqrt(r[0]*r[0]+r[1]*r[1]+r[2]*r[2]);
    };

double length_of_position_vector(Coords point) {
        return sqrt(point.x*point.x+point.y*point.y+point.z*point.z);
    };

Coords make_position(double *r) {
    Coords temp;
    
    temp.x = r[0];temp.y = r[1];temp.z = r[2];
    return temp;
};

Coords coords_scalar_mult(Coords input,double scalar) {
    return coords_set(scalar*input.x,scalar*input.y,scalar*input.z);
};

double union_coords_dot(Coords vector1,Coords vector2) {
    return vector1.x*vector2.x + vector1.y*vector2.y + vector1.z*vector2.z;
}


int sum_int_list(struct pointer_to_1d_int_list list) {
    int iterate,sum = 0;
    for (iterate = 0;iterate < list.num_elements;iterate++) sum += list.elements[iterate];
    return sum;
    };
    
int on_int_list(struct pointer_to_1d_int_list list,int target) {
    int iterate,output=0;
    for (iterate = 0; iterate<list.num_elements ;iterate++) {
        if (list.elements[iterate] == target) output = 1;
    }
    return output;
    };

int find_on_int_list(struct pointer_to_1d_int_list list,int target) {
    int iterate;
    for (iterate = 0;iterate < list.num_elements;iterate++) {
        if (list.elements[iterate] == target) return iterate;
    }
    return -1;
    };
    
void on_both_int_lists(struct pointer_to_1d_int_list *list1, struct pointer_to_1d_int_list *list2, struct pointer_to_1d_int_list *common) {
    // Assume common.elements is allocated to a number of int's large enough to hold the resulting list
    int iterate,used_elements=0;
    for (iterate=0;iterate<list1->num_elements;iterate++) {
        if (on_int_list(*list2,list1->elements[iterate])) common->elements[used_elements++] = list1->elements[iterate];
    }
    common->num_elements = used_elements;
    };

void remove_element_in_list_by_index(struct pointer_to_1d_int_list *list,int index) {
    if (index >= list->num_elements) {
      printf("ERROR(remove_element_in_list_by_index): trying to remove an index that wasn't allocated to begin with");
      exit(1);
    }
    else {
        int iterate;
        int *temp;
        for (iterate = index;iterate < list->num_elements -1;iterate++) {
            list->elements[iterate] = list->elements[iterate+1];
        }
        list->num_elements--;
        //if (list->num_elements==0) printf("Making empty list!\n");
        temp = malloc(list->num_elements * sizeof(int));
        for (iterate = 0;iterate < list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
        free(list->elements);
        list->elements = malloc(list->num_elements * sizeof(int));
        for (iterate = 0;iterate < list->num_elements;iterate++) list->elements[iterate] = temp[iterate];
        free(temp);

    }
    };
    
void remove_element_in_list_by_value(struct pointer_to_1d_int_list *list,int value) {
    int iterate;
    for (iterate = 0;iterate < list->num_elements;iterate++) {
        if (list->elements[iterate] == value) remove_element_in_list_by_index(list,iterate);
    }
    };
    
void merge_lists(struct pointer_to_1d_int_list *result,struct pointer_to_1d_int_list *list1,struct pointer_to_1d_int_list *list2) {
    if (result->num_elements > 0) free(result->elements);
    result->num_elements = list1->num_elements + list2->num_elements;
    if (result->num_elements != 0) {
      result->elements = malloc(result->num_elements*sizeof(int));
      int iterate;
      for (iterate = 0;iterate < list1->num_elements;iterate++)
          result->elements[iterate] = list1->elements[iterate];
      for (iterate = 0;iterate < list2->num_elements;iterate++)
          result->elements[list1->num_elements+iterate] = list2->elements[iterate];
    }
    };

void add_element_to_double_list(struct pointer_to_1d_double_list *list,double value) {
    if (list->num_elements == 0) {
      list->num_elements++;
      list-> elements = malloc(list->num_elements*sizeof(double));
      list-> elements[0] = value;
    } else {
      double temp[list->num_elements];
      int iterate;
      for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
      free(list->elements);
      list->num_elements++;
      list-> elements = malloc(list->num_elements*sizeof(double));
      for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
      list->elements[list->num_elements-1] = value;
    }
    };

void add_element_to_int_list(struct pointer_to_1d_int_list *list,int value) {
    if (list->num_elements == 0) {
      list->num_elements++;
      list-> elements = malloc(list->num_elements*sizeof(int));
      list-> elements[0] = value;
    } else {
      int temp[list->num_elements];
      int iterate;
      for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
      free(list->elements);
      list->num_elements++;
      list-> elements = malloc(list->num_elements*sizeof(int));
      for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
      list->elements[list->num_elements-1] = value;
    }
    };

// Need to check if absolute_rotation is preserved correctly.
void add_element_to_focus_data_array(struct focus_data_array_struct *focus_data_array,struct focus_data_struct focus_data) {
    if (focus_data_array->num_elements == 0) {
      focus_data_array->num_elements++;
      focus_data_array-> elements = malloc(focus_data_array->num_elements*sizeof(struct focus_data_struct));
      focus_data_array-> elements[0] = focus_data;
    } else {
      struct focus_data_struct temp[focus_data_array->num_elements];
      int iterate;
      for (iterate=0;iterate<focus_data_array->num_elements;iterate++) temp[iterate] = focus_data_array->elements[iterate];
      free(focus_data_array->elements);
      focus_data_array->num_elements++;
      focus_data_array-> elements = malloc(focus_data_array->num_elements*sizeof(struct focus_data_struct));
      for (iterate=0;iterate<focus_data_array->num_elements-1;iterate++) focus_data_array->elements[iterate] = temp[iterate];
      focus_data_array->elements[focus_data_array->num_elements-1] = focus_data;
    }
    };


void add_to_logger_with_data(struct logger_with_data_struct *logger_with_data, struct logger_struct *logger) {
    // May reorder the order of the if conditions to avoid checking the == 0 for every single ray
    if (logger_with_data->allocated_elements == 0) {
        logger_with_data->allocated_elements = 5;
        logger_with_data->logger_pointers = malloc(logger_with_data->allocated_elements*sizeof(struct logger_struct*));
        logger_with_data->used_elements = 1;
        logger_with_data->logger_pointers[0] = logger;
    } else if (logger_with_data->used_elements > logger_with_data->allocated_elements-1) {
        struct logger_with_data_struct temp_logger_with_data;
        temp_logger_with_data.logger_pointers = malloc((logger_with_data->used_elements)*sizeof(struct logger_struct*));
        int iterate;
        for (iterate=0;iterate<logger_with_data->used_elements;iterate++) {
            temp_logger_with_data.logger_pointers[iterate]  = logger_with_data->logger_pointers[iterate];
        }
        free(logger_with_data->logger_pointers);
        logger_with_data->allocated_elements = logger_with_data->allocated_elements+5;
        logger_with_data->logger_pointers = malloc(logger_with_data->allocated_elements*sizeof(struct logger_struct*));
        for (iterate=0;iterate<logger_with_data->used_elements;iterate++) {
            logger_with_data->logger_pointers[iterate]  = temp_logger_with_data.logger_pointers[iterate];
        }

        logger_with_data->logger_pointers[logger_with_data->used_elements++] = logger;
        
        
    } else {
        logger_with_data->logger_pointers[logger_with_data->used_elements++] = logger;
    }
    
};

void add_to_abs_logger_with_data(struct abs_logger_with_data_struct *abs_logger_with_data, struct abs_logger_struct *abs_logger) {
    // May reorder the order of the if conditions to avoid checking the == 0 for every single ray
    if (abs_logger_with_data->allocated_elements == 0) {
        abs_logger_with_data->allocated_elements = 5;
        abs_logger_with_data->abs_logger_pointers = malloc(abs_logger_with_data->allocated_elements*sizeof(struct abs_logger_struct*));
        abs_logger_with_data->used_elements = 1;
        abs_logger_with_data->abs_logger_pointers[0] = abs_logger;
    } else if (abs_logger_with_data->used_elements > abs_logger_with_data->allocated_elements-1) {
        struct abs_logger_with_data_struct temp_abs_logger_with_data;
        temp_abs_logger_with_data.abs_logger_pointers = malloc((abs_logger_with_data->used_elements)*sizeof(struct abs_logger_struct*));
        int iterate;
        for (iterate=0;iterate<abs_logger_with_data->used_elements;iterate++) {
            temp_abs_logger_with_data.abs_logger_pointers[iterate]  = abs_logger_with_data->abs_logger_pointers[iterate];
        }
        free(abs_logger_with_data->abs_logger_pointers);
        abs_logger_with_data->allocated_elements = abs_logger_with_data->allocated_elements+5;
        abs_logger_with_data->abs_logger_pointers = malloc(abs_logger_with_data->allocated_elements*sizeof(struct abs_logger_struct*));
        for (iterate=0;iterate<abs_logger_with_data->used_elements;iterate++) {
            abs_logger_with_data->abs_logger_pointers[iterate] = temp_abs_logger_with_data.abs_logger_pointers[iterate];
        }

        abs_logger_with_data->abs_logger_pointers[abs_logger_with_data->used_elements++] = abs_logger;
        
        
    } else {
        abs_logger_with_data->abs_logger_pointers[abs_logger_with_data->used_elements++] = abs_logger;
    }
    
};


// Used typedef to avoid having to change this function later. May update others to use same phillosphy.
void add_function_to_conditional_list(struct conditional_list_struct *list,conditional_function_pointer new, union conditional_data_union *data_union) {
    if (list->num_elements == 0) {
      list->num_elements++;
      list->conditional_functions = malloc(list->num_elements*sizeof(conditional_function_pointer));
      list->p_data_unions = malloc(list->num_elements*sizeof(union conditional_data_union*));
      list->conditional_functions[0] = new;
      list->p_data_unions[0] = data_union;
    }
    else {
    conditional_function_pointer temp_fp[list->num_elements];
    union conditional_data_union *temp_du[list->num_elements];
    int iterate;
    // Could even get away with a shallow copy here instead of the loop, but it is not relevant for performance.
    for (iterate=0;iterate<list->num_elements;iterate++) {
      temp_fp[iterate] = list->conditional_functions[iterate];
      temp_du[iterate] = list->p_data_unions[iterate];
    }
    free(list->conditional_functions);
    free(list->p_data_unions);
    list->num_elements++;
    list->conditional_functions = malloc(list->num_elements*sizeof(conditional_function_pointer));
    list->p_data_unions = malloc(list->num_elements*sizeof(union conditional_data_union*));
    
    for (iterate=0;iterate<list->num_elements-1;iterate++) {
      list->conditional_functions[iterate] = temp_fp[iterate];
      list->p_data_unions[iterate] = temp_du[iterate];
    }
    list->conditional_functions[list->num_elements-1] = new;
    list->p_data_unions[list->num_elements-1] = data_union;
    }
};


// could make function that removes a element from a 1d_int_list, and have each list generation as a function that takes a copy of an overlap list

void print_1d_int_list(struct pointer_to_1d_int_list list,char *name) {
        int iterate;
        printf("LIST: ");printf("%s",name);printf(" = [");
        for (iterate = 0; iterate < list.num_elements; iterate++) {
            printf("%d",list.elements[iterate]);
            if (iterate < list.num_elements - 1) printf(",");
        }
        printf("]\n");
    };

void print_1d_double_list(struct pointer_to_1d_double_list list,char *name) {
        int iterate;
        printf("LIST: ");printf("%s",name);printf(" = [");
        for (iterate = 0; iterate < list.num_elements; iterate++) {
            printf("%f",list.elements[iterate]);
            if (iterate < list.num_elements - 1) printf(",");
        }
        printf("]\n");
    };

void print_position(Coords pos,char *name) {
    printf("POSITION: ");printf("%s",name);printf(" = (%f,%f,%f)\n",pos.x,pos.y,pos.z);
    };

void print_rotation(Rotation rot, char *name) {
    printf("ROT MATRIX: %s \n",name);
    printf("[%f %f %f]\n",rot[0][0],rot[0][1],rot[0][2]);
    printf("[%f %f %f]\n",rot[1][0],rot[1][1],rot[1][2]);
    printf("[%f %f %f]\n\n",rot[2][0],rot[2][1],rot[2][2]);
};
    
void allocate_list_from_temp(int num_elements,struct pointer_to_1d_int_list original,struct pointer_to_1d_int_list *new) {
        int iterate;
    
        new->num_elements = num_elements;
        if (num_elements > 0) {
            new->elements = malloc(num_elements*sizeof(int));
            for (iterate = 0;iterate < num_elements; iterate++) new->elements[iterate] = original.elements[iterate];
        } else new->elements = NULL;
    
    };

void allocate_logic_list_from_temp(int num_elements,struct pointer_to_1d_int_list original, struct pointer_to_1d_int_list *new) {
        // A logic list shares the same structure of a normal list, but instead of listing numbers, it is a list of yes / no (1/0)
        // Giving this function a list of [1 3 5] (and num_elements = 9) would return [0 1 0 1 0 1 0 0 0];
        int iterate;
    
        new->num_elements = num_elements;
        if (num_elements > 0) {
            new->elements = malloc(num_elements*sizeof(int));
            for (iterate = 0;iterate < num_elements;iterate++) new->elements[iterate] = 0;
            for (iterate = 0;iterate < original.num_elements;iterate++) {
                if (original.elements[iterate] < num_elements)
                    new->elements[original.elements[iterate]] = 1;
                else printf("Trying to allocate logical list without enough memory\n");
            }
        } else new->elements = NULL;
    };

/*
struct global_positions_to_transform_list_struct {
int num_elements;
Coords **positions;
}

struct global_rotations_to_transform_list_struct {
int num_elements;
Rotation **rotations;
}
*/

void add_position_pointer_to_list(struct global_positions_to_transform_list_struct *list, Coords *new_position_pointer) {
    if (list->num_elements == 0) {
      list->num_elements++;
      list->positions = malloc(list->num_elements*sizeof(Coords*));
      list->positions[0] = new_position_pointer;
    } else {
      Coords **temp;
      temp = malloc(list->num_elements*sizeof(Coords*));
      if (temp == NULL) printf("malloc failed in add_position_pointer_to_list for temp\n");
      
      int iterate;
      for (iterate=0;iterate<list->num_elements;iterate++)
        temp[iterate] = list->positions[iterate];
      free(list->positions);
      list->num_elements++;
      list->positions = malloc(list->num_elements*sizeof(Coords*));
      if (list->positions == NULL) printf("malloc failed in add_position_pointer_to_list for list->positions\n");
      
      for (iterate=0;iterate<list->num_elements-1;iterate++)
        list->positions[iterate] = temp[iterate];
      free(temp);
      list->positions[list->num_elements-1] = new_position_pointer;
    }
};

void add_rotation_pointer_to_list(struct global_rotations_to_transform_list_struct *list, Rotation *new_rotation_pointer) {
    if (list->num_elements == 0) {
      list->num_elements++;
      list->rotations = malloc(list->num_elements*sizeof(Rotation*));
      list->rotations[0] = new_rotation_pointer;
    } else {
      Rotation **temp;
      temp = malloc(list->num_elements*sizeof(Rotation*));
      int iterate;
      for (iterate=0;iterate<list->num_elements;iterate++)
        temp[iterate] = list->rotations[iterate];
      free(list->rotations);
      list->num_elements++;
      list->rotations = malloc(list->num_elements*sizeof(Rotation*));
      
      for (iterate=0;iterate<list->num_elements-1;iterate++)
        list->rotations[iterate] = temp[iterate];
      free(temp);
      list->rotations[list->num_elements-1] = new_rotation_pointer;
    }
};

void add_element_to_process_list(struct pointer_to_global_process_list *list,struct global_process_element_struct new_element) {
    if (list->num_elements == 0) {
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_process_element_struct));
    list-> elements[0] = new_element;
    }
    else {
    struct global_process_element_struct temp[list->num_elements];
    int iterate;
    for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
    free(list->elements);
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_process_element_struct));
    for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
    list->elements[list->num_elements-1] = new_element;
    }
};

void add_element_to_material_list(struct pointer_to_global_material_list *list,struct global_material_element_struct new_element) {
    if (list->num_elements == 0) {
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_material_element_struct));
    list-> elements[0] = new_element;
    }
    else {
    struct global_material_element_struct temp[list->num_elements];
    int iterate;
    for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
    free(list->elements);
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_material_element_struct));
    for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
    list->elements[list->num_elements-1] = new_element;
    }
};

void add_element_to_geometry_list(struct pointer_to_global_geometry_list *list,struct global_geometry_element_struct new_element) {
    if (list->num_elements == 0) {
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_geometry_element_struct));
    list-> elements[0] = new_element;
    }
    else {
    struct global_geometry_element_struct temp[list->num_elements];
    int iterate;
    for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
    free(list->elements);
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_geometry_element_struct));
    for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
    list->elements[list->num_elements-1] = new_element;
    }
};

void add_element_to_logger_list(struct pointer_to_global_logger_list *list,struct global_logger_element_struct new_element) {
    if (list->num_elements == 0) {
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_logger_element_struct));
    list-> elements[0] = new_element;
    }
    else {
    struct global_logger_element_struct temp[list->num_elements];
    int iterate;
    for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
    free(list->elements);
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_logger_element_struct));
    for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
    list->elements[list->num_elements-1] = new_element;
    }
};

void add_element_to_abs_logger_list(struct pointer_to_global_abs_logger_list *list, struct global_abs_logger_element_struct new_element) {
    if (list->num_elements == 0) {
      list->num_elements++;
      list->elements = malloc(list->num_elements*sizeof(struct global_abs_logger_element_struct));
      list->elements[0] = new_element;
    }
    else {
      struct global_abs_logger_element_struct temp[list->num_elements];
      int iterate;
      for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
      free(list->elements);
      list->num_elements++;
      list-> elements = malloc(list->num_elements*sizeof(struct global_abs_logger_element_struct));
      for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
      list->elements[list->num_elements-1] = new_element;
    }
};

void add_element_to_tagging_conditional_list(struct global_tagging_conditional_list_struct *list,struct global_tagging_conditional_element_struct new_element) {
    if (list->num_elements == 0) {
    list->num_elements++;
    list->elements = malloc(list->num_elements*sizeof(struct global_tagging_conditional_element_struct));
    list->elements[0] = new_element;
    }
    else {
    struct global_tagging_conditional_element_struct temp[list->num_elements];
    int iterate;
    for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
    free(list->elements);
    list->num_elements++;
    list->elements = malloc(list->num_elements*sizeof(struct global_tagging_conditional_element_struct));
    for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
    list->elements[list->num_elements-1] = new_element;
    }
};

void add_element_to_master_list(struct pointer_to_global_master_list *list,struct global_master_element_struct new_element) {
    if (list->num_elements == 0) {
    list->num_elements++;
    list->elements = malloc(list->num_elements*sizeof(struct global_master_element_struct));
    list->elements[0] = new_element;
    }
    else {
    struct global_master_element_struct temp[list->num_elements];
    int iterate;
    for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
    free(list->elements);
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_master_element_struct));
    for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
    list->elements[list->num_elements-1] = new_element;
    }
};

void add_initialized_logger_in_volume(struct loggers_struct *loggers,int number_of_processes) {
  int iterate;
  if (loggers->num_elements == 0) {
    loggers->num_elements++;
    loggers->p_logger_volume = malloc(loggers->num_elements * sizeof(struct logger_for_each_process_list));
    loggers->p_logger_volume[0].num_elements = number_of_processes;
    loggers->p_logger_volume[0].p_logger_process = malloc(number_of_processes * sizeof(struct logger_struct**));
    for (iterate=0;iterate<number_of_processes;iterate++)
      loggers->p_logger_volume[0].p_logger_process[iterate] = NULL;
  } else {
    // Already some elements, store them in temp, free main, transfer back and add newest.
    struct logger_for_each_process_list temp[loggers->num_elements];
    
    for (iterate=0;iterate<loggers->num_elements;iterate++) temp[iterate] = loggers->p_logger_volume[iterate];
    free(loggers->p_logger_volume);
    loggers->num_elements++;
    loggers->p_logger_volume = malloc(loggers->num_elements*sizeof(struct logger_for_each_process_list));
    for (iterate=0;iterate<loggers->num_elements-1;iterate++) loggers->p_logger_volume[iterate] = temp[iterate];
    loggers->p_logger_volume[loggers->num_elements-1].num_elements = number_of_processes;
    loggers->p_logger_volume[loggers->num_elements-1].p_logger_process = malloc(number_of_processes * sizeof(struct logger_struct**));
    for (iterate=0;iterate<number_of_processes;iterate++) {
      loggers->p_logger_volume[loggers->num_elements-1].p_logger_process[iterate] = NULL;
    }
  }
};

/*
void add_initialized_abs_logger_in_volume(struct abs_loggers_struct *abs_loggers, int number_of_processes) {
  int iterate;
  if (abs_loggers->num_elements == 0) {
    abs_loggers->num_elements++;
    abs_loggers->p_abs_logger_volume = malloc(abs_loggers->num_elements * sizeof(struct abs_loggers_struct));
    abs_loggers->p_abs_logger_volume[0].num_elements = number_of_processes;
    abs_loggers->p_abs_logger_volume[0].p_abs_logger_process = malloc(number_of_processes * sizeof(struct abs_logger_struct**));
    for (iterate=0;iterate<number_of_processes;iterate++)
      abs_loggers->p_abs_logger_volume[0].p_abs_logger_process[iterate] = NULL;
  } else {
    // Already some elements, store them in temp, free main, transfer back and add newest.
    struct abs_logger_for_each_process_list temp[abs_loggers->num_elements];
    
    for (iterate=0;iterate<abs_loggers->num_elements;iterate++) temp[iterate] = abs_loggers->p_abs_logger_volume[iterate];
    free(abs_loggers->p_abs_logger_volume);
    abs_loggers->num_elements++;
    abs_loggers->p_abs_logger_volume = malloc(abs_loggers->num_elements*sizeof(struct abs_logger_for_each_process_list));
    for (iterate=0;iterate<abs_loggers->num_elements-1;iterate++) abs_loggers->p_abs_logger_volume[iterate] = temp[iterate];
    abs_loggers->p_abs_logger_volume[abs_loggers->num_elements-1].num_elements = number_of_processes;
    abs_loggers->p_abs_logger_volume[abs_loggers->num_elements-1].p_abs_logger_process = malloc(number_of_processes * sizeof(struct abs_logger_struct**));
    for (iterate=0;iterate<number_of_processes;iterate++) {
      abs_loggers->p_abs_logger_volume[abs_loggers->num_elements-1].p_abs_logger_process[iterate] = NULL;
    }
  }
};
*/

void add_initialized_abs_logger_in_volume(struct abs_loggers_struct *abs_loggers) {
  int iterate;
  if (abs_loggers->num_elements == 0) {
    abs_loggers->num_elements++;
    abs_loggers->p_abs_logger = malloc(abs_loggers->num_elements * sizeof(struct abs_logger_struct*));
  } else {
    // Already some elements, store them in temp, free main, transfer back and add newest.
    struct abs_logger_struct *temp[abs_loggers->num_elements];
    
    for (iterate=0;iterate<abs_loggers->num_elements;iterate++) temp[iterate] = abs_loggers->p_abs_logger[iterate];
    free(abs_loggers->p_abs_logger);
    
    abs_loggers->num_elements++;
    abs_loggers->p_abs_logger = malloc(abs_loggers->num_elements*sizeof(struct abs_logger_struct*));
    for (iterate=0;iterate<abs_loggers->num_elements-1;iterate++) abs_loggers->p_abs_logger[iterate] = temp[iterate];
    abs_loggers->p_abs_logger[abs_loggers->num_elements-1] = NULL;
    
  }
};


// -------------    Functions used to shorten master trace    ---------------------------------------------


void update_current_mask_intersect_status(struct pointer_to_1d_int_list *current_mask_intersect_list_status, struct pointer_to_1d_int_list *mask_status_list, struct Volume_struct **Volumes, int *current_volume) {
  // This function is to be executed whenever the current volume changes, or the mask status changes
  // It updates the effective mask status for each element of a volumes mask_intersect_list
  // The effective mask takes the ALL/ANY mode assosiated with each volume into account,
  //  meaning a volume needs to be within ALL/ANY of it's masks to have a status of 1
  // In most cases this mask_intersect_list will be empty, and memory operations are avoided
  //printf("Number of elements to be check: %d \n",Volumes[*current_volume]->geometry.mask_intersect_list.num_elements);
  
  if (Volumes[*current_volume]->geometry.mask_intersect_list.num_elements > 0) {
    int iterate,this_element,*mask_start,*mask_check;
    for (iterate=0;iterate<Volumes[*current_volume]->geometry.mask_intersect_list.num_elements;iterate++) {
      this_element = Volumes[*current_volume]->geometry.mask_intersect_list.elements[iterate];
      //printf("We are investigating volume number %d from the mask_intersect_list of volume %d",this_element,*current_volume);
      if (Volumes[this_element]->geometry.mask_mode == 2) { // ANY mask mode
        //printf("We are in ANY mask mode! \n");
        current_mask_intersect_list_status->elements[iterate] = 0; // Assume the mask status is 0, but if any are 1, take that instead
        for (mask_start=mask_check=Volumes[this_element]->geometry.masked_by_mask_index_list.elements;mask_check-mask_start<Volumes[this_element]->geometry.masked_by_mask_index_list.num_elements;mask_check++) {
          //printf("Checking all the mask statuses of volumes masking %d, now mask with global mask index %d ! \n",this_element,*mask_check);
          if (mask_status_list->elements[*mask_check] == 1) {
            //printf("The status was 1, so the effective status is set to 1 and the loop is stopped");
            current_mask_intersect_list_status->elements[iterate] = 1;
            break;
          }
        }
      } else { // ALL mask mode
        //printf("We are in ALL mask mode! \n");
        current_mask_intersect_list_status->elements[iterate] = 1; // Assume the mask status is 1, but if any one is 0, take that instead
        for (mask_start=mask_check=Volumes[this_element]->geometry.masked_by_mask_index_list.elements;mask_check-mask_start<Volumes[this_element]->geometry.masked_by_mask_index_list.num_elements;mask_check++) {
          //printf("Checking all the mask statuses of volumes masking %d, now mask with global mask index %d ! \n",this_element,*mask_check);
          if (mask_status_list->elements[*mask_check] == 0) {
            //printf("The status was 0, so the effective status is set to 0 and the loop is stopped \n");
            current_mask_intersect_list_status->elements[iterate] = 0;
            break;
          }
        }
      }
    }
  }
    
  /* Original version compatible with trace scope
  for (iterate=0;iterate<Volumes[current_volume]->geometry.mask_intersect_list.num_elements;iterate++) {
    this_element = Volumes[current_volume]->geometry.mask_intersect_list.elements[iterate];
    if (Volumes[this_element]->geometry.mask_mode == 2) { // ANY mask mode
      current_mask_intersect_list_status.elements[iterate] = 0; // Assume the mask status is 0, but if any are 1, take that instead
      for (mask_start=mask_check=Volumes[this_element]->geometry.masked_by_mask_index_list.elements;mask_check-mask_start<Volumes[this_element]->geometry.masked_by_mask_index_list.num_elements;mask_check++) {
        if (mask_status_list[*mask_check] == 1) {
          current_mask_intersect_list_status.elements[iterate] = 1;
          break;
        }
      }
    } else { // ALL mask mode
      current_mask_intersect_list_status.elements[iterate] = 1; // Assume the mask status is 1, but if any one is 0, take that instead
      for (mask_start=mask_check=Volumes[this_element]->geometry.masked_by_mask_index_list.elements;mask_check-mask_start<Volumes[this_element]->geometry.masked_by_mask_index_list.num_elements;mask_check++) {
        if (mask_status_list[*mask_check] == 0) {
          current_mask_intersect_list_status.elements[iterate] = 0;
          break;
        }
      }
    }
  }
  */
}

// -------------    Tagging functions    ------------------------------------------------------------------

struct tagging_tree_node_struct {
  // Statistics:
  double intensity;
  int number_of_rays;
  // tree pointers
  struct tagging_tree_node_struct *above;   // Pointer to node above
  struct tagging_tree_node_struct **volume_branches;
  struct tagging_tree_node_struct **process_branches;
};

struct list_of_tagging_tree_node_pointers {
  struct tagging_tree_node_struct **elements;
  int num_elements;
};

struct tagging_tree_node_struct *make_tagging_tree_node(void) {
    return (struct tagging_tree_node_struct *) malloc(sizeof(struct tagging_tree_node_struct));
}


struct tagging_tree_node_struct  *simple_initialize_tagging_tree_node(struct tagging_tree_node_struct *new_node) {
    //struct tagging_tree_node_struct *dummy;
    //dummy = malloc(sizeof(struct tagging_tree_node_struct));
    //new_node = dummy;
    
    //new_node->element = (struct tagging_tree_node_struct *) malloc(sizeof(struct tagging_tree_node_struct));
    //new_node = (struct tagging_tree_node_struct *) malloc(sizeof(struct tagging_tree_node_struct));
    new_node = make_tagging_tree_node();
    
    if (new_node == NULL) printf("ERROR, Union tagging system could not allocate memory\n");
    new_node->intensity = 4.2; // (double) 4.2;
    new_node->number_of_rays = 42; //(int) 42;
    
    printf("new_node->intensity = %f, new_node->number_of_rays = %d \n",new_node->intensity,new_node->number_of_rays);
    return new_node;
};


struct tagging_tree_node_struct *initialize_tagging_tree_node(struct tagging_tree_node_struct *new_node, struct tagging_tree_node_struct *above_node, struct Volume_struct *this_volume) {
    new_node = make_tagging_tree_node();
    
    new_node->intensity = (double) 0;
    new_node->number_of_rays = (int) 0;
    new_node->above = above_node;
    
    int next_volume_list_length = this_volume->geometry.next_volume_list.num_elements;
    new_node->volume_branches = malloc(next_volume_list_length*sizeof(struct tagging_tree_node_struct*));
    int iterate;
    // Initializing pointers so that they can be checked for NULL later. Is this redundant? Does malloc return null pointers?
    for (iterate=0;iterate<next_volume_list_length;iterate++) new_node->volume_branches[iterate] = NULL;
    //new_node->volume_branches.num_elements = dest_list_length; // May be removed
    
    int number_of_processes;
    if (this_volume->p_physics == NULL) number_of_processes = 0;
    else number_of_processes = this_volume->p_physics->number_of_processes;
    
    new_node->process_branches = malloc(number_of_processes*sizeof(struct tagging_tree_node_struct*));
    // Initializing pointers so that they can be checked for NULL later. Is this redundant? Does malloc return null pointers?
    for (iterate=0;iterate<number_of_processes;iterate++) new_node->process_branches[iterate] = NULL;
    //new_node->process_branches.num_elements=number_of_processes; // May be removed
    
    return new_node;
};

struct tagging_tree_node_struct *goto_process_node(struct tagging_tree_node_struct *current_node, int process_index, struct Volume_struct *this_volume, int *stop_tagging_ray, int stop_creating_nodes) {
    // Either create a new node if it has not been created yet, or travel down the tree
    if (current_node->process_branches[process_index] == NULL) {
      if (stop_creating_nodes == 0) {
        current_node->process_branches[process_index] = initialize_tagging_tree_node(current_node->process_branches[process_index],current_node,this_volume);
        return current_node->process_branches[process_index];
      } else {
        // This stops the ray from using more goto node functions and being counted in the statistics.
        // Happens because the history limit is reached, and no new histories should be started.
        *stop_tagging_ray = 1;
        return current_node;
      }
    } else {
        current_node = current_node->process_branches[process_index];
        return current_node;
    }
    
    //return current_node;
};

struct tagging_tree_node_struct *goto_volume_node(struct tagging_tree_node_struct *current_node,int current_volume, int next_volume, struct Volume_struct **Volumes, int *stop_tagging_ray, int stop_creating_nodes) {
    // I have only allocated the number of node branches that corresponds to the current_volumes next_volume_list
    // The problem is to find where on the destination list the current volume is without doing a manual search
    
    // With the new mask system there is a risk of going from and to the same volume, which should be ignored by the tagging system
    if (current_volume == next_volume) return current_node;
    
    struct tagging_tree_node_struct *output;
    int next_volume_list_index = -1;
    // Temporary slow method for finding the correct index on the destination list
    int iterate;
    
    for (iterate=0;iterate<Volumes[current_volume]->geometry.next_volume_list.num_elements;iterate++)
        if (Volumes[current_volume]->geometry.next_volume_list.elements[iterate] == next_volume) {
            next_volume_list_index = iterate;
            break;
        }
    
    // Debug phase
    //printf("Tagging: going from volume %d to volume %d, which is index number %d on it's next volume list \n",current_volume,next_volume,next_volume_list_index);
    #ifndef OPENACC
    if (next_volume_list_index == -1) {
        printf("ERROR in Union component, tagging or destination system failed, next volume was not on next volume list\n");
        printf("current_volume = %d, next_volume = %d \n",current_volume,next_volume);
        print_1d_int_list(Volumes[current_volume]->geometry.destinations_list,"destinations_list for current volume");
        exit(1);
    }
    #endif

    // Either create a new node if it has not been created yet, or travel down the tree
    if (current_node->volume_branches[next_volume_list_index] == NULL) {
      if (stop_creating_nodes == 0) {
        current_node->volume_branches[next_volume_list_index] =  initialize_tagging_tree_node(current_node->volume_branches[next_volume_list_index],current_node,Volumes[next_volume]);
        return current_node->volume_branches[next_volume_list_index];
      } else {
        // This stops the ray from using more goto node functions and being counted in the statistics.
        // Happens because the history limit is reached, and no new histories should be started.
        *stop_tagging_ray = 1;
        return current_node;
      }
    } else {
        //current_node = current_node->volume_branches[next_volume_list_index];
        //return current_node;
        current_node = current_node->volume_branches[next_volume_list_index];
        //printf("used allocated node \n");
        return current_node;
    }
};

void add_statistics_to_node(struct tagging_tree_node_struct *current_node, Coords *r, Coords *v, double *weight, int *counter) {
    if (current_node->number_of_rays == 0) (*counter)++;
    current_node->number_of_rays = current_node->number_of_rays + 1;
    current_node->intensity = current_node->intensity + *weight;
};

struct history_node_struct {
    int volume_index;
    int process_index;
};

struct dynamic_history_list {
  struct history_node_struct *elements;
  int used_elements;
  int allocated_elements;
};

struct saved_history_struct {
    struct history_node_struct *elements;
    int used_elements;
    double intensity;
    int number_of_rays;
};

struct total_history_struct {
    struct saved_history_struct *saved_histories;
    int used_elements;
    int allocated_elements;
};


void add_to_history(struct dynamic_history_list *history, int volume_index, int process_index) {
    //printf("Adding to history[%d]: volume_index = %d, process_index = %d \n",history->used_elements,volume_index,process_index);
    if (history->allocated_elements == 0) {
        history->elements = malloc(5*sizeof(struct history_node_struct));
        history->used_elements = 1;
        history->allocated_elements = 5;
        history->elements[0].volume_index = volume_index;
        history->elements[0].process_index = process_index;
    } else if (history->used_elements > history->allocated_elements-1) {
        struct dynamic_history_list temp_history;
        temp_history.elements = malloc((history->used_elements)*sizeof(struct history_node_struct));
        int iterate;
        for (iterate=0;iterate<history->used_elements;iterate++) {
            temp_history.elements[iterate].volume_index  = history->elements[iterate].volume_index;
            temp_history.elements[iterate].process_index = history->elements[iterate].process_index;
        }
        free(history->elements);
        history->elements = malloc((history->allocated_elements+5)*sizeof(struct history_node_struct));
        for (iterate=0;iterate<history->used_elements;iterate++) {
            history->elements[iterate].volume_index  = temp_history.elements[iterate].volume_index;
            history->elements[iterate].process_index = temp_history.elements[iterate].process_index;
        }
        
        history->allocated_elements = history->allocated_elements+5;
        
        history->elements[history->used_elements].volume_index = volume_index;
        history->elements[history->used_elements].process_index = process_index;
        history->used_elements = history->used_elements+1;
        
    } else {
        history->elements[history->used_elements].volume_index = volume_index;
        history->elements[history->used_elements].process_index = process_index;
        history->used_elements++;
    }
    
};

void printf_history(struct dynamic_history_list *history) {
    int history_iterate;
          //printf("History number %d, intensity = %f, number of rays = %d:",hist_num, search_node->intensity, search_node->number_of_rays);
          for (history_iterate=0;history_iterate<history->used_elements-1;history_iterate++) {
            if (history->elements[history_iterate].process_index == -1) {
                printf(" V%d ->",history->elements[history_iterate].volume_index);
            } else {
                printf(" P%d ->",history->elements[history_iterate].process_index);
            }
          }
          if (history->elements[history_iterate].process_index == -1) {
                printf(" V%d \n",history->elements[history_iterate].volume_index);
          } else {
                printf(" P%d \n",history->elements[history_iterate].process_index);
          }
}

void fprintf_total_history(struct saved_history_struct *history, FILE *fp) {
    fprintf(fp,"%d\t N I=%E \t", history->number_of_rays, history->intensity);
        int history_iterate;
          //printf("History number %d, intensity = %f, number of rays = %d:",hist_num, search_node->intensity, search_node->number_of_rays);
          for (history_iterate=0;history_iterate<history->used_elements-1;history_iterate++) {
            if (history->elements[history_iterate].process_index == -1) {
                fprintf(fp," V%d ->",history->elements[history_iterate].volume_index);
            } else {
                fprintf(fp," P%d ->",history->elements[history_iterate].process_index);
            }
          }
          if (history->elements[history_iterate].process_index == -1) {
                fprintf(fp," V%d \n",history->elements[history_iterate].volume_index);
          } else {
                fprintf(fp," P%d \n",history->elements[history_iterate].process_index);
          }
}

/*
int Sample_compare_doubles (const void *a, const void *b) {
  const double *da = (const double *) a;
  const double *db = (const double *) b;

  return (*da > *db) - (*da < *db);
}
*/


int Sample_compare_history_intensities (const struct saved_history_struct *a, const struct saved_history_struct *b) {
  const double *da = (const double *) &(a->intensity);
  const double *db = (const double *) &(b->intensity);

  return (*da < *db) - (*da > *db);
}

void write_tagging_tree(struct list_of_tagging_tree_node_pointers *master_list, struct Volume_struct **Volumes, int total_history_counter, int number_of_volumes) {
  // Start from top of tree, go to extremeties, take results and add to disk / database, free that node
  int volume_index,done,volume_iterate,process_iterate,current_volume,next_node_found,current_number_of_processes,history_iterate,hist_num;
  
  struct tagging_tree_node_struct *search_node;
  struct tagging_tree_node_struct **kill_candidate;
  struct dynamic_history_list history_data; // Allocate the history list struct
  struct dynamic_history_list *history;     // Use this pointer in the algorithm
  
  struct total_history_struct total_history;
  total_history.saved_histories = malloc(total_history_counter * sizeof(struct saved_history_struct));
  total_history.allocated_elements = total_history_counter;
  total_history.used_elements = 0;
  
  history = &history_data;
  
  history->used_elements = 0;
  history->allocated_elements = 0;
  
  hist_num = 0;
  for (volume_index=0;volume_index<master_list->num_elements;volume_index++) {
    
    search_node = master_list->elements[volume_index];
    
    if (volume_index != 0)
        current_number_of_processes = Volumes[volume_index]->p_physics->number_of_processes;
    else
        current_number_of_processes = 0;
    
    current_volume = volume_index;
    done = 0;
    
    history->used_elements = 0;
    
    add_to_history(history,current_volume,-1);
    while(done == 0) {
        next_node_found=0;
        for (volume_iterate=0;volume_iterate<Volumes[current_volume]->geometry.next_volume_list.num_elements;volume_iterate++) {
            //printf("searc_node->volume_branches[0]->intensity = %f\n",search_node->volume_branches[0]->intensity);
            if (search_node->volume_branches[volume_iterate] != NULL) {
                current_volume = Volumes[current_volume]->geometry.next_volume_list.elements[volume_iterate];
                if (current_volume != 0)
                    current_number_of_processes = Volumes[current_volume]->p_physics->number_of_processes;
                else
                    current_number_of_processes = 0;
                
                //search_node = &(search_node->volume_branches[volume_iterate]);
                kill_candidate = &(search_node->volume_branches[volume_iterate]);
                search_node = search_node->volume_branches[volume_iterate];
                next_node_found = 1;
                add_to_history(history,current_volume,-1);
                //printf_history(history);
                break;
            }
        }
        if (next_node_found == 0) {
          //printf("doing process loop with %d steps \n",current_number_of_processes);
          for (process_iterate=0;process_iterate<current_number_of_processes;process_iterate++) {
            //if (&(search_node->process_branches[volume_iterate]) != NULL) {
            if (search_node->process_branches[process_iterate] != NULL) {
                //printf("was not NULL (process)\n");
                //search_node = &(search_node->process_branches[process_iterate]);
                kill_candidate = &(search_node->process_branches[process_iterate]);
                search_node = search_node->process_branches[process_iterate];
                next_node_found = 1;
                add_to_history(history,current_volume,process_iterate);
                //printf_history(history);
                break;
            }
          }
        }
        
        if (next_node_found == 0) {
          // write this history to disk / memory
          hist_num++;
          //printf("Reached next_node_found == 0 \n");
          if (history->used_elements > 0 && search_node->number_of_rays > 0) {
            //printf("%d rays (I=%E) with history: \t", search_node->number_of_rays, search_node->intensity);
            //printf_history(history);
            
            total_history.saved_histories[total_history.used_elements].used_elements = history->used_elements;
            total_history.saved_histories[total_history.used_elements].elements = malloc(total_history.saved_histories[total_history.used_elements].used_elements*sizeof(struct history_node_struct));
            for (history_iterate = 0;history_iterate<history->used_elements;history_iterate++) {
                total_history.saved_histories[total_history.used_elements].elements[history_iterate] = history->elements[history_iterate];
                //printf("total_history.saved_histories[total_history.used_elements].elements[%d].volume_index \n",history_iterate,total_history.saved_histories[total_history.used_elements].elements[history_iterate].volume_index);
            }
            //total_history.saved_histories[total_history.used_elements].elements = history->elements;
            total_history.saved_histories[total_history.used_elements].intensity = search_node->intensity;
            total_history.saved_histories[total_history.used_elements].number_of_rays = search_node->number_of_rays;
            total_history.used_elements++;
          }
          
          history->used_elements = 0;
          
          // end of tree, no new nodes
          if (search_node->above == NULL) {
            done = 1;
          } else {
            // reset to the root of the tree
            *kill_candidate = NULL;
            free(search_node);
            search_node = master_list->elements[volume_index];
            
            if (volume_index != 0)
              current_number_of_processes = Volumes[volume_index]->p_physics->number_of_processes;
            else
              current_number_of_processes = 0;
    
            current_volume = volume_index;
            add_to_history(history,current_volume,-1);
          }
          
        }
    }
  }
  
  if (history->allocated_elements > 0) free(history->elements);

  qsort(total_history.saved_histories,total_history.used_elements,sizeof (struct saved_history_struct), Sample_compare_history_intensities);
  
  
  
  MPI_MASTER(
  printf("\n\n");
  printf("Top 20 most common histories. Shows the index of volumes entered (VX), and the scattering processes (PX)\n");
  for (history_iterate=0;history_iterate<total_history.used_elements && history_iterate<20;history_iterate++) {
    printf("%d\t N I=%E \t", total_history.saved_histories[history_iterate].number_of_rays, total_history.saved_histories[history_iterate].intensity);
    printf_history(&total_history.saved_histories[history_iterate]);
  }
   
  FILE *fp;
  fp = fopen("union_history.dat","w");
  
  fprintf(fp,"History file written by the McStas component Union_master \n");
  fprintf(fp,"When running with MPI, the results may be from just a single thread, meaning intensities are divided by number of threads\n");
  fprintf(fp,"----- Description of the used volumes -----------------------------------------------------------------------------------\n");
  
  fprintf(fp,"V0: Surrounding vacuum \n");
  for (volume_iterate=1;volume_iterate<number_of_volumes;volume_iterate++) {
    fprintf(fp,"V%d: %s  ",volume_iterate,Volumes[volume_iterate]->name);
    fprintf(fp,"Material: %s  ",Volumes[volume_iterate]->p_physics->name);
    for (process_iterate=0;process_iterate<Volumes[volume_iterate]->p_physics->number_of_processes;process_iterate++) {
      fprintf(fp," P%d: %s",process_iterate,Volumes[volume_iterate]->p_physics->p_scattering_array[process_iterate].name);
    }
    fprintf(fp,"\n");
  }
  fprintf(fp,"----- Histories sorted after intensity ----------------------------------------------------------------------------------\n");
  
  for (history_iterate=0;history_iterate<total_history.used_elements;history_iterate++) {
    fprintf_total_history(&total_history.saved_histories[history_iterate],fp);
    // Garbage collection
    if (total_history.saved_histories[history_iterate].used_elements > 0) free(total_history.saved_histories[history_iterate].elements);
  }
  fclose(fp);
  
  )
  
  // Garbage collection
  if (total_history.allocated_elements > 0) free(total_history.saved_histories);
  
  
};


// -------------    Intersection table functions   --------------------------------------------------------
int clear_intersection_table(struct intersection_time_table_struct *intersection_time_table) {
    // Resets the intersection table when a scattering have occured.
    int iterate_volumes,iterate_solutions;
    
    // Start at one because vacuum (0) does not have a listing in the intersection table
    for (iterate_volumes = 1;iterate_volumes < intersection_time_table->num_volumes;iterate_volumes++) {
        intersection_time_table->calculated[iterate_volumes] = 0;
        // This second loop is added for safty in debugging phase, but can be removed as the information should never be accesed when calculated = 0
        for (iterate_solutions = 0;iterate_solutions < intersection_time_table->n_elements[iterate_volumes];iterate_solutions++) {
            intersection_time_table->intersection_times[iterate_volumes][iterate_solutions] = -1;
        }
    }
    
    return 1;
};

void print_intersection_table(struct intersection_time_table_struct *intersection_time_table) {
    int num_volumes,iterate,solutions;
    int max_number_of_solutions = 0;
    
    num_volumes = intersection_time_table->num_volumes;
    
    for (iterate = 0;iterate < num_volumes;iterate++) {
        if (max_number_of_solutions < intersection_time_table->n_elements[iterate])
            max_number_of_solutions = intersection_time_table->n_elements[iterate];
    }
    
    printf("------------------ INTERSECTION_TIME_TABLE -----------------");
    for (solutions = 2;solutions < max_number_of_solutions;solutions++) printf("------------");
    printf("\n");
    
    // printf("iterate      |");

    printf("           ");
    printf("| CALCULATED  |");
    for (solutions = 0;solutions < max_number_of_solutions;solutions++) {
        printf(" - SOLUTION %d - |",solutions);
    }

    printf("\n");
    for (iterate = 0;iterate < num_volumes;iterate++){
    // print iterate number
    printf("Volume %d   |",iterate);
    printf(" ---- %d ---- |",intersection_time_table->calculated[iterate]);
    
        for (solutions = 0;solutions < max_number_of_solutions;solutions++) {
          if (intersection_time_table->n_elements[iterate] > solutions && intersection_time_table->calculated[iterate] == 1)
            if (intersection_time_table->intersection_times[iterate][solutions] > 0)
             printf("   %1.8f   |",intersection_time_table->intersection_times[iterate][solutions]);
            else
             printf("   %1.7f   |",intersection_time_table->intersection_times[iterate][solutions]);
          else
            printf("                |");
        }
    printf("\n");
    }
    printf("------------------------------------------------------------");
    for (solutions = 2;solutions < max_number_of_solutions;solutions++) printf("------------");
    printf("\n");
    
    
    };
    
// -------------    Drawing functions   --------------------------------------------------------
void merge_lines_to_draw(struct lines_to_draw *lines_master,struct lines_to_draw *lines_new) {
    if (lines_master->number_of_lines == 0) {
    lines_master->number_of_lines = lines_new->number_of_lines;
    lines_master->lines = malloc(lines_master->number_of_lines*sizeof(struct line_segment));
    lines_master->lines = lines_new->lines;
    // One could free lines_new->lines;
    } else {
    int iterate;
    struct line_segment *temp_lines;
    temp_lines = malloc(lines_master->number_of_lines*sizeof(struct line_segment));
    for (iterate = 0;iterate < lines_master->number_of_lines;iterate++) temp_lines[iterate] = lines_master->lines[iterate];
    free(lines_master->lines);
    lines_master->lines = malloc((lines_master->number_of_lines+lines_new->number_of_lines)*sizeof(struct line_segment));
    for (iterate = 0;iterate < lines_master->number_of_lines;iterate++) lines_master->lines[iterate] = temp_lines[iterate];
    for (iterate = 0;iterate < lines_new->number_of_lines;iterate++) lines_master->lines[iterate+lines_master->number_of_lines] = lines_new->lines[iterate];
    lines_master->number_of_lines = lines_master->number_of_lines + lines_new->number_of_lines;
    free(temp_lines);
    }
    };

int r_has_highest_priority(Coords point,int N,struct geometry_struct **Geometries,int number_of_volumes) {
    // Function that test if a point in Volume N has the highest priority.
    // Returns 0 if another volume has higher priority at the point, and it's mask status is 1
    // Returns 1 if no other volume has higher priority at the point
    // (not active) Returns a larger integer if the volume N is a mask, and the point is not in the volume it is masking,
    //  which results in the drawing function making that number of dashes
    
    
    int mask_status,*mask_start,*mask_check;
    // If the volume is a mask, it does not have a priority, and is always on top
    if (Geometries[N]->is_mask_volume == 1) {
      return 1;
      // Can draw parts of the mask that is outside the volume it masks as dashed lines, but it just looks messy
      /*
      mask_status = 1;
      for (mask_check=mask_start=Geometries[N]->mask_list.elements;mask_check-mask_start<Geometries[N]->mask_list.num_elements;mask_check++) {
        if (Geometries[*mask_check]->within_function(point,Geometries[*mask_check]) == 1) {
          return 1; // If the point is within a volume the mask is masking, draw it as a solid line
        }
      }
      // As it was not inside any of the volumes the mask is masking, draw it as a dashed line
      return 5;
      */
    }
    
    // If the volume is a masked volume, check if the point is within it's masks
    if (Geometries[N]->is_masked_volume == 1) {
      if (Geometries[N]->mask_mode == 1) { // ALL mode, need to be within ALL masks
        for (mask_check=mask_start=Geometries[N]->masked_by_list.elements;mask_check-mask_start<Geometries[N]->masked_by_list.num_elements;mask_check++) {
          if (Geometries[*mask_check]->within_function(point,Geometries[*mask_check]) == 0) {
            return 0; // If the point is just outside one mask, the mask status is 0 and the point does not have highest priority
          }
        }
      } else { // ANY mode, need to be within at least one mask
        mask_status = 0;
        for (mask_check=mask_start=Geometries[N]->masked_by_list.elements;mask_check-mask_start<Geometries[N]->masked_by_list.num_elements;mask_check++) {
          if (Geometries[*mask_check]->within_function(point,Geometries[*mask_check]) == 1) {
            mask_status = 1;
            break;
          }
        }
        if (mask_status == 0) {
          return 0; // If it was not in a single of it's masks, the point did not have highest priority
        }
      }
    }
    
    int volume_index;
    double self_priority;
    self_priority = Geometries[N]->priority_value;
    
    
    for (volume_index = 1;volume_index<number_of_volumes;volume_index++) {
      if (volume_index != N && Geometries[volume_index]->is_mask_volume == 0) {
        if (Geometries[volume_index]->within_function(point,Geometries[volume_index])) {
          if (Geometries[volume_index]->is_masked_volume == 1) {
            // Since this volume is masked, the mask status need to be checked
            if (Geometries[volume_index]->mask_mode == 1) { //ALL mode, need to be within ALL masks
              mask_status = 1;
              for (mask_check=mask_start=Geometries[volume_index]->masked_by_list.elements;mask_check-mask_start<Geometries[volume_index]->masked_by_list.num_elements;mask_check++) {
                if (Geometries[*mask_check]->within_function(point,Geometries[*mask_check]) == 0) {
                  mask_status = 0;
                  break;
                }
              }
            } else { // ANY mode, need to be within at least one mask
              mask_status = 0;
              for (mask_check=mask_start=Geometries[volume_index]->masked_by_list.elements;mask_check-mask_start<Geometries[volume_index]->masked_by_list.num_elements;mask_check++) {
                if (Geometries[*mask_check]->within_function(point,Geometries[*mask_check]) == 1) {
                  mask_status = 1;
                  break;
                }
              }
            }
          } else mask_status = 1;
          if (Geometries[volume_index]->priority_value > self_priority && mask_status == 1) {
            // printf("Volume %d did not have highest priority at (%f,%f,%f) (Volume number %d was above)\n",N,point.x,point.y,point.z,volume_index);
            return 0;
          }
        }
      }
    }
    // printf("Volume %d did have highest priority at (%f,%f,%f) \n",N,point.x,point.y,point.z);
    return 1;
    }
    
void draw_line_positions(Coords point1,Coords point2) {
        //line(point1.x,point1.y,point1.z,point2.x,point2.y,point2.z);
        // sigh, can not use line in share. Need to save the information and pass to the mcdisplay part.
    }

int Sample_compare_doubles (const void *a, const void *b) {
  const double *da = (const double *) a;
  const double *db = (const double *) b;

  return (*da > *db) - (*da < *db);
}

struct lines_to_draw draw_line_with_highest_priority(Coords position1,Coords position2,int N,struct geometry_struct **Geometries,int number_of_volumes,int max_number_of_solutions) {
    
    int volume_index,iterate,permanent_list_length = 0;
    int number_of_solutions;
    double temp_intersection[max_number_of_solutions];
    double r1[3],r2[3],direction[3];
    struct pointer_to_1d_double_list intersection_list;
    
    intersection_list.num_elements = 0;
    
    // double *permanent_intersection_list,*storage; // old ways
    
    r1[0] = position1.x;
    r1[1] = position1.y;
    r1[2] = position1.z;
    r2[0] = position2.x;
    r2[1] = position2.y;
    r2[2] = position2.z;
    
    // printf("r1 = (%f,%f,%f) \n",r1[0],r1[1],r1[2]);
    // printf("r2 = (%f,%f,%f) \n",r2[0],r2[1],r2[2]);
    
    direction[0] = r2[0] - r1[0];
    direction[1] = r2[1] - r1[1];
    direction[2] = r2[2] - r1[2];
    int geometry_output;
    
    // Find intersections
    for (volume_index = 1;volume_index < number_of_volumes; volume_index++) {
        if (volume_index != N) {
            geometry_output = Geometries[volume_index]->intersect_function(temp_intersection,&number_of_solutions,r1,direction,Geometries[volume_index]);
             // printf("No solutions for intersection (Volume %d) with %d \n",N,volume_index);
                for (iterate=0;iterate<number_of_solutions;iterate++) {
                    // print_1d_double_list(intersection_list,"intersection_list");
                    if (temp_intersection[iterate] > 0 && temp_intersection[iterate] < 1) {
                        add_element_to_double_list(&intersection_list,temp_intersection[iterate]);
                        // printf("solution taken = %f\n",temp_intersection[iterate]);
                        }                         // printf("solution ignored = %f\n",temp_intersection[iterate]);
                    // print_1d_double_list(intersection_list,"intersection_list");
                
                }
                // printf("First (%d) solutions added to the solution stack, intersection with %d \n",number_of_solutions,volume_index);
                // printf(" Solutions: ");
                // for (iterate = 0;iterate < intersection_list.num_elements;iterate++) printf("%f ",intersection_list.elements[iterate]);
                // printf("\n");
            
        }
    }
    // Now we have a list of intersection distances between r1 and r2 and all volumes.
    // This list needs to be sorted before we continue!
    
    qsort(intersection_list.elements,intersection_list.num_elements,sizeof (double), Sample_compare_doubles);
    // print_1d_double_list(intersection_list,"after sorting");
    // printf(" Solutions (after sorting): ");
    // for (iterate = 0;iterate < intersection_list.num_elements;iterate++) printf("%f ",intersection_list.elements[iterate]);
    // printf("\n");
    
    Coords points[intersection_list.num_elements+2];
    points[0] = coords_set(r1[0],r1[1],r1[2]);
    points[intersection_list.num_elements+1] = coords_set(r2[0],r2[1],r2[2]);
    for (iterate = 1;iterate < intersection_list.num_elements+1;iterate++) {
        points[iterate].x = r1[0] + direction[0]*intersection_list.elements[iterate-1];
        points[iterate].y = r1[1] + direction[1]*intersection_list.elements[iterate-1];
        points[iterate].z = r1[2] + direction[2]*intersection_list.elements[iterate-1];
    }
    
    // printf("Points on the list:\n");
    // for (iterate = 0;iterate < intersection_list.num_elements + 2;iterate++) {
    // //         printf("(%f,%f,%f)\n",points[iterate].x,points[iterate].y,points[iterate].z);
    // }
    // printf("\n");
  
    struct line_segment lines[intersection_list.num_elements+1];
    int draw_logic[intersection_list.num_elements+1];
    //struct lines_to_draw draw_order;
    Coords midpoint;
    struct lines_to_draw draw_order;
    draw_order.number_of_lines = 0;
    
    // printf("test2 in function \n");
    int number_of_dashes;
    
    for (iterate = 0;iterate < intersection_list.num_elements + 1;iterate++) {
        lines[iterate].point1 = points[iterate];
        lines[iterate].point2 = points[iterate+1];
        midpoint.x = 0.5*(lines[iterate].point1.x + lines[iterate].point2.x);
        midpoint.y = 0.5*(lines[iterate].point1.y + lines[iterate].point2.y);
        midpoint.z = 0.5*(lines[iterate].point1.z + lines[iterate].point2.z);
        
        if ((number_of_dashes = r_has_highest_priority(midpoint,N,Geometries,number_of_volumes)) != 0) {
            draw_order.number_of_lines++;
            draw_logic[iterate] = number_of_dashes;
        } else draw_logic[iterate] = 0;
    }
   
    // printf("test3 in function \n");
    
    if (draw_order.number_of_lines > 0) {
        draw_order.lines = malloc(draw_order.number_of_lines*sizeof(struct line_segment));
        draw_order.number_of_lines = 0;
        for (iterate = 0;iterate < intersection_list.num_elements + 1;iterate++) {
            if (draw_logic[iterate] != 0) {
              lines[iterate].number_of_dashes = draw_logic[iterate];
              draw_order.lines[draw_order.number_of_lines++] = lines[iterate];
            }
        }
        if (intersection_list.num_elements > 0) free(intersection_list.elements);
    }

    // printf("function done \n");
    return draw_order;
    }

struct lines_to_draw draw_circle_with_highest_priority(Coords center,Coords vector,double radius,int N,struct geometry_struct **Geometries,int number_of_volumes,int max_number_of_solutions) {
    int number_of_positions = 100;

    // normalize vector
    double vector_length = length_of_position_vector(vector);
    // print_position(vector,"start vector");
    vector.x /= vector_length;
    vector.y /= vector_length;
    vector.z /= vector_length;
    // print_position(vector,"start vector normalized");
    
    // Create a vector from the center of the circle to a point on the circumference by cross product
    double cross_input[3] = {0,1,0};
    // In case the cross input is parallel with the vector, a new is chosen. Both can't be parallel.
    if (scalar_prod(cross_input[0],cross_input[1],cross_input[2],vector.x,vector.y,vector.z) > 0.99) {
        cross_input[0] = 1; cross_input[1] = 0; cross_input[2] = 0;
    }
    // print_position(make_position(cross_input),"cross input");
    
    double cross_product1[3] = {0,0,0};
    vec_prod(cross_product1[0],cross_product1[1],cross_product1[2],vector.x,vector.y,vector.z,cross_input[0],cross_input[1],cross_input[2]);
    
    // print_position(make_position(cross_product1),"cross_product1");
    
    double cross_length = length_of_3vector(cross_product1);
    
    // printf("cross_length = %f \n",cross_length);
    
    cross_product1[0] /= cross_length;
    cross_product1[1] /= cross_length;
    cross_product1[2] /= cross_length;
    
    cross_product1[0] *= radius;
    cross_product1[1] *= radius;
    cross_product1[2] *= radius;
    
    // printf("cross_product1 = (%f,%f,%f) \n",cross_product1[0],cross_product1[1],cross_product1[2]);
    
    int iterate;
    double rotate_angle;
    Coords radial_position,old_radial_position;
    struct lines_to_draw temp_draw_order,return_draw_order;
    return_draw_order.number_of_lines = 0;
    // Generate an array of positions by rotating this vector around the given vector, this corresponds to points on the circle
    old_radial_position.x = center.x + cross_product1[0];
    old_radial_position.y = center.y + cross_product1[1];
    old_radial_position.z = center.z + cross_product1[2];

    // printf("old_radial_position = (%f,%f,%f) \n",old_radial_position.x,old_radial_position.y,old_radial_position.z);
    
    for (iterate = 0;iterate < number_of_positions-1;iterate++) {
        rotate_angle = 2*3.14159*((double) iterate + 1.0)/((double) number_of_positions);
        rotate(radial_position.x,radial_position.y,radial_position.z,cross_product1[0],cross_product1[1],cross_product1[2],rotate_angle,vector.x,vector.y,vector.z);
        radial_position.x += center.x;
        radial_position.y += center.y;
        radial_position.z += center.z;
        
        // Use the draw_lines_with_highest_priority to draw get draw_orders for each line piece
        temp_draw_order = draw_line_with_highest_priority(radial_position,old_radial_position,N,Geometries,number_of_volumes,max_number_of_solutions);
       // Assemble these draw_orders to one large draw order
        merge_lines_to_draw(&return_draw_order,&temp_draw_order);

        old_radial_position.x = radial_position.x;
        old_radial_position.y = radial_position.y;
        old_radial_position.z = radial_position.z;
    }
    
    radial_position.x = center.x + cross_product1[0];
    radial_position.y = center.y + cross_product1[1];
    radial_position.z = center.z + cross_product1[2];
    // Use the draw_lines_with_highest_priority to draw get draw_orders for each line piece
    temp_draw_order = draw_line_with_highest_priority(radial_position,old_radial_position,N,Geometries,number_of_volumes,max_number_of_solutions);
    // Assemble these draw_orders to one large draw order
    merge_lines_to_draw(&return_draw_order,&temp_draw_order);
    
    // clean up
    if (temp_draw_order.number_of_lines > 0) free(temp_draw_order.lines);
    
    // return the large draw order.
    return return_draw_order;
    }

// -------------    Geometry functions   --------------------------------------------------------

#include "Geometry_functions.c"

// -------------    List generator functions   --------------------------------------------------


int within_which_volume(Coords pos, struct pointer_to_1d_int_list input_list, struct pointer_to_1d_int_list destinations_list, struct Volume_struct **Volumes, struct pointer_to_1d_int_list *mask_status_list, int number_of_volumes, int *volume_logic_copy, int *ListA, int *ListB) {
    // This function identifies in which of the volumes of the input list the position pos lies in.
    // pos: position for which the current volume should be found for
    // input list: list of potential volumes, reduced to the ones without parents (their children will be checked)
    // OLD VERSION: volume logic: A logic list of allowed volumes for lookup, volume 1 3 and 5 in a case of 10 volumes would be [0 1 0 1 0 1 0 0 0 0]
    // destinations_list: list of allowed destinations (the original destinations list)
    // Volumes: Main volumes array
    // volume_logic_copy: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    // ListA: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    // ListB: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    
    // Algorithm description
    // Check all of input list for pos being within them, those that have it within them are:
    //      checked against the current highest priority
    //          if higher, is the new highest priority and the new pick for volume
    //      have all their direct children added to the next list to be checked (but any volume can only be added to that list once)
    // Once a run have been made where the next list to check is empty, the answer for new pick for volume is taken.
    
    // Mask update:
    // Algorithm description
    // Check all of input list for pos being within them, those that have it within them are:
    //      checked against the current highest priority and mask status
    //          if higher, this is the new highest priority and the new pick for volume
    //      have all their direct children added to the next list to be checked (but any volume can only be added to that list once)
    // Once a run have been made where the next list to check is empty, the answer for new pick for volume is taken.
    
    // The advantage of the method is that potentially large numbers of children are skipped when their parents do not contain the position.
    // The overhead cost is low, as all the lists are prealocated.
    // Should be checked which of the two implementations is faster, as this is much more complicated than simply checking all possibilities.
    // No within_function call should be made twice, as the same volume number will not be checked twice because of the properties of the direct_children list and the volume_logic that removes duplicates on each level.
    
    
    // This function uses too much memory, the memory required for the volume logic list is n_volumes^2 ints, or for a MACS monochromator 127000 ints.
    // Instead the original destinations list must be used, and a function for quick lookup in a (sorted) destinations list made.
    // Still need a list of n_volumes length for control to avoid adding the same volume to the list twice.
    
    
    int ListA_length=0,ListB_length=0;
    int done = 0;
    int i,direct_children_index;
    int *temp_pointer;
    double max_priority=-1000000;
    int residing_volume=0; // 0 can be removed from the input list if default is 0
    int this_mask_status,mask_index,mask_global_index;
    
    // volume_logic_copy
    //for (i=0;i<volume_logic.num_elements;i++) volume_logic_copy[i] = volume_logic.elements[i];
    
    // low memory version of volume_logic_copy
    for (i=0;i<number_of_volumes;i++) volume_logic_copy[i] = 0;
    for (i=0;i<destinations_list.num_elements;i++) volume_logic_copy[destinations_list.elements[i]] = 1;
    printf("within_which_volume debug %i\n",input_list.num_elements);

    // Does one loop through the algorithm first to set up ListA instead of copying it from input_list, which takes time
    for (i=0;i<input_list.num_elements;i++) {
            if (Volumes[input_list.elements[i]]->geometry.within_function(pos,&Volumes[input_list.elements[i]]->geometry) == 1) {
                printf("The position is inside of volume %d\n",input_list.elements[i]);
                if (Volumes[input_list.elements[i]]->geometry.is_masked_volume == 1) {
                    // if the volume is masked, I need to know if it can be a destination volume from the mask_status_list.
                    // if the masked volume is in ANY mode,
                    this_mask_status=1;
                    //print_1d_int_list(*mask_status_list,"mask status list from within_which_volume");
                    for (mask_index=0;mask_index<Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.num_elements;mask_index++) {
                        //printf("Looking at the mask with global index %d \n",Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.elements[mask_index]);
                        if (mask_status_list->elements[Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.elements[mask_index]] == 1) {
                            if (Volumes[input_list.elements[i]]->geometry.mask_mode == 2) { // ANY (break if any one in)
                                this_mask_status=1;
                                break;
                            }
                            //printf("global index %d had mask status = 1 \n",Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.elements[mask_index]);
                        } else {
                          //printf("global index %d had mask status = 0 \n",Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.elements[mask_index]);
                          this_mask_status = 0;
                          if(Volumes[input_list.elements[i]]->geometry.mask_mode == 1) break; // ALL (break if any one out)
                        }
                    }
                    //printf("This volume is masked, and the mask status is %d\n",this_mask_status);
                } else this_mask_status = 1; // if the volume is not masked
            
                if (Volumes[input_list.elements[i]]->geometry.priority_value > max_priority && this_mask_status == 1) {
                    max_priority = Volumes[input_list.elements[i]]->geometry.priority_value;
                    residing_volume = input_list.elements[i];
                    //printf("residing volume set to %d\n",residing_volume);
                }
                    for (direct_children_index = 0;direct_children_index < Volumes[input_list.elements[i]]->geometry.direct_children.num_elements;direct_children_index++) {
                        if (volume_logic_copy[Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index]] == 1) {
                            ListA[ListA_length++] = Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index];
                            volume_logic_copy[Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index]] = 0;
                        }
                    }
            }
    }
    //printf("Completed first loop, continued in while loop\n");
    if (ListA_length > 0) {
        while (done == 0) {
            for (i=0;i<ListA_length;i++) {
              //printf("checking element number %d of list A which is volume number %d \n",i,ListA[i]);
                if (Volumes[ListA[i]]->geometry.within_function(pos,&Volumes[ListA[i]]->geometry) == 1) {
                  //printf("ray was inside this volume \n");
                    if (Volumes[ListA[i]]->geometry.is_masked_volume == 1) {
                      //printf("it is a mask and thus need check of mask status \n");
                        // if the volume is masked, I need to know if it can be a destination volume from the mask_status_list.
                        // if the masked volume is in ANY mode,
                        this_mask_status=1;
                        for (mask_index=0;mask_index<Volumes[ListA[i]]->geometry.masked_by_mask_index_list.num_elements;mask_index++) {
                            if (mask_status_list->elements[Volumes[ListA[i]]->geometry.masked_by_mask_index_list.elements[mask_index]] == 1) {
                                if (Volumes[ListA[i]]->geometry.mask_mode == 2) { // ANY (break if any one in)
                                    this_mask_status=1;
                                    break;
                                }
                            } else {
                              this_mask_status = 0;
                              if(Volumes[ListA[i]]->geometry.mask_mode == 1) break; // ALL (break if any one out)
                            }
                        }
                    } else this_mask_status = 1;
                    //printf("the mask status is %d \n",this_mask_status);
                    if (Volumes[ListA[i]]->geometry.priority_value > max_priority && this_mask_status == 1) {
                        max_priority = Volumes[ListA[i]]->geometry.priority_value;
                        residing_volume = ListA[i];
                    }
                    //printf("Adding direct children to list B \n");
                        for (direct_children_index = 0;direct_children_index < Volumes[ListA[i]]->geometry.direct_children.num_elements;direct_children_index++) {
                            //printf("Checking direct_child number %d which is %d \n",direct_children_index,Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index]);
                            if (volume_logic_copy[Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index]] == 1) {
                                //printf("It's volume_logic was 1, and it is thus added to listB with index %d \n",ListB_length);
                                ListB[ListB_length++] = Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index];
                                volume_logic_copy[Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index]] = 0;
                            }
                        }
                    //printf("List B is now: ");
                    //for (direct_children_index=0;direct_children_index<ListB_length;direct_children_index++) printf("%d ",ListB[direct_children_index]);
                    //printf("\n");
                    
                    
                }
            }
            if (ListB_length==0) done = 1;
            else {
                
                for (i=0;i<ListB_length;i++) ListA[i] = ListB[i];
                ListA_length = ListB_length;
                ListB_length = 0;
                
                /*
                // Could do this with pointers instead to avoid this for loop (and needless copy)
                // This code block fails on the cluster in rare circumstances
                ListA = temp_pointer;
                ListA = ListB;
                ListB = temp_pointer;
                ListA_length=ListB_length;
                ListB_length=0;
                */
            }
        }
    }
    //printf("residing volume returned %d\n",residing_volume);
    return residing_volume;
};

int within_which_volume_GPU(Coords pos, struct pointer_to_1d_int_list input_list, struct pointer_to_1d_int_list destinations_list, struct Volume_struct **Volumes, struct pointer_to_1d_int_list *mask_status_list, int number_of_volumes, int *volume_logic_copy, int *ListA, int *ListB) {
    // This function identifies in which of the volumes of the input list the position pos lies in.
    // pos: position for which the current volume should be found for
    // input list: list of potential volumes, reduced to the ones without parents (their children will be checked)
    // OLD VERSION: volume logic: A logic list of allowed volumes for lookup, volume 1 3 and 5 in a case of 10 volumes would be [0 1 0 1 0 1 0 0 0 0]
    // destinations_list: list of allowed destinations (the original destinations list)
    // Volumes: Main volumes array
    // volume_logic_copy: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    // ListA: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    // ListB: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    
    // Algorithm description
    // Check all of input list for pos being within them, those that have it within them are:
    //      checked against the current highest priority
    //          if higher, is the new highest priority and the new pick for volume
    //      have all their direct children added to the next list to be checked (but any volume can only be added to that list once)
    // Once a run have been made where the next list to check is empty, the answer for new pick for volume is taken.
    
    // Mask update:
    // Algorithm description
    // Check all of input list for pos being within them, those that have it within them are:
    //      checked against the current highest priority and mask status
    //          if higher, this is the new highest priority and the new pick for volume
    //      have all their direct children added to the next list to be checked (but any volume can only be added to that list once)
    // Once a run have been made where the next list to check is empty, the answer for new pick for volume is taken.
    
    // The advantage of the method is that potentially large numbers of children are skipped when their parents do not contain the position.
    // The overhead cost is low, as all the lists are prealocated.
    // Should be checked which of the two implementations is faster, as this is much more complicated than simply checking all possibilities.
    // No within_function call should be made twice, as the same volume number will not be checked twice because of the properties of the direct_children list and the volume_logic that removes duplicates on each level.
    
    
    // This function uses too much memory, the memory required for the volume logic list is n_volumes^2 ints, or for a MACS monochromator 127000 ints.
    // Instead the original destinations list must be used, and a function for quick lookup in a (sorted) destinations list made.
    // Still need a list of n_volumes length for control to avoid adding the same volume to the list twice.
    
    
    int ListA_length=0,ListB_length=0;
    int done = 0;
    int i,direct_children_index;
    int *temp_pointer;
    double max_priority=-1000000;
    int residing_volume=0; // 0 can be removed from the input list if default is 0
    int this_mask_status,mask_index,mask_global_index;
    
    // volume_logic_copy
    //for (i=0;i<volume_logic.num_elements;i++) volume_logic_copy[i] = volume_logic.elements[i];
    
    // low memory version of volume_logic_copy
    for (i=0;i<number_of_volumes;i++) volume_logic_copy[i] = 0;
    for (i=0;i<destinations_list.num_elements;i++) volume_logic_copy[destinations_list.elements[i]] = 1;
    //printf("within_which_volume debug\n");

    // Does one loop through the algorithm first to set up ListA instead of copying it from input_list, which takes time
    for (i=0;i<input_list.num_elements;i++) {
            if (r_within_function(pos, &Volumes[input_list.elements[i]]->geometry) == 1) {
                //printf("The position is inside of volume %d\n",input_list.elements[i]);
                if (Volumes[input_list.elements[i]]->geometry.is_masked_volume == 1) {
                    // if the volume is masked, I need to know if it can be a destination volume from the mask_status_list.
                    // if the masked volume is in ANY mode,
                    this_mask_status=1;
                    //print_1d_int_list(*mask_status_list,"mask status list from within_which_volume");
                    for (mask_index=0;mask_index<Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.num_elements;mask_index++) {
                        //printf("Looking at the mask with global index %d \n",Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.elements[mask_index]);
                        if (mask_status_list->elements[Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.elements[mask_index]] == 1) {
                            if (Volumes[input_list.elements[i]]->geometry.mask_mode == 2) { // ANY (break if any one in)
                                this_mask_status=1;
                                break;
                            }
                            //printf("global index %d had mask status = 1 \n",Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.elements[mask_index]);
                        } else {
                          //printf("global index %d had mask status = 0 \n",Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.elements[mask_index]);
                          this_mask_status = 0;
                          if(Volumes[input_list.elements[i]]->geometry.mask_mode == 1) break; // ALL (break if any one out)
                        }
                    }
                    //printf("This volume is masked, and the mask status is %d\n",this_mask_status);
                } else this_mask_status = 1; // if the volume is not masked
            
                if (Volumes[input_list.elements[i]]->geometry.priority_value > max_priority && this_mask_status == 1) {
                    max_priority = Volumes[input_list.elements[i]]->geometry.priority_value;
                    residing_volume = input_list.elements[i];
                    //printf("residing volume set to %d\n",residing_volume);
                }
                    for (direct_children_index = 0;direct_children_index < Volumes[input_list.elements[i]]->geometry.direct_children.num_elements;direct_children_index++) {
                        if (volume_logic_copy[Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index]] == 1) {
                            ListA[ListA_length++] = Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index];
                            volume_logic_copy[Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index]] = 0;
                        }
                    }
            }
    }
    //printf("Completed first loop, continued in while loop\n");
    if (ListA_length > 0) {
        while (done == 0) {
            for (i=0;i<ListA_length;i++) {
              //printf("checking element number %d of list A which is volume number %d \n",i,ListA[i]);
                if (r_within_function(pos,&Volumes[ListA[i]]->geometry) == 1) {
                  //printf("ray was inside this volume \n");
                    if (Volumes[ListA[i]]->geometry.is_masked_volume == 1) {
                      //printf("it is a mask and thus need check of mask status \n");
                        // if the volume is masked, I need to know if it can be a destination volume from the mask_status_list.
                        // if the masked volume is in ANY mode,
                        this_mask_status=1;
                        for (mask_index=0;mask_index<Volumes[ListA[i]]->geometry.masked_by_mask_index_list.num_elements;mask_index++) {
                            if (mask_status_list->elements[Volumes[ListA[i]]->geometry.masked_by_mask_index_list.elements[mask_index]] == 1) {
                                if (Volumes[ListA[i]]->geometry.mask_mode == 2) { // ANY (break if any one in)
                                    this_mask_status=1;
                                    break;
                                }
                            } else {
                              this_mask_status = 0;
                              if(Volumes[ListA[i]]->geometry.mask_mode == 1) break; // ALL (break if any one out)
                            }
                        }
                    } else this_mask_status = 1;
                    //printf("the mask status is %d \n",this_mask_status);
                    if (Volumes[ListA[i]]->geometry.priority_value > max_priority && this_mask_status == 1) {
                        max_priority = Volumes[ListA[i]]->geometry.priority_value;
                        residing_volume = ListA[i];
                    }
                    //printf("Adding direct children to list B \n");
                        for (direct_children_index = 0;direct_children_index < Volumes[ListA[i]]->geometry.direct_children.num_elements;direct_children_index++) {
                            //printf("Checking direct_child number %d which is %d \n",direct_children_index,Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index]);
                            if (volume_logic_copy[Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index]] == 1) {
                                //printf("It's volume_logic was 1, and it is thus added to listB with index %d \n",ListB_length);
                                ListB[ListB_length++] = Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index];
                                volume_logic_copy[Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index]] = 0;
                            }
                        }
                    //printf("List B is now: ");
                    //for (direct_children_index=0;direct_children_index<ListB_length;direct_children_index++) printf("%d ",ListB[direct_children_index]);
                    //printf("\n");
                    
                    
                }
            }
            if (ListB_length==0) done = 1;
            else {
                
                for (i=0;i<ListB_length;i++) ListA[i] = ListB[i];
                ListA_length = ListB_length;
                ListB_length = 0;
                
                /*
                // Could do this with pointers instead to avoid this for loop (and needless copy)
                // This code block fails on the cluster in rare circumstances
                ListA = temp_pointer;
                ListA = ListB;
                ListB = temp_pointer;
                ListA_length=ListB_length;
                ListB_length=0;
                */
            }
        }
    }
    //printf("residing volume returned %d\n",residing_volume);
    return residing_volume;
};




int within_which_volume_debug(Coords pos, struct pointer_to_1d_int_list input_list, struct pointer_to_1d_int_list volume_logic, struct Volume_struct **Volumes, int *volume_logic_copy, int *ListA, int *ListB) {
    // This function identifies in which of the volumes of the input list the position pos lies in.
    // pos: position for which the current volume should be found for
    // input list: list of potential volumes, reduced to the ones without parents (their children will be checked)
    // volume logic: A logic list of allowed volumes for lookup, volume 1 3 and 5 in a case of 10 volumes would be [0 1 0 1 0 1 0 0 0 0]
    // Volumes: Main volumes array
    // volume_logic_copy: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    // ListA: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    // ListB: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    
    // Algorithm description
    // Check all of input list for pos being within them, those that have it within them are:
    //      checked against the current highest priority
    //          if higher, is the new highest priority and the new pick for volume
    //      have all their direct children added to the next list to be checked (but any volume can only be added to that list once)
    // Once a run have been made where the next list to check is empty, the answer for new pick for volume is taken.
    
    // The advantage of the method is that potentially large numbers of children are skipped when their parents do not contain the position.
    // The overhead cost is low, as all the lists are prealocated.
    // Should be checked which of the two implementations is faster, as this is much more complicated than simply checking all possibilities.
    // No within_function call should be made twice, as the same volume number will not be checked twice because of the properties of the direct_children list and the volume_logic that removes duplicates on each level.
    
    
    int ListA_length=0,ListB_length=0;
    int done = 0;
    int i,direct_children_index;
    int *temp_pointer;
    double max_priority=-1000000;
    int residing_volume=0; // 0 can be removed from the input list if default is 0
    
    // volume_logic_copy
    for (i=0;i<volume_logic.num_elements;i++) volume_logic_copy[i] = volume_logic.elements[i];
    
    // Does one loop through the algorithm first to set up ListA instead of copying it from input_list, which takes time
    for (i=0;i<input_list.num_elements;i++) {
            if (Volumes[input_list.elements[i]]->geometry.within_function(pos,&Volumes[input_list.elements[i]]->geometry) == 1) {
                if (Volumes[input_list.elements[i]]->geometry.priority_value > max_priority) {
                    max_priority = Volumes[input_list.elements[i]]->geometry.priority_value;
                    residing_volume = input_list.elements[i];
                }
                    for (direct_children_index = 0;direct_children_index < Volumes[input_list.elements[i]]->geometry.direct_children.num_elements;direct_children_index++) {
                        if (volume_logic_copy[Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index]] == 1) {
                            ListA[ListA_length++] = Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index];
                            volume_logic_copy[Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index]] = 0;
                        }
                    }
            }
    }
    if (ListA_length > 0) {
        while (done == 0) {
        
            printf("ListA = [");
            for (i=0;i<ListA_length;i++) {
                printf("%d,",ListA[i]);
            }
            printf("]\n");
            for (i=0;i<ListA_length;i++) {
                if (Volumes[ListA[i]]->geometry.within_function(pos,&Volumes[ListA[i]]->geometry) == 1) {
                    if (Volumes[ListA[i]]->geometry.priority_value > max_priority) {
                        max_priority = Volumes[ListA[i]]->geometry.priority_value;
                        residing_volume = ListA[i];
                    }
                    //if (ListA[i]!=0) {
                        for (direct_children_index = 0;direct_children_index < Volumes[ListA[i]]->geometry.direct_children.num_elements;direct_children_index++) {
                            if (volume_logic_copy[Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index]] == 1) {
                                ListB[ListB_length++] = Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index];
                                volume_logic_copy[Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index]] = 0;
                            }
                        }
                    //}
                }
            }
            if (ListB_length==0) done = 1;
            else {
                ListA = temp_pointer;
                ListA = ListB;
                ListB = temp_pointer;
                ListA_length=ListB_length;
                ListB_length=0;
            }
        }
    }
    printf("Volume number %d had the highest priority of checked volumes\n",residing_volume);
    return residing_volume;
};

int inside_function(struct Volume_struct *parent_volume, struct Volume_struct *child_volume) {
    // Function that calls the correct within function depending on the shapes of the two volumes
    if (strcmp("sphere",parent_volume->geometry.shape) == 0 && strcmp("sphere",child_volume->geometry.shape) == 0) {
        if (sphere_within_sphere(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("cylinder",parent_volume->geometry.shape) == 0 && strcmp("cylinder",child_volume->geometry.shape) == 0) {
        if (cylinder_within_cylinder(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("box",parent_volume->geometry.shape) == 0 && strcmp("box",child_volume->geometry.shape) == 0) {
        if (box_within_box(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("cone",parent_volume->geometry.shape) == 0 && strcmp("cone",child_volume->geometry.shape) == 0) {
        if (cone_within_cone(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("mesh",parent_volume->geometry.shape) == 0 && strcmp("mesh",child_volume->geometry.shape) == 0) {
        if (mesh_within_mesh(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("box",parent_volume->geometry.shape) == 0 && strcmp("cylinder",child_volume->geometry.shape) == 0) {
        if (cylinder_within_box(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("cylinder",parent_volume->geometry.shape) == 0 && strcmp("box",child_volume->geometry.shape) == 0) {
        if (box_within_cylinder(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("box",parent_volume->geometry.shape) == 0 && strcmp("sphere",child_volume->geometry.shape) == 0) {
        if (sphere_within_box(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("sphere",parent_volume->geometry.shape) == 0 && strcmp("box",child_volume->geometry.shape) == 0) {
        if (box_within_sphere(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("sphere",parent_volume->geometry.shape) == 0 && strcmp("cylinder",child_volume->geometry.shape) == 0) {
        if (cylinder_within_sphere(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("cylinder",parent_volume->geometry.shape) == 0 && strcmp("sphere",child_volume->geometry.shape) == 0) {
        if (sphere_within_cylinder(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("cone",parent_volume->geometry.shape) == 0 && strcmp("sphere",child_volume->geometry.shape) == 0) {
        if (sphere_within_cone(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("sphere",parent_volume->geometry.shape) == 0 && strcmp("cone",child_volume->geometry.shape) == 0) {
        if (cone_within_sphere(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("cone",parent_volume->geometry.shape) == 0 && strcmp("cylinder",child_volume->geometry.shape) == 0) {
        if (cylinder_within_cone(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("cylinder",parent_volume->geometry.shape) == 0 && strcmp("cone",child_volume->geometry.shape) == 0) {
        if (cone_within_cylinder(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("cone",parent_volume->geometry.shape) == 0 && strcmp("box",child_volume->geometry.shape) == 0) {
        if (box_within_cone(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("box",parent_volume->geometry.shape) == 0 && strcmp("cone",child_volume->geometry.shape) == 0) {
        if (cone_within_box(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else {
        #ifndef OPENACC
        printf("Need within function for type: ");
        printf("%s",parent_volume->geometry.shape);
        printf(" and type: ");
        printf("%s",child_volume->geometry.shape);
        printf(".\n");
        printf("It is not yet supported to mix mesh geometries with the basic shapes, but several mesh geometries are allowed.\n");
        exit(1);
	#endif
    }
    
    return 0;
};

void generate_children_lists(struct Volume_struct **Volumes, struct pointer_to_1d_int_list **true_children_lists, int number_of_volumes, int verbal) {
  // This function generates a list of children for each volume.
  // A volume m is a child of volume n, if the entire space ocupied by volume m is inside of the space ocupied by volume n
  // A volume m is a true child of volume n, if the entire space coupied by volume m after it's masks are applied is inside the volume ocupied by volume n after it's masks are applied
  
  
  MPI_MASTER(
  if (verbal) printf("\nGenerating children lists --------------------------- \n");
  )

  // Mask update: Creating a temporary list for each volume
  struct pointer_to_1d_int_list *temporary_children_lists;
  temporary_children_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list));

  // The surrounding vacuum, volume 0, done outside of for loop.
  temporary_children_lists[0].num_elements = number_of_volumes - 1;
  temporary_children_lists[0].elements = malloc(temporary_children_lists[0].num_elements*sizeof(int));
  
  int parent;
  for (parent=1;parent<number_of_volumes;parent++) {
      temporary_children_lists[0].elements[parent-1] = parent;
  }
  
  //if (verbal) printf("did temporary children list \n");
  //print_1d_int_list(temporary_children_lists[0],"temp children list [0]");
  
  // Hardcoding that every volume is a child of the surrounding vacuum
  Volumes[0]->geometry.children.num_elements = number_of_volumes-1;
  Volumes[0]->geometry.children.elements = malloc((number_of_volumes-1)*sizeof(int));
  true_children_lists[0] = malloc(sizeof(struct pointer_to_1d_int_list));
  true_children_lists[0]->num_elements = number_of_volumes - 1;
  true_children_lists[0]->elements = malloc((number_of_volumes-1)*sizeof(int));
  
  //if (verbal) printf("allocated true children lists \n");
  
  for (parent=1;parent<number_of_volumes;parent++) {
      Volumes[0]->geometry.children.elements[parent-1] = parent;
      true_children_lists[0]->elements[parent-1] = parent;
  }
  
  char string_output[128];
  MPI_MASTER(
  if (verbal) sprintf(string_output,"Children for Volume %d",0);
  if (verbal) print_1d_int_list(Volumes[0]->geometry.children,string_output);
  )
  
  
  // Generating the children lists for all other volumes using the appropriate geometry functions
  struct pointer_to_1d_int_list temp_list_local;
  temp_list_local.num_elements = number_of_volumes;
  temp_list_local.elements = malloc(number_of_volumes*sizeof(int));
  
  struct pointer_to_1d_int_list true_temp_list_local;
  true_temp_list_local.num_elements = number_of_volumes;
  true_temp_list_local.elements = malloc(number_of_volumes*sizeof(int));
  
  
  int child,used_elements,used_elements_true;
  for (parent=1;parent<number_of_volumes;parent++) {
      used_elements = 0;used_elements_true = 0;
      for (child=1;child<number_of_volumes;child++) {
        if (child != parent) {
          // Call inside_function that selects the proper within function for the two volumes
          if (1 == inside_function(Volumes[parent],Volumes[child])) {
            temp_list_local.elements[used_elements++] = child;
            true_temp_list_local.elements[used_elements_true++] = child;
          }
        } else true_temp_list_local.elements[used_elements_true++] = child; // Needed when children list takes mask into account
      }
      // Temp test
      allocate_list_from_temp(used_elements,temp_list_local,&Volumes[parent]->geometry.children);
      // Assing the children list to a temporary list as the masks have yet to be taken into account
      temporary_children_lists[parent].num_elements=0;
      allocate_list_from_temp(used_elements_true,true_temp_list_local,&temporary_children_lists[parent]);
      
      
      MPI_MASTER(
      if (verbal) sprintf(string_output,"Children for Volume %d (temporary_list)",parent);
      if (verbal) print_1d_int_list(temporary_children_lists[parent],string_output);
      )
      
      MPI_MASTER(
      if (verbal) sprintf(string_output,"Children for Volume %d (permanent_list)",parent);
      if (verbal) print_1d_int_list(Volumes[parent]->geometry.children,string_output);
      )
      
  }
  
  // mask update:
  // The logical expression: (child c parent AND child c parent_mask) OR (child_mask c parent AND child_mask c parent_mask)
  //  needs to be evaluated for each child / parent combination in order to take the masks of each into account
  
  int logic_var1,logic_var2,logic_var_ANY,logic_var_ALL;
  int mask_index,mask_index_child,mask_index_parent;
  int volume_C,volume_P;
  // Loop that takes masks into account
  for (parent=1;parent<number_of_volumes;parent++) {
    used_elements = 0;
    for (child=1;child<number_of_volumes;child++) {
     if (child != parent && 0 == on_int_list(Volumes[parent]->geometry.masked_by_list,child)) {
        // The children list for each volume does not need to contain the volume itself
        //  And a parent masked by it's child can not have that mask as a child
      
      // Here c means within in the sense of a set being part of another set
      // Logical expression to be evaluated: (child c parent AND child c parent_mask) OR (child_mask c parent AND child_mask c parent_mask)
      logic_var1 = on_int_list(temporary_children_lists[parent],child);
      if (logic_var1 == 1 && Volumes[parent]->geometry.is_masked_volume == 1) {
        // if the parent volume is masked, the child also need to be inclosed in these masks to fulfill this side of the logical expression
        logic_var_ANY = 0;
        for (mask_index=0;mask_index<Volumes[parent]->geometry.masked_by_list.num_elements;mask_index++) {
          if (0 == on_int_list(temporary_children_lists[Volumes[parent]->geometry.masked_by_list.elements[mask_index]],child)) {
            if (Volumes[parent]->geometry.mask_mode == 1) {
              logic_var1 = 0;
              break;
            }
          } else logic_var_ANY = 1;
        }
        if (Volumes[parent]->geometry.mask_mode == 2) logic_var1 = logic_var_ANY;
      }
      
      if (logic_var1 == 1) true_temp_list_local.elements[used_elements++] = child;
      else if (Volumes[child]->geometry.is_masked_volume == 1) {
        // If the first side of the logical expression is false, evalute the other side
        // The other side is only relevant if the child volume is masked, otherwise it is ignored
        //printf("Second side of logical expression \n");
        
        logic_var1 = 1; // Assume true
          
        // child_mask c parent
        logic_var_ALL = 0;
        for (mask_index=0;mask_index<Volumes[child]->geometry.masked_by_list.num_elements;mask_index++) {
          if (0 == on_int_list(temporary_children_lists[parent],Volumes[child]->geometry.masked_by_list.elements[mask_index])){
            if (Volumes[child]->geometry.mask_mode == 2) {
              logic_var1 = 0;
              break;
            }
          } else logic_var_ALL = 1;
        }
        if (Volumes[child]->geometry.mask_mode == 1) logic_var1 = logic_var_ALL;
        
        // This line allows the second part of the logical expression to be true in cases where the parent volume is not masked
        if (logic_var1 == 1 && Volumes[parent]->geometry.is_masked_volume == 0) true_temp_list_local.elements[used_elements++] = child;
        
        // There is no reason to check the other part (child_mask c parent_mask) if the first part was not true
        if (logic_var1 == 1 && Volumes[parent]->geometry.is_masked_volume == 1) {
          // This last part requires both the child and the parent to be masked
          // Need to evaluate (child_mask c parent_mask), where both can be a be a list of volume with ALL/ANY modes
          
          if (Volumes[parent]->geometry.mask_mode == 1) {
            logic_var2 = 1; // Assume the logical expression (child_mask c parent_mask) is true
            for (mask_index_parent=0;mask_index_parent<Volumes[parent]->geometry.masked_by_list.num_elements;mask_index_parent++) {
              // As the parent is in ALL mode, the child masks must be within ALL parent masks
              logic_var_ANY = 0; // Assume not a single child is within this parent
              for (mask_index_child=0;mask_index_child<Volumes[child]->geometry.masked_by_list.num_elements;mask_index_child++) {
                volume_P = Volumes[parent]->geometry.masked_by_list.elements[mask_index_parent];
                volume_C = Volumes[child]->geometry.masked_by_list.elements[mask_index_child];
                // Is volume C inside volume P? If yes, volume C must be on volume P's temporary children list
                if (0 == on_int_list(temporary_children_lists[volume_P],volume_C)) {
                  if (Volumes[child]->geometry.mask_mode == 2) {
                    // If child is in ANY mode, any one mask outside is enough to make the expression false
                    logic_var2 = 0;
                    break;
                  }
                } else logic_var_ANY = 1;
              }
              // If child is in ALL mode, then if any one child were within this mask, the logic expression holds true
              if (Volumes[child]->geometry.mask_mode == 1) logic_var2 = logic_var_ANY;
              if (logic_var2 == 0) break; // No need to continue
            }
          } else if (Volumes[parent]->geometry.mask_mode == 2) {
            // If the parent is in ANY mode, it is enough if the child masks are within just 1 of the parent masks
            for (mask_index_parent=0;mask_index_parent<Volumes[parent]->geometry.masked_by_list.num_elements;mask_index_parent++) {
              logic_var2 = 1; // Assume the logical expression (child_mask c parent_mask) is true
              logic_var_ANY = 0; // Assume not a single child is within this parent
              for (mask_index_child=0;mask_index_child<Volumes[child]->geometry.masked_by_list.num_elements;mask_index_child++) {
                volume_P = Volumes[parent]->geometry.masked_by_list.elements[mask_index_parent];
                volume_C = Volumes[child]->geometry.masked_by_list.elements[mask_index_child];
                // Is volume C inside volume P? If yes, volume C must be on volume P's temporary children list
                if (0 == on_int_list(temporary_children_lists[volume_P],volume_C)) {
                  if (Volumes[child]->geometry.mask_mode == 2) {
                    // If child is in ANY mode, any one mask outside is enough to make the expression false
                    logic_var2 = 0;
                    break;
                  }
                } else logic_var_ANY = 1;
              }
              // If child is in ALL mode, then if any one child were within this mask, the logic expression holds true
              if (Volumes[child]->geometry.mask_mode == 1) logic_var2 = logic_var_ANY;
              if (logic_var2 == 1) break; // No need to continue
            }
          }
          
          // if this point is reached, and logic_var2 is true, volume[child] is a child of volume[parent]
          if (logic_var2 == 1) true_temp_list_local.elements[used_elements++] = child;
          
        }
      }
     }
    }
    
    true_children_lists[parent] = malloc(sizeof(struct pointer_to_1d_int_list));
    true_children_lists[parent]->num_elements = 0;
    allocate_list_from_temp(used_elements,true_temp_list_local,true_children_lists[parent]);
    
    MPI_MASTER(
      if (verbal) sprintf(string_output,"True children for Volume (post mask) %d",parent);
      if (verbal) print_1d_int_list(*true_children_lists[parent],string_output);
    )
  }
  
  
  // Clean up of dynamically allocated memory
  
  for(child=0;child<number_of_volumes;child++) free(temporary_children_lists[child].elements);
  free(temporary_children_lists);
  free(true_temp_list_local.elements);
  free(temp_list_local.elements);
};

void generate_overlap_lists(struct pointer_to_1d_int_list **true_overlap_lists, struct pointer_to_1d_int_list **raw_overlap_lists, struct Volume_struct **Volumes, int number_of_volumes, int verbal) {
  // This function generates overlap lists for each volume
  // Volume m overlaps volume n if there is some subset of space they both ocupy (this is called raw overlap)
  // the true overlap list takes mask into account, meaning that the masks are applied before searching for overlap

  MPI_MASTER(
  if (verbal) printf("\nGenerating overlap lists ---------------------------- \n");
  )
  
  // Manually create the overlap list for the surrounding Vacuum, as it overlaps all other volumes
  
  // temporary_overlap_lists are used to save the calculated overlaps to avoid evaluating the heavy functions more than once (twice) for each possible volume pair
  struct pointer_to_1d_int_list *temporary_overlap_lists;
  temporary_overlap_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list));
  
  // Overlap_lists are the final result of the function, and for the surrounding volume it can be set immediatly
  true_overlap_lists[0] = malloc(sizeof(struct pointer_to_1d_int_list));
  true_overlap_lists[0]->num_elements = number_of_volumes-1;
  true_overlap_lists[0]->elements = malloc((number_of_volumes-1)*sizeof(int));
  
  raw_overlap_lists[0] =  malloc(sizeof(struct pointer_to_1d_int_list));
  raw_overlap_lists[0]->num_elements = number_of_volumes;
  raw_overlap_lists[0]->elements = malloc(number_of_volumes*sizeof(int));
  raw_overlap_lists[0]->elements[0] = 0; // Volume 0 overlaps itself
  
  int parent;
  for (parent=1;parent<number_of_volumes;parent++) {
      true_overlap_lists[0]->elements[parent-1] = parent;
      raw_overlap_lists[0]->elements[parent] = parent;
  }
  
  char string_output[128];
  MPI_MASTER(
  if (verbal) sprintf(string_output,"Overlaps for Volume %d",0);
  if (verbal) print_1d_int_list(*true_overlap_lists[0],string_output);
  )
  
  // Generate the overlap lists for the remaining volumes
  struct pointer_to_1d_int_list temp_list_local;
  temp_list_local.num_elements = number_of_volumes;
  temp_list_local.elements = malloc(number_of_volumes*sizeof(int));
  
  int child,used_elements;
  // Create overlap for the remaining volumes
  for (parent=1;parent<number_of_volumes;parent++) {
      true_overlap_lists[parent] = malloc(sizeof(struct pointer_to_1d_int_list));
      used_elements = 0;
      temp_list_local.elements[used_elements++] = 0; // Alwasy overlaps with the surrounding vacuum.
      
      for (child=1;child<number_of_volumes;child++) {
        if (child == parent) temp_list_local.elements[used_elements++] = child;
        else {
        if (strcmp("sphere",Volumes[parent]->geometry.shape) == 0 && strcmp("sphere",Volumes[child]->geometry.shape) == 0) {
            if (sphere_overlaps_sphere(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("cylinder",Volumes[parent]->geometry.shape) == 0 && strcmp("cylinder",Volumes[child]->geometry.shape) == 0) {
            if (cylinder_overlaps_cylinder(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("box",Volumes[parent]->geometry.shape) == 0 && strcmp("box",Volumes[child]->geometry.shape) == 0) {
            if (box_overlaps_box(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("cone",Volumes[parent]->geometry.shape) == 0 && strcmp("cone",Volumes[child]->geometry.shape) == 0) {
            if (cone_overlaps_cone(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("mesh",Volumes[parent]->geometry.shape) == 0 && strcmp("mesh",Volumes[child]->geometry.shape) == 0) {
            if (mesh_overlaps_mesh(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("box",Volumes[parent]->geometry.shape) == 0 && strcmp("cylinder",Volumes[child]->geometry.shape) == 0) {
            if (box_overlaps_cylinder(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("cylinder",Volumes[parent]->geometry.shape) == 0 && strcmp("box",Volumes[child]->geometry.shape) == 0) {
            if (cylinder_overlaps_box(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("box",Volumes[parent]->geometry.shape) == 0 && strcmp("sphere",Volumes[child]->geometry.shape) == 0) {
            if (box_overlaps_sphere(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("sphere",Volumes[parent]->geometry.shape) == 0 && strcmp("box",Volumes[child]->geometry.shape) == 0) {
            if (sphere_overlaps_box(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("sphere",Volumes[parent]->geometry.shape) == 0 && strcmp("cylinder",Volumes[child]->geometry.shape) == 0) {
            if (sphere_overlaps_cylinder(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("cylinder",Volumes[parent]->geometry.shape) == 0 && strcmp("sphere",Volumes[child]->geometry.shape) == 0) {
            if (cylinder_overlaps_sphere(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("cone",Volumes[parent]->geometry.shape) == 0 && strcmp("sphere",Volumes[child]->geometry.shape) == 0) {
            if (cone_overlaps_sphere(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("sphere",Volumes[parent]->geometry.shape) == 0 && strcmp("cone",Volumes[child]->geometry.shape) == 0) {
            if (sphere_overlaps_cone(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("cone",Volumes[parent]->geometry.shape) == 0 && strcmp("cylinder",Volumes[child]->geometry.shape) == 0) {
            if (cone_overlaps_cylinder(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("cylinder",Volumes[parent]->geometry.shape) == 0 && strcmp("cone",Volumes[child]->geometry.shape) == 0) {
            if (cylinder_overlaps_cone(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("cone",Volumes[parent]->geometry.shape) == 0 && strcmp("box",Volumes[child]->geometry.shape) == 0) {
            if (cone_overlaps_box(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("box",Volumes[parent]->geometry.shape) == 0 && strcmp("cone",Volumes[child]->geometry.shape) == 0) {
            if (box_overlaps_cone(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else {
            printf("Need overlap function for type: ");
            printf("%s",Volumes[parent]->geometry.shape);
            printf(" and type: ");
            printf("%s",Volumes[child]->geometry.shape);
            printf(".\n");
            printf("It is not yet supported to mix mesh geometries with the basic shapes, but several mesh geometries are allowed.\n");
            exit(1);
        }
        }
      }
      //allocate_list_from_temp(used_elements,temp_list_local,overlap_lists[parent]);
      allocate_list_from_temp(used_elements,temp_list_local,&temporary_overlap_lists[parent]);
      
      // Save the raw overlap data to the raw_overlap_lists[parent] list
      raw_overlap_lists[parent] = malloc(sizeof(struct pointer_to_1d_int_list));
      raw_overlap_lists[parent]->num_elements = 0;
      allocate_list_from_temp(used_elements,temp_list_local,raw_overlap_lists[parent]);
      
      if (verbal) sprintf(string_output,"Overlaps for Volume (pre mask) %d",parent);
      MPI_MASTER(
      if (verbal) print_1d_int_list(temporary_overlap_lists[parent],string_output);
      )
  }
  
  
  // The temporary_overlap_lists gives the raw overlap data for all volume pairs
  // The next tasks is to take the masks into account, so that a volume is only said to overlap another if all of these statements are true:
  // The volumes overlap each other
  // The mask's of volume 1 overlap volume 2
  // The mask's of volume 2 overlap volume 1
  // The mask's of volume 1 overlap the masks of volume 2
  
  
  int logic_var;
  int overlap_ANY,overlap_ANY_p,overlap_ANY_c;
  int mask_index,mask_index_c,mask_index_p;
  int mask_volume_index,mask_volume_index_p,mask_volume_index_c;

  for (parent=1;parent<number_of_volumes;parent++) {
    used_elements = 0;
    temp_list_local.elements[used_elements++] = 0; // Alwasy overlaps with the surrounding vacuum.
    for (child=1;child<number_of_volumes;child++) {
      if (child != parent) {
        logic_var = 1; // Assume the volumes overlap, and search for evidence that they do not.
      
        // First check if the volumes overlap
        if (0 == on_int_list(temporary_overlap_lists[parent],child)) logic_var = 0;
        
      
        // Check if child overlap with parents masks
        if (logic_var == 1 && Volumes[parent]->geometry.is_masked_volume == 1) {
          overlap_ANY = 0;
          for (mask_index=0;mask_index<Volumes[parent]->geometry.masked_by_list.num_elements;mask_index++) {
            mask_volume_index = Volumes[parent]->geometry.masked_by_list.elements[mask_index];
            if (0 == on_int_list(temporary_overlap_lists[mask_volume_index],child)) {
              if (Volumes[parent]->geometry.mask_mode == 1) {
                logic_var = 0;
                break;
              }
            } else overlap_ANY = 1;
          }
          if (Volumes[parent]->geometry.mask_mode == 2) logic_var = overlap_ANY;
        }
      
        // Check if parent overlap with childs masks
        if (logic_var == 1 && Volumes[child]->geometry.is_masked_volume == 1) {
          overlap_ANY = 0;
          for (mask_index=0;mask_index<Volumes[child]->geometry.masked_by_list.num_elements;mask_index++) {
            mask_volume_index = Volumes[child]->geometry.masked_by_list.elements[mask_index];
            if (0 == on_int_list(temporary_overlap_lists[mask_volume_index],parent)) {
              if (Volumes[child]->geometry.mask_mode == 1) {
                logic_var = 0;
                break;
              }
            } else overlap_ANY = 1;
          }
          if (Volumes[child]->geometry.mask_mode == 2) logic_var = overlap_ANY;
        }
      
        // Check if parents masks overlap childrens masks
        if (logic_var == 1 && Volumes[parent]->geometry.is_masked_volume == 1 && Volumes[child]->geometry.is_masked_volume == 1) {
          overlap_ANY = 0;
          for (mask_index_p=0;mask_index_p<Volumes[parent]->geometry.masked_by_list.num_elements;mask_index_p++) {
            mask_volume_index_p = Volumes[parent]->geometry.masked_by_list.elements[mask_index_p];
            overlap_ANY_p = 1;
            overlap_ANY_c = 0;
            for (mask_index_c=0;mask_index_c<Volumes[child]->geometry.masked_by_list.num_elements;mask_index_c++) {
              mask_volume_index_c = Volumes[child]->geometry.masked_by_list.elements[mask_index_c];
              if (0 == on_int_list(temporary_overlap_lists[mask_volume_index_p],mask_volume_index_c)) {
                if (Volumes[parent]->geometry.mask_mode == 1 && Volumes[child]->geometry.mask_mode == 1) {
                  // If both are in ALL mode and just one combination of masks does not overlap, neither does the common set
                  logic_var = 0;
                  break;
                }
                if (Volumes[parent]->geometry.mask_mode == 2 && Volumes[child]->geometry.mask_mode == 1) {
                  // If the parent is in ANY mode, but the child is in ALL, any one child not overlapping this parent mask, stops the chance for this parent mask
                  overlap_ANY_p = 0;
                  break;
                }
                
              } else {
                // Here because mask_volume_index_p and mask_volume_index_c does overlap
                if (Volumes[parent]->geometry.mask_mode == 1 && Volumes[child]->geometry.mask_mode == 2) {
                  // If the parent is in ALL mode and the child is in ANY mode, stop if a single parent volume does not overlap any child
                  overlap_ANY_c = 1;
                }
                if (Volumes[parent]->geometry.mask_mode == 2 && Volumes[child]->geometry.mask_mode == 2) {
                  // If both parent and child are in any mode, any one overlap between the masks is sufficient
                  overlap_ANY = 1;
                  // Could actually just commit to the overlap list here, and stop all loops.
                }
              }
              
            }
            if (Volumes[parent]->geometry.mask_mode == 1 && Volumes[child]->geometry.mask_mode == 2) logic_var = overlap_ANY_c;
            if (Volumes[parent]->geometry.mask_mode == 2 && Volumes[child]->geometry.mask_mode == 1 && overlap_ANY_p == 1) {
              // When parent is in any mode, and child is in ALL mode, any parent mask that overlaps all children masks is enough to end the parent loop
              logic_var = 1;
              break; // Without this break, only the last parent will matter
            }
            // if (overlap_ANY == 1) break; would speed things up a bit, but only after testing has been started and then it will be repeated
          }
          if (Volumes[parent]->geometry.mask_mode == 2 && Volumes[child]->geometry.mask_mode == 2) logic_var = overlap_ANY;
          // If both volumes have the ANY mode, just one case of overlap is enough.
        }
        
        // If all of the 4 statements above evaluate to true, the two volumes parent and child do overlap and it is added to the list.
        if (logic_var == 1) temp_list_local.elements[used_elements++] = child;
        
      }
    }
    // Allocate the actual overlap list with the new temp_list_local
    allocate_list_from_temp(used_elements,temp_list_local,true_overlap_lists[parent]);
    if (verbal) sprintf(string_output,"Overlaps for Volume (post mask) %d",parent);
    MPI_MASTER(
    if (verbal) print_1d_int_list(*true_overlap_lists[parent],string_output);
    )
  }
  
  // Clean up of dynamically allocated memory
  for(child=1;child<number_of_volumes;child++) free(temporary_overlap_lists[child].elements);
  free(temporary_overlap_lists);
  free(temp_list_local.elements);
  
};

void add_to_mask_intersect_list(struct pointer_to_1d_int_list *mask_intersect_list, int given_volume_index) {
    // A bit simpler than before
    if (on_int_list(*mask_intersect_list,given_volume_index) == 0)
      add_element_to_int_list(mask_intersect_list,given_volume_index);
}


void generate_intersect_check_lists(struct pointer_to_1d_int_list **overlap_lists,struct Volume_struct **Volumes, int number_of_volumes, int verbal) {
  // Generate intersection list for each volume
  MPI_MASTER(
  if (verbal) printf("\nGenerating intersect check lists -------------------- \n");
  )
  // Take overlap list for volume n
  // Remove entries with p < p(n)
  // Remove entries with parents on the list
  
  // Mask update: Mask volumes does not have priorities, do not do step 2 for mask volumes
  // Mask update: Instead of step 3 (Remove entries with parents on the list), move these entries to a Mask intersect list if that parent is masking the entry, otherwise remove
  
  // 1) Mask update: Take overlap list for volume n
  // 2) Mask update: Remove entries with p < p(n), except mask volumes, that are moved to another list A
  // 3) Mask update: Entries with parents on the list are removed (if these parents are masked, only remove the entry if it is a child of their parents masks)
  // 4) Possible optimization: Entries with parents on the A list, are moved to the appropriate mask list for this volume
  // 5) Mask update: Entries on list A are added again, if they are not parents of volume n
  // 6) Mask update: Entries on the main list that are masked are moved to the appropriate mask list for this volume, if their mask is on list A
  
  // Justification of the top algorithm:
  // No need to check intersections with something that does not overlap this volume, so start with intersect list and remove from that.
  // Entries with p < p(n) are "beneath" this volume, and thus doesn't cut it, mask volumes are however always "above" and are treated with care (moved to list A)
  // Entries with parents still on the list is removed, as it is impossible to intersect with them without first going through the parent
  // Entries with parents on the A list can only be seen if the ray is in that particular mask (the parent on the A list), so it can be added to the appropriate mask list for this volume
  // Now all masks that cut the volume are transfered back to the main list, the only masks that overlap and does not cut are parents of this volume, and these are thus not added
  // The entries on the list that are masked are added to the appropriate mask lists of this volume, but only if their mask is on the A list, as only masks on list A can have mask status 1 which is required for this to be relevant (this was true automatically for the ones added to the appropriate lists in the optimization step).
  
  
  // The intersect check list for volume 0 is done manually
  // Declare list for holding logic data for each index in the overlap list of this volume
  struct pointer_to_1d_int_list logic_list;
  logic_list.num_elements = overlap_lists[0]->num_elements;
  logic_list.elements = malloc(logic_list.num_elements * sizeof(int));
  
  // Declare similar list for List A
  struct pointer_to_1d_int_list mask_logic_list;
  mask_logic_list.num_elements = overlap_lists[0]->num_elements;
  mask_logic_list.elements = malloc(mask_logic_list.num_elements * sizeof(int));
  
  
  int iterate;
  int overlap_index;
  
  /*
  // Thise code is an old broken version of intersection_check_list generation only for volume 0, but this case is now included in the loop.
  // Assume all volumes on overlap list should be on intersection check list, and remove the once that should not.
  for (iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 1;
  // Asuume no volumes should go on the mask_logic_list, but add the ones that do.
  for (iterate=0;iterate<mask_logic_list.num_elements;iterate++) mask_logic_list.elements[iterate] = 0;
  
  for (overlap_index = 0;overlap_index < overlap_lists[0]->num_elements;overlap_index++) {
    // No volumes to remove with lower priority, as surrounding vacuum have the lowest.
    
    // Check if this overlap_lists[0]->elements[overlap_index] is a child of another member of the overlap list
    for (iterate = 0;iterate < overlap_lists[0]->num_elements;iterate++) {
        if (iterate != overlap_index) {
            // We are now checking if Volumes[overlap_lists[0]->elements[iterate]]->geometry.children contains overlap_lists[overlap_index]
            if (on_int_list(Volumes[overlap_lists[0]->elements[iterate]]->geometry.children,overlap_lists[0]->elements[overlap_index])) logic_list.elements[overlap_index] = 0;
        }
    }
  }

  Volumes[0]->geometry.intersect_check_list.num_elements = sum_int_list(logic_list);
  Volumes[0]->geometry.intersect_check_list.elements = malloc(Volumes[0]->geometry.intersect_check_list.num_elements * sizeof(int));
  
  iterate = 0;
  for (overlap_index = 0;overlap_index < overlap_lists[0]->num_elements;overlap_index++) {
        if (logic_list.elements[overlap_index])   Volumes[0]->geometry.intersect_check_list.elements[iterate++] = overlap_lists[0]->elements[overlap_index];
  }
  if (logic_list.num_elements > 0) free(logic_list.elements);
  if (mask_logic_list.num_elements > 0) free(mask_logic_list.elements);
  
  MPI_MASTER(
  print_1d_int_list(Volumes[0]->geometry.intersect_check_list,"Intersect check list for Volume 0 (manual)");
  )
  */
  
  // The intersect check lists for the remaining volumes are generated
  int volume_index,mask_volume_number,mask_index;
  //for (volume_index = 1;volume_index < number_of_volumes;volume_index++) {
  for (volume_index = 0;volume_index < number_of_volumes;volume_index++) {
    
      // 1) Take overlap list for volume n
      logic_list.num_elements = overlap_lists[volume_index]->num_elements;
      logic_list.elements = malloc(logic_list.num_elements * sizeof(int));
      mask_logic_list.num_elements = overlap_lists[volume_index]->num_elements;
      mask_logic_list.elements = malloc(mask_logic_list.num_elements * sizeof(int));
      
      
      
      if (Volumes[volume_index]->geometry.is_mask_volume == 1) {
        // Masks do not have any entries on their intersect check list, as they are never the current volume
        for (iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 0;
        for (iterate=0;iterate<mask_logic_list.num_elements;iterate++) mask_logic_list.elements[iterate] = 0;
      } else {
        for (iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 1;
        for (iterate=0;iterate<mask_logic_list.num_elements;iterate++) mask_logic_list.elements[iterate] = 0;
      }
      
      //2) Remove entries with p < p(n), except mask volumes, that are moved to another list A
      for (overlap_index = 0;overlap_index < overlap_lists[volume_index]->num_elements;overlap_index++) {
        
        if (overlap_lists[volume_index]->elements[overlap_index] == 0) logic_list.elements[overlap_index] = 0; // The surrounding vacuum has lower priority (and is not a mask)
        else if (Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.is_mask_volume == 1) {
           logic_list.elements[overlap_index] = 0;      // Remove this volume from the main list
           mask_logic_list.elements[overlap_index] = 1; // Add mask volumes to seperat list
        }
        else if (volume_index != 0) { // Only relevant to remove elements because of priority if the volume_index is different from 0, meaning the surrounding vacuum skips this step
           if (Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.priority_value < Volumes[volume_index]->geometry.priority_value) {
           // If the investigated volume on the overlap_list have lower priority than the volume with volume_index, remove it
           logic_list.elements[overlap_index] = 0; // Remove this volume from the list
           }
        }
      }
      
      // 3) Entries with parents on the list are removed
      for (overlap_index = 0;overlap_index < overlap_lists[volume_index]->num_elements;overlap_index++) {
        // Check if this overlap_lists[0]->elements[overlap_index] is a child of another member of the overlap list
        for (iterate = 0;iterate < overlap_lists[volume_index]->num_elements;iterate++) {
             if (iterate != overlap_index) { // Only necessary if a volume determines that it has itself as child, but a nice safety.
                // We are now checking if Volumes[overlap_lists[volume_index]->elements[iterate]]->geometry.children contains overlap_lists[overlap_index]
                // The && part is needed because we do not remove children of elements that have allready been removed because of their priority
                
                // if (on_int_list(Volumes[overlap_lists[volume_index]->elements[iterate]]->geometry.children,overlap_lists[volume_index]->elements[overlap_index]) && logic_list.elements[overlap_lists[volume_index]->elements[iterate]] == 1) {logic_list.elements[overlap_index] = 0; bug fixed on 3/4 2016
                if (on_int_list(Volumes[overlap_lists[volume_index]->elements[iterate]]->geometry.children,overlap_lists[volume_index]->elements[overlap_index]) && logic_list.elements[iterate] == 1) logic_list.elements[overlap_index] = 0;
                
                /*
                Explanation with simpler notation
                
                Overlap list of volume i = O_i
                Element j of overlap list i = O_i(j)
                
                Children list of volume i = C_i
                Element j on children list i = C_i(j)
                
                Priority of volume i p(i)
                
                for i = volumes
                    for j in O_i(j) 
                        logic(j) = 1;
                        if p(i) > p(O_i(j)) logic(j) = 0; // Remove if lower priority than the currently checked
                        
                        for k in O_i(k)
                            if (O_i(j) is contained on the list C_k && logic(k) == 1) logic(j) = 0
                */
                
                // 4) Entries with parents on the A list, are moved to the appropriate mask list for this volume
                if (on_int_list(Volumes[overlap_lists[volume_index]->elements[iterate]]->geometry.children,overlap_lists[volume_index]->elements[overlap_index]) && mask_logic_list.elements[iterate] == 1) {
                   logic_list.elements[overlap_index] = 0; // Remove from main list (Not strictly needed as it will be removed already if it is on list A)
                   // Add overlap_lists[volume_index]->elements[overlap_index] to volumes mask intersect list for mask with index overlap_lists[volume_index]->elements[iterate]
                   //add_to_mask_intersect_lists(&Volumes[volume_index]->geometry.mask_intersect_lists,overlap_lists[volume_index]->elements[iterate],overlap_lists[volume_index]->elements[overlap_index]);
                   // Simpler method that just collects all the masked intersect parts on a 1d_int list instead of seperating them for each mask
                   add_to_mask_intersect_list(&Volumes[volume_index]->geometry.mask_intersect_list,overlap_lists[volume_index]->elements[overlap_index]);
                }
                
             }
        }
      }
      
      // 5) Entries on list A are added again, if they are not parents of volume n
      // Time to add mask volumes removed back to the intersect list, if they are not parents of the current volume, meaning the current volume should not be a child of the mask
      for (overlap_index = 0;overlap_index < overlap_lists[volume_index]->num_elements;overlap_index++) {
        if (mask_logic_list.elements[overlap_index] == 1) {
          mask_volume_number = overlap_lists[volume_index]->elements[overlap_index];
          if (on_int_list(Volumes[mask_volume_number]->geometry.children,volume_index) == 0) logic_list.elements[overlap_index] = 1; // Add it back to the list
        }
      }
      
      // 6) Entries on the main list that are masked are moved to the appropriate mask list for this volume, if their mask is on list A
      int mask_index,mask_mode,found_index,logic_ALL;
      for (overlap_index = 0;overlap_index < overlap_lists[volume_index]->num_elements;overlap_index++) {
        if (logic_list.elements[overlap_index] == 1 && Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.is_masked_volume == 1) {
          logic_list.elements[overlap_index] = 0; // If the volume is masked, remove it from the intersect check list
          // Could actually keep it on the intersect list if the volume's mask is a parent of the current volume, now it will just be added in all cases
          
          // When ALL setting is used, all masks should overlap the current volume, otherwise the user probably made a mistake, this should be checked in error checking
          // When ANY setting is used, at least one mask should overlap the current volume, otherwise the user probably made a mistake, this should be checked in error checking
          // mask_mode == 1 => ALL / mask_mode == 2 => ANY
          mask_mode = Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.mask_mode;
          
          if (mask_mode == 1) {
            logic_ALL = 1;
            for (iterate=0;iterate<Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.masked_by_list.num_elements;iterate++) {
              mask_index = Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.masked_by_list.elements[iterate];
              
              if (on_int_list(*overlap_lists[volume_index],mask_index) == 0) {
                // If any one of the volumes masks do not overlap with this volume, there is no reason check intersections with the volume regardless of the mask status's
                logic_ALL = 0;
                break;
              }
            }
          if (logic_ALL == 1) {
            // If all of it's masks are overlapping the current volume, add it to all relevant mask intersect lists of this volume
            for (iterate=0;iterate<Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.masked_by_list.num_elements;iterate++) {
              mask_index = Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.masked_by_list.elements[iterate];
              //add_to_mask_intersect_lists(&Volumes[volume_index]->geometry.mask_intersect_lists,mask_index,overlap_lists[volume_index]->elements[overlap_index]);
              // Adding the masked intersect list elements to another list
              add_to_mask_intersect_list(&Volumes[volume_index]->geometry.mask_intersect_list,overlap_lists[volume_index]->elements[overlap_index]);
            }
          }
            
            
          } else if (mask_mode == 2) {
          // When in ANY mode, the problem is easier, as not all masks have to overlap the volume, and we can add each one to the list
            for (iterate=0;iterate<Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.masked_by_list.num_elements;iterate++) {
              mask_index = Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.masked_by_list.elements[iterate];
            
              if (on_int_list(*overlap_lists[volume_index],mask_index) == 1) {
                //add_to_mask_intersect_lists(&Volumes[volume_index]->geometry.mask_intersect_lists,mask_index,overlap_lists[volume_index]->elements[overlap_index]);
                // Adding the masked intersect list elements to another list
                add_to_mask_intersect_list(&Volumes[volume_index]->geometry.mask_intersect_list,overlap_lists[volume_index]->elements[overlap_index]);
              }
            }
            
          }
        }
      }
      
      
      Volumes[volume_index]->geometry.intersect_check_list.num_elements = sum_int_list(logic_list);
      Volumes[volume_index]->geometry.intersect_check_list.elements = malloc(Volumes[volume_index]->geometry.intersect_check_list.num_elements * sizeof(int));
      
      iterate = 0;
      for (overlap_index = 0;overlap_index < overlap_lists[volume_index]->num_elements;overlap_index++) {
            if (logic_list.elements[overlap_index])   Volumes[volume_index]->geometry.intersect_check_list.elements[iterate++] = overlap_lists[volume_index]->elements[overlap_index];
      }
      free(logic_list.elements); // Need to be careful with names for variables to be freed, as they can collide with names in main because of automatic declaration of external variables
      free(mask_logic_list.elements);
      
      
      char string_output[128];
      MPI_MASTER(
      if (verbal) sprintf(string_output,"Intersect check list for Volume %d",volume_index);
      if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.intersect_check_list,string_output);
      if (verbal) sprintf(string_output,"Mask intersect check list for Volume %d",volume_index);
      if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.mask_intersect_list,string_output);
      /*
      if (verbal) {
        for (mask_index=0;mask_index<Volumes[volume_index]->geometry.mask_intersect_lists.number_of_lists;mask_index++) {
          sprintf(string_output," - mask intersect check list for mask %d which is volume %d",mask_index,Volumes[volume_index]->geometry.mask_intersect_lists.mask_indices.elements[mask_index]);
          print_1d_int_list(Volumes[volume_index]->geometry.mask_intersect_lists.lists[mask_index],string_output);
        }
      }
      */
      
      )
  }
};

void generate_parents_lists(struct pointer_to_1d_int_list **parents_lists, struct Volume_struct **Volumes, int number_of_volumes, int verbal, int mask_mode) {
  // Function for generating parent lists for all volumes
  // A volume m has n as a parent, if volume n has volume m as a child
  
  // if mask_mode == 0, masks are ignored, if mask_mode == 1, masks are taken into account.
  
  MPI_MASTER(
  if (verbal) {
    if (mask_mode == 1)
        printf("\nGenerating parents lists ---------------------------- \n");
    else if (mask_mode == 0)
        printf("\nGenerating parents lists (ignoring masks) ----------- \n");
    else {
        printf("Error, the function parents_lists got a non defined mask_mode");
        exit(1);
    }
  }
  
  )
  // Volume iterate has volume p as a parent, if volume p has volume iterate as child.
  
  struct pointer_to_1d_int_list temp_list_local;
  temp_list_local.num_elements = number_of_volumes;
  temp_list_local.elements = malloc(number_of_volumes*sizeof(int));
  
  // Loop over
  int iterate,parent,used_elements;
  for (iterate = 0;iterate < number_of_volumes;iterate++) {
    // clear temp list
    used_elements = 0;
    parents_lists[iterate] = malloc(sizeof(struct pointer_to_1d_int_list));
    for (parent = 0;parent < number_of_volumes;parent++) {
        if (on_int_list(Volumes[parent]->geometry.children,iterate))
          if (mask_mode == 1 || (Volumes[parent]->geometry.is_mask_volume == 0 && Volumes[iterate]->geometry.is_mask_volume == 0))
            temp_list_local.elements[used_elements++] = parent;
    }
      allocate_list_from_temp(used_elements,temp_list_local,parents_lists[iterate]);
      
      char string_output[128];
      MPI_MASTER(
      if (verbal) sprintf(string_output,"Parents for Volume %d",iterate);
      if (verbal) print_1d_int_list(*parents_lists[iterate],string_output);
      )
  }
  free(temp_list_local.elements);

};

void generate_true_parents_lists(struct pointer_to_1d_int_list **parents_lists, struct pointer_to_1d_int_list **true_children_lists, struct Volume_struct **Volumes, int number_of_volumes, int verbal, int mask_mode) {
  // Function for generating parent lists for all volumes
  // A volume m has n as a parent, if volume n has volume m as a child
  
  // if mask_mode == 0, masks are ignored, if mask_mode == 1, masks are taken into account.
  
  MPI_MASTER(
  if (verbal) {
    if (mask_mode == 1)
        printf("\nGenerating parents lists ---------------------------- \n");
    else if (mask_mode == 0)
        printf("\nGenerating parents lists (ignoring masks) ----------- \n");
    else {
        printf("Error, the function parents_lists got a non defined mask_mode");
        exit(1);
    }
  }
  
  )
  // Volume iterate has volume p as a parent, if volume p has volume iterate as child.
  
  struct pointer_to_1d_int_list temp_list_local;
  temp_list_local.num_elements = number_of_volumes;
  temp_list_local.elements = malloc(number_of_volumes*sizeof(int));
  
  // Loop over
  int iterate,parent,used_elements;
  for (iterate = 0;iterate < number_of_volumes;iterate++) {
    // clear temp list
    used_elements = 0;
    parents_lists[iterate] = malloc(sizeof(struct pointer_to_1d_int_list)); // allocate_list_from_temp allocates
    for (parent = 0;parent < number_of_volumes;parent++) {
        //if (on_int_list(Volumes[parent]->geometry.children,iterate))
        if (on_int_list(*true_children_lists[parent],iterate))
          if (mask_mode == 1 || (Volumes[parent]->geometry.is_mask_volume == 0 && Volumes[iterate]->geometry.is_mask_volume == 0))
            temp_list_local.elements[used_elements++] = parent;
    }
      allocate_list_from_temp(used_elements,temp_list_local,parents_lists[iterate]);
      
      char string_output[128];
      MPI_MASTER(
      if (verbal) sprintf(string_output,"Parents for Volume %d",iterate);
      if (verbal) print_1d_int_list(*parents_lists[iterate],string_output);
      )
  }
  free(temp_list_local.elements);

};

void generate_intersect_check_lists_experimental(struct pointer_to_1d_int_list **true_overlap_lists, struct pointer_to_1d_int_list **raw_overlap_lists, struct pointer_to_1d_int_list **parents_lists, struct pointer_to_1d_int_list **true_parents_lists , struct Volume_struct **Volumes, int number_of_volumes, int verbal) {
  // Generates the intersect_check_list and mask_intersect_list for each Volume.
  /*
  Description of needed lists for volume n:
  Children list: List of volumes that is contained within volume n (is stored in the Volumes struct)
  True Children list: List of volumes that when masked by their masks is contained within volume n when masked by it's masks
  
  Parents list: List of volumes that contains volume n
  True parents list: List of volumes that when masked by their masks contains volume n when it is masked by it's masks
  
  raw overlap list: List of volumes whos geometry overlaps the geometry of volume n
  true overlap list: List of volumes whos geometry overlaps the geometry of volume n when the masks of both volumes are applied
  
  
  
  The algorithm:
  
  1) Take the true overlap list for volume n
  2) remove parents of n (normal parent list, with masks)
  3) remove volumes that do not mask n and are true parents of n
  4) remove volumes that are not masks and have lower priority than n
  5) remove volumes that have at least one true parent on the list (in step 4) that is not a mask volume
  6) split the list into two, the intersect_check_list which is all non masked volumes still on the list, and the mask_intersect_list which is the masked volumes
  7) remove volumes on the mask_intersect_list whos mask does not overlap (standard overlap list) n
  
  In step 5 the order in which the volumes are tested and removed may matter, so it is specifically stated that it is as the list looked in step 4.
  */
  
  MPI_MASTER(
  if (verbal) printf("\nGenerating intersect check lists -------------------- \n");
  )
  
  struct pointer_to_1d_int_list work_list;
  struct pointer_to_1d_int_list logic_list;
  
  int volume_index,iterate,parent,mask_index,masked_volume_index,ANY_logic,true_parent_volume_number;
  int *mask_check,*mask_start;
  for (volume_index = 0;volume_index < number_of_volumes;volume_index++) {
    
    // 1) Take the true overlap list for volume n
    // Create copy of true_overlap_lists to work with
    
    if (Volumes[volume_index]->geometry.is_mask_volume == 1) {
      // Bug fixed on 26/11/2016, do not create intersection lists for masks as they are not used, and affects destinations lists in a problematic way
      Volumes[volume_index]->geometry.intersect_check_list.num_elements = 0;
      Volumes[volume_index]->geometry.mask_intersect_list.num_elements = 0;
      
    } else {
      work_list.num_elements = true_overlap_lists[volume_index]->num_elements;
      work_list.elements = malloc(work_list.num_elements * sizeof(int));
      for (iterate=0;iterate<work_list.num_elements;iterate++) work_list.elements[iterate] = true_overlap_lists[volume_index]->elements[iterate];
    
    //if (verbal) print_1d_int_list(work_list,"After 1)");
    
    
    //2) remove parents of n (normal parent list, with masks)
    for (iterate=work_list.num_elements-1;iterate>-1;iterate--) {
      if (on_int_list(*parents_lists[volume_index],work_list.elements[iterate]))
        remove_element_in_list_by_index(&work_list,iterate);
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 2)");
    
    //3) remove volumes that do not mask n and are true parents of n
    for (iterate=work_list.num_elements-1;iterate>-1;iterate--) {
      if (on_int_list(Volumes[volume_index]->geometry.masked_by_list,work_list.elements[iterate]) == 0 && on_int_list(*true_parents_lists[volume_index],work_list.elements[iterate]))
        remove_element_in_list_by_index(&work_list,iterate);
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 3)");
    
    //4) remove volumes that are not masks and have lower priority than n
    for (iterate=work_list.num_elements-1;iterate>-1;iterate--) {
      if (Volumes[work_list.elements[iterate]]->geometry.is_mask_volume == 0 && Volumes[work_list.elements[iterate]]->geometry.priority_value < Volumes[volume_index]->geometry.priority_value)
        remove_element_in_list_by_index(&work_list,iterate);
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 4)");
    
    //5) remove volumes that have at least one true parent on the list (in step 4) that is not a mask volume
    // Here a logic_list is used to not have the order of removal matter
    logic_list.num_elements = work_list.num_elements;
    logic_list.elements = malloc(logic_list.num_elements * sizeof(int));
    for(iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 1;
    
    for (iterate=0;iterate<work_list.num_elements;iterate++) {
      for (parent=0;parent<true_parents_lists[work_list.elements[iterate]]->num_elements;parent++) {
        true_parent_volume_number = true_parents_lists[work_list.elements[iterate]]->elements[parent];
        if (on_int_list(work_list,true_parent_volume_number) && Volumes[true_parent_volume_number]->geometry.is_mask_volume == 0) {
          // Since element number iterate on the work list have a true parent on the work list, it can be removed
          logic_list.elements[iterate] = 0;
          break;
        }
      }
    }
    
    // Now the elements marked for removal can be removed without interfering in the operation
    for (iterate=work_list.num_elements-1;iterate>-1;iterate--) {
      if (logic_list.elements[iterate] == 0) remove_element_in_list_by_index(&work_list,iterate);
    }
    free(logic_list.elements);
    
    //if (verbal) print_1d_int_list(work_list,"After 5)");
      
    //6) split the list into two, the intersect_check_list which is all non masked volumes still on the list, and the mask_intersect_list which is the masked volumes
    
    Volumes[volume_index]->geometry.intersect_check_list.num_elements = 0;
    Volumes[volume_index]->geometry.mask_intersect_list.num_elements = 0;
    
    for (iterate=0;iterate<work_list.num_elements;iterate++) {
      if (Volumes[work_list.elements[iterate]]->geometry.is_masked_volume == 1) {
        add_element_to_int_list(&Volumes[volume_index]->geometry.mask_intersect_list,work_list.elements[iterate]);
      } else {
        add_element_to_int_list(&Volumes[volume_index]->geometry.intersect_check_list,work_list.elements[iterate]);
      }
    }
    
    //if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.intersect_check_list,"After 6) intersect_check_list");
    //if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.mask_intersect_list,"After 6) mask_intersect_list");
    
    //7) remove volumes on the mask_intersect_list whos masks does not overlap (standard overlap list) n
    for (iterate=Volumes[volume_index]->geometry.mask_intersect_list.num_elements-1;iterate>-1;iterate--) {
      // Need to check if the volumes masking Volumes[volume_index]->geometry.mask_intersect_list.elements[iterate] overlaps with n
      masked_volume_index = Volumes[volume_index]->geometry.mask_intersect_list.elements[iterate];
      
      if (Volumes[masked_volume_index]->geometry.mask_mode == 1) {// All mode, if just one does not overlap, remove the element
        for (mask_start=mask_check=Volumes[masked_volume_index]->geometry.masked_by_list.elements;mask_check-mask_start<Volumes[masked_volume_index]->geometry.masked_by_list.num_elements;mask_check++) {
          if (on_int_list(*raw_overlap_lists[volume_index],*mask_check) == 0) {
            remove_element_in_list_by_index(&Volumes[volume_index]->geometry.mask_intersect_list,iterate);
            break;
          }
        }
      } else { // ANY mode, just one need to overlap in order to keep the element
        ANY_logic = 0;
        for (mask_start=mask_check=Volumes[masked_volume_index]->geometry.masked_by_list.elements;mask_check-mask_start<Volumes[masked_volume_index]->geometry.masked_by_list.num_elements;mask_check++) {
          if (on_int_list(*raw_overlap_lists[volume_index],*mask_check) == 1) {
            ANY_logic = 1;
            break;
          }
        }
        if (ANY_logic == 0) remove_element_in_list_by_index(&Volumes[volume_index]->geometry.mask_intersect_list,iterate);
      }
    }
    
    if (work_list.num_elements > 0) free(work_list.elements);
    }
    
    
    //if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.mask_intersect_list,"After 7) mask_intersect_list");
    
    char string_output[128];
    MPI_MASTER(
    if (verbal) sprintf(string_output,"Intersect check list for Volume %d",volume_index);
    if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.intersect_check_list,string_output);
    if (verbal) sprintf(string_output,"Mask intersect check list for Volume %d",volume_index);
    if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.mask_intersect_list,string_output);
    )
    
    
  }
}

void generate_grandparents_lists(struct pointer_to_1d_int_list **grandparents_lists, struct pointer_to_1d_int_list **parents_lists, int number_of_volumes, int verbal) {
  // Function for generating grandparents lists
  // Volume iterate has volume p as a grandparent, if volume p has a parent T, who has volume iterate as parent.
  // Alternertively:
  // Volume iterate has volume p as a grandparent, if volume p have a child that is volume iterate's parent.
  
  MPI_MASTER(
  if (verbal) printf("\nGenerating grandparents lists ----------------------- \n");
  )

  struct pointer_to_1d_int_list common;
  common.num_elements = number_of_volumes;
  common.elements = malloc(common.num_elements*sizeof(int)); // Maximum needed space.
  
  struct pointer_to_1d_int_list temp_list_local;
  temp_list_local.num_elements = number_of_volumes;
  temp_list_local.elements = malloc(number_of_volumes*sizeof(int));
  
  int iterate,reset_int,parent,child,used_elements;
  for (iterate = 0;iterate < number_of_volumes;iterate++) {
    // clear temp list
    used_elements = 0;

    for (reset_int=0; reset_int<number_of_volumes; reset_int++) common.elements[reset_int] = -1; // Initialize to impossible volume ids
    for (reset_int=0; reset_int<number_of_volumes; reset_int++) temp_list_local.elements[reset_int] = -1; // Initialize to impossible volume ids

    grandparents_lists[iterate] = malloc(sizeof(struct pointer_to_1d_int_list));
    
    for (parent = 0; parent<parents_lists[iterate]->num_elements; parent++) {
        // parent number p parents_lists[iterate].elements.[p] in the parent_list for iterate.
        on_both_int_lists(parents_lists[parents_lists[iterate]->elements[parent]], parents_lists[iterate], &common);
        // returns a pointer_to_1d_list, with all the elements that are in common.
        for (child = 0;child < common.num_elements;child++) {
            // Need to make sure the element is not already on the list
            if (0 == on_int_list(temp_list_local, common.elements[child])) {
              temp_list_local.elements[used_elements++] = common.elements[child];
            }
        }
    }
    allocate_list_from_temp(used_elements, temp_list_local, grandparents_lists[iterate]);
  
    char string_output[128];
    MPI_MASTER(
      if (verbal) sprintf(string_output,"Grandparents for Volume %d", iterate);
      if (verbal) print_1d_int_list(*grandparents_lists[iterate], string_output);
    )
  }
  free(temp_list_local.elements);
  free(common.elements);
};


void generate_destinations_lists_experimental(struct pointer_to_1d_int_list **true_overlap_lists, struct pointer_to_1d_int_list **true_children_lists, struct pointer_to_1d_int_list **true_parents_lists, struct pointer_to_1d_int_list **true_grandparents_lists, struct Volume_struct **Volumes, int number_of_volumes, int verbal) {
  // Generates destinations list for for all volumes
  // Current implementation uses true_parents_lists and true_grandparents_lists that are generated as if no masks were defined

  MPI_MASTER(
  if (verbal) printf("\nGenerating destinations lists ----------------------- \n");
  )
  // Volume 0 has an hardcoded empty destinations list
  Volumes[0]->geometry.destinations_list.num_elements = 0;

  struct pointer_to_1d_int_list work_list;
  int volume_index,iterate,iterate2,found_index,I_index,I_volume;
  
  for (volume_index=1;volume_index<number_of_volumes;volume_index++) {
    
    // 1) Take the true overlap list for volume n
    // Create copy of true_overlap_lists to work with
    work_list.num_elements = true_overlap_lists[volume_index]->num_elements;
    work_list.elements = malloc(work_list.num_elements * sizeof(int));
    for (iterate=0;iterate<work_list.num_elements;iterate++) work_list.elements[iterate] = true_overlap_lists[volume_index]->elements[iterate];
    
    //if (verbal) print_1d_int_list(work_list,"After 1)");
    
    // 2) Remove elements from n's intersection list
    for (iterate=work_list.num_elements-1;iterate>-1;iterate--) {
      if (on_int_list(Volumes[volume_index]->geometry.intersect_check_list,work_list.elements[iterate]))
        remove_element_in_list_by_index(&work_list,iterate);
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 2)");
  
    // 3) Remove true children of non-mask elements on n's intersection list
    for (I_index=0;I_index<Volumes[volume_index]->geometry.intersect_check_list.num_elements;I_index++) {
      I_volume = Volumes[volume_index]->geometry.intersect_check_list.elements[I_index];
      if (Volumes[I_volume]->geometry.is_mask_volume == 0) {
        for (iterate=0;iterate<true_children_lists[I_volume]->num_elements;iterate++) {
          remove_element_in_list_by_value(&work_list,true_children_lists[I_volume]->elements[iterate]);
        }
      }
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 3)");
    
    // 4) Remove elements from mask intersection list
    for (iterate=work_list.num_elements-1;iterate>-1;iterate--) {
      if (on_int_list(Volumes[volume_index]->geometry.mask_intersect_list,work_list.elements[iterate]))
        remove_element_in_list_by_index(&work_list,iterate);
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 4)");
    
    // 5) Remove true children of elements on n's mask intersection list
    for (I_index=0;I_index<Volumes[volume_index]->geometry.mask_intersect_list.num_elements;I_index++) {
      I_volume = Volumes[volume_index]->geometry.mask_intersect_list.elements[I_index];
      if (Volumes[I_volume]->geometry.is_mask_volume == 0) {
        for (iterate=0;iterate<true_children_lists[I_volume]->num_elements;iterate++) {
          remove_element_in_list_by_value(&work_list,true_children_lists[I_volume]->elements[iterate]);
        }
      }
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 5)");
    
    // 6) Remove true children of n
    for (iterate=0;iterate<true_children_lists[volume_index]->num_elements;iterate++)
      remove_element_in_list_by_value(&work_list,true_children_lists[volume_index]->elements[iterate]);
      
    //if (verbal) print_1d_int_list(work_list,"After 6)");
      
    // 7) Remove true grandparents of n
    for (iterate=0;iterate<true_grandparents_lists[volume_index]->num_elements;iterate++)
      remove_element_in_list_by_value(&work_list,true_grandparents_lists[volume_index]->elements[iterate]);
      
    //if (verbal) print_1d_int_list(work_list,"After 7)");
      
    // 8) Remove mask volumes
    for (iterate=work_list.num_elements-1;iterate>-1;iterate--) {
      if (Volumes[work_list.elements[iterate]]->geometry.is_mask_volume == 1)
        remove_element_in_list_by_index(&work_list,iterate);
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 8)");
    
    // 9) Remove true parents of n on the list that has other true parents of n on the list with higher priority
    for (iterate=work_list.num_elements-1;iterate>-1;iterate--) {
      if (on_int_list(*true_parents_lists[volume_index],work_list.elements[iterate])){
        // work_list.elements[iterate] is the volume index of a volume that is a true parent of n
        for (iterate2=0;iterate2<work_list.num_elements;iterate2++) {
          if (on_int_list(*true_parents_lists[volume_index],work_list.elements[iterate2]) && iterate != iterate2) {
            if (Volumes[work_list.elements[iterate]]->geometry.priority_value < Volumes[work_list.elements[iterate2]]->geometry.priority_value) {
              //printf("Removing element number %d (V%d) because element number %d (V%d) had higher priority \n",iterate,work_list.elements[iterate],iterate2,work_list.elements[iterate2]);
              remove_element_in_list_by_index(&work_list,iterate);
              break; // Missing break inserted on 14/9/2016
            }
          }
        }
      }
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 9)");
    
    // 10) The remaining list is the destinations_list
    Volumes[volume_index]->geometry.destinations_list.num_elements = work_list.num_elements;
    Volumes[volume_index]->geometry.destinations_list.elements = malloc(Volumes[volume_index]->geometry.destinations_list.num_elements*sizeof(int));
    
    for (iterate=0;iterate<work_list.num_elements;iterate++)
      Volumes[volume_index]->geometry.destinations_list.elements[iterate] = work_list.elements[iterate];
    
    // Clean up after work_list so the next can be allocated
    free(work_list.elements);
  
  
    char string_output[128];
    MPI_MASTER(
    if (verbal) sprintf(string_output,"Destinations list for Volume %d",volume_index);
    if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.destinations_list,string_output);
    )

  }

};

void generate_destinations_list(int N_volume,struct Volume_struct **Volumes,struct pointer_to_1d_int_list original_overlap_list,struct pointer_to_1d_int_list *parent_list, struct pointer_to_1d_int_list *grandparent_list) {
    // This function generates the destinations_list for a single volume index, N_volume
    // The destination list describes which volumes a ray can enter when leaving N_volume
    // Each of the 6 steps for the algorithm is commented individually, and debug print statements are available
    
    // Mask update: Mask volumes are to be removed from all destinations_list as they can not be the current volume
    // It is not that simple, as some volume will then get empty destination lists. Need to revise this algorithm.
    
    // 1) Start with the overlap list of volume N
    // 2) remove all Volumes from the overlap list of N, which is also on the intersect_check_list
    // 3) remove the children of volumes removed in step 2)
    // 4) remove the children of N
    // 5) remove the grandparents of N
    // 6) remove volumes with lower priority than parents of N still on the list
    // 7) The remaing list is the destinations list
    
    
    // The destination list system should run without masks altogether, meaning parent and grandparent lists should not use them,
    //  and all masks should be removed in step 2
    
    // 1) Start with the overlap list of volume N
    // 2) remove all masks on the list
    // 3) remove all Volumes from the overlap list of N, which is also on the intersect_check_list
    // 4) remove the children of volumes removed in step 2) (if the removed volume was not a mask )
    // 5) remove the children of N
    // 6) remove the grandparents of N
    // 7) remove volumes with lower priority than parents of N still on the list
    // 8) The remaing list is the destinations list
    
    

    // 1) Start with the overlap list of volume N
    struct pointer_to_1d_int_list overlap_list;
    overlap_list.num_elements = original_overlap_list.num_elements;
    overlap_list.elements = malloc(overlap_list.num_elements*sizeof(int));
    int iterate;
    for (iterate=0;iterate<overlap_list.num_elements;iterate++) overlap_list.elements[iterate] = original_overlap_list.elements[iterate];
    
    char string_output[124];
    // sprintf(string_output,"Destinations list for Volume %d step 1",N_volume);
    // print_1d_int_list(overlap_list,string_output);
    
    // 2) remove all masks
    for (iterate=overlap_list.num_elements-1;iterate > -1;iterate--) {
        if (Volumes[overlap_list.elements[iterate]]->geometry.is_mask_volume == 1) {
            remove_element_in_list_by_index(&overlap_list,iterate);
        }
    }
    
    // 3) remove all Volumes from the overlap list of N, which is also on the intersect_check_list
    struct pointer_to_1d_int_list removed_under_2;
    removed_under_2.num_elements = 0;
    int to_check;
    removed_under_2.elements = malloc( Volumes[N_volume]->geometry.intersect_check_list.num_elements * sizeof(int));
    for (iterate=0;iterate < Volumes[N_volume]->geometry.intersect_check_list.num_elements;iterate++) {
        to_check = Volumes[N_volume]->geometry.intersect_check_list.elements[iterate];
        if (on_int_list(overlap_list,to_check)) {
            removed_under_2.elements[removed_under_2.num_elements++] = to_check;
            remove_element_in_list_by_value(&overlap_list,to_check);
        }
    }
    
    // sprintf(string_output,"Destinations list for Volume %d step 2",N_volume);
    // print_1d_int_list(overlap_list,string_output);
    
    // 4) remove the children of volumes removed in step 2)
    int children;
    for (iterate=0;iterate<removed_under_2.num_elements;iterate++){
        for (children = 0;children < Volumes[removed_under_2.elements[iterate]]->geometry.children.num_elements;children++) {
          remove_element_in_list_by_value(&overlap_list,Volumes[removed_under_2.elements[iterate]]->geometry.children.elements[children]);
        }
    }
    
    // sprintf(string_output,"Destinations list for Volume %d step 3",N_volume);
    // print_1d_int_list(overlap_list,string_output);
    
    // 5) remove the children of N
    for (children = 0;children < Volumes[N_volume]->geometry.children.num_elements;children++) {
        remove_element_in_list_by_value(&overlap_list,Volumes[N_volume]->geometry.children.elements[children]);
    }
    
    // sprintf(string_output,"Destinations list for Volume %d step 4",N_volume);
    // print_1d_int_list(overlap_list,string_output);
    
    // 6) remove the grandparents of N
    int grandparent;
    for (grandparent = 0;grandparent < grandparent_list->num_elements;grandparent++) {
        remove_element_in_list_by_value(&overlap_list,grandparent_list->elements[grandparent]);
    }
    
    // sprintf(string_output,"Destinations list for Volume %d step 5",N_volume);
    // print_1d_int_list(overlap_list,string_output);
    
    // 7) remove volumes with lower priority than parents of N still on the list
    struct pointer_to_1d_int_list logic_list;
    logic_list.num_elements = overlap_list.num_elements;
    if (logic_list.num_elements>0) logic_list.elements = malloc(logic_list.num_elements*sizeof(int));
    for (iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 1;
    
    int parent;
    for (parent=0;parent<parent_list->num_elements;parent++) {
      if (on_int_list(overlap_list,parent_list->elements[parent])) {
        // Found a parent to N on the list, now check all other elements on the list
        for (iterate=0;iterate<overlap_list.num_elements;iterate++) {
          if (parent_list->elements[parent] != overlap_list.elements[iterate]) {
            // if the element iterate have lower priority than the parent, remove it from the list
            if (Volumes[overlap_list.elements[iterate]]->geometry.priority_value < Volumes[parent_list->elements[parent]]->geometry.priority_value) logic_list.elements[iterate] = 0;
          }
        }
      }
    }
    
    // 8) The remaing list is the destinations list
    Volumes[N_volume]->geometry.destinations_list.num_elements = sum_int_list(logic_list);
    Volumes[N_volume]->geometry.destinations_list.elements = malloc(Volumes[N_volume]->geometry.destinations_list.num_elements * sizeof(int));
    
    int overlap_index,used_elements=0;
    for (overlap_index=0;overlap_index < overlap_list.num_elements;overlap_index++)
      if (logic_list.elements[overlap_index] == 1)
        Volumes[N_volume]->geometry.destinations_list.elements[used_elements++] = overlap_list.elements[overlap_index];
    
    if (overlap_list.num_elements>0) free(overlap_list.elements);
    if (logic_list.num_elements>0) free(logic_list.elements);
    
    
    /*
    // Old version before rule 6 and 2 was added
    // 8) The remaing list is the destinations list
    Volumes[N_volume]->geometry.destinations_list.num_elements = overlap_list.num_elements;
    Volumes[N_volume]->geometry.destinations_list.elements = malloc(Volumes[N_volume]->geometry.destinations_list.num_elements * sizeof(int));
    
    int overlap_index;
    for (overlap_index=0;overlap_index < overlap_list.num_elements;overlap_index++)
        Volumes[N_volume]->geometry.destinations_list.elements[overlap_index] = overlap_list.elements[overlap_index];
    
    // sprintf(string_output,"Destination list for Volume %d step 6",N_volume);
    // print_1d_int_list(Volumes[N_volume]->geometry.destinations_list,string_output);
    
    if (overlap_list.num_elements>0) free(overlap_list.elements);
    */
    
    };

void generate_destinations_lists(struct pointer_to_1d_int_list **grandparents_lists, struct pointer_to_1d_int_list **parents_lists, struct pointer_to_1d_int_list **overlap_lists,struct Volume_struct **Volumes, int number_of_volumes, int verbal) {
    // Because of the complexity of the algortithm for generating the destinations list, the function is made for a single volume at the time to keep the notation simpler
    // This funtion runs the destinations list function for each volume
    MPI_MASTER(
    if (verbal) printf("\nGenerating destinations lists ----------------------- \n");
    )
    
    int volume_index;
    for (volume_index = 0;volume_index < number_of_volumes;volume_index++) {
      generate_destinations_list(volume_index,Volumes,*overlap_lists[volume_index],parents_lists[volume_index],grandparents_lists[volume_index]);
      
      char string_output[128];
      if (verbal) sprintf(string_output,"Destinations list for Volume %d",volume_index);
      MPI_MASTER(
      if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.destinations_list,string_output);
      )
    }
};

/* OBSOLETE
void generate_destinations_logic_lists(struct Volume_struct **Volumes,int number_of_volumes,int verbal) {
  // The destinations logic list is another way to store the destinations list that makes some tasks quicker
  
  MPI_MASTER(
  if (verbal) printf("\nGenerating destinations logic lists ----------------------- \n");
  )

  int volume_index;
  for (volume_index = 0;volume_index < number_of_volumes;volume_index++) {
      allocate_logic_list_from_temp(number_of_volumes,Volumes[volume_index]->geometry.destinations_list,&Volumes[volume_index]->geometry.destinations_logic_list);
      
      char string_output[128];
      MPI_MASTER(
      if (verbal) sprintf(string_output,"Destinations logic list for Volume %d",volume_index);
      if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.destinations_logic_list,string_output);
      )
  }

};
*/

void generate_reduced_destinations_lists(struct pointer_to_1d_int_list **parents_lists, struct Volume_struct **Volumes,int number_of_volumes,int verbal) {
    // The reduced destination list is the destination list of a volume, where each element that has a parent on the same destination list is removed
    // This list is to be fed to the which_volume function, as this funtion will automatically search through the direct children in a tree like manner
    // The optimization reduces the number of calculations of within functions in nested geometries.
    
    MPI_MASTER(
    if (verbal) printf("\nGenerating reduced destination lists ----------------------- \n");
    )
    
    struct pointer_to_1d_int_list logic_list;
  
    int volume_index,checked_dest_index,checked_dest_volume,rest_dest_index,rest_dest_volume,dest_index,iterate;
    for (volume_index = 0;volume_index < number_of_volumes;volume_index++) {
      
      //printf("Generating reduced destinations lists for volume %d\n",volume_index);
      logic_list.num_elements = Volumes[volume_index]->geometry.destinations_list.num_elements;
      logic_list.elements = malloc( (int) logic_list.num_elements * sizeof(int));
      //printf("Sucsessfully allocated space for %d integers in logic list %d\n",logic_list.num_elements,volume_index);
      for (iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 1;
      
      for (checked_dest_index=0;checked_dest_index<Volumes[volume_index]->geometry.destinations_list.num_elements;checked_dest_index++) {
            checked_dest_volume = Volumes[volume_index]->geometry.destinations_list.elements[checked_dest_index];
            for (rest_dest_index=0;rest_dest_index<Volumes[volume_index]->geometry.destinations_list.num_elements;rest_dest_index++) {
                rest_dest_volume = Volumes[volume_index]->geometry.destinations_list.elements[rest_dest_index];
                // As every volume has 0 as a parent, these are ignored. It would work to include this, but would add an extra trivial step to within_which_volume
                if (rest_dest_volume != 0) {
                    if (on_int_list(*parents_lists[checked_dest_volume],rest_dest_volume) == 1) {
                        // In this case, do not include element checked_dest_index on the reduced destinations list
                        // ADD mask check
                        if (Volumes[rest_dest_volume]->geometry.is_masked_volume == 0) {
                          logic_list.elements[checked_dest_index] = 0;
                        }
                    }
                }
            }
      }
      
      Volumes[volume_index]->geometry.reduced_destinations_list.num_elements = sum_int_list(logic_list);
      Volumes[volume_index]->geometry.reduced_destinations_list.elements = malloc((int)Volumes[volume_index]->geometry.reduced_destinations_list.num_elements * sizeof(int));
      
      iterate = 0;
      for (dest_index = 0;dest_index < Volumes[volume_index]->geometry.destinations_list.num_elements;dest_index++) {
            if (logic_list.elements[dest_index] == 1)   Volumes[volume_index]->geometry.reduced_destinations_list.elements[iterate++] = Volumes[volume_index]->geometry.destinations_list.elements[dest_index];
      }
      
      free(logic_list.elements);
      
      // Testing an optimization
      remove_element_in_list_by_value(&Volumes[volume_index]->geometry.reduced_destinations_list,0);
      
      char string_output[128];
      MPI_MASTER(
      if (verbal) sprintf(string_output,"Reduced destinations list for Volume %d",volume_index);
      if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.reduced_destinations_list,string_output);
      )
    }
};

void generate_direct_children_lists(struct pointer_to_1d_int_list **parents_lists, struct Volume_struct **Volumes,int number_of_volumes,int verbal) {

  MPI_MASTER(
  if (verbal) printf("\nGenerating direct children lists ----------------------- \n");
  )
  // A direct children of volume n is a volume that is a child of n, but no other child of n is its parent
  
  // Mask update: Need to check that this step does not bug out when the mask system interferes with child/parent systems
  
  struct pointer_to_1d_int_list logic_list;
  int volume_index,child,parent,iterate;
  for (volume_index = 0;volume_index < number_of_volumes;volume_index++) {
        // Temp elements is used, and its actual number of elements is edited even though the memory allocated is not changed. This is so that the list functions handles it correctly.
        // The free function will free all the allocated memory regardless of the value of the .num_elements structure field, it is just there for convinience.
      
        logic_list.num_elements = Volumes[volume_index]->geometry.children.num_elements;
        logic_list.elements = malloc(logic_list.num_elements * sizeof(int));
        for (iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 1;
      
        // Look through each child of volume n, and for each of those look through all the other children of n and search for one of them being a parent to the child
        for (child=0;child<Volumes[volume_index]->geometry.children.num_elements;child++) {
            for (parent=0;parent<parents_lists[Volumes[volume_index]->geometry.children.elements[child]]->num_elements;parent++) {
                if (on_int_list(Volumes[volume_index]->geometry.children,parents_lists[Volumes[volume_index]->geometry.children.elements[child]]->elements[parent]))
                    // If such a parent is found, remove that child from the list
                    logic_list.elements[child] = 0;
            }
        }
      
      Volumes[volume_index]->geometry.direct_children.num_elements = sum_int_list(logic_list);
      Volumes[volume_index]->geometry.direct_children.elements = malloc(Volumes[volume_index]->geometry.direct_children.num_elements*sizeof(int));
      
      iterate = 0;
      for (child = 0;child < Volumes[volume_index]->geometry.children.num_elements;child++) {
            if (logic_list.elements[child])   Volumes[volume_index]->geometry.direct_children.elements[iterate++] = Volumes[volume_index]->geometry.children.elements[child];
      }
      // Be careful with names in both main and a function, as they are automatically declared as external variables, which would then also free the main.
      free(logic_list.elements);
      
      char string_output[128];
      MPI_MASTER(
      if (verbal) sprintf(string_output,"Children list for Volume        %d",volume_index);
      if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.children,string_output);
      if (verbal) sprintf(string_output,"Direct_children list for Volume %d",volume_index);
      if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.direct_children,string_output);
      )
      
  }

};

void generate_starting_logic_list(struct starting_lists_struct *starting_lists, struct Volume_struct **Volumes, int number_of_volumes, int verbal) {
    // Function for generating logic list of volumes the ray can start in without an error.
    // Start with a list of all vacuum volumes
    // Remove all volumes that are children of non-vacuum volumes
    // It is still possible to have a volume on this list that is surrounded by non-vacuum volumes, but it is hard to detect these situations,
    //  meaning that it is ultimately partly the users responsibility to not send neutrons directly into materials.
    
    int volume_index,*start,*check;
    
    struct pointer_to_1d_int_list temp_list_local;
    temp_list_local.num_elements = number_of_volumes;
    temp_list_local.elements = malloc(number_of_volumes*sizeof(int));
    
    //if (verbal==1) printf("sucessfully allocated temp_list_local.elements, now to enter which volumes are vacuums as a logic list\n");
    temp_list_local.elements[0] = 1; // Volume 0 is a vacuum volume.
    for (volume_index = 1;volume_index < number_of_volumes;volume_index++) {
        if (Volumes[volume_index]->p_physics->is_vacuum == 1) temp_list_local.elements[volume_index] = 1;
        else temp_list_local.elements[volume_index] = 0;
    }
    // temp_list_local is now a logic list of all vacuum volumes
    //if (verbal==1) printf("list of vacuum volumes done, now to remove the children of non-vcauum volumes\n");
    for (volume_index = 1;volume_index < number_of_volumes;volume_index++) { // All volumes ...
        if (temp_list_local.elements[volume_index] == 0) { // ... that are not vacuum ...
            for (start = check = Volumes[volume_index]->geometry.children.elements;check - start < Volumes[volume_index]->geometry.children.num_elements;check++) { // ... have all their children ...
                temp_list_local.elements[*check] = 0; // .. removed from the allowed_start_logic_list
            }
        }
    }
    //if (verbal==1) printf("sucessfully removed children of non-vacuum volumes, now allocate allowed_start_logic_list\n");
    allocate_list_from_temp(number_of_volumes,temp_list_local,&starting_lists->allowed_starting_volume_logic_list);
    //if (verbal==1) printf("sucsessfully allocated allowed_start_logic_list, now freeing temp_list_local.elements. \n");
    
    free(temp_list_local.elements);
    //if (verbal==1) printf("sucessfully freed temp_list_local.elements, generate starting lists done\n");
    
    char string_output[128];
    MPI_MASTER(
    if (verbal) sprintf(string_output,"Allowed starting volume logic list");
    if (verbal) print_1d_int_list(starting_lists->allowed_starting_volume_logic_list,string_output);
    )
    
    
};

void generate_reduced_starting_destinations_list(struct starting_lists_struct *starting_lists, struct pointer_to_1d_int_list **parents_lists, struct Volume_struct **Volumes,int number_of_volumes,int verbal) {
    // The starting_destinations_list is trivial, as it contains all volumes that are not masks.


    struct pointer_to_1d_int_list logic_list;
    //printf("Generating reduced destinations lists for volume %d\n",volume_index);
    logic_list.num_elements = number_of_volumes;
    logic_list.elements = malloc( (int) logic_list.num_elements * sizeof(int));
    int iterate;
    for (iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 0;
    
    
    for (iterate=1;iterate<number_of_volumes;iterate++)
      if (Volumes[iterate]->geometry.is_mask_volume == 0) logic_list.elements[iterate] = 1;
      //else logic_list.elements[iterate] = 0;
    
    starting_lists->starting_destinations_list.num_elements = sum_int_list(logic_list);
    starting_lists->starting_destinations_list.elements = malloc(starting_lists->starting_destinations_list.num_elements*sizeof(int));
    
    int used_elements=0;
    for (iterate=1;iterate<number_of_volumes;iterate++)
      if (logic_list.elements[iterate] == 1) starting_lists->starting_destinations_list.elements[used_elements++] = iterate;
    
    MPI_MASTER(
    if (verbal) printf("\nGenerating start destinations list ------------------------------ \n");
    if (verbal) print_1d_int_list(starting_lists->starting_destinations_list,"Starting destinations list");
    )

    // The reduced starting destination list is used when a ray enters the component in the search for which volume it starts in.
    // It facilitates the same optimization as the regular destination list.
    // The start logic list is also generated, as it is very simple and does not need a seperate function
    
    // Mask update: Need to remove mask volumes from the reduced starting destination list
    
    MPI_MASTER(
    if (verbal) printf("\nGenerating reduced start destination list ----------------------- \n");
    )
    
    int checked_dest_index,checked_dest_volume,rest_dest_index,rest_dest_volume,dest_index;
    
    /* Old version before masks were introduced
    struct pointer_to_1d_int_list starting_dest_list;
    
    starting_dest_list.num_elements = number_of_volumes - 1;
    starting_dest_list.elements = malloc ( starting_dest_list.num_elements * sizeof(int));
    for (iterate=0;iterate<number_of_volumes-1;iterate++) starting_dest_list.elements[iterate] = iterate + 1; // All volumes to be checked for starting
    */
    
    logic_list.num_elements = starting_lists->starting_destinations_list.num_elements;
    free(logic_list.elements);
    logic_list.elements = malloc( (int) logic_list.num_elements * sizeof(int));
    
    //printf("Sucsessfully allocated space for %d integers in logic list %d\n",logic_list.num_elements,volume_index);
    for (iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 1;
  
    for (checked_dest_index=0;checked_dest_index<starting_lists->starting_destinations_list.num_elements;checked_dest_index++) {
        checked_dest_volume = starting_lists->starting_destinations_list.elements[checked_dest_index];
        for (rest_dest_index=0;rest_dest_index<starting_lists->starting_destinations_list.num_elements;rest_dest_index++) {
            rest_dest_volume = starting_lists->starting_destinations_list.elements[rest_dest_index];
            // As every volume has 0 as a parent, these are ignored. It would work to include this, but would add an extra trivial step to within_which_volume
            if (rest_dest_volume != 0) {
                if (on_int_list(*parents_lists[checked_dest_volume],rest_dest_volume) == 1) {
                    // In this case, do not include element checked_dest_index on the reduced destinations list
                    logic_list.elements[checked_dest_index] = 0;
                }
            }
        }
  }
  
  starting_lists->reduced_start_list.num_elements = sum_int_list(logic_list);
  starting_lists->reduced_start_list.elements = malloc((int)starting_lists->reduced_start_list.num_elements * sizeof(int));
  
  iterate = 0;
  for (dest_index = 0;dest_index < starting_lists->starting_destinations_list.num_elements;dest_index++) {
        if (logic_list.elements[dest_index] == 1) starting_lists->reduced_start_list.elements[iterate++] = starting_lists->starting_destinations_list.elements[dest_index];
  }
  
  free(logic_list.elements);
  //free(starting_dest_list.elements);
  
  MPI_MASTER(
  if (verbal) print_1d_int_list(starting_lists->reduced_start_list,"Reduced start destinations list");
  )
    
  // Making the start_logic_list.
  starting_lists->start_logic_list.num_elements = number_of_volumes;
  starting_lists->start_logic_list.elements = malloc ( starting_lists->start_logic_list.num_elements * sizeof(int));
  starting_lists->start_logic_list.elements[0] = 0;
  for (iterate=1;iterate<number_of_volumes;iterate++) starting_lists->start_logic_list.elements[iterate] = 1; // All volumes to be checked for starting volume
  MPI_MASTER(
  if (verbal) print_1d_int_list(starting_lists->start_logic_list,"Start logic list");
  )
};


void generate_next_volume_list(struct Volume_struct **Volumes, int number_of_volumes, int verbal) {
    // Generate list of volumes that can be the next volume which the ray enters. It is used for tagging, not the simulation / propagation

    // Mask update: These lists should be done as if all mask statuses are on, meaning they include all possible next volumes (will include more input to this function)
    
    // bug: next volume list is not complete and contains duplicates

    MPI_MASTER(
    if (verbal) printf("\nGenerating next volume list ------------------------------------- \n");
    )
    // Merge destinations list, intersection list and mask_intersection_list
    int volume_index,iterate,mask_index;
    struct pointer_to_1d_int_list full_intersection_list;
    full_intersection_list.num_elements=0;
    
    for (volume_index=0;volume_index<number_of_volumes;volume_index++) {
        Volumes[volume_index]->geometry.next_volume_list.num_elements = 0;
        // Before mask update
        //merge_lists(&Volumes[volume_index]->geometry.next_volume_list, &Volumes[volume_index]->geometry.destinations_list, &Volumes[volume_index]->geometry.intersect_check_list);
        
        merge_lists(&full_intersection_list, &Volumes[volume_index]->geometry.mask_intersect_list, &Volumes[volume_index]->geometry.intersect_check_list);
        merge_lists(&Volumes[volume_index]->geometry.next_volume_list,&Volumes[volume_index]->geometry.destinations_list,&full_intersection_list);
        
        /* // This complication is taken into account by adding the mask_intersect list instead
        // It is possible that the next volume is still not on this list, as when masks are encountered the next volume can be a volume they mask.
        // For each on the list, add the volumes masked by that mask (do not iterate over this, as masks can not be masked regardless)
        for (iterate=Volumes[volume_index]->geometry.next_volume_list.num_elements-1;iterate>-1;iterate--) {
          if (Volumes[Volumes[volume_index]->geometry.next_volume_list.elements[iterate]]->geometry.is_mask_volume == 1) {
            for (mask_index=0;mask_index<Volumes[Volumes[volume_index]->geometry.next_volume_list.elements[iterate]]->geometry.mask_list.num_elements;mask_index++) {
              add_element_to_int_list(&Volumes[volume_index]->geometry.next_volume_list,Volumes[Volumes[volume_index]->geometry.next_volume_list.elements[iterate]]->geometry.mask_list.elements[mask_index]);
            }
          }
        }
        */
        
        // Remove mask volumes from the next volume list, as they never occur as current_volume, and thus just create dead branches that takes up memory
        for (iterate=Volumes[volume_index]->geometry.next_volume_list.num_elements-1;iterate>-1;iterate--)
          if (Volumes[Volumes[volume_index]->geometry.next_volume_list.elements[iterate]]->geometry.is_mask_volume == 1)
            remove_element_in_list_by_index(&Volumes[volume_index]->geometry.next_volume_list,iterate);
        
        
        if (full_intersection_list.num_elements>0) free(full_intersection_list.elements);
        full_intersection_list.num_elements=0;
        
        char string_output[128];
        MPI_MASTER(
        if (verbal) sprintf(string_output,"Next volume list                %d",volume_index);
        if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.next_volume_list,string_output);
        )
    }
};

void generate_lists(struct Volume_struct **Volumes, struct starting_lists_struct *starting_lists, int number_of_volumes, int verbal) {
    // Function to control the generation of lists
    // Some lists are only needed temporary, and are thus declared here to keep them out of the main scope
    // Others are stored in the volume structs as they are needed in the trace algorithm (or tagging)
    
    
    struct pointer_to_1d_int_list **true_children_lists;
    true_children_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    // generate_children_lists both generate the normal children list for each volume, but also the true children list needed locally.
    generate_children_lists(Volumes, true_children_lists, number_of_volumes,verbal);
    
    
    struct pointer_to_1d_int_list **true_overlap_lists;
    true_overlap_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    struct pointer_to_1d_int_list **raw_overlap_lists;
    raw_overlap_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    
    generate_overlap_lists(true_overlap_lists, raw_overlap_lists, Volumes,number_of_volumes,verbal);
    
    
    //generate_intersect_check_lists(true_overlap_lists, Volumes, number_of_volumes, verbal);
    
    
    struct pointer_to_1d_int_list **parents_lists;
    parents_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    generate_parents_lists(parents_lists,Volumes,number_of_volumes,verbal,1); // The last 1 means masks are taken into account
    
    // Generate version of parent list as it would be without masks
    struct pointer_to_1d_int_list **parents_lists_no_masks;
    parents_lists_no_masks = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    generate_parents_lists(parents_lists_no_masks,Volumes,number_of_volumes,verbal,0); // The last 0 means masks are NOT taken into account
    
    // Generate version of parent list using true_children instead
    struct pointer_to_1d_int_list **true_parents_lists;
    true_parents_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    generate_true_parents_lists(true_parents_lists, true_children_lists, Volumes, number_of_volumes, verbal, 1);
    
    // Generate version of parent list no masks using true_children instead
    struct pointer_to_1d_int_list **true_parents_lists_no_masks;
    true_parents_lists_no_masks = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    generate_true_parents_lists(true_parents_lists_no_masks, true_children_lists, Volumes, number_of_volumes, verbal, 0);
    
    // New version of generate intersect lists
    generate_intersect_check_lists_experimental(true_overlap_lists, raw_overlap_lists, parents_lists, true_parents_lists, Volumes, number_of_volumes, verbal);
    
    struct pointer_to_1d_int_list **grandparents_lists;
    grandparents_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    generate_grandparents_lists(grandparents_lists,parents_lists,number_of_volumes,verbal);
    
    // Generate version of grandparents list as it would have been if no masks were defined
    struct pointer_to_1d_int_list **grandparents_lists_no_masks;
    grandparents_lists_no_masks = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    generate_grandparents_lists(grandparents_lists_no_masks,parents_lists_no_masks,number_of_volumes,verbal);
    
    // Generate true_grandparents_lists
    struct pointer_to_1d_int_list **true_grandparents_lists;
    true_grandparents_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    generate_grandparents_lists(true_grandparents_lists,true_parents_lists,number_of_volumes,verbal);
    
    struct pointer_to_1d_int_list **true_grandparents_lists_no_masks;
    true_grandparents_lists_no_masks = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    generate_grandparents_lists(true_grandparents_lists_no_masks,true_parents_lists_no_masks,number_of_volumes,verbal);
    
    // The destinations lists are generated without taking masks into account (they are removed from the overlap list in an early step)
    //generate_destinations_lists(grandparents_lists_no_masks,parents_lists_no_masks,true_overlap_lists,Volumes,number_of_volumes,verbal);
    generate_destinations_lists_experimental(true_overlap_lists, true_children_lists, true_parents_lists_no_masks, true_grandparents_lists_no_masks, Volumes, number_of_volumes, verbal);
    
    // Obsolete, found a way around them in within_which_volume, but need to test the performance difference
    // generate_destinations_logic_lists(Volumes,number_of_volumes,verbal);
    
    generate_reduced_destinations_lists(parents_lists,Volumes,number_of_volumes,verbal);
    
    generate_direct_children_lists(parents_lists,Volumes,number_of_volumes,verbal);
    
    //generate_starting_list(starting_lists,Volumes,number_of_volumes,verbal);
    generate_starting_logic_list(starting_lists,Volumes,number_of_volumes,verbal);
    
    generate_reduced_starting_destinations_list(starting_lists,parents_lists,Volumes,number_of_volumes,verbal);
    
    // This list is stored with the volumes for convinience, but is only used for tagging
    generate_next_volume_list(Volumes,number_of_volumes,verbal);
    
    // Garbage collection for temporary dynamically allocated lists. (Permanent lists freed from FINALLY)
    int iterate;
    for (iterate=0;iterate<number_of_volumes;iterate++) {
        //printf("freeing for volume nr %d\n",iterate);
        //printf("true_overlap_lists[iterate]->num_elements = %d \n",true_overlap_lists[iterate]->num_elements);
        if (true_overlap_lists[iterate]->num_elements > 0) free(true_overlap_lists[iterate]->elements);
        free(true_overlap_lists[iterate]);
        
        //printf("raw_overlap_lists[iterate]->num_elements = %d \n",raw_overlap_lists[iterate]->num_elements);
        if (raw_overlap_lists[iterate]->num_elements > 0) free(raw_overlap_lists[iterate]->elements);
        free(raw_overlap_lists[iterate]);
        
        //printf("parents_lists[iterate]->num_elements = %d \n",parents_lists[iterate]->num_elements);
        if (parents_lists[iterate]->num_elements > 0) free(parents_lists[iterate]->elements);
        free(parents_lists[iterate]);
        
        //printf("parents_lists_no_masks[iterate]->num_elements = %d \n",parents_lists_no_masks[iterate]->num_elements);
        if (parents_lists_no_masks[iterate]->num_elements > 0) free(parents_lists_no_masks[iterate]->elements);
        free(parents_lists_no_masks[iterate]);
        
        //printf("true_parents_lists[iterate]->num_elements = %d \n",true_parents_lists[iterate]->num_elements);
        if (true_parents_lists[iterate]->num_elements > 0) free(true_parents_lists[iterate]->elements);
        free(true_parents_lists[iterate]);
        
        //printf("true_parents_lists_no_masks[iterate]->num_elements = %d \n",true_parents_lists_no_masks[iterate]->num_elements);
        if (true_parents_lists_no_masks[iterate]->num_elements > 0) free(true_parents_lists_no_masks[iterate]->elements);
        free(true_parents_lists_no_masks[iterate]);
        
        //printf("grandparents_lists[iterate]->num_elements = %d \n",grandparents_lists[iterate]->num_elements);
        if (grandparents_lists[iterate]->num_elements > 0) free(grandparents_lists[iterate]->elements);
        free(grandparents_lists[iterate]);
        
        //printf("true_grandparents_lists[iterate]->num_elements = %d \n",true_grandparents_lists[iterate]->num_elements);
        if (true_grandparents_lists[iterate]->num_elements > 0) free(true_grandparents_lists[iterate]->elements);
        free(true_grandparents_lists[iterate]);
        
        //printf("grandparents_lists_no_masks[iterate]->num_elements = %d \n",grandparents_lists_no_masks[iterate]->num_elements);
        if (grandparents_lists_no_masks[iterate]->num_elements > 0) free(grandparents_lists_no_masks[iterate]->elements);
        free(grandparents_lists_no_masks[iterate]);
        
        //printf("true_grandparents_lists_no_masks[iterate]->num_elements = %d \n",true_grandparents_lists_no_masks[iterate]->num_elements);
        if (true_grandparents_lists_no_masks[iterate]->num_elements > 0) free(true_grandparents_lists_no_masks[iterate]->elements);
        free(true_grandparents_lists_no_masks[iterate]);
        
        //printf("true_children_lists[iterate]->num_elements = %d \n",true_children_lists[iterate]->num_elements);
        if (true_children_lists[iterate]->num_elements > 0) free(true_children_lists[iterate]->elements);
        free(true_children_lists[iterate]);
    }
    //printf("generate lists volume specific free completed\n");
    free(true_overlap_lists);free(raw_overlap_lists);free(parents_lists);free(true_parents_lists);free(true_parents_lists_no_masks);
    free(parents_lists_no_masks);free(true_grandparents_lists);free(grandparents_lists);free(grandparents_lists_no_masks);free(true_grandparents_lists_no_masks);
    free(true_children_lists);
    //printf("generate lists free completed\n");
};

// -------------    Focusing functions   --------------------------------------------------------

// The focusing_data structure is set up by the geometry component, and a pointer to the appropriate
//  focusing function is added to the Volume structure (for this reason the input of all the functions
//  need to be identical, at least in terms of types). In this way there are no if statements to check
//  which of these to be used in the trace, but the focus_data_struct will carry some redundant
//  information, as only the appropriate parameters are set:
// Angular focus on a rectangle (angular_focus_height / angular_focus_width)
// Spatial focus on a rectangle (spatial_focus_height / spatial_focus_width)
// Spatial focus on a disk (sptial_focus_radius)
// No focus (randvec in 4pi) (all set to zero, will select randvec circle as it is slightly faster
//
// When adding a new physical process focusing becomes very easy, as one just calls the master focusing
//  function assosiated with the volume (placed in the geometry struct), using the focus_data_struct
//  also found in the geometry struct, and the process then supports all the focusing modes. It is even
//  possible to add new focusing modes in the future by updating just the geometry components, and this
//  section.

// focus_data_struct definitioon shown here, defined at the start of this file
//struct focus_data_struct {
//Coords Aim;
//double angular_focus_width;
//double angular_focus_height;
//double spatial_focus_width;
//double spatial_focus_height;
//double spatial_focus_radius;
//Rotation absolute_rotation;
//// focusing_function creates a vector per selected criteria of focus_data_struct / selected focus function and returns solid angle
//void (*focusing_function)(Coords*, double*, struct focus_data_struct*);
////                        v_out  , solid_a,
//};

void randvec_target_rect_angular_union(Coords *v_out,double *solid_angle_out, struct focus_data_struct *focus_data) {
    // Calls the standard McStas randvec_target_rect_angular focusing function, but is with the new data input format.
    randvec_target_rect_angular(&v_out->x, &v_out->y, &v_out->z, solid_angle_out, focus_data->Aim.x,focus_data->Aim.y, focus_data->Aim.z, focus_data->angular_focus_width, focus_data->angular_focus_height,focus_data->absolute_rotation);
    //randvec_target_rect_angular(&vx, &vy, &vz, &solid_angle,aim_x, aim_y, aim_z, VarsInc.aw, VarsInc.ah, ROT_A_CURRENT_COMP);
};

void randvec_target_rect_union(Coords *v_out,double *solid_angle_out, struct focus_data_struct *focus_data) {
// Calls the standard McStas randvec_target_rect focusing function, but is with the new data input format.
    randvec_target_rect(&v_out->x, &v_out->y, &v_out->z, solid_angle_out, focus_data->Aim.x,focus_data->Aim.y, focus_data->Aim.z, focus_data->spatial_focus_width, focus_data->spatial_focus_height,focus_data->absolute_rotation);
    // randvec_target_rect(&vx, &vy, &vz, &solid_angle,aim_x, aim_y, aim_z, VarsInc.xw, VarsInc.yh, ROT_A_CURRENT_COMP);
};

void randvec_target_circle_union(Coords *v_out,double *solid_angle_out, struct focus_data_struct *focus_data) {
// Calls the standard McStas randvec_target_circle focusing function, but is with the new data input format.

    // debug input into randvec_target_circle
    //print_position(focus_data->Aim,"Aim vector input for randvec_target_circle");
    //printf("Radius input %f\n",focus_data->spatial_focus_radius);

    randvec_target_circle(&v_out->x, &v_out->y, &v_out->z, solid_angle_out, focus_data->Aim.x,focus_data->Aim.y, focus_data->Aim.z, focus_data->spatial_focus_radius);
    //randvec_target_circle(&vx, &vy, &vz, &solid_angle, aim_x, aim_y, aim_z, focus_r);
};



void focus_initialize(struct geometry_struct *geometry, Coords POS_A_TARGET, Coords POS_A_CURRENT, Rotation ROT_A_CURRENT, int target_index, double target_x, double target_y, double target_z, double angular_focus_width, double angular_focus_height, double spatial_focus_width, double spatial_focus_height, double spatial_focus_radius, char *component_name) {
    // Initialize focusing system
    // target_x/y/z needs to be double setting parameters with default value 0
    // target_index needs to be int setting parameter with default value 0
    // angular_focus_width, angular_focus_height, spatial_focus_width, spatial_focus_height, spatial_focus_radius nneds to be double setting parameters with default value 0
    // When those conditions are met, this code will identify which settings have been entered by the user and select the appropriate focusing parameters, which are loaded into the focus_data struct and the geometry struct.
    // The aim vector in the focus_data struct will be transformed from the local coordinate system of the geometry component to the coordinate system of the master component during the master component initialize

  // Input sanitation
  if (angular_focus_width < 0) {
    printf("\nERROR in Union geometry component named \"%s\", angular focus width focus_aw < 0! \n",component_name);
    exit(1);
  }
  if (angular_focus_height < 0) {
    printf("\nERROR in Union geometry component named \"%s\", angular focus width focus_ah < 0! \n",component_name);
    exit(1);
  }
  if (spatial_focus_width < 0) {
    printf("\nERROR in Union geometry component named \"%s\", spatial focus width focus_xw < 0! \n",component_name);
    exit(1);
  }
  if (spatial_focus_height < 0) {
    printf("\nERROR in Union geometry component named \"%s\", spatial focus height focus_xh < 0! \n",component_name);
    exit(1);
  }
  if (spatial_focus_radius < 0) {
    printf("\nERROR in Union geometry component named \"%s\", spatial focus radius focus_r < 0! \n",component_name);
    exit(1);
  }
  
  struct focus_data_struct focus_data;

  // Initialize focus_data_struct
  /*
  geometry->focus_data.Aim = coords_set(0,0,0);
  geometry->focus_data.angular_focus_width = 0;
  geometry->focus_data.angular_focus_height = 0;
  geometry->focus_data.spatial_focus_width = 0;
  geometry->focus_data.spatial_focus_height = 0;
  geometry->focus_data.spatial_focus_radius = 0;
  rot_copy(geometry->focus_data.absolute_rotation,ROT_A_CURRENT);
  */
  focus_data.Aim = coords_set(0,0,0);
  focus_data.angular_focus_width = 0;
  focus_data.angular_focus_height = 0;
  focus_data.spatial_focus_width = 0;
  focus_data.spatial_focus_height = 0;
  focus_data.spatial_focus_radius = 0;
  rot_copy(focus_data.absolute_rotation,ROT_A_CURRENT);

  // Built on code from Incoherent.comp by Kim Lefmann and Kristian Nielsen
  if (target_index != 0 && !target_x && !target_y && !target_z)
  {
    Coords ToTarget;
    //ToTarget = coords_sub(POS_A_COMP_INDEX(INDEX_CURRENT_COMP+target_index),POS_A_CURRENT_COMP);
    ToTarget = coords_sub(POS_A_TARGET,POS_A_CURRENT);
    //ToTarget = rot_apply(ROT_A_CURRENT_COMP, ToTarget);
    ToTarget = rot_apply(ROT_A_CURRENT, ToTarget);
    coords_get(ToTarget, &focus_data.Aim.x, &focus_data.Aim.y, &focus_data.Aim.z);
  }
  else
  { focus_data.Aim.x = target_x; focus_data.Aim.y = target_y; focus_data.Aim.z = target_z; }
  
  if (!(focus_data.Aim.x || focus_data.Aim.y || focus_data.Aim.z)) {
    // Somehow set a variable to signify scattering into 4pi
    // printf("Union %s: The target is not defined. Using scattering into 4pi.\n",NAME_CURRENT_COMP);
    focus_data.Aim.z=1; // set aim to one so that the randvec output vector has length 1 instead of 0
  }

  int focusing_model_selected = 0;
  if (angular_focus_width != 0 && angular_focus_height != 0) {
    focus_data.focusing_function = &randvec_target_rect_angular_union;
    focus_data.angular_focus_width = DEG2RAD*angular_focus_width; // Convert to radians here
    focus_data.angular_focus_height = DEG2RAD*angular_focus_height;
    focusing_model_selected = 1;
  }
  if (spatial_focus_width != 0 && spatial_focus_height != 0) {
    focus_data.focusing_function = &randvec_target_rect_union;
    focus_data.spatial_focus_width = spatial_focus_width;
    focus_data.spatial_focus_height = spatial_focus_height;
    if (focusing_model_selected) {
        printf("ERROR %s: Select either angular or spatial focusing, not both! Exiting \n",component_name);
        exit(1);
    }
    focusing_model_selected = 1;
  }
  if (spatial_focus_radius != 0) {
    focus_data.focusing_function = &randvec_target_circle_union;
    focus_data.spatial_focus_radius = spatial_focus_radius;
    if (focusing_model_selected) {
        printf("ERROR %s: Select a maximum of one focusing method (spatial rectangle or cicle, or angular rectangle! Exiting \n",component_name);
        exit(1);
    }
    focusing_model_selected = 1;
  }
  if (focusing_model_selected == 0) {
    // Select 4pi focusing
    focus_data.spatial_focus_radius = 0;
    focus_data.focusing_function = &randvec_target_circle_union;
  }
  
  // Allocate the isotropic focus_data struct
  geometry->focus_data_array.num_elements = 0;
  //geometry->focus_data_array.elements = malloc(sizeof(focus_data_struct));
  add_element_to_focus_data_array(&geometry->focus_data_array,focus_data);
};


struct abs_event{
    double time1;
    double position1[3];
    double time2;
    double position2[3];
    double weight_change;
    int volume_index;
    int neutron_id;
};

// Functions for recording absorption
void initialize_absorption_file() {
  FILE *fp;
  fp = fopen("Union_absorption.dat","w");
  fprintf(fp,"r_old x, r_old y, r_old z, old t, r x, r y, r z, new t, weight change, volume index, neutron id \n");
  
  fclose(fp);
}

void write_events_to_file(int last_index, struct abs_event *events) {

  FILE *fp;
  fp = fopen("Union_absorption.dat","a");
  
  struct abs_event *this_event;
  int iterate;
  for (iterate=0; iterate<last_index; iterate++) {
  
    this_event = &events[iterate];
    fprintf(fp,"%g, %g, %g, %g, %g, %g, %g, %g, %e, %i, %i \n",
           this_event->position1[0], this_event->position1[1], this_event->position1[2], this_event->time1,
           this_event->position2[0], this_event->position2[1], this_event->position2[2], this_event->time2,
           this_event->weight_change,
           this_event->volume_index,
           this_event->neutron_id);
  }

  fclose(fp);
}

void record_abs_to_file(double *r, double t1, double *r_old, double t2, double weight_change, int volume, int neutron_id, int *data_index, struct abs_event *events) {
  
  
  
  struct abs_event *this_event;
  
  this_event = &events[(*data_index)++];
  
  //printf("Recording something! %i\n", *data_index);
  
  this_event->position1[0] = r[0];
  this_event->position1[1] = r[1];
  this_event->position1[2] = r[2];
  this_event->time1 = t1;
  this_event->position2[0] = r_old[0];
  this_event->position2[1] = r_old[1];
  this_event->position2[2] = r_old[2];
  this_event->time2 = t2;
  this_event->weight_change = weight_change;
  this_event->volume_index = volume;
  this_event->neutron_id = neutron_id;
  
  
  if (*data_index == 999) {
    
      write_events_to_file(*data_index, events);
      *data_index = 0;

  }

};




/*******************************************************************************
*
*  McStas, neutron ray-tracing package
*  Copyright(C) 2007 Risoe National Laboratory.
*
* %I
* Written by: Mads Bertelsen
* Date: 20.08.15
* Version: $Revision: 0.1 $
* Origin: University of Copenhagen
*
* A sample component to separate geometry and phsysics
*
* %D
* Alpha version, no input system yet
* Hardcode input to geometry engine
* Allows complicated geometry by combination of simple shapes
*
* Algorithm:
* Described elsewhere
*
* %P
* INPUT PARAMETERS:
* radius:  [m] Outer radius of sample in (x,z) plane
*
* OUTPUT PARAMETERS:
* V_rho:  [AA^-3] Atomic density
*
* %L
* The test/example instrument <a href="../examples/Test_Phonon.instr">Test_Phonon.instr</a>.
*
* %E
******************************************************************************/


// This file sets up global lists needed for Union components to communicate with each other
// These all have dynamically allocated memory somewhere in the structure, which is deallocated
//  by the last Union_master.



// Initialize global positions / rotations to transform lists
  // These are lists of pointers to positons / rotations, that will be updated from global frame
  //  to the frame of the master component that uses them in that masters initialize section.
  struct global_positions_to_transform_list_struct global_positions_to_transform_list = {0,NULL};
  struct global_rotations_to_transform_list_struct global_rotations_to_transform_list = {0,NULL};

// Initialize global_process_list
  // Used to facilitate communication between processes and the other types of Union components
  struct pointer_to_global_process_list global_process_list = {0,NULL};

// Initialize global_material_list
  // Used to facilitate communication between materials and the other types of Union components
  struct pointer_to_global_material_list global_material_list = {0,NULL};

// Initialize global_geometry_list
  // Used to facilitate communication between geometries and the other types of Union components
  struct pointer_to_global_geometry_list global_geometry_list = {0,NULL};

// Initialize global_logger_lists
  // Used to facilitate communication between loggers and the other types of Union components
  struct pointer_to_global_logger_list global_all_volume_logger_list = {0,NULL};
  struct pointer_to_global_logger_list global_specific_volumes_logger_list = {0,NULL};

// Initialize global_abs_logger_lists
  struct pointer_to_global_abs_logger_list global_all_volume_abs_logger_list = {0,NULL};
  struct pointer_to_global_abs_logger_list global_specific_volumes_abs_logger_list = {0,NULL};

// Initialize global_tagging_conditional_list
  // Used to facilitate communication between conditionals and the other types of Union components
  struct global_tagging_conditional_list_struct global_tagging_conditional_list = {0,0,NULL};

// Initialize global_master_list
  // Used to facilitate communication between Master components (mainly for deallocation)
  struct pointer_to_global_master_list global_master_list = {0,NULL};

// Initialize global_mantid_min_pixel_id
  // Used for ensuring pixel id's on Mantid monitors do not overlap
  int global_mantid_min_pixel_id = 0;


#endif


struct Incoherent_physics_storage_struct{
    // Variables that needs to be transfered between any of the following places:
    // The initialize in this component
    // The function for calculating my
    // The function for calculating scattering
    
    double my_scattering;
    double QE_sampling_frequency;
    double lorentzian_width;
    
};

// Function for calculating my in Incoherent case
int Incoherent_physics_my(double *my,double *k_initial, union data_transfer_union data_transfer, struct focus_data_struct *focus_data, _class_particle *_particle) {
    *my = data_transfer.pointer_to_a_Incoherent_physics_storage_struct->my_scattering;
    return 1;
};

// Function for basic incoherent scattering event
int Incoherent_physics_scattering(double *k_final, double *k_initial, double *weight, union data_transfer_union data_transfer, struct focus_data_struct *focus_data, _class_particle *_particle) {

    //New version of incoherent scattering
    double k_length = sqrt(k_initial[0]*k_initial[0]+k_initial[1]*k_initial[1]+k_initial[2]*k_initial[2]);

    Coords k_out;
    // Here is the focusing system in action, get a vector
    double solid_angle;
    focus_data->focusing_function(&k_out,&solid_angle,focus_data);
    NORM(k_out.x,k_out.y,k_out.z);
    *weight *= solid_angle*0.25/PI;
    
    double v_i,v_f,E_i,dE,E_f;
    
    if (rand01() < data_transfer.pointer_to_a_Incoherent_physics_storage_struct->QE_sampling_frequency) {
      v_i = k_length * K2V;
      E_i = VS2E*v_i*v_i;
      dE = data_transfer.pointer_to_a_Incoherent_physics_storage_struct->lorentzian_width*tan(PI/2*randpm1());
      E_f = E_i + dE;
      if (E_f <= 0)
        return 0;
      v_f = SE2V*sqrt(E_f);
      k_length = v_f*V2K;
    }
    
    k_final[0] = k_out.x*k_length; k_final[1] = k_out.y*k_length; k_final[2] = k_out.z*k_length;
    return 1;
};

#ifndef PROCESS_DETECTOR
    #define PROCESS_DETECTOR dummy
#endif

#ifndef PROCESS_INCOHERENT_DETECTOR
    #define PROCESS_INCOHERENT_DETECTOR dummy
#endif

/* Shared user declarations for all components types 'Powder_process'. */
#ifndef Union
#define Union $Revision: 0.8 $

/*******************************************************************************
*
*  McStas, neutron ray-tracing package
*  Copyright(C) 2007 Risoe National Laboratory.
*
* %I
* Written by: Mads Bertelsen
* Date: 20.08.15
* Version: $Revision: 0.1 $
* Origin: University of Copenhagen
*
* Functions and structure definitons for Union components.
*
******************************************************************************/

// -------------    Definition of data structures   ---------------------------------------------
// GPU
enum shape {
  surroundings,
  box,
  sphere,
  cylinder,
  cone,
  mesh
};

enum process {
  Incoherent,
  Powder,
  Single_crystal,
  AF_HB_1D,
  PhononSimple,
  Texture,
  IncoherentPhonon,
  NCrystal,
  Template
};

struct intersection_time_table_struct {
int num_volumes;
int *calculated;
int *n_elements;
double **intersection_times;
};

struct line_segment{
//struct position point1;
//struct position point2;
Coords point1;
Coords point2;
int number_of_dashes;
};

struct pointer_to_1d_int_list {
int num_elements;
int *elements;
#pragma acc shape(elements[0:num_elements]) init_needed(num_elements)
};

struct pointer_to_1d_double_list {
int num_elements;
double *elements;
#pragma acc shape(elements[0:num_elements]) init_needed(num_elements)
};

struct pointer_to_1d_coords_list {
int num_elements;
Coords *elements;
#pragma acc shape(elements[0:num_elements]) init_needed(num_elements)
};

struct lines_to_draw{
int number_of_lines;
struct line_segment *lines;
#pragma acc shape(lines[0:number_of_lines]) init_needed(number_of_lines)
};

// 2D lists not needed anyway
// struct pointer_to_2d_int_list {
// int n_lists;
// struct pointer_to_1d_int_list *lists;
// }

// Todo: see if the union geometry_parameter_union and other geometry structs can be here
union geometry_parameter_union{
    struct sphere_storage   *p_sphere_storage;
    struct cylinder_storage *p_cylinder_storage;
    struct box_storage      *p_box_storage;
    struct cone_storage     *p_cone_storage;
    struct mesh_storage     *p_mesh_storage;
    // add as many pointers to structs as wanted, without increasing memory footprint.
};


struct rotation_struct{
double x;
double y;
double z;
};

struct focus_data_struct {
Coords Aim;
double angular_focus_width;
double angular_focus_height;
double spatial_focus_width;
double spatial_focus_height;
double spatial_focus_radius;
Rotation absolute_rotation;
// focusing_function creates a vector per selected criteria of focus_data_struct / selected focus function and returns solid angle
void (*focusing_function)(Coords*, double*, struct focus_data_struct*);
//                        v_out  , solid_a,
};

struct focus_data_array_struct
{
struct focus_data_struct *elements;
int num_elements;
#pragma acc shape(elements[0:num_elements]) init_needed(num_elements)
};

struct Detector_3D_struct {
  char title_string[256];
  char string_axis_1[256];
  char string_axis_2[256];
  char string_axis_3[256];
  char Filename[256];
  double D1min;
  double D1max;
  double D2min;
  double D2max;
  double D3min;
  double D3max;
  double bins_1; // McStas uses doubles for bin numbers for some reason
  double bins_2;
  double bins_3;
  double ***Array_N; // McStas uses doubles for number of rays in each bin for some reason
  double ***Array_p;
  double ***Array_p2;
};

struct Detector_2D_struct {
  char title_string[256];
  char string_axis_1[256];
  char string_axis_2[256];
  char Filename[256];
  double D1min;
  double D1max;
  double D2min;
  double D2max;
  double bins_1; // McStas uses doubles for bin numbers for some reason
  double bins_2;
  double **Array_N; // McStas uses doubles for number of rays in each bin for some reason
  double **Array_p;
  double **Array_p2;
};

struct Detector_1D_struct {
  char title_string[256];
  char string_axis[256];
  char string_axis_short[64];
  char string_axis_value[256];
  char Filename[256];
  double min;
  double max;
  double bins; // McStas uses doubles for bin numbers for some reason
  double *Array_N; // McStas uses doubles for number of rays in each bin for some reason
  double *Array_p;
  double *Array_p2;
};


union logger_data_union{
  struct a_2DQ_storage_struct *p_2DQ_storage;
  struct a_2DS_storage_struct *p_2DS_storage;
  struct a_3DS_storage_struct *p_3DS_storage;
  struct a_1D_storage_struct *p_1D_storage;
  struct a_2DS_t_storage_struct *p_2DS_t_storage;
  struct a_2D_kf_storage_struct *p_2D_kf_storage;
  struct a_2D_kf_t_storage_struct *p_2D_kf_t_storage;
  // Additional logger storage structs to be addedd
};

struct logger_with_data_struct {
  int used_elements;
  int allocated_elements;
  struct logger_struct **logger_pointers;
};

// logger_pointer_struct
// contains pointers to the different logger functions and it's data union
struct logger_pointer_set_struct {
  // The logger has two record functions, an active and an inactive. Normally the active one will be to permanent storage,
  //  but if a conditional has been defined, it can switch the two, making the active one recording to temporary, which
  //  can then be filtered based on the future path of the ray
  
  // function input Coords position, k[3], k_old[3], p, p_old, NV, NPV, N, logger_data_union, logger_with_data_struct
  void (*active_record_function)(Coords*, double*, double*, double, double, double, int, int, int, struct logger_struct*, struct logger_with_data_struct*);
  void (*inactive_record_function)(Coords*, double*, double*, double, double, double, int, int, int, struct logger_struct*, struct logger_with_data_struct*);
  
  // A clear temporary data function (for new ray)
  void (*clear_temp)(union logger_data_union*);
  
  // Write temporary to permanent is used when the record to temp function is active, and the condition is met.
  void (*temp_to_perm)(union logger_data_union*);
  
  // Write temporary final_p to permanent is used when the record to temp function is active, and the condition is met
  //  and the final weight is given to be used for all stored events in the logger.
  void (*temp_to_perm_final_p)(union logger_data_union*, double);
  
  // Select which temp_to_perm function to use
  int select_t_to_p; // 1: temp_to_perm, 2: temp_to_perm_final_p
  
};

union abs_logger_data_union{
  struct a_2D_abs_storage_struct *p_2D_abs_storage;
  struct a_1D_abs_storage_struct *p_1D_abs_storage;
  struct a_1D_time_abs_storage_struct *p_1D_time_abs_storage;
  struct a_1D_time_to_lambda_abs_storage_struct *p_1D_time_to_lambda_abs_storage;
  struct a_event_abs_storage_struct *p_event_abs_storage;
  struct a_1D_event_abs_storage_struct *p_1D_event_abs_storage;
  // Additional logger storage structs to be addedd
};

struct abs_logger_with_data_struct {
  int used_elements;
  int allocated_elements;
  struct abs_logger_struct **abs_logger_pointers;
};

struct abs_logger_pointer_set_struct {
  // The logger has two record functions, an active and an inactive. Normally the active one will be to permanent storage,
  //  but if a conditional has been defined, it can switch the two, making the active one recording to temporary, which
  //  can then be filtered based on the future path of the ray
  
  // function input Coords position, k[3], p, NV, N, logger_data_union, logger_with_data_struct
  void (*active_record_function)(Coords*, double*, double,  double, int, int, struct abs_logger_struct*, struct abs_logger_with_data_struct*);
  void (*inactive_record_function)(Coords*, double*, double,  double, int, int, struct abs_logger_struct*, struct abs_logger_with_data_struct*);
  
  // A clear temporary data function (for new ray)
  void (*clear_temp)(union abs_logger_data_union*);
  
  // Write temporary to permanent is used when the record to temp function is active, and the condition is met.
  void (*temp_to_perm)(union abs_logger_data_union*);
  
  // Write temporary final_p to permanent is used when the record to temp function is active, and the condition is met
  //  and the final weight is given to be used for all stored events in the logger.
  void (*temp_to_perm_final_p)(union abs_logger_data_union*, double);
  
  // Select which temp_to_perm function to use
  //int select_t_to_p; // 1: temp_to_perm, 2: temp_to_perm_final_p
  
};


struct conditional_standard_struct{
  // Data to be transfered to the conditional function
  double Emax;
  double Emin;
  int E_limit;
  
  double Tmin;
  double Tmax;
  int T_limit;
  
  int volume_index;
  
  double Total_scat_max;
  double Total_scat_min;
  int Total_scat_limit;
  
  double exit_volume_index;
  
  // Test
  Coords test_position;
  Rotation test_rotation;
  Rotation test_t_rotation;
};

struct conditional_PSD_struct{
  double PSD_half_xwidth;
  double PSD_half_yheight;
  
  double Tmin;
  double Tmax;
  int T_limit;
  
  // Position of the PSD
  Coords PSD_position;
  Rotation PSD_rotation;
  Rotation PSD_t_rotation;
};

union conditional_data_union {
  struct conditional_standard_struct *p_standard;
  struct conditional_PSD_struct *p_PSD;
  // Add more as conditional components are made
};

// General input for conditional functions: Position, Velocity/Wavevector, weight, time, total_scat, scattered_flag, scattered_flag_VP,
// Optional extras: data_union? tree base(s)? tree base for current ray?
typedef int (*conditional_function_pointer)(union conditional_data_union*,Coords*, Coords*, double*, double*, int*, int*, int*, int**);
//typedef int (**conditional_function_pointer_array)(union *conditional_data_union,Coords*, Coords*, double*, double*, int*, int*, int**);

struct conditional_list_struct{
  int num_elements;
  
  union conditional_data_union **p_data_unions;
  conditional_function_pointer *conditional_functions;
  //int (**conditional_functions)(Coords*, Coords*, double*, double*, int*, int*, int**);
};

struct logger_struct {
  char name[256];
  // Contains ponters to all the functions assosiated with this logger
  struct logger_pointer_set_struct function_pointers;
  // Contains hard copy of logger_data_union since the size is the same as a pointer.
  union logger_data_union data_union;
  
  int logger_extend_index; // Contain index conditional_extend_array defined in master that can be acsessed from extend section.
  
  struct conditional_list_struct conditional_list;
};


// To be stored in volume, a list of pointers to the relevant loggers corresponding to each process
struct logger_for_each_process_list {
  int num_elements;
  struct logger_struct **p_logger_process;
};

// List of logger_for_each_process_list
struct loggers_struct {
  int num_elements;
  struct logger_for_each_process_list *p_logger_volume;
  #pragma acc shape(p_logger_volume[0:num_elements]) init_needed(num_elements)
};


struct abs_logger_struct {
  char name[256];
  // Contains ponters to all the functions assosiated with this logger
  struct abs_logger_pointer_set_struct function_pointers;
  // Contains hard copy of logger_data_union since the size is the same as a pointer.
  union abs_logger_data_union data_union;
  
  // Position and rotation of the abs_logger
  Coords position;
  Rotation rotation;
  Rotation t_rotation;
  
  int abs_logger_extend_index; // Contain index conditional_extend_array defined in master that can be acsessed from extend section.
  
  struct conditional_list_struct conditional_list;
};

// To be stored in volume, a list of pointers to the relevant abs loggers corresponding to each process
/*
struct abs_logger_for_each_process_list {
  int num_elements;
  struct abs_logger_struct **p_abs_logger_process;
};
*/

// List of abs logger_for_each_process_list
struct abs_loggers_struct {
  int num_elements;
  //struct abs_logger_for_each_process_list *p_abs_logger_volume;
  struct abs_logger_struct **p_abs_logger;
};



struct geometry_struct
{
char shape[64];     // name of shape used (sphere, cylinder, box, off, ...)
enum shape eShape;  // enum with shape for flexible functions GPU
double priority_value;    // priority of the geometry
Coords center;      // Center position of volume, reported by components in global frame, updated to main frame in initialize
// Rotation of this volume
Rotation rotation_matrix; // rotation matrix of volume, reported by component in global frame, updated to main frame in initialize
Rotation transpose_rotation_matrix; // As above
// Array of prrotation matrixes for processes assigned to this volume (indexed by non_isotropic_rot_index in the processes)
Rotation *process_rot_matrix_array;             // matrix that transforms from main coordinate system to local process in this specific volume
Rotation *transpose_process_rot_matrix_array;   // matrix that transforms from local process in this specific volume to main coordinate system
int process_rot_allocated;  // Keeps track of allocation status of rot_matrix_array

struct rotation_struct rotation; // Not used, is the x y and z rotation angles.
int visualization_on; // If visualization_on is true, the volume will be drawn in mcdisplay, otherwise not
int is_exit_volume; // If is exit volume = 1, the ray will exit the component when it enters this volume.
int is_mask_volume; // 1 if volume itself is a mask (masking the ones in it's mask list), otherwise 0
int mask_index;
int is_masked_volume; // 1 if this volume is being masked by another volume, the volumes that mask it is in masked_by_list
int mask_mode; // ALL/ANY 1/2. In ALL mode, only parts covered by all masks is simulated, in ANY mode, area covered by just one mask is simulated
double geometry_p_interact; // fraction of rays that interact with this volume for each scattering (between 0 and 1, 0 for disable)
union geometry_parameter_union geometry_parameters; // relevant parameters for this shape
union geometry_parameter_union (*copy_geometry_parameters)(union geometry_parameter_union*);
struct focus_data_struct focus_data; // Used for focusing from this geometry

// New focus data implementation to remove the focusing bug for non-isotripic processes
struct focus_data_array_struct focus_data_array;
struct pointer_to_1d_int_list focus_array_indices; // Add 1D integer array with indecies for correct focus_data for each process


// intersect_function takes position/velocity of ray and parameters, returns time list
int (*intersect_function)(double*,int*,double*,double*,struct geometry_struct*);
//                        t_array,n_ar,r      ,v

// within_function that checks if the ray origin is within this volume
int (*within_function)(Coords,struct geometry_struct*);
//                     r,      parameters

// mcdisplay function, draws the geometry
//void (*mcdisplay_function)(struct lines_to_draw*,int,struct Volume_struct**,int);
void (*mcdisplay_function)(struct lines_to_draw*,int,struct geometry_struct**,int);
//                                lines          index             Geometries   N

void (*initialize_from_main_function)(struct geometry_struct*);

struct pointer_to_1d_coords_list (*shell_points)(struct geometry_struct*, int maximum_number_of_points);

// List of other volumes to be check when ray starts within this volume.
struct pointer_to_1d_int_list intersect_check_list;
// List of other volumes the ray may enter, if the ray intersects the volume itself.
struct pointer_to_1d_int_list destinations_list;
// The destinations list stored as a logic list which makes some tasks quicker. OBSOLETE
//struct pointer_to_1d_int_list destinations_logic_list;
// Reduced list of other volumes the ray may enter, if the ray intersects the volume itself.
struct pointer_to_1d_int_list reduced_destinations_list;
// List of other volumes that are within this volume
struct pointer_to_1d_int_list children;
// List of other volumes that are within this volume, but does not have any parents that are children of this volume
struct pointer_to_1d_int_list direct_children;
// List of next possible volumes (only used in tagging)
struct pointer_to_1d_int_list next_volume_list;
// List of volumes masked by this volume (usually empty)
struct pointer_to_1d_int_list mask_list;
// List of volumes masking this volume
struct pointer_to_1d_int_list masked_by_list;
// List of masks masking this volume (global mask indices)
struct pointer_to_1d_int_list masked_by_mask_index_list;
// Additional intersect lists dependent on mask status
//struct indexed_mask_lists_struct mask_intersect_lists;
// Simpler way of storing the mask_intersect_lists
struct pointer_to_1d_int_list mask_intersect_list;
};

struct physics_struct
{
char name[256]; // User defined material name
int interact_control;
int is_vacuum;
double my_a;
int number_of_processes;
// pointer to array of pointers to physics_sub structures that each describe a scattering process
struct scattering_process_struct *p_scattering_array;
};

union data_transfer_union{
    // List of pointers to storage structs for all supported physical processes
    struct Incoherent_physics_storage_struct  *pointer_to_a_Incoherent_physics_storage_struct;
    struct Powder_physics_storage_struct *pointer_to_a_Powder_physics_storage_struct;
    struct Single_crystal_physics_storage_struct *pointer_to_a_Single_crystal_physics_storage_struct;
    struct AF_HB_1D_physics_storage_struct *pointer_to_a_AF_HB_1D_physics_storage_struct;
    struct IncoherentPhonon_physics_storage_struct *pointer_to_a_IncoherentPhonon_physics_storage_struct;
    struct PhononSimpleNumeric_physics_storage_struct *pointer_to_a_PhononSimpleNumeric_storage_struct;
    struct PhononSimple_physics_storage_struct *pointer_to_a_PhononSimple_storage_struct;
    struct MagnonSimple_physics_storage_struct *pointer_to_a_MagnonSimple_storage_struct;
    struct Sans_spheres_physics_storage_struct *pointer_to_a_Sans_spheres_physics_storage_struct;
    struct Texture_physics_storage_struct *pointer_to_a_Texture_physics_storage_struct;
    struct NCrystal_physics_storage_struct *pointer_to_a_NCrystal_physics_storage_struct;
    struct Non_physics_storage_struct *pointer_to_a_Non_physics_storage_struct;
    struct Template_physics_storage_struct *pointer_to_a_Template_physics_storage_struct;
    // possible to add as many structs as wanted, without increasing memory footprint.
};



struct scattering_process_struct
{
char name[256];                // User defined process name
enum process eProcess;         // enum value corresponding to this process GPU
double process_p_interact;     // double between 0 and 1 that describes the fraction of events forced to undergo this process. -1 for disable
int non_isotropic_rot_index;   // -1 if process is isotrpic, otherwise is the index of the process rotation matrix in the volume
Rotation rotation_matrix;      // rotation matrix of process, reported by component in local frame, transformed and moved to volume struct in main

union data_transfer_union data_transfer; // The way to reach the storage space allocated for this process (see examples in process.comp files)

// probability_for_scattering_functions calculates this probability given k_i and parameters
int (*probability_for_scattering_function)(double*,double*,union data_transfer_union,struct focus_data_struct*, _class_particle *_particle);
//                                         prop,   k_i,   ,parameters               , focus data / function

// A scattering_function takes k_i and parameters, returns k_f
int (*scattering_function)(double*,double*,double*,union data_transfer_union,struct focus_data_struct*, _class_particle *_particle);
//                         k_f,    k_i,    weight, parameters               , focus data / function
};

struct Volume_struct
{
char name[256]; // User defined volume name
struct geometry_struct geometry;        // Geometry properties (including intersect functions, generated lists)
struct physics_struct *p_physics;       // Physical properties (list of scattering processes, absorption)
struct loggers_struct loggers;          // Loggers assosiated with this volume
struct abs_loggers_struct abs_loggers;  // Loggers assosiated with this volume
};

// example of calling a scattering process
// volume_pointer_list[3]->physics.scattering_process[5].probability_for_scattering_function(input,volume_pointer_list[3]->physics.scattering_process[5])

struct starting_lists_struct
{
struct pointer_to_1d_int_list allowed_starting_volume_logic_list;
struct pointer_to_1d_int_list reduced_start_list;
struct pointer_to_1d_int_list start_logic_list;
struct pointer_to_1d_int_list starting_destinations_list;
};

struct global_positions_to_transform_list_struct {
int num_elements;
Coords **positions;
};

struct global_rotations_to_transform_list_struct {
int num_elements;
Rotation **rotations;
};

struct global_process_element_struct
{
char name[128]; // Name of the process
int component_index;
struct scattering_process_struct *p_scattering_process;
};

struct pointer_to_global_process_list {
int num_elements;
struct global_process_element_struct *elements;
};

struct global_material_element_struct
{
char name[128];
int component_index;
struct physics_struct *physics;
};

struct pointer_to_global_material_list {
int num_elements;
struct global_material_element_struct *elements;
};

struct global_geometry_element_struct
{
char name[128];
int component_index;
int activation_counter;
int stored_copies;
int active;
struct Volume_struct *Volume;
};

struct pointer_to_global_geometry_list {
int num_elements;
struct global_geometry_element_struct *elements;
};

struct global_logger_element_struct {
char name[128];
int component_index;
struct logger_struct *logger;
};

struct pointer_to_global_logger_list {
int num_elements;
struct global_logger_element_struct *elements;
};

struct global_abs_logger_element_struct {
char name[128];
int component_index;
struct abs_logger_struct *abs_logger;
};

struct pointer_to_global_abs_logger_list {
int num_elements;
struct global_abs_logger_element_struct *elements;
};

struct global_tagging_conditional_element_struct {
struct conditional_list_struct conditional_list;
int extend_index;
char name[1024];
int use_status;
};

struct global_tagging_conditional_list_struct {
int num_elements;
int current_index;
struct global_tagging_conditional_element_struct *elements;
};


struct global_master_element_struct {
char name[128];
int component_index;
int stored_number_of_scattering_events; // TEST
struct conditional_list_struct *tagging_conditional_list_pointer;
};

struct pointer_to_global_master_list {
int num_elements;
struct global_master_element_struct *elements;
};


// -------------    Physics functions   ---------------------------------------------------------

//#include "Test_physics.c"
//#include "Incoherent_test.c"

// -------------    General functions   ---------------------------------------------------------
double distance_between(Coords position1,Coords position2) {
    return sqrt((position1.x-position2.x)*(position1.x-position2.x) +
                (position1.y-position2.y)*(position1.y-position2.y) +
                (position1.z-position2.z)*(position1.z-position2.z));
};



double length_of_3vector(double *r) {
        return sqrt(r[0]*r[0]+r[1]*r[1]+r[2]*r[2]);
    };

double length_of_position_vector(Coords point) {
        return sqrt(point.x*point.x+point.y*point.y+point.z*point.z);
    };

Coords make_position(double *r) {
    Coords temp;
    
    temp.x = r[0];temp.y = r[1];temp.z = r[2];
    return temp;
};

Coords coords_scalar_mult(Coords input,double scalar) {
    return coords_set(scalar*input.x,scalar*input.y,scalar*input.z);
};

double union_coords_dot(Coords vector1,Coords vector2) {
    return vector1.x*vector2.x + vector1.y*vector2.y + vector1.z*vector2.z;
}


int sum_int_list(struct pointer_to_1d_int_list list) {
    int iterate,sum = 0;
    for (iterate = 0;iterate < list.num_elements;iterate++) sum += list.elements[iterate];
    return sum;
    };
    
int on_int_list(struct pointer_to_1d_int_list list,int target) {
    int iterate,output=0;
    for (iterate = 0; iterate<list.num_elements ;iterate++) {
        if (list.elements[iterate] == target) output = 1;
    }
    return output;
    };

int find_on_int_list(struct pointer_to_1d_int_list list,int target) {
    int iterate;
    for (iterate = 0;iterate < list.num_elements;iterate++) {
        if (list.elements[iterate] == target) return iterate;
    }
    return -1;
    };
    
void on_both_int_lists(struct pointer_to_1d_int_list *list1, struct pointer_to_1d_int_list *list2, struct pointer_to_1d_int_list *common) {
    // Assume common.elements is allocated to a number of int's large enough to hold the resulting list
    int iterate,used_elements=0;
    for (iterate=0;iterate<list1->num_elements;iterate++) {
        if (on_int_list(*list2,list1->elements[iterate])) common->elements[used_elements++] = list1->elements[iterate];
    }
    common->num_elements = used_elements;
    };

void remove_element_in_list_by_index(struct pointer_to_1d_int_list *list,int index) {
    if (index >= list->num_elements) {
      printf("ERROR(remove_element_in_list_by_index): trying to remove an index that wasn't allocated to begin with");
      exit(1);
    }
    else {
        int iterate;
        int *temp;
        for (iterate = index;iterate < list->num_elements -1;iterate++) {
            list->elements[iterate] = list->elements[iterate+1];
        }
        list->num_elements--;
        //if (list->num_elements==0) printf("Making empty list!\n");
        temp = malloc(list->num_elements * sizeof(int));
        for (iterate = 0;iterate < list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
        free(list->elements);
        list->elements = malloc(list->num_elements * sizeof(int));
        for (iterate = 0;iterate < list->num_elements;iterate++) list->elements[iterate] = temp[iterate];
        free(temp);

    }
    };
    
void remove_element_in_list_by_value(struct pointer_to_1d_int_list *list,int value) {
    int iterate;
    for (iterate = 0;iterate < list->num_elements;iterate++) {
        if (list->elements[iterate] == value) remove_element_in_list_by_index(list,iterate);
    }
    };
    
void merge_lists(struct pointer_to_1d_int_list *result,struct pointer_to_1d_int_list *list1,struct pointer_to_1d_int_list *list2) {
    if (result->num_elements > 0) free(result->elements);
    result->num_elements = list1->num_elements + list2->num_elements;
    if (result->num_elements != 0) {
      result->elements = malloc(result->num_elements*sizeof(int));
      int iterate;
      for (iterate = 0;iterate < list1->num_elements;iterate++)
          result->elements[iterate] = list1->elements[iterate];
      for (iterate = 0;iterate < list2->num_elements;iterate++)
          result->elements[list1->num_elements+iterate] = list2->elements[iterate];
    }
    };

void add_element_to_double_list(struct pointer_to_1d_double_list *list,double value) {
    if (list->num_elements == 0) {
      list->num_elements++;
      list-> elements = malloc(list->num_elements*sizeof(double));
      list-> elements[0] = value;
    } else {
      double temp[list->num_elements];
      int iterate;
      for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
      free(list->elements);
      list->num_elements++;
      list-> elements = malloc(list->num_elements*sizeof(double));
      for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
      list->elements[list->num_elements-1] = value;
    }
    };

void add_element_to_int_list(struct pointer_to_1d_int_list *list,int value) {
    if (list->num_elements == 0) {
      list->num_elements++;
      list-> elements = malloc(list->num_elements*sizeof(int));
      list-> elements[0] = value;
    } else {
      int temp[list->num_elements];
      int iterate;
      for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
      free(list->elements);
      list->num_elements++;
      list-> elements = malloc(list->num_elements*sizeof(int));
      for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
      list->elements[list->num_elements-1] = value;
    }
    };

// Need to check if absolute_rotation is preserved correctly.
void add_element_to_focus_data_array(struct focus_data_array_struct *focus_data_array,struct focus_data_struct focus_data) {
    if (focus_data_array->num_elements == 0) {
      focus_data_array->num_elements++;
      focus_data_array-> elements = malloc(focus_data_array->num_elements*sizeof(struct focus_data_struct));
      focus_data_array-> elements[0] = focus_data;
    } else {
      struct focus_data_struct temp[focus_data_array->num_elements];
      int iterate;
      for (iterate=0;iterate<focus_data_array->num_elements;iterate++) temp[iterate] = focus_data_array->elements[iterate];
      free(focus_data_array->elements);
      focus_data_array->num_elements++;
      focus_data_array-> elements = malloc(focus_data_array->num_elements*sizeof(struct focus_data_struct));
      for (iterate=0;iterate<focus_data_array->num_elements-1;iterate++) focus_data_array->elements[iterate] = temp[iterate];
      focus_data_array->elements[focus_data_array->num_elements-1] = focus_data;
    }
    };


void add_to_logger_with_data(struct logger_with_data_struct *logger_with_data, struct logger_struct *logger) {
    // May reorder the order of the if conditions to avoid checking the == 0 for every single ray
    if (logger_with_data->allocated_elements == 0) {
        logger_with_data->allocated_elements = 5;
        logger_with_data->logger_pointers = malloc(logger_with_data->allocated_elements*sizeof(struct logger_struct*));
        logger_with_data->used_elements = 1;
        logger_with_data->logger_pointers[0] = logger;
    } else if (logger_with_data->used_elements > logger_with_data->allocated_elements-1) {
        struct logger_with_data_struct temp_logger_with_data;
        temp_logger_with_data.logger_pointers = malloc((logger_with_data->used_elements)*sizeof(struct logger_struct*));
        int iterate;
        for (iterate=0;iterate<logger_with_data->used_elements;iterate++) {
            temp_logger_with_data.logger_pointers[iterate]  = logger_with_data->logger_pointers[iterate];
        }
        free(logger_with_data->logger_pointers);
        logger_with_data->allocated_elements = logger_with_data->allocated_elements+5;
        logger_with_data->logger_pointers = malloc(logger_with_data->allocated_elements*sizeof(struct logger_struct*));
        for (iterate=0;iterate<logger_with_data->used_elements;iterate++) {
            logger_with_data->logger_pointers[iterate]  = temp_logger_with_data.logger_pointers[iterate];
        }

        logger_with_data->logger_pointers[logger_with_data->used_elements++] = logger;
        
        
    } else {
        logger_with_data->logger_pointers[logger_with_data->used_elements++] = logger;
    }
    
};

void add_to_abs_logger_with_data(struct abs_logger_with_data_struct *abs_logger_with_data, struct abs_logger_struct *abs_logger) {
    // May reorder the order of the if conditions to avoid checking the == 0 for every single ray
    if (abs_logger_with_data->allocated_elements == 0) {
        abs_logger_with_data->allocated_elements = 5;
        abs_logger_with_data->abs_logger_pointers = malloc(abs_logger_with_data->allocated_elements*sizeof(struct abs_logger_struct*));
        abs_logger_with_data->used_elements = 1;
        abs_logger_with_data->abs_logger_pointers[0] = abs_logger;
    } else if (abs_logger_with_data->used_elements > abs_logger_with_data->allocated_elements-1) {
        struct abs_logger_with_data_struct temp_abs_logger_with_data;
        temp_abs_logger_with_data.abs_logger_pointers = malloc((abs_logger_with_data->used_elements)*sizeof(struct abs_logger_struct*));
        int iterate;
        for (iterate=0;iterate<abs_logger_with_data->used_elements;iterate++) {
            temp_abs_logger_with_data.abs_logger_pointers[iterate]  = abs_logger_with_data->abs_logger_pointers[iterate];
        }
        free(abs_logger_with_data->abs_logger_pointers);
        abs_logger_with_data->allocated_elements = abs_logger_with_data->allocated_elements+5;
        abs_logger_with_data->abs_logger_pointers = malloc(abs_logger_with_data->allocated_elements*sizeof(struct abs_logger_struct*));
        for (iterate=0;iterate<abs_logger_with_data->used_elements;iterate++) {
            abs_logger_with_data->abs_logger_pointers[iterate] = temp_abs_logger_with_data.abs_logger_pointers[iterate];
        }

        abs_logger_with_data->abs_logger_pointers[abs_logger_with_data->used_elements++] = abs_logger;
        
        
    } else {
        abs_logger_with_data->abs_logger_pointers[abs_logger_with_data->used_elements++] = abs_logger;
    }
    
};


// Used typedef to avoid having to change this function later. May update others to use same phillosphy.
void add_function_to_conditional_list(struct conditional_list_struct *list,conditional_function_pointer new, union conditional_data_union *data_union) {
    if (list->num_elements == 0) {
      list->num_elements++;
      list->conditional_functions = malloc(list->num_elements*sizeof(conditional_function_pointer));
      list->p_data_unions = malloc(list->num_elements*sizeof(union conditional_data_union*));
      list->conditional_functions[0] = new;
      list->p_data_unions[0] = data_union;
    }
    else {
    conditional_function_pointer temp_fp[list->num_elements];
    union conditional_data_union *temp_du[list->num_elements];
    int iterate;
    // Could even get away with a shallow copy here instead of the loop, but it is not relevant for performance.
    for (iterate=0;iterate<list->num_elements;iterate++) {
      temp_fp[iterate] = list->conditional_functions[iterate];
      temp_du[iterate] = list->p_data_unions[iterate];
    }
    free(list->conditional_functions);
    free(list->p_data_unions);
    list->num_elements++;
    list->conditional_functions = malloc(list->num_elements*sizeof(conditional_function_pointer));
    list->p_data_unions = malloc(list->num_elements*sizeof(union conditional_data_union*));
    
    for (iterate=0;iterate<list->num_elements-1;iterate++) {
      list->conditional_functions[iterate] = temp_fp[iterate];
      list->p_data_unions[iterate] = temp_du[iterate];
    }
    list->conditional_functions[list->num_elements-1] = new;
    list->p_data_unions[list->num_elements-1] = data_union;
    }
};


// could make function that removes a element from a 1d_int_list, and have each list generation as a function that takes a copy of an overlap list

void print_1d_int_list(struct pointer_to_1d_int_list list,char *name) {
        int iterate;
        printf("LIST: ");printf("%s",name);printf(" = [");
        for (iterate = 0; iterate < list.num_elements; iterate++) {
            printf("%d",list.elements[iterate]);
            if (iterate < list.num_elements - 1) printf(",");
        }
        printf("]\n");
    };

void print_1d_double_list(struct pointer_to_1d_double_list list,char *name) {
        int iterate;
        printf("LIST: ");printf("%s",name);printf(" = [");
        for (iterate = 0; iterate < list.num_elements; iterate++) {
            printf("%f",list.elements[iterate]);
            if (iterate < list.num_elements - 1) printf(",");
        }
        printf("]\n");
    };

void print_position(Coords pos,char *name) {
    printf("POSITION: ");printf("%s",name);printf(" = (%f,%f,%f)\n",pos.x,pos.y,pos.z);
    };

void print_rotation(Rotation rot, char *name) {
    printf("ROT MATRIX: %s \n",name);
    printf("[%f %f %f]\n",rot[0][0],rot[0][1],rot[0][2]);
    printf("[%f %f %f]\n",rot[1][0],rot[1][1],rot[1][2]);
    printf("[%f %f %f]\n\n",rot[2][0],rot[2][1],rot[2][2]);
};
    
void allocate_list_from_temp(int num_elements,struct pointer_to_1d_int_list original,struct pointer_to_1d_int_list *new) {
        int iterate;
    
        new->num_elements = num_elements;
        if (num_elements > 0) {
            new->elements = malloc(num_elements*sizeof(int));
            for (iterate = 0;iterate < num_elements; iterate++) new->elements[iterate] = original.elements[iterate];
        } else new->elements = NULL;
    
    };

void allocate_logic_list_from_temp(int num_elements,struct pointer_to_1d_int_list original, struct pointer_to_1d_int_list *new) {
        // A logic list shares the same structure of a normal list, but instead of listing numbers, it is a list of yes / no (1/0)
        // Giving this function a list of [1 3 5] (and num_elements = 9) would return [0 1 0 1 0 1 0 0 0];
        int iterate;
    
        new->num_elements = num_elements;
        if (num_elements > 0) {
            new->elements = malloc(num_elements*sizeof(int));
            for (iterate = 0;iterate < num_elements;iterate++) new->elements[iterate] = 0;
            for (iterate = 0;iterate < original.num_elements;iterate++) {
                if (original.elements[iterate] < num_elements)
                    new->elements[original.elements[iterate]] = 1;
                else printf("Trying to allocate logical list without enough memory\n");
            }
        } else new->elements = NULL;
    };

/*
struct global_positions_to_transform_list_struct {
int num_elements;
Coords **positions;
}

struct global_rotations_to_transform_list_struct {
int num_elements;
Rotation **rotations;
}
*/

void add_position_pointer_to_list(struct global_positions_to_transform_list_struct *list, Coords *new_position_pointer) {
    if (list->num_elements == 0) {
      list->num_elements++;
      list->positions = malloc(list->num_elements*sizeof(Coords*));
      list->positions[0] = new_position_pointer;
    } else {
      Coords **temp;
      temp = malloc(list->num_elements*sizeof(Coords*));
      if (temp == NULL) printf("malloc failed in add_position_pointer_to_list for temp\n");
      
      int iterate;
      for (iterate=0;iterate<list->num_elements;iterate++)
        temp[iterate] = list->positions[iterate];
      free(list->positions);
      list->num_elements++;
      list->positions = malloc(list->num_elements*sizeof(Coords*));
      if (list->positions == NULL) printf("malloc failed in add_position_pointer_to_list for list->positions\n");
      
      for (iterate=0;iterate<list->num_elements-1;iterate++)
        list->positions[iterate] = temp[iterate];
      free(temp);
      list->positions[list->num_elements-1] = new_position_pointer;
    }
};

void add_rotation_pointer_to_list(struct global_rotations_to_transform_list_struct *list, Rotation *new_rotation_pointer) {
    if (list->num_elements == 0) {
      list->num_elements++;
      list->rotations = malloc(list->num_elements*sizeof(Rotation*));
      list->rotations[0] = new_rotation_pointer;
    } else {
      Rotation **temp;
      temp = malloc(list->num_elements*sizeof(Rotation*));
      int iterate;
      for (iterate=0;iterate<list->num_elements;iterate++)
        temp[iterate] = list->rotations[iterate];
      free(list->rotations);
      list->num_elements++;
      list->rotations = malloc(list->num_elements*sizeof(Rotation*));
      
      for (iterate=0;iterate<list->num_elements-1;iterate++)
        list->rotations[iterate] = temp[iterate];
      free(temp);
      list->rotations[list->num_elements-1] = new_rotation_pointer;
    }
};

void add_element_to_process_list(struct pointer_to_global_process_list *list,struct global_process_element_struct new_element) {
    if (list->num_elements == 0) {
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_process_element_struct));
    list-> elements[0] = new_element;
    }
    else {
    struct global_process_element_struct temp[list->num_elements];
    int iterate;
    for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
    free(list->elements);
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_process_element_struct));
    for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
    list->elements[list->num_elements-1] = new_element;
    }
};

void add_element_to_material_list(struct pointer_to_global_material_list *list,struct global_material_element_struct new_element) {
    if (list->num_elements == 0) {
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_material_element_struct));
    list-> elements[0] = new_element;
    }
    else {
    struct global_material_element_struct temp[list->num_elements];
    int iterate;
    for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
    free(list->elements);
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_material_element_struct));
    for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
    list->elements[list->num_elements-1] = new_element;
    }
};

void add_element_to_geometry_list(struct pointer_to_global_geometry_list *list,struct global_geometry_element_struct new_element) {
    if (list->num_elements == 0) {
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_geometry_element_struct));
    list-> elements[0] = new_element;
    }
    else {
    struct global_geometry_element_struct temp[list->num_elements];
    int iterate;
    for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
    free(list->elements);
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_geometry_element_struct));
    for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
    list->elements[list->num_elements-1] = new_element;
    }
};

void add_element_to_logger_list(struct pointer_to_global_logger_list *list,struct global_logger_element_struct new_element) {
    if (list->num_elements == 0) {
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_logger_element_struct));
    list-> elements[0] = new_element;
    }
    else {
    struct global_logger_element_struct temp[list->num_elements];
    int iterate;
    for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
    free(list->elements);
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_logger_element_struct));
    for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
    list->elements[list->num_elements-1] = new_element;
    }
};

void add_element_to_abs_logger_list(struct pointer_to_global_abs_logger_list *list, struct global_abs_logger_element_struct new_element) {
    if (list->num_elements == 0) {
      list->num_elements++;
      list->elements = malloc(list->num_elements*sizeof(struct global_abs_logger_element_struct));
      list->elements[0] = new_element;
    }
    else {
      struct global_abs_logger_element_struct temp[list->num_elements];
      int iterate;
      for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
      free(list->elements);
      list->num_elements++;
      list-> elements = malloc(list->num_elements*sizeof(struct global_abs_logger_element_struct));
      for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
      list->elements[list->num_elements-1] = new_element;
    }
};

void add_element_to_tagging_conditional_list(struct global_tagging_conditional_list_struct *list,struct global_tagging_conditional_element_struct new_element) {
    if (list->num_elements == 0) {
    list->num_elements++;
    list->elements = malloc(list->num_elements*sizeof(struct global_tagging_conditional_element_struct));
    list->elements[0] = new_element;
    }
    else {
    struct global_tagging_conditional_element_struct temp[list->num_elements];
    int iterate;
    for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
    free(list->elements);
    list->num_elements++;
    list->elements = malloc(list->num_elements*sizeof(struct global_tagging_conditional_element_struct));
    for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
    list->elements[list->num_elements-1] = new_element;
    }
};

void add_element_to_master_list(struct pointer_to_global_master_list *list,struct global_master_element_struct new_element) {
    if (list->num_elements == 0) {
    list->num_elements++;
    list->elements = malloc(list->num_elements*sizeof(struct global_master_element_struct));
    list->elements[0] = new_element;
    }
    else {
    struct global_master_element_struct temp[list->num_elements];
    int iterate;
    for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
    free(list->elements);
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_master_element_struct));
    for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
    list->elements[list->num_elements-1] = new_element;
    }
};

void add_initialized_logger_in_volume(struct loggers_struct *loggers,int number_of_processes) {
  int iterate;
  if (loggers->num_elements == 0) {
    loggers->num_elements++;
    loggers->p_logger_volume = malloc(loggers->num_elements * sizeof(struct logger_for_each_process_list));
    loggers->p_logger_volume[0].num_elements = number_of_processes;
    loggers->p_logger_volume[0].p_logger_process = malloc(number_of_processes * sizeof(struct logger_struct**));
    for (iterate=0;iterate<number_of_processes;iterate++)
      loggers->p_logger_volume[0].p_logger_process[iterate] = NULL;
  } else {
    // Already some elements, store them in temp, free main, transfer back and add newest.
    struct logger_for_each_process_list temp[loggers->num_elements];
    
    for (iterate=0;iterate<loggers->num_elements;iterate++) temp[iterate] = loggers->p_logger_volume[iterate];
    free(loggers->p_logger_volume);
    loggers->num_elements++;
    loggers->p_logger_volume = malloc(loggers->num_elements*sizeof(struct logger_for_each_process_list));
    for (iterate=0;iterate<loggers->num_elements-1;iterate++) loggers->p_logger_volume[iterate] = temp[iterate];
    loggers->p_logger_volume[loggers->num_elements-1].num_elements = number_of_processes;
    loggers->p_logger_volume[loggers->num_elements-1].p_logger_process = malloc(number_of_processes * sizeof(struct logger_struct**));
    for (iterate=0;iterate<number_of_processes;iterate++) {
      loggers->p_logger_volume[loggers->num_elements-1].p_logger_process[iterate] = NULL;
    }
  }
};

/*
void add_initialized_abs_logger_in_volume(struct abs_loggers_struct *abs_loggers, int number_of_processes) {
  int iterate;
  if (abs_loggers->num_elements == 0) {
    abs_loggers->num_elements++;
    abs_loggers->p_abs_logger_volume = malloc(abs_loggers->num_elements * sizeof(struct abs_loggers_struct));
    abs_loggers->p_abs_logger_volume[0].num_elements = number_of_processes;
    abs_loggers->p_abs_logger_volume[0].p_abs_logger_process = malloc(number_of_processes * sizeof(struct abs_logger_struct**));
    for (iterate=0;iterate<number_of_processes;iterate++)
      abs_loggers->p_abs_logger_volume[0].p_abs_logger_process[iterate] = NULL;
  } else {
    // Already some elements, store them in temp, free main, transfer back and add newest.
    struct abs_logger_for_each_process_list temp[abs_loggers->num_elements];
    
    for (iterate=0;iterate<abs_loggers->num_elements;iterate++) temp[iterate] = abs_loggers->p_abs_logger_volume[iterate];
    free(abs_loggers->p_abs_logger_volume);
    abs_loggers->num_elements++;
    abs_loggers->p_abs_logger_volume = malloc(abs_loggers->num_elements*sizeof(struct abs_logger_for_each_process_list));
    for (iterate=0;iterate<abs_loggers->num_elements-1;iterate++) abs_loggers->p_abs_logger_volume[iterate] = temp[iterate];
    abs_loggers->p_abs_logger_volume[abs_loggers->num_elements-1].num_elements = number_of_processes;
    abs_loggers->p_abs_logger_volume[abs_loggers->num_elements-1].p_abs_logger_process = malloc(number_of_processes * sizeof(struct abs_logger_struct**));
    for (iterate=0;iterate<number_of_processes;iterate++) {
      abs_loggers->p_abs_logger_volume[abs_loggers->num_elements-1].p_abs_logger_process[iterate] = NULL;
    }
  }
};
*/

void add_initialized_abs_logger_in_volume(struct abs_loggers_struct *abs_loggers) {
  int iterate;
  if (abs_loggers->num_elements == 0) {
    abs_loggers->num_elements++;
    abs_loggers->p_abs_logger = malloc(abs_loggers->num_elements * sizeof(struct abs_logger_struct*));
  } else {
    // Already some elements, store them in temp, free main, transfer back and add newest.
    struct abs_logger_struct *temp[abs_loggers->num_elements];
    
    for (iterate=0;iterate<abs_loggers->num_elements;iterate++) temp[iterate] = abs_loggers->p_abs_logger[iterate];
    free(abs_loggers->p_abs_logger);
    
    abs_loggers->num_elements++;
    abs_loggers->p_abs_logger = malloc(abs_loggers->num_elements*sizeof(struct abs_logger_struct*));
    for (iterate=0;iterate<abs_loggers->num_elements-1;iterate++) abs_loggers->p_abs_logger[iterate] = temp[iterate];
    abs_loggers->p_abs_logger[abs_loggers->num_elements-1] = NULL;
    
  }
};


// -------------    Functions used to shorten master trace    ---------------------------------------------


void update_current_mask_intersect_status(struct pointer_to_1d_int_list *current_mask_intersect_list_status, struct pointer_to_1d_int_list *mask_status_list, struct Volume_struct **Volumes, int *current_volume) {
  // This function is to be executed whenever the current volume changes, or the mask status changes
  // It updates the effective mask status for each element of a volumes mask_intersect_list
  // The effective mask takes the ALL/ANY mode assosiated with each volume into account,
  //  meaning a volume needs to be within ALL/ANY of it's masks to have a status of 1
  // In most cases this mask_intersect_list will be empty, and memory operations are avoided
  //printf("Number of elements to be check: %d \n",Volumes[*current_volume]->geometry.mask_intersect_list.num_elements);
  
  if (Volumes[*current_volume]->geometry.mask_intersect_list.num_elements > 0) {
    int iterate,this_element,*mask_start,*mask_check;
    for (iterate=0;iterate<Volumes[*current_volume]->geometry.mask_intersect_list.num_elements;iterate++) {
      this_element = Volumes[*current_volume]->geometry.mask_intersect_list.elements[iterate];
      //printf("We are investigating volume number %d from the mask_intersect_list of volume %d",this_element,*current_volume);
      if (Volumes[this_element]->geometry.mask_mode == 2) { // ANY mask mode
        //printf("We are in ANY mask mode! \n");
        current_mask_intersect_list_status->elements[iterate] = 0; // Assume the mask status is 0, but if any are 1, take that instead
        for (mask_start=mask_check=Volumes[this_element]->geometry.masked_by_mask_index_list.elements;mask_check-mask_start<Volumes[this_element]->geometry.masked_by_mask_index_list.num_elements;mask_check++) {
          //printf("Checking all the mask statuses of volumes masking %d, now mask with global mask index %d ! \n",this_element,*mask_check);
          if (mask_status_list->elements[*mask_check] == 1) {
            //printf("The status was 1, so the effective status is set to 1 and the loop is stopped");
            current_mask_intersect_list_status->elements[iterate] = 1;
            break;
          }
        }
      } else { // ALL mask mode
        //printf("We are in ALL mask mode! \n");
        current_mask_intersect_list_status->elements[iterate] = 1; // Assume the mask status is 1, but if any one is 0, take that instead
        for (mask_start=mask_check=Volumes[this_element]->geometry.masked_by_mask_index_list.elements;mask_check-mask_start<Volumes[this_element]->geometry.masked_by_mask_index_list.num_elements;mask_check++) {
          //printf("Checking all the mask statuses of volumes masking %d, now mask with global mask index %d ! \n",this_element,*mask_check);
          if (mask_status_list->elements[*mask_check] == 0) {
            //printf("The status was 0, so the effective status is set to 0 and the loop is stopped \n");
            current_mask_intersect_list_status->elements[iterate] = 0;
            break;
          }
        }
      }
    }
  }
    
  /* Original version compatible with trace scope
  for (iterate=0;iterate<Volumes[current_volume]->geometry.mask_intersect_list.num_elements;iterate++) {
    this_element = Volumes[current_volume]->geometry.mask_intersect_list.elements[iterate];
    if (Volumes[this_element]->geometry.mask_mode == 2) { // ANY mask mode
      current_mask_intersect_list_status.elements[iterate] = 0; // Assume the mask status is 0, but if any are 1, take that instead
      for (mask_start=mask_check=Volumes[this_element]->geometry.masked_by_mask_index_list.elements;mask_check-mask_start<Volumes[this_element]->geometry.masked_by_mask_index_list.num_elements;mask_check++) {
        if (mask_status_list[*mask_check] == 1) {
          current_mask_intersect_list_status.elements[iterate] = 1;
          break;
        }
      }
    } else { // ALL mask mode
      current_mask_intersect_list_status.elements[iterate] = 1; // Assume the mask status is 1, but if any one is 0, take that instead
      for (mask_start=mask_check=Volumes[this_element]->geometry.masked_by_mask_index_list.elements;mask_check-mask_start<Volumes[this_element]->geometry.masked_by_mask_index_list.num_elements;mask_check++) {
        if (mask_status_list[*mask_check] == 0) {
          current_mask_intersect_list_status.elements[iterate] = 0;
          break;
        }
      }
    }
  }
  */
}

// -------------    Tagging functions    ------------------------------------------------------------------

struct tagging_tree_node_struct {
  // Statistics:
  double intensity;
  int number_of_rays;
  // tree pointers
  struct tagging_tree_node_struct *above;   // Pointer to node above
  struct tagging_tree_node_struct **volume_branches;
  struct tagging_tree_node_struct **process_branches;
};

struct list_of_tagging_tree_node_pointers {
  struct tagging_tree_node_struct **elements;
  int num_elements;
};

struct tagging_tree_node_struct *make_tagging_tree_node(void) {
    return (struct tagging_tree_node_struct *) malloc(sizeof(struct tagging_tree_node_struct));
}


struct tagging_tree_node_struct  *simple_initialize_tagging_tree_node(struct tagging_tree_node_struct *new_node) {
    //struct tagging_tree_node_struct *dummy;
    //dummy = malloc(sizeof(struct tagging_tree_node_struct));
    //new_node = dummy;
    
    //new_node->element = (struct tagging_tree_node_struct *) malloc(sizeof(struct tagging_tree_node_struct));
    //new_node = (struct tagging_tree_node_struct *) malloc(sizeof(struct tagging_tree_node_struct));
    new_node = make_tagging_tree_node();
    
    if (new_node == NULL) printf("ERROR, Union tagging system could not allocate memory\n");
    new_node->intensity = 4.2; // (double) 4.2;
    new_node->number_of_rays = 42; //(int) 42;
    
    printf("new_node->intensity = %f, new_node->number_of_rays = %d \n",new_node->intensity,new_node->number_of_rays);
    return new_node;
};


struct tagging_tree_node_struct *initialize_tagging_tree_node(struct tagging_tree_node_struct *new_node, struct tagging_tree_node_struct *above_node, struct Volume_struct *this_volume) {
    new_node = make_tagging_tree_node();
    
    new_node->intensity = (double) 0;
    new_node->number_of_rays = (int) 0;
    new_node->above = above_node;
    
    int next_volume_list_length = this_volume->geometry.next_volume_list.num_elements;
    new_node->volume_branches = malloc(next_volume_list_length*sizeof(struct tagging_tree_node_struct*));
    int iterate;
    // Initializing pointers so that they can be checked for NULL later. Is this redundant? Does malloc return null pointers?
    for (iterate=0;iterate<next_volume_list_length;iterate++) new_node->volume_branches[iterate] = NULL;
    //new_node->volume_branches.num_elements = dest_list_length; // May be removed
    
    int number_of_processes;
    if (this_volume->p_physics == NULL) number_of_processes = 0;
    else number_of_processes = this_volume->p_physics->number_of_processes;
    
    new_node->process_branches = malloc(number_of_processes*sizeof(struct tagging_tree_node_struct*));
    // Initializing pointers so that they can be checked for NULL later. Is this redundant? Does malloc return null pointers?
    for (iterate=0;iterate<number_of_processes;iterate++) new_node->process_branches[iterate] = NULL;
    //new_node->process_branches.num_elements=number_of_processes; // May be removed
    
    return new_node;
};

struct tagging_tree_node_struct *goto_process_node(struct tagging_tree_node_struct *current_node, int process_index, struct Volume_struct *this_volume, int *stop_tagging_ray, int stop_creating_nodes) {
    // Either create a new node if it has not been created yet, or travel down the tree
    if (current_node->process_branches[process_index] == NULL) {
      if (stop_creating_nodes == 0) {
        current_node->process_branches[process_index] = initialize_tagging_tree_node(current_node->process_branches[process_index],current_node,this_volume);
        return current_node->process_branches[process_index];
      } else {
        // This stops the ray from using more goto node functions and being counted in the statistics.
        // Happens because the history limit is reached, and no new histories should be started.
        *stop_tagging_ray = 1;
        return current_node;
      }
    } else {
        current_node = current_node->process_branches[process_index];
        return current_node;
    }
    
    //return current_node;
};

struct tagging_tree_node_struct *goto_volume_node(struct tagging_tree_node_struct *current_node,int current_volume, int next_volume, struct Volume_struct **Volumes, int *stop_tagging_ray, int stop_creating_nodes) {
    // I have only allocated the number of node branches that corresponds to the current_volumes next_volume_list
    // The problem is to find where on the destination list the current volume is without doing a manual search
    
    // With the new mask system there is a risk of going from and to the same volume, which should be ignored by the tagging system
    if (current_volume == next_volume) return current_node;
    
    struct tagging_tree_node_struct *output;
    int next_volume_list_index = -1;
    // Temporary slow method for finding the correct index on the destination list
    int iterate;
    
    for (iterate=0;iterate<Volumes[current_volume]->geometry.next_volume_list.num_elements;iterate++)
        if (Volumes[current_volume]->geometry.next_volume_list.elements[iterate] == next_volume) {
            next_volume_list_index = iterate;
            break;
        }
    
    // Debug phase
    //printf("Tagging: going from volume %d to volume %d, which is index number %d on it's next volume list \n",current_volume,next_volume,next_volume_list_index);
    #ifndef OPENACC
    if (next_volume_list_index == -1) {
        printf("ERROR in Union component, tagging or destination system failed, next volume was not on next volume list\n");
        printf("current_volume = %d, next_volume = %d \n",current_volume,next_volume);
        print_1d_int_list(Volumes[current_volume]->geometry.destinations_list,"destinations_list for current volume");
        exit(1);
    }
    #endif

    // Either create a new node if it has not been created yet, or travel down the tree
    if (current_node->volume_branches[next_volume_list_index] == NULL) {
      if (stop_creating_nodes == 0) {
        current_node->volume_branches[next_volume_list_index] =  initialize_tagging_tree_node(current_node->volume_branches[next_volume_list_index],current_node,Volumes[next_volume]);
        return current_node->volume_branches[next_volume_list_index];
      } else {
        // This stops the ray from using more goto node functions and being counted in the statistics.
        // Happens because the history limit is reached, and no new histories should be started.
        *stop_tagging_ray = 1;
        return current_node;
      }
    } else {
        //current_node = current_node->volume_branches[next_volume_list_index];
        //return current_node;
        current_node = current_node->volume_branches[next_volume_list_index];
        //printf("used allocated node \n");
        return current_node;
    }
};

void add_statistics_to_node(struct tagging_tree_node_struct *current_node, Coords *r, Coords *v, double *weight, int *counter) {
    if (current_node->number_of_rays == 0) (*counter)++;
    current_node->number_of_rays = current_node->number_of_rays + 1;
    current_node->intensity = current_node->intensity + *weight;
};

struct history_node_struct {
    int volume_index;
    int process_index;
};

struct dynamic_history_list {
  struct history_node_struct *elements;
  int used_elements;
  int allocated_elements;
};

struct saved_history_struct {
    struct history_node_struct *elements;
    int used_elements;
    double intensity;
    int number_of_rays;
};

struct total_history_struct {
    struct saved_history_struct *saved_histories;
    int used_elements;
    int allocated_elements;
};


void add_to_history(struct dynamic_history_list *history, int volume_index, int process_index) {
    //printf("Adding to history[%d]: volume_index = %d, process_index = %d \n",history->used_elements,volume_index,process_index);
    if (history->allocated_elements == 0) {
        history->elements = malloc(5*sizeof(struct history_node_struct));
        history->used_elements = 1;
        history->allocated_elements = 5;
        history->elements[0].volume_index = volume_index;
        history->elements[0].process_index = process_index;
    } else if (history->used_elements > history->allocated_elements-1) {
        struct dynamic_history_list temp_history;
        temp_history.elements = malloc((history->used_elements)*sizeof(struct history_node_struct));
        int iterate;
        for (iterate=0;iterate<history->used_elements;iterate++) {
            temp_history.elements[iterate].volume_index  = history->elements[iterate].volume_index;
            temp_history.elements[iterate].process_index = history->elements[iterate].process_index;
        }
        free(history->elements);
        history->elements = malloc((history->allocated_elements+5)*sizeof(struct history_node_struct));
        for (iterate=0;iterate<history->used_elements;iterate++) {
            history->elements[iterate].volume_index  = temp_history.elements[iterate].volume_index;
            history->elements[iterate].process_index = temp_history.elements[iterate].process_index;
        }
        
        history->allocated_elements = history->allocated_elements+5;
        
        history->elements[history->used_elements].volume_index = volume_index;
        history->elements[history->used_elements].process_index = process_index;
        history->used_elements = history->used_elements+1;
        
    } else {
        history->elements[history->used_elements].volume_index = volume_index;
        history->elements[history->used_elements].process_index = process_index;
        history->used_elements++;
    }
    
};

void printf_history(struct dynamic_history_list *history) {
    int history_iterate;
          //printf("History number %d, intensity = %f, number of rays = %d:",hist_num, search_node->intensity, search_node->number_of_rays);
          for (history_iterate=0;history_iterate<history->used_elements-1;history_iterate++) {
            if (history->elements[history_iterate].process_index == -1) {
                printf(" V%d ->",history->elements[history_iterate].volume_index);
            } else {
                printf(" P%d ->",history->elements[history_iterate].process_index);
            }
          }
          if (history->elements[history_iterate].process_index == -1) {
                printf(" V%d \n",history->elements[history_iterate].volume_index);
          } else {
                printf(" P%d \n",history->elements[history_iterate].process_index);
          }
}

void fprintf_total_history(struct saved_history_struct *history, FILE *fp) {
    fprintf(fp,"%d\t N I=%E \t", history->number_of_rays, history->intensity);
        int history_iterate;
          //printf("History number %d, intensity = %f, number of rays = %d:",hist_num, search_node->intensity, search_node->number_of_rays);
          for (history_iterate=0;history_iterate<history->used_elements-1;history_iterate++) {
            if (history->elements[history_iterate].process_index == -1) {
                fprintf(fp," V%d ->",history->elements[history_iterate].volume_index);
            } else {
                fprintf(fp," P%d ->",history->elements[history_iterate].process_index);
            }
          }
          if (history->elements[history_iterate].process_index == -1) {
                fprintf(fp," V%d \n",history->elements[history_iterate].volume_index);
          } else {
                fprintf(fp," P%d \n",history->elements[history_iterate].process_index);
          }
}

/*
int Sample_compare_doubles (const void *a, const void *b) {
  const double *da = (const double *) a;
  const double *db = (const double *) b;

  return (*da > *db) - (*da < *db);
}
*/


int Sample_compare_history_intensities (const struct saved_history_struct *a, const struct saved_history_struct *b) {
  const double *da = (const double *) &(a->intensity);
  const double *db = (const double *) &(b->intensity);

  return (*da < *db) - (*da > *db);
}

void write_tagging_tree(struct list_of_tagging_tree_node_pointers *master_list, struct Volume_struct **Volumes, int total_history_counter, int number_of_volumes) {
  // Start from top of tree, go to extremeties, take results and add to disk / database, free that node
  int volume_index,done,volume_iterate,process_iterate,current_volume,next_node_found,current_number_of_processes,history_iterate,hist_num;
  
  struct tagging_tree_node_struct *search_node;
  struct tagging_tree_node_struct **kill_candidate;
  struct dynamic_history_list history_data; // Allocate the history list struct
  struct dynamic_history_list *history;     // Use this pointer in the algorithm
  
  struct total_history_struct total_history;
  total_history.saved_histories = malloc(total_history_counter * sizeof(struct saved_history_struct));
  total_history.allocated_elements = total_history_counter;
  total_history.used_elements = 0;
  
  history = &history_data;
  
  history->used_elements = 0;
  history->allocated_elements = 0;
  
  hist_num = 0;
  for (volume_index=0;volume_index<master_list->num_elements;volume_index++) {
    
    search_node = master_list->elements[volume_index];
    
    if (volume_index != 0)
        current_number_of_processes = Volumes[volume_index]->p_physics->number_of_processes;
    else
        current_number_of_processes = 0;
    
    current_volume = volume_index;
    done = 0;
    
    history->used_elements = 0;
    
    add_to_history(history,current_volume,-1);
    while(done == 0) {
        next_node_found=0;
        for (volume_iterate=0;volume_iterate<Volumes[current_volume]->geometry.next_volume_list.num_elements;volume_iterate++) {
            //printf("searc_node->volume_branches[0]->intensity = %f\n",search_node->volume_branches[0]->intensity);
            if (search_node->volume_branches[volume_iterate] != NULL) {
                current_volume = Volumes[current_volume]->geometry.next_volume_list.elements[volume_iterate];
                if (current_volume != 0)
                    current_number_of_processes = Volumes[current_volume]->p_physics->number_of_processes;
                else
                    current_number_of_processes = 0;
                
                //search_node = &(search_node->volume_branches[volume_iterate]);
                kill_candidate = &(search_node->volume_branches[volume_iterate]);
                search_node = search_node->volume_branches[volume_iterate];
                next_node_found = 1;
                add_to_history(history,current_volume,-1);
                //printf_history(history);
                break;
            }
        }
        if (next_node_found == 0) {
          //printf("doing process loop with %d steps \n",current_number_of_processes);
          for (process_iterate=0;process_iterate<current_number_of_processes;process_iterate++) {
            //if (&(search_node->process_branches[volume_iterate]) != NULL) {
            if (search_node->process_branches[process_iterate] != NULL) {
                //printf("was not NULL (process)\n");
                //search_node = &(search_node->process_branches[process_iterate]);
                kill_candidate = &(search_node->process_branches[process_iterate]);
                search_node = search_node->process_branches[process_iterate];
                next_node_found = 1;
                add_to_history(history,current_volume,process_iterate);
                //printf_history(history);
                break;
            }
          }
        }
        
        if (next_node_found == 0) {
          // write this history to disk / memory
          hist_num++;
          //printf("Reached next_node_found == 0 \n");
          if (history->used_elements > 0 && search_node->number_of_rays > 0) {
            //printf("%d rays (I=%E) with history: \t", search_node->number_of_rays, search_node->intensity);
            //printf_history(history);
            
            total_history.saved_histories[total_history.used_elements].used_elements = history->used_elements;
            total_history.saved_histories[total_history.used_elements].elements = malloc(total_history.saved_histories[total_history.used_elements].used_elements*sizeof(struct history_node_struct));
            for (history_iterate = 0;history_iterate<history->used_elements;history_iterate++) {
                total_history.saved_histories[total_history.used_elements].elements[history_iterate] = history->elements[history_iterate];
                //printf("total_history.saved_histories[total_history.used_elements].elements[%d].volume_index \n",history_iterate,total_history.saved_histories[total_history.used_elements].elements[history_iterate].volume_index);
            }
            //total_history.saved_histories[total_history.used_elements].elements = history->elements;
            total_history.saved_histories[total_history.used_elements].intensity = search_node->intensity;
            total_history.saved_histories[total_history.used_elements].number_of_rays = search_node->number_of_rays;
            total_history.used_elements++;
          }
          
          history->used_elements = 0;
          
          // end of tree, no new nodes
          if (search_node->above == NULL) {
            done = 1;
          } else {
            // reset to the root of the tree
            *kill_candidate = NULL;
            free(search_node);
            search_node = master_list->elements[volume_index];
            
            if (volume_index != 0)
              current_number_of_processes = Volumes[volume_index]->p_physics->number_of_processes;
            else
              current_number_of_processes = 0;
    
            current_volume = volume_index;
            add_to_history(history,current_volume,-1);
          }
          
        }
    }
  }
  
  if (history->allocated_elements > 0) free(history->elements);

  qsort(total_history.saved_histories,total_history.used_elements,sizeof (struct saved_history_struct), Sample_compare_history_intensities);
  
  
  
  MPI_MASTER(
  printf("\n\n");
  printf("Top 20 most common histories. Shows the index of volumes entered (VX), and the scattering processes (PX)\n");
  for (history_iterate=0;history_iterate<total_history.used_elements && history_iterate<20;history_iterate++) {
    printf("%d\t N I=%E \t", total_history.saved_histories[history_iterate].number_of_rays, total_history.saved_histories[history_iterate].intensity);
    printf_history(&total_history.saved_histories[history_iterate]);
  }
   
  FILE *fp;
  fp = fopen("union_history.dat","w");
  
  fprintf(fp,"History file written by the McStas component Union_master \n");
  fprintf(fp,"When running with MPI, the results may be from just a single thread, meaning intensities are divided by number of threads\n");
  fprintf(fp,"----- Description of the used volumes -----------------------------------------------------------------------------------\n");
  
  fprintf(fp,"V0: Surrounding vacuum \n");
  for (volume_iterate=1;volume_iterate<number_of_volumes;volume_iterate++) {
    fprintf(fp,"V%d: %s  ",volume_iterate,Volumes[volume_iterate]->name);
    fprintf(fp,"Material: %s  ",Volumes[volume_iterate]->p_physics->name);
    for (process_iterate=0;process_iterate<Volumes[volume_iterate]->p_physics->number_of_processes;process_iterate++) {
      fprintf(fp," P%d: %s",process_iterate,Volumes[volume_iterate]->p_physics->p_scattering_array[process_iterate].name);
    }
    fprintf(fp,"\n");
  }
  fprintf(fp,"----- Histories sorted after intensity ----------------------------------------------------------------------------------\n");
  
  for (history_iterate=0;history_iterate<total_history.used_elements;history_iterate++) {
    fprintf_total_history(&total_history.saved_histories[history_iterate],fp);
    // Garbage collection
    if (total_history.saved_histories[history_iterate].used_elements > 0) free(total_history.saved_histories[history_iterate].elements);
  }
  fclose(fp);
  
  )
  
  // Garbage collection
  if (total_history.allocated_elements > 0) free(total_history.saved_histories);
  
  
};


// -------------    Intersection table functions   --------------------------------------------------------
int clear_intersection_table(struct intersection_time_table_struct *intersection_time_table) {
    // Resets the intersection table when a scattering have occured.
    int iterate_volumes,iterate_solutions;
    
    // Start at one because vacuum (0) does not have a listing in the intersection table
    for (iterate_volumes = 1;iterate_volumes < intersection_time_table->num_volumes;iterate_volumes++) {
        intersection_time_table->calculated[iterate_volumes] = 0;
        // This second loop is added for safty in debugging phase, but can be removed as the information should never be accesed when calculated = 0
        for (iterate_solutions = 0;iterate_solutions < intersection_time_table->n_elements[iterate_volumes];iterate_solutions++) {
            intersection_time_table->intersection_times[iterate_volumes][iterate_solutions] = -1;
        }
    }
    
    return 1;
};

void print_intersection_table(struct intersection_time_table_struct *intersection_time_table) {
    int num_volumes,iterate,solutions;
    int max_number_of_solutions = 0;
    
    num_volumes = intersection_time_table->num_volumes;
    
    for (iterate = 0;iterate < num_volumes;iterate++) {
        if (max_number_of_solutions < intersection_time_table->n_elements[iterate])
            max_number_of_solutions = intersection_time_table->n_elements[iterate];
    }
    
    printf("------------------ INTERSECTION_TIME_TABLE -----------------");
    for (solutions = 2;solutions < max_number_of_solutions;solutions++) printf("------------");
    printf("\n");
    
    // printf("iterate      |");

    printf("           ");
    printf("| CALCULATED  |");
    for (solutions = 0;solutions < max_number_of_solutions;solutions++) {
        printf(" - SOLUTION %d - |",solutions);
    }

    printf("\n");
    for (iterate = 0;iterate < num_volumes;iterate++){
    // print iterate number
    printf("Volume %d   |",iterate);
    printf(" ---- %d ---- |",intersection_time_table->calculated[iterate]);
    
        for (solutions = 0;solutions < max_number_of_solutions;solutions++) {
          if (intersection_time_table->n_elements[iterate] > solutions && intersection_time_table->calculated[iterate] == 1)
            if (intersection_time_table->intersection_times[iterate][solutions] > 0)
             printf("   %1.8f   |",intersection_time_table->intersection_times[iterate][solutions]);
            else
             printf("   %1.7f   |",intersection_time_table->intersection_times[iterate][solutions]);
          else
            printf("                |");
        }
    printf("\n");
    }
    printf("------------------------------------------------------------");
    for (solutions = 2;solutions < max_number_of_solutions;solutions++) printf("------------");
    printf("\n");
    
    
    };
    
// -------------    Drawing functions   --------------------------------------------------------
void merge_lines_to_draw(struct lines_to_draw *lines_master,struct lines_to_draw *lines_new) {
    if (lines_master->number_of_lines == 0) {
    lines_master->number_of_lines = lines_new->number_of_lines;
    lines_master->lines = malloc(lines_master->number_of_lines*sizeof(struct line_segment));
    lines_master->lines = lines_new->lines;
    // One could free lines_new->lines;
    } else {
    int iterate;
    struct line_segment *temp_lines;
    temp_lines = malloc(lines_master->number_of_lines*sizeof(struct line_segment));
    for (iterate = 0;iterate < lines_master->number_of_lines;iterate++) temp_lines[iterate] = lines_master->lines[iterate];
    free(lines_master->lines);
    lines_master->lines = malloc((lines_master->number_of_lines+lines_new->number_of_lines)*sizeof(struct line_segment));
    for (iterate = 0;iterate < lines_master->number_of_lines;iterate++) lines_master->lines[iterate] = temp_lines[iterate];
    for (iterate = 0;iterate < lines_new->number_of_lines;iterate++) lines_master->lines[iterate+lines_master->number_of_lines] = lines_new->lines[iterate];
    lines_master->number_of_lines = lines_master->number_of_lines + lines_new->number_of_lines;
    free(temp_lines);
    }
    };

int r_has_highest_priority(Coords point,int N,struct geometry_struct **Geometries,int number_of_volumes) {
    // Function that test if a point in Volume N has the highest priority.
    // Returns 0 if another volume has higher priority at the point, and it's mask status is 1
    // Returns 1 if no other volume has higher priority at the point
    // (not active) Returns a larger integer if the volume N is a mask, and the point is not in the volume it is masking,
    //  which results in the drawing function making that number of dashes
    
    
    int mask_status,*mask_start,*mask_check;
    // If the volume is a mask, it does not have a priority, and is always on top
    if (Geometries[N]->is_mask_volume == 1) {
      return 1;
      // Can draw parts of the mask that is outside the volume it masks as dashed lines, but it just looks messy
      /*
      mask_status = 1;
      for (mask_check=mask_start=Geometries[N]->mask_list.elements;mask_check-mask_start<Geometries[N]->mask_list.num_elements;mask_check++) {
        if (Geometries[*mask_check]->within_function(point,Geometries[*mask_check]) == 1) {
          return 1; // If the point is within a volume the mask is masking, draw it as a solid line
        }
      }
      // As it was not inside any of the volumes the mask is masking, draw it as a dashed line
      return 5;
      */
    }
    
    // If the volume is a masked volume, check if the point is within it's masks
    if (Geometries[N]->is_masked_volume == 1) {
      if (Geometries[N]->mask_mode == 1) { // ALL mode, need to be within ALL masks
        for (mask_check=mask_start=Geometries[N]->masked_by_list.elements;mask_check-mask_start<Geometries[N]->masked_by_list.num_elements;mask_check++) {
          if (Geometries[*mask_check]->within_function(point,Geometries[*mask_check]) == 0) {
            return 0; // If the point is just outside one mask, the mask status is 0 and the point does not have highest priority
          }
        }
      } else { // ANY mode, need to be within at least one mask
        mask_status = 0;
        for (mask_check=mask_start=Geometries[N]->masked_by_list.elements;mask_check-mask_start<Geometries[N]->masked_by_list.num_elements;mask_check++) {
          if (Geometries[*mask_check]->within_function(point,Geometries[*mask_check]) == 1) {
            mask_status = 1;
            break;
          }
        }
        if (mask_status == 0) {
          return 0; // If it was not in a single of it's masks, the point did not have highest priority
        }
      }
    }
    
    int volume_index;
    double self_priority;
    self_priority = Geometries[N]->priority_value;
    
    
    for (volume_index = 1;volume_index<number_of_volumes;volume_index++) {
      if (volume_index != N && Geometries[volume_index]->is_mask_volume == 0) {
        if (Geometries[volume_index]->within_function(point,Geometries[volume_index])) {
          if (Geometries[volume_index]->is_masked_volume == 1) {
            // Since this volume is masked, the mask status need to be checked
            if (Geometries[volume_index]->mask_mode == 1) { //ALL mode, need to be within ALL masks
              mask_status = 1;
              for (mask_check=mask_start=Geometries[volume_index]->masked_by_list.elements;mask_check-mask_start<Geometries[volume_index]->masked_by_list.num_elements;mask_check++) {
                if (Geometries[*mask_check]->within_function(point,Geometries[*mask_check]) == 0) {
                  mask_status = 0;
                  break;
                }
              }
            } else { // ANY mode, need to be within at least one mask
              mask_status = 0;
              for (mask_check=mask_start=Geometries[volume_index]->masked_by_list.elements;mask_check-mask_start<Geometries[volume_index]->masked_by_list.num_elements;mask_check++) {
                if (Geometries[*mask_check]->within_function(point,Geometries[*mask_check]) == 1) {
                  mask_status = 1;
                  break;
                }
              }
            }
          } else mask_status = 1;
          if (Geometries[volume_index]->priority_value > self_priority && mask_status == 1) {
            // printf("Volume %d did not have highest priority at (%f,%f,%f) (Volume number %d was above)\n",N,point.x,point.y,point.z,volume_index);
            return 0;
          }
        }
      }
    }
    // printf("Volume %d did have highest priority at (%f,%f,%f) \n",N,point.x,point.y,point.z);
    return 1;
    }
    
void draw_line_positions(Coords point1,Coords point2) {
        //line(point1.x,point1.y,point1.z,point2.x,point2.y,point2.z);
        // sigh, can not use line in share. Need to save the information and pass to the mcdisplay part.
    }

int Sample_compare_doubles (const void *a, const void *b) {
  const double *da = (const double *) a;
  const double *db = (const double *) b;

  return (*da > *db) - (*da < *db);
}

struct lines_to_draw draw_line_with_highest_priority(Coords position1,Coords position2,int N,struct geometry_struct **Geometries,int number_of_volumes,int max_number_of_solutions) {
    
    int volume_index,iterate,permanent_list_length = 0;
    int number_of_solutions;
    double temp_intersection[max_number_of_solutions];
    double r1[3],r2[3],direction[3];
    struct pointer_to_1d_double_list intersection_list;
    
    intersection_list.num_elements = 0;
    
    // double *permanent_intersection_list,*storage; // old ways
    
    r1[0] = position1.x;
    r1[1] = position1.y;
    r1[2] = position1.z;
    r2[0] = position2.x;
    r2[1] = position2.y;
    r2[2] = position2.z;
    
    // printf("r1 = (%f,%f,%f) \n",r1[0],r1[1],r1[2]);
    // printf("r2 = (%f,%f,%f) \n",r2[0],r2[1],r2[2]);
    
    direction[0] = r2[0] - r1[0];
    direction[1] = r2[1] - r1[1];
    direction[2] = r2[2] - r1[2];
    int geometry_output;
    
    // Find intersections
    for (volume_index = 1;volume_index < number_of_volumes; volume_index++) {
        if (volume_index != N) {
            geometry_output = Geometries[volume_index]->intersect_function(temp_intersection,&number_of_solutions,r1,direction,Geometries[volume_index]);
             // printf("No solutions for intersection (Volume %d) with %d \n",N,volume_index);
                for (iterate=0;iterate<number_of_solutions;iterate++) {
                    // print_1d_double_list(intersection_list,"intersection_list");
                    if (temp_intersection[iterate] > 0 && temp_intersection[iterate] < 1) {
                        add_element_to_double_list(&intersection_list,temp_intersection[iterate]);
                        // printf("solution taken = %f\n",temp_intersection[iterate]);
                        }                         // printf("solution ignored = %f\n",temp_intersection[iterate]);
                    // print_1d_double_list(intersection_list,"intersection_list");
                
                }
                // printf("First (%d) solutions added to the solution stack, intersection with %d \n",number_of_solutions,volume_index);
                // printf(" Solutions: ");
                // for (iterate = 0;iterate < intersection_list.num_elements;iterate++) printf("%f ",intersection_list.elements[iterate]);
                // printf("\n");
            
        }
    }
    // Now we have a list of intersection distances between r1 and r2 and all volumes.
    // This list needs to be sorted before we continue!
    
    qsort(intersection_list.elements,intersection_list.num_elements,sizeof (double), Sample_compare_doubles);
    // print_1d_double_list(intersection_list,"after sorting");
    // printf(" Solutions (after sorting): ");
    // for (iterate = 0;iterate < intersection_list.num_elements;iterate++) printf("%f ",intersection_list.elements[iterate]);
    // printf("\n");
    
    Coords points[intersection_list.num_elements+2];
    points[0] = coords_set(r1[0],r1[1],r1[2]);
    points[intersection_list.num_elements+1] = coords_set(r2[0],r2[1],r2[2]);
    for (iterate = 1;iterate < intersection_list.num_elements+1;iterate++) {
        points[iterate].x = r1[0] + direction[0]*intersection_list.elements[iterate-1];
        points[iterate].y = r1[1] + direction[1]*intersection_list.elements[iterate-1];
        points[iterate].z = r1[2] + direction[2]*intersection_list.elements[iterate-1];
    }
    
    // printf("Points on the list:\n");
    // for (iterate = 0;iterate < intersection_list.num_elements + 2;iterate++) {
    // //         printf("(%f,%f,%f)\n",points[iterate].x,points[iterate].y,points[iterate].z);
    // }
    // printf("\n");
  
    struct line_segment lines[intersection_list.num_elements+1];
    int draw_logic[intersection_list.num_elements+1];
    //struct lines_to_draw draw_order;
    Coords midpoint;
    struct lines_to_draw draw_order;
    draw_order.number_of_lines = 0;
    
    // printf("test2 in function \n");
    int number_of_dashes;
    
    for (iterate = 0;iterate < intersection_list.num_elements + 1;iterate++) {
        lines[iterate].point1 = points[iterate];
        lines[iterate].point2 = points[iterate+1];
        midpoint.x = 0.5*(lines[iterate].point1.x + lines[iterate].point2.x);
        midpoint.y = 0.5*(lines[iterate].point1.y + lines[iterate].point2.y);
        midpoint.z = 0.5*(lines[iterate].point1.z + lines[iterate].point2.z);
        
        if ((number_of_dashes = r_has_highest_priority(midpoint,N,Geometries,number_of_volumes)) != 0) {
            draw_order.number_of_lines++;
            draw_logic[iterate] = number_of_dashes;
        } else draw_logic[iterate] = 0;
    }
   
    // printf("test3 in function \n");
    
    if (draw_order.number_of_lines > 0) {
        draw_order.lines = malloc(draw_order.number_of_lines*sizeof(struct line_segment));
        draw_order.number_of_lines = 0;
        for (iterate = 0;iterate < intersection_list.num_elements + 1;iterate++) {
            if (draw_logic[iterate] != 0) {
              lines[iterate].number_of_dashes = draw_logic[iterate];
              draw_order.lines[draw_order.number_of_lines++] = lines[iterate];
            }
        }
        if (intersection_list.num_elements > 0) free(intersection_list.elements);
    }

    // printf("function done \n");
    return draw_order;
    }

struct lines_to_draw draw_circle_with_highest_priority(Coords center,Coords vector,double radius,int N,struct geometry_struct **Geometries,int number_of_volumes,int max_number_of_solutions) {
    int number_of_positions = 100;

    // normalize vector
    double vector_length = length_of_position_vector(vector);
    // print_position(vector,"start vector");
    vector.x /= vector_length;
    vector.y /= vector_length;
    vector.z /= vector_length;
    // print_position(vector,"start vector normalized");
    
    // Create a vector from the center of the circle to a point on the circumference by cross product
    double cross_input[3] = {0,1,0};
    // In case the cross input is parallel with the vector, a new is chosen. Both can't be parallel.
    if (scalar_prod(cross_input[0],cross_input[1],cross_input[2],vector.x,vector.y,vector.z) > 0.99) {
        cross_input[0] = 1; cross_input[1] = 0; cross_input[2] = 0;
    }
    // print_position(make_position(cross_input),"cross input");
    
    double cross_product1[3] = {0,0,0};
    vec_prod(cross_product1[0],cross_product1[1],cross_product1[2],vector.x,vector.y,vector.z,cross_input[0],cross_input[1],cross_input[2]);
    
    // print_position(make_position(cross_product1),"cross_product1");
    
    double cross_length = length_of_3vector(cross_product1);
    
    // printf("cross_length = %f \n",cross_length);
    
    cross_product1[0] /= cross_length;
    cross_product1[1] /= cross_length;
    cross_product1[2] /= cross_length;
    
    cross_product1[0] *= radius;
    cross_product1[1] *= radius;
    cross_product1[2] *= radius;
    
    // printf("cross_product1 = (%f,%f,%f) \n",cross_product1[0],cross_product1[1],cross_product1[2]);
    
    int iterate;
    double rotate_angle;
    Coords radial_position,old_radial_position;
    struct lines_to_draw temp_draw_order,return_draw_order;
    return_draw_order.number_of_lines = 0;
    // Generate an array of positions by rotating this vector around the given vector, this corresponds to points on the circle
    old_radial_position.x = center.x + cross_product1[0];
    old_radial_position.y = center.y + cross_product1[1];
    old_radial_position.z = center.z + cross_product1[2];

    // printf("old_radial_position = (%f,%f,%f) \n",old_radial_position.x,old_radial_position.y,old_radial_position.z);
    
    for (iterate = 0;iterate < number_of_positions-1;iterate++) {
        rotate_angle = 2*3.14159*((double) iterate + 1.0)/((double) number_of_positions);
        rotate(radial_position.x,radial_position.y,radial_position.z,cross_product1[0],cross_product1[1],cross_product1[2],rotate_angle,vector.x,vector.y,vector.z);
        radial_position.x += center.x;
        radial_position.y += center.y;
        radial_position.z += center.z;
        
        // Use the draw_lines_with_highest_priority to draw get draw_orders for each line piece
        temp_draw_order = draw_line_with_highest_priority(radial_position,old_radial_position,N,Geometries,number_of_volumes,max_number_of_solutions);
       // Assemble these draw_orders to one large draw order
        merge_lines_to_draw(&return_draw_order,&temp_draw_order);

        old_radial_position.x = radial_position.x;
        old_radial_position.y = radial_position.y;
        old_radial_position.z = radial_position.z;
    }
    
    radial_position.x = center.x + cross_product1[0];
    radial_position.y = center.y + cross_product1[1];
    radial_position.z = center.z + cross_product1[2];
    // Use the draw_lines_with_highest_priority to draw get draw_orders for each line piece
    temp_draw_order = draw_line_with_highest_priority(radial_position,old_radial_position,N,Geometries,number_of_volumes,max_number_of_solutions);
    // Assemble these draw_orders to one large draw order
    merge_lines_to_draw(&return_draw_order,&temp_draw_order);
    
    // clean up
    if (temp_draw_order.number_of_lines > 0) free(temp_draw_order.lines);
    
    // return the large draw order.
    return return_draw_order;
    }

// -------------    Geometry functions   --------------------------------------------------------

#include "Geometry_functions.c"

// -------------    List generator functions   --------------------------------------------------


int within_which_volume(Coords pos, struct pointer_to_1d_int_list input_list, struct pointer_to_1d_int_list destinations_list, struct Volume_struct **Volumes, struct pointer_to_1d_int_list *mask_status_list, int number_of_volumes, int *volume_logic_copy, int *ListA, int *ListB) {
    // This function identifies in which of the volumes of the input list the position pos lies in.
    // pos: position for which the current volume should be found for
    // input list: list of potential volumes, reduced to the ones without parents (their children will be checked)
    // OLD VERSION: volume logic: A logic list of allowed volumes for lookup, volume 1 3 and 5 in a case of 10 volumes would be [0 1 0 1 0 1 0 0 0 0]
    // destinations_list: list of allowed destinations (the original destinations list)
    // Volumes: Main volumes array
    // volume_logic_copy: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    // ListA: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    // ListB: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    
    // Algorithm description
    // Check all of input list for pos being within them, those that have it within them are:
    //      checked against the current highest priority
    //          if higher, is the new highest priority and the new pick for volume
    //      have all their direct children added to the next list to be checked (but any volume can only be added to that list once)
    // Once a run have been made where the next list to check is empty, the answer for new pick for volume is taken.
    
    // Mask update:
    // Algorithm description
    // Check all of input list for pos being within them, those that have it within them are:
    //      checked against the current highest priority and mask status
    //          if higher, this is the new highest priority and the new pick for volume
    //      have all their direct children added to the next list to be checked (but any volume can only be added to that list once)
    // Once a run have been made where the next list to check is empty, the answer for new pick for volume is taken.
    
    // The advantage of the method is that potentially large numbers of children are skipped when their parents do not contain the position.
    // The overhead cost is low, as all the lists are prealocated.
    // Should be checked which of the two implementations is faster, as this is much more complicated than simply checking all possibilities.
    // No within_function call should be made twice, as the same volume number will not be checked twice because of the properties of the direct_children list and the volume_logic that removes duplicates on each level.
    
    
    // This function uses too much memory, the memory required for the volume logic list is n_volumes^2 ints, or for a MACS monochromator 127000 ints.
    // Instead the original destinations list must be used, and a function for quick lookup in a (sorted) destinations list made.
    // Still need a list of n_volumes length for control to avoid adding the same volume to the list twice.
    
    
    int ListA_length=0,ListB_length=0;
    int done = 0;
    int i,direct_children_index;
    int *temp_pointer;
    double max_priority=-1000000;
    int residing_volume=0; // 0 can be removed from the input list if default is 0
    int this_mask_status,mask_index,mask_global_index;
    
    // volume_logic_copy
    //for (i=0;i<volume_logic.num_elements;i++) volume_logic_copy[i] = volume_logic.elements[i];
    
    // low memory version of volume_logic_copy
    for (i=0;i<number_of_volumes;i++) volume_logic_copy[i] = 0;
    for (i=0;i<destinations_list.num_elements;i++) volume_logic_copy[destinations_list.elements[i]] = 1;
    printf("within_which_volume debug %i\n",input_list.num_elements);

    // Does one loop through the algorithm first to set up ListA instead of copying it from input_list, which takes time
    for (i=0;i<input_list.num_elements;i++) {
            if (Volumes[input_list.elements[i]]->geometry.within_function(pos,&Volumes[input_list.elements[i]]->geometry) == 1) {
                printf("The position is inside of volume %d\n",input_list.elements[i]);
                if (Volumes[input_list.elements[i]]->geometry.is_masked_volume == 1) {
                    // if the volume is masked, I need to know if it can be a destination volume from the mask_status_list.
                    // if the masked volume is in ANY mode,
                    this_mask_status=1;
                    //print_1d_int_list(*mask_status_list,"mask status list from within_which_volume");
                    for (mask_index=0;mask_index<Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.num_elements;mask_index++) {
                        //printf("Looking at the mask with global index %d \n",Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.elements[mask_index]);
                        if (mask_status_list->elements[Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.elements[mask_index]] == 1) {
                            if (Volumes[input_list.elements[i]]->geometry.mask_mode == 2) { // ANY (break if any one in)
                                this_mask_status=1;
                                break;
                            }
                            //printf("global index %d had mask status = 1 \n",Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.elements[mask_index]);
                        } else {
                          //printf("global index %d had mask status = 0 \n",Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.elements[mask_index]);
                          this_mask_status = 0;
                          if(Volumes[input_list.elements[i]]->geometry.mask_mode == 1) break; // ALL (break if any one out)
                        }
                    }
                    //printf("This volume is masked, and the mask status is %d\n",this_mask_status);
                } else this_mask_status = 1; // if the volume is not masked
            
                if (Volumes[input_list.elements[i]]->geometry.priority_value > max_priority && this_mask_status == 1) {
                    max_priority = Volumes[input_list.elements[i]]->geometry.priority_value;
                    residing_volume = input_list.elements[i];
                    //printf("residing volume set to %d\n",residing_volume);
                }
                    for (direct_children_index = 0;direct_children_index < Volumes[input_list.elements[i]]->geometry.direct_children.num_elements;direct_children_index++) {
                        if (volume_logic_copy[Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index]] == 1) {
                            ListA[ListA_length++] = Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index];
                            volume_logic_copy[Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index]] = 0;
                        }
                    }
            }
    }
    //printf("Completed first loop, continued in while loop\n");
    if (ListA_length > 0) {
        while (done == 0) {
            for (i=0;i<ListA_length;i++) {
              //printf("checking element number %d of list A which is volume number %d \n",i,ListA[i]);
                if (Volumes[ListA[i]]->geometry.within_function(pos,&Volumes[ListA[i]]->geometry) == 1) {
                  //printf("ray was inside this volume \n");
                    if (Volumes[ListA[i]]->geometry.is_masked_volume == 1) {
                      //printf("it is a mask and thus need check of mask status \n");
                        // if the volume is masked, I need to know if it can be a destination volume from the mask_status_list.
                        // if the masked volume is in ANY mode,
                        this_mask_status=1;
                        for (mask_index=0;mask_index<Volumes[ListA[i]]->geometry.masked_by_mask_index_list.num_elements;mask_index++) {
                            if (mask_status_list->elements[Volumes[ListA[i]]->geometry.masked_by_mask_index_list.elements[mask_index]] == 1) {
                                if (Volumes[ListA[i]]->geometry.mask_mode == 2) { // ANY (break if any one in)
                                    this_mask_status=1;
                                    break;
                                }
                            } else {
                              this_mask_status = 0;
                              if(Volumes[ListA[i]]->geometry.mask_mode == 1) break; // ALL (break if any one out)
                            }
                        }
                    } else this_mask_status = 1;
                    //printf("the mask status is %d \n",this_mask_status);
                    if (Volumes[ListA[i]]->geometry.priority_value > max_priority && this_mask_status == 1) {
                        max_priority = Volumes[ListA[i]]->geometry.priority_value;
                        residing_volume = ListA[i];
                    }
                    //printf("Adding direct children to list B \n");
                        for (direct_children_index = 0;direct_children_index < Volumes[ListA[i]]->geometry.direct_children.num_elements;direct_children_index++) {
                            //printf("Checking direct_child number %d which is %d \n",direct_children_index,Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index]);
                            if (volume_logic_copy[Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index]] == 1) {
                                //printf("It's volume_logic was 1, and it is thus added to listB with index %d \n",ListB_length);
                                ListB[ListB_length++] = Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index];
                                volume_logic_copy[Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index]] = 0;
                            }
                        }
                    //printf("List B is now: ");
                    //for (direct_children_index=0;direct_children_index<ListB_length;direct_children_index++) printf("%d ",ListB[direct_children_index]);
                    //printf("\n");
                    
                    
                }
            }
            if (ListB_length==0) done = 1;
            else {
                
                for (i=0;i<ListB_length;i++) ListA[i] = ListB[i];
                ListA_length = ListB_length;
                ListB_length = 0;
                
                /*
                // Could do this with pointers instead to avoid this for loop (and needless copy)
                // This code block fails on the cluster in rare circumstances
                ListA = temp_pointer;
                ListA = ListB;
                ListB = temp_pointer;
                ListA_length=ListB_length;
                ListB_length=0;
                */
            }
        }
    }
    //printf("residing volume returned %d\n",residing_volume);
    return residing_volume;
};

int within_which_volume_GPU(Coords pos, struct pointer_to_1d_int_list input_list, struct pointer_to_1d_int_list destinations_list, struct Volume_struct **Volumes, struct pointer_to_1d_int_list *mask_status_list, int number_of_volumes, int *volume_logic_copy, int *ListA, int *ListB) {
    // This function identifies in which of the volumes of the input list the position pos lies in.
    // pos: position for which the current volume should be found for
    // input list: list of potential volumes, reduced to the ones without parents (their children will be checked)
    // OLD VERSION: volume logic: A logic list of allowed volumes for lookup, volume 1 3 and 5 in a case of 10 volumes would be [0 1 0 1 0 1 0 0 0 0]
    // destinations_list: list of allowed destinations (the original destinations list)
    // Volumes: Main volumes array
    // volume_logic_copy: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    // ListA: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    // ListB: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    
    // Algorithm description
    // Check all of input list for pos being within them, those that have it within them are:
    //      checked against the current highest priority
    //          if higher, is the new highest priority and the new pick for volume
    //      have all their direct children added to the next list to be checked (but any volume can only be added to that list once)
    // Once a run have been made where the next list to check is empty, the answer for new pick for volume is taken.
    
    // Mask update:
    // Algorithm description
    // Check all of input list for pos being within them, those that have it within them are:
    //      checked against the current highest priority and mask status
    //          if higher, this is the new highest priority and the new pick for volume
    //      have all their direct children added to the next list to be checked (but any volume can only be added to that list once)
    // Once a run have been made where the next list to check is empty, the answer for new pick for volume is taken.
    
    // The advantage of the method is that potentially large numbers of children are skipped when their parents do not contain the position.
    // The overhead cost is low, as all the lists are prealocated.
    // Should be checked which of the two implementations is faster, as this is much more complicated than simply checking all possibilities.
    // No within_function call should be made twice, as the same volume number will not be checked twice because of the properties of the direct_children list and the volume_logic that removes duplicates on each level.
    
    
    // This function uses too much memory, the memory required for the volume logic list is n_volumes^2 ints, or for a MACS monochromator 127000 ints.
    // Instead the original destinations list must be used, and a function for quick lookup in a (sorted) destinations list made.
    // Still need a list of n_volumes length for control to avoid adding the same volume to the list twice.
    
    
    int ListA_length=0,ListB_length=0;
    int done = 0;
    int i,direct_children_index;
    int *temp_pointer;
    double max_priority=-1000000;
    int residing_volume=0; // 0 can be removed from the input list if default is 0
    int this_mask_status,mask_index,mask_global_index;
    
    // volume_logic_copy
    //for (i=0;i<volume_logic.num_elements;i++) volume_logic_copy[i] = volume_logic.elements[i];
    
    // low memory version of volume_logic_copy
    for (i=0;i<number_of_volumes;i++) volume_logic_copy[i] = 0;
    for (i=0;i<destinations_list.num_elements;i++) volume_logic_copy[destinations_list.elements[i]] = 1;
    //printf("within_which_volume debug\n");

    // Does one loop through the algorithm first to set up ListA instead of copying it from input_list, which takes time
    for (i=0;i<input_list.num_elements;i++) {
            if (r_within_function(pos, &Volumes[input_list.elements[i]]->geometry) == 1) {
                //printf("The position is inside of volume %d\n",input_list.elements[i]);
                if (Volumes[input_list.elements[i]]->geometry.is_masked_volume == 1) {
                    // if the volume is masked, I need to know if it can be a destination volume from the mask_status_list.
                    // if the masked volume is in ANY mode,
                    this_mask_status=1;
                    //print_1d_int_list(*mask_status_list,"mask status list from within_which_volume");
                    for (mask_index=0;mask_index<Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.num_elements;mask_index++) {
                        //printf("Looking at the mask with global index %d \n",Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.elements[mask_index]);
                        if (mask_status_list->elements[Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.elements[mask_index]] == 1) {
                            if (Volumes[input_list.elements[i]]->geometry.mask_mode == 2) { // ANY (break if any one in)
                                this_mask_status=1;
                                break;
                            }
                            //printf("global index %d had mask status = 1 \n",Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.elements[mask_index]);
                        } else {
                          //printf("global index %d had mask status = 0 \n",Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.elements[mask_index]);
                          this_mask_status = 0;
                          if(Volumes[input_list.elements[i]]->geometry.mask_mode == 1) break; // ALL (break if any one out)
                        }
                    }
                    //printf("This volume is masked, and the mask status is %d\n",this_mask_status);
                } else this_mask_status = 1; // if the volume is not masked
            
                if (Volumes[input_list.elements[i]]->geometry.priority_value > max_priority && this_mask_status == 1) {
                    max_priority = Volumes[input_list.elements[i]]->geometry.priority_value;
                    residing_volume = input_list.elements[i];
                    //printf("residing volume set to %d\n",residing_volume);
                }
                    for (direct_children_index = 0;direct_children_index < Volumes[input_list.elements[i]]->geometry.direct_children.num_elements;direct_children_index++) {
                        if (volume_logic_copy[Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index]] == 1) {
                            ListA[ListA_length++] = Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index];
                            volume_logic_copy[Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index]] = 0;
                        }
                    }
            }
    }
    //printf("Completed first loop, continued in while loop\n");
    if (ListA_length > 0) {
        while (done == 0) {
            for (i=0;i<ListA_length;i++) {
              //printf("checking element number %d of list A which is volume number %d \n",i,ListA[i]);
                if (r_within_function(pos,&Volumes[ListA[i]]->geometry) == 1) {
                  //printf("ray was inside this volume \n");
                    if (Volumes[ListA[i]]->geometry.is_masked_volume == 1) {
                      //printf("it is a mask and thus need check of mask status \n");
                        // if the volume is masked, I need to know if it can be a destination volume from the mask_status_list.
                        // if the masked volume is in ANY mode,
                        this_mask_status=1;
                        for (mask_index=0;mask_index<Volumes[ListA[i]]->geometry.masked_by_mask_index_list.num_elements;mask_index++) {
                            if (mask_status_list->elements[Volumes[ListA[i]]->geometry.masked_by_mask_index_list.elements[mask_index]] == 1) {
                                if (Volumes[ListA[i]]->geometry.mask_mode == 2) { // ANY (break if any one in)
                                    this_mask_status=1;
                                    break;
                                }
                            } else {
                              this_mask_status = 0;
                              if(Volumes[ListA[i]]->geometry.mask_mode == 1) break; // ALL (break if any one out)
                            }
                        }
                    } else this_mask_status = 1;
                    //printf("the mask status is %d \n",this_mask_status);
                    if (Volumes[ListA[i]]->geometry.priority_value > max_priority && this_mask_status == 1) {
                        max_priority = Volumes[ListA[i]]->geometry.priority_value;
                        residing_volume = ListA[i];
                    }
                    //printf("Adding direct children to list B \n");
                        for (direct_children_index = 0;direct_children_index < Volumes[ListA[i]]->geometry.direct_children.num_elements;direct_children_index++) {
                            //printf("Checking direct_child number %d which is %d \n",direct_children_index,Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index]);
                            if (volume_logic_copy[Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index]] == 1) {
                                //printf("It's volume_logic was 1, and it is thus added to listB with index %d \n",ListB_length);
                                ListB[ListB_length++] = Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index];
                                volume_logic_copy[Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index]] = 0;
                            }
                        }
                    //printf("List B is now: ");
                    //for (direct_children_index=0;direct_children_index<ListB_length;direct_children_index++) printf("%d ",ListB[direct_children_index]);
                    //printf("\n");
                    
                    
                }
            }
            if (ListB_length==0) done = 1;
            else {
                
                for (i=0;i<ListB_length;i++) ListA[i] = ListB[i];
                ListA_length = ListB_length;
                ListB_length = 0;
                
                /*
                // Could do this with pointers instead to avoid this for loop (and needless copy)
                // This code block fails on the cluster in rare circumstances
                ListA = temp_pointer;
                ListA = ListB;
                ListB = temp_pointer;
                ListA_length=ListB_length;
                ListB_length=0;
                */
            }
        }
    }
    //printf("residing volume returned %d\n",residing_volume);
    return residing_volume;
};




int within_which_volume_debug(Coords pos, struct pointer_to_1d_int_list input_list, struct pointer_to_1d_int_list volume_logic, struct Volume_struct **Volumes, int *volume_logic_copy, int *ListA, int *ListB) {
    // This function identifies in which of the volumes of the input list the position pos lies in.
    // pos: position for which the current volume should be found for
    // input list: list of potential volumes, reduced to the ones without parents (their children will be checked)
    // volume logic: A logic list of allowed volumes for lookup, volume 1 3 and 5 in a case of 10 volumes would be [0 1 0 1 0 1 0 0 0 0]
    // Volumes: Main volumes array
    // volume_logic_copy: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    // ListA: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    // ListB: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    
    // Algorithm description
    // Check all of input list for pos being within them, those that have it within them are:
    //      checked against the current highest priority
    //          if higher, is the new highest priority and the new pick for volume
    //      have all their direct children added to the next list to be checked (but any volume can only be added to that list once)
    // Once a run have been made where the next list to check is empty, the answer for new pick for volume is taken.
    
    // The advantage of the method is that potentially large numbers of children are skipped when their parents do not contain the position.
    // The overhead cost is low, as all the lists are prealocated.
    // Should be checked which of the two implementations is faster, as this is much more complicated than simply checking all possibilities.
    // No within_function call should be made twice, as the same volume number will not be checked twice because of the properties of the direct_children list and the volume_logic that removes duplicates on each level.
    
    
    int ListA_length=0,ListB_length=0;
    int done = 0;
    int i,direct_children_index;
    int *temp_pointer;
    double max_priority=-1000000;
    int residing_volume=0; // 0 can be removed from the input list if default is 0
    
    // volume_logic_copy
    for (i=0;i<volume_logic.num_elements;i++) volume_logic_copy[i] = volume_logic.elements[i];
    
    // Does one loop through the algorithm first to set up ListA instead of copying it from input_list, which takes time
    for (i=0;i<input_list.num_elements;i++) {
            if (Volumes[input_list.elements[i]]->geometry.within_function(pos,&Volumes[input_list.elements[i]]->geometry) == 1) {
                if (Volumes[input_list.elements[i]]->geometry.priority_value > max_priority) {
                    max_priority = Volumes[input_list.elements[i]]->geometry.priority_value;
                    residing_volume = input_list.elements[i];
                }
                    for (direct_children_index = 0;direct_children_index < Volumes[input_list.elements[i]]->geometry.direct_children.num_elements;direct_children_index++) {
                        if (volume_logic_copy[Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index]] == 1) {
                            ListA[ListA_length++] = Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index];
                            volume_logic_copy[Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index]] = 0;
                        }
                    }
            }
    }
    if (ListA_length > 0) {
        while (done == 0) {
        
            printf("ListA = [");
            for (i=0;i<ListA_length;i++) {
                printf("%d,",ListA[i]);
            }
            printf("]\n");
            for (i=0;i<ListA_length;i++) {
                if (Volumes[ListA[i]]->geometry.within_function(pos,&Volumes[ListA[i]]->geometry) == 1) {
                    if (Volumes[ListA[i]]->geometry.priority_value > max_priority) {
                        max_priority = Volumes[ListA[i]]->geometry.priority_value;
                        residing_volume = ListA[i];
                    }
                    //if (ListA[i]!=0) {
                        for (direct_children_index = 0;direct_children_index < Volumes[ListA[i]]->geometry.direct_children.num_elements;direct_children_index++) {
                            if (volume_logic_copy[Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index]] == 1) {
                                ListB[ListB_length++] = Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index];
                                volume_logic_copy[Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index]] = 0;
                            }
                        }
                    //}
                }
            }
            if (ListB_length==0) done = 1;
            else {
                ListA = temp_pointer;
                ListA = ListB;
                ListB = temp_pointer;
                ListA_length=ListB_length;
                ListB_length=0;
            }
        }
    }
    printf("Volume number %d had the highest priority of checked volumes\n",residing_volume);
    return residing_volume;
};

int inside_function(struct Volume_struct *parent_volume, struct Volume_struct *child_volume) {
    // Function that calls the correct within function depending on the shapes of the two volumes
    if (strcmp("sphere",parent_volume->geometry.shape) == 0 && strcmp("sphere",child_volume->geometry.shape) == 0) {
        if (sphere_within_sphere(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("cylinder",parent_volume->geometry.shape) == 0 && strcmp("cylinder",child_volume->geometry.shape) == 0) {
        if (cylinder_within_cylinder(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("box",parent_volume->geometry.shape) == 0 && strcmp("box",child_volume->geometry.shape) == 0) {
        if (box_within_box(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("cone",parent_volume->geometry.shape) == 0 && strcmp("cone",child_volume->geometry.shape) == 0) {
        if (cone_within_cone(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("mesh",parent_volume->geometry.shape) == 0 && strcmp("mesh",child_volume->geometry.shape) == 0) {
        if (mesh_within_mesh(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("box",parent_volume->geometry.shape) == 0 && strcmp("cylinder",child_volume->geometry.shape) == 0) {
        if (cylinder_within_box(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("cylinder",parent_volume->geometry.shape) == 0 && strcmp("box",child_volume->geometry.shape) == 0) {
        if (box_within_cylinder(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("box",parent_volume->geometry.shape) == 0 && strcmp("sphere",child_volume->geometry.shape) == 0) {
        if (sphere_within_box(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("sphere",parent_volume->geometry.shape) == 0 && strcmp("box",child_volume->geometry.shape) == 0) {
        if (box_within_sphere(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("sphere",parent_volume->geometry.shape) == 0 && strcmp("cylinder",child_volume->geometry.shape) == 0) {
        if (cylinder_within_sphere(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("cylinder",parent_volume->geometry.shape) == 0 && strcmp("sphere",child_volume->geometry.shape) == 0) {
        if (sphere_within_cylinder(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("cone",parent_volume->geometry.shape) == 0 && strcmp("sphere",child_volume->geometry.shape) == 0) {
        if (sphere_within_cone(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("sphere",parent_volume->geometry.shape) == 0 && strcmp("cone",child_volume->geometry.shape) == 0) {
        if (cone_within_sphere(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("cone",parent_volume->geometry.shape) == 0 && strcmp("cylinder",child_volume->geometry.shape) == 0) {
        if (cylinder_within_cone(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("cylinder",parent_volume->geometry.shape) == 0 && strcmp("cone",child_volume->geometry.shape) == 0) {
        if (cone_within_cylinder(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("cone",parent_volume->geometry.shape) == 0 && strcmp("box",child_volume->geometry.shape) == 0) {
        if (box_within_cone(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("box",parent_volume->geometry.shape) == 0 && strcmp("cone",child_volume->geometry.shape) == 0) {
        if (cone_within_box(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else {
        #ifndef OPENACC
        printf("Need within function for type: ");
        printf("%s",parent_volume->geometry.shape);
        printf(" and type: ");
        printf("%s",child_volume->geometry.shape);
        printf(".\n");
        printf("It is not yet supported to mix mesh geometries with the basic shapes, but several mesh geometries are allowed.\n");
        exit(1);
	#endif
    }
    
    return 0;
};

void generate_children_lists(struct Volume_struct **Volumes, struct pointer_to_1d_int_list **true_children_lists, int number_of_volumes, int verbal) {
  // This function generates a list of children for each volume.
  // A volume m is a child of volume n, if the entire space ocupied by volume m is inside of the space ocupied by volume n
  // A volume m is a true child of volume n, if the entire space coupied by volume m after it's masks are applied is inside the volume ocupied by volume n after it's masks are applied
  
  
  MPI_MASTER(
  if (verbal) printf("\nGenerating children lists --------------------------- \n");
  )

  // Mask update: Creating a temporary list for each volume
  struct pointer_to_1d_int_list *temporary_children_lists;
  temporary_children_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list));

  // The surrounding vacuum, volume 0, done outside of for loop.
  temporary_children_lists[0].num_elements = number_of_volumes - 1;
  temporary_children_lists[0].elements = malloc(temporary_children_lists[0].num_elements*sizeof(int));
  
  int parent;
  for (parent=1;parent<number_of_volumes;parent++) {
      temporary_children_lists[0].elements[parent-1] = parent;
  }
  
  //if (verbal) printf("did temporary children list \n");
  //print_1d_int_list(temporary_children_lists[0],"temp children list [0]");
  
  // Hardcoding that every volume is a child of the surrounding vacuum
  Volumes[0]->geometry.children.num_elements = number_of_volumes-1;
  Volumes[0]->geometry.children.elements = malloc((number_of_volumes-1)*sizeof(int));
  true_children_lists[0] = malloc(sizeof(struct pointer_to_1d_int_list));
  true_children_lists[0]->num_elements = number_of_volumes - 1;
  true_children_lists[0]->elements = malloc((number_of_volumes-1)*sizeof(int));
  
  //if (verbal) printf("allocated true children lists \n");
  
  for (parent=1;parent<number_of_volumes;parent++) {
      Volumes[0]->geometry.children.elements[parent-1] = parent;
      true_children_lists[0]->elements[parent-1] = parent;
  }
  
  char string_output[128];
  MPI_MASTER(
  if (verbal) sprintf(string_output,"Children for Volume %d",0);
  if (verbal) print_1d_int_list(Volumes[0]->geometry.children,string_output);
  )
  
  
  // Generating the children lists for all other volumes using the appropriate geometry functions
  struct pointer_to_1d_int_list temp_list_local;
  temp_list_local.num_elements = number_of_volumes;
  temp_list_local.elements = malloc(number_of_volumes*sizeof(int));
  
  struct pointer_to_1d_int_list true_temp_list_local;
  true_temp_list_local.num_elements = number_of_volumes;
  true_temp_list_local.elements = malloc(number_of_volumes*sizeof(int));
  
  
  int child,used_elements,used_elements_true;
  for (parent=1;parent<number_of_volumes;parent++) {
      used_elements = 0;used_elements_true = 0;
      for (child=1;child<number_of_volumes;child++) {
        if (child != parent) {
          // Call inside_function that selects the proper within function for the two volumes
          if (1 == inside_function(Volumes[parent],Volumes[child])) {
            temp_list_local.elements[used_elements++] = child;
            true_temp_list_local.elements[used_elements_true++] = child;
          }
        } else true_temp_list_local.elements[used_elements_true++] = child; // Needed when children list takes mask into account
      }
      // Temp test
      allocate_list_from_temp(used_elements,temp_list_local,&Volumes[parent]->geometry.children);
      // Assing the children list to a temporary list as the masks have yet to be taken into account
      temporary_children_lists[parent].num_elements=0;
      allocate_list_from_temp(used_elements_true,true_temp_list_local,&temporary_children_lists[parent]);
      
      
      MPI_MASTER(
      if (verbal) sprintf(string_output,"Children for Volume %d (temporary_list)",parent);
      if (verbal) print_1d_int_list(temporary_children_lists[parent],string_output);
      )
      
      MPI_MASTER(
      if (verbal) sprintf(string_output,"Children for Volume %d (permanent_list)",parent);
      if (verbal) print_1d_int_list(Volumes[parent]->geometry.children,string_output);
      )
      
  }
  
  // mask update:
  // The logical expression: (child c parent AND child c parent_mask) OR (child_mask c parent AND child_mask c parent_mask)
  //  needs to be evaluated for each child / parent combination in order to take the masks of each into account
  
  int logic_var1,logic_var2,logic_var_ANY,logic_var_ALL;
  int mask_index,mask_index_child,mask_index_parent;
  int volume_C,volume_P;
  // Loop that takes masks into account
  for (parent=1;parent<number_of_volumes;parent++) {
    used_elements = 0;
    for (child=1;child<number_of_volumes;child++) {
     if (child != parent && 0 == on_int_list(Volumes[parent]->geometry.masked_by_list,child)) {
        // The children list for each volume does not need to contain the volume itself
        //  And a parent masked by it's child can not have that mask as a child
      
      // Here c means within in the sense of a set being part of another set
      // Logical expression to be evaluated: (child c parent AND child c parent_mask) OR (child_mask c parent AND child_mask c parent_mask)
      logic_var1 = on_int_list(temporary_children_lists[parent],child);
      if (logic_var1 == 1 && Volumes[parent]->geometry.is_masked_volume == 1) {
        // if the parent volume is masked, the child also need to be inclosed in these masks to fulfill this side of the logical expression
        logic_var_ANY = 0;
        for (mask_index=0;mask_index<Volumes[parent]->geometry.masked_by_list.num_elements;mask_index++) {
          if (0 == on_int_list(temporary_children_lists[Volumes[parent]->geometry.masked_by_list.elements[mask_index]],child)) {
            if (Volumes[parent]->geometry.mask_mode == 1) {
              logic_var1 = 0;
              break;
            }
          } else logic_var_ANY = 1;
        }
        if (Volumes[parent]->geometry.mask_mode == 2) logic_var1 = logic_var_ANY;
      }
      
      if (logic_var1 == 1) true_temp_list_local.elements[used_elements++] = child;
      else if (Volumes[child]->geometry.is_masked_volume == 1) {
        // If the first side of the logical expression is false, evalute the other side
        // The other side is only relevant if the child volume is masked, otherwise it is ignored
        //printf("Second side of logical expression \n");
        
        logic_var1 = 1; // Assume true
          
        // child_mask c parent
        logic_var_ALL = 0;
        for (mask_index=0;mask_index<Volumes[child]->geometry.masked_by_list.num_elements;mask_index++) {
          if (0 == on_int_list(temporary_children_lists[parent],Volumes[child]->geometry.masked_by_list.elements[mask_index])){
            if (Volumes[child]->geometry.mask_mode == 2) {
              logic_var1 = 0;
              break;
            }
          } else logic_var_ALL = 1;
        }
        if (Volumes[child]->geometry.mask_mode == 1) logic_var1 = logic_var_ALL;
        
        // This line allows the second part of the logical expression to be true in cases where the parent volume is not masked
        if (logic_var1 == 1 && Volumes[parent]->geometry.is_masked_volume == 0) true_temp_list_local.elements[used_elements++] = child;
        
        // There is no reason to check the other part (child_mask c parent_mask) if the first part was not true
        if (logic_var1 == 1 && Volumes[parent]->geometry.is_masked_volume == 1) {
          // This last part requires both the child and the parent to be masked
          // Need to evaluate (child_mask c parent_mask), where both can be a be a list of volume with ALL/ANY modes
          
          if (Volumes[parent]->geometry.mask_mode == 1) {
            logic_var2 = 1; // Assume the logical expression (child_mask c parent_mask) is true
            for (mask_index_parent=0;mask_index_parent<Volumes[parent]->geometry.masked_by_list.num_elements;mask_index_parent++) {
              // As the parent is in ALL mode, the child masks must be within ALL parent masks
              logic_var_ANY = 0; // Assume not a single child is within this parent
              for (mask_index_child=0;mask_index_child<Volumes[child]->geometry.masked_by_list.num_elements;mask_index_child++) {
                volume_P = Volumes[parent]->geometry.masked_by_list.elements[mask_index_parent];
                volume_C = Volumes[child]->geometry.masked_by_list.elements[mask_index_child];
                // Is volume C inside volume P? If yes, volume C must be on volume P's temporary children list
                if (0 == on_int_list(temporary_children_lists[volume_P],volume_C)) {
                  if (Volumes[child]->geometry.mask_mode == 2) {
                    // If child is in ANY mode, any one mask outside is enough to make the expression false
                    logic_var2 = 0;
                    break;
                  }
                } else logic_var_ANY = 1;
              }
              // If child is in ALL mode, then if any one child were within this mask, the logic expression holds true
              if (Volumes[child]->geometry.mask_mode == 1) logic_var2 = logic_var_ANY;
              if (logic_var2 == 0) break; // No need to continue
            }
          } else if (Volumes[parent]->geometry.mask_mode == 2) {
            // If the parent is in ANY mode, it is enough if the child masks are within just 1 of the parent masks
            for (mask_index_parent=0;mask_index_parent<Volumes[parent]->geometry.masked_by_list.num_elements;mask_index_parent++) {
              logic_var2 = 1; // Assume the logical expression (child_mask c parent_mask) is true
              logic_var_ANY = 0; // Assume not a single child is within this parent
              for (mask_index_child=0;mask_index_child<Volumes[child]->geometry.masked_by_list.num_elements;mask_index_child++) {
                volume_P = Volumes[parent]->geometry.masked_by_list.elements[mask_index_parent];
                volume_C = Volumes[child]->geometry.masked_by_list.elements[mask_index_child];
                // Is volume C inside volume P? If yes, volume C must be on volume P's temporary children list
                if (0 == on_int_list(temporary_children_lists[volume_P],volume_C)) {
                  if (Volumes[child]->geometry.mask_mode == 2) {
                    // If child is in ANY mode, any one mask outside is enough to make the expression false
                    logic_var2 = 0;
                    break;
                  }
                } else logic_var_ANY = 1;
              }
              // If child is in ALL mode, then if any one child were within this mask, the logic expression holds true
              if (Volumes[child]->geometry.mask_mode == 1) logic_var2 = logic_var_ANY;
              if (logic_var2 == 1) break; // No need to continue
            }
          }
          
          // if this point is reached, and logic_var2 is true, volume[child] is a child of volume[parent]
          if (logic_var2 == 1) true_temp_list_local.elements[used_elements++] = child;
          
        }
      }
     }
    }
    
    true_children_lists[parent] = malloc(sizeof(struct pointer_to_1d_int_list));
    true_children_lists[parent]->num_elements = 0;
    allocate_list_from_temp(used_elements,true_temp_list_local,true_children_lists[parent]);
    
    MPI_MASTER(
      if (verbal) sprintf(string_output,"True children for Volume (post mask) %d",parent);
      if (verbal) print_1d_int_list(*true_children_lists[parent],string_output);
    )
  }
  
  
  // Clean up of dynamically allocated memory
  
  for(child=0;child<number_of_volumes;child++) free(temporary_children_lists[child].elements);
  free(temporary_children_lists);
  free(true_temp_list_local.elements);
  free(temp_list_local.elements);
};

void generate_overlap_lists(struct pointer_to_1d_int_list **true_overlap_lists, struct pointer_to_1d_int_list **raw_overlap_lists, struct Volume_struct **Volumes, int number_of_volumes, int verbal) {
  // This function generates overlap lists for each volume
  // Volume m overlaps volume n if there is some subset of space they both ocupy (this is called raw overlap)
  // the true overlap list takes mask into account, meaning that the masks are applied before searching for overlap

  MPI_MASTER(
  if (verbal) printf("\nGenerating overlap lists ---------------------------- \n");
  )
  
  // Manually create the overlap list for the surrounding Vacuum, as it overlaps all other volumes
  
  // temporary_overlap_lists are used to save the calculated overlaps to avoid evaluating the heavy functions more than once (twice) for each possible volume pair
  struct pointer_to_1d_int_list *temporary_overlap_lists;
  temporary_overlap_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list));
  
  // Overlap_lists are the final result of the function, and for the surrounding volume it can be set immediatly
  true_overlap_lists[0] = malloc(sizeof(struct pointer_to_1d_int_list));
  true_overlap_lists[0]->num_elements = number_of_volumes-1;
  true_overlap_lists[0]->elements = malloc((number_of_volumes-1)*sizeof(int));
  
  raw_overlap_lists[0] =  malloc(sizeof(struct pointer_to_1d_int_list));
  raw_overlap_lists[0]->num_elements = number_of_volumes;
  raw_overlap_lists[0]->elements = malloc(number_of_volumes*sizeof(int));
  raw_overlap_lists[0]->elements[0] = 0; // Volume 0 overlaps itself
  
  int parent;
  for (parent=1;parent<number_of_volumes;parent++) {
      true_overlap_lists[0]->elements[parent-1] = parent;
      raw_overlap_lists[0]->elements[parent] = parent;
  }
  
  char string_output[128];
  MPI_MASTER(
  if (verbal) sprintf(string_output,"Overlaps for Volume %d",0);
  if (verbal) print_1d_int_list(*true_overlap_lists[0],string_output);
  )
  
  // Generate the overlap lists for the remaining volumes
  struct pointer_to_1d_int_list temp_list_local;
  temp_list_local.num_elements = number_of_volumes;
  temp_list_local.elements = malloc(number_of_volumes*sizeof(int));
  
  int child,used_elements;
  // Create overlap for the remaining volumes
  for (parent=1;parent<number_of_volumes;parent++) {
      true_overlap_lists[parent] = malloc(sizeof(struct pointer_to_1d_int_list));
      used_elements = 0;
      temp_list_local.elements[used_elements++] = 0; // Alwasy overlaps with the surrounding vacuum.
      
      for (child=1;child<number_of_volumes;child++) {
        if (child == parent) temp_list_local.elements[used_elements++] = child;
        else {
        if (strcmp("sphere",Volumes[parent]->geometry.shape) == 0 && strcmp("sphere",Volumes[child]->geometry.shape) == 0) {
            if (sphere_overlaps_sphere(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("cylinder",Volumes[parent]->geometry.shape) == 0 && strcmp("cylinder",Volumes[child]->geometry.shape) == 0) {
            if (cylinder_overlaps_cylinder(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("box",Volumes[parent]->geometry.shape) == 0 && strcmp("box",Volumes[child]->geometry.shape) == 0) {
            if (box_overlaps_box(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("cone",Volumes[parent]->geometry.shape) == 0 && strcmp("cone",Volumes[child]->geometry.shape) == 0) {
            if (cone_overlaps_cone(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("mesh",Volumes[parent]->geometry.shape) == 0 && strcmp("mesh",Volumes[child]->geometry.shape) == 0) {
            if (mesh_overlaps_mesh(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("box",Volumes[parent]->geometry.shape) == 0 && strcmp("cylinder",Volumes[child]->geometry.shape) == 0) {
            if (box_overlaps_cylinder(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("cylinder",Volumes[parent]->geometry.shape) == 0 && strcmp("box",Volumes[child]->geometry.shape) == 0) {
            if (cylinder_overlaps_box(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("box",Volumes[parent]->geometry.shape) == 0 && strcmp("sphere",Volumes[child]->geometry.shape) == 0) {
            if (box_overlaps_sphere(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("sphere",Volumes[parent]->geometry.shape) == 0 && strcmp("box",Volumes[child]->geometry.shape) == 0) {
            if (sphere_overlaps_box(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("sphere",Volumes[parent]->geometry.shape) == 0 && strcmp("cylinder",Volumes[child]->geometry.shape) == 0) {
            if (sphere_overlaps_cylinder(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("cylinder",Volumes[parent]->geometry.shape) == 0 && strcmp("sphere",Volumes[child]->geometry.shape) == 0) {
            if (cylinder_overlaps_sphere(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("cone",Volumes[parent]->geometry.shape) == 0 && strcmp("sphere",Volumes[child]->geometry.shape) == 0) {
            if (cone_overlaps_sphere(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("sphere",Volumes[parent]->geometry.shape) == 0 && strcmp("cone",Volumes[child]->geometry.shape) == 0) {
            if (sphere_overlaps_cone(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("cone",Volumes[parent]->geometry.shape) == 0 && strcmp("cylinder",Volumes[child]->geometry.shape) == 0) {
            if (cone_overlaps_cylinder(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("cylinder",Volumes[parent]->geometry.shape) == 0 && strcmp("cone",Volumes[child]->geometry.shape) == 0) {
            if (cylinder_overlaps_cone(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("cone",Volumes[parent]->geometry.shape) == 0 && strcmp("box",Volumes[child]->geometry.shape) == 0) {
            if (cone_overlaps_box(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("box",Volumes[parent]->geometry.shape) == 0 && strcmp("cone",Volumes[child]->geometry.shape) == 0) {
            if (box_overlaps_cone(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else {
            printf("Need overlap function for type: ");
            printf("%s",Volumes[parent]->geometry.shape);
            printf(" and type: ");
            printf("%s",Volumes[child]->geometry.shape);
            printf(".\n");
            printf("It is not yet supported to mix mesh geometries with the basic shapes, but several mesh geometries are allowed.\n");
            exit(1);
        }
        }
      }
      //allocate_list_from_temp(used_elements,temp_list_local,overlap_lists[parent]);
      allocate_list_from_temp(used_elements,temp_list_local,&temporary_overlap_lists[parent]);
      
      // Save the raw overlap data to the raw_overlap_lists[parent] list
      raw_overlap_lists[parent] = malloc(sizeof(struct pointer_to_1d_int_list));
      raw_overlap_lists[parent]->num_elements = 0;
      allocate_list_from_temp(used_elements,temp_list_local,raw_overlap_lists[parent]);
      
      if (verbal) sprintf(string_output,"Overlaps for Volume (pre mask) %d",parent);
      MPI_MASTER(
      if (verbal) print_1d_int_list(temporary_overlap_lists[parent],string_output);
      )
  }
  
  
  // The temporary_overlap_lists gives the raw overlap data for all volume pairs
  // The next tasks is to take the masks into account, so that a volume is only said to overlap another if all of these statements are true:
  // The volumes overlap each other
  // The mask's of volume 1 overlap volume 2
  // The mask's of volume 2 overlap volume 1
  // The mask's of volume 1 overlap the masks of volume 2
  
  
  int logic_var;
  int overlap_ANY,overlap_ANY_p,overlap_ANY_c;
  int mask_index,mask_index_c,mask_index_p;
  int mask_volume_index,mask_volume_index_p,mask_volume_index_c;

  for (parent=1;parent<number_of_volumes;parent++) {
    used_elements = 0;
    temp_list_local.elements[used_elements++] = 0; // Alwasy overlaps with the surrounding vacuum.
    for (child=1;child<number_of_volumes;child++) {
      if (child != parent) {
        logic_var = 1; // Assume the volumes overlap, and search for evidence that they do not.
      
        // First check if the volumes overlap
        if (0 == on_int_list(temporary_overlap_lists[parent],child)) logic_var = 0;
        
      
        // Check if child overlap with parents masks
        if (logic_var == 1 && Volumes[parent]->geometry.is_masked_volume == 1) {
          overlap_ANY = 0;
          for (mask_index=0;mask_index<Volumes[parent]->geometry.masked_by_list.num_elements;mask_index++) {
            mask_volume_index = Volumes[parent]->geometry.masked_by_list.elements[mask_index];
            if (0 == on_int_list(temporary_overlap_lists[mask_volume_index],child)) {
              if (Volumes[parent]->geometry.mask_mode == 1) {
                logic_var = 0;
                break;
              }
            } else overlap_ANY = 1;
          }
          if (Volumes[parent]->geometry.mask_mode == 2) logic_var = overlap_ANY;
        }
      
        // Check if parent overlap with childs masks
        if (logic_var == 1 && Volumes[child]->geometry.is_masked_volume == 1) {
          overlap_ANY = 0;
          for (mask_index=0;mask_index<Volumes[child]->geometry.masked_by_list.num_elements;mask_index++) {
            mask_volume_index = Volumes[child]->geometry.masked_by_list.elements[mask_index];
            if (0 == on_int_list(temporary_overlap_lists[mask_volume_index],parent)) {
              if (Volumes[child]->geometry.mask_mode == 1) {
                logic_var = 0;
                break;
              }
            } else overlap_ANY = 1;
          }
          if (Volumes[child]->geometry.mask_mode == 2) logic_var = overlap_ANY;
        }
      
        // Check if parents masks overlap childrens masks
        if (logic_var == 1 && Volumes[parent]->geometry.is_masked_volume == 1 && Volumes[child]->geometry.is_masked_volume == 1) {
          overlap_ANY = 0;
          for (mask_index_p=0;mask_index_p<Volumes[parent]->geometry.masked_by_list.num_elements;mask_index_p++) {
            mask_volume_index_p = Volumes[parent]->geometry.masked_by_list.elements[mask_index_p];
            overlap_ANY_p = 1;
            overlap_ANY_c = 0;
            for (mask_index_c=0;mask_index_c<Volumes[child]->geometry.masked_by_list.num_elements;mask_index_c++) {
              mask_volume_index_c = Volumes[child]->geometry.masked_by_list.elements[mask_index_c];
              if (0 == on_int_list(temporary_overlap_lists[mask_volume_index_p],mask_volume_index_c)) {
                if (Volumes[parent]->geometry.mask_mode == 1 && Volumes[child]->geometry.mask_mode == 1) {
                  // If both are in ALL mode and just one combination of masks does not overlap, neither does the common set
                  logic_var = 0;
                  break;
                }
                if (Volumes[parent]->geometry.mask_mode == 2 && Volumes[child]->geometry.mask_mode == 1) {
                  // If the parent is in ANY mode, but the child is in ALL, any one child not overlapping this parent mask, stops the chance for this parent mask
                  overlap_ANY_p = 0;
                  break;
                }
                
              } else {
                // Here because mask_volume_index_p and mask_volume_index_c does overlap
                if (Volumes[parent]->geometry.mask_mode == 1 && Volumes[child]->geometry.mask_mode == 2) {
                  // If the parent is in ALL mode and the child is in ANY mode, stop if a single parent volume does not overlap any child
                  overlap_ANY_c = 1;
                }
                if (Volumes[parent]->geometry.mask_mode == 2 && Volumes[child]->geometry.mask_mode == 2) {
                  // If both parent and child are in any mode, any one overlap between the masks is sufficient
                  overlap_ANY = 1;
                  // Could actually just commit to the overlap list here, and stop all loops.
                }
              }
              
            }
            if (Volumes[parent]->geometry.mask_mode == 1 && Volumes[child]->geometry.mask_mode == 2) logic_var = overlap_ANY_c;
            if (Volumes[parent]->geometry.mask_mode == 2 && Volumes[child]->geometry.mask_mode == 1 && overlap_ANY_p == 1) {
              // When parent is in any mode, and child is in ALL mode, any parent mask that overlaps all children masks is enough to end the parent loop
              logic_var = 1;
              break; // Without this break, only the last parent will matter
            }
            // if (overlap_ANY == 1) break; would speed things up a bit, but only after testing has been started and then it will be repeated
          }
          if (Volumes[parent]->geometry.mask_mode == 2 && Volumes[child]->geometry.mask_mode == 2) logic_var = overlap_ANY;
          // If both volumes have the ANY mode, just one case of overlap is enough.
        }
        
        // If all of the 4 statements above evaluate to true, the two volumes parent and child do overlap and it is added to the list.
        if (logic_var == 1) temp_list_local.elements[used_elements++] = child;
        
      }
    }
    // Allocate the actual overlap list with the new temp_list_local
    allocate_list_from_temp(used_elements,temp_list_local,true_overlap_lists[parent]);
    if (verbal) sprintf(string_output,"Overlaps for Volume (post mask) %d",parent);
    MPI_MASTER(
    if (verbal) print_1d_int_list(*true_overlap_lists[parent],string_output);
    )
  }
  
  // Clean up of dynamically allocated memory
  for(child=1;child<number_of_volumes;child++) free(temporary_overlap_lists[child].elements);
  free(temporary_overlap_lists);
  free(temp_list_local.elements);
  
};

void add_to_mask_intersect_list(struct pointer_to_1d_int_list *mask_intersect_list, int given_volume_index) {
    // A bit simpler than before
    if (on_int_list(*mask_intersect_list,given_volume_index) == 0)
      add_element_to_int_list(mask_intersect_list,given_volume_index);
}


void generate_intersect_check_lists(struct pointer_to_1d_int_list **overlap_lists,struct Volume_struct **Volumes, int number_of_volumes, int verbal) {
  // Generate intersection list for each volume
  MPI_MASTER(
  if (verbal) printf("\nGenerating intersect check lists -------------------- \n");
  )
  // Take overlap list for volume n
  // Remove entries with p < p(n)
  // Remove entries with parents on the list
  
  // Mask update: Mask volumes does not have priorities, do not do step 2 for mask volumes
  // Mask update: Instead of step 3 (Remove entries with parents on the list), move these entries to a Mask intersect list if that parent is masking the entry, otherwise remove
  
  // 1) Mask update: Take overlap list for volume n
  // 2) Mask update: Remove entries with p < p(n), except mask volumes, that are moved to another list A
  // 3) Mask update: Entries with parents on the list are removed (if these parents are masked, only remove the entry if it is a child of their parents masks)
  // 4) Possible optimization: Entries with parents on the A list, are moved to the appropriate mask list for this volume
  // 5) Mask update: Entries on list A are added again, if they are not parents of volume n
  // 6) Mask update: Entries on the main list that are masked are moved to the appropriate mask list for this volume, if their mask is on list A
  
  // Justification of the top algorithm:
  // No need to check intersections with something that does not overlap this volume, so start with intersect list and remove from that.
  // Entries with p < p(n) are "beneath" this volume, and thus doesn't cut it, mask volumes are however always "above" and are treated with care (moved to list A)
  // Entries with parents still on the list is removed, as it is impossible to intersect with them without first going through the parent
  // Entries with parents on the A list can only be seen if the ray is in that particular mask (the parent on the A list), so it can be added to the appropriate mask list for this volume
  // Now all masks that cut the volume are transfered back to the main list, the only masks that overlap and does not cut are parents of this volume, and these are thus not added
  // The entries on the list that are masked are added to the appropriate mask lists of this volume, but only if their mask is on the A list, as only masks on list A can have mask status 1 which is required for this to be relevant (this was true automatically for the ones added to the appropriate lists in the optimization step).
  
  
  // The intersect check list for volume 0 is done manually
  // Declare list for holding logic data for each index in the overlap list of this volume
  struct pointer_to_1d_int_list logic_list;
  logic_list.num_elements = overlap_lists[0]->num_elements;
  logic_list.elements = malloc(logic_list.num_elements * sizeof(int));
  
  // Declare similar list for List A
  struct pointer_to_1d_int_list mask_logic_list;
  mask_logic_list.num_elements = overlap_lists[0]->num_elements;
  mask_logic_list.elements = malloc(mask_logic_list.num_elements * sizeof(int));
  
  
  int iterate;
  int overlap_index;
  
  /*
  // Thise code is an old broken version of intersection_check_list generation only for volume 0, but this case is now included in the loop.
  // Assume all volumes on overlap list should be on intersection check list, and remove the once that should not.
  for (iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 1;
  // Asuume no volumes should go on the mask_logic_list, but add the ones that do.
  for (iterate=0;iterate<mask_logic_list.num_elements;iterate++) mask_logic_list.elements[iterate] = 0;
  
  for (overlap_index = 0;overlap_index < overlap_lists[0]->num_elements;overlap_index++) {
    // No volumes to remove with lower priority, as surrounding vacuum have the lowest.
    
    // Check if this overlap_lists[0]->elements[overlap_index] is a child of another member of the overlap list
    for (iterate = 0;iterate < overlap_lists[0]->num_elements;iterate++) {
        if (iterate != overlap_index) {
            // We are now checking if Volumes[overlap_lists[0]->elements[iterate]]->geometry.children contains overlap_lists[overlap_index]
            if (on_int_list(Volumes[overlap_lists[0]->elements[iterate]]->geometry.children,overlap_lists[0]->elements[overlap_index])) logic_list.elements[overlap_index] = 0;
        }
    }
  }

  Volumes[0]->geometry.intersect_check_list.num_elements = sum_int_list(logic_list);
  Volumes[0]->geometry.intersect_check_list.elements = malloc(Volumes[0]->geometry.intersect_check_list.num_elements * sizeof(int));
  
  iterate = 0;
  for (overlap_index = 0;overlap_index < overlap_lists[0]->num_elements;overlap_index++) {
        if (logic_list.elements[overlap_index])   Volumes[0]->geometry.intersect_check_list.elements[iterate++] = overlap_lists[0]->elements[overlap_index];
  }
  if (logic_list.num_elements > 0) free(logic_list.elements);
  if (mask_logic_list.num_elements > 0) free(mask_logic_list.elements);
  
  MPI_MASTER(
  print_1d_int_list(Volumes[0]->geometry.intersect_check_list,"Intersect check list for Volume 0 (manual)");
  )
  */
  
  // The intersect check lists for the remaining volumes are generated
  int volume_index,mask_volume_number,mask_index;
  //for (volume_index = 1;volume_index < number_of_volumes;volume_index++) {
  for (volume_index = 0;volume_index < number_of_volumes;volume_index++) {
    
      // 1) Take overlap list for volume n
      logic_list.num_elements = overlap_lists[volume_index]->num_elements;
      logic_list.elements = malloc(logic_list.num_elements * sizeof(int));
      mask_logic_list.num_elements = overlap_lists[volume_index]->num_elements;
      mask_logic_list.elements = malloc(mask_logic_list.num_elements * sizeof(int));
      
      
      
      if (Volumes[volume_index]->geometry.is_mask_volume == 1) {
        // Masks do not have any entries on their intersect check list, as they are never the current volume
        for (iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 0;
        for (iterate=0;iterate<mask_logic_list.num_elements;iterate++) mask_logic_list.elements[iterate] = 0;
      } else {
        for (iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 1;
        for (iterate=0;iterate<mask_logic_list.num_elements;iterate++) mask_logic_list.elements[iterate] = 0;
      }
      
      //2) Remove entries with p < p(n), except mask volumes, that are moved to another list A
      for (overlap_index = 0;overlap_index < overlap_lists[volume_index]->num_elements;overlap_index++) {
        
        if (overlap_lists[volume_index]->elements[overlap_index] == 0) logic_list.elements[overlap_index] = 0; // The surrounding vacuum has lower priority (and is not a mask)
        else if (Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.is_mask_volume == 1) {
           logic_list.elements[overlap_index] = 0;      // Remove this volume from the main list
           mask_logic_list.elements[overlap_index] = 1; // Add mask volumes to seperat list
        }
        else if (volume_index != 0) { // Only relevant to remove elements because of priority if the volume_index is different from 0, meaning the surrounding vacuum skips this step
           if (Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.priority_value < Volumes[volume_index]->geometry.priority_value) {
           // If the investigated volume on the overlap_list have lower priority than the volume with volume_index, remove it
           logic_list.elements[overlap_index] = 0; // Remove this volume from the list
           }
        }
      }
      
      // 3) Entries with parents on the list are removed
      for (overlap_index = 0;overlap_index < overlap_lists[volume_index]->num_elements;overlap_index++) {
        // Check if this overlap_lists[0]->elements[overlap_index] is a child of another member of the overlap list
        for (iterate = 0;iterate < overlap_lists[volume_index]->num_elements;iterate++) {
             if (iterate != overlap_index) { // Only necessary if a volume determines that it has itself as child, but a nice safety.
                // We are now checking if Volumes[overlap_lists[volume_index]->elements[iterate]]->geometry.children contains overlap_lists[overlap_index]
                // The && part is needed because we do not remove children of elements that have allready been removed because of their priority
                
                // if (on_int_list(Volumes[overlap_lists[volume_index]->elements[iterate]]->geometry.children,overlap_lists[volume_index]->elements[overlap_index]) && logic_list.elements[overlap_lists[volume_index]->elements[iterate]] == 1) {logic_list.elements[overlap_index] = 0; bug fixed on 3/4 2016
                if (on_int_list(Volumes[overlap_lists[volume_index]->elements[iterate]]->geometry.children,overlap_lists[volume_index]->elements[overlap_index]) && logic_list.elements[iterate] == 1) logic_list.elements[overlap_index] = 0;
                
                /*
                Explanation with simpler notation
                
                Overlap list of volume i = O_i
                Element j of overlap list i = O_i(j)
                
                Children list of volume i = C_i
                Element j on children list i = C_i(j)
                
                Priority of volume i p(i)
                
                for i = volumes
                    for j in O_i(j) 
                        logic(j) = 1;
                        if p(i) > p(O_i(j)) logic(j) = 0; // Remove if lower priority than the currently checked
                        
                        for k in O_i(k)
                            if (O_i(j) is contained on the list C_k && logic(k) == 1) logic(j) = 0
                */
                
                // 4) Entries with parents on the A list, are moved to the appropriate mask list for this volume
                if (on_int_list(Volumes[overlap_lists[volume_index]->elements[iterate]]->geometry.children,overlap_lists[volume_index]->elements[overlap_index]) && mask_logic_list.elements[iterate] == 1) {
                   logic_list.elements[overlap_index] = 0; // Remove from main list (Not strictly needed as it will be removed already if it is on list A)
                   // Add overlap_lists[volume_index]->elements[overlap_index] to volumes mask intersect list for mask with index overlap_lists[volume_index]->elements[iterate]
                   //add_to_mask_intersect_lists(&Volumes[volume_index]->geometry.mask_intersect_lists,overlap_lists[volume_index]->elements[iterate],overlap_lists[volume_index]->elements[overlap_index]);
                   // Simpler method that just collects all the masked intersect parts on a 1d_int list instead of seperating them for each mask
                   add_to_mask_intersect_list(&Volumes[volume_index]->geometry.mask_intersect_list,overlap_lists[volume_index]->elements[overlap_index]);
                }
                
             }
        }
      }
      
      // 5) Entries on list A are added again, if they are not parents of volume n
      // Time to add mask volumes removed back to the intersect list, if they are not parents of the current volume, meaning the current volume should not be a child of the mask
      for (overlap_index = 0;overlap_index < overlap_lists[volume_index]->num_elements;overlap_index++) {
        if (mask_logic_list.elements[overlap_index] == 1) {
          mask_volume_number = overlap_lists[volume_index]->elements[overlap_index];
          if (on_int_list(Volumes[mask_volume_number]->geometry.children,volume_index) == 0) logic_list.elements[overlap_index] = 1; // Add it back to the list
        }
      }
      
      // 6) Entries on the main list that are masked are moved to the appropriate mask list for this volume, if their mask is on list A
      int mask_index,mask_mode,found_index,logic_ALL;
      for (overlap_index = 0;overlap_index < overlap_lists[volume_index]->num_elements;overlap_index++) {
        if (logic_list.elements[overlap_index] == 1 && Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.is_masked_volume == 1) {
          logic_list.elements[overlap_index] = 0; // If the volume is masked, remove it from the intersect check list
          // Could actually keep it on the intersect list if the volume's mask is a parent of the current volume, now it will just be added in all cases
          
          // When ALL setting is used, all masks should overlap the current volume, otherwise the user probably made a mistake, this should be checked in error checking
          // When ANY setting is used, at least one mask should overlap the current volume, otherwise the user probably made a mistake, this should be checked in error checking
          // mask_mode == 1 => ALL / mask_mode == 2 => ANY
          mask_mode = Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.mask_mode;
          
          if (mask_mode == 1) {
            logic_ALL = 1;
            for (iterate=0;iterate<Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.masked_by_list.num_elements;iterate++) {
              mask_index = Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.masked_by_list.elements[iterate];
              
              if (on_int_list(*overlap_lists[volume_index],mask_index) == 0) {
                // If any one of the volumes masks do not overlap with this volume, there is no reason check intersections with the volume regardless of the mask status's
                logic_ALL = 0;
                break;
              }
            }
          if (logic_ALL == 1) {
            // If all of it's masks are overlapping the current volume, add it to all relevant mask intersect lists of this volume
            for (iterate=0;iterate<Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.masked_by_list.num_elements;iterate++) {
              mask_index = Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.masked_by_list.elements[iterate];
              //add_to_mask_intersect_lists(&Volumes[volume_index]->geometry.mask_intersect_lists,mask_index,overlap_lists[volume_index]->elements[overlap_index]);
              // Adding the masked intersect list elements to another list
              add_to_mask_intersect_list(&Volumes[volume_index]->geometry.mask_intersect_list,overlap_lists[volume_index]->elements[overlap_index]);
            }
          }
            
            
          } else if (mask_mode == 2) {
          // When in ANY mode, the problem is easier, as not all masks have to overlap the volume, and we can add each one to the list
            for (iterate=0;iterate<Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.masked_by_list.num_elements;iterate++) {
              mask_index = Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.masked_by_list.elements[iterate];
            
              if (on_int_list(*overlap_lists[volume_index],mask_index) == 1) {
                //add_to_mask_intersect_lists(&Volumes[volume_index]->geometry.mask_intersect_lists,mask_index,overlap_lists[volume_index]->elements[overlap_index]);
                // Adding the masked intersect list elements to another list
                add_to_mask_intersect_list(&Volumes[volume_index]->geometry.mask_intersect_list,overlap_lists[volume_index]->elements[overlap_index]);
              }
            }
            
          }
        }
      }
      
      
      Volumes[volume_index]->geometry.intersect_check_list.num_elements = sum_int_list(logic_list);
      Volumes[volume_index]->geometry.intersect_check_list.elements = malloc(Volumes[volume_index]->geometry.intersect_check_list.num_elements * sizeof(int));
      
      iterate = 0;
      for (overlap_index = 0;overlap_index < overlap_lists[volume_index]->num_elements;overlap_index++) {
            if (logic_list.elements[overlap_index])   Volumes[volume_index]->geometry.intersect_check_list.elements[iterate++] = overlap_lists[volume_index]->elements[overlap_index];
      }
      free(logic_list.elements); // Need to be careful with names for variables to be freed, as they can collide with names in main because of automatic declaration of external variables
      free(mask_logic_list.elements);
      
      
      char string_output[128];
      MPI_MASTER(
      if (verbal) sprintf(string_output,"Intersect check list for Volume %d",volume_index);
      if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.intersect_check_list,string_output);
      if (verbal) sprintf(string_output,"Mask intersect check list for Volume %d",volume_index);
      if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.mask_intersect_list,string_output);
      /*
      if (verbal) {
        for (mask_index=0;mask_index<Volumes[volume_index]->geometry.mask_intersect_lists.number_of_lists;mask_index++) {
          sprintf(string_output," - mask intersect check list for mask %d which is volume %d",mask_index,Volumes[volume_index]->geometry.mask_intersect_lists.mask_indices.elements[mask_index]);
          print_1d_int_list(Volumes[volume_index]->geometry.mask_intersect_lists.lists[mask_index],string_output);
        }
      }
      */
      
      )
  }
};

void generate_parents_lists(struct pointer_to_1d_int_list **parents_lists, struct Volume_struct **Volumes, int number_of_volumes, int verbal, int mask_mode) {
  // Function for generating parent lists for all volumes
  // A volume m has n as a parent, if volume n has volume m as a child
  
  // if mask_mode == 0, masks are ignored, if mask_mode == 1, masks are taken into account.
  
  MPI_MASTER(
  if (verbal) {
    if (mask_mode == 1)
        printf("\nGenerating parents lists ---------------------------- \n");
    else if (mask_mode == 0)
        printf("\nGenerating parents lists (ignoring masks) ----------- \n");
    else {
        printf("Error, the function parents_lists got a non defined mask_mode");
        exit(1);
    }
  }
  
  )
  // Volume iterate has volume p as a parent, if volume p has volume iterate as child.
  
  struct pointer_to_1d_int_list temp_list_local;
  temp_list_local.num_elements = number_of_volumes;
  temp_list_local.elements = malloc(number_of_volumes*sizeof(int));
  
  // Loop over
  int iterate,parent,used_elements;
  for (iterate = 0;iterate < number_of_volumes;iterate++) {
    // clear temp list
    used_elements = 0;
    parents_lists[iterate] = malloc(sizeof(struct pointer_to_1d_int_list));
    for (parent = 0;parent < number_of_volumes;parent++) {
        if (on_int_list(Volumes[parent]->geometry.children,iterate))
          if (mask_mode == 1 || (Volumes[parent]->geometry.is_mask_volume == 0 && Volumes[iterate]->geometry.is_mask_volume == 0))
            temp_list_local.elements[used_elements++] = parent;
    }
      allocate_list_from_temp(used_elements,temp_list_local,parents_lists[iterate]);
      
      char string_output[128];
      MPI_MASTER(
      if (verbal) sprintf(string_output,"Parents for Volume %d",iterate);
      if (verbal) print_1d_int_list(*parents_lists[iterate],string_output);
      )
  }
  free(temp_list_local.elements);

};

void generate_true_parents_lists(struct pointer_to_1d_int_list **parents_lists, struct pointer_to_1d_int_list **true_children_lists, struct Volume_struct **Volumes, int number_of_volumes, int verbal, int mask_mode) {
  // Function for generating parent lists for all volumes
  // A volume m has n as a parent, if volume n has volume m as a child
  
  // if mask_mode == 0, masks are ignored, if mask_mode == 1, masks are taken into account.
  
  MPI_MASTER(
  if (verbal) {
    if (mask_mode == 1)
        printf("\nGenerating parents lists ---------------------------- \n");
    else if (mask_mode == 0)
        printf("\nGenerating parents lists (ignoring masks) ----------- \n");
    else {
        printf("Error, the function parents_lists got a non defined mask_mode");
        exit(1);
    }
  }
  
  )
  // Volume iterate has volume p as a parent, if volume p has volume iterate as child.
  
  struct pointer_to_1d_int_list temp_list_local;
  temp_list_local.num_elements = number_of_volumes;
  temp_list_local.elements = malloc(number_of_volumes*sizeof(int));
  
  // Loop over
  int iterate,parent,used_elements;
  for (iterate = 0;iterate < number_of_volumes;iterate++) {
    // clear temp list
    used_elements = 0;
    parents_lists[iterate] = malloc(sizeof(struct pointer_to_1d_int_list)); // allocate_list_from_temp allocates
    for (parent = 0;parent < number_of_volumes;parent++) {
        //if (on_int_list(Volumes[parent]->geometry.children,iterate))
        if (on_int_list(*true_children_lists[parent],iterate))
          if (mask_mode == 1 || (Volumes[parent]->geometry.is_mask_volume == 0 && Volumes[iterate]->geometry.is_mask_volume == 0))
            temp_list_local.elements[used_elements++] = parent;
    }
      allocate_list_from_temp(used_elements,temp_list_local,parents_lists[iterate]);
      
      char string_output[128];
      MPI_MASTER(
      if (verbal) sprintf(string_output,"Parents for Volume %d",iterate);
      if (verbal) print_1d_int_list(*parents_lists[iterate],string_output);
      )
  }
  free(temp_list_local.elements);

};

void generate_intersect_check_lists_experimental(struct pointer_to_1d_int_list **true_overlap_lists, struct pointer_to_1d_int_list **raw_overlap_lists, struct pointer_to_1d_int_list **parents_lists, struct pointer_to_1d_int_list **true_parents_lists , struct Volume_struct **Volumes, int number_of_volumes, int verbal) {
  // Generates the intersect_check_list and mask_intersect_list for each Volume.
  /*
  Description of needed lists for volume n:
  Children list: List of volumes that is contained within volume n (is stored in the Volumes struct)
  True Children list: List of volumes that when masked by their masks is contained within volume n when masked by it's masks
  
  Parents list: List of volumes that contains volume n
  True parents list: List of volumes that when masked by their masks contains volume n when it is masked by it's masks
  
  raw overlap list: List of volumes whos geometry overlaps the geometry of volume n
  true overlap list: List of volumes whos geometry overlaps the geometry of volume n when the masks of both volumes are applied
  
  
  
  The algorithm:
  
  1) Take the true overlap list for volume n
  2) remove parents of n (normal parent list, with masks)
  3) remove volumes that do not mask n and are true parents of n
  4) remove volumes that are not masks and have lower priority than n
  5) remove volumes that have at least one true parent on the list (in step 4) that is not a mask volume
  6) split the list into two, the intersect_check_list which is all non masked volumes still on the list, and the mask_intersect_list which is the masked volumes
  7) remove volumes on the mask_intersect_list whos mask does not overlap (standard overlap list) n
  
  In step 5 the order in which the volumes are tested and removed may matter, so it is specifically stated that it is as the list looked in step 4.
  */
  
  MPI_MASTER(
  if (verbal) printf("\nGenerating intersect check lists -------------------- \n");
  )
  
  struct pointer_to_1d_int_list work_list;
  struct pointer_to_1d_int_list logic_list;
  
  int volume_index,iterate,parent,mask_index,masked_volume_index,ANY_logic,true_parent_volume_number;
  int *mask_check,*mask_start;
  for (volume_index = 0;volume_index < number_of_volumes;volume_index++) {
    
    // 1) Take the true overlap list for volume n
    // Create copy of true_overlap_lists to work with
    
    if (Volumes[volume_index]->geometry.is_mask_volume == 1) {
      // Bug fixed on 26/11/2016, do not create intersection lists for masks as they are not used, and affects destinations lists in a problematic way
      Volumes[volume_index]->geometry.intersect_check_list.num_elements = 0;
      Volumes[volume_index]->geometry.mask_intersect_list.num_elements = 0;
      
    } else {
      work_list.num_elements = true_overlap_lists[volume_index]->num_elements;
      work_list.elements = malloc(work_list.num_elements * sizeof(int));
      for (iterate=0;iterate<work_list.num_elements;iterate++) work_list.elements[iterate] = true_overlap_lists[volume_index]->elements[iterate];
    
    //if (verbal) print_1d_int_list(work_list,"After 1)");
    
    
    //2) remove parents of n (normal parent list, with masks)
    for (iterate=work_list.num_elements-1;iterate>-1;iterate--) {
      if (on_int_list(*parents_lists[volume_index],work_list.elements[iterate]))
        remove_element_in_list_by_index(&work_list,iterate);
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 2)");
    
    //3) remove volumes that do not mask n and are true parents of n
    for (iterate=work_list.num_elements-1;iterate>-1;iterate--) {
      if (on_int_list(Volumes[volume_index]->geometry.masked_by_list,work_list.elements[iterate]) == 0 && on_int_list(*true_parents_lists[volume_index],work_list.elements[iterate]))
        remove_element_in_list_by_index(&work_list,iterate);
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 3)");
    
    //4) remove volumes that are not masks and have lower priority than n
    for (iterate=work_list.num_elements-1;iterate>-1;iterate--) {
      if (Volumes[work_list.elements[iterate]]->geometry.is_mask_volume == 0 && Volumes[work_list.elements[iterate]]->geometry.priority_value < Volumes[volume_index]->geometry.priority_value)
        remove_element_in_list_by_index(&work_list,iterate);
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 4)");
    
    //5) remove volumes that have at least one true parent on the list (in step 4) that is not a mask volume
    // Here a logic_list is used to not have the order of removal matter
    logic_list.num_elements = work_list.num_elements;
    logic_list.elements = malloc(logic_list.num_elements * sizeof(int));
    for(iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 1;
    
    for (iterate=0;iterate<work_list.num_elements;iterate++) {
      for (parent=0;parent<true_parents_lists[work_list.elements[iterate]]->num_elements;parent++) {
        true_parent_volume_number = true_parents_lists[work_list.elements[iterate]]->elements[parent];
        if (on_int_list(work_list,true_parent_volume_number) && Volumes[true_parent_volume_number]->geometry.is_mask_volume == 0) {
          // Since element number iterate on the work list have a true parent on the work list, it can be removed
          logic_list.elements[iterate] = 0;
          break;
        }
      }
    }
    
    // Now the elements marked for removal can be removed without interfering in the operation
    for (iterate=work_list.num_elements-1;iterate>-1;iterate--) {
      if (logic_list.elements[iterate] == 0) remove_element_in_list_by_index(&work_list,iterate);
    }
    free(logic_list.elements);
    
    //if (verbal) print_1d_int_list(work_list,"After 5)");
      
    //6) split the list into two, the intersect_check_list which is all non masked volumes still on the list, and the mask_intersect_list which is the masked volumes
    
    Volumes[volume_index]->geometry.intersect_check_list.num_elements = 0;
    Volumes[volume_index]->geometry.mask_intersect_list.num_elements = 0;
    
    for (iterate=0;iterate<work_list.num_elements;iterate++) {
      if (Volumes[work_list.elements[iterate]]->geometry.is_masked_volume == 1) {
        add_element_to_int_list(&Volumes[volume_index]->geometry.mask_intersect_list,work_list.elements[iterate]);
      } else {
        add_element_to_int_list(&Volumes[volume_index]->geometry.intersect_check_list,work_list.elements[iterate]);
      }
    }
    
    //if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.intersect_check_list,"After 6) intersect_check_list");
    //if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.mask_intersect_list,"After 6) mask_intersect_list");
    
    //7) remove volumes on the mask_intersect_list whos masks does not overlap (standard overlap list) n
    for (iterate=Volumes[volume_index]->geometry.mask_intersect_list.num_elements-1;iterate>-1;iterate--) {
      // Need to check if the volumes masking Volumes[volume_index]->geometry.mask_intersect_list.elements[iterate] overlaps with n
      masked_volume_index = Volumes[volume_index]->geometry.mask_intersect_list.elements[iterate];
      
      if (Volumes[masked_volume_index]->geometry.mask_mode == 1) {// All mode, if just one does not overlap, remove the element
        for (mask_start=mask_check=Volumes[masked_volume_index]->geometry.masked_by_list.elements;mask_check-mask_start<Volumes[masked_volume_index]->geometry.masked_by_list.num_elements;mask_check++) {
          if (on_int_list(*raw_overlap_lists[volume_index],*mask_check) == 0) {
            remove_element_in_list_by_index(&Volumes[volume_index]->geometry.mask_intersect_list,iterate);
            break;
          }
        }
      } else { // ANY mode, just one need to overlap in order to keep the element
        ANY_logic = 0;
        for (mask_start=mask_check=Volumes[masked_volume_index]->geometry.masked_by_list.elements;mask_check-mask_start<Volumes[masked_volume_index]->geometry.masked_by_list.num_elements;mask_check++) {
          if (on_int_list(*raw_overlap_lists[volume_index],*mask_check) == 1) {
            ANY_logic = 1;
            break;
          }
        }
        if (ANY_logic == 0) remove_element_in_list_by_index(&Volumes[volume_index]->geometry.mask_intersect_list,iterate);
      }
    }
    
    if (work_list.num_elements > 0) free(work_list.elements);
    }
    
    
    //if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.mask_intersect_list,"After 7) mask_intersect_list");
    
    char string_output[128];
    MPI_MASTER(
    if (verbal) sprintf(string_output,"Intersect check list for Volume %d",volume_index);
    if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.intersect_check_list,string_output);
    if (verbal) sprintf(string_output,"Mask intersect check list for Volume %d",volume_index);
    if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.mask_intersect_list,string_output);
    )
    
    
  }
}

void generate_grandparents_lists(struct pointer_to_1d_int_list **grandparents_lists, struct pointer_to_1d_int_list **parents_lists, int number_of_volumes, int verbal) {
  // Function for generating grandparents lists
  // Volume iterate has volume p as a grandparent, if volume p has a parent T, who has volume iterate as parent.
  // Alternertively:
  // Volume iterate has volume p as a grandparent, if volume p have a child that is volume iterate's parent.
  
  MPI_MASTER(
  if (verbal) printf("\nGenerating grandparents lists ----------------------- \n");
  )

  struct pointer_to_1d_int_list common;
  common.num_elements = number_of_volumes;
  common.elements = malloc(common.num_elements*sizeof(int)); // Maximum needed space.
  
  struct pointer_to_1d_int_list temp_list_local;
  temp_list_local.num_elements = number_of_volumes;
  temp_list_local.elements = malloc(number_of_volumes*sizeof(int));
  
  int iterate,reset_int,parent,child,used_elements;
  for (iterate = 0;iterate < number_of_volumes;iterate++) {
    // clear temp list
    used_elements = 0;

    for (reset_int=0; reset_int<number_of_volumes; reset_int++) common.elements[reset_int] = -1; // Initialize to impossible volume ids
    for (reset_int=0; reset_int<number_of_volumes; reset_int++) temp_list_local.elements[reset_int] = -1; // Initialize to impossible volume ids

    grandparents_lists[iterate] = malloc(sizeof(struct pointer_to_1d_int_list));
    
    for (parent = 0; parent<parents_lists[iterate]->num_elements; parent++) {
        // parent number p parents_lists[iterate].elements.[p] in the parent_list for iterate.
        on_both_int_lists(parents_lists[parents_lists[iterate]->elements[parent]], parents_lists[iterate], &common);
        // returns a pointer_to_1d_list, with all the elements that are in common.
        for (child = 0;child < common.num_elements;child++) {
            // Need to make sure the element is not already on the list
            if (0 == on_int_list(temp_list_local, common.elements[child])) {
              temp_list_local.elements[used_elements++] = common.elements[child];
            }
        }
    }
    allocate_list_from_temp(used_elements, temp_list_local, grandparents_lists[iterate]);
  
    char string_output[128];
    MPI_MASTER(
      if (verbal) sprintf(string_output,"Grandparents for Volume %d", iterate);
      if (verbal) print_1d_int_list(*grandparents_lists[iterate], string_output);
    )
  }
  free(temp_list_local.elements);
  free(common.elements);
};


void generate_destinations_lists_experimental(struct pointer_to_1d_int_list **true_overlap_lists, struct pointer_to_1d_int_list **true_children_lists, struct pointer_to_1d_int_list **true_parents_lists, struct pointer_to_1d_int_list **true_grandparents_lists, struct Volume_struct **Volumes, int number_of_volumes, int verbal) {
  // Generates destinations list for for all volumes
  // Current implementation uses true_parents_lists and true_grandparents_lists that are generated as if no masks were defined

  MPI_MASTER(
  if (verbal) printf("\nGenerating destinations lists ----------------------- \n");
  )
  // Volume 0 has an hardcoded empty destinations list
  Volumes[0]->geometry.destinations_list.num_elements = 0;

  struct pointer_to_1d_int_list work_list;
  int volume_index,iterate,iterate2,found_index,I_index,I_volume;
  
  for (volume_index=1;volume_index<number_of_volumes;volume_index++) {
    
    // 1) Take the true overlap list for volume n
    // Create copy of true_overlap_lists to work with
    work_list.num_elements = true_overlap_lists[volume_index]->num_elements;
    work_list.elements = malloc(work_list.num_elements * sizeof(int));
    for (iterate=0;iterate<work_list.num_elements;iterate++) work_list.elements[iterate] = true_overlap_lists[volume_index]->elements[iterate];
    
    //if (verbal) print_1d_int_list(work_list,"After 1)");
    
    // 2) Remove elements from n's intersection list
    for (iterate=work_list.num_elements-1;iterate>-1;iterate--) {
      if (on_int_list(Volumes[volume_index]->geometry.intersect_check_list,work_list.elements[iterate]))
        remove_element_in_list_by_index(&work_list,iterate);
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 2)");
  
    // 3) Remove true children of non-mask elements on n's intersection list
    for (I_index=0;I_index<Volumes[volume_index]->geometry.intersect_check_list.num_elements;I_index++) {
      I_volume = Volumes[volume_index]->geometry.intersect_check_list.elements[I_index];
      if (Volumes[I_volume]->geometry.is_mask_volume == 0) {
        for (iterate=0;iterate<true_children_lists[I_volume]->num_elements;iterate++) {
          remove_element_in_list_by_value(&work_list,true_children_lists[I_volume]->elements[iterate]);
        }
      }
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 3)");
    
    // 4) Remove elements from mask intersection list
    for (iterate=work_list.num_elements-1;iterate>-1;iterate--) {
      if (on_int_list(Volumes[volume_index]->geometry.mask_intersect_list,work_list.elements[iterate]))
        remove_element_in_list_by_index(&work_list,iterate);
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 4)");
    
    // 5) Remove true children of elements on n's mask intersection list
    for (I_index=0;I_index<Volumes[volume_index]->geometry.mask_intersect_list.num_elements;I_index++) {
      I_volume = Volumes[volume_index]->geometry.mask_intersect_list.elements[I_index];
      if (Volumes[I_volume]->geometry.is_mask_volume == 0) {
        for (iterate=0;iterate<true_children_lists[I_volume]->num_elements;iterate++) {
          remove_element_in_list_by_value(&work_list,true_children_lists[I_volume]->elements[iterate]);
        }
      }
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 5)");
    
    // 6) Remove true children of n
    for (iterate=0;iterate<true_children_lists[volume_index]->num_elements;iterate++)
      remove_element_in_list_by_value(&work_list,true_children_lists[volume_index]->elements[iterate]);
      
    //if (verbal) print_1d_int_list(work_list,"After 6)");
      
    // 7) Remove true grandparents of n
    for (iterate=0;iterate<true_grandparents_lists[volume_index]->num_elements;iterate++)
      remove_element_in_list_by_value(&work_list,true_grandparents_lists[volume_index]->elements[iterate]);
      
    //if (verbal) print_1d_int_list(work_list,"After 7)");
      
    // 8) Remove mask volumes
    for (iterate=work_list.num_elements-1;iterate>-1;iterate--) {
      if (Volumes[work_list.elements[iterate]]->geometry.is_mask_volume == 1)
        remove_element_in_list_by_index(&work_list,iterate);
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 8)");
    
    // 9) Remove true parents of n on the list that has other true parents of n on the list with higher priority
    for (iterate=work_list.num_elements-1;iterate>-1;iterate--) {
      if (on_int_list(*true_parents_lists[volume_index],work_list.elements[iterate])){
        // work_list.elements[iterate] is the volume index of a volume that is a true parent of n
        for (iterate2=0;iterate2<work_list.num_elements;iterate2++) {
          if (on_int_list(*true_parents_lists[volume_index],work_list.elements[iterate2]) && iterate != iterate2) {
            if (Volumes[work_list.elements[iterate]]->geometry.priority_value < Volumes[work_list.elements[iterate2]]->geometry.priority_value) {
              //printf("Removing element number %d (V%d) because element number %d (V%d) had higher priority \n",iterate,work_list.elements[iterate],iterate2,work_list.elements[iterate2]);
              remove_element_in_list_by_index(&work_list,iterate);
              break; // Missing break inserted on 14/9/2016
            }
          }
        }
      }
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 9)");
    
    // 10) The remaining list is the destinations_list
    Volumes[volume_index]->geometry.destinations_list.num_elements = work_list.num_elements;
    Volumes[volume_index]->geometry.destinations_list.elements = malloc(Volumes[volume_index]->geometry.destinations_list.num_elements*sizeof(int));
    
    for (iterate=0;iterate<work_list.num_elements;iterate++)
      Volumes[volume_index]->geometry.destinations_list.elements[iterate] = work_list.elements[iterate];
    
    // Clean up after work_list so the next can be allocated
    free(work_list.elements);
  
  
    char string_output[128];
    MPI_MASTER(
    if (verbal) sprintf(string_output,"Destinations list for Volume %d",volume_index);
    if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.destinations_list,string_output);
    )

  }

};

void generate_destinations_list(int N_volume,struct Volume_struct **Volumes,struct pointer_to_1d_int_list original_overlap_list,struct pointer_to_1d_int_list *parent_list, struct pointer_to_1d_int_list *grandparent_list) {
    // This function generates the destinations_list for a single volume index, N_volume
    // The destination list describes which volumes a ray can enter when leaving N_volume
    // Each of the 6 steps for the algorithm is commented individually, and debug print statements are available
    
    // Mask update: Mask volumes are to be removed from all destinations_list as they can not be the current volume
    // It is not that simple, as some volume will then get empty destination lists. Need to revise this algorithm.
    
    // 1) Start with the overlap list of volume N
    // 2) remove all Volumes from the overlap list of N, which is also on the intersect_check_list
    // 3) remove the children of volumes removed in step 2)
    // 4) remove the children of N
    // 5) remove the grandparents of N
    // 6) remove volumes with lower priority than parents of N still on the list
    // 7) The remaing list is the destinations list
    
    
    // The destination list system should run without masks altogether, meaning parent and grandparent lists should not use them,
    //  and all masks should be removed in step 2
    
    // 1) Start with the overlap list of volume N
    // 2) remove all masks on the list
    // 3) remove all Volumes from the overlap list of N, which is also on the intersect_check_list
    // 4) remove the children of volumes removed in step 2) (if the removed volume was not a mask )
    // 5) remove the children of N
    // 6) remove the grandparents of N
    // 7) remove volumes with lower priority than parents of N still on the list
    // 8) The remaing list is the destinations list
    
    

    // 1) Start with the overlap list of volume N
    struct pointer_to_1d_int_list overlap_list;
    overlap_list.num_elements = original_overlap_list.num_elements;
    overlap_list.elements = malloc(overlap_list.num_elements*sizeof(int));
    int iterate;
    for (iterate=0;iterate<overlap_list.num_elements;iterate++) overlap_list.elements[iterate] = original_overlap_list.elements[iterate];
    
    char string_output[124];
    // sprintf(string_output,"Destinations list for Volume %d step 1",N_volume);
    // print_1d_int_list(overlap_list,string_output);
    
    // 2) remove all masks
    for (iterate=overlap_list.num_elements-1;iterate > -1;iterate--) {
        if (Volumes[overlap_list.elements[iterate]]->geometry.is_mask_volume == 1) {
            remove_element_in_list_by_index(&overlap_list,iterate);
        }
    }
    
    // 3) remove all Volumes from the overlap list of N, which is also on the intersect_check_list
    struct pointer_to_1d_int_list removed_under_2;
    removed_under_2.num_elements = 0;
    int to_check;
    removed_under_2.elements = malloc( Volumes[N_volume]->geometry.intersect_check_list.num_elements * sizeof(int));
    for (iterate=0;iterate < Volumes[N_volume]->geometry.intersect_check_list.num_elements;iterate++) {
        to_check = Volumes[N_volume]->geometry.intersect_check_list.elements[iterate];
        if (on_int_list(overlap_list,to_check)) {
            removed_under_2.elements[removed_under_2.num_elements++] = to_check;
            remove_element_in_list_by_value(&overlap_list,to_check);
        }
    }
    
    // sprintf(string_output,"Destinations list for Volume %d step 2",N_volume);
    // print_1d_int_list(overlap_list,string_output);
    
    // 4) remove the children of volumes removed in step 2)
    int children;
    for (iterate=0;iterate<removed_under_2.num_elements;iterate++){
        for (children = 0;children < Volumes[removed_under_2.elements[iterate]]->geometry.children.num_elements;children++) {
          remove_element_in_list_by_value(&overlap_list,Volumes[removed_under_2.elements[iterate]]->geometry.children.elements[children]);
        }
    }
    
    // sprintf(string_output,"Destinations list for Volume %d step 3",N_volume);
    // print_1d_int_list(overlap_list,string_output);
    
    // 5) remove the children of N
    for (children = 0;children < Volumes[N_volume]->geometry.children.num_elements;children++) {
        remove_element_in_list_by_value(&overlap_list,Volumes[N_volume]->geometry.children.elements[children]);
    }
    
    // sprintf(string_output,"Destinations list for Volume %d step 4",N_volume);
    // print_1d_int_list(overlap_list,string_output);
    
    // 6) remove the grandparents of N
    int grandparent;
    for (grandparent = 0;grandparent < grandparent_list->num_elements;grandparent++) {
        remove_element_in_list_by_value(&overlap_list,grandparent_list->elements[grandparent]);
    }
    
    // sprintf(string_output,"Destinations list for Volume %d step 5",N_volume);
    // print_1d_int_list(overlap_list,string_output);
    
    // 7) remove volumes with lower priority than parents of N still on the list
    struct pointer_to_1d_int_list logic_list;
    logic_list.num_elements = overlap_list.num_elements;
    if (logic_list.num_elements>0) logic_list.elements = malloc(logic_list.num_elements*sizeof(int));
    for (iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 1;
    
    int parent;
    for (parent=0;parent<parent_list->num_elements;parent++) {
      if (on_int_list(overlap_list,parent_list->elements[parent])) {
        // Found a parent to N on the list, now check all other elements on the list
        for (iterate=0;iterate<overlap_list.num_elements;iterate++) {
          if (parent_list->elements[parent] != overlap_list.elements[iterate]) {
            // if the element iterate have lower priority than the parent, remove it from the list
            if (Volumes[overlap_list.elements[iterate]]->geometry.priority_value < Volumes[parent_list->elements[parent]]->geometry.priority_value) logic_list.elements[iterate] = 0;
          }
        }
      }
    }
    
    // 8) The remaing list is the destinations list
    Volumes[N_volume]->geometry.destinations_list.num_elements = sum_int_list(logic_list);
    Volumes[N_volume]->geometry.destinations_list.elements = malloc(Volumes[N_volume]->geometry.destinations_list.num_elements * sizeof(int));
    
    int overlap_index,used_elements=0;
    for (overlap_index=0;overlap_index < overlap_list.num_elements;overlap_index++)
      if (logic_list.elements[overlap_index] == 1)
        Volumes[N_volume]->geometry.destinations_list.elements[used_elements++] = overlap_list.elements[overlap_index];
    
    if (overlap_list.num_elements>0) free(overlap_list.elements);
    if (logic_list.num_elements>0) free(logic_list.elements);
    
    
    /*
    // Old version before rule 6 and 2 was added
    // 8) The remaing list is the destinations list
    Volumes[N_volume]->geometry.destinations_list.num_elements = overlap_list.num_elements;
    Volumes[N_volume]->geometry.destinations_list.elements = malloc(Volumes[N_volume]->geometry.destinations_list.num_elements * sizeof(int));
    
    int overlap_index;
    for (overlap_index=0;overlap_index < overlap_list.num_elements;overlap_index++)
        Volumes[N_volume]->geometry.destinations_list.elements[overlap_index] = overlap_list.elements[overlap_index];
    
    // sprintf(string_output,"Destination list for Volume %d step 6",N_volume);
    // print_1d_int_list(Volumes[N_volume]->geometry.destinations_list,string_output);
    
    if (overlap_list.num_elements>0) free(overlap_list.elements);
    */
    
    };

void generate_destinations_lists(struct pointer_to_1d_int_list **grandparents_lists, struct pointer_to_1d_int_list **parents_lists, struct pointer_to_1d_int_list **overlap_lists,struct Volume_struct **Volumes, int number_of_volumes, int verbal) {
    // Because of the complexity of the algortithm for generating the destinations list, the function is made for a single volume at the time to keep the notation simpler
    // This funtion runs the destinations list function for each volume
    MPI_MASTER(
    if (verbal) printf("\nGenerating destinations lists ----------------------- \n");
    )
    
    int volume_index;
    for (volume_index = 0;volume_index < number_of_volumes;volume_index++) {
      generate_destinations_list(volume_index,Volumes,*overlap_lists[volume_index],parents_lists[volume_index],grandparents_lists[volume_index]);
      
      char string_output[128];
      if (verbal) sprintf(string_output,"Destinations list for Volume %d",volume_index);
      MPI_MASTER(
      if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.destinations_list,string_output);
      )
    }
};

/* OBSOLETE
void generate_destinations_logic_lists(struct Volume_struct **Volumes,int number_of_volumes,int verbal) {
  // The destinations logic list is another way to store the destinations list that makes some tasks quicker
  
  MPI_MASTER(
  if (verbal) printf("\nGenerating destinations logic lists ----------------------- \n");
  )

  int volume_index;
  for (volume_index = 0;volume_index < number_of_volumes;volume_index++) {
      allocate_logic_list_from_temp(number_of_volumes,Volumes[volume_index]->geometry.destinations_list,&Volumes[volume_index]->geometry.destinations_logic_list);
      
      char string_output[128];
      MPI_MASTER(
      if (verbal) sprintf(string_output,"Destinations logic list for Volume %d",volume_index);
      if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.destinations_logic_list,string_output);
      )
  }

};
*/

void generate_reduced_destinations_lists(struct pointer_to_1d_int_list **parents_lists, struct Volume_struct **Volumes,int number_of_volumes,int verbal) {
    // The reduced destination list is the destination list of a volume, where each element that has a parent on the same destination list is removed
    // This list is to be fed to the which_volume function, as this funtion will automatically search through the direct children in a tree like manner
    // The optimization reduces the number of calculations of within functions in nested geometries.
    
    MPI_MASTER(
    if (verbal) printf("\nGenerating reduced destination lists ----------------------- \n");
    )
    
    struct pointer_to_1d_int_list logic_list;
  
    int volume_index,checked_dest_index,checked_dest_volume,rest_dest_index,rest_dest_volume,dest_index,iterate;
    for (volume_index = 0;volume_index < number_of_volumes;volume_index++) {
      
      //printf("Generating reduced destinations lists for volume %d\n",volume_index);
      logic_list.num_elements = Volumes[volume_index]->geometry.destinations_list.num_elements;
      logic_list.elements = malloc( (int) logic_list.num_elements * sizeof(int));
      //printf("Sucsessfully allocated space for %d integers in logic list %d\n",logic_list.num_elements,volume_index);
      for (iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 1;
      
      for (checked_dest_index=0;checked_dest_index<Volumes[volume_index]->geometry.destinations_list.num_elements;checked_dest_index++) {
            checked_dest_volume = Volumes[volume_index]->geometry.destinations_list.elements[checked_dest_index];
            for (rest_dest_index=0;rest_dest_index<Volumes[volume_index]->geometry.destinations_list.num_elements;rest_dest_index++) {
                rest_dest_volume = Volumes[volume_index]->geometry.destinations_list.elements[rest_dest_index];
                // As every volume has 0 as a parent, these are ignored. It would work to include this, but would add an extra trivial step to within_which_volume
                if (rest_dest_volume != 0) {
                    if (on_int_list(*parents_lists[checked_dest_volume],rest_dest_volume) == 1) {
                        // In this case, do not include element checked_dest_index on the reduced destinations list
                        // ADD mask check
                        if (Volumes[rest_dest_volume]->geometry.is_masked_volume == 0) {
                          logic_list.elements[checked_dest_index] = 0;
                        }
                    }
                }
            }
      }
      
      Volumes[volume_index]->geometry.reduced_destinations_list.num_elements = sum_int_list(logic_list);
      Volumes[volume_index]->geometry.reduced_destinations_list.elements = malloc((int)Volumes[volume_index]->geometry.reduced_destinations_list.num_elements * sizeof(int));
      
      iterate = 0;
      for (dest_index = 0;dest_index < Volumes[volume_index]->geometry.destinations_list.num_elements;dest_index++) {
            if (logic_list.elements[dest_index] == 1)   Volumes[volume_index]->geometry.reduced_destinations_list.elements[iterate++] = Volumes[volume_index]->geometry.destinations_list.elements[dest_index];
      }
      
      free(logic_list.elements);
      
      // Testing an optimization
      remove_element_in_list_by_value(&Volumes[volume_index]->geometry.reduced_destinations_list,0);
      
      char string_output[128];
      MPI_MASTER(
      if (verbal) sprintf(string_output,"Reduced destinations list for Volume %d",volume_index);
      if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.reduced_destinations_list,string_output);
      )
    }
};

void generate_direct_children_lists(struct pointer_to_1d_int_list **parents_lists, struct Volume_struct **Volumes,int number_of_volumes,int verbal) {

  MPI_MASTER(
  if (verbal) printf("\nGenerating direct children lists ----------------------- \n");
  )
  // A direct children of volume n is a volume that is a child of n, but no other child of n is its parent
  
  // Mask update: Need to check that this step does not bug out when the mask system interferes with child/parent systems
  
  struct pointer_to_1d_int_list logic_list;
  int volume_index,child,parent,iterate;
  for (volume_index = 0;volume_index < number_of_volumes;volume_index++) {
        // Temp elements is used, and its actual number of elements is edited even though the memory allocated is not changed. This is so that the list functions handles it correctly.
        // The free function will free all the allocated memory regardless of the value of the .num_elements structure field, it is just there for convinience.
      
        logic_list.num_elements = Volumes[volume_index]->geometry.children.num_elements;
        logic_list.elements = malloc(logic_list.num_elements * sizeof(int));
        for (iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 1;
      
        // Look through each child of volume n, and for each of those look through all the other children of n and search for one of them being a parent to the child
        for (child=0;child<Volumes[volume_index]->geometry.children.num_elements;child++) {
            for (parent=0;parent<parents_lists[Volumes[volume_index]->geometry.children.elements[child]]->num_elements;parent++) {
                if (on_int_list(Volumes[volume_index]->geometry.children,parents_lists[Volumes[volume_index]->geometry.children.elements[child]]->elements[parent]))
                    // If such a parent is found, remove that child from the list
                    logic_list.elements[child] = 0;
            }
        }
      
      Volumes[volume_index]->geometry.direct_children.num_elements = sum_int_list(logic_list);
      Volumes[volume_index]->geometry.direct_children.elements = malloc(Volumes[volume_index]->geometry.direct_children.num_elements*sizeof(int));
      
      iterate = 0;
      for (child = 0;child < Volumes[volume_index]->geometry.children.num_elements;child++) {
            if (logic_list.elements[child])   Volumes[volume_index]->geometry.direct_children.elements[iterate++] = Volumes[volume_index]->geometry.children.elements[child];
      }
      // Be careful with names in both main and a function, as they are automatically declared as external variables, which would then also free the main.
      free(logic_list.elements);
      
      char string_output[128];
      MPI_MASTER(
      if (verbal) sprintf(string_output,"Children list for Volume        %d",volume_index);
      if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.children,string_output);
      if (verbal) sprintf(string_output,"Direct_children list for Volume %d",volume_index);
      if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.direct_children,string_output);
      )
      
  }

};

void generate_starting_logic_list(struct starting_lists_struct *starting_lists, struct Volume_struct **Volumes, int number_of_volumes, int verbal) {
    // Function for generating logic list of volumes the ray can start in without an error.
    // Start with a list of all vacuum volumes
    // Remove all volumes that are children of non-vacuum volumes
    // It is still possible to have a volume on this list that is surrounded by non-vacuum volumes, but it is hard to detect these situations,
    //  meaning that it is ultimately partly the users responsibility to not send neutrons directly into materials.
    
    int volume_index,*start,*check;
    
    struct pointer_to_1d_int_list temp_list_local;
    temp_list_local.num_elements = number_of_volumes;
    temp_list_local.elements = malloc(number_of_volumes*sizeof(int));
    
    //if (verbal==1) printf("sucessfully allocated temp_list_local.elements, now to enter which volumes are vacuums as a logic list\n");
    temp_list_local.elements[0] = 1; // Volume 0 is a vacuum volume.
    for (volume_index = 1;volume_index < number_of_volumes;volume_index++) {
        if (Volumes[volume_index]->p_physics->is_vacuum == 1) temp_list_local.elements[volume_index] = 1;
        else temp_list_local.elements[volume_index] = 0;
    }
    // temp_list_local is now a logic list of all vacuum volumes
    //if (verbal==1) printf("list of vacuum volumes done, now to remove the children of non-vcauum volumes\n");
    for (volume_index = 1;volume_index < number_of_volumes;volume_index++) { // All volumes ...
        if (temp_list_local.elements[volume_index] == 0) { // ... that are not vacuum ...
            for (start = check = Volumes[volume_index]->geometry.children.elements;check - start < Volumes[volume_index]->geometry.children.num_elements;check++) { // ... have all their children ...
                temp_list_local.elements[*check] = 0; // .. removed from the allowed_start_logic_list
            }
        }
    }
    //if (verbal==1) printf("sucessfully removed children of non-vacuum volumes, now allocate allowed_start_logic_list\n");
    allocate_list_from_temp(number_of_volumes,temp_list_local,&starting_lists->allowed_starting_volume_logic_list);
    //if (verbal==1) printf("sucsessfully allocated allowed_start_logic_list, now freeing temp_list_local.elements. \n");
    
    free(temp_list_local.elements);
    //if (verbal==1) printf("sucessfully freed temp_list_local.elements, generate starting lists done\n");
    
    char string_output[128];
    MPI_MASTER(
    if (verbal) sprintf(string_output,"Allowed starting volume logic list");
    if (verbal) print_1d_int_list(starting_lists->allowed_starting_volume_logic_list,string_output);
    )
    
    
};

void generate_reduced_starting_destinations_list(struct starting_lists_struct *starting_lists, struct pointer_to_1d_int_list **parents_lists, struct Volume_struct **Volumes,int number_of_volumes,int verbal) {
    // The starting_destinations_list is trivial, as it contains all volumes that are not masks.


    struct pointer_to_1d_int_list logic_list;
    //printf("Generating reduced destinations lists for volume %d\n",volume_index);
    logic_list.num_elements = number_of_volumes;
    logic_list.elements = malloc( (int) logic_list.num_elements * sizeof(int));
    int iterate;
    for (iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 0;
    
    
    for (iterate=1;iterate<number_of_volumes;iterate++)
      if (Volumes[iterate]->geometry.is_mask_volume == 0) logic_list.elements[iterate] = 1;
      //else logic_list.elements[iterate] = 0;
    
    starting_lists->starting_destinations_list.num_elements = sum_int_list(logic_list);
    starting_lists->starting_destinations_list.elements = malloc(starting_lists->starting_destinations_list.num_elements*sizeof(int));
    
    int used_elements=0;
    for (iterate=1;iterate<number_of_volumes;iterate++)
      if (logic_list.elements[iterate] == 1) starting_lists->starting_destinations_list.elements[used_elements++] = iterate;
    
    MPI_MASTER(
    if (verbal) printf("\nGenerating start destinations list ------------------------------ \n");
    if (verbal) print_1d_int_list(starting_lists->starting_destinations_list,"Starting destinations list");
    )

    // The reduced starting destination list is used when a ray enters the component in the search for which volume it starts in.
    // It facilitates the same optimization as the regular destination list.
    // The start logic list is also generated, as it is very simple and does not need a seperate function
    
    // Mask update: Need to remove mask volumes from the reduced starting destination list
    
    MPI_MASTER(
    if (verbal) printf("\nGenerating reduced start destination list ----------------------- \n");
    )
    
    int checked_dest_index,checked_dest_volume,rest_dest_index,rest_dest_volume,dest_index;
    
    /* Old version before masks were introduced
    struct pointer_to_1d_int_list starting_dest_list;
    
    starting_dest_list.num_elements = number_of_volumes - 1;
    starting_dest_list.elements = malloc ( starting_dest_list.num_elements * sizeof(int));
    for (iterate=0;iterate<number_of_volumes-1;iterate++) starting_dest_list.elements[iterate] = iterate + 1; // All volumes to be checked for starting
    */
    
    logic_list.num_elements = starting_lists->starting_destinations_list.num_elements;
    free(logic_list.elements);
    logic_list.elements = malloc( (int) logic_list.num_elements * sizeof(int));
    
    //printf("Sucsessfully allocated space for %d integers in logic list %d\n",logic_list.num_elements,volume_index);
    for (iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 1;
  
    for (checked_dest_index=0;checked_dest_index<starting_lists->starting_destinations_list.num_elements;checked_dest_index++) {
        checked_dest_volume = starting_lists->starting_destinations_list.elements[checked_dest_index];
        for (rest_dest_index=0;rest_dest_index<starting_lists->starting_destinations_list.num_elements;rest_dest_index++) {
            rest_dest_volume = starting_lists->starting_destinations_list.elements[rest_dest_index];
            // As every volume has 0 as a parent, these are ignored. It would work to include this, but would add an extra trivial step to within_which_volume
            if (rest_dest_volume != 0) {
                if (on_int_list(*parents_lists[checked_dest_volume],rest_dest_volume) == 1) {
                    // In this case, do not include element checked_dest_index on the reduced destinations list
                    logic_list.elements[checked_dest_index] = 0;
                }
            }
        }
  }
  
  starting_lists->reduced_start_list.num_elements = sum_int_list(logic_list);
  starting_lists->reduced_start_list.elements = malloc((int)starting_lists->reduced_start_list.num_elements * sizeof(int));
  
  iterate = 0;
  for (dest_index = 0;dest_index < starting_lists->starting_destinations_list.num_elements;dest_index++) {
        if (logic_list.elements[dest_index] == 1) starting_lists->reduced_start_list.elements[iterate++] = starting_lists->starting_destinations_list.elements[dest_index];
  }
  
  free(logic_list.elements);
  //free(starting_dest_list.elements);
  
  MPI_MASTER(
  if (verbal) print_1d_int_list(starting_lists->reduced_start_list,"Reduced start destinations list");
  )
    
  // Making the start_logic_list.
  starting_lists->start_logic_list.num_elements = number_of_volumes;
  starting_lists->start_logic_list.elements = malloc ( starting_lists->start_logic_list.num_elements * sizeof(int));
  starting_lists->start_logic_list.elements[0] = 0;
  for (iterate=1;iterate<number_of_volumes;iterate++) starting_lists->start_logic_list.elements[iterate] = 1; // All volumes to be checked for starting volume
  MPI_MASTER(
  if (verbal) print_1d_int_list(starting_lists->start_logic_list,"Start logic list");
  )
};


void generate_next_volume_list(struct Volume_struct **Volumes, int number_of_volumes, int verbal) {
    // Generate list of volumes that can be the next volume which the ray enters. It is used for tagging, not the simulation / propagation

    // Mask update: These lists should be done as if all mask statuses are on, meaning they include all possible next volumes (will include more input to this function)
    
    // bug: next volume list is not complete and contains duplicates

    MPI_MASTER(
    if (verbal) printf("\nGenerating next volume list ------------------------------------- \n");
    )
    // Merge destinations list, intersection list and mask_intersection_list
    int volume_index,iterate,mask_index;
    struct pointer_to_1d_int_list full_intersection_list;
    full_intersection_list.num_elements=0;
    
    for (volume_index=0;volume_index<number_of_volumes;volume_index++) {
        Volumes[volume_index]->geometry.next_volume_list.num_elements = 0;
        // Before mask update
        //merge_lists(&Volumes[volume_index]->geometry.next_volume_list, &Volumes[volume_index]->geometry.destinations_list, &Volumes[volume_index]->geometry.intersect_check_list);
        
        merge_lists(&full_intersection_list, &Volumes[volume_index]->geometry.mask_intersect_list, &Volumes[volume_index]->geometry.intersect_check_list);
        merge_lists(&Volumes[volume_index]->geometry.next_volume_list,&Volumes[volume_index]->geometry.destinations_list,&full_intersection_list);
        
        /* // This complication is taken into account by adding the mask_intersect list instead
        // It is possible that the next volume is still not on this list, as when masks are encountered the next volume can be a volume they mask.
        // For each on the list, add the volumes masked by that mask (do not iterate over this, as masks can not be masked regardless)
        for (iterate=Volumes[volume_index]->geometry.next_volume_list.num_elements-1;iterate>-1;iterate--) {
          if (Volumes[Volumes[volume_index]->geometry.next_volume_list.elements[iterate]]->geometry.is_mask_volume == 1) {
            for (mask_index=0;mask_index<Volumes[Volumes[volume_index]->geometry.next_volume_list.elements[iterate]]->geometry.mask_list.num_elements;mask_index++) {
              add_element_to_int_list(&Volumes[volume_index]->geometry.next_volume_list,Volumes[Volumes[volume_index]->geometry.next_volume_list.elements[iterate]]->geometry.mask_list.elements[mask_index]);
            }
          }
        }
        */
        
        // Remove mask volumes from the next volume list, as they never occur as current_volume, and thus just create dead branches that takes up memory
        for (iterate=Volumes[volume_index]->geometry.next_volume_list.num_elements-1;iterate>-1;iterate--)
          if (Volumes[Volumes[volume_index]->geometry.next_volume_list.elements[iterate]]->geometry.is_mask_volume == 1)
            remove_element_in_list_by_index(&Volumes[volume_index]->geometry.next_volume_list,iterate);
        
        
        if (full_intersection_list.num_elements>0) free(full_intersection_list.elements);
        full_intersection_list.num_elements=0;
        
        char string_output[128];
        MPI_MASTER(
        if (verbal) sprintf(string_output,"Next volume list                %d",volume_index);
        if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.next_volume_list,string_output);
        )
    }
};

void generate_lists(struct Volume_struct **Volumes, struct starting_lists_struct *starting_lists, int number_of_volumes, int verbal) {
    // Function to control the generation of lists
    // Some lists are only needed temporary, and are thus declared here to keep them out of the main scope
    // Others are stored in the volume structs as they are needed in the trace algorithm (or tagging)
    
    
    struct pointer_to_1d_int_list **true_children_lists;
    true_children_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    // generate_children_lists both generate the normal children list for each volume, but also the true children list needed locally.
    generate_children_lists(Volumes, true_children_lists, number_of_volumes,verbal);
    
    
    struct pointer_to_1d_int_list **true_overlap_lists;
    true_overlap_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    struct pointer_to_1d_int_list **raw_overlap_lists;
    raw_overlap_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    
    generate_overlap_lists(true_overlap_lists, raw_overlap_lists, Volumes,number_of_volumes,verbal);
    
    
    //generate_intersect_check_lists(true_overlap_lists, Volumes, number_of_volumes, verbal);
    
    
    struct pointer_to_1d_int_list **parents_lists;
    parents_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    generate_parents_lists(parents_lists,Volumes,number_of_volumes,verbal,1); // The last 1 means masks are taken into account
    
    // Generate version of parent list as it would be without masks
    struct pointer_to_1d_int_list **parents_lists_no_masks;
    parents_lists_no_masks = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    generate_parents_lists(parents_lists_no_masks,Volumes,number_of_volumes,verbal,0); // The last 0 means masks are NOT taken into account
    
    // Generate version of parent list using true_children instead
    struct pointer_to_1d_int_list **true_parents_lists;
    true_parents_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    generate_true_parents_lists(true_parents_lists, true_children_lists, Volumes, number_of_volumes, verbal, 1);
    
    // Generate version of parent list no masks using true_children instead
    struct pointer_to_1d_int_list **true_parents_lists_no_masks;
    true_parents_lists_no_masks = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    generate_true_parents_lists(true_parents_lists_no_masks, true_children_lists, Volumes, number_of_volumes, verbal, 0);
    
    // New version of generate intersect lists
    generate_intersect_check_lists_experimental(true_overlap_lists, raw_overlap_lists, parents_lists, true_parents_lists, Volumes, number_of_volumes, verbal);
    
    struct pointer_to_1d_int_list **grandparents_lists;
    grandparents_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    generate_grandparents_lists(grandparents_lists,parents_lists,number_of_volumes,verbal);
    
    // Generate version of grandparents list as it would have been if no masks were defined
    struct pointer_to_1d_int_list **grandparents_lists_no_masks;
    grandparents_lists_no_masks = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    generate_grandparents_lists(grandparents_lists_no_masks,parents_lists_no_masks,number_of_volumes,verbal);
    
    // Generate true_grandparents_lists
    struct pointer_to_1d_int_list **true_grandparents_lists;
    true_grandparents_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    generate_grandparents_lists(true_grandparents_lists,true_parents_lists,number_of_volumes,verbal);
    
    struct pointer_to_1d_int_list **true_grandparents_lists_no_masks;
    true_grandparents_lists_no_masks = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    generate_grandparents_lists(true_grandparents_lists_no_masks,true_parents_lists_no_masks,number_of_volumes,verbal);
    
    // The destinations lists are generated without taking masks into account (they are removed from the overlap list in an early step)
    //generate_destinations_lists(grandparents_lists_no_masks,parents_lists_no_masks,true_overlap_lists,Volumes,number_of_volumes,verbal);
    generate_destinations_lists_experimental(true_overlap_lists, true_children_lists, true_parents_lists_no_masks, true_grandparents_lists_no_masks, Volumes, number_of_volumes, verbal);
    
    // Obsolete, found a way around them in within_which_volume, but need to test the performance difference
    // generate_destinations_logic_lists(Volumes,number_of_volumes,verbal);
    
    generate_reduced_destinations_lists(parents_lists,Volumes,number_of_volumes,verbal);
    
    generate_direct_children_lists(parents_lists,Volumes,number_of_volumes,verbal);
    
    //generate_starting_list(starting_lists,Volumes,number_of_volumes,verbal);
    generate_starting_logic_list(starting_lists,Volumes,number_of_volumes,verbal);
    
    generate_reduced_starting_destinations_list(starting_lists,parents_lists,Volumes,number_of_volumes,verbal);
    
    // This list is stored with the volumes for convinience, but is only used for tagging
    generate_next_volume_list(Volumes,number_of_volumes,verbal);
    
    // Garbage collection for temporary dynamically allocated lists. (Permanent lists freed from FINALLY)
    int iterate;
    for (iterate=0;iterate<number_of_volumes;iterate++) {
        //printf("freeing for volume nr %d\n",iterate);
        //printf("true_overlap_lists[iterate]->num_elements = %d \n",true_overlap_lists[iterate]->num_elements);
        if (true_overlap_lists[iterate]->num_elements > 0) free(true_overlap_lists[iterate]->elements);
        free(true_overlap_lists[iterate]);
        
        //printf("raw_overlap_lists[iterate]->num_elements = %d \n",raw_overlap_lists[iterate]->num_elements);
        if (raw_overlap_lists[iterate]->num_elements > 0) free(raw_overlap_lists[iterate]->elements);
        free(raw_overlap_lists[iterate]);
        
        //printf("parents_lists[iterate]->num_elements = %d \n",parents_lists[iterate]->num_elements);
        if (parents_lists[iterate]->num_elements > 0) free(parents_lists[iterate]->elements);
        free(parents_lists[iterate]);
        
        //printf("parents_lists_no_masks[iterate]->num_elements = %d \n",parents_lists_no_masks[iterate]->num_elements);
        if (parents_lists_no_masks[iterate]->num_elements > 0) free(parents_lists_no_masks[iterate]->elements);
        free(parents_lists_no_masks[iterate]);
        
        //printf("true_parents_lists[iterate]->num_elements = %d \n",true_parents_lists[iterate]->num_elements);
        if (true_parents_lists[iterate]->num_elements > 0) free(true_parents_lists[iterate]->elements);
        free(true_parents_lists[iterate]);
        
        //printf("true_parents_lists_no_masks[iterate]->num_elements = %d \n",true_parents_lists_no_masks[iterate]->num_elements);
        if (true_parents_lists_no_masks[iterate]->num_elements > 0) free(true_parents_lists_no_masks[iterate]->elements);
        free(true_parents_lists_no_masks[iterate]);
        
        //printf("grandparents_lists[iterate]->num_elements = %d \n",grandparents_lists[iterate]->num_elements);
        if (grandparents_lists[iterate]->num_elements > 0) free(grandparents_lists[iterate]->elements);
        free(grandparents_lists[iterate]);
        
        //printf("true_grandparents_lists[iterate]->num_elements = %d \n",true_grandparents_lists[iterate]->num_elements);
        if (true_grandparents_lists[iterate]->num_elements > 0) free(true_grandparents_lists[iterate]->elements);
        free(true_grandparents_lists[iterate]);
        
        //printf("grandparents_lists_no_masks[iterate]->num_elements = %d \n",grandparents_lists_no_masks[iterate]->num_elements);
        if (grandparents_lists_no_masks[iterate]->num_elements > 0) free(grandparents_lists_no_masks[iterate]->elements);
        free(grandparents_lists_no_masks[iterate]);
        
        //printf("true_grandparents_lists_no_masks[iterate]->num_elements = %d \n",true_grandparents_lists_no_masks[iterate]->num_elements);
        if (true_grandparents_lists_no_masks[iterate]->num_elements > 0) free(true_grandparents_lists_no_masks[iterate]->elements);
        free(true_grandparents_lists_no_masks[iterate]);
        
        //printf("true_children_lists[iterate]->num_elements = %d \n",true_children_lists[iterate]->num_elements);
        if (true_children_lists[iterate]->num_elements > 0) free(true_children_lists[iterate]->elements);
        free(true_children_lists[iterate]);
    }
    //printf("generate lists volume specific free completed\n");
    free(true_overlap_lists);free(raw_overlap_lists);free(parents_lists);free(true_parents_lists);free(true_parents_lists_no_masks);
    free(parents_lists_no_masks);free(true_grandparents_lists);free(grandparents_lists);free(grandparents_lists_no_masks);free(true_grandparents_lists_no_masks);
    free(true_children_lists);
    //printf("generate lists free completed\n");
};

// -------------    Focusing functions   --------------------------------------------------------

// The focusing_data structure is set up by the geometry component, and a pointer to the appropriate
//  focusing function is added to the Volume structure (for this reason the input of all the functions
//  need to be identical, at least in terms of types). In this way there are no if statements to check
//  which of these to be used in the trace, but the focus_data_struct will carry some redundant
//  information, as only the appropriate parameters are set:
// Angular focus on a rectangle (angular_focus_height / angular_focus_width)
// Spatial focus on a rectangle (spatial_focus_height / spatial_focus_width)
// Spatial focus on a disk (sptial_focus_radius)
// No focus (randvec in 4pi) (all set to zero, will select randvec circle as it is slightly faster
//
// When adding a new physical process focusing becomes very easy, as one just calls the master focusing
//  function assosiated with the volume (placed in the geometry struct), using the focus_data_struct
//  also found in the geometry struct, and the process then supports all the focusing modes. It is even
//  possible to add new focusing modes in the future by updating just the geometry components, and this
//  section.

// focus_data_struct definitioon shown here, defined at the start of this file
//struct focus_data_struct {
//Coords Aim;
//double angular_focus_width;
//double angular_focus_height;
//double spatial_focus_width;
//double spatial_focus_height;
//double spatial_focus_radius;
//Rotation absolute_rotation;
//// focusing_function creates a vector per selected criteria of focus_data_struct / selected focus function and returns solid angle
//void (*focusing_function)(Coords*, double*, struct focus_data_struct*);
////                        v_out  , solid_a,
//};

void randvec_target_rect_angular_union(Coords *v_out,double *solid_angle_out, struct focus_data_struct *focus_data) {
    // Calls the standard McStas randvec_target_rect_angular focusing function, but is with the new data input format.
    randvec_target_rect_angular(&v_out->x, &v_out->y, &v_out->z, solid_angle_out, focus_data->Aim.x,focus_data->Aim.y, focus_data->Aim.z, focus_data->angular_focus_width, focus_data->angular_focus_height,focus_data->absolute_rotation);
    //randvec_target_rect_angular(&vx, &vy, &vz, &solid_angle,aim_x, aim_y, aim_z, VarsInc.aw, VarsInc.ah, ROT_A_CURRENT_COMP);
};

void randvec_target_rect_union(Coords *v_out,double *solid_angle_out, struct focus_data_struct *focus_data) {
// Calls the standard McStas randvec_target_rect focusing function, but is with the new data input format.
    randvec_target_rect(&v_out->x, &v_out->y, &v_out->z, solid_angle_out, focus_data->Aim.x,focus_data->Aim.y, focus_data->Aim.z, focus_data->spatial_focus_width, focus_data->spatial_focus_height,focus_data->absolute_rotation);
    // randvec_target_rect(&vx, &vy, &vz, &solid_angle,aim_x, aim_y, aim_z, VarsInc.xw, VarsInc.yh, ROT_A_CURRENT_COMP);
};

void randvec_target_circle_union(Coords *v_out,double *solid_angle_out, struct focus_data_struct *focus_data) {
// Calls the standard McStas randvec_target_circle focusing function, but is with the new data input format.

    // debug input into randvec_target_circle
    //print_position(focus_data->Aim,"Aim vector input for randvec_target_circle");
    //printf("Radius input %f\n",focus_data->spatial_focus_radius);

    randvec_target_circle(&v_out->x, &v_out->y, &v_out->z, solid_angle_out, focus_data->Aim.x,focus_data->Aim.y, focus_data->Aim.z, focus_data->spatial_focus_radius);
    //randvec_target_circle(&vx, &vy, &vz, &solid_angle, aim_x, aim_y, aim_z, focus_r);
};



void focus_initialize(struct geometry_struct *geometry, Coords POS_A_TARGET, Coords POS_A_CURRENT, Rotation ROT_A_CURRENT, int target_index, double target_x, double target_y, double target_z, double angular_focus_width, double angular_focus_height, double spatial_focus_width, double spatial_focus_height, double spatial_focus_radius, char *component_name) {
    // Initialize focusing system
    // target_x/y/z needs to be double setting parameters with default value 0
    // target_index needs to be int setting parameter with default value 0
    // angular_focus_width, angular_focus_height, spatial_focus_width, spatial_focus_height, spatial_focus_radius nneds to be double setting parameters with default value 0
    // When those conditions are met, this code will identify which settings have been entered by the user and select the appropriate focusing parameters, which are loaded into the focus_data struct and the geometry struct.
    // The aim vector in the focus_data struct will be transformed from the local coordinate system of the geometry component to the coordinate system of the master component during the master component initialize

  // Input sanitation
  if (angular_focus_width < 0) {
    printf("\nERROR in Union geometry component named \"%s\", angular focus width focus_aw < 0! \n",component_name);
    exit(1);
  }
  if (angular_focus_height < 0) {
    printf("\nERROR in Union geometry component named \"%s\", angular focus width focus_ah < 0! \n",component_name);
    exit(1);
  }
  if (spatial_focus_width < 0) {
    printf("\nERROR in Union geometry component named \"%s\", spatial focus width focus_xw < 0! \n",component_name);
    exit(1);
  }
  if (spatial_focus_height < 0) {
    printf("\nERROR in Union geometry component named \"%s\", spatial focus height focus_xh < 0! \n",component_name);
    exit(1);
  }
  if (spatial_focus_radius < 0) {
    printf("\nERROR in Union geometry component named \"%s\", spatial focus radius focus_r < 0! \n",component_name);
    exit(1);
  }
  
  struct focus_data_struct focus_data;

  // Initialize focus_data_struct
  /*
  geometry->focus_data.Aim = coords_set(0,0,0);
  geometry->focus_data.angular_focus_width = 0;
  geometry->focus_data.angular_focus_height = 0;
  geometry->focus_data.spatial_focus_width = 0;
  geometry->focus_data.spatial_focus_height = 0;
  geometry->focus_data.spatial_focus_radius = 0;
  rot_copy(geometry->focus_data.absolute_rotation,ROT_A_CURRENT);
  */
  focus_data.Aim = coords_set(0,0,0);
  focus_data.angular_focus_width = 0;
  focus_data.angular_focus_height = 0;
  focus_data.spatial_focus_width = 0;
  focus_data.spatial_focus_height = 0;
  focus_data.spatial_focus_radius = 0;
  rot_copy(focus_data.absolute_rotation,ROT_A_CURRENT);

  // Built on code from Incoherent.comp by Kim Lefmann and Kristian Nielsen
  if (target_index != 0 && !target_x && !target_y && !target_z)
  {
    Coords ToTarget;
    //ToTarget = coords_sub(POS_A_COMP_INDEX(INDEX_CURRENT_COMP+target_index),POS_A_CURRENT_COMP);
    ToTarget = coords_sub(POS_A_TARGET,POS_A_CURRENT);
    //ToTarget = rot_apply(ROT_A_CURRENT_COMP, ToTarget);
    ToTarget = rot_apply(ROT_A_CURRENT, ToTarget);
    coords_get(ToTarget, &focus_data.Aim.x, &focus_data.Aim.y, &focus_data.Aim.z);
  }
  else
  { focus_data.Aim.x = target_x; focus_data.Aim.y = target_y; focus_data.Aim.z = target_z; }
  
  if (!(focus_data.Aim.x || focus_data.Aim.y || focus_data.Aim.z)) {
    // Somehow set a variable to signify scattering into 4pi
    // printf("Union %s: The target is not defined. Using scattering into 4pi.\n",NAME_CURRENT_COMP);
    focus_data.Aim.z=1; // set aim to one so that the randvec output vector has length 1 instead of 0
  }

  int focusing_model_selected = 0;
  if (angular_focus_width != 0 && angular_focus_height != 0) {
    focus_data.focusing_function = &randvec_target_rect_angular_union;
    focus_data.angular_focus_width = DEG2RAD*angular_focus_width; // Convert to radians here
    focus_data.angular_focus_height = DEG2RAD*angular_focus_height;
    focusing_model_selected = 1;
  }
  if (spatial_focus_width != 0 && spatial_focus_height != 0) {
    focus_data.focusing_function = &randvec_target_rect_union;
    focus_data.spatial_focus_width = spatial_focus_width;
    focus_data.spatial_focus_height = spatial_focus_height;
    if (focusing_model_selected) {
        printf("ERROR %s: Select either angular or spatial focusing, not both! Exiting \n",component_name);
        exit(1);
    }
    focusing_model_selected = 1;
  }
  if (spatial_focus_radius != 0) {
    focus_data.focusing_function = &randvec_target_circle_union;
    focus_data.spatial_focus_radius = spatial_focus_radius;
    if (focusing_model_selected) {
        printf("ERROR %s: Select a maximum of one focusing method (spatial rectangle or cicle, or angular rectangle! Exiting \n",component_name);
        exit(1);
    }
    focusing_model_selected = 1;
  }
  if (focusing_model_selected == 0) {
    // Select 4pi focusing
    focus_data.spatial_focus_radius = 0;
    focus_data.focusing_function = &randvec_target_circle_union;
  }
  
  // Allocate the isotropic focus_data struct
  geometry->focus_data_array.num_elements = 0;
  //geometry->focus_data_array.elements = malloc(sizeof(focus_data_struct));
  add_element_to_focus_data_array(&geometry->focus_data_array,focus_data);
};


struct abs_event{
    double time1;
    double position1[3];
    double time2;
    double position2[3];
    double weight_change;
    int volume_index;
    int neutron_id;
};

// Functions for recording absorption
void initialize_absorption_file() {
  FILE *fp;
  fp = fopen("Union_absorption.dat","w");
  fprintf(fp,"r_old x, r_old y, r_old z, old t, r x, r y, r z, new t, weight change, volume index, neutron id \n");
  
  fclose(fp);
}

void write_events_to_file(int last_index, struct abs_event *events) {

  FILE *fp;
  fp = fopen("Union_absorption.dat","a");
  
  struct abs_event *this_event;
  int iterate;
  for (iterate=0; iterate<last_index; iterate++) {
  
    this_event = &events[iterate];
    fprintf(fp,"%g, %g, %g, %g, %g, %g, %g, %g, %e, %i, %i \n",
           this_event->position1[0], this_event->position1[1], this_event->position1[2], this_event->time1,
           this_event->position2[0], this_event->position2[1], this_event->position2[2], this_event->time2,
           this_event->weight_change,
           this_event->volume_index,
           this_event->neutron_id);
  }

  fclose(fp);
}

void record_abs_to_file(double *r, double t1, double *r_old, double t2, double weight_change, int volume, int neutron_id, int *data_index, struct abs_event *events) {
  
  
  
  struct abs_event *this_event;
  
  this_event = &events[(*data_index)++];
  
  //printf("Recording something! %i\n", *data_index);
  
  this_event->position1[0] = r[0];
  this_event->position1[1] = r[1];
  this_event->position1[2] = r[2];
  this_event->time1 = t1;
  this_event->position2[0] = r_old[0];
  this_event->position2[1] = r_old[1];
  this_event->position2[2] = r_old[2];
  this_event->time2 = t2;
  this_event->weight_change = weight_change;
  this_event->volume_index = volume;
  this_event->neutron_id = neutron_id;
  
  
  if (*data_index == 999) {
    
      write_events_to_file(*data_index, events);
      *data_index = 0;

  }

};




/*******************************************************************************
*
*  McStas, neutron ray-tracing package
*  Copyright(C) 2007 Risoe National Laboratory.
*
* %I
* Written by: Mads Bertelsen
* Date: 20.08.15
* Version: $Revision: 0.1 $
* Origin: University of Copenhagen
*
* A sample component to separate geometry and phsysics
*
* %D
* Alpha version, no input system yet
* Hardcode input to geometry engine
* Allows complicated geometry by combination of simple shapes
*
* Algorithm:
* Described elsewhere
*
* %P
* INPUT PARAMETERS:
* radius:  [m] Outer radius of sample in (x,z) plane
*
* OUTPUT PARAMETERS:
* V_rho:  [AA^-3] Atomic density
*
* %L
* The test/example instrument <a href="../examples/Test_Phonon.instr">Test_Phonon.instr</a>.
*
* %E
******************************************************************************/


// This file sets up global lists needed for Union components to communicate with each other
// These all have dynamically allocated memory somewhere in the structure, which is deallocated
//  by the last Union_master.



// Initialize global positions / rotations to transform lists
  // These are lists of pointers to positons / rotations, that will be updated from global frame
  //  to the frame of the master component that uses them in that masters initialize section.
  struct global_positions_to_transform_list_struct global_positions_to_transform_list = {0,NULL};
  struct global_rotations_to_transform_list_struct global_rotations_to_transform_list = {0,NULL};

// Initialize global_process_list
  // Used to facilitate communication between processes and the other types of Union components
  struct pointer_to_global_process_list global_process_list = {0,NULL};

// Initialize global_material_list
  // Used to facilitate communication between materials and the other types of Union components
  struct pointer_to_global_material_list global_material_list = {0,NULL};

// Initialize global_geometry_list
  // Used to facilitate communication between geometries and the other types of Union components
  struct pointer_to_global_geometry_list global_geometry_list = {0,NULL};

// Initialize global_logger_lists
  // Used to facilitate communication between loggers and the other types of Union components
  struct pointer_to_global_logger_list global_all_volume_logger_list = {0,NULL};
  struct pointer_to_global_logger_list global_specific_volumes_logger_list = {0,NULL};

// Initialize global_abs_logger_lists
  struct pointer_to_global_abs_logger_list global_all_volume_abs_logger_list = {0,NULL};
  struct pointer_to_global_abs_logger_list global_specific_volumes_abs_logger_list = {0,NULL};

// Initialize global_tagging_conditional_list
  // Used to facilitate communication between conditionals and the other types of Union components
  struct global_tagging_conditional_list_struct global_tagging_conditional_list = {0,0,NULL};

// Initialize global_master_list
  // Used to facilitate communication between Master components (mainly for deallocation)
  struct pointer_to_global_master_list global_master_list = {0,NULL};

// Initialize global_mantid_min_pixel_id
  // Used for ensuring pixel id's on Mantid monitors do not overlap
  int global_mantid_min_pixel_id = 0;


#endif



// Share section of PowderN 8/3 2016 from McStas.org

  /* used for reading data table from file */

/*******************************************************************************
*
* McStas, neutron ray-tracing package
*         Copyright (C) 1997-2008, All rights reserved
*         Risoe National Laboratory, Roskilde, Denmark
*         Institut Laue Langevin, Grenoble, France
*
* Runtime: share/interoff.h
*
* %Identification
* Written by: Reynald Arnerin
* Date:    Jun 12, 2008
* Release:
* Version:
*
* Object File Format intersection header for McStas. Requires the qsort function.
*
* Such files may be obtained with e.g.
*   qhull < points.xyz Qx Qv Tv o > points.off
* where points.xyz has format:
*   3
*   <nb_points>
*   <x> <y> <z>
*   ...
* The resulting file should have its first line being changed from '3' into 'OFF'.
* It can then be displayed with geomview.
* A similar, but somewhat older solution is to use 'powercrust' with e.g.
*   powercrust -i points.xyz
* which will generate a 'pc.off' file to be renamed as suited.
*
*******************************************************************************/

#ifndef INTEROFF_LIB_H
#define INTEROFF_LIB_H "$Revision$"

#ifndef OFF_EPSILON
#define OFF_EPSILON 1e-13
#endif

#ifndef OFF_INTERSECT_MAX
#ifdef OPENACC
#define OFF_INTERSECT_MAX 100
#else
#define OFF_INTERSECT_MAX 1024
#endif
#endif

//#include <float.h>

#define N_VERTEX_DISPLAYED    200000

typedef struct intersection {
	MCNUM time;  	  //time of the intersection
	Coords v;	      //intersection point
	Coords normal;  //normal vector of the surface intersected
	short in_out;	  //1 if the ray enters the volume, -1 otherwise
	short edge;	    //1 if the intersection is on the boundary of the polygon, and error is possible
	unsigned long index; // index of the face
} intersection;

typedef struct polygon {
  MCNUM* p;       //vertices of the polygon in adjacent order, this way : x1 | y1 | z1 | x2 | y2 | z2 ...
  int npol;       //number of vertices
  #pragma acc shape(p[0:npol]) init_needed(npol)
  Coords normal;
  double D;
} polygon;

typedef struct off_struct {
    long vtxSize;
    long polySize;
    long faceSize;
    Coords* vtxArray;
    #pragma acc shape(vtxArray[0:vtxSize]) init_needed(vtxSize)
    Coords* normalArray;
    #pragma acc shape(vtxArray[0:faceSize]) init_needed(faceSize)
    unsigned long* faceArray;
    #pragma acc shape(vtxArray[0:faceSize][0:polySize]) init_needed(faceSize,polySize)
    double* DArray;
    #pragma acc shape(vtxArray[0:polySize]) init_needed(polySize)
    char *filename;
    int mantidflag;
    long mantidoffset;
    intersection intersects[OFF_INTERSECT_MAX]; // After a call to off_intersect_all contains the list of intersections.
    int nextintersect;                 // 'Next' intersection (first t>0) solution after call to off_intersect_all
    int numintersect;               // Number of intersections after call to off_intersect_all
} off_struct;

/*******************************************************************************
* long off_init(  char *offfile, double xwidth, double yheight, double zdepth, off_struct* data)
* ACTION: read an OFF file, optionally center object and rescale, initialize OFF data structure
* INPUT: 'offfile' OFF file to read
*        'xwidth,yheight,zdepth' if given as non-zero, apply bounding box.
*           Specifying only one of these will also use the same ratio on all axes
*        'notcenter' center the object to the (0,0,0) position in local frame when set to zero
* RETURN: number of polyhedra and 'data' OFF structure
*******************************************************************************/
long off_init(  char *offfile, double xwidth, double yheight, double zdepth,
                int notcenter, off_struct* data);

/*******************************************************************************
* int off_intersect_all(double* t0, double* t3,
     Coords *n0, Coords *n3,
     double x, double y, double z,
     double vx, double vy, double vz,
     double ax, double ay, double az,
     off_struct *data )
* ACTION: computes intersection of neutron trajectory with an object.
* INPUT:  x,y,z and vx,vy,vz are the position and velocity of the neutron
*         ax, ay, az are the local acceleration vector
*         data points to the OFF data structure
* RETURN: the number of polyhedra which trajectory intersects
*         t0 and t3 are the smallest incoming and outgoing intersection times
*         n0 and n3 are the corresponding normal vectors to the surface
*         data is the full OFF structure, including a list intersection type
*******************************************************************************/
#pragma acc routine
int off_intersect_all(double* t0, double* t3,
     Coords *n0, Coords *n3,
     double x, double y, double z,
     double vx, double vy, double vz,
     double ax, double ay, double az,
     off_struct *data );

/*******************************************************************************
* int off_intersect(double* t0, double* t3,
     Coords *n0, Coords *n3,
     double x, double y, double z,
     double vx, double vy, double vz,
     double ax, double ay, double az,
     off_struct data )
* ACTION: computes intersection of neutron trajectory with an object.
* INPUT:  x,y,z and vx,vy,vz are the position and velocity of the neutron
*         ax, ay, az are the local acceleration vector
*         data points to the OFF data structure
* RETURN: the number of polyhedra which trajectory intersects
*         t0 and t3 are the smallest incoming and outgoing intersection times
*         n0 and n3 are the corresponding normal vectors to the surface
*******************************************************************************/
#pragma acc routine
int off_intersect(double* t0, double* t3,
     Coords *n0, Coords *n3,
     double x, double y, double z,
     double vx, double vy, double vz,
     double ax, double ay, double az,
     off_struct data );

/*****************************************************************************
* int off_intersectx(double* l0, double* l3,
     Coords *n0, Coords *n3,
     double x, double y, double z,
     double kx, double ky, double kz,
     off_struct data )
* ACTION: computes intersection of an xray trajectory with an object.
* INPUT:  x,y,z and kx,ky,kz, are spatial coordinates and wavevector of the x-ray
*         respectively. data points to the OFF data structure.
* RETURN: the number of polyhedra the trajectory intersects
*         l0 and l3 are the smallest incoming and outgoing intersection lengths
*         n0 and n3 are the corresponding normal vectors to the surface
*******************************************************************************/
#pragma acc routine
int off_x_intersect(double *l0,double *l3,
     Coords *n0, Coords *n3,
     double x,  double y,  double z,
     double kx, double ky, double kz,
     off_struct data );

/*******************************************************************************
* void off_display(off_struct data)
* ACTION: display up to N_VERTEX_DISPLAYED points from the object
*******************************************************************************/
void off_display(off_struct);

/*******************************************************************************
void p_to_quadratic(double eq[], Coords acc,
                    Coords pos, Coords vel,
                    double* teq)
* ACTION: define the quadratic for the intersection of a parabola with a plane
* INPUT: 'eq' plane equation
*        'acc' acceleration vector
*        'vel' velocity of the particle
*        'pos' position of the particle
*         equation of plane A * x + B * y + C * z - D = 0
*         eq[0] = (C*az)/2+(B*ay)/2+(A*ax)/2
*         eq[1] = C*vz+B*vy+A*vx
*         eq[2] = C*z0+B*y0+A*x0-D
* RETURN: equation of parabola: teq(0) * t^2 + teq(1) * t + teq(2)
*******************************************************************************/
void p_to_quadratic(Coords norm, MCNUM d, Coords acc, Coords pos, Coords vel,
		    double* teq);

/*******************************************************************************
int quadraticSolve(double eq[], double* x1, double* x2);
* ACTION: solves the quadratic for the roots x1 and x2 
*         eq[0] * t^2 + eq[1] * t + eq[2] = 0
* INPUT: 'eq' the coefficients of the parabola
* RETURN: roots x1 and x2 and the number of solutions
*******************************************************************************/
int quadraticSolve(double* eq, double* x1, double* x2);

#endif

/* end of interoff-lib.h */
/*******************************************************************************
*
* McStas, neutron ray-tracing package
*         Copyright (C) 1997-2008, All rights reserved
*         Risoe National Laboratory, Roskilde, Denmark
*         Institut Laue Langevin, Grenoble, France
*
* Runtime: share/interoff-lib.c
*
* %Identification
* Written by: Reynald Arnerin
* Date:    Jun 12, 2008
* Origin: ILL
* Release: $Revision$
* Version: McStas X.Y
*
* Object File Format intersection library for McStas. Requires the qsort function.
*
* Such files may be obtained with e.g.
*   qhull < points.xyz Qx Qv Tv o > points.off
* where points.xyz has format (it supports comments):
*   3
*   <nb_points>
*   <x> <y> <z>
*   ...
* The resulting file should have its first line being changed from '3' into 'OFF'.
* It can then be displayed with geomview.
* A similar, but somewhat older solution is to use 'powercrust' with e.g.
*   powercrust -i points.xyz
* which will generate a 'pc.off' file to be renamed as suited.
*
*******************************************************************************/

#ifndef INTEROFF_LIB_H
#include "interoff-lib.h"
#endif

#pragma acc routine
double off_F(double x, double y,double z,double A,double B,double C,double D) {
  return ( A*x + B*y + C*z + D );
}

#pragma acc routine
char off_sign(double a) {
  if (a<0)       return(-1);
  else if (a==0) return(0);
  else           return(1);
}

// off_normal ******************************************************************
//gives the normal vector of p
#pragma acc routine
void off_normal(Coords* n, polygon p)
{
  //using Newell method
  int i=0,j=0;
  n->x=0;n->y=0;n->z=0;
  for (i = 0, j = p.npol-1; i < p.npol; j = i++)
  {
    MCNUM x1=p.p[3*i],
          y1=p.p[3*i+1],
          z1=p.p[3*i+2];
    MCNUM x2=p.p[3*j],
          y2=p.p[3*j+1],
          z2=p.p[3*j+2];
    // n is the cross product of v1*v2
    n->x += (y1 - y2) * (z1 + z2);
    n->y += (z1 - z2) * (x1 + x2);
    n->z += (x1 - x2) * (y1 + y2);
  }
} /* off_normal */

// off_pnpoly ******************************************************************
//based on http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
//return 0 if the vertex is out
//    1 if it is in
//   -1 if on the boundary
#pragma acc routine
int off_pnpoly(polygon p, Coords v)
{
  int i=0, c = 0;
  MCNUM minx=FLT_MAX,maxx=-FLT_MAX,miny=FLT_MAX,maxy=-FLT_MAX,minz=FLT_MAX,maxz=-FLT_MAX;
  MCNUM areax=0,areay=0,areaz=0;

  int pol2dx=0,pol2dy=1;          //2d restriction of the poly
  MCNUM x=v.x,y=v.y;

  /*areax: projected area with x-scratched = |v1_yz x v2_yz|, where v1=(x1-x0,0,z1-z0) & v2=(x2-x0,0,z2-z0).*/
  /* In principle, if polygon is triangle area should be scaled by 1/2, but this is irrelevant for finding the maximum area.*/
  /* Similarly for y and z scratched.*/
  areax=coords_len(coords_xp(
        coords_set(0,p.p[3*1+1]-p.p[0+1],p.p[3*1+2]-p.p[0+2]),
        coords_set(0,p.p[3*2+1]-p.p[0+1],p.p[3*2+2]-p.p[0+2])));
  areay=coords_len(coords_xp(
        coords_set(p.p[3*1+0]-p.p[0+0],0,p.p[3*1+2]-p.p[0+2]),
        coords_set(p.p[3*2+0]-p.p[0+0],0,p.p[3*2+2]-p.p[0+2])));
  areaz=coords_len(coords_xp(
        coords_set(p.p[3*1+0]-p.p[0+0],p.p[3*1+1]-p.p[0+1],0),
        coords_set(p.p[3*2+0]-p.p[0+0],p.p[3*2+1]-p.p[0+1],0)));

  if(areaz<areax){
    if(areax<areay){
      /*pick areay - i.e. scratch y*/
      pol2dy=2;
      y=v.z;
    }else{
      /*scratch x*/
      pol2dx=2;
      x=v.z;
    }
  }else if (areaz<areay){
    pol2dy=2;
    y=v.z;
  }

  //trace rays and test number of intersection
  int j;
  for (i = 0, j = p.npol-1; i < p.npol; j = i++) {
    if (((((p.p[3*i+pol2dy])<=y) && (y<(p.p[3*j+pol2dy]))) ||
         (((p.p[3*j+pol2dy])<=y) && (y<(p.p[3*i+pol2dy])))) &&
        (x < ( (p.p[3*j+pol2dx] - p.p[3*i+pol2dx]) * (y - p.p[3*i+pol2dy])
             / (p.p[3*j+pol2dy] - p.p[3*i+pol2dy]) + p.p[3*i+pol2dx]) ))
      c = !c;

    if (((fabs(p.p[3*i+pol2dy]-y)<=OFF_EPSILON) || ((fabs(p.p[3*j+pol2dy]-y)<=OFF_EPSILON))) &&
        fabs(x -((p.p[3*j+pol2dx] - p.p[3*i+pol2dx]) * (y - p.p[3*i+pol2dy])
          / (p.p[3*j+pol2dy] - p.p[3*i+pol2dy]) + p.p[3*i+pol2dx])) < OFF_EPSILON)
    {
      //the point lies on the edge
      c=-1;
      break;
    }
  }

  return c;
} /* off_pnpoly */

// off_intersectPoly ***********************************************************
//gives the intersection vertex between ray [a,b) and polygon p and its parametric value on (a b)
//based on http://geometryalgorithms.com/Archive/algorithm_0105/algorithm_0105.htm
#pragma acc routine
int off_intersectPoly(intersection *inter, Coords a, Coords b, polygon p)
{
  //direction vector of [a,b]
  Coords dir = {b.x-a.x, b.y-a.y, b.z-a.z};

  //the normal vector to the polygon
  Coords normale=p.normal;
  //off_normal(&normale, p); done at the init stage

  //direction vector from a to a vertex of the polygon
  Coords w0 = {a.x-p.p[0], a.y-p.p[1], a.z-p.p[2]};

  //scalar product
  MCNUM nw0  =-scalar_prod(normale.x,normale.y,normale.z,w0.x,w0.y,w0.z);
  MCNUM ndir = scalar_prod(normale.x,normale.y,normale.z,dir.x,dir.y,dir.z);
  inter->time = inter->edge = inter->in_out=0;
  inter->v = inter->normal = coords_set(0,0,1);

  if (fabs(ndir) < OFF_EPSILON)    // ray is parallel to polygon plane
  {
    if (nw0 == 0)              // ray lies in polygon plane (infinite number of solution)
      return 0;
    else return 0;             // ray disjoint from plane (no solution)
  }

  // get intersect point of ray with polygon plane
  inter->time = nw0 / ndir;            //parametric value the point on line (a,b)

  inter->v = coords_set(a.x + inter->time * dir.x,// intersect point of ray and plane
    a.y + inter->time * dir.y,
    a.z + inter->time * dir.z);

  int res=off_pnpoly(p,inter->v);

  inter->edge=(res==-1);
  if (ndir<0)
    inter->in_out=1;  //the negative dot product means we enter the surface
  else
    inter->in_out=-1;

  inter->normal=p.normal;

  return res;         //true if the intersection point lies inside the poly
} /* off_intersectPoly */


// off_getBlocksIndex **********************************************************
/*reads the indexes at the beginning of the off file as this :
line 1  OFF
line 2  nbVertex nbFaces nbEdges
*/
FILE *off_getBlocksIndex(char* filename, long* vtxSize, long* polySize )
{
  FILE* f = Open_File(filename,"r", NULL); /* from read_table-lib: FILE *Open_File(char *name, char *Mode, char *path) */
  if (!f) return (f);

  char line[CHAR_BUF_LENGTH];
  char *ret=0;
  *vtxSize = *polySize = 0;

  /* **************** start to read the file header */
  /* OFF file:
     'OFF' or '3'
   */

  ret=fgets(line,CHAR_BUF_LENGTH , f);// line 1 = "OFF"
  if (ret == NULL)
  {
    fprintf(stderr, "Error: Can not read 1st line in file %s (interoff/off_getBlocksIndex)\n", filename);
    exit(1);
  }
  if (strlen(line)>5)
  {
      fprintf(stderr,"Error: First line in %s is too long (=%lu). Possibly the line is not terminated by '\\n'.\n"
              "       The first line is required to be exactly 'OFF', '3' or 'ply'.\n",
              filename,(long unsigned)strlen(line));
      fclose(f);
      return(NULL);
  }

  if (strncmp(line,"OFF",3) && strncmp(line,"3",1) && strncmp(line,"ply",1))
  {
    fprintf(stderr, "Error: %s is probably not an OFF, NOFF or PLY file (interoff/off_getBlocksIndex).\n"
                    "       Requires first line to be 'OFF', '3' or 'ply'.\n",filename);
    fclose(f);
    return(NULL);
  }

  if (!strncmp(line,"OFF",3) || !strncmp(line,"3",1)) {
    do  /* OFF file: skip # comments which may be there */
    {
      ret=fgets(line,CHAR_BUF_LENGTH , f);
      if (ret == NULL)
      {
        fprintf(stderr, "Error: Can not read line in file %s (interoff/off_getBlocksIndex)\n", filename);
        exit(1);
      }
    } while (line[0]=='#');
    //line = nblines of vertex,faces and edges arrays
    sscanf(line,"%lu %lu",vtxSize,polySize);
  } else {
    do  /* PLY file: read all lines until find 'end_header'
           and locate 'element faces' and 'element vertex' */
    {
      ret=fgets(line,CHAR_BUF_LENGTH , f);
      if (ret == NULL)
      {
        fprintf(stderr, "Error: Can not read line in file %s (interoff/off_getBlocksIndex)\n", filename);
        exit(1);
      }
      if (!strncmp(line,"element face",12))
        sscanf(line,"element face %lu",polySize);
      else if (!strncmp(line,"element vertex",14))
        sscanf(line,"element vertex %lu",vtxSize);
      else if (!strncmp(line,"format binary",13))
        exit(fprintf(stderr,
          "Error: Can not read binary PLY file %s, only 'format ascii' (interoff/off_getBlocksIndex)\n%s\n",
          filename, line));
    } while (strncmp(line,"end_header",10));
  }

  /* The FILE is left opened ready to read 'vtxSize' vertices (vtxSize *3 numbers)
     and then polySize polygons (rows) */

  return(f);
} /* off_getBlocksIndex */

// off_init_planes *************************************************************
//gives the equations of 2 perpandicular planes of [ab]
#pragma acc routine
void off_init_planes(Coords a, Coords b,
  MCNUM* A1, MCNUM* C1, MCNUM* D1, MCNUM *A2, MCNUM* B2, MCNUM* C2, MCNUM* D2)
{
  //direction vector of [a b]
  Coords dir={b.x-a.x, b.y-a.y, b.z-a.z};

  //the plane parallel to the 'y' is computed with the normal vector of the projection of [ab] on plane 'xz'
  *A1= dir.z;
  *C1=-dir.x;
  if(*A1!=0 || *C1!=0)
    *D1=-(a.x)*(*A1)-(a.z)*(*C1);
  else
  {
    //the plane does not support the vector, take the one parallel to 'z''
    *A1=1;
    //B1=dir.x=0
    *D1=-(a.x);
  }
  //the plane parallel to the 'x' is computed with the normal vector of the projection of [ab] on plane 'yz'
  *B2= dir.z;
  *C2=-dir.y;
  *A2= 0;
  if (*B2==0 && *C2==0)
  {
    //the plane does not support the vector, take the one parallel to 'z'
    *B2=1;
    //B1=dir.x=0
    *D2=-(a.y);
  }
  else {
    if (dir.z==0)
    {
      //the planes are the same, take the one parallel to 'z'
      *A2= dir.y;
      *B2=-dir.x;
      *D2=-(a.x)*(*A2)-(a.y)*(*B2);
    }
    else
      *D2=-(a.y)**B2-(a.z)**C2;
  }
} /* off_init_planes */

// off_clip_3D_mod *************************************************************
#pragma acc routine
int off_clip_3D_mod(intersection* t, Coords a, Coords b,
  Coords* vtxArray, unsigned long vtxSize, unsigned long* faceArray,
  unsigned long faceSize, Coords* normalArray)
{
  MCNUM A1=0, C1=0, D1=0, A2=0, B2=0, C2=0, D2=0;      //perpendicular plane equations to [a,b]
  off_init_planes(a, b, &A1, &C1, &D1, &A2, &B2, &C2, &D2);

  int t_size=0;
  MCNUM popol[3*4]; /*3 dimensions and max 4 vertices to form a polygon*/
  unsigned long i=0,indPoly=0;

  //exploring the polygons :
  i=indPoly=0;
  while (i<faceSize)
  {
    polygon pol;
    pol.npol  = faceArray[i];                //nb vertex of polygon
    pol.p     = popol;
    pol.normal= coords_set(0,0,1);
    unsigned long indVertP1=faceArray[++i];  //polygon's first vertex index in vtxTable
    int j=1;
    /*check whether vertex is left or right of plane*/
    char sg0=off_sign(off_F(vtxArray[indVertP1].x,vtxArray[indVertP1].y,vtxArray[indVertP1].z,A1,0,C1,D1));
    while (j<pol.npol)
    {
      //polygon's j-th vertex index in vtxTable
      unsigned long indVertP2=faceArray[i+j];
      /*check whether vertex is left or right of plane*/
      char sg1=off_sign(off_F(vtxArray[indVertP2].x,vtxArray[indVertP2].y,vtxArray[indVertP2].z,A1,0,C1,D1));
      if (sg0!=sg1) //if the plane intersect the polygon
        break;

      ++j;
    }

    if (j<pol.npol)          //ok, let's test with the second plane
    {
      char sg1=off_sign(off_F(vtxArray[indVertP1].x,vtxArray[indVertP1].y,vtxArray[indVertP1].z,A2,B2,C2,D2));//tells if vertex is left or right of the plane

      j=1;
      while (j<pol.npol)
      {
        //unsigned long indVertPi=faceArray[i+j];  //polyg's j-th vertex index in vtxTable
        Coords vertPi=vtxArray[faceArray[i+j]];
        if (sg1!=off_sign(off_F(vertPi.x,vertPi.y,vertPi.z,A2,B2,C2,D2)))//if the plane intersect the polygon
          break;
        ++j;
      }
      if (j<pol.npol)
      {
#ifdef OFF_LEGACY
        if (t_size>OFF_INTERSECT_MAX)
        {
#ifndef OPENACC
          fprintf(stderr, "Warning: number of intersection exceeded (%d) (interoff-lib/off_clip_3D_mod)\n", OFF_INTERSECT_MAX);
#endif
            return (t_size);
        }
#endif
        //both planes intersect the polygon, let's find the intersection point
        //our polygon :
        int k;
        for (k=0; k<pol.npol; ++k)
        {
          Coords vertPk=vtxArray[faceArray[i+k]];
          pol.p[3*k]  =vertPk.x;
          pol.p[3*k+1]=vertPk.y;
          pol.p[3*k+2]=vertPk.z;
        }
        pol.normal=normalArray[indPoly];
        intersection x;
        if (off_intersectPoly(&x, a, b, pol))
        {
          x.index = indPoly;
#ifdef OFF_LEGACY
          t[t_size++]=x;
#else
	  /* Check against our 4 existing times, starting from [-FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX] */
	  /* Case 1, negative time? */
	  if (t_size < 4) t_size++;	  
	  if (x.time < 0) {
	    if (x.time > t[0].time) {
	      t[0]=x;
	    }
	  } else {
	    /* Case 2, positive time */
	    intersection xtmp;
	    if (x.time < t[3].time) {
	      t[3]=x;
	      if (t[3].time < t[2].time) {
		xtmp = t[2];
		t[2] = t[3];
		t[3] = xtmp;
	      }
	      if (t[2].time < t[1].time) {
		xtmp = t[1];
		t[1] = t[2];
		t[2] = xtmp;
	      }
	    } 
	  }
#endif
	}
      } /* if (j<pol.npol) */
    } /* if (j<pol.npol) */
    i += pol.npol;
    indPoly++;
  } /* while i<faceSize */
  return t_size;
} /* off_clip_3D_mod */

// off_clip_3D_mod_grav *************************************************************
/*******************************************************************************
version of off_clip_3D_mod_grav
*******************************************************************************/
#pragma acc routine seq
int off_clip_3D_mod_grav(intersection* t, Coords pos, Coords vel, Coords acc,
  Coords* vtxArray, unsigned long vtxSize, unsigned long* faceArray,
  unsigned long faceSize, Coords* normalArray, double* DArray)
{
  int t_size=0;
  MCNUM popol[3*CHAR_BUF_LENGTH];
  double plane_Eq [4];
  double quadratic [3];
  unsigned long i=0,indPoly=0;
  //exploring the polygons :
  i=indPoly=0;
  while (i<faceSize)
  {
    polygon pol;
    pol.npol  = faceArray[i];                //nb vertex of polygon
    pol.p     = popol;
    pol.normal= coords_set(0,0,1);
    unsigned long indVertP1=faceArray[++i];  //polygon's first vertex index in vtxTable
    
    if (t_size>CHAR_BUF_LENGTH)
      {
#ifndef OPENACC
	fprintf(stderr, "Warning: number of intersection exceeded (%d) (interoff-lib/off_clip_3D_mod)\n", CHAR_BUF_LENGTH);
#endif
	return (t_size);
      }
    //both planes intersect the polygon, let's find the intersection point
    //our polygon :
    int k;
    for (k=0; k<pol.npol; ++k)
      {
	Coords vertPk=vtxArray[faceArray[i+k]];
	pol.p[3*k]  =vertPk.x;
	pol.p[3*k+1]=vertPk.y;
	pol.p[3*k+2]=vertPk.z;
      }
    pol.normal=normalArray[indPoly];
    pol.D=DArray[indPoly];
    p_to_quadratic(pol.normal, pol.D, acc, pos, vel, quadratic);
    double x1, x2;
    int nsol = quadraticSolve(quadratic, &x1, &x2);

    if (nsol >= 1) {
      double time = 1.0e36;
      if (x1 < time && x1 > 0.0) {
	time = x1;
      }
      if (nsol == 2 && x2 < time && x2 > 0.0) {
	time = x2;
      }
      if (time != 1.0e36) {
	intersection inters;
	double t2 = time * time * 0.5;
	double tx = pos.x + time * vel.x;
	if (acc.x != 0.0) {
	  tx = tx + t2 * acc.x;
	}
	double ty = pos.y + time * vel.y;
	if (acc.y != 0.0) {
	  ty = ty + t2 * acc.y;
	}
	double tz = pos.z + time * vel.z;
	if (acc.z != 0.0) {
	  tz = tz + t2 * acc.z;
	}
	inters.v = coords_set(tx, ty, tz);
	Coords tvel = coords_set(vel.x + time * acc.x,
				 vel.y + time * acc.y,
				 vel.z + time * acc.z);
	inters.time = time;
	inters.normal = pol.normal;
	inters.index = indPoly;
	int res=off_pnpoly(pol,inters.v);
	if (res != 0) {
	  inters.edge=(res==-1);
	  MCNUM ndir = scalar_prod(pol.normal.x,pol.normal.y,pol.normal.z,tvel.x,tvel.y,tvel.z);
	  if (ndir<0) {
	    inters.in_out=1;  //the negative dot product means we enter the surface
	  } else {
	    inters.in_out=-1;
	  }
#ifdef OFF_LEGACY
          t[t_size++]=inters;
#else
    /* Check against our 4 existing times, starting from [-FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX] */
    /* Case 1, negative time? */
    if (t_size < 4) t_size++;
    if (inters.time < 0) {
      if (inters.time > t[0].time) {
        t[0]=inters;
      }
    } else {
      /* Case 2, positive time */
      intersection xtmp;
      if (inters.time < t[3].time) {
      t[3]=inters;
        if (t[3].time < t[2].time) {
    xtmp = t[2];
    t[2] = t[3];
    t[3] = xtmp;
        }
        if (t[2].time < t[1].time) {
    xtmp = t[1];
    t[1] = t[2];
    t[2] = xtmp;
        }
      }
    }
#endif
	}
      }
    }
    i += pol.npol;
    indPoly++;
  } /* while i<faceSize */
  return t_size;
} /* off_clip_3D_mod_grav */

// off_compare *****************************************************************
#pragma acc routine
int off_compare (void const *a, void const *b)
{
   intersection const *pa = a;
   intersection const *pb = b;

   return off_sign(pa->time - pb->time);
} /* off_compare */

// off_cleanDouble *************************************************************
//given an array of intersections throw those which appear several times
//returns 1 if there is a possibility of error
#pragma acc routine
int off_cleanDouble(intersection* t, int* t_size)
{
  int i=1;
  intersection prev=t[0];
  while (i<*t_size)
  {
    int j=i;
    //for each intersection with the same time
    while (j<*t_size && fabs(prev.time-t[j].time)<OFF_EPSILON)
    {
      //if the intersection is the exact same erase it
      if (prev.in_out==t[j].in_out)
      {
        int k;
        for (k=j+1; k<*t_size; ++k)
        {
          t[k-1]=t[k];
        }
        *t_size-=1;
      }
      else
        ++j;
    }
    prev=t[i];
    ++i;

  }
  return 1;
} /* off_cleanDouble */

// off_cleanInOut **************************************************************
//given an array of intesections throw those which enter and exit in the same time
//Meaning the ray passes very close to the volume
//returns 1 if there is a possibility of error
#pragma acc routine
int off_cleanInOut(intersection* t, int* t_size)
{
  int i=1;
  intersection prev=t[0];
  while (i<*t_size)
  {
    //if two intersection have the same time but one enters and the other exits erase both
    //(such intersections must be adjacent in the array : run off_cleanDouble before)
    if (fabs(prev.time-t[i].time)<OFF_EPSILON && prev.in_out!=t[i].in_out)
    {
      int j=0;
      for (j=i+1; j<*t_size; ++j)
      {
        t[j-2]=t[j];
      }
      *t_size-=2;
      prev=t[i-1];
    }
    else
    {
      prev=t[i];
      ++i;
    }
  }
  return (*t_size);
} /* off_cleanInOut */

/* PUBLIC functions ******************************************************** */

/*******************************************************************************
* long off_init(  char *offfile, double xwidth, double yheight, double zdepth, off_struct* data)
* ACTION: read an OFF file, optionally center object and rescale, initialize OFF data structure
* INPUT: 'offfile' OFF file to read
*        'xwidth,yheight,zdepth' if given as non-zero, apply bounding box.
*           Specifying only one of these will also use the same ratio on all axes
*        'notcenter' center the object to the (0,0,0) position in local frame when set to zero
* RETURN: number of polyhedra and 'data' OFF structure
*******************************************************************************/
long off_init(  char *offfile, double xwidth, double yheight, double zdepth,
                int notcenter, off_struct* data)
{
  // data to be initialized
  long    vtxSize =0, polySize=0, i=0, ret=0, faceSize=0;
  Coords* vtxArray        =NULL;
  Coords* normalArray     =NULL;
  double* DArray          =NULL;
  unsigned long* faceArray=NULL;
  FILE*   f               =NULL; /* the FILE with vertices and polygons */
  double minx=FLT_MAX,maxx=-FLT_MAX,miny=FLT_MAX,maxy=-FLT_MAX,minz=FLT_MAX,maxz=-FLT_MAX;

  // get the indexes
  if (!data) return(0);

  MPI_MASTER(
  printf("Loading geometry file (OFF/PLY): %s\n", offfile);
  );

  f=off_getBlocksIndex(offfile,&vtxSize,&polySize);
  if (!f) return(0);

  // read vertex table = [x y z | x y z | ...] =================================
  // now we read the vertices as 'vtxSize*3' numbers and store it in vtxArray
  MPI_MASTER(
  printf("  Number of vertices: %ld\n", vtxSize);
  );
  vtxArray   = malloc(vtxSize*sizeof(Coords));
  if (!vtxArray) return(0);
  i=0;
  while (i<vtxSize && ~feof(f))
  {
    double x,y,z;
    ret=fscanf(f, "%lg%lg%lg", &x,&y,&z);
    if (!ret) {
      // invalid line: we skip it (probably a comment)
      char line[CHAR_BUF_LENGTH];
      char *s=fgets(line, CHAR_BUF_LENGTH, f);
      continue;
    }
    if (ret != 3) {
      fprintf(stderr, "Error: can not read [xyz] coordinates for vertex %li in file %s (interoff/off_init). Read %li values.\n",
        i, offfile, ret);
      exit(2);
    }
    vtxArray[i].x=x;
    vtxArray[i].y=y;
    vtxArray[i].z=z;

    //bounding box
    if (vtxArray[i].x<minx) minx=vtxArray[i].x;
    if (vtxArray[i].x>maxx) maxx=vtxArray[i].x;
    if (vtxArray[i].y<miny) miny=vtxArray[i].y;
    if (vtxArray[i].y>maxy) maxy=vtxArray[i].y;
    if (vtxArray[i].z<minz) minz=vtxArray[i].z;
    if (vtxArray[i].z>maxz) maxz=vtxArray[i].z;
    i++; // inquire next vertex
  }

  // resizing and repositioning params
  double centerx=0, centery=0, centerz=0;
  if (!notcenter) {
    centerx=(minx+maxx)*0.5;
    centery=(miny+maxy)*0.5;
    centerz=(minz+maxz)*0.5;
  }

  double rangex=-minx+maxx,
         rangey=-miny+maxy,
         rangez=-minz+maxz;

  double ratiox=1,ratioy=1,ratioz=1;

  if (xwidth && rangex)
  {
    ratiox=xwidth/rangex;
    ratioy=ratiox;
    ratioz=ratiox;
  }

  if (yheight && rangey)
  {
    ratioy=yheight/rangey;
    if(!xwidth)  ratiox=ratioy;
    ratioz=ratioy;
  }

  if (zdepth && rangez)
  {
    ratioz=zdepth/rangez;
    if(!xwidth)  ratiox=ratioz;
    if(!yheight) ratioy=ratioz;
  }

  rangex *= ratiox;
  rangey *= ratioy;
  rangez *= ratioz;

  //center and resize the object
  for (i=0; i<vtxSize; ++i)
  {
    vtxArray[i].x=(vtxArray[i].x-centerx)*ratiox+(!notcenter ? 0 : centerx);
    vtxArray[i].y=(vtxArray[i].y-centery)*ratioy+(!notcenter ? 0 : centery);
    vtxArray[i].z=(vtxArray[i].z-centerz)*ratioz+(!notcenter ? 0 : centerz);
  }

  // read face table = [nbvertex v1 v2 vn | nbvertex v1 v2 vn ...] =============
  MPI_MASTER(
  printf("  Number of polygons: %ld\n", polySize);
  );
  normalArray= malloc(polySize*sizeof(Coords));
  faceArray  = malloc(polySize*10*sizeof(unsigned long)); // we assume polygons have less than 9 vertices
  DArray     = malloc(polySize*sizeof(double));
  if (!normalArray || !faceArray || !DArray) return(0);

  // fill faces
  faceSize=0;
  i=0;
  while (i<polySize && ~feof(f)) {
    int  nbVertex=0, j=0;
    // read the length of this polygon
    ret=fscanf(f, "%d", &nbVertex);
    if (!ret) {
      // invalid line: we skip it (probably a comment)
      char line[CHAR_BUF_LENGTH];
      char *s=fgets(line, CHAR_BUF_LENGTH, f);
      continue;
    }
    if (ret != 1) {
      fprintf(stderr, "Error: can not read polygon %li length in file %s (interoff/off_init)\n",
        i, offfile);
      exit(3);
    }
    if (faceSize > polySize*10) {
      fprintf(stderr, "Error: %li exceeded allocated polygon array[%li] in file %s (interoff/off_init)\n",
        faceSize, polySize*10, offfile);
    }
    faceArray[faceSize++] = nbVertex; // length of the polygon/face
    // then read the vertex ID's
    for (j=0; j<nbVertex; j++) {
      double vtx=0;
      ret=fscanf(f, "%lg", &vtx);
      faceArray[faceSize++] = vtx;   // add vertices index after length of polygon
    }
    i++;
  }

  // precomputes normals
  long indNormal=0;//index in polyArray
  i=0;    //index in faceArray
  while (i<faceSize)
  {
    int    nbVertex=faceArray[i];//nb of vertices of this polygon
    double vertices[3*nbVertex];
    int j;

    for (j=0; j<nbVertex; ++j)
    {
      unsigned long indVertPj=faceArray[i+j+1];
      vertices[3*j]  =vtxArray[indVertPj].x;
      vertices[3*j+1]=vtxArray[indVertPj].y;
      vertices[3*j+2]=vtxArray[indVertPj].z;
    }

    polygon p;
    p.p   =vertices;
    p.npol=nbVertex;
    off_normal(&(p.normal),p);

    normalArray[indNormal]=p.normal;
    p.D = scalar_prod(p.normal.x,p.normal.y,p.normal.z,
		      vertices[0],vertices[1],vertices[2]);
    DArray[indNormal]=p.D;

    i += nbVertex+1;
    indNormal++;

  }

  MPI_MASTER(
  if (ratiox!=ratioy || ratiox!=ratioz || ratioy!=ratioz)
    printf("Warning: Aspect ratio of the geometry %s was modified.\n"
           "         If you want to keep the original proportions, specifiy only one of the dimensions.\n",
           offfile);
  if ( xwidth==0 && yheight==0 && zdepth==0 ) {
    printf("Warning: Neither xwidth, yheight or zdepth are defined.\n"
	   "           The file-defined (non-scaled) geometry the OFF geometry %s will be applied!\n",
           offfile);
  }
  printf("  Bounding box dimensions for geometry %s:\n", offfile);
  printf("    Length=%f (%.3f%%)\n", rangex, ratiox*100);
  printf("    Width= %f (%.3f%%)\n", rangey, ratioy*100);
  printf("    Depth= %f (%.3f%%)\n", rangez, ratioz*100);
  );

  data->vtxArray   = vtxArray;
  data->normalArray= normalArray;
  data->DArray     = DArray;
  data->faceArray  = faceArray;
  data->vtxSize    = vtxSize;
  data->polySize   = polySize;
  data->faceSize   = faceSize;
  data->filename   = offfile;
  #ifdef OPENACC
  acc_attach((void *)&vtxArray);
  acc_attach((void *)&normalArray);
  acc_attach((void *)&faceArray);
  #endif

  return(polySize);
} /* off_init */

#pragma acc routine
int Min_int(int x, int y) {
  return (x<y)? x :y;
}

 
#pragma acc routine
void merge(intersection *arr, int l, int m, int r)
{
int i, j, k;
int n1 = m - l + 1;
int n2 =  r - m;

/* create temp arrays */
intersection *L, *R;
 L = (intersection *)malloc(sizeof(intersection) * n1);
 R = (intersection *)malloc(sizeof(intersection) * n2);
/* Copy data to temp arrays L[] and R[] */
 #pragma acc loop independent
for (i = 0; i < n1; i++)
    L[i] = arr[l + i];
 #pragma acc loop independent
for (j = 0; j < n2; j++)
    R[j] = arr[m + 1+ j];

/* Merge the temp arrays back into arr[l..r]*/
i = 0;
j = 0;
k = l;

while (i < n1 && j < n2)
{
    if (L[i].time <= R[j].time)
    {
        arr[k] = L[i];
        i++;
    }
    else
    {
        arr[k] = R[j];
        j++;
    }
    k++;
}

/* Copy the remaining elements of L[], if there are any */

while (i < n1)
{
    arr[k] = L[i];
    i++;
    k++;
}

/* Copy the remaining elements of R[], if there are any */
while (j < n2)
{
    arr[k] = R[j];
    j++;
    k++;
}
free(L);
free(R);
}


#ifdef USE_OFF
#pragma acc routine
void gpusort(intersection *arr, int size)
{
  int curr_size;  // For current size of subarrays to be merged
  // curr_size varies from 1 to n/2
  int left_start; // For picking starting index of left subarray
  // to be merged
  // pcopying (R[0:n2])
  {
    for (curr_size=1; curr_size<=size-1; curr_size = 2*curr_size)
      {
	// Pick starting point of different subarrays of current size
	for (left_start=0; left_start<size-1; left_start += 2*curr_size)
	  {
	    // Find ending point of left subarray. mid+1 is starting
	    // point of right
	    int mid = left_start + curr_size - 1;

	    int right_end = Min_int(left_start + 2*curr_size - 1, size-1);

	    // Merge Subarrays arr[left_start...mid] & arr[mid+1...right_end]
	    if (mid < right_end) merge(arr, left_start, mid, right_end);
	  }
      }
  }
}
#endif

/*******************************************************************************
void p_to_quadratic(double eq[], Coords acc,
                    Coords pos, Coords vel,
                    double* teq)
* ACTION: define the quadratic for the intersection of a parabola with a plane
* INPUT: 'eq' plane equation
*        'acc' acceleration vector
*        'vel' velocity of the particle
*        'pos' position of the particle
*         equation of plane A * x + B * y + C * z - D = 0
*         eq[0] = (C*az)/2+(B*ay)/2+(A*ax)/2
*         eq[1] = C*vz+B*vy+A*vx
*         eq[2] = C*z0+B*y0+A*x0-D
* RETURN: equation of parabola: teq(0) * t^2 + teq(1) * t + teq(2)
*******************************************************************************/
void p_to_quadratic(Coords norm, MCNUM d, Coords acc, Coords pos, Coords vel,
		    double* teq)
{
  teq[0] = scalar_prod(norm.x, norm.y, norm.z, acc.x, acc.y, acc.z) * 0.5;
  teq[1] = scalar_prod(norm.x, norm.y, norm.z, vel.x, vel.y, vel.z);
  teq[2] = scalar_prod(norm.x, norm.y, norm.z, pos.x, pos.y, pos.z) - d;
  return;
}

/*******************************************************************************
int quadraticSolve(double eq[], double* x1, double* x2);
* ACTION: solves the quadratic for the roots x1 and x2 
*         eq[0] * t^2 + eq[1] * t + eq[2] = 0
* INPUT: 'eq' the coefficients of the parabola
* RETURN: roots x1 and x2 and the number of solutions
*******************************************************************************/
int quadraticSolve(double* eq, double* x1, double* x2)
{
  if (eq[0] == 0.0) { // This is a linear equation
    if (eq[1] != 0.0) { // one solution
      *x1 = -eq[2]/eq[1];
      *x2 = 1.0e36;
      return 1;
    }else { // no solutions, 1.0e36 will be ignored.
      *x1 = 1.0e36;
      *x2 = 1.0e36;
      return 0;
    }
  }
  double delta = eq[1]*eq[1]-4.0*eq[0]*eq[2];
  if (delta < 0.0) { // no solutions, both are imaginary
    *x1 = 1.0e36;
    *x2 = 1.0e36;
    return 0;
  }
  double s = 1.0;
  if (eq[1] < 0) {
    s = -1.0;
  }
  *x1 = (-eq[1] - s * sqrt(delta))/(2.0*eq[0]);
  if (eq[0] != 0.0) { //two solutions
    *x2 = eq[2]/(eq[0]*(*x1));
    return 2;
  } else { //one solution
    *x2 = 1.0e36;
    return 1;
  }
}

/*******************************************************************************
* int off_intersect_all(double* t0, double* t3,
     Coords *n0, Coords *n3,
     double x, double y, double z,
     double vx, double vy, double vz,
     double ax, double ay, double az,
     off_struct *data )
* ACTION: computes intersection of neutron trajectory with an object.
* INPUT:  x,y,z and vx,vy,vz are the position and velocity of the neutron
*         ax, ay, az are the local acceleration vector
*         data points to the OFF data structure
* RETURN: the number of polyhedra which trajectory intersects
*         t0 and t3 are the smallest incoming and outgoing intersection times
*         n0 and n3 are the corresponding normal vectors to the surface
*         data is the full OFF structure, including a list intersection type
*******************************************************************************/
int off_intersect_all(double* t0, double* t3,
     Coords *n0, Coords *n3,
     double x,  double y,  double z,
     double vx, double vy, double vz,
     double ax, double ay, double az,
     off_struct *data )
{

    int t_size = 0;
#ifdef OFF_LEGACY

    if(mcgravitation) {
      Coords pos={ x,  y,  z};
      Coords vel={vx, vy, vz};
      Coords acc={ax, ay, az};
      t_size=off_clip_3D_mod_grav(data->intersects, pos, vel, acc,
				  data->vtxArray, data->vtxSize, data->faceArray,
				  data->faceSize, data->normalArray, data->DArray );
    } else {
    ///////////////////////////////////
    // non-grav
      Coords A={x, y, z};
      Coords B={x+vx, y+vy, z+vz};
      t_size=off_clip_3D_mod(data->intersects, A, B,
			     data->vtxArray, data->vtxSize, data->faceArray,
			     data->faceSize, data->normalArray );
    }
    #ifndef OPENACC
    qsort(data->intersects, t_size, sizeof(intersection),  off_compare);
    #else
    #ifdef USE_OFF
    gpusort(data->intersects, t_size);
    #endif
    #endif
    off_cleanDouble(data->intersects, &t_size);
    off_cleanInOut(data->intersects,  &t_size);

    /*find intersections "closest" to 0 (favouring positive ones)*/
    if(t_size>0){
      int i=0;
      if(t_size>1) {
        for (i=1; i < t_size-1; i++){
          if (data->intersects[i-1].time > 0 && data->intersects[i].time > 0)
            break;
        }

	data->nextintersect=i-1;
	data->numintersect=t_size;

        if (t0) *t0 = data->intersects[i-1].time;
        if (n0) *n0 = data->intersects[i-1].normal;
        if (t3) *t3 = data->intersects[i].time;
        if (n3) *n3 = data->intersects[i].normal;
      } else {
        if (t0) *t0 = data->intersects[0].time;
	      if (n0) *n0 = data->intersects[0].normal;
      }
      /* should also return t[0].index and t[i].index as polygon ID */
      data->nextintersect=(data->intersects[data->nextintersect]).index;
      return t_size;
    }
#else
    intersection intersect4[4];
    intersect4[0].time=-FLT_MAX;
    intersect4[1].time=FLT_MAX;
    intersect4[2].time=FLT_MAX;
    intersect4[3].time=FLT_MAX;
    if(mcgravitation) {
      Coords pos={ x,  y,  z};
      Coords vel={vx, vy, vz};
      Coords acc={ax, ay, az};
      t_size=off_clip_3D_mod_grav(intersect4, pos, vel, acc,
				  data->vtxArray, data->vtxSize, data->faceArray,
				  data->faceSize, data->normalArray, data->DArray);
    } else {
    ///////////////////////////////////
    // non-grav
      Coords A={x, y, z};
      Coords B={x+vx, y+vy, z+vz};
      t_size=off_clip_3D_mod(intersect4, A, B,
	  data->vtxArray, data->vtxSize, data->faceArray, data->faceSize, data->normalArray );
    }
    if(t_size>0){
      int i=0;
      if (intersect4[0].time == -FLT_MAX) i=1;
      data->numintersect=t_size;
      if (t0) *t0 = intersect4[i].time;
      if (n0) *n0 = intersect4[i].normal;
      if (t3) *t3 = intersect4[i+1].time;
      if (n3) *n3 = intersect4[i+1].normal;

      if (intersect4[1].time == FLT_MAX)
      {
        if (t3) *t3 = 0.0;
      }

      /* should also return t[0].index and t[i].index as polygon ID */
      data->nextintersect=(int)intersect4[i].index;
      return t_size;
    }
#endif
    return 0;
} /* off_intersect */

/*******************************************************************************
* int off_intersect(double* t0, double* t3,
     Coords *n0, Coords *n3,
     double x, double y, double z,
     double vx, double vy, double vz,
     off_struct data )
* ACTION: computes intersection of neutron trajectory with an object.
* INPUT:  x,y,z and vx,vy,vz are the position and velocity of the neutron
*         data points to the OFF data structure
* RETURN: the number of polyhedra which trajectory intersects
*         t0 and t3 are the smallest incoming and outgoing intersection times
*         n0 and n3 are the corresponding normal vectors to the surface
*******************************************************************************/
int off_intersect(double* t0, double* t3,
     Coords *n0, Coords *n3,
     double x,  double y,  double z,
     double vx, double vy, double vz,
     double ax, double ay, double az,
     off_struct data )
{
  return off_intersect_all(t0, t3, n0, n3, x, y, z, vx, vy, vz, ax, ay, az, &data );
} /* off_intersect */

/*****************************************************************************
* int off_x_intersect(double* l0, double* l3,
     Coords *n0, Coords *n3,
     double x, double y, double z,
     double kx, double ky, double kz,
     off_struct data )
* ACTION: computes intersection of an xray trajectory with an object.
* INPUT:  x,y,z and kx,ky,kz, are spatial coordinates and wavevector of the x-ray
*         respectively. data points to the OFF data structure.
* RETURN: the number of polyhedra the trajectory intersects
*         l0 and l3 are the smallest incoming and outgoing intersection lengths
*         n0 and n3 are the corresponding normal vectors to the surface
*******************************************************************************/
int off_x_intersect(double *l0,double *l3,
     Coords *n0, Coords *n3,
     double x,  double y,  double z,
     double kx, double ky, double kz,
     off_struct data )
{
  /*This function simply reformats and calls off_intersect (as for neutrons)
   *by normalizing the wavevector - this will yield the intersection lengths
   *in m*/
  double jx,jy,jz,invk;
  int n;
  invk=1/sqrt(scalar_prod(kx,ky,kz,kx,ky,kz));
  jx=kx*invk;jy=ky*invk;jz=kz*invk;
  n=off_intersect(l0,l3,n0,n3,x,y,z,jx,jy,jz,0.0,0.0,0.0,data);
  return n;
}


/*******************************************************************************
* void off_display(off_struct data)
* ACTION: display up to N_VERTEX_DISPLAYED polygons from the object
*******************************************************************************/
void off_display(off_struct data)
{
  unsigned int i;
  double ratio=(double)(N_VERTEX_DISPLAYED)/(double)data.faceSize;
  unsigned int pixel=0;
  for (i=0; i<data.faceSize-1; i++) {
    int j;
    int nbVertex = data.faceArray[i];
    double x0,y0,z0;
    x0 = data.vtxArray[data.faceArray[i+1]].x;
    y0 = data.vtxArray[data.faceArray[i+1]].y;
    z0 = data.vtxArray[data.faceArray[i+1]].z;
    double x1=x0,y1=y0,z1=z0;
    double cmx=0,cmy=0,cmz=0;

    int drawthis = rand01() < ratio;
    // First pass, calculate center of mass location...
    for (j=1; j<=nbVertex; j++) {
      cmx = cmx+data.vtxArray[data.faceArray[i+j]].x;
      cmy = cmy+data.vtxArray[data.faceArray[i+j]].y;
      cmz = cmz+data.vtxArray[data.faceArray[i+j]].z;
    }
    cmx /= nbVertex;
    cmy /= nbVertex;
    cmz /= nbVertex;

    char pixelinfo[1024];
    sprintf(pixelinfo, "%li,%li,%li,%i,%g,%g,%g,%g,%g,%g", data.mantidoffset+pixel, data.mantidoffset, data.mantidoffset+data.polySize-1, nbVertex, cmx, cmy, cmz, x1-cmx, y1-cmy, z1-cmz);
    for (j=2; j<=nbVertex; j++) {
      double x2,y2,z2;
      x2 = data.vtxArray[data.faceArray[i+j]].x;
      y2 = data.vtxArray[data.faceArray[i+j]].y;
      z2 = data.vtxArray[data.faceArray[i+j]].z;
      sprintf(pixelinfo, "%s,%g,%g,%g", pixelinfo, x2-cmx, y2-cmy, z2-cmz);
      if (ratio > 1 || drawthis) {
	mcdis_line(x1,y1,z1,x2,y2,z2);
      }
      x1 = x2; y1 = y2; z1 = z2;
    }
    if (ratio > 1 || drawthis) {
	mcdis_line(x1,y1,z1,x0,y0,z0);
      }
    if (data.mantidflag) {
      printf("MANTID_PIXEL: %s\n", pixelinfo);
      pixel++;
    }
    i += nbVertex;
  }
} /* off_display */

/* end of interoff-lib.c */

/* Declare structures and functions only once in each instrument. */
#ifndef POWDERN_DECL_UNION
#define POWDERN_DECL_UNION
/* format definitions in the order {j d F2 DW Dd inv2d q F strain} */
#ifndef Crystallographica
#define Crystallographica { 4,5,7,0,0,0,0,0,0 }
#define Fullprof          { 4,0,8,0,0,5,0,0,0 }
#define Lazy              {17,6,0,0,0,0,0,13,0 }
#define Undefined         { 0,0,0,0,0,0,0,0,0 }
#endif

  struct line_data_union
    {
      double F2;                  /* Value of structure factor */
      double q;                   /* Qvector */
      int j;                      /* Multiplicity */
      double DWfactor;            /* Debye-Waller factor */
      double w;                   /* Intrinsic line width */
      double Epsilon;             /* Strain=delta_d_d/d shift in ppm */
    };

  struct line_info_struct_union
    {
      struct line_data_union *list;     /* Reflection array */
      int  count;                  /* Number of reflections */
      double Dd;
      double DWfactor;
      double V_0;
      double rho;
      double at_weight;
      double at_nb;
      double sigma_a; // should not be used
      double sigma_i; // should not be used
      char   compname[256];
      double flag_barns;
      int    shape; /* 0 cylinder, 1 box, 2 sphere, 3 OFF file */
      int    column_order[9]; /* column signification */
      int    flag_warning;
      char   type;  /* interaction type of event t=Transmit, i=Incoherent, c=Coherent */
      double dq;    /* wavevector transfer [Angs-1] */
      double Epsilon; /* global strain in ppm */
      double XsectionFactor;
      double my_s_v2_sum;
      double my_a_v;
      double my_inc;
      double *w_v,*q_v, *my_s_v2;
      double radius_i,xwidth_i,yheight_i,zdepth_i; // not to be used, but still here
      double v; /* last velocity (cached) */
      double Nq;
      int    nb_reuses, nb_refl, nb_refl_count;
      double v_min, v_max;
      double xs_Nq[CHAR_BUF_LENGTH];
      double xs_sum[CHAR_BUF_LENGTH];
      double neutron_passed;
      long   xs_compute, xs_reuse, xs_calls;
    };

  off_struct offdata_union;
  
  // PN_list_compare *****************************************************************

  int PN_list_compare_union (void const *a, void const *b)
  {
     struct line_data_union const *pa = a;
     struct line_data_union const *pb = b;
     double s = pa->q - pb->q;
     
     if (!s) return 0;
     else    return (s < 0 ? -1 : 1);
  } /* PN_list_compare */

  int read_line_data_union(char *SC_file, struct line_info_struct_union *info)
  {
    struct line_data_union *list = NULL;
    int    size = 0;
    t_Table sTable; /* sample data table structure from SC_file */
    int    i=0;
    int    mult_count  =0;
    char   flag=0;
    double q_count=0, j_count=0, F2_count=0;
    char **parsing;
    int    list_count=0;

    if (!SC_file || !strlen(SC_file) || !strcmp(SC_file, "NULL")) {
      printf("PowderN: %s: Using incoherent elastic scattering only\n",info->compname);
      info->count = 0;
      return(0);
    }
    Table_Read(&sTable, SC_file, 1); /* read 1st block data from SC_file into sTable*/

    /* parsing of header */
    parsing = Table_ParseHeader(sTable.header,
      "Vc","V_0",
      "sigma_abs","sigma_a ",
      "sigma_inc","sigma_i ",
      "column_j",
      "column_d",
      "column_F2",
      "column_DW",
      "column_Dd",
      "column_inv2d", "column_1/2d", "column_sintheta/lambda",
      "column_q", /* 14 */
      "DW", "Debye_Waller",
      "delta_d_d/d",
      "column_F ",
      "V_rho",
      "density",
      "weight",
      "nb_atoms","multiplicity", /* 23 */
      "column_ppm","column_strain",
      NULL);

    if (parsing) {
      if (parsing[0] && !info->V_0)     info->V_0    =atof(parsing[0]);
      if (parsing[1] && !info->V_0)     info->V_0    =atof(parsing[1]);
      if (parsing[2] && !info->sigma_a) info->sigma_a=atof(parsing[2]);
      if (parsing[3] && !info->sigma_a) info->sigma_a=atof(parsing[3]);
      if (parsing[4] && !info->sigma_i) info->sigma_i=atof(parsing[4]);
      if (parsing[5] && !info->sigma_i) info->sigma_i=atof(parsing[5]);
      if (parsing[6])                   info->column_order[0]=atoi(parsing[6]);
      if (parsing[7])                   info->column_order[1]=atoi(parsing[7]);
      if (parsing[8])                   info->column_order[2]=atoi(parsing[8]);
      if (parsing[9])                   info->column_order[3]=atoi(parsing[9]);
      if (parsing[10])                  info->column_order[4]=atoi(parsing[10]);
      if (parsing[11])                  info->column_order[5]=atoi(parsing[11]);
      if (parsing[12])                  info->column_order[5]=atoi(parsing[12]);
      if (parsing[13])                  info->column_order[5]=atoi(parsing[13]);
      if (parsing[14])                  info->column_order[6]=atoi(parsing[14]);
      if (parsing[15] && info->DWfactor<=0)    info->DWfactor=atof(parsing[15]);
      if (parsing[16] && info->DWfactor<=0)    info->DWfactor=atof(parsing[16]);
      if (parsing[17] && info->Dd <0)          info->Dd      =atof(parsing[17]);
      if (parsing[18])                  info->column_order[7]=atoi(parsing[18]);
      if (parsing[19] && !info->V_0)    info->V_0    =1/atof(parsing[19]);
      if (parsing[20] && !info->rho)    info->rho    =atof(parsing[20]);
      if (parsing[21] && !info->at_weight)     info->at_weight    =atof(parsing[21]);
      if (parsing[22] && info->at_nb <= 1)  info->at_nb    =atof(parsing[22]);
      if (parsing[23] && info->at_nb <= 1)  info->at_nb    =atof(parsing[23]);
      if (parsing[24])                  info->column_order[8]=atoi(parsing[24]);
      if (parsing[25])                  info->column_order[8]=atoi(parsing[25]);
      for (i=0; i<=25; i++) if (parsing[i]) free(parsing[i]);
      free(parsing);
    }

    if (!sTable.rows)
      exit(fprintf(stderr, "PowderN: %s: Error: The number of rows in %s "
       "should be at least %d\n", info->compname, SC_file, 1));
    else size = sTable.rows;
    Table_Info(sTable);
    printf("PowderN: %s: Reading %d rows from %s\n",
          info->compname, size, SC_file);

    if (info->column_order[0] == 4 && info->flag_barns !=0)
    printf("PowderN: %s: Powder file probably of type Crystallographica/Fullprof (lau)\n"
           "WARNING: but F2 unit is set to barns=1 (barns). Intensity might be 100 times too high.\n",
           info->compname);
  if (info->column_order[0] == 17 && info->flag_barns == 0)
    printf("PowderN: %s: Powder file probably of type Lazy Pulver (laz)\n"
           "WARNING: but F2 unit is set to barns=0 (fm^2). Intensity might be 100 times too low.\n",
           info->compname);
    /* allocate line_data array */
    list = (struct line_data_union*)malloc(size*sizeof(struct line_data_union));

    for (i=0; i<size; i++)
    {
      /*      printf("Reading in line %i\n",i);*/
      double j=0, d=0, w=0, q=0, DWfactor=0, F2=0, Epsilon=0;
      int index;

      if (info->Dd >= 0)      w         = info->Dd;
      if (info->DWfactor > 0) DWfactor  = info->DWfactor;
      if (info->Epsilon)      Epsilon   = info->Epsilon*1e-6;

      /* get data from table using columns {j d F2 DW Dd inv2d q F} */
      /* column indexes start at 1, thus need to substract 1 */
      if (info->column_order[0] >0)
        j = Table_Index(sTable, i, info->column_order[0]-1);
      if (info->column_order[1] >0)
        d = Table_Index(sTable, i, info->column_order[1]-1);
      if (info->column_order[2] >0)
        F2 = Table_Index(sTable, i, info->column_order[2]-1);
      if (info->column_order[3] >0)
        DWfactor = Table_Index(sTable, i, info->column_order[3]-1);
      if (info->column_order[4] >0)
        w = Table_Index(sTable, i, info->column_order[4]-1);
      if (info->column_order[5] >0)
        { d = Table_Index(sTable, i, info->column_order[5]-1);
          d = (d > 0? 1/d/2 : 0); }
      if (info->column_order[6] >0)
        { q = Table_Index(sTable, i, info->column_order[6]-1);
          d = (q > 0 ? 2*PI/q : 0); }
      if (info->column_order[7] >0  && !F2)
        { F2 = Table_Index(sTable, i, info->column_order[7]-1); F2 *= F2; }
      if (info->column_order[8] >0  && !Epsilon)
        { Epsilon = Table_Index(sTable, i, info->column_order[8]-1)*1e-6; }

      /* assign and check values */
      j        = (j > 0 ? j : 0);
      q        = (d > 0 ? 2*PI/d : 0); /* this is q */
      if (Epsilon && fabs(Epsilon) < 1e6) {
        q     -= Epsilon*q; /* dq/q = -delta_d_d/d = -Epsilon */
      }
      DWfactor = (DWfactor > 0 ? DWfactor : 1);
      w        = (w>0 ? w : 0); /* this is q and d relative spreading */
      F2       = (F2 >= 0 ? F2 : 0);
      if (j == 0 || q == 0) {
        printf("PowderN: %s: line %i has invalid definition\n"
               "         (mult=0 or q=0 or d=0)\n", info->compname, i);
        continue;
      }
      list[list_count].j = j;
      list[list_count].q = q;
      list[list_count].DWfactor = DWfactor;
      list[list_count].w = w;
      list[list_count].F2= F2;
      list[list_count].Epsilon = Epsilon;

      /* adjust multiplicity if j-column + multiple d-spacing lines */
      /* if  d = previous d, increase line duplication index */
      if (!q_count)      q_count  = q;
      if (!j_count)      j_count  = j;
      if (!F2_count)     F2_count = F2;
      if (fabs(q_count-q) < 0.0001*fabs(q)
       && fabs(F2_count-F2) < 0.0001*fabs(F2) && j_count == j) {
       mult_count++; flag=0; }
      else flag=1;
      if (i == size-1) flag=1;
      /* else if d != previous d : just passed equivalent lines */
      if (flag) {
        if (i == size-1) list_count++;
      /*   if duplication index == previous multiplicity */
      /*      set back multiplicity of previous lines to 1 */
        if ((mult_count && list_count>0)
            && (mult_count == list[list_count-1].j
                || ((list_count < size) && (i == size - 1)
                    && (mult_count == list[list_count].j))) ) {
          printf("PowderN: %s: Set multiplicity to 1 for lines [%i:%i]\n"
                  "         (d-spacing %g is duplicated %i times)\n",
            info->compname, list_count-mult_count, list_count-1, list[list_count-1].q, mult_count);
          for (index=list_count-mult_count; index<list_count; list[index++].j = 1);
          mult_count = 1;
          q_count   = q;
          j_count   = j;
          F2_count  = F2;
        }
        if (i == size-1) list_count--;
        flag=0;
      }
      list_count++;
    } /* end for */ 
    
    Table_Free(&sTable);
    
    /* sort the list with increasing q */
    qsort(list, list_count, sizeof(struct line_data_union),  PN_list_compare_union);
    
    printf("PowderN: %s: Read %i reflections from file '%s'\n", 
      info->compname, list_count, SC_file);
    
    info->list  = list;
    info->count = list_count;

    return(list_count);
  } /* read_line_data_union */


/* computes the number of possible reflections (return value), and the total xsection 'sum' */
/* this routine looks for a pre-computed value in the Nq and sum cache tables               */
/* when found, the earch starts from the corresponding lower element in the table           */
int calc_xsect_union(double v, double *qv, double *my_sv2, int count, double *sum,
  struct line_info_struct_union *line_info) {
  int    Nq = 0, line=0, line0=0;
  *sum=0;
  
  //printf("Line_info when entering cross_section calculation\n");
  //printf("v = %f, qv = %f, my_sv2 = %f, count = %d, sum = %f\n",v,*qv,*my_sv2,count,*sum);
  //printf("v = %f\n",v);
  //printf("line_info->v = %f, line_info->v_min = %f, line_info->v_max = %f, line_info->neutron_passed = %f\n",line_info->v,line_info->v_min,line_info->v_max,line_info->neutron_passed);
  //printf("line_info->xs_reuses = %d, line_info->xs_compute = %d\n",line_info->xs_reuse,line_info->xs_compute);
  
  
  /* check if a line_info element has been recorded already */
  if (v >= line_info->v_min && v <= line_info->v_max && line_info->neutron_passed >= CHAR_BUF_LENGTH) {
    line = (int)floor(v - line_info->v_min)*CHAR_BUF_LENGTH/(line_info->v_max - line_info->v_min);
    Nq    = line_info->xs_Nq[line];
    *sum  = line_info->xs_sum[line];
    if (!Nq && *sum == 0) {
      /* not yet set: we compute the sum up to the corresponding speed in the table cache */
      //printf("Nq and sum not yet set, have to do this calculation now\n");
      double line_v = line_info->v_min + line*(line_info->v_max - line_info->v_min)/CHAR_BUF_LENGTH;
      for(line0=0; line0<count; line0++) {
        if (qv[line0] <= 2*line_v) { /* q < 2*kf: restrict structural range */
          *sum += my_sv2[line0];
          if (Nq < line0+1) Nq=line0+1; /* determine maximum line index which can scatter */
        } else break;
      }
      line_info->xs_Nq[line] = Nq;
      line_info->xs_sum[line]= *sum;
      line_info->xs_compute++;
      //printf("line_info->xs_Nq[line] = %f, line_info->xs_sum[line] = %f, line_info->xs_compute = %d\n",line_info->xs_Nq[line],line_info->xs_sum[line],line_info->xs_compute);
    } else line_info->xs_reuse++;
    line0 = Nq;
  }
  
  line_info->xs_calls++;
  
  for(line=line0; line<count; line++) {
    if (qv[line] <= 2*v) { /* q < 2*kf: restrict structural range */
      *sum += my_sv2[line];
      if (Nq < line+1) Nq=line+1; /* determine maximum line index which can scatter */
    } else break;
  }
  
  //printf("cross_section function to return %d lines to scatter with, with cross section sum %f \n",Nq,*sum);
  return(Nq);
} /* calc_xsect_union */

#endif /* !POWDERN_DECL */



struct Powder_physics_storage_struct{
    // Variables that needs to be transfered between any of the following places:
    // The initialize in this component
    // The function for calculating my
    // The function for calculating scattering
    
    struct line_info_struct_union *line_info_storage;
    double my_scattering;
    double vertical_angular_limit;
};

// Obsolete: Function for initializing test_physics. Done in component instead.
int Powder_physics_initialize(union data_transfer_union data_transfer) {
      // Obsolte
      return 1;
};

// Function for calculating my in a test case.
int Powder_physics_my(double *my,double *k_initial, union data_transfer_union data_transfer, struct focus_data_struct *focus_data, _class_particle *_particle) {
    //*my = data_transfer.pointer_to_a_Powder_physics_storage_struct->my_scattering;
    
    
    int method_switch = 1;
    // For test
    int line_v,line0,line,count;
    
    // Should not interfer with the global variables
    double vx = k_initial[0]*K2V;
    double vy = k_initial[1]*K2V;
    double vz = k_initial[2]*K2V;
    
    // Not sure one can do this, but I do not see why not
    struct line_info_struct_union *line_info = data_transfer.pointer_to_a_Powder_physics_storage_struct->line_info_storage;
    
    double v = sqrt(vx*vx + vy*vy + vz*vz);
    //printf("Velocity = %f \n",v);
    
    //printf("line_info->v = %f, line_info->v_min = %f, line_info->v_max = %f, line_info->neutron_passed = %f\n",line_info->v,line_info->v_min,line_info->v_max,line_info->neutron_passed);
    // Here the maximum and minimum v is recorded, should this be for scattering events or cross section calculations?
    if (line_info->neutron_passed < CHAR_BUF_LENGTH) {
      if (v < line_info->v_min) line_info->v_min = v;
      if (v > line_info->v_max) line_info->v_max = v;
      line_info->neutron_passed++;
    }
    
    if (method_switch == 1) {
    // Here the cross section is calculated and stored
    if ( fabs(v - line_info->v) < 1e-6) {
        line_info->nb_reuses++;
      } else {
        //printf("calling crosssection calculation \n");
        // int calc_xsect_union(double v, double *qv, double *my_sv2, int count, double *sum, struct line_info_struct *line_info)
        line_info->Nq = calc_xsect_union(v, line_info->q_v, line_info->my_s_v2, line_info->count, &line_info->my_s_v2_sum, line_info);
        line_info->v = v;
        line_info->nb_refl += line_info->Nq;
        line_info->nb_refl_count++;
      }
    } else {
    if ( fabs(v - line_info->v) < 1e-6) {
        line_info->nb_reuses++;
      } else {
        //printf("calling crosssection calculation \n");
        if (v >= line_info->v_min && v <= line_info->v_max && line_info->neutron_passed >= CHAR_BUF_LENGTH) {
        line = (int)floor(v - line_info->v_min)*CHAR_BUF_LENGTH/(line_info->v_max - line_info->v_min);
        line_info->Nq = line_info->xs_Nq[line];
        line_info->my_s_v2_sum  = line_info->xs_sum[line];
        if (!line_info->Nq && line_info->my_s_v2_sum == 0) {
          /* not yet set: we compute the sum up to the corresponding speed in the table cache */
          //printf("Nq and sum not yet set, have to do this calculation now\n");
          double line_v = line_info->v_min + line*(line_info->v_max - line_info->v_min)/CHAR_BUF_LENGTH;
          for(line0=0; line0<count; line0++) {
            if (line_info->q_v[line0] <= 2*line_v) { /* q < 2*kf: restrict structural range */
              line_info->my_s_v2_sum += line_info->my_s_v2[line0];
              if (line_info->Nq < line0+1) line_info->Nq=line0+1; /* determine maximum line index which can scatter */
            } else break;
          }
          line_info->xs_Nq[line] = line_info->Nq;
          line_info->xs_sum[line]= line_info->my_s_v2_sum;
          line_info->xs_compute++;
          //printf("line_info->xs_Nq[line] = %f, line_info->xs_sum[line] = %f, line_info->xs_compute = %d\n",line_info->xs_Nq[line],line_info->xs_sum[line],line_info->xs_compute);
        } else line_info->xs_reuse++;
        line0 = line_info->Nq;
        }
          
        line_info->xs_calls++;
          
        for(line=line0; line<count; line++) {
          if (line_info->q_v[line] <= 2*v) { /* q < 2*kf: restrict structural range */
            line_info->my_s_v2_sum += line_info->my_s_v2[line];
            if (line_info->Nq < line+1) line_info->Nq=line+1; /* determine maximum line index which can scatter */
          } else break;
        }
        line_info->v = v;
        line_info->nb_refl += line_info->Nq;
        line_info->nb_refl_count++;
      }
    }
    
     *my = line_info->my_s_v2_sum/(v*v);
    //printf("Returned my scattering of %f \n",*my);
    //printf("compute = %d and reuse = %d \n",line_info->xs_compute,line_info->xs_reuse);
    
    return 1;
};

// Function that provides a basic nonuniform elastic scattering. Unphysical for testing purposes.
int Powder_physics_scattering(double *k_final, double *k_initial, double *weight, union data_transfer_union data_transfer, struct focus_data_struct *focus_data, _class_particle *_particle) {

    // This component need to write to its storage transfer for each event, is that possible with this structure?
    struct line_info_struct_union *line_info = data_transfer.pointer_to_a_Powder_physics_storage_struct->line_info_storage;
    double vertical_angular_limit = data_transfer.pointer_to_a_Powder_physics_storage_struct->vertical_angular_limit;
    
    
    // Should not interfer with the global variables
    double vx = k_initial[0]*K2V;
    double vy = k_initial[1]*K2V;
    double vz = k_initial[2]*K2V;
    
    double v = sqrt(vx*vx + vy*vy + vz*vz);
    
    int line;
    double arg;
    double theta;
    double alpha,alpha0;
    
    double vout_x,vout_y,vout_z;
    double tmp_vx,tmp_vy,tmp_vz;
    double nx,ny,nz;
    double my_s_n;
    
    // copy from PowderN component
    if (line_info->count > 0) {
          /* choose line */
          if (line_info->Nq > 1) line=floor(line_info->Nq*rand01());  /* Select between Nq powder lines */
          else line = 0;
          if (line_info->w_v[line])
            arg = line_info->q_v[line]*(1+line_info->w_v[line]*randnorm())/(2.0*v);
          else
            arg = line_info->q_v[line]/(2.0*v);
            my_s_n = line_info->my_s_v2[line]/(v*v);
          if(fabs(arg) > 1) {
            //printf("Powder scattering function returned 0, should not happen\n");
            return 0; /* No bragg scattering possible (was absorb)*/
          }
          theta = asin(arg);          /* Bragg scattering law */

          /* Choose point on Debye-Scherrer cone */
          if (vertical_angular_limit)
          { /* relate height of detector to the height on DS cone */
            arg = sin(vertical_angular_limit*DEG2RAD/2)/sin(2*theta);
            /* If full Debye-Scherrer cone is within d_phi, don't focus */
            if (arg < -1 || arg > 1) vertical_angular_limit = 0;
            /* Otherwise, determine alpha to rotate from scattering plane
               into vertical_angular_limit focusing area*/
            else alpha = 2*asin(arg);
          }
          if (vertical_angular_limit) {
            /* Focusing */
            alpha = fabs(alpha);
            /* Trick to get scattering for pos/neg theta's */
            alpha0= 2*rand01()*alpha;
            if (alpha0 > alpha) {
              alpha0=PI+(alpha0-1.5*alpha);
            } else {
              alpha0=alpha0-0.5*alpha;
            }
          }
          else
            alpha0 = PI*randpm1();

          /* now find a nearly vertical rotation axis:
           * Either
           *  (v along Z) x (X axis) -> nearly Y axis
           * Or
           *  (v along X) x (Z axis) -> nearly Y axis
           */
          if (fabs(scalar_prod(1,0,0,vx/v,vy/v,vz/v)) < fabs(scalar_prod(0,0,1,vx/v,vy/v,vz/v))) {
            nx = 1; ny = 0; nz = 0;
          } else {
            nx = 0; ny = 0; nz = 1;
          }
          vec_prod(tmp_vx,tmp_vy,tmp_vz, vx,vy,vz, nx,ny,nz);

          /* v_out = rotate 'v' by 2*theta around tmp_v: Bragg angle */
          rotate(vout_x,vout_y,vout_z, vx,vy,vz, 2*theta, tmp_vx,tmp_vy,tmp_vz);

          /* tmp_v = rotate v_out by alpha0 around 'v' (Debye-Scherrer cone) */
          rotate(tmp_vx,tmp_vy,tmp_vz, vout_x,vout_y,vout_z, alpha0, vx, vy, vz);
          vx = tmp_vx;
          vy = tmp_vy;
          vz = tmp_vz;
        
          k_final[0] = V2K*vx; k_final[1] = V2K*vy; k_final[2] = V2K*vz;
        
          //*weight *= line_info->Nq*my_s_n; I believe my_s_n is part of the correction for sampling posistion, not to be done here
          *weight *= line_info->Nq*my_s_n/line_info->my_s_v2_sum*v*v;
        
          //printf("my_s_n = %f \n",my_s_n);
        
          // What to do with my_s_n ?
          /*
          pmul  = line_info->Nq*l_full*my_s_n*exp(-(line_info->my_a_v/v+my_s)*(l+l_1))
                                  /(1-(p_inc+p_transmit));
          */
          // Correction in case of vertical_angular_limit focusing - BUT only when d_phi != 0
          if (vertical_angular_limit) *weight *= alpha/PI;
          
        
          line_info->type = 'c';
          line_info->dq = line_info->q_v[line]*V2K;
          
          
        } else {
        /* else transmit <-- No powder lines in file */
        printf("Error, need lines in the PowderN input file\n");
        }


    //printf("Powder scattering function returned 1\n");
    return 1;
};
#ifndef PROCESS_DETECTOR
    #define PROCESS_DETECTOR dummy
#endif

#ifndef PROCESS_POWDER_DETECTOR
    #define PROCESS_POWDER_DETECTOR dummy
#endif

/* Shared user declarations for all components types 'Union_make_material'. */

#ifndef Union
#define Union $Revision: 0.8 $

/*******************************************************************************
*
*  McStas, neutron ray-tracing package
*  Copyright(C) 2007 Risoe National Laboratory.
*
* %I
* Written by: Mads Bertelsen
* Date: 20.08.15
* Version: $Revision: 0.1 $
* Origin: University of Copenhagen
*
* Functions and structure definitons for Union components.
*
******************************************************************************/

// -------------    Definition of data structures   ---------------------------------------------
// GPU
enum shape {
  surroundings,
  box,
  sphere,
  cylinder,
  cone,
  mesh
};

enum process {
  Incoherent,
  Powder,
  Single_crystal,
  AF_HB_1D,
  PhononSimple,
  Texture,
  IncoherentPhonon,
  NCrystal,
  Template
};

struct intersection_time_table_struct {
int num_volumes;
int *calculated;
int *n_elements;
double **intersection_times;
};

struct line_segment{
//struct position point1;
//struct position point2;
Coords point1;
Coords point2;
int number_of_dashes;
};

struct pointer_to_1d_int_list {
int num_elements;
int *elements;
#pragma acc shape(elements[0:num_elements]) init_needed(num_elements)
};

struct pointer_to_1d_double_list {
int num_elements;
double *elements;
#pragma acc shape(elements[0:num_elements]) init_needed(num_elements)
};

struct pointer_to_1d_coords_list {
int num_elements;
Coords *elements;
#pragma acc shape(elements[0:num_elements]) init_needed(num_elements)
};

struct lines_to_draw{
int number_of_lines;
struct line_segment *lines;
#pragma acc shape(lines[0:number_of_lines]) init_needed(number_of_lines)
};

// 2D lists not needed anyway
// struct pointer_to_2d_int_list {
// int n_lists;
// struct pointer_to_1d_int_list *lists;
// }

// Todo: see if the union geometry_parameter_union and other geometry structs can be here
union geometry_parameter_union{
    struct sphere_storage   *p_sphere_storage;
    struct cylinder_storage *p_cylinder_storage;
    struct box_storage      *p_box_storage;
    struct cone_storage     *p_cone_storage;
    struct mesh_storage     *p_mesh_storage;
    // add as many pointers to structs as wanted, without increasing memory footprint.
};


struct rotation_struct{
double x;
double y;
double z;
};

struct focus_data_struct {
Coords Aim;
double angular_focus_width;
double angular_focus_height;
double spatial_focus_width;
double spatial_focus_height;
double spatial_focus_radius;
Rotation absolute_rotation;
// focusing_function creates a vector per selected criteria of focus_data_struct / selected focus function and returns solid angle
void (*focusing_function)(Coords*, double*, struct focus_data_struct*);
//                        v_out  , solid_a,
};

struct focus_data_array_struct
{
struct focus_data_struct *elements;
int num_elements;
#pragma acc shape(elements[0:num_elements]) init_needed(num_elements)
};

struct Detector_3D_struct {
  char title_string[256];
  char string_axis_1[256];
  char string_axis_2[256];
  char string_axis_3[256];
  char Filename[256];
  double D1min;
  double D1max;
  double D2min;
  double D2max;
  double D3min;
  double D3max;
  double bins_1; // McStas uses doubles for bin numbers for some reason
  double bins_2;
  double bins_3;
  double ***Array_N; // McStas uses doubles for number of rays in each bin for some reason
  double ***Array_p;
  double ***Array_p2;
};

struct Detector_2D_struct {
  char title_string[256];
  char string_axis_1[256];
  char string_axis_2[256];
  char Filename[256];
  double D1min;
  double D1max;
  double D2min;
  double D2max;
  double bins_1; // McStas uses doubles for bin numbers for some reason
  double bins_2;
  double **Array_N; // McStas uses doubles for number of rays in each bin for some reason
  double **Array_p;
  double **Array_p2;
};

struct Detector_1D_struct {
  char title_string[256];
  char string_axis[256];
  char string_axis_short[64];
  char string_axis_value[256];
  char Filename[256];
  double min;
  double max;
  double bins; // McStas uses doubles for bin numbers for some reason
  double *Array_N; // McStas uses doubles for number of rays in each bin for some reason
  double *Array_p;
  double *Array_p2;
};


union logger_data_union{
  struct a_2DQ_storage_struct *p_2DQ_storage;
  struct a_2DS_storage_struct *p_2DS_storage;
  struct a_3DS_storage_struct *p_3DS_storage;
  struct a_1D_storage_struct *p_1D_storage;
  struct a_2DS_t_storage_struct *p_2DS_t_storage;
  struct a_2D_kf_storage_struct *p_2D_kf_storage;
  struct a_2D_kf_t_storage_struct *p_2D_kf_t_storage;
  // Additional logger storage structs to be addedd
};

struct logger_with_data_struct {
  int used_elements;
  int allocated_elements;
  struct logger_struct **logger_pointers;
};

// logger_pointer_struct
// contains pointers to the different logger functions and it's data union
struct logger_pointer_set_struct {
  // The logger has two record functions, an active and an inactive. Normally the active one will be to permanent storage,
  //  but if a conditional has been defined, it can switch the two, making the active one recording to temporary, which
  //  can then be filtered based on the future path of the ray
  
  // function input Coords position, k[3], k_old[3], p, p_old, NV, NPV, N, logger_data_union, logger_with_data_struct
  void (*active_record_function)(Coords*, double*, double*, double, double, double, int, int, int, struct logger_struct*, struct logger_with_data_struct*);
  void (*inactive_record_function)(Coords*, double*, double*, double, double, double, int, int, int, struct logger_struct*, struct logger_with_data_struct*);
  
  // A clear temporary data function (for new ray)
  void (*clear_temp)(union logger_data_union*);
  
  // Write temporary to permanent is used when the record to temp function is active, and the condition is met.
  void (*temp_to_perm)(union logger_data_union*);
  
  // Write temporary final_p to permanent is used when the record to temp function is active, and the condition is met
  //  and the final weight is given to be used for all stored events in the logger.
  void (*temp_to_perm_final_p)(union logger_data_union*, double);
  
  // Select which temp_to_perm function to use
  int select_t_to_p; // 1: temp_to_perm, 2: temp_to_perm_final_p
  
};

union abs_logger_data_union{
  struct a_2D_abs_storage_struct *p_2D_abs_storage;
  struct a_1D_abs_storage_struct *p_1D_abs_storage;
  struct a_1D_time_abs_storage_struct *p_1D_time_abs_storage;
  struct a_1D_time_to_lambda_abs_storage_struct *p_1D_time_to_lambda_abs_storage;
  struct a_event_abs_storage_struct *p_event_abs_storage;
  struct a_1D_event_abs_storage_struct *p_1D_event_abs_storage;
  // Additional logger storage structs to be addedd
};

struct abs_logger_with_data_struct {
  int used_elements;
  int allocated_elements;
  struct abs_logger_struct **abs_logger_pointers;
};

struct abs_logger_pointer_set_struct {
  // The logger has two record functions, an active and an inactive. Normally the active one will be to permanent storage,
  //  but if a conditional has been defined, it can switch the two, making the active one recording to temporary, which
  //  can then be filtered based on the future path of the ray
  
  // function input Coords position, k[3], p, NV, N, logger_data_union, logger_with_data_struct
  void (*active_record_function)(Coords*, double*, double,  double, int, int, struct abs_logger_struct*, struct abs_logger_with_data_struct*);
  void (*inactive_record_function)(Coords*, double*, double,  double, int, int, struct abs_logger_struct*, struct abs_logger_with_data_struct*);
  
  // A clear temporary data function (for new ray)
  void (*clear_temp)(union abs_logger_data_union*);
  
  // Write temporary to permanent is used when the record to temp function is active, and the condition is met.
  void (*temp_to_perm)(union abs_logger_data_union*);
  
  // Write temporary final_p to permanent is used when the record to temp function is active, and the condition is met
  //  and the final weight is given to be used for all stored events in the logger.
  void (*temp_to_perm_final_p)(union abs_logger_data_union*, double);
  
  // Select which temp_to_perm function to use
  //int select_t_to_p; // 1: temp_to_perm, 2: temp_to_perm_final_p
  
};


struct conditional_standard_struct{
  // Data to be transfered to the conditional function
  double Emax;
  double Emin;
  int E_limit;
  
  double Tmin;
  double Tmax;
  int T_limit;
  
  int volume_index;
  
  double Total_scat_max;
  double Total_scat_min;
  int Total_scat_limit;
  
  double exit_volume_index;
  
  // Test
  Coords test_position;
  Rotation test_rotation;
  Rotation test_t_rotation;
};

struct conditional_PSD_struct{
  double PSD_half_xwidth;
  double PSD_half_yheight;
  
  double Tmin;
  double Tmax;
  int T_limit;
  
  // Position of the PSD
  Coords PSD_position;
  Rotation PSD_rotation;
  Rotation PSD_t_rotation;
};

union conditional_data_union {
  struct conditional_standard_struct *p_standard;
  struct conditional_PSD_struct *p_PSD;
  // Add more as conditional components are made
};

// General input for conditional functions: Position, Velocity/Wavevector, weight, time, total_scat, scattered_flag, scattered_flag_VP,
// Optional extras: data_union? tree base(s)? tree base for current ray?
typedef int (*conditional_function_pointer)(union conditional_data_union*,Coords*, Coords*, double*, double*, int*, int*, int*, int**);
//typedef int (**conditional_function_pointer_array)(union *conditional_data_union,Coords*, Coords*, double*, double*, int*, int*, int**);

struct conditional_list_struct{
  int num_elements;
  
  union conditional_data_union **p_data_unions;
  conditional_function_pointer *conditional_functions;
  //int (**conditional_functions)(Coords*, Coords*, double*, double*, int*, int*, int**);
};

struct logger_struct {
  char name[256];
  // Contains ponters to all the functions assosiated with this logger
  struct logger_pointer_set_struct function_pointers;
  // Contains hard copy of logger_data_union since the size is the same as a pointer.
  union logger_data_union data_union;
  
  int logger_extend_index; // Contain index conditional_extend_array defined in master that can be acsessed from extend section.
  
  struct conditional_list_struct conditional_list;
};


// To be stored in volume, a list of pointers to the relevant loggers corresponding to each process
struct logger_for_each_process_list {
  int num_elements;
  struct logger_struct **p_logger_process;
};

// List of logger_for_each_process_list
struct loggers_struct {
  int num_elements;
  struct logger_for_each_process_list *p_logger_volume;
  #pragma acc shape(p_logger_volume[0:num_elements]) init_needed(num_elements)
};


struct abs_logger_struct {
  char name[256];
  // Contains ponters to all the functions assosiated with this logger
  struct abs_logger_pointer_set_struct function_pointers;
  // Contains hard copy of logger_data_union since the size is the same as a pointer.
  union abs_logger_data_union data_union;
  
  // Position and rotation of the abs_logger
  Coords position;
  Rotation rotation;
  Rotation t_rotation;
  
  int abs_logger_extend_index; // Contain index conditional_extend_array defined in master that can be acsessed from extend section.
  
  struct conditional_list_struct conditional_list;
};

// To be stored in volume, a list of pointers to the relevant abs loggers corresponding to each process
/*
struct abs_logger_for_each_process_list {
  int num_elements;
  struct abs_logger_struct **p_abs_logger_process;
};
*/

// List of abs logger_for_each_process_list
struct abs_loggers_struct {
  int num_elements;
  //struct abs_logger_for_each_process_list *p_abs_logger_volume;
  struct abs_logger_struct **p_abs_logger;
};



struct geometry_struct
{
char shape[64];     // name of shape used (sphere, cylinder, box, off, ...)
enum shape eShape;  // enum with shape for flexible functions GPU
double priority_value;    // priority of the geometry
Coords center;      // Center position of volume, reported by components in global frame, updated to main frame in initialize
// Rotation of this volume
Rotation rotation_matrix; // rotation matrix of volume, reported by component in global frame, updated to main frame in initialize
Rotation transpose_rotation_matrix; // As above
// Array of prrotation matrixes for processes assigned to this volume (indexed by non_isotropic_rot_index in the processes)
Rotation *process_rot_matrix_array;             // matrix that transforms from main coordinate system to local process in this specific volume
Rotation *transpose_process_rot_matrix_array;   // matrix that transforms from local process in this specific volume to main coordinate system
int process_rot_allocated;  // Keeps track of allocation status of rot_matrix_array

struct rotation_struct rotation; // Not used, is the x y and z rotation angles.
int visualization_on; // If visualization_on is true, the volume will be drawn in mcdisplay, otherwise not
int is_exit_volume; // If is exit volume = 1, the ray will exit the component when it enters this volume.
int is_mask_volume; // 1 if volume itself is a mask (masking the ones in it's mask list), otherwise 0
int mask_index;
int is_masked_volume; // 1 if this volume is being masked by another volume, the volumes that mask it is in masked_by_list
int mask_mode; // ALL/ANY 1/2. In ALL mode, only parts covered by all masks is simulated, in ANY mode, area covered by just one mask is simulated
double geometry_p_interact; // fraction of rays that interact with this volume for each scattering (between 0 and 1, 0 for disable)
union geometry_parameter_union geometry_parameters; // relevant parameters for this shape
union geometry_parameter_union (*copy_geometry_parameters)(union geometry_parameter_union*);
struct focus_data_struct focus_data; // Used for focusing from this geometry

// New focus data implementation to remove the focusing bug for non-isotripic processes
struct focus_data_array_struct focus_data_array;
struct pointer_to_1d_int_list focus_array_indices; // Add 1D integer array with indecies for correct focus_data for each process


// intersect_function takes position/velocity of ray and parameters, returns time list
int (*intersect_function)(double*,int*,double*,double*,struct geometry_struct*);
//                        t_array,n_ar,r      ,v

// within_function that checks if the ray origin is within this volume
int (*within_function)(Coords,struct geometry_struct*);
//                     r,      parameters

// mcdisplay function, draws the geometry
//void (*mcdisplay_function)(struct lines_to_draw*,int,struct Volume_struct**,int);
void (*mcdisplay_function)(struct lines_to_draw*,int,struct geometry_struct**,int);
//                                lines          index             Geometries   N

void (*initialize_from_main_function)(struct geometry_struct*);

struct pointer_to_1d_coords_list (*shell_points)(struct geometry_struct*, int maximum_number_of_points);

// List of other volumes to be check when ray starts within this volume.
struct pointer_to_1d_int_list intersect_check_list;
// List of other volumes the ray may enter, if the ray intersects the volume itself.
struct pointer_to_1d_int_list destinations_list;
// The destinations list stored as a logic list which makes some tasks quicker. OBSOLETE
//struct pointer_to_1d_int_list destinations_logic_list;
// Reduced list of other volumes the ray may enter, if the ray intersects the volume itself.
struct pointer_to_1d_int_list reduced_destinations_list;
// List of other volumes that are within this volume
struct pointer_to_1d_int_list children;
// List of other volumes that are within this volume, but does not have any parents that are children of this volume
struct pointer_to_1d_int_list direct_children;
// List of next possible volumes (only used in tagging)
struct pointer_to_1d_int_list next_volume_list;
// List of volumes masked by this volume (usually empty)
struct pointer_to_1d_int_list mask_list;
// List of volumes masking this volume
struct pointer_to_1d_int_list masked_by_list;
// List of masks masking this volume (global mask indices)
struct pointer_to_1d_int_list masked_by_mask_index_list;
// Additional intersect lists dependent on mask status
//struct indexed_mask_lists_struct mask_intersect_lists;
// Simpler way of storing the mask_intersect_lists
struct pointer_to_1d_int_list mask_intersect_list;
};

struct physics_struct
{
char name[256]; // User defined material name
int interact_control;
int is_vacuum;
double my_a;
int number_of_processes;
// pointer to array of pointers to physics_sub structures that each describe a scattering process
struct scattering_process_struct *p_scattering_array;
};

union data_transfer_union{
    // List of pointers to storage structs for all supported physical processes
    struct Incoherent_physics_storage_struct  *pointer_to_a_Incoherent_physics_storage_struct;
    struct Powder_physics_storage_struct *pointer_to_a_Powder_physics_storage_struct;
    struct Single_crystal_physics_storage_struct *pointer_to_a_Single_crystal_physics_storage_struct;
    struct AF_HB_1D_physics_storage_struct *pointer_to_a_AF_HB_1D_physics_storage_struct;
    struct IncoherentPhonon_physics_storage_struct *pointer_to_a_IncoherentPhonon_physics_storage_struct;
    struct PhononSimpleNumeric_physics_storage_struct *pointer_to_a_PhononSimpleNumeric_storage_struct;
    struct PhononSimple_physics_storage_struct *pointer_to_a_PhononSimple_storage_struct;
    struct MagnonSimple_physics_storage_struct *pointer_to_a_MagnonSimple_storage_struct;
    struct Sans_spheres_physics_storage_struct *pointer_to_a_Sans_spheres_physics_storage_struct;
    struct Texture_physics_storage_struct *pointer_to_a_Texture_physics_storage_struct;
    struct NCrystal_physics_storage_struct *pointer_to_a_NCrystal_physics_storage_struct;
    struct Non_physics_storage_struct *pointer_to_a_Non_physics_storage_struct;
    struct Template_physics_storage_struct *pointer_to_a_Template_physics_storage_struct;
    // possible to add as many structs as wanted, without increasing memory footprint.
};



struct scattering_process_struct
{
char name[256];                // User defined process name
enum process eProcess;         // enum value corresponding to this process GPU
double process_p_interact;     // double between 0 and 1 that describes the fraction of events forced to undergo this process. -1 for disable
int non_isotropic_rot_index;   // -1 if process is isotrpic, otherwise is the index of the process rotation matrix in the volume
Rotation rotation_matrix;      // rotation matrix of process, reported by component in local frame, transformed and moved to volume struct in main

union data_transfer_union data_transfer; // The way to reach the storage space allocated for this process (see examples in process.comp files)

// probability_for_scattering_functions calculates this probability given k_i and parameters
int (*probability_for_scattering_function)(double*,double*,union data_transfer_union,struct focus_data_struct*, _class_particle *_particle);
//                                         prop,   k_i,   ,parameters               , focus data / function

// A scattering_function takes k_i and parameters, returns k_f
int (*scattering_function)(double*,double*,double*,union data_transfer_union,struct focus_data_struct*, _class_particle *_particle);
//                         k_f,    k_i,    weight, parameters               , focus data / function
};

struct Volume_struct
{
char name[256]; // User defined volume name
struct geometry_struct geometry;        // Geometry properties (including intersect functions, generated lists)
struct physics_struct *p_physics;       // Physical properties (list of scattering processes, absorption)
struct loggers_struct loggers;          // Loggers assosiated with this volume
struct abs_loggers_struct abs_loggers;  // Loggers assosiated with this volume
};

// example of calling a scattering process
// volume_pointer_list[3]->physics.scattering_process[5].probability_for_scattering_function(input,volume_pointer_list[3]->physics.scattering_process[5])

struct starting_lists_struct
{
struct pointer_to_1d_int_list allowed_starting_volume_logic_list;
struct pointer_to_1d_int_list reduced_start_list;
struct pointer_to_1d_int_list start_logic_list;
struct pointer_to_1d_int_list starting_destinations_list;
};

struct global_positions_to_transform_list_struct {
int num_elements;
Coords **positions;
};

struct global_rotations_to_transform_list_struct {
int num_elements;
Rotation **rotations;
};

struct global_process_element_struct
{
char name[128]; // Name of the process
int component_index;
struct scattering_process_struct *p_scattering_process;
};

struct pointer_to_global_process_list {
int num_elements;
struct global_process_element_struct *elements;
};

struct global_material_element_struct
{
char name[128];
int component_index;
struct physics_struct *physics;
};

struct pointer_to_global_material_list {
int num_elements;
struct global_material_element_struct *elements;
};

struct global_geometry_element_struct
{
char name[128];
int component_index;
int activation_counter;
int stored_copies;
int active;
struct Volume_struct *Volume;
};

struct pointer_to_global_geometry_list {
int num_elements;
struct global_geometry_element_struct *elements;
};

struct global_logger_element_struct {
char name[128];
int component_index;
struct logger_struct *logger;
};

struct pointer_to_global_logger_list {
int num_elements;
struct global_logger_element_struct *elements;
};

struct global_abs_logger_element_struct {
char name[128];
int component_index;
struct abs_logger_struct *abs_logger;
};

struct pointer_to_global_abs_logger_list {
int num_elements;
struct global_abs_logger_element_struct *elements;
};

struct global_tagging_conditional_element_struct {
struct conditional_list_struct conditional_list;
int extend_index;
char name[1024];
int use_status;
};

struct global_tagging_conditional_list_struct {
int num_elements;
int current_index;
struct global_tagging_conditional_element_struct *elements;
};


struct global_master_element_struct {
char name[128];
int component_index;
int stored_number_of_scattering_events; // TEST
struct conditional_list_struct *tagging_conditional_list_pointer;
};

struct pointer_to_global_master_list {
int num_elements;
struct global_master_element_struct *elements;
};


// -------------    Physics functions   ---------------------------------------------------------

//#include "Test_physics.c"
//#include "Incoherent_test.c"

// -------------    General functions   ---------------------------------------------------------
double distance_between(Coords position1,Coords position2) {
    return sqrt((position1.x-position2.x)*(position1.x-position2.x) +
                (position1.y-position2.y)*(position1.y-position2.y) +
                (position1.z-position2.z)*(position1.z-position2.z));
};



double length_of_3vector(double *r) {
        return sqrt(r[0]*r[0]+r[1]*r[1]+r[2]*r[2]);
    };

double length_of_position_vector(Coords point) {
        return sqrt(point.x*point.x+point.y*point.y+point.z*point.z);
    };

Coords make_position(double *r) {
    Coords temp;
    
    temp.x = r[0];temp.y = r[1];temp.z = r[2];
    return temp;
};

Coords coords_scalar_mult(Coords input,double scalar) {
    return coords_set(scalar*input.x,scalar*input.y,scalar*input.z);
};

double union_coords_dot(Coords vector1,Coords vector2) {
    return vector1.x*vector2.x + vector1.y*vector2.y + vector1.z*vector2.z;
}


int sum_int_list(struct pointer_to_1d_int_list list) {
    int iterate,sum = 0;
    for (iterate = 0;iterate < list.num_elements;iterate++) sum += list.elements[iterate];
    return sum;
    };
    
int on_int_list(struct pointer_to_1d_int_list list,int target) {
    int iterate,output=0;
    for (iterate = 0; iterate<list.num_elements ;iterate++) {
        if (list.elements[iterate] == target) output = 1;
    }
    return output;
    };

int find_on_int_list(struct pointer_to_1d_int_list list,int target) {
    int iterate;
    for (iterate = 0;iterate < list.num_elements;iterate++) {
        if (list.elements[iterate] == target) return iterate;
    }
    return -1;
    };
    
void on_both_int_lists(struct pointer_to_1d_int_list *list1, struct pointer_to_1d_int_list *list2, struct pointer_to_1d_int_list *common) {
    // Assume common.elements is allocated to a number of int's large enough to hold the resulting list
    int iterate,used_elements=0;
    for (iterate=0;iterate<list1->num_elements;iterate++) {
        if (on_int_list(*list2,list1->elements[iterate])) common->elements[used_elements++] = list1->elements[iterate];
    }
    common->num_elements = used_elements;
    };

void remove_element_in_list_by_index(struct pointer_to_1d_int_list *list,int index) {
    if (index >= list->num_elements) {
      printf("ERROR(remove_element_in_list_by_index): trying to remove an index that wasn't allocated to begin with");
      exit(1);
    }
    else {
        int iterate;
        int *temp;
        for (iterate = index;iterate < list->num_elements -1;iterate++) {
            list->elements[iterate] = list->elements[iterate+1];
        }
        list->num_elements--;
        //if (list->num_elements==0) printf("Making empty list!\n");
        temp = malloc(list->num_elements * sizeof(int));
        for (iterate = 0;iterate < list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
        free(list->elements);
        list->elements = malloc(list->num_elements * sizeof(int));
        for (iterate = 0;iterate < list->num_elements;iterate++) list->elements[iterate] = temp[iterate];
        free(temp);

    }
    };
    
void remove_element_in_list_by_value(struct pointer_to_1d_int_list *list,int value) {
    int iterate;
    for (iterate = 0;iterate < list->num_elements;iterate++) {
        if (list->elements[iterate] == value) remove_element_in_list_by_index(list,iterate);
    }
    };
    
void merge_lists(struct pointer_to_1d_int_list *result,struct pointer_to_1d_int_list *list1,struct pointer_to_1d_int_list *list2) {
    if (result->num_elements > 0) free(result->elements);
    result->num_elements = list1->num_elements + list2->num_elements;
    if (result->num_elements != 0) {
      result->elements = malloc(result->num_elements*sizeof(int));
      int iterate;
      for (iterate = 0;iterate < list1->num_elements;iterate++)
          result->elements[iterate] = list1->elements[iterate];
      for (iterate = 0;iterate < list2->num_elements;iterate++)
          result->elements[list1->num_elements+iterate] = list2->elements[iterate];
    }
    };

void add_element_to_double_list(struct pointer_to_1d_double_list *list,double value) {
    if (list->num_elements == 0) {
      list->num_elements++;
      list-> elements = malloc(list->num_elements*sizeof(double));
      list-> elements[0] = value;
    } else {
      double temp[list->num_elements];
      int iterate;
      for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
      free(list->elements);
      list->num_elements++;
      list-> elements = malloc(list->num_elements*sizeof(double));
      for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
      list->elements[list->num_elements-1] = value;
    }
    };

void add_element_to_int_list(struct pointer_to_1d_int_list *list,int value) {
    if (list->num_elements == 0) {
      list->num_elements++;
      list-> elements = malloc(list->num_elements*sizeof(int));
      list-> elements[0] = value;
    } else {
      int temp[list->num_elements];
      int iterate;
      for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
      free(list->elements);
      list->num_elements++;
      list-> elements = malloc(list->num_elements*sizeof(int));
      for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
      list->elements[list->num_elements-1] = value;
    }
    };

// Need to check if absolute_rotation is preserved correctly.
void add_element_to_focus_data_array(struct focus_data_array_struct *focus_data_array,struct focus_data_struct focus_data) {
    if (focus_data_array->num_elements == 0) {
      focus_data_array->num_elements++;
      focus_data_array-> elements = malloc(focus_data_array->num_elements*sizeof(struct focus_data_struct));
      focus_data_array-> elements[0] = focus_data;
    } else {
      struct focus_data_struct temp[focus_data_array->num_elements];
      int iterate;
      for (iterate=0;iterate<focus_data_array->num_elements;iterate++) temp[iterate] = focus_data_array->elements[iterate];
      free(focus_data_array->elements);
      focus_data_array->num_elements++;
      focus_data_array-> elements = malloc(focus_data_array->num_elements*sizeof(struct focus_data_struct));
      for (iterate=0;iterate<focus_data_array->num_elements-1;iterate++) focus_data_array->elements[iterate] = temp[iterate];
      focus_data_array->elements[focus_data_array->num_elements-1] = focus_data;
    }
    };


void add_to_logger_with_data(struct logger_with_data_struct *logger_with_data, struct logger_struct *logger) {
    // May reorder the order of the if conditions to avoid checking the == 0 for every single ray
    if (logger_with_data->allocated_elements == 0) {
        logger_with_data->allocated_elements = 5;
        logger_with_data->logger_pointers = malloc(logger_with_data->allocated_elements*sizeof(struct logger_struct*));
        logger_with_data->used_elements = 1;
        logger_with_data->logger_pointers[0] = logger;
    } else if (logger_with_data->used_elements > logger_with_data->allocated_elements-1) {
        struct logger_with_data_struct temp_logger_with_data;
        temp_logger_with_data.logger_pointers = malloc((logger_with_data->used_elements)*sizeof(struct logger_struct*));
        int iterate;
        for (iterate=0;iterate<logger_with_data->used_elements;iterate++) {
            temp_logger_with_data.logger_pointers[iterate]  = logger_with_data->logger_pointers[iterate];
        }
        free(logger_with_data->logger_pointers);
        logger_with_data->allocated_elements = logger_with_data->allocated_elements+5;
        logger_with_data->logger_pointers = malloc(logger_with_data->allocated_elements*sizeof(struct logger_struct*));
        for (iterate=0;iterate<logger_with_data->used_elements;iterate++) {
            logger_with_data->logger_pointers[iterate]  = temp_logger_with_data.logger_pointers[iterate];
        }

        logger_with_data->logger_pointers[logger_with_data->used_elements++] = logger;
        
        
    } else {
        logger_with_data->logger_pointers[logger_with_data->used_elements++] = logger;
    }
    
};

void add_to_abs_logger_with_data(struct abs_logger_with_data_struct *abs_logger_with_data, struct abs_logger_struct *abs_logger) {
    // May reorder the order of the if conditions to avoid checking the == 0 for every single ray
    if (abs_logger_with_data->allocated_elements == 0) {
        abs_logger_with_data->allocated_elements = 5;
        abs_logger_with_data->abs_logger_pointers = malloc(abs_logger_with_data->allocated_elements*sizeof(struct abs_logger_struct*));
        abs_logger_with_data->used_elements = 1;
        abs_logger_with_data->abs_logger_pointers[0] = abs_logger;
    } else if (abs_logger_with_data->used_elements > abs_logger_with_data->allocated_elements-1) {
        struct abs_logger_with_data_struct temp_abs_logger_with_data;
        temp_abs_logger_with_data.abs_logger_pointers = malloc((abs_logger_with_data->used_elements)*sizeof(struct abs_logger_struct*));
        int iterate;
        for (iterate=0;iterate<abs_logger_with_data->used_elements;iterate++) {
            temp_abs_logger_with_data.abs_logger_pointers[iterate]  = abs_logger_with_data->abs_logger_pointers[iterate];
        }
        free(abs_logger_with_data->abs_logger_pointers);
        abs_logger_with_data->allocated_elements = abs_logger_with_data->allocated_elements+5;
        abs_logger_with_data->abs_logger_pointers = malloc(abs_logger_with_data->allocated_elements*sizeof(struct abs_logger_struct*));
        for (iterate=0;iterate<abs_logger_with_data->used_elements;iterate++) {
            abs_logger_with_data->abs_logger_pointers[iterate] = temp_abs_logger_with_data.abs_logger_pointers[iterate];
        }

        abs_logger_with_data->abs_logger_pointers[abs_logger_with_data->used_elements++] = abs_logger;
        
        
    } else {
        abs_logger_with_data->abs_logger_pointers[abs_logger_with_data->used_elements++] = abs_logger;
    }
    
};


// Used typedef to avoid having to change this function later. May update others to use same phillosphy.
void add_function_to_conditional_list(struct conditional_list_struct *list,conditional_function_pointer new, union conditional_data_union *data_union) {
    if (list->num_elements == 0) {
      list->num_elements++;
      list->conditional_functions = malloc(list->num_elements*sizeof(conditional_function_pointer));
      list->p_data_unions = malloc(list->num_elements*sizeof(union conditional_data_union*));
      list->conditional_functions[0] = new;
      list->p_data_unions[0] = data_union;
    }
    else {
    conditional_function_pointer temp_fp[list->num_elements];
    union conditional_data_union *temp_du[list->num_elements];
    int iterate;
    // Could even get away with a shallow copy here instead of the loop, but it is not relevant for performance.
    for (iterate=0;iterate<list->num_elements;iterate++) {
      temp_fp[iterate] = list->conditional_functions[iterate];
      temp_du[iterate] = list->p_data_unions[iterate];
    }
    free(list->conditional_functions);
    free(list->p_data_unions);
    list->num_elements++;
    list->conditional_functions = malloc(list->num_elements*sizeof(conditional_function_pointer));
    list->p_data_unions = malloc(list->num_elements*sizeof(union conditional_data_union*));
    
    for (iterate=0;iterate<list->num_elements-1;iterate++) {
      list->conditional_functions[iterate] = temp_fp[iterate];
      list->p_data_unions[iterate] = temp_du[iterate];
    }
    list->conditional_functions[list->num_elements-1] = new;
    list->p_data_unions[list->num_elements-1] = data_union;
    }
};


// could make function that removes a element from a 1d_int_list, and have each list generation as a function that takes a copy of an overlap list

void print_1d_int_list(struct pointer_to_1d_int_list list,char *name) {
        int iterate;
        printf("LIST: ");printf("%s",name);printf(" = [");
        for (iterate = 0; iterate < list.num_elements; iterate++) {
            printf("%d",list.elements[iterate]);
            if (iterate < list.num_elements - 1) printf(",");
        }
        printf("]\n");
    };

void print_1d_double_list(struct pointer_to_1d_double_list list,char *name) {
        int iterate;
        printf("LIST: ");printf("%s",name);printf(" = [");
        for (iterate = 0; iterate < list.num_elements; iterate++) {
            printf("%f",list.elements[iterate]);
            if (iterate < list.num_elements - 1) printf(",");
        }
        printf("]\n");
    };

void print_position(Coords pos,char *name) {
    printf("POSITION: ");printf("%s",name);printf(" = (%f,%f,%f)\n",pos.x,pos.y,pos.z);
    };

void print_rotation(Rotation rot, char *name) {
    printf("ROT MATRIX: %s \n",name);
    printf("[%f %f %f]\n",rot[0][0],rot[0][1],rot[0][2]);
    printf("[%f %f %f]\n",rot[1][0],rot[1][1],rot[1][2]);
    printf("[%f %f %f]\n\n",rot[2][0],rot[2][1],rot[2][2]);
};
    
void allocate_list_from_temp(int num_elements,struct pointer_to_1d_int_list original,struct pointer_to_1d_int_list *new) {
        int iterate;
    
        new->num_elements = num_elements;
        if (num_elements > 0) {
            new->elements = malloc(num_elements*sizeof(int));
            for (iterate = 0;iterate < num_elements; iterate++) new->elements[iterate] = original.elements[iterate];
        } else new->elements = NULL;
    
    };

void allocate_logic_list_from_temp(int num_elements,struct pointer_to_1d_int_list original, struct pointer_to_1d_int_list *new) {
        // A logic list shares the same structure of a normal list, but instead of listing numbers, it is a list of yes / no (1/0)
        // Giving this function a list of [1 3 5] (and num_elements = 9) would return [0 1 0 1 0 1 0 0 0];
        int iterate;
    
        new->num_elements = num_elements;
        if (num_elements > 0) {
            new->elements = malloc(num_elements*sizeof(int));
            for (iterate = 0;iterate < num_elements;iterate++) new->elements[iterate] = 0;
            for (iterate = 0;iterate < original.num_elements;iterate++) {
                if (original.elements[iterate] < num_elements)
                    new->elements[original.elements[iterate]] = 1;
                else printf("Trying to allocate logical list without enough memory\n");
            }
        } else new->elements = NULL;
    };

/*
struct global_positions_to_transform_list_struct {
int num_elements;
Coords **positions;
}

struct global_rotations_to_transform_list_struct {
int num_elements;
Rotation **rotations;
}
*/

void add_position_pointer_to_list(struct global_positions_to_transform_list_struct *list, Coords *new_position_pointer) {
    if (list->num_elements == 0) {
      list->num_elements++;
      list->positions = malloc(list->num_elements*sizeof(Coords*));
      list->positions[0] = new_position_pointer;
    } else {
      Coords **temp;
      temp = malloc(list->num_elements*sizeof(Coords*));
      if (temp == NULL) printf("malloc failed in add_position_pointer_to_list for temp\n");
      
      int iterate;
      for (iterate=0;iterate<list->num_elements;iterate++)
        temp[iterate] = list->positions[iterate];
      free(list->positions);
      list->num_elements++;
      list->positions = malloc(list->num_elements*sizeof(Coords*));
      if (list->positions == NULL) printf("malloc failed in add_position_pointer_to_list for list->positions\n");
      
      for (iterate=0;iterate<list->num_elements-1;iterate++)
        list->positions[iterate] = temp[iterate];
      free(temp);
      list->positions[list->num_elements-1] = new_position_pointer;
    }
};

void add_rotation_pointer_to_list(struct global_rotations_to_transform_list_struct *list, Rotation *new_rotation_pointer) {
    if (list->num_elements == 0) {
      list->num_elements++;
      list->rotations = malloc(list->num_elements*sizeof(Rotation*));
      list->rotations[0] = new_rotation_pointer;
    } else {
      Rotation **temp;
      temp = malloc(list->num_elements*sizeof(Rotation*));
      int iterate;
      for (iterate=0;iterate<list->num_elements;iterate++)
        temp[iterate] = list->rotations[iterate];
      free(list->rotations);
      list->num_elements++;
      list->rotations = malloc(list->num_elements*sizeof(Rotation*));
      
      for (iterate=0;iterate<list->num_elements-1;iterate++)
        list->rotations[iterate] = temp[iterate];
      free(temp);
      list->rotations[list->num_elements-1] = new_rotation_pointer;
    }
};

void add_element_to_process_list(struct pointer_to_global_process_list *list,struct global_process_element_struct new_element) {
    if (list->num_elements == 0) {
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_process_element_struct));
    list-> elements[0] = new_element;
    }
    else {
    struct global_process_element_struct temp[list->num_elements];
    int iterate;
    for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
    free(list->elements);
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_process_element_struct));
    for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
    list->elements[list->num_elements-1] = new_element;
    }
};

void add_element_to_material_list(struct pointer_to_global_material_list *list,struct global_material_element_struct new_element) {
    if (list->num_elements == 0) {
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_material_element_struct));
    list-> elements[0] = new_element;
    }
    else {
    struct global_material_element_struct temp[list->num_elements];
    int iterate;
    for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
    free(list->elements);
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_material_element_struct));
    for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
    list->elements[list->num_elements-1] = new_element;
    }
};

void add_element_to_geometry_list(struct pointer_to_global_geometry_list *list,struct global_geometry_element_struct new_element) {
    if (list->num_elements == 0) {
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_geometry_element_struct));
    list-> elements[0] = new_element;
    }
    else {
    struct global_geometry_element_struct temp[list->num_elements];
    int iterate;
    for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
    free(list->elements);
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_geometry_element_struct));
    for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
    list->elements[list->num_elements-1] = new_element;
    }
};

void add_element_to_logger_list(struct pointer_to_global_logger_list *list,struct global_logger_element_struct new_element) {
    if (list->num_elements == 0) {
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_logger_element_struct));
    list-> elements[0] = new_element;
    }
    else {
    struct global_logger_element_struct temp[list->num_elements];
    int iterate;
    for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
    free(list->elements);
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_logger_element_struct));
    for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
    list->elements[list->num_elements-1] = new_element;
    }
};

void add_element_to_abs_logger_list(struct pointer_to_global_abs_logger_list *list, struct global_abs_logger_element_struct new_element) {
    if (list->num_elements == 0) {
      list->num_elements++;
      list->elements = malloc(list->num_elements*sizeof(struct global_abs_logger_element_struct));
      list->elements[0] = new_element;
    }
    else {
      struct global_abs_logger_element_struct temp[list->num_elements];
      int iterate;
      for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
      free(list->elements);
      list->num_elements++;
      list-> elements = malloc(list->num_elements*sizeof(struct global_abs_logger_element_struct));
      for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
      list->elements[list->num_elements-1] = new_element;
    }
};

void add_element_to_tagging_conditional_list(struct global_tagging_conditional_list_struct *list,struct global_tagging_conditional_element_struct new_element) {
    if (list->num_elements == 0) {
    list->num_elements++;
    list->elements = malloc(list->num_elements*sizeof(struct global_tagging_conditional_element_struct));
    list->elements[0] = new_element;
    }
    else {
    struct global_tagging_conditional_element_struct temp[list->num_elements];
    int iterate;
    for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
    free(list->elements);
    list->num_elements++;
    list->elements = malloc(list->num_elements*sizeof(struct global_tagging_conditional_element_struct));
    for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
    list->elements[list->num_elements-1] = new_element;
    }
};

void add_element_to_master_list(struct pointer_to_global_master_list *list,struct global_master_element_struct new_element) {
    if (list->num_elements == 0) {
    list->num_elements++;
    list->elements = malloc(list->num_elements*sizeof(struct global_master_element_struct));
    list->elements[0] = new_element;
    }
    else {
    struct global_master_element_struct temp[list->num_elements];
    int iterate;
    for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
    free(list->elements);
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_master_element_struct));
    for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
    list->elements[list->num_elements-1] = new_element;
    }
};

void add_initialized_logger_in_volume(struct loggers_struct *loggers,int number_of_processes) {
  int iterate;
  if (loggers->num_elements == 0) {
    loggers->num_elements++;
    loggers->p_logger_volume = malloc(loggers->num_elements * sizeof(struct logger_for_each_process_list));
    loggers->p_logger_volume[0].num_elements = number_of_processes;
    loggers->p_logger_volume[0].p_logger_process = malloc(number_of_processes * sizeof(struct logger_struct**));
    for (iterate=0;iterate<number_of_processes;iterate++)
      loggers->p_logger_volume[0].p_logger_process[iterate] = NULL;
  } else {
    // Already some elements, store them in temp, free main, transfer back and add newest.
    struct logger_for_each_process_list temp[loggers->num_elements];
    
    for (iterate=0;iterate<loggers->num_elements;iterate++) temp[iterate] = loggers->p_logger_volume[iterate];
    free(loggers->p_logger_volume);
    loggers->num_elements++;
    loggers->p_logger_volume = malloc(loggers->num_elements*sizeof(struct logger_for_each_process_list));
    for (iterate=0;iterate<loggers->num_elements-1;iterate++) loggers->p_logger_volume[iterate] = temp[iterate];
    loggers->p_logger_volume[loggers->num_elements-1].num_elements = number_of_processes;
    loggers->p_logger_volume[loggers->num_elements-1].p_logger_process = malloc(number_of_processes * sizeof(struct logger_struct**));
    for (iterate=0;iterate<number_of_processes;iterate++) {
      loggers->p_logger_volume[loggers->num_elements-1].p_logger_process[iterate] = NULL;
    }
  }
};

/*
void add_initialized_abs_logger_in_volume(struct abs_loggers_struct *abs_loggers, int number_of_processes) {
  int iterate;
  if (abs_loggers->num_elements == 0) {
    abs_loggers->num_elements++;
    abs_loggers->p_abs_logger_volume = malloc(abs_loggers->num_elements * sizeof(struct abs_loggers_struct));
    abs_loggers->p_abs_logger_volume[0].num_elements = number_of_processes;
    abs_loggers->p_abs_logger_volume[0].p_abs_logger_process = malloc(number_of_processes * sizeof(struct abs_logger_struct**));
    for (iterate=0;iterate<number_of_processes;iterate++)
      abs_loggers->p_abs_logger_volume[0].p_abs_logger_process[iterate] = NULL;
  } else {
    // Already some elements, store them in temp, free main, transfer back and add newest.
    struct abs_logger_for_each_process_list temp[abs_loggers->num_elements];
    
    for (iterate=0;iterate<abs_loggers->num_elements;iterate++) temp[iterate] = abs_loggers->p_abs_logger_volume[iterate];
    free(abs_loggers->p_abs_logger_volume);
    abs_loggers->num_elements++;
    abs_loggers->p_abs_logger_volume = malloc(abs_loggers->num_elements*sizeof(struct abs_logger_for_each_process_list));
    for (iterate=0;iterate<abs_loggers->num_elements-1;iterate++) abs_loggers->p_abs_logger_volume[iterate] = temp[iterate];
    abs_loggers->p_abs_logger_volume[abs_loggers->num_elements-1].num_elements = number_of_processes;
    abs_loggers->p_abs_logger_volume[abs_loggers->num_elements-1].p_abs_logger_process = malloc(number_of_processes * sizeof(struct abs_logger_struct**));
    for (iterate=0;iterate<number_of_processes;iterate++) {
      abs_loggers->p_abs_logger_volume[abs_loggers->num_elements-1].p_abs_logger_process[iterate] = NULL;
    }
  }
};
*/

void add_initialized_abs_logger_in_volume(struct abs_loggers_struct *abs_loggers) {
  int iterate;
  if (abs_loggers->num_elements == 0) {
    abs_loggers->num_elements++;
    abs_loggers->p_abs_logger = malloc(abs_loggers->num_elements * sizeof(struct abs_logger_struct*));
  } else {
    // Already some elements, store them in temp, free main, transfer back and add newest.
    struct abs_logger_struct *temp[abs_loggers->num_elements];
    
    for (iterate=0;iterate<abs_loggers->num_elements;iterate++) temp[iterate] = abs_loggers->p_abs_logger[iterate];
    free(abs_loggers->p_abs_logger);
    
    abs_loggers->num_elements++;
    abs_loggers->p_abs_logger = malloc(abs_loggers->num_elements*sizeof(struct abs_logger_struct*));
    for (iterate=0;iterate<abs_loggers->num_elements-1;iterate++) abs_loggers->p_abs_logger[iterate] = temp[iterate];
    abs_loggers->p_abs_logger[abs_loggers->num_elements-1] = NULL;
    
  }
};


// -------------    Functions used to shorten master trace    ---------------------------------------------


void update_current_mask_intersect_status(struct pointer_to_1d_int_list *current_mask_intersect_list_status, struct pointer_to_1d_int_list *mask_status_list, struct Volume_struct **Volumes, int *current_volume) {
  // This function is to be executed whenever the current volume changes, or the mask status changes
  // It updates the effective mask status for each element of a volumes mask_intersect_list
  // The effective mask takes the ALL/ANY mode assosiated with each volume into account,
  //  meaning a volume needs to be within ALL/ANY of it's masks to have a status of 1
  // In most cases this mask_intersect_list will be empty, and memory operations are avoided
  //printf("Number of elements to be check: %d \n",Volumes[*current_volume]->geometry.mask_intersect_list.num_elements);
  
  if (Volumes[*current_volume]->geometry.mask_intersect_list.num_elements > 0) {
    int iterate,this_element,*mask_start,*mask_check;
    for (iterate=0;iterate<Volumes[*current_volume]->geometry.mask_intersect_list.num_elements;iterate++) {
      this_element = Volumes[*current_volume]->geometry.mask_intersect_list.elements[iterate];
      //printf("We are investigating volume number %d from the mask_intersect_list of volume %d",this_element,*current_volume);
      if (Volumes[this_element]->geometry.mask_mode == 2) { // ANY mask mode
        //printf("We are in ANY mask mode! \n");
        current_mask_intersect_list_status->elements[iterate] = 0; // Assume the mask status is 0, but if any are 1, take that instead
        for (mask_start=mask_check=Volumes[this_element]->geometry.masked_by_mask_index_list.elements;mask_check-mask_start<Volumes[this_element]->geometry.masked_by_mask_index_list.num_elements;mask_check++) {
          //printf("Checking all the mask statuses of volumes masking %d, now mask with global mask index %d ! \n",this_element,*mask_check);
          if (mask_status_list->elements[*mask_check] == 1) {
            //printf("The status was 1, so the effective status is set to 1 and the loop is stopped");
            current_mask_intersect_list_status->elements[iterate] = 1;
            break;
          }
        }
      } else { // ALL mask mode
        //printf("We are in ALL mask mode! \n");
        current_mask_intersect_list_status->elements[iterate] = 1; // Assume the mask status is 1, but if any one is 0, take that instead
        for (mask_start=mask_check=Volumes[this_element]->geometry.masked_by_mask_index_list.elements;mask_check-mask_start<Volumes[this_element]->geometry.masked_by_mask_index_list.num_elements;mask_check++) {
          //printf("Checking all the mask statuses of volumes masking %d, now mask with global mask index %d ! \n",this_element,*mask_check);
          if (mask_status_list->elements[*mask_check] == 0) {
            //printf("The status was 0, so the effective status is set to 0 and the loop is stopped \n");
            current_mask_intersect_list_status->elements[iterate] = 0;
            break;
          }
        }
      }
    }
  }
    
  /* Original version compatible with trace scope
  for (iterate=0;iterate<Volumes[current_volume]->geometry.mask_intersect_list.num_elements;iterate++) {
    this_element = Volumes[current_volume]->geometry.mask_intersect_list.elements[iterate];
    if (Volumes[this_element]->geometry.mask_mode == 2) { // ANY mask mode
      current_mask_intersect_list_status.elements[iterate] = 0; // Assume the mask status is 0, but if any are 1, take that instead
      for (mask_start=mask_check=Volumes[this_element]->geometry.masked_by_mask_index_list.elements;mask_check-mask_start<Volumes[this_element]->geometry.masked_by_mask_index_list.num_elements;mask_check++) {
        if (mask_status_list[*mask_check] == 1) {
          current_mask_intersect_list_status.elements[iterate] = 1;
          break;
        }
      }
    } else { // ALL mask mode
      current_mask_intersect_list_status.elements[iterate] = 1; // Assume the mask status is 1, but if any one is 0, take that instead
      for (mask_start=mask_check=Volumes[this_element]->geometry.masked_by_mask_index_list.elements;mask_check-mask_start<Volumes[this_element]->geometry.masked_by_mask_index_list.num_elements;mask_check++) {
        if (mask_status_list[*mask_check] == 0) {
          current_mask_intersect_list_status.elements[iterate] = 0;
          break;
        }
      }
    }
  }
  */
}

// -------------    Tagging functions    ------------------------------------------------------------------

struct tagging_tree_node_struct {
  // Statistics:
  double intensity;
  int number_of_rays;
  // tree pointers
  struct tagging_tree_node_struct *above;   // Pointer to node above
  struct tagging_tree_node_struct **volume_branches;
  struct tagging_tree_node_struct **process_branches;
};

struct list_of_tagging_tree_node_pointers {
  struct tagging_tree_node_struct **elements;
  int num_elements;
};

struct tagging_tree_node_struct *make_tagging_tree_node(void) {
    return (struct tagging_tree_node_struct *) malloc(sizeof(struct tagging_tree_node_struct));
}


struct tagging_tree_node_struct  *simple_initialize_tagging_tree_node(struct tagging_tree_node_struct *new_node) {
    //struct tagging_tree_node_struct *dummy;
    //dummy = malloc(sizeof(struct tagging_tree_node_struct));
    //new_node = dummy;
    
    //new_node->element = (struct tagging_tree_node_struct *) malloc(sizeof(struct tagging_tree_node_struct));
    //new_node = (struct tagging_tree_node_struct *) malloc(sizeof(struct tagging_tree_node_struct));
    new_node = make_tagging_tree_node();
    
    if (new_node == NULL) printf("ERROR, Union tagging system could not allocate memory\n");
    new_node->intensity = 4.2; // (double) 4.2;
    new_node->number_of_rays = 42; //(int) 42;
    
    printf("new_node->intensity = %f, new_node->number_of_rays = %d \n",new_node->intensity,new_node->number_of_rays);
    return new_node;
};


struct tagging_tree_node_struct *initialize_tagging_tree_node(struct tagging_tree_node_struct *new_node, struct tagging_tree_node_struct *above_node, struct Volume_struct *this_volume) {
    new_node = make_tagging_tree_node();
    
    new_node->intensity = (double) 0;
    new_node->number_of_rays = (int) 0;
    new_node->above = above_node;
    
    int next_volume_list_length = this_volume->geometry.next_volume_list.num_elements;
    new_node->volume_branches = malloc(next_volume_list_length*sizeof(struct tagging_tree_node_struct*));
    int iterate;
    // Initializing pointers so that they can be checked for NULL later. Is this redundant? Does malloc return null pointers?
    for (iterate=0;iterate<next_volume_list_length;iterate++) new_node->volume_branches[iterate] = NULL;
    //new_node->volume_branches.num_elements = dest_list_length; // May be removed
    
    int number_of_processes;
    if (this_volume->p_physics == NULL) number_of_processes = 0;
    else number_of_processes = this_volume->p_physics->number_of_processes;
    
    new_node->process_branches = malloc(number_of_processes*sizeof(struct tagging_tree_node_struct*));
    // Initializing pointers so that they can be checked for NULL later. Is this redundant? Does malloc return null pointers?
    for (iterate=0;iterate<number_of_processes;iterate++) new_node->process_branches[iterate] = NULL;
    //new_node->process_branches.num_elements=number_of_processes; // May be removed
    
    return new_node;
};

struct tagging_tree_node_struct *goto_process_node(struct tagging_tree_node_struct *current_node, int process_index, struct Volume_struct *this_volume, int *stop_tagging_ray, int stop_creating_nodes) {
    // Either create a new node if it has not been created yet, or travel down the tree
    if (current_node->process_branches[process_index] == NULL) {
      if (stop_creating_nodes == 0) {
        current_node->process_branches[process_index] = initialize_tagging_tree_node(current_node->process_branches[process_index],current_node,this_volume);
        return current_node->process_branches[process_index];
      } else {
        // This stops the ray from using more goto node functions and being counted in the statistics.
        // Happens because the history limit is reached, and no new histories should be started.
        *stop_tagging_ray = 1;
        return current_node;
      }
    } else {
        current_node = current_node->process_branches[process_index];
        return current_node;
    }
    
    //return current_node;
};

struct tagging_tree_node_struct *goto_volume_node(struct tagging_tree_node_struct *current_node,int current_volume, int next_volume, struct Volume_struct **Volumes, int *stop_tagging_ray, int stop_creating_nodes) {
    // I have only allocated the number of node branches that corresponds to the current_volumes next_volume_list
    // The problem is to find where on the destination list the current volume is without doing a manual search
    
    // With the new mask system there is a risk of going from and to the same volume, which should be ignored by the tagging system
    if (current_volume == next_volume) return current_node;
    
    struct tagging_tree_node_struct *output;
    int next_volume_list_index = -1;
    // Temporary slow method for finding the correct index on the destination list
    int iterate;
    
    for (iterate=0;iterate<Volumes[current_volume]->geometry.next_volume_list.num_elements;iterate++)
        if (Volumes[current_volume]->geometry.next_volume_list.elements[iterate] == next_volume) {
            next_volume_list_index = iterate;
            break;
        }
    
    // Debug phase
    //printf("Tagging: going from volume %d to volume %d, which is index number %d on it's next volume list \n",current_volume,next_volume,next_volume_list_index);
    #ifndef OPENACC
    if (next_volume_list_index == -1) {
        printf("ERROR in Union component, tagging or destination system failed, next volume was not on next volume list\n");
        printf("current_volume = %d, next_volume = %d \n",current_volume,next_volume);
        print_1d_int_list(Volumes[current_volume]->geometry.destinations_list,"destinations_list for current volume");
        exit(1);
    }
    #endif

    // Either create a new node if it has not been created yet, or travel down the tree
    if (current_node->volume_branches[next_volume_list_index] == NULL) {
      if (stop_creating_nodes == 0) {
        current_node->volume_branches[next_volume_list_index] =  initialize_tagging_tree_node(current_node->volume_branches[next_volume_list_index],current_node,Volumes[next_volume]);
        return current_node->volume_branches[next_volume_list_index];
      } else {
        // This stops the ray from using more goto node functions and being counted in the statistics.
        // Happens because the history limit is reached, and no new histories should be started.
        *stop_tagging_ray = 1;
        return current_node;
      }
    } else {
        //current_node = current_node->volume_branches[next_volume_list_index];
        //return current_node;
        current_node = current_node->volume_branches[next_volume_list_index];
        //printf("used allocated node \n");
        return current_node;
    }
};

void add_statistics_to_node(struct tagging_tree_node_struct *current_node, Coords *r, Coords *v, double *weight, int *counter) {
    if (current_node->number_of_rays == 0) (*counter)++;
    current_node->number_of_rays = current_node->number_of_rays + 1;
    current_node->intensity = current_node->intensity + *weight;
};

struct history_node_struct {
    int volume_index;
    int process_index;
};

struct dynamic_history_list {
  struct history_node_struct *elements;
  int used_elements;
  int allocated_elements;
};

struct saved_history_struct {
    struct history_node_struct *elements;
    int used_elements;
    double intensity;
    int number_of_rays;
};

struct total_history_struct {
    struct saved_history_struct *saved_histories;
    int used_elements;
    int allocated_elements;
};


void add_to_history(struct dynamic_history_list *history, int volume_index, int process_index) {
    //printf("Adding to history[%d]: volume_index = %d, process_index = %d \n",history->used_elements,volume_index,process_index);
    if (history->allocated_elements == 0) {
        history->elements = malloc(5*sizeof(struct history_node_struct));
        history->used_elements = 1;
        history->allocated_elements = 5;
        history->elements[0].volume_index = volume_index;
        history->elements[0].process_index = process_index;
    } else if (history->used_elements > history->allocated_elements-1) {
        struct dynamic_history_list temp_history;
        temp_history.elements = malloc((history->used_elements)*sizeof(struct history_node_struct));
        int iterate;
        for (iterate=0;iterate<history->used_elements;iterate++) {
            temp_history.elements[iterate].volume_index  = history->elements[iterate].volume_index;
            temp_history.elements[iterate].process_index = history->elements[iterate].process_index;
        }
        free(history->elements);
        history->elements = malloc((history->allocated_elements+5)*sizeof(struct history_node_struct));
        for (iterate=0;iterate<history->used_elements;iterate++) {
            history->elements[iterate].volume_index  = temp_history.elements[iterate].volume_index;
            history->elements[iterate].process_index = temp_history.elements[iterate].process_index;
        }
        
        history->allocated_elements = history->allocated_elements+5;
        
        history->elements[history->used_elements].volume_index = volume_index;
        history->elements[history->used_elements].process_index = process_index;
        history->used_elements = history->used_elements+1;
        
    } else {
        history->elements[history->used_elements].volume_index = volume_index;
        history->elements[history->used_elements].process_index = process_index;
        history->used_elements++;
    }
    
};

void printf_history(struct dynamic_history_list *history) {
    int history_iterate;
          //printf("History number %d, intensity = %f, number of rays = %d:",hist_num, search_node->intensity, search_node->number_of_rays);
          for (history_iterate=0;history_iterate<history->used_elements-1;history_iterate++) {
            if (history->elements[history_iterate].process_index == -1) {
                printf(" V%d ->",history->elements[history_iterate].volume_index);
            } else {
                printf(" P%d ->",history->elements[history_iterate].process_index);
            }
          }
          if (history->elements[history_iterate].process_index == -1) {
                printf(" V%d \n",history->elements[history_iterate].volume_index);
          } else {
                printf(" P%d \n",history->elements[history_iterate].process_index);
          }
}

void fprintf_total_history(struct saved_history_struct *history, FILE *fp) {
    fprintf(fp,"%d\t N I=%E \t", history->number_of_rays, history->intensity);
        int history_iterate;
          //printf("History number %d, intensity = %f, number of rays = %d:",hist_num, search_node->intensity, search_node->number_of_rays);
          for (history_iterate=0;history_iterate<history->used_elements-1;history_iterate++) {
            if (history->elements[history_iterate].process_index == -1) {
                fprintf(fp," V%d ->",history->elements[history_iterate].volume_index);
            } else {
                fprintf(fp," P%d ->",history->elements[history_iterate].process_index);
            }
          }
          if (history->elements[history_iterate].process_index == -1) {
                fprintf(fp," V%d \n",history->elements[history_iterate].volume_index);
          } else {
                fprintf(fp," P%d \n",history->elements[history_iterate].process_index);
          }
}

/*
int Sample_compare_doubles (const void *a, const void *b) {
  const double *da = (const double *) a;
  const double *db = (const double *) b;

  return (*da > *db) - (*da < *db);
}
*/


int Sample_compare_history_intensities (const struct saved_history_struct *a, const struct saved_history_struct *b) {
  const double *da = (const double *) &(a->intensity);
  const double *db = (const double *) &(b->intensity);

  return (*da < *db) - (*da > *db);
}

void write_tagging_tree(struct list_of_tagging_tree_node_pointers *master_list, struct Volume_struct **Volumes, int total_history_counter, int number_of_volumes) {
  // Start from top of tree, go to extremeties, take results and add to disk / database, free that node
  int volume_index,done,volume_iterate,process_iterate,current_volume,next_node_found,current_number_of_processes,history_iterate,hist_num;
  
  struct tagging_tree_node_struct *search_node;
  struct tagging_tree_node_struct **kill_candidate;
  struct dynamic_history_list history_data; // Allocate the history list struct
  struct dynamic_history_list *history;     // Use this pointer in the algorithm
  
  struct total_history_struct total_history;
  total_history.saved_histories = malloc(total_history_counter * sizeof(struct saved_history_struct));
  total_history.allocated_elements = total_history_counter;
  total_history.used_elements = 0;
  
  history = &history_data;
  
  history->used_elements = 0;
  history->allocated_elements = 0;
  
  hist_num = 0;
  for (volume_index=0;volume_index<master_list->num_elements;volume_index++) {
    
    search_node = master_list->elements[volume_index];
    
    if (volume_index != 0)
        current_number_of_processes = Volumes[volume_index]->p_physics->number_of_processes;
    else
        current_number_of_processes = 0;
    
    current_volume = volume_index;
    done = 0;
    
    history->used_elements = 0;
    
    add_to_history(history,current_volume,-1);
    while(done == 0) {
        next_node_found=0;
        for (volume_iterate=0;volume_iterate<Volumes[current_volume]->geometry.next_volume_list.num_elements;volume_iterate++) {
            //printf("searc_node->volume_branches[0]->intensity = %f\n",search_node->volume_branches[0]->intensity);
            if (search_node->volume_branches[volume_iterate] != NULL) {
                current_volume = Volumes[current_volume]->geometry.next_volume_list.elements[volume_iterate];
                if (current_volume != 0)
                    current_number_of_processes = Volumes[current_volume]->p_physics->number_of_processes;
                else
                    current_number_of_processes = 0;
                
                //search_node = &(search_node->volume_branches[volume_iterate]);
                kill_candidate = &(search_node->volume_branches[volume_iterate]);
                search_node = search_node->volume_branches[volume_iterate];
                next_node_found = 1;
                add_to_history(history,current_volume,-1);
                //printf_history(history);
                break;
            }
        }
        if (next_node_found == 0) {
          //printf("doing process loop with %d steps \n",current_number_of_processes);
          for (process_iterate=0;process_iterate<current_number_of_processes;process_iterate++) {
            //if (&(search_node->process_branches[volume_iterate]) != NULL) {
            if (search_node->process_branches[process_iterate] != NULL) {
                //printf("was not NULL (process)\n");
                //search_node = &(search_node->process_branches[process_iterate]);
                kill_candidate = &(search_node->process_branches[process_iterate]);
                search_node = search_node->process_branches[process_iterate];
                next_node_found = 1;
                add_to_history(history,current_volume,process_iterate);
                //printf_history(history);
                break;
            }
          }
        }
        
        if (next_node_found == 0) {
          // write this history to disk / memory
          hist_num++;
          //printf("Reached next_node_found == 0 \n");
          if (history->used_elements > 0 && search_node->number_of_rays > 0) {
            //printf("%d rays (I=%E) with history: \t", search_node->number_of_rays, search_node->intensity);
            //printf_history(history);
            
            total_history.saved_histories[total_history.used_elements].used_elements = history->used_elements;
            total_history.saved_histories[total_history.used_elements].elements = malloc(total_history.saved_histories[total_history.used_elements].used_elements*sizeof(struct history_node_struct));
            for (history_iterate = 0;history_iterate<history->used_elements;history_iterate++) {
                total_history.saved_histories[total_history.used_elements].elements[history_iterate] = history->elements[history_iterate];
                //printf("total_history.saved_histories[total_history.used_elements].elements[%d].volume_index \n",history_iterate,total_history.saved_histories[total_history.used_elements].elements[history_iterate].volume_index);
            }
            //total_history.saved_histories[total_history.used_elements].elements = history->elements;
            total_history.saved_histories[total_history.used_elements].intensity = search_node->intensity;
            total_history.saved_histories[total_history.used_elements].number_of_rays = search_node->number_of_rays;
            total_history.used_elements++;
          }
          
          history->used_elements = 0;
          
          // end of tree, no new nodes
          if (search_node->above == NULL) {
            done = 1;
          } else {
            // reset to the root of the tree
            *kill_candidate = NULL;
            free(search_node);
            search_node = master_list->elements[volume_index];
            
            if (volume_index != 0)
              current_number_of_processes = Volumes[volume_index]->p_physics->number_of_processes;
            else
              current_number_of_processes = 0;
    
            current_volume = volume_index;
            add_to_history(history,current_volume,-1);
          }
          
        }
    }
  }
  
  if (history->allocated_elements > 0) free(history->elements);

  qsort(total_history.saved_histories,total_history.used_elements,sizeof (struct saved_history_struct), Sample_compare_history_intensities);
  
  
  
  MPI_MASTER(
  printf("\n\n");
  printf("Top 20 most common histories. Shows the index of volumes entered (VX), and the scattering processes (PX)\n");
  for (history_iterate=0;history_iterate<total_history.used_elements && history_iterate<20;history_iterate++) {
    printf("%d\t N I=%E \t", total_history.saved_histories[history_iterate].number_of_rays, total_history.saved_histories[history_iterate].intensity);
    printf_history(&total_history.saved_histories[history_iterate]);
  }
   
  FILE *fp;
  fp = fopen("union_history.dat","w");
  
  fprintf(fp,"History file written by the McStas component Union_master \n");
  fprintf(fp,"When running with MPI, the results may be from just a single thread, meaning intensities are divided by number of threads\n");
  fprintf(fp,"----- Description of the used volumes -----------------------------------------------------------------------------------\n");
  
  fprintf(fp,"V0: Surrounding vacuum \n");
  for (volume_iterate=1;volume_iterate<number_of_volumes;volume_iterate++) {
    fprintf(fp,"V%d: %s  ",volume_iterate,Volumes[volume_iterate]->name);
    fprintf(fp,"Material: %s  ",Volumes[volume_iterate]->p_physics->name);
    for (process_iterate=0;process_iterate<Volumes[volume_iterate]->p_physics->number_of_processes;process_iterate++) {
      fprintf(fp," P%d: %s",process_iterate,Volumes[volume_iterate]->p_physics->p_scattering_array[process_iterate].name);
    }
    fprintf(fp,"\n");
  }
  fprintf(fp,"----- Histories sorted after intensity ----------------------------------------------------------------------------------\n");
  
  for (history_iterate=0;history_iterate<total_history.used_elements;history_iterate++) {
    fprintf_total_history(&total_history.saved_histories[history_iterate],fp);
    // Garbage collection
    if (total_history.saved_histories[history_iterate].used_elements > 0) free(total_history.saved_histories[history_iterate].elements);
  }
  fclose(fp);
  
  )
  
  // Garbage collection
  if (total_history.allocated_elements > 0) free(total_history.saved_histories);
  
  
};


// -------------    Intersection table functions   --------------------------------------------------------
int clear_intersection_table(struct intersection_time_table_struct *intersection_time_table) {
    // Resets the intersection table when a scattering have occured.
    int iterate_volumes,iterate_solutions;
    
    // Start at one because vacuum (0) does not have a listing in the intersection table
    for (iterate_volumes = 1;iterate_volumes < intersection_time_table->num_volumes;iterate_volumes++) {
        intersection_time_table->calculated[iterate_volumes] = 0;
        // This second loop is added for safty in debugging phase, but can be removed as the information should never be accesed when calculated = 0
        for (iterate_solutions = 0;iterate_solutions < intersection_time_table->n_elements[iterate_volumes];iterate_solutions++) {
            intersection_time_table->intersection_times[iterate_volumes][iterate_solutions] = -1;
        }
    }
    
    return 1;
};

void print_intersection_table(struct intersection_time_table_struct *intersection_time_table) {
    int num_volumes,iterate,solutions;
    int max_number_of_solutions = 0;
    
    num_volumes = intersection_time_table->num_volumes;
    
    for (iterate = 0;iterate < num_volumes;iterate++) {
        if (max_number_of_solutions < intersection_time_table->n_elements[iterate])
            max_number_of_solutions = intersection_time_table->n_elements[iterate];
    }
    
    printf("------------------ INTERSECTION_TIME_TABLE -----------------");
    for (solutions = 2;solutions < max_number_of_solutions;solutions++) printf("------------");
    printf("\n");
    
    // printf("iterate      |");

    printf("           ");
    printf("| CALCULATED  |");
    for (solutions = 0;solutions < max_number_of_solutions;solutions++) {
        printf(" - SOLUTION %d - |",solutions);
    }

    printf("\n");
    for (iterate = 0;iterate < num_volumes;iterate++){
    // print iterate number
    printf("Volume %d   |",iterate);
    printf(" ---- %d ---- |",intersection_time_table->calculated[iterate]);
    
        for (solutions = 0;solutions < max_number_of_solutions;solutions++) {
          if (intersection_time_table->n_elements[iterate] > solutions && intersection_time_table->calculated[iterate] == 1)
            if (intersection_time_table->intersection_times[iterate][solutions] > 0)
             printf("   %1.8f   |",intersection_time_table->intersection_times[iterate][solutions]);
            else
             printf("   %1.7f   |",intersection_time_table->intersection_times[iterate][solutions]);
          else
            printf("                |");
        }
    printf("\n");
    }
    printf("------------------------------------------------------------");
    for (solutions = 2;solutions < max_number_of_solutions;solutions++) printf("------------");
    printf("\n");
    
    
    };
    
// -------------    Drawing functions   --------------------------------------------------------
void merge_lines_to_draw(struct lines_to_draw *lines_master,struct lines_to_draw *lines_new) {
    if (lines_master->number_of_lines == 0) {
    lines_master->number_of_lines = lines_new->number_of_lines;
    lines_master->lines = malloc(lines_master->number_of_lines*sizeof(struct line_segment));
    lines_master->lines = lines_new->lines;
    // One could free lines_new->lines;
    } else {
    int iterate;
    struct line_segment *temp_lines;
    temp_lines = malloc(lines_master->number_of_lines*sizeof(struct line_segment));
    for (iterate = 0;iterate < lines_master->number_of_lines;iterate++) temp_lines[iterate] = lines_master->lines[iterate];
    free(lines_master->lines);
    lines_master->lines = malloc((lines_master->number_of_lines+lines_new->number_of_lines)*sizeof(struct line_segment));
    for (iterate = 0;iterate < lines_master->number_of_lines;iterate++) lines_master->lines[iterate] = temp_lines[iterate];
    for (iterate = 0;iterate < lines_new->number_of_lines;iterate++) lines_master->lines[iterate+lines_master->number_of_lines] = lines_new->lines[iterate];
    lines_master->number_of_lines = lines_master->number_of_lines + lines_new->number_of_lines;
    free(temp_lines);
    }
    };

int r_has_highest_priority(Coords point,int N,struct geometry_struct **Geometries,int number_of_volumes) {
    // Function that test if a point in Volume N has the highest priority.
    // Returns 0 if another volume has higher priority at the point, and it's mask status is 1
    // Returns 1 if no other volume has higher priority at the point
    // (not active) Returns a larger integer if the volume N is a mask, and the point is not in the volume it is masking,
    //  which results in the drawing function making that number of dashes
    
    
    int mask_status,*mask_start,*mask_check;
    // If the volume is a mask, it does not have a priority, and is always on top
    if (Geometries[N]->is_mask_volume == 1) {
      return 1;
      // Can draw parts of the mask that is outside the volume it masks as dashed lines, but it just looks messy
      /*
      mask_status = 1;
      for (mask_check=mask_start=Geometries[N]->mask_list.elements;mask_check-mask_start<Geometries[N]->mask_list.num_elements;mask_check++) {
        if (Geometries[*mask_check]->within_function(point,Geometries[*mask_check]) == 1) {
          return 1; // If the point is within a volume the mask is masking, draw it as a solid line
        }
      }
      // As it was not inside any of the volumes the mask is masking, draw it as a dashed line
      return 5;
      */
    }
    
    // If the volume is a masked volume, check if the point is within it's masks
    if (Geometries[N]->is_masked_volume == 1) {
      if (Geometries[N]->mask_mode == 1) { // ALL mode, need to be within ALL masks
        for (mask_check=mask_start=Geometries[N]->masked_by_list.elements;mask_check-mask_start<Geometries[N]->masked_by_list.num_elements;mask_check++) {
          if (Geometries[*mask_check]->within_function(point,Geometries[*mask_check]) == 0) {
            return 0; // If the point is just outside one mask, the mask status is 0 and the point does not have highest priority
          }
        }
      } else { // ANY mode, need to be within at least one mask
        mask_status = 0;
        for (mask_check=mask_start=Geometries[N]->masked_by_list.elements;mask_check-mask_start<Geometries[N]->masked_by_list.num_elements;mask_check++) {
          if (Geometries[*mask_check]->within_function(point,Geometries[*mask_check]) == 1) {
            mask_status = 1;
            break;
          }
        }
        if (mask_status == 0) {
          return 0; // If it was not in a single of it's masks, the point did not have highest priority
        }
      }
    }
    
    int volume_index;
    double self_priority;
    self_priority = Geometries[N]->priority_value;
    
    
    for (volume_index = 1;volume_index<number_of_volumes;volume_index++) {
      if (volume_index != N && Geometries[volume_index]->is_mask_volume == 0) {
        if (Geometries[volume_index]->within_function(point,Geometries[volume_index])) {
          if (Geometries[volume_index]->is_masked_volume == 1) {
            // Since this volume is masked, the mask status need to be checked
            if (Geometries[volume_index]->mask_mode == 1) { //ALL mode, need to be within ALL masks
              mask_status = 1;
              for (mask_check=mask_start=Geometries[volume_index]->masked_by_list.elements;mask_check-mask_start<Geometries[volume_index]->masked_by_list.num_elements;mask_check++) {
                if (Geometries[*mask_check]->within_function(point,Geometries[*mask_check]) == 0) {
                  mask_status = 0;
                  break;
                }
              }
            } else { // ANY mode, need to be within at least one mask
              mask_status = 0;
              for (mask_check=mask_start=Geometries[volume_index]->masked_by_list.elements;mask_check-mask_start<Geometries[volume_index]->masked_by_list.num_elements;mask_check++) {
                if (Geometries[*mask_check]->within_function(point,Geometries[*mask_check]) == 1) {
                  mask_status = 1;
                  break;
                }
              }
            }
          } else mask_status = 1;
          if (Geometries[volume_index]->priority_value > self_priority && mask_status == 1) {
            // printf("Volume %d did not have highest priority at (%f,%f,%f) (Volume number %d was above)\n",N,point.x,point.y,point.z,volume_index);
            return 0;
          }
        }
      }
    }
    // printf("Volume %d did have highest priority at (%f,%f,%f) \n",N,point.x,point.y,point.z);
    return 1;
    }
    
void draw_line_positions(Coords point1,Coords point2) {
        //line(point1.x,point1.y,point1.z,point2.x,point2.y,point2.z);
        // sigh, can not use line in share. Need to save the information and pass to the mcdisplay part.
    }

int Sample_compare_doubles (const void *a, const void *b) {
  const double *da = (const double *) a;
  const double *db = (const double *) b;

  return (*da > *db) - (*da < *db);
}

struct lines_to_draw draw_line_with_highest_priority(Coords position1,Coords position2,int N,struct geometry_struct **Geometries,int number_of_volumes,int max_number_of_solutions) {
    
    int volume_index,iterate,permanent_list_length = 0;
    int number_of_solutions;
    double temp_intersection[max_number_of_solutions];
    double r1[3],r2[3],direction[3];
    struct pointer_to_1d_double_list intersection_list;
    
    intersection_list.num_elements = 0;
    
    // double *permanent_intersection_list,*storage; // old ways
    
    r1[0] = position1.x;
    r1[1] = position1.y;
    r1[2] = position1.z;
    r2[0] = position2.x;
    r2[1] = position2.y;
    r2[2] = position2.z;
    
    // printf("r1 = (%f,%f,%f) \n",r1[0],r1[1],r1[2]);
    // printf("r2 = (%f,%f,%f) \n",r2[0],r2[1],r2[2]);
    
    direction[0] = r2[0] - r1[0];
    direction[1] = r2[1] - r1[1];
    direction[2] = r2[2] - r1[2];
    int geometry_output;
    
    // Find intersections
    for (volume_index = 1;volume_index < number_of_volumes; volume_index++) {
        if (volume_index != N) {
            geometry_output = Geometries[volume_index]->intersect_function(temp_intersection,&number_of_solutions,r1,direction,Geometries[volume_index]);
             // printf("No solutions for intersection (Volume %d) with %d \n",N,volume_index);
                for (iterate=0;iterate<number_of_solutions;iterate++) {
                    // print_1d_double_list(intersection_list,"intersection_list");
                    if (temp_intersection[iterate] > 0 && temp_intersection[iterate] < 1) {
                        add_element_to_double_list(&intersection_list,temp_intersection[iterate]);
                        // printf("solution taken = %f\n",temp_intersection[iterate]);
                        }                         // printf("solution ignored = %f\n",temp_intersection[iterate]);
                    // print_1d_double_list(intersection_list,"intersection_list");
                
                }
                // printf("First (%d) solutions added to the solution stack, intersection with %d \n",number_of_solutions,volume_index);
                // printf(" Solutions: ");
                // for (iterate = 0;iterate < intersection_list.num_elements;iterate++) printf("%f ",intersection_list.elements[iterate]);
                // printf("\n");
            
        }
    }
    // Now we have a list of intersection distances between r1 and r2 and all volumes.
    // This list needs to be sorted before we continue!
    
    qsort(intersection_list.elements,intersection_list.num_elements,sizeof (double), Sample_compare_doubles);
    // print_1d_double_list(intersection_list,"after sorting");
    // printf(" Solutions (after sorting): ");
    // for (iterate = 0;iterate < intersection_list.num_elements;iterate++) printf("%f ",intersection_list.elements[iterate]);
    // printf("\n");
    
    Coords points[intersection_list.num_elements+2];
    points[0] = coords_set(r1[0],r1[1],r1[2]);
    points[intersection_list.num_elements+1] = coords_set(r2[0],r2[1],r2[2]);
    for (iterate = 1;iterate < intersection_list.num_elements+1;iterate++) {
        points[iterate].x = r1[0] + direction[0]*intersection_list.elements[iterate-1];
        points[iterate].y = r1[1] + direction[1]*intersection_list.elements[iterate-1];
        points[iterate].z = r1[2] + direction[2]*intersection_list.elements[iterate-1];
    }
    
    // printf("Points on the list:\n");
    // for (iterate = 0;iterate < intersection_list.num_elements + 2;iterate++) {
    // //         printf("(%f,%f,%f)\n",points[iterate].x,points[iterate].y,points[iterate].z);
    // }
    // printf("\n");
  
    struct line_segment lines[intersection_list.num_elements+1];
    int draw_logic[intersection_list.num_elements+1];
    //struct lines_to_draw draw_order;
    Coords midpoint;
    struct lines_to_draw draw_order;
    draw_order.number_of_lines = 0;
    
    // printf("test2 in function \n");
    int number_of_dashes;
    
    for (iterate = 0;iterate < intersection_list.num_elements + 1;iterate++) {
        lines[iterate].point1 = points[iterate];
        lines[iterate].point2 = points[iterate+1];
        midpoint.x = 0.5*(lines[iterate].point1.x + lines[iterate].point2.x);
        midpoint.y = 0.5*(lines[iterate].point1.y + lines[iterate].point2.y);
        midpoint.z = 0.5*(lines[iterate].point1.z + lines[iterate].point2.z);
        
        if ((number_of_dashes = r_has_highest_priority(midpoint,N,Geometries,number_of_volumes)) != 0) {
            draw_order.number_of_lines++;
            draw_logic[iterate] = number_of_dashes;
        } else draw_logic[iterate] = 0;
    }
   
    // printf("test3 in function \n");
    
    if (draw_order.number_of_lines > 0) {
        draw_order.lines = malloc(draw_order.number_of_lines*sizeof(struct line_segment));
        draw_order.number_of_lines = 0;
        for (iterate = 0;iterate < intersection_list.num_elements + 1;iterate++) {
            if (draw_logic[iterate] != 0) {
              lines[iterate].number_of_dashes = draw_logic[iterate];
              draw_order.lines[draw_order.number_of_lines++] = lines[iterate];
            }
        }
        if (intersection_list.num_elements > 0) free(intersection_list.elements);
    }

    // printf("function done \n");
    return draw_order;
    }

struct lines_to_draw draw_circle_with_highest_priority(Coords center,Coords vector,double radius,int N,struct geometry_struct **Geometries,int number_of_volumes,int max_number_of_solutions) {
    int number_of_positions = 100;

    // normalize vector
    double vector_length = length_of_position_vector(vector);
    // print_position(vector,"start vector");
    vector.x /= vector_length;
    vector.y /= vector_length;
    vector.z /= vector_length;
    // print_position(vector,"start vector normalized");
    
    // Create a vector from the center of the circle to a point on the circumference by cross product
    double cross_input[3] = {0,1,0};
    // In case the cross input is parallel with the vector, a new is chosen. Both can't be parallel.
    if (scalar_prod(cross_input[0],cross_input[1],cross_input[2],vector.x,vector.y,vector.z) > 0.99) {
        cross_input[0] = 1; cross_input[1] = 0; cross_input[2] = 0;
    }
    // print_position(make_position(cross_input),"cross input");
    
    double cross_product1[3] = {0,0,0};
    vec_prod(cross_product1[0],cross_product1[1],cross_product1[2],vector.x,vector.y,vector.z,cross_input[0],cross_input[1],cross_input[2]);
    
    // print_position(make_position(cross_product1),"cross_product1");
    
    double cross_length = length_of_3vector(cross_product1);
    
    // printf("cross_length = %f \n",cross_length);
    
    cross_product1[0] /= cross_length;
    cross_product1[1] /= cross_length;
    cross_product1[2] /= cross_length;
    
    cross_product1[0] *= radius;
    cross_product1[1] *= radius;
    cross_product1[2] *= radius;
    
    // printf("cross_product1 = (%f,%f,%f) \n",cross_product1[0],cross_product1[1],cross_product1[2]);
    
    int iterate;
    double rotate_angle;
    Coords radial_position,old_radial_position;
    struct lines_to_draw temp_draw_order,return_draw_order;
    return_draw_order.number_of_lines = 0;
    // Generate an array of positions by rotating this vector around the given vector, this corresponds to points on the circle
    old_radial_position.x = center.x + cross_product1[0];
    old_radial_position.y = center.y + cross_product1[1];
    old_radial_position.z = center.z + cross_product1[2];

    // printf("old_radial_position = (%f,%f,%f) \n",old_radial_position.x,old_radial_position.y,old_radial_position.z);
    
    for (iterate = 0;iterate < number_of_positions-1;iterate++) {
        rotate_angle = 2*3.14159*((double) iterate + 1.0)/((double) number_of_positions);
        rotate(radial_position.x,radial_position.y,radial_position.z,cross_product1[0],cross_product1[1],cross_product1[2],rotate_angle,vector.x,vector.y,vector.z);
        radial_position.x += center.x;
        radial_position.y += center.y;
        radial_position.z += center.z;
        
        // Use the draw_lines_with_highest_priority to draw get draw_orders for each line piece
        temp_draw_order = draw_line_with_highest_priority(radial_position,old_radial_position,N,Geometries,number_of_volumes,max_number_of_solutions);
       // Assemble these draw_orders to one large draw order
        merge_lines_to_draw(&return_draw_order,&temp_draw_order);

        old_radial_position.x = radial_position.x;
        old_radial_position.y = radial_position.y;
        old_radial_position.z = radial_position.z;
    }
    
    radial_position.x = center.x + cross_product1[0];
    radial_position.y = center.y + cross_product1[1];
    radial_position.z = center.z + cross_product1[2];
    // Use the draw_lines_with_highest_priority to draw get draw_orders for each line piece
    temp_draw_order = draw_line_with_highest_priority(radial_position,old_radial_position,N,Geometries,number_of_volumes,max_number_of_solutions);
    // Assemble these draw_orders to one large draw order
    merge_lines_to_draw(&return_draw_order,&temp_draw_order);
    
    // clean up
    if (temp_draw_order.number_of_lines > 0) free(temp_draw_order.lines);
    
    // return the large draw order.
    return return_draw_order;
    }

// -------------    Geometry functions   --------------------------------------------------------

#include "Geometry_functions.c"

// -------------    List generator functions   --------------------------------------------------


int within_which_volume(Coords pos, struct pointer_to_1d_int_list input_list, struct pointer_to_1d_int_list destinations_list, struct Volume_struct **Volumes, struct pointer_to_1d_int_list *mask_status_list, int number_of_volumes, int *volume_logic_copy, int *ListA, int *ListB) {
    // This function identifies in which of the volumes of the input list the position pos lies in.
    // pos: position for which the current volume should be found for
    // input list: list of potential volumes, reduced to the ones without parents (their children will be checked)
    // OLD VERSION: volume logic: A logic list of allowed volumes for lookup, volume 1 3 and 5 in a case of 10 volumes would be [0 1 0 1 0 1 0 0 0 0]
    // destinations_list: list of allowed destinations (the original destinations list)
    // Volumes: Main volumes array
    // volume_logic_copy: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    // ListA: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    // ListB: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    
    // Algorithm description
    // Check all of input list for pos being within them, those that have it within them are:
    //      checked against the current highest priority
    //          if higher, is the new highest priority and the new pick for volume
    //      have all their direct children added to the next list to be checked (but any volume can only be added to that list once)
    // Once a run have been made where the next list to check is empty, the answer for new pick for volume is taken.
    
    // Mask update:
    // Algorithm description
    // Check all of input list for pos being within them, those that have it within them are:
    //      checked against the current highest priority and mask status
    //          if higher, this is the new highest priority and the new pick for volume
    //      have all their direct children added to the next list to be checked (but any volume can only be added to that list once)
    // Once a run have been made where the next list to check is empty, the answer for new pick for volume is taken.
    
    // The advantage of the method is that potentially large numbers of children are skipped when their parents do not contain the position.
    // The overhead cost is low, as all the lists are prealocated.
    // Should be checked which of the two implementations is faster, as this is much more complicated than simply checking all possibilities.
    // No within_function call should be made twice, as the same volume number will not be checked twice because of the properties of the direct_children list and the volume_logic that removes duplicates on each level.
    
    
    // This function uses too much memory, the memory required for the volume logic list is n_volumes^2 ints, or for a MACS monochromator 127000 ints.
    // Instead the original destinations list must be used, and a function for quick lookup in a (sorted) destinations list made.
    // Still need a list of n_volumes length for control to avoid adding the same volume to the list twice.
    
    
    int ListA_length=0,ListB_length=0;
    int done = 0;
    int i,direct_children_index;
    int *temp_pointer;
    double max_priority=-1000000;
    int residing_volume=0; // 0 can be removed from the input list if default is 0
    int this_mask_status,mask_index,mask_global_index;
    
    // volume_logic_copy
    //for (i=0;i<volume_logic.num_elements;i++) volume_logic_copy[i] = volume_logic.elements[i];
    
    // low memory version of volume_logic_copy
    for (i=0;i<number_of_volumes;i++) volume_logic_copy[i] = 0;
    for (i=0;i<destinations_list.num_elements;i++) volume_logic_copy[destinations_list.elements[i]] = 1;
    printf("within_which_volume debug %i\n",input_list.num_elements);

    // Does one loop through the algorithm first to set up ListA instead of copying it from input_list, which takes time
    for (i=0;i<input_list.num_elements;i++) {
            if (Volumes[input_list.elements[i]]->geometry.within_function(pos,&Volumes[input_list.elements[i]]->geometry) == 1) {
                printf("The position is inside of volume %d\n",input_list.elements[i]);
                if (Volumes[input_list.elements[i]]->geometry.is_masked_volume == 1) {
                    // if the volume is masked, I need to know if it can be a destination volume from the mask_status_list.
                    // if the masked volume is in ANY mode,
                    this_mask_status=1;
                    //print_1d_int_list(*mask_status_list,"mask status list from within_which_volume");
                    for (mask_index=0;mask_index<Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.num_elements;mask_index++) {
                        //printf("Looking at the mask with global index %d \n",Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.elements[mask_index]);
                        if (mask_status_list->elements[Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.elements[mask_index]] == 1) {
                            if (Volumes[input_list.elements[i]]->geometry.mask_mode == 2) { // ANY (break if any one in)
                                this_mask_status=1;
                                break;
                            }
                            //printf("global index %d had mask status = 1 \n",Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.elements[mask_index]);
                        } else {
                          //printf("global index %d had mask status = 0 \n",Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.elements[mask_index]);
                          this_mask_status = 0;
                          if(Volumes[input_list.elements[i]]->geometry.mask_mode == 1) break; // ALL (break if any one out)
                        }
                    }
                    //printf("This volume is masked, and the mask status is %d\n",this_mask_status);
                } else this_mask_status = 1; // if the volume is not masked
            
                if (Volumes[input_list.elements[i]]->geometry.priority_value > max_priority && this_mask_status == 1) {
                    max_priority = Volumes[input_list.elements[i]]->geometry.priority_value;
                    residing_volume = input_list.elements[i];
                    //printf("residing volume set to %d\n",residing_volume);
                }
                    for (direct_children_index = 0;direct_children_index < Volumes[input_list.elements[i]]->geometry.direct_children.num_elements;direct_children_index++) {
                        if (volume_logic_copy[Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index]] == 1) {
                            ListA[ListA_length++] = Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index];
                            volume_logic_copy[Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index]] = 0;
                        }
                    }
            }
    }
    //printf("Completed first loop, continued in while loop\n");
    if (ListA_length > 0) {
        while (done == 0) {
            for (i=0;i<ListA_length;i++) {
              //printf("checking element number %d of list A which is volume number %d \n",i,ListA[i]);
                if (Volumes[ListA[i]]->geometry.within_function(pos,&Volumes[ListA[i]]->geometry) == 1) {
                  //printf("ray was inside this volume \n");
                    if (Volumes[ListA[i]]->geometry.is_masked_volume == 1) {
                      //printf("it is a mask and thus need check of mask status \n");
                        // if the volume is masked, I need to know if it can be a destination volume from the mask_status_list.
                        // if the masked volume is in ANY mode,
                        this_mask_status=1;
                        for (mask_index=0;mask_index<Volumes[ListA[i]]->geometry.masked_by_mask_index_list.num_elements;mask_index++) {
                            if (mask_status_list->elements[Volumes[ListA[i]]->geometry.masked_by_mask_index_list.elements[mask_index]] == 1) {
                                if (Volumes[ListA[i]]->geometry.mask_mode == 2) { // ANY (break if any one in)
                                    this_mask_status=1;
                                    break;
                                }
                            } else {
                              this_mask_status = 0;
                              if(Volumes[ListA[i]]->geometry.mask_mode == 1) break; // ALL (break if any one out)
                            }
                        }
                    } else this_mask_status = 1;
                    //printf("the mask status is %d \n",this_mask_status);
                    if (Volumes[ListA[i]]->geometry.priority_value > max_priority && this_mask_status == 1) {
                        max_priority = Volumes[ListA[i]]->geometry.priority_value;
                        residing_volume = ListA[i];
                    }
                    //printf("Adding direct children to list B \n");
                        for (direct_children_index = 0;direct_children_index < Volumes[ListA[i]]->geometry.direct_children.num_elements;direct_children_index++) {
                            //printf("Checking direct_child number %d which is %d \n",direct_children_index,Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index]);
                            if (volume_logic_copy[Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index]] == 1) {
                                //printf("It's volume_logic was 1, and it is thus added to listB with index %d \n",ListB_length);
                                ListB[ListB_length++] = Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index];
                                volume_logic_copy[Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index]] = 0;
                            }
                        }
                    //printf("List B is now: ");
                    //for (direct_children_index=0;direct_children_index<ListB_length;direct_children_index++) printf("%d ",ListB[direct_children_index]);
                    //printf("\n");
                    
                    
                }
            }
            if (ListB_length==0) done = 1;
            else {
                
                for (i=0;i<ListB_length;i++) ListA[i] = ListB[i];
                ListA_length = ListB_length;
                ListB_length = 0;
                
                /*
                // Could do this with pointers instead to avoid this for loop (and needless copy)
                // This code block fails on the cluster in rare circumstances
                ListA = temp_pointer;
                ListA = ListB;
                ListB = temp_pointer;
                ListA_length=ListB_length;
                ListB_length=0;
                */
            }
        }
    }
    //printf("residing volume returned %d\n",residing_volume);
    return residing_volume;
};

int within_which_volume_GPU(Coords pos, struct pointer_to_1d_int_list input_list, struct pointer_to_1d_int_list destinations_list, struct Volume_struct **Volumes, struct pointer_to_1d_int_list *mask_status_list, int number_of_volumes, int *volume_logic_copy, int *ListA, int *ListB) {
    // This function identifies in which of the volumes of the input list the position pos lies in.
    // pos: position for which the current volume should be found for
    // input list: list of potential volumes, reduced to the ones without parents (their children will be checked)
    // OLD VERSION: volume logic: A logic list of allowed volumes for lookup, volume 1 3 and 5 in a case of 10 volumes would be [0 1 0 1 0 1 0 0 0 0]
    // destinations_list: list of allowed destinations (the original destinations list)
    // Volumes: Main volumes array
    // volume_logic_copy: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    // ListA: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    // ListB: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    
    // Algorithm description
    // Check all of input list for pos being within them, those that have it within them are:
    //      checked against the current highest priority
    //          if higher, is the new highest priority and the new pick for volume
    //      have all their direct children added to the next list to be checked (but any volume can only be added to that list once)
    // Once a run have been made where the next list to check is empty, the answer for new pick for volume is taken.
    
    // Mask update:
    // Algorithm description
    // Check all of input list for pos being within them, those that have it within them are:
    //      checked against the current highest priority and mask status
    //          if higher, this is the new highest priority and the new pick for volume
    //      have all their direct children added to the next list to be checked (but any volume can only be added to that list once)
    // Once a run have been made where the next list to check is empty, the answer for new pick for volume is taken.
    
    // The advantage of the method is that potentially large numbers of children are skipped when their parents do not contain the position.
    // The overhead cost is low, as all the lists are prealocated.
    // Should be checked which of the two implementations is faster, as this is much more complicated than simply checking all possibilities.
    // No within_function call should be made twice, as the same volume number will not be checked twice because of the properties of the direct_children list and the volume_logic that removes duplicates on each level.
    
    
    // This function uses too much memory, the memory required for the volume logic list is n_volumes^2 ints, or for a MACS monochromator 127000 ints.
    // Instead the original destinations list must be used, and a function for quick lookup in a (sorted) destinations list made.
    // Still need a list of n_volumes length for control to avoid adding the same volume to the list twice.
    
    
    int ListA_length=0,ListB_length=0;
    int done = 0;
    int i,direct_children_index;
    int *temp_pointer;
    double max_priority=-1000000;
    int residing_volume=0; // 0 can be removed from the input list if default is 0
    int this_mask_status,mask_index,mask_global_index;
    
    // volume_logic_copy
    //for (i=0;i<volume_logic.num_elements;i++) volume_logic_copy[i] = volume_logic.elements[i];
    
    // low memory version of volume_logic_copy
    for (i=0;i<number_of_volumes;i++) volume_logic_copy[i] = 0;
    for (i=0;i<destinations_list.num_elements;i++) volume_logic_copy[destinations_list.elements[i]] = 1;
    //printf("within_which_volume debug\n");

    // Does one loop through the algorithm first to set up ListA instead of copying it from input_list, which takes time
    for (i=0;i<input_list.num_elements;i++) {
            if (r_within_function(pos, &Volumes[input_list.elements[i]]->geometry) == 1) {
                //printf("The position is inside of volume %d\n",input_list.elements[i]);
                if (Volumes[input_list.elements[i]]->geometry.is_masked_volume == 1) {
                    // if the volume is masked, I need to know if it can be a destination volume from the mask_status_list.
                    // if the masked volume is in ANY mode,
                    this_mask_status=1;
                    //print_1d_int_list(*mask_status_list,"mask status list from within_which_volume");
                    for (mask_index=0;mask_index<Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.num_elements;mask_index++) {
                        //printf("Looking at the mask with global index %d \n",Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.elements[mask_index]);
                        if (mask_status_list->elements[Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.elements[mask_index]] == 1) {
                            if (Volumes[input_list.elements[i]]->geometry.mask_mode == 2) { // ANY (break if any one in)
                                this_mask_status=1;
                                break;
                            }
                            //printf("global index %d had mask status = 1 \n",Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.elements[mask_index]);
                        } else {
                          //printf("global index %d had mask status = 0 \n",Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.elements[mask_index]);
                          this_mask_status = 0;
                          if(Volumes[input_list.elements[i]]->geometry.mask_mode == 1) break; // ALL (break if any one out)
                        }
                    }
                    //printf("This volume is masked, and the mask status is %d\n",this_mask_status);
                } else this_mask_status = 1; // if the volume is not masked
            
                if (Volumes[input_list.elements[i]]->geometry.priority_value > max_priority && this_mask_status == 1) {
                    max_priority = Volumes[input_list.elements[i]]->geometry.priority_value;
                    residing_volume = input_list.elements[i];
                    //printf("residing volume set to %d\n",residing_volume);
                }
                    for (direct_children_index = 0;direct_children_index < Volumes[input_list.elements[i]]->geometry.direct_children.num_elements;direct_children_index++) {
                        if (volume_logic_copy[Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index]] == 1) {
                            ListA[ListA_length++] = Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index];
                            volume_logic_copy[Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index]] = 0;
                        }
                    }
            }
    }
    //printf("Completed first loop, continued in while loop\n");
    if (ListA_length > 0) {
        while (done == 0) {
            for (i=0;i<ListA_length;i++) {
              //printf("checking element number %d of list A which is volume number %d \n",i,ListA[i]);
                if (r_within_function(pos,&Volumes[ListA[i]]->geometry) == 1) {
                  //printf("ray was inside this volume \n");
                    if (Volumes[ListA[i]]->geometry.is_masked_volume == 1) {
                      //printf("it is a mask and thus need check of mask status \n");
                        // if the volume is masked, I need to know if it can be a destination volume from the mask_status_list.
                        // if the masked volume is in ANY mode,
                        this_mask_status=1;
                        for (mask_index=0;mask_index<Volumes[ListA[i]]->geometry.masked_by_mask_index_list.num_elements;mask_index++) {
                            if (mask_status_list->elements[Volumes[ListA[i]]->geometry.masked_by_mask_index_list.elements[mask_index]] == 1) {
                                if (Volumes[ListA[i]]->geometry.mask_mode == 2) { // ANY (break if any one in)
                                    this_mask_status=1;
                                    break;
                                }
                            } else {
                              this_mask_status = 0;
                              if(Volumes[ListA[i]]->geometry.mask_mode == 1) break; // ALL (break if any one out)
                            }
                        }
                    } else this_mask_status = 1;
                    //printf("the mask status is %d \n",this_mask_status);
                    if (Volumes[ListA[i]]->geometry.priority_value > max_priority && this_mask_status == 1) {
                        max_priority = Volumes[ListA[i]]->geometry.priority_value;
                        residing_volume = ListA[i];
                    }
                    //printf("Adding direct children to list B \n");
                        for (direct_children_index = 0;direct_children_index < Volumes[ListA[i]]->geometry.direct_children.num_elements;direct_children_index++) {
                            //printf("Checking direct_child number %d which is %d \n",direct_children_index,Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index]);
                            if (volume_logic_copy[Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index]] == 1) {
                                //printf("It's volume_logic was 1, and it is thus added to listB with index %d \n",ListB_length);
                                ListB[ListB_length++] = Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index];
                                volume_logic_copy[Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index]] = 0;
                            }
                        }
                    //printf("List B is now: ");
                    //for (direct_children_index=0;direct_children_index<ListB_length;direct_children_index++) printf("%d ",ListB[direct_children_index]);
                    //printf("\n");
                    
                    
                }
            }
            if (ListB_length==0) done = 1;
            else {
                
                for (i=0;i<ListB_length;i++) ListA[i] = ListB[i];
                ListA_length = ListB_length;
                ListB_length = 0;
                
                /*
                // Could do this with pointers instead to avoid this for loop (and needless copy)
                // This code block fails on the cluster in rare circumstances
                ListA = temp_pointer;
                ListA = ListB;
                ListB = temp_pointer;
                ListA_length=ListB_length;
                ListB_length=0;
                */
            }
        }
    }
    //printf("residing volume returned %d\n",residing_volume);
    return residing_volume;
};




int within_which_volume_debug(Coords pos, struct pointer_to_1d_int_list input_list, struct pointer_to_1d_int_list volume_logic, struct Volume_struct **Volumes, int *volume_logic_copy, int *ListA, int *ListB) {
    // This function identifies in which of the volumes of the input list the position pos lies in.
    // pos: position for which the current volume should be found for
    // input list: list of potential volumes, reduced to the ones without parents (their children will be checked)
    // volume logic: A logic list of allowed volumes for lookup, volume 1 3 and 5 in a case of 10 volumes would be [0 1 0 1 0 1 0 0 0 0]
    // Volumes: Main volumes array
    // volume_logic_copy: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    // ListA: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    // ListB: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    
    // Algorithm description
    // Check all of input list for pos being within them, those that have it within them are:
    //      checked against the current highest priority
    //          if higher, is the new highest priority and the new pick for volume
    //      have all their direct children added to the next list to be checked (but any volume can only be added to that list once)
    // Once a run have been made where the next list to check is empty, the answer for new pick for volume is taken.
    
    // The advantage of the method is that potentially large numbers of children are skipped when their parents do not contain the position.
    // The overhead cost is low, as all the lists are prealocated.
    // Should be checked which of the two implementations is faster, as this is much more complicated than simply checking all possibilities.
    // No within_function call should be made twice, as the same volume number will not be checked twice because of the properties of the direct_children list and the volume_logic that removes duplicates on each level.
    
    
    int ListA_length=0,ListB_length=0;
    int done = 0;
    int i,direct_children_index;
    int *temp_pointer;
    double max_priority=-1000000;
    int residing_volume=0; // 0 can be removed from the input list if default is 0
    
    // volume_logic_copy
    for (i=0;i<volume_logic.num_elements;i++) volume_logic_copy[i] = volume_logic.elements[i];
    
    // Does one loop through the algorithm first to set up ListA instead of copying it from input_list, which takes time
    for (i=0;i<input_list.num_elements;i++) {
            if (Volumes[input_list.elements[i]]->geometry.within_function(pos,&Volumes[input_list.elements[i]]->geometry) == 1) {
                if (Volumes[input_list.elements[i]]->geometry.priority_value > max_priority) {
                    max_priority = Volumes[input_list.elements[i]]->geometry.priority_value;
                    residing_volume = input_list.elements[i];
                }
                    for (direct_children_index = 0;direct_children_index < Volumes[input_list.elements[i]]->geometry.direct_children.num_elements;direct_children_index++) {
                        if (volume_logic_copy[Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index]] == 1) {
                            ListA[ListA_length++] = Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index];
                            volume_logic_copy[Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index]] = 0;
                        }
                    }
            }
    }
    if (ListA_length > 0) {
        while (done == 0) {
        
            printf("ListA = [");
            for (i=0;i<ListA_length;i++) {
                printf("%d,",ListA[i]);
            }
            printf("]\n");
            for (i=0;i<ListA_length;i++) {
                if (Volumes[ListA[i]]->geometry.within_function(pos,&Volumes[ListA[i]]->geometry) == 1) {
                    if (Volumes[ListA[i]]->geometry.priority_value > max_priority) {
                        max_priority = Volumes[ListA[i]]->geometry.priority_value;
                        residing_volume = ListA[i];
                    }
                    //if (ListA[i]!=0) {
                        for (direct_children_index = 0;direct_children_index < Volumes[ListA[i]]->geometry.direct_children.num_elements;direct_children_index++) {
                            if (volume_logic_copy[Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index]] == 1) {
                                ListB[ListB_length++] = Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index];
                                volume_logic_copy[Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index]] = 0;
                            }
                        }
                    //}
                }
            }
            if (ListB_length==0) done = 1;
            else {
                ListA = temp_pointer;
                ListA = ListB;
                ListB = temp_pointer;
                ListA_length=ListB_length;
                ListB_length=0;
            }
        }
    }
    printf("Volume number %d had the highest priority of checked volumes\n",residing_volume);
    return residing_volume;
};

int inside_function(struct Volume_struct *parent_volume, struct Volume_struct *child_volume) {
    // Function that calls the correct within function depending on the shapes of the two volumes
    if (strcmp("sphere",parent_volume->geometry.shape) == 0 && strcmp("sphere",child_volume->geometry.shape) == 0) {
        if (sphere_within_sphere(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("cylinder",parent_volume->geometry.shape) == 0 && strcmp("cylinder",child_volume->geometry.shape) == 0) {
        if (cylinder_within_cylinder(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("box",parent_volume->geometry.shape) == 0 && strcmp("box",child_volume->geometry.shape) == 0) {
        if (box_within_box(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("cone",parent_volume->geometry.shape) == 0 && strcmp("cone",child_volume->geometry.shape) == 0) {
        if (cone_within_cone(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("mesh",parent_volume->geometry.shape) == 0 && strcmp("mesh",child_volume->geometry.shape) == 0) {
        if (mesh_within_mesh(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("box",parent_volume->geometry.shape) == 0 && strcmp("cylinder",child_volume->geometry.shape) == 0) {
        if (cylinder_within_box(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("cylinder",parent_volume->geometry.shape) == 0 && strcmp("box",child_volume->geometry.shape) == 0) {
        if (box_within_cylinder(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("box",parent_volume->geometry.shape) == 0 && strcmp("sphere",child_volume->geometry.shape) == 0) {
        if (sphere_within_box(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("sphere",parent_volume->geometry.shape) == 0 && strcmp("box",child_volume->geometry.shape) == 0) {
        if (box_within_sphere(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("sphere",parent_volume->geometry.shape) == 0 && strcmp("cylinder",child_volume->geometry.shape) == 0) {
        if (cylinder_within_sphere(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("cylinder",parent_volume->geometry.shape) == 0 && strcmp("sphere",child_volume->geometry.shape) == 0) {
        if (sphere_within_cylinder(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("cone",parent_volume->geometry.shape) == 0 && strcmp("sphere",child_volume->geometry.shape) == 0) {
        if (sphere_within_cone(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("sphere",parent_volume->geometry.shape) == 0 && strcmp("cone",child_volume->geometry.shape) == 0) {
        if (cone_within_sphere(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("cone",parent_volume->geometry.shape) == 0 && strcmp("cylinder",child_volume->geometry.shape) == 0) {
        if (cylinder_within_cone(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("cylinder",parent_volume->geometry.shape) == 0 && strcmp("cone",child_volume->geometry.shape) == 0) {
        if (cone_within_cylinder(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("cone",parent_volume->geometry.shape) == 0 && strcmp("box",child_volume->geometry.shape) == 0) {
        if (box_within_cone(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("box",parent_volume->geometry.shape) == 0 && strcmp("cone",child_volume->geometry.shape) == 0) {
        if (cone_within_box(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else {
        #ifndef OPENACC
        printf("Need within function for type: ");
        printf("%s",parent_volume->geometry.shape);
        printf(" and type: ");
        printf("%s",child_volume->geometry.shape);
        printf(".\n");
        printf("It is not yet supported to mix mesh geometries with the basic shapes, but several mesh geometries are allowed.\n");
        exit(1);
	#endif
    }
    
    return 0;
};

void generate_children_lists(struct Volume_struct **Volumes, struct pointer_to_1d_int_list **true_children_lists, int number_of_volumes, int verbal) {
  // This function generates a list of children for each volume.
  // A volume m is a child of volume n, if the entire space ocupied by volume m is inside of the space ocupied by volume n
  // A volume m is a true child of volume n, if the entire space coupied by volume m after it's masks are applied is inside the volume ocupied by volume n after it's masks are applied
  
  
  MPI_MASTER(
  if (verbal) printf("\nGenerating children lists --------------------------- \n");
  )

  // Mask update: Creating a temporary list for each volume
  struct pointer_to_1d_int_list *temporary_children_lists;
  temporary_children_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list));

  // The surrounding vacuum, volume 0, done outside of for loop.
  temporary_children_lists[0].num_elements = number_of_volumes - 1;
  temporary_children_lists[0].elements = malloc(temporary_children_lists[0].num_elements*sizeof(int));
  
  int parent;
  for (parent=1;parent<number_of_volumes;parent++) {
      temporary_children_lists[0].elements[parent-1] = parent;
  }
  
  //if (verbal) printf("did temporary children list \n");
  //print_1d_int_list(temporary_children_lists[0],"temp children list [0]");
  
  // Hardcoding that every volume is a child of the surrounding vacuum
  Volumes[0]->geometry.children.num_elements = number_of_volumes-1;
  Volumes[0]->geometry.children.elements = malloc((number_of_volumes-1)*sizeof(int));
  true_children_lists[0] = malloc(sizeof(struct pointer_to_1d_int_list));
  true_children_lists[0]->num_elements = number_of_volumes - 1;
  true_children_lists[0]->elements = malloc((number_of_volumes-1)*sizeof(int));
  
  //if (verbal) printf("allocated true children lists \n");
  
  for (parent=1;parent<number_of_volumes;parent++) {
      Volumes[0]->geometry.children.elements[parent-1] = parent;
      true_children_lists[0]->elements[parent-1] = parent;
  }
  
  char string_output[128];
  MPI_MASTER(
  if (verbal) sprintf(string_output,"Children for Volume %d",0);
  if (verbal) print_1d_int_list(Volumes[0]->geometry.children,string_output);
  )
  
  
  // Generating the children lists for all other volumes using the appropriate geometry functions
  struct pointer_to_1d_int_list temp_list_local;
  temp_list_local.num_elements = number_of_volumes;
  temp_list_local.elements = malloc(number_of_volumes*sizeof(int));
  
  struct pointer_to_1d_int_list true_temp_list_local;
  true_temp_list_local.num_elements = number_of_volumes;
  true_temp_list_local.elements = malloc(number_of_volumes*sizeof(int));
  
  
  int child,used_elements,used_elements_true;
  for (parent=1;parent<number_of_volumes;parent++) {
      used_elements = 0;used_elements_true = 0;
      for (child=1;child<number_of_volumes;child++) {
        if (child != parent) {
          // Call inside_function that selects the proper within function for the two volumes
          if (1 == inside_function(Volumes[parent],Volumes[child])) {
            temp_list_local.elements[used_elements++] = child;
            true_temp_list_local.elements[used_elements_true++] = child;
          }
        } else true_temp_list_local.elements[used_elements_true++] = child; // Needed when children list takes mask into account
      }
      // Temp test
      allocate_list_from_temp(used_elements,temp_list_local,&Volumes[parent]->geometry.children);
      // Assing the children list to a temporary list as the masks have yet to be taken into account
      temporary_children_lists[parent].num_elements=0;
      allocate_list_from_temp(used_elements_true,true_temp_list_local,&temporary_children_lists[parent]);
      
      
      MPI_MASTER(
      if (verbal) sprintf(string_output,"Children for Volume %d (temporary_list)",parent);
      if (verbal) print_1d_int_list(temporary_children_lists[parent],string_output);
      )
      
      MPI_MASTER(
      if (verbal) sprintf(string_output,"Children for Volume %d (permanent_list)",parent);
      if (verbal) print_1d_int_list(Volumes[parent]->geometry.children,string_output);
      )
      
  }
  
  // mask update:
  // The logical expression: (child c parent AND child c parent_mask) OR (child_mask c parent AND child_mask c parent_mask)
  //  needs to be evaluated for each child / parent combination in order to take the masks of each into account
  
  int logic_var1,logic_var2,logic_var_ANY,logic_var_ALL;
  int mask_index,mask_index_child,mask_index_parent;
  int volume_C,volume_P;
  // Loop that takes masks into account
  for (parent=1;parent<number_of_volumes;parent++) {
    used_elements = 0;
    for (child=1;child<number_of_volumes;child++) {
     if (child != parent && 0 == on_int_list(Volumes[parent]->geometry.masked_by_list,child)) {
        // The children list for each volume does not need to contain the volume itself
        //  And a parent masked by it's child can not have that mask as a child
      
      // Here c means within in the sense of a set being part of another set
      // Logical expression to be evaluated: (child c parent AND child c parent_mask) OR (child_mask c parent AND child_mask c parent_mask)
      logic_var1 = on_int_list(temporary_children_lists[parent],child);
      if (logic_var1 == 1 && Volumes[parent]->geometry.is_masked_volume == 1) {
        // if the parent volume is masked, the child also need to be inclosed in these masks to fulfill this side of the logical expression
        logic_var_ANY = 0;
        for (mask_index=0;mask_index<Volumes[parent]->geometry.masked_by_list.num_elements;mask_index++) {
          if (0 == on_int_list(temporary_children_lists[Volumes[parent]->geometry.masked_by_list.elements[mask_index]],child)) {
            if (Volumes[parent]->geometry.mask_mode == 1) {
              logic_var1 = 0;
              break;
            }
          } else logic_var_ANY = 1;
        }
        if (Volumes[parent]->geometry.mask_mode == 2) logic_var1 = logic_var_ANY;
      }
      
      if (logic_var1 == 1) true_temp_list_local.elements[used_elements++] = child;
      else if (Volumes[child]->geometry.is_masked_volume == 1) {
        // If the first side of the logical expression is false, evalute the other side
        // The other side is only relevant if the child volume is masked, otherwise it is ignored
        //printf("Second side of logical expression \n");
        
        logic_var1 = 1; // Assume true
          
        // child_mask c parent
        logic_var_ALL = 0;
        for (mask_index=0;mask_index<Volumes[child]->geometry.masked_by_list.num_elements;mask_index++) {
          if (0 == on_int_list(temporary_children_lists[parent],Volumes[child]->geometry.masked_by_list.elements[mask_index])){
            if (Volumes[child]->geometry.mask_mode == 2) {
              logic_var1 = 0;
              break;
            }
          } else logic_var_ALL = 1;
        }
        if (Volumes[child]->geometry.mask_mode == 1) logic_var1 = logic_var_ALL;
        
        // This line allows the second part of the logical expression to be true in cases where the parent volume is not masked
        if (logic_var1 == 1 && Volumes[parent]->geometry.is_masked_volume == 0) true_temp_list_local.elements[used_elements++] = child;
        
        // There is no reason to check the other part (child_mask c parent_mask) if the first part was not true
        if (logic_var1 == 1 && Volumes[parent]->geometry.is_masked_volume == 1) {
          // This last part requires both the child and the parent to be masked
          // Need to evaluate (child_mask c parent_mask), where both can be a be a list of volume with ALL/ANY modes
          
          if (Volumes[parent]->geometry.mask_mode == 1) {
            logic_var2 = 1; // Assume the logical expression (child_mask c parent_mask) is true
            for (mask_index_parent=0;mask_index_parent<Volumes[parent]->geometry.masked_by_list.num_elements;mask_index_parent++) {
              // As the parent is in ALL mode, the child masks must be within ALL parent masks
              logic_var_ANY = 0; // Assume not a single child is within this parent
              for (mask_index_child=0;mask_index_child<Volumes[child]->geometry.masked_by_list.num_elements;mask_index_child++) {
                volume_P = Volumes[parent]->geometry.masked_by_list.elements[mask_index_parent];
                volume_C = Volumes[child]->geometry.masked_by_list.elements[mask_index_child];
                // Is volume C inside volume P? If yes, volume C must be on volume P's temporary children list
                if (0 == on_int_list(temporary_children_lists[volume_P],volume_C)) {
                  if (Volumes[child]->geometry.mask_mode == 2) {
                    // If child is in ANY mode, any one mask outside is enough to make the expression false
                    logic_var2 = 0;
                    break;
                  }
                } else logic_var_ANY = 1;
              }
              // If child is in ALL mode, then if any one child were within this mask, the logic expression holds true
              if (Volumes[child]->geometry.mask_mode == 1) logic_var2 = logic_var_ANY;
              if (logic_var2 == 0) break; // No need to continue
            }
          } else if (Volumes[parent]->geometry.mask_mode == 2) {
            // If the parent is in ANY mode, it is enough if the child masks are within just 1 of the parent masks
            for (mask_index_parent=0;mask_index_parent<Volumes[parent]->geometry.masked_by_list.num_elements;mask_index_parent++) {
              logic_var2 = 1; // Assume the logical expression (child_mask c parent_mask) is true
              logic_var_ANY = 0; // Assume not a single child is within this parent
              for (mask_index_child=0;mask_index_child<Volumes[child]->geometry.masked_by_list.num_elements;mask_index_child++) {
                volume_P = Volumes[parent]->geometry.masked_by_list.elements[mask_index_parent];
                volume_C = Volumes[child]->geometry.masked_by_list.elements[mask_index_child];
                // Is volume C inside volume P? If yes, volume C must be on volume P's temporary children list
                if (0 == on_int_list(temporary_children_lists[volume_P],volume_C)) {
                  if (Volumes[child]->geometry.mask_mode == 2) {
                    // If child is in ANY mode, any one mask outside is enough to make the expression false
                    logic_var2 = 0;
                    break;
                  }
                } else logic_var_ANY = 1;
              }
              // If child is in ALL mode, then if any one child were within this mask, the logic expression holds true
              if (Volumes[child]->geometry.mask_mode == 1) logic_var2 = logic_var_ANY;
              if (logic_var2 == 1) break; // No need to continue
            }
          }
          
          // if this point is reached, and logic_var2 is true, volume[child] is a child of volume[parent]
          if (logic_var2 == 1) true_temp_list_local.elements[used_elements++] = child;
          
        }
      }
     }
    }
    
    true_children_lists[parent] = malloc(sizeof(struct pointer_to_1d_int_list));
    true_children_lists[parent]->num_elements = 0;
    allocate_list_from_temp(used_elements,true_temp_list_local,true_children_lists[parent]);
    
    MPI_MASTER(
      if (verbal) sprintf(string_output,"True children for Volume (post mask) %d",parent);
      if (verbal) print_1d_int_list(*true_children_lists[parent],string_output);
    )
  }
  
  
  // Clean up of dynamically allocated memory
  
  for(child=0;child<number_of_volumes;child++) free(temporary_children_lists[child].elements);
  free(temporary_children_lists);
  free(true_temp_list_local.elements);
  free(temp_list_local.elements);
};

void generate_overlap_lists(struct pointer_to_1d_int_list **true_overlap_lists, struct pointer_to_1d_int_list **raw_overlap_lists, struct Volume_struct **Volumes, int number_of_volumes, int verbal) {
  // This function generates overlap lists for each volume
  // Volume m overlaps volume n if there is some subset of space they both ocupy (this is called raw overlap)
  // the true overlap list takes mask into account, meaning that the masks are applied before searching for overlap

  MPI_MASTER(
  if (verbal) printf("\nGenerating overlap lists ---------------------------- \n");
  )
  
  // Manually create the overlap list for the surrounding Vacuum, as it overlaps all other volumes
  
  // temporary_overlap_lists are used to save the calculated overlaps to avoid evaluating the heavy functions more than once (twice) for each possible volume pair
  struct pointer_to_1d_int_list *temporary_overlap_lists;
  temporary_overlap_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list));
  
  // Overlap_lists are the final result of the function, and for the surrounding volume it can be set immediatly
  true_overlap_lists[0] = malloc(sizeof(struct pointer_to_1d_int_list));
  true_overlap_lists[0]->num_elements = number_of_volumes-1;
  true_overlap_lists[0]->elements = malloc((number_of_volumes-1)*sizeof(int));
  
  raw_overlap_lists[0] =  malloc(sizeof(struct pointer_to_1d_int_list));
  raw_overlap_lists[0]->num_elements = number_of_volumes;
  raw_overlap_lists[0]->elements = malloc(number_of_volumes*sizeof(int));
  raw_overlap_lists[0]->elements[0] = 0; // Volume 0 overlaps itself
  
  int parent;
  for (parent=1;parent<number_of_volumes;parent++) {
      true_overlap_lists[0]->elements[parent-1] = parent;
      raw_overlap_lists[0]->elements[parent] = parent;
  }
  
  char string_output[128];
  MPI_MASTER(
  if (verbal) sprintf(string_output,"Overlaps for Volume %d",0);
  if (verbal) print_1d_int_list(*true_overlap_lists[0],string_output);
  )
  
  // Generate the overlap lists for the remaining volumes
  struct pointer_to_1d_int_list temp_list_local;
  temp_list_local.num_elements = number_of_volumes;
  temp_list_local.elements = malloc(number_of_volumes*sizeof(int));
  
  int child,used_elements;
  // Create overlap for the remaining volumes
  for (parent=1;parent<number_of_volumes;parent++) {
      true_overlap_lists[parent] = malloc(sizeof(struct pointer_to_1d_int_list));
      used_elements = 0;
      temp_list_local.elements[used_elements++] = 0; // Alwasy overlaps with the surrounding vacuum.
      
      for (child=1;child<number_of_volumes;child++) {
        if (child == parent) temp_list_local.elements[used_elements++] = child;
        else {
        if (strcmp("sphere",Volumes[parent]->geometry.shape) == 0 && strcmp("sphere",Volumes[child]->geometry.shape) == 0) {
            if (sphere_overlaps_sphere(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("cylinder",Volumes[parent]->geometry.shape) == 0 && strcmp("cylinder",Volumes[child]->geometry.shape) == 0) {
            if (cylinder_overlaps_cylinder(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("box",Volumes[parent]->geometry.shape) == 0 && strcmp("box",Volumes[child]->geometry.shape) == 0) {
            if (box_overlaps_box(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("cone",Volumes[parent]->geometry.shape) == 0 && strcmp("cone",Volumes[child]->geometry.shape) == 0) {
            if (cone_overlaps_cone(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("mesh",Volumes[parent]->geometry.shape) == 0 && strcmp("mesh",Volumes[child]->geometry.shape) == 0) {
            if (mesh_overlaps_mesh(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("box",Volumes[parent]->geometry.shape) == 0 && strcmp("cylinder",Volumes[child]->geometry.shape) == 0) {
            if (box_overlaps_cylinder(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("cylinder",Volumes[parent]->geometry.shape) == 0 && strcmp("box",Volumes[child]->geometry.shape) == 0) {
            if (cylinder_overlaps_box(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("box",Volumes[parent]->geometry.shape) == 0 && strcmp("sphere",Volumes[child]->geometry.shape) == 0) {
            if (box_overlaps_sphere(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("sphere",Volumes[parent]->geometry.shape) == 0 && strcmp("box",Volumes[child]->geometry.shape) == 0) {
            if (sphere_overlaps_box(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("sphere",Volumes[parent]->geometry.shape) == 0 && strcmp("cylinder",Volumes[child]->geometry.shape) == 0) {
            if (sphere_overlaps_cylinder(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("cylinder",Volumes[parent]->geometry.shape) == 0 && strcmp("sphere",Volumes[child]->geometry.shape) == 0) {
            if (cylinder_overlaps_sphere(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("cone",Volumes[parent]->geometry.shape) == 0 && strcmp("sphere",Volumes[child]->geometry.shape) == 0) {
            if (cone_overlaps_sphere(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("sphere",Volumes[parent]->geometry.shape) == 0 && strcmp("cone",Volumes[child]->geometry.shape) == 0) {
            if (sphere_overlaps_cone(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("cone",Volumes[parent]->geometry.shape) == 0 && strcmp("cylinder",Volumes[child]->geometry.shape) == 0) {
            if (cone_overlaps_cylinder(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("cylinder",Volumes[parent]->geometry.shape) == 0 && strcmp("cone",Volumes[child]->geometry.shape) == 0) {
            if (cylinder_overlaps_cone(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("cone",Volumes[parent]->geometry.shape) == 0 && strcmp("box",Volumes[child]->geometry.shape) == 0) {
            if (cone_overlaps_box(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("box",Volumes[parent]->geometry.shape) == 0 && strcmp("cone",Volumes[child]->geometry.shape) == 0) {
            if (box_overlaps_cone(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else {
            printf("Need overlap function for type: ");
            printf("%s",Volumes[parent]->geometry.shape);
            printf(" and type: ");
            printf("%s",Volumes[child]->geometry.shape);
            printf(".\n");
            printf("It is not yet supported to mix mesh geometries with the basic shapes, but several mesh geometries are allowed.\n");
            exit(1);
        }
        }
      }
      //allocate_list_from_temp(used_elements,temp_list_local,overlap_lists[parent]);
      allocate_list_from_temp(used_elements,temp_list_local,&temporary_overlap_lists[parent]);
      
      // Save the raw overlap data to the raw_overlap_lists[parent] list
      raw_overlap_lists[parent] = malloc(sizeof(struct pointer_to_1d_int_list));
      raw_overlap_lists[parent]->num_elements = 0;
      allocate_list_from_temp(used_elements,temp_list_local,raw_overlap_lists[parent]);
      
      if (verbal) sprintf(string_output,"Overlaps for Volume (pre mask) %d",parent);
      MPI_MASTER(
      if (verbal) print_1d_int_list(temporary_overlap_lists[parent],string_output);
      )
  }
  
  
  // The temporary_overlap_lists gives the raw overlap data for all volume pairs
  // The next tasks is to take the masks into account, so that a volume is only said to overlap another if all of these statements are true:
  // The volumes overlap each other
  // The mask's of volume 1 overlap volume 2
  // The mask's of volume 2 overlap volume 1
  // The mask's of volume 1 overlap the masks of volume 2
  
  
  int logic_var;
  int overlap_ANY,overlap_ANY_p,overlap_ANY_c;
  int mask_index,mask_index_c,mask_index_p;
  int mask_volume_index,mask_volume_index_p,mask_volume_index_c;

  for (parent=1;parent<number_of_volumes;parent++) {
    used_elements = 0;
    temp_list_local.elements[used_elements++] = 0; // Alwasy overlaps with the surrounding vacuum.
    for (child=1;child<number_of_volumes;child++) {
      if (child != parent) {
        logic_var = 1; // Assume the volumes overlap, and search for evidence that they do not.
      
        // First check if the volumes overlap
        if (0 == on_int_list(temporary_overlap_lists[parent],child)) logic_var = 0;
        
      
        // Check if child overlap with parents masks
        if (logic_var == 1 && Volumes[parent]->geometry.is_masked_volume == 1) {
          overlap_ANY = 0;
          for (mask_index=0;mask_index<Volumes[parent]->geometry.masked_by_list.num_elements;mask_index++) {
            mask_volume_index = Volumes[parent]->geometry.masked_by_list.elements[mask_index];
            if (0 == on_int_list(temporary_overlap_lists[mask_volume_index],child)) {
              if (Volumes[parent]->geometry.mask_mode == 1) {
                logic_var = 0;
                break;
              }
            } else overlap_ANY = 1;
          }
          if (Volumes[parent]->geometry.mask_mode == 2) logic_var = overlap_ANY;
        }
      
        // Check if parent overlap with childs masks
        if (logic_var == 1 && Volumes[child]->geometry.is_masked_volume == 1) {
          overlap_ANY = 0;
          for (mask_index=0;mask_index<Volumes[child]->geometry.masked_by_list.num_elements;mask_index++) {
            mask_volume_index = Volumes[child]->geometry.masked_by_list.elements[mask_index];
            if (0 == on_int_list(temporary_overlap_lists[mask_volume_index],parent)) {
              if (Volumes[child]->geometry.mask_mode == 1) {
                logic_var = 0;
                break;
              }
            } else overlap_ANY = 1;
          }
          if (Volumes[child]->geometry.mask_mode == 2) logic_var = overlap_ANY;
        }
      
        // Check if parents masks overlap childrens masks
        if (logic_var == 1 && Volumes[parent]->geometry.is_masked_volume == 1 && Volumes[child]->geometry.is_masked_volume == 1) {
          overlap_ANY = 0;
          for (mask_index_p=0;mask_index_p<Volumes[parent]->geometry.masked_by_list.num_elements;mask_index_p++) {
            mask_volume_index_p = Volumes[parent]->geometry.masked_by_list.elements[mask_index_p];
            overlap_ANY_p = 1;
            overlap_ANY_c = 0;
            for (mask_index_c=0;mask_index_c<Volumes[child]->geometry.masked_by_list.num_elements;mask_index_c++) {
              mask_volume_index_c = Volumes[child]->geometry.masked_by_list.elements[mask_index_c];
              if (0 == on_int_list(temporary_overlap_lists[mask_volume_index_p],mask_volume_index_c)) {
                if (Volumes[parent]->geometry.mask_mode == 1 && Volumes[child]->geometry.mask_mode == 1) {
                  // If both are in ALL mode and just one combination of masks does not overlap, neither does the common set
                  logic_var = 0;
                  break;
                }
                if (Volumes[parent]->geometry.mask_mode == 2 && Volumes[child]->geometry.mask_mode == 1) {
                  // If the parent is in ANY mode, but the child is in ALL, any one child not overlapping this parent mask, stops the chance for this parent mask
                  overlap_ANY_p = 0;
                  break;
                }
                
              } else {
                // Here because mask_volume_index_p and mask_volume_index_c does overlap
                if (Volumes[parent]->geometry.mask_mode == 1 && Volumes[child]->geometry.mask_mode == 2) {
                  // If the parent is in ALL mode and the child is in ANY mode, stop if a single parent volume does not overlap any child
                  overlap_ANY_c = 1;
                }
                if (Volumes[parent]->geometry.mask_mode == 2 && Volumes[child]->geometry.mask_mode == 2) {
                  // If both parent and child are in any mode, any one overlap between the masks is sufficient
                  overlap_ANY = 1;
                  // Could actually just commit to the overlap list here, and stop all loops.
                }
              }
              
            }
            if (Volumes[parent]->geometry.mask_mode == 1 && Volumes[child]->geometry.mask_mode == 2) logic_var = overlap_ANY_c;
            if (Volumes[parent]->geometry.mask_mode == 2 && Volumes[child]->geometry.mask_mode == 1 && overlap_ANY_p == 1) {
              // When parent is in any mode, and child is in ALL mode, any parent mask that overlaps all children masks is enough to end the parent loop
              logic_var = 1;
              break; // Without this break, only the last parent will matter
            }
            // if (overlap_ANY == 1) break; would speed things up a bit, but only after testing has been started and then it will be repeated
          }
          if (Volumes[parent]->geometry.mask_mode == 2 && Volumes[child]->geometry.mask_mode == 2) logic_var = overlap_ANY;
          // If both volumes have the ANY mode, just one case of overlap is enough.
        }
        
        // If all of the 4 statements above evaluate to true, the two volumes parent and child do overlap and it is added to the list.
        if (logic_var == 1) temp_list_local.elements[used_elements++] = child;
        
      }
    }
    // Allocate the actual overlap list with the new temp_list_local
    allocate_list_from_temp(used_elements,temp_list_local,true_overlap_lists[parent]);
    if (verbal) sprintf(string_output,"Overlaps for Volume (post mask) %d",parent);
    MPI_MASTER(
    if (verbal) print_1d_int_list(*true_overlap_lists[parent],string_output);
    )
  }
  
  // Clean up of dynamically allocated memory
  for(child=1;child<number_of_volumes;child++) free(temporary_overlap_lists[child].elements);
  free(temporary_overlap_lists);
  free(temp_list_local.elements);
  
};

void add_to_mask_intersect_list(struct pointer_to_1d_int_list *mask_intersect_list, int given_volume_index) {
    // A bit simpler than before
    if (on_int_list(*mask_intersect_list,given_volume_index) == 0)
      add_element_to_int_list(mask_intersect_list,given_volume_index);
}


void generate_intersect_check_lists(struct pointer_to_1d_int_list **overlap_lists,struct Volume_struct **Volumes, int number_of_volumes, int verbal) {
  // Generate intersection list for each volume
  MPI_MASTER(
  if (verbal) printf("\nGenerating intersect check lists -------------------- \n");
  )
  // Take overlap list for volume n
  // Remove entries with p < p(n)
  // Remove entries with parents on the list
  
  // Mask update: Mask volumes does not have priorities, do not do step 2 for mask volumes
  // Mask update: Instead of step 3 (Remove entries with parents on the list), move these entries to a Mask intersect list if that parent is masking the entry, otherwise remove
  
  // 1) Mask update: Take overlap list for volume n
  // 2) Mask update: Remove entries with p < p(n), except mask volumes, that are moved to another list A
  // 3) Mask update: Entries with parents on the list are removed (if these parents are masked, only remove the entry if it is a child of their parents masks)
  // 4) Possible optimization: Entries with parents on the A list, are moved to the appropriate mask list for this volume
  // 5) Mask update: Entries on list A are added again, if they are not parents of volume n
  // 6) Mask update: Entries on the main list that are masked are moved to the appropriate mask list for this volume, if their mask is on list A
  
  // Justification of the top algorithm:
  // No need to check intersections with something that does not overlap this volume, so start with intersect list and remove from that.
  // Entries with p < p(n) are "beneath" this volume, and thus doesn't cut it, mask volumes are however always "above" and are treated with care (moved to list A)
  // Entries with parents still on the list is removed, as it is impossible to intersect with them without first going through the parent
  // Entries with parents on the A list can only be seen if the ray is in that particular mask (the parent on the A list), so it can be added to the appropriate mask list for this volume
  // Now all masks that cut the volume are transfered back to the main list, the only masks that overlap and does not cut are parents of this volume, and these are thus not added
  // The entries on the list that are masked are added to the appropriate mask lists of this volume, but only if their mask is on the A list, as only masks on list A can have mask status 1 which is required for this to be relevant (this was true automatically for the ones added to the appropriate lists in the optimization step).
  
  
  // The intersect check list for volume 0 is done manually
  // Declare list for holding logic data for each index in the overlap list of this volume
  struct pointer_to_1d_int_list logic_list;
  logic_list.num_elements = overlap_lists[0]->num_elements;
  logic_list.elements = malloc(logic_list.num_elements * sizeof(int));
  
  // Declare similar list for List A
  struct pointer_to_1d_int_list mask_logic_list;
  mask_logic_list.num_elements = overlap_lists[0]->num_elements;
  mask_logic_list.elements = malloc(mask_logic_list.num_elements * sizeof(int));
  
  
  int iterate;
  int overlap_index;
  
  /*
  // Thise code is an old broken version of intersection_check_list generation only for volume 0, but this case is now included in the loop.
  // Assume all volumes on overlap list should be on intersection check list, and remove the once that should not.
  for (iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 1;
  // Asuume no volumes should go on the mask_logic_list, but add the ones that do.
  for (iterate=0;iterate<mask_logic_list.num_elements;iterate++) mask_logic_list.elements[iterate] = 0;
  
  for (overlap_index = 0;overlap_index < overlap_lists[0]->num_elements;overlap_index++) {
    // No volumes to remove with lower priority, as surrounding vacuum have the lowest.
    
    // Check if this overlap_lists[0]->elements[overlap_index] is a child of another member of the overlap list
    for (iterate = 0;iterate < overlap_lists[0]->num_elements;iterate++) {
        if (iterate != overlap_index) {
            // We are now checking if Volumes[overlap_lists[0]->elements[iterate]]->geometry.children contains overlap_lists[overlap_index]
            if (on_int_list(Volumes[overlap_lists[0]->elements[iterate]]->geometry.children,overlap_lists[0]->elements[overlap_index])) logic_list.elements[overlap_index] = 0;
        }
    }
  }

  Volumes[0]->geometry.intersect_check_list.num_elements = sum_int_list(logic_list);
  Volumes[0]->geometry.intersect_check_list.elements = malloc(Volumes[0]->geometry.intersect_check_list.num_elements * sizeof(int));
  
  iterate = 0;
  for (overlap_index = 0;overlap_index < overlap_lists[0]->num_elements;overlap_index++) {
        if (logic_list.elements[overlap_index])   Volumes[0]->geometry.intersect_check_list.elements[iterate++] = overlap_lists[0]->elements[overlap_index];
  }
  if (logic_list.num_elements > 0) free(logic_list.elements);
  if (mask_logic_list.num_elements > 0) free(mask_logic_list.elements);
  
  MPI_MASTER(
  print_1d_int_list(Volumes[0]->geometry.intersect_check_list,"Intersect check list for Volume 0 (manual)");
  )
  */
  
  // The intersect check lists for the remaining volumes are generated
  int volume_index,mask_volume_number,mask_index;
  //for (volume_index = 1;volume_index < number_of_volumes;volume_index++) {
  for (volume_index = 0;volume_index < number_of_volumes;volume_index++) {
    
      // 1) Take overlap list for volume n
      logic_list.num_elements = overlap_lists[volume_index]->num_elements;
      logic_list.elements = malloc(logic_list.num_elements * sizeof(int));
      mask_logic_list.num_elements = overlap_lists[volume_index]->num_elements;
      mask_logic_list.elements = malloc(mask_logic_list.num_elements * sizeof(int));
      
      
      
      if (Volumes[volume_index]->geometry.is_mask_volume == 1) {
        // Masks do not have any entries on their intersect check list, as they are never the current volume
        for (iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 0;
        for (iterate=0;iterate<mask_logic_list.num_elements;iterate++) mask_logic_list.elements[iterate] = 0;
      } else {
        for (iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 1;
        for (iterate=0;iterate<mask_logic_list.num_elements;iterate++) mask_logic_list.elements[iterate] = 0;
      }
      
      //2) Remove entries with p < p(n), except mask volumes, that are moved to another list A
      for (overlap_index = 0;overlap_index < overlap_lists[volume_index]->num_elements;overlap_index++) {
        
        if (overlap_lists[volume_index]->elements[overlap_index] == 0) logic_list.elements[overlap_index] = 0; // The surrounding vacuum has lower priority (and is not a mask)
        else if (Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.is_mask_volume == 1) {
           logic_list.elements[overlap_index] = 0;      // Remove this volume from the main list
           mask_logic_list.elements[overlap_index] = 1; // Add mask volumes to seperat list
        }
        else if (volume_index != 0) { // Only relevant to remove elements because of priority if the volume_index is different from 0, meaning the surrounding vacuum skips this step
           if (Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.priority_value < Volumes[volume_index]->geometry.priority_value) {
           // If the investigated volume on the overlap_list have lower priority than the volume with volume_index, remove it
           logic_list.elements[overlap_index] = 0; // Remove this volume from the list
           }
        }
      }
      
      // 3) Entries with parents on the list are removed
      for (overlap_index = 0;overlap_index < overlap_lists[volume_index]->num_elements;overlap_index++) {
        // Check if this overlap_lists[0]->elements[overlap_index] is a child of another member of the overlap list
        for (iterate = 0;iterate < overlap_lists[volume_index]->num_elements;iterate++) {
             if (iterate != overlap_index) { // Only necessary if a volume determines that it has itself as child, but a nice safety.
                // We are now checking if Volumes[overlap_lists[volume_index]->elements[iterate]]->geometry.children contains overlap_lists[overlap_index]
                // The && part is needed because we do not remove children of elements that have allready been removed because of their priority
                
                // if (on_int_list(Volumes[overlap_lists[volume_index]->elements[iterate]]->geometry.children,overlap_lists[volume_index]->elements[overlap_index]) && logic_list.elements[overlap_lists[volume_index]->elements[iterate]] == 1) {logic_list.elements[overlap_index] = 0; bug fixed on 3/4 2016
                if (on_int_list(Volumes[overlap_lists[volume_index]->elements[iterate]]->geometry.children,overlap_lists[volume_index]->elements[overlap_index]) && logic_list.elements[iterate] == 1) logic_list.elements[overlap_index] = 0;
                
                /*
                Explanation with simpler notation
                
                Overlap list of volume i = O_i
                Element j of overlap list i = O_i(j)
                
                Children list of volume i = C_i
                Element j on children list i = C_i(j)
                
                Priority of volume i p(i)
                
                for i = volumes
                    for j in O_i(j) 
                        logic(j) = 1;
                        if p(i) > p(O_i(j)) logic(j) = 0; // Remove if lower priority than the currently checked
                        
                        for k in O_i(k)
                            if (O_i(j) is contained on the list C_k && logic(k) == 1) logic(j) = 0
                */
                
                // 4) Entries with parents on the A list, are moved to the appropriate mask list for this volume
                if (on_int_list(Volumes[overlap_lists[volume_index]->elements[iterate]]->geometry.children,overlap_lists[volume_index]->elements[overlap_index]) && mask_logic_list.elements[iterate] == 1) {
                   logic_list.elements[overlap_index] = 0; // Remove from main list (Not strictly needed as it will be removed already if it is on list A)
                   // Add overlap_lists[volume_index]->elements[overlap_index] to volumes mask intersect list for mask with index overlap_lists[volume_index]->elements[iterate]
                   //add_to_mask_intersect_lists(&Volumes[volume_index]->geometry.mask_intersect_lists,overlap_lists[volume_index]->elements[iterate],overlap_lists[volume_index]->elements[overlap_index]);
                   // Simpler method that just collects all the masked intersect parts on a 1d_int list instead of seperating them for each mask
                   add_to_mask_intersect_list(&Volumes[volume_index]->geometry.mask_intersect_list,overlap_lists[volume_index]->elements[overlap_index]);
                }
                
             }
        }
      }
      
      // 5) Entries on list A are added again, if they are not parents of volume n
      // Time to add mask volumes removed back to the intersect list, if they are not parents of the current volume, meaning the current volume should not be a child of the mask
      for (overlap_index = 0;overlap_index < overlap_lists[volume_index]->num_elements;overlap_index++) {
        if (mask_logic_list.elements[overlap_index] == 1) {
          mask_volume_number = overlap_lists[volume_index]->elements[overlap_index];
          if (on_int_list(Volumes[mask_volume_number]->geometry.children,volume_index) == 0) logic_list.elements[overlap_index] = 1; // Add it back to the list
        }
      }
      
      // 6) Entries on the main list that are masked are moved to the appropriate mask list for this volume, if their mask is on list A
      int mask_index,mask_mode,found_index,logic_ALL;
      for (overlap_index = 0;overlap_index < overlap_lists[volume_index]->num_elements;overlap_index++) {
        if (logic_list.elements[overlap_index] == 1 && Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.is_masked_volume == 1) {
          logic_list.elements[overlap_index] = 0; // If the volume is masked, remove it from the intersect check list
          // Could actually keep it on the intersect list if the volume's mask is a parent of the current volume, now it will just be added in all cases
          
          // When ALL setting is used, all masks should overlap the current volume, otherwise the user probably made a mistake, this should be checked in error checking
          // When ANY setting is used, at least one mask should overlap the current volume, otherwise the user probably made a mistake, this should be checked in error checking
          // mask_mode == 1 => ALL / mask_mode == 2 => ANY
          mask_mode = Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.mask_mode;
          
          if (mask_mode == 1) {
            logic_ALL = 1;
            for (iterate=0;iterate<Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.masked_by_list.num_elements;iterate++) {
              mask_index = Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.masked_by_list.elements[iterate];
              
              if (on_int_list(*overlap_lists[volume_index],mask_index) == 0) {
                // If any one of the volumes masks do not overlap with this volume, there is no reason check intersections with the volume regardless of the mask status's
                logic_ALL = 0;
                break;
              }
            }
          if (logic_ALL == 1) {
            // If all of it's masks are overlapping the current volume, add it to all relevant mask intersect lists of this volume
            for (iterate=0;iterate<Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.masked_by_list.num_elements;iterate++) {
              mask_index = Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.masked_by_list.elements[iterate];
              //add_to_mask_intersect_lists(&Volumes[volume_index]->geometry.mask_intersect_lists,mask_index,overlap_lists[volume_index]->elements[overlap_index]);
              // Adding the masked intersect list elements to another list
              add_to_mask_intersect_list(&Volumes[volume_index]->geometry.mask_intersect_list,overlap_lists[volume_index]->elements[overlap_index]);
            }
          }
            
            
          } else if (mask_mode == 2) {
          // When in ANY mode, the problem is easier, as not all masks have to overlap the volume, and we can add each one to the list
            for (iterate=0;iterate<Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.masked_by_list.num_elements;iterate++) {
              mask_index = Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.masked_by_list.elements[iterate];
            
              if (on_int_list(*overlap_lists[volume_index],mask_index) == 1) {
                //add_to_mask_intersect_lists(&Volumes[volume_index]->geometry.mask_intersect_lists,mask_index,overlap_lists[volume_index]->elements[overlap_index]);
                // Adding the masked intersect list elements to another list
                add_to_mask_intersect_list(&Volumes[volume_index]->geometry.mask_intersect_list,overlap_lists[volume_index]->elements[overlap_index]);
              }
            }
            
          }
        }
      }
      
      
      Volumes[volume_index]->geometry.intersect_check_list.num_elements = sum_int_list(logic_list);
      Volumes[volume_index]->geometry.intersect_check_list.elements = malloc(Volumes[volume_index]->geometry.intersect_check_list.num_elements * sizeof(int));
      
      iterate = 0;
      for (overlap_index = 0;overlap_index < overlap_lists[volume_index]->num_elements;overlap_index++) {
            if (logic_list.elements[overlap_index])   Volumes[volume_index]->geometry.intersect_check_list.elements[iterate++] = overlap_lists[volume_index]->elements[overlap_index];
      }
      free(logic_list.elements); // Need to be careful with names for variables to be freed, as they can collide with names in main because of automatic declaration of external variables
      free(mask_logic_list.elements);
      
      
      char string_output[128];
      MPI_MASTER(
      if (verbal) sprintf(string_output,"Intersect check list for Volume %d",volume_index);
      if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.intersect_check_list,string_output);
      if (verbal) sprintf(string_output,"Mask intersect check list for Volume %d",volume_index);
      if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.mask_intersect_list,string_output);
      /*
      if (verbal) {
        for (mask_index=0;mask_index<Volumes[volume_index]->geometry.mask_intersect_lists.number_of_lists;mask_index++) {
          sprintf(string_output," - mask intersect check list for mask %d which is volume %d",mask_index,Volumes[volume_index]->geometry.mask_intersect_lists.mask_indices.elements[mask_index]);
          print_1d_int_list(Volumes[volume_index]->geometry.mask_intersect_lists.lists[mask_index],string_output);
        }
      }
      */
      
      )
  }
};

void generate_parents_lists(struct pointer_to_1d_int_list **parents_lists, struct Volume_struct **Volumes, int number_of_volumes, int verbal, int mask_mode) {
  // Function for generating parent lists for all volumes
  // A volume m has n as a parent, if volume n has volume m as a child
  
  // if mask_mode == 0, masks are ignored, if mask_mode == 1, masks are taken into account.
  
  MPI_MASTER(
  if (verbal) {
    if (mask_mode == 1)
        printf("\nGenerating parents lists ---------------------------- \n");
    else if (mask_mode == 0)
        printf("\nGenerating parents lists (ignoring masks) ----------- \n");
    else {
        printf("Error, the function parents_lists got a non defined mask_mode");
        exit(1);
    }
  }
  
  )
  // Volume iterate has volume p as a parent, if volume p has volume iterate as child.
  
  struct pointer_to_1d_int_list temp_list_local;
  temp_list_local.num_elements = number_of_volumes;
  temp_list_local.elements = malloc(number_of_volumes*sizeof(int));
  
  // Loop over
  int iterate,parent,used_elements;
  for (iterate = 0;iterate < number_of_volumes;iterate++) {
    // clear temp list
    used_elements = 0;
    parents_lists[iterate] = malloc(sizeof(struct pointer_to_1d_int_list));
    for (parent = 0;parent < number_of_volumes;parent++) {
        if (on_int_list(Volumes[parent]->geometry.children,iterate))
          if (mask_mode == 1 || (Volumes[parent]->geometry.is_mask_volume == 0 && Volumes[iterate]->geometry.is_mask_volume == 0))
            temp_list_local.elements[used_elements++] = parent;
    }
      allocate_list_from_temp(used_elements,temp_list_local,parents_lists[iterate]);
      
      char string_output[128];
      MPI_MASTER(
      if (verbal) sprintf(string_output,"Parents for Volume %d",iterate);
      if (verbal) print_1d_int_list(*parents_lists[iterate],string_output);
      )
  }
  free(temp_list_local.elements);

};

void generate_true_parents_lists(struct pointer_to_1d_int_list **parents_lists, struct pointer_to_1d_int_list **true_children_lists, struct Volume_struct **Volumes, int number_of_volumes, int verbal, int mask_mode) {
  // Function for generating parent lists for all volumes
  // A volume m has n as a parent, if volume n has volume m as a child
  
  // if mask_mode == 0, masks are ignored, if mask_mode == 1, masks are taken into account.
  
  MPI_MASTER(
  if (verbal) {
    if (mask_mode == 1)
        printf("\nGenerating parents lists ---------------------------- \n");
    else if (mask_mode == 0)
        printf("\nGenerating parents lists (ignoring masks) ----------- \n");
    else {
        printf("Error, the function parents_lists got a non defined mask_mode");
        exit(1);
    }
  }
  
  )
  // Volume iterate has volume p as a parent, if volume p has volume iterate as child.
  
  struct pointer_to_1d_int_list temp_list_local;
  temp_list_local.num_elements = number_of_volumes;
  temp_list_local.elements = malloc(number_of_volumes*sizeof(int));
  
  // Loop over
  int iterate,parent,used_elements;
  for (iterate = 0;iterate < number_of_volumes;iterate++) {
    // clear temp list
    used_elements = 0;
    parents_lists[iterate] = malloc(sizeof(struct pointer_to_1d_int_list)); // allocate_list_from_temp allocates
    for (parent = 0;parent < number_of_volumes;parent++) {
        //if (on_int_list(Volumes[parent]->geometry.children,iterate))
        if (on_int_list(*true_children_lists[parent],iterate))
          if (mask_mode == 1 || (Volumes[parent]->geometry.is_mask_volume == 0 && Volumes[iterate]->geometry.is_mask_volume == 0))
            temp_list_local.elements[used_elements++] = parent;
    }
      allocate_list_from_temp(used_elements,temp_list_local,parents_lists[iterate]);
      
      char string_output[128];
      MPI_MASTER(
      if (verbal) sprintf(string_output,"Parents for Volume %d",iterate);
      if (verbal) print_1d_int_list(*parents_lists[iterate],string_output);
      )
  }
  free(temp_list_local.elements);

};

void generate_intersect_check_lists_experimental(struct pointer_to_1d_int_list **true_overlap_lists, struct pointer_to_1d_int_list **raw_overlap_lists, struct pointer_to_1d_int_list **parents_lists, struct pointer_to_1d_int_list **true_parents_lists , struct Volume_struct **Volumes, int number_of_volumes, int verbal) {
  // Generates the intersect_check_list and mask_intersect_list for each Volume.
  /*
  Description of needed lists for volume n:
  Children list: List of volumes that is contained within volume n (is stored in the Volumes struct)
  True Children list: List of volumes that when masked by their masks is contained within volume n when masked by it's masks
  
  Parents list: List of volumes that contains volume n
  True parents list: List of volumes that when masked by their masks contains volume n when it is masked by it's masks
  
  raw overlap list: List of volumes whos geometry overlaps the geometry of volume n
  true overlap list: List of volumes whos geometry overlaps the geometry of volume n when the masks of both volumes are applied
  
  
  
  The algorithm:
  
  1) Take the true overlap list for volume n
  2) remove parents of n (normal parent list, with masks)
  3) remove volumes that do not mask n and are true parents of n
  4) remove volumes that are not masks and have lower priority than n
  5) remove volumes that have at least one true parent on the list (in step 4) that is not a mask volume
  6) split the list into two, the intersect_check_list which is all non masked volumes still on the list, and the mask_intersect_list which is the masked volumes
  7) remove volumes on the mask_intersect_list whos mask does not overlap (standard overlap list) n
  
  In step 5 the order in which the volumes are tested and removed may matter, so it is specifically stated that it is as the list looked in step 4.
  */
  
  MPI_MASTER(
  if (verbal) printf("\nGenerating intersect check lists -------------------- \n");
  )
  
  struct pointer_to_1d_int_list work_list;
  struct pointer_to_1d_int_list logic_list;
  
  int volume_index,iterate,parent,mask_index,masked_volume_index,ANY_logic,true_parent_volume_number;
  int *mask_check,*mask_start;
  for (volume_index = 0;volume_index < number_of_volumes;volume_index++) {
    
    // 1) Take the true overlap list for volume n
    // Create copy of true_overlap_lists to work with
    
    if (Volumes[volume_index]->geometry.is_mask_volume == 1) {
      // Bug fixed on 26/11/2016, do not create intersection lists for masks as they are not used, and affects destinations lists in a problematic way
      Volumes[volume_index]->geometry.intersect_check_list.num_elements = 0;
      Volumes[volume_index]->geometry.mask_intersect_list.num_elements = 0;
      
    } else {
      work_list.num_elements = true_overlap_lists[volume_index]->num_elements;
      work_list.elements = malloc(work_list.num_elements * sizeof(int));
      for (iterate=0;iterate<work_list.num_elements;iterate++) work_list.elements[iterate] = true_overlap_lists[volume_index]->elements[iterate];
    
    //if (verbal) print_1d_int_list(work_list,"After 1)");
    
    
    //2) remove parents of n (normal parent list, with masks)
    for (iterate=work_list.num_elements-1;iterate>-1;iterate--) {
      if (on_int_list(*parents_lists[volume_index],work_list.elements[iterate]))
        remove_element_in_list_by_index(&work_list,iterate);
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 2)");
    
    //3) remove volumes that do not mask n and are true parents of n
    for (iterate=work_list.num_elements-1;iterate>-1;iterate--) {
      if (on_int_list(Volumes[volume_index]->geometry.masked_by_list,work_list.elements[iterate]) == 0 && on_int_list(*true_parents_lists[volume_index],work_list.elements[iterate]))
        remove_element_in_list_by_index(&work_list,iterate);
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 3)");
    
    //4) remove volumes that are not masks and have lower priority than n
    for (iterate=work_list.num_elements-1;iterate>-1;iterate--) {
      if (Volumes[work_list.elements[iterate]]->geometry.is_mask_volume == 0 && Volumes[work_list.elements[iterate]]->geometry.priority_value < Volumes[volume_index]->geometry.priority_value)
        remove_element_in_list_by_index(&work_list,iterate);
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 4)");
    
    //5) remove volumes that have at least one true parent on the list (in step 4) that is not a mask volume
    // Here a logic_list is used to not have the order of removal matter
    logic_list.num_elements = work_list.num_elements;
    logic_list.elements = malloc(logic_list.num_elements * sizeof(int));
    for(iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 1;
    
    for (iterate=0;iterate<work_list.num_elements;iterate++) {
      for (parent=0;parent<true_parents_lists[work_list.elements[iterate]]->num_elements;parent++) {
        true_parent_volume_number = true_parents_lists[work_list.elements[iterate]]->elements[parent];
        if (on_int_list(work_list,true_parent_volume_number) && Volumes[true_parent_volume_number]->geometry.is_mask_volume == 0) {
          // Since element number iterate on the work list have a true parent on the work list, it can be removed
          logic_list.elements[iterate] = 0;
          break;
        }
      }
    }
    
    // Now the elements marked for removal can be removed without interfering in the operation
    for (iterate=work_list.num_elements-1;iterate>-1;iterate--) {
      if (logic_list.elements[iterate] == 0) remove_element_in_list_by_index(&work_list,iterate);
    }
    free(logic_list.elements);
    
    //if (verbal) print_1d_int_list(work_list,"After 5)");
      
    //6) split the list into two, the intersect_check_list which is all non masked volumes still on the list, and the mask_intersect_list which is the masked volumes
    
    Volumes[volume_index]->geometry.intersect_check_list.num_elements = 0;
    Volumes[volume_index]->geometry.mask_intersect_list.num_elements = 0;
    
    for (iterate=0;iterate<work_list.num_elements;iterate++) {
      if (Volumes[work_list.elements[iterate]]->geometry.is_masked_volume == 1) {
        add_element_to_int_list(&Volumes[volume_index]->geometry.mask_intersect_list,work_list.elements[iterate]);
      } else {
        add_element_to_int_list(&Volumes[volume_index]->geometry.intersect_check_list,work_list.elements[iterate]);
      }
    }
    
    //if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.intersect_check_list,"After 6) intersect_check_list");
    //if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.mask_intersect_list,"After 6) mask_intersect_list");
    
    //7) remove volumes on the mask_intersect_list whos masks does not overlap (standard overlap list) n
    for (iterate=Volumes[volume_index]->geometry.mask_intersect_list.num_elements-1;iterate>-1;iterate--) {
      // Need to check if the volumes masking Volumes[volume_index]->geometry.mask_intersect_list.elements[iterate] overlaps with n
      masked_volume_index = Volumes[volume_index]->geometry.mask_intersect_list.elements[iterate];
      
      if (Volumes[masked_volume_index]->geometry.mask_mode == 1) {// All mode, if just one does not overlap, remove the element
        for (mask_start=mask_check=Volumes[masked_volume_index]->geometry.masked_by_list.elements;mask_check-mask_start<Volumes[masked_volume_index]->geometry.masked_by_list.num_elements;mask_check++) {
          if (on_int_list(*raw_overlap_lists[volume_index],*mask_check) == 0) {
            remove_element_in_list_by_index(&Volumes[volume_index]->geometry.mask_intersect_list,iterate);
            break;
          }
        }
      } else { // ANY mode, just one need to overlap in order to keep the element
        ANY_logic = 0;
        for (mask_start=mask_check=Volumes[masked_volume_index]->geometry.masked_by_list.elements;mask_check-mask_start<Volumes[masked_volume_index]->geometry.masked_by_list.num_elements;mask_check++) {
          if (on_int_list(*raw_overlap_lists[volume_index],*mask_check) == 1) {
            ANY_logic = 1;
            break;
          }
        }
        if (ANY_logic == 0) remove_element_in_list_by_index(&Volumes[volume_index]->geometry.mask_intersect_list,iterate);
      }
    }
    
    if (work_list.num_elements > 0) free(work_list.elements);
    }
    
    
    //if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.mask_intersect_list,"After 7) mask_intersect_list");
    
    char string_output[128];
    MPI_MASTER(
    if (verbal) sprintf(string_output,"Intersect check list for Volume %d",volume_index);
    if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.intersect_check_list,string_output);
    if (verbal) sprintf(string_output,"Mask intersect check list for Volume %d",volume_index);
    if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.mask_intersect_list,string_output);
    )
    
    
  }
}

void generate_grandparents_lists(struct pointer_to_1d_int_list **grandparents_lists, struct pointer_to_1d_int_list **parents_lists, int number_of_volumes, int verbal) {
  // Function for generating grandparents lists
  // Volume iterate has volume p as a grandparent, if volume p has a parent T, who has volume iterate as parent.
  // Alternertively:
  // Volume iterate has volume p as a grandparent, if volume p have a child that is volume iterate's parent.
  
  MPI_MASTER(
  if (verbal) printf("\nGenerating grandparents lists ----------------------- \n");
  )

  struct pointer_to_1d_int_list common;
  common.num_elements = number_of_volumes;
  common.elements = malloc(common.num_elements*sizeof(int)); // Maximum needed space.
  
  struct pointer_to_1d_int_list temp_list_local;
  temp_list_local.num_elements = number_of_volumes;
  temp_list_local.elements = malloc(number_of_volumes*sizeof(int));
  
  int iterate,reset_int,parent,child,used_elements;
  for (iterate = 0;iterate < number_of_volumes;iterate++) {
    // clear temp list
    used_elements = 0;

    for (reset_int=0; reset_int<number_of_volumes; reset_int++) common.elements[reset_int] = -1; // Initialize to impossible volume ids
    for (reset_int=0; reset_int<number_of_volumes; reset_int++) temp_list_local.elements[reset_int] = -1; // Initialize to impossible volume ids

    grandparents_lists[iterate] = malloc(sizeof(struct pointer_to_1d_int_list));
    
    for (parent = 0; parent<parents_lists[iterate]->num_elements; parent++) {
        // parent number p parents_lists[iterate].elements.[p] in the parent_list for iterate.
        on_both_int_lists(parents_lists[parents_lists[iterate]->elements[parent]], parents_lists[iterate], &common);
        // returns a pointer_to_1d_list, with all the elements that are in common.
        for (child = 0;child < common.num_elements;child++) {
            // Need to make sure the element is not already on the list
            if (0 == on_int_list(temp_list_local, common.elements[child])) {
              temp_list_local.elements[used_elements++] = common.elements[child];
            }
        }
    }
    allocate_list_from_temp(used_elements, temp_list_local, grandparents_lists[iterate]);
  
    char string_output[128];
    MPI_MASTER(
      if (verbal) sprintf(string_output,"Grandparents for Volume %d", iterate);
      if (verbal) print_1d_int_list(*grandparents_lists[iterate], string_output);
    )
  }
  free(temp_list_local.elements);
  free(common.elements);
};


void generate_destinations_lists_experimental(struct pointer_to_1d_int_list **true_overlap_lists, struct pointer_to_1d_int_list **true_children_lists, struct pointer_to_1d_int_list **true_parents_lists, struct pointer_to_1d_int_list **true_grandparents_lists, struct Volume_struct **Volumes, int number_of_volumes, int verbal) {
  // Generates destinations list for for all volumes
  // Current implementation uses true_parents_lists and true_grandparents_lists that are generated as if no masks were defined

  MPI_MASTER(
  if (verbal) printf("\nGenerating destinations lists ----------------------- \n");
  )
  // Volume 0 has an hardcoded empty destinations list
  Volumes[0]->geometry.destinations_list.num_elements = 0;

  struct pointer_to_1d_int_list work_list;
  int volume_index,iterate,iterate2,found_index,I_index,I_volume;
  
  for (volume_index=1;volume_index<number_of_volumes;volume_index++) {
    
    // 1) Take the true overlap list for volume n
    // Create copy of true_overlap_lists to work with
    work_list.num_elements = true_overlap_lists[volume_index]->num_elements;
    work_list.elements = malloc(work_list.num_elements * sizeof(int));
    for (iterate=0;iterate<work_list.num_elements;iterate++) work_list.elements[iterate] = true_overlap_lists[volume_index]->elements[iterate];
    
    //if (verbal) print_1d_int_list(work_list,"After 1)");
    
    // 2) Remove elements from n's intersection list
    for (iterate=work_list.num_elements-1;iterate>-1;iterate--) {
      if (on_int_list(Volumes[volume_index]->geometry.intersect_check_list,work_list.elements[iterate]))
        remove_element_in_list_by_index(&work_list,iterate);
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 2)");
  
    // 3) Remove true children of non-mask elements on n's intersection list
    for (I_index=0;I_index<Volumes[volume_index]->geometry.intersect_check_list.num_elements;I_index++) {
      I_volume = Volumes[volume_index]->geometry.intersect_check_list.elements[I_index];
      if (Volumes[I_volume]->geometry.is_mask_volume == 0) {
        for (iterate=0;iterate<true_children_lists[I_volume]->num_elements;iterate++) {
          remove_element_in_list_by_value(&work_list,true_children_lists[I_volume]->elements[iterate]);
        }
      }
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 3)");
    
    // 4) Remove elements from mask intersection list
    for (iterate=work_list.num_elements-1;iterate>-1;iterate--) {
      if (on_int_list(Volumes[volume_index]->geometry.mask_intersect_list,work_list.elements[iterate]))
        remove_element_in_list_by_index(&work_list,iterate);
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 4)");
    
    // 5) Remove true children of elements on n's mask intersection list
    for (I_index=0;I_index<Volumes[volume_index]->geometry.mask_intersect_list.num_elements;I_index++) {
      I_volume = Volumes[volume_index]->geometry.mask_intersect_list.elements[I_index];
      if (Volumes[I_volume]->geometry.is_mask_volume == 0) {
        for (iterate=0;iterate<true_children_lists[I_volume]->num_elements;iterate++) {
          remove_element_in_list_by_value(&work_list,true_children_lists[I_volume]->elements[iterate]);
        }
      }
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 5)");
    
    // 6) Remove true children of n
    for (iterate=0;iterate<true_children_lists[volume_index]->num_elements;iterate++)
      remove_element_in_list_by_value(&work_list,true_children_lists[volume_index]->elements[iterate]);
      
    //if (verbal) print_1d_int_list(work_list,"After 6)");
      
    // 7) Remove true grandparents of n
    for (iterate=0;iterate<true_grandparents_lists[volume_index]->num_elements;iterate++)
      remove_element_in_list_by_value(&work_list,true_grandparents_lists[volume_index]->elements[iterate]);
      
    //if (verbal) print_1d_int_list(work_list,"After 7)");
      
    // 8) Remove mask volumes
    for (iterate=work_list.num_elements-1;iterate>-1;iterate--) {
      if (Volumes[work_list.elements[iterate]]->geometry.is_mask_volume == 1)
        remove_element_in_list_by_index(&work_list,iterate);
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 8)");
    
    // 9) Remove true parents of n on the list that has other true parents of n on the list with higher priority
    for (iterate=work_list.num_elements-1;iterate>-1;iterate--) {
      if (on_int_list(*true_parents_lists[volume_index],work_list.elements[iterate])){
        // work_list.elements[iterate] is the volume index of a volume that is a true parent of n
        for (iterate2=0;iterate2<work_list.num_elements;iterate2++) {
          if (on_int_list(*true_parents_lists[volume_index],work_list.elements[iterate2]) && iterate != iterate2) {
            if (Volumes[work_list.elements[iterate]]->geometry.priority_value < Volumes[work_list.elements[iterate2]]->geometry.priority_value) {
              //printf("Removing element number %d (V%d) because element number %d (V%d) had higher priority \n",iterate,work_list.elements[iterate],iterate2,work_list.elements[iterate2]);
              remove_element_in_list_by_index(&work_list,iterate);
              break; // Missing break inserted on 14/9/2016
            }
          }
        }
      }
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 9)");
    
    // 10) The remaining list is the destinations_list
    Volumes[volume_index]->geometry.destinations_list.num_elements = work_list.num_elements;
    Volumes[volume_index]->geometry.destinations_list.elements = malloc(Volumes[volume_index]->geometry.destinations_list.num_elements*sizeof(int));
    
    for (iterate=0;iterate<work_list.num_elements;iterate++)
      Volumes[volume_index]->geometry.destinations_list.elements[iterate] = work_list.elements[iterate];
    
    // Clean up after work_list so the next can be allocated
    free(work_list.elements);
  
  
    char string_output[128];
    MPI_MASTER(
    if (verbal) sprintf(string_output,"Destinations list for Volume %d",volume_index);
    if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.destinations_list,string_output);
    )

  }

};

void generate_destinations_list(int N_volume,struct Volume_struct **Volumes,struct pointer_to_1d_int_list original_overlap_list,struct pointer_to_1d_int_list *parent_list, struct pointer_to_1d_int_list *grandparent_list) {
    // This function generates the destinations_list for a single volume index, N_volume
    // The destination list describes which volumes a ray can enter when leaving N_volume
    // Each of the 6 steps for the algorithm is commented individually, and debug print statements are available
    
    // Mask update: Mask volumes are to be removed from all destinations_list as they can not be the current volume
    // It is not that simple, as some volume will then get empty destination lists. Need to revise this algorithm.
    
    // 1) Start with the overlap list of volume N
    // 2) remove all Volumes from the overlap list of N, which is also on the intersect_check_list
    // 3) remove the children of volumes removed in step 2)
    // 4) remove the children of N
    // 5) remove the grandparents of N
    // 6) remove volumes with lower priority than parents of N still on the list
    // 7) The remaing list is the destinations list
    
    
    // The destination list system should run without masks altogether, meaning parent and grandparent lists should not use them,
    //  and all masks should be removed in step 2
    
    // 1) Start with the overlap list of volume N
    // 2) remove all masks on the list
    // 3) remove all Volumes from the overlap list of N, which is also on the intersect_check_list
    // 4) remove the children of volumes removed in step 2) (if the removed volume was not a mask )
    // 5) remove the children of N
    // 6) remove the grandparents of N
    // 7) remove volumes with lower priority than parents of N still on the list
    // 8) The remaing list is the destinations list
    
    

    // 1) Start with the overlap list of volume N
    struct pointer_to_1d_int_list overlap_list;
    overlap_list.num_elements = original_overlap_list.num_elements;
    overlap_list.elements = malloc(overlap_list.num_elements*sizeof(int));
    int iterate;
    for (iterate=0;iterate<overlap_list.num_elements;iterate++) overlap_list.elements[iterate] = original_overlap_list.elements[iterate];
    
    char string_output[124];
    // sprintf(string_output,"Destinations list for Volume %d step 1",N_volume);
    // print_1d_int_list(overlap_list,string_output);
    
    // 2) remove all masks
    for (iterate=overlap_list.num_elements-1;iterate > -1;iterate--) {
        if (Volumes[overlap_list.elements[iterate]]->geometry.is_mask_volume == 1) {
            remove_element_in_list_by_index(&overlap_list,iterate);
        }
    }
    
    // 3) remove all Volumes from the overlap list of N, which is also on the intersect_check_list
    struct pointer_to_1d_int_list removed_under_2;
    removed_under_2.num_elements = 0;
    int to_check;
    removed_under_2.elements = malloc( Volumes[N_volume]->geometry.intersect_check_list.num_elements * sizeof(int));
    for (iterate=0;iterate < Volumes[N_volume]->geometry.intersect_check_list.num_elements;iterate++) {
        to_check = Volumes[N_volume]->geometry.intersect_check_list.elements[iterate];
        if (on_int_list(overlap_list,to_check)) {
            removed_under_2.elements[removed_under_2.num_elements++] = to_check;
            remove_element_in_list_by_value(&overlap_list,to_check);
        }
    }
    
    // sprintf(string_output,"Destinations list for Volume %d step 2",N_volume);
    // print_1d_int_list(overlap_list,string_output);
    
    // 4) remove the children of volumes removed in step 2)
    int children;
    for (iterate=0;iterate<removed_under_2.num_elements;iterate++){
        for (children = 0;children < Volumes[removed_under_2.elements[iterate]]->geometry.children.num_elements;children++) {
          remove_element_in_list_by_value(&overlap_list,Volumes[removed_under_2.elements[iterate]]->geometry.children.elements[children]);
        }
    }
    
    // sprintf(string_output,"Destinations list for Volume %d step 3",N_volume);
    // print_1d_int_list(overlap_list,string_output);
    
    // 5) remove the children of N
    for (children = 0;children < Volumes[N_volume]->geometry.children.num_elements;children++) {
        remove_element_in_list_by_value(&overlap_list,Volumes[N_volume]->geometry.children.elements[children]);
    }
    
    // sprintf(string_output,"Destinations list for Volume %d step 4",N_volume);
    // print_1d_int_list(overlap_list,string_output);
    
    // 6) remove the grandparents of N
    int grandparent;
    for (grandparent = 0;grandparent < grandparent_list->num_elements;grandparent++) {
        remove_element_in_list_by_value(&overlap_list,grandparent_list->elements[grandparent]);
    }
    
    // sprintf(string_output,"Destinations list for Volume %d step 5",N_volume);
    // print_1d_int_list(overlap_list,string_output);
    
    // 7) remove volumes with lower priority than parents of N still on the list
    struct pointer_to_1d_int_list logic_list;
    logic_list.num_elements = overlap_list.num_elements;
    if (logic_list.num_elements>0) logic_list.elements = malloc(logic_list.num_elements*sizeof(int));
    for (iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 1;
    
    int parent;
    for (parent=0;parent<parent_list->num_elements;parent++) {
      if (on_int_list(overlap_list,parent_list->elements[parent])) {
        // Found a parent to N on the list, now check all other elements on the list
        for (iterate=0;iterate<overlap_list.num_elements;iterate++) {
          if (parent_list->elements[parent] != overlap_list.elements[iterate]) {
            // if the element iterate have lower priority than the parent, remove it from the list
            if (Volumes[overlap_list.elements[iterate]]->geometry.priority_value < Volumes[parent_list->elements[parent]]->geometry.priority_value) logic_list.elements[iterate] = 0;
          }
        }
      }
    }
    
    // 8) The remaing list is the destinations list
    Volumes[N_volume]->geometry.destinations_list.num_elements = sum_int_list(logic_list);
    Volumes[N_volume]->geometry.destinations_list.elements = malloc(Volumes[N_volume]->geometry.destinations_list.num_elements * sizeof(int));
    
    int overlap_index,used_elements=0;
    for (overlap_index=0;overlap_index < overlap_list.num_elements;overlap_index++)
      if (logic_list.elements[overlap_index] == 1)
        Volumes[N_volume]->geometry.destinations_list.elements[used_elements++] = overlap_list.elements[overlap_index];
    
    if (overlap_list.num_elements>0) free(overlap_list.elements);
    if (logic_list.num_elements>0) free(logic_list.elements);
    
    
    /*
    // Old version before rule 6 and 2 was added
    // 8) The remaing list is the destinations list
    Volumes[N_volume]->geometry.destinations_list.num_elements = overlap_list.num_elements;
    Volumes[N_volume]->geometry.destinations_list.elements = malloc(Volumes[N_volume]->geometry.destinations_list.num_elements * sizeof(int));
    
    int overlap_index;
    for (overlap_index=0;overlap_index < overlap_list.num_elements;overlap_index++)
        Volumes[N_volume]->geometry.destinations_list.elements[overlap_index] = overlap_list.elements[overlap_index];
    
    // sprintf(string_output,"Destination list for Volume %d step 6",N_volume);
    // print_1d_int_list(Volumes[N_volume]->geometry.destinations_list,string_output);
    
    if (overlap_list.num_elements>0) free(overlap_list.elements);
    */
    
    };

void generate_destinations_lists(struct pointer_to_1d_int_list **grandparents_lists, struct pointer_to_1d_int_list **parents_lists, struct pointer_to_1d_int_list **overlap_lists,struct Volume_struct **Volumes, int number_of_volumes, int verbal) {
    // Because of the complexity of the algortithm for generating the destinations list, the function is made for a single volume at the time to keep the notation simpler
    // This funtion runs the destinations list function for each volume
    MPI_MASTER(
    if (verbal) printf("\nGenerating destinations lists ----------------------- \n");
    )
    
    int volume_index;
    for (volume_index = 0;volume_index < number_of_volumes;volume_index++) {
      generate_destinations_list(volume_index,Volumes,*overlap_lists[volume_index],parents_lists[volume_index],grandparents_lists[volume_index]);
      
      char string_output[128];
      if (verbal) sprintf(string_output,"Destinations list for Volume %d",volume_index);
      MPI_MASTER(
      if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.destinations_list,string_output);
      )
    }
};

/* OBSOLETE
void generate_destinations_logic_lists(struct Volume_struct **Volumes,int number_of_volumes,int verbal) {
  // The destinations logic list is another way to store the destinations list that makes some tasks quicker
  
  MPI_MASTER(
  if (verbal) printf("\nGenerating destinations logic lists ----------------------- \n");
  )

  int volume_index;
  for (volume_index = 0;volume_index < number_of_volumes;volume_index++) {
      allocate_logic_list_from_temp(number_of_volumes,Volumes[volume_index]->geometry.destinations_list,&Volumes[volume_index]->geometry.destinations_logic_list);
      
      char string_output[128];
      MPI_MASTER(
      if (verbal) sprintf(string_output,"Destinations logic list for Volume %d",volume_index);
      if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.destinations_logic_list,string_output);
      )
  }

};
*/

void generate_reduced_destinations_lists(struct pointer_to_1d_int_list **parents_lists, struct Volume_struct **Volumes,int number_of_volumes,int verbal) {
    // The reduced destination list is the destination list of a volume, where each element that has a parent on the same destination list is removed
    // This list is to be fed to the which_volume function, as this funtion will automatically search through the direct children in a tree like manner
    // The optimization reduces the number of calculations of within functions in nested geometries.
    
    MPI_MASTER(
    if (verbal) printf("\nGenerating reduced destination lists ----------------------- \n");
    )
    
    struct pointer_to_1d_int_list logic_list;
  
    int volume_index,checked_dest_index,checked_dest_volume,rest_dest_index,rest_dest_volume,dest_index,iterate;
    for (volume_index = 0;volume_index < number_of_volumes;volume_index++) {
      
      //printf("Generating reduced destinations lists for volume %d\n",volume_index);
      logic_list.num_elements = Volumes[volume_index]->geometry.destinations_list.num_elements;
      logic_list.elements = malloc( (int) logic_list.num_elements * sizeof(int));
      //printf("Sucsessfully allocated space for %d integers in logic list %d\n",logic_list.num_elements,volume_index);
      for (iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 1;
      
      for (checked_dest_index=0;checked_dest_index<Volumes[volume_index]->geometry.destinations_list.num_elements;checked_dest_index++) {
            checked_dest_volume = Volumes[volume_index]->geometry.destinations_list.elements[checked_dest_index];
            for (rest_dest_index=0;rest_dest_index<Volumes[volume_index]->geometry.destinations_list.num_elements;rest_dest_index++) {
                rest_dest_volume = Volumes[volume_index]->geometry.destinations_list.elements[rest_dest_index];
                // As every volume has 0 as a parent, these are ignored. It would work to include this, but would add an extra trivial step to within_which_volume
                if (rest_dest_volume != 0) {
                    if (on_int_list(*parents_lists[checked_dest_volume],rest_dest_volume) == 1) {
                        // In this case, do not include element checked_dest_index on the reduced destinations list
                        // ADD mask check
                        if (Volumes[rest_dest_volume]->geometry.is_masked_volume == 0) {
                          logic_list.elements[checked_dest_index] = 0;
                        }
                    }
                }
            }
      }
      
      Volumes[volume_index]->geometry.reduced_destinations_list.num_elements = sum_int_list(logic_list);
      Volumes[volume_index]->geometry.reduced_destinations_list.elements = malloc((int)Volumes[volume_index]->geometry.reduced_destinations_list.num_elements * sizeof(int));
      
      iterate = 0;
      for (dest_index = 0;dest_index < Volumes[volume_index]->geometry.destinations_list.num_elements;dest_index++) {
            if (logic_list.elements[dest_index] == 1)   Volumes[volume_index]->geometry.reduced_destinations_list.elements[iterate++] = Volumes[volume_index]->geometry.destinations_list.elements[dest_index];
      }
      
      free(logic_list.elements);
      
      // Testing an optimization
      remove_element_in_list_by_value(&Volumes[volume_index]->geometry.reduced_destinations_list,0);
      
      char string_output[128];
      MPI_MASTER(
      if (verbal) sprintf(string_output,"Reduced destinations list for Volume %d",volume_index);
      if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.reduced_destinations_list,string_output);
      )
    }
};

void generate_direct_children_lists(struct pointer_to_1d_int_list **parents_lists, struct Volume_struct **Volumes,int number_of_volumes,int verbal) {

  MPI_MASTER(
  if (verbal) printf("\nGenerating direct children lists ----------------------- \n");
  )
  // A direct children of volume n is a volume that is a child of n, but no other child of n is its parent
  
  // Mask update: Need to check that this step does not bug out when the mask system interferes with child/parent systems
  
  struct pointer_to_1d_int_list logic_list;
  int volume_index,child,parent,iterate;
  for (volume_index = 0;volume_index < number_of_volumes;volume_index++) {
        // Temp elements is used, and its actual number of elements is edited even though the memory allocated is not changed. This is so that the list functions handles it correctly.
        // The free function will free all the allocated memory regardless of the value of the .num_elements structure field, it is just there for convinience.
      
        logic_list.num_elements = Volumes[volume_index]->geometry.children.num_elements;
        logic_list.elements = malloc(logic_list.num_elements * sizeof(int));
        for (iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 1;
      
        // Look through each child of volume n, and for each of those look through all the other children of n and search for one of them being a parent to the child
        for (child=0;child<Volumes[volume_index]->geometry.children.num_elements;child++) {
            for (parent=0;parent<parents_lists[Volumes[volume_index]->geometry.children.elements[child]]->num_elements;parent++) {
                if (on_int_list(Volumes[volume_index]->geometry.children,parents_lists[Volumes[volume_index]->geometry.children.elements[child]]->elements[parent]))
                    // If such a parent is found, remove that child from the list
                    logic_list.elements[child] = 0;
            }
        }
      
      Volumes[volume_index]->geometry.direct_children.num_elements = sum_int_list(logic_list);
      Volumes[volume_index]->geometry.direct_children.elements = malloc(Volumes[volume_index]->geometry.direct_children.num_elements*sizeof(int));
      
      iterate = 0;
      for (child = 0;child < Volumes[volume_index]->geometry.children.num_elements;child++) {
            if (logic_list.elements[child])   Volumes[volume_index]->geometry.direct_children.elements[iterate++] = Volumes[volume_index]->geometry.children.elements[child];
      }
      // Be careful with names in both main and a function, as they are automatically declared as external variables, which would then also free the main.
      free(logic_list.elements);
      
      char string_output[128];
      MPI_MASTER(
      if (verbal) sprintf(string_output,"Children list for Volume        %d",volume_index);
      if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.children,string_output);
      if (verbal) sprintf(string_output,"Direct_children list for Volume %d",volume_index);
      if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.direct_children,string_output);
      )
      
  }

};

void generate_starting_logic_list(struct starting_lists_struct *starting_lists, struct Volume_struct **Volumes, int number_of_volumes, int verbal) {
    // Function for generating logic list of volumes the ray can start in without an error.
    // Start with a list of all vacuum volumes
    // Remove all volumes that are children of non-vacuum volumes
    // It is still possible to have a volume on this list that is surrounded by non-vacuum volumes, but it is hard to detect these situations,
    //  meaning that it is ultimately partly the users responsibility to not send neutrons directly into materials.
    
    int volume_index,*start,*check;
    
    struct pointer_to_1d_int_list temp_list_local;
    temp_list_local.num_elements = number_of_volumes;
    temp_list_local.elements = malloc(number_of_volumes*sizeof(int));
    
    //if (verbal==1) printf("sucessfully allocated temp_list_local.elements, now to enter which volumes are vacuums as a logic list\n");
    temp_list_local.elements[0] = 1; // Volume 0 is a vacuum volume.
    for (volume_index = 1;volume_index < number_of_volumes;volume_index++) {
        if (Volumes[volume_index]->p_physics->is_vacuum == 1) temp_list_local.elements[volume_index] = 1;
        else temp_list_local.elements[volume_index] = 0;
    }
    // temp_list_local is now a logic list of all vacuum volumes
    //if (verbal==1) printf("list of vacuum volumes done, now to remove the children of non-vcauum volumes\n");
    for (volume_index = 1;volume_index < number_of_volumes;volume_index++) { // All volumes ...
        if (temp_list_local.elements[volume_index] == 0) { // ... that are not vacuum ...
            for (start = check = Volumes[volume_index]->geometry.children.elements;check - start < Volumes[volume_index]->geometry.children.num_elements;check++) { // ... have all their children ...
                temp_list_local.elements[*check] = 0; // .. removed from the allowed_start_logic_list
            }
        }
    }
    //if (verbal==1) printf("sucessfully removed children of non-vacuum volumes, now allocate allowed_start_logic_list\n");
    allocate_list_from_temp(number_of_volumes,temp_list_local,&starting_lists->allowed_starting_volume_logic_list);
    //if (verbal==1) printf("sucsessfully allocated allowed_start_logic_list, now freeing temp_list_local.elements. \n");
    
    free(temp_list_local.elements);
    //if (verbal==1) printf("sucessfully freed temp_list_local.elements, generate starting lists done\n");
    
    char string_output[128];
    MPI_MASTER(
    if (verbal) sprintf(string_output,"Allowed starting volume logic list");
    if (verbal) print_1d_int_list(starting_lists->allowed_starting_volume_logic_list,string_output);
    )
    
    
};

void generate_reduced_starting_destinations_list(struct starting_lists_struct *starting_lists, struct pointer_to_1d_int_list **parents_lists, struct Volume_struct **Volumes,int number_of_volumes,int verbal) {
    // The starting_destinations_list is trivial, as it contains all volumes that are not masks.


    struct pointer_to_1d_int_list logic_list;
    //printf("Generating reduced destinations lists for volume %d\n",volume_index);
    logic_list.num_elements = number_of_volumes;
    logic_list.elements = malloc( (int) logic_list.num_elements * sizeof(int));
    int iterate;
    for (iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 0;
    
    
    for (iterate=1;iterate<number_of_volumes;iterate++)
      if (Volumes[iterate]->geometry.is_mask_volume == 0) logic_list.elements[iterate] = 1;
      //else logic_list.elements[iterate] = 0;
    
    starting_lists->starting_destinations_list.num_elements = sum_int_list(logic_list);
    starting_lists->starting_destinations_list.elements = malloc(starting_lists->starting_destinations_list.num_elements*sizeof(int));
    
    int used_elements=0;
    for (iterate=1;iterate<number_of_volumes;iterate++)
      if (logic_list.elements[iterate] == 1) starting_lists->starting_destinations_list.elements[used_elements++] = iterate;
    
    MPI_MASTER(
    if (verbal) printf("\nGenerating start destinations list ------------------------------ \n");
    if (verbal) print_1d_int_list(starting_lists->starting_destinations_list,"Starting destinations list");
    )

    // The reduced starting destination list is used when a ray enters the component in the search for which volume it starts in.
    // It facilitates the same optimization as the regular destination list.
    // The start logic list is also generated, as it is very simple and does not need a seperate function
    
    // Mask update: Need to remove mask volumes from the reduced starting destination list
    
    MPI_MASTER(
    if (verbal) printf("\nGenerating reduced start destination list ----------------------- \n");
    )
    
    int checked_dest_index,checked_dest_volume,rest_dest_index,rest_dest_volume,dest_index;
    
    /* Old version before masks were introduced
    struct pointer_to_1d_int_list starting_dest_list;
    
    starting_dest_list.num_elements = number_of_volumes - 1;
    starting_dest_list.elements = malloc ( starting_dest_list.num_elements * sizeof(int));
    for (iterate=0;iterate<number_of_volumes-1;iterate++) starting_dest_list.elements[iterate] = iterate + 1; // All volumes to be checked for starting
    */
    
    logic_list.num_elements = starting_lists->starting_destinations_list.num_elements;
    free(logic_list.elements);
    logic_list.elements = malloc( (int) logic_list.num_elements * sizeof(int));
    
    //printf("Sucsessfully allocated space for %d integers in logic list %d\n",logic_list.num_elements,volume_index);
    for (iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 1;
  
    for (checked_dest_index=0;checked_dest_index<starting_lists->starting_destinations_list.num_elements;checked_dest_index++) {
        checked_dest_volume = starting_lists->starting_destinations_list.elements[checked_dest_index];
        for (rest_dest_index=0;rest_dest_index<starting_lists->starting_destinations_list.num_elements;rest_dest_index++) {
            rest_dest_volume = starting_lists->starting_destinations_list.elements[rest_dest_index];
            // As every volume has 0 as a parent, these are ignored. It would work to include this, but would add an extra trivial step to within_which_volume
            if (rest_dest_volume != 0) {
                if (on_int_list(*parents_lists[checked_dest_volume],rest_dest_volume) == 1) {
                    // In this case, do not include element checked_dest_index on the reduced destinations list
                    logic_list.elements[checked_dest_index] = 0;
                }
            }
        }
  }
  
  starting_lists->reduced_start_list.num_elements = sum_int_list(logic_list);
  starting_lists->reduced_start_list.elements = malloc((int)starting_lists->reduced_start_list.num_elements * sizeof(int));
  
  iterate = 0;
  for (dest_index = 0;dest_index < starting_lists->starting_destinations_list.num_elements;dest_index++) {
        if (logic_list.elements[dest_index] == 1) starting_lists->reduced_start_list.elements[iterate++] = starting_lists->starting_destinations_list.elements[dest_index];
  }
  
  free(logic_list.elements);
  //free(starting_dest_list.elements);
  
  MPI_MASTER(
  if (verbal) print_1d_int_list(starting_lists->reduced_start_list,"Reduced start destinations list");
  )
    
  // Making the start_logic_list.
  starting_lists->start_logic_list.num_elements = number_of_volumes;
  starting_lists->start_logic_list.elements = malloc ( starting_lists->start_logic_list.num_elements * sizeof(int));
  starting_lists->start_logic_list.elements[0] = 0;
  for (iterate=1;iterate<number_of_volumes;iterate++) starting_lists->start_logic_list.elements[iterate] = 1; // All volumes to be checked for starting volume
  MPI_MASTER(
  if (verbal) print_1d_int_list(starting_lists->start_logic_list,"Start logic list");
  )
};


void generate_next_volume_list(struct Volume_struct **Volumes, int number_of_volumes, int verbal) {
    // Generate list of volumes that can be the next volume which the ray enters. It is used for tagging, not the simulation / propagation

    // Mask update: These lists should be done as if all mask statuses are on, meaning they include all possible next volumes (will include more input to this function)
    
    // bug: next volume list is not complete and contains duplicates

    MPI_MASTER(
    if (verbal) printf("\nGenerating next volume list ------------------------------------- \n");
    )
    // Merge destinations list, intersection list and mask_intersection_list
    int volume_index,iterate,mask_index;
    struct pointer_to_1d_int_list full_intersection_list;
    full_intersection_list.num_elements=0;
    
    for (volume_index=0;volume_index<number_of_volumes;volume_index++) {
        Volumes[volume_index]->geometry.next_volume_list.num_elements = 0;
        // Before mask update
        //merge_lists(&Volumes[volume_index]->geometry.next_volume_list, &Volumes[volume_index]->geometry.destinations_list, &Volumes[volume_index]->geometry.intersect_check_list);
        
        merge_lists(&full_intersection_list, &Volumes[volume_index]->geometry.mask_intersect_list, &Volumes[volume_index]->geometry.intersect_check_list);
        merge_lists(&Volumes[volume_index]->geometry.next_volume_list,&Volumes[volume_index]->geometry.destinations_list,&full_intersection_list);
        
        /* // This complication is taken into account by adding the mask_intersect list instead
        // It is possible that the next volume is still not on this list, as when masks are encountered the next volume can be a volume they mask.
        // For each on the list, add the volumes masked by that mask (do not iterate over this, as masks can not be masked regardless)
        for (iterate=Volumes[volume_index]->geometry.next_volume_list.num_elements-1;iterate>-1;iterate--) {
          if (Volumes[Volumes[volume_index]->geometry.next_volume_list.elements[iterate]]->geometry.is_mask_volume == 1) {
            for (mask_index=0;mask_index<Volumes[Volumes[volume_index]->geometry.next_volume_list.elements[iterate]]->geometry.mask_list.num_elements;mask_index++) {
              add_element_to_int_list(&Volumes[volume_index]->geometry.next_volume_list,Volumes[Volumes[volume_index]->geometry.next_volume_list.elements[iterate]]->geometry.mask_list.elements[mask_index]);
            }
          }
        }
        */
        
        // Remove mask volumes from the next volume list, as they never occur as current_volume, and thus just create dead branches that takes up memory
        for (iterate=Volumes[volume_index]->geometry.next_volume_list.num_elements-1;iterate>-1;iterate--)
          if (Volumes[Volumes[volume_index]->geometry.next_volume_list.elements[iterate]]->geometry.is_mask_volume == 1)
            remove_element_in_list_by_index(&Volumes[volume_index]->geometry.next_volume_list,iterate);
        
        
        if (full_intersection_list.num_elements>0) free(full_intersection_list.elements);
        full_intersection_list.num_elements=0;
        
        char string_output[128];
        MPI_MASTER(
        if (verbal) sprintf(string_output,"Next volume list                %d",volume_index);
        if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.next_volume_list,string_output);
        )
    }
};

void generate_lists(struct Volume_struct **Volumes, struct starting_lists_struct *starting_lists, int number_of_volumes, int verbal) {
    // Function to control the generation of lists
    // Some lists are only needed temporary, and are thus declared here to keep them out of the main scope
    // Others are stored in the volume structs as they are needed in the trace algorithm (or tagging)
    
    
    struct pointer_to_1d_int_list **true_children_lists;
    true_children_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    // generate_children_lists both generate the normal children list for each volume, but also the true children list needed locally.
    generate_children_lists(Volumes, true_children_lists, number_of_volumes,verbal);
    
    
    struct pointer_to_1d_int_list **true_overlap_lists;
    true_overlap_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    struct pointer_to_1d_int_list **raw_overlap_lists;
    raw_overlap_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    
    generate_overlap_lists(true_overlap_lists, raw_overlap_lists, Volumes,number_of_volumes,verbal);
    
    
    //generate_intersect_check_lists(true_overlap_lists, Volumes, number_of_volumes, verbal);
    
    
    struct pointer_to_1d_int_list **parents_lists;
    parents_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    generate_parents_lists(parents_lists,Volumes,number_of_volumes,verbal,1); // The last 1 means masks are taken into account
    
    // Generate version of parent list as it would be without masks
    struct pointer_to_1d_int_list **parents_lists_no_masks;
    parents_lists_no_masks = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    generate_parents_lists(parents_lists_no_masks,Volumes,number_of_volumes,verbal,0); // The last 0 means masks are NOT taken into account
    
    // Generate version of parent list using true_children instead
    struct pointer_to_1d_int_list **true_parents_lists;
    true_parents_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    generate_true_parents_lists(true_parents_lists, true_children_lists, Volumes, number_of_volumes, verbal, 1);
    
    // Generate version of parent list no masks using true_children instead
    struct pointer_to_1d_int_list **true_parents_lists_no_masks;
    true_parents_lists_no_masks = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    generate_true_parents_lists(true_parents_lists_no_masks, true_children_lists, Volumes, number_of_volumes, verbal, 0);
    
    // New version of generate intersect lists
    generate_intersect_check_lists_experimental(true_overlap_lists, raw_overlap_lists, parents_lists, true_parents_lists, Volumes, number_of_volumes, verbal);
    
    struct pointer_to_1d_int_list **grandparents_lists;
    grandparents_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    generate_grandparents_lists(grandparents_lists,parents_lists,number_of_volumes,verbal);
    
    // Generate version of grandparents list as it would have been if no masks were defined
    struct pointer_to_1d_int_list **grandparents_lists_no_masks;
    grandparents_lists_no_masks = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    generate_grandparents_lists(grandparents_lists_no_masks,parents_lists_no_masks,number_of_volumes,verbal);
    
    // Generate true_grandparents_lists
    struct pointer_to_1d_int_list **true_grandparents_lists;
    true_grandparents_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    generate_grandparents_lists(true_grandparents_lists,true_parents_lists,number_of_volumes,verbal);
    
    struct pointer_to_1d_int_list **true_grandparents_lists_no_masks;
    true_grandparents_lists_no_masks = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    generate_grandparents_lists(true_grandparents_lists_no_masks,true_parents_lists_no_masks,number_of_volumes,verbal);
    
    // The destinations lists are generated without taking masks into account (they are removed from the overlap list in an early step)
    //generate_destinations_lists(grandparents_lists_no_masks,parents_lists_no_masks,true_overlap_lists,Volumes,number_of_volumes,verbal);
    generate_destinations_lists_experimental(true_overlap_lists, true_children_lists, true_parents_lists_no_masks, true_grandparents_lists_no_masks, Volumes, number_of_volumes, verbal);
    
    // Obsolete, found a way around them in within_which_volume, but need to test the performance difference
    // generate_destinations_logic_lists(Volumes,number_of_volumes,verbal);
    
    generate_reduced_destinations_lists(parents_lists,Volumes,number_of_volumes,verbal);
    
    generate_direct_children_lists(parents_lists,Volumes,number_of_volumes,verbal);
    
    //generate_starting_list(starting_lists,Volumes,number_of_volumes,verbal);
    generate_starting_logic_list(starting_lists,Volumes,number_of_volumes,verbal);
    
    generate_reduced_starting_destinations_list(starting_lists,parents_lists,Volumes,number_of_volumes,verbal);
    
    // This list is stored with the volumes for convinience, but is only used for tagging
    generate_next_volume_list(Volumes,number_of_volumes,verbal);
    
    // Garbage collection for temporary dynamically allocated lists. (Permanent lists freed from FINALLY)
    int iterate;
    for (iterate=0;iterate<number_of_volumes;iterate++) {
        //printf("freeing for volume nr %d\n",iterate);
        //printf("true_overlap_lists[iterate]->num_elements = %d \n",true_overlap_lists[iterate]->num_elements);
        if (true_overlap_lists[iterate]->num_elements > 0) free(true_overlap_lists[iterate]->elements);
        free(true_overlap_lists[iterate]);
        
        //printf("raw_overlap_lists[iterate]->num_elements = %d \n",raw_overlap_lists[iterate]->num_elements);
        if (raw_overlap_lists[iterate]->num_elements > 0) free(raw_overlap_lists[iterate]->elements);
        free(raw_overlap_lists[iterate]);
        
        //printf("parents_lists[iterate]->num_elements = %d \n",parents_lists[iterate]->num_elements);
        if (parents_lists[iterate]->num_elements > 0) free(parents_lists[iterate]->elements);
        free(parents_lists[iterate]);
        
        //printf("parents_lists_no_masks[iterate]->num_elements = %d \n",parents_lists_no_masks[iterate]->num_elements);
        if (parents_lists_no_masks[iterate]->num_elements > 0) free(parents_lists_no_masks[iterate]->elements);
        free(parents_lists_no_masks[iterate]);
        
        //printf("true_parents_lists[iterate]->num_elements = %d \n",true_parents_lists[iterate]->num_elements);
        if (true_parents_lists[iterate]->num_elements > 0) free(true_parents_lists[iterate]->elements);
        free(true_parents_lists[iterate]);
        
        //printf("true_parents_lists_no_masks[iterate]->num_elements = %d \n",true_parents_lists_no_masks[iterate]->num_elements);
        if (true_parents_lists_no_masks[iterate]->num_elements > 0) free(true_parents_lists_no_masks[iterate]->elements);
        free(true_parents_lists_no_masks[iterate]);
        
        //printf("grandparents_lists[iterate]->num_elements = %d \n",grandparents_lists[iterate]->num_elements);
        if (grandparents_lists[iterate]->num_elements > 0) free(grandparents_lists[iterate]->elements);
        free(grandparents_lists[iterate]);
        
        //printf("true_grandparents_lists[iterate]->num_elements = %d \n",true_grandparents_lists[iterate]->num_elements);
        if (true_grandparents_lists[iterate]->num_elements > 0) free(true_grandparents_lists[iterate]->elements);
        free(true_grandparents_lists[iterate]);
        
        //printf("grandparents_lists_no_masks[iterate]->num_elements = %d \n",grandparents_lists_no_masks[iterate]->num_elements);
        if (grandparents_lists_no_masks[iterate]->num_elements > 0) free(grandparents_lists_no_masks[iterate]->elements);
        free(grandparents_lists_no_masks[iterate]);
        
        //printf("true_grandparents_lists_no_masks[iterate]->num_elements = %d \n",true_grandparents_lists_no_masks[iterate]->num_elements);
        if (true_grandparents_lists_no_masks[iterate]->num_elements > 0) free(true_grandparents_lists_no_masks[iterate]->elements);
        free(true_grandparents_lists_no_masks[iterate]);
        
        //printf("true_children_lists[iterate]->num_elements = %d \n",true_children_lists[iterate]->num_elements);
        if (true_children_lists[iterate]->num_elements > 0) free(true_children_lists[iterate]->elements);
        free(true_children_lists[iterate]);
    }
    //printf("generate lists volume specific free completed\n");
    free(true_overlap_lists);free(raw_overlap_lists);free(parents_lists);free(true_parents_lists);free(true_parents_lists_no_masks);
    free(parents_lists_no_masks);free(true_grandparents_lists);free(grandparents_lists);free(grandparents_lists_no_masks);free(true_grandparents_lists_no_masks);
    free(true_children_lists);
    //printf("generate lists free completed\n");
};

// -------------    Focusing functions   --------------------------------------------------------

// The focusing_data structure is set up by the geometry component, and a pointer to the appropriate
//  focusing function is added to the Volume structure (for this reason the input of all the functions
//  need to be identical, at least in terms of types). In this way there are no if statements to check
//  which of these to be used in the trace, but the focus_data_struct will carry some redundant
//  information, as only the appropriate parameters are set:
// Angular focus on a rectangle (angular_focus_height / angular_focus_width)
// Spatial focus on a rectangle (spatial_focus_height / spatial_focus_width)
// Spatial focus on a disk (sptial_focus_radius)
// No focus (randvec in 4pi) (all set to zero, will select randvec circle as it is slightly faster
//
// When adding a new physical process focusing becomes very easy, as one just calls the master focusing
//  function assosiated with the volume (placed in the geometry struct), using the focus_data_struct
//  also found in the geometry struct, and the process then supports all the focusing modes. It is even
//  possible to add new focusing modes in the future by updating just the geometry components, and this
//  section.

// focus_data_struct definitioon shown here, defined at the start of this file
//struct focus_data_struct {
//Coords Aim;
//double angular_focus_width;
//double angular_focus_height;
//double spatial_focus_width;
//double spatial_focus_height;
//double spatial_focus_radius;
//Rotation absolute_rotation;
//// focusing_function creates a vector per selected criteria of focus_data_struct / selected focus function and returns solid angle
//void (*focusing_function)(Coords*, double*, struct focus_data_struct*);
////                        v_out  , solid_a,
//};

void randvec_target_rect_angular_union(Coords *v_out,double *solid_angle_out, struct focus_data_struct *focus_data) {
    // Calls the standard McStas randvec_target_rect_angular focusing function, but is with the new data input format.
    randvec_target_rect_angular(&v_out->x, &v_out->y, &v_out->z, solid_angle_out, focus_data->Aim.x,focus_data->Aim.y, focus_data->Aim.z, focus_data->angular_focus_width, focus_data->angular_focus_height,focus_data->absolute_rotation);
    //randvec_target_rect_angular(&vx, &vy, &vz, &solid_angle,aim_x, aim_y, aim_z, VarsInc.aw, VarsInc.ah, ROT_A_CURRENT_COMP);
};

void randvec_target_rect_union(Coords *v_out,double *solid_angle_out, struct focus_data_struct *focus_data) {
// Calls the standard McStas randvec_target_rect focusing function, but is with the new data input format.
    randvec_target_rect(&v_out->x, &v_out->y, &v_out->z, solid_angle_out, focus_data->Aim.x,focus_data->Aim.y, focus_data->Aim.z, focus_data->spatial_focus_width, focus_data->spatial_focus_height,focus_data->absolute_rotation);
    // randvec_target_rect(&vx, &vy, &vz, &solid_angle,aim_x, aim_y, aim_z, VarsInc.xw, VarsInc.yh, ROT_A_CURRENT_COMP);
};

void randvec_target_circle_union(Coords *v_out,double *solid_angle_out, struct focus_data_struct *focus_data) {
// Calls the standard McStas randvec_target_circle focusing function, but is with the new data input format.

    // debug input into randvec_target_circle
    //print_position(focus_data->Aim,"Aim vector input for randvec_target_circle");
    //printf("Radius input %f\n",focus_data->spatial_focus_radius);

    randvec_target_circle(&v_out->x, &v_out->y, &v_out->z, solid_angle_out, focus_data->Aim.x,focus_data->Aim.y, focus_data->Aim.z, focus_data->spatial_focus_radius);
    //randvec_target_circle(&vx, &vy, &vz, &solid_angle, aim_x, aim_y, aim_z, focus_r);
};



void focus_initialize(struct geometry_struct *geometry, Coords POS_A_TARGET, Coords POS_A_CURRENT, Rotation ROT_A_CURRENT, int target_index, double target_x, double target_y, double target_z, double angular_focus_width, double angular_focus_height, double spatial_focus_width, double spatial_focus_height, double spatial_focus_radius, char *component_name) {
    // Initialize focusing system
    // target_x/y/z needs to be double setting parameters with default value 0
    // target_index needs to be int setting parameter with default value 0
    // angular_focus_width, angular_focus_height, spatial_focus_width, spatial_focus_height, spatial_focus_radius nneds to be double setting parameters with default value 0
    // When those conditions are met, this code will identify which settings have been entered by the user and select the appropriate focusing parameters, which are loaded into the focus_data struct and the geometry struct.
    // The aim vector in the focus_data struct will be transformed from the local coordinate system of the geometry component to the coordinate system of the master component during the master component initialize

  // Input sanitation
  if (angular_focus_width < 0) {
    printf("\nERROR in Union geometry component named \"%s\", angular focus width focus_aw < 0! \n",component_name);
    exit(1);
  }
  if (angular_focus_height < 0) {
    printf("\nERROR in Union geometry component named \"%s\", angular focus width focus_ah < 0! \n",component_name);
    exit(1);
  }
  if (spatial_focus_width < 0) {
    printf("\nERROR in Union geometry component named \"%s\", spatial focus width focus_xw < 0! \n",component_name);
    exit(1);
  }
  if (spatial_focus_height < 0) {
    printf("\nERROR in Union geometry component named \"%s\", spatial focus height focus_xh < 0! \n",component_name);
    exit(1);
  }
  if (spatial_focus_radius < 0) {
    printf("\nERROR in Union geometry component named \"%s\", spatial focus radius focus_r < 0! \n",component_name);
    exit(1);
  }
  
  struct focus_data_struct focus_data;

  // Initialize focus_data_struct
  /*
  geometry->focus_data.Aim = coords_set(0,0,0);
  geometry->focus_data.angular_focus_width = 0;
  geometry->focus_data.angular_focus_height = 0;
  geometry->focus_data.spatial_focus_width = 0;
  geometry->focus_data.spatial_focus_height = 0;
  geometry->focus_data.spatial_focus_radius = 0;
  rot_copy(geometry->focus_data.absolute_rotation,ROT_A_CURRENT);
  */
  focus_data.Aim = coords_set(0,0,0);
  focus_data.angular_focus_width = 0;
  focus_data.angular_focus_height = 0;
  focus_data.spatial_focus_width = 0;
  focus_data.spatial_focus_height = 0;
  focus_data.spatial_focus_radius = 0;
  rot_copy(focus_data.absolute_rotation,ROT_A_CURRENT);

  // Built on code from Incoherent.comp by Kim Lefmann and Kristian Nielsen
  if (target_index != 0 && !target_x && !target_y && !target_z)
  {
    Coords ToTarget;
    //ToTarget = coords_sub(POS_A_COMP_INDEX(INDEX_CURRENT_COMP+target_index),POS_A_CURRENT_COMP);
    ToTarget = coords_sub(POS_A_TARGET,POS_A_CURRENT);
    //ToTarget = rot_apply(ROT_A_CURRENT_COMP, ToTarget);
    ToTarget = rot_apply(ROT_A_CURRENT, ToTarget);
    coords_get(ToTarget, &focus_data.Aim.x, &focus_data.Aim.y, &focus_data.Aim.z);
  }
  else
  { focus_data.Aim.x = target_x; focus_data.Aim.y = target_y; focus_data.Aim.z = target_z; }
  
  if (!(focus_data.Aim.x || focus_data.Aim.y || focus_data.Aim.z)) {
    // Somehow set a variable to signify scattering into 4pi
    // printf("Union %s: The target is not defined. Using scattering into 4pi.\n",NAME_CURRENT_COMP);
    focus_data.Aim.z=1; // set aim to one so that the randvec output vector has length 1 instead of 0
  }

  int focusing_model_selected = 0;
  if (angular_focus_width != 0 && angular_focus_height != 0) {
    focus_data.focusing_function = &randvec_target_rect_angular_union;
    focus_data.angular_focus_width = DEG2RAD*angular_focus_width; // Convert to radians here
    focus_data.angular_focus_height = DEG2RAD*angular_focus_height;
    focusing_model_selected = 1;
  }
  if (spatial_focus_width != 0 && spatial_focus_height != 0) {
    focus_data.focusing_function = &randvec_target_rect_union;
    focus_data.spatial_focus_width = spatial_focus_width;
    focus_data.spatial_focus_height = spatial_focus_height;
    if (focusing_model_selected) {
        printf("ERROR %s: Select either angular or spatial focusing, not both! Exiting \n",component_name);
        exit(1);
    }
    focusing_model_selected = 1;
  }
  if (spatial_focus_radius != 0) {
    focus_data.focusing_function = &randvec_target_circle_union;
    focus_data.spatial_focus_radius = spatial_focus_radius;
    if (focusing_model_selected) {
        printf("ERROR %s: Select a maximum of one focusing method (spatial rectangle or cicle, or angular rectangle! Exiting \n",component_name);
        exit(1);
    }
    focusing_model_selected = 1;
  }
  if (focusing_model_selected == 0) {
    // Select 4pi focusing
    focus_data.spatial_focus_radius = 0;
    focus_data.focusing_function = &randvec_target_circle_union;
  }
  
  // Allocate the isotropic focus_data struct
  geometry->focus_data_array.num_elements = 0;
  //geometry->focus_data_array.elements = malloc(sizeof(focus_data_struct));
  add_element_to_focus_data_array(&geometry->focus_data_array,focus_data);
};


struct abs_event{
    double time1;
    double position1[3];
    double time2;
    double position2[3];
    double weight_change;
    int volume_index;
    int neutron_id;
};

// Functions for recording absorption
void initialize_absorption_file() {
  FILE *fp;
  fp = fopen("Union_absorption.dat","w");
  fprintf(fp,"r_old x, r_old y, r_old z, old t, r x, r y, r z, new t, weight change, volume index, neutron id \n");
  
  fclose(fp);
}

void write_events_to_file(int last_index, struct abs_event *events) {

  FILE *fp;
  fp = fopen("Union_absorption.dat","a");
  
  struct abs_event *this_event;
  int iterate;
  for (iterate=0; iterate<last_index; iterate++) {
  
    this_event = &events[iterate];
    fprintf(fp,"%g, %g, %g, %g, %g, %g, %g, %g, %e, %i, %i \n",
           this_event->position1[0], this_event->position1[1], this_event->position1[2], this_event->time1,
           this_event->position2[0], this_event->position2[1], this_event->position2[2], this_event->time2,
           this_event->weight_change,
           this_event->volume_index,
           this_event->neutron_id);
  }

  fclose(fp);
}

void record_abs_to_file(double *r, double t1, double *r_old, double t2, double weight_change, int volume, int neutron_id, int *data_index, struct abs_event *events) {
  
  
  
  struct abs_event *this_event;
  
  this_event = &events[(*data_index)++];
  
  //printf("Recording something! %i\n", *data_index);
  
  this_event->position1[0] = r[0];
  this_event->position1[1] = r[1];
  this_event->position1[2] = r[2];
  this_event->time1 = t1;
  this_event->position2[0] = r_old[0];
  this_event->position2[1] = r_old[1];
  this_event->position2[2] = r_old[2];
  this_event->time2 = t2;
  this_event->weight_change = weight_change;
  this_event->volume_index = volume;
  this_event->neutron_id = neutron_id;
  
  
  if (*data_index == 999) {
    
      write_events_to_file(*data_index, events);
      *data_index = 0;

  }

};




/*******************************************************************************
*
*  McStas, neutron ray-tracing package
*  Copyright(C) 2007 Risoe National Laboratory.
*
* %I
* Written by: Mads Bertelsen
* Date: 20.08.15
* Version: $Revision: 0.1 $
* Origin: University of Copenhagen
*
* A sample component to separate geometry and phsysics
*
* %D
* Alpha version, no input system yet
* Hardcode input to geometry engine
* Allows complicated geometry by combination of simple shapes
*
* Algorithm:
* Described elsewhere
*
* %P
* INPUT PARAMETERS:
* radius:  [m] Outer radius of sample in (x,z) plane
*
* OUTPUT PARAMETERS:
* V_rho:  [AA^-3] Atomic density
*
* %L
* The test/example instrument <a href="../examples/Test_Phonon.instr">Test_Phonon.instr</a>.
*
* %E
******************************************************************************/


// This file sets up global lists needed for Union components to communicate with each other
// These all have dynamically allocated memory somewhere in the structure, which is deallocated
//  by the last Union_master.



// Initialize global positions / rotations to transform lists
  // These are lists of pointers to positons / rotations, that will be updated from global frame
  //  to the frame of the master component that uses them in that masters initialize section.
  struct global_positions_to_transform_list_struct global_positions_to_transform_list = {0,NULL};
  struct global_rotations_to_transform_list_struct global_rotations_to_transform_list = {0,NULL};

// Initialize global_process_list
  // Used to facilitate communication between processes and the other types of Union components
  struct pointer_to_global_process_list global_process_list = {0,NULL};

// Initialize global_material_list
  // Used to facilitate communication between materials and the other types of Union components
  struct pointer_to_global_material_list global_material_list = {0,NULL};

// Initialize global_geometry_list
  // Used to facilitate communication between geometries and the other types of Union components
  struct pointer_to_global_geometry_list global_geometry_list = {0,NULL};

// Initialize global_logger_lists
  // Used to facilitate communication between loggers and the other types of Union components
  struct pointer_to_global_logger_list global_all_volume_logger_list = {0,NULL};
  struct pointer_to_global_logger_list global_specific_volumes_logger_list = {0,NULL};

// Initialize global_abs_logger_lists
  struct pointer_to_global_abs_logger_list global_all_volume_abs_logger_list = {0,NULL};
  struct pointer_to_global_abs_logger_list global_specific_volumes_abs_logger_list = {0,NULL};

// Initialize global_tagging_conditional_list
  // Used to facilitate communication between conditionals and the other types of Union components
  struct global_tagging_conditional_list_struct global_tagging_conditional_list = {0,0,NULL};

// Initialize global_master_list
  // Used to facilitate communication between Master components (mainly for deallocation)
  struct pointer_to_global_master_list global_master_list = {0,NULL};

// Initialize global_mantid_min_pixel_id
  // Used for ensuring pixel id's on Mantid monitors do not overlap
  int global_mantid_min_pixel_id = 0;


#endif


// This function checks if global_process_element should be included in this material when using automatic linking, returns 1 if yes, 0 if no.
int automatic_linking_materials_function(struct global_process_element_struct global_process_element, struct pointer_to_global_material_list global_material_list,int current_index) {
    // Remember this function is used before the current material is added to global_material_list
    // debug info
    //MPI_MASTER(
    //printf("Checking if process with index %d should be automatically linked to material with index %d\n",global_process_element.component_index,current_index);
    //)

    // Check if this is the first make_material, which makes the problem simpler.
    if (global_material_list.num_elements == 0) {
       if (global_process_element.component_index < current_index) return 1;
       else return 0;
    }
    // In case there are more than 1 make_material, global_material_list.elements[global_material_list.num_elements-1].component_index makes sense.
    if (global_process_element.component_index < current_index && global_process_element.component_index > global_material_list.elements[global_material_list.num_elements-1].component_index) return 1;
    else return 0;
}

void manual_linking_function_material(char *input_string, struct pointer_to_global_process_list *global_process_list, struct pointer_to_1d_int_list *accepted_processes, char *component_name) {
    // Need to check a input_string of text for an occurance of name. If it is in the inputstring, yes return 1, otherwise 0.
   char *token;
   int loop_index;
   char local_string[256];
   
   strcpy(local_string,input_string);
   // get the first token
   token = strtok(local_string,",");
   
   // walk through other tokens
   while( token != NULL ) 
   {
      //printf( " %s\n", token );
      for (loop_index=0;loop_index<global_process_list->num_elements;loop_index++) {
        if (strcmp(token,global_process_list->elements[loop_index].name) == 0) {
          add_element_to_int_list(accepted_processes,loop_index);
          break;
        }
        
        if (loop_index == global_process_list->num_elements - 1) {
          // All possible process names have been looked through, and the break was not executed.
          // Alert the user to this problem by showing the process name that was not found and the currently available processes
            printf("\n");
            printf("ERROR: The process string \"%s\" in Union material \"%s\" had an entry that did not match a specified process. \n",input_string,component_name);
            printf("       The unrecoignized process name was: \"%s\" \n",token);
            printf("       The processes available at this point (need to be defined before the material): \n");
            for (loop_index=0;loop_index<global_process_list->num_elements;loop_index++)
              printf("         %s\n",global_process_list->elements[loop_index].name);
            exit(1);
        }
      }
      
      // Updates the token
      token = strtok(NULL,",");
   }
}

// This function is needed in initialize of all geometry components
// Possible to insert these functions in make material, as they are only compiled once instead of many times
int manual_linking_function(char *name, char *input_string) {
    // Need to check a input_string of text for an occurance of name. If it is in the inputstring, yes return 1, otherwise 0.
   char *token;
   int return_integer=0;
   char local_string[124];
   
   strcpy(local_string,input_string);
   /* get the first token */
   token = strtok(local_string,",");
   
   /* walk through other tokens */
   while( token != NULL ) 
   {
      //printf( " %s\n", token );
      if (strcmp(token,name) == 0) return_integer=1;
      
      token = strtok(NULL,",");
   }
   
   return return_integer;
}

#ifndef MATERIAL_DETECTOR
    //struct pointer_to_global_material_list global_material_list = {0,NULL};
    #define MATERIAL_DETECTOR dummy
#endif


/* Shared user declarations for all components types 'Single_crystal_process'. */
#ifndef Union
#define Union $Revision: 0.8 $

/*******************************************************************************
*
*  McStas, neutron ray-tracing package
*  Copyright(C) 2007 Risoe National Laboratory.
*
* %I
* Written by: Mads Bertelsen
* Date: 20.08.15
* Version: $Revision: 0.1 $
* Origin: University of Copenhagen
*
* Functions and structure definitons for Union components.
*
******************************************************************************/

// -------------    Definition of data structures   ---------------------------------------------
// GPU
enum shape {
  surroundings,
  box,
  sphere,
  cylinder,
  cone,
  mesh
};

enum process {
  Incoherent,
  Powder,
  Single_crystal,
  AF_HB_1D,
  PhononSimple,
  Texture,
  IncoherentPhonon,
  NCrystal,
  Template
};

struct intersection_time_table_struct {
int num_volumes;
int *calculated;
int *n_elements;
double **intersection_times;
};

struct line_segment{
//struct position point1;
//struct position point2;
Coords point1;
Coords point2;
int number_of_dashes;
};

struct pointer_to_1d_int_list {
int num_elements;
int *elements;
#pragma acc shape(elements[0:num_elements]) init_needed(num_elements)
};

struct pointer_to_1d_double_list {
int num_elements;
double *elements;
#pragma acc shape(elements[0:num_elements]) init_needed(num_elements)
};

struct pointer_to_1d_coords_list {
int num_elements;
Coords *elements;
#pragma acc shape(elements[0:num_elements]) init_needed(num_elements)
};

struct lines_to_draw{
int number_of_lines;
struct line_segment *lines;
#pragma acc shape(lines[0:number_of_lines]) init_needed(number_of_lines)
};

// 2D lists not needed anyway
// struct pointer_to_2d_int_list {
// int n_lists;
// struct pointer_to_1d_int_list *lists;
// }

// Todo: see if the union geometry_parameter_union and other geometry structs can be here
union geometry_parameter_union{
    struct sphere_storage   *p_sphere_storage;
    struct cylinder_storage *p_cylinder_storage;
    struct box_storage      *p_box_storage;
    struct cone_storage     *p_cone_storage;
    struct mesh_storage     *p_mesh_storage;
    // add as many pointers to structs as wanted, without increasing memory footprint.
};


struct rotation_struct{
double x;
double y;
double z;
};

struct focus_data_struct {
Coords Aim;
double angular_focus_width;
double angular_focus_height;
double spatial_focus_width;
double spatial_focus_height;
double spatial_focus_radius;
Rotation absolute_rotation;
// focusing_function creates a vector per selected criteria of focus_data_struct / selected focus function and returns solid angle
void (*focusing_function)(Coords*, double*, struct focus_data_struct*);
//                        v_out  , solid_a,
};

struct focus_data_array_struct
{
struct focus_data_struct *elements;
int num_elements;
#pragma acc shape(elements[0:num_elements]) init_needed(num_elements)
};

struct Detector_3D_struct {
  char title_string[256];
  char string_axis_1[256];
  char string_axis_2[256];
  char string_axis_3[256];
  char Filename[256];
  double D1min;
  double D1max;
  double D2min;
  double D2max;
  double D3min;
  double D3max;
  double bins_1; // McStas uses doubles for bin numbers for some reason
  double bins_2;
  double bins_3;
  double ***Array_N; // McStas uses doubles for number of rays in each bin for some reason
  double ***Array_p;
  double ***Array_p2;
};

struct Detector_2D_struct {
  char title_string[256];
  char string_axis_1[256];
  char string_axis_2[256];
  char Filename[256];
  double D1min;
  double D1max;
  double D2min;
  double D2max;
  double bins_1; // McStas uses doubles for bin numbers for some reason
  double bins_2;
  double **Array_N; // McStas uses doubles for number of rays in each bin for some reason
  double **Array_p;
  double **Array_p2;
};

struct Detector_1D_struct {
  char title_string[256];
  char string_axis[256];
  char string_axis_short[64];
  char string_axis_value[256];
  char Filename[256];
  double min;
  double max;
  double bins; // McStas uses doubles for bin numbers for some reason
  double *Array_N; // McStas uses doubles for number of rays in each bin for some reason
  double *Array_p;
  double *Array_p2;
};


union logger_data_union{
  struct a_2DQ_storage_struct *p_2DQ_storage;
  struct a_2DS_storage_struct *p_2DS_storage;
  struct a_3DS_storage_struct *p_3DS_storage;
  struct a_1D_storage_struct *p_1D_storage;
  struct a_2DS_t_storage_struct *p_2DS_t_storage;
  struct a_2D_kf_storage_struct *p_2D_kf_storage;
  struct a_2D_kf_t_storage_struct *p_2D_kf_t_storage;
  // Additional logger storage structs to be addedd
};

struct logger_with_data_struct {
  int used_elements;
  int allocated_elements;
  struct logger_struct **logger_pointers;
};

// logger_pointer_struct
// contains pointers to the different logger functions and it's data union
struct logger_pointer_set_struct {
  // The logger has two record functions, an active and an inactive. Normally the active one will be to permanent storage,
  //  but if a conditional has been defined, it can switch the two, making the active one recording to temporary, which
  //  can then be filtered based on the future path of the ray
  
  // function input Coords position, k[3], k_old[3], p, p_old, NV, NPV, N, logger_data_union, logger_with_data_struct
  void (*active_record_function)(Coords*, double*, double*, double, double, double, int, int, int, struct logger_struct*, struct logger_with_data_struct*);
  void (*inactive_record_function)(Coords*, double*, double*, double, double, double, int, int, int, struct logger_struct*, struct logger_with_data_struct*);
  
  // A clear temporary data function (for new ray)
  void (*clear_temp)(union logger_data_union*);
  
  // Write temporary to permanent is used when the record to temp function is active, and the condition is met.
  void (*temp_to_perm)(union logger_data_union*);
  
  // Write temporary final_p to permanent is used when the record to temp function is active, and the condition is met
  //  and the final weight is given to be used for all stored events in the logger.
  void (*temp_to_perm_final_p)(union logger_data_union*, double);
  
  // Select which temp_to_perm function to use
  int select_t_to_p; // 1: temp_to_perm, 2: temp_to_perm_final_p
  
};

union abs_logger_data_union{
  struct a_2D_abs_storage_struct *p_2D_abs_storage;
  struct a_1D_abs_storage_struct *p_1D_abs_storage;
  struct a_1D_time_abs_storage_struct *p_1D_time_abs_storage;
  struct a_1D_time_to_lambda_abs_storage_struct *p_1D_time_to_lambda_abs_storage;
  struct a_event_abs_storage_struct *p_event_abs_storage;
  struct a_1D_event_abs_storage_struct *p_1D_event_abs_storage;
  // Additional logger storage structs to be addedd
};

struct abs_logger_with_data_struct {
  int used_elements;
  int allocated_elements;
  struct abs_logger_struct **abs_logger_pointers;
};

struct abs_logger_pointer_set_struct {
  // The logger has two record functions, an active and an inactive. Normally the active one will be to permanent storage,
  //  but if a conditional has been defined, it can switch the two, making the active one recording to temporary, which
  //  can then be filtered based on the future path of the ray
  
  // function input Coords position, k[3], p, NV, N, logger_data_union, logger_with_data_struct
  void (*active_record_function)(Coords*, double*, double,  double, int, int, struct abs_logger_struct*, struct abs_logger_with_data_struct*);
  void (*inactive_record_function)(Coords*, double*, double,  double, int, int, struct abs_logger_struct*, struct abs_logger_with_data_struct*);
  
  // A clear temporary data function (for new ray)
  void (*clear_temp)(union abs_logger_data_union*);
  
  // Write temporary to permanent is used when the record to temp function is active, and the condition is met.
  void (*temp_to_perm)(union abs_logger_data_union*);
  
  // Write temporary final_p to permanent is used when the record to temp function is active, and the condition is met
  //  and the final weight is given to be used for all stored events in the logger.
  void (*temp_to_perm_final_p)(union abs_logger_data_union*, double);
  
  // Select which temp_to_perm function to use
  //int select_t_to_p; // 1: temp_to_perm, 2: temp_to_perm_final_p
  
};


struct conditional_standard_struct{
  // Data to be transfered to the conditional function
  double Emax;
  double Emin;
  int E_limit;
  
  double Tmin;
  double Tmax;
  int T_limit;
  
  int volume_index;
  
  double Total_scat_max;
  double Total_scat_min;
  int Total_scat_limit;
  
  double exit_volume_index;
  
  // Test
  Coords test_position;
  Rotation test_rotation;
  Rotation test_t_rotation;
};

struct conditional_PSD_struct{
  double PSD_half_xwidth;
  double PSD_half_yheight;
  
  double Tmin;
  double Tmax;
  int T_limit;
  
  // Position of the PSD
  Coords PSD_position;
  Rotation PSD_rotation;
  Rotation PSD_t_rotation;
};

union conditional_data_union {
  struct conditional_standard_struct *p_standard;
  struct conditional_PSD_struct *p_PSD;
  // Add more as conditional components are made
};

// General input for conditional functions: Position, Velocity/Wavevector, weight, time, total_scat, scattered_flag, scattered_flag_VP,
// Optional extras: data_union? tree base(s)? tree base for current ray?
typedef int (*conditional_function_pointer)(union conditional_data_union*,Coords*, Coords*, double*, double*, int*, int*, int*, int**);
//typedef int (**conditional_function_pointer_array)(union *conditional_data_union,Coords*, Coords*, double*, double*, int*, int*, int**);

struct conditional_list_struct{
  int num_elements;
  
  union conditional_data_union **p_data_unions;
  conditional_function_pointer *conditional_functions;
  //int (**conditional_functions)(Coords*, Coords*, double*, double*, int*, int*, int**);
};

struct logger_struct {
  char name[256];
  // Contains ponters to all the functions assosiated with this logger
  struct logger_pointer_set_struct function_pointers;
  // Contains hard copy of logger_data_union since the size is the same as a pointer.
  union logger_data_union data_union;
  
  int logger_extend_index; // Contain index conditional_extend_array defined in master that can be acsessed from extend section.
  
  struct conditional_list_struct conditional_list;
};


// To be stored in volume, a list of pointers to the relevant loggers corresponding to each process
struct logger_for_each_process_list {
  int num_elements;
  struct logger_struct **p_logger_process;
};

// List of logger_for_each_process_list
struct loggers_struct {
  int num_elements;
  struct logger_for_each_process_list *p_logger_volume;
  #pragma acc shape(p_logger_volume[0:num_elements]) init_needed(num_elements)
};


struct abs_logger_struct {
  char name[256];
  // Contains ponters to all the functions assosiated with this logger
  struct abs_logger_pointer_set_struct function_pointers;
  // Contains hard copy of logger_data_union since the size is the same as a pointer.
  union abs_logger_data_union data_union;
  
  // Position and rotation of the abs_logger
  Coords position;
  Rotation rotation;
  Rotation t_rotation;
  
  int abs_logger_extend_index; // Contain index conditional_extend_array defined in master that can be acsessed from extend section.
  
  struct conditional_list_struct conditional_list;
};

// To be stored in volume, a list of pointers to the relevant abs loggers corresponding to each process
/*
struct abs_logger_for_each_process_list {
  int num_elements;
  struct abs_logger_struct **p_abs_logger_process;
};
*/

// List of abs logger_for_each_process_list
struct abs_loggers_struct {
  int num_elements;
  //struct abs_logger_for_each_process_list *p_abs_logger_volume;
  struct abs_logger_struct **p_abs_logger;
};



struct geometry_struct
{
char shape[64];     // name of shape used (sphere, cylinder, box, off, ...)
enum shape eShape;  // enum with shape for flexible functions GPU
double priority_value;    // priority of the geometry
Coords center;      // Center position of volume, reported by components in global frame, updated to main frame in initialize
// Rotation of this volume
Rotation rotation_matrix; // rotation matrix of volume, reported by component in global frame, updated to main frame in initialize
Rotation transpose_rotation_matrix; // As above
// Array of prrotation matrixes for processes assigned to this volume (indexed by non_isotropic_rot_index in the processes)
Rotation *process_rot_matrix_array;             // matrix that transforms from main coordinate system to local process in this specific volume
Rotation *transpose_process_rot_matrix_array;   // matrix that transforms from local process in this specific volume to main coordinate system
int process_rot_allocated;  // Keeps track of allocation status of rot_matrix_array

struct rotation_struct rotation; // Not used, is the x y and z rotation angles.
int visualization_on; // If visualization_on is true, the volume will be drawn in mcdisplay, otherwise not
int is_exit_volume; // If is exit volume = 1, the ray will exit the component when it enters this volume.
int is_mask_volume; // 1 if volume itself is a mask (masking the ones in it's mask list), otherwise 0
int mask_index;
int is_masked_volume; // 1 if this volume is being masked by another volume, the volumes that mask it is in masked_by_list
int mask_mode; // ALL/ANY 1/2. In ALL mode, only parts covered by all masks is simulated, in ANY mode, area covered by just one mask is simulated
double geometry_p_interact; // fraction of rays that interact with this volume for each scattering (between 0 and 1, 0 for disable)
union geometry_parameter_union geometry_parameters; // relevant parameters for this shape
union geometry_parameter_union (*copy_geometry_parameters)(union geometry_parameter_union*);
struct focus_data_struct focus_data; // Used for focusing from this geometry

// New focus data implementation to remove the focusing bug for non-isotripic processes
struct focus_data_array_struct focus_data_array;
struct pointer_to_1d_int_list focus_array_indices; // Add 1D integer array with indecies for correct focus_data for each process


// intersect_function takes position/velocity of ray and parameters, returns time list
int (*intersect_function)(double*,int*,double*,double*,struct geometry_struct*);
//                        t_array,n_ar,r      ,v

// within_function that checks if the ray origin is within this volume
int (*within_function)(Coords,struct geometry_struct*);
//                     r,      parameters

// mcdisplay function, draws the geometry
//void (*mcdisplay_function)(struct lines_to_draw*,int,struct Volume_struct**,int);
void (*mcdisplay_function)(struct lines_to_draw*,int,struct geometry_struct**,int);
//                                lines          index             Geometries   N

void (*initialize_from_main_function)(struct geometry_struct*);

struct pointer_to_1d_coords_list (*shell_points)(struct geometry_struct*, int maximum_number_of_points);

// List of other volumes to be check when ray starts within this volume.
struct pointer_to_1d_int_list intersect_check_list;
// List of other volumes the ray may enter, if the ray intersects the volume itself.
struct pointer_to_1d_int_list destinations_list;
// The destinations list stored as a logic list which makes some tasks quicker. OBSOLETE
//struct pointer_to_1d_int_list destinations_logic_list;
// Reduced list of other volumes the ray may enter, if the ray intersects the volume itself.
struct pointer_to_1d_int_list reduced_destinations_list;
// List of other volumes that are within this volume
struct pointer_to_1d_int_list children;
// List of other volumes that are within this volume, but does not have any parents that are children of this volume
struct pointer_to_1d_int_list direct_children;
// List of next possible volumes (only used in tagging)
struct pointer_to_1d_int_list next_volume_list;
// List of volumes masked by this volume (usually empty)
struct pointer_to_1d_int_list mask_list;
// List of volumes masking this volume
struct pointer_to_1d_int_list masked_by_list;
// List of masks masking this volume (global mask indices)
struct pointer_to_1d_int_list masked_by_mask_index_list;
// Additional intersect lists dependent on mask status
//struct indexed_mask_lists_struct mask_intersect_lists;
// Simpler way of storing the mask_intersect_lists
struct pointer_to_1d_int_list mask_intersect_list;
};

struct physics_struct
{
char name[256]; // User defined material name
int interact_control;
int is_vacuum;
double my_a;
int number_of_processes;
// pointer to array of pointers to physics_sub structures that each describe a scattering process
struct scattering_process_struct *p_scattering_array;
};

union data_transfer_union{
    // List of pointers to storage structs for all supported physical processes
    struct Incoherent_physics_storage_struct  *pointer_to_a_Incoherent_physics_storage_struct;
    struct Powder_physics_storage_struct *pointer_to_a_Powder_physics_storage_struct;
    struct Single_crystal_physics_storage_struct *pointer_to_a_Single_crystal_physics_storage_struct;
    struct AF_HB_1D_physics_storage_struct *pointer_to_a_AF_HB_1D_physics_storage_struct;
    struct IncoherentPhonon_physics_storage_struct *pointer_to_a_IncoherentPhonon_physics_storage_struct;
    struct PhononSimpleNumeric_physics_storage_struct *pointer_to_a_PhononSimpleNumeric_storage_struct;
    struct PhononSimple_physics_storage_struct *pointer_to_a_PhononSimple_storage_struct;
    struct MagnonSimple_physics_storage_struct *pointer_to_a_MagnonSimple_storage_struct;
    struct Sans_spheres_physics_storage_struct *pointer_to_a_Sans_spheres_physics_storage_struct;
    struct Texture_physics_storage_struct *pointer_to_a_Texture_physics_storage_struct;
    struct NCrystal_physics_storage_struct *pointer_to_a_NCrystal_physics_storage_struct;
    struct Non_physics_storage_struct *pointer_to_a_Non_physics_storage_struct;
    struct Template_physics_storage_struct *pointer_to_a_Template_physics_storage_struct;
    // possible to add as many structs as wanted, without increasing memory footprint.
};



struct scattering_process_struct
{
char name[256];                // User defined process name
enum process eProcess;         // enum value corresponding to this process GPU
double process_p_interact;     // double between 0 and 1 that describes the fraction of events forced to undergo this process. -1 for disable
int non_isotropic_rot_index;   // -1 if process is isotrpic, otherwise is the index of the process rotation matrix in the volume
Rotation rotation_matrix;      // rotation matrix of process, reported by component in local frame, transformed and moved to volume struct in main

union data_transfer_union data_transfer; // The way to reach the storage space allocated for this process (see examples in process.comp files)

// probability_for_scattering_functions calculates this probability given k_i and parameters
int (*probability_for_scattering_function)(double*,double*,union data_transfer_union,struct focus_data_struct*, _class_particle *_particle);
//                                         prop,   k_i,   ,parameters               , focus data / function

// A scattering_function takes k_i and parameters, returns k_f
int (*scattering_function)(double*,double*,double*,union data_transfer_union,struct focus_data_struct*, _class_particle *_particle);
//                         k_f,    k_i,    weight, parameters               , focus data / function
};

struct Volume_struct
{
char name[256]; // User defined volume name
struct geometry_struct geometry;        // Geometry properties (including intersect functions, generated lists)
struct physics_struct *p_physics;       // Physical properties (list of scattering processes, absorption)
struct loggers_struct loggers;          // Loggers assosiated with this volume
struct abs_loggers_struct abs_loggers;  // Loggers assosiated with this volume
};

// example of calling a scattering process
// volume_pointer_list[3]->physics.scattering_process[5].probability_for_scattering_function(input,volume_pointer_list[3]->physics.scattering_process[5])

struct starting_lists_struct
{
struct pointer_to_1d_int_list allowed_starting_volume_logic_list;
struct pointer_to_1d_int_list reduced_start_list;
struct pointer_to_1d_int_list start_logic_list;
struct pointer_to_1d_int_list starting_destinations_list;
};

struct global_positions_to_transform_list_struct {
int num_elements;
Coords **positions;
};

struct global_rotations_to_transform_list_struct {
int num_elements;
Rotation **rotations;
};

struct global_process_element_struct
{
char name[128]; // Name of the process
int component_index;
struct scattering_process_struct *p_scattering_process;
};

struct pointer_to_global_process_list {
int num_elements;
struct global_process_element_struct *elements;
};

struct global_material_element_struct
{
char name[128];
int component_index;
struct physics_struct *physics;
};

struct pointer_to_global_material_list {
int num_elements;
struct global_material_element_struct *elements;
};

struct global_geometry_element_struct
{
char name[128];
int component_index;
int activation_counter;
int stored_copies;
int active;
struct Volume_struct *Volume;
};

struct pointer_to_global_geometry_list {
int num_elements;
struct global_geometry_element_struct *elements;
};

struct global_logger_element_struct {
char name[128];
int component_index;
struct logger_struct *logger;
};

struct pointer_to_global_logger_list {
int num_elements;
struct global_logger_element_struct *elements;
};

struct global_abs_logger_element_struct {
char name[128];
int component_index;
struct abs_logger_struct *abs_logger;
};

struct pointer_to_global_abs_logger_list {
int num_elements;
struct global_abs_logger_element_struct *elements;
};

struct global_tagging_conditional_element_struct {
struct conditional_list_struct conditional_list;
int extend_index;
char name[1024];
int use_status;
};

struct global_tagging_conditional_list_struct {
int num_elements;
int current_index;
struct global_tagging_conditional_element_struct *elements;
};


struct global_master_element_struct {
char name[128];
int component_index;
int stored_number_of_scattering_events; // TEST
struct conditional_list_struct *tagging_conditional_list_pointer;
};

struct pointer_to_global_master_list {
int num_elements;
struct global_master_element_struct *elements;
};


// -------------    Physics functions   ---------------------------------------------------------

//#include "Test_physics.c"
//#include "Incoherent_test.c"

// -------------    General functions   ---------------------------------------------------------
double distance_between(Coords position1,Coords position2) {
    return sqrt((position1.x-position2.x)*(position1.x-position2.x) +
                (position1.y-position2.y)*(position1.y-position2.y) +
                (position1.z-position2.z)*(position1.z-position2.z));
};



double length_of_3vector(double *r) {
        return sqrt(r[0]*r[0]+r[1]*r[1]+r[2]*r[2]);
    };

double length_of_position_vector(Coords point) {
        return sqrt(point.x*point.x+point.y*point.y+point.z*point.z);
    };

Coords make_position(double *r) {
    Coords temp;
    
    temp.x = r[0];temp.y = r[1];temp.z = r[2];
    return temp;
};

Coords coords_scalar_mult(Coords input,double scalar) {
    return coords_set(scalar*input.x,scalar*input.y,scalar*input.z);
};

double union_coords_dot(Coords vector1,Coords vector2) {
    return vector1.x*vector2.x + vector1.y*vector2.y + vector1.z*vector2.z;
}


int sum_int_list(struct pointer_to_1d_int_list list) {
    int iterate,sum = 0;
    for (iterate = 0;iterate < list.num_elements;iterate++) sum += list.elements[iterate];
    return sum;
    };
    
int on_int_list(struct pointer_to_1d_int_list list,int target) {
    int iterate,output=0;
    for (iterate = 0; iterate<list.num_elements ;iterate++) {
        if (list.elements[iterate] == target) output = 1;
    }
    return output;
    };

int find_on_int_list(struct pointer_to_1d_int_list list,int target) {
    int iterate;
    for (iterate = 0;iterate < list.num_elements;iterate++) {
        if (list.elements[iterate] == target) return iterate;
    }
    return -1;
    };
    
void on_both_int_lists(struct pointer_to_1d_int_list *list1, struct pointer_to_1d_int_list *list2, struct pointer_to_1d_int_list *common) {
    // Assume common.elements is allocated to a number of int's large enough to hold the resulting list
    int iterate,used_elements=0;
    for (iterate=0;iterate<list1->num_elements;iterate++) {
        if (on_int_list(*list2,list1->elements[iterate])) common->elements[used_elements++] = list1->elements[iterate];
    }
    common->num_elements = used_elements;
    };

void remove_element_in_list_by_index(struct pointer_to_1d_int_list *list,int index) {
    if (index >= list->num_elements) {
      printf("ERROR(remove_element_in_list_by_index): trying to remove an index that wasn't allocated to begin with");
      exit(1);
    }
    else {
        int iterate;
        int *temp;
        for (iterate = index;iterate < list->num_elements -1;iterate++) {
            list->elements[iterate] = list->elements[iterate+1];
        }
        list->num_elements--;
        //if (list->num_elements==0) printf("Making empty list!\n");
        temp = malloc(list->num_elements * sizeof(int));
        for (iterate = 0;iterate < list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
        free(list->elements);
        list->elements = malloc(list->num_elements * sizeof(int));
        for (iterate = 0;iterate < list->num_elements;iterate++) list->elements[iterate] = temp[iterate];
        free(temp);

    }
    };
    
void remove_element_in_list_by_value(struct pointer_to_1d_int_list *list,int value) {
    int iterate;
    for (iterate = 0;iterate < list->num_elements;iterate++) {
        if (list->elements[iterate] == value) remove_element_in_list_by_index(list,iterate);
    }
    };
    
void merge_lists(struct pointer_to_1d_int_list *result,struct pointer_to_1d_int_list *list1,struct pointer_to_1d_int_list *list2) {
    if (result->num_elements > 0) free(result->elements);
    result->num_elements = list1->num_elements + list2->num_elements;
    if (result->num_elements != 0) {
      result->elements = malloc(result->num_elements*sizeof(int));
      int iterate;
      for (iterate = 0;iterate < list1->num_elements;iterate++)
          result->elements[iterate] = list1->elements[iterate];
      for (iterate = 0;iterate < list2->num_elements;iterate++)
          result->elements[list1->num_elements+iterate] = list2->elements[iterate];
    }
    };

void add_element_to_double_list(struct pointer_to_1d_double_list *list,double value) {
    if (list->num_elements == 0) {
      list->num_elements++;
      list-> elements = malloc(list->num_elements*sizeof(double));
      list-> elements[0] = value;
    } else {
      double temp[list->num_elements];
      int iterate;
      for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
      free(list->elements);
      list->num_elements++;
      list-> elements = malloc(list->num_elements*sizeof(double));
      for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
      list->elements[list->num_elements-1] = value;
    }
    };

void add_element_to_int_list(struct pointer_to_1d_int_list *list,int value) {
    if (list->num_elements == 0) {
      list->num_elements++;
      list-> elements = malloc(list->num_elements*sizeof(int));
      list-> elements[0] = value;
    } else {
      int temp[list->num_elements];
      int iterate;
      for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
      free(list->elements);
      list->num_elements++;
      list-> elements = malloc(list->num_elements*sizeof(int));
      for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
      list->elements[list->num_elements-1] = value;
    }
    };

// Need to check if absolute_rotation is preserved correctly.
void add_element_to_focus_data_array(struct focus_data_array_struct *focus_data_array,struct focus_data_struct focus_data) {
    if (focus_data_array->num_elements == 0) {
      focus_data_array->num_elements++;
      focus_data_array-> elements = malloc(focus_data_array->num_elements*sizeof(struct focus_data_struct));
      focus_data_array-> elements[0] = focus_data;
    } else {
      struct focus_data_struct temp[focus_data_array->num_elements];
      int iterate;
      for (iterate=0;iterate<focus_data_array->num_elements;iterate++) temp[iterate] = focus_data_array->elements[iterate];
      free(focus_data_array->elements);
      focus_data_array->num_elements++;
      focus_data_array-> elements = malloc(focus_data_array->num_elements*sizeof(struct focus_data_struct));
      for (iterate=0;iterate<focus_data_array->num_elements-1;iterate++) focus_data_array->elements[iterate] = temp[iterate];
      focus_data_array->elements[focus_data_array->num_elements-1] = focus_data;
    }
    };


void add_to_logger_with_data(struct logger_with_data_struct *logger_with_data, struct logger_struct *logger) {
    // May reorder the order of the if conditions to avoid checking the == 0 for every single ray
    if (logger_with_data->allocated_elements == 0) {
        logger_with_data->allocated_elements = 5;
        logger_with_data->logger_pointers = malloc(logger_with_data->allocated_elements*sizeof(struct logger_struct*));
        logger_with_data->used_elements = 1;
        logger_with_data->logger_pointers[0] = logger;
    } else if (logger_with_data->used_elements > logger_with_data->allocated_elements-1) {
        struct logger_with_data_struct temp_logger_with_data;
        temp_logger_with_data.logger_pointers = malloc((logger_with_data->used_elements)*sizeof(struct logger_struct*));
        int iterate;
        for (iterate=0;iterate<logger_with_data->used_elements;iterate++) {
            temp_logger_with_data.logger_pointers[iterate]  = logger_with_data->logger_pointers[iterate];
        }
        free(logger_with_data->logger_pointers);
        logger_with_data->allocated_elements = logger_with_data->allocated_elements+5;
        logger_with_data->logger_pointers = malloc(logger_with_data->allocated_elements*sizeof(struct logger_struct*));
        for (iterate=0;iterate<logger_with_data->used_elements;iterate++) {
            logger_with_data->logger_pointers[iterate]  = temp_logger_with_data.logger_pointers[iterate];
        }

        logger_with_data->logger_pointers[logger_with_data->used_elements++] = logger;
        
        
    } else {
        logger_with_data->logger_pointers[logger_with_data->used_elements++] = logger;
    }
    
};

void add_to_abs_logger_with_data(struct abs_logger_with_data_struct *abs_logger_with_data, struct abs_logger_struct *abs_logger) {
    // May reorder the order of the if conditions to avoid checking the == 0 for every single ray
    if (abs_logger_with_data->allocated_elements == 0) {
        abs_logger_with_data->allocated_elements = 5;
        abs_logger_with_data->abs_logger_pointers = malloc(abs_logger_with_data->allocated_elements*sizeof(struct abs_logger_struct*));
        abs_logger_with_data->used_elements = 1;
        abs_logger_with_data->abs_logger_pointers[0] = abs_logger;
    } else if (abs_logger_with_data->used_elements > abs_logger_with_data->allocated_elements-1) {
        struct abs_logger_with_data_struct temp_abs_logger_with_data;
        temp_abs_logger_with_data.abs_logger_pointers = malloc((abs_logger_with_data->used_elements)*sizeof(struct abs_logger_struct*));
        int iterate;
        for (iterate=0;iterate<abs_logger_with_data->used_elements;iterate++) {
            temp_abs_logger_with_data.abs_logger_pointers[iterate]  = abs_logger_with_data->abs_logger_pointers[iterate];
        }
        free(abs_logger_with_data->abs_logger_pointers);
        abs_logger_with_data->allocated_elements = abs_logger_with_data->allocated_elements+5;
        abs_logger_with_data->abs_logger_pointers = malloc(abs_logger_with_data->allocated_elements*sizeof(struct abs_logger_struct*));
        for (iterate=0;iterate<abs_logger_with_data->used_elements;iterate++) {
            abs_logger_with_data->abs_logger_pointers[iterate] = temp_abs_logger_with_data.abs_logger_pointers[iterate];
        }

        abs_logger_with_data->abs_logger_pointers[abs_logger_with_data->used_elements++] = abs_logger;
        
        
    } else {
        abs_logger_with_data->abs_logger_pointers[abs_logger_with_data->used_elements++] = abs_logger;
    }
    
};


// Used typedef to avoid having to change this function later. May update others to use same phillosphy.
void add_function_to_conditional_list(struct conditional_list_struct *list,conditional_function_pointer new, union conditional_data_union *data_union) {
    if (list->num_elements == 0) {
      list->num_elements++;
      list->conditional_functions = malloc(list->num_elements*sizeof(conditional_function_pointer));
      list->p_data_unions = malloc(list->num_elements*sizeof(union conditional_data_union*));
      list->conditional_functions[0] = new;
      list->p_data_unions[0] = data_union;
    }
    else {
    conditional_function_pointer temp_fp[list->num_elements];
    union conditional_data_union *temp_du[list->num_elements];
    int iterate;
    // Could even get away with a shallow copy here instead of the loop, but it is not relevant for performance.
    for (iterate=0;iterate<list->num_elements;iterate++) {
      temp_fp[iterate] = list->conditional_functions[iterate];
      temp_du[iterate] = list->p_data_unions[iterate];
    }
    free(list->conditional_functions);
    free(list->p_data_unions);
    list->num_elements++;
    list->conditional_functions = malloc(list->num_elements*sizeof(conditional_function_pointer));
    list->p_data_unions = malloc(list->num_elements*sizeof(union conditional_data_union*));
    
    for (iterate=0;iterate<list->num_elements-1;iterate++) {
      list->conditional_functions[iterate] = temp_fp[iterate];
      list->p_data_unions[iterate] = temp_du[iterate];
    }
    list->conditional_functions[list->num_elements-1] = new;
    list->p_data_unions[list->num_elements-1] = data_union;
    }
};


// could make function that removes a element from a 1d_int_list, and have each list generation as a function that takes a copy of an overlap list

void print_1d_int_list(struct pointer_to_1d_int_list list,char *name) {
        int iterate;
        printf("LIST: ");printf("%s",name);printf(" = [");
        for (iterate = 0; iterate < list.num_elements; iterate++) {
            printf("%d",list.elements[iterate]);
            if (iterate < list.num_elements - 1) printf(",");
        }
        printf("]\n");
    };

void print_1d_double_list(struct pointer_to_1d_double_list list,char *name) {
        int iterate;
        printf("LIST: ");printf("%s",name);printf(" = [");
        for (iterate = 0; iterate < list.num_elements; iterate++) {
            printf("%f",list.elements[iterate]);
            if (iterate < list.num_elements - 1) printf(",");
        }
        printf("]\n");
    };

void print_position(Coords pos,char *name) {
    printf("POSITION: ");printf("%s",name);printf(" = (%f,%f,%f)\n",pos.x,pos.y,pos.z);
    };

void print_rotation(Rotation rot, char *name) {
    printf("ROT MATRIX: %s \n",name);
    printf("[%f %f %f]\n",rot[0][0],rot[0][1],rot[0][2]);
    printf("[%f %f %f]\n",rot[1][0],rot[1][1],rot[1][2]);
    printf("[%f %f %f]\n\n",rot[2][0],rot[2][1],rot[2][2]);
};
    
void allocate_list_from_temp(int num_elements,struct pointer_to_1d_int_list original,struct pointer_to_1d_int_list *new) {
        int iterate;
    
        new->num_elements = num_elements;
        if (num_elements > 0) {
            new->elements = malloc(num_elements*sizeof(int));
            for (iterate = 0;iterate < num_elements; iterate++) new->elements[iterate] = original.elements[iterate];
        } else new->elements = NULL;
    
    };

void allocate_logic_list_from_temp(int num_elements,struct pointer_to_1d_int_list original, struct pointer_to_1d_int_list *new) {
        // A logic list shares the same structure of a normal list, but instead of listing numbers, it is a list of yes / no (1/0)
        // Giving this function a list of [1 3 5] (and num_elements = 9) would return [0 1 0 1 0 1 0 0 0];
        int iterate;
    
        new->num_elements = num_elements;
        if (num_elements > 0) {
            new->elements = malloc(num_elements*sizeof(int));
            for (iterate = 0;iterate < num_elements;iterate++) new->elements[iterate] = 0;
            for (iterate = 0;iterate < original.num_elements;iterate++) {
                if (original.elements[iterate] < num_elements)
                    new->elements[original.elements[iterate]] = 1;
                else printf("Trying to allocate logical list without enough memory\n");
            }
        } else new->elements = NULL;
    };

/*
struct global_positions_to_transform_list_struct {
int num_elements;
Coords **positions;
}

struct global_rotations_to_transform_list_struct {
int num_elements;
Rotation **rotations;
}
*/

void add_position_pointer_to_list(struct global_positions_to_transform_list_struct *list, Coords *new_position_pointer) {
    if (list->num_elements == 0) {
      list->num_elements++;
      list->positions = malloc(list->num_elements*sizeof(Coords*));
      list->positions[0] = new_position_pointer;
    } else {
      Coords **temp;
      temp = malloc(list->num_elements*sizeof(Coords*));
      if (temp == NULL) printf("malloc failed in add_position_pointer_to_list for temp\n");
      
      int iterate;
      for (iterate=0;iterate<list->num_elements;iterate++)
        temp[iterate] = list->positions[iterate];
      free(list->positions);
      list->num_elements++;
      list->positions = malloc(list->num_elements*sizeof(Coords*));
      if (list->positions == NULL) printf("malloc failed in add_position_pointer_to_list for list->positions\n");
      
      for (iterate=0;iterate<list->num_elements-1;iterate++)
        list->positions[iterate] = temp[iterate];
      free(temp);
      list->positions[list->num_elements-1] = new_position_pointer;
    }
};

void add_rotation_pointer_to_list(struct global_rotations_to_transform_list_struct *list, Rotation *new_rotation_pointer) {
    if (list->num_elements == 0) {
      list->num_elements++;
      list->rotations = malloc(list->num_elements*sizeof(Rotation*));
      list->rotations[0] = new_rotation_pointer;
    } else {
      Rotation **temp;
      temp = malloc(list->num_elements*sizeof(Rotation*));
      int iterate;
      for (iterate=0;iterate<list->num_elements;iterate++)
        temp[iterate] = list->rotations[iterate];
      free(list->rotations);
      list->num_elements++;
      list->rotations = malloc(list->num_elements*sizeof(Rotation*));
      
      for (iterate=0;iterate<list->num_elements-1;iterate++)
        list->rotations[iterate] = temp[iterate];
      free(temp);
      list->rotations[list->num_elements-1] = new_rotation_pointer;
    }
};

void add_element_to_process_list(struct pointer_to_global_process_list *list,struct global_process_element_struct new_element) {
    if (list->num_elements == 0) {
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_process_element_struct));
    list-> elements[0] = new_element;
    }
    else {
    struct global_process_element_struct temp[list->num_elements];
    int iterate;
    for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
    free(list->elements);
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_process_element_struct));
    for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
    list->elements[list->num_elements-1] = new_element;
    }
};

void add_element_to_material_list(struct pointer_to_global_material_list *list,struct global_material_element_struct new_element) {
    if (list->num_elements == 0) {
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_material_element_struct));
    list-> elements[0] = new_element;
    }
    else {
    struct global_material_element_struct temp[list->num_elements];
    int iterate;
    for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
    free(list->elements);
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_material_element_struct));
    for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
    list->elements[list->num_elements-1] = new_element;
    }
};

void add_element_to_geometry_list(struct pointer_to_global_geometry_list *list,struct global_geometry_element_struct new_element) {
    if (list->num_elements == 0) {
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_geometry_element_struct));
    list-> elements[0] = new_element;
    }
    else {
    struct global_geometry_element_struct temp[list->num_elements];
    int iterate;
    for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
    free(list->elements);
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_geometry_element_struct));
    for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
    list->elements[list->num_elements-1] = new_element;
    }
};

void add_element_to_logger_list(struct pointer_to_global_logger_list *list,struct global_logger_element_struct new_element) {
    if (list->num_elements == 0) {
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_logger_element_struct));
    list-> elements[0] = new_element;
    }
    else {
    struct global_logger_element_struct temp[list->num_elements];
    int iterate;
    for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
    free(list->elements);
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_logger_element_struct));
    for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
    list->elements[list->num_elements-1] = new_element;
    }
};

void add_element_to_abs_logger_list(struct pointer_to_global_abs_logger_list *list, struct global_abs_logger_element_struct new_element) {
    if (list->num_elements == 0) {
      list->num_elements++;
      list->elements = malloc(list->num_elements*sizeof(struct global_abs_logger_element_struct));
      list->elements[0] = new_element;
    }
    else {
      struct global_abs_logger_element_struct temp[list->num_elements];
      int iterate;
      for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
      free(list->elements);
      list->num_elements++;
      list-> elements = malloc(list->num_elements*sizeof(struct global_abs_logger_element_struct));
      for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
      list->elements[list->num_elements-1] = new_element;
    }
};

void add_element_to_tagging_conditional_list(struct global_tagging_conditional_list_struct *list,struct global_tagging_conditional_element_struct new_element) {
    if (list->num_elements == 0) {
    list->num_elements++;
    list->elements = malloc(list->num_elements*sizeof(struct global_tagging_conditional_element_struct));
    list->elements[0] = new_element;
    }
    else {
    struct global_tagging_conditional_element_struct temp[list->num_elements];
    int iterate;
    for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
    free(list->elements);
    list->num_elements++;
    list->elements = malloc(list->num_elements*sizeof(struct global_tagging_conditional_element_struct));
    for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
    list->elements[list->num_elements-1] = new_element;
    }
};

void add_element_to_master_list(struct pointer_to_global_master_list *list,struct global_master_element_struct new_element) {
    if (list->num_elements == 0) {
    list->num_elements++;
    list->elements = malloc(list->num_elements*sizeof(struct global_master_element_struct));
    list->elements[0] = new_element;
    }
    else {
    struct global_master_element_struct temp[list->num_elements];
    int iterate;
    for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
    free(list->elements);
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_master_element_struct));
    for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
    list->elements[list->num_elements-1] = new_element;
    }
};

void add_initialized_logger_in_volume(struct loggers_struct *loggers,int number_of_processes) {
  int iterate;
  if (loggers->num_elements == 0) {
    loggers->num_elements++;
    loggers->p_logger_volume = malloc(loggers->num_elements * sizeof(struct logger_for_each_process_list));
    loggers->p_logger_volume[0].num_elements = number_of_processes;
    loggers->p_logger_volume[0].p_logger_process = malloc(number_of_processes * sizeof(struct logger_struct**));
    for (iterate=0;iterate<number_of_processes;iterate++)
      loggers->p_logger_volume[0].p_logger_process[iterate] = NULL;
  } else {
    // Already some elements, store them in temp, free main, transfer back and add newest.
    struct logger_for_each_process_list temp[loggers->num_elements];
    
    for (iterate=0;iterate<loggers->num_elements;iterate++) temp[iterate] = loggers->p_logger_volume[iterate];
    free(loggers->p_logger_volume);
    loggers->num_elements++;
    loggers->p_logger_volume = malloc(loggers->num_elements*sizeof(struct logger_for_each_process_list));
    for (iterate=0;iterate<loggers->num_elements-1;iterate++) loggers->p_logger_volume[iterate] = temp[iterate];
    loggers->p_logger_volume[loggers->num_elements-1].num_elements = number_of_processes;
    loggers->p_logger_volume[loggers->num_elements-1].p_logger_process = malloc(number_of_processes * sizeof(struct logger_struct**));
    for (iterate=0;iterate<number_of_processes;iterate++) {
      loggers->p_logger_volume[loggers->num_elements-1].p_logger_process[iterate] = NULL;
    }
  }
};

/*
void add_initialized_abs_logger_in_volume(struct abs_loggers_struct *abs_loggers, int number_of_processes) {
  int iterate;
  if (abs_loggers->num_elements == 0) {
    abs_loggers->num_elements++;
    abs_loggers->p_abs_logger_volume = malloc(abs_loggers->num_elements * sizeof(struct abs_loggers_struct));
    abs_loggers->p_abs_logger_volume[0].num_elements = number_of_processes;
    abs_loggers->p_abs_logger_volume[0].p_abs_logger_process = malloc(number_of_processes * sizeof(struct abs_logger_struct**));
    for (iterate=0;iterate<number_of_processes;iterate++)
      abs_loggers->p_abs_logger_volume[0].p_abs_logger_process[iterate] = NULL;
  } else {
    // Already some elements, store them in temp, free main, transfer back and add newest.
    struct abs_logger_for_each_process_list temp[abs_loggers->num_elements];
    
    for (iterate=0;iterate<abs_loggers->num_elements;iterate++) temp[iterate] = abs_loggers->p_abs_logger_volume[iterate];
    free(abs_loggers->p_abs_logger_volume);
    abs_loggers->num_elements++;
    abs_loggers->p_abs_logger_volume = malloc(abs_loggers->num_elements*sizeof(struct abs_logger_for_each_process_list));
    for (iterate=0;iterate<abs_loggers->num_elements-1;iterate++) abs_loggers->p_abs_logger_volume[iterate] = temp[iterate];
    abs_loggers->p_abs_logger_volume[abs_loggers->num_elements-1].num_elements = number_of_processes;
    abs_loggers->p_abs_logger_volume[abs_loggers->num_elements-1].p_abs_logger_process = malloc(number_of_processes * sizeof(struct abs_logger_struct**));
    for (iterate=0;iterate<number_of_processes;iterate++) {
      abs_loggers->p_abs_logger_volume[abs_loggers->num_elements-1].p_abs_logger_process[iterate] = NULL;
    }
  }
};
*/

void add_initialized_abs_logger_in_volume(struct abs_loggers_struct *abs_loggers) {
  int iterate;
  if (abs_loggers->num_elements == 0) {
    abs_loggers->num_elements++;
    abs_loggers->p_abs_logger = malloc(abs_loggers->num_elements * sizeof(struct abs_logger_struct*));
  } else {
    // Already some elements, store them in temp, free main, transfer back and add newest.
    struct abs_logger_struct *temp[abs_loggers->num_elements];
    
    for (iterate=0;iterate<abs_loggers->num_elements;iterate++) temp[iterate] = abs_loggers->p_abs_logger[iterate];
    free(abs_loggers->p_abs_logger);
    
    abs_loggers->num_elements++;
    abs_loggers->p_abs_logger = malloc(abs_loggers->num_elements*sizeof(struct abs_logger_struct*));
    for (iterate=0;iterate<abs_loggers->num_elements-1;iterate++) abs_loggers->p_abs_logger[iterate] = temp[iterate];
    abs_loggers->p_abs_logger[abs_loggers->num_elements-1] = NULL;
    
  }
};


// -------------    Functions used to shorten master trace    ---------------------------------------------


void update_current_mask_intersect_status(struct pointer_to_1d_int_list *current_mask_intersect_list_status, struct pointer_to_1d_int_list *mask_status_list, struct Volume_struct **Volumes, int *current_volume) {
  // This function is to be executed whenever the current volume changes, or the mask status changes
  // It updates the effective mask status for each element of a volumes mask_intersect_list
  // The effective mask takes the ALL/ANY mode assosiated with each volume into account,
  //  meaning a volume needs to be within ALL/ANY of it's masks to have a status of 1
  // In most cases this mask_intersect_list will be empty, and memory operations are avoided
  //printf("Number of elements to be check: %d \n",Volumes[*current_volume]->geometry.mask_intersect_list.num_elements);
  
  if (Volumes[*current_volume]->geometry.mask_intersect_list.num_elements > 0) {
    int iterate,this_element,*mask_start,*mask_check;
    for (iterate=0;iterate<Volumes[*current_volume]->geometry.mask_intersect_list.num_elements;iterate++) {
      this_element = Volumes[*current_volume]->geometry.mask_intersect_list.elements[iterate];
      //printf("We are investigating volume number %d from the mask_intersect_list of volume %d",this_element,*current_volume);
      if (Volumes[this_element]->geometry.mask_mode == 2) { // ANY mask mode
        //printf("We are in ANY mask mode! \n");
        current_mask_intersect_list_status->elements[iterate] = 0; // Assume the mask status is 0, but if any are 1, take that instead
        for (mask_start=mask_check=Volumes[this_element]->geometry.masked_by_mask_index_list.elements;mask_check-mask_start<Volumes[this_element]->geometry.masked_by_mask_index_list.num_elements;mask_check++) {
          //printf("Checking all the mask statuses of volumes masking %d, now mask with global mask index %d ! \n",this_element,*mask_check);
          if (mask_status_list->elements[*mask_check] == 1) {
            //printf("The status was 1, so the effective status is set to 1 and the loop is stopped");
            current_mask_intersect_list_status->elements[iterate] = 1;
            break;
          }
        }
      } else { // ALL mask mode
        //printf("We are in ALL mask mode! \n");
        current_mask_intersect_list_status->elements[iterate] = 1; // Assume the mask status is 1, but if any one is 0, take that instead
        for (mask_start=mask_check=Volumes[this_element]->geometry.masked_by_mask_index_list.elements;mask_check-mask_start<Volumes[this_element]->geometry.masked_by_mask_index_list.num_elements;mask_check++) {
          //printf("Checking all the mask statuses of volumes masking %d, now mask with global mask index %d ! \n",this_element,*mask_check);
          if (mask_status_list->elements[*mask_check] == 0) {
            //printf("The status was 0, so the effective status is set to 0 and the loop is stopped \n");
            current_mask_intersect_list_status->elements[iterate] = 0;
            break;
          }
        }
      }
    }
  }
    
  /* Original version compatible with trace scope
  for (iterate=0;iterate<Volumes[current_volume]->geometry.mask_intersect_list.num_elements;iterate++) {
    this_element = Volumes[current_volume]->geometry.mask_intersect_list.elements[iterate];
    if (Volumes[this_element]->geometry.mask_mode == 2) { // ANY mask mode
      current_mask_intersect_list_status.elements[iterate] = 0; // Assume the mask status is 0, but if any are 1, take that instead
      for (mask_start=mask_check=Volumes[this_element]->geometry.masked_by_mask_index_list.elements;mask_check-mask_start<Volumes[this_element]->geometry.masked_by_mask_index_list.num_elements;mask_check++) {
        if (mask_status_list[*mask_check] == 1) {
          current_mask_intersect_list_status.elements[iterate] = 1;
          break;
        }
      }
    } else { // ALL mask mode
      current_mask_intersect_list_status.elements[iterate] = 1; // Assume the mask status is 1, but if any one is 0, take that instead
      for (mask_start=mask_check=Volumes[this_element]->geometry.masked_by_mask_index_list.elements;mask_check-mask_start<Volumes[this_element]->geometry.masked_by_mask_index_list.num_elements;mask_check++) {
        if (mask_status_list[*mask_check] == 0) {
          current_mask_intersect_list_status.elements[iterate] = 0;
          break;
        }
      }
    }
  }
  */
}

// -------------    Tagging functions    ------------------------------------------------------------------

struct tagging_tree_node_struct {
  // Statistics:
  double intensity;
  int number_of_rays;
  // tree pointers
  struct tagging_tree_node_struct *above;   // Pointer to node above
  struct tagging_tree_node_struct **volume_branches;
  struct tagging_tree_node_struct **process_branches;
};

struct list_of_tagging_tree_node_pointers {
  struct tagging_tree_node_struct **elements;
  int num_elements;
};

struct tagging_tree_node_struct *make_tagging_tree_node(void) {
    return (struct tagging_tree_node_struct *) malloc(sizeof(struct tagging_tree_node_struct));
}


struct tagging_tree_node_struct  *simple_initialize_tagging_tree_node(struct tagging_tree_node_struct *new_node) {
    //struct tagging_tree_node_struct *dummy;
    //dummy = malloc(sizeof(struct tagging_tree_node_struct));
    //new_node = dummy;
    
    //new_node->element = (struct tagging_tree_node_struct *) malloc(sizeof(struct tagging_tree_node_struct));
    //new_node = (struct tagging_tree_node_struct *) malloc(sizeof(struct tagging_tree_node_struct));
    new_node = make_tagging_tree_node();
    
    if (new_node == NULL) printf("ERROR, Union tagging system could not allocate memory\n");
    new_node->intensity = 4.2; // (double) 4.2;
    new_node->number_of_rays = 42; //(int) 42;
    
    printf("new_node->intensity = %f, new_node->number_of_rays = %d \n",new_node->intensity,new_node->number_of_rays);
    return new_node;
};


struct tagging_tree_node_struct *initialize_tagging_tree_node(struct tagging_tree_node_struct *new_node, struct tagging_tree_node_struct *above_node, struct Volume_struct *this_volume) {
    new_node = make_tagging_tree_node();
    
    new_node->intensity = (double) 0;
    new_node->number_of_rays = (int) 0;
    new_node->above = above_node;
    
    int next_volume_list_length = this_volume->geometry.next_volume_list.num_elements;
    new_node->volume_branches = malloc(next_volume_list_length*sizeof(struct tagging_tree_node_struct*));
    int iterate;
    // Initializing pointers so that they can be checked for NULL later. Is this redundant? Does malloc return null pointers?
    for (iterate=0;iterate<next_volume_list_length;iterate++) new_node->volume_branches[iterate] = NULL;
    //new_node->volume_branches.num_elements = dest_list_length; // May be removed
    
    int number_of_processes;
    if (this_volume->p_physics == NULL) number_of_processes = 0;
    else number_of_processes = this_volume->p_physics->number_of_processes;
    
    new_node->process_branches = malloc(number_of_processes*sizeof(struct tagging_tree_node_struct*));
    // Initializing pointers so that they can be checked for NULL later. Is this redundant? Does malloc return null pointers?
    for (iterate=0;iterate<number_of_processes;iterate++) new_node->process_branches[iterate] = NULL;
    //new_node->process_branches.num_elements=number_of_processes; // May be removed
    
    return new_node;
};

struct tagging_tree_node_struct *goto_process_node(struct tagging_tree_node_struct *current_node, int process_index, struct Volume_struct *this_volume, int *stop_tagging_ray, int stop_creating_nodes) {
    // Either create a new node if it has not been created yet, or travel down the tree
    if (current_node->process_branches[process_index] == NULL) {
      if (stop_creating_nodes == 0) {
        current_node->process_branches[process_index] = initialize_tagging_tree_node(current_node->process_branches[process_index],current_node,this_volume);
        return current_node->process_branches[process_index];
      } else {
        // This stops the ray from using more goto node functions and being counted in the statistics.
        // Happens because the history limit is reached, and no new histories should be started.
        *stop_tagging_ray = 1;
        return current_node;
      }
    } else {
        current_node = current_node->process_branches[process_index];
        return current_node;
    }
    
    //return current_node;
};

struct tagging_tree_node_struct *goto_volume_node(struct tagging_tree_node_struct *current_node,int current_volume, int next_volume, struct Volume_struct **Volumes, int *stop_tagging_ray, int stop_creating_nodes) {
    // I have only allocated the number of node branches that corresponds to the current_volumes next_volume_list
    // The problem is to find where on the destination list the current volume is without doing a manual search
    
    // With the new mask system there is a risk of going from and to the same volume, which should be ignored by the tagging system
    if (current_volume == next_volume) return current_node;
    
    struct tagging_tree_node_struct *output;
    int next_volume_list_index = -1;
    // Temporary slow method for finding the correct index on the destination list
    int iterate;
    
    for (iterate=0;iterate<Volumes[current_volume]->geometry.next_volume_list.num_elements;iterate++)
        if (Volumes[current_volume]->geometry.next_volume_list.elements[iterate] == next_volume) {
            next_volume_list_index = iterate;
            break;
        }
    
    // Debug phase
    //printf("Tagging: going from volume %d to volume %d, which is index number %d on it's next volume list \n",current_volume,next_volume,next_volume_list_index);
    #ifndef OPENACC
    if (next_volume_list_index == -1) {
        printf("ERROR in Union component, tagging or destination system failed, next volume was not on next volume list\n");
        printf("current_volume = %d, next_volume = %d \n",current_volume,next_volume);
        print_1d_int_list(Volumes[current_volume]->geometry.destinations_list,"destinations_list for current volume");
        exit(1);
    }
    #endif

    // Either create a new node if it has not been created yet, or travel down the tree
    if (current_node->volume_branches[next_volume_list_index] == NULL) {
      if (stop_creating_nodes == 0) {
        current_node->volume_branches[next_volume_list_index] =  initialize_tagging_tree_node(current_node->volume_branches[next_volume_list_index],current_node,Volumes[next_volume]);
        return current_node->volume_branches[next_volume_list_index];
      } else {
        // This stops the ray from using more goto node functions and being counted in the statistics.
        // Happens because the history limit is reached, and no new histories should be started.
        *stop_tagging_ray = 1;
        return current_node;
      }
    } else {
        //current_node = current_node->volume_branches[next_volume_list_index];
        //return current_node;
        current_node = current_node->volume_branches[next_volume_list_index];
        //printf("used allocated node \n");
        return current_node;
    }
};

void add_statistics_to_node(struct tagging_tree_node_struct *current_node, Coords *r, Coords *v, double *weight, int *counter) {
    if (current_node->number_of_rays == 0) (*counter)++;
    current_node->number_of_rays = current_node->number_of_rays + 1;
    current_node->intensity = current_node->intensity + *weight;
};

struct history_node_struct {
    int volume_index;
    int process_index;
};

struct dynamic_history_list {
  struct history_node_struct *elements;
  int used_elements;
  int allocated_elements;
};

struct saved_history_struct {
    struct history_node_struct *elements;
    int used_elements;
    double intensity;
    int number_of_rays;
};

struct total_history_struct {
    struct saved_history_struct *saved_histories;
    int used_elements;
    int allocated_elements;
};


void add_to_history(struct dynamic_history_list *history, int volume_index, int process_index) {
    //printf("Adding to history[%d]: volume_index = %d, process_index = %d \n",history->used_elements,volume_index,process_index);
    if (history->allocated_elements == 0) {
        history->elements = malloc(5*sizeof(struct history_node_struct));
        history->used_elements = 1;
        history->allocated_elements = 5;
        history->elements[0].volume_index = volume_index;
        history->elements[0].process_index = process_index;
    } else if (history->used_elements > history->allocated_elements-1) {
        struct dynamic_history_list temp_history;
        temp_history.elements = malloc((history->used_elements)*sizeof(struct history_node_struct));
        int iterate;
        for (iterate=0;iterate<history->used_elements;iterate++) {
            temp_history.elements[iterate].volume_index  = history->elements[iterate].volume_index;
            temp_history.elements[iterate].process_index = history->elements[iterate].process_index;
        }
        free(history->elements);
        history->elements = malloc((history->allocated_elements+5)*sizeof(struct history_node_struct));
        for (iterate=0;iterate<history->used_elements;iterate++) {
            history->elements[iterate].volume_index  = temp_history.elements[iterate].volume_index;
            history->elements[iterate].process_index = temp_history.elements[iterate].process_index;
        }
        
        history->allocated_elements = history->allocated_elements+5;
        
        history->elements[history->used_elements].volume_index = volume_index;
        history->elements[history->used_elements].process_index = process_index;
        history->used_elements = history->used_elements+1;
        
    } else {
        history->elements[history->used_elements].volume_index = volume_index;
        history->elements[history->used_elements].process_index = process_index;
        history->used_elements++;
    }
    
};

void printf_history(struct dynamic_history_list *history) {
    int history_iterate;
          //printf("History number %d, intensity = %f, number of rays = %d:",hist_num, search_node->intensity, search_node->number_of_rays);
          for (history_iterate=0;history_iterate<history->used_elements-1;history_iterate++) {
            if (history->elements[history_iterate].process_index == -1) {
                printf(" V%d ->",history->elements[history_iterate].volume_index);
            } else {
                printf(" P%d ->",history->elements[history_iterate].process_index);
            }
          }
          if (history->elements[history_iterate].process_index == -1) {
                printf(" V%d \n",history->elements[history_iterate].volume_index);
          } else {
                printf(" P%d \n",history->elements[history_iterate].process_index);
          }
}

void fprintf_total_history(struct saved_history_struct *history, FILE *fp) {
    fprintf(fp,"%d\t N I=%E \t", history->number_of_rays, history->intensity);
        int history_iterate;
          //printf("History number %d, intensity = %f, number of rays = %d:",hist_num, search_node->intensity, search_node->number_of_rays);
          for (history_iterate=0;history_iterate<history->used_elements-1;history_iterate++) {
            if (history->elements[history_iterate].process_index == -1) {
                fprintf(fp," V%d ->",history->elements[history_iterate].volume_index);
            } else {
                fprintf(fp," P%d ->",history->elements[history_iterate].process_index);
            }
          }
          if (history->elements[history_iterate].process_index == -1) {
                fprintf(fp," V%d \n",history->elements[history_iterate].volume_index);
          } else {
                fprintf(fp," P%d \n",history->elements[history_iterate].process_index);
          }
}

/*
int Sample_compare_doubles (const void *a, const void *b) {
  const double *da = (const double *) a;
  const double *db = (const double *) b;

  return (*da > *db) - (*da < *db);
}
*/


int Sample_compare_history_intensities (const struct saved_history_struct *a, const struct saved_history_struct *b) {
  const double *da = (const double *) &(a->intensity);
  const double *db = (const double *) &(b->intensity);

  return (*da < *db) - (*da > *db);
}

void write_tagging_tree(struct list_of_tagging_tree_node_pointers *master_list, struct Volume_struct **Volumes, int total_history_counter, int number_of_volumes) {
  // Start from top of tree, go to extremeties, take results and add to disk / database, free that node
  int volume_index,done,volume_iterate,process_iterate,current_volume,next_node_found,current_number_of_processes,history_iterate,hist_num;
  
  struct tagging_tree_node_struct *search_node;
  struct tagging_tree_node_struct **kill_candidate;
  struct dynamic_history_list history_data; // Allocate the history list struct
  struct dynamic_history_list *history;     // Use this pointer in the algorithm
  
  struct total_history_struct total_history;
  total_history.saved_histories = malloc(total_history_counter * sizeof(struct saved_history_struct));
  total_history.allocated_elements = total_history_counter;
  total_history.used_elements = 0;
  
  history = &history_data;
  
  history->used_elements = 0;
  history->allocated_elements = 0;
  
  hist_num = 0;
  for (volume_index=0;volume_index<master_list->num_elements;volume_index++) {
    
    search_node = master_list->elements[volume_index];
    
    if (volume_index != 0)
        current_number_of_processes = Volumes[volume_index]->p_physics->number_of_processes;
    else
        current_number_of_processes = 0;
    
    current_volume = volume_index;
    done = 0;
    
    history->used_elements = 0;
    
    add_to_history(history,current_volume,-1);
    while(done == 0) {
        next_node_found=0;
        for (volume_iterate=0;volume_iterate<Volumes[current_volume]->geometry.next_volume_list.num_elements;volume_iterate++) {
            //printf("searc_node->volume_branches[0]->intensity = %f\n",search_node->volume_branches[0]->intensity);
            if (search_node->volume_branches[volume_iterate] != NULL) {
                current_volume = Volumes[current_volume]->geometry.next_volume_list.elements[volume_iterate];
                if (current_volume != 0)
                    current_number_of_processes = Volumes[current_volume]->p_physics->number_of_processes;
                else
                    current_number_of_processes = 0;
                
                //search_node = &(search_node->volume_branches[volume_iterate]);
                kill_candidate = &(search_node->volume_branches[volume_iterate]);
                search_node = search_node->volume_branches[volume_iterate];
                next_node_found = 1;
                add_to_history(history,current_volume,-1);
                //printf_history(history);
                break;
            }
        }
        if (next_node_found == 0) {
          //printf("doing process loop with %d steps \n",current_number_of_processes);
          for (process_iterate=0;process_iterate<current_number_of_processes;process_iterate++) {
            //if (&(search_node->process_branches[volume_iterate]) != NULL) {
            if (search_node->process_branches[process_iterate] != NULL) {
                //printf("was not NULL (process)\n");
                //search_node = &(search_node->process_branches[process_iterate]);
                kill_candidate = &(search_node->process_branches[process_iterate]);
                search_node = search_node->process_branches[process_iterate];
                next_node_found = 1;
                add_to_history(history,current_volume,process_iterate);
                //printf_history(history);
                break;
            }
          }
        }
        
        if (next_node_found == 0) {
          // write this history to disk / memory
          hist_num++;
          //printf("Reached next_node_found == 0 \n");
          if (history->used_elements > 0 && search_node->number_of_rays > 0) {
            //printf("%d rays (I=%E) with history: \t", search_node->number_of_rays, search_node->intensity);
            //printf_history(history);
            
            total_history.saved_histories[total_history.used_elements].used_elements = history->used_elements;
            total_history.saved_histories[total_history.used_elements].elements = malloc(total_history.saved_histories[total_history.used_elements].used_elements*sizeof(struct history_node_struct));
            for (history_iterate = 0;history_iterate<history->used_elements;history_iterate++) {
                total_history.saved_histories[total_history.used_elements].elements[history_iterate] = history->elements[history_iterate];
                //printf("total_history.saved_histories[total_history.used_elements].elements[%d].volume_index \n",history_iterate,total_history.saved_histories[total_history.used_elements].elements[history_iterate].volume_index);
            }
            //total_history.saved_histories[total_history.used_elements].elements = history->elements;
            total_history.saved_histories[total_history.used_elements].intensity = search_node->intensity;
            total_history.saved_histories[total_history.used_elements].number_of_rays = search_node->number_of_rays;
            total_history.used_elements++;
          }
          
          history->used_elements = 0;
          
          // end of tree, no new nodes
          if (search_node->above == NULL) {
            done = 1;
          } else {
            // reset to the root of the tree
            *kill_candidate = NULL;
            free(search_node);
            search_node = master_list->elements[volume_index];
            
            if (volume_index != 0)
              current_number_of_processes = Volumes[volume_index]->p_physics->number_of_processes;
            else
              current_number_of_processes = 0;
    
            current_volume = volume_index;
            add_to_history(history,current_volume,-1);
          }
          
        }
    }
  }
  
  if (history->allocated_elements > 0) free(history->elements);

  qsort(total_history.saved_histories,total_history.used_elements,sizeof (struct saved_history_struct), Sample_compare_history_intensities);
  
  
  
  MPI_MASTER(
  printf("\n\n");
  printf("Top 20 most common histories. Shows the index of volumes entered (VX), and the scattering processes (PX)\n");
  for (history_iterate=0;history_iterate<total_history.used_elements && history_iterate<20;history_iterate++) {
    printf("%d\t N I=%E \t", total_history.saved_histories[history_iterate].number_of_rays, total_history.saved_histories[history_iterate].intensity);
    printf_history(&total_history.saved_histories[history_iterate]);
  }
   
  FILE *fp;
  fp = fopen("union_history.dat","w");
  
  fprintf(fp,"History file written by the McStas component Union_master \n");
  fprintf(fp,"When running with MPI, the results may be from just a single thread, meaning intensities are divided by number of threads\n");
  fprintf(fp,"----- Description of the used volumes -----------------------------------------------------------------------------------\n");
  
  fprintf(fp,"V0: Surrounding vacuum \n");
  for (volume_iterate=1;volume_iterate<number_of_volumes;volume_iterate++) {
    fprintf(fp,"V%d: %s  ",volume_iterate,Volumes[volume_iterate]->name);
    fprintf(fp,"Material: %s  ",Volumes[volume_iterate]->p_physics->name);
    for (process_iterate=0;process_iterate<Volumes[volume_iterate]->p_physics->number_of_processes;process_iterate++) {
      fprintf(fp," P%d: %s",process_iterate,Volumes[volume_iterate]->p_physics->p_scattering_array[process_iterate].name);
    }
    fprintf(fp,"\n");
  }
  fprintf(fp,"----- Histories sorted after intensity ----------------------------------------------------------------------------------\n");
  
  for (history_iterate=0;history_iterate<total_history.used_elements;history_iterate++) {
    fprintf_total_history(&total_history.saved_histories[history_iterate],fp);
    // Garbage collection
    if (total_history.saved_histories[history_iterate].used_elements > 0) free(total_history.saved_histories[history_iterate].elements);
  }
  fclose(fp);
  
  )
  
  // Garbage collection
  if (total_history.allocated_elements > 0) free(total_history.saved_histories);
  
  
};


// -------------    Intersection table functions   --------------------------------------------------------
int clear_intersection_table(struct intersection_time_table_struct *intersection_time_table) {
    // Resets the intersection table when a scattering have occured.
    int iterate_volumes,iterate_solutions;
    
    // Start at one because vacuum (0) does not have a listing in the intersection table
    for (iterate_volumes = 1;iterate_volumes < intersection_time_table->num_volumes;iterate_volumes++) {
        intersection_time_table->calculated[iterate_volumes] = 0;
        // This second loop is added for safty in debugging phase, but can be removed as the information should never be accesed when calculated = 0
        for (iterate_solutions = 0;iterate_solutions < intersection_time_table->n_elements[iterate_volumes];iterate_solutions++) {
            intersection_time_table->intersection_times[iterate_volumes][iterate_solutions] = -1;
        }
    }
    
    return 1;
};

void print_intersection_table(struct intersection_time_table_struct *intersection_time_table) {
    int num_volumes,iterate,solutions;
    int max_number_of_solutions = 0;
    
    num_volumes = intersection_time_table->num_volumes;
    
    for (iterate = 0;iterate < num_volumes;iterate++) {
        if (max_number_of_solutions < intersection_time_table->n_elements[iterate])
            max_number_of_solutions = intersection_time_table->n_elements[iterate];
    }
    
    printf("------------------ INTERSECTION_TIME_TABLE -----------------");
    for (solutions = 2;solutions < max_number_of_solutions;solutions++) printf("------------");
    printf("\n");
    
    // printf("iterate      |");

    printf("           ");
    printf("| CALCULATED  |");
    for (solutions = 0;solutions < max_number_of_solutions;solutions++) {
        printf(" - SOLUTION %d - |",solutions);
    }

    printf("\n");
    for (iterate = 0;iterate < num_volumes;iterate++){
    // print iterate number
    printf("Volume %d   |",iterate);
    printf(" ---- %d ---- |",intersection_time_table->calculated[iterate]);
    
        for (solutions = 0;solutions < max_number_of_solutions;solutions++) {
          if (intersection_time_table->n_elements[iterate] > solutions && intersection_time_table->calculated[iterate] == 1)
            if (intersection_time_table->intersection_times[iterate][solutions] > 0)
             printf("   %1.8f   |",intersection_time_table->intersection_times[iterate][solutions]);
            else
             printf("   %1.7f   |",intersection_time_table->intersection_times[iterate][solutions]);
          else
            printf("                |");
        }
    printf("\n");
    }
    printf("------------------------------------------------------------");
    for (solutions = 2;solutions < max_number_of_solutions;solutions++) printf("------------");
    printf("\n");
    
    
    };
    
// -------------    Drawing functions   --------------------------------------------------------
void merge_lines_to_draw(struct lines_to_draw *lines_master,struct lines_to_draw *lines_new) {
    if (lines_master->number_of_lines == 0) {
    lines_master->number_of_lines = lines_new->number_of_lines;
    lines_master->lines = malloc(lines_master->number_of_lines*sizeof(struct line_segment));
    lines_master->lines = lines_new->lines;
    // One could free lines_new->lines;
    } else {
    int iterate;
    struct line_segment *temp_lines;
    temp_lines = malloc(lines_master->number_of_lines*sizeof(struct line_segment));
    for (iterate = 0;iterate < lines_master->number_of_lines;iterate++) temp_lines[iterate] = lines_master->lines[iterate];
    free(lines_master->lines);
    lines_master->lines = malloc((lines_master->number_of_lines+lines_new->number_of_lines)*sizeof(struct line_segment));
    for (iterate = 0;iterate < lines_master->number_of_lines;iterate++) lines_master->lines[iterate] = temp_lines[iterate];
    for (iterate = 0;iterate < lines_new->number_of_lines;iterate++) lines_master->lines[iterate+lines_master->number_of_lines] = lines_new->lines[iterate];
    lines_master->number_of_lines = lines_master->number_of_lines + lines_new->number_of_lines;
    free(temp_lines);
    }
    };

int r_has_highest_priority(Coords point,int N,struct geometry_struct **Geometries,int number_of_volumes) {
    // Function that test if a point in Volume N has the highest priority.
    // Returns 0 if another volume has higher priority at the point, and it's mask status is 1
    // Returns 1 if no other volume has higher priority at the point
    // (not active) Returns a larger integer if the volume N is a mask, and the point is not in the volume it is masking,
    //  which results in the drawing function making that number of dashes
    
    
    int mask_status,*mask_start,*mask_check;
    // If the volume is a mask, it does not have a priority, and is always on top
    if (Geometries[N]->is_mask_volume == 1) {
      return 1;
      // Can draw parts of the mask that is outside the volume it masks as dashed lines, but it just looks messy
      /*
      mask_status = 1;
      for (mask_check=mask_start=Geometries[N]->mask_list.elements;mask_check-mask_start<Geometries[N]->mask_list.num_elements;mask_check++) {
        if (Geometries[*mask_check]->within_function(point,Geometries[*mask_check]) == 1) {
          return 1; // If the point is within a volume the mask is masking, draw it as a solid line
        }
      }
      // As it was not inside any of the volumes the mask is masking, draw it as a dashed line
      return 5;
      */
    }
    
    // If the volume is a masked volume, check if the point is within it's masks
    if (Geometries[N]->is_masked_volume == 1) {
      if (Geometries[N]->mask_mode == 1) { // ALL mode, need to be within ALL masks
        for (mask_check=mask_start=Geometries[N]->masked_by_list.elements;mask_check-mask_start<Geometries[N]->masked_by_list.num_elements;mask_check++) {
          if (Geometries[*mask_check]->within_function(point,Geometries[*mask_check]) == 0) {
            return 0; // If the point is just outside one mask, the mask status is 0 and the point does not have highest priority
          }
        }
      } else { // ANY mode, need to be within at least one mask
        mask_status = 0;
        for (mask_check=mask_start=Geometries[N]->masked_by_list.elements;mask_check-mask_start<Geometries[N]->masked_by_list.num_elements;mask_check++) {
          if (Geometries[*mask_check]->within_function(point,Geometries[*mask_check]) == 1) {
            mask_status = 1;
            break;
          }
        }
        if (mask_status == 0) {
          return 0; // If it was not in a single of it's masks, the point did not have highest priority
        }
      }
    }
    
    int volume_index;
    double self_priority;
    self_priority = Geometries[N]->priority_value;
    
    
    for (volume_index = 1;volume_index<number_of_volumes;volume_index++) {
      if (volume_index != N && Geometries[volume_index]->is_mask_volume == 0) {
        if (Geometries[volume_index]->within_function(point,Geometries[volume_index])) {
          if (Geometries[volume_index]->is_masked_volume == 1) {
            // Since this volume is masked, the mask status need to be checked
            if (Geometries[volume_index]->mask_mode == 1) { //ALL mode, need to be within ALL masks
              mask_status = 1;
              for (mask_check=mask_start=Geometries[volume_index]->masked_by_list.elements;mask_check-mask_start<Geometries[volume_index]->masked_by_list.num_elements;mask_check++) {
                if (Geometries[*mask_check]->within_function(point,Geometries[*mask_check]) == 0) {
                  mask_status = 0;
                  break;
                }
              }
            } else { // ANY mode, need to be within at least one mask
              mask_status = 0;
              for (mask_check=mask_start=Geometries[volume_index]->masked_by_list.elements;mask_check-mask_start<Geometries[volume_index]->masked_by_list.num_elements;mask_check++) {
                if (Geometries[*mask_check]->within_function(point,Geometries[*mask_check]) == 1) {
                  mask_status = 1;
                  break;
                }
              }
            }
          } else mask_status = 1;
          if (Geometries[volume_index]->priority_value > self_priority && mask_status == 1) {
            // printf("Volume %d did not have highest priority at (%f,%f,%f) (Volume number %d was above)\n",N,point.x,point.y,point.z,volume_index);
            return 0;
          }
        }
      }
    }
    // printf("Volume %d did have highest priority at (%f,%f,%f) \n",N,point.x,point.y,point.z);
    return 1;
    }
    
void draw_line_positions(Coords point1,Coords point2) {
        //line(point1.x,point1.y,point1.z,point2.x,point2.y,point2.z);
        // sigh, can not use line in share. Need to save the information and pass to the mcdisplay part.
    }

int Sample_compare_doubles (const void *a, const void *b) {
  const double *da = (const double *) a;
  const double *db = (const double *) b;

  return (*da > *db) - (*da < *db);
}

struct lines_to_draw draw_line_with_highest_priority(Coords position1,Coords position2,int N,struct geometry_struct **Geometries,int number_of_volumes,int max_number_of_solutions) {
    
    int volume_index,iterate,permanent_list_length = 0;
    int number_of_solutions;
    double temp_intersection[max_number_of_solutions];
    double r1[3],r2[3],direction[3];
    struct pointer_to_1d_double_list intersection_list;
    
    intersection_list.num_elements = 0;
    
    // double *permanent_intersection_list,*storage; // old ways
    
    r1[0] = position1.x;
    r1[1] = position1.y;
    r1[2] = position1.z;
    r2[0] = position2.x;
    r2[1] = position2.y;
    r2[2] = position2.z;
    
    // printf("r1 = (%f,%f,%f) \n",r1[0],r1[1],r1[2]);
    // printf("r2 = (%f,%f,%f) \n",r2[0],r2[1],r2[2]);
    
    direction[0] = r2[0] - r1[0];
    direction[1] = r2[1] - r1[1];
    direction[2] = r2[2] - r1[2];
    int geometry_output;
    
    // Find intersections
    for (volume_index = 1;volume_index < number_of_volumes; volume_index++) {
        if (volume_index != N) {
            geometry_output = Geometries[volume_index]->intersect_function(temp_intersection,&number_of_solutions,r1,direction,Geometries[volume_index]);
             // printf("No solutions for intersection (Volume %d) with %d \n",N,volume_index);
                for (iterate=0;iterate<number_of_solutions;iterate++) {
                    // print_1d_double_list(intersection_list,"intersection_list");
                    if (temp_intersection[iterate] > 0 && temp_intersection[iterate] < 1) {
                        add_element_to_double_list(&intersection_list,temp_intersection[iterate]);
                        // printf("solution taken = %f\n",temp_intersection[iterate]);
                        }                         // printf("solution ignored = %f\n",temp_intersection[iterate]);
                    // print_1d_double_list(intersection_list,"intersection_list");
                
                }
                // printf("First (%d) solutions added to the solution stack, intersection with %d \n",number_of_solutions,volume_index);
                // printf(" Solutions: ");
                // for (iterate = 0;iterate < intersection_list.num_elements;iterate++) printf("%f ",intersection_list.elements[iterate]);
                // printf("\n");
            
        }
    }
    // Now we have a list of intersection distances between r1 and r2 and all volumes.
    // This list needs to be sorted before we continue!
    
    qsort(intersection_list.elements,intersection_list.num_elements,sizeof (double), Sample_compare_doubles);
    // print_1d_double_list(intersection_list,"after sorting");
    // printf(" Solutions (after sorting): ");
    // for (iterate = 0;iterate < intersection_list.num_elements;iterate++) printf("%f ",intersection_list.elements[iterate]);
    // printf("\n");
    
    Coords points[intersection_list.num_elements+2];
    points[0] = coords_set(r1[0],r1[1],r1[2]);
    points[intersection_list.num_elements+1] = coords_set(r2[0],r2[1],r2[2]);
    for (iterate = 1;iterate < intersection_list.num_elements+1;iterate++) {
        points[iterate].x = r1[0] + direction[0]*intersection_list.elements[iterate-1];
        points[iterate].y = r1[1] + direction[1]*intersection_list.elements[iterate-1];
        points[iterate].z = r1[2] + direction[2]*intersection_list.elements[iterate-1];
    }
    
    // printf("Points on the list:\n");
    // for (iterate = 0;iterate < intersection_list.num_elements + 2;iterate++) {
    // //         printf("(%f,%f,%f)\n",points[iterate].x,points[iterate].y,points[iterate].z);
    // }
    // printf("\n");
  
    struct line_segment lines[intersection_list.num_elements+1];
    int draw_logic[intersection_list.num_elements+1];
    //struct lines_to_draw draw_order;
    Coords midpoint;
    struct lines_to_draw draw_order;
    draw_order.number_of_lines = 0;
    
    // printf("test2 in function \n");
    int number_of_dashes;
    
    for (iterate = 0;iterate < intersection_list.num_elements + 1;iterate++) {
        lines[iterate].point1 = points[iterate];
        lines[iterate].point2 = points[iterate+1];
        midpoint.x = 0.5*(lines[iterate].point1.x + lines[iterate].point2.x);
        midpoint.y = 0.5*(lines[iterate].point1.y + lines[iterate].point2.y);
        midpoint.z = 0.5*(lines[iterate].point1.z + lines[iterate].point2.z);
        
        if ((number_of_dashes = r_has_highest_priority(midpoint,N,Geometries,number_of_volumes)) != 0) {
            draw_order.number_of_lines++;
            draw_logic[iterate] = number_of_dashes;
        } else draw_logic[iterate] = 0;
    }
   
    // printf("test3 in function \n");
    
    if (draw_order.number_of_lines > 0) {
        draw_order.lines = malloc(draw_order.number_of_lines*sizeof(struct line_segment));
        draw_order.number_of_lines = 0;
        for (iterate = 0;iterate < intersection_list.num_elements + 1;iterate++) {
            if (draw_logic[iterate] != 0) {
              lines[iterate].number_of_dashes = draw_logic[iterate];
              draw_order.lines[draw_order.number_of_lines++] = lines[iterate];
            }
        }
        if (intersection_list.num_elements > 0) free(intersection_list.elements);
    }

    // printf("function done \n");
    return draw_order;
    }

struct lines_to_draw draw_circle_with_highest_priority(Coords center,Coords vector,double radius,int N,struct geometry_struct **Geometries,int number_of_volumes,int max_number_of_solutions) {
    int number_of_positions = 100;

    // normalize vector
    double vector_length = length_of_position_vector(vector);
    // print_position(vector,"start vector");
    vector.x /= vector_length;
    vector.y /= vector_length;
    vector.z /= vector_length;
    // print_position(vector,"start vector normalized");
    
    // Create a vector from the center of the circle to a point on the circumference by cross product
    double cross_input[3] = {0,1,0};
    // In case the cross input is parallel with the vector, a new is chosen. Both can't be parallel.
    if (scalar_prod(cross_input[0],cross_input[1],cross_input[2],vector.x,vector.y,vector.z) > 0.99) {
        cross_input[0] = 1; cross_input[1] = 0; cross_input[2] = 0;
    }
    // print_position(make_position(cross_input),"cross input");
    
    double cross_product1[3] = {0,0,0};
    vec_prod(cross_product1[0],cross_product1[1],cross_product1[2],vector.x,vector.y,vector.z,cross_input[0],cross_input[1],cross_input[2]);
    
    // print_position(make_position(cross_product1),"cross_product1");
    
    double cross_length = length_of_3vector(cross_product1);
    
    // printf("cross_length = %f \n",cross_length);
    
    cross_product1[0] /= cross_length;
    cross_product1[1] /= cross_length;
    cross_product1[2] /= cross_length;
    
    cross_product1[0] *= radius;
    cross_product1[1] *= radius;
    cross_product1[2] *= radius;
    
    // printf("cross_product1 = (%f,%f,%f) \n",cross_product1[0],cross_product1[1],cross_product1[2]);
    
    int iterate;
    double rotate_angle;
    Coords radial_position,old_radial_position;
    struct lines_to_draw temp_draw_order,return_draw_order;
    return_draw_order.number_of_lines = 0;
    // Generate an array of positions by rotating this vector around the given vector, this corresponds to points on the circle
    old_radial_position.x = center.x + cross_product1[0];
    old_radial_position.y = center.y + cross_product1[1];
    old_radial_position.z = center.z + cross_product1[2];

    // printf("old_radial_position = (%f,%f,%f) \n",old_radial_position.x,old_radial_position.y,old_radial_position.z);
    
    for (iterate = 0;iterate < number_of_positions-1;iterate++) {
        rotate_angle = 2*3.14159*((double) iterate + 1.0)/((double) number_of_positions);
        rotate(radial_position.x,radial_position.y,radial_position.z,cross_product1[0],cross_product1[1],cross_product1[2],rotate_angle,vector.x,vector.y,vector.z);
        radial_position.x += center.x;
        radial_position.y += center.y;
        radial_position.z += center.z;
        
        // Use the draw_lines_with_highest_priority to draw get draw_orders for each line piece
        temp_draw_order = draw_line_with_highest_priority(radial_position,old_radial_position,N,Geometries,number_of_volumes,max_number_of_solutions);
       // Assemble these draw_orders to one large draw order
        merge_lines_to_draw(&return_draw_order,&temp_draw_order);

        old_radial_position.x = radial_position.x;
        old_radial_position.y = radial_position.y;
        old_radial_position.z = radial_position.z;
    }
    
    radial_position.x = center.x + cross_product1[0];
    radial_position.y = center.y + cross_product1[1];
    radial_position.z = center.z + cross_product1[2];
    // Use the draw_lines_with_highest_priority to draw get draw_orders for each line piece
    temp_draw_order = draw_line_with_highest_priority(radial_position,old_radial_position,N,Geometries,number_of_volumes,max_number_of_solutions);
    // Assemble these draw_orders to one large draw order
    merge_lines_to_draw(&return_draw_order,&temp_draw_order);
    
    // clean up
    if (temp_draw_order.number_of_lines > 0) free(temp_draw_order.lines);
    
    // return the large draw order.
    return return_draw_order;
    }

// -------------    Geometry functions   --------------------------------------------------------

#include "Geometry_functions.c"

// -------------    List generator functions   --------------------------------------------------


int within_which_volume(Coords pos, struct pointer_to_1d_int_list input_list, struct pointer_to_1d_int_list destinations_list, struct Volume_struct **Volumes, struct pointer_to_1d_int_list *mask_status_list, int number_of_volumes, int *volume_logic_copy, int *ListA, int *ListB) {
    // This function identifies in which of the volumes of the input list the position pos lies in.
    // pos: position for which the current volume should be found for
    // input list: list of potential volumes, reduced to the ones without parents (their children will be checked)
    // OLD VERSION: volume logic: A logic list of allowed volumes for lookup, volume 1 3 and 5 in a case of 10 volumes would be [0 1 0 1 0 1 0 0 0 0]
    // destinations_list: list of allowed destinations (the original destinations list)
    // Volumes: Main volumes array
    // volume_logic_copy: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    // ListA: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    // ListB: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    
    // Algorithm description
    // Check all of input list for pos being within them, those that have it within them are:
    //      checked against the current highest priority
    //          if higher, is the new highest priority and the new pick for volume
    //      have all their direct children added to the next list to be checked (but any volume can only be added to that list once)
    // Once a run have been made where the next list to check is empty, the answer for new pick for volume is taken.
    
    // Mask update:
    // Algorithm description
    // Check all of input list for pos being within them, those that have it within them are:
    //      checked against the current highest priority and mask status
    //          if higher, this is the new highest priority and the new pick for volume
    //      have all their direct children added to the next list to be checked (but any volume can only be added to that list once)
    // Once a run have been made where the next list to check is empty, the answer for new pick for volume is taken.
    
    // The advantage of the method is that potentially large numbers of children are skipped when their parents do not contain the position.
    // The overhead cost is low, as all the lists are prealocated.
    // Should be checked which of the two implementations is faster, as this is much more complicated than simply checking all possibilities.
    // No within_function call should be made twice, as the same volume number will not be checked twice because of the properties of the direct_children list and the volume_logic that removes duplicates on each level.
    
    
    // This function uses too much memory, the memory required for the volume logic list is n_volumes^2 ints, or for a MACS monochromator 127000 ints.
    // Instead the original destinations list must be used, and a function for quick lookup in a (sorted) destinations list made.
    // Still need a list of n_volumes length for control to avoid adding the same volume to the list twice.
    
    
    int ListA_length=0,ListB_length=0;
    int done = 0;
    int i,direct_children_index;
    int *temp_pointer;
    double max_priority=-1000000;
    int residing_volume=0; // 0 can be removed from the input list if default is 0
    int this_mask_status,mask_index,mask_global_index;
    
    // volume_logic_copy
    //for (i=0;i<volume_logic.num_elements;i++) volume_logic_copy[i] = volume_logic.elements[i];
    
    // low memory version of volume_logic_copy
    for (i=0;i<number_of_volumes;i++) volume_logic_copy[i] = 0;
    for (i=0;i<destinations_list.num_elements;i++) volume_logic_copy[destinations_list.elements[i]] = 1;
    printf("within_which_volume debug %i\n",input_list.num_elements);

    // Does one loop through the algorithm first to set up ListA instead of copying it from input_list, which takes time
    for (i=0;i<input_list.num_elements;i++) {
            if (Volumes[input_list.elements[i]]->geometry.within_function(pos,&Volumes[input_list.elements[i]]->geometry) == 1) {
                printf("The position is inside of volume %d\n",input_list.elements[i]);
                if (Volumes[input_list.elements[i]]->geometry.is_masked_volume == 1) {
                    // if the volume is masked, I need to know if it can be a destination volume from the mask_status_list.
                    // if the masked volume is in ANY mode,
                    this_mask_status=1;
                    //print_1d_int_list(*mask_status_list,"mask status list from within_which_volume");
                    for (mask_index=0;mask_index<Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.num_elements;mask_index++) {
                        //printf("Looking at the mask with global index %d \n",Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.elements[mask_index]);
                        if (mask_status_list->elements[Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.elements[mask_index]] == 1) {
                            if (Volumes[input_list.elements[i]]->geometry.mask_mode == 2) { // ANY (break if any one in)
                                this_mask_status=1;
                                break;
                            }
                            //printf("global index %d had mask status = 1 \n",Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.elements[mask_index]);
                        } else {
                          //printf("global index %d had mask status = 0 \n",Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.elements[mask_index]);
                          this_mask_status = 0;
                          if(Volumes[input_list.elements[i]]->geometry.mask_mode == 1) break; // ALL (break if any one out)
                        }
                    }
                    //printf("This volume is masked, and the mask status is %d\n",this_mask_status);
                } else this_mask_status = 1; // if the volume is not masked
            
                if (Volumes[input_list.elements[i]]->geometry.priority_value > max_priority && this_mask_status == 1) {
                    max_priority = Volumes[input_list.elements[i]]->geometry.priority_value;
                    residing_volume = input_list.elements[i];
                    //printf("residing volume set to %d\n",residing_volume);
                }
                    for (direct_children_index = 0;direct_children_index < Volumes[input_list.elements[i]]->geometry.direct_children.num_elements;direct_children_index++) {
                        if (volume_logic_copy[Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index]] == 1) {
                            ListA[ListA_length++] = Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index];
                            volume_logic_copy[Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index]] = 0;
                        }
                    }
            }
    }
    //printf("Completed first loop, continued in while loop\n");
    if (ListA_length > 0) {
        while (done == 0) {
            for (i=0;i<ListA_length;i++) {
              //printf("checking element number %d of list A which is volume number %d \n",i,ListA[i]);
                if (Volumes[ListA[i]]->geometry.within_function(pos,&Volumes[ListA[i]]->geometry) == 1) {
                  //printf("ray was inside this volume \n");
                    if (Volumes[ListA[i]]->geometry.is_masked_volume == 1) {
                      //printf("it is a mask and thus need check of mask status \n");
                        // if the volume is masked, I need to know if it can be a destination volume from the mask_status_list.
                        // if the masked volume is in ANY mode,
                        this_mask_status=1;
                        for (mask_index=0;mask_index<Volumes[ListA[i]]->geometry.masked_by_mask_index_list.num_elements;mask_index++) {
                            if (mask_status_list->elements[Volumes[ListA[i]]->geometry.masked_by_mask_index_list.elements[mask_index]] == 1) {
                                if (Volumes[ListA[i]]->geometry.mask_mode == 2) { // ANY (break if any one in)
                                    this_mask_status=1;
                                    break;
                                }
                            } else {
                              this_mask_status = 0;
                              if(Volumes[ListA[i]]->geometry.mask_mode == 1) break; // ALL (break if any one out)
                            }
                        }
                    } else this_mask_status = 1;
                    //printf("the mask status is %d \n",this_mask_status);
                    if (Volumes[ListA[i]]->geometry.priority_value > max_priority && this_mask_status == 1) {
                        max_priority = Volumes[ListA[i]]->geometry.priority_value;
                        residing_volume = ListA[i];
                    }
                    //printf("Adding direct children to list B \n");
                        for (direct_children_index = 0;direct_children_index < Volumes[ListA[i]]->geometry.direct_children.num_elements;direct_children_index++) {
                            //printf("Checking direct_child number %d which is %d \n",direct_children_index,Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index]);
                            if (volume_logic_copy[Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index]] == 1) {
                                //printf("It's volume_logic was 1, and it is thus added to listB with index %d \n",ListB_length);
                                ListB[ListB_length++] = Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index];
                                volume_logic_copy[Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index]] = 0;
                            }
                        }
                    //printf("List B is now: ");
                    //for (direct_children_index=0;direct_children_index<ListB_length;direct_children_index++) printf("%d ",ListB[direct_children_index]);
                    //printf("\n");
                    
                    
                }
            }
            if (ListB_length==0) done = 1;
            else {
                
                for (i=0;i<ListB_length;i++) ListA[i] = ListB[i];
                ListA_length = ListB_length;
                ListB_length = 0;
                
                /*
                // Could do this with pointers instead to avoid this for loop (and needless copy)
                // This code block fails on the cluster in rare circumstances
                ListA = temp_pointer;
                ListA = ListB;
                ListB = temp_pointer;
                ListA_length=ListB_length;
                ListB_length=0;
                */
            }
        }
    }
    //printf("residing volume returned %d\n",residing_volume);
    return residing_volume;
};

int within_which_volume_GPU(Coords pos, struct pointer_to_1d_int_list input_list, struct pointer_to_1d_int_list destinations_list, struct Volume_struct **Volumes, struct pointer_to_1d_int_list *mask_status_list, int number_of_volumes, int *volume_logic_copy, int *ListA, int *ListB) {
    // This function identifies in which of the volumes of the input list the position pos lies in.
    // pos: position for which the current volume should be found for
    // input list: list of potential volumes, reduced to the ones without parents (their children will be checked)
    // OLD VERSION: volume logic: A logic list of allowed volumes for lookup, volume 1 3 and 5 in a case of 10 volumes would be [0 1 0 1 0 1 0 0 0 0]
    // destinations_list: list of allowed destinations (the original destinations list)
    // Volumes: Main volumes array
    // volume_logic_copy: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    // ListA: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    // ListB: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    
    // Algorithm description
    // Check all of input list for pos being within them, those that have it within them are:
    //      checked against the current highest priority
    //          if higher, is the new highest priority and the new pick for volume
    //      have all their direct children added to the next list to be checked (but any volume can only be added to that list once)
    // Once a run have been made where the next list to check is empty, the answer for new pick for volume is taken.
    
    // Mask update:
    // Algorithm description
    // Check all of input list for pos being within them, those that have it within them are:
    //      checked against the current highest priority and mask status
    //          if higher, this is the new highest priority and the new pick for volume
    //      have all their direct children added to the next list to be checked (but any volume can only be added to that list once)
    // Once a run have been made where the next list to check is empty, the answer for new pick for volume is taken.
    
    // The advantage of the method is that potentially large numbers of children are skipped when their parents do not contain the position.
    // The overhead cost is low, as all the lists are prealocated.
    // Should be checked which of the two implementations is faster, as this is much more complicated than simply checking all possibilities.
    // No within_function call should be made twice, as the same volume number will not be checked twice because of the properties of the direct_children list and the volume_logic that removes duplicates on each level.
    
    
    // This function uses too much memory, the memory required for the volume logic list is n_volumes^2 ints, or for a MACS monochromator 127000 ints.
    // Instead the original destinations list must be used, and a function for quick lookup in a (sorted) destinations list made.
    // Still need a list of n_volumes length for control to avoid adding the same volume to the list twice.
    
    
    int ListA_length=0,ListB_length=0;
    int done = 0;
    int i,direct_children_index;
    int *temp_pointer;
    double max_priority=-1000000;
    int residing_volume=0; // 0 can be removed from the input list if default is 0
    int this_mask_status,mask_index,mask_global_index;
    
    // volume_logic_copy
    //for (i=0;i<volume_logic.num_elements;i++) volume_logic_copy[i] = volume_logic.elements[i];
    
    // low memory version of volume_logic_copy
    for (i=0;i<number_of_volumes;i++) volume_logic_copy[i] = 0;
    for (i=0;i<destinations_list.num_elements;i++) volume_logic_copy[destinations_list.elements[i]] = 1;
    //printf("within_which_volume debug\n");

    // Does one loop through the algorithm first to set up ListA instead of copying it from input_list, which takes time
    for (i=0;i<input_list.num_elements;i++) {
            if (r_within_function(pos, &Volumes[input_list.elements[i]]->geometry) == 1) {
                //printf("The position is inside of volume %d\n",input_list.elements[i]);
                if (Volumes[input_list.elements[i]]->geometry.is_masked_volume == 1) {
                    // if the volume is masked, I need to know if it can be a destination volume from the mask_status_list.
                    // if the masked volume is in ANY mode,
                    this_mask_status=1;
                    //print_1d_int_list(*mask_status_list,"mask status list from within_which_volume");
                    for (mask_index=0;mask_index<Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.num_elements;mask_index++) {
                        //printf("Looking at the mask with global index %d \n",Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.elements[mask_index]);
                        if (mask_status_list->elements[Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.elements[mask_index]] == 1) {
                            if (Volumes[input_list.elements[i]]->geometry.mask_mode == 2) { // ANY (break if any one in)
                                this_mask_status=1;
                                break;
                            }
                            //printf("global index %d had mask status = 1 \n",Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.elements[mask_index]);
                        } else {
                          //printf("global index %d had mask status = 0 \n",Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.elements[mask_index]);
                          this_mask_status = 0;
                          if(Volumes[input_list.elements[i]]->geometry.mask_mode == 1) break; // ALL (break if any one out)
                        }
                    }
                    //printf("This volume is masked, and the mask status is %d\n",this_mask_status);
                } else this_mask_status = 1; // if the volume is not masked
            
                if (Volumes[input_list.elements[i]]->geometry.priority_value > max_priority && this_mask_status == 1) {
                    max_priority = Volumes[input_list.elements[i]]->geometry.priority_value;
                    residing_volume = input_list.elements[i];
                    //printf("residing volume set to %d\n",residing_volume);
                }
                    for (direct_children_index = 0;direct_children_index < Volumes[input_list.elements[i]]->geometry.direct_children.num_elements;direct_children_index++) {
                        if (volume_logic_copy[Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index]] == 1) {
                            ListA[ListA_length++] = Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index];
                            volume_logic_copy[Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index]] = 0;
                        }
                    }
            }
    }
    //printf("Completed first loop, continued in while loop\n");
    if (ListA_length > 0) {
        while (done == 0) {
            for (i=0;i<ListA_length;i++) {
              //printf("checking element number %d of list A which is volume number %d \n",i,ListA[i]);
                if (r_within_function(pos,&Volumes[ListA[i]]->geometry) == 1) {
                  //printf("ray was inside this volume \n");
                    if (Volumes[ListA[i]]->geometry.is_masked_volume == 1) {
                      //printf("it is a mask and thus need check of mask status \n");
                        // if the volume is masked, I need to know if it can be a destination volume from the mask_status_list.
                        // if the masked volume is in ANY mode,
                        this_mask_status=1;
                        for (mask_index=0;mask_index<Volumes[ListA[i]]->geometry.masked_by_mask_index_list.num_elements;mask_index++) {
                            if (mask_status_list->elements[Volumes[ListA[i]]->geometry.masked_by_mask_index_list.elements[mask_index]] == 1) {
                                if (Volumes[ListA[i]]->geometry.mask_mode == 2) { // ANY (break if any one in)
                                    this_mask_status=1;
                                    break;
                                }
                            } else {
                              this_mask_status = 0;
                              if(Volumes[ListA[i]]->geometry.mask_mode == 1) break; // ALL (break if any one out)
                            }
                        }
                    } else this_mask_status = 1;
                    //printf("the mask status is %d \n",this_mask_status);
                    if (Volumes[ListA[i]]->geometry.priority_value > max_priority && this_mask_status == 1) {
                        max_priority = Volumes[ListA[i]]->geometry.priority_value;
                        residing_volume = ListA[i];
                    }
                    //printf("Adding direct children to list B \n");
                        for (direct_children_index = 0;direct_children_index < Volumes[ListA[i]]->geometry.direct_children.num_elements;direct_children_index++) {
                            //printf("Checking direct_child number %d which is %d \n",direct_children_index,Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index]);
                            if (volume_logic_copy[Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index]] == 1) {
                                //printf("It's volume_logic was 1, and it is thus added to listB with index %d \n",ListB_length);
                                ListB[ListB_length++] = Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index];
                                volume_logic_copy[Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index]] = 0;
                            }
                        }
                    //printf("List B is now: ");
                    //for (direct_children_index=0;direct_children_index<ListB_length;direct_children_index++) printf("%d ",ListB[direct_children_index]);
                    //printf("\n");
                    
                    
                }
            }
            if (ListB_length==0) done = 1;
            else {
                
                for (i=0;i<ListB_length;i++) ListA[i] = ListB[i];
                ListA_length = ListB_length;
                ListB_length = 0;
                
                /*
                // Could do this with pointers instead to avoid this for loop (and needless copy)
                // This code block fails on the cluster in rare circumstances
                ListA = temp_pointer;
                ListA = ListB;
                ListB = temp_pointer;
                ListA_length=ListB_length;
                ListB_length=0;
                */
            }
        }
    }
    //printf("residing volume returned %d\n",residing_volume);
    return residing_volume;
};




int within_which_volume_debug(Coords pos, struct pointer_to_1d_int_list input_list, struct pointer_to_1d_int_list volume_logic, struct Volume_struct **Volumes, int *volume_logic_copy, int *ListA, int *ListB) {
    // This function identifies in which of the volumes of the input list the position pos lies in.
    // pos: position for which the current volume should be found for
    // input list: list of potential volumes, reduced to the ones without parents (their children will be checked)
    // volume logic: A logic list of allowed volumes for lookup, volume 1 3 and 5 in a case of 10 volumes would be [0 1 0 1 0 1 0 0 0 0]
    // Volumes: Main volumes array
    // volume_logic_copy: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    // ListA: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    // ListB: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    
    // Algorithm description
    // Check all of input list for pos being within them, those that have it within them are:
    //      checked against the current highest priority
    //          if higher, is the new highest priority and the new pick for volume
    //      have all their direct children added to the next list to be checked (but any volume can only be added to that list once)
    // Once a run have been made where the next list to check is empty, the answer for new pick for volume is taken.
    
    // The advantage of the method is that potentially large numbers of children are skipped when their parents do not contain the position.
    // The overhead cost is low, as all the lists are prealocated.
    // Should be checked which of the two implementations is faster, as this is much more complicated than simply checking all possibilities.
    // No within_function call should be made twice, as the same volume number will not be checked twice because of the properties of the direct_children list and the volume_logic that removes duplicates on each level.
    
    
    int ListA_length=0,ListB_length=0;
    int done = 0;
    int i,direct_children_index;
    int *temp_pointer;
    double max_priority=-1000000;
    int residing_volume=0; // 0 can be removed from the input list if default is 0
    
    // volume_logic_copy
    for (i=0;i<volume_logic.num_elements;i++) volume_logic_copy[i] = volume_logic.elements[i];
    
    // Does one loop through the algorithm first to set up ListA instead of copying it from input_list, which takes time
    for (i=0;i<input_list.num_elements;i++) {
            if (Volumes[input_list.elements[i]]->geometry.within_function(pos,&Volumes[input_list.elements[i]]->geometry) == 1) {
                if (Volumes[input_list.elements[i]]->geometry.priority_value > max_priority) {
                    max_priority = Volumes[input_list.elements[i]]->geometry.priority_value;
                    residing_volume = input_list.elements[i];
                }
                    for (direct_children_index = 0;direct_children_index < Volumes[input_list.elements[i]]->geometry.direct_children.num_elements;direct_children_index++) {
                        if (volume_logic_copy[Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index]] == 1) {
                            ListA[ListA_length++] = Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index];
                            volume_logic_copy[Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index]] = 0;
                        }
                    }
            }
    }
    if (ListA_length > 0) {
        while (done == 0) {
        
            printf("ListA = [");
            for (i=0;i<ListA_length;i++) {
                printf("%d,",ListA[i]);
            }
            printf("]\n");
            for (i=0;i<ListA_length;i++) {
                if (Volumes[ListA[i]]->geometry.within_function(pos,&Volumes[ListA[i]]->geometry) == 1) {
                    if (Volumes[ListA[i]]->geometry.priority_value > max_priority) {
                        max_priority = Volumes[ListA[i]]->geometry.priority_value;
                        residing_volume = ListA[i];
                    }
                    //if (ListA[i]!=0) {
                        for (direct_children_index = 0;direct_children_index < Volumes[ListA[i]]->geometry.direct_children.num_elements;direct_children_index++) {
                            if (volume_logic_copy[Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index]] == 1) {
                                ListB[ListB_length++] = Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index];
                                volume_logic_copy[Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index]] = 0;
                            }
                        }
                    //}
                }
            }
            if (ListB_length==0) done = 1;
            else {
                ListA = temp_pointer;
                ListA = ListB;
                ListB = temp_pointer;
                ListA_length=ListB_length;
                ListB_length=0;
            }
        }
    }
    printf("Volume number %d had the highest priority of checked volumes\n",residing_volume);
    return residing_volume;
};

int inside_function(struct Volume_struct *parent_volume, struct Volume_struct *child_volume) {
    // Function that calls the correct within function depending on the shapes of the two volumes
    if (strcmp("sphere",parent_volume->geometry.shape) == 0 && strcmp("sphere",child_volume->geometry.shape) == 0) {
        if (sphere_within_sphere(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("cylinder",parent_volume->geometry.shape) == 0 && strcmp("cylinder",child_volume->geometry.shape) == 0) {
        if (cylinder_within_cylinder(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("box",parent_volume->geometry.shape) == 0 && strcmp("box",child_volume->geometry.shape) == 0) {
        if (box_within_box(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("cone",parent_volume->geometry.shape) == 0 && strcmp("cone",child_volume->geometry.shape) == 0) {
        if (cone_within_cone(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("mesh",parent_volume->geometry.shape) == 0 && strcmp("mesh",child_volume->geometry.shape) == 0) {
        if (mesh_within_mesh(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("box",parent_volume->geometry.shape) == 0 && strcmp("cylinder",child_volume->geometry.shape) == 0) {
        if (cylinder_within_box(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("cylinder",parent_volume->geometry.shape) == 0 && strcmp("box",child_volume->geometry.shape) == 0) {
        if (box_within_cylinder(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("box",parent_volume->geometry.shape) == 0 && strcmp("sphere",child_volume->geometry.shape) == 0) {
        if (sphere_within_box(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("sphere",parent_volume->geometry.shape) == 0 && strcmp("box",child_volume->geometry.shape) == 0) {
        if (box_within_sphere(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("sphere",parent_volume->geometry.shape) == 0 && strcmp("cylinder",child_volume->geometry.shape) == 0) {
        if (cylinder_within_sphere(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("cylinder",parent_volume->geometry.shape) == 0 && strcmp("sphere",child_volume->geometry.shape) == 0) {
        if (sphere_within_cylinder(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("cone",parent_volume->geometry.shape) == 0 && strcmp("sphere",child_volume->geometry.shape) == 0) {
        if (sphere_within_cone(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("sphere",parent_volume->geometry.shape) == 0 && strcmp("cone",child_volume->geometry.shape) == 0) {
        if (cone_within_sphere(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("cone",parent_volume->geometry.shape) == 0 && strcmp("cylinder",child_volume->geometry.shape) == 0) {
        if (cylinder_within_cone(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("cylinder",parent_volume->geometry.shape) == 0 && strcmp("cone",child_volume->geometry.shape) == 0) {
        if (cone_within_cylinder(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("cone",parent_volume->geometry.shape) == 0 && strcmp("box",child_volume->geometry.shape) == 0) {
        if (box_within_cone(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("box",parent_volume->geometry.shape) == 0 && strcmp("cone",child_volume->geometry.shape) == 0) {
        if (cone_within_box(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else {
        #ifndef OPENACC
        printf("Need within function for type: ");
        printf("%s",parent_volume->geometry.shape);
        printf(" and type: ");
        printf("%s",child_volume->geometry.shape);
        printf(".\n");
        printf("It is not yet supported to mix mesh geometries with the basic shapes, but several mesh geometries are allowed.\n");
        exit(1);
	#endif
    }
    
    return 0;
};

void generate_children_lists(struct Volume_struct **Volumes, struct pointer_to_1d_int_list **true_children_lists, int number_of_volumes, int verbal) {
  // This function generates a list of children for each volume.
  // A volume m is a child of volume n, if the entire space ocupied by volume m is inside of the space ocupied by volume n
  // A volume m is a true child of volume n, if the entire space coupied by volume m after it's masks are applied is inside the volume ocupied by volume n after it's masks are applied
  
  
  MPI_MASTER(
  if (verbal) printf("\nGenerating children lists --------------------------- \n");
  )

  // Mask update: Creating a temporary list for each volume
  struct pointer_to_1d_int_list *temporary_children_lists;
  temporary_children_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list));

  // The surrounding vacuum, volume 0, done outside of for loop.
  temporary_children_lists[0].num_elements = number_of_volumes - 1;
  temporary_children_lists[0].elements = malloc(temporary_children_lists[0].num_elements*sizeof(int));
  
  int parent;
  for (parent=1;parent<number_of_volumes;parent++) {
      temporary_children_lists[0].elements[parent-1] = parent;
  }
  
  //if (verbal) printf("did temporary children list \n");
  //print_1d_int_list(temporary_children_lists[0],"temp children list [0]");
  
  // Hardcoding that every volume is a child of the surrounding vacuum
  Volumes[0]->geometry.children.num_elements = number_of_volumes-1;
  Volumes[0]->geometry.children.elements = malloc((number_of_volumes-1)*sizeof(int));
  true_children_lists[0] = malloc(sizeof(struct pointer_to_1d_int_list));
  true_children_lists[0]->num_elements = number_of_volumes - 1;
  true_children_lists[0]->elements = malloc((number_of_volumes-1)*sizeof(int));
  
  //if (verbal) printf("allocated true children lists \n");
  
  for (parent=1;parent<number_of_volumes;parent++) {
      Volumes[0]->geometry.children.elements[parent-1] = parent;
      true_children_lists[0]->elements[parent-1] = parent;
  }
  
  char string_output[128];
  MPI_MASTER(
  if (verbal) sprintf(string_output,"Children for Volume %d",0);
  if (verbal) print_1d_int_list(Volumes[0]->geometry.children,string_output);
  )
  
  
  // Generating the children lists for all other volumes using the appropriate geometry functions
  struct pointer_to_1d_int_list temp_list_local;
  temp_list_local.num_elements = number_of_volumes;
  temp_list_local.elements = malloc(number_of_volumes*sizeof(int));
  
  struct pointer_to_1d_int_list true_temp_list_local;
  true_temp_list_local.num_elements = number_of_volumes;
  true_temp_list_local.elements = malloc(number_of_volumes*sizeof(int));
  
  
  int child,used_elements,used_elements_true;
  for (parent=1;parent<number_of_volumes;parent++) {
      used_elements = 0;used_elements_true = 0;
      for (child=1;child<number_of_volumes;child++) {
        if (child != parent) {
          // Call inside_function that selects the proper within function for the two volumes
          if (1 == inside_function(Volumes[parent],Volumes[child])) {
            temp_list_local.elements[used_elements++] = child;
            true_temp_list_local.elements[used_elements_true++] = child;
          }
        } else true_temp_list_local.elements[used_elements_true++] = child; // Needed when children list takes mask into account
      }
      // Temp test
      allocate_list_from_temp(used_elements,temp_list_local,&Volumes[parent]->geometry.children);
      // Assing the children list to a temporary list as the masks have yet to be taken into account
      temporary_children_lists[parent].num_elements=0;
      allocate_list_from_temp(used_elements_true,true_temp_list_local,&temporary_children_lists[parent]);
      
      
      MPI_MASTER(
      if (verbal) sprintf(string_output,"Children for Volume %d (temporary_list)",parent);
      if (verbal) print_1d_int_list(temporary_children_lists[parent],string_output);
      )
      
      MPI_MASTER(
      if (verbal) sprintf(string_output,"Children for Volume %d (permanent_list)",parent);
      if (verbal) print_1d_int_list(Volumes[parent]->geometry.children,string_output);
      )
      
  }
  
  // mask update:
  // The logical expression: (child c parent AND child c parent_mask) OR (child_mask c parent AND child_mask c parent_mask)
  //  needs to be evaluated for each child / parent combination in order to take the masks of each into account
  
  int logic_var1,logic_var2,logic_var_ANY,logic_var_ALL;
  int mask_index,mask_index_child,mask_index_parent;
  int volume_C,volume_P;
  // Loop that takes masks into account
  for (parent=1;parent<number_of_volumes;parent++) {
    used_elements = 0;
    for (child=1;child<number_of_volumes;child++) {
     if (child != parent && 0 == on_int_list(Volumes[parent]->geometry.masked_by_list,child)) {
        // The children list for each volume does not need to contain the volume itself
        //  And a parent masked by it's child can not have that mask as a child
      
      // Here c means within in the sense of a set being part of another set
      // Logical expression to be evaluated: (child c parent AND child c parent_mask) OR (child_mask c parent AND child_mask c parent_mask)
      logic_var1 = on_int_list(temporary_children_lists[parent],child);
      if (logic_var1 == 1 && Volumes[parent]->geometry.is_masked_volume == 1) {
        // if the parent volume is masked, the child also need to be inclosed in these masks to fulfill this side of the logical expression
        logic_var_ANY = 0;
        for (mask_index=0;mask_index<Volumes[parent]->geometry.masked_by_list.num_elements;mask_index++) {
          if (0 == on_int_list(temporary_children_lists[Volumes[parent]->geometry.masked_by_list.elements[mask_index]],child)) {
            if (Volumes[parent]->geometry.mask_mode == 1) {
              logic_var1 = 0;
              break;
            }
          } else logic_var_ANY = 1;
        }
        if (Volumes[parent]->geometry.mask_mode == 2) logic_var1 = logic_var_ANY;
      }
      
      if (logic_var1 == 1) true_temp_list_local.elements[used_elements++] = child;
      else if (Volumes[child]->geometry.is_masked_volume == 1) {
        // If the first side of the logical expression is false, evalute the other side
        // The other side is only relevant if the child volume is masked, otherwise it is ignored
        //printf("Second side of logical expression \n");
        
        logic_var1 = 1; // Assume true
          
        // child_mask c parent
        logic_var_ALL = 0;
        for (mask_index=0;mask_index<Volumes[child]->geometry.masked_by_list.num_elements;mask_index++) {
          if (0 == on_int_list(temporary_children_lists[parent],Volumes[child]->geometry.masked_by_list.elements[mask_index])){
            if (Volumes[child]->geometry.mask_mode == 2) {
              logic_var1 = 0;
              break;
            }
          } else logic_var_ALL = 1;
        }
        if (Volumes[child]->geometry.mask_mode == 1) logic_var1 = logic_var_ALL;
        
        // This line allows the second part of the logical expression to be true in cases where the parent volume is not masked
        if (logic_var1 == 1 && Volumes[parent]->geometry.is_masked_volume == 0) true_temp_list_local.elements[used_elements++] = child;
        
        // There is no reason to check the other part (child_mask c parent_mask) if the first part was not true
        if (logic_var1 == 1 && Volumes[parent]->geometry.is_masked_volume == 1) {
          // This last part requires both the child and the parent to be masked
          // Need to evaluate (child_mask c parent_mask), where both can be a be a list of volume with ALL/ANY modes
          
          if (Volumes[parent]->geometry.mask_mode == 1) {
            logic_var2 = 1; // Assume the logical expression (child_mask c parent_mask) is true
            for (mask_index_parent=0;mask_index_parent<Volumes[parent]->geometry.masked_by_list.num_elements;mask_index_parent++) {
              // As the parent is in ALL mode, the child masks must be within ALL parent masks
              logic_var_ANY = 0; // Assume not a single child is within this parent
              for (mask_index_child=0;mask_index_child<Volumes[child]->geometry.masked_by_list.num_elements;mask_index_child++) {
                volume_P = Volumes[parent]->geometry.masked_by_list.elements[mask_index_parent];
                volume_C = Volumes[child]->geometry.masked_by_list.elements[mask_index_child];
                // Is volume C inside volume P? If yes, volume C must be on volume P's temporary children list
                if (0 == on_int_list(temporary_children_lists[volume_P],volume_C)) {
                  if (Volumes[child]->geometry.mask_mode == 2) {
                    // If child is in ANY mode, any one mask outside is enough to make the expression false
                    logic_var2 = 0;
                    break;
                  }
                } else logic_var_ANY = 1;
              }
              // If child is in ALL mode, then if any one child were within this mask, the logic expression holds true
              if (Volumes[child]->geometry.mask_mode == 1) logic_var2 = logic_var_ANY;
              if (logic_var2 == 0) break; // No need to continue
            }
          } else if (Volumes[parent]->geometry.mask_mode == 2) {
            // If the parent is in ANY mode, it is enough if the child masks are within just 1 of the parent masks
            for (mask_index_parent=0;mask_index_parent<Volumes[parent]->geometry.masked_by_list.num_elements;mask_index_parent++) {
              logic_var2 = 1; // Assume the logical expression (child_mask c parent_mask) is true
              logic_var_ANY = 0; // Assume not a single child is within this parent
              for (mask_index_child=0;mask_index_child<Volumes[child]->geometry.masked_by_list.num_elements;mask_index_child++) {
                volume_P = Volumes[parent]->geometry.masked_by_list.elements[mask_index_parent];
                volume_C = Volumes[child]->geometry.masked_by_list.elements[mask_index_child];
                // Is volume C inside volume P? If yes, volume C must be on volume P's temporary children list
                if (0 == on_int_list(temporary_children_lists[volume_P],volume_C)) {
                  if (Volumes[child]->geometry.mask_mode == 2) {
                    // If child is in ANY mode, any one mask outside is enough to make the expression false
                    logic_var2 = 0;
                    break;
                  }
                } else logic_var_ANY = 1;
              }
              // If child is in ALL mode, then if any one child were within this mask, the logic expression holds true
              if (Volumes[child]->geometry.mask_mode == 1) logic_var2 = logic_var_ANY;
              if (logic_var2 == 1) break; // No need to continue
            }
          }
          
          // if this point is reached, and logic_var2 is true, volume[child] is a child of volume[parent]
          if (logic_var2 == 1) true_temp_list_local.elements[used_elements++] = child;
          
        }
      }
     }
    }
    
    true_children_lists[parent] = malloc(sizeof(struct pointer_to_1d_int_list));
    true_children_lists[parent]->num_elements = 0;
    allocate_list_from_temp(used_elements,true_temp_list_local,true_children_lists[parent]);
    
    MPI_MASTER(
      if (verbal) sprintf(string_output,"True children for Volume (post mask) %d",parent);
      if (verbal) print_1d_int_list(*true_children_lists[parent],string_output);
    )
  }
  
  
  // Clean up of dynamically allocated memory
  
  for(child=0;child<number_of_volumes;child++) free(temporary_children_lists[child].elements);
  free(temporary_children_lists);
  free(true_temp_list_local.elements);
  free(temp_list_local.elements);
};

void generate_overlap_lists(struct pointer_to_1d_int_list **true_overlap_lists, struct pointer_to_1d_int_list **raw_overlap_lists, struct Volume_struct **Volumes, int number_of_volumes, int verbal) {
  // This function generates overlap lists for each volume
  // Volume m overlaps volume n if there is some subset of space they both ocupy (this is called raw overlap)
  // the true overlap list takes mask into account, meaning that the masks are applied before searching for overlap

  MPI_MASTER(
  if (verbal) printf("\nGenerating overlap lists ---------------------------- \n");
  )
  
  // Manually create the overlap list for the surrounding Vacuum, as it overlaps all other volumes
  
  // temporary_overlap_lists are used to save the calculated overlaps to avoid evaluating the heavy functions more than once (twice) for each possible volume pair
  struct pointer_to_1d_int_list *temporary_overlap_lists;
  temporary_overlap_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list));
  
  // Overlap_lists are the final result of the function, and for the surrounding volume it can be set immediatly
  true_overlap_lists[0] = malloc(sizeof(struct pointer_to_1d_int_list));
  true_overlap_lists[0]->num_elements = number_of_volumes-1;
  true_overlap_lists[0]->elements = malloc((number_of_volumes-1)*sizeof(int));
  
  raw_overlap_lists[0] =  malloc(sizeof(struct pointer_to_1d_int_list));
  raw_overlap_lists[0]->num_elements = number_of_volumes;
  raw_overlap_lists[0]->elements = malloc(number_of_volumes*sizeof(int));
  raw_overlap_lists[0]->elements[0] = 0; // Volume 0 overlaps itself
  
  int parent;
  for (parent=1;parent<number_of_volumes;parent++) {
      true_overlap_lists[0]->elements[parent-1] = parent;
      raw_overlap_lists[0]->elements[parent] = parent;
  }
  
  char string_output[128];
  MPI_MASTER(
  if (verbal) sprintf(string_output,"Overlaps for Volume %d",0);
  if (verbal) print_1d_int_list(*true_overlap_lists[0],string_output);
  )
  
  // Generate the overlap lists for the remaining volumes
  struct pointer_to_1d_int_list temp_list_local;
  temp_list_local.num_elements = number_of_volumes;
  temp_list_local.elements = malloc(number_of_volumes*sizeof(int));
  
  int child,used_elements;
  // Create overlap for the remaining volumes
  for (parent=1;parent<number_of_volumes;parent++) {
      true_overlap_lists[parent] = malloc(sizeof(struct pointer_to_1d_int_list));
      used_elements = 0;
      temp_list_local.elements[used_elements++] = 0; // Alwasy overlaps with the surrounding vacuum.
      
      for (child=1;child<number_of_volumes;child++) {
        if (child == parent) temp_list_local.elements[used_elements++] = child;
        else {
        if (strcmp("sphere",Volumes[parent]->geometry.shape) == 0 && strcmp("sphere",Volumes[child]->geometry.shape) == 0) {
            if (sphere_overlaps_sphere(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("cylinder",Volumes[parent]->geometry.shape) == 0 && strcmp("cylinder",Volumes[child]->geometry.shape) == 0) {
            if (cylinder_overlaps_cylinder(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("box",Volumes[parent]->geometry.shape) == 0 && strcmp("box",Volumes[child]->geometry.shape) == 0) {
            if (box_overlaps_box(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("cone",Volumes[parent]->geometry.shape) == 0 && strcmp("cone",Volumes[child]->geometry.shape) == 0) {
            if (cone_overlaps_cone(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("mesh",Volumes[parent]->geometry.shape) == 0 && strcmp("mesh",Volumes[child]->geometry.shape) == 0) {
            if (mesh_overlaps_mesh(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("box",Volumes[parent]->geometry.shape) == 0 && strcmp("cylinder",Volumes[child]->geometry.shape) == 0) {
            if (box_overlaps_cylinder(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("cylinder",Volumes[parent]->geometry.shape) == 0 && strcmp("box",Volumes[child]->geometry.shape) == 0) {
            if (cylinder_overlaps_box(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("box",Volumes[parent]->geometry.shape) == 0 && strcmp("sphere",Volumes[child]->geometry.shape) == 0) {
            if (box_overlaps_sphere(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("sphere",Volumes[parent]->geometry.shape) == 0 && strcmp("box",Volumes[child]->geometry.shape) == 0) {
            if (sphere_overlaps_box(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("sphere",Volumes[parent]->geometry.shape) == 0 && strcmp("cylinder",Volumes[child]->geometry.shape) == 0) {
            if (sphere_overlaps_cylinder(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("cylinder",Volumes[parent]->geometry.shape) == 0 && strcmp("sphere",Volumes[child]->geometry.shape) == 0) {
            if (cylinder_overlaps_sphere(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("cone",Volumes[parent]->geometry.shape) == 0 && strcmp("sphere",Volumes[child]->geometry.shape) == 0) {
            if (cone_overlaps_sphere(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("sphere",Volumes[parent]->geometry.shape) == 0 && strcmp("cone",Volumes[child]->geometry.shape) == 0) {
            if (sphere_overlaps_cone(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("cone",Volumes[parent]->geometry.shape) == 0 && strcmp("cylinder",Volumes[child]->geometry.shape) == 0) {
            if (cone_overlaps_cylinder(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("cylinder",Volumes[parent]->geometry.shape) == 0 && strcmp("cone",Volumes[child]->geometry.shape) == 0) {
            if (cylinder_overlaps_cone(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("cone",Volumes[parent]->geometry.shape) == 0 && strcmp("box",Volumes[child]->geometry.shape) == 0) {
            if (cone_overlaps_box(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("box",Volumes[parent]->geometry.shape) == 0 && strcmp("cone",Volumes[child]->geometry.shape) == 0) {
            if (box_overlaps_cone(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else {
            printf("Need overlap function for type: ");
            printf("%s",Volumes[parent]->geometry.shape);
            printf(" and type: ");
            printf("%s",Volumes[child]->geometry.shape);
            printf(".\n");
            printf("It is not yet supported to mix mesh geometries with the basic shapes, but several mesh geometries are allowed.\n");
            exit(1);
        }
        }
      }
      //allocate_list_from_temp(used_elements,temp_list_local,overlap_lists[parent]);
      allocate_list_from_temp(used_elements,temp_list_local,&temporary_overlap_lists[parent]);
      
      // Save the raw overlap data to the raw_overlap_lists[parent] list
      raw_overlap_lists[parent] = malloc(sizeof(struct pointer_to_1d_int_list));
      raw_overlap_lists[parent]->num_elements = 0;
      allocate_list_from_temp(used_elements,temp_list_local,raw_overlap_lists[parent]);
      
      if (verbal) sprintf(string_output,"Overlaps for Volume (pre mask) %d",parent);
      MPI_MASTER(
      if (verbal) print_1d_int_list(temporary_overlap_lists[parent],string_output);
      )
  }
  
  
  // The temporary_overlap_lists gives the raw overlap data for all volume pairs
  // The next tasks is to take the masks into account, so that a volume is only said to overlap another if all of these statements are true:
  // The volumes overlap each other
  // The mask's of volume 1 overlap volume 2
  // The mask's of volume 2 overlap volume 1
  // The mask's of volume 1 overlap the masks of volume 2
  
  
  int logic_var;
  int overlap_ANY,overlap_ANY_p,overlap_ANY_c;
  int mask_index,mask_index_c,mask_index_p;
  int mask_volume_index,mask_volume_index_p,mask_volume_index_c;

  for (parent=1;parent<number_of_volumes;parent++) {
    used_elements = 0;
    temp_list_local.elements[used_elements++] = 0; // Alwasy overlaps with the surrounding vacuum.
    for (child=1;child<number_of_volumes;child++) {
      if (child != parent) {
        logic_var = 1; // Assume the volumes overlap, and search for evidence that they do not.
      
        // First check if the volumes overlap
        if (0 == on_int_list(temporary_overlap_lists[parent],child)) logic_var = 0;
        
      
        // Check if child overlap with parents masks
        if (logic_var == 1 && Volumes[parent]->geometry.is_masked_volume == 1) {
          overlap_ANY = 0;
          for (mask_index=0;mask_index<Volumes[parent]->geometry.masked_by_list.num_elements;mask_index++) {
            mask_volume_index = Volumes[parent]->geometry.masked_by_list.elements[mask_index];
            if (0 == on_int_list(temporary_overlap_lists[mask_volume_index],child)) {
              if (Volumes[parent]->geometry.mask_mode == 1) {
                logic_var = 0;
                break;
              }
            } else overlap_ANY = 1;
          }
          if (Volumes[parent]->geometry.mask_mode == 2) logic_var = overlap_ANY;
        }
      
        // Check if parent overlap with childs masks
        if (logic_var == 1 && Volumes[child]->geometry.is_masked_volume == 1) {
          overlap_ANY = 0;
          for (mask_index=0;mask_index<Volumes[child]->geometry.masked_by_list.num_elements;mask_index++) {
            mask_volume_index = Volumes[child]->geometry.masked_by_list.elements[mask_index];
            if (0 == on_int_list(temporary_overlap_lists[mask_volume_index],parent)) {
              if (Volumes[child]->geometry.mask_mode == 1) {
                logic_var = 0;
                break;
              }
            } else overlap_ANY = 1;
          }
          if (Volumes[child]->geometry.mask_mode == 2) logic_var = overlap_ANY;
        }
      
        // Check if parents masks overlap childrens masks
        if (logic_var == 1 && Volumes[parent]->geometry.is_masked_volume == 1 && Volumes[child]->geometry.is_masked_volume == 1) {
          overlap_ANY = 0;
          for (mask_index_p=0;mask_index_p<Volumes[parent]->geometry.masked_by_list.num_elements;mask_index_p++) {
            mask_volume_index_p = Volumes[parent]->geometry.masked_by_list.elements[mask_index_p];
            overlap_ANY_p = 1;
            overlap_ANY_c = 0;
            for (mask_index_c=0;mask_index_c<Volumes[child]->geometry.masked_by_list.num_elements;mask_index_c++) {
              mask_volume_index_c = Volumes[child]->geometry.masked_by_list.elements[mask_index_c];
              if (0 == on_int_list(temporary_overlap_lists[mask_volume_index_p],mask_volume_index_c)) {
                if (Volumes[parent]->geometry.mask_mode == 1 && Volumes[child]->geometry.mask_mode == 1) {
                  // If both are in ALL mode and just one combination of masks does not overlap, neither does the common set
                  logic_var = 0;
                  break;
                }
                if (Volumes[parent]->geometry.mask_mode == 2 && Volumes[child]->geometry.mask_mode == 1) {
                  // If the parent is in ANY mode, but the child is in ALL, any one child not overlapping this parent mask, stops the chance for this parent mask
                  overlap_ANY_p = 0;
                  break;
                }
                
              } else {
                // Here because mask_volume_index_p and mask_volume_index_c does overlap
                if (Volumes[parent]->geometry.mask_mode == 1 && Volumes[child]->geometry.mask_mode == 2) {
                  // If the parent is in ALL mode and the child is in ANY mode, stop if a single parent volume does not overlap any child
                  overlap_ANY_c = 1;
                }
                if (Volumes[parent]->geometry.mask_mode == 2 && Volumes[child]->geometry.mask_mode == 2) {
                  // If both parent and child are in any mode, any one overlap between the masks is sufficient
                  overlap_ANY = 1;
                  // Could actually just commit to the overlap list here, and stop all loops.
                }
              }
              
            }
            if (Volumes[parent]->geometry.mask_mode == 1 && Volumes[child]->geometry.mask_mode == 2) logic_var = overlap_ANY_c;
            if (Volumes[parent]->geometry.mask_mode == 2 && Volumes[child]->geometry.mask_mode == 1 && overlap_ANY_p == 1) {
              // When parent is in any mode, and child is in ALL mode, any parent mask that overlaps all children masks is enough to end the parent loop
              logic_var = 1;
              break; // Without this break, only the last parent will matter
            }
            // if (overlap_ANY == 1) break; would speed things up a bit, but only after testing has been started and then it will be repeated
          }
          if (Volumes[parent]->geometry.mask_mode == 2 && Volumes[child]->geometry.mask_mode == 2) logic_var = overlap_ANY;
          // If both volumes have the ANY mode, just one case of overlap is enough.
        }
        
        // If all of the 4 statements above evaluate to true, the two volumes parent and child do overlap and it is added to the list.
        if (logic_var == 1) temp_list_local.elements[used_elements++] = child;
        
      }
    }
    // Allocate the actual overlap list with the new temp_list_local
    allocate_list_from_temp(used_elements,temp_list_local,true_overlap_lists[parent]);
    if (verbal) sprintf(string_output,"Overlaps for Volume (post mask) %d",parent);
    MPI_MASTER(
    if (verbal) print_1d_int_list(*true_overlap_lists[parent],string_output);
    )
  }
  
  // Clean up of dynamically allocated memory
  for(child=1;child<number_of_volumes;child++) free(temporary_overlap_lists[child].elements);
  free(temporary_overlap_lists);
  free(temp_list_local.elements);
  
};

void add_to_mask_intersect_list(struct pointer_to_1d_int_list *mask_intersect_list, int given_volume_index) {
    // A bit simpler than before
    if (on_int_list(*mask_intersect_list,given_volume_index) == 0)
      add_element_to_int_list(mask_intersect_list,given_volume_index);
}


void generate_intersect_check_lists(struct pointer_to_1d_int_list **overlap_lists,struct Volume_struct **Volumes, int number_of_volumes, int verbal) {
  // Generate intersection list for each volume
  MPI_MASTER(
  if (verbal) printf("\nGenerating intersect check lists -------------------- \n");
  )
  // Take overlap list for volume n
  // Remove entries with p < p(n)
  // Remove entries with parents on the list
  
  // Mask update: Mask volumes does not have priorities, do not do step 2 for mask volumes
  // Mask update: Instead of step 3 (Remove entries with parents on the list), move these entries to a Mask intersect list if that parent is masking the entry, otherwise remove
  
  // 1) Mask update: Take overlap list for volume n
  // 2) Mask update: Remove entries with p < p(n), except mask volumes, that are moved to another list A
  // 3) Mask update: Entries with parents on the list are removed (if these parents are masked, only remove the entry if it is a child of their parents masks)
  // 4) Possible optimization: Entries with parents on the A list, are moved to the appropriate mask list for this volume
  // 5) Mask update: Entries on list A are added again, if they are not parents of volume n
  // 6) Mask update: Entries on the main list that are masked are moved to the appropriate mask list for this volume, if their mask is on list A
  
  // Justification of the top algorithm:
  // No need to check intersections with something that does not overlap this volume, so start with intersect list and remove from that.
  // Entries with p < p(n) are "beneath" this volume, and thus doesn't cut it, mask volumes are however always "above" and are treated with care (moved to list A)
  // Entries with parents still on the list is removed, as it is impossible to intersect with them without first going through the parent
  // Entries with parents on the A list can only be seen if the ray is in that particular mask (the parent on the A list), so it can be added to the appropriate mask list for this volume
  // Now all masks that cut the volume are transfered back to the main list, the only masks that overlap and does not cut are parents of this volume, and these are thus not added
  // The entries on the list that are masked are added to the appropriate mask lists of this volume, but only if their mask is on the A list, as only masks on list A can have mask status 1 which is required for this to be relevant (this was true automatically for the ones added to the appropriate lists in the optimization step).
  
  
  // The intersect check list for volume 0 is done manually
  // Declare list for holding logic data for each index in the overlap list of this volume
  struct pointer_to_1d_int_list logic_list;
  logic_list.num_elements = overlap_lists[0]->num_elements;
  logic_list.elements = malloc(logic_list.num_elements * sizeof(int));
  
  // Declare similar list for List A
  struct pointer_to_1d_int_list mask_logic_list;
  mask_logic_list.num_elements = overlap_lists[0]->num_elements;
  mask_logic_list.elements = malloc(mask_logic_list.num_elements * sizeof(int));
  
  
  int iterate;
  int overlap_index;
  
  /*
  // Thise code is an old broken version of intersection_check_list generation only for volume 0, but this case is now included in the loop.
  // Assume all volumes on overlap list should be on intersection check list, and remove the once that should not.
  for (iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 1;
  // Asuume no volumes should go on the mask_logic_list, but add the ones that do.
  for (iterate=0;iterate<mask_logic_list.num_elements;iterate++) mask_logic_list.elements[iterate] = 0;
  
  for (overlap_index = 0;overlap_index < overlap_lists[0]->num_elements;overlap_index++) {
    // No volumes to remove with lower priority, as surrounding vacuum have the lowest.
    
    // Check if this overlap_lists[0]->elements[overlap_index] is a child of another member of the overlap list
    for (iterate = 0;iterate < overlap_lists[0]->num_elements;iterate++) {
        if (iterate != overlap_index) {
            // We are now checking if Volumes[overlap_lists[0]->elements[iterate]]->geometry.children contains overlap_lists[overlap_index]
            if (on_int_list(Volumes[overlap_lists[0]->elements[iterate]]->geometry.children,overlap_lists[0]->elements[overlap_index])) logic_list.elements[overlap_index] = 0;
        }
    }
  }

  Volumes[0]->geometry.intersect_check_list.num_elements = sum_int_list(logic_list);
  Volumes[0]->geometry.intersect_check_list.elements = malloc(Volumes[0]->geometry.intersect_check_list.num_elements * sizeof(int));
  
  iterate = 0;
  for (overlap_index = 0;overlap_index < overlap_lists[0]->num_elements;overlap_index++) {
        if (logic_list.elements[overlap_index])   Volumes[0]->geometry.intersect_check_list.elements[iterate++] = overlap_lists[0]->elements[overlap_index];
  }
  if (logic_list.num_elements > 0) free(logic_list.elements);
  if (mask_logic_list.num_elements > 0) free(mask_logic_list.elements);
  
  MPI_MASTER(
  print_1d_int_list(Volumes[0]->geometry.intersect_check_list,"Intersect check list for Volume 0 (manual)");
  )
  */
  
  // The intersect check lists for the remaining volumes are generated
  int volume_index,mask_volume_number,mask_index;
  //for (volume_index = 1;volume_index < number_of_volumes;volume_index++) {
  for (volume_index = 0;volume_index < number_of_volumes;volume_index++) {
    
      // 1) Take overlap list for volume n
      logic_list.num_elements = overlap_lists[volume_index]->num_elements;
      logic_list.elements = malloc(logic_list.num_elements * sizeof(int));
      mask_logic_list.num_elements = overlap_lists[volume_index]->num_elements;
      mask_logic_list.elements = malloc(mask_logic_list.num_elements * sizeof(int));
      
      
      
      if (Volumes[volume_index]->geometry.is_mask_volume == 1) {
        // Masks do not have any entries on their intersect check list, as they are never the current volume
        for (iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 0;
        for (iterate=0;iterate<mask_logic_list.num_elements;iterate++) mask_logic_list.elements[iterate] = 0;
      } else {
        for (iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 1;
        for (iterate=0;iterate<mask_logic_list.num_elements;iterate++) mask_logic_list.elements[iterate] = 0;
      }
      
      //2) Remove entries with p < p(n), except mask volumes, that are moved to another list A
      for (overlap_index = 0;overlap_index < overlap_lists[volume_index]->num_elements;overlap_index++) {
        
        if (overlap_lists[volume_index]->elements[overlap_index] == 0) logic_list.elements[overlap_index] = 0; // The surrounding vacuum has lower priority (and is not a mask)
        else if (Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.is_mask_volume == 1) {
           logic_list.elements[overlap_index] = 0;      // Remove this volume from the main list
           mask_logic_list.elements[overlap_index] = 1; // Add mask volumes to seperat list
        }
        else if (volume_index != 0) { // Only relevant to remove elements because of priority if the volume_index is different from 0, meaning the surrounding vacuum skips this step
           if (Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.priority_value < Volumes[volume_index]->geometry.priority_value) {
           // If the investigated volume on the overlap_list have lower priority than the volume with volume_index, remove it
           logic_list.elements[overlap_index] = 0; // Remove this volume from the list
           }
        }
      }
      
      // 3) Entries with parents on the list are removed
      for (overlap_index = 0;overlap_index < overlap_lists[volume_index]->num_elements;overlap_index++) {
        // Check if this overlap_lists[0]->elements[overlap_index] is a child of another member of the overlap list
        for (iterate = 0;iterate < overlap_lists[volume_index]->num_elements;iterate++) {
             if (iterate != overlap_index) { // Only necessary if a volume determines that it has itself as child, but a nice safety.
                // We are now checking if Volumes[overlap_lists[volume_index]->elements[iterate]]->geometry.children contains overlap_lists[overlap_index]
                // The && part is needed because we do not remove children of elements that have allready been removed because of their priority
                
                // if (on_int_list(Volumes[overlap_lists[volume_index]->elements[iterate]]->geometry.children,overlap_lists[volume_index]->elements[overlap_index]) && logic_list.elements[overlap_lists[volume_index]->elements[iterate]] == 1) {logic_list.elements[overlap_index] = 0; bug fixed on 3/4 2016
                if (on_int_list(Volumes[overlap_lists[volume_index]->elements[iterate]]->geometry.children,overlap_lists[volume_index]->elements[overlap_index]) && logic_list.elements[iterate] == 1) logic_list.elements[overlap_index] = 0;
                
                /*
                Explanation with simpler notation
                
                Overlap list of volume i = O_i
                Element j of overlap list i = O_i(j)
                
                Children list of volume i = C_i
                Element j on children list i = C_i(j)
                
                Priority of volume i p(i)
                
                for i = volumes
                    for j in O_i(j) 
                        logic(j) = 1;
                        if p(i) > p(O_i(j)) logic(j) = 0; // Remove if lower priority than the currently checked
                        
                        for k in O_i(k)
                            if (O_i(j) is contained on the list C_k && logic(k) == 1) logic(j) = 0
                */
                
                // 4) Entries with parents on the A list, are moved to the appropriate mask list for this volume
                if (on_int_list(Volumes[overlap_lists[volume_index]->elements[iterate]]->geometry.children,overlap_lists[volume_index]->elements[overlap_index]) && mask_logic_list.elements[iterate] == 1) {
                   logic_list.elements[overlap_index] = 0; // Remove from main list (Not strictly needed as it will be removed already if it is on list A)
                   // Add overlap_lists[volume_index]->elements[overlap_index] to volumes mask intersect list for mask with index overlap_lists[volume_index]->elements[iterate]
                   //add_to_mask_intersect_lists(&Volumes[volume_index]->geometry.mask_intersect_lists,overlap_lists[volume_index]->elements[iterate],overlap_lists[volume_index]->elements[overlap_index]);
                   // Simpler method that just collects all the masked intersect parts on a 1d_int list instead of seperating them for each mask
                   add_to_mask_intersect_list(&Volumes[volume_index]->geometry.mask_intersect_list,overlap_lists[volume_index]->elements[overlap_index]);
                }
                
             }
        }
      }
      
      // 5) Entries on list A are added again, if they are not parents of volume n
      // Time to add mask volumes removed back to the intersect list, if they are not parents of the current volume, meaning the current volume should not be a child of the mask
      for (overlap_index = 0;overlap_index < overlap_lists[volume_index]->num_elements;overlap_index++) {
        if (mask_logic_list.elements[overlap_index] == 1) {
          mask_volume_number = overlap_lists[volume_index]->elements[overlap_index];
          if (on_int_list(Volumes[mask_volume_number]->geometry.children,volume_index) == 0) logic_list.elements[overlap_index] = 1; // Add it back to the list
        }
      }
      
      // 6) Entries on the main list that are masked are moved to the appropriate mask list for this volume, if their mask is on list A
      int mask_index,mask_mode,found_index,logic_ALL;
      for (overlap_index = 0;overlap_index < overlap_lists[volume_index]->num_elements;overlap_index++) {
        if (logic_list.elements[overlap_index] == 1 && Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.is_masked_volume == 1) {
          logic_list.elements[overlap_index] = 0; // If the volume is masked, remove it from the intersect check list
          // Could actually keep it on the intersect list if the volume's mask is a parent of the current volume, now it will just be added in all cases
          
          // When ALL setting is used, all masks should overlap the current volume, otherwise the user probably made a mistake, this should be checked in error checking
          // When ANY setting is used, at least one mask should overlap the current volume, otherwise the user probably made a mistake, this should be checked in error checking
          // mask_mode == 1 => ALL / mask_mode == 2 => ANY
          mask_mode = Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.mask_mode;
          
          if (mask_mode == 1) {
            logic_ALL = 1;
            for (iterate=0;iterate<Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.masked_by_list.num_elements;iterate++) {
              mask_index = Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.masked_by_list.elements[iterate];
              
              if (on_int_list(*overlap_lists[volume_index],mask_index) == 0) {
                // If any one of the volumes masks do not overlap with this volume, there is no reason check intersections with the volume regardless of the mask status's
                logic_ALL = 0;
                break;
              }
            }
          if (logic_ALL == 1) {
            // If all of it's masks are overlapping the current volume, add it to all relevant mask intersect lists of this volume
            for (iterate=0;iterate<Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.masked_by_list.num_elements;iterate++) {
              mask_index = Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.masked_by_list.elements[iterate];
              //add_to_mask_intersect_lists(&Volumes[volume_index]->geometry.mask_intersect_lists,mask_index,overlap_lists[volume_index]->elements[overlap_index]);
              // Adding the masked intersect list elements to another list
              add_to_mask_intersect_list(&Volumes[volume_index]->geometry.mask_intersect_list,overlap_lists[volume_index]->elements[overlap_index]);
            }
          }
            
            
          } else if (mask_mode == 2) {
          // When in ANY mode, the problem is easier, as not all masks have to overlap the volume, and we can add each one to the list
            for (iterate=0;iterate<Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.masked_by_list.num_elements;iterate++) {
              mask_index = Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.masked_by_list.elements[iterate];
            
              if (on_int_list(*overlap_lists[volume_index],mask_index) == 1) {
                //add_to_mask_intersect_lists(&Volumes[volume_index]->geometry.mask_intersect_lists,mask_index,overlap_lists[volume_index]->elements[overlap_index]);
                // Adding the masked intersect list elements to another list
                add_to_mask_intersect_list(&Volumes[volume_index]->geometry.mask_intersect_list,overlap_lists[volume_index]->elements[overlap_index]);
              }
            }
            
          }
        }
      }
      
      
      Volumes[volume_index]->geometry.intersect_check_list.num_elements = sum_int_list(logic_list);
      Volumes[volume_index]->geometry.intersect_check_list.elements = malloc(Volumes[volume_index]->geometry.intersect_check_list.num_elements * sizeof(int));
      
      iterate = 0;
      for (overlap_index = 0;overlap_index < overlap_lists[volume_index]->num_elements;overlap_index++) {
            if (logic_list.elements[overlap_index])   Volumes[volume_index]->geometry.intersect_check_list.elements[iterate++] = overlap_lists[volume_index]->elements[overlap_index];
      }
      free(logic_list.elements); // Need to be careful with names for variables to be freed, as they can collide with names in main because of automatic declaration of external variables
      free(mask_logic_list.elements);
      
      
      char string_output[128];
      MPI_MASTER(
      if (verbal) sprintf(string_output,"Intersect check list for Volume %d",volume_index);
      if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.intersect_check_list,string_output);
      if (verbal) sprintf(string_output,"Mask intersect check list for Volume %d",volume_index);
      if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.mask_intersect_list,string_output);
      /*
      if (verbal) {
        for (mask_index=0;mask_index<Volumes[volume_index]->geometry.mask_intersect_lists.number_of_lists;mask_index++) {
          sprintf(string_output," - mask intersect check list for mask %d which is volume %d",mask_index,Volumes[volume_index]->geometry.mask_intersect_lists.mask_indices.elements[mask_index]);
          print_1d_int_list(Volumes[volume_index]->geometry.mask_intersect_lists.lists[mask_index],string_output);
        }
      }
      */
      
      )
  }
};

void generate_parents_lists(struct pointer_to_1d_int_list **parents_lists, struct Volume_struct **Volumes, int number_of_volumes, int verbal, int mask_mode) {
  // Function for generating parent lists for all volumes
  // A volume m has n as a parent, if volume n has volume m as a child
  
  // if mask_mode == 0, masks are ignored, if mask_mode == 1, masks are taken into account.
  
  MPI_MASTER(
  if (verbal) {
    if (mask_mode == 1)
        printf("\nGenerating parents lists ---------------------------- \n");
    else if (mask_mode == 0)
        printf("\nGenerating parents lists (ignoring masks) ----------- \n");
    else {
        printf("Error, the function parents_lists got a non defined mask_mode");
        exit(1);
    }
  }
  
  )
  // Volume iterate has volume p as a parent, if volume p has volume iterate as child.
  
  struct pointer_to_1d_int_list temp_list_local;
  temp_list_local.num_elements = number_of_volumes;
  temp_list_local.elements = malloc(number_of_volumes*sizeof(int));
  
  // Loop over
  int iterate,parent,used_elements;
  for (iterate = 0;iterate < number_of_volumes;iterate++) {
    // clear temp list
    used_elements = 0;
    parents_lists[iterate] = malloc(sizeof(struct pointer_to_1d_int_list));
    for (parent = 0;parent < number_of_volumes;parent++) {
        if (on_int_list(Volumes[parent]->geometry.children,iterate))
          if (mask_mode == 1 || (Volumes[parent]->geometry.is_mask_volume == 0 && Volumes[iterate]->geometry.is_mask_volume == 0))
            temp_list_local.elements[used_elements++] = parent;
    }
      allocate_list_from_temp(used_elements,temp_list_local,parents_lists[iterate]);
      
      char string_output[128];
      MPI_MASTER(
      if (verbal) sprintf(string_output,"Parents for Volume %d",iterate);
      if (verbal) print_1d_int_list(*parents_lists[iterate],string_output);
      )
  }
  free(temp_list_local.elements);

};

void generate_true_parents_lists(struct pointer_to_1d_int_list **parents_lists, struct pointer_to_1d_int_list **true_children_lists, struct Volume_struct **Volumes, int number_of_volumes, int verbal, int mask_mode) {
  // Function for generating parent lists for all volumes
  // A volume m has n as a parent, if volume n has volume m as a child
  
  // if mask_mode == 0, masks are ignored, if mask_mode == 1, masks are taken into account.
  
  MPI_MASTER(
  if (verbal) {
    if (mask_mode == 1)
        printf("\nGenerating parents lists ---------------------------- \n");
    else if (mask_mode == 0)
        printf("\nGenerating parents lists (ignoring masks) ----------- \n");
    else {
        printf("Error, the function parents_lists got a non defined mask_mode");
        exit(1);
    }
  }
  
  )
  // Volume iterate has volume p as a parent, if volume p has volume iterate as child.
  
  struct pointer_to_1d_int_list temp_list_local;
  temp_list_local.num_elements = number_of_volumes;
  temp_list_local.elements = malloc(number_of_volumes*sizeof(int));
  
  // Loop over
  int iterate,parent,used_elements;
  for (iterate = 0;iterate < number_of_volumes;iterate++) {
    // clear temp list
    used_elements = 0;
    parents_lists[iterate] = malloc(sizeof(struct pointer_to_1d_int_list)); // allocate_list_from_temp allocates
    for (parent = 0;parent < number_of_volumes;parent++) {
        //if (on_int_list(Volumes[parent]->geometry.children,iterate))
        if (on_int_list(*true_children_lists[parent],iterate))
          if (mask_mode == 1 || (Volumes[parent]->geometry.is_mask_volume == 0 && Volumes[iterate]->geometry.is_mask_volume == 0))
            temp_list_local.elements[used_elements++] = parent;
    }
      allocate_list_from_temp(used_elements,temp_list_local,parents_lists[iterate]);
      
      char string_output[128];
      MPI_MASTER(
      if (verbal) sprintf(string_output,"Parents for Volume %d",iterate);
      if (verbal) print_1d_int_list(*parents_lists[iterate],string_output);
      )
  }
  free(temp_list_local.elements);

};

void generate_intersect_check_lists_experimental(struct pointer_to_1d_int_list **true_overlap_lists, struct pointer_to_1d_int_list **raw_overlap_lists, struct pointer_to_1d_int_list **parents_lists, struct pointer_to_1d_int_list **true_parents_lists , struct Volume_struct **Volumes, int number_of_volumes, int verbal) {
  // Generates the intersect_check_list and mask_intersect_list for each Volume.
  /*
  Description of needed lists for volume n:
  Children list: List of volumes that is contained within volume n (is stored in the Volumes struct)
  True Children list: List of volumes that when masked by their masks is contained within volume n when masked by it's masks
  
  Parents list: List of volumes that contains volume n
  True parents list: List of volumes that when masked by their masks contains volume n when it is masked by it's masks
  
  raw overlap list: List of volumes whos geometry overlaps the geometry of volume n
  true overlap list: List of volumes whos geometry overlaps the geometry of volume n when the masks of both volumes are applied
  
  
  
  The algorithm:
  
  1) Take the true overlap list for volume n
  2) remove parents of n (normal parent list, with masks)
  3) remove volumes that do not mask n and are true parents of n
  4) remove volumes that are not masks and have lower priority than n
  5) remove volumes that have at least one true parent on the list (in step 4) that is not a mask volume
  6) split the list into two, the intersect_check_list which is all non masked volumes still on the list, and the mask_intersect_list which is the masked volumes
  7) remove volumes on the mask_intersect_list whos mask does not overlap (standard overlap list) n
  
  In step 5 the order in which the volumes are tested and removed may matter, so it is specifically stated that it is as the list looked in step 4.
  */
  
  MPI_MASTER(
  if (verbal) printf("\nGenerating intersect check lists -------------------- \n");
  )
  
  struct pointer_to_1d_int_list work_list;
  struct pointer_to_1d_int_list logic_list;
  
  int volume_index,iterate,parent,mask_index,masked_volume_index,ANY_logic,true_parent_volume_number;
  int *mask_check,*mask_start;
  for (volume_index = 0;volume_index < number_of_volumes;volume_index++) {
    
    // 1) Take the true overlap list for volume n
    // Create copy of true_overlap_lists to work with
    
    if (Volumes[volume_index]->geometry.is_mask_volume == 1) {
      // Bug fixed on 26/11/2016, do not create intersection lists for masks as they are not used, and affects destinations lists in a problematic way
      Volumes[volume_index]->geometry.intersect_check_list.num_elements = 0;
      Volumes[volume_index]->geometry.mask_intersect_list.num_elements = 0;
      
    } else {
      work_list.num_elements = true_overlap_lists[volume_index]->num_elements;
      work_list.elements = malloc(work_list.num_elements * sizeof(int));
      for (iterate=0;iterate<work_list.num_elements;iterate++) work_list.elements[iterate] = true_overlap_lists[volume_index]->elements[iterate];
    
    //if (verbal) print_1d_int_list(work_list,"After 1)");
    
    
    //2) remove parents of n (normal parent list, with masks)
    for (iterate=work_list.num_elements-1;iterate>-1;iterate--) {
      if (on_int_list(*parents_lists[volume_index],work_list.elements[iterate]))
        remove_element_in_list_by_index(&work_list,iterate);
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 2)");
    
    //3) remove volumes that do not mask n and are true parents of n
    for (iterate=work_list.num_elements-1;iterate>-1;iterate--) {
      if (on_int_list(Volumes[volume_index]->geometry.masked_by_list,work_list.elements[iterate]) == 0 && on_int_list(*true_parents_lists[volume_index],work_list.elements[iterate]))
        remove_element_in_list_by_index(&work_list,iterate);
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 3)");
    
    //4) remove volumes that are not masks and have lower priority than n
    for (iterate=work_list.num_elements-1;iterate>-1;iterate--) {
      if (Volumes[work_list.elements[iterate]]->geometry.is_mask_volume == 0 && Volumes[work_list.elements[iterate]]->geometry.priority_value < Volumes[volume_index]->geometry.priority_value)
        remove_element_in_list_by_index(&work_list,iterate);
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 4)");
    
    //5) remove volumes that have at least one true parent on the list (in step 4) that is not a mask volume
    // Here a logic_list is used to not have the order of removal matter
    logic_list.num_elements = work_list.num_elements;
    logic_list.elements = malloc(logic_list.num_elements * sizeof(int));
    for(iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 1;
    
    for (iterate=0;iterate<work_list.num_elements;iterate++) {
      for (parent=0;parent<true_parents_lists[work_list.elements[iterate]]->num_elements;parent++) {
        true_parent_volume_number = true_parents_lists[work_list.elements[iterate]]->elements[parent];
        if (on_int_list(work_list,true_parent_volume_number) && Volumes[true_parent_volume_number]->geometry.is_mask_volume == 0) {
          // Since element number iterate on the work list have a true parent on the work list, it can be removed
          logic_list.elements[iterate] = 0;
          break;
        }
      }
    }
    
    // Now the elements marked for removal can be removed without interfering in the operation
    for (iterate=work_list.num_elements-1;iterate>-1;iterate--) {
      if (logic_list.elements[iterate] == 0) remove_element_in_list_by_index(&work_list,iterate);
    }
    free(logic_list.elements);
    
    //if (verbal) print_1d_int_list(work_list,"After 5)");
      
    //6) split the list into two, the intersect_check_list which is all non masked volumes still on the list, and the mask_intersect_list which is the masked volumes
    
    Volumes[volume_index]->geometry.intersect_check_list.num_elements = 0;
    Volumes[volume_index]->geometry.mask_intersect_list.num_elements = 0;
    
    for (iterate=0;iterate<work_list.num_elements;iterate++) {
      if (Volumes[work_list.elements[iterate]]->geometry.is_masked_volume == 1) {
        add_element_to_int_list(&Volumes[volume_index]->geometry.mask_intersect_list,work_list.elements[iterate]);
      } else {
        add_element_to_int_list(&Volumes[volume_index]->geometry.intersect_check_list,work_list.elements[iterate]);
      }
    }
    
    //if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.intersect_check_list,"After 6) intersect_check_list");
    //if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.mask_intersect_list,"After 6) mask_intersect_list");
    
    //7) remove volumes on the mask_intersect_list whos masks does not overlap (standard overlap list) n
    for (iterate=Volumes[volume_index]->geometry.mask_intersect_list.num_elements-1;iterate>-1;iterate--) {
      // Need to check if the volumes masking Volumes[volume_index]->geometry.mask_intersect_list.elements[iterate] overlaps with n
      masked_volume_index = Volumes[volume_index]->geometry.mask_intersect_list.elements[iterate];
      
      if (Volumes[masked_volume_index]->geometry.mask_mode == 1) {// All mode, if just one does not overlap, remove the element
        for (mask_start=mask_check=Volumes[masked_volume_index]->geometry.masked_by_list.elements;mask_check-mask_start<Volumes[masked_volume_index]->geometry.masked_by_list.num_elements;mask_check++) {
          if (on_int_list(*raw_overlap_lists[volume_index],*mask_check) == 0) {
            remove_element_in_list_by_index(&Volumes[volume_index]->geometry.mask_intersect_list,iterate);
            break;
          }
        }
      } else { // ANY mode, just one need to overlap in order to keep the element
        ANY_logic = 0;
        for (mask_start=mask_check=Volumes[masked_volume_index]->geometry.masked_by_list.elements;mask_check-mask_start<Volumes[masked_volume_index]->geometry.masked_by_list.num_elements;mask_check++) {
          if (on_int_list(*raw_overlap_lists[volume_index],*mask_check) == 1) {
            ANY_logic = 1;
            break;
          }
        }
        if (ANY_logic == 0) remove_element_in_list_by_index(&Volumes[volume_index]->geometry.mask_intersect_list,iterate);
      }
    }
    
    if (work_list.num_elements > 0) free(work_list.elements);
    }
    
    
    //if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.mask_intersect_list,"After 7) mask_intersect_list");
    
    char string_output[128];
    MPI_MASTER(
    if (verbal) sprintf(string_output,"Intersect check list for Volume %d",volume_index);
    if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.intersect_check_list,string_output);
    if (verbal) sprintf(string_output,"Mask intersect check list for Volume %d",volume_index);
    if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.mask_intersect_list,string_output);
    )
    
    
  }
}

void generate_grandparents_lists(struct pointer_to_1d_int_list **grandparents_lists, struct pointer_to_1d_int_list **parents_lists, int number_of_volumes, int verbal) {
  // Function for generating grandparents lists
  // Volume iterate has volume p as a grandparent, if volume p has a parent T, who has volume iterate as parent.
  // Alternertively:
  // Volume iterate has volume p as a grandparent, if volume p have a child that is volume iterate's parent.
  
  MPI_MASTER(
  if (verbal) printf("\nGenerating grandparents lists ----------------------- \n");
  )

  struct pointer_to_1d_int_list common;
  common.num_elements = number_of_volumes;
  common.elements = malloc(common.num_elements*sizeof(int)); // Maximum needed space.
  
  struct pointer_to_1d_int_list temp_list_local;
  temp_list_local.num_elements = number_of_volumes;
  temp_list_local.elements = malloc(number_of_volumes*sizeof(int));
  
  int iterate,reset_int,parent,child,used_elements;
  for (iterate = 0;iterate < number_of_volumes;iterate++) {
    // clear temp list
    used_elements = 0;

    for (reset_int=0; reset_int<number_of_volumes; reset_int++) common.elements[reset_int] = -1; // Initialize to impossible volume ids
    for (reset_int=0; reset_int<number_of_volumes; reset_int++) temp_list_local.elements[reset_int] = -1; // Initialize to impossible volume ids

    grandparents_lists[iterate] = malloc(sizeof(struct pointer_to_1d_int_list));
    
    for (parent = 0; parent<parents_lists[iterate]->num_elements; parent++) {
        // parent number p parents_lists[iterate].elements.[p] in the parent_list for iterate.
        on_both_int_lists(parents_lists[parents_lists[iterate]->elements[parent]], parents_lists[iterate], &common);
        // returns a pointer_to_1d_list, with all the elements that are in common.
        for (child = 0;child < common.num_elements;child++) {
            // Need to make sure the element is not already on the list
            if (0 == on_int_list(temp_list_local, common.elements[child])) {
              temp_list_local.elements[used_elements++] = common.elements[child];
            }
        }
    }
    allocate_list_from_temp(used_elements, temp_list_local, grandparents_lists[iterate]);
  
    char string_output[128];
    MPI_MASTER(
      if (verbal) sprintf(string_output,"Grandparents for Volume %d", iterate);
      if (verbal) print_1d_int_list(*grandparents_lists[iterate], string_output);
    )
  }
  free(temp_list_local.elements);
  free(common.elements);
};


void generate_destinations_lists_experimental(struct pointer_to_1d_int_list **true_overlap_lists, struct pointer_to_1d_int_list **true_children_lists, struct pointer_to_1d_int_list **true_parents_lists, struct pointer_to_1d_int_list **true_grandparents_lists, struct Volume_struct **Volumes, int number_of_volumes, int verbal) {
  // Generates destinations list for for all volumes
  // Current implementation uses true_parents_lists and true_grandparents_lists that are generated as if no masks were defined

  MPI_MASTER(
  if (verbal) printf("\nGenerating destinations lists ----------------------- \n");
  )
  // Volume 0 has an hardcoded empty destinations list
  Volumes[0]->geometry.destinations_list.num_elements = 0;

  struct pointer_to_1d_int_list work_list;
  int volume_index,iterate,iterate2,found_index,I_index,I_volume;
  
  for (volume_index=1;volume_index<number_of_volumes;volume_index++) {
    
    // 1) Take the true overlap list for volume n
    // Create copy of true_overlap_lists to work with
    work_list.num_elements = true_overlap_lists[volume_index]->num_elements;
    work_list.elements = malloc(work_list.num_elements * sizeof(int));
    for (iterate=0;iterate<work_list.num_elements;iterate++) work_list.elements[iterate] = true_overlap_lists[volume_index]->elements[iterate];
    
    //if (verbal) print_1d_int_list(work_list,"After 1)");
    
    // 2) Remove elements from n's intersection list
    for (iterate=work_list.num_elements-1;iterate>-1;iterate--) {
      if (on_int_list(Volumes[volume_index]->geometry.intersect_check_list,work_list.elements[iterate]))
        remove_element_in_list_by_index(&work_list,iterate);
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 2)");
  
    // 3) Remove true children of non-mask elements on n's intersection list
    for (I_index=0;I_index<Volumes[volume_index]->geometry.intersect_check_list.num_elements;I_index++) {
      I_volume = Volumes[volume_index]->geometry.intersect_check_list.elements[I_index];
      if (Volumes[I_volume]->geometry.is_mask_volume == 0) {
        for (iterate=0;iterate<true_children_lists[I_volume]->num_elements;iterate++) {
          remove_element_in_list_by_value(&work_list,true_children_lists[I_volume]->elements[iterate]);
        }
      }
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 3)");
    
    // 4) Remove elements from mask intersection list
    for (iterate=work_list.num_elements-1;iterate>-1;iterate--) {
      if (on_int_list(Volumes[volume_index]->geometry.mask_intersect_list,work_list.elements[iterate]))
        remove_element_in_list_by_index(&work_list,iterate);
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 4)");
    
    // 5) Remove true children of elements on n's mask intersection list
    for (I_index=0;I_index<Volumes[volume_index]->geometry.mask_intersect_list.num_elements;I_index++) {
      I_volume = Volumes[volume_index]->geometry.mask_intersect_list.elements[I_index];
      if (Volumes[I_volume]->geometry.is_mask_volume == 0) {
        for (iterate=0;iterate<true_children_lists[I_volume]->num_elements;iterate++) {
          remove_element_in_list_by_value(&work_list,true_children_lists[I_volume]->elements[iterate]);
        }
      }
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 5)");
    
    // 6) Remove true children of n
    for (iterate=0;iterate<true_children_lists[volume_index]->num_elements;iterate++)
      remove_element_in_list_by_value(&work_list,true_children_lists[volume_index]->elements[iterate]);
      
    //if (verbal) print_1d_int_list(work_list,"After 6)");
      
    // 7) Remove true grandparents of n
    for (iterate=0;iterate<true_grandparents_lists[volume_index]->num_elements;iterate++)
      remove_element_in_list_by_value(&work_list,true_grandparents_lists[volume_index]->elements[iterate]);
      
    //if (verbal) print_1d_int_list(work_list,"After 7)");
      
    // 8) Remove mask volumes
    for (iterate=work_list.num_elements-1;iterate>-1;iterate--) {
      if (Volumes[work_list.elements[iterate]]->geometry.is_mask_volume == 1)
        remove_element_in_list_by_index(&work_list,iterate);
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 8)");
    
    // 9) Remove true parents of n on the list that has other true parents of n on the list with higher priority
    for (iterate=work_list.num_elements-1;iterate>-1;iterate--) {
      if (on_int_list(*true_parents_lists[volume_index],work_list.elements[iterate])){
        // work_list.elements[iterate] is the volume index of a volume that is a true parent of n
        for (iterate2=0;iterate2<work_list.num_elements;iterate2++) {
          if (on_int_list(*true_parents_lists[volume_index],work_list.elements[iterate2]) && iterate != iterate2) {
            if (Volumes[work_list.elements[iterate]]->geometry.priority_value < Volumes[work_list.elements[iterate2]]->geometry.priority_value) {
              //printf("Removing element number %d (V%d) because element number %d (V%d) had higher priority \n",iterate,work_list.elements[iterate],iterate2,work_list.elements[iterate2]);
              remove_element_in_list_by_index(&work_list,iterate);
              break; // Missing break inserted on 14/9/2016
            }
          }
        }
      }
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 9)");
    
    // 10) The remaining list is the destinations_list
    Volumes[volume_index]->geometry.destinations_list.num_elements = work_list.num_elements;
    Volumes[volume_index]->geometry.destinations_list.elements = malloc(Volumes[volume_index]->geometry.destinations_list.num_elements*sizeof(int));
    
    for (iterate=0;iterate<work_list.num_elements;iterate++)
      Volumes[volume_index]->geometry.destinations_list.elements[iterate] = work_list.elements[iterate];
    
    // Clean up after work_list so the next can be allocated
    free(work_list.elements);
  
  
    char string_output[128];
    MPI_MASTER(
    if (verbal) sprintf(string_output,"Destinations list for Volume %d",volume_index);
    if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.destinations_list,string_output);
    )

  }

};

void generate_destinations_list(int N_volume,struct Volume_struct **Volumes,struct pointer_to_1d_int_list original_overlap_list,struct pointer_to_1d_int_list *parent_list, struct pointer_to_1d_int_list *grandparent_list) {
    // This function generates the destinations_list for a single volume index, N_volume
    // The destination list describes which volumes a ray can enter when leaving N_volume
    // Each of the 6 steps for the algorithm is commented individually, and debug print statements are available
    
    // Mask update: Mask volumes are to be removed from all destinations_list as they can not be the current volume
    // It is not that simple, as some volume will then get empty destination lists. Need to revise this algorithm.
    
    // 1) Start with the overlap list of volume N
    // 2) remove all Volumes from the overlap list of N, which is also on the intersect_check_list
    // 3) remove the children of volumes removed in step 2)
    // 4) remove the children of N
    // 5) remove the grandparents of N
    // 6) remove volumes with lower priority than parents of N still on the list
    // 7) The remaing list is the destinations list
    
    
    // The destination list system should run without masks altogether, meaning parent and grandparent lists should not use them,
    //  and all masks should be removed in step 2
    
    // 1) Start with the overlap list of volume N
    // 2) remove all masks on the list
    // 3) remove all Volumes from the overlap list of N, which is also on the intersect_check_list
    // 4) remove the children of volumes removed in step 2) (if the removed volume was not a mask )
    // 5) remove the children of N
    // 6) remove the grandparents of N
    // 7) remove volumes with lower priority than parents of N still on the list
    // 8) The remaing list is the destinations list
    
    

    // 1) Start with the overlap list of volume N
    struct pointer_to_1d_int_list overlap_list;
    overlap_list.num_elements = original_overlap_list.num_elements;
    overlap_list.elements = malloc(overlap_list.num_elements*sizeof(int));
    int iterate;
    for (iterate=0;iterate<overlap_list.num_elements;iterate++) overlap_list.elements[iterate] = original_overlap_list.elements[iterate];
    
    char string_output[124];
    // sprintf(string_output,"Destinations list for Volume %d step 1",N_volume);
    // print_1d_int_list(overlap_list,string_output);
    
    // 2) remove all masks
    for (iterate=overlap_list.num_elements-1;iterate > -1;iterate--) {
        if (Volumes[overlap_list.elements[iterate]]->geometry.is_mask_volume == 1) {
            remove_element_in_list_by_index(&overlap_list,iterate);
        }
    }
    
    // 3) remove all Volumes from the overlap list of N, which is also on the intersect_check_list
    struct pointer_to_1d_int_list removed_under_2;
    removed_under_2.num_elements = 0;
    int to_check;
    removed_under_2.elements = malloc( Volumes[N_volume]->geometry.intersect_check_list.num_elements * sizeof(int));
    for (iterate=0;iterate < Volumes[N_volume]->geometry.intersect_check_list.num_elements;iterate++) {
        to_check = Volumes[N_volume]->geometry.intersect_check_list.elements[iterate];
        if (on_int_list(overlap_list,to_check)) {
            removed_under_2.elements[removed_under_2.num_elements++] = to_check;
            remove_element_in_list_by_value(&overlap_list,to_check);
        }
    }
    
    // sprintf(string_output,"Destinations list for Volume %d step 2",N_volume);
    // print_1d_int_list(overlap_list,string_output);
    
    // 4) remove the children of volumes removed in step 2)
    int children;
    for (iterate=0;iterate<removed_under_2.num_elements;iterate++){
        for (children = 0;children < Volumes[removed_under_2.elements[iterate]]->geometry.children.num_elements;children++) {
          remove_element_in_list_by_value(&overlap_list,Volumes[removed_under_2.elements[iterate]]->geometry.children.elements[children]);
        }
    }
    
    // sprintf(string_output,"Destinations list for Volume %d step 3",N_volume);
    // print_1d_int_list(overlap_list,string_output);
    
    // 5) remove the children of N
    for (children = 0;children < Volumes[N_volume]->geometry.children.num_elements;children++) {
        remove_element_in_list_by_value(&overlap_list,Volumes[N_volume]->geometry.children.elements[children]);
    }
    
    // sprintf(string_output,"Destinations list for Volume %d step 4",N_volume);
    // print_1d_int_list(overlap_list,string_output);
    
    // 6) remove the grandparents of N
    int grandparent;
    for (grandparent = 0;grandparent < grandparent_list->num_elements;grandparent++) {
        remove_element_in_list_by_value(&overlap_list,grandparent_list->elements[grandparent]);
    }
    
    // sprintf(string_output,"Destinations list for Volume %d step 5",N_volume);
    // print_1d_int_list(overlap_list,string_output);
    
    // 7) remove volumes with lower priority than parents of N still on the list
    struct pointer_to_1d_int_list logic_list;
    logic_list.num_elements = overlap_list.num_elements;
    if (logic_list.num_elements>0) logic_list.elements = malloc(logic_list.num_elements*sizeof(int));
    for (iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 1;
    
    int parent;
    for (parent=0;parent<parent_list->num_elements;parent++) {
      if (on_int_list(overlap_list,parent_list->elements[parent])) {
        // Found a parent to N on the list, now check all other elements on the list
        for (iterate=0;iterate<overlap_list.num_elements;iterate++) {
          if (parent_list->elements[parent] != overlap_list.elements[iterate]) {
            // if the element iterate have lower priority than the parent, remove it from the list
            if (Volumes[overlap_list.elements[iterate]]->geometry.priority_value < Volumes[parent_list->elements[parent]]->geometry.priority_value) logic_list.elements[iterate] = 0;
          }
        }
      }
    }
    
    // 8) The remaing list is the destinations list
    Volumes[N_volume]->geometry.destinations_list.num_elements = sum_int_list(logic_list);
    Volumes[N_volume]->geometry.destinations_list.elements = malloc(Volumes[N_volume]->geometry.destinations_list.num_elements * sizeof(int));
    
    int overlap_index,used_elements=0;
    for (overlap_index=0;overlap_index < overlap_list.num_elements;overlap_index++)
      if (logic_list.elements[overlap_index] == 1)
        Volumes[N_volume]->geometry.destinations_list.elements[used_elements++] = overlap_list.elements[overlap_index];
    
    if (overlap_list.num_elements>0) free(overlap_list.elements);
    if (logic_list.num_elements>0) free(logic_list.elements);
    
    
    /*
    // Old version before rule 6 and 2 was added
    // 8) The remaing list is the destinations list
    Volumes[N_volume]->geometry.destinations_list.num_elements = overlap_list.num_elements;
    Volumes[N_volume]->geometry.destinations_list.elements = malloc(Volumes[N_volume]->geometry.destinations_list.num_elements * sizeof(int));
    
    int overlap_index;
    for (overlap_index=0;overlap_index < overlap_list.num_elements;overlap_index++)
        Volumes[N_volume]->geometry.destinations_list.elements[overlap_index] = overlap_list.elements[overlap_index];
    
    // sprintf(string_output,"Destination list for Volume %d step 6",N_volume);
    // print_1d_int_list(Volumes[N_volume]->geometry.destinations_list,string_output);
    
    if (overlap_list.num_elements>0) free(overlap_list.elements);
    */
    
    };

void generate_destinations_lists(struct pointer_to_1d_int_list **grandparents_lists, struct pointer_to_1d_int_list **parents_lists, struct pointer_to_1d_int_list **overlap_lists,struct Volume_struct **Volumes, int number_of_volumes, int verbal) {
    // Because of the complexity of the algortithm for generating the destinations list, the function is made for a single volume at the time to keep the notation simpler
    // This funtion runs the destinations list function for each volume
    MPI_MASTER(
    if (verbal) printf("\nGenerating destinations lists ----------------------- \n");
    )
    
    int volume_index;
    for (volume_index = 0;volume_index < number_of_volumes;volume_index++) {
      generate_destinations_list(volume_index,Volumes,*overlap_lists[volume_index],parents_lists[volume_index],grandparents_lists[volume_index]);
      
      char string_output[128];
      if (verbal) sprintf(string_output,"Destinations list for Volume %d",volume_index);
      MPI_MASTER(
      if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.destinations_list,string_output);
      )
    }
};

/* OBSOLETE
void generate_destinations_logic_lists(struct Volume_struct **Volumes,int number_of_volumes,int verbal) {
  // The destinations logic list is another way to store the destinations list that makes some tasks quicker
  
  MPI_MASTER(
  if (verbal) printf("\nGenerating destinations logic lists ----------------------- \n");
  )

  int volume_index;
  for (volume_index = 0;volume_index < number_of_volumes;volume_index++) {
      allocate_logic_list_from_temp(number_of_volumes,Volumes[volume_index]->geometry.destinations_list,&Volumes[volume_index]->geometry.destinations_logic_list);
      
      char string_output[128];
      MPI_MASTER(
      if (verbal) sprintf(string_output,"Destinations logic list for Volume %d",volume_index);
      if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.destinations_logic_list,string_output);
      )
  }

};
*/

void generate_reduced_destinations_lists(struct pointer_to_1d_int_list **parents_lists, struct Volume_struct **Volumes,int number_of_volumes,int verbal) {
    // The reduced destination list is the destination list of a volume, where each element that has a parent on the same destination list is removed
    // This list is to be fed to the which_volume function, as this funtion will automatically search through the direct children in a tree like manner
    // The optimization reduces the number of calculations of within functions in nested geometries.
    
    MPI_MASTER(
    if (verbal) printf("\nGenerating reduced destination lists ----------------------- \n");
    )
    
    struct pointer_to_1d_int_list logic_list;
  
    int volume_index,checked_dest_index,checked_dest_volume,rest_dest_index,rest_dest_volume,dest_index,iterate;
    for (volume_index = 0;volume_index < number_of_volumes;volume_index++) {
      
      //printf("Generating reduced destinations lists for volume %d\n",volume_index);
      logic_list.num_elements = Volumes[volume_index]->geometry.destinations_list.num_elements;
      logic_list.elements = malloc( (int) logic_list.num_elements * sizeof(int));
      //printf("Sucsessfully allocated space for %d integers in logic list %d\n",logic_list.num_elements,volume_index);
      for (iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 1;
      
      for (checked_dest_index=0;checked_dest_index<Volumes[volume_index]->geometry.destinations_list.num_elements;checked_dest_index++) {
            checked_dest_volume = Volumes[volume_index]->geometry.destinations_list.elements[checked_dest_index];
            for (rest_dest_index=0;rest_dest_index<Volumes[volume_index]->geometry.destinations_list.num_elements;rest_dest_index++) {
                rest_dest_volume = Volumes[volume_index]->geometry.destinations_list.elements[rest_dest_index];
                // As every volume has 0 as a parent, these are ignored. It would work to include this, but would add an extra trivial step to within_which_volume
                if (rest_dest_volume != 0) {
                    if (on_int_list(*parents_lists[checked_dest_volume],rest_dest_volume) == 1) {
                        // In this case, do not include element checked_dest_index on the reduced destinations list
                        // ADD mask check
                        if (Volumes[rest_dest_volume]->geometry.is_masked_volume == 0) {
                          logic_list.elements[checked_dest_index] = 0;
                        }
                    }
                }
            }
      }
      
      Volumes[volume_index]->geometry.reduced_destinations_list.num_elements = sum_int_list(logic_list);
      Volumes[volume_index]->geometry.reduced_destinations_list.elements = malloc((int)Volumes[volume_index]->geometry.reduced_destinations_list.num_elements * sizeof(int));
      
      iterate = 0;
      for (dest_index = 0;dest_index < Volumes[volume_index]->geometry.destinations_list.num_elements;dest_index++) {
            if (logic_list.elements[dest_index] == 1)   Volumes[volume_index]->geometry.reduced_destinations_list.elements[iterate++] = Volumes[volume_index]->geometry.destinations_list.elements[dest_index];
      }
      
      free(logic_list.elements);
      
      // Testing an optimization
      remove_element_in_list_by_value(&Volumes[volume_index]->geometry.reduced_destinations_list,0);
      
      char string_output[128];
      MPI_MASTER(
      if (verbal) sprintf(string_output,"Reduced destinations list for Volume %d",volume_index);
      if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.reduced_destinations_list,string_output);
      )
    }
};

void generate_direct_children_lists(struct pointer_to_1d_int_list **parents_lists, struct Volume_struct **Volumes,int number_of_volumes,int verbal) {

  MPI_MASTER(
  if (verbal) printf("\nGenerating direct children lists ----------------------- \n");
  )
  // A direct children of volume n is a volume that is a child of n, but no other child of n is its parent
  
  // Mask update: Need to check that this step does not bug out when the mask system interferes with child/parent systems
  
  struct pointer_to_1d_int_list logic_list;
  int volume_index,child,parent,iterate;
  for (volume_index = 0;volume_index < number_of_volumes;volume_index++) {
        // Temp elements is used, and its actual number of elements is edited even though the memory allocated is not changed. This is so that the list functions handles it correctly.
        // The free function will free all the allocated memory regardless of the value of the .num_elements structure field, it is just there for convinience.
      
        logic_list.num_elements = Volumes[volume_index]->geometry.children.num_elements;
        logic_list.elements = malloc(logic_list.num_elements * sizeof(int));
        for (iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 1;
      
        // Look through each child of volume n, and for each of those look through all the other children of n and search for one of them being a parent to the child
        for (child=0;child<Volumes[volume_index]->geometry.children.num_elements;child++) {
            for (parent=0;parent<parents_lists[Volumes[volume_index]->geometry.children.elements[child]]->num_elements;parent++) {
                if (on_int_list(Volumes[volume_index]->geometry.children,parents_lists[Volumes[volume_index]->geometry.children.elements[child]]->elements[parent]))
                    // If such a parent is found, remove that child from the list
                    logic_list.elements[child] = 0;
            }
        }
      
      Volumes[volume_index]->geometry.direct_children.num_elements = sum_int_list(logic_list);
      Volumes[volume_index]->geometry.direct_children.elements = malloc(Volumes[volume_index]->geometry.direct_children.num_elements*sizeof(int));
      
      iterate = 0;
      for (child = 0;child < Volumes[volume_index]->geometry.children.num_elements;child++) {
            if (logic_list.elements[child])   Volumes[volume_index]->geometry.direct_children.elements[iterate++] = Volumes[volume_index]->geometry.children.elements[child];
      }
      // Be careful with names in both main and a function, as they are automatically declared as external variables, which would then also free the main.
      free(logic_list.elements);
      
      char string_output[128];
      MPI_MASTER(
      if (verbal) sprintf(string_output,"Children list for Volume        %d",volume_index);
      if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.children,string_output);
      if (verbal) sprintf(string_output,"Direct_children list for Volume %d",volume_index);
      if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.direct_children,string_output);
      )
      
  }

};

void generate_starting_logic_list(struct starting_lists_struct *starting_lists, struct Volume_struct **Volumes, int number_of_volumes, int verbal) {
    // Function for generating logic list of volumes the ray can start in without an error.
    // Start with a list of all vacuum volumes
    // Remove all volumes that are children of non-vacuum volumes
    // It is still possible to have a volume on this list that is surrounded by non-vacuum volumes, but it is hard to detect these situations,
    //  meaning that it is ultimately partly the users responsibility to not send neutrons directly into materials.
    
    int volume_index,*start,*check;
    
    struct pointer_to_1d_int_list temp_list_local;
    temp_list_local.num_elements = number_of_volumes;
    temp_list_local.elements = malloc(number_of_volumes*sizeof(int));
    
    //if (verbal==1) printf("sucessfully allocated temp_list_local.elements, now to enter which volumes are vacuums as a logic list\n");
    temp_list_local.elements[0] = 1; // Volume 0 is a vacuum volume.
    for (volume_index = 1;volume_index < number_of_volumes;volume_index++) {
        if (Volumes[volume_index]->p_physics->is_vacuum == 1) temp_list_local.elements[volume_index] = 1;
        else temp_list_local.elements[volume_index] = 0;
    }
    // temp_list_local is now a logic list of all vacuum volumes
    //if (verbal==1) printf("list of vacuum volumes done, now to remove the children of non-vcauum volumes\n");
    for (volume_index = 1;volume_index < number_of_volumes;volume_index++) { // All volumes ...
        if (temp_list_local.elements[volume_index] == 0) { // ... that are not vacuum ...
            for (start = check = Volumes[volume_index]->geometry.children.elements;check - start < Volumes[volume_index]->geometry.children.num_elements;check++) { // ... have all their children ...
                temp_list_local.elements[*check] = 0; // .. removed from the allowed_start_logic_list
            }
        }
    }
    //if (verbal==1) printf("sucessfully removed children of non-vacuum volumes, now allocate allowed_start_logic_list\n");
    allocate_list_from_temp(number_of_volumes,temp_list_local,&starting_lists->allowed_starting_volume_logic_list);
    //if (verbal==1) printf("sucsessfully allocated allowed_start_logic_list, now freeing temp_list_local.elements. \n");
    
    free(temp_list_local.elements);
    //if (verbal==1) printf("sucessfully freed temp_list_local.elements, generate starting lists done\n");
    
    char string_output[128];
    MPI_MASTER(
    if (verbal) sprintf(string_output,"Allowed starting volume logic list");
    if (verbal) print_1d_int_list(starting_lists->allowed_starting_volume_logic_list,string_output);
    )
    
    
};

void generate_reduced_starting_destinations_list(struct starting_lists_struct *starting_lists, struct pointer_to_1d_int_list **parents_lists, struct Volume_struct **Volumes,int number_of_volumes,int verbal) {
    // The starting_destinations_list is trivial, as it contains all volumes that are not masks.


    struct pointer_to_1d_int_list logic_list;
    //printf("Generating reduced destinations lists for volume %d\n",volume_index);
    logic_list.num_elements = number_of_volumes;
    logic_list.elements = malloc( (int) logic_list.num_elements * sizeof(int));
    int iterate;
    for (iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 0;
    
    
    for (iterate=1;iterate<number_of_volumes;iterate++)
      if (Volumes[iterate]->geometry.is_mask_volume == 0) logic_list.elements[iterate] = 1;
      //else logic_list.elements[iterate] = 0;
    
    starting_lists->starting_destinations_list.num_elements = sum_int_list(logic_list);
    starting_lists->starting_destinations_list.elements = malloc(starting_lists->starting_destinations_list.num_elements*sizeof(int));
    
    int used_elements=0;
    for (iterate=1;iterate<number_of_volumes;iterate++)
      if (logic_list.elements[iterate] == 1) starting_lists->starting_destinations_list.elements[used_elements++] = iterate;
    
    MPI_MASTER(
    if (verbal) printf("\nGenerating start destinations list ------------------------------ \n");
    if (verbal) print_1d_int_list(starting_lists->starting_destinations_list,"Starting destinations list");
    )

    // The reduced starting destination list is used when a ray enters the component in the search for which volume it starts in.
    // It facilitates the same optimization as the regular destination list.
    // The start logic list is also generated, as it is very simple and does not need a seperate function
    
    // Mask update: Need to remove mask volumes from the reduced starting destination list
    
    MPI_MASTER(
    if (verbal) printf("\nGenerating reduced start destination list ----------------------- \n");
    )
    
    int checked_dest_index,checked_dest_volume,rest_dest_index,rest_dest_volume,dest_index;
    
    /* Old version before masks were introduced
    struct pointer_to_1d_int_list starting_dest_list;
    
    starting_dest_list.num_elements = number_of_volumes - 1;
    starting_dest_list.elements = malloc ( starting_dest_list.num_elements * sizeof(int));
    for (iterate=0;iterate<number_of_volumes-1;iterate++) starting_dest_list.elements[iterate] = iterate + 1; // All volumes to be checked for starting
    */
    
    logic_list.num_elements = starting_lists->starting_destinations_list.num_elements;
    free(logic_list.elements);
    logic_list.elements = malloc( (int) logic_list.num_elements * sizeof(int));
    
    //printf("Sucsessfully allocated space for %d integers in logic list %d\n",logic_list.num_elements,volume_index);
    for (iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 1;
  
    for (checked_dest_index=0;checked_dest_index<starting_lists->starting_destinations_list.num_elements;checked_dest_index++) {
        checked_dest_volume = starting_lists->starting_destinations_list.elements[checked_dest_index];
        for (rest_dest_index=0;rest_dest_index<starting_lists->starting_destinations_list.num_elements;rest_dest_index++) {
            rest_dest_volume = starting_lists->starting_destinations_list.elements[rest_dest_index];
            // As every volume has 0 as a parent, these are ignored. It would work to include this, but would add an extra trivial step to within_which_volume
            if (rest_dest_volume != 0) {
                if (on_int_list(*parents_lists[checked_dest_volume],rest_dest_volume) == 1) {
                    // In this case, do not include element checked_dest_index on the reduced destinations list
                    logic_list.elements[checked_dest_index] = 0;
                }
            }
        }
  }
  
  starting_lists->reduced_start_list.num_elements = sum_int_list(logic_list);
  starting_lists->reduced_start_list.elements = malloc((int)starting_lists->reduced_start_list.num_elements * sizeof(int));
  
  iterate = 0;
  for (dest_index = 0;dest_index < starting_lists->starting_destinations_list.num_elements;dest_index++) {
        if (logic_list.elements[dest_index] == 1) starting_lists->reduced_start_list.elements[iterate++] = starting_lists->starting_destinations_list.elements[dest_index];
  }
  
  free(logic_list.elements);
  //free(starting_dest_list.elements);
  
  MPI_MASTER(
  if (verbal) print_1d_int_list(starting_lists->reduced_start_list,"Reduced start destinations list");
  )
    
  // Making the start_logic_list.
  starting_lists->start_logic_list.num_elements = number_of_volumes;
  starting_lists->start_logic_list.elements = malloc ( starting_lists->start_logic_list.num_elements * sizeof(int));
  starting_lists->start_logic_list.elements[0] = 0;
  for (iterate=1;iterate<number_of_volumes;iterate++) starting_lists->start_logic_list.elements[iterate] = 1; // All volumes to be checked for starting volume
  MPI_MASTER(
  if (verbal) print_1d_int_list(starting_lists->start_logic_list,"Start logic list");
  )
};


void generate_next_volume_list(struct Volume_struct **Volumes, int number_of_volumes, int verbal) {
    // Generate list of volumes that can be the next volume which the ray enters. It is used for tagging, not the simulation / propagation

    // Mask update: These lists should be done as if all mask statuses are on, meaning they include all possible next volumes (will include more input to this function)
    
    // bug: next volume list is not complete and contains duplicates

    MPI_MASTER(
    if (verbal) printf("\nGenerating next volume list ------------------------------------- \n");
    )
    // Merge destinations list, intersection list and mask_intersection_list
    int volume_index,iterate,mask_index;
    struct pointer_to_1d_int_list full_intersection_list;
    full_intersection_list.num_elements=0;
    
    for (volume_index=0;volume_index<number_of_volumes;volume_index++) {
        Volumes[volume_index]->geometry.next_volume_list.num_elements = 0;
        // Before mask update
        //merge_lists(&Volumes[volume_index]->geometry.next_volume_list, &Volumes[volume_index]->geometry.destinations_list, &Volumes[volume_index]->geometry.intersect_check_list);
        
        merge_lists(&full_intersection_list, &Volumes[volume_index]->geometry.mask_intersect_list, &Volumes[volume_index]->geometry.intersect_check_list);
        merge_lists(&Volumes[volume_index]->geometry.next_volume_list,&Volumes[volume_index]->geometry.destinations_list,&full_intersection_list);
        
        /* // This complication is taken into account by adding the mask_intersect list instead
        // It is possible that the next volume is still not on this list, as when masks are encountered the next volume can be a volume they mask.
        // For each on the list, add the volumes masked by that mask (do not iterate over this, as masks can not be masked regardless)
        for (iterate=Volumes[volume_index]->geometry.next_volume_list.num_elements-1;iterate>-1;iterate--) {
          if (Volumes[Volumes[volume_index]->geometry.next_volume_list.elements[iterate]]->geometry.is_mask_volume == 1) {
            for (mask_index=0;mask_index<Volumes[Volumes[volume_index]->geometry.next_volume_list.elements[iterate]]->geometry.mask_list.num_elements;mask_index++) {
              add_element_to_int_list(&Volumes[volume_index]->geometry.next_volume_list,Volumes[Volumes[volume_index]->geometry.next_volume_list.elements[iterate]]->geometry.mask_list.elements[mask_index]);
            }
          }
        }
        */
        
        // Remove mask volumes from the next volume list, as they never occur as current_volume, and thus just create dead branches that takes up memory
        for (iterate=Volumes[volume_index]->geometry.next_volume_list.num_elements-1;iterate>-1;iterate--)
          if (Volumes[Volumes[volume_index]->geometry.next_volume_list.elements[iterate]]->geometry.is_mask_volume == 1)
            remove_element_in_list_by_index(&Volumes[volume_index]->geometry.next_volume_list,iterate);
        
        
        if (full_intersection_list.num_elements>0) free(full_intersection_list.elements);
        full_intersection_list.num_elements=0;
        
        char string_output[128];
        MPI_MASTER(
        if (verbal) sprintf(string_output,"Next volume list                %d",volume_index);
        if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.next_volume_list,string_output);
        )
    }
};

void generate_lists(struct Volume_struct **Volumes, struct starting_lists_struct *starting_lists, int number_of_volumes, int verbal) {
    // Function to control the generation of lists
    // Some lists are only needed temporary, and are thus declared here to keep them out of the main scope
    // Others are stored in the volume structs as they are needed in the trace algorithm (or tagging)
    
    
    struct pointer_to_1d_int_list **true_children_lists;
    true_children_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    // generate_children_lists both generate the normal children list for each volume, but also the true children list needed locally.
    generate_children_lists(Volumes, true_children_lists, number_of_volumes,verbal);
    
    
    struct pointer_to_1d_int_list **true_overlap_lists;
    true_overlap_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    struct pointer_to_1d_int_list **raw_overlap_lists;
    raw_overlap_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    
    generate_overlap_lists(true_overlap_lists, raw_overlap_lists, Volumes,number_of_volumes,verbal);
    
    
    //generate_intersect_check_lists(true_overlap_lists, Volumes, number_of_volumes, verbal);
    
    
    struct pointer_to_1d_int_list **parents_lists;
    parents_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    generate_parents_lists(parents_lists,Volumes,number_of_volumes,verbal,1); // The last 1 means masks are taken into account
    
    // Generate version of parent list as it would be without masks
    struct pointer_to_1d_int_list **parents_lists_no_masks;
    parents_lists_no_masks = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    generate_parents_lists(parents_lists_no_masks,Volumes,number_of_volumes,verbal,0); // The last 0 means masks are NOT taken into account
    
    // Generate version of parent list using true_children instead
    struct pointer_to_1d_int_list **true_parents_lists;
    true_parents_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    generate_true_parents_lists(true_parents_lists, true_children_lists, Volumes, number_of_volumes, verbal, 1);
    
    // Generate version of parent list no masks using true_children instead
    struct pointer_to_1d_int_list **true_parents_lists_no_masks;
    true_parents_lists_no_masks = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    generate_true_parents_lists(true_parents_lists_no_masks, true_children_lists, Volumes, number_of_volumes, verbal, 0);
    
    // New version of generate intersect lists
    generate_intersect_check_lists_experimental(true_overlap_lists, raw_overlap_lists, parents_lists, true_parents_lists, Volumes, number_of_volumes, verbal);
    
    struct pointer_to_1d_int_list **grandparents_lists;
    grandparents_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    generate_grandparents_lists(grandparents_lists,parents_lists,number_of_volumes,verbal);
    
    // Generate version of grandparents list as it would have been if no masks were defined
    struct pointer_to_1d_int_list **grandparents_lists_no_masks;
    grandparents_lists_no_masks = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    generate_grandparents_lists(grandparents_lists_no_masks,parents_lists_no_masks,number_of_volumes,verbal);
    
    // Generate true_grandparents_lists
    struct pointer_to_1d_int_list **true_grandparents_lists;
    true_grandparents_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    generate_grandparents_lists(true_grandparents_lists,true_parents_lists,number_of_volumes,verbal);
    
    struct pointer_to_1d_int_list **true_grandparents_lists_no_masks;
    true_grandparents_lists_no_masks = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    generate_grandparents_lists(true_grandparents_lists_no_masks,true_parents_lists_no_masks,number_of_volumes,verbal);
    
    // The destinations lists are generated without taking masks into account (they are removed from the overlap list in an early step)
    //generate_destinations_lists(grandparents_lists_no_masks,parents_lists_no_masks,true_overlap_lists,Volumes,number_of_volumes,verbal);
    generate_destinations_lists_experimental(true_overlap_lists, true_children_lists, true_parents_lists_no_masks, true_grandparents_lists_no_masks, Volumes, number_of_volumes, verbal);
    
    // Obsolete, found a way around them in within_which_volume, but need to test the performance difference
    // generate_destinations_logic_lists(Volumes,number_of_volumes,verbal);
    
    generate_reduced_destinations_lists(parents_lists,Volumes,number_of_volumes,verbal);
    
    generate_direct_children_lists(parents_lists,Volumes,number_of_volumes,verbal);
    
    //generate_starting_list(starting_lists,Volumes,number_of_volumes,verbal);
    generate_starting_logic_list(starting_lists,Volumes,number_of_volumes,verbal);
    
    generate_reduced_starting_destinations_list(starting_lists,parents_lists,Volumes,number_of_volumes,verbal);
    
    // This list is stored with the volumes for convinience, but is only used for tagging
    generate_next_volume_list(Volumes,number_of_volumes,verbal);
    
    // Garbage collection for temporary dynamically allocated lists. (Permanent lists freed from FINALLY)
    int iterate;
    for (iterate=0;iterate<number_of_volumes;iterate++) {
        //printf("freeing for volume nr %d\n",iterate);
        //printf("true_overlap_lists[iterate]->num_elements = %d \n",true_overlap_lists[iterate]->num_elements);
        if (true_overlap_lists[iterate]->num_elements > 0) free(true_overlap_lists[iterate]->elements);
        free(true_overlap_lists[iterate]);
        
        //printf("raw_overlap_lists[iterate]->num_elements = %d \n",raw_overlap_lists[iterate]->num_elements);
        if (raw_overlap_lists[iterate]->num_elements > 0) free(raw_overlap_lists[iterate]->elements);
        free(raw_overlap_lists[iterate]);
        
        //printf("parents_lists[iterate]->num_elements = %d \n",parents_lists[iterate]->num_elements);
        if (parents_lists[iterate]->num_elements > 0) free(parents_lists[iterate]->elements);
        free(parents_lists[iterate]);
        
        //printf("parents_lists_no_masks[iterate]->num_elements = %d \n",parents_lists_no_masks[iterate]->num_elements);
        if (parents_lists_no_masks[iterate]->num_elements > 0) free(parents_lists_no_masks[iterate]->elements);
        free(parents_lists_no_masks[iterate]);
        
        //printf("true_parents_lists[iterate]->num_elements = %d \n",true_parents_lists[iterate]->num_elements);
        if (true_parents_lists[iterate]->num_elements > 0) free(true_parents_lists[iterate]->elements);
        free(true_parents_lists[iterate]);
        
        //printf("true_parents_lists_no_masks[iterate]->num_elements = %d \n",true_parents_lists_no_masks[iterate]->num_elements);
        if (true_parents_lists_no_masks[iterate]->num_elements > 0) free(true_parents_lists_no_masks[iterate]->elements);
        free(true_parents_lists_no_masks[iterate]);
        
        //printf("grandparents_lists[iterate]->num_elements = %d \n",grandparents_lists[iterate]->num_elements);
        if (grandparents_lists[iterate]->num_elements > 0) free(grandparents_lists[iterate]->elements);
        free(grandparents_lists[iterate]);
        
        //printf("true_grandparents_lists[iterate]->num_elements = %d \n",true_grandparents_lists[iterate]->num_elements);
        if (true_grandparents_lists[iterate]->num_elements > 0) free(true_grandparents_lists[iterate]->elements);
        free(true_grandparents_lists[iterate]);
        
        //printf("grandparents_lists_no_masks[iterate]->num_elements = %d \n",grandparents_lists_no_masks[iterate]->num_elements);
        if (grandparents_lists_no_masks[iterate]->num_elements > 0) free(grandparents_lists_no_masks[iterate]->elements);
        free(grandparents_lists_no_masks[iterate]);
        
        //printf("true_grandparents_lists_no_masks[iterate]->num_elements = %d \n",true_grandparents_lists_no_masks[iterate]->num_elements);
        if (true_grandparents_lists_no_masks[iterate]->num_elements > 0) free(true_grandparents_lists_no_masks[iterate]->elements);
        free(true_grandparents_lists_no_masks[iterate]);
        
        //printf("true_children_lists[iterate]->num_elements = %d \n",true_children_lists[iterate]->num_elements);
        if (true_children_lists[iterate]->num_elements > 0) free(true_children_lists[iterate]->elements);
        free(true_children_lists[iterate]);
    }
    //printf("generate lists volume specific free completed\n");
    free(true_overlap_lists);free(raw_overlap_lists);free(parents_lists);free(true_parents_lists);free(true_parents_lists_no_masks);
    free(parents_lists_no_masks);free(true_grandparents_lists);free(grandparents_lists);free(grandparents_lists_no_masks);free(true_grandparents_lists_no_masks);
    free(true_children_lists);
    //printf("generate lists free completed\n");
};

// -------------    Focusing functions   --------------------------------------------------------

// The focusing_data structure is set up by the geometry component, and a pointer to the appropriate
//  focusing function is added to the Volume structure (for this reason the input of all the functions
//  need to be identical, at least in terms of types). In this way there are no if statements to check
//  which of these to be used in the trace, but the focus_data_struct will carry some redundant
//  information, as only the appropriate parameters are set:
// Angular focus on a rectangle (angular_focus_height / angular_focus_width)
// Spatial focus on a rectangle (spatial_focus_height / spatial_focus_width)
// Spatial focus on a disk (sptial_focus_radius)
// No focus (randvec in 4pi) (all set to zero, will select randvec circle as it is slightly faster
//
// When adding a new physical process focusing becomes very easy, as one just calls the master focusing
//  function assosiated with the volume (placed in the geometry struct), using the focus_data_struct
//  also found in the geometry struct, and the process then supports all the focusing modes. It is even
//  possible to add new focusing modes in the future by updating just the geometry components, and this
//  section.

// focus_data_struct definitioon shown here, defined at the start of this file
//struct focus_data_struct {
//Coords Aim;
//double angular_focus_width;
//double angular_focus_height;
//double spatial_focus_width;
//double spatial_focus_height;
//double spatial_focus_radius;
//Rotation absolute_rotation;
//// focusing_function creates a vector per selected criteria of focus_data_struct / selected focus function and returns solid angle
//void (*focusing_function)(Coords*, double*, struct focus_data_struct*);
////                        v_out  , solid_a,
//};

void randvec_target_rect_angular_union(Coords *v_out,double *solid_angle_out, struct focus_data_struct *focus_data) {
    // Calls the standard McStas randvec_target_rect_angular focusing function, but is with the new data input format.
    randvec_target_rect_angular(&v_out->x, &v_out->y, &v_out->z, solid_angle_out, focus_data->Aim.x,focus_data->Aim.y, focus_data->Aim.z, focus_data->angular_focus_width, focus_data->angular_focus_height,focus_data->absolute_rotation);
    //randvec_target_rect_angular(&vx, &vy, &vz, &solid_angle,aim_x, aim_y, aim_z, VarsInc.aw, VarsInc.ah, ROT_A_CURRENT_COMP);
};

void randvec_target_rect_union(Coords *v_out,double *solid_angle_out, struct focus_data_struct *focus_data) {
// Calls the standard McStas randvec_target_rect focusing function, but is with the new data input format.
    randvec_target_rect(&v_out->x, &v_out->y, &v_out->z, solid_angle_out, focus_data->Aim.x,focus_data->Aim.y, focus_data->Aim.z, focus_data->spatial_focus_width, focus_data->spatial_focus_height,focus_data->absolute_rotation);
    // randvec_target_rect(&vx, &vy, &vz, &solid_angle,aim_x, aim_y, aim_z, VarsInc.xw, VarsInc.yh, ROT_A_CURRENT_COMP);
};

void randvec_target_circle_union(Coords *v_out,double *solid_angle_out, struct focus_data_struct *focus_data) {
// Calls the standard McStas randvec_target_circle focusing function, but is with the new data input format.

    // debug input into randvec_target_circle
    //print_position(focus_data->Aim,"Aim vector input for randvec_target_circle");
    //printf("Radius input %f\n",focus_data->spatial_focus_radius);

    randvec_target_circle(&v_out->x, &v_out->y, &v_out->z, solid_angle_out, focus_data->Aim.x,focus_data->Aim.y, focus_data->Aim.z, focus_data->spatial_focus_radius);
    //randvec_target_circle(&vx, &vy, &vz, &solid_angle, aim_x, aim_y, aim_z, focus_r);
};



void focus_initialize(struct geometry_struct *geometry, Coords POS_A_TARGET, Coords POS_A_CURRENT, Rotation ROT_A_CURRENT, int target_index, double target_x, double target_y, double target_z, double angular_focus_width, double angular_focus_height, double spatial_focus_width, double spatial_focus_height, double spatial_focus_radius, char *component_name) {
    // Initialize focusing system
    // target_x/y/z needs to be double setting parameters with default value 0
    // target_index needs to be int setting parameter with default value 0
    // angular_focus_width, angular_focus_height, spatial_focus_width, spatial_focus_height, spatial_focus_radius nneds to be double setting parameters with default value 0
    // When those conditions are met, this code will identify which settings have been entered by the user and select the appropriate focusing parameters, which are loaded into the focus_data struct and the geometry struct.
    // The aim vector in the focus_data struct will be transformed from the local coordinate system of the geometry component to the coordinate system of the master component during the master component initialize

  // Input sanitation
  if (angular_focus_width < 0) {
    printf("\nERROR in Union geometry component named \"%s\", angular focus width focus_aw < 0! \n",component_name);
    exit(1);
  }
  if (angular_focus_height < 0) {
    printf("\nERROR in Union geometry component named \"%s\", angular focus width focus_ah < 0! \n",component_name);
    exit(1);
  }
  if (spatial_focus_width < 0) {
    printf("\nERROR in Union geometry component named \"%s\", spatial focus width focus_xw < 0! \n",component_name);
    exit(1);
  }
  if (spatial_focus_height < 0) {
    printf("\nERROR in Union geometry component named \"%s\", spatial focus height focus_xh < 0! \n",component_name);
    exit(1);
  }
  if (spatial_focus_radius < 0) {
    printf("\nERROR in Union geometry component named \"%s\", spatial focus radius focus_r < 0! \n",component_name);
    exit(1);
  }
  
  struct focus_data_struct focus_data;

  // Initialize focus_data_struct
  /*
  geometry->focus_data.Aim = coords_set(0,0,0);
  geometry->focus_data.angular_focus_width = 0;
  geometry->focus_data.angular_focus_height = 0;
  geometry->focus_data.spatial_focus_width = 0;
  geometry->focus_data.spatial_focus_height = 0;
  geometry->focus_data.spatial_focus_radius = 0;
  rot_copy(geometry->focus_data.absolute_rotation,ROT_A_CURRENT);
  */
  focus_data.Aim = coords_set(0,0,0);
  focus_data.angular_focus_width = 0;
  focus_data.angular_focus_height = 0;
  focus_data.spatial_focus_width = 0;
  focus_data.spatial_focus_height = 0;
  focus_data.spatial_focus_radius = 0;
  rot_copy(focus_data.absolute_rotation,ROT_A_CURRENT);

  // Built on code from Incoherent.comp by Kim Lefmann and Kristian Nielsen
  if (target_index != 0 && !target_x && !target_y && !target_z)
  {
    Coords ToTarget;
    //ToTarget = coords_sub(POS_A_COMP_INDEX(INDEX_CURRENT_COMP+target_index),POS_A_CURRENT_COMP);
    ToTarget = coords_sub(POS_A_TARGET,POS_A_CURRENT);
    //ToTarget = rot_apply(ROT_A_CURRENT_COMP, ToTarget);
    ToTarget = rot_apply(ROT_A_CURRENT, ToTarget);
    coords_get(ToTarget, &focus_data.Aim.x, &focus_data.Aim.y, &focus_data.Aim.z);
  }
  else
  { focus_data.Aim.x = target_x; focus_data.Aim.y = target_y; focus_data.Aim.z = target_z; }
  
  if (!(focus_data.Aim.x || focus_data.Aim.y || focus_data.Aim.z)) {
    // Somehow set a variable to signify scattering into 4pi
    // printf("Union %s: The target is not defined. Using scattering into 4pi.\n",NAME_CURRENT_COMP);
    focus_data.Aim.z=1; // set aim to one so that the randvec output vector has length 1 instead of 0
  }

  int focusing_model_selected = 0;
  if (angular_focus_width != 0 && angular_focus_height != 0) {
    focus_data.focusing_function = &randvec_target_rect_angular_union;
    focus_data.angular_focus_width = DEG2RAD*angular_focus_width; // Convert to radians here
    focus_data.angular_focus_height = DEG2RAD*angular_focus_height;
    focusing_model_selected = 1;
  }
  if (spatial_focus_width != 0 && spatial_focus_height != 0) {
    focus_data.focusing_function = &randvec_target_rect_union;
    focus_data.spatial_focus_width = spatial_focus_width;
    focus_data.spatial_focus_height = spatial_focus_height;
    if (focusing_model_selected) {
        printf("ERROR %s: Select either angular or spatial focusing, not both! Exiting \n",component_name);
        exit(1);
    }
    focusing_model_selected = 1;
  }
  if (spatial_focus_radius != 0) {
    focus_data.focusing_function = &randvec_target_circle_union;
    focus_data.spatial_focus_radius = spatial_focus_radius;
    if (focusing_model_selected) {
        printf("ERROR %s: Select a maximum of one focusing method (spatial rectangle or cicle, or angular rectangle! Exiting \n",component_name);
        exit(1);
    }
    focusing_model_selected = 1;
  }
  if (focusing_model_selected == 0) {
    // Select 4pi focusing
    focus_data.spatial_focus_radius = 0;
    focus_data.focusing_function = &randvec_target_circle_union;
  }
  
  // Allocate the isotropic focus_data struct
  geometry->focus_data_array.num_elements = 0;
  //geometry->focus_data_array.elements = malloc(sizeof(focus_data_struct));
  add_element_to_focus_data_array(&geometry->focus_data_array,focus_data);
};


struct abs_event{
    double time1;
    double position1[3];
    double time2;
    double position2[3];
    double weight_change;
    int volume_index;
    int neutron_id;
};

// Functions for recording absorption
void initialize_absorption_file() {
  FILE *fp;
  fp = fopen("Union_absorption.dat","w");
  fprintf(fp,"r_old x, r_old y, r_old z, old t, r x, r y, r z, new t, weight change, volume index, neutron id \n");
  
  fclose(fp);
}

void write_events_to_file(int last_index, struct abs_event *events) {

  FILE *fp;
  fp = fopen("Union_absorption.dat","a");
  
  struct abs_event *this_event;
  int iterate;
  for (iterate=0; iterate<last_index; iterate++) {
  
    this_event = &events[iterate];
    fprintf(fp,"%g, %g, %g, %g, %g, %g, %g, %g, %e, %i, %i \n",
           this_event->position1[0], this_event->position1[1], this_event->position1[2], this_event->time1,
           this_event->position2[0], this_event->position2[1], this_event->position2[2], this_event->time2,
           this_event->weight_change,
           this_event->volume_index,
           this_event->neutron_id);
  }

  fclose(fp);
}

void record_abs_to_file(double *r, double t1, double *r_old, double t2, double weight_change, int volume, int neutron_id, int *data_index, struct abs_event *events) {
  
  
  
  struct abs_event *this_event;
  
  this_event = &events[(*data_index)++];
  
  //printf("Recording something! %i\n", *data_index);
  
  this_event->position1[0] = r[0];
  this_event->position1[1] = r[1];
  this_event->position1[2] = r[2];
  this_event->time1 = t1;
  this_event->position2[0] = r_old[0];
  this_event->position2[1] = r_old[1];
  this_event->position2[2] = r_old[2];
  this_event->time2 = t2;
  this_event->weight_change = weight_change;
  this_event->volume_index = volume;
  this_event->neutron_id = neutron_id;
  
  
  if (*data_index == 999) {
    
      write_events_to_file(*data_index, events);
      *data_index = 0;

  }

};




/*******************************************************************************
*
*  McStas, neutron ray-tracing package
*  Copyright(C) 2007 Risoe National Laboratory.
*
* %I
* Written by: Mads Bertelsen
* Date: 20.08.15
* Version: $Revision: 0.1 $
* Origin: University of Copenhagen
*
* A sample component to separate geometry and phsysics
*
* %D
* Alpha version, no input system yet
* Hardcode input to geometry engine
* Allows complicated geometry by combination of simple shapes
*
* Algorithm:
* Described elsewhere
*
* %P
* INPUT PARAMETERS:
* radius:  [m] Outer radius of sample in (x,z) plane
*
* OUTPUT PARAMETERS:
* V_rho:  [AA^-3] Atomic density
*
* %L
* The test/example instrument <a href="../examples/Test_Phonon.instr">Test_Phonon.instr</a>.
*
* %E
******************************************************************************/


// This file sets up global lists needed for Union components to communicate with each other
// These all have dynamically allocated memory somewhere in the structure, which is deallocated
//  by the last Union_master.



// Initialize global positions / rotations to transform lists
  // These are lists of pointers to positons / rotations, that will be updated from global frame
  //  to the frame of the master component that uses them in that masters initialize section.
  struct global_positions_to_transform_list_struct global_positions_to_transform_list = {0,NULL};
  struct global_rotations_to_transform_list_struct global_rotations_to_transform_list = {0,NULL};

// Initialize global_process_list
  // Used to facilitate communication between processes and the other types of Union components
  struct pointer_to_global_process_list global_process_list = {0,NULL};

// Initialize global_material_list
  // Used to facilitate communication between materials and the other types of Union components
  struct pointer_to_global_material_list global_material_list = {0,NULL};

// Initialize global_geometry_list
  // Used to facilitate communication between geometries and the other types of Union components
  struct pointer_to_global_geometry_list global_geometry_list = {0,NULL};

// Initialize global_logger_lists
  // Used to facilitate communication between loggers and the other types of Union components
  struct pointer_to_global_logger_list global_all_volume_logger_list = {0,NULL};
  struct pointer_to_global_logger_list global_specific_volumes_logger_list = {0,NULL};

// Initialize global_abs_logger_lists
  struct pointer_to_global_abs_logger_list global_all_volume_abs_logger_list = {0,NULL};
  struct pointer_to_global_abs_logger_list global_specific_volumes_abs_logger_list = {0,NULL};

// Initialize global_tagging_conditional_list
  // Used to facilitate communication between conditionals and the other types of Union components
  struct global_tagging_conditional_list_struct global_tagging_conditional_list = {0,0,NULL};

// Initialize global_master_list
  // Used to facilitate communication between Master components (mainly for deallocation)
  struct pointer_to_global_master_list global_master_list = {0,NULL};

// Initialize global_mantid_min_pixel_id
  // Used for ensuring pixel id's on Mantid monitors do not overlap
  int global_mantid_min_pixel_id = 0;


#endif




#ifndef SINGLE_CRYSTAL_PROCESS_DECL
#define SINGLE_CRYSTAL_PROCESS_DECL

#ifndef Mosaic_AB_Undefined
#define Mosaic_AB_Undefined {0,0, 0,0,0, 0,0,0}
#endif

    struct hkl_data_union
    {
      int h,k,l;                  /* Indices for this reflection */
      double F2;                  /* Value of structure factor */
      double tau_x, tau_y, tau_z; /* Coordinates in reciprocal space */
      double tau;                 /* Length of (tau_x, tau_y, tau_z) */
      double u1x, u1y, u1z;       /* First axis of local coordinate system */
      double u2x, u2y, u2z;       /* Second axis of local coordinate system */
      double u3x, u3y, u3z;       /* Third axis of local coordinate system */
      double sig123;              /* The product sig1*sig2*sig3 = volume of spot */
      double m1, m2, m3;          /* Diagonal matrix representation of Gauss */
      double cutoff;              /* Cutoff value for Gaussian tails */
    };
    
  struct tau_data_union
    {
      int index;                  /* Index into reflection table */
      double refl;
      double xsect;
      /* The following vectors are in local koordinates. */
      double rho_x, rho_y, rho_z; /* The vector ki - tau */
      double rho;                 /* Length of rho vector */
      double ox, oy, oz;          /* Origin of Ewald sphere tangent plane */
      double b1x, b1y, b1z;       /* Spanning vectors of Ewald sphere tangent */
      double b2x, b2y, b2z;
      double l11, l12, l22;       /* Cholesky decomposition L of 2D Gauss */
      double y0x, y0y;            /* 2D Gauss center in tangent plane */
    };

  struct hkl_info_struct_union
    {
      struct hkl_data_union *list;      /* Reflection array */
      int count;                  /* Number of reflections */
      struct tau_data_union *tau_list;  /* Reflections close to Ewald Sphere */
      double m_delta_d_d;         /* Delta-d/d FWHM */
      double m_ax,m_ay,m_az;      /* First unit cell axis (direct space, AA) */
      double m_bx,m_by,m_bz;      /* Second unit cell axis */
      double m_cx,m_cy,m_cz;      /* Third unit cell axis */
      double asx,asy,asz;         /* First reciprocal lattice axis (1/AA) */
      double bsx,bsy,bsz;         /* Second reciprocal lattice axis */
      double csx,csy,csz;         /* Third reciprocal lattice axis */
      double m_a, m_b, m_c;       /* length of lattice parameter lengths */
      double m_aa, m_bb, m_cc;    /* lattice angles */
      double sigma_a, sigma_i;    /* abs and inc X sect */
      double rho;                 /* density */
      double at_weight;           /* atomic weight */
      double at_nb;               /* nb of atoms in a cell */
      double V0;                  /* Unit cell volume (AA**3) */
      int    column_order[5];     /* column signification [h,k,l,F,F2] */
      int    recip;               /* Flag to indicate if recip or direct cell axes given */
      int    shape;               /* 0:cylinder, 1:box, 2:sphere 3:any shape*/
      int    flag_warning;        /* number of warnings */
      char   type;                /* type of last event: t=transmit,c=coherent or i=incoherent */
      int    h,k,l;               /* last coherent scattering momentum transfer indices */
      int    tau_count;           /* Number of reflections within cutoff */
      double coh_refl, coh_xsect; /* cross section computed with last tau_list */
      double kix, kiy, kiz;       /* last incoming neutron ki */
      int    nb_reuses, nb_refl, nb_refl_count;
    };

  int SX_list_compare_union (void const *a, void const *b)
  {
     struct hkl_data_union const *pa = a;
     struct hkl_data_union const *pb = b;
     double s = pa->tau - pb->tau;
     
     if (!s) return 0;
     else    return (s < 0 ? -1 : 1);
  } /* PN_list_compare */
  
  /* ------------------------------------------------------------------------ */
  int
  read_hkl_data_union(char *SC_file, struct hkl_info_struct_union *info,
      double SC_mosaic, double SC_mosaic_a, double SC_mosaic_b, double SC_mosaic_c, double *SC_mosaic_AB)
  {
    struct hkl_data_union *list = NULL;
    int size = 0;
    t_Table sTable; /* sample data table structure from SC_file */
    int i=0;
    double tmp_x, tmp_y, tmp_z;
    char **parsing;
    char flag=0;
    double nb_atoms=1;

    if (!SC_file || !strlen(SC_file) || !strcmp(SC_file,"NULL") || !strcmp(SC_file,"0")) {
      info->count = 0;
      flag=1;
    }
    if (!flag) {
      Table_Read(&sTable, SC_file, 1); /* read 1st block data from SC_file into sTable*/
      if (sTable.columns < 4) {
        fprintf(stderr, "Single_crystal: Error: The number of columns in %s should be at least %d for [h,k,l,F2]\n", SC_file, 4);
        return(0);
      }
      if (!sTable.rows) {
        fprintf(stderr, "Single_crystal: Error: The number of rows in %s should be at least %d\n", SC_file, 1);
        return(0);
      } else size = sTable.rows;

      /* parsing of header */
      parsing = Table_ParseHeader(sTable.header,
        "sigma_abs","sigma_a ",
        "sigma_inc","sigma_i ",
        "column_h",
        "column_k",
        "column_l",
        "column_F ",
        "column_F2",
        "Delta_d/d",
        "lattice_a ",
        "lattice_b ",
        "lattice_c ",
        "lattice_aa",
        "lattice_bb",
        "lattice_cc",
        "nb_atoms","multiplicity",
        NULL);

      if (parsing) {
        if (parsing[0] && !info->sigma_a) info->sigma_a=atof(parsing[0]);
        if (parsing[1] && !info->sigma_a) info->sigma_a=atof(parsing[1]);
        if (parsing[2] && !info->sigma_i) info->sigma_i=atof(parsing[2]);
        if (parsing[3] && !info->sigma_i) info->sigma_i=atof(parsing[3]);
        if (parsing[4])                   info->column_order[0]=atoi(parsing[4]);
        if (parsing[5])                   info->column_order[1]=atoi(parsing[5]);
        if (parsing[6])                   info->column_order[2]=atoi(parsing[6]);
        if (parsing[7])                   info->column_order[3]=atoi(parsing[7]);
        if (parsing[8])                   info->column_order[4]=atoi(parsing[8]);
        if (parsing[9] && info->m_delta_d_d <0) info->m_delta_d_d=atof(parsing[9]);
        if (parsing[10] && !info->m_a)    info->m_a =atof(parsing[10]);
        if (parsing[11] && !info->m_b)    info->m_b =atof(parsing[11]);
        if (parsing[12] && !info->m_c)    info->m_c =atof(parsing[12]);
        if (parsing[13] && !info->m_aa)   info->m_aa=atof(parsing[13]);
        if (parsing[14] && !info->m_bb)   info->m_bb=atof(parsing[14]);
        if (parsing[15] && !info->m_cc)   info->m_cc=atof(parsing[15]);
        if (parsing[16])   nb_atoms=atof(parsing[16]);
        if (parsing[17])   nb_atoms=atof(parsing[17]);
        for (i=0; i<=17; i++) if (parsing[i]) free(parsing[i]);
        free(parsing);
      }
    }
    
    if (nb_atoms > 1) { info->sigma_a *= nb_atoms; info->sigma_i *= nb_atoms; }

    /* special cases for the structure definition */
    if (info->m_ax || info->m_ay || info->m_az) info->m_a=0; /* means we specify by hand the vectors */
    if (info->m_bx || info->m_by || info->m_bz) info->m_b=0;
    if (info->m_cx || info->m_cy || info->m_cz) info->m_c=0;

    /* compute the norm from vector a if missing */
    if (info->m_ax || info->m_ay || info->m_az) {
      double as=sqrt(info->m_ax*info->m_ax+info->m_ay*info->m_ay+info->m_az*info->m_az);
      if (!info->m_bx && !info->m_by && !info->m_bz) info->m_a=info->m_b=as;
      if (!info->m_cx && !info->m_cy && !info->m_cz) info->m_a=info->m_c=as;
    }
    if (info->m_a && !info->m_b) info->m_b=info->m_a;
    if (info->m_b && !info->m_c) info->m_c=info->m_b;
    
    /* compute the lattive angles if not set from data file. Not used when in vector mode. */
    if (info->m_a && !info->m_aa) info->m_aa=90;
    if (info->m_aa && !info->m_bb) info->m_bb=info->m_aa;
    if (info->m_bb && !info->m_cc) info->m_cc=info->m_bb;
    
    /* parameters consistency checks */
    if (!info->m_ax && !info->m_ay && !info->m_az && !info->m_a) {
      fprintf(stderr,
              "Single_crystal: Error: Wrong a lattice vector definition\n");
      return(0);
    }
    if (!info->m_bx && !info->m_by && !info->m_bz && !info->m_b) {
      fprintf(stderr,
              "Single_crystal: Error: Wrong b lattice vector definition\n");
      return(0);
    }
    if (!info->m_cx && !info->m_cy && !info->m_cz && !info->m_c) {
      fprintf(stderr,
              "Single_crystal: Error: Wrong c lattice vector definition\n");
      return(0);
    }
    if (info->m_aa && info->m_bb && info->m_cc && info->recip) {
      fprintf(stderr,
              "Single_crystal: Error: Selecting reciprocal cell and angles is unmeaningful\n");
      return(0);
    }

    /* when lengths a,b,c + angles are given (instead of vectors a,b,c) */
    if (info->m_aa && info->m_bb && info->m_cc)
    {
      double as,bs,cs;
      if (info->m_a) as = info->m_a;
      else as = sqrt(info->m_ax*info->m_ax+info->m_ay*info->m_ay+info->m_az*info->m_az);
      if (info->m_b) bs = info->m_b;
      else bs = sqrt(info->m_bx*info->m_bx+info->m_by*info->m_by+info->m_bz*info->m_bz);
      if (info->m_c) cs = info->m_c;
      else cs =  sqrt(info->m_cx*info->m_cx+info->m_cy*info->m_cy+info->m_cz*info->m_cz);

      info->m_bz = as; info->m_by = 0; info->m_bx = 0;
      info->m_az = bs*cos(info->m_cc*DEG2RAD);
      info->m_ay = bs*sin(info->m_cc*DEG2RAD);
      info->m_ax = 0;
      info->m_cz = cs*cos(info->m_bb*DEG2RAD);
      info->m_cy = cs*(cos(info->m_aa*DEG2RAD)-cos(info->m_cc*DEG2RAD)*cos(info->m_bb*DEG2RAD))
                     /sin(info->m_cc*DEG2RAD);
      info->m_cx = sqrt(cs*cs - info->m_cz*info->m_cz - info->m_cy*info->m_cy);

      printf("Single_crystal: %s structure a=%g b=%g c=%g aa=%g bb=%g cc=%g ",
        (flag ? "INC" : SC_file), as, bs, cs, info->m_aa, info->m_bb, info->m_cc);
    } else {
      if (!info->recip) {
        printf("Single_crystal: %s structure a=[%g,%g,%g] b=[%g,%g,%g] c=[%g,%g,%g] ",
	       (flag ? "INC" : SC_file), info->m_ax ,info->m_ay ,info->m_az,
	       info->m_bx ,info->m_by ,info->m_bz,
	       info->m_cx ,info->m_cy ,info->m_cz);
      } else {
        printf("Single_crystal: %s structure a*=[%g,%g,%g] b*=[%g,%g,%g] c*=[%g,%g,%g] ",
	       (flag ? "INC" : SC_file), info->m_ax ,info->m_ay ,info->m_az,
	       info->m_bx ,info->m_by ,info->m_bz,
	       info->m_cx ,info->m_cy ,info->m_cz);
      }
    }
    /* Compute reciprocal or direct lattice vectors. */
    if (!info->recip) {
      vec_prod(tmp_x, tmp_y, tmp_z,
	       info->m_bx, info->m_by, info->m_bz,
	       info->m_cx, info->m_cy, info->m_cz);
      info->V0 = fabs(scalar_prod(info->m_ax, info->m_ay, info->m_az, tmp_x, tmp_y, tmp_z));
      printf("V0=%g\n", info->V0);
      
      info->asx = 2*PI/info->V0*tmp_x;
      info->asy = 2*PI/info->V0*tmp_y;
      info->asz = 2*PI/info->V0*tmp_z;
      vec_prod(tmp_x, tmp_y, tmp_z, info->m_cx, info->m_cy, info->m_cz, info->m_ax, info->m_ay, info->m_az);
      info->bsx = 2*PI/info->V0*tmp_x;
      info->bsy = 2*PI/info->V0*tmp_y;
      info->bsz = 2*PI/info->V0*tmp_z;
      vec_prod(tmp_x, tmp_y, tmp_z, info->m_ax, info->m_ay, info->m_az, info->m_bx, info->m_by, info->m_bz);
      info->csx = 2*PI/info->V0*tmp_x;
      info->csy = 2*PI/info->V0*tmp_y;
      info->csz = 2*PI/info->V0*tmp_z;
    } else {
      info->asx = info->m_ax;
      info->asy = info->m_ay;
      info->asz = info->m_az;
      info->bsx = info->m_bx;
      info->bsy = info->m_by;
      info->bsz = info->m_bz;
      info->csx = info->m_cx;
      info->csy = info->m_cy;
      info->csz = info->m_cz;
      
      vec_prod(tmp_x, tmp_y, tmp_z,
	       info->bsx/(2*PI), info->bsy/(2*PI), info->bsz/(2*PI),
	       info->csx/(2*PI), info->csy/(2*PI), info->csz/(2*PI));
      info->V0 = 1/fabs(scalar_prod(info->asx/(2*PI), info->asy/(2*PI), info->asz/(2*PI), tmp_x, tmp_y, tmp_z));
      printf("V0=%g\n", info->V0);
      
      /*compute the direct cell parameters, ofr completeness*/ 
      info->m_ax = tmp_x*info->V0;
      info->m_ay = tmp_y*info->V0;
      info->m_az = tmp_z*info->V0;
      vec_prod(tmp_x, tmp_y, tmp_z,info->csx/(2*PI), info->csy/(2*PI), info->csz/(2*PI),info->asx/(2*PI), info->asy/(2*PI), info->asz/(2*PI));
      info->m_bx = tmp_x*info->V0;
      info->m_by = tmp_y*info->V0;
      info->m_bz = tmp_z*info->V0;
      vec_prod(tmp_x, tmp_y, tmp_z,info->asx/(2*PI), info->asy/(2*PI), info->asz/(2*PI),info->bsx/(2*PI), info->bsy/(2*PI), info->bsz/(2*PI));
      info->m_cx = tmp_x*info->V0;
      info->m_cy = tmp_y*info->V0;
      info->m_cz = tmp_z*info->V0;
    }

    if (flag) return(-1);

    if (!info->column_order[0] || !info->column_order[1] || !info->column_order[2]) {
      fprintf(stderr,
              "Single_crystal: Error: Wrong h,k,l column definition\n");
      return(0);
    }
    if (!info->column_order[3] && !info->column_order[4]) {
      fprintf(stderr,
              "Single_crystal: Error: Wrong F,F2 column definition\n");
      return(0);
    }

    /* allocate hkl_data array */
    list = (struct hkl_data_union*)malloc(size*sizeof(struct hkl_data_union));

    for (i=0; i<size; i++)
    {
      double h=0, k=0, l=0, F2=0;
      double b1[3], b2[3];
      double sig1, sig2, sig3;

      /* get data from table */
      h = Table_Index(sTable, i, info->column_order[0]-1);
      k = Table_Index(sTable, i, info->column_order[1]-1);
      l = Table_Index(sTable, i, info->column_order[2]-1);
      if (info->column_order[3])
      { F2= Table_Index(sTable, i, info->column_order[3]-1); F2 *= F2; }
      else if (info->column_order[4])
        F2= Table_Index(sTable, i, info->column_order[4]-1);

      list[i].h = h;
      list[i].k = k;
      list[i].l = l;
      list[i].F2 = F2;
      
      /* Precompute some values */
      list[i].tau_x = h*info->asx + k*info->bsx + l*info->csx;
      list[i].tau_y = h*info->asy + k*info->bsy + l*info->csy;
      list[i].tau_z = h*info->asz + k*info->bsz + l*info->csz;
      list[i].tau = sqrt(list[i].tau_x*list[i].tau_x +
                         list[i].tau_y*list[i].tau_y +
                         list[i].tau_z*list[i].tau_z);
      list[i].u1x = list[i].tau_x/list[i].tau;
      list[i].u1y = list[i].tau_y/list[i].tau;
      list[i].u1z = list[i].tau_z/list[i].tau;
      sig1 = FWHM2RMS*info->m_delta_d_d*list[i].tau;

      /* Find two arbitrary axes perpendicular to tau and each other. */
      normal_vec(&b1[0], &b1[1], &b1[2],
                 list[i].u1x, list[i].u1y, list[i].u1z);
      vec_prod(b2[0], b2[1], b2[2],
               list[i].u1x, list[i].u1y, list[i].u1z,
               b1[0], b1[1], b1[2]);
               
      /* Find the two mosaic axes perpendicular to tau. */
      if(SC_mosaic > 0) {
        /* Use isotropic mosaic. */
        list[i].u2x = b1[0];
        list[i].u2y = b1[1];
        list[i].u2z = b1[2];
        sig2 = FWHM2RMS*list[i].tau*MIN2RAD*SC_mosaic;
        list[i].u3x = b2[0];
        list[i].u3y = b2[1];
        list[i].u3z = b2[2];
        sig3 = FWHM2RMS*list[i].tau*MIN2RAD*SC_mosaic;
      } else if(SC_mosaic_a > 0 && SC_mosaic_b > 0 && SC_mosaic_c > 0) {
        /* Use anisotropic mosaic. */
        fprintf(stderr,"Single_crystal: Warning: you are using an experimental feature:\n"
          "  anistropic mosaicity. Please examine your data carefully.\n");
        /* compute the jacobian of (tau_v,tau_n) from rotations around the unit cell vectors. */
        struct hkl_data_union *l =&(list[i]);
        double xia_x,xia_y,xia_z,xib_x,xib_y,xib_z,xic_x,xic_y,xic_z;
        /*input parameters are in arc minutes*/
        double sig_fi_a=SC_mosaic_a*MIN2RAD;
        double sig_fi_b=SC_mosaic_b*MIN2RAD;
        double sig_fi_c=SC_mosaic_c*MIN2RAD;
        if(info->m_a==0) info->m_a=sqrt(scalar_prod( info->m_ax,info->m_ay,info->m_az,info->m_ax,info->m_ay,info->m_az));
        if(info->m_b==0) info->m_b=sqrt(scalar_prod( info->m_bx,info->m_by,info->m_bz,info->m_bx,info->m_by,info->m_bz));
        if(info->m_c==0) info->m_c=sqrt(scalar_prod( info->m_cx,info->m_cy,info->m_cz,info->m_cx,info->m_cy,info->m_cz));

        l->u2x = b1[0];
        l->u2y = b1[1];
        l->u2z = b1[2];
        l->u3x = b2[0];
        l->u3y = b2[1];
        l->u3z = b2[2];
                                                                          
        xia_x=l->tau_x-(M_2_PI*h/info->m_a)*info->asx;
        xia_y=l->tau_y-(M_2_PI*h/info->m_a)*info->asy;
        xia_z=l->tau_z-(M_2_PI*h/info->m_a)*info->asz;
        xib_x=l->tau_x-(M_2_PI*h/info->m_b)*info->bsx;
        xib_y=l->tau_y-(M_2_PI*h/info->m_b)*info->bsy;
        xib_z=l->tau_z-(M_2_PI*h/info->m_b)*info->bsz;
        xic_x=l->tau_x-(M_2_PI*h/info->m_c)*info->csx;
        xic_y=l->tau_y-(M_2_PI*h/info->m_c)*info->csy;
        xic_z=l->tau_z-(M_2_PI*h/info->m_c)*info->csz;

        double xia=sqrt(xia_x*xia_x + xia_y*xia_y + xia_z*xia_z);
        double xib=sqrt(xib_x*xib_x + xib_y*xib_y + xib_z*xib_z);
        double xic=sqrt(xic_x*xic_x + xic_y*xic_y + xic_z*xic_z);

        vec_prod(tmp_x,tmp_y,tmp_z,l->tau_x,l->tau_y,l->tau_z, l->u2x,l->u2y,l->u2z);
        double J_n_fia= xia/info->m_a/l->tau*scalar_prod(info->asx,info->asy,info->asz,tmp_x,tmp_y,tmp_z);
        vec_prod(tmp_x,tmp_y,tmp_z,l->tau_x,l->tau_y,l->tau_z, l->u2x,l->u2y,l->u2z);
        double J_n_fib= xib/info->m_b/l->tau*scalar_prod(info->bsx,info->bsy,info->bsz,tmp_x,tmp_y,tmp_z);
        vec_prod(tmp_x,tmp_y,tmp_z,l->tau_x,l->tau_y,l->tau_z, l->u2x,l->u2y,l->u2z);
        double J_n_fic= xic/info->m_c/l->tau*scalar_prod(info->csx,info->csy,info->csz,tmp_x,tmp_y,tmp_z);

        vec_prod(tmp_x,tmp_y,tmp_z,l->tau_x,l->tau_y,l->tau_z, l->u3x,l->u3y,l->u3z);
        double J_v_fia= xia/info->m_a/l->tau*scalar_prod(info->asx,info->asy,info->asz,tmp_x,tmp_y,tmp_z);
        vec_prod(tmp_x,tmp_y,tmp_z,l->tau_x,l->tau_y,l->tau_z, l->u3x,l->u3y,l->u3z);
        double J_v_fib= xib/info->m_b/l->tau*scalar_prod(info->bsx,info->bsy,info->bsz,tmp_x,tmp_y,tmp_z);
        vec_prod(tmp_x,tmp_y,tmp_z,l->tau_x,l->tau_y,l->tau_z, l->u3x,l->u3y,l->u3z);
        double J_v_fic= xic/info->m_c/l->tau*scalar_prod(info->csx,info->csy,info->csz,tmp_x,tmp_y,tmp_z);

        /*with the jacobian we can compute the sigmas in terms of the orthogonal vectors u2 and u3*/
        sig2=sig_fi_a*fabs(J_v_fia) + sig_fi_b*fabs(J_v_fib) + sig_fi_c*fabs(J_v_fic);
        sig3=sig_fi_a*fabs(J_n_fia) + sig_fi_b*fabs(J_n_fib) + sig_fi_c*fabs(J_n_fic);
      } else if (SC_mosaic_AB[0]!=0 && SC_mosaic_AB[1]!=0){
        if ( (SC_mosaic_AB[2]==0 && SC_mosaic_AB[3]==0 && SC_mosaic_AB[4]==0) || (SC_mosaic_AB[5]==0 && SC_mosaic_AB[6]==0 && SC_mosaic_AB[7]==0) ){
          fprintf(stderr,"Single_crystal: Error: in-plane mosaics are specified but one (or both)\n"
              "  in-plane reciprocal vector is the zero vector\n");
          return(0);
        }
        fprintf(stderr,"Single_crystal: Warning: you are using an experimental feature: \n"
              "  \"in-plane\" anistropic mosaicity. Please examine your data carefully.\n");
 
        /*for given reflection in list - compute linear comb of tau_a and tau_b*/
        /*check for not in plane - f.i. check if (tau_a X tau_b).tau_i)==0*/
        struct hkl_data_union *l =&(list[i]);
        double det,c1,c2,sig_tau_c;
        double em_x,em_y,em_z, tmp_x,tmp_y,tmp_z;
        double tau_a[3],tau_b[3];
        /*convert Miller indices to taus*/
        if(info->m_a==0) info->m_a=sqrt(scalar_prod( info->m_ax,info->m_ay,info->m_az,info->m_ax,info->m_ay,info->m_az));
        if(info->m_b==0) info->m_b=sqrt(scalar_prod( info->m_bx,info->m_by,info->m_bz,info->m_bx,info->m_by,info->m_bz));
        if(info->m_c==0) info->m_c=sqrt(scalar_prod( info->m_cx,info->m_cy,info->m_cz,info->m_cx,info->m_cy,info->m_cz));
        tau_a[0]=M_2_PI*( (SC_mosaic_AB[2]/info->m_a)*info->asx + (SC_mosaic_AB[3]/info->m_b)*info->bsx + (SC_mosaic_AB[4]/info->m_c)*info->csx );
        tau_a[1]=M_2_PI*( (SC_mosaic_AB[2]/info->m_a)*info->asy + (SC_mosaic_AB[3]/info->m_b)*info->bsy + (SC_mosaic_AB[4]/info->m_c)*info->csy );
        tau_a[2]=M_2_PI*( (SC_mosaic_AB[2]/info->m_a)*info->asz + (SC_mosaic_AB[3]/info->m_b)*info->bsz + (SC_mosaic_AB[4]/info->m_c)*info->csz );
        tau_b[0]=M_2_PI*( (SC_mosaic_AB[5]/info->m_a)*info->asx + (SC_mosaic_AB[6]/info->m_b)*info->bsx + (SC_mosaic_AB[7]/info->m_c)*info->csx );
        tau_b[1]=M_2_PI*( (SC_mosaic_AB[5]/info->m_a)*info->asy + (SC_mosaic_AB[6]/info->m_b)*info->bsy + (SC_mosaic_AB[7]/info->m_c)*info->csy );
        tau_b[2]=M_2_PI*( (SC_mosaic_AB[5]/info->m_a)*info->asz + (SC_mosaic_AB[6]/info->m_b)*info->bsz + (SC_mosaic_AB[7]/info->m_c)*info->csz );
        
        /*check determinants to see how we should compute the linear combination of a and b (to match c)*/
        if ((det=tau_a[0]*tau_b[1]-tau_a[1]*tau_b[0])!=0){
          c1= (l->tau_x*tau_b[1] - l->tau_y*tau_b[0])/det;
          c2= (tau_a[0]*l->tau_y - tau_a[1]*l->tau_x)/det;
        }else if ((det=tau_a[1]*tau_b[2]-tau_a[2]*tau_b[1])!=0){
          c1= (l->tau_y*tau_b[2] - l->tau_z*tau_b[1])/det;
          c2= (tau_a[1]*l->tau_z - tau_a[2]*l->tau_y)/det;
        }else if ((det=tau_a[0]*tau_b[2]-tau_a[2]*tau_b[0])!=0){
          c1= (l->tau_x*tau_b[2] - l->tau_z*tau_b[0])/det;
          c2= (tau_a[0]*l->tau_z - tau_a[2]*l->tau_x)/det;
        }
        if ((c1==0) && (c2==0)){
          fprintf(stderr,"Single_crystal: Warning: reflection tau[%i]=(%g %g %g) "
          "has no component in defined mosaic plane\n", 
          i, l->tau_x,l->tau_y,l->tau_z);
        }
        /*compute linear combination => sig_tau_i = | c1*sig_tau_a + c2*sig_tau_b |  - also add in the minute to radian scaling factor*/;
        sig_tau_c = MIN2RAD*sqrt(c1*SC_mosaic_AB[0]*c1*SC_mosaic_AB[0] + c2*SC_mosaic_AB[1]*c2*SC_mosaic_AB[1]);
        l->u2x = b1[0]; l->u2y = b1[1]; l->u2z = b1[2];
        l->u3x = b2[0]; l->u3y = b2[1]; l->u3z = b2[2];

        /*so now let's compute the rotation around planenormal tau_a X tau_b*/
        /*g_bar (unit normal of rotation plane) = tau_a X tau_b / norm(tau_a X tau_b)*/
        vec_prod(tmp_x,tmp_y,tmp_z, tau_a[0],tau_a[1],tau_a[2],tau_b[0],tau_b[1],tau_b[2]);
        vec_prod(em_x,em_y,em_z, l->tau_x, l->tau_y, l->tau_z, tmp_x,tmp_y,tmp_z);
        NORM(em_x,em_y,em_z);
        sig2 = l->tau*sig_tau_c*fabs(scalar_prod(em_x,em_y,em_z, l->u2x,l->u2y,l->u2z));
        sig3 = l->tau*sig_tau_c*fabs(scalar_prod(em_x,em_y,em_z, l->u3x,l->u3y,l->u3z));
        /*protect against collapsing gaussians. These seem to be sensible values.*/
        if (sig2<1e-5) sig2=1e-5;
        if (sig3<1e-5) sig3=1e-5;
      }
      else {
        fprintf(stderr,
                "Single_crystal: Error: EITHER mosaic OR (mosaic_a, mosaic_b, mosaic_c)\n"
                "  must be given and be >0.\n");
        return(0);
      }
      list[i].sig123 = sig1*sig2*sig3;
      list[i].m1 = 1/(2*sig1*sig1);
      list[i].m2 = 1/(2*sig2*sig2);
      list[i].m3 = 1/(2*sig3*sig3);
      /* Set Gauss cutoff to 5 times the maximal sigma. */
      if(sig1 > sig2)
        if(sig1 > sig3)
          list[i].cutoff = 5*sig1;
        else
          list[i].cutoff = 5*sig3;
      else
        if(sig2 > sig3)
          list[i].cutoff = 5*sig2;
        else
          list[i].cutoff = 5*sig3;
    }
    Table_Free(&sTable);
    
    /* sort the list with increasing tau */
    qsort(list, i, sizeof(struct hkl_data_union),  SX_list_compare_union);
    
    info->list = list;
    info->count = i;
    info->tau_list = malloc(i*sizeof(*info->tau_list));
    if(!info->tau_list)
    {
      fprintf(stderr, "Single_crystal: Error: Out of memory!\n");
      return(0);
    }
    return(info->count);
  } /* read_hkl_data */

  /* ------------------------------------------------------------------------ */
  /* hkl_search
    search the HKL reflections which are on the Ewald sphere
    input:
      L,T,count,V0: constants for all calls
      kix,kiy,kiz: may be different for each call
    this function returns:
      tau_count (return), coh_refl, coh_xsect, T (updated elements in the array up to [j])
   */
  int hkl_search_union(struct hkl_data_union *L, struct tau_data_union *T, int count, double V0,
    double kix, double kiy, double kiz, double tau_max,
    double *coh_refl, double *coh_xsect)
  {
    double rho, rho_x, rho_y, rho_z;
    double diff;
    int    i,j;
    double ox,oy,oz;
    double b1x,b1y,b1z, b2x,b2y,b2z, kx, ky, kz, nx, ny, nz;
    double n11, n22, n12, det_N, inv_n11, inv_n22, inv_n12, l11, l22, l12,  det_L;
    double Bt_D_O_x, Bt_D_O_y, y0x, y0y, alpha;
    
    double ki = sqrt(kix*kix+kiy*kiy+kiz*kiz);

    /* Common factor in coherent cross-section */
    double xsect_factor = pow(2*PI, 5.0/2.0)/(V0*ki*ki);
    
    for(i = j = 0; i < count; i++)
      {
    /* Assuming reflections are sorted, stop search when max tau exceeded. */
        if(L[i].tau > tau_max)
          break;
        /* Check if this reciprocal lattice point is close enough to the
           Ewald sphere to make scattering possible. */
        rho_x = kix - L[i].tau_x;
        rho_y = kiy - L[i].tau_y;
        rho_z = kiz - L[i].tau_z;
        rho = sqrt(rho_x*rho_x + rho_y*rho_y + rho_z*rho_z);
        diff = fabs(rho - ki);

        /* Check if scattering is possible (cutoff of Gaussian tails). */
        if(diff <= L[i].cutoff)
        {
          /* Store reflection. */
          T[j].index = i;
          /* Get ki vector in local coordinates. */
          kx = kix*L[i].u1x + kiy*L[i].u1y + kiz*L[i].u1z;
          ky = kix*L[i].u2x + kiy*L[i].u2y + kiz*L[i].u2z;
          kz = kix*L[i].u3x + kiy*L[i].u3y + kiz*L[i].u3z;
          T[j].rho_x = kx - L[i].tau;
          T[j].rho_y = ky;
          T[j].rho_z = kz;
          T[j].rho = rho;
          /* Compute the tangent plane of the Ewald sphere. */
          nx = T[j].rho_x/T[j].rho;
          ny = T[j].rho_y/T[j].rho;
          nz = T[j].rho_z/T[j].rho;
          ox = (ki - T[j].rho)*nx;
          oy = (ki - T[j].rho)*ny;
          oz = (ki - T[j].rho)*nz;
          T[j].ox = ox;
          T[j].oy = oy;
          T[j].oz = oz;
          /* Compute unit vectors b1 and b2 that span the tangent plane. */
          normal_vec(&b1x, &b1y, &b1z, nx, ny, nz);
          vec_prod(b2x, b2y, b2z, nx, ny, nz, b1x, b1y, b1z);
          T[j].b1x = b1x;
          T[j].b1y = b1y;
          T[j].b1z = b1z;
          T[j].b2x = b2x;
          T[j].b2y = b2y;
          T[j].b2z = b2z;
          /* Compute the 2D projection of the 3D Gauss of the reflection. */
          /* The symmetric 2x2 matrix N describing the 2D gauss. */
          n11 = L[i].m1*b1x*b1x + L[i].m2*b1y*b1y + L[i].m3*b1z*b1z;
          n12 = L[i].m1*b1x*b2x + L[i].m2*b1y*b2y + L[i].m3*b1z*b2z;
          n22 = L[i].m1*b2x*b2x + L[i].m2*b2y*b2y + L[i].m3*b2z*b2z;
          /* The (symmetric) inverse matrix of N. */
          det_N = n11*n22 - n12*n12;
          inv_n11 = n22/det_N;
          inv_n12 = -n12/det_N;
          inv_n22 = n11/det_N;
          /* The Cholesky decomposition of 1/2*inv_n (lower triangular L). */
          l11 = sqrt(inv_n11/2);
          l12 = inv_n12/(2*l11);
          l22 = sqrt(inv_n22/2 - l12*l12);
          T[j].l11 = l11;
          T[j].l12 = l12;
          T[j].l22 = l22;
          det_L = l11*l22;
          /* The product B^T D o. */
          Bt_D_O_x = b1x*L[i].m1*ox + b1y*L[i].m2*oy + b1z*L[i].m3*oz;
          Bt_D_O_y = b2x*L[i].m1*ox + b2y*L[i].m2*oy + b2z*L[i].m3*oz;
          /* Center of 2D Gauss in plane coordinates. */
          y0x = -(Bt_D_O_x*inv_n11 + Bt_D_O_y*inv_n12);
          y0y = -(Bt_D_O_x*inv_n12 + Bt_D_O_y*inv_n22);
          T[j].y0x = y0x;
          T[j].y0y = y0y;
          /* Factor alpha for the distance of the 2D Gauss from the origin. */
          alpha = L[i].m1*ox*ox + L[i].m2*oy*oy + L[i].m3*oz*oz -
                       (y0x*y0x*n11 + y0y*y0y*n22 + 2*y0x*y0y*n12);
          T[j].refl = xsect_factor*det_L*exp(-alpha)/L[i].sig123; /* intensity of that Bragg */
          *coh_refl += T[j].refl;                                  /* total scatterable intensity */
          T[j].xsect = T[j].refl*L[i].F2;
          *coh_xsect += T[j].xsect;
          j++;
        }
        
      } /* end for */
        return (j); // this is 'tau_count', i.e. number of reachable reflections
    } /* end hkl_search */
    
    int hkl_select_union(struct tau_data_union *T, int tau_count, double coh_refl, double *sum, _class_particle *_particle) {
      int j;
      double r = rand0max(coh_refl);
      *sum = 0;
      for(j = 0; j < tau_count; j++)
      {
        *sum += T[j].refl;
        if(*sum > r) break;
      }
      return j;
    }

    /* Functions for "reorientation", powder and PG modes */
    /* Powder, forward */
    void randrotate_union(double *nx, double *ny, double *nz, double a, double b) {
      double nvx, nvy, nvz;
      rotate(nvx,nvy,nvz, *nx, *ny, *nz, a, 1, 0, 0);
      rotate(*nx,*ny,*nz, nvx,nvy,nvz, b, 0, 1, 0);
    }
    /* Powder, back */
    void randderotate_union(double *nx, double *ny, double *nz, double a, double b) {
      double nvx, nvy, nvz;
      rotate(nvx,nvy,nvz,*nx,*ny,*nz, -b, 0, 1, 0);
      rotate(*nx, *ny, *nz, nvx,nvy,nvz, -a, 1, 0, 0);
    }
    /* PG, forward */
    void PGrotate_union(double *nx, double *ny, double *nz, double a, double csx, double csy, double csz) {
      /* Currently assumes c-axis along 'x', ought to be generalized... */
      double nvx, nvy, nvz;
      rotate(nvx,nvy,nvz, *nx, *ny, *nz, a, csx, csy, csz);
      *nx = nvx; *ny = nvy; *nz = nvz;
    }
    /* PG, back */
    void PGderotate_union(double *nx, double *ny, double *nz, double a, double csx, double csy, double csz) {
      /* Currently assumes c-axis along 'x', ought to be generalized... */
      double nvx, nvy, nvz;
      rotate(nvx,nvy,nvz, *nx, *ny, *nz, -a, csx, csy, csz);
      *nx = nvx; *ny = nvy; *nz = nvz;
    }


#endif /* !SINGLE_CRYSTAL_PROCESS_DECL */

// Very important to add a pointer to this struct in the Union_functions.c file
struct Single_crystal_physics_storage_struct{
    // Variables that needs to be transfered between any of the following places:
    // The initialize in this component
    // The function for calculating my
    // The function for calculating scattering
    
    // Avoid duplicates of output parameters and setting parameters in naming
    double PG_setting;     // 0 if PG mode is diabled, 1 if enabled. Values between apporximates texture?
    double powder_setting; // 0 if powder mode is disabled, 1 if enabled. Values between approximates texture?
    double Alpha;          // random angle between 0 and 2*Pi*powder
    double Beta;           // random angle between -Pi/2 and Pi/2
    
    struct hkl_info_struct_union *hkl_info_storage; // struct containing all necessary info for SC
    double pack; // packing factor
    double barns_setting; // Sets wether barns of fm^2 is used
};

// Function for calculating my, the inverse penetration depth (for only this scattering process).
// The input for this function and its order may not be changed, but the names may be updated.
int Single_crystal_physics_my(double *my, double *k_initial, union data_transfer_union data_transfer, struct focus_data_struct *focus_data, _class_particle *_particle) {
    // *k_initial is a pointer to a simple vector with 3 doubles, k[0], k[1], k[2] which describes the wavevector
    double kix = k_initial[0],kiy = k_initial[1],kiz = k_initial[2];
    double ki = sqrt(k_initial[0]*k_initial[0]+k_initial[1]*k_initial[1]+k_initial[2]*k_initial[2]);
    
    struct hkl_info_struct_union *hkl_info = data_transfer.pointer_to_a_Single_crystal_physics_storage_struct->hkl_info_storage;
    
    // Need the Powder/PG/curvature mode rotate added here
    
    // Taken from Single_crystal and changed hkl_info to a pointer.
    // The split optimization is less useful here than normally
    
    if (data_transfer.pointer_to_a_Single_crystal_physics_storage_struct->powder_setting) {
      //orientation of crystallite is random
	  data_transfer.pointer_to_a_Single_crystal_physics_storage_struct->Alpha = randpm1()*PI*data_transfer.pointer_to_a_Single_crystal_physics_storage_struct->powder_setting;
	  data_transfer.pointer_to_a_Single_crystal_physics_storage_struct->Beta = randpm1()*PI/2;
      
	  randrotate_union(&kix, &kiy, &kiz,
        data_transfer.pointer_to_a_Single_crystal_physics_storage_struct->Alpha,
        data_transfer.pointer_to_a_Single_crystal_physics_storage_struct->Beta);
    }
    if (data_transfer.pointer_to_a_Single_crystal_physics_storage_struct->PG_setting) {
      // orientation of crystallite is random
      data_transfer.pointer_to_a_Single_crystal_physics_storage_struct->Alpha = rand01()*2*PI*data_transfer.pointer_to_a_Single_crystal_physics_storage_struct->PG_setting;
	  PGrotate_union(&kix, &kiy, &kiz,
        data_transfer.pointer_to_a_Single_crystal_physics_storage_struct->Alpha,
        hkl_info->csx, hkl_info->csy, hkl_info->csz);
    }
    
    /* in case we use 'SPLIT' then consecutive neutrons can be identical when entering here
         and we may skip the hkl_search call */
    if ( fabs(kix - hkl_info->kix) < 1e-6
        && fabs(kiy - hkl_info->kiy) < 1e-6
        && fabs(kiz - hkl_info->kiz) < 1e-6) {
        hkl_info->nb_reuses++;
      } else {
        /* Max possible tau for this ki with 5*sigma delta-d/d cutoff. */
        double tau_max   = 2*ki/(1 - 5*hkl_info->m_delta_d_d);
        double coh_xsect = 0, coh_refl = 0;
        
        /* call hkl_search */
        hkl_info->tau_count = hkl_search_union(hkl_info->list, hkl_info->tau_list, hkl_info->count, hkl_info->V0, kix, kiy, kiz, tau_max, &coh_refl, &coh_xsect); /* CPU consuming */
          
        // This is problematic as there is no way to know if this is the first scattering in this material or not with the current structure.
        // Need to do one of the following:
        //  remove this optimization
        //  find a way to set event_counter to 0 when a neutron enters a volume with a SC process
        //  pass the number of scatterings in this volume to all my functions
        // temporary solution: all events are considered the first
        int event_counter = 0;
        
        
        /* store ki so that we can check for further SPLIT iterations */
        if (event_counter == 0 ) { /* only for incoming neutron */
          hkl_info->kix = kix;
          hkl_info->kiy = kiy;
          hkl_info->kiz = kiz;
        }
        
        hkl_info->coh_refl  = coh_refl;
        hkl_info->coh_xsect = coh_xsect;
        hkl_info->nb_refl += hkl_info->tau_count;
        hkl_info->nb_refl_count++;
        
        
      }

      /* (3). Probabilities of the different possible interactions. */
      //tot_xsect = abs_xsect + inc_xsect + hkl_info.coh_xsect;
      /* Cross-sections are in barns = 10**-28 m**2, and unit cell volumes are
         in AA**3 = 10**-30 m**2. Hence a factor of 100 is used to convert
         scattering lengths to m**-1 */
      double coh_xlen = hkl_info->coh_xsect/hkl_info->V0;
      if (data_transfer.pointer_to_a_Single_crystal_physics_storage_struct->barns_setting) {
        coh_xlen *= 100;
      }
    
      //printf("Single crystal process returned coh_xlen = %E \n",coh_xlen);
      *my = coh_xlen*data_transfer.pointer_to_a_Single_crystal_physics_storage_struct->pack;
      int iterate;
      //if (hkl_info->tau_count == 0) printf("my: ki=%f, no reflections matched \n",ki);
      for (iterate=0;iterate<hkl_info->tau_count;iterate++)
        //printf("my: ki=%f, hkl_info->coh_xsect = %f, T[%d].refl = %f, coh_xlen = %f \n",ki,hkl_info->coh_xsect,iterate,hkl_info->tau_list[iterate].refl,coh_xlen);
      // Probably need to rotate back from Powder/PG/curvature mode, as another process than this one could be selected. Would just need to send the used rotation parameters to the process to repeat that rotation.
    
    return 1;
};

// Function that provides description of a basic scattering event.
// Do not change the
int Single_crystal_physics_scattering(double *k_final, double *k_initial, double *weight, union data_transfer_union data_transfer, struct focus_data_struct *focus_data, _class_particle *_particle) {

    int i;                        /* Index into structure factor list */
    struct hkl_data_union *L;     /* Structure factor list */
    int j;                        /* Index into reflection list */
    struct tau_data_union *T;     /* List of reflections close to Ewald sphere */
    //double ox, oy, oz;            /* Origin of Ewald sphere tangent plane */
    //double l11, l12, l22;         /* Cholesky decomposition L of 1/2*inv(N) */
    double b1x, b1y, b1z;         /* First vector spanning tangent plane */
    double b2x, b2y, b2z;         /* Second vector spanning tangent plane */
    double z1, z2, y1, y2;        /* Temporaries to choose kf from 2D Gauss */
    double adjust, r, sum;        /* Temporaries */
    double kfx, kfy, kfz;         /* Final wave vector */

    double kix = k_initial[0],kiy = k_initial[1],kiz = k_initial[2];
    double ki = sqrt(k_initial[0]*k_initial[0]+k_initial[1]*k_initial[1]+k_initial[2]*k_initial[2]);
    
    struct hkl_info_struct_union *hkl_info = data_transfer.pointer_to_a_Single_crystal_physics_storage_struct->hkl_info_storage;
    
    L = hkl_info->list;
    T = hkl_info->tau_list;
    
    
    // Taken from Single_crystal.comp
    if(hkl_info->coh_refl <= 0){
          return 0; // Return 0 will use ABSORB in main component (as it is not allowed in a function)
    }
    sum = 0;
    j = hkl_select_union(T, hkl_info->tau_count, hkl_info->coh_refl, &sum, _particle);
    //printf("Selected j = %d with T[%d].refl = %f \n",j,j,T[j].refl);

    if(j >= hkl_info->tau_count)
    {
      #ifndef OPENACC
      if (hkl_info->flag_warning < 100)
        fprintf(stderr, "Single_crystal_process: Error: Illegal tau search "
          "(r=%g, sum=%g, j=%i, tau_count=%i).\n", r, sum, j , hkl_info->tau_count);
      #endif
      hkl_info->flag_warning++;
      j = hkl_info->tau_count - 1;
    }
    i = T[j].index;
    /* (8). Pick scattered wavevector kf from 2D Gauss distribution. */
    z1 = randnorm();
    z2 = randnorm();
    y1 = T[j].l11*z1 + T[j].y0x;
    y2 = T[j].l12*z1 + T[j].l22*z2 + T[j].y0y;
    kfx = T[j].rho_x + T[j].ox + T[j].b1x*y1 + T[j].b2x*y2;
    kfy = T[j].rho_y + T[j].oy + T[j].b1y*y1 + T[j].b2y*y2;
    kfz = T[j].rho_z + T[j].oz + T[j].b1z*y1 + T[j].b2z*y2;
    /* Normalize kf to length of ki, to account for planer
      approximation of the Ewald sphere. */
    adjust = ki/sqrt(kfx*kfx + kfy*kfy + kfz*kfz);
    kfx *= adjust;
    kfy *= adjust;
    kfz *= adjust;
    /* Adjust neutron weight (see manual for explanation). */
    *weight *= T[j].xsect*hkl_info->coh_refl/(hkl_info->coh_xsect*T[j].refl);
    //printf("SCATTERING: hkl_info->coh_refl=%f, hkl_info->coh_xsect = %f, T[%d].refl = %f, hkl_info->tau.count = %d \n",hkl_info->coh_refl,hkl_info->coh_xsect,j,T[j].refl,hkl_info->tau_count);

    // These is the returned final wavevector
    k_final[0] = L[i].u1x*kfx + L[i].u2x*kfy + L[i].u3x*kfz;
    k_final[1] = L[i].u1y*kfx + L[i].u2y*kfy + L[i].u3y*kfz;
    k_final[2] = L[i].u1z*kfx + L[i].u2z*kfy + L[i].u3z*kfz;
    
    if (data_transfer.pointer_to_a_Single_crystal_physics_storage_struct->powder_setting) {
      // orientation of crystallite is no longer random
	  randderotate_union(&k_final[0], &k_final[1], &k_final[2], data_transfer.pointer_to_a_Single_crystal_physics_storage_struct->Alpha, data_transfer.pointer_to_a_Single_crystal_physics_storage_struct->Beta);
    }
    if (data_transfer.pointer_to_a_Single_crystal_physics_storage_struct->PG_setting) {
      // orientation of crystallite is longer random
	  PGderotate_union(&k_final[0], &k_final[1], &k_final[2], data_transfer.pointer_to_a_Single_crystal_physics_storage_struct->Alpha, hkl_info->csx, hkl_info->csy, hkl_info->csz);
    }

    hkl_info->type = 'c';
    hkl_info->h    = L[i].h;
    hkl_info->k    = L[i].k;
    hkl_info->l    = L[i].l;

    return 1;
};

#ifndef PROCESS_DETECTOR
    #define PROCESS_DETECTOR dummy
#endif

#ifndef PROCESS_SINGLE_CRYSTAL_DETECTOR
    #define PROCESS_SINGLE_CRYSTAL_DETECTOR dummy
#endif

/* Shared user declarations for all components types 'Union_cylinder'. */
#ifndef Union
#define Union $Revision: 0.8 $

/*******************************************************************************
*
*  McStas, neutron ray-tracing package
*  Copyright(C) 2007 Risoe National Laboratory.
*
* %I
* Written by: Mads Bertelsen
* Date: 20.08.15
* Version: $Revision: 0.1 $
* Origin: University of Copenhagen
*
* Functions and structure definitons for Union components.
*
******************************************************************************/

// -------------    Definition of data structures   ---------------------------------------------
// GPU
enum shape {
  surroundings,
  box,
  sphere,
  cylinder,
  cone,
  mesh
};

enum process {
  Incoherent,
  Powder,
  Single_crystal,
  AF_HB_1D,
  PhononSimple,
  Texture,
  IncoherentPhonon,
  NCrystal,
  Template
};

struct intersection_time_table_struct {
int num_volumes;
int *calculated;
int *n_elements;
double **intersection_times;
};

struct line_segment{
//struct position point1;
//struct position point2;
Coords point1;
Coords point2;
int number_of_dashes;
};

struct pointer_to_1d_int_list {
int num_elements;
int *elements;
#pragma acc shape(elements[0:num_elements]) init_needed(num_elements)
};

struct pointer_to_1d_double_list {
int num_elements;
double *elements;
#pragma acc shape(elements[0:num_elements]) init_needed(num_elements)
};

struct pointer_to_1d_coords_list {
int num_elements;
Coords *elements;
#pragma acc shape(elements[0:num_elements]) init_needed(num_elements)
};

struct lines_to_draw{
int number_of_lines;
struct line_segment *lines;
#pragma acc shape(lines[0:number_of_lines]) init_needed(number_of_lines)
};

// 2D lists not needed anyway
// struct pointer_to_2d_int_list {
// int n_lists;
// struct pointer_to_1d_int_list *lists;
// }

// Todo: see if the union geometry_parameter_union and other geometry structs can be here
union geometry_parameter_union{
    struct sphere_storage   *p_sphere_storage;
    struct cylinder_storage *p_cylinder_storage;
    struct box_storage      *p_box_storage;
    struct cone_storage     *p_cone_storage;
    struct mesh_storage     *p_mesh_storage;
    // add as many pointers to structs as wanted, without increasing memory footprint.
};


struct rotation_struct{
double x;
double y;
double z;
};

struct focus_data_struct {
Coords Aim;
double angular_focus_width;
double angular_focus_height;
double spatial_focus_width;
double spatial_focus_height;
double spatial_focus_radius;
Rotation absolute_rotation;
// focusing_function creates a vector per selected criteria of focus_data_struct / selected focus function and returns solid angle
void (*focusing_function)(Coords*, double*, struct focus_data_struct*);
//                        v_out  , solid_a,
};

struct focus_data_array_struct
{
struct focus_data_struct *elements;
int num_elements;
#pragma acc shape(elements[0:num_elements]) init_needed(num_elements)
};

struct Detector_3D_struct {
  char title_string[256];
  char string_axis_1[256];
  char string_axis_2[256];
  char string_axis_3[256];
  char Filename[256];
  double D1min;
  double D1max;
  double D2min;
  double D2max;
  double D3min;
  double D3max;
  double bins_1; // McStas uses doubles for bin numbers for some reason
  double bins_2;
  double bins_3;
  double ***Array_N; // McStas uses doubles for number of rays in each bin for some reason
  double ***Array_p;
  double ***Array_p2;
};

struct Detector_2D_struct {
  char title_string[256];
  char string_axis_1[256];
  char string_axis_2[256];
  char Filename[256];
  double D1min;
  double D1max;
  double D2min;
  double D2max;
  double bins_1; // McStas uses doubles for bin numbers for some reason
  double bins_2;
  double **Array_N; // McStas uses doubles for number of rays in each bin for some reason
  double **Array_p;
  double **Array_p2;
};

struct Detector_1D_struct {
  char title_string[256];
  char string_axis[256];
  char string_axis_short[64];
  char string_axis_value[256];
  char Filename[256];
  double min;
  double max;
  double bins; // McStas uses doubles for bin numbers for some reason
  double *Array_N; // McStas uses doubles for number of rays in each bin for some reason
  double *Array_p;
  double *Array_p2;
};


union logger_data_union{
  struct a_2DQ_storage_struct *p_2DQ_storage;
  struct a_2DS_storage_struct *p_2DS_storage;
  struct a_3DS_storage_struct *p_3DS_storage;
  struct a_1D_storage_struct *p_1D_storage;
  struct a_2DS_t_storage_struct *p_2DS_t_storage;
  struct a_2D_kf_storage_struct *p_2D_kf_storage;
  struct a_2D_kf_t_storage_struct *p_2D_kf_t_storage;
  // Additional logger storage structs to be addedd
};

struct logger_with_data_struct {
  int used_elements;
  int allocated_elements;
  struct logger_struct **logger_pointers;
};

// logger_pointer_struct
// contains pointers to the different logger functions and it's data union
struct logger_pointer_set_struct {
  // The logger has two record functions, an active and an inactive. Normally the active one will be to permanent storage,
  //  but if a conditional has been defined, it can switch the two, making the active one recording to temporary, which
  //  can then be filtered based on the future path of the ray
  
  // function input Coords position, k[3], k_old[3], p, p_old, NV, NPV, N, logger_data_union, logger_with_data_struct
  void (*active_record_function)(Coords*, double*, double*, double, double, double, int, int, int, struct logger_struct*, struct logger_with_data_struct*);
  void (*inactive_record_function)(Coords*, double*, double*, double, double, double, int, int, int, struct logger_struct*, struct logger_with_data_struct*);
  
  // A clear temporary data function (for new ray)
  void (*clear_temp)(union logger_data_union*);
  
  // Write temporary to permanent is used when the record to temp function is active, and the condition is met.
  void (*temp_to_perm)(union logger_data_union*);
  
  // Write temporary final_p to permanent is used when the record to temp function is active, and the condition is met
  //  and the final weight is given to be used for all stored events in the logger.
  void (*temp_to_perm_final_p)(union logger_data_union*, double);
  
  // Select which temp_to_perm function to use
  int select_t_to_p; // 1: temp_to_perm, 2: temp_to_perm_final_p
  
};

union abs_logger_data_union{
  struct a_2D_abs_storage_struct *p_2D_abs_storage;
  struct a_1D_abs_storage_struct *p_1D_abs_storage;
  struct a_1D_time_abs_storage_struct *p_1D_time_abs_storage;
  struct a_1D_time_to_lambda_abs_storage_struct *p_1D_time_to_lambda_abs_storage;
  struct a_event_abs_storage_struct *p_event_abs_storage;
  struct a_1D_event_abs_storage_struct *p_1D_event_abs_storage;
  // Additional logger storage structs to be addedd
};

struct abs_logger_with_data_struct {
  int used_elements;
  int allocated_elements;
  struct abs_logger_struct **abs_logger_pointers;
};

struct abs_logger_pointer_set_struct {
  // The logger has two record functions, an active and an inactive. Normally the active one will be to permanent storage,
  //  but if a conditional has been defined, it can switch the two, making the active one recording to temporary, which
  //  can then be filtered based on the future path of the ray
  
  // function input Coords position, k[3], p, NV, N, logger_data_union, logger_with_data_struct
  void (*active_record_function)(Coords*, double*, double,  double, int, int, struct abs_logger_struct*, struct abs_logger_with_data_struct*);
  void (*inactive_record_function)(Coords*, double*, double,  double, int, int, struct abs_logger_struct*, struct abs_logger_with_data_struct*);
  
  // A clear temporary data function (for new ray)
  void (*clear_temp)(union abs_logger_data_union*);
  
  // Write temporary to permanent is used when the record to temp function is active, and the condition is met.
  void (*temp_to_perm)(union abs_logger_data_union*);
  
  // Write temporary final_p to permanent is used when the record to temp function is active, and the condition is met
  //  and the final weight is given to be used for all stored events in the logger.
  void (*temp_to_perm_final_p)(union abs_logger_data_union*, double);
  
  // Select which temp_to_perm function to use
  //int select_t_to_p; // 1: temp_to_perm, 2: temp_to_perm_final_p
  
};


struct conditional_standard_struct{
  // Data to be transfered to the conditional function
  double Emax;
  double Emin;
  int E_limit;
  
  double Tmin;
  double Tmax;
  int T_limit;
  
  int volume_index;
  
  double Total_scat_max;
  double Total_scat_min;
  int Total_scat_limit;
  
  double exit_volume_index;
  
  // Test
  Coords test_position;
  Rotation test_rotation;
  Rotation test_t_rotation;
};

struct conditional_PSD_struct{
  double PSD_half_xwidth;
  double PSD_half_yheight;
  
  double Tmin;
  double Tmax;
  int T_limit;
  
  // Position of the PSD
  Coords PSD_position;
  Rotation PSD_rotation;
  Rotation PSD_t_rotation;
};

union conditional_data_union {
  struct conditional_standard_struct *p_standard;
  struct conditional_PSD_struct *p_PSD;
  // Add more as conditional components are made
};

// General input for conditional functions: Position, Velocity/Wavevector, weight, time, total_scat, scattered_flag, scattered_flag_VP,
// Optional extras: data_union? tree base(s)? tree base for current ray?
typedef int (*conditional_function_pointer)(union conditional_data_union*,Coords*, Coords*, double*, double*, int*, int*, int*, int**);
//typedef int (**conditional_function_pointer_array)(union *conditional_data_union,Coords*, Coords*, double*, double*, int*, int*, int**);

struct conditional_list_struct{
  int num_elements;
  
  union conditional_data_union **p_data_unions;
  conditional_function_pointer *conditional_functions;
  //int (**conditional_functions)(Coords*, Coords*, double*, double*, int*, int*, int**);
};

struct logger_struct {
  char name[256];
  // Contains ponters to all the functions assosiated with this logger
  struct logger_pointer_set_struct function_pointers;
  // Contains hard copy of logger_data_union since the size is the same as a pointer.
  union logger_data_union data_union;
  
  int logger_extend_index; // Contain index conditional_extend_array defined in master that can be acsessed from extend section.
  
  struct conditional_list_struct conditional_list;
};


// To be stored in volume, a list of pointers to the relevant loggers corresponding to each process
struct logger_for_each_process_list {
  int num_elements;
  struct logger_struct **p_logger_process;
};

// List of logger_for_each_process_list
struct loggers_struct {
  int num_elements;
  struct logger_for_each_process_list *p_logger_volume;
  #pragma acc shape(p_logger_volume[0:num_elements]) init_needed(num_elements)
};


struct abs_logger_struct {
  char name[256];
  // Contains ponters to all the functions assosiated with this logger
  struct abs_logger_pointer_set_struct function_pointers;
  // Contains hard copy of logger_data_union since the size is the same as a pointer.
  union abs_logger_data_union data_union;
  
  // Position and rotation of the abs_logger
  Coords position;
  Rotation rotation;
  Rotation t_rotation;
  
  int abs_logger_extend_index; // Contain index conditional_extend_array defined in master that can be acsessed from extend section.
  
  struct conditional_list_struct conditional_list;
};

// To be stored in volume, a list of pointers to the relevant abs loggers corresponding to each process
/*
struct abs_logger_for_each_process_list {
  int num_elements;
  struct abs_logger_struct **p_abs_logger_process;
};
*/

// List of abs logger_for_each_process_list
struct abs_loggers_struct {
  int num_elements;
  //struct abs_logger_for_each_process_list *p_abs_logger_volume;
  struct abs_logger_struct **p_abs_logger;
};



struct geometry_struct
{
char shape[64];     // name of shape used (sphere, cylinder, box, off, ...)
enum shape eShape;  // enum with shape for flexible functions GPU
double priority_value;    // priority of the geometry
Coords center;      // Center position of volume, reported by components in global frame, updated to main frame in initialize
// Rotation of this volume
Rotation rotation_matrix; // rotation matrix of volume, reported by component in global frame, updated to main frame in initialize
Rotation transpose_rotation_matrix; // As above
// Array of prrotation matrixes for processes assigned to this volume (indexed by non_isotropic_rot_index in the processes)
Rotation *process_rot_matrix_array;             // matrix that transforms from main coordinate system to local process in this specific volume
Rotation *transpose_process_rot_matrix_array;   // matrix that transforms from local process in this specific volume to main coordinate system
int process_rot_allocated;  // Keeps track of allocation status of rot_matrix_array

struct rotation_struct rotation; // Not used, is the x y and z rotation angles.
int visualization_on; // If visualization_on is true, the volume will be drawn in mcdisplay, otherwise not
int is_exit_volume; // If is exit volume = 1, the ray will exit the component when it enters this volume.
int is_mask_volume; // 1 if volume itself is a mask (masking the ones in it's mask list), otherwise 0
int mask_index;
int is_masked_volume; // 1 if this volume is being masked by another volume, the volumes that mask it is in masked_by_list
int mask_mode; // ALL/ANY 1/2. In ALL mode, only parts covered by all masks is simulated, in ANY mode, area covered by just one mask is simulated
double geometry_p_interact; // fraction of rays that interact with this volume for each scattering (between 0 and 1, 0 for disable)
union geometry_parameter_union geometry_parameters; // relevant parameters for this shape
union geometry_parameter_union (*copy_geometry_parameters)(union geometry_parameter_union*);
struct focus_data_struct focus_data; // Used for focusing from this geometry

// New focus data implementation to remove the focusing bug for non-isotripic processes
struct focus_data_array_struct focus_data_array;
struct pointer_to_1d_int_list focus_array_indices; // Add 1D integer array with indecies for correct focus_data for each process


// intersect_function takes position/velocity of ray and parameters, returns time list
int (*intersect_function)(double*,int*,double*,double*,struct geometry_struct*);
//                        t_array,n_ar,r      ,v

// within_function that checks if the ray origin is within this volume
int (*within_function)(Coords,struct geometry_struct*);
//                     r,      parameters

// mcdisplay function, draws the geometry
//void (*mcdisplay_function)(struct lines_to_draw*,int,struct Volume_struct**,int);
void (*mcdisplay_function)(struct lines_to_draw*,int,struct geometry_struct**,int);
//                                lines          index             Geometries   N

void (*initialize_from_main_function)(struct geometry_struct*);

struct pointer_to_1d_coords_list (*shell_points)(struct geometry_struct*, int maximum_number_of_points);

// List of other volumes to be check when ray starts within this volume.
struct pointer_to_1d_int_list intersect_check_list;
// List of other volumes the ray may enter, if the ray intersects the volume itself.
struct pointer_to_1d_int_list destinations_list;
// The destinations list stored as a logic list which makes some tasks quicker. OBSOLETE
//struct pointer_to_1d_int_list destinations_logic_list;
// Reduced list of other volumes the ray may enter, if the ray intersects the volume itself.
struct pointer_to_1d_int_list reduced_destinations_list;
// List of other volumes that are within this volume
struct pointer_to_1d_int_list children;
// List of other volumes that are within this volume, but does not have any parents that are children of this volume
struct pointer_to_1d_int_list direct_children;
// List of next possible volumes (only used in tagging)
struct pointer_to_1d_int_list next_volume_list;
// List of volumes masked by this volume (usually empty)
struct pointer_to_1d_int_list mask_list;
// List of volumes masking this volume
struct pointer_to_1d_int_list masked_by_list;
// List of masks masking this volume (global mask indices)
struct pointer_to_1d_int_list masked_by_mask_index_list;
// Additional intersect lists dependent on mask status
//struct indexed_mask_lists_struct mask_intersect_lists;
// Simpler way of storing the mask_intersect_lists
struct pointer_to_1d_int_list mask_intersect_list;
};

struct physics_struct
{
char name[256]; // User defined material name
int interact_control;
int is_vacuum;
double my_a;
int number_of_processes;
// pointer to array of pointers to physics_sub structures that each describe a scattering process
struct scattering_process_struct *p_scattering_array;
};

union data_transfer_union{
    // List of pointers to storage structs for all supported physical processes
    struct Incoherent_physics_storage_struct  *pointer_to_a_Incoherent_physics_storage_struct;
    struct Powder_physics_storage_struct *pointer_to_a_Powder_physics_storage_struct;
    struct Single_crystal_physics_storage_struct *pointer_to_a_Single_crystal_physics_storage_struct;
    struct AF_HB_1D_physics_storage_struct *pointer_to_a_AF_HB_1D_physics_storage_struct;
    struct IncoherentPhonon_physics_storage_struct *pointer_to_a_IncoherentPhonon_physics_storage_struct;
    struct PhononSimpleNumeric_physics_storage_struct *pointer_to_a_PhononSimpleNumeric_storage_struct;
    struct PhononSimple_physics_storage_struct *pointer_to_a_PhononSimple_storage_struct;
    struct MagnonSimple_physics_storage_struct *pointer_to_a_MagnonSimple_storage_struct;
    struct Sans_spheres_physics_storage_struct *pointer_to_a_Sans_spheres_physics_storage_struct;
    struct Texture_physics_storage_struct *pointer_to_a_Texture_physics_storage_struct;
    struct NCrystal_physics_storage_struct *pointer_to_a_NCrystal_physics_storage_struct;
    struct Non_physics_storage_struct *pointer_to_a_Non_physics_storage_struct;
    struct Template_physics_storage_struct *pointer_to_a_Template_physics_storage_struct;
    // possible to add as many structs as wanted, without increasing memory footprint.
};



struct scattering_process_struct
{
char name[256];                // User defined process name
enum process eProcess;         // enum value corresponding to this process GPU
double process_p_interact;     // double between 0 and 1 that describes the fraction of events forced to undergo this process. -1 for disable
int non_isotropic_rot_index;   // -1 if process is isotrpic, otherwise is the index of the process rotation matrix in the volume
Rotation rotation_matrix;      // rotation matrix of process, reported by component in local frame, transformed and moved to volume struct in main

union data_transfer_union data_transfer; // The way to reach the storage space allocated for this process (see examples in process.comp files)

// probability_for_scattering_functions calculates this probability given k_i and parameters
int (*probability_for_scattering_function)(double*,double*,union data_transfer_union,struct focus_data_struct*, _class_particle *_particle);
//                                         prop,   k_i,   ,parameters               , focus data / function

// A scattering_function takes k_i and parameters, returns k_f
int (*scattering_function)(double*,double*,double*,union data_transfer_union,struct focus_data_struct*, _class_particle *_particle);
//                         k_f,    k_i,    weight, parameters               , focus data / function
};

struct Volume_struct
{
char name[256]; // User defined volume name
struct geometry_struct geometry;        // Geometry properties (including intersect functions, generated lists)
struct physics_struct *p_physics;       // Physical properties (list of scattering processes, absorption)
struct loggers_struct loggers;          // Loggers assosiated with this volume
struct abs_loggers_struct abs_loggers;  // Loggers assosiated with this volume
};

// example of calling a scattering process
// volume_pointer_list[3]->physics.scattering_process[5].probability_for_scattering_function(input,volume_pointer_list[3]->physics.scattering_process[5])

struct starting_lists_struct
{
struct pointer_to_1d_int_list allowed_starting_volume_logic_list;
struct pointer_to_1d_int_list reduced_start_list;
struct pointer_to_1d_int_list start_logic_list;
struct pointer_to_1d_int_list starting_destinations_list;
};

struct global_positions_to_transform_list_struct {
int num_elements;
Coords **positions;
};

struct global_rotations_to_transform_list_struct {
int num_elements;
Rotation **rotations;
};

struct global_process_element_struct
{
char name[128]; // Name of the process
int component_index;
struct scattering_process_struct *p_scattering_process;
};

struct pointer_to_global_process_list {
int num_elements;
struct global_process_element_struct *elements;
};

struct global_material_element_struct
{
char name[128];
int component_index;
struct physics_struct *physics;
};

struct pointer_to_global_material_list {
int num_elements;
struct global_material_element_struct *elements;
};

struct global_geometry_element_struct
{
char name[128];
int component_index;
int activation_counter;
int stored_copies;
int active;
struct Volume_struct *Volume;
};

struct pointer_to_global_geometry_list {
int num_elements;
struct global_geometry_element_struct *elements;
};

struct global_logger_element_struct {
char name[128];
int component_index;
struct logger_struct *logger;
};

struct pointer_to_global_logger_list {
int num_elements;
struct global_logger_element_struct *elements;
};

struct global_abs_logger_element_struct {
char name[128];
int component_index;
struct abs_logger_struct *abs_logger;
};

struct pointer_to_global_abs_logger_list {
int num_elements;
struct global_abs_logger_element_struct *elements;
};

struct global_tagging_conditional_element_struct {
struct conditional_list_struct conditional_list;
int extend_index;
char name[1024];
int use_status;
};

struct global_tagging_conditional_list_struct {
int num_elements;
int current_index;
struct global_tagging_conditional_element_struct *elements;
};


struct global_master_element_struct {
char name[128];
int component_index;
int stored_number_of_scattering_events; // TEST
struct conditional_list_struct *tagging_conditional_list_pointer;
};

struct pointer_to_global_master_list {
int num_elements;
struct global_master_element_struct *elements;
};


// -------------    Physics functions   ---------------------------------------------------------

//#include "Test_physics.c"
//#include "Incoherent_test.c"

// -------------    General functions   ---------------------------------------------------------
double distance_between(Coords position1,Coords position2) {
    return sqrt((position1.x-position2.x)*(position1.x-position2.x) +
                (position1.y-position2.y)*(position1.y-position2.y) +
                (position1.z-position2.z)*(position1.z-position2.z));
};



double length_of_3vector(double *r) {
        return sqrt(r[0]*r[0]+r[1]*r[1]+r[2]*r[2]);
    };

double length_of_position_vector(Coords point) {
        return sqrt(point.x*point.x+point.y*point.y+point.z*point.z);
    };

Coords make_position(double *r) {
    Coords temp;
    
    temp.x = r[0];temp.y = r[1];temp.z = r[2];
    return temp;
};

Coords coords_scalar_mult(Coords input,double scalar) {
    return coords_set(scalar*input.x,scalar*input.y,scalar*input.z);
};

double union_coords_dot(Coords vector1,Coords vector2) {
    return vector1.x*vector2.x + vector1.y*vector2.y + vector1.z*vector2.z;
}


int sum_int_list(struct pointer_to_1d_int_list list) {
    int iterate,sum = 0;
    for (iterate = 0;iterate < list.num_elements;iterate++) sum += list.elements[iterate];
    return sum;
    };
    
int on_int_list(struct pointer_to_1d_int_list list,int target) {
    int iterate,output=0;
    for (iterate = 0; iterate<list.num_elements ;iterate++) {
        if (list.elements[iterate] == target) output = 1;
    }
    return output;
    };

int find_on_int_list(struct pointer_to_1d_int_list list,int target) {
    int iterate;
    for (iterate = 0;iterate < list.num_elements;iterate++) {
        if (list.elements[iterate] == target) return iterate;
    }
    return -1;
    };
    
void on_both_int_lists(struct pointer_to_1d_int_list *list1, struct pointer_to_1d_int_list *list2, struct pointer_to_1d_int_list *common) {
    // Assume common.elements is allocated to a number of int's large enough to hold the resulting list
    int iterate,used_elements=0;
    for (iterate=0;iterate<list1->num_elements;iterate++) {
        if (on_int_list(*list2,list1->elements[iterate])) common->elements[used_elements++] = list1->elements[iterate];
    }
    common->num_elements = used_elements;
    };

void remove_element_in_list_by_index(struct pointer_to_1d_int_list *list,int index) {
    if (index >= list->num_elements) {
      printf("ERROR(remove_element_in_list_by_index): trying to remove an index that wasn't allocated to begin with");
      exit(1);
    }
    else {
        int iterate;
        int *temp;
        for (iterate = index;iterate < list->num_elements -1;iterate++) {
            list->elements[iterate] = list->elements[iterate+1];
        }
        list->num_elements--;
        //if (list->num_elements==0) printf("Making empty list!\n");
        temp = malloc(list->num_elements * sizeof(int));
        for (iterate = 0;iterate < list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
        free(list->elements);
        list->elements = malloc(list->num_elements * sizeof(int));
        for (iterate = 0;iterate < list->num_elements;iterate++) list->elements[iterate] = temp[iterate];
        free(temp);

    }
    };
    
void remove_element_in_list_by_value(struct pointer_to_1d_int_list *list,int value) {
    int iterate;
    for (iterate = 0;iterate < list->num_elements;iterate++) {
        if (list->elements[iterate] == value) remove_element_in_list_by_index(list,iterate);
    }
    };
    
void merge_lists(struct pointer_to_1d_int_list *result,struct pointer_to_1d_int_list *list1,struct pointer_to_1d_int_list *list2) {
    if (result->num_elements > 0) free(result->elements);
    result->num_elements = list1->num_elements + list2->num_elements;
    if (result->num_elements != 0) {
      result->elements = malloc(result->num_elements*sizeof(int));
      int iterate;
      for (iterate = 0;iterate < list1->num_elements;iterate++)
          result->elements[iterate] = list1->elements[iterate];
      for (iterate = 0;iterate < list2->num_elements;iterate++)
          result->elements[list1->num_elements+iterate] = list2->elements[iterate];
    }
    };

void add_element_to_double_list(struct pointer_to_1d_double_list *list,double value) {
    if (list->num_elements == 0) {
      list->num_elements++;
      list-> elements = malloc(list->num_elements*sizeof(double));
      list-> elements[0] = value;
    } else {
      double temp[list->num_elements];
      int iterate;
      for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
      free(list->elements);
      list->num_elements++;
      list-> elements = malloc(list->num_elements*sizeof(double));
      for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
      list->elements[list->num_elements-1] = value;
    }
    };

void add_element_to_int_list(struct pointer_to_1d_int_list *list,int value) {
    if (list->num_elements == 0) {
      list->num_elements++;
      list-> elements = malloc(list->num_elements*sizeof(int));
      list-> elements[0] = value;
    } else {
      int temp[list->num_elements];
      int iterate;
      for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
      free(list->elements);
      list->num_elements++;
      list-> elements = malloc(list->num_elements*sizeof(int));
      for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
      list->elements[list->num_elements-1] = value;
    }
    };

// Need to check if absolute_rotation is preserved correctly.
void add_element_to_focus_data_array(struct focus_data_array_struct *focus_data_array,struct focus_data_struct focus_data) {
    if (focus_data_array->num_elements == 0) {
      focus_data_array->num_elements++;
      focus_data_array-> elements = malloc(focus_data_array->num_elements*sizeof(struct focus_data_struct));
      focus_data_array-> elements[0] = focus_data;
    } else {
      struct focus_data_struct temp[focus_data_array->num_elements];
      int iterate;
      for (iterate=0;iterate<focus_data_array->num_elements;iterate++) temp[iterate] = focus_data_array->elements[iterate];
      free(focus_data_array->elements);
      focus_data_array->num_elements++;
      focus_data_array-> elements = malloc(focus_data_array->num_elements*sizeof(struct focus_data_struct));
      for (iterate=0;iterate<focus_data_array->num_elements-1;iterate++) focus_data_array->elements[iterate] = temp[iterate];
      focus_data_array->elements[focus_data_array->num_elements-1] = focus_data;
    }
    };


void add_to_logger_with_data(struct logger_with_data_struct *logger_with_data, struct logger_struct *logger) {
    // May reorder the order of the if conditions to avoid checking the == 0 for every single ray
    if (logger_with_data->allocated_elements == 0) {
        logger_with_data->allocated_elements = 5;
        logger_with_data->logger_pointers = malloc(logger_with_data->allocated_elements*sizeof(struct logger_struct*));
        logger_with_data->used_elements = 1;
        logger_with_data->logger_pointers[0] = logger;
    } else if (logger_with_data->used_elements > logger_with_data->allocated_elements-1) {
        struct logger_with_data_struct temp_logger_with_data;
        temp_logger_with_data.logger_pointers = malloc((logger_with_data->used_elements)*sizeof(struct logger_struct*));
        int iterate;
        for (iterate=0;iterate<logger_with_data->used_elements;iterate++) {
            temp_logger_with_data.logger_pointers[iterate]  = logger_with_data->logger_pointers[iterate];
        }
        free(logger_with_data->logger_pointers);
        logger_with_data->allocated_elements = logger_with_data->allocated_elements+5;
        logger_with_data->logger_pointers = malloc(logger_with_data->allocated_elements*sizeof(struct logger_struct*));
        for (iterate=0;iterate<logger_with_data->used_elements;iterate++) {
            logger_with_data->logger_pointers[iterate]  = temp_logger_with_data.logger_pointers[iterate];
        }

        logger_with_data->logger_pointers[logger_with_data->used_elements++] = logger;
        
        
    } else {
        logger_with_data->logger_pointers[logger_with_data->used_elements++] = logger;
    }
    
};

void add_to_abs_logger_with_data(struct abs_logger_with_data_struct *abs_logger_with_data, struct abs_logger_struct *abs_logger) {
    // May reorder the order of the if conditions to avoid checking the == 0 for every single ray
    if (abs_logger_with_data->allocated_elements == 0) {
        abs_logger_with_data->allocated_elements = 5;
        abs_logger_with_data->abs_logger_pointers = malloc(abs_logger_with_data->allocated_elements*sizeof(struct abs_logger_struct*));
        abs_logger_with_data->used_elements = 1;
        abs_logger_with_data->abs_logger_pointers[0] = abs_logger;
    } else if (abs_logger_with_data->used_elements > abs_logger_with_data->allocated_elements-1) {
        struct abs_logger_with_data_struct temp_abs_logger_with_data;
        temp_abs_logger_with_data.abs_logger_pointers = malloc((abs_logger_with_data->used_elements)*sizeof(struct abs_logger_struct*));
        int iterate;
        for (iterate=0;iterate<abs_logger_with_data->used_elements;iterate++) {
            temp_abs_logger_with_data.abs_logger_pointers[iterate]  = abs_logger_with_data->abs_logger_pointers[iterate];
        }
        free(abs_logger_with_data->abs_logger_pointers);
        abs_logger_with_data->allocated_elements = abs_logger_with_data->allocated_elements+5;
        abs_logger_with_data->abs_logger_pointers = malloc(abs_logger_with_data->allocated_elements*sizeof(struct abs_logger_struct*));
        for (iterate=0;iterate<abs_logger_with_data->used_elements;iterate++) {
            abs_logger_with_data->abs_logger_pointers[iterate] = temp_abs_logger_with_data.abs_logger_pointers[iterate];
        }

        abs_logger_with_data->abs_logger_pointers[abs_logger_with_data->used_elements++] = abs_logger;
        
        
    } else {
        abs_logger_with_data->abs_logger_pointers[abs_logger_with_data->used_elements++] = abs_logger;
    }
    
};


// Used typedef to avoid having to change this function later. May update others to use same phillosphy.
void add_function_to_conditional_list(struct conditional_list_struct *list,conditional_function_pointer new, union conditional_data_union *data_union) {
    if (list->num_elements == 0) {
      list->num_elements++;
      list->conditional_functions = malloc(list->num_elements*sizeof(conditional_function_pointer));
      list->p_data_unions = malloc(list->num_elements*sizeof(union conditional_data_union*));
      list->conditional_functions[0] = new;
      list->p_data_unions[0] = data_union;
    }
    else {
    conditional_function_pointer temp_fp[list->num_elements];
    union conditional_data_union *temp_du[list->num_elements];
    int iterate;
    // Could even get away with a shallow copy here instead of the loop, but it is not relevant for performance.
    for (iterate=0;iterate<list->num_elements;iterate++) {
      temp_fp[iterate] = list->conditional_functions[iterate];
      temp_du[iterate] = list->p_data_unions[iterate];
    }
    free(list->conditional_functions);
    free(list->p_data_unions);
    list->num_elements++;
    list->conditional_functions = malloc(list->num_elements*sizeof(conditional_function_pointer));
    list->p_data_unions = malloc(list->num_elements*sizeof(union conditional_data_union*));
    
    for (iterate=0;iterate<list->num_elements-1;iterate++) {
      list->conditional_functions[iterate] = temp_fp[iterate];
      list->p_data_unions[iterate] = temp_du[iterate];
    }
    list->conditional_functions[list->num_elements-1] = new;
    list->p_data_unions[list->num_elements-1] = data_union;
    }
};


// could make function that removes a element from a 1d_int_list, and have each list generation as a function that takes a copy of an overlap list

void print_1d_int_list(struct pointer_to_1d_int_list list,char *name) {
        int iterate;
        printf("LIST: ");printf("%s",name);printf(" = [");
        for (iterate = 0; iterate < list.num_elements; iterate++) {
            printf("%d",list.elements[iterate]);
            if (iterate < list.num_elements - 1) printf(",");
        }
        printf("]\n");
    };

void print_1d_double_list(struct pointer_to_1d_double_list list,char *name) {
        int iterate;
        printf("LIST: ");printf("%s",name);printf(" = [");
        for (iterate = 0; iterate < list.num_elements; iterate++) {
            printf("%f",list.elements[iterate]);
            if (iterate < list.num_elements - 1) printf(",");
        }
        printf("]\n");
    };

void print_position(Coords pos,char *name) {
    printf("POSITION: ");printf("%s",name);printf(" = (%f,%f,%f)\n",pos.x,pos.y,pos.z);
    };

void print_rotation(Rotation rot, char *name) {
    printf("ROT MATRIX: %s \n",name);
    printf("[%f %f %f]\n",rot[0][0],rot[0][1],rot[0][2]);
    printf("[%f %f %f]\n",rot[1][0],rot[1][1],rot[1][2]);
    printf("[%f %f %f]\n\n",rot[2][0],rot[2][1],rot[2][2]);
};
    
void allocate_list_from_temp(int num_elements,struct pointer_to_1d_int_list original,struct pointer_to_1d_int_list *new) {
        int iterate;
    
        new->num_elements = num_elements;
        if (num_elements > 0) {
            new->elements = malloc(num_elements*sizeof(int));
            for (iterate = 0;iterate < num_elements; iterate++) new->elements[iterate] = original.elements[iterate];
        } else new->elements = NULL;
    
    };

void allocate_logic_list_from_temp(int num_elements,struct pointer_to_1d_int_list original, struct pointer_to_1d_int_list *new) {
        // A logic list shares the same structure of a normal list, but instead of listing numbers, it is a list of yes / no (1/0)
        // Giving this function a list of [1 3 5] (and num_elements = 9) would return [0 1 0 1 0 1 0 0 0];
        int iterate;
    
        new->num_elements = num_elements;
        if (num_elements > 0) {
            new->elements = malloc(num_elements*sizeof(int));
            for (iterate = 0;iterate < num_elements;iterate++) new->elements[iterate] = 0;
            for (iterate = 0;iterate < original.num_elements;iterate++) {
                if (original.elements[iterate] < num_elements)
                    new->elements[original.elements[iterate]] = 1;
                else printf("Trying to allocate logical list without enough memory\n");
            }
        } else new->elements = NULL;
    };

/*
struct global_positions_to_transform_list_struct {
int num_elements;
Coords **positions;
}

struct global_rotations_to_transform_list_struct {
int num_elements;
Rotation **rotations;
}
*/

void add_position_pointer_to_list(struct global_positions_to_transform_list_struct *list, Coords *new_position_pointer) {
    if (list->num_elements == 0) {
      list->num_elements++;
      list->positions = malloc(list->num_elements*sizeof(Coords*));
      list->positions[0] = new_position_pointer;
    } else {
      Coords **temp;
      temp = malloc(list->num_elements*sizeof(Coords*));
      if (temp == NULL) printf("malloc failed in add_position_pointer_to_list for temp\n");
      
      int iterate;
      for (iterate=0;iterate<list->num_elements;iterate++)
        temp[iterate] = list->positions[iterate];
      free(list->positions);
      list->num_elements++;
      list->positions = malloc(list->num_elements*sizeof(Coords*));
      if (list->positions == NULL) printf("malloc failed in add_position_pointer_to_list for list->positions\n");
      
      for (iterate=0;iterate<list->num_elements-1;iterate++)
        list->positions[iterate] = temp[iterate];
      free(temp);
      list->positions[list->num_elements-1] = new_position_pointer;
    }
};

void add_rotation_pointer_to_list(struct global_rotations_to_transform_list_struct *list, Rotation *new_rotation_pointer) {
    if (list->num_elements == 0) {
      list->num_elements++;
      list->rotations = malloc(list->num_elements*sizeof(Rotation*));
      list->rotations[0] = new_rotation_pointer;
    } else {
      Rotation **temp;
      temp = malloc(list->num_elements*sizeof(Rotation*));
      int iterate;
      for (iterate=0;iterate<list->num_elements;iterate++)
        temp[iterate] = list->rotations[iterate];
      free(list->rotations);
      list->num_elements++;
      list->rotations = malloc(list->num_elements*sizeof(Rotation*));
      
      for (iterate=0;iterate<list->num_elements-1;iterate++)
        list->rotations[iterate] = temp[iterate];
      free(temp);
      list->rotations[list->num_elements-1] = new_rotation_pointer;
    }
};

void add_element_to_process_list(struct pointer_to_global_process_list *list,struct global_process_element_struct new_element) {
    if (list->num_elements == 0) {
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_process_element_struct));
    list-> elements[0] = new_element;
    }
    else {
    struct global_process_element_struct temp[list->num_elements];
    int iterate;
    for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
    free(list->elements);
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_process_element_struct));
    for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
    list->elements[list->num_elements-1] = new_element;
    }
};

void add_element_to_material_list(struct pointer_to_global_material_list *list,struct global_material_element_struct new_element) {
    if (list->num_elements == 0) {
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_material_element_struct));
    list-> elements[0] = new_element;
    }
    else {
    struct global_material_element_struct temp[list->num_elements];
    int iterate;
    for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
    free(list->elements);
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_material_element_struct));
    for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
    list->elements[list->num_elements-1] = new_element;
    }
};

void add_element_to_geometry_list(struct pointer_to_global_geometry_list *list,struct global_geometry_element_struct new_element) {
    if (list->num_elements == 0) {
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_geometry_element_struct));
    list-> elements[0] = new_element;
    }
    else {
    struct global_geometry_element_struct temp[list->num_elements];
    int iterate;
    for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
    free(list->elements);
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_geometry_element_struct));
    for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
    list->elements[list->num_elements-1] = new_element;
    }
};

void add_element_to_logger_list(struct pointer_to_global_logger_list *list,struct global_logger_element_struct new_element) {
    if (list->num_elements == 0) {
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_logger_element_struct));
    list-> elements[0] = new_element;
    }
    else {
    struct global_logger_element_struct temp[list->num_elements];
    int iterate;
    for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
    free(list->elements);
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_logger_element_struct));
    for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
    list->elements[list->num_elements-1] = new_element;
    }
};

void add_element_to_abs_logger_list(struct pointer_to_global_abs_logger_list *list, struct global_abs_logger_element_struct new_element) {
    if (list->num_elements == 0) {
      list->num_elements++;
      list->elements = malloc(list->num_elements*sizeof(struct global_abs_logger_element_struct));
      list->elements[0] = new_element;
    }
    else {
      struct global_abs_logger_element_struct temp[list->num_elements];
      int iterate;
      for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
      free(list->elements);
      list->num_elements++;
      list-> elements = malloc(list->num_elements*sizeof(struct global_abs_logger_element_struct));
      for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
      list->elements[list->num_elements-1] = new_element;
    }
};

void add_element_to_tagging_conditional_list(struct global_tagging_conditional_list_struct *list,struct global_tagging_conditional_element_struct new_element) {
    if (list->num_elements == 0) {
    list->num_elements++;
    list->elements = malloc(list->num_elements*sizeof(struct global_tagging_conditional_element_struct));
    list->elements[0] = new_element;
    }
    else {
    struct global_tagging_conditional_element_struct temp[list->num_elements];
    int iterate;
    for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
    free(list->elements);
    list->num_elements++;
    list->elements = malloc(list->num_elements*sizeof(struct global_tagging_conditional_element_struct));
    for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
    list->elements[list->num_elements-1] = new_element;
    }
};

void add_element_to_master_list(struct pointer_to_global_master_list *list,struct global_master_element_struct new_element) {
    if (list->num_elements == 0) {
    list->num_elements++;
    list->elements = malloc(list->num_elements*sizeof(struct global_master_element_struct));
    list->elements[0] = new_element;
    }
    else {
    struct global_master_element_struct temp[list->num_elements];
    int iterate;
    for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
    free(list->elements);
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_master_element_struct));
    for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
    list->elements[list->num_elements-1] = new_element;
    }
};

void add_initialized_logger_in_volume(struct loggers_struct *loggers,int number_of_processes) {
  int iterate;
  if (loggers->num_elements == 0) {
    loggers->num_elements++;
    loggers->p_logger_volume = malloc(loggers->num_elements * sizeof(struct logger_for_each_process_list));
    loggers->p_logger_volume[0].num_elements = number_of_processes;
    loggers->p_logger_volume[0].p_logger_process = malloc(number_of_processes * sizeof(struct logger_struct**));
    for (iterate=0;iterate<number_of_processes;iterate++)
      loggers->p_logger_volume[0].p_logger_process[iterate] = NULL;
  } else {
    // Already some elements, store them in temp, free main, transfer back and add newest.
    struct logger_for_each_process_list temp[loggers->num_elements];
    
    for (iterate=0;iterate<loggers->num_elements;iterate++) temp[iterate] = loggers->p_logger_volume[iterate];
    free(loggers->p_logger_volume);
    loggers->num_elements++;
    loggers->p_logger_volume = malloc(loggers->num_elements*sizeof(struct logger_for_each_process_list));
    for (iterate=0;iterate<loggers->num_elements-1;iterate++) loggers->p_logger_volume[iterate] = temp[iterate];
    loggers->p_logger_volume[loggers->num_elements-1].num_elements = number_of_processes;
    loggers->p_logger_volume[loggers->num_elements-1].p_logger_process = malloc(number_of_processes * sizeof(struct logger_struct**));
    for (iterate=0;iterate<number_of_processes;iterate++) {
      loggers->p_logger_volume[loggers->num_elements-1].p_logger_process[iterate] = NULL;
    }
  }
};

/*
void add_initialized_abs_logger_in_volume(struct abs_loggers_struct *abs_loggers, int number_of_processes) {
  int iterate;
  if (abs_loggers->num_elements == 0) {
    abs_loggers->num_elements++;
    abs_loggers->p_abs_logger_volume = malloc(abs_loggers->num_elements * sizeof(struct abs_loggers_struct));
    abs_loggers->p_abs_logger_volume[0].num_elements = number_of_processes;
    abs_loggers->p_abs_logger_volume[0].p_abs_logger_process = malloc(number_of_processes * sizeof(struct abs_logger_struct**));
    for (iterate=0;iterate<number_of_processes;iterate++)
      abs_loggers->p_abs_logger_volume[0].p_abs_logger_process[iterate] = NULL;
  } else {
    // Already some elements, store them in temp, free main, transfer back and add newest.
    struct abs_logger_for_each_process_list temp[abs_loggers->num_elements];
    
    for (iterate=0;iterate<abs_loggers->num_elements;iterate++) temp[iterate] = abs_loggers->p_abs_logger_volume[iterate];
    free(abs_loggers->p_abs_logger_volume);
    abs_loggers->num_elements++;
    abs_loggers->p_abs_logger_volume = malloc(abs_loggers->num_elements*sizeof(struct abs_logger_for_each_process_list));
    for (iterate=0;iterate<abs_loggers->num_elements-1;iterate++) abs_loggers->p_abs_logger_volume[iterate] = temp[iterate];
    abs_loggers->p_abs_logger_volume[abs_loggers->num_elements-1].num_elements = number_of_processes;
    abs_loggers->p_abs_logger_volume[abs_loggers->num_elements-1].p_abs_logger_process = malloc(number_of_processes * sizeof(struct abs_logger_struct**));
    for (iterate=0;iterate<number_of_processes;iterate++) {
      abs_loggers->p_abs_logger_volume[abs_loggers->num_elements-1].p_abs_logger_process[iterate] = NULL;
    }
  }
};
*/

void add_initialized_abs_logger_in_volume(struct abs_loggers_struct *abs_loggers) {
  int iterate;
  if (abs_loggers->num_elements == 0) {
    abs_loggers->num_elements++;
    abs_loggers->p_abs_logger = malloc(abs_loggers->num_elements * sizeof(struct abs_logger_struct*));
  } else {
    // Already some elements, store them in temp, free main, transfer back and add newest.
    struct abs_logger_struct *temp[abs_loggers->num_elements];
    
    for (iterate=0;iterate<abs_loggers->num_elements;iterate++) temp[iterate] = abs_loggers->p_abs_logger[iterate];
    free(abs_loggers->p_abs_logger);
    
    abs_loggers->num_elements++;
    abs_loggers->p_abs_logger = malloc(abs_loggers->num_elements*sizeof(struct abs_logger_struct*));
    for (iterate=0;iterate<abs_loggers->num_elements-1;iterate++) abs_loggers->p_abs_logger[iterate] = temp[iterate];
    abs_loggers->p_abs_logger[abs_loggers->num_elements-1] = NULL;
    
  }
};


// -------------    Functions used to shorten master trace    ---------------------------------------------


void update_current_mask_intersect_status(struct pointer_to_1d_int_list *current_mask_intersect_list_status, struct pointer_to_1d_int_list *mask_status_list, struct Volume_struct **Volumes, int *current_volume) {
  // This function is to be executed whenever the current volume changes, or the mask status changes
  // It updates the effective mask status for each element of a volumes mask_intersect_list
  // The effective mask takes the ALL/ANY mode assosiated with each volume into account,
  //  meaning a volume needs to be within ALL/ANY of it's masks to have a status of 1
  // In most cases this mask_intersect_list will be empty, and memory operations are avoided
  //printf("Number of elements to be check: %d \n",Volumes[*current_volume]->geometry.mask_intersect_list.num_elements);
  
  if (Volumes[*current_volume]->geometry.mask_intersect_list.num_elements > 0) {
    int iterate,this_element,*mask_start,*mask_check;
    for (iterate=0;iterate<Volumes[*current_volume]->geometry.mask_intersect_list.num_elements;iterate++) {
      this_element = Volumes[*current_volume]->geometry.mask_intersect_list.elements[iterate];
      //printf("We are investigating volume number %d from the mask_intersect_list of volume %d",this_element,*current_volume);
      if (Volumes[this_element]->geometry.mask_mode == 2) { // ANY mask mode
        //printf("We are in ANY mask mode! \n");
        current_mask_intersect_list_status->elements[iterate] = 0; // Assume the mask status is 0, but if any are 1, take that instead
        for (mask_start=mask_check=Volumes[this_element]->geometry.masked_by_mask_index_list.elements;mask_check-mask_start<Volumes[this_element]->geometry.masked_by_mask_index_list.num_elements;mask_check++) {
          //printf("Checking all the mask statuses of volumes masking %d, now mask with global mask index %d ! \n",this_element,*mask_check);
          if (mask_status_list->elements[*mask_check] == 1) {
            //printf("The status was 1, so the effective status is set to 1 and the loop is stopped");
            current_mask_intersect_list_status->elements[iterate] = 1;
            break;
          }
        }
      } else { // ALL mask mode
        //printf("We are in ALL mask mode! \n");
        current_mask_intersect_list_status->elements[iterate] = 1; // Assume the mask status is 1, but if any one is 0, take that instead
        for (mask_start=mask_check=Volumes[this_element]->geometry.masked_by_mask_index_list.elements;mask_check-mask_start<Volumes[this_element]->geometry.masked_by_mask_index_list.num_elements;mask_check++) {
          //printf("Checking all the mask statuses of volumes masking %d, now mask with global mask index %d ! \n",this_element,*mask_check);
          if (mask_status_list->elements[*mask_check] == 0) {
            //printf("The status was 0, so the effective status is set to 0 and the loop is stopped \n");
            current_mask_intersect_list_status->elements[iterate] = 0;
            break;
          }
        }
      }
    }
  }
    
  /* Original version compatible with trace scope
  for (iterate=0;iterate<Volumes[current_volume]->geometry.mask_intersect_list.num_elements;iterate++) {
    this_element = Volumes[current_volume]->geometry.mask_intersect_list.elements[iterate];
    if (Volumes[this_element]->geometry.mask_mode == 2) { // ANY mask mode
      current_mask_intersect_list_status.elements[iterate] = 0; // Assume the mask status is 0, but if any are 1, take that instead
      for (mask_start=mask_check=Volumes[this_element]->geometry.masked_by_mask_index_list.elements;mask_check-mask_start<Volumes[this_element]->geometry.masked_by_mask_index_list.num_elements;mask_check++) {
        if (mask_status_list[*mask_check] == 1) {
          current_mask_intersect_list_status.elements[iterate] = 1;
          break;
        }
      }
    } else { // ALL mask mode
      current_mask_intersect_list_status.elements[iterate] = 1; // Assume the mask status is 1, but if any one is 0, take that instead
      for (mask_start=mask_check=Volumes[this_element]->geometry.masked_by_mask_index_list.elements;mask_check-mask_start<Volumes[this_element]->geometry.masked_by_mask_index_list.num_elements;mask_check++) {
        if (mask_status_list[*mask_check] == 0) {
          current_mask_intersect_list_status.elements[iterate] = 0;
          break;
        }
      }
    }
  }
  */
}

// -------------    Tagging functions    ------------------------------------------------------------------

struct tagging_tree_node_struct {
  // Statistics:
  double intensity;
  int number_of_rays;
  // tree pointers
  struct tagging_tree_node_struct *above;   // Pointer to node above
  struct tagging_tree_node_struct **volume_branches;
  struct tagging_tree_node_struct **process_branches;
};

struct list_of_tagging_tree_node_pointers {
  struct tagging_tree_node_struct **elements;
  int num_elements;
};

struct tagging_tree_node_struct *make_tagging_tree_node(void) {
    return (struct tagging_tree_node_struct *) malloc(sizeof(struct tagging_tree_node_struct));
}


struct tagging_tree_node_struct  *simple_initialize_tagging_tree_node(struct tagging_tree_node_struct *new_node) {
    //struct tagging_tree_node_struct *dummy;
    //dummy = malloc(sizeof(struct tagging_tree_node_struct));
    //new_node = dummy;
    
    //new_node->element = (struct tagging_tree_node_struct *) malloc(sizeof(struct tagging_tree_node_struct));
    //new_node = (struct tagging_tree_node_struct *) malloc(sizeof(struct tagging_tree_node_struct));
    new_node = make_tagging_tree_node();
    
    if (new_node == NULL) printf("ERROR, Union tagging system could not allocate memory\n");
    new_node->intensity = 4.2; // (double) 4.2;
    new_node->number_of_rays = 42; //(int) 42;
    
    printf("new_node->intensity = %f, new_node->number_of_rays = %d \n",new_node->intensity,new_node->number_of_rays);
    return new_node;
};


struct tagging_tree_node_struct *initialize_tagging_tree_node(struct tagging_tree_node_struct *new_node, struct tagging_tree_node_struct *above_node, struct Volume_struct *this_volume) {
    new_node = make_tagging_tree_node();
    
    new_node->intensity = (double) 0;
    new_node->number_of_rays = (int) 0;
    new_node->above = above_node;
    
    int next_volume_list_length = this_volume->geometry.next_volume_list.num_elements;
    new_node->volume_branches = malloc(next_volume_list_length*sizeof(struct tagging_tree_node_struct*));
    int iterate;
    // Initializing pointers so that they can be checked for NULL later. Is this redundant? Does malloc return null pointers?
    for (iterate=0;iterate<next_volume_list_length;iterate++) new_node->volume_branches[iterate] = NULL;
    //new_node->volume_branches.num_elements = dest_list_length; // May be removed
    
    int number_of_processes;
    if (this_volume->p_physics == NULL) number_of_processes = 0;
    else number_of_processes = this_volume->p_physics->number_of_processes;
    
    new_node->process_branches = malloc(number_of_processes*sizeof(struct tagging_tree_node_struct*));
    // Initializing pointers so that they can be checked for NULL later. Is this redundant? Does malloc return null pointers?
    for (iterate=0;iterate<number_of_processes;iterate++) new_node->process_branches[iterate] = NULL;
    //new_node->process_branches.num_elements=number_of_processes; // May be removed
    
    return new_node;
};

struct tagging_tree_node_struct *goto_process_node(struct tagging_tree_node_struct *current_node, int process_index, struct Volume_struct *this_volume, int *stop_tagging_ray, int stop_creating_nodes) {
    // Either create a new node if it has not been created yet, or travel down the tree
    if (current_node->process_branches[process_index] == NULL) {
      if (stop_creating_nodes == 0) {
        current_node->process_branches[process_index] = initialize_tagging_tree_node(current_node->process_branches[process_index],current_node,this_volume);
        return current_node->process_branches[process_index];
      } else {
        // This stops the ray from using more goto node functions and being counted in the statistics.
        // Happens because the history limit is reached, and no new histories should be started.
        *stop_tagging_ray = 1;
        return current_node;
      }
    } else {
        current_node = current_node->process_branches[process_index];
        return current_node;
    }
    
    //return current_node;
};

struct tagging_tree_node_struct *goto_volume_node(struct tagging_tree_node_struct *current_node,int current_volume, int next_volume, struct Volume_struct **Volumes, int *stop_tagging_ray, int stop_creating_nodes) {
    // I have only allocated the number of node branches that corresponds to the current_volumes next_volume_list
    // The problem is to find where on the destination list the current volume is without doing a manual search
    
    // With the new mask system there is a risk of going from and to the same volume, which should be ignored by the tagging system
    if (current_volume == next_volume) return current_node;
    
    struct tagging_tree_node_struct *output;
    int next_volume_list_index = -1;
    // Temporary slow method for finding the correct index on the destination list
    int iterate;
    
    for (iterate=0;iterate<Volumes[current_volume]->geometry.next_volume_list.num_elements;iterate++)
        if (Volumes[current_volume]->geometry.next_volume_list.elements[iterate] == next_volume) {
            next_volume_list_index = iterate;
            break;
        }
    
    // Debug phase
    //printf("Tagging: going from volume %d to volume %d, which is index number %d on it's next volume list \n",current_volume,next_volume,next_volume_list_index);
    #ifndef OPENACC
    if (next_volume_list_index == -1) {
        printf("ERROR in Union component, tagging or destination system failed, next volume was not on next volume list\n");
        printf("current_volume = %d, next_volume = %d \n",current_volume,next_volume);
        print_1d_int_list(Volumes[current_volume]->geometry.destinations_list,"destinations_list for current volume");
        exit(1);
    }
    #endif

    // Either create a new node if it has not been created yet, or travel down the tree
    if (current_node->volume_branches[next_volume_list_index] == NULL) {
      if (stop_creating_nodes == 0) {
        current_node->volume_branches[next_volume_list_index] =  initialize_tagging_tree_node(current_node->volume_branches[next_volume_list_index],current_node,Volumes[next_volume]);
        return current_node->volume_branches[next_volume_list_index];
      } else {
        // This stops the ray from using more goto node functions and being counted in the statistics.
        // Happens because the history limit is reached, and no new histories should be started.
        *stop_tagging_ray = 1;
        return current_node;
      }
    } else {
        //current_node = current_node->volume_branches[next_volume_list_index];
        //return current_node;
        current_node = current_node->volume_branches[next_volume_list_index];
        //printf("used allocated node \n");
        return current_node;
    }
};

void add_statistics_to_node(struct tagging_tree_node_struct *current_node, Coords *r, Coords *v, double *weight, int *counter) {
    if (current_node->number_of_rays == 0) (*counter)++;
    current_node->number_of_rays = current_node->number_of_rays + 1;
    current_node->intensity = current_node->intensity + *weight;
};

struct history_node_struct {
    int volume_index;
    int process_index;
};

struct dynamic_history_list {
  struct history_node_struct *elements;
  int used_elements;
  int allocated_elements;
};

struct saved_history_struct {
    struct history_node_struct *elements;
    int used_elements;
    double intensity;
    int number_of_rays;
};

struct total_history_struct {
    struct saved_history_struct *saved_histories;
    int used_elements;
    int allocated_elements;
};


void add_to_history(struct dynamic_history_list *history, int volume_index, int process_index) {
    //printf("Adding to history[%d]: volume_index = %d, process_index = %d \n",history->used_elements,volume_index,process_index);
    if (history->allocated_elements == 0) {
        history->elements = malloc(5*sizeof(struct history_node_struct));
        history->used_elements = 1;
        history->allocated_elements = 5;
        history->elements[0].volume_index = volume_index;
        history->elements[0].process_index = process_index;
    } else if (history->used_elements > history->allocated_elements-1) {
        struct dynamic_history_list temp_history;
        temp_history.elements = malloc((history->used_elements)*sizeof(struct history_node_struct));
        int iterate;
        for (iterate=0;iterate<history->used_elements;iterate++) {
            temp_history.elements[iterate].volume_index  = history->elements[iterate].volume_index;
            temp_history.elements[iterate].process_index = history->elements[iterate].process_index;
        }
        free(history->elements);
        history->elements = malloc((history->allocated_elements+5)*sizeof(struct history_node_struct));
        for (iterate=0;iterate<history->used_elements;iterate++) {
            history->elements[iterate].volume_index  = temp_history.elements[iterate].volume_index;
            history->elements[iterate].process_index = temp_history.elements[iterate].process_index;
        }
        
        history->allocated_elements = history->allocated_elements+5;
        
        history->elements[history->used_elements].volume_index = volume_index;
        history->elements[history->used_elements].process_index = process_index;
        history->used_elements = history->used_elements+1;
        
    } else {
        history->elements[history->used_elements].volume_index = volume_index;
        history->elements[history->used_elements].process_index = process_index;
        history->used_elements++;
    }
    
};

void printf_history(struct dynamic_history_list *history) {
    int history_iterate;
          //printf("History number %d, intensity = %f, number of rays = %d:",hist_num, search_node->intensity, search_node->number_of_rays);
          for (history_iterate=0;history_iterate<history->used_elements-1;history_iterate++) {
            if (history->elements[history_iterate].process_index == -1) {
                printf(" V%d ->",history->elements[history_iterate].volume_index);
            } else {
                printf(" P%d ->",history->elements[history_iterate].process_index);
            }
          }
          if (history->elements[history_iterate].process_index == -1) {
                printf(" V%d \n",history->elements[history_iterate].volume_index);
          } else {
                printf(" P%d \n",history->elements[history_iterate].process_index);
          }
}

void fprintf_total_history(struct saved_history_struct *history, FILE *fp) {
    fprintf(fp,"%d\t N I=%E \t", history->number_of_rays, history->intensity);
        int history_iterate;
          //printf("History number %d, intensity = %f, number of rays = %d:",hist_num, search_node->intensity, search_node->number_of_rays);
          for (history_iterate=0;history_iterate<history->used_elements-1;history_iterate++) {
            if (history->elements[history_iterate].process_index == -1) {
                fprintf(fp," V%d ->",history->elements[history_iterate].volume_index);
            } else {
                fprintf(fp," P%d ->",history->elements[history_iterate].process_index);
            }
          }
          if (history->elements[history_iterate].process_index == -1) {
                fprintf(fp," V%d \n",history->elements[history_iterate].volume_index);
          } else {
                fprintf(fp," P%d \n",history->elements[history_iterate].process_index);
          }
}

/*
int Sample_compare_doubles (const void *a, const void *b) {
  const double *da = (const double *) a;
  const double *db = (const double *) b;

  return (*da > *db) - (*da < *db);
}
*/


int Sample_compare_history_intensities (const struct saved_history_struct *a, const struct saved_history_struct *b) {
  const double *da = (const double *) &(a->intensity);
  const double *db = (const double *) &(b->intensity);

  return (*da < *db) - (*da > *db);
}

void write_tagging_tree(struct list_of_tagging_tree_node_pointers *master_list, struct Volume_struct **Volumes, int total_history_counter, int number_of_volumes) {
  // Start from top of tree, go to extremeties, take results and add to disk / database, free that node
  int volume_index,done,volume_iterate,process_iterate,current_volume,next_node_found,current_number_of_processes,history_iterate,hist_num;
  
  struct tagging_tree_node_struct *search_node;
  struct tagging_tree_node_struct **kill_candidate;
  struct dynamic_history_list history_data; // Allocate the history list struct
  struct dynamic_history_list *history;     // Use this pointer in the algorithm
  
  struct total_history_struct total_history;
  total_history.saved_histories = malloc(total_history_counter * sizeof(struct saved_history_struct));
  total_history.allocated_elements = total_history_counter;
  total_history.used_elements = 0;
  
  history = &history_data;
  
  history->used_elements = 0;
  history->allocated_elements = 0;
  
  hist_num = 0;
  for (volume_index=0;volume_index<master_list->num_elements;volume_index++) {
    
    search_node = master_list->elements[volume_index];
    
    if (volume_index != 0)
        current_number_of_processes = Volumes[volume_index]->p_physics->number_of_processes;
    else
        current_number_of_processes = 0;
    
    current_volume = volume_index;
    done = 0;
    
    history->used_elements = 0;
    
    add_to_history(history,current_volume,-1);
    while(done == 0) {
        next_node_found=0;
        for (volume_iterate=0;volume_iterate<Volumes[current_volume]->geometry.next_volume_list.num_elements;volume_iterate++) {
            //printf("searc_node->volume_branches[0]->intensity = %f\n",search_node->volume_branches[0]->intensity);
            if (search_node->volume_branches[volume_iterate] != NULL) {
                current_volume = Volumes[current_volume]->geometry.next_volume_list.elements[volume_iterate];
                if (current_volume != 0)
                    current_number_of_processes = Volumes[current_volume]->p_physics->number_of_processes;
                else
                    current_number_of_processes = 0;
                
                //search_node = &(search_node->volume_branches[volume_iterate]);
                kill_candidate = &(search_node->volume_branches[volume_iterate]);
                search_node = search_node->volume_branches[volume_iterate];
                next_node_found = 1;
                add_to_history(history,current_volume,-1);
                //printf_history(history);
                break;
            }
        }
        if (next_node_found == 0) {
          //printf("doing process loop with %d steps \n",current_number_of_processes);
          for (process_iterate=0;process_iterate<current_number_of_processes;process_iterate++) {
            //if (&(search_node->process_branches[volume_iterate]) != NULL) {
            if (search_node->process_branches[process_iterate] != NULL) {
                //printf("was not NULL (process)\n");
                //search_node = &(search_node->process_branches[process_iterate]);
                kill_candidate = &(search_node->process_branches[process_iterate]);
                search_node = search_node->process_branches[process_iterate];
                next_node_found = 1;
                add_to_history(history,current_volume,process_iterate);
                //printf_history(history);
                break;
            }
          }
        }
        
        if (next_node_found == 0) {
          // write this history to disk / memory
          hist_num++;
          //printf("Reached next_node_found == 0 \n");
          if (history->used_elements > 0 && search_node->number_of_rays > 0) {
            //printf("%d rays (I=%E) with history: \t", search_node->number_of_rays, search_node->intensity);
            //printf_history(history);
            
            total_history.saved_histories[total_history.used_elements].used_elements = history->used_elements;
            total_history.saved_histories[total_history.used_elements].elements = malloc(total_history.saved_histories[total_history.used_elements].used_elements*sizeof(struct history_node_struct));
            for (history_iterate = 0;history_iterate<history->used_elements;history_iterate++) {
                total_history.saved_histories[total_history.used_elements].elements[history_iterate] = history->elements[history_iterate];
                //printf("total_history.saved_histories[total_history.used_elements].elements[%d].volume_index \n",history_iterate,total_history.saved_histories[total_history.used_elements].elements[history_iterate].volume_index);
            }
            //total_history.saved_histories[total_history.used_elements].elements = history->elements;
            total_history.saved_histories[total_history.used_elements].intensity = search_node->intensity;
            total_history.saved_histories[total_history.used_elements].number_of_rays = search_node->number_of_rays;
            total_history.used_elements++;
          }
          
          history->used_elements = 0;
          
          // end of tree, no new nodes
          if (search_node->above == NULL) {
            done = 1;
          } else {
            // reset to the root of the tree
            *kill_candidate = NULL;
            free(search_node);
            search_node = master_list->elements[volume_index];
            
            if (volume_index != 0)
              current_number_of_processes = Volumes[volume_index]->p_physics->number_of_processes;
            else
              current_number_of_processes = 0;
    
            current_volume = volume_index;
            add_to_history(history,current_volume,-1);
          }
          
        }
    }
  }
  
  if (history->allocated_elements > 0) free(history->elements);

  qsort(total_history.saved_histories,total_history.used_elements,sizeof (struct saved_history_struct), Sample_compare_history_intensities);
  
  
  
  MPI_MASTER(
  printf("\n\n");
  printf("Top 20 most common histories. Shows the index of volumes entered (VX), and the scattering processes (PX)\n");
  for (history_iterate=0;history_iterate<total_history.used_elements && history_iterate<20;history_iterate++) {
    printf("%d\t N I=%E \t", total_history.saved_histories[history_iterate].number_of_rays, total_history.saved_histories[history_iterate].intensity);
    printf_history(&total_history.saved_histories[history_iterate]);
  }
   
  FILE *fp;
  fp = fopen("union_history.dat","w");
  
  fprintf(fp,"History file written by the McStas component Union_master \n");
  fprintf(fp,"When running with MPI, the results may be from just a single thread, meaning intensities are divided by number of threads\n");
  fprintf(fp,"----- Description of the used volumes -----------------------------------------------------------------------------------\n");
  
  fprintf(fp,"V0: Surrounding vacuum \n");
  for (volume_iterate=1;volume_iterate<number_of_volumes;volume_iterate++) {
    fprintf(fp,"V%d: %s  ",volume_iterate,Volumes[volume_iterate]->name);
    fprintf(fp,"Material: %s  ",Volumes[volume_iterate]->p_physics->name);
    for (process_iterate=0;process_iterate<Volumes[volume_iterate]->p_physics->number_of_processes;process_iterate++) {
      fprintf(fp," P%d: %s",process_iterate,Volumes[volume_iterate]->p_physics->p_scattering_array[process_iterate].name);
    }
    fprintf(fp,"\n");
  }
  fprintf(fp,"----- Histories sorted after intensity ----------------------------------------------------------------------------------\n");
  
  for (history_iterate=0;history_iterate<total_history.used_elements;history_iterate++) {
    fprintf_total_history(&total_history.saved_histories[history_iterate],fp);
    // Garbage collection
    if (total_history.saved_histories[history_iterate].used_elements > 0) free(total_history.saved_histories[history_iterate].elements);
  }
  fclose(fp);
  
  )
  
  // Garbage collection
  if (total_history.allocated_elements > 0) free(total_history.saved_histories);
  
  
};


// -------------    Intersection table functions   --------------------------------------------------------
int clear_intersection_table(struct intersection_time_table_struct *intersection_time_table) {
    // Resets the intersection table when a scattering have occured.
    int iterate_volumes,iterate_solutions;
    
    // Start at one because vacuum (0) does not have a listing in the intersection table
    for (iterate_volumes = 1;iterate_volumes < intersection_time_table->num_volumes;iterate_volumes++) {
        intersection_time_table->calculated[iterate_volumes] = 0;
        // This second loop is added for safty in debugging phase, but can be removed as the information should never be accesed when calculated = 0
        for (iterate_solutions = 0;iterate_solutions < intersection_time_table->n_elements[iterate_volumes];iterate_solutions++) {
            intersection_time_table->intersection_times[iterate_volumes][iterate_solutions] = -1;
        }
    }
    
    return 1;
};

void print_intersection_table(struct intersection_time_table_struct *intersection_time_table) {
    int num_volumes,iterate,solutions;
    int max_number_of_solutions = 0;
    
    num_volumes = intersection_time_table->num_volumes;
    
    for (iterate = 0;iterate < num_volumes;iterate++) {
        if (max_number_of_solutions < intersection_time_table->n_elements[iterate])
            max_number_of_solutions = intersection_time_table->n_elements[iterate];
    }
    
    printf("------------------ INTERSECTION_TIME_TABLE -----------------");
    for (solutions = 2;solutions < max_number_of_solutions;solutions++) printf("------------");
    printf("\n");
    
    // printf("iterate      |");

    printf("           ");
    printf("| CALCULATED  |");
    for (solutions = 0;solutions < max_number_of_solutions;solutions++) {
        printf(" - SOLUTION %d - |",solutions);
    }

    printf("\n");
    for (iterate = 0;iterate < num_volumes;iterate++){
    // print iterate number
    printf("Volume %d   |",iterate);
    printf(" ---- %d ---- |",intersection_time_table->calculated[iterate]);
    
        for (solutions = 0;solutions < max_number_of_solutions;solutions++) {
          if (intersection_time_table->n_elements[iterate] > solutions && intersection_time_table->calculated[iterate] == 1)
            if (intersection_time_table->intersection_times[iterate][solutions] > 0)
             printf("   %1.8f   |",intersection_time_table->intersection_times[iterate][solutions]);
            else
             printf("   %1.7f   |",intersection_time_table->intersection_times[iterate][solutions]);
          else
            printf("                |");
        }
    printf("\n");
    }
    printf("------------------------------------------------------------");
    for (solutions = 2;solutions < max_number_of_solutions;solutions++) printf("------------");
    printf("\n");
    
    
    };
    
// -------------    Drawing functions   --------------------------------------------------------
void merge_lines_to_draw(struct lines_to_draw *lines_master,struct lines_to_draw *lines_new) {
    if (lines_master->number_of_lines == 0) {
    lines_master->number_of_lines = lines_new->number_of_lines;
    lines_master->lines = malloc(lines_master->number_of_lines*sizeof(struct line_segment));
    lines_master->lines = lines_new->lines;
    // One could free lines_new->lines;
    } else {
    int iterate;
    struct line_segment *temp_lines;
    temp_lines = malloc(lines_master->number_of_lines*sizeof(struct line_segment));
    for (iterate = 0;iterate < lines_master->number_of_lines;iterate++) temp_lines[iterate] = lines_master->lines[iterate];
    free(lines_master->lines);
    lines_master->lines = malloc((lines_master->number_of_lines+lines_new->number_of_lines)*sizeof(struct line_segment));
    for (iterate = 0;iterate < lines_master->number_of_lines;iterate++) lines_master->lines[iterate] = temp_lines[iterate];
    for (iterate = 0;iterate < lines_new->number_of_lines;iterate++) lines_master->lines[iterate+lines_master->number_of_lines] = lines_new->lines[iterate];
    lines_master->number_of_lines = lines_master->number_of_lines + lines_new->number_of_lines;
    free(temp_lines);
    }
    };

int r_has_highest_priority(Coords point,int N,struct geometry_struct **Geometries,int number_of_volumes) {
    // Function that test if a point in Volume N has the highest priority.
    // Returns 0 if another volume has higher priority at the point, and it's mask status is 1
    // Returns 1 if no other volume has higher priority at the point
    // (not active) Returns a larger integer if the volume N is a mask, and the point is not in the volume it is masking,
    //  which results in the drawing function making that number of dashes
    
    
    int mask_status,*mask_start,*mask_check;
    // If the volume is a mask, it does not have a priority, and is always on top
    if (Geometries[N]->is_mask_volume == 1) {
      return 1;
      // Can draw parts of the mask that is outside the volume it masks as dashed lines, but it just looks messy
      /*
      mask_status = 1;
      for (mask_check=mask_start=Geometries[N]->mask_list.elements;mask_check-mask_start<Geometries[N]->mask_list.num_elements;mask_check++) {
        if (Geometries[*mask_check]->within_function(point,Geometries[*mask_check]) == 1) {
          return 1; // If the point is within a volume the mask is masking, draw it as a solid line
        }
      }
      // As it was not inside any of the volumes the mask is masking, draw it as a dashed line
      return 5;
      */
    }
    
    // If the volume is a masked volume, check if the point is within it's masks
    if (Geometries[N]->is_masked_volume == 1) {
      if (Geometries[N]->mask_mode == 1) { // ALL mode, need to be within ALL masks
        for (mask_check=mask_start=Geometries[N]->masked_by_list.elements;mask_check-mask_start<Geometries[N]->masked_by_list.num_elements;mask_check++) {
          if (Geometries[*mask_check]->within_function(point,Geometries[*mask_check]) == 0) {
            return 0; // If the point is just outside one mask, the mask status is 0 and the point does not have highest priority
          }
        }
      } else { // ANY mode, need to be within at least one mask
        mask_status = 0;
        for (mask_check=mask_start=Geometries[N]->masked_by_list.elements;mask_check-mask_start<Geometries[N]->masked_by_list.num_elements;mask_check++) {
          if (Geometries[*mask_check]->within_function(point,Geometries[*mask_check]) == 1) {
            mask_status = 1;
            break;
          }
        }
        if (mask_status == 0) {
          return 0; // If it was not in a single of it's masks, the point did not have highest priority
        }
      }
    }
    
    int volume_index;
    double self_priority;
    self_priority = Geometries[N]->priority_value;
    
    
    for (volume_index = 1;volume_index<number_of_volumes;volume_index++) {
      if (volume_index != N && Geometries[volume_index]->is_mask_volume == 0) {
        if (Geometries[volume_index]->within_function(point,Geometries[volume_index])) {
          if (Geometries[volume_index]->is_masked_volume == 1) {
            // Since this volume is masked, the mask status need to be checked
            if (Geometries[volume_index]->mask_mode == 1) { //ALL mode, need to be within ALL masks
              mask_status = 1;
              for (mask_check=mask_start=Geometries[volume_index]->masked_by_list.elements;mask_check-mask_start<Geometries[volume_index]->masked_by_list.num_elements;mask_check++) {
                if (Geometries[*mask_check]->within_function(point,Geometries[*mask_check]) == 0) {
                  mask_status = 0;
                  break;
                }
              }
            } else { // ANY mode, need to be within at least one mask
              mask_status = 0;
              for (mask_check=mask_start=Geometries[volume_index]->masked_by_list.elements;mask_check-mask_start<Geometries[volume_index]->masked_by_list.num_elements;mask_check++) {
                if (Geometries[*mask_check]->within_function(point,Geometries[*mask_check]) == 1) {
                  mask_status = 1;
                  break;
                }
              }
            }
          } else mask_status = 1;
          if (Geometries[volume_index]->priority_value > self_priority && mask_status == 1) {
            // printf("Volume %d did not have highest priority at (%f,%f,%f) (Volume number %d was above)\n",N,point.x,point.y,point.z,volume_index);
            return 0;
          }
        }
      }
    }
    // printf("Volume %d did have highest priority at (%f,%f,%f) \n",N,point.x,point.y,point.z);
    return 1;
    }
    
void draw_line_positions(Coords point1,Coords point2) {
        //line(point1.x,point1.y,point1.z,point2.x,point2.y,point2.z);
        // sigh, can not use line in share. Need to save the information and pass to the mcdisplay part.
    }

int Sample_compare_doubles (const void *a, const void *b) {
  const double *da = (const double *) a;
  const double *db = (const double *) b;

  return (*da > *db) - (*da < *db);
}

struct lines_to_draw draw_line_with_highest_priority(Coords position1,Coords position2,int N,struct geometry_struct **Geometries,int number_of_volumes,int max_number_of_solutions) {
    
    int volume_index,iterate,permanent_list_length = 0;
    int number_of_solutions;
    double temp_intersection[max_number_of_solutions];
    double r1[3],r2[3],direction[3];
    struct pointer_to_1d_double_list intersection_list;
    
    intersection_list.num_elements = 0;
    
    // double *permanent_intersection_list,*storage; // old ways
    
    r1[0] = position1.x;
    r1[1] = position1.y;
    r1[2] = position1.z;
    r2[0] = position2.x;
    r2[1] = position2.y;
    r2[2] = position2.z;
    
    // printf("r1 = (%f,%f,%f) \n",r1[0],r1[1],r1[2]);
    // printf("r2 = (%f,%f,%f) \n",r2[0],r2[1],r2[2]);
    
    direction[0] = r2[0] - r1[0];
    direction[1] = r2[1] - r1[1];
    direction[2] = r2[2] - r1[2];
    int geometry_output;
    
    // Find intersections
    for (volume_index = 1;volume_index < number_of_volumes; volume_index++) {
        if (volume_index != N) {
            geometry_output = Geometries[volume_index]->intersect_function(temp_intersection,&number_of_solutions,r1,direction,Geometries[volume_index]);
             // printf("No solutions for intersection (Volume %d) with %d \n",N,volume_index);
                for (iterate=0;iterate<number_of_solutions;iterate++) {
                    // print_1d_double_list(intersection_list,"intersection_list");
                    if (temp_intersection[iterate] > 0 && temp_intersection[iterate] < 1) {
                        add_element_to_double_list(&intersection_list,temp_intersection[iterate]);
                        // printf("solution taken = %f\n",temp_intersection[iterate]);
                        }                         // printf("solution ignored = %f\n",temp_intersection[iterate]);
                    // print_1d_double_list(intersection_list,"intersection_list");
                
                }
                // printf("First (%d) solutions added to the solution stack, intersection with %d \n",number_of_solutions,volume_index);
                // printf(" Solutions: ");
                // for (iterate = 0;iterate < intersection_list.num_elements;iterate++) printf("%f ",intersection_list.elements[iterate]);
                // printf("\n");
            
        }
    }
    // Now we have a list of intersection distances between r1 and r2 and all volumes.
    // This list needs to be sorted before we continue!
    
    qsort(intersection_list.elements,intersection_list.num_elements,sizeof (double), Sample_compare_doubles);
    // print_1d_double_list(intersection_list,"after sorting");
    // printf(" Solutions (after sorting): ");
    // for (iterate = 0;iterate < intersection_list.num_elements;iterate++) printf("%f ",intersection_list.elements[iterate]);
    // printf("\n");
    
    Coords points[intersection_list.num_elements+2];
    points[0] = coords_set(r1[0],r1[1],r1[2]);
    points[intersection_list.num_elements+1] = coords_set(r2[0],r2[1],r2[2]);
    for (iterate = 1;iterate < intersection_list.num_elements+1;iterate++) {
        points[iterate].x = r1[0] + direction[0]*intersection_list.elements[iterate-1];
        points[iterate].y = r1[1] + direction[1]*intersection_list.elements[iterate-1];
        points[iterate].z = r1[2] + direction[2]*intersection_list.elements[iterate-1];
    }
    
    // printf("Points on the list:\n");
    // for (iterate = 0;iterate < intersection_list.num_elements + 2;iterate++) {
    // //         printf("(%f,%f,%f)\n",points[iterate].x,points[iterate].y,points[iterate].z);
    // }
    // printf("\n");
  
    struct line_segment lines[intersection_list.num_elements+1];
    int draw_logic[intersection_list.num_elements+1];
    //struct lines_to_draw draw_order;
    Coords midpoint;
    struct lines_to_draw draw_order;
    draw_order.number_of_lines = 0;
    
    // printf("test2 in function \n");
    int number_of_dashes;
    
    for (iterate = 0;iterate < intersection_list.num_elements + 1;iterate++) {
        lines[iterate].point1 = points[iterate];
        lines[iterate].point2 = points[iterate+1];
        midpoint.x = 0.5*(lines[iterate].point1.x + lines[iterate].point2.x);
        midpoint.y = 0.5*(lines[iterate].point1.y + lines[iterate].point2.y);
        midpoint.z = 0.5*(lines[iterate].point1.z + lines[iterate].point2.z);
        
        if ((number_of_dashes = r_has_highest_priority(midpoint,N,Geometries,number_of_volumes)) != 0) {
            draw_order.number_of_lines++;
            draw_logic[iterate] = number_of_dashes;
        } else draw_logic[iterate] = 0;
    }
   
    // printf("test3 in function \n");
    
    if (draw_order.number_of_lines > 0) {
        draw_order.lines = malloc(draw_order.number_of_lines*sizeof(struct line_segment));
        draw_order.number_of_lines = 0;
        for (iterate = 0;iterate < intersection_list.num_elements + 1;iterate++) {
            if (draw_logic[iterate] != 0) {
              lines[iterate].number_of_dashes = draw_logic[iterate];
              draw_order.lines[draw_order.number_of_lines++] = lines[iterate];
            }
        }
        if (intersection_list.num_elements > 0) free(intersection_list.elements);
    }

    // printf("function done \n");
    return draw_order;
    }

struct lines_to_draw draw_circle_with_highest_priority(Coords center,Coords vector,double radius,int N,struct geometry_struct **Geometries,int number_of_volumes,int max_number_of_solutions) {
    int number_of_positions = 100;

    // normalize vector
    double vector_length = length_of_position_vector(vector);
    // print_position(vector,"start vector");
    vector.x /= vector_length;
    vector.y /= vector_length;
    vector.z /= vector_length;
    // print_position(vector,"start vector normalized");
    
    // Create a vector from the center of the circle to a point on the circumference by cross product
    double cross_input[3] = {0,1,0};
    // In case the cross input is parallel with the vector, a new is chosen. Both can't be parallel.
    if (scalar_prod(cross_input[0],cross_input[1],cross_input[2],vector.x,vector.y,vector.z) > 0.99) {
        cross_input[0] = 1; cross_input[1] = 0; cross_input[2] = 0;
    }
    // print_position(make_position(cross_input),"cross input");
    
    double cross_product1[3] = {0,0,0};
    vec_prod(cross_product1[0],cross_product1[1],cross_product1[2],vector.x,vector.y,vector.z,cross_input[0],cross_input[1],cross_input[2]);
    
    // print_position(make_position(cross_product1),"cross_product1");
    
    double cross_length = length_of_3vector(cross_product1);
    
    // printf("cross_length = %f \n",cross_length);
    
    cross_product1[0] /= cross_length;
    cross_product1[1] /= cross_length;
    cross_product1[2] /= cross_length;
    
    cross_product1[0] *= radius;
    cross_product1[1] *= radius;
    cross_product1[2] *= radius;
    
    // printf("cross_product1 = (%f,%f,%f) \n",cross_product1[0],cross_product1[1],cross_product1[2]);
    
    int iterate;
    double rotate_angle;
    Coords radial_position,old_radial_position;
    struct lines_to_draw temp_draw_order,return_draw_order;
    return_draw_order.number_of_lines = 0;
    // Generate an array of positions by rotating this vector around the given vector, this corresponds to points on the circle
    old_radial_position.x = center.x + cross_product1[0];
    old_radial_position.y = center.y + cross_product1[1];
    old_radial_position.z = center.z + cross_product1[2];

    // printf("old_radial_position = (%f,%f,%f) \n",old_radial_position.x,old_radial_position.y,old_radial_position.z);
    
    for (iterate = 0;iterate < number_of_positions-1;iterate++) {
        rotate_angle = 2*3.14159*((double) iterate + 1.0)/((double) number_of_positions);
        rotate(radial_position.x,radial_position.y,radial_position.z,cross_product1[0],cross_product1[1],cross_product1[2],rotate_angle,vector.x,vector.y,vector.z);
        radial_position.x += center.x;
        radial_position.y += center.y;
        radial_position.z += center.z;
        
        // Use the draw_lines_with_highest_priority to draw get draw_orders for each line piece
        temp_draw_order = draw_line_with_highest_priority(radial_position,old_radial_position,N,Geometries,number_of_volumes,max_number_of_solutions);
       // Assemble these draw_orders to one large draw order
        merge_lines_to_draw(&return_draw_order,&temp_draw_order);

        old_radial_position.x = radial_position.x;
        old_radial_position.y = radial_position.y;
        old_radial_position.z = radial_position.z;
    }
    
    radial_position.x = center.x + cross_product1[0];
    radial_position.y = center.y + cross_product1[1];
    radial_position.z = center.z + cross_product1[2];
    // Use the draw_lines_with_highest_priority to draw get draw_orders for each line piece
    temp_draw_order = draw_line_with_highest_priority(radial_position,old_radial_position,N,Geometries,number_of_volumes,max_number_of_solutions);
    // Assemble these draw_orders to one large draw order
    merge_lines_to_draw(&return_draw_order,&temp_draw_order);
    
    // clean up
    if (temp_draw_order.number_of_lines > 0) free(temp_draw_order.lines);
    
    // return the large draw order.
    return return_draw_order;
    }

// -------------    Geometry functions   --------------------------------------------------------

#include "Geometry_functions.c"

// -------------    List generator functions   --------------------------------------------------


int within_which_volume(Coords pos, struct pointer_to_1d_int_list input_list, struct pointer_to_1d_int_list destinations_list, struct Volume_struct **Volumes, struct pointer_to_1d_int_list *mask_status_list, int number_of_volumes, int *volume_logic_copy, int *ListA, int *ListB) {
    // This function identifies in which of the volumes of the input list the position pos lies in.
    // pos: position for which the current volume should be found for
    // input list: list of potential volumes, reduced to the ones without parents (their children will be checked)
    // OLD VERSION: volume logic: A logic list of allowed volumes for lookup, volume 1 3 and 5 in a case of 10 volumes would be [0 1 0 1 0 1 0 0 0 0]
    // destinations_list: list of allowed destinations (the original destinations list)
    // Volumes: Main volumes array
    // volume_logic_copy: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    // ListA: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    // ListB: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    
    // Algorithm description
    // Check all of input list for pos being within them, those that have it within them are:
    //      checked against the current highest priority
    //          if higher, is the new highest priority and the new pick for volume
    //      have all their direct children added to the next list to be checked (but any volume can only be added to that list once)
    // Once a run have been made where the next list to check is empty, the answer for new pick for volume is taken.
    
    // Mask update:
    // Algorithm description
    // Check all of input list for pos being within them, those that have it within them are:
    //      checked against the current highest priority and mask status
    //          if higher, this is the new highest priority and the new pick for volume
    //      have all their direct children added to the next list to be checked (but any volume can only be added to that list once)
    // Once a run have been made where the next list to check is empty, the answer for new pick for volume is taken.
    
    // The advantage of the method is that potentially large numbers of children are skipped when their parents do not contain the position.
    // The overhead cost is low, as all the lists are prealocated.
    // Should be checked which of the two implementations is faster, as this is much more complicated than simply checking all possibilities.
    // No within_function call should be made twice, as the same volume number will not be checked twice because of the properties of the direct_children list and the volume_logic that removes duplicates on each level.
    
    
    // This function uses too much memory, the memory required for the volume logic list is n_volumes^2 ints, or for a MACS monochromator 127000 ints.
    // Instead the original destinations list must be used, and a function for quick lookup in a (sorted) destinations list made.
    // Still need a list of n_volumes length for control to avoid adding the same volume to the list twice.
    
    
    int ListA_length=0,ListB_length=0;
    int done = 0;
    int i,direct_children_index;
    int *temp_pointer;
    double max_priority=-1000000;
    int residing_volume=0; // 0 can be removed from the input list if default is 0
    int this_mask_status,mask_index,mask_global_index;
    
    // volume_logic_copy
    //for (i=0;i<volume_logic.num_elements;i++) volume_logic_copy[i] = volume_logic.elements[i];
    
    // low memory version of volume_logic_copy
    for (i=0;i<number_of_volumes;i++) volume_logic_copy[i] = 0;
    for (i=0;i<destinations_list.num_elements;i++) volume_logic_copy[destinations_list.elements[i]] = 1;
    printf("within_which_volume debug %i\n",input_list.num_elements);

    // Does one loop through the algorithm first to set up ListA instead of copying it from input_list, which takes time
    for (i=0;i<input_list.num_elements;i++) {
            if (Volumes[input_list.elements[i]]->geometry.within_function(pos,&Volumes[input_list.elements[i]]->geometry) == 1) {
                printf("The position is inside of volume %d\n",input_list.elements[i]);
                if (Volumes[input_list.elements[i]]->geometry.is_masked_volume == 1) {
                    // if the volume is masked, I need to know if it can be a destination volume from the mask_status_list.
                    // if the masked volume is in ANY mode,
                    this_mask_status=1;
                    //print_1d_int_list(*mask_status_list,"mask status list from within_which_volume");
                    for (mask_index=0;mask_index<Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.num_elements;mask_index++) {
                        //printf("Looking at the mask with global index %d \n",Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.elements[mask_index]);
                        if (mask_status_list->elements[Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.elements[mask_index]] == 1) {
                            if (Volumes[input_list.elements[i]]->geometry.mask_mode == 2) { // ANY (break if any one in)
                                this_mask_status=1;
                                break;
                            }
                            //printf("global index %d had mask status = 1 \n",Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.elements[mask_index]);
                        } else {
                          //printf("global index %d had mask status = 0 \n",Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.elements[mask_index]);
                          this_mask_status = 0;
                          if(Volumes[input_list.elements[i]]->geometry.mask_mode == 1) break; // ALL (break if any one out)
                        }
                    }
                    //printf("This volume is masked, and the mask status is %d\n",this_mask_status);
                } else this_mask_status = 1; // if the volume is not masked
            
                if (Volumes[input_list.elements[i]]->geometry.priority_value > max_priority && this_mask_status == 1) {
                    max_priority = Volumes[input_list.elements[i]]->geometry.priority_value;
                    residing_volume = input_list.elements[i];
                    //printf("residing volume set to %d\n",residing_volume);
                }
                    for (direct_children_index = 0;direct_children_index < Volumes[input_list.elements[i]]->geometry.direct_children.num_elements;direct_children_index++) {
                        if (volume_logic_copy[Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index]] == 1) {
                            ListA[ListA_length++] = Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index];
                            volume_logic_copy[Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index]] = 0;
                        }
                    }
            }
    }
    //printf("Completed first loop, continued in while loop\n");
    if (ListA_length > 0) {
        while (done == 0) {
            for (i=0;i<ListA_length;i++) {
              //printf("checking element number %d of list A which is volume number %d \n",i,ListA[i]);
                if (Volumes[ListA[i]]->geometry.within_function(pos,&Volumes[ListA[i]]->geometry) == 1) {
                  //printf("ray was inside this volume \n");
                    if (Volumes[ListA[i]]->geometry.is_masked_volume == 1) {
                      //printf("it is a mask and thus need check of mask status \n");
                        // if the volume is masked, I need to know if it can be a destination volume from the mask_status_list.
                        // if the masked volume is in ANY mode,
                        this_mask_status=1;
                        for (mask_index=0;mask_index<Volumes[ListA[i]]->geometry.masked_by_mask_index_list.num_elements;mask_index++) {
                            if (mask_status_list->elements[Volumes[ListA[i]]->geometry.masked_by_mask_index_list.elements[mask_index]] == 1) {
                                if (Volumes[ListA[i]]->geometry.mask_mode == 2) { // ANY (break if any one in)
                                    this_mask_status=1;
                                    break;
                                }
                            } else {
                              this_mask_status = 0;
                              if(Volumes[ListA[i]]->geometry.mask_mode == 1) break; // ALL (break if any one out)
                            }
                        }
                    } else this_mask_status = 1;
                    //printf("the mask status is %d \n",this_mask_status);
                    if (Volumes[ListA[i]]->geometry.priority_value > max_priority && this_mask_status == 1) {
                        max_priority = Volumes[ListA[i]]->geometry.priority_value;
                        residing_volume = ListA[i];
                    }
                    //printf("Adding direct children to list B \n");
                        for (direct_children_index = 0;direct_children_index < Volumes[ListA[i]]->geometry.direct_children.num_elements;direct_children_index++) {
                            //printf("Checking direct_child number %d which is %d \n",direct_children_index,Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index]);
                            if (volume_logic_copy[Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index]] == 1) {
                                //printf("It's volume_logic was 1, and it is thus added to listB with index %d \n",ListB_length);
                                ListB[ListB_length++] = Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index];
                                volume_logic_copy[Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index]] = 0;
                            }
                        }
                    //printf("List B is now: ");
                    //for (direct_children_index=0;direct_children_index<ListB_length;direct_children_index++) printf("%d ",ListB[direct_children_index]);
                    //printf("\n");
                    
                    
                }
            }
            if (ListB_length==0) done = 1;
            else {
                
                for (i=0;i<ListB_length;i++) ListA[i] = ListB[i];
                ListA_length = ListB_length;
                ListB_length = 0;
                
                /*
                // Could do this with pointers instead to avoid this for loop (and needless copy)
                // This code block fails on the cluster in rare circumstances
                ListA = temp_pointer;
                ListA = ListB;
                ListB = temp_pointer;
                ListA_length=ListB_length;
                ListB_length=0;
                */
            }
        }
    }
    //printf("residing volume returned %d\n",residing_volume);
    return residing_volume;
};

int within_which_volume_GPU(Coords pos, struct pointer_to_1d_int_list input_list, struct pointer_to_1d_int_list destinations_list, struct Volume_struct **Volumes, struct pointer_to_1d_int_list *mask_status_list, int number_of_volumes, int *volume_logic_copy, int *ListA, int *ListB) {
    // This function identifies in which of the volumes of the input list the position pos lies in.
    // pos: position for which the current volume should be found for
    // input list: list of potential volumes, reduced to the ones without parents (their children will be checked)
    // OLD VERSION: volume logic: A logic list of allowed volumes for lookup, volume 1 3 and 5 in a case of 10 volumes would be [0 1 0 1 0 1 0 0 0 0]
    // destinations_list: list of allowed destinations (the original destinations list)
    // Volumes: Main volumes array
    // volume_logic_copy: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    // ListA: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    // ListB: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    
    // Algorithm description
    // Check all of input list for pos being within them, those that have it within them are:
    //      checked against the current highest priority
    //          if higher, is the new highest priority and the new pick for volume
    //      have all their direct children added to the next list to be checked (but any volume can only be added to that list once)
    // Once a run have been made where the next list to check is empty, the answer for new pick for volume is taken.
    
    // Mask update:
    // Algorithm description
    // Check all of input list for pos being within them, those that have it within them are:
    //      checked against the current highest priority and mask status
    //          if higher, this is the new highest priority and the new pick for volume
    //      have all their direct children added to the next list to be checked (but any volume can only be added to that list once)
    // Once a run have been made where the next list to check is empty, the answer for new pick for volume is taken.
    
    // The advantage of the method is that potentially large numbers of children are skipped when their parents do not contain the position.
    // The overhead cost is low, as all the lists are prealocated.
    // Should be checked which of the two implementations is faster, as this is much more complicated than simply checking all possibilities.
    // No within_function call should be made twice, as the same volume number will not be checked twice because of the properties of the direct_children list and the volume_logic that removes duplicates on each level.
    
    
    // This function uses too much memory, the memory required for the volume logic list is n_volumes^2 ints, or for a MACS monochromator 127000 ints.
    // Instead the original destinations list must be used, and a function for quick lookup in a (sorted) destinations list made.
    // Still need a list of n_volumes length for control to avoid adding the same volume to the list twice.
    
    
    int ListA_length=0,ListB_length=0;
    int done = 0;
    int i,direct_children_index;
    int *temp_pointer;
    double max_priority=-1000000;
    int residing_volume=0; // 0 can be removed from the input list if default is 0
    int this_mask_status,mask_index,mask_global_index;
    
    // volume_logic_copy
    //for (i=0;i<volume_logic.num_elements;i++) volume_logic_copy[i] = volume_logic.elements[i];
    
    // low memory version of volume_logic_copy
    for (i=0;i<number_of_volumes;i++) volume_logic_copy[i] = 0;
    for (i=0;i<destinations_list.num_elements;i++) volume_logic_copy[destinations_list.elements[i]] = 1;
    //printf("within_which_volume debug\n");

    // Does one loop through the algorithm first to set up ListA instead of copying it from input_list, which takes time
    for (i=0;i<input_list.num_elements;i++) {
            if (r_within_function(pos, &Volumes[input_list.elements[i]]->geometry) == 1) {
                //printf("The position is inside of volume %d\n",input_list.elements[i]);
                if (Volumes[input_list.elements[i]]->geometry.is_masked_volume == 1) {
                    // if the volume is masked, I need to know if it can be a destination volume from the mask_status_list.
                    // if the masked volume is in ANY mode,
                    this_mask_status=1;
                    //print_1d_int_list(*mask_status_list,"mask status list from within_which_volume");
                    for (mask_index=0;mask_index<Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.num_elements;mask_index++) {
                        //printf("Looking at the mask with global index %d \n",Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.elements[mask_index]);
                        if (mask_status_list->elements[Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.elements[mask_index]] == 1) {
                            if (Volumes[input_list.elements[i]]->geometry.mask_mode == 2) { // ANY (break if any one in)
                                this_mask_status=1;
                                break;
                            }
                            //printf("global index %d had mask status = 1 \n",Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.elements[mask_index]);
                        } else {
                          //printf("global index %d had mask status = 0 \n",Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.elements[mask_index]);
                          this_mask_status = 0;
                          if(Volumes[input_list.elements[i]]->geometry.mask_mode == 1) break; // ALL (break if any one out)
                        }
                    }
                    //printf("This volume is masked, and the mask status is %d\n",this_mask_status);
                } else this_mask_status = 1; // if the volume is not masked
            
                if (Volumes[input_list.elements[i]]->geometry.priority_value > max_priority && this_mask_status == 1) {
                    max_priority = Volumes[input_list.elements[i]]->geometry.priority_value;
                    residing_volume = input_list.elements[i];
                    //printf("residing volume set to %d\n",residing_volume);
                }
                    for (direct_children_index = 0;direct_children_index < Volumes[input_list.elements[i]]->geometry.direct_children.num_elements;direct_children_index++) {
                        if (volume_logic_copy[Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index]] == 1) {
                            ListA[ListA_length++] = Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index];
                            volume_logic_copy[Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index]] = 0;
                        }
                    }
            }
    }
    //printf("Completed first loop, continued in while loop\n");
    if (ListA_length > 0) {
        while (done == 0) {
            for (i=0;i<ListA_length;i++) {
              //printf("checking element number %d of list A which is volume number %d \n",i,ListA[i]);
                if (r_within_function(pos,&Volumes[ListA[i]]->geometry) == 1) {
                  //printf("ray was inside this volume \n");
                    if (Volumes[ListA[i]]->geometry.is_masked_volume == 1) {
                      //printf("it is a mask and thus need check of mask status \n");
                        // if the volume is masked, I need to know if it can be a destination volume from the mask_status_list.
                        // if the masked volume is in ANY mode,
                        this_mask_status=1;
                        for (mask_index=0;mask_index<Volumes[ListA[i]]->geometry.masked_by_mask_index_list.num_elements;mask_index++) {
                            if (mask_status_list->elements[Volumes[ListA[i]]->geometry.masked_by_mask_index_list.elements[mask_index]] == 1) {
                                if (Volumes[ListA[i]]->geometry.mask_mode == 2) { // ANY (break if any one in)
                                    this_mask_status=1;
                                    break;
                                }
                            } else {
                              this_mask_status = 0;
                              if(Volumes[ListA[i]]->geometry.mask_mode == 1) break; // ALL (break if any one out)
                            }
                        }
                    } else this_mask_status = 1;
                    //printf("the mask status is %d \n",this_mask_status);
                    if (Volumes[ListA[i]]->geometry.priority_value > max_priority && this_mask_status == 1) {
                        max_priority = Volumes[ListA[i]]->geometry.priority_value;
                        residing_volume = ListA[i];
                    }
                    //printf("Adding direct children to list B \n");
                        for (direct_children_index = 0;direct_children_index < Volumes[ListA[i]]->geometry.direct_children.num_elements;direct_children_index++) {
                            //printf("Checking direct_child number %d which is %d \n",direct_children_index,Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index]);
                            if (volume_logic_copy[Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index]] == 1) {
                                //printf("It's volume_logic was 1, and it is thus added to listB with index %d \n",ListB_length);
                                ListB[ListB_length++] = Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index];
                                volume_logic_copy[Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index]] = 0;
                            }
                        }
                    //printf("List B is now: ");
                    //for (direct_children_index=0;direct_children_index<ListB_length;direct_children_index++) printf("%d ",ListB[direct_children_index]);
                    //printf("\n");
                    
                    
                }
            }
            if (ListB_length==0) done = 1;
            else {
                
                for (i=0;i<ListB_length;i++) ListA[i] = ListB[i];
                ListA_length = ListB_length;
                ListB_length = 0;
                
                /*
                // Could do this with pointers instead to avoid this for loop (and needless copy)
                // This code block fails on the cluster in rare circumstances
                ListA = temp_pointer;
                ListA = ListB;
                ListB = temp_pointer;
                ListA_length=ListB_length;
                ListB_length=0;
                */
            }
        }
    }
    //printf("residing volume returned %d\n",residing_volume);
    return residing_volume;
};




int within_which_volume_debug(Coords pos, struct pointer_to_1d_int_list input_list, struct pointer_to_1d_int_list volume_logic, struct Volume_struct **Volumes, int *volume_logic_copy, int *ListA, int *ListB) {
    // This function identifies in which of the volumes of the input list the position pos lies in.
    // pos: position for which the current volume should be found for
    // input list: list of potential volumes, reduced to the ones without parents (their children will be checked)
    // volume logic: A logic list of allowed volumes for lookup, volume 1 3 and 5 in a case of 10 volumes would be [0 1 0 1 0 1 0 0 0 0]
    // Volumes: Main volumes array
    // volume_logic_copy: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    // ListA: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    // ListB: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    
    // Algorithm description
    // Check all of input list for pos being within them, those that have it within them are:
    //      checked against the current highest priority
    //          if higher, is the new highest priority and the new pick for volume
    //      have all their direct children added to the next list to be checked (but any volume can only be added to that list once)
    // Once a run have been made where the next list to check is empty, the answer for new pick for volume is taken.
    
    // The advantage of the method is that potentially large numbers of children are skipped when their parents do not contain the position.
    // The overhead cost is low, as all the lists are prealocated.
    // Should be checked which of the two implementations is faster, as this is much more complicated than simply checking all possibilities.
    // No within_function call should be made twice, as the same volume number will not be checked twice because of the properties of the direct_children list and the volume_logic that removes duplicates on each level.
    
    
    int ListA_length=0,ListB_length=0;
    int done = 0;
    int i,direct_children_index;
    int *temp_pointer;
    double max_priority=-1000000;
    int residing_volume=0; // 0 can be removed from the input list if default is 0
    
    // volume_logic_copy
    for (i=0;i<volume_logic.num_elements;i++) volume_logic_copy[i] = volume_logic.elements[i];
    
    // Does one loop through the algorithm first to set up ListA instead of copying it from input_list, which takes time
    for (i=0;i<input_list.num_elements;i++) {
            if (Volumes[input_list.elements[i]]->geometry.within_function(pos,&Volumes[input_list.elements[i]]->geometry) == 1) {
                if (Volumes[input_list.elements[i]]->geometry.priority_value > max_priority) {
                    max_priority = Volumes[input_list.elements[i]]->geometry.priority_value;
                    residing_volume = input_list.elements[i];
                }
                    for (direct_children_index = 0;direct_children_index < Volumes[input_list.elements[i]]->geometry.direct_children.num_elements;direct_children_index++) {
                        if (volume_logic_copy[Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index]] == 1) {
                            ListA[ListA_length++] = Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index];
                            volume_logic_copy[Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index]] = 0;
                        }
                    }
            }
    }
    if (ListA_length > 0) {
        while (done == 0) {
        
            printf("ListA = [");
            for (i=0;i<ListA_length;i++) {
                printf("%d,",ListA[i]);
            }
            printf("]\n");
            for (i=0;i<ListA_length;i++) {
                if (Volumes[ListA[i]]->geometry.within_function(pos,&Volumes[ListA[i]]->geometry) == 1) {
                    if (Volumes[ListA[i]]->geometry.priority_value > max_priority) {
                        max_priority = Volumes[ListA[i]]->geometry.priority_value;
                        residing_volume = ListA[i];
                    }
                    //if (ListA[i]!=0) {
                        for (direct_children_index = 0;direct_children_index < Volumes[ListA[i]]->geometry.direct_children.num_elements;direct_children_index++) {
                            if (volume_logic_copy[Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index]] == 1) {
                                ListB[ListB_length++] = Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index];
                                volume_logic_copy[Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index]] = 0;
                            }
                        }
                    //}
                }
            }
            if (ListB_length==0) done = 1;
            else {
                ListA = temp_pointer;
                ListA = ListB;
                ListB = temp_pointer;
                ListA_length=ListB_length;
                ListB_length=0;
            }
        }
    }
    printf("Volume number %d had the highest priority of checked volumes\n",residing_volume);
    return residing_volume;
};

int inside_function(struct Volume_struct *parent_volume, struct Volume_struct *child_volume) {
    // Function that calls the correct within function depending on the shapes of the two volumes
    if (strcmp("sphere",parent_volume->geometry.shape) == 0 && strcmp("sphere",child_volume->geometry.shape) == 0) {
        if (sphere_within_sphere(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("cylinder",parent_volume->geometry.shape) == 0 && strcmp("cylinder",child_volume->geometry.shape) == 0) {
        if (cylinder_within_cylinder(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("box",parent_volume->geometry.shape) == 0 && strcmp("box",child_volume->geometry.shape) == 0) {
        if (box_within_box(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("cone",parent_volume->geometry.shape) == 0 && strcmp("cone",child_volume->geometry.shape) == 0) {
        if (cone_within_cone(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("mesh",parent_volume->geometry.shape) == 0 && strcmp("mesh",child_volume->geometry.shape) == 0) {
        if (mesh_within_mesh(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("box",parent_volume->geometry.shape) == 0 && strcmp("cylinder",child_volume->geometry.shape) == 0) {
        if (cylinder_within_box(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("cylinder",parent_volume->geometry.shape) == 0 && strcmp("box",child_volume->geometry.shape) == 0) {
        if (box_within_cylinder(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("box",parent_volume->geometry.shape) == 0 && strcmp("sphere",child_volume->geometry.shape) == 0) {
        if (sphere_within_box(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("sphere",parent_volume->geometry.shape) == 0 && strcmp("box",child_volume->geometry.shape) == 0) {
        if (box_within_sphere(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("sphere",parent_volume->geometry.shape) == 0 && strcmp("cylinder",child_volume->geometry.shape) == 0) {
        if (cylinder_within_sphere(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("cylinder",parent_volume->geometry.shape) == 0 && strcmp("sphere",child_volume->geometry.shape) == 0) {
        if (sphere_within_cylinder(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("cone",parent_volume->geometry.shape) == 0 && strcmp("sphere",child_volume->geometry.shape) == 0) {
        if (sphere_within_cone(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("sphere",parent_volume->geometry.shape) == 0 && strcmp("cone",child_volume->geometry.shape) == 0) {
        if (cone_within_sphere(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("cone",parent_volume->geometry.shape) == 0 && strcmp("cylinder",child_volume->geometry.shape) == 0) {
        if (cylinder_within_cone(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("cylinder",parent_volume->geometry.shape) == 0 && strcmp("cone",child_volume->geometry.shape) == 0) {
        if (cone_within_cylinder(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("cone",parent_volume->geometry.shape) == 0 && strcmp("box",child_volume->geometry.shape) == 0) {
        if (box_within_cone(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("box",parent_volume->geometry.shape) == 0 && strcmp("cone",child_volume->geometry.shape) == 0) {
        if (cone_within_box(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else {
        #ifndef OPENACC
        printf("Need within function for type: ");
        printf("%s",parent_volume->geometry.shape);
        printf(" and type: ");
        printf("%s",child_volume->geometry.shape);
        printf(".\n");
        printf("It is not yet supported to mix mesh geometries with the basic shapes, but several mesh geometries are allowed.\n");
        exit(1);
	#endif
    }
    
    return 0;
};

void generate_children_lists(struct Volume_struct **Volumes, struct pointer_to_1d_int_list **true_children_lists, int number_of_volumes, int verbal) {
  // This function generates a list of children for each volume.
  // A volume m is a child of volume n, if the entire space ocupied by volume m is inside of the space ocupied by volume n
  // A volume m is a true child of volume n, if the entire space coupied by volume m after it's masks are applied is inside the volume ocupied by volume n after it's masks are applied
  
  
  MPI_MASTER(
  if (verbal) printf("\nGenerating children lists --------------------------- \n");
  )

  // Mask update: Creating a temporary list for each volume
  struct pointer_to_1d_int_list *temporary_children_lists;
  temporary_children_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list));

  // The surrounding vacuum, volume 0, done outside of for loop.
  temporary_children_lists[0].num_elements = number_of_volumes - 1;
  temporary_children_lists[0].elements = malloc(temporary_children_lists[0].num_elements*sizeof(int));
  
  int parent;
  for (parent=1;parent<number_of_volumes;parent++) {
      temporary_children_lists[0].elements[parent-1] = parent;
  }
  
  //if (verbal) printf("did temporary children list \n");
  //print_1d_int_list(temporary_children_lists[0],"temp children list [0]");
  
  // Hardcoding that every volume is a child of the surrounding vacuum
  Volumes[0]->geometry.children.num_elements = number_of_volumes-1;
  Volumes[0]->geometry.children.elements = malloc((number_of_volumes-1)*sizeof(int));
  true_children_lists[0] = malloc(sizeof(struct pointer_to_1d_int_list));
  true_children_lists[0]->num_elements = number_of_volumes - 1;
  true_children_lists[0]->elements = malloc((number_of_volumes-1)*sizeof(int));
  
  //if (verbal) printf("allocated true children lists \n");
  
  for (parent=1;parent<number_of_volumes;parent++) {
      Volumes[0]->geometry.children.elements[parent-1] = parent;
      true_children_lists[0]->elements[parent-1] = parent;
  }
  
  char string_output[128];
  MPI_MASTER(
  if (verbal) sprintf(string_output,"Children for Volume %d",0);
  if (verbal) print_1d_int_list(Volumes[0]->geometry.children,string_output);
  )
  
  
  // Generating the children lists for all other volumes using the appropriate geometry functions
  struct pointer_to_1d_int_list temp_list_local;
  temp_list_local.num_elements = number_of_volumes;
  temp_list_local.elements = malloc(number_of_volumes*sizeof(int));
  
  struct pointer_to_1d_int_list true_temp_list_local;
  true_temp_list_local.num_elements = number_of_volumes;
  true_temp_list_local.elements = malloc(number_of_volumes*sizeof(int));
  
  
  int child,used_elements,used_elements_true;
  for (parent=1;parent<number_of_volumes;parent++) {
      used_elements = 0;used_elements_true = 0;
      for (child=1;child<number_of_volumes;child++) {
        if (child != parent) {
          // Call inside_function that selects the proper within function for the two volumes
          if (1 == inside_function(Volumes[parent],Volumes[child])) {
            temp_list_local.elements[used_elements++] = child;
            true_temp_list_local.elements[used_elements_true++] = child;
          }
        } else true_temp_list_local.elements[used_elements_true++] = child; // Needed when children list takes mask into account
      }
      // Temp test
      allocate_list_from_temp(used_elements,temp_list_local,&Volumes[parent]->geometry.children);
      // Assing the children list to a temporary list as the masks have yet to be taken into account
      temporary_children_lists[parent].num_elements=0;
      allocate_list_from_temp(used_elements_true,true_temp_list_local,&temporary_children_lists[parent]);
      
      
      MPI_MASTER(
      if (verbal) sprintf(string_output,"Children for Volume %d (temporary_list)",parent);
      if (verbal) print_1d_int_list(temporary_children_lists[parent],string_output);
      )
      
      MPI_MASTER(
      if (verbal) sprintf(string_output,"Children for Volume %d (permanent_list)",parent);
      if (verbal) print_1d_int_list(Volumes[parent]->geometry.children,string_output);
      )
      
  }
  
  // mask update:
  // The logical expression: (child c parent AND child c parent_mask) OR (child_mask c parent AND child_mask c parent_mask)
  //  needs to be evaluated for each child / parent combination in order to take the masks of each into account
  
  int logic_var1,logic_var2,logic_var_ANY,logic_var_ALL;
  int mask_index,mask_index_child,mask_index_parent;
  int volume_C,volume_P;
  // Loop that takes masks into account
  for (parent=1;parent<number_of_volumes;parent++) {
    used_elements = 0;
    for (child=1;child<number_of_volumes;child++) {
     if (child != parent && 0 == on_int_list(Volumes[parent]->geometry.masked_by_list,child)) {
        // The children list for each volume does not need to contain the volume itself
        //  And a parent masked by it's child can not have that mask as a child
      
      // Here c means within in the sense of a set being part of another set
      // Logical expression to be evaluated: (child c parent AND child c parent_mask) OR (child_mask c parent AND child_mask c parent_mask)
      logic_var1 = on_int_list(temporary_children_lists[parent],child);
      if (logic_var1 == 1 && Volumes[parent]->geometry.is_masked_volume == 1) {
        // if the parent volume is masked, the child also need to be inclosed in these masks to fulfill this side of the logical expression
        logic_var_ANY = 0;
        for (mask_index=0;mask_index<Volumes[parent]->geometry.masked_by_list.num_elements;mask_index++) {
          if (0 == on_int_list(temporary_children_lists[Volumes[parent]->geometry.masked_by_list.elements[mask_index]],child)) {
            if (Volumes[parent]->geometry.mask_mode == 1) {
              logic_var1 = 0;
              break;
            }
          } else logic_var_ANY = 1;
        }
        if (Volumes[parent]->geometry.mask_mode == 2) logic_var1 = logic_var_ANY;
      }
      
      if (logic_var1 == 1) true_temp_list_local.elements[used_elements++] = child;
      else if (Volumes[child]->geometry.is_masked_volume == 1) {
        // If the first side of the logical expression is false, evalute the other side
        // The other side is only relevant if the child volume is masked, otherwise it is ignored
        //printf("Second side of logical expression \n");
        
        logic_var1 = 1; // Assume true
          
        // child_mask c parent
        logic_var_ALL = 0;
        for (mask_index=0;mask_index<Volumes[child]->geometry.masked_by_list.num_elements;mask_index++) {
          if (0 == on_int_list(temporary_children_lists[parent],Volumes[child]->geometry.masked_by_list.elements[mask_index])){
            if (Volumes[child]->geometry.mask_mode == 2) {
              logic_var1 = 0;
              break;
            }
          } else logic_var_ALL = 1;
        }
        if (Volumes[child]->geometry.mask_mode == 1) logic_var1 = logic_var_ALL;
        
        // This line allows the second part of the logical expression to be true in cases where the parent volume is not masked
        if (logic_var1 == 1 && Volumes[parent]->geometry.is_masked_volume == 0) true_temp_list_local.elements[used_elements++] = child;
        
        // There is no reason to check the other part (child_mask c parent_mask) if the first part was not true
        if (logic_var1 == 1 && Volumes[parent]->geometry.is_masked_volume == 1) {
          // This last part requires both the child and the parent to be masked
          // Need to evaluate (child_mask c parent_mask), where both can be a be a list of volume with ALL/ANY modes
          
          if (Volumes[parent]->geometry.mask_mode == 1) {
            logic_var2 = 1; // Assume the logical expression (child_mask c parent_mask) is true
            for (mask_index_parent=0;mask_index_parent<Volumes[parent]->geometry.masked_by_list.num_elements;mask_index_parent++) {
              // As the parent is in ALL mode, the child masks must be within ALL parent masks
              logic_var_ANY = 0; // Assume not a single child is within this parent
              for (mask_index_child=0;mask_index_child<Volumes[child]->geometry.masked_by_list.num_elements;mask_index_child++) {
                volume_P = Volumes[parent]->geometry.masked_by_list.elements[mask_index_parent];
                volume_C = Volumes[child]->geometry.masked_by_list.elements[mask_index_child];
                // Is volume C inside volume P? If yes, volume C must be on volume P's temporary children list
                if (0 == on_int_list(temporary_children_lists[volume_P],volume_C)) {
                  if (Volumes[child]->geometry.mask_mode == 2) {
                    // If child is in ANY mode, any one mask outside is enough to make the expression false
                    logic_var2 = 0;
                    break;
                  }
                } else logic_var_ANY = 1;
              }
              // If child is in ALL mode, then if any one child were within this mask, the logic expression holds true
              if (Volumes[child]->geometry.mask_mode == 1) logic_var2 = logic_var_ANY;
              if (logic_var2 == 0) break; // No need to continue
            }
          } else if (Volumes[parent]->geometry.mask_mode == 2) {
            // If the parent is in ANY mode, it is enough if the child masks are within just 1 of the parent masks
            for (mask_index_parent=0;mask_index_parent<Volumes[parent]->geometry.masked_by_list.num_elements;mask_index_parent++) {
              logic_var2 = 1; // Assume the logical expression (child_mask c parent_mask) is true
              logic_var_ANY = 0; // Assume not a single child is within this parent
              for (mask_index_child=0;mask_index_child<Volumes[child]->geometry.masked_by_list.num_elements;mask_index_child++) {
                volume_P = Volumes[parent]->geometry.masked_by_list.elements[mask_index_parent];
                volume_C = Volumes[child]->geometry.masked_by_list.elements[mask_index_child];
                // Is volume C inside volume P? If yes, volume C must be on volume P's temporary children list
                if (0 == on_int_list(temporary_children_lists[volume_P],volume_C)) {
                  if (Volumes[child]->geometry.mask_mode == 2) {
                    // If child is in ANY mode, any one mask outside is enough to make the expression false
                    logic_var2 = 0;
                    break;
                  }
                } else logic_var_ANY = 1;
              }
              // If child is in ALL mode, then if any one child were within this mask, the logic expression holds true
              if (Volumes[child]->geometry.mask_mode == 1) logic_var2 = logic_var_ANY;
              if (logic_var2 == 1) break; // No need to continue
            }
          }
          
          // if this point is reached, and logic_var2 is true, volume[child] is a child of volume[parent]
          if (logic_var2 == 1) true_temp_list_local.elements[used_elements++] = child;
          
        }
      }
     }
    }
    
    true_children_lists[parent] = malloc(sizeof(struct pointer_to_1d_int_list));
    true_children_lists[parent]->num_elements = 0;
    allocate_list_from_temp(used_elements,true_temp_list_local,true_children_lists[parent]);
    
    MPI_MASTER(
      if (verbal) sprintf(string_output,"True children for Volume (post mask) %d",parent);
      if (verbal) print_1d_int_list(*true_children_lists[parent],string_output);
    )
  }
  
  
  // Clean up of dynamically allocated memory
  
  for(child=0;child<number_of_volumes;child++) free(temporary_children_lists[child].elements);
  free(temporary_children_lists);
  free(true_temp_list_local.elements);
  free(temp_list_local.elements);
};

void generate_overlap_lists(struct pointer_to_1d_int_list **true_overlap_lists, struct pointer_to_1d_int_list **raw_overlap_lists, struct Volume_struct **Volumes, int number_of_volumes, int verbal) {
  // This function generates overlap lists for each volume
  // Volume m overlaps volume n if there is some subset of space they both ocupy (this is called raw overlap)
  // the true overlap list takes mask into account, meaning that the masks are applied before searching for overlap

  MPI_MASTER(
  if (verbal) printf("\nGenerating overlap lists ---------------------------- \n");
  )
  
  // Manually create the overlap list for the surrounding Vacuum, as it overlaps all other volumes
  
  // temporary_overlap_lists are used to save the calculated overlaps to avoid evaluating the heavy functions more than once (twice) for each possible volume pair
  struct pointer_to_1d_int_list *temporary_overlap_lists;
  temporary_overlap_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list));
  
  // Overlap_lists are the final result of the function, and for the surrounding volume it can be set immediatly
  true_overlap_lists[0] = malloc(sizeof(struct pointer_to_1d_int_list));
  true_overlap_lists[0]->num_elements = number_of_volumes-1;
  true_overlap_lists[0]->elements = malloc((number_of_volumes-1)*sizeof(int));
  
  raw_overlap_lists[0] =  malloc(sizeof(struct pointer_to_1d_int_list));
  raw_overlap_lists[0]->num_elements = number_of_volumes;
  raw_overlap_lists[0]->elements = malloc(number_of_volumes*sizeof(int));
  raw_overlap_lists[0]->elements[0] = 0; // Volume 0 overlaps itself
  
  int parent;
  for (parent=1;parent<number_of_volumes;parent++) {
      true_overlap_lists[0]->elements[parent-1] = parent;
      raw_overlap_lists[0]->elements[parent] = parent;
  }
  
  char string_output[128];
  MPI_MASTER(
  if (verbal) sprintf(string_output,"Overlaps for Volume %d",0);
  if (verbal) print_1d_int_list(*true_overlap_lists[0],string_output);
  )
  
  // Generate the overlap lists for the remaining volumes
  struct pointer_to_1d_int_list temp_list_local;
  temp_list_local.num_elements = number_of_volumes;
  temp_list_local.elements = malloc(number_of_volumes*sizeof(int));
  
  int child,used_elements;
  // Create overlap for the remaining volumes
  for (parent=1;parent<number_of_volumes;parent++) {
      true_overlap_lists[parent] = malloc(sizeof(struct pointer_to_1d_int_list));
      used_elements = 0;
      temp_list_local.elements[used_elements++] = 0; // Alwasy overlaps with the surrounding vacuum.
      
      for (child=1;child<number_of_volumes;child++) {
        if (child == parent) temp_list_local.elements[used_elements++] = child;
        else {
        if (strcmp("sphere",Volumes[parent]->geometry.shape) == 0 && strcmp("sphere",Volumes[child]->geometry.shape) == 0) {
            if (sphere_overlaps_sphere(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("cylinder",Volumes[parent]->geometry.shape) == 0 && strcmp("cylinder",Volumes[child]->geometry.shape) == 0) {
            if (cylinder_overlaps_cylinder(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("box",Volumes[parent]->geometry.shape) == 0 && strcmp("box",Volumes[child]->geometry.shape) == 0) {
            if (box_overlaps_box(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("cone",Volumes[parent]->geometry.shape) == 0 && strcmp("cone",Volumes[child]->geometry.shape) == 0) {
            if (cone_overlaps_cone(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("mesh",Volumes[parent]->geometry.shape) == 0 && strcmp("mesh",Volumes[child]->geometry.shape) == 0) {
            if (mesh_overlaps_mesh(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("box",Volumes[parent]->geometry.shape) == 0 && strcmp("cylinder",Volumes[child]->geometry.shape) == 0) {
            if (box_overlaps_cylinder(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("cylinder",Volumes[parent]->geometry.shape) == 0 && strcmp("box",Volumes[child]->geometry.shape) == 0) {
            if (cylinder_overlaps_box(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("box",Volumes[parent]->geometry.shape) == 0 && strcmp("sphere",Volumes[child]->geometry.shape) == 0) {
            if (box_overlaps_sphere(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("sphere",Volumes[parent]->geometry.shape) == 0 && strcmp("box",Volumes[child]->geometry.shape) == 0) {
            if (sphere_overlaps_box(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("sphere",Volumes[parent]->geometry.shape) == 0 && strcmp("cylinder",Volumes[child]->geometry.shape) == 0) {
            if (sphere_overlaps_cylinder(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("cylinder",Volumes[parent]->geometry.shape) == 0 && strcmp("sphere",Volumes[child]->geometry.shape) == 0) {
            if (cylinder_overlaps_sphere(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("cone",Volumes[parent]->geometry.shape) == 0 && strcmp("sphere",Volumes[child]->geometry.shape) == 0) {
            if (cone_overlaps_sphere(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("sphere",Volumes[parent]->geometry.shape) == 0 && strcmp("cone",Volumes[child]->geometry.shape) == 0) {
            if (sphere_overlaps_cone(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("cone",Volumes[parent]->geometry.shape) == 0 && strcmp("cylinder",Volumes[child]->geometry.shape) == 0) {
            if (cone_overlaps_cylinder(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("cylinder",Volumes[parent]->geometry.shape) == 0 && strcmp("cone",Volumes[child]->geometry.shape) == 0) {
            if (cylinder_overlaps_cone(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("cone",Volumes[parent]->geometry.shape) == 0 && strcmp("box",Volumes[child]->geometry.shape) == 0) {
            if (cone_overlaps_box(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("box",Volumes[parent]->geometry.shape) == 0 && strcmp("cone",Volumes[child]->geometry.shape) == 0) {
            if (box_overlaps_cone(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else {
            printf("Need overlap function for type: ");
            printf("%s",Volumes[parent]->geometry.shape);
            printf(" and type: ");
            printf("%s",Volumes[child]->geometry.shape);
            printf(".\n");
            printf("It is not yet supported to mix mesh geometries with the basic shapes, but several mesh geometries are allowed.\n");
            exit(1);
        }
        }
      }
      //allocate_list_from_temp(used_elements,temp_list_local,overlap_lists[parent]);
      allocate_list_from_temp(used_elements,temp_list_local,&temporary_overlap_lists[parent]);
      
      // Save the raw overlap data to the raw_overlap_lists[parent] list
      raw_overlap_lists[parent] = malloc(sizeof(struct pointer_to_1d_int_list));
      raw_overlap_lists[parent]->num_elements = 0;
      allocate_list_from_temp(used_elements,temp_list_local,raw_overlap_lists[parent]);
      
      if (verbal) sprintf(string_output,"Overlaps for Volume (pre mask) %d",parent);
      MPI_MASTER(
      if (verbal) print_1d_int_list(temporary_overlap_lists[parent],string_output);
      )
  }
  
  
  // The temporary_overlap_lists gives the raw overlap data for all volume pairs
  // The next tasks is to take the masks into account, so that a volume is only said to overlap another if all of these statements are true:
  // The volumes overlap each other
  // The mask's of volume 1 overlap volume 2
  // The mask's of volume 2 overlap volume 1
  // The mask's of volume 1 overlap the masks of volume 2
  
  
  int logic_var;
  int overlap_ANY,overlap_ANY_p,overlap_ANY_c;
  int mask_index,mask_index_c,mask_index_p;
  int mask_volume_index,mask_volume_index_p,mask_volume_index_c;

  for (parent=1;parent<number_of_volumes;parent++) {
    used_elements = 0;
    temp_list_local.elements[used_elements++] = 0; // Alwasy overlaps with the surrounding vacuum.
    for (child=1;child<number_of_volumes;child++) {
      if (child != parent) {
        logic_var = 1; // Assume the volumes overlap, and search for evidence that they do not.
      
        // First check if the volumes overlap
        if (0 == on_int_list(temporary_overlap_lists[parent],child)) logic_var = 0;
        
      
        // Check if child overlap with parents masks
        if (logic_var == 1 && Volumes[parent]->geometry.is_masked_volume == 1) {
          overlap_ANY = 0;
          for (mask_index=0;mask_index<Volumes[parent]->geometry.masked_by_list.num_elements;mask_index++) {
            mask_volume_index = Volumes[parent]->geometry.masked_by_list.elements[mask_index];
            if (0 == on_int_list(temporary_overlap_lists[mask_volume_index],child)) {
              if (Volumes[parent]->geometry.mask_mode == 1) {
                logic_var = 0;
                break;
              }
            } else overlap_ANY = 1;
          }
          if (Volumes[parent]->geometry.mask_mode == 2) logic_var = overlap_ANY;
        }
      
        // Check if parent overlap with childs masks
        if (logic_var == 1 && Volumes[child]->geometry.is_masked_volume == 1) {
          overlap_ANY = 0;
          for (mask_index=0;mask_index<Volumes[child]->geometry.masked_by_list.num_elements;mask_index++) {
            mask_volume_index = Volumes[child]->geometry.masked_by_list.elements[mask_index];
            if (0 == on_int_list(temporary_overlap_lists[mask_volume_index],parent)) {
              if (Volumes[child]->geometry.mask_mode == 1) {
                logic_var = 0;
                break;
              }
            } else overlap_ANY = 1;
          }
          if (Volumes[child]->geometry.mask_mode == 2) logic_var = overlap_ANY;
        }
      
        // Check if parents masks overlap childrens masks
        if (logic_var == 1 && Volumes[parent]->geometry.is_masked_volume == 1 && Volumes[child]->geometry.is_masked_volume == 1) {
          overlap_ANY = 0;
          for (mask_index_p=0;mask_index_p<Volumes[parent]->geometry.masked_by_list.num_elements;mask_index_p++) {
            mask_volume_index_p = Volumes[parent]->geometry.masked_by_list.elements[mask_index_p];
            overlap_ANY_p = 1;
            overlap_ANY_c = 0;
            for (mask_index_c=0;mask_index_c<Volumes[child]->geometry.masked_by_list.num_elements;mask_index_c++) {
              mask_volume_index_c = Volumes[child]->geometry.masked_by_list.elements[mask_index_c];
              if (0 == on_int_list(temporary_overlap_lists[mask_volume_index_p],mask_volume_index_c)) {
                if (Volumes[parent]->geometry.mask_mode == 1 && Volumes[child]->geometry.mask_mode == 1) {
                  // If both are in ALL mode and just one combination of masks does not overlap, neither does the common set
                  logic_var = 0;
                  break;
                }
                if (Volumes[parent]->geometry.mask_mode == 2 && Volumes[child]->geometry.mask_mode == 1) {
                  // If the parent is in ANY mode, but the child is in ALL, any one child not overlapping this parent mask, stops the chance for this parent mask
                  overlap_ANY_p = 0;
                  break;
                }
                
              } else {
                // Here because mask_volume_index_p and mask_volume_index_c does overlap
                if (Volumes[parent]->geometry.mask_mode == 1 && Volumes[child]->geometry.mask_mode == 2) {
                  // If the parent is in ALL mode and the child is in ANY mode, stop if a single parent volume does not overlap any child
                  overlap_ANY_c = 1;
                }
                if (Volumes[parent]->geometry.mask_mode == 2 && Volumes[child]->geometry.mask_mode == 2) {
                  // If both parent and child are in any mode, any one overlap between the masks is sufficient
                  overlap_ANY = 1;
                  // Could actually just commit to the overlap list here, and stop all loops.
                }
              }
              
            }
            if (Volumes[parent]->geometry.mask_mode == 1 && Volumes[child]->geometry.mask_mode == 2) logic_var = overlap_ANY_c;
            if (Volumes[parent]->geometry.mask_mode == 2 && Volumes[child]->geometry.mask_mode == 1 && overlap_ANY_p == 1) {
              // When parent is in any mode, and child is in ALL mode, any parent mask that overlaps all children masks is enough to end the parent loop
              logic_var = 1;
              break; // Without this break, only the last parent will matter
            }
            // if (overlap_ANY == 1) break; would speed things up a bit, but only after testing has been started and then it will be repeated
          }
          if (Volumes[parent]->geometry.mask_mode == 2 && Volumes[child]->geometry.mask_mode == 2) logic_var = overlap_ANY;
          // If both volumes have the ANY mode, just one case of overlap is enough.
        }
        
        // If all of the 4 statements above evaluate to true, the two volumes parent and child do overlap and it is added to the list.
        if (logic_var == 1) temp_list_local.elements[used_elements++] = child;
        
      }
    }
    // Allocate the actual overlap list with the new temp_list_local
    allocate_list_from_temp(used_elements,temp_list_local,true_overlap_lists[parent]);
    if (verbal) sprintf(string_output,"Overlaps for Volume (post mask) %d",parent);
    MPI_MASTER(
    if (verbal) print_1d_int_list(*true_overlap_lists[parent],string_output);
    )
  }
  
  // Clean up of dynamically allocated memory
  for(child=1;child<number_of_volumes;child++) free(temporary_overlap_lists[child].elements);
  free(temporary_overlap_lists);
  free(temp_list_local.elements);
  
};

void add_to_mask_intersect_list(struct pointer_to_1d_int_list *mask_intersect_list, int given_volume_index) {
    // A bit simpler than before
    if (on_int_list(*mask_intersect_list,given_volume_index) == 0)
      add_element_to_int_list(mask_intersect_list,given_volume_index);
}


void generate_intersect_check_lists(struct pointer_to_1d_int_list **overlap_lists,struct Volume_struct **Volumes, int number_of_volumes, int verbal) {
  // Generate intersection list for each volume
  MPI_MASTER(
  if (verbal) printf("\nGenerating intersect check lists -------------------- \n");
  )
  // Take overlap list for volume n
  // Remove entries with p < p(n)
  // Remove entries with parents on the list
  
  // Mask update: Mask volumes does not have priorities, do not do step 2 for mask volumes
  // Mask update: Instead of step 3 (Remove entries with parents on the list), move these entries to a Mask intersect list if that parent is masking the entry, otherwise remove
  
  // 1) Mask update: Take overlap list for volume n
  // 2) Mask update: Remove entries with p < p(n), except mask volumes, that are moved to another list A
  // 3) Mask update: Entries with parents on the list are removed (if these parents are masked, only remove the entry if it is a child of their parents masks)
  // 4) Possible optimization: Entries with parents on the A list, are moved to the appropriate mask list for this volume
  // 5) Mask update: Entries on list A are added again, if they are not parents of volume n
  // 6) Mask update: Entries on the main list that are masked are moved to the appropriate mask list for this volume, if their mask is on list A
  
  // Justification of the top algorithm:
  // No need to check intersections with something that does not overlap this volume, so start with intersect list and remove from that.
  // Entries with p < p(n) are "beneath" this volume, and thus doesn't cut it, mask volumes are however always "above" and are treated with care (moved to list A)
  // Entries with parents still on the list is removed, as it is impossible to intersect with them without first going through the parent
  // Entries with parents on the A list can only be seen if the ray is in that particular mask (the parent on the A list), so it can be added to the appropriate mask list for this volume
  // Now all masks that cut the volume are transfered back to the main list, the only masks that overlap and does not cut are parents of this volume, and these are thus not added
  // The entries on the list that are masked are added to the appropriate mask lists of this volume, but only if their mask is on the A list, as only masks on list A can have mask status 1 which is required for this to be relevant (this was true automatically for the ones added to the appropriate lists in the optimization step).
  
  
  // The intersect check list for volume 0 is done manually
  // Declare list for holding logic data for each index in the overlap list of this volume
  struct pointer_to_1d_int_list logic_list;
  logic_list.num_elements = overlap_lists[0]->num_elements;
  logic_list.elements = malloc(logic_list.num_elements * sizeof(int));
  
  // Declare similar list for List A
  struct pointer_to_1d_int_list mask_logic_list;
  mask_logic_list.num_elements = overlap_lists[0]->num_elements;
  mask_logic_list.elements = malloc(mask_logic_list.num_elements * sizeof(int));
  
  
  int iterate;
  int overlap_index;
  
  /*
  // Thise code is an old broken version of intersection_check_list generation only for volume 0, but this case is now included in the loop.
  // Assume all volumes on overlap list should be on intersection check list, and remove the once that should not.
  for (iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 1;
  // Asuume no volumes should go on the mask_logic_list, but add the ones that do.
  for (iterate=0;iterate<mask_logic_list.num_elements;iterate++) mask_logic_list.elements[iterate] = 0;
  
  for (overlap_index = 0;overlap_index < overlap_lists[0]->num_elements;overlap_index++) {
    // No volumes to remove with lower priority, as surrounding vacuum have the lowest.
    
    // Check if this overlap_lists[0]->elements[overlap_index] is a child of another member of the overlap list
    for (iterate = 0;iterate < overlap_lists[0]->num_elements;iterate++) {
        if (iterate != overlap_index) {
            // We are now checking if Volumes[overlap_lists[0]->elements[iterate]]->geometry.children contains overlap_lists[overlap_index]
            if (on_int_list(Volumes[overlap_lists[0]->elements[iterate]]->geometry.children,overlap_lists[0]->elements[overlap_index])) logic_list.elements[overlap_index] = 0;
        }
    }
  }

  Volumes[0]->geometry.intersect_check_list.num_elements = sum_int_list(logic_list);
  Volumes[0]->geometry.intersect_check_list.elements = malloc(Volumes[0]->geometry.intersect_check_list.num_elements * sizeof(int));
  
  iterate = 0;
  for (overlap_index = 0;overlap_index < overlap_lists[0]->num_elements;overlap_index++) {
        if (logic_list.elements[overlap_index])   Volumes[0]->geometry.intersect_check_list.elements[iterate++] = overlap_lists[0]->elements[overlap_index];
  }
  if (logic_list.num_elements > 0) free(logic_list.elements);
  if (mask_logic_list.num_elements > 0) free(mask_logic_list.elements);
  
  MPI_MASTER(
  print_1d_int_list(Volumes[0]->geometry.intersect_check_list,"Intersect check list for Volume 0 (manual)");
  )
  */
  
  // The intersect check lists for the remaining volumes are generated
  int volume_index,mask_volume_number,mask_index;
  //for (volume_index = 1;volume_index < number_of_volumes;volume_index++) {
  for (volume_index = 0;volume_index < number_of_volumes;volume_index++) {
    
      // 1) Take overlap list for volume n
      logic_list.num_elements = overlap_lists[volume_index]->num_elements;
      logic_list.elements = malloc(logic_list.num_elements * sizeof(int));
      mask_logic_list.num_elements = overlap_lists[volume_index]->num_elements;
      mask_logic_list.elements = malloc(mask_logic_list.num_elements * sizeof(int));
      
      
      
      if (Volumes[volume_index]->geometry.is_mask_volume == 1) {
        // Masks do not have any entries on their intersect check list, as they are never the current volume
        for (iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 0;
        for (iterate=0;iterate<mask_logic_list.num_elements;iterate++) mask_logic_list.elements[iterate] = 0;
      } else {
        for (iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 1;
        for (iterate=0;iterate<mask_logic_list.num_elements;iterate++) mask_logic_list.elements[iterate] = 0;
      }
      
      //2) Remove entries with p < p(n), except mask volumes, that are moved to another list A
      for (overlap_index = 0;overlap_index < overlap_lists[volume_index]->num_elements;overlap_index++) {
        
        if (overlap_lists[volume_index]->elements[overlap_index] == 0) logic_list.elements[overlap_index] = 0; // The surrounding vacuum has lower priority (and is not a mask)
        else if (Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.is_mask_volume == 1) {
           logic_list.elements[overlap_index] = 0;      // Remove this volume from the main list
           mask_logic_list.elements[overlap_index] = 1; // Add mask volumes to seperat list
        }
        else if (volume_index != 0) { // Only relevant to remove elements because of priority if the volume_index is different from 0, meaning the surrounding vacuum skips this step
           if (Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.priority_value < Volumes[volume_index]->geometry.priority_value) {
           // If the investigated volume on the overlap_list have lower priority than the volume with volume_index, remove it
           logic_list.elements[overlap_index] = 0; // Remove this volume from the list
           }
        }
      }
      
      // 3) Entries with parents on the list are removed
      for (overlap_index = 0;overlap_index < overlap_lists[volume_index]->num_elements;overlap_index++) {
        // Check if this overlap_lists[0]->elements[overlap_index] is a child of another member of the overlap list
        for (iterate = 0;iterate < overlap_lists[volume_index]->num_elements;iterate++) {
             if (iterate != overlap_index) { // Only necessary if a volume determines that it has itself as child, but a nice safety.
                // We are now checking if Volumes[overlap_lists[volume_index]->elements[iterate]]->geometry.children contains overlap_lists[overlap_index]
                // The && part is needed because we do not remove children of elements that have allready been removed because of their priority
                
                // if (on_int_list(Volumes[overlap_lists[volume_index]->elements[iterate]]->geometry.children,overlap_lists[volume_index]->elements[overlap_index]) && logic_list.elements[overlap_lists[volume_index]->elements[iterate]] == 1) {logic_list.elements[overlap_index] = 0; bug fixed on 3/4 2016
                if (on_int_list(Volumes[overlap_lists[volume_index]->elements[iterate]]->geometry.children,overlap_lists[volume_index]->elements[overlap_index]) && logic_list.elements[iterate] == 1) logic_list.elements[overlap_index] = 0;
                
                /*
                Explanation with simpler notation
                
                Overlap list of volume i = O_i
                Element j of overlap list i = O_i(j)
                
                Children list of volume i = C_i
                Element j on children list i = C_i(j)
                
                Priority of volume i p(i)
                
                for i = volumes
                    for j in O_i(j) 
                        logic(j) = 1;
                        if p(i) > p(O_i(j)) logic(j) = 0; // Remove if lower priority than the currently checked
                        
                        for k in O_i(k)
                            if (O_i(j) is contained on the list C_k && logic(k) == 1) logic(j) = 0
                */
                
                // 4) Entries with parents on the A list, are moved to the appropriate mask list for this volume
                if (on_int_list(Volumes[overlap_lists[volume_index]->elements[iterate]]->geometry.children,overlap_lists[volume_index]->elements[overlap_index]) && mask_logic_list.elements[iterate] == 1) {
                   logic_list.elements[overlap_index] = 0; // Remove from main list (Not strictly needed as it will be removed already if it is on list A)
                   // Add overlap_lists[volume_index]->elements[overlap_index] to volumes mask intersect list for mask with index overlap_lists[volume_index]->elements[iterate]
                   //add_to_mask_intersect_lists(&Volumes[volume_index]->geometry.mask_intersect_lists,overlap_lists[volume_index]->elements[iterate],overlap_lists[volume_index]->elements[overlap_index]);
                   // Simpler method that just collects all the masked intersect parts on a 1d_int list instead of seperating them for each mask
                   add_to_mask_intersect_list(&Volumes[volume_index]->geometry.mask_intersect_list,overlap_lists[volume_index]->elements[overlap_index]);
                }
                
             }
        }
      }
      
      // 5) Entries on list A are added again, if they are not parents of volume n
      // Time to add mask volumes removed back to the intersect list, if they are not parents of the current volume, meaning the current volume should not be a child of the mask
      for (overlap_index = 0;overlap_index < overlap_lists[volume_index]->num_elements;overlap_index++) {
        if (mask_logic_list.elements[overlap_index] == 1) {
          mask_volume_number = overlap_lists[volume_index]->elements[overlap_index];
          if (on_int_list(Volumes[mask_volume_number]->geometry.children,volume_index) == 0) logic_list.elements[overlap_index] = 1; // Add it back to the list
        }
      }
      
      // 6) Entries on the main list that are masked are moved to the appropriate mask list for this volume, if their mask is on list A
      int mask_index,mask_mode,found_index,logic_ALL;
      for (overlap_index = 0;overlap_index < overlap_lists[volume_index]->num_elements;overlap_index++) {
        if (logic_list.elements[overlap_index] == 1 && Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.is_masked_volume == 1) {
          logic_list.elements[overlap_index] = 0; // If the volume is masked, remove it from the intersect check list
          // Could actually keep it on the intersect list if the volume's mask is a parent of the current volume, now it will just be added in all cases
          
          // When ALL setting is used, all masks should overlap the current volume, otherwise the user probably made a mistake, this should be checked in error checking
          // When ANY setting is used, at least one mask should overlap the current volume, otherwise the user probably made a mistake, this should be checked in error checking
          // mask_mode == 1 => ALL / mask_mode == 2 => ANY
          mask_mode = Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.mask_mode;
          
          if (mask_mode == 1) {
            logic_ALL = 1;
            for (iterate=0;iterate<Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.masked_by_list.num_elements;iterate++) {
              mask_index = Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.masked_by_list.elements[iterate];
              
              if (on_int_list(*overlap_lists[volume_index],mask_index) == 0) {
                // If any one of the volumes masks do not overlap with this volume, there is no reason check intersections with the volume regardless of the mask status's
                logic_ALL = 0;
                break;
              }
            }
          if (logic_ALL == 1) {
            // If all of it's masks are overlapping the current volume, add it to all relevant mask intersect lists of this volume
            for (iterate=0;iterate<Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.masked_by_list.num_elements;iterate++) {
              mask_index = Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.masked_by_list.elements[iterate];
              //add_to_mask_intersect_lists(&Volumes[volume_index]->geometry.mask_intersect_lists,mask_index,overlap_lists[volume_index]->elements[overlap_index]);
              // Adding the masked intersect list elements to another list
              add_to_mask_intersect_list(&Volumes[volume_index]->geometry.mask_intersect_list,overlap_lists[volume_index]->elements[overlap_index]);
            }
          }
            
            
          } else if (mask_mode == 2) {
          // When in ANY mode, the problem is easier, as not all masks have to overlap the volume, and we can add each one to the list
            for (iterate=0;iterate<Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.masked_by_list.num_elements;iterate++) {
              mask_index = Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.masked_by_list.elements[iterate];
            
              if (on_int_list(*overlap_lists[volume_index],mask_index) == 1) {
                //add_to_mask_intersect_lists(&Volumes[volume_index]->geometry.mask_intersect_lists,mask_index,overlap_lists[volume_index]->elements[overlap_index]);
                // Adding the masked intersect list elements to another list
                add_to_mask_intersect_list(&Volumes[volume_index]->geometry.mask_intersect_list,overlap_lists[volume_index]->elements[overlap_index]);
              }
            }
            
          }
        }
      }
      
      
      Volumes[volume_index]->geometry.intersect_check_list.num_elements = sum_int_list(logic_list);
      Volumes[volume_index]->geometry.intersect_check_list.elements = malloc(Volumes[volume_index]->geometry.intersect_check_list.num_elements * sizeof(int));
      
      iterate = 0;
      for (overlap_index = 0;overlap_index < overlap_lists[volume_index]->num_elements;overlap_index++) {
            if (logic_list.elements[overlap_index])   Volumes[volume_index]->geometry.intersect_check_list.elements[iterate++] = overlap_lists[volume_index]->elements[overlap_index];
      }
      free(logic_list.elements); // Need to be careful with names for variables to be freed, as they can collide with names in main because of automatic declaration of external variables
      free(mask_logic_list.elements);
      
      
      char string_output[128];
      MPI_MASTER(
      if (verbal) sprintf(string_output,"Intersect check list for Volume %d",volume_index);
      if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.intersect_check_list,string_output);
      if (verbal) sprintf(string_output,"Mask intersect check list for Volume %d",volume_index);
      if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.mask_intersect_list,string_output);
      /*
      if (verbal) {
        for (mask_index=0;mask_index<Volumes[volume_index]->geometry.mask_intersect_lists.number_of_lists;mask_index++) {
          sprintf(string_output," - mask intersect check list for mask %d which is volume %d",mask_index,Volumes[volume_index]->geometry.mask_intersect_lists.mask_indices.elements[mask_index]);
          print_1d_int_list(Volumes[volume_index]->geometry.mask_intersect_lists.lists[mask_index],string_output);
        }
      }
      */
      
      )
  }
};

void generate_parents_lists(struct pointer_to_1d_int_list **parents_lists, struct Volume_struct **Volumes, int number_of_volumes, int verbal, int mask_mode) {
  // Function for generating parent lists for all volumes
  // A volume m has n as a parent, if volume n has volume m as a child
  
  // if mask_mode == 0, masks are ignored, if mask_mode == 1, masks are taken into account.
  
  MPI_MASTER(
  if (verbal) {
    if (mask_mode == 1)
        printf("\nGenerating parents lists ---------------------------- \n");
    else if (mask_mode == 0)
        printf("\nGenerating parents lists (ignoring masks) ----------- \n");
    else {
        printf("Error, the function parents_lists got a non defined mask_mode");
        exit(1);
    }
  }
  
  )
  // Volume iterate has volume p as a parent, if volume p has volume iterate as child.
  
  struct pointer_to_1d_int_list temp_list_local;
  temp_list_local.num_elements = number_of_volumes;
  temp_list_local.elements = malloc(number_of_volumes*sizeof(int));
  
  // Loop over
  int iterate,parent,used_elements;
  for (iterate = 0;iterate < number_of_volumes;iterate++) {
    // clear temp list
    used_elements = 0;
    parents_lists[iterate] = malloc(sizeof(struct pointer_to_1d_int_list));
    for (parent = 0;parent < number_of_volumes;parent++) {
        if (on_int_list(Volumes[parent]->geometry.children,iterate))
          if (mask_mode == 1 || (Volumes[parent]->geometry.is_mask_volume == 0 && Volumes[iterate]->geometry.is_mask_volume == 0))
            temp_list_local.elements[used_elements++] = parent;
    }
      allocate_list_from_temp(used_elements,temp_list_local,parents_lists[iterate]);
      
      char string_output[128];
      MPI_MASTER(
      if (verbal) sprintf(string_output,"Parents for Volume %d",iterate);
      if (verbal) print_1d_int_list(*parents_lists[iterate],string_output);
      )
  }
  free(temp_list_local.elements);

};

void generate_true_parents_lists(struct pointer_to_1d_int_list **parents_lists, struct pointer_to_1d_int_list **true_children_lists, struct Volume_struct **Volumes, int number_of_volumes, int verbal, int mask_mode) {
  // Function for generating parent lists for all volumes
  // A volume m has n as a parent, if volume n has volume m as a child
  
  // if mask_mode == 0, masks are ignored, if mask_mode == 1, masks are taken into account.
  
  MPI_MASTER(
  if (verbal) {
    if (mask_mode == 1)
        printf("\nGenerating parents lists ---------------------------- \n");
    else if (mask_mode == 0)
        printf("\nGenerating parents lists (ignoring masks) ----------- \n");
    else {
        printf("Error, the function parents_lists got a non defined mask_mode");
        exit(1);
    }
  }
  
  )
  // Volume iterate has volume p as a parent, if volume p has volume iterate as child.
  
  struct pointer_to_1d_int_list temp_list_local;
  temp_list_local.num_elements = number_of_volumes;
  temp_list_local.elements = malloc(number_of_volumes*sizeof(int));
  
  // Loop over
  int iterate,parent,used_elements;
  for (iterate = 0;iterate < number_of_volumes;iterate++) {
    // clear temp list
    used_elements = 0;
    parents_lists[iterate] = malloc(sizeof(struct pointer_to_1d_int_list)); // allocate_list_from_temp allocates
    for (parent = 0;parent < number_of_volumes;parent++) {
        //if (on_int_list(Volumes[parent]->geometry.children,iterate))
        if (on_int_list(*true_children_lists[parent],iterate))
          if (mask_mode == 1 || (Volumes[parent]->geometry.is_mask_volume == 0 && Volumes[iterate]->geometry.is_mask_volume == 0))
            temp_list_local.elements[used_elements++] = parent;
    }
      allocate_list_from_temp(used_elements,temp_list_local,parents_lists[iterate]);
      
      char string_output[128];
      MPI_MASTER(
      if (verbal) sprintf(string_output,"Parents for Volume %d",iterate);
      if (verbal) print_1d_int_list(*parents_lists[iterate],string_output);
      )
  }
  free(temp_list_local.elements);

};

void generate_intersect_check_lists_experimental(struct pointer_to_1d_int_list **true_overlap_lists, struct pointer_to_1d_int_list **raw_overlap_lists, struct pointer_to_1d_int_list **parents_lists, struct pointer_to_1d_int_list **true_parents_lists , struct Volume_struct **Volumes, int number_of_volumes, int verbal) {
  // Generates the intersect_check_list and mask_intersect_list for each Volume.
  /*
  Description of needed lists for volume n:
  Children list: List of volumes that is contained within volume n (is stored in the Volumes struct)
  True Children list: List of volumes that when masked by their masks is contained within volume n when masked by it's masks
  
  Parents list: List of volumes that contains volume n
  True parents list: List of volumes that when masked by their masks contains volume n when it is masked by it's masks
  
  raw overlap list: List of volumes whos geometry overlaps the geometry of volume n
  true overlap list: List of volumes whos geometry overlaps the geometry of volume n when the masks of both volumes are applied
  
  
  
  The algorithm:
  
  1) Take the true overlap list for volume n
  2) remove parents of n (normal parent list, with masks)
  3) remove volumes that do not mask n and are true parents of n
  4) remove volumes that are not masks and have lower priority than n
  5) remove volumes that have at least one true parent on the list (in step 4) that is not a mask volume
  6) split the list into two, the intersect_check_list which is all non masked volumes still on the list, and the mask_intersect_list which is the masked volumes
  7) remove volumes on the mask_intersect_list whos mask does not overlap (standard overlap list) n
  
  In step 5 the order in which the volumes are tested and removed may matter, so it is specifically stated that it is as the list looked in step 4.
  */
  
  MPI_MASTER(
  if (verbal) printf("\nGenerating intersect check lists -------------------- \n");
  )
  
  struct pointer_to_1d_int_list work_list;
  struct pointer_to_1d_int_list logic_list;
  
  int volume_index,iterate,parent,mask_index,masked_volume_index,ANY_logic,true_parent_volume_number;
  int *mask_check,*mask_start;
  for (volume_index = 0;volume_index < number_of_volumes;volume_index++) {
    
    // 1) Take the true overlap list for volume n
    // Create copy of true_overlap_lists to work with
    
    if (Volumes[volume_index]->geometry.is_mask_volume == 1) {
      // Bug fixed on 26/11/2016, do not create intersection lists for masks as they are not used, and affects destinations lists in a problematic way
      Volumes[volume_index]->geometry.intersect_check_list.num_elements = 0;
      Volumes[volume_index]->geometry.mask_intersect_list.num_elements = 0;
      
    } else {
      work_list.num_elements = true_overlap_lists[volume_index]->num_elements;
      work_list.elements = malloc(work_list.num_elements * sizeof(int));
      for (iterate=0;iterate<work_list.num_elements;iterate++) work_list.elements[iterate] = true_overlap_lists[volume_index]->elements[iterate];
    
    //if (verbal) print_1d_int_list(work_list,"After 1)");
    
    
    //2) remove parents of n (normal parent list, with masks)
    for (iterate=work_list.num_elements-1;iterate>-1;iterate--) {
      if (on_int_list(*parents_lists[volume_index],work_list.elements[iterate]))
        remove_element_in_list_by_index(&work_list,iterate);
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 2)");
    
    //3) remove volumes that do not mask n and are true parents of n
    for (iterate=work_list.num_elements-1;iterate>-1;iterate--) {
      if (on_int_list(Volumes[volume_index]->geometry.masked_by_list,work_list.elements[iterate]) == 0 && on_int_list(*true_parents_lists[volume_index],work_list.elements[iterate]))
        remove_element_in_list_by_index(&work_list,iterate);
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 3)");
    
    //4) remove volumes that are not masks and have lower priority than n
    for (iterate=work_list.num_elements-1;iterate>-1;iterate--) {
      if (Volumes[work_list.elements[iterate]]->geometry.is_mask_volume == 0 && Volumes[work_list.elements[iterate]]->geometry.priority_value < Volumes[volume_index]->geometry.priority_value)
        remove_element_in_list_by_index(&work_list,iterate);
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 4)");
    
    //5) remove volumes that have at least one true parent on the list (in step 4) that is not a mask volume
    // Here a logic_list is used to not have the order of removal matter
    logic_list.num_elements = work_list.num_elements;
    logic_list.elements = malloc(logic_list.num_elements * sizeof(int));
    for(iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 1;
    
    for (iterate=0;iterate<work_list.num_elements;iterate++) {
      for (parent=0;parent<true_parents_lists[work_list.elements[iterate]]->num_elements;parent++) {
        true_parent_volume_number = true_parents_lists[work_list.elements[iterate]]->elements[parent];
        if (on_int_list(work_list,true_parent_volume_number) && Volumes[true_parent_volume_number]->geometry.is_mask_volume == 0) {
          // Since element number iterate on the work list have a true parent on the work list, it can be removed
          logic_list.elements[iterate] = 0;
          break;
        }
      }
    }
    
    // Now the elements marked for removal can be removed without interfering in the operation
    for (iterate=work_list.num_elements-1;iterate>-1;iterate--) {
      if (logic_list.elements[iterate] == 0) remove_element_in_list_by_index(&work_list,iterate);
    }
    free(logic_list.elements);
    
    //if (verbal) print_1d_int_list(work_list,"After 5)");
      
    //6) split the list into two, the intersect_check_list which is all non masked volumes still on the list, and the mask_intersect_list which is the masked volumes
    
    Volumes[volume_index]->geometry.intersect_check_list.num_elements = 0;
    Volumes[volume_index]->geometry.mask_intersect_list.num_elements = 0;
    
    for (iterate=0;iterate<work_list.num_elements;iterate++) {
      if (Volumes[work_list.elements[iterate]]->geometry.is_masked_volume == 1) {
        add_element_to_int_list(&Volumes[volume_index]->geometry.mask_intersect_list,work_list.elements[iterate]);
      } else {
        add_element_to_int_list(&Volumes[volume_index]->geometry.intersect_check_list,work_list.elements[iterate]);
      }
    }
    
    //if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.intersect_check_list,"After 6) intersect_check_list");
    //if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.mask_intersect_list,"After 6) mask_intersect_list");
    
    //7) remove volumes on the mask_intersect_list whos masks does not overlap (standard overlap list) n
    for (iterate=Volumes[volume_index]->geometry.mask_intersect_list.num_elements-1;iterate>-1;iterate--) {
      // Need to check if the volumes masking Volumes[volume_index]->geometry.mask_intersect_list.elements[iterate] overlaps with n
      masked_volume_index = Volumes[volume_index]->geometry.mask_intersect_list.elements[iterate];
      
      if (Volumes[masked_volume_index]->geometry.mask_mode == 1) {// All mode, if just one does not overlap, remove the element
        for (mask_start=mask_check=Volumes[masked_volume_index]->geometry.masked_by_list.elements;mask_check-mask_start<Volumes[masked_volume_index]->geometry.masked_by_list.num_elements;mask_check++) {
          if (on_int_list(*raw_overlap_lists[volume_index],*mask_check) == 0) {
            remove_element_in_list_by_index(&Volumes[volume_index]->geometry.mask_intersect_list,iterate);
            break;
          }
        }
      } else { // ANY mode, just one need to overlap in order to keep the element
        ANY_logic = 0;
        for (mask_start=mask_check=Volumes[masked_volume_index]->geometry.masked_by_list.elements;mask_check-mask_start<Volumes[masked_volume_index]->geometry.masked_by_list.num_elements;mask_check++) {
          if (on_int_list(*raw_overlap_lists[volume_index],*mask_check) == 1) {
            ANY_logic = 1;
            break;
          }
        }
        if (ANY_logic == 0) remove_element_in_list_by_index(&Volumes[volume_index]->geometry.mask_intersect_list,iterate);
      }
    }
    
    if (work_list.num_elements > 0) free(work_list.elements);
    }
    
    
    //if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.mask_intersect_list,"After 7) mask_intersect_list");
    
    char string_output[128];
    MPI_MASTER(
    if (verbal) sprintf(string_output,"Intersect check list for Volume %d",volume_index);
    if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.intersect_check_list,string_output);
    if (verbal) sprintf(string_output,"Mask intersect check list for Volume %d",volume_index);
    if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.mask_intersect_list,string_output);
    )
    
    
  }
}

void generate_grandparents_lists(struct pointer_to_1d_int_list **grandparents_lists, struct pointer_to_1d_int_list **parents_lists, int number_of_volumes, int verbal) {
  // Function for generating grandparents lists
  // Volume iterate has volume p as a grandparent, if volume p has a parent T, who has volume iterate as parent.
  // Alternertively:
  // Volume iterate has volume p as a grandparent, if volume p have a child that is volume iterate's parent.
  
  MPI_MASTER(
  if (verbal) printf("\nGenerating grandparents lists ----------------------- \n");
  )

  struct pointer_to_1d_int_list common;
  common.num_elements = number_of_volumes;
  common.elements = malloc(common.num_elements*sizeof(int)); // Maximum needed space.
  
  struct pointer_to_1d_int_list temp_list_local;
  temp_list_local.num_elements = number_of_volumes;
  temp_list_local.elements = malloc(number_of_volumes*sizeof(int));
  
  int iterate,reset_int,parent,child,used_elements;
  for (iterate = 0;iterate < number_of_volumes;iterate++) {
    // clear temp list
    used_elements = 0;

    for (reset_int=0; reset_int<number_of_volumes; reset_int++) common.elements[reset_int] = -1; // Initialize to impossible volume ids
    for (reset_int=0; reset_int<number_of_volumes; reset_int++) temp_list_local.elements[reset_int] = -1; // Initialize to impossible volume ids

    grandparents_lists[iterate] = malloc(sizeof(struct pointer_to_1d_int_list));
    
    for (parent = 0; parent<parents_lists[iterate]->num_elements; parent++) {
        // parent number p parents_lists[iterate].elements.[p] in the parent_list for iterate.
        on_both_int_lists(parents_lists[parents_lists[iterate]->elements[parent]], parents_lists[iterate], &common);
        // returns a pointer_to_1d_list, with all the elements that are in common.
        for (child = 0;child < common.num_elements;child++) {
            // Need to make sure the element is not already on the list
            if (0 == on_int_list(temp_list_local, common.elements[child])) {
              temp_list_local.elements[used_elements++] = common.elements[child];
            }
        }
    }
    allocate_list_from_temp(used_elements, temp_list_local, grandparents_lists[iterate]);
  
    char string_output[128];
    MPI_MASTER(
      if (verbal) sprintf(string_output,"Grandparents for Volume %d", iterate);
      if (verbal) print_1d_int_list(*grandparents_lists[iterate], string_output);
    )
  }
  free(temp_list_local.elements);
  free(common.elements);
};


void generate_destinations_lists_experimental(struct pointer_to_1d_int_list **true_overlap_lists, struct pointer_to_1d_int_list **true_children_lists, struct pointer_to_1d_int_list **true_parents_lists, struct pointer_to_1d_int_list **true_grandparents_lists, struct Volume_struct **Volumes, int number_of_volumes, int verbal) {
  // Generates destinations list for for all volumes
  // Current implementation uses true_parents_lists and true_grandparents_lists that are generated as if no masks were defined

  MPI_MASTER(
  if (verbal) printf("\nGenerating destinations lists ----------------------- \n");
  )
  // Volume 0 has an hardcoded empty destinations list
  Volumes[0]->geometry.destinations_list.num_elements = 0;

  struct pointer_to_1d_int_list work_list;
  int volume_index,iterate,iterate2,found_index,I_index,I_volume;
  
  for (volume_index=1;volume_index<number_of_volumes;volume_index++) {
    
    // 1) Take the true overlap list for volume n
    // Create copy of true_overlap_lists to work with
    work_list.num_elements = true_overlap_lists[volume_index]->num_elements;
    work_list.elements = malloc(work_list.num_elements * sizeof(int));
    for (iterate=0;iterate<work_list.num_elements;iterate++) work_list.elements[iterate] = true_overlap_lists[volume_index]->elements[iterate];
    
    //if (verbal) print_1d_int_list(work_list,"After 1)");
    
    // 2) Remove elements from n's intersection list
    for (iterate=work_list.num_elements-1;iterate>-1;iterate--) {
      if (on_int_list(Volumes[volume_index]->geometry.intersect_check_list,work_list.elements[iterate]))
        remove_element_in_list_by_index(&work_list,iterate);
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 2)");
  
    // 3) Remove true children of non-mask elements on n's intersection list
    for (I_index=0;I_index<Volumes[volume_index]->geometry.intersect_check_list.num_elements;I_index++) {
      I_volume = Volumes[volume_index]->geometry.intersect_check_list.elements[I_index];
      if (Volumes[I_volume]->geometry.is_mask_volume == 0) {
        for (iterate=0;iterate<true_children_lists[I_volume]->num_elements;iterate++) {
          remove_element_in_list_by_value(&work_list,true_children_lists[I_volume]->elements[iterate]);
        }
      }
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 3)");
    
    // 4) Remove elements from mask intersection list
    for (iterate=work_list.num_elements-1;iterate>-1;iterate--) {
      if (on_int_list(Volumes[volume_index]->geometry.mask_intersect_list,work_list.elements[iterate]))
        remove_element_in_list_by_index(&work_list,iterate);
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 4)");
    
    // 5) Remove true children of elements on n's mask intersection list
    for (I_index=0;I_index<Volumes[volume_index]->geometry.mask_intersect_list.num_elements;I_index++) {
      I_volume = Volumes[volume_index]->geometry.mask_intersect_list.elements[I_index];
      if (Volumes[I_volume]->geometry.is_mask_volume == 0) {
        for (iterate=0;iterate<true_children_lists[I_volume]->num_elements;iterate++) {
          remove_element_in_list_by_value(&work_list,true_children_lists[I_volume]->elements[iterate]);
        }
      }
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 5)");
    
    // 6) Remove true children of n
    for (iterate=0;iterate<true_children_lists[volume_index]->num_elements;iterate++)
      remove_element_in_list_by_value(&work_list,true_children_lists[volume_index]->elements[iterate]);
      
    //if (verbal) print_1d_int_list(work_list,"After 6)");
      
    // 7) Remove true grandparents of n
    for (iterate=0;iterate<true_grandparents_lists[volume_index]->num_elements;iterate++)
      remove_element_in_list_by_value(&work_list,true_grandparents_lists[volume_index]->elements[iterate]);
      
    //if (verbal) print_1d_int_list(work_list,"After 7)");
      
    // 8) Remove mask volumes
    for (iterate=work_list.num_elements-1;iterate>-1;iterate--) {
      if (Volumes[work_list.elements[iterate]]->geometry.is_mask_volume == 1)
        remove_element_in_list_by_index(&work_list,iterate);
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 8)");
    
    // 9) Remove true parents of n on the list that has other true parents of n on the list with higher priority
    for (iterate=work_list.num_elements-1;iterate>-1;iterate--) {
      if (on_int_list(*true_parents_lists[volume_index],work_list.elements[iterate])){
        // work_list.elements[iterate] is the volume index of a volume that is a true parent of n
        for (iterate2=0;iterate2<work_list.num_elements;iterate2++) {
          if (on_int_list(*true_parents_lists[volume_index],work_list.elements[iterate2]) && iterate != iterate2) {
            if (Volumes[work_list.elements[iterate]]->geometry.priority_value < Volumes[work_list.elements[iterate2]]->geometry.priority_value) {
              //printf("Removing element number %d (V%d) because element number %d (V%d) had higher priority \n",iterate,work_list.elements[iterate],iterate2,work_list.elements[iterate2]);
              remove_element_in_list_by_index(&work_list,iterate);
              break; // Missing break inserted on 14/9/2016
            }
          }
        }
      }
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 9)");
    
    // 10) The remaining list is the destinations_list
    Volumes[volume_index]->geometry.destinations_list.num_elements = work_list.num_elements;
    Volumes[volume_index]->geometry.destinations_list.elements = malloc(Volumes[volume_index]->geometry.destinations_list.num_elements*sizeof(int));
    
    for (iterate=0;iterate<work_list.num_elements;iterate++)
      Volumes[volume_index]->geometry.destinations_list.elements[iterate] = work_list.elements[iterate];
    
    // Clean up after work_list so the next can be allocated
    free(work_list.elements);
  
  
    char string_output[128];
    MPI_MASTER(
    if (verbal) sprintf(string_output,"Destinations list for Volume %d",volume_index);
    if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.destinations_list,string_output);
    )

  }

};

void generate_destinations_list(int N_volume,struct Volume_struct **Volumes,struct pointer_to_1d_int_list original_overlap_list,struct pointer_to_1d_int_list *parent_list, struct pointer_to_1d_int_list *grandparent_list) {
    // This function generates the destinations_list for a single volume index, N_volume
    // The destination list describes which volumes a ray can enter when leaving N_volume
    // Each of the 6 steps for the algorithm is commented individually, and debug print statements are available
    
    // Mask update: Mask volumes are to be removed from all destinations_list as they can not be the current volume
    // It is not that simple, as some volume will then get empty destination lists. Need to revise this algorithm.
    
    // 1) Start with the overlap list of volume N
    // 2) remove all Volumes from the overlap list of N, which is also on the intersect_check_list
    // 3) remove the children of volumes removed in step 2)
    // 4) remove the children of N
    // 5) remove the grandparents of N
    // 6) remove volumes with lower priority than parents of N still on the list
    // 7) The remaing list is the destinations list
    
    
    // The destination list system should run without masks altogether, meaning parent and grandparent lists should not use them,
    //  and all masks should be removed in step 2
    
    // 1) Start with the overlap list of volume N
    // 2) remove all masks on the list
    // 3) remove all Volumes from the overlap list of N, which is also on the intersect_check_list
    // 4) remove the children of volumes removed in step 2) (if the removed volume was not a mask )
    // 5) remove the children of N
    // 6) remove the grandparents of N
    // 7) remove volumes with lower priority than parents of N still on the list
    // 8) The remaing list is the destinations list
    
    

    // 1) Start with the overlap list of volume N
    struct pointer_to_1d_int_list overlap_list;
    overlap_list.num_elements = original_overlap_list.num_elements;
    overlap_list.elements = malloc(overlap_list.num_elements*sizeof(int));
    int iterate;
    for (iterate=0;iterate<overlap_list.num_elements;iterate++) overlap_list.elements[iterate] = original_overlap_list.elements[iterate];
    
    char string_output[124];
    // sprintf(string_output,"Destinations list for Volume %d step 1",N_volume);
    // print_1d_int_list(overlap_list,string_output);
    
    // 2) remove all masks
    for (iterate=overlap_list.num_elements-1;iterate > -1;iterate--) {
        if (Volumes[overlap_list.elements[iterate]]->geometry.is_mask_volume == 1) {
            remove_element_in_list_by_index(&overlap_list,iterate);
        }
    }
    
    // 3) remove all Volumes from the overlap list of N, which is also on the intersect_check_list
    struct pointer_to_1d_int_list removed_under_2;
    removed_under_2.num_elements = 0;
    int to_check;
    removed_under_2.elements = malloc( Volumes[N_volume]->geometry.intersect_check_list.num_elements * sizeof(int));
    for (iterate=0;iterate < Volumes[N_volume]->geometry.intersect_check_list.num_elements;iterate++) {
        to_check = Volumes[N_volume]->geometry.intersect_check_list.elements[iterate];
        if (on_int_list(overlap_list,to_check)) {
            removed_under_2.elements[removed_under_2.num_elements++] = to_check;
            remove_element_in_list_by_value(&overlap_list,to_check);
        }
    }
    
    // sprintf(string_output,"Destinations list for Volume %d step 2",N_volume);
    // print_1d_int_list(overlap_list,string_output);
    
    // 4) remove the children of volumes removed in step 2)
    int children;
    for (iterate=0;iterate<removed_under_2.num_elements;iterate++){
        for (children = 0;children < Volumes[removed_under_2.elements[iterate]]->geometry.children.num_elements;children++) {
          remove_element_in_list_by_value(&overlap_list,Volumes[removed_under_2.elements[iterate]]->geometry.children.elements[children]);
        }
    }
    
    // sprintf(string_output,"Destinations list for Volume %d step 3",N_volume);
    // print_1d_int_list(overlap_list,string_output);
    
    // 5) remove the children of N
    for (children = 0;children < Volumes[N_volume]->geometry.children.num_elements;children++) {
        remove_element_in_list_by_value(&overlap_list,Volumes[N_volume]->geometry.children.elements[children]);
    }
    
    // sprintf(string_output,"Destinations list for Volume %d step 4",N_volume);
    // print_1d_int_list(overlap_list,string_output);
    
    // 6) remove the grandparents of N
    int grandparent;
    for (grandparent = 0;grandparent < grandparent_list->num_elements;grandparent++) {
        remove_element_in_list_by_value(&overlap_list,grandparent_list->elements[grandparent]);
    }
    
    // sprintf(string_output,"Destinations list for Volume %d step 5",N_volume);
    // print_1d_int_list(overlap_list,string_output);
    
    // 7) remove volumes with lower priority than parents of N still on the list
    struct pointer_to_1d_int_list logic_list;
    logic_list.num_elements = overlap_list.num_elements;
    if (logic_list.num_elements>0) logic_list.elements = malloc(logic_list.num_elements*sizeof(int));
    for (iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 1;
    
    int parent;
    for (parent=0;parent<parent_list->num_elements;parent++) {
      if (on_int_list(overlap_list,parent_list->elements[parent])) {
        // Found a parent to N on the list, now check all other elements on the list
        for (iterate=0;iterate<overlap_list.num_elements;iterate++) {
          if (parent_list->elements[parent] != overlap_list.elements[iterate]) {
            // if the element iterate have lower priority than the parent, remove it from the list
            if (Volumes[overlap_list.elements[iterate]]->geometry.priority_value < Volumes[parent_list->elements[parent]]->geometry.priority_value) logic_list.elements[iterate] = 0;
          }
        }
      }
    }
    
    // 8) The remaing list is the destinations list
    Volumes[N_volume]->geometry.destinations_list.num_elements = sum_int_list(logic_list);
    Volumes[N_volume]->geometry.destinations_list.elements = malloc(Volumes[N_volume]->geometry.destinations_list.num_elements * sizeof(int));
    
    int overlap_index,used_elements=0;
    for (overlap_index=0;overlap_index < overlap_list.num_elements;overlap_index++)
      if (logic_list.elements[overlap_index] == 1)
        Volumes[N_volume]->geometry.destinations_list.elements[used_elements++] = overlap_list.elements[overlap_index];
    
    if (overlap_list.num_elements>0) free(overlap_list.elements);
    if (logic_list.num_elements>0) free(logic_list.elements);
    
    
    /*
    // Old version before rule 6 and 2 was added
    // 8) The remaing list is the destinations list
    Volumes[N_volume]->geometry.destinations_list.num_elements = overlap_list.num_elements;
    Volumes[N_volume]->geometry.destinations_list.elements = malloc(Volumes[N_volume]->geometry.destinations_list.num_elements * sizeof(int));
    
    int overlap_index;
    for (overlap_index=0;overlap_index < overlap_list.num_elements;overlap_index++)
        Volumes[N_volume]->geometry.destinations_list.elements[overlap_index] = overlap_list.elements[overlap_index];
    
    // sprintf(string_output,"Destination list for Volume %d step 6",N_volume);
    // print_1d_int_list(Volumes[N_volume]->geometry.destinations_list,string_output);
    
    if (overlap_list.num_elements>0) free(overlap_list.elements);
    */
    
    };

void generate_destinations_lists(struct pointer_to_1d_int_list **grandparents_lists, struct pointer_to_1d_int_list **parents_lists, struct pointer_to_1d_int_list **overlap_lists,struct Volume_struct **Volumes, int number_of_volumes, int verbal) {
    // Because of the complexity of the algortithm for generating the destinations list, the function is made for a single volume at the time to keep the notation simpler
    // This funtion runs the destinations list function for each volume
    MPI_MASTER(
    if (verbal) printf("\nGenerating destinations lists ----------------------- \n");
    )
    
    int volume_index;
    for (volume_index = 0;volume_index < number_of_volumes;volume_index++) {
      generate_destinations_list(volume_index,Volumes,*overlap_lists[volume_index],parents_lists[volume_index],grandparents_lists[volume_index]);
      
      char string_output[128];
      if (verbal) sprintf(string_output,"Destinations list for Volume %d",volume_index);
      MPI_MASTER(
      if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.destinations_list,string_output);
      )
    }
};

/* OBSOLETE
void generate_destinations_logic_lists(struct Volume_struct **Volumes,int number_of_volumes,int verbal) {
  // The destinations logic list is another way to store the destinations list that makes some tasks quicker
  
  MPI_MASTER(
  if (verbal) printf("\nGenerating destinations logic lists ----------------------- \n");
  )

  int volume_index;
  for (volume_index = 0;volume_index < number_of_volumes;volume_index++) {
      allocate_logic_list_from_temp(number_of_volumes,Volumes[volume_index]->geometry.destinations_list,&Volumes[volume_index]->geometry.destinations_logic_list);
      
      char string_output[128];
      MPI_MASTER(
      if (verbal) sprintf(string_output,"Destinations logic list for Volume %d",volume_index);
      if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.destinations_logic_list,string_output);
      )
  }

};
*/

void generate_reduced_destinations_lists(struct pointer_to_1d_int_list **parents_lists, struct Volume_struct **Volumes,int number_of_volumes,int verbal) {
    // The reduced destination list is the destination list of a volume, where each element that has a parent on the same destination list is removed
    // This list is to be fed to the which_volume function, as this funtion will automatically search through the direct children in a tree like manner
    // The optimization reduces the number of calculations of within functions in nested geometries.
    
    MPI_MASTER(
    if (verbal) printf("\nGenerating reduced destination lists ----------------------- \n");
    )
    
    struct pointer_to_1d_int_list logic_list;
  
    int volume_index,checked_dest_index,checked_dest_volume,rest_dest_index,rest_dest_volume,dest_index,iterate;
    for (volume_index = 0;volume_index < number_of_volumes;volume_index++) {
      
      //printf("Generating reduced destinations lists for volume %d\n",volume_index);
      logic_list.num_elements = Volumes[volume_index]->geometry.destinations_list.num_elements;
      logic_list.elements = malloc( (int) logic_list.num_elements * sizeof(int));
      //printf("Sucsessfully allocated space for %d integers in logic list %d\n",logic_list.num_elements,volume_index);
      for (iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 1;
      
      for (checked_dest_index=0;checked_dest_index<Volumes[volume_index]->geometry.destinations_list.num_elements;checked_dest_index++) {
            checked_dest_volume = Volumes[volume_index]->geometry.destinations_list.elements[checked_dest_index];
            for (rest_dest_index=0;rest_dest_index<Volumes[volume_index]->geometry.destinations_list.num_elements;rest_dest_index++) {
                rest_dest_volume = Volumes[volume_index]->geometry.destinations_list.elements[rest_dest_index];
                // As every volume has 0 as a parent, these are ignored. It would work to include this, but would add an extra trivial step to within_which_volume
                if (rest_dest_volume != 0) {
                    if (on_int_list(*parents_lists[checked_dest_volume],rest_dest_volume) == 1) {
                        // In this case, do not include element checked_dest_index on the reduced destinations list
                        // ADD mask check
                        if (Volumes[rest_dest_volume]->geometry.is_masked_volume == 0) {
                          logic_list.elements[checked_dest_index] = 0;
                        }
                    }
                }
            }
      }
      
      Volumes[volume_index]->geometry.reduced_destinations_list.num_elements = sum_int_list(logic_list);
      Volumes[volume_index]->geometry.reduced_destinations_list.elements = malloc((int)Volumes[volume_index]->geometry.reduced_destinations_list.num_elements * sizeof(int));
      
      iterate = 0;
      for (dest_index = 0;dest_index < Volumes[volume_index]->geometry.destinations_list.num_elements;dest_index++) {
            if (logic_list.elements[dest_index] == 1)   Volumes[volume_index]->geometry.reduced_destinations_list.elements[iterate++] = Volumes[volume_index]->geometry.destinations_list.elements[dest_index];
      }
      
      free(logic_list.elements);
      
      // Testing an optimization
      remove_element_in_list_by_value(&Volumes[volume_index]->geometry.reduced_destinations_list,0);
      
      char string_output[128];
      MPI_MASTER(
      if (verbal) sprintf(string_output,"Reduced destinations list for Volume %d",volume_index);
      if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.reduced_destinations_list,string_output);
      )
    }
};

void generate_direct_children_lists(struct pointer_to_1d_int_list **parents_lists, struct Volume_struct **Volumes,int number_of_volumes,int verbal) {

  MPI_MASTER(
  if (verbal) printf("\nGenerating direct children lists ----------------------- \n");
  )
  // A direct children of volume n is a volume that is a child of n, but no other child of n is its parent
  
  // Mask update: Need to check that this step does not bug out when the mask system interferes with child/parent systems
  
  struct pointer_to_1d_int_list logic_list;
  int volume_index,child,parent,iterate;
  for (volume_index = 0;volume_index < number_of_volumes;volume_index++) {
        // Temp elements is used, and its actual number of elements is edited even though the memory allocated is not changed. This is so that the list functions handles it correctly.
        // The free function will free all the allocated memory regardless of the value of the .num_elements structure field, it is just there for convinience.
      
        logic_list.num_elements = Volumes[volume_index]->geometry.children.num_elements;
        logic_list.elements = malloc(logic_list.num_elements * sizeof(int));
        for (iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 1;
      
        // Look through each child of volume n, and for each of those look through all the other children of n and search for one of them being a parent to the child
        for (child=0;child<Volumes[volume_index]->geometry.children.num_elements;child++) {
            for (parent=0;parent<parents_lists[Volumes[volume_index]->geometry.children.elements[child]]->num_elements;parent++) {
                if (on_int_list(Volumes[volume_index]->geometry.children,parents_lists[Volumes[volume_index]->geometry.children.elements[child]]->elements[parent]))
                    // If such a parent is found, remove that child from the list
                    logic_list.elements[child] = 0;
            }
        }
      
      Volumes[volume_index]->geometry.direct_children.num_elements = sum_int_list(logic_list);
      Volumes[volume_index]->geometry.direct_children.elements = malloc(Volumes[volume_index]->geometry.direct_children.num_elements*sizeof(int));
      
      iterate = 0;
      for (child = 0;child < Volumes[volume_index]->geometry.children.num_elements;child++) {
            if (logic_list.elements[child])   Volumes[volume_index]->geometry.direct_children.elements[iterate++] = Volumes[volume_index]->geometry.children.elements[child];
      }
      // Be careful with names in both main and a function, as they are automatically declared as external variables, which would then also free the main.
      free(logic_list.elements);
      
      char string_output[128];
      MPI_MASTER(
      if (verbal) sprintf(string_output,"Children list for Volume        %d",volume_index);
      if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.children,string_output);
      if (verbal) sprintf(string_output,"Direct_children list for Volume %d",volume_index);
      if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.direct_children,string_output);
      )
      
  }

};

void generate_starting_logic_list(struct starting_lists_struct *starting_lists, struct Volume_struct **Volumes, int number_of_volumes, int verbal) {
    // Function for generating logic list of volumes the ray can start in without an error.
    // Start with a list of all vacuum volumes
    // Remove all volumes that are children of non-vacuum volumes
    // It is still possible to have a volume on this list that is surrounded by non-vacuum volumes, but it is hard to detect these situations,
    //  meaning that it is ultimately partly the users responsibility to not send neutrons directly into materials.
    
    int volume_index,*start,*check;
    
    struct pointer_to_1d_int_list temp_list_local;
    temp_list_local.num_elements = number_of_volumes;
    temp_list_local.elements = malloc(number_of_volumes*sizeof(int));
    
    //if (verbal==1) printf("sucessfully allocated temp_list_local.elements, now to enter which volumes are vacuums as a logic list\n");
    temp_list_local.elements[0] = 1; // Volume 0 is a vacuum volume.
    for (volume_index = 1;volume_index < number_of_volumes;volume_index++) {
        if (Volumes[volume_index]->p_physics->is_vacuum == 1) temp_list_local.elements[volume_index] = 1;
        else temp_list_local.elements[volume_index] = 0;
    }
    // temp_list_local is now a logic list of all vacuum volumes
    //if (verbal==1) printf("list of vacuum volumes done, now to remove the children of non-vcauum volumes\n");
    for (volume_index = 1;volume_index < number_of_volumes;volume_index++) { // All volumes ...
        if (temp_list_local.elements[volume_index] == 0) { // ... that are not vacuum ...
            for (start = check = Volumes[volume_index]->geometry.children.elements;check - start < Volumes[volume_index]->geometry.children.num_elements;check++) { // ... have all their children ...
                temp_list_local.elements[*check] = 0; // .. removed from the allowed_start_logic_list
            }
        }
    }
    //if (verbal==1) printf("sucessfully removed children of non-vacuum volumes, now allocate allowed_start_logic_list\n");
    allocate_list_from_temp(number_of_volumes,temp_list_local,&starting_lists->allowed_starting_volume_logic_list);
    //if (verbal==1) printf("sucsessfully allocated allowed_start_logic_list, now freeing temp_list_local.elements. \n");
    
    free(temp_list_local.elements);
    //if (verbal==1) printf("sucessfully freed temp_list_local.elements, generate starting lists done\n");
    
    char string_output[128];
    MPI_MASTER(
    if (verbal) sprintf(string_output,"Allowed starting volume logic list");
    if (verbal) print_1d_int_list(starting_lists->allowed_starting_volume_logic_list,string_output);
    )
    
    
};

void generate_reduced_starting_destinations_list(struct starting_lists_struct *starting_lists, struct pointer_to_1d_int_list **parents_lists, struct Volume_struct **Volumes,int number_of_volumes,int verbal) {
    // The starting_destinations_list is trivial, as it contains all volumes that are not masks.


    struct pointer_to_1d_int_list logic_list;
    //printf("Generating reduced destinations lists for volume %d\n",volume_index);
    logic_list.num_elements = number_of_volumes;
    logic_list.elements = malloc( (int) logic_list.num_elements * sizeof(int));
    int iterate;
    for (iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 0;
    
    
    for (iterate=1;iterate<number_of_volumes;iterate++)
      if (Volumes[iterate]->geometry.is_mask_volume == 0) logic_list.elements[iterate] = 1;
      //else logic_list.elements[iterate] = 0;
    
    starting_lists->starting_destinations_list.num_elements = sum_int_list(logic_list);
    starting_lists->starting_destinations_list.elements = malloc(starting_lists->starting_destinations_list.num_elements*sizeof(int));
    
    int used_elements=0;
    for (iterate=1;iterate<number_of_volumes;iterate++)
      if (logic_list.elements[iterate] == 1) starting_lists->starting_destinations_list.elements[used_elements++] = iterate;
    
    MPI_MASTER(
    if (verbal) printf("\nGenerating start destinations list ------------------------------ \n");
    if (verbal) print_1d_int_list(starting_lists->starting_destinations_list,"Starting destinations list");
    )

    // The reduced starting destination list is used when a ray enters the component in the search for which volume it starts in.
    // It facilitates the same optimization as the regular destination list.
    // The start logic list is also generated, as it is very simple and does not need a seperate function
    
    // Mask update: Need to remove mask volumes from the reduced starting destination list
    
    MPI_MASTER(
    if (verbal) printf("\nGenerating reduced start destination list ----------------------- \n");
    )
    
    int checked_dest_index,checked_dest_volume,rest_dest_index,rest_dest_volume,dest_index;
    
    /* Old version before masks were introduced
    struct pointer_to_1d_int_list starting_dest_list;
    
    starting_dest_list.num_elements = number_of_volumes - 1;
    starting_dest_list.elements = malloc ( starting_dest_list.num_elements * sizeof(int));
    for (iterate=0;iterate<number_of_volumes-1;iterate++) starting_dest_list.elements[iterate] = iterate + 1; // All volumes to be checked for starting
    */
    
    logic_list.num_elements = starting_lists->starting_destinations_list.num_elements;
    free(logic_list.elements);
    logic_list.elements = malloc( (int) logic_list.num_elements * sizeof(int));
    
    //printf("Sucsessfully allocated space for %d integers in logic list %d\n",logic_list.num_elements,volume_index);
    for (iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 1;
  
    for (checked_dest_index=0;checked_dest_index<starting_lists->starting_destinations_list.num_elements;checked_dest_index++) {
        checked_dest_volume = starting_lists->starting_destinations_list.elements[checked_dest_index];
        for (rest_dest_index=0;rest_dest_index<starting_lists->starting_destinations_list.num_elements;rest_dest_index++) {
            rest_dest_volume = starting_lists->starting_destinations_list.elements[rest_dest_index];
            // As every volume has 0 as a parent, these are ignored. It would work to include this, but would add an extra trivial step to within_which_volume
            if (rest_dest_volume != 0) {
                if (on_int_list(*parents_lists[checked_dest_volume],rest_dest_volume) == 1) {
                    // In this case, do not include element checked_dest_index on the reduced destinations list
                    logic_list.elements[checked_dest_index] = 0;
                }
            }
        }
  }
  
  starting_lists->reduced_start_list.num_elements = sum_int_list(logic_list);
  starting_lists->reduced_start_list.elements = malloc((int)starting_lists->reduced_start_list.num_elements * sizeof(int));
  
  iterate = 0;
  for (dest_index = 0;dest_index < starting_lists->starting_destinations_list.num_elements;dest_index++) {
        if (logic_list.elements[dest_index] == 1) starting_lists->reduced_start_list.elements[iterate++] = starting_lists->starting_destinations_list.elements[dest_index];
  }
  
  free(logic_list.elements);
  //free(starting_dest_list.elements);
  
  MPI_MASTER(
  if (verbal) print_1d_int_list(starting_lists->reduced_start_list,"Reduced start destinations list");
  )
    
  // Making the start_logic_list.
  starting_lists->start_logic_list.num_elements = number_of_volumes;
  starting_lists->start_logic_list.elements = malloc ( starting_lists->start_logic_list.num_elements * sizeof(int));
  starting_lists->start_logic_list.elements[0] = 0;
  for (iterate=1;iterate<number_of_volumes;iterate++) starting_lists->start_logic_list.elements[iterate] = 1; // All volumes to be checked for starting volume
  MPI_MASTER(
  if (verbal) print_1d_int_list(starting_lists->start_logic_list,"Start logic list");
  )
};


void generate_next_volume_list(struct Volume_struct **Volumes, int number_of_volumes, int verbal) {
    // Generate list of volumes that can be the next volume which the ray enters. It is used for tagging, not the simulation / propagation

    // Mask update: These lists should be done as if all mask statuses are on, meaning they include all possible next volumes (will include more input to this function)
    
    // bug: next volume list is not complete and contains duplicates

    MPI_MASTER(
    if (verbal) printf("\nGenerating next volume list ------------------------------------- \n");
    )
    // Merge destinations list, intersection list and mask_intersection_list
    int volume_index,iterate,mask_index;
    struct pointer_to_1d_int_list full_intersection_list;
    full_intersection_list.num_elements=0;
    
    for (volume_index=0;volume_index<number_of_volumes;volume_index++) {
        Volumes[volume_index]->geometry.next_volume_list.num_elements = 0;
        // Before mask update
        //merge_lists(&Volumes[volume_index]->geometry.next_volume_list, &Volumes[volume_index]->geometry.destinations_list, &Volumes[volume_index]->geometry.intersect_check_list);
        
        merge_lists(&full_intersection_list, &Volumes[volume_index]->geometry.mask_intersect_list, &Volumes[volume_index]->geometry.intersect_check_list);
        merge_lists(&Volumes[volume_index]->geometry.next_volume_list,&Volumes[volume_index]->geometry.destinations_list,&full_intersection_list);
        
        /* // This complication is taken into account by adding the mask_intersect list instead
        // It is possible that the next volume is still not on this list, as when masks are encountered the next volume can be a volume they mask.
        // For each on the list, add the volumes masked by that mask (do not iterate over this, as masks can not be masked regardless)
        for (iterate=Volumes[volume_index]->geometry.next_volume_list.num_elements-1;iterate>-1;iterate--) {
          if (Volumes[Volumes[volume_index]->geometry.next_volume_list.elements[iterate]]->geometry.is_mask_volume == 1) {
            for (mask_index=0;mask_index<Volumes[Volumes[volume_index]->geometry.next_volume_list.elements[iterate]]->geometry.mask_list.num_elements;mask_index++) {
              add_element_to_int_list(&Volumes[volume_index]->geometry.next_volume_list,Volumes[Volumes[volume_index]->geometry.next_volume_list.elements[iterate]]->geometry.mask_list.elements[mask_index]);
            }
          }
        }
        */
        
        // Remove mask volumes from the next volume list, as they never occur as current_volume, and thus just create dead branches that takes up memory
        for (iterate=Volumes[volume_index]->geometry.next_volume_list.num_elements-1;iterate>-1;iterate--)
          if (Volumes[Volumes[volume_index]->geometry.next_volume_list.elements[iterate]]->geometry.is_mask_volume == 1)
            remove_element_in_list_by_index(&Volumes[volume_index]->geometry.next_volume_list,iterate);
        
        
        if (full_intersection_list.num_elements>0) free(full_intersection_list.elements);
        full_intersection_list.num_elements=0;
        
        char string_output[128];
        MPI_MASTER(
        if (verbal) sprintf(string_output,"Next volume list                %d",volume_index);
        if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.next_volume_list,string_output);
        )
    }
};

void generate_lists(struct Volume_struct **Volumes, struct starting_lists_struct *starting_lists, int number_of_volumes, int verbal) {
    // Function to control the generation of lists
    // Some lists are only needed temporary, and are thus declared here to keep them out of the main scope
    // Others are stored in the volume structs as they are needed in the trace algorithm (or tagging)
    
    
    struct pointer_to_1d_int_list **true_children_lists;
    true_children_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    // generate_children_lists both generate the normal children list for each volume, but also the true children list needed locally.
    generate_children_lists(Volumes, true_children_lists, number_of_volumes,verbal);
    
    
    struct pointer_to_1d_int_list **true_overlap_lists;
    true_overlap_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    struct pointer_to_1d_int_list **raw_overlap_lists;
    raw_overlap_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    
    generate_overlap_lists(true_overlap_lists, raw_overlap_lists, Volumes,number_of_volumes,verbal);
    
    
    //generate_intersect_check_lists(true_overlap_lists, Volumes, number_of_volumes, verbal);
    
    
    struct pointer_to_1d_int_list **parents_lists;
    parents_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    generate_parents_lists(parents_lists,Volumes,number_of_volumes,verbal,1); // The last 1 means masks are taken into account
    
    // Generate version of parent list as it would be without masks
    struct pointer_to_1d_int_list **parents_lists_no_masks;
    parents_lists_no_masks = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    generate_parents_lists(parents_lists_no_masks,Volumes,number_of_volumes,verbal,0); // The last 0 means masks are NOT taken into account
    
    // Generate version of parent list using true_children instead
    struct pointer_to_1d_int_list **true_parents_lists;
    true_parents_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    generate_true_parents_lists(true_parents_lists, true_children_lists, Volumes, number_of_volumes, verbal, 1);
    
    // Generate version of parent list no masks using true_children instead
    struct pointer_to_1d_int_list **true_parents_lists_no_masks;
    true_parents_lists_no_masks = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    generate_true_parents_lists(true_parents_lists_no_masks, true_children_lists, Volumes, number_of_volumes, verbal, 0);
    
    // New version of generate intersect lists
    generate_intersect_check_lists_experimental(true_overlap_lists, raw_overlap_lists, parents_lists, true_parents_lists, Volumes, number_of_volumes, verbal);
    
    struct pointer_to_1d_int_list **grandparents_lists;
    grandparents_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    generate_grandparents_lists(grandparents_lists,parents_lists,number_of_volumes,verbal);
    
    // Generate version of grandparents list as it would have been if no masks were defined
    struct pointer_to_1d_int_list **grandparents_lists_no_masks;
    grandparents_lists_no_masks = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    generate_grandparents_lists(grandparents_lists_no_masks,parents_lists_no_masks,number_of_volumes,verbal);
    
    // Generate true_grandparents_lists
    struct pointer_to_1d_int_list **true_grandparents_lists;
    true_grandparents_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    generate_grandparents_lists(true_grandparents_lists,true_parents_lists,number_of_volumes,verbal);
    
    struct pointer_to_1d_int_list **true_grandparents_lists_no_masks;
    true_grandparents_lists_no_masks = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    generate_grandparents_lists(true_grandparents_lists_no_masks,true_parents_lists_no_masks,number_of_volumes,verbal);
    
    // The destinations lists are generated without taking masks into account (they are removed from the overlap list in an early step)
    //generate_destinations_lists(grandparents_lists_no_masks,parents_lists_no_masks,true_overlap_lists,Volumes,number_of_volumes,verbal);
    generate_destinations_lists_experimental(true_overlap_lists, true_children_lists, true_parents_lists_no_masks, true_grandparents_lists_no_masks, Volumes, number_of_volumes, verbal);
    
    // Obsolete, found a way around them in within_which_volume, but need to test the performance difference
    // generate_destinations_logic_lists(Volumes,number_of_volumes,verbal);
    
    generate_reduced_destinations_lists(parents_lists,Volumes,number_of_volumes,verbal);
    
    generate_direct_children_lists(parents_lists,Volumes,number_of_volumes,verbal);
    
    //generate_starting_list(starting_lists,Volumes,number_of_volumes,verbal);
    generate_starting_logic_list(starting_lists,Volumes,number_of_volumes,verbal);
    
    generate_reduced_starting_destinations_list(starting_lists,parents_lists,Volumes,number_of_volumes,verbal);
    
    // This list is stored with the volumes for convinience, but is only used for tagging
    generate_next_volume_list(Volumes,number_of_volumes,verbal);
    
    // Garbage collection for temporary dynamically allocated lists. (Permanent lists freed from FINALLY)
    int iterate;
    for (iterate=0;iterate<number_of_volumes;iterate++) {
        //printf("freeing for volume nr %d\n",iterate);
        //printf("true_overlap_lists[iterate]->num_elements = %d \n",true_overlap_lists[iterate]->num_elements);
        if (true_overlap_lists[iterate]->num_elements > 0) free(true_overlap_lists[iterate]->elements);
        free(true_overlap_lists[iterate]);
        
        //printf("raw_overlap_lists[iterate]->num_elements = %d \n",raw_overlap_lists[iterate]->num_elements);
        if (raw_overlap_lists[iterate]->num_elements > 0) free(raw_overlap_lists[iterate]->elements);
        free(raw_overlap_lists[iterate]);
        
        //printf("parents_lists[iterate]->num_elements = %d \n",parents_lists[iterate]->num_elements);
        if (parents_lists[iterate]->num_elements > 0) free(parents_lists[iterate]->elements);
        free(parents_lists[iterate]);
        
        //printf("parents_lists_no_masks[iterate]->num_elements = %d \n",parents_lists_no_masks[iterate]->num_elements);
        if (parents_lists_no_masks[iterate]->num_elements > 0) free(parents_lists_no_masks[iterate]->elements);
        free(parents_lists_no_masks[iterate]);
        
        //printf("true_parents_lists[iterate]->num_elements = %d \n",true_parents_lists[iterate]->num_elements);
        if (true_parents_lists[iterate]->num_elements > 0) free(true_parents_lists[iterate]->elements);
        free(true_parents_lists[iterate]);
        
        //printf("true_parents_lists_no_masks[iterate]->num_elements = %d \n",true_parents_lists_no_masks[iterate]->num_elements);
        if (true_parents_lists_no_masks[iterate]->num_elements > 0) free(true_parents_lists_no_masks[iterate]->elements);
        free(true_parents_lists_no_masks[iterate]);
        
        //printf("grandparents_lists[iterate]->num_elements = %d \n",grandparents_lists[iterate]->num_elements);
        if (grandparents_lists[iterate]->num_elements > 0) free(grandparents_lists[iterate]->elements);
        free(grandparents_lists[iterate]);
        
        //printf("true_grandparents_lists[iterate]->num_elements = %d \n",true_grandparents_lists[iterate]->num_elements);
        if (true_grandparents_lists[iterate]->num_elements > 0) free(true_grandparents_lists[iterate]->elements);
        free(true_grandparents_lists[iterate]);
        
        //printf("grandparents_lists_no_masks[iterate]->num_elements = %d \n",grandparents_lists_no_masks[iterate]->num_elements);
        if (grandparents_lists_no_masks[iterate]->num_elements > 0) free(grandparents_lists_no_masks[iterate]->elements);
        free(grandparents_lists_no_masks[iterate]);
        
        //printf("true_grandparents_lists_no_masks[iterate]->num_elements = %d \n",true_grandparents_lists_no_masks[iterate]->num_elements);
        if (true_grandparents_lists_no_masks[iterate]->num_elements > 0) free(true_grandparents_lists_no_masks[iterate]->elements);
        free(true_grandparents_lists_no_masks[iterate]);
        
        //printf("true_children_lists[iterate]->num_elements = %d \n",true_children_lists[iterate]->num_elements);
        if (true_children_lists[iterate]->num_elements > 0) free(true_children_lists[iterate]->elements);
        free(true_children_lists[iterate]);
    }
    //printf("generate lists volume specific free completed\n");
    free(true_overlap_lists);free(raw_overlap_lists);free(parents_lists);free(true_parents_lists);free(true_parents_lists_no_masks);
    free(parents_lists_no_masks);free(true_grandparents_lists);free(grandparents_lists);free(grandparents_lists_no_masks);free(true_grandparents_lists_no_masks);
    free(true_children_lists);
    //printf("generate lists free completed\n");
};

// -------------    Focusing functions   --------------------------------------------------------

// The focusing_data structure is set up by the geometry component, and a pointer to the appropriate
//  focusing function is added to the Volume structure (for this reason the input of all the functions
//  need to be identical, at least in terms of types). In this way there are no if statements to check
//  which of these to be used in the trace, but the focus_data_struct will carry some redundant
//  information, as only the appropriate parameters are set:
// Angular focus on a rectangle (angular_focus_height / angular_focus_width)
// Spatial focus on a rectangle (spatial_focus_height / spatial_focus_width)
// Spatial focus on a disk (sptial_focus_radius)
// No focus (randvec in 4pi) (all set to zero, will select randvec circle as it is slightly faster
//
// When adding a new physical process focusing becomes very easy, as one just calls the master focusing
//  function assosiated with the volume (placed in the geometry struct), using the focus_data_struct
//  also found in the geometry struct, and the process then supports all the focusing modes. It is even
//  possible to add new focusing modes in the future by updating just the geometry components, and this
//  section.

// focus_data_struct definitioon shown here, defined at the start of this file
//struct focus_data_struct {
//Coords Aim;
//double angular_focus_width;
//double angular_focus_height;
//double spatial_focus_width;
//double spatial_focus_height;
//double spatial_focus_radius;
//Rotation absolute_rotation;
//// focusing_function creates a vector per selected criteria of focus_data_struct / selected focus function and returns solid angle
//void (*focusing_function)(Coords*, double*, struct focus_data_struct*);
////                        v_out  , solid_a,
//};

void randvec_target_rect_angular_union(Coords *v_out,double *solid_angle_out, struct focus_data_struct *focus_data) {
    // Calls the standard McStas randvec_target_rect_angular focusing function, but is with the new data input format.
    randvec_target_rect_angular(&v_out->x, &v_out->y, &v_out->z, solid_angle_out, focus_data->Aim.x,focus_data->Aim.y, focus_data->Aim.z, focus_data->angular_focus_width, focus_data->angular_focus_height,focus_data->absolute_rotation);
    //randvec_target_rect_angular(&vx, &vy, &vz, &solid_angle,aim_x, aim_y, aim_z, VarsInc.aw, VarsInc.ah, ROT_A_CURRENT_COMP);
};

void randvec_target_rect_union(Coords *v_out,double *solid_angle_out, struct focus_data_struct *focus_data) {
// Calls the standard McStas randvec_target_rect focusing function, but is with the new data input format.
    randvec_target_rect(&v_out->x, &v_out->y, &v_out->z, solid_angle_out, focus_data->Aim.x,focus_data->Aim.y, focus_data->Aim.z, focus_data->spatial_focus_width, focus_data->spatial_focus_height,focus_data->absolute_rotation);
    // randvec_target_rect(&vx, &vy, &vz, &solid_angle,aim_x, aim_y, aim_z, VarsInc.xw, VarsInc.yh, ROT_A_CURRENT_COMP);
};

void randvec_target_circle_union(Coords *v_out,double *solid_angle_out, struct focus_data_struct *focus_data) {
// Calls the standard McStas randvec_target_circle focusing function, but is with the new data input format.

    // debug input into randvec_target_circle
    //print_position(focus_data->Aim,"Aim vector input for randvec_target_circle");
    //printf("Radius input %f\n",focus_data->spatial_focus_radius);

    randvec_target_circle(&v_out->x, &v_out->y, &v_out->z, solid_angle_out, focus_data->Aim.x,focus_data->Aim.y, focus_data->Aim.z, focus_data->spatial_focus_radius);
    //randvec_target_circle(&vx, &vy, &vz, &solid_angle, aim_x, aim_y, aim_z, focus_r);
};



void focus_initialize(struct geometry_struct *geometry, Coords POS_A_TARGET, Coords POS_A_CURRENT, Rotation ROT_A_CURRENT, int target_index, double target_x, double target_y, double target_z, double angular_focus_width, double angular_focus_height, double spatial_focus_width, double spatial_focus_height, double spatial_focus_radius, char *component_name) {
    // Initialize focusing system
    // target_x/y/z needs to be double setting parameters with default value 0
    // target_index needs to be int setting parameter with default value 0
    // angular_focus_width, angular_focus_height, spatial_focus_width, spatial_focus_height, spatial_focus_radius nneds to be double setting parameters with default value 0
    // When those conditions are met, this code will identify which settings have been entered by the user and select the appropriate focusing parameters, which are loaded into the focus_data struct and the geometry struct.
    // The aim vector in the focus_data struct will be transformed from the local coordinate system of the geometry component to the coordinate system of the master component during the master component initialize

  // Input sanitation
  if (angular_focus_width < 0) {
    printf("\nERROR in Union geometry component named \"%s\", angular focus width focus_aw < 0! \n",component_name);
    exit(1);
  }
  if (angular_focus_height < 0) {
    printf("\nERROR in Union geometry component named \"%s\", angular focus width focus_ah < 0! \n",component_name);
    exit(1);
  }
  if (spatial_focus_width < 0) {
    printf("\nERROR in Union geometry component named \"%s\", spatial focus width focus_xw < 0! \n",component_name);
    exit(1);
  }
  if (spatial_focus_height < 0) {
    printf("\nERROR in Union geometry component named \"%s\", spatial focus height focus_xh < 0! \n",component_name);
    exit(1);
  }
  if (spatial_focus_radius < 0) {
    printf("\nERROR in Union geometry component named \"%s\", spatial focus radius focus_r < 0! \n",component_name);
    exit(1);
  }
  
  struct focus_data_struct focus_data;

  // Initialize focus_data_struct
  /*
  geometry->focus_data.Aim = coords_set(0,0,0);
  geometry->focus_data.angular_focus_width = 0;
  geometry->focus_data.angular_focus_height = 0;
  geometry->focus_data.spatial_focus_width = 0;
  geometry->focus_data.spatial_focus_height = 0;
  geometry->focus_data.spatial_focus_radius = 0;
  rot_copy(geometry->focus_data.absolute_rotation,ROT_A_CURRENT);
  */
  focus_data.Aim = coords_set(0,0,0);
  focus_data.angular_focus_width = 0;
  focus_data.angular_focus_height = 0;
  focus_data.spatial_focus_width = 0;
  focus_data.spatial_focus_height = 0;
  focus_data.spatial_focus_radius = 0;
  rot_copy(focus_data.absolute_rotation,ROT_A_CURRENT);

  // Built on code from Incoherent.comp by Kim Lefmann and Kristian Nielsen
  if (target_index != 0 && !target_x && !target_y && !target_z)
  {
    Coords ToTarget;
    //ToTarget = coords_sub(POS_A_COMP_INDEX(INDEX_CURRENT_COMP+target_index),POS_A_CURRENT_COMP);
    ToTarget = coords_sub(POS_A_TARGET,POS_A_CURRENT);
    //ToTarget = rot_apply(ROT_A_CURRENT_COMP, ToTarget);
    ToTarget = rot_apply(ROT_A_CURRENT, ToTarget);
    coords_get(ToTarget, &focus_data.Aim.x, &focus_data.Aim.y, &focus_data.Aim.z);
  }
  else
  { focus_data.Aim.x = target_x; focus_data.Aim.y = target_y; focus_data.Aim.z = target_z; }
  
  if (!(focus_data.Aim.x || focus_data.Aim.y || focus_data.Aim.z)) {
    // Somehow set a variable to signify scattering into 4pi
    // printf("Union %s: The target is not defined. Using scattering into 4pi.\n",NAME_CURRENT_COMP);
    focus_data.Aim.z=1; // set aim to one so that the randvec output vector has length 1 instead of 0
  }

  int focusing_model_selected = 0;
  if (angular_focus_width != 0 && angular_focus_height != 0) {
    focus_data.focusing_function = &randvec_target_rect_angular_union;
    focus_data.angular_focus_width = DEG2RAD*angular_focus_width; // Convert to radians here
    focus_data.angular_focus_height = DEG2RAD*angular_focus_height;
    focusing_model_selected = 1;
  }
  if (spatial_focus_width != 0 && spatial_focus_height != 0) {
    focus_data.focusing_function = &randvec_target_rect_union;
    focus_data.spatial_focus_width = spatial_focus_width;
    focus_data.spatial_focus_height = spatial_focus_height;
    if (focusing_model_selected) {
        printf("ERROR %s: Select either angular or spatial focusing, not both! Exiting \n",component_name);
        exit(1);
    }
    focusing_model_selected = 1;
  }
  if (spatial_focus_radius != 0) {
    focus_data.focusing_function = &randvec_target_circle_union;
    focus_data.spatial_focus_radius = spatial_focus_radius;
    if (focusing_model_selected) {
        printf("ERROR %s: Select a maximum of one focusing method (spatial rectangle or cicle, or angular rectangle! Exiting \n",component_name);
        exit(1);
    }
    focusing_model_selected = 1;
  }
  if (focusing_model_selected == 0) {
    // Select 4pi focusing
    focus_data.spatial_focus_radius = 0;
    focus_data.focusing_function = &randvec_target_circle_union;
  }
  
  // Allocate the isotropic focus_data struct
  geometry->focus_data_array.num_elements = 0;
  //geometry->focus_data_array.elements = malloc(sizeof(focus_data_struct));
  add_element_to_focus_data_array(&geometry->focus_data_array,focus_data);
};


struct abs_event{
    double time1;
    double position1[3];
    double time2;
    double position2[3];
    double weight_change;
    int volume_index;
    int neutron_id;
};

// Functions for recording absorption
void initialize_absorption_file() {
  FILE *fp;
  fp = fopen("Union_absorption.dat","w");
  fprintf(fp,"r_old x, r_old y, r_old z, old t, r x, r y, r z, new t, weight change, volume index, neutron id \n");
  
  fclose(fp);
}

void write_events_to_file(int last_index, struct abs_event *events) {

  FILE *fp;
  fp = fopen("Union_absorption.dat","a");
  
  struct abs_event *this_event;
  int iterate;
  for (iterate=0; iterate<last_index; iterate++) {
  
    this_event = &events[iterate];
    fprintf(fp,"%g, %g, %g, %g, %g, %g, %g, %g, %e, %i, %i \n",
           this_event->position1[0], this_event->position1[1], this_event->position1[2], this_event->time1,
           this_event->position2[0], this_event->position2[1], this_event->position2[2], this_event->time2,
           this_event->weight_change,
           this_event->volume_index,
           this_event->neutron_id);
  }

  fclose(fp);
}

void record_abs_to_file(double *r, double t1, double *r_old, double t2, double weight_change, int volume, int neutron_id, int *data_index, struct abs_event *events) {
  
  
  
  struct abs_event *this_event;
  
  this_event = &events[(*data_index)++];
  
  //printf("Recording something! %i\n", *data_index);
  
  this_event->position1[0] = r[0];
  this_event->position1[1] = r[1];
  this_event->position1[2] = r[2];
  this_event->time1 = t1;
  this_event->position2[0] = r_old[0];
  this_event->position2[1] = r_old[1];
  this_event->position2[2] = r_old[2];
  this_event->time2 = t2;
  this_event->weight_change = weight_change;
  this_event->volume_index = volume;
  this_event->neutron_id = neutron_id;
  
  
  if (*data_index == 999) {
    
      write_events_to_file(*data_index, events);
      *data_index = 0;

  }

};




/*******************************************************************************
*
*  McStas, neutron ray-tracing package
*  Copyright(C) 2007 Risoe National Laboratory.
*
* %I
* Written by: Mads Bertelsen
* Date: 20.08.15
* Version: $Revision: 0.1 $
* Origin: University of Copenhagen
*
* A sample component to separate geometry and phsysics
*
* %D
* Alpha version, no input system yet
* Hardcode input to geometry engine
* Allows complicated geometry by combination of simple shapes
*
* Algorithm:
* Described elsewhere
*
* %P
* INPUT PARAMETERS:
* radius:  [m] Outer radius of sample in (x,z) plane
*
* OUTPUT PARAMETERS:
* V_rho:  [AA^-3] Atomic density
*
* %L
* The test/example instrument <a href="../examples/Test_Phonon.instr">Test_Phonon.instr</a>.
*
* %E
******************************************************************************/


// This file sets up global lists needed for Union components to communicate with each other
// These all have dynamically allocated memory somewhere in the structure, which is deallocated
//  by the last Union_master.



// Initialize global positions / rotations to transform lists
  // These are lists of pointers to positons / rotations, that will be updated from global frame
  //  to the frame of the master component that uses them in that masters initialize section.
  struct global_positions_to_transform_list_struct global_positions_to_transform_list = {0,NULL};
  struct global_rotations_to_transform_list_struct global_rotations_to_transform_list = {0,NULL};

// Initialize global_process_list
  // Used to facilitate communication between processes and the other types of Union components
  struct pointer_to_global_process_list global_process_list = {0,NULL};

// Initialize global_material_list
  // Used to facilitate communication between materials and the other types of Union components
  struct pointer_to_global_material_list global_material_list = {0,NULL};

// Initialize global_geometry_list
  // Used to facilitate communication between geometries and the other types of Union components
  struct pointer_to_global_geometry_list global_geometry_list = {0,NULL};

// Initialize global_logger_lists
  // Used to facilitate communication between loggers and the other types of Union components
  struct pointer_to_global_logger_list global_all_volume_logger_list = {0,NULL};
  struct pointer_to_global_logger_list global_specific_volumes_logger_list = {0,NULL};

// Initialize global_abs_logger_lists
  struct pointer_to_global_abs_logger_list global_all_volume_abs_logger_list = {0,NULL};
  struct pointer_to_global_abs_logger_list global_specific_volumes_abs_logger_list = {0,NULL};

// Initialize global_tagging_conditional_list
  // Used to facilitate communication between conditionals and the other types of Union components
  struct global_tagging_conditional_list_struct global_tagging_conditional_list = {0,0,NULL};

// Initialize global_master_list
  // Used to facilitate communication between Master components (mainly for deallocation)
  struct pointer_to_global_master_list global_master_list = {0,NULL};

// Initialize global_mantid_min_pixel_id
  // Used for ensuring pixel id's on Mantid monitors do not overlap
  int global_mantid_min_pixel_id = 0;


#endif


void mcdisplay_cylinder_function(struct lines_to_draw *lines_to_draw_output,int index, struct geometry_struct **Geometries,int number_of_volumes) {
    // Function to call in mcdisplay section of the sample component for this volume
    // One can assume that Geometries[index] refers to a geometry as described in this file
    // The 4 lines describin the cylinders side are aligned to the local frame of the cylinder,
    //   it would be nicer to have them alligned with the global frame so that they show up nicely in
    //   pgplotters on mcdisplay.
    // One could get the current global rotation and use this to counteract this effect.
    
    double height = Geometries[index]->geometry_parameters.p_cylinder_storage->height;
    double radius = Geometries[index]->geometry_parameters.p_cylinder_storage->cyl_radius;
    Coords direction = Geometries[index]->geometry_parameters.p_cylinder_storage->direction_vector;
    Coords center = Geometries[index]->center;
    
    Coords bottom_point = coords_add(center,coords_scalar_mult(direction,0.5*height));
    Coords top_point = coords_add(center,coords_scalar_mult(direction,-0.5*height));
    
    struct lines_to_draw lines_to_draw_temp;
    lines_to_draw_temp.number_of_lines = 0;
    
    lines_to_draw_temp = draw_circle_with_highest_priority(top_point,direction,radius,index,Geometries,number_of_volumes,2);
    merge_lines_to_draw(lines_to_draw_output,&lines_to_draw_temp);

    lines_to_draw_temp = draw_circle_with_highest_priority(bottom_point,direction,radius,index,Geometries,number_of_volumes,2);
    merge_lines_to_draw(lines_to_draw_output,&lines_to_draw_temp);
    
    Coords point1,point2;
    int iterate,number_of_points=4;
    
    for (iterate=0;iterate<number_of_points;iterate++) {
        point1 = point_on_circle(top_point,direction,radius,iterate,number_of_points);
        point2 = point_on_circle(bottom_point,direction,radius,iterate,number_of_points);
        lines_to_draw_temp = draw_line_with_highest_priority(point1,point2,index,Geometries,number_of_volumes,2);
        merge_lines_to_draw(lines_to_draw_output,&lines_to_draw_temp);
    }
};

void initialize_cylinder_geometry_from_main_component(struct geometry_struct *cylinder) {
    // Function to be called in initialize of the main component
    // This is done as the rotation matrix needs to be relative to the main component instead of global
    // Everything done in initialize in this component file has the rotation matrix relative to global
    
    Coords simple_vector;
    Coords cyl_vector;
    
    // Start with vector that points along the cylinder in the local frame
    simple_vector = coords_set(0,1,0);

    // Rotate the direction vector of the cylinder to the master component frame of reference
    cyl_vector = rot_apply(cylinder->rotation_matrix,simple_vector);
    NORM(cyl_vector.x,cyl_vector.y,cyl_vector.z);
    cylinder->geometry_parameters.p_cylinder_storage->direction_vector.x = cyl_vector.x;
    cylinder->geometry_parameters.p_cylinder_storage->direction_vector.y = cyl_vector.y;
    cylinder->geometry_parameters.p_cylinder_storage->direction_vector.z = cyl_vector.z;
    // if (verbal == 1) printf("Cords vector1 = (%f,%f,%f)\n",cyl_vector.x,cyl_vector.y,
}

struct pointer_to_1d_coords_list cylinder_shell_points(struct geometry_struct *geometry,int max_number_of_points) {
  // Function that returns a number (less than max) of points on the geometry surface
  // If used, remember to free the space allocated.
  int points_per_circle = floor(max_number_of_points/2.0);
  
  struct pointer_to_1d_coords_list cylinder_shell_array;
  cylinder_shell_array.elements = malloc(2*points_per_circle*sizeof(Coords));
  cylinder_shell_array.num_elements = 2*points_per_circle;
  
  Coords cyl_direction = geometry->geometry_parameters.p_cylinder_storage->direction_vector;
  Coords center = geometry->center;
  double radius = geometry->geometry_parameters.p_cylinder_storage->cyl_radius;
  double height = geometry->geometry_parameters.p_cylinder_storage->height;
  
  Coords cyl_top_point = coords_add(center,coords_scalar_mult(cyl_direction,0.5*height));
  Coords cyl_bottom_point = coords_add(center,coords_scalar_mult(cyl_direction,-0.5*height));
  
  points_on_circle(cylinder_shell_array.elements,cyl_top_point,cyl_direction,radius,points_per_circle);
  // Need to verify this pointer arithimatic works as intended
  points_on_circle(cylinder_shell_array.elements+points_per_circle,cyl_bottom_point,cyl_direction,radius,points_per_circle);
  
  return cylinder_shell_array;
}

#ifndef ANY_GEOMETRY_DETECTOR_DECLARE
    #define ANY_GEOMETRY_DETECTOR_DECLARE dummy
    //struct pointer_to_global_geometry_list global_geometry_list = {0,NULL};
#endif

/* Shared user declarations for all components types 'Union_master'. */

#ifndef Union
#define Union $Revision: 0.9 $

/*******************************************************************************
*
*  McStas, neutron ray-tracing package
*  Copyright(C) 2007 Risoe National Laboratory.
*
* %I
* Written by: Mads Bertelsen
* Date: 20.08.15
* Version: $Revision: 0.1 $
* Origin: University of Copenhagen
*
* Functions and structure definitons for Union components.
*
******************************************************************************/

// -------------    Definition of data structures   ---------------------------------------------
// GPU
enum shape {
  surroundings,
  box,
  sphere,
  cylinder,
  cone,
  mesh
};

enum process {
  Incoherent,
  Powder,
  Single_crystal,
  AF_HB_1D,
  PhononSimple,
  Texture,
  IncoherentPhonon,
  NCrystal,
  Template
};

struct intersection_time_table_struct {
int num_volumes;
int *calculated;
int *n_elements;
double **intersection_times;
};

struct line_segment{
//struct position point1;
//struct position point2;
Coords point1;
Coords point2;
int number_of_dashes;
};

struct pointer_to_1d_int_list {
int num_elements;
int *elements;
#pragma acc shape(elements[0:num_elements]) init_needed(num_elements)
};

struct pointer_to_1d_double_list {
int num_elements;
double *elements;
#pragma acc shape(elements[0:num_elements]) init_needed(num_elements)
};

struct pointer_to_1d_coords_list {
int num_elements;
Coords *elements;
#pragma acc shape(elements[0:num_elements]) init_needed(num_elements)
};

struct lines_to_draw{
int number_of_lines;
struct line_segment *lines;
#pragma acc shape(lines[0:number_of_lines]) init_needed(number_of_lines)
};

// 2D lists not needed anyway
// struct pointer_to_2d_int_list {
// int n_lists;
// struct pointer_to_1d_int_list *lists;
// }

// Todo: see if the union geometry_parameter_union and other geometry structs can be here
union geometry_parameter_union{
    struct sphere_storage   *p_sphere_storage;
    struct cylinder_storage *p_cylinder_storage;
    struct box_storage      *p_box_storage;
    struct cone_storage     *p_cone_storage;
    struct mesh_storage     *p_mesh_storage;
    // add as many pointers to structs as wanted, without increasing memory footprint.
};


struct rotation_struct{
double x;
double y;
double z;
};

struct focus_data_struct {
Coords Aim;
double angular_focus_width;
double angular_focus_height;
double spatial_focus_width;
double spatial_focus_height;
double spatial_focus_radius;
Rotation absolute_rotation;
// focusing_function creates a vector per selected criteria of focus_data_struct / selected focus function and returns solid angle
void (*focusing_function)(Coords*, double*, struct focus_data_struct*);
//                        v_out  , solid_a,
};

struct focus_data_array_struct
{
struct focus_data_struct *elements;
int num_elements;
#pragma acc shape(elements[0:num_elements]) init_needed(num_elements)
};

struct Detector_3D_struct {
  char title_string[256];
  char string_axis_1[256];
  char string_axis_2[256];
  char string_axis_3[256];
  char Filename[256];
  double D1min;
  double D1max;
  double D2min;
  double D2max;
  double D3min;
  double D3max;
  double bins_1; // McStas uses doubles for bin numbers for some reason
  double bins_2;
  double bins_3;
  double ***Array_N; // McStas uses doubles for number of rays in each bin for some reason
  double ***Array_p;
  double ***Array_p2;
};

struct Detector_2D_struct {
  char title_string[256];
  char string_axis_1[256];
  char string_axis_2[256];
  char Filename[256];
  double D1min;
  double D1max;
  double D2min;
  double D2max;
  double bins_1; // McStas uses doubles for bin numbers for some reason
  double bins_2;
  double **Array_N; // McStas uses doubles for number of rays in each bin for some reason
  double **Array_p;
  double **Array_p2;
};

struct Detector_1D_struct {
  char title_string[256];
  char string_axis[256];
  char string_axis_short[64];
  char string_axis_value[256];
  char Filename[256];
  double min;
  double max;
  double bins; // McStas uses doubles for bin numbers for some reason
  double *Array_N; // McStas uses doubles for number of rays in each bin for some reason
  double *Array_p;
  double *Array_p2;
};


union logger_data_union{
  struct a_2DQ_storage_struct *p_2DQ_storage;
  struct a_2DS_storage_struct *p_2DS_storage;
  struct a_3DS_storage_struct *p_3DS_storage;
  struct a_1D_storage_struct *p_1D_storage;
  struct a_2DS_t_storage_struct *p_2DS_t_storage;
  struct a_2D_kf_storage_struct *p_2D_kf_storage;
  struct a_2D_kf_t_storage_struct *p_2D_kf_t_storage;
  // Additional logger storage structs to be addedd
};

struct logger_with_data_struct {
  int used_elements;
  int allocated_elements;
  struct logger_struct **logger_pointers;
};

// logger_pointer_struct
// contains pointers to the different logger functions and it's data union
struct logger_pointer_set_struct {
  // The logger has two record functions, an active and an inactive. Normally the active one will be to permanent storage,
  //  but if a conditional has been defined, it can switch the two, making the active one recording to temporary, which
  //  can then be filtered based on the future path of the ray
  
  // function input Coords position, k[3], k_old[3], p, p_old, NV, NPV, N, logger_data_union, logger_with_data_struct
  void (*active_record_function)(Coords*, double*, double*, double, double, double, int, int, int, struct logger_struct*, struct logger_with_data_struct*);
  void (*inactive_record_function)(Coords*, double*, double*, double, double, double, int, int, int, struct logger_struct*, struct logger_with_data_struct*);
  
  // A clear temporary data function (for new ray)
  void (*clear_temp)(union logger_data_union*);
  
  // Write temporary to permanent is used when the record to temp function is active, and the condition is met.
  void (*temp_to_perm)(union logger_data_union*);
  
  // Write temporary final_p to permanent is used when the record to temp function is active, and the condition is met
  //  and the final weight is given to be used for all stored events in the logger.
  void (*temp_to_perm_final_p)(union logger_data_union*, double);
  
  // Select which temp_to_perm function to use
  int select_t_to_p; // 1: temp_to_perm, 2: temp_to_perm_final_p
  
};

union abs_logger_data_union{
  struct a_2D_abs_storage_struct *p_2D_abs_storage;
  struct a_1D_abs_storage_struct *p_1D_abs_storage;
  struct a_1D_time_abs_storage_struct *p_1D_time_abs_storage;
  struct a_1D_time_to_lambda_abs_storage_struct *p_1D_time_to_lambda_abs_storage;
  struct a_event_abs_storage_struct *p_event_abs_storage;
  struct a_1D_event_abs_storage_struct *p_1D_event_abs_storage;
  // Additional logger storage structs to be addedd
};

struct abs_logger_with_data_struct {
  int used_elements;
  int allocated_elements;
  struct abs_logger_struct **abs_logger_pointers;
};

struct abs_logger_pointer_set_struct {
  // The logger has two record functions, an active and an inactive. Normally the active one will be to permanent storage,
  //  but if a conditional has been defined, it can switch the two, making the active one recording to temporary, which
  //  can then be filtered based on the future path of the ray
  
  // function input Coords position, k[3], p, NV, N, logger_data_union, logger_with_data_struct
  void (*active_record_function)(Coords*, double*, double,  double, int, int, struct abs_logger_struct*, struct abs_logger_with_data_struct*);
  void (*inactive_record_function)(Coords*, double*, double,  double, int, int, struct abs_logger_struct*, struct abs_logger_with_data_struct*);
  
  // A clear temporary data function (for new ray)
  void (*clear_temp)(union abs_logger_data_union*);
  
  // Write temporary to permanent is used when the record to temp function is active, and the condition is met.
  void (*temp_to_perm)(union abs_logger_data_union*);
  
  // Write temporary final_p to permanent is used when the record to temp function is active, and the condition is met
  //  and the final weight is given to be used for all stored events in the logger.
  void (*temp_to_perm_final_p)(union abs_logger_data_union*, double);
  
  // Select which temp_to_perm function to use
  //int select_t_to_p; // 1: temp_to_perm, 2: temp_to_perm_final_p
  
};


struct conditional_standard_struct{
  // Data to be transfered to the conditional function
  double Emax;
  double Emin;
  int E_limit;
  
  double Tmin;
  double Tmax;
  int T_limit;
  
  int volume_index;
  
  double Total_scat_max;
  double Total_scat_min;
  int Total_scat_limit;
  
  double exit_volume_index;
  
  // Test
  Coords test_position;
  Rotation test_rotation;
  Rotation test_t_rotation;
};

struct conditional_PSD_struct{
  double PSD_half_xwidth;
  double PSD_half_yheight;
  
  double Tmin;
  double Tmax;
  int T_limit;
  
  // Position of the PSD
  Coords PSD_position;
  Rotation PSD_rotation;
  Rotation PSD_t_rotation;
};

union conditional_data_union {
  struct conditional_standard_struct *p_standard;
  struct conditional_PSD_struct *p_PSD;
  // Add more as conditional components are made
};

// General input for conditional functions: Position, Velocity/Wavevector, weight, time, total_scat, scattered_flag, scattered_flag_VP,
// Optional extras: data_union? tree base(s)? tree base for current ray?
typedef int (*conditional_function_pointer)(union conditional_data_union*,Coords*, Coords*, double*, double*, int*, int*, int*, int**);
//typedef int (**conditional_function_pointer_array)(union *conditional_data_union,Coords*, Coords*, double*, double*, int*, int*, int**);

struct conditional_list_struct{
  int num_elements;
  
  union conditional_data_union **p_data_unions;
  conditional_function_pointer *conditional_functions;
  //int (**conditional_functions)(Coords*, Coords*, double*, double*, int*, int*, int**);
};

struct logger_struct {
  char name[256];
  // Contains ponters to all the functions assosiated with this logger
  struct logger_pointer_set_struct function_pointers;
  // Contains hard copy of logger_data_union since the size is the same as a pointer.
  union logger_data_union data_union;
  
  int logger_extend_index; // Contain index conditional_extend_array defined in master that can be acsessed from extend section.
  
  struct conditional_list_struct conditional_list;
};


// To be stored in volume, a list of pointers to the relevant loggers corresponding to each process
struct logger_for_each_process_list {
  int num_elements;
  struct logger_struct **p_logger_process;
};

// List of logger_for_each_process_list
struct loggers_struct {
  int num_elements;
  struct logger_for_each_process_list *p_logger_volume;
  #pragma acc shape(p_logger_volume[0:num_elements]) init_needed(num_elements)
};


struct abs_logger_struct {
  char name[256];
  // Contains ponters to all the functions assosiated with this logger
  struct abs_logger_pointer_set_struct function_pointers;
  // Contains hard copy of logger_data_union since the size is the same as a pointer.
  union abs_logger_data_union data_union;
  
  // Position and rotation of the abs_logger
  Coords position;
  Rotation rotation;
  Rotation t_rotation;
  
  int abs_logger_extend_index; // Contain index conditional_extend_array defined in master that can be acsessed from extend section.
  
  struct conditional_list_struct conditional_list;
};

// To be stored in volume, a list of pointers to the relevant abs loggers corresponding to each process
/*
struct abs_logger_for_each_process_list {
  int num_elements;
  struct abs_logger_struct **p_abs_logger_process;
};
*/

// List of abs logger_for_each_process_list
struct abs_loggers_struct {
  int num_elements;
  //struct abs_logger_for_each_process_list *p_abs_logger_volume;
  struct abs_logger_struct **p_abs_logger;
};



struct geometry_struct
{
char shape[64];     // name of shape used (sphere, cylinder, box, off, ...)
enum shape eShape;  // enum with shape for flexible functions GPU
double priority_value;    // priority of the geometry
Coords center;      // Center position of volume, reported by components in global frame, updated to main frame in initialize
// Rotation of this volume
Rotation rotation_matrix; // rotation matrix of volume, reported by component in global frame, updated to main frame in initialize
Rotation transpose_rotation_matrix; // As above
// Array of prrotation matrixes for processes assigned to this volume (indexed by non_isotropic_rot_index in the processes)
Rotation *process_rot_matrix_array;             // matrix that transforms from main coordinate system to local process in this specific volume
Rotation *transpose_process_rot_matrix_array;   // matrix that transforms from local process in this specific volume to main coordinate system
int process_rot_allocated;  // Keeps track of allocation status of rot_matrix_array

struct rotation_struct rotation; // Not used, is the x y and z rotation angles.
int visualization_on; // If visualization_on is true, the volume will be drawn in mcdisplay, otherwise not
int is_exit_volume; // If is exit volume = 1, the ray will exit the component when it enters this volume.
int is_mask_volume; // 1 if volume itself is a mask (masking the ones in it's mask list), otherwise 0
int mask_index;
int is_masked_volume; // 1 if this volume is being masked by another volume, the volumes that mask it is in masked_by_list
int mask_mode; // ALL/ANY 1/2. In ALL mode, only parts covered by all masks is simulated, in ANY mode, area covered by just one mask is simulated
double geometry_p_interact; // fraction of rays that interact with this volume for each scattering (between 0 and 1, 0 for disable)
union geometry_parameter_union geometry_parameters; // relevant parameters for this shape
union geometry_parameter_union (*copy_geometry_parameters)(union geometry_parameter_union*);
struct focus_data_struct focus_data; // Used for focusing from this geometry

// New focus data implementation to remove the focusing bug for non-isotripic processes
struct focus_data_array_struct focus_data_array;
struct pointer_to_1d_int_list focus_array_indices; // Add 1D integer array with indecies for correct focus_data for each process


// intersect_function takes position/velocity of ray and parameters, returns time list
int (*intersect_function)(double*,int*,double*,double*,struct geometry_struct*);
//                        t_array,n_ar,r      ,v

// within_function that checks if the ray origin is within this volume
int (*within_function)(Coords,struct geometry_struct*);
//                     r,      parameters

// mcdisplay function, draws the geometry
//void (*mcdisplay_function)(struct lines_to_draw*,int,struct Volume_struct**,int);
void (*mcdisplay_function)(struct lines_to_draw*,int,struct geometry_struct**,int);
//                                lines          index             Geometries   N

void (*initialize_from_main_function)(struct geometry_struct*);

struct pointer_to_1d_coords_list (*shell_points)(struct geometry_struct*, int maximum_number_of_points);

// List of other volumes to be check when ray starts within this volume.
struct pointer_to_1d_int_list intersect_check_list;
// List of other volumes the ray may enter, if the ray intersects the volume itself.
struct pointer_to_1d_int_list destinations_list;
// The destinations list stored as a logic list which makes some tasks quicker. OBSOLETE
//struct pointer_to_1d_int_list destinations_logic_list;
// Reduced list of other volumes the ray may enter, if the ray intersects the volume itself.
struct pointer_to_1d_int_list reduced_destinations_list;
// List of other volumes that are within this volume
struct pointer_to_1d_int_list children;
// List of other volumes that are within this volume, but does not have any parents that are children of this volume
struct pointer_to_1d_int_list direct_children;
// List of next possible volumes (only used in tagging)
struct pointer_to_1d_int_list next_volume_list;
// List of volumes masked by this volume (usually empty)
struct pointer_to_1d_int_list mask_list;
// List of volumes masking this volume
struct pointer_to_1d_int_list masked_by_list;
// List of masks masking this volume (global mask indices)
struct pointer_to_1d_int_list masked_by_mask_index_list;
// Additional intersect lists dependent on mask status
//struct indexed_mask_lists_struct mask_intersect_lists;
// Simpler way of storing the mask_intersect_lists
struct pointer_to_1d_int_list mask_intersect_list;
};

struct physics_struct
{
char name[256]; // User defined material name
int interact_control;
int is_vacuum;
double my_a;
int number_of_processes;
// pointer to array of pointers to physics_sub structures that each describe a scattering process
struct scattering_process_struct *p_scattering_array;
};

union data_transfer_union{
    // List of pointers to storage structs for all supported physical processes
    struct Incoherent_physics_storage_struct  *pointer_to_a_Incoherent_physics_storage_struct;
    struct Powder_physics_storage_struct *pointer_to_a_Powder_physics_storage_struct;
    struct Single_crystal_physics_storage_struct *pointer_to_a_Single_crystal_physics_storage_struct;
    struct AF_HB_1D_physics_storage_struct *pointer_to_a_AF_HB_1D_physics_storage_struct;
    struct IncoherentPhonon_physics_storage_struct *pointer_to_a_IncoherentPhonon_physics_storage_struct;
    struct PhononSimpleNumeric_physics_storage_struct *pointer_to_a_PhononSimpleNumeric_storage_struct;
    struct PhononSimple_physics_storage_struct *pointer_to_a_PhononSimple_storage_struct;
    struct MagnonSimple_physics_storage_struct *pointer_to_a_MagnonSimple_storage_struct;
    struct Sans_spheres_physics_storage_struct *pointer_to_a_Sans_spheres_physics_storage_struct;
    struct Texture_physics_storage_struct *pointer_to_a_Texture_physics_storage_struct;
    struct NCrystal_physics_storage_struct *pointer_to_a_NCrystal_physics_storage_struct;
    struct Non_physics_storage_struct *pointer_to_a_Non_physics_storage_struct;
    struct Template_physics_storage_struct *pointer_to_a_Template_physics_storage_struct;
    // possible to add as many structs as wanted, without increasing memory footprint.
};



struct scattering_process_struct
{
char name[256];                // User defined process name
enum process eProcess;         // enum value corresponding to this process GPU
double process_p_interact;     // double between 0 and 1 that describes the fraction of events forced to undergo this process. -1 for disable
int non_isotropic_rot_index;   // -1 if process is isotrpic, otherwise is the index of the process rotation matrix in the volume
Rotation rotation_matrix;      // rotation matrix of process, reported by component in local frame, transformed and moved to volume struct in main

union data_transfer_union data_transfer; // The way to reach the storage space allocated for this process (see examples in process.comp files)

// probability_for_scattering_functions calculates this probability given k_i and parameters
int (*probability_for_scattering_function)(double*,double*,union data_transfer_union,struct focus_data_struct*, _class_particle *_particle);
//                                         prop,   k_i,   ,parameters               , focus data / function

// A scattering_function takes k_i and parameters, returns k_f
int (*scattering_function)(double*,double*,double*,union data_transfer_union,struct focus_data_struct*, _class_particle *_particle);
//                         k_f,    k_i,    weight, parameters               , focus data / function
};

struct Volume_struct
{
char name[256]; // User defined volume name
struct geometry_struct geometry;        // Geometry properties (including intersect functions, generated lists)
struct physics_struct *p_physics;       // Physical properties (list of scattering processes, absorption)
struct loggers_struct loggers;          // Loggers assosiated with this volume
struct abs_loggers_struct abs_loggers;  // Loggers assosiated with this volume
};

// example of calling a scattering process
// volume_pointer_list[3]->physics.scattering_process[5].probability_for_scattering_function(input,volume_pointer_list[3]->physics.scattering_process[5])

struct starting_lists_struct
{
struct pointer_to_1d_int_list allowed_starting_volume_logic_list;
struct pointer_to_1d_int_list reduced_start_list;
struct pointer_to_1d_int_list start_logic_list;
struct pointer_to_1d_int_list starting_destinations_list;
};

struct global_positions_to_transform_list_struct {
int num_elements;
Coords **positions;
};

struct global_rotations_to_transform_list_struct {
int num_elements;
Rotation **rotations;
};

struct global_process_element_struct
{
char name[128]; // Name of the process
int component_index;
struct scattering_process_struct *p_scattering_process;
};

struct pointer_to_global_process_list {
int num_elements;
struct global_process_element_struct *elements;
};

struct global_material_element_struct
{
char name[128];
int component_index;
struct physics_struct *physics;
};

struct pointer_to_global_material_list {
int num_elements;
struct global_material_element_struct *elements;
};

struct global_geometry_element_struct
{
char name[128];
int component_index;
int activation_counter;
int stored_copies;
int active;
struct Volume_struct *Volume;
};

struct pointer_to_global_geometry_list {
int num_elements;
struct global_geometry_element_struct *elements;
};

struct global_logger_element_struct {
char name[128];
int component_index;
struct logger_struct *logger;
};

struct pointer_to_global_logger_list {
int num_elements;
struct global_logger_element_struct *elements;
};

struct global_abs_logger_element_struct {
char name[128];
int component_index;
struct abs_logger_struct *abs_logger;
};

struct pointer_to_global_abs_logger_list {
int num_elements;
struct global_abs_logger_element_struct *elements;
};

struct global_tagging_conditional_element_struct {
struct conditional_list_struct conditional_list;
int extend_index;
char name[1024];
int use_status;
};

struct global_tagging_conditional_list_struct {
int num_elements;
int current_index;
struct global_tagging_conditional_element_struct *elements;
};


struct global_master_element_struct {
char name[128];
int component_index;
int stored_number_of_scattering_events; // TEST
struct conditional_list_struct *tagging_conditional_list_pointer;
};

struct pointer_to_global_master_list {
int num_elements;
struct global_master_element_struct *elements;
};


// -------------    Physics functions   ---------------------------------------------------------

//#include "Test_physics.c"
//#include "Incoherent_test.c"

// -------------    General functions   ---------------------------------------------------------
double distance_between(Coords position1,Coords position2) {
    return sqrt((position1.x-position2.x)*(position1.x-position2.x) +
                (position1.y-position2.y)*(position1.y-position2.y) +
                (position1.z-position2.z)*(position1.z-position2.z));
};



double length_of_3vector(double *r) {
        return sqrt(r[0]*r[0]+r[1]*r[1]+r[2]*r[2]);
    };

double length_of_position_vector(Coords point) {
        return sqrt(point.x*point.x+point.y*point.y+point.z*point.z);
    };

Coords make_position(double *r) {
    Coords temp;
    
    temp.x = r[0];temp.y = r[1];temp.z = r[2];
    return temp;
};

Coords coords_scalar_mult(Coords input,double scalar) {
    return coords_set(scalar*input.x,scalar*input.y,scalar*input.z);
};

double union_coords_dot(Coords vector1,Coords vector2) {
    return vector1.x*vector2.x + vector1.y*vector2.y + vector1.z*vector2.z;
}


int sum_int_list(struct pointer_to_1d_int_list list) {
    int iterate,sum = 0;
    for (iterate = 0;iterate < list.num_elements;iterate++) sum += list.elements[iterate];
    return sum;
    };
    
int on_int_list(struct pointer_to_1d_int_list list,int target) {
    int iterate,output=0;
    for (iterate = 0; iterate<list.num_elements ;iterate++) {
        if (list.elements[iterate] == target) output = 1;
    }
    return output;
    };

int find_on_int_list(struct pointer_to_1d_int_list list,int target) {
    int iterate;
    for (iterate = 0;iterate < list.num_elements;iterate++) {
        if (list.elements[iterate] == target) return iterate;
    }
    return -1;
    };
    
void on_both_int_lists(struct pointer_to_1d_int_list *list1, struct pointer_to_1d_int_list *list2, struct pointer_to_1d_int_list *common) {
    // Assume common.elements is allocated to a number of int's large enough to hold the resulting list
    int iterate,used_elements=0;
    for (iterate=0;iterate<list1->num_elements;iterate++) {
        if (on_int_list(*list2,list1->elements[iterate])) common->elements[used_elements++] = list1->elements[iterate];
    }
    common->num_elements = used_elements;
    };

void remove_element_in_list_by_index(struct pointer_to_1d_int_list *list,int index) {
    if (index >= list->num_elements) {
      printf("ERROR(remove_element_in_list_by_index): trying to remove an index that wasn't allocated to begin with");
      exit(1);
    }
    else {
        int iterate;
        int *temp;
        for (iterate = index;iterate < list->num_elements -1;iterate++) {
            list->elements[iterate] = list->elements[iterate+1];
        }
        list->num_elements--;
        //if (list->num_elements==0) printf("Making empty list!\n");
        temp = malloc(list->num_elements * sizeof(int));
        for (iterate = 0;iterate < list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
        free(list->elements);
        list->elements = malloc(list->num_elements * sizeof(int));
        for (iterate = 0;iterate < list->num_elements;iterate++) list->elements[iterate] = temp[iterate];
        free(temp);

    }
    };
    
void remove_element_in_list_by_value(struct pointer_to_1d_int_list *list,int value) {
    int iterate;
    for (iterate = 0;iterate < list->num_elements;iterate++) {
        if (list->elements[iterate] == value) remove_element_in_list_by_index(list,iterate);
    }
    };
    
void merge_lists(struct pointer_to_1d_int_list *result,struct pointer_to_1d_int_list *list1,struct pointer_to_1d_int_list *list2) {
    if (result->num_elements > 0) free(result->elements);
    result->num_elements = list1->num_elements + list2->num_elements;
    if (result->num_elements != 0) {
      result->elements = malloc(result->num_elements*sizeof(int));
      int iterate;
      for (iterate = 0;iterate < list1->num_elements;iterate++)
          result->elements[iterate] = list1->elements[iterate];
      for (iterate = 0;iterate < list2->num_elements;iterate++)
          result->elements[list1->num_elements+iterate] = list2->elements[iterate];
    }
    };

void add_element_to_double_list(struct pointer_to_1d_double_list *list,double value) {
    if (list->num_elements == 0) {
      list->num_elements++;
      list-> elements = malloc(list->num_elements*sizeof(double));
      list-> elements[0] = value;
    } else {
      double temp[list->num_elements];
      int iterate;
      for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
      free(list->elements);
      list->num_elements++;
      list-> elements = malloc(list->num_elements*sizeof(double));
      for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
      list->elements[list->num_elements-1] = value;
    }
    };

void add_element_to_int_list(struct pointer_to_1d_int_list *list,int value) {
    if (list->num_elements == 0) {
      list->num_elements++;
      list-> elements = malloc(list->num_elements*sizeof(int));
      list-> elements[0] = value;
    } else {
      int temp[list->num_elements];
      int iterate;
      for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
      free(list->elements);
      list->num_elements++;
      list-> elements = malloc(list->num_elements*sizeof(int));
      for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
      list->elements[list->num_elements-1] = value;
    }
    };

// Need to check if absolute_rotation is preserved correctly.
void add_element_to_focus_data_array(struct focus_data_array_struct *focus_data_array,struct focus_data_struct focus_data) {
    if (focus_data_array->num_elements == 0) {
      focus_data_array->num_elements++;
      focus_data_array-> elements = malloc(focus_data_array->num_elements*sizeof(struct focus_data_struct));
      focus_data_array-> elements[0] = focus_data;
    } else {
      struct focus_data_struct temp[focus_data_array->num_elements];
      int iterate;
      for (iterate=0;iterate<focus_data_array->num_elements;iterate++) temp[iterate] = focus_data_array->elements[iterate];
      free(focus_data_array->elements);
      focus_data_array->num_elements++;
      focus_data_array-> elements = malloc(focus_data_array->num_elements*sizeof(struct focus_data_struct));
      for (iterate=0;iterate<focus_data_array->num_elements-1;iterate++) focus_data_array->elements[iterate] = temp[iterate];
      focus_data_array->elements[focus_data_array->num_elements-1] = focus_data;
    }
    };


void add_to_logger_with_data(struct logger_with_data_struct *logger_with_data, struct logger_struct *logger) {
    // May reorder the order of the if conditions to avoid checking the == 0 for every single ray
    if (logger_with_data->allocated_elements == 0) {
        logger_with_data->allocated_elements = 5;
        logger_with_data->logger_pointers = malloc(logger_with_data->allocated_elements*sizeof(struct logger_struct*));
        logger_with_data->used_elements = 1;
        logger_with_data->logger_pointers[0] = logger;
    } else if (logger_with_data->used_elements > logger_with_data->allocated_elements-1) {
        struct logger_with_data_struct temp_logger_with_data;
        temp_logger_with_data.logger_pointers = malloc((logger_with_data->used_elements)*sizeof(struct logger_struct*));
        int iterate;
        for (iterate=0;iterate<logger_with_data->used_elements;iterate++) {
            temp_logger_with_data.logger_pointers[iterate]  = logger_with_data->logger_pointers[iterate];
        }
        free(logger_with_data->logger_pointers);
        logger_with_data->allocated_elements = logger_with_data->allocated_elements+5;
        logger_with_data->logger_pointers = malloc(logger_with_data->allocated_elements*sizeof(struct logger_struct*));
        for (iterate=0;iterate<logger_with_data->used_elements;iterate++) {
            logger_with_data->logger_pointers[iterate]  = temp_logger_with_data.logger_pointers[iterate];
        }

        logger_with_data->logger_pointers[logger_with_data->used_elements++] = logger;
        
        
    } else {
        logger_with_data->logger_pointers[logger_with_data->used_elements++] = logger;
    }
    
};

void add_to_abs_logger_with_data(struct abs_logger_with_data_struct *abs_logger_with_data, struct abs_logger_struct *abs_logger) {
    // May reorder the order of the if conditions to avoid checking the == 0 for every single ray
    if (abs_logger_with_data->allocated_elements == 0) {
        abs_logger_with_data->allocated_elements = 5;
        abs_logger_with_data->abs_logger_pointers = malloc(abs_logger_with_data->allocated_elements*sizeof(struct abs_logger_struct*));
        abs_logger_with_data->used_elements = 1;
        abs_logger_with_data->abs_logger_pointers[0] = abs_logger;
    } else if (abs_logger_with_data->used_elements > abs_logger_with_data->allocated_elements-1) {
        struct abs_logger_with_data_struct temp_abs_logger_with_data;
        temp_abs_logger_with_data.abs_logger_pointers = malloc((abs_logger_with_data->used_elements)*sizeof(struct abs_logger_struct*));
        int iterate;
        for (iterate=0;iterate<abs_logger_with_data->used_elements;iterate++) {
            temp_abs_logger_with_data.abs_logger_pointers[iterate]  = abs_logger_with_data->abs_logger_pointers[iterate];
        }
        free(abs_logger_with_data->abs_logger_pointers);
        abs_logger_with_data->allocated_elements = abs_logger_with_data->allocated_elements+5;
        abs_logger_with_data->abs_logger_pointers = malloc(abs_logger_with_data->allocated_elements*sizeof(struct abs_logger_struct*));
        for (iterate=0;iterate<abs_logger_with_data->used_elements;iterate++) {
            abs_logger_with_data->abs_logger_pointers[iterate] = temp_abs_logger_with_data.abs_logger_pointers[iterate];
        }

        abs_logger_with_data->abs_logger_pointers[abs_logger_with_data->used_elements++] = abs_logger;
        
        
    } else {
        abs_logger_with_data->abs_logger_pointers[abs_logger_with_data->used_elements++] = abs_logger;
    }
    
};


// Used typedef to avoid having to change this function later. May update others to use same phillosphy.
void add_function_to_conditional_list(struct conditional_list_struct *list,conditional_function_pointer new, union conditional_data_union *data_union) {
    if (list->num_elements == 0) {
      list->num_elements++;
      list->conditional_functions = malloc(list->num_elements*sizeof(conditional_function_pointer));
      list->p_data_unions = malloc(list->num_elements*sizeof(union conditional_data_union*));
      list->conditional_functions[0] = new;
      list->p_data_unions[0] = data_union;
    }
    else {
    conditional_function_pointer temp_fp[list->num_elements];
    union conditional_data_union *temp_du[list->num_elements];
    int iterate;
    // Could even get away with a shallow copy here instead of the loop, but it is not relevant for performance.
    for (iterate=0;iterate<list->num_elements;iterate++) {
      temp_fp[iterate] = list->conditional_functions[iterate];
      temp_du[iterate] = list->p_data_unions[iterate];
    }
    free(list->conditional_functions);
    free(list->p_data_unions);
    list->num_elements++;
    list->conditional_functions = malloc(list->num_elements*sizeof(conditional_function_pointer));
    list->p_data_unions = malloc(list->num_elements*sizeof(union conditional_data_union*));
    
    for (iterate=0;iterate<list->num_elements-1;iterate++) {
      list->conditional_functions[iterate] = temp_fp[iterate];
      list->p_data_unions[iterate] = temp_du[iterate];
    }
    list->conditional_functions[list->num_elements-1] = new;
    list->p_data_unions[list->num_elements-1] = data_union;
    }
};


// could make function that removes a element from a 1d_int_list, and have each list generation as a function that takes a copy of an overlap list

void print_1d_int_list(struct pointer_to_1d_int_list list,char *name) {
        int iterate;
        printf("LIST: ");printf("%s",name);printf(" = [");
        for (iterate = 0; iterate < list.num_elements; iterate++) {
            printf("%d",list.elements[iterate]);
            if (iterate < list.num_elements - 1) printf(",");
        }
        printf("]\n");
    };

void print_1d_double_list(struct pointer_to_1d_double_list list,char *name) {
        int iterate;
        printf("LIST: ");printf("%s",name);printf(" = [");
        for (iterate = 0; iterate < list.num_elements; iterate++) {
            printf("%f",list.elements[iterate]);
            if (iterate < list.num_elements - 1) printf(",");
        }
        printf("]\n");
    };

void print_position(Coords pos,char *name) {
    printf("POSITION: ");printf("%s",name);printf(" = (%f,%f,%f)\n",pos.x,pos.y,pos.z);
    };

void print_rotation(Rotation rot, char *name) {
    printf("ROT MATRIX: %s \n",name);
    printf("[%f %f %f]\n",rot[0][0],rot[0][1],rot[0][2]);
    printf("[%f %f %f]\n",rot[1][0],rot[1][1],rot[1][2]);
    printf("[%f %f %f]\n\n",rot[2][0],rot[2][1],rot[2][2]);
};
    
void allocate_list_from_temp(int num_elements,struct pointer_to_1d_int_list original,struct pointer_to_1d_int_list *new) {
        int iterate;
    
        new->num_elements = num_elements;
        if (num_elements > 0) {
            new->elements = malloc(num_elements*sizeof(int));
            for (iterate = 0;iterate < num_elements; iterate++) new->elements[iterate] = original.elements[iterate];
        } else new->elements = NULL;
    
    };

void allocate_logic_list_from_temp(int num_elements,struct pointer_to_1d_int_list original, struct pointer_to_1d_int_list *new) {
        // A logic list shares the same structure of a normal list, but instead of listing numbers, it is a list of yes / no (1/0)
        // Giving this function a list of [1 3 5] (and num_elements = 9) would return [0 1 0 1 0 1 0 0 0];
        int iterate;
    
        new->num_elements = num_elements;
        if (num_elements > 0) {
            new->elements = malloc(num_elements*sizeof(int));
            for (iterate = 0;iterate < num_elements;iterate++) new->elements[iterate] = 0;
            for (iterate = 0;iterate < original.num_elements;iterate++) {
                if (original.elements[iterate] < num_elements)
                    new->elements[original.elements[iterate]] = 1;
                else printf("Trying to allocate logical list without enough memory\n");
            }
        } else new->elements = NULL;
    };

/*
struct global_positions_to_transform_list_struct {
int num_elements;
Coords **positions;
}

struct global_rotations_to_transform_list_struct {
int num_elements;
Rotation **rotations;
}
*/

void add_position_pointer_to_list(struct global_positions_to_transform_list_struct *list, Coords *new_position_pointer) {
    if (list->num_elements == 0) {
      list->num_elements++;
      list->positions = malloc(list->num_elements*sizeof(Coords*));
      list->positions[0] = new_position_pointer;
    } else {
      Coords **temp;
      temp = malloc(list->num_elements*sizeof(Coords*));
      if (temp == NULL) printf("malloc failed in add_position_pointer_to_list for temp\n");
      
      int iterate;
      for (iterate=0;iterate<list->num_elements;iterate++)
        temp[iterate] = list->positions[iterate];
      free(list->positions);
      list->num_elements++;
      list->positions = malloc(list->num_elements*sizeof(Coords*));
      if (list->positions == NULL) printf("malloc failed in add_position_pointer_to_list for list->positions\n");
      
      for (iterate=0;iterate<list->num_elements-1;iterate++)
        list->positions[iterate] = temp[iterate];
      free(temp);
      list->positions[list->num_elements-1] = new_position_pointer;
    }
};

void add_rotation_pointer_to_list(struct global_rotations_to_transform_list_struct *list, Rotation *new_rotation_pointer) {
    if (list->num_elements == 0) {
      list->num_elements++;
      list->rotations = malloc(list->num_elements*sizeof(Rotation*));
      list->rotations[0] = new_rotation_pointer;
    } else {
      Rotation **temp;
      temp = malloc(list->num_elements*sizeof(Rotation*));
      int iterate;
      for (iterate=0;iterate<list->num_elements;iterate++)
        temp[iterate] = list->rotations[iterate];
      free(list->rotations);
      list->num_elements++;
      list->rotations = malloc(list->num_elements*sizeof(Rotation*));
      
      for (iterate=0;iterate<list->num_elements-1;iterate++)
        list->rotations[iterate] = temp[iterate];
      free(temp);
      list->rotations[list->num_elements-1] = new_rotation_pointer;
    }
};

void add_element_to_process_list(struct pointer_to_global_process_list *list,struct global_process_element_struct new_element) {
    if (list->num_elements == 0) {
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_process_element_struct));
    list-> elements[0] = new_element;
    }
    else {
    struct global_process_element_struct temp[list->num_elements];
    int iterate;
    for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
    free(list->elements);
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_process_element_struct));
    for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
    list->elements[list->num_elements-1] = new_element;
    }
};

void add_element_to_material_list(struct pointer_to_global_material_list *list,struct global_material_element_struct new_element) {
    if (list->num_elements == 0) {
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_material_element_struct));
    list-> elements[0] = new_element;
    }
    else {
    struct global_material_element_struct temp[list->num_elements];
    int iterate;
    for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
    free(list->elements);
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_material_element_struct));
    for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
    list->elements[list->num_elements-1] = new_element;
    }
};

void add_element_to_geometry_list(struct pointer_to_global_geometry_list *list,struct global_geometry_element_struct new_element) {
    if (list->num_elements == 0) {
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_geometry_element_struct));
    list-> elements[0] = new_element;
    }
    else {
    struct global_geometry_element_struct temp[list->num_elements];
    int iterate;
    for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
    free(list->elements);
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_geometry_element_struct));
    for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
    list->elements[list->num_elements-1] = new_element;
    }
};

void add_element_to_logger_list(struct pointer_to_global_logger_list *list,struct global_logger_element_struct new_element) {
    if (list->num_elements == 0) {
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_logger_element_struct));
    list-> elements[0] = new_element;
    }
    else {
    struct global_logger_element_struct temp[list->num_elements];
    int iterate;
    for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
    free(list->elements);
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_logger_element_struct));
    for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
    list->elements[list->num_elements-1] = new_element;
    }
};

void add_element_to_abs_logger_list(struct pointer_to_global_abs_logger_list *list, struct global_abs_logger_element_struct new_element) {
    if (list->num_elements == 0) {
      list->num_elements++;
      list->elements = malloc(list->num_elements*sizeof(struct global_abs_logger_element_struct));
      list->elements[0] = new_element;
    }
    else {
      struct global_abs_logger_element_struct temp[list->num_elements];
      int iterate;
      for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
      free(list->elements);
      list->num_elements++;
      list-> elements = malloc(list->num_elements*sizeof(struct global_abs_logger_element_struct));
      for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
      list->elements[list->num_elements-1] = new_element;
    }
};

void add_element_to_tagging_conditional_list(struct global_tagging_conditional_list_struct *list,struct global_tagging_conditional_element_struct new_element) {
    if (list->num_elements == 0) {
    list->num_elements++;
    list->elements = malloc(list->num_elements*sizeof(struct global_tagging_conditional_element_struct));
    list->elements[0] = new_element;
    }
    else {
    struct global_tagging_conditional_element_struct temp[list->num_elements];
    int iterate;
    for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
    free(list->elements);
    list->num_elements++;
    list->elements = malloc(list->num_elements*sizeof(struct global_tagging_conditional_element_struct));
    for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
    list->elements[list->num_elements-1] = new_element;
    }
};

void add_element_to_master_list(struct pointer_to_global_master_list *list,struct global_master_element_struct new_element) {
    if (list->num_elements == 0) {
    list->num_elements++;
    list->elements = malloc(list->num_elements*sizeof(struct global_master_element_struct));
    list->elements[0] = new_element;
    }
    else {
    struct global_master_element_struct temp[list->num_elements];
    int iterate;
    for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
    free(list->elements);
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_master_element_struct));
    for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
    list->elements[list->num_elements-1] = new_element;
    }
};

void add_initialized_logger_in_volume(struct loggers_struct *loggers,int number_of_processes) {
  int iterate;
  if (loggers->num_elements == 0) {
    loggers->num_elements++;
    loggers->p_logger_volume = malloc(loggers->num_elements * sizeof(struct logger_for_each_process_list));
    loggers->p_logger_volume[0].num_elements = number_of_processes;
    loggers->p_logger_volume[0].p_logger_process = malloc(number_of_processes * sizeof(struct logger_struct**));
    for (iterate=0;iterate<number_of_processes;iterate++)
      loggers->p_logger_volume[0].p_logger_process[iterate] = NULL;
  } else {
    // Already some elements, store them in temp, free main, transfer back and add newest.
    struct logger_for_each_process_list temp[loggers->num_elements];
    
    for (iterate=0;iterate<loggers->num_elements;iterate++) temp[iterate] = loggers->p_logger_volume[iterate];
    free(loggers->p_logger_volume);
    loggers->num_elements++;
    loggers->p_logger_volume = malloc(loggers->num_elements*sizeof(struct logger_for_each_process_list));
    for (iterate=0;iterate<loggers->num_elements-1;iterate++) loggers->p_logger_volume[iterate] = temp[iterate];
    loggers->p_logger_volume[loggers->num_elements-1].num_elements = number_of_processes;
    loggers->p_logger_volume[loggers->num_elements-1].p_logger_process = malloc(number_of_processes * sizeof(struct logger_struct**));
    for (iterate=0;iterate<number_of_processes;iterate++) {
      loggers->p_logger_volume[loggers->num_elements-1].p_logger_process[iterate] = NULL;
    }
  }
};

/*
void add_initialized_abs_logger_in_volume(struct abs_loggers_struct *abs_loggers, int number_of_processes) {
  int iterate;
  if (abs_loggers->num_elements == 0) {
    abs_loggers->num_elements++;
    abs_loggers->p_abs_logger_volume = malloc(abs_loggers->num_elements * sizeof(struct abs_loggers_struct));
    abs_loggers->p_abs_logger_volume[0].num_elements = number_of_processes;
    abs_loggers->p_abs_logger_volume[0].p_abs_logger_process = malloc(number_of_processes * sizeof(struct abs_logger_struct**));
    for (iterate=0;iterate<number_of_processes;iterate++)
      abs_loggers->p_abs_logger_volume[0].p_abs_logger_process[iterate] = NULL;
  } else {
    // Already some elements, store them in temp, free main, transfer back and add newest.
    struct abs_logger_for_each_process_list temp[abs_loggers->num_elements];
    
    for (iterate=0;iterate<abs_loggers->num_elements;iterate++) temp[iterate] = abs_loggers->p_abs_logger_volume[iterate];
    free(abs_loggers->p_abs_logger_volume);
    abs_loggers->num_elements++;
    abs_loggers->p_abs_logger_volume = malloc(abs_loggers->num_elements*sizeof(struct abs_logger_for_each_process_list));
    for (iterate=0;iterate<abs_loggers->num_elements-1;iterate++) abs_loggers->p_abs_logger_volume[iterate] = temp[iterate];
    abs_loggers->p_abs_logger_volume[abs_loggers->num_elements-1].num_elements = number_of_processes;
    abs_loggers->p_abs_logger_volume[abs_loggers->num_elements-1].p_abs_logger_process = malloc(number_of_processes * sizeof(struct abs_logger_struct**));
    for (iterate=0;iterate<number_of_processes;iterate++) {
      abs_loggers->p_abs_logger_volume[abs_loggers->num_elements-1].p_abs_logger_process[iterate] = NULL;
    }
  }
};
*/

void add_initialized_abs_logger_in_volume(struct abs_loggers_struct *abs_loggers) {
  int iterate;
  if (abs_loggers->num_elements == 0) {
    abs_loggers->num_elements++;
    abs_loggers->p_abs_logger = malloc(abs_loggers->num_elements * sizeof(struct abs_logger_struct*));
  } else {
    // Already some elements, store them in temp, free main, transfer back and add newest.
    struct abs_logger_struct *temp[abs_loggers->num_elements];
    
    for (iterate=0;iterate<abs_loggers->num_elements;iterate++) temp[iterate] = abs_loggers->p_abs_logger[iterate];
    free(abs_loggers->p_abs_logger);
    
    abs_loggers->num_elements++;
    abs_loggers->p_abs_logger = malloc(abs_loggers->num_elements*sizeof(struct abs_logger_struct*));
    for (iterate=0;iterate<abs_loggers->num_elements-1;iterate++) abs_loggers->p_abs_logger[iterate] = temp[iterate];
    abs_loggers->p_abs_logger[abs_loggers->num_elements-1] = NULL;
    
  }
};


// -------------    Functions used to shorten master trace    ---------------------------------------------


void update_current_mask_intersect_status(struct pointer_to_1d_int_list *current_mask_intersect_list_status, struct pointer_to_1d_int_list *mask_status_list, struct Volume_struct **Volumes, int *current_volume) {
  // This function is to be executed whenever the current volume changes, or the mask status changes
  // It updates the effective mask status for each element of a volumes mask_intersect_list
  // The effective mask takes the ALL/ANY mode assosiated with each volume into account,
  //  meaning a volume needs to be within ALL/ANY of it's masks to have a status of 1
  // In most cases this mask_intersect_list will be empty, and memory operations are avoided
  //printf("Number of elements to be check: %d \n",Volumes[*current_volume]->geometry.mask_intersect_list.num_elements);
  
  if (Volumes[*current_volume]->geometry.mask_intersect_list.num_elements > 0) {
    int iterate,this_element,*mask_start,*mask_check;
    for (iterate=0;iterate<Volumes[*current_volume]->geometry.mask_intersect_list.num_elements;iterate++) {
      this_element = Volumes[*current_volume]->geometry.mask_intersect_list.elements[iterate];
      //printf("We are investigating volume number %d from the mask_intersect_list of volume %d",this_element,*current_volume);
      if (Volumes[this_element]->geometry.mask_mode == 2) { // ANY mask mode
        //printf("We are in ANY mask mode! \n");
        current_mask_intersect_list_status->elements[iterate] = 0; // Assume the mask status is 0, but if any are 1, take that instead
        for (mask_start=mask_check=Volumes[this_element]->geometry.masked_by_mask_index_list.elements;mask_check-mask_start<Volumes[this_element]->geometry.masked_by_mask_index_list.num_elements;mask_check++) {
          //printf("Checking all the mask statuses of volumes masking %d, now mask with global mask index %d ! \n",this_element,*mask_check);
          if (mask_status_list->elements[*mask_check] == 1) {
            //printf("The status was 1, so the effective status is set to 1 and the loop is stopped");
            current_mask_intersect_list_status->elements[iterate] = 1;
            break;
          }
        }
      } else { // ALL mask mode
        //printf("We are in ALL mask mode! \n");
        current_mask_intersect_list_status->elements[iterate] = 1; // Assume the mask status is 1, but if any one is 0, take that instead
        for (mask_start=mask_check=Volumes[this_element]->geometry.masked_by_mask_index_list.elements;mask_check-mask_start<Volumes[this_element]->geometry.masked_by_mask_index_list.num_elements;mask_check++) {
          //printf("Checking all the mask statuses of volumes masking %d, now mask with global mask index %d ! \n",this_element,*mask_check);
          if (mask_status_list->elements[*mask_check] == 0) {
            //printf("The status was 0, so the effective status is set to 0 and the loop is stopped \n");
            current_mask_intersect_list_status->elements[iterate] = 0;
            break;
          }
        }
      }
    }
  }
    
  /* Original version compatible with trace scope
  for (iterate=0;iterate<Volumes[current_volume]->geometry.mask_intersect_list.num_elements;iterate++) {
    this_element = Volumes[current_volume]->geometry.mask_intersect_list.elements[iterate];
    if (Volumes[this_element]->geometry.mask_mode == 2) { // ANY mask mode
      current_mask_intersect_list_status.elements[iterate] = 0; // Assume the mask status is 0, but if any are 1, take that instead
      for (mask_start=mask_check=Volumes[this_element]->geometry.masked_by_mask_index_list.elements;mask_check-mask_start<Volumes[this_element]->geometry.masked_by_mask_index_list.num_elements;mask_check++) {
        if (mask_status_list[*mask_check] == 1) {
          current_mask_intersect_list_status.elements[iterate] = 1;
          break;
        }
      }
    } else { // ALL mask mode
      current_mask_intersect_list_status.elements[iterate] = 1; // Assume the mask status is 1, but if any one is 0, take that instead
      for (mask_start=mask_check=Volumes[this_element]->geometry.masked_by_mask_index_list.elements;mask_check-mask_start<Volumes[this_element]->geometry.masked_by_mask_index_list.num_elements;mask_check++) {
        if (mask_status_list[*mask_check] == 0) {
          current_mask_intersect_list_status.elements[iterate] = 0;
          break;
        }
      }
    }
  }
  */
}

// -------------    Tagging functions    ------------------------------------------------------------------

struct tagging_tree_node_struct {
  // Statistics:
  double intensity;
  int number_of_rays;
  // tree pointers
  struct tagging_tree_node_struct *above;   // Pointer to node above
  struct tagging_tree_node_struct **volume_branches;
  struct tagging_tree_node_struct **process_branches;
};

struct list_of_tagging_tree_node_pointers {
  struct tagging_tree_node_struct **elements;
  int num_elements;
};

struct tagging_tree_node_struct *make_tagging_tree_node(void) {
    return (struct tagging_tree_node_struct *) malloc(sizeof(struct tagging_tree_node_struct));
}


struct tagging_tree_node_struct  *simple_initialize_tagging_tree_node(struct tagging_tree_node_struct *new_node) {
    //struct tagging_tree_node_struct *dummy;
    //dummy = malloc(sizeof(struct tagging_tree_node_struct));
    //new_node = dummy;
    
    //new_node->element = (struct tagging_tree_node_struct *) malloc(sizeof(struct tagging_tree_node_struct));
    //new_node = (struct tagging_tree_node_struct *) malloc(sizeof(struct tagging_tree_node_struct));
    new_node = make_tagging_tree_node();
    
    if (new_node == NULL) printf("ERROR, Union tagging system could not allocate memory\n");
    new_node->intensity = 4.2; // (double) 4.2;
    new_node->number_of_rays = 42; //(int) 42;
    
    printf("new_node->intensity = %f, new_node->number_of_rays = %d \n",new_node->intensity,new_node->number_of_rays);
    return new_node;
};


struct tagging_tree_node_struct *initialize_tagging_tree_node(struct tagging_tree_node_struct *new_node, struct tagging_tree_node_struct *above_node, struct Volume_struct *this_volume) {
    new_node = make_tagging_tree_node();
    
    new_node->intensity = (double) 0;
    new_node->number_of_rays = (int) 0;
    new_node->above = above_node;
    
    int next_volume_list_length = this_volume->geometry.next_volume_list.num_elements;
    new_node->volume_branches = malloc(next_volume_list_length*sizeof(struct tagging_tree_node_struct*));
    int iterate;
    // Initializing pointers so that they can be checked for NULL later. Is this redundant? Does malloc return null pointers?
    for (iterate=0;iterate<next_volume_list_length;iterate++) new_node->volume_branches[iterate] = NULL;
    //new_node->volume_branches.num_elements = dest_list_length; // May be removed
    
    int number_of_processes;
    if (this_volume->p_physics == NULL) number_of_processes = 0;
    else number_of_processes = this_volume->p_physics->number_of_processes;
    
    new_node->process_branches = malloc(number_of_processes*sizeof(struct tagging_tree_node_struct*));
    // Initializing pointers so that they can be checked for NULL later. Is this redundant? Does malloc return null pointers?
    for (iterate=0;iterate<number_of_processes;iterate++) new_node->process_branches[iterate] = NULL;
    //new_node->process_branches.num_elements=number_of_processes; // May be removed
    
    return new_node;
};

struct tagging_tree_node_struct *goto_process_node(struct tagging_tree_node_struct *current_node, int process_index, struct Volume_struct *this_volume, int *stop_tagging_ray, int stop_creating_nodes) {
    // Either create a new node if it has not been created yet, or travel down the tree
    if (current_node->process_branches[process_index] == NULL) {
      if (stop_creating_nodes == 0) {
        current_node->process_branches[process_index] = initialize_tagging_tree_node(current_node->process_branches[process_index],current_node,this_volume);
        return current_node->process_branches[process_index];
      } else {
        // This stops the ray from using more goto node functions and being counted in the statistics.
        // Happens because the history limit is reached, and no new histories should be started.
        *stop_tagging_ray = 1;
        return current_node;
      }
    } else {
        current_node = current_node->process_branches[process_index];
        return current_node;
    }
    
    //return current_node;
};

struct tagging_tree_node_struct *goto_volume_node(struct tagging_tree_node_struct *current_node,int current_volume, int next_volume, struct Volume_struct **Volumes, int *stop_tagging_ray, int stop_creating_nodes) {
    // I have only allocated the number of node branches that corresponds to the current_volumes next_volume_list
    // The problem is to find where on the destination list the current volume is without doing a manual search
    
    // With the new mask system there is a risk of going from and to the same volume, which should be ignored by the tagging system
    if (current_volume == next_volume) return current_node;
    
    struct tagging_tree_node_struct *output;
    int next_volume_list_index = -1;
    // Temporary slow method for finding the correct index on the destination list
    int iterate;
    
    for (iterate=0;iterate<Volumes[current_volume]->geometry.next_volume_list.num_elements;iterate++)
        if (Volumes[current_volume]->geometry.next_volume_list.elements[iterate] == next_volume) {
            next_volume_list_index = iterate;
            break;
        }
    
    // Debug phase
    //printf("Tagging: going from volume %d to volume %d, which is index number %d on it's next volume list \n",current_volume,next_volume,next_volume_list_index);
    #ifndef OPENACC
    if (next_volume_list_index == -1) {
        printf("ERROR in Union component, tagging or destination system failed, next volume was not on next volume list\n");
        printf("current_volume = %d, next_volume = %d \n",current_volume,next_volume);
        print_1d_int_list(Volumes[current_volume]->geometry.destinations_list,"destinations_list for current volume");
        exit(1);
    }
    #endif

    // Either create a new node if it has not been created yet, or travel down the tree
    if (current_node->volume_branches[next_volume_list_index] == NULL) {
      if (stop_creating_nodes == 0) {
        current_node->volume_branches[next_volume_list_index] =  initialize_tagging_tree_node(current_node->volume_branches[next_volume_list_index],current_node,Volumes[next_volume]);
        return current_node->volume_branches[next_volume_list_index];
      } else {
        // This stops the ray from using more goto node functions and being counted in the statistics.
        // Happens because the history limit is reached, and no new histories should be started.
        *stop_tagging_ray = 1;
        return current_node;
      }
    } else {
        //current_node = current_node->volume_branches[next_volume_list_index];
        //return current_node;
        current_node = current_node->volume_branches[next_volume_list_index];
        //printf("used allocated node \n");
        return current_node;
    }
};

void add_statistics_to_node(struct tagging_tree_node_struct *current_node, Coords *r, Coords *v, double *weight, int *counter) {
    if (current_node->number_of_rays == 0) (*counter)++;
    current_node->number_of_rays = current_node->number_of_rays + 1;
    current_node->intensity = current_node->intensity + *weight;
};

struct history_node_struct {
    int volume_index;
    int process_index;
};

struct dynamic_history_list {
  struct history_node_struct *elements;
  int used_elements;
  int allocated_elements;
};

struct saved_history_struct {
    struct history_node_struct *elements;
    int used_elements;
    double intensity;
    int number_of_rays;
};

struct total_history_struct {
    struct saved_history_struct *saved_histories;
    int used_elements;
    int allocated_elements;
};


void add_to_history(struct dynamic_history_list *history, int volume_index, int process_index) {
    //printf("Adding to history[%d]: volume_index = %d, process_index = %d \n",history->used_elements,volume_index,process_index);
    if (history->allocated_elements == 0) {
        history->elements = malloc(5*sizeof(struct history_node_struct));
        history->used_elements = 1;
        history->allocated_elements = 5;
        history->elements[0].volume_index = volume_index;
        history->elements[0].process_index = process_index;
    } else if (history->used_elements > history->allocated_elements-1) {
        struct dynamic_history_list temp_history;
        temp_history.elements = malloc((history->used_elements)*sizeof(struct history_node_struct));
        int iterate;
        for (iterate=0;iterate<history->used_elements;iterate++) {
            temp_history.elements[iterate].volume_index  = history->elements[iterate].volume_index;
            temp_history.elements[iterate].process_index = history->elements[iterate].process_index;
        }
        free(history->elements);
        history->elements = malloc((history->allocated_elements+5)*sizeof(struct history_node_struct));
        for (iterate=0;iterate<history->used_elements;iterate++) {
            history->elements[iterate].volume_index  = temp_history.elements[iterate].volume_index;
            history->elements[iterate].process_index = temp_history.elements[iterate].process_index;
        }
        
        history->allocated_elements = history->allocated_elements+5;
        
        history->elements[history->used_elements].volume_index = volume_index;
        history->elements[history->used_elements].process_index = process_index;
        history->used_elements = history->used_elements+1;
        
    } else {
        history->elements[history->used_elements].volume_index = volume_index;
        history->elements[history->used_elements].process_index = process_index;
        history->used_elements++;
    }
    
};

void printf_history(struct dynamic_history_list *history) {
    int history_iterate;
          //printf("History number %d, intensity = %f, number of rays = %d:",hist_num, search_node->intensity, search_node->number_of_rays);
          for (history_iterate=0;history_iterate<history->used_elements-1;history_iterate++) {
            if (history->elements[history_iterate].process_index == -1) {
                printf(" V%d ->",history->elements[history_iterate].volume_index);
            } else {
                printf(" P%d ->",history->elements[history_iterate].process_index);
            }
          }
          if (history->elements[history_iterate].process_index == -1) {
                printf(" V%d \n",history->elements[history_iterate].volume_index);
          } else {
                printf(" P%d \n",history->elements[history_iterate].process_index);
          }
}

void fprintf_total_history(struct saved_history_struct *history, FILE *fp) {
    fprintf(fp,"%d\t N I=%E \t", history->number_of_rays, history->intensity);
        int history_iterate;
          //printf("History number %d, intensity = %f, number of rays = %d:",hist_num, search_node->intensity, search_node->number_of_rays);
          for (history_iterate=0;history_iterate<history->used_elements-1;history_iterate++) {
            if (history->elements[history_iterate].process_index == -1) {
                fprintf(fp," V%d ->",history->elements[history_iterate].volume_index);
            } else {
                fprintf(fp," P%d ->",history->elements[history_iterate].process_index);
            }
          }
          if (history->elements[history_iterate].process_index == -1) {
                fprintf(fp," V%d \n",history->elements[history_iterate].volume_index);
          } else {
                fprintf(fp," P%d \n",history->elements[history_iterate].process_index);
          }
}

/*
int Sample_compare_doubles (const void *a, const void *b) {
  const double *da = (const double *) a;
  const double *db = (const double *) b;

  return (*da > *db) - (*da < *db);
}
*/


int Sample_compare_history_intensities (const struct saved_history_struct *a, const struct saved_history_struct *b) {
  const double *da = (const double *) &(a->intensity);
  const double *db = (const double *) &(b->intensity);

  return (*da < *db) - (*da > *db);
}

void write_tagging_tree(struct list_of_tagging_tree_node_pointers *master_list, struct Volume_struct **Volumes, int total_history_counter, int number_of_volumes) {
  // Start from top of tree, go to extremeties, take results and add to disk / database, free that node
  int volume_index,done,volume_iterate,process_iterate,current_volume,next_node_found,current_number_of_processes,history_iterate,hist_num;
  
  struct tagging_tree_node_struct *search_node;
  struct tagging_tree_node_struct **kill_candidate;
  struct dynamic_history_list history_data; // Allocate the history list struct
  struct dynamic_history_list *history;     // Use this pointer in the algorithm
  
  struct total_history_struct total_history;
  total_history.saved_histories = malloc(total_history_counter * sizeof(struct saved_history_struct));
  total_history.allocated_elements = total_history_counter;
  total_history.used_elements = 0;
  
  history = &history_data;
  
  history->used_elements = 0;
  history->allocated_elements = 0;
  
  hist_num = 0;
  for (volume_index=0;volume_index<master_list->num_elements;volume_index++) {
    
    search_node = master_list->elements[volume_index];
    
    if (volume_index != 0)
        current_number_of_processes = Volumes[volume_index]->p_physics->number_of_processes;
    else
        current_number_of_processes = 0;
    
    current_volume = volume_index;
    done = 0;
    
    history->used_elements = 0;
    
    add_to_history(history,current_volume,-1);
    while(done == 0) {
        next_node_found=0;
        for (volume_iterate=0;volume_iterate<Volumes[current_volume]->geometry.next_volume_list.num_elements;volume_iterate++) {
            //printf("searc_node->volume_branches[0]->intensity = %f\n",search_node->volume_branches[0]->intensity);
            if (search_node->volume_branches[volume_iterate] != NULL) {
                current_volume = Volumes[current_volume]->geometry.next_volume_list.elements[volume_iterate];
                if (current_volume != 0)
                    current_number_of_processes = Volumes[current_volume]->p_physics->number_of_processes;
                else
                    current_number_of_processes = 0;
                
                //search_node = &(search_node->volume_branches[volume_iterate]);
                kill_candidate = &(search_node->volume_branches[volume_iterate]);
                search_node = search_node->volume_branches[volume_iterate];
                next_node_found = 1;
                add_to_history(history,current_volume,-1);
                //printf_history(history);
                break;
            }
        }
        if (next_node_found == 0) {
          //printf("doing process loop with %d steps \n",current_number_of_processes);
          for (process_iterate=0;process_iterate<current_number_of_processes;process_iterate++) {
            //if (&(search_node->process_branches[volume_iterate]) != NULL) {
            if (search_node->process_branches[process_iterate] != NULL) {
                //printf("was not NULL (process)\n");
                //search_node = &(search_node->process_branches[process_iterate]);
                kill_candidate = &(search_node->process_branches[process_iterate]);
                search_node = search_node->process_branches[process_iterate];
                next_node_found = 1;
                add_to_history(history,current_volume,process_iterate);
                //printf_history(history);
                break;
            }
          }
        }
        
        if (next_node_found == 0) {
          // write this history to disk / memory
          hist_num++;
          //printf("Reached next_node_found == 0 \n");
          if (history->used_elements > 0 && search_node->number_of_rays > 0) {
            //printf("%d rays (I=%E) with history: \t", search_node->number_of_rays, search_node->intensity);
            //printf_history(history);
            
            total_history.saved_histories[total_history.used_elements].used_elements = history->used_elements;
            total_history.saved_histories[total_history.used_elements].elements = malloc(total_history.saved_histories[total_history.used_elements].used_elements*sizeof(struct history_node_struct));
            for (history_iterate = 0;history_iterate<history->used_elements;history_iterate++) {
                total_history.saved_histories[total_history.used_elements].elements[history_iterate] = history->elements[history_iterate];
                //printf("total_history.saved_histories[total_history.used_elements].elements[%d].volume_index \n",history_iterate,total_history.saved_histories[total_history.used_elements].elements[history_iterate].volume_index);
            }
            //total_history.saved_histories[total_history.used_elements].elements = history->elements;
            total_history.saved_histories[total_history.used_elements].intensity = search_node->intensity;
            total_history.saved_histories[total_history.used_elements].number_of_rays = search_node->number_of_rays;
            total_history.used_elements++;
          }
          
          history->used_elements = 0;
          
          // end of tree, no new nodes
          if (search_node->above == NULL) {
            done = 1;
          } else {
            // reset to the root of the tree
            *kill_candidate = NULL;
            free(search_node);
            search_node = master_list->elements[volume_index];
            
            if (volume_index != 0)
              current_number_of_processes = Volumes[volume_index]->p_physics->number_of_processes;
            else
              current_number_of_processes = 0;
    
            current_volume = volume_index;
            add_to_history(history,current_volume,-1);
          }
          
        }
    }
  }
  
  if (history->allocated_elements > 0) free(history->elements);

  qsort(total_history.saved_histories,total_history.used_elements,sizeof (struct saved_history_struct), Sample_compare_history_intensities);
  
  
  
  MPI_MASTER(
  printf("\n\n");
  printf("Top 20 most common histories. Shows the index of volumes entered (VX), and the scattering processes (PX)\n");
  for (history_iterate=0;history_iterate<total_history.used_elements && history_iterate<20;history_iterate++) {
    printf("%d\t N I=%E \t", total_history.saved_histories[history_iterate].number_of_rays, total_history.saved_histories[history_iterate].intensity);
    printf_history(&total_history.saved_histories[history_iterate]);
  }
   
  FILE *fp;
  fp = fopen("union_history.dat","w");
  
  fprintf(fp,"History file written by the McStas component Union_master \n");
  fprintf(fp,"When running with MPI, the results may be from just a single thread, meaning intensities are divided by number of threads\n");
  fprintf(fp,"----- Description of the used volumes -----------------------------------------------------------------------------------\n");
  
  fprintf(fp,"V0: Surrounding vacuum \n");
  for (volume_iterate=1;volume_iterate<number_of_volumes;volume_iterate++) {
    fprintf(fp,"V%d: %s  ",volume_iterate,Volumes[volume_iterate]->name);
    fprintf(fp,"Material: %s  ",Volumes[volume_iterate]->p_physics->name);
    for (process_iterate=0;process_iterate<Volumes[volume_iterate]->p_physics->number_of_processes;process_iterate++) {
      fprintf(fp," P%d: %s",process_iterate,Volumes[volume_iterate]->p_physics->p_scattering_array[process_iterate].name);
    }
    fprintf(fp,"\n");
  }
  fprintf(fp,"----- Histories sorted after intensity ----------------------------------------------------------------------------------\n");
  
  for (history_iterate=0;history_iterate<total_history.used_elements;history_iterate++) {
    fprintf_total_history(&total_history.saved_histories[history_iterate],fp);
    // Garbage collection
    if (total_history.saved_histories[history_iterate].used_elements > 0) free(total_history.saved_histories[history_iterate].elements);
  }
  fclose(fp);
  
  )
  
  // Garbage collection
  if (total_history.allocated_elements > 0) free(total_history.saved_histories);
  
  
};


// -------------    Intersection table functions   --------------------------------------------------------
int clear_intersection_table(struct intersection_time_table_struct *intersection_time_table) {
    // Resets the intersection table when a scattering have occured.
    int iterate_volumes,iterate_solutions;
    
    // Start at one because vacuum (0) does not have a listing in the intersection table
    for (iterate_volumes = 1;iterate_volumes < intersection_time_table->num_volumes;iterate_volumes++) {
        intersection_time_table->calculated[iterate_volumes] = 0;
        // This second loop is added for safty in debugging phase, but can be removed as the information should never be accesed when calculated = 0
        for (iterate_solutions = 0;iterate_solutions < intersection_time_table->n_elements[iterate_volumes];iterate_solutions++) {
            intersection_time_table->intersection_times[iterate_volumes][iterate_solutions] = -1;
        }
    }
    
    return 1;
};

void print_intersection_table(struct intersection_time_table_struct *intersection_time_table) {
    int num_volumes,iterate,solutions;
    int max_number_of_solutions = 0;
    
    num_volumes = intersection_time_table->num_volumes;
    
    for (iterate = 0;iterate < num_volumes;iterate++) {
        if (max_number_of_solutions < intersection_time_table->n_elements[iterate])
            max_number_of_solutions = intersection_time_table->n_elements[iterate];
    }
    
    printf("------------------ INTERSECTION_TIME_TABLE -----------------");
    for (solutions = 2;solutions < max_number_of_solutions;solutions++) printf("------------");
    printf("\n");
    
    // printf("iterate      |");

    printf("           ");
    printf("| CALCULATED  |");
    for (solutions = 0;solutions < max_number_of_solutions;solutions++) {
        printf(" - SOLUTION %d - |",solutions);
    }

    printf("\n");
    for (iterate = 0;iterate < num_volumes;iterate++){
    // print iterate number
    printf("Volume %d   |",iterate);
    printf(" ---- %d ---- |",intersection_time_table->calculated[iterate]);
    
        for (solutions = 0;solutions < max_number_of_solutions;solutions++) {
          if (intersection_time_table->n_elements[iterate] > solutions && intersection_time_table->calculated[iterate] == 1)
            if (intersection_time_table->intersection_times[iterate][solutions] > 0)
             printf("   %1.8f   |",intersection_time_table->intersection_times[iterate][solutions]);
            else
             printf("   %1.7f   |",intersection_time_table->intersection_times[iterate][solutions]);
          else
            printf("                |");
        }
    printf("\n");
    }
    printf("------------------------------------------------------------");
    for (solutions = 2;solutions < max_number_of_solutions;solutions++) printf("------------");
    printf("\n");
    
    
    };
    
// -------------    Drawing functions   --------------------------------------------------------
void merge_lines_to_draw(struct lines_to_draw *lines_master,struct lines_to_draw *lines_new) {
    if (lines_master->number_of_lines == 0) {
    lines_master->number_of_lines = lines_new->number_of_lines;
    lines_master->lines = malloc(lines_master->number_of_lines*sizeof(struct line_segment));
    lines_master->lines = lines_new->lines;
    // One could free lines_new->lines;
    } else {
    int iterate;
    struct line_segment *temp_lines;
    temp_lines = malloc(lines_master->number_of_lines*sizeof(struct line_segment));
    for (iterate = 0;iterate < lines_master->number_of_lines;iterate++) temp_lines[iterate] = lines_master->lines[iterate];
    free(lines_master->lines);
    lines_master->lines = malloc((lines_master->number_of_lines+lines_new->number_of_lines)*sizeof(struct line_segment));
    for (iterate = 0;iterate < lines_master->number_of_lines;iterate++) lines_master->lines[iterate] = temp_lines[iterate];
    for (iterate = 0;iterate < lines_new->number_of_lines;iterate++) lines_master->lines[iterate+lines_master->number_of_lines] = lines_new->lines[iterate];
    lines_master->number_of_lines = lines_master->number_of_lines + lines_new->number_of_lines;
    free(temp_lines);
    }
    };

int r_has_highest_priority(Coords point,int N,struct geometry_struct **Geometries,int number_of_volumes) {
    // Function that test if a point in Volume N has the highest priority.
    // Returns 0 if another volume has higher priority at the point, and it's mask status is 1
    // Returns 1 if no other volume has higher priority at the point
    // (not active) Returns a larger integer if the volume N is a mask, and the point is not in the volume it is masking,
    //  which results in the drawing function making that number of dashes
    
    
    int mask_status,*mask_start,*mask_check;
    // If the volume is a mask, it does not have a priority, and is always on top
    if (Geometries[N]->is_mask_volume == 1) {
      return 1;
      // Can draw parts of the mask that is outside the volume it masks as dashed lines, but it just looks messy
      /*
      mask_status = 1;
      for (mask_check=mask_start=Geometries[N]->mask_list.elements;mask_check-mask_start<Geometries[N]->mask_list.num_elements;mask_check++) {
        if (Geometries[*mask_check]->within_function(point,Geometries[*mask_check]) == 1) {
          return 1; // If the point is within a volume the mask is masking, draw it as a solid line
        }
      }
      // As it was not inside any of the volumes the mask is masking, draw it as a dashed line
      return 5;
      */
    }
    
    // If the volume is a masked volume, check if the point is within it's masks
    if (Geometries[N]->is_masked_volume == 1) {
      if (Geometries[N]->mask_mode == 1) { // ALL mode, need to be within ALL masks
        for (mask_check=mask_start=Geometries[N]->masked_by_list.elements;mask_check-mask_start<Geometries[N]->masked_by_list.num_elements;mask_check++) {
          if (Geometries[*mask_check]->within_function(point,Geometries[*mask_check]) == 0) {
            return 0; // If the point is just outside one mask, the mask status is 0 and the point does not have highest priority
          }
        }
      } else { // ANY mode, need to be within at least one mask
        mask_status = 0;
        for (mask_check=mask_start=Geometries[N]->masked_by_list.elements;mask_check-mask_start<Geometries[N]->masked_by_list.num_elements;mask_check++) {
          if (Geometries[*mask_check]->within_function(point,Geometries[*mask_check]) == 1) {
            mask_status = 1;
            break;
          }
        }
        if (mask_status == 0) {
          return 0; // If it was not in a single of it's masks, the point did not have highest priority
        }
      }
    }
    
    int volume_index;
    double self_priority;
    self_priority = Geometries[N]->priority_value;
    
    
    for (volume_index = 1;volume_index<number_of_volumes;volume_index++) {
      if (volume_index != N && Geometries[volume_index]->is_mask_volume == 0) {
        if (Geometries[volume_index]->within_function(point,Geometries[volume_index])) {
          if (Geometries[volume_index]->is_masked_volume == 1) {
            // Since this volume is masked, the mask status need to be checked
            if (Geometries[volume_index]->mask_mode == 1) { //ALL mode, need to be within ALL masks
              mask_status = 1;
              for (mask_check=mask_start=Geometries[volume_index]->masked_by_list.elements;mask_check-mask_start<Geometries[volume_index]->masked_by_list.num_elements;mask_check++) {
                if (Geometries[*mask_check]->within_function(point,Geometries[*mask_check]) == 0) {
                  mask_status = 0;
                  break;
                }
              }
            } else { // ANY mode, need to be within at least one mask
              mask_status = 0;
              for (mask_check=mask_start=Geometries[volume_index]->masked_by_list.elements;mask_check-mask_start<Geometries[volume_index]->masked_by_list.num_elements;mask_check++) {
                if (Geometries[*mask_check]->within_function(point,Geometries[*mask_check]) == 1) {
                  mask_status = 1;
                  break;
                }
              }
            }
          } else mask_status = 1;
          if (Geometries[volume_index]->priority_value > self_priority && mask_status == 1) {
            // printf("Volume %d did not have highest priority at (%f,%f,%f) (Volume number %d was above)\n",N,point.x,point.y,point.z,volume_index);
            return 0;
          }
        }
      }
    }
    // printf("Volume %d did have highest priority at (%f,%f,%f) \n",N,point.x,point.y,point.z);
    return 1;
    }
    
void draw_line_positions(Coords point1,Coords point2) {
        //line(point1.x,point1.y,point1.z,point2.x,point2.y,point2.z);
        // sigh, can not use line in share. Need to save the information and pass to the mcdisplay part.
    }

int Sample_compare_doubles (const void *a, const void *b) {
  const double *da = (const double *) a;
  const double *db = (const double *) b;

  return (*da > *db) - (*da < *db);
}

struct lines_to_draw draw_line_with_highest_priority(Coords position1,Coords position2,int N,struct geometry_struct **Geometries,int number_of_volumes,int max_number_of_solutions) {
    
    int volume_index,iterate,permanent_list_length = 0;
    int number_of_solutions;
    double temp_intersection[max_number_of_solutions];
    double r1[3],r2[3],direction[3];
    struct pointer_to_1d_double_list intersection_list;
    
    intersection_list.num_elements = 0;
    
    // double *permanent_intersection_list,*storage; // old ways
    
    r1[0] = position1.x;
    r1[1] = position1.y;
    r1[2] = position1.z;
    r2[0] = position2.x;
    r2[1] = position2.y;
    r2[2] = position2.z;
    
    // printf("r1 = (%f,%f,%f) \n",r1[0],r1[1],r1[2]);
    // printf("r2 = (%f,%f,%f) \n",r2[0],r2[1],r2[2]);
    
    direction[0] = r2[0] - r1[0];
    direction[1] = r2[1] - r1[1];
    direction[2] = r2[2] - r1[2];
    int geometry_output;
    
    // Find intersections
    for (volume_index = 1;volume_index < number_of_volumes; volume_index++) {
        if (volume_index != N) {
            geometry_output = Geometries[volume_index]->intersect_function(temp_intersection,&number_of_solutions,r1,direction,Geometries[volume_index]);
             // printf("No solutions for intersection (Volume %d) with %d \n",N,volume_index);
                for (iterate=0;iterate<number_of_solutions;iterate++) {
                    // print_1d_double_list(intersection_list,"intersection_list");
                    if (temp_intersection[iterate] > 0 && temp_intersection[iterate] < 1) {
                        add_element_to_double_list(&intersection_list,temp_intersection[iterate]);
                        // printf("solution taken = %f\n",temp_intersection[iterate]);
                        }                         // printf("solution ignored = %f\n",temp_intersection[iterate]);
                    // print_1d_double_list(intersection_list,"intersection_list");
                
                }
                // printf("First (%d) solutions added to the solution stack, intersection with %d \n",number_of_solutions,volume_index);
                // printf(" Solutions: ");
                // for (iterate = 0;iterate < intersection_list.num_elements;iterate++) printf("%f ",intersection_list.elements[iterate]);
                // printf("\n");
            
        }
    }
    // Now we have a list of intersection distances between r1 and r2 and all volumes.
    // This list needs to be sorted before we continue!
    
    qsort(intersection_list.elements,intersection_list.num_elements,sizeof (double), Sample_compare_doubles);
    // print_1d_double_list(intersection_list,"after sorting");
    // printf(" Solutions (after sorting): ");
    // for (iterate = 0;iterate < intersection_list.num_elements;iterate++) printf("%f ",intersection_list.elements[iterate]);
    // printf("\n");
    
    Coords points[intersection_list.num_elements+2];
    points[0] = coords_set(r1[0],r1[1],r1[2]);
    points[intersection_list.num_elements+1] = coords_set(r2[0],r2[1],r2[2]);
    for (iterate = 1;iterate < intersection_list.num_elements+1;iterate++) {
        points[iterate].x = r1[0] + direction[0]*intersection_list.elements[iterate-1];
        points[iterate].y = r1[1] + direction[1]*intersection_list.elements[iterate-1];
        points[iterate].z = r1[2] + direction[2]*intersection_list.elements[iterate-1];
    }
    
    // printf("Points on the list:\n");
    // for (iterate = 0;iterate < intersection_list.num_elements + 2;iterate++) {
    // //         printf("(%f,%f,%f)\n",points[iterate].x,points[iterate].y,points[iterate].z);
    // }
    // printf("\n");
  
    struct line_segment lines[intersection_list.num_elements+1];
    int draw_logic[intersection_list.num_elements+1];
    //struct lines_to_draw draw_order;
    Coords midpoint;
    struct lines_to_draw draw_order;
    draw_order.number_of_lines = 0;
    
    // printf("test2 in function \n");
    int number_of_dashes;
    
    for (iterate = 0;iterate < intersection_list.num_elements + 1;iterate++) {
        lines[iterate].point1 = points[iterate];
        lines[iterate].point2 = points[iterate+1];
        midpoint.x = 0.5*(lines[iterate].point1.x + lines[iterate].point2.x);
        midpoint.y = 0.5*(lines[iterate].point1.y + lines[iterate].point2.y);
        midpoint.z = 0.5*(lines[iterate].point1.z + lines[iterate].point2.z);
        
        if ((number_of_dashes = r_has_highest_priority(midpoint,N,Geometries,number_of_volumes)) != 0) {
            draw_order.number_of_lines++;
            draw_logic[iterate] = number_of_dashes;
        } else draw_logic[iterate] = 0;
    }
   
    // printf("test3 in function \n");
    
    if (draw_order.number_of_lines > 0) {
        draw_order.lines = malloc(draw_order.number_of_lines*sizeof(struct line_segment));
        draw_order.number_of_lines = 0;
        for (iterate = 0;iterate < intersection_list.num_elements + 1;iterate++) {
            if (draw_logic[iterate] != 0) {
              lines[iterate].number_of_dashes = draw_logic[iterate];
              draw_order.lines[draw_order.number_of_lines++] = lines[iterate];
            }
        }
        if (intersection_list.num_elements > 0) free(intersection_list.elements);
    }

    // printf("function done \n");
    return draw_order;
    }

struct lines_to_draw draw_circle_with_highest_priority(Coords center,Coords vector,double radius,int N,struct geometry_struct **Geometries,int number_of_volumes,int max_number_of_solutions) {
    int number_of_positions = 100;

    // normalize vector
    double vector_length = length_of_position_vector(vector);
    // print_position(vector,"start vector");
    vector.x /= vector_length;
    vector.y /= vector_length;
    vector.z /= vector_length;
    // print_position(vector,"start vector normalized");
    
    // Create a vector from the center of the circle to a point on the circumference by cross product
    double cross_input[3] = {0,1,0};
    // In case the cross input is parallel with the vector, a new is chosen. Both can't be parallel.
    if (scalar_prod(cross_input[0],cross_input[1],cross_input[2],vector.x,vector.y,vector.z) > 0.99) {
        cross_input[0] = 1; cross_input[1] = 0; cross_input[2] = 0;
    }
    // print_position(make_position(cross_input),"cross input");
    
    double cross_product1[3] = {0,0,0};
    vec_prod(cross_product1[0],cross_product1[1],cross_product1[2],vector.x,vector.y,vector.z,cross_input[0],cross_input[1],cross_input[2]);
    
    // print_position(make_position(cross_product1),"cross_product1");
    
    double cross_length = length_of_3vector(cross_product1);
    
    // printf("cross_length = %f \n",cross_length);
    
    cross_product1[0] /= cross_length;
    cross_product1[1] /= cross_length;
    cross_product1[2] /= cross_length;
    
    cross_product1[0] *= radius;
    cross_product1[1] *= radius;
    cross_product1[2] *= radius;
    
    // printf("cross_product1 = (%f,%f,%f) \n",cross_product1[0],cross_product1[1],cross_product1[2]);
    
    int iterate;
    double rotate_angle;
    Coords radial_position,old_radial_position;
    struct lines_to_draw temp_draw_order,return_draw_order;
    return_draw_order.number_of_lines = 0;
    // Generate an array of positions by rotating this vector around the given vector, this corresponds to points on the circle
    old_radial_position.x = center.x + cross_product1[0];
    old_radial_position.y = center.y + cross_product1[1];
    old_radial_position.z = center.z + cross_product1[2];

    // printf("old_radial_position = (%f,%f,%f) \n",old_radial_position.x,old_radial_position.y,old_radial_position.z);
    
    for (iterate = 0;iterate < number_of_positions-1;iterate++) {
        rotate_angle = 2*3.14159*((double) iterate + 1.0)/((double) number_of_positions);
        rotate(radial_position.x,radial_position.y,radial_position.z,cross_product1[0],cross_product1[1],cross_product1[2],rotate_angle,vector.x,vector.y,vector.z);
        radial_position.x += center.x;
        radial_position.y += center.y;
        radial_position.z += center.z;
        
        // Use the draw_lines_with_highest_priority to draw get draw_orders for each line piece
        temp_draw_order = draw_line_with_highest_priority(radial_position,old_radial_position,N,Geometries,number_of_volumes,max_number_of_solutions);
       // Assemble these draw_orders to one large draw order
        merge_lines_to_draw(&return_draw_order,&temp_draw_order);

        old_radial_position.x = radial_position.x;
        old_radial_position.y = radial_position.y;
        old_radial_position.z = radial_position.z;
    }
    
    radial_position.x = center.x + cross_product1[0];
    radial_position.y = center.y + cross_product1[1];
    radial_position.z = center.z + cross_product1[2];
    // Use the draw_lines_with_highest_priority to draw get draw_orders for each line piece
    temp_draw_order = draw_line_with_highest_priority(radial_position,old_radial_position,N,Geometries,number_of_volumes,max_number_of_solutions);
    // Assemble these draw_orders to one large draw order
    merge_lines_to_draw(&return_draw_order,&temp_draw_order);
    
    // clean up
    if (temp_draw_order.number_of_lines > 0) free(temp_draw_order.lines);
    
    // return the large draw order.
    return return_draw_order;
    }

// -------------    Geometry functions   --------------------------------------------------------

#include "Geometry_functions.c"

// -------------    List generator functions   --------------------------------------------------


int within_which_volume(Coords pos, struct pointer_to_1d_int_list input_list, struct pointer_to_1d_int_list destinations_list, struct Volume_struct **Volumes, struct pointer_to_1d_int_list *mask_status_list, int number_of_volumes, int *volume_logic_copy, int *ListA, int *ListB) {
    // This function identifies in which of the volumes of the input list the position pos lies in.
    // pos: position for which the current volume should be found for
    // input list: list of potential volumes, reduced to the ones without parents (their children will be checked)
    // OLD VERSION: volume logic: A logic list of allowed volumes for lookup, volume 1 3 and 5 in a case of 10 volumes would be [0 1 0 1 0 1 0 0 0 0]
    // destinations_list: list of allowed destinations (the original destinations list)
    // Volumes: Main volumes array
    // volume_logic_copy: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    // ListA: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    // ListB: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    
    // Algorithm description
    // Check all of input list for pos being within them, those that have it within them are:
    //      checked against the current highest priority
    //          if higher, is the new highest priority and the new pick for volume
    //      have all their direct children added to the next list to be checked (but any volume can only be added to that list once)
    // Once a run have been made where the next list to check is empty, the answer for new pick for volume is taken.
    
    // Mask update:
    // Algorithm description
    // Check all of input list for pos being within them, those that have it within them are:
    //      checked against the current highest priority and mask status
    //          if higher, this is the new highest priority and the new pick for volume
    //      have all their direct children added to the next list to be checked (but any volume can only be added to that list once)
    // Once a run have been made where the next list to check is empty, the answer for new pick for volume is taken.
    
    // The advantage of the method is that potentially large numbers of children are skipped when their parents do not contain the position.
    // The overhead cost is low, as all the lists are prealocated.
    // Should be checked which of the two implementations is faster, as this is much more complicated than simply checking all possibilities.
    // No within_function call should be made twice, as the same volume number will not be checked twice because of the properties of the direct_children list and the volume_logic that removes duplicates on each level.
    
    
    // This function uses too much memory, the memory required for the volume logic list is n_volumes^2 ints, or for a MACS monochromator 127000 ints.
    // Instead the original destinations list must be used, and a function for quick lookup in a (sorted) destinations list made.
    // Still need a list of n_volumes length for control to avoid adding the same volume to the list twice.
    
    
    int ListA_length=0,ListB_length=0;
    int done = 0;
    int i,direct_children_index;
    int *temp_pointer;
    double max_priority=-1000000;
    int residing_volume=0; // 0 can be removed from the input list if default is 0
    int this_mask_status,mask_index,mask_global_index;
    
    // volume_logic_copy
    //for (i=0;i<volume_logic.num_elements;i++) volume_logic_copy[i] = volume_logic.elements[i];
    
    // low memory version of volume_logic_copy
    for (i=0;i<number_of_volumes;i++) volume_logic_copy[i] = 0;
    for (i=0;i<destinations_list.num_elements;i++) volume_logic_copy[destinations_list.elements[i]] = 1;
    printf("within_which_volume debug %i\n",input_list.num_elements);

    // Does one loop through the algorithm first to set up ListA instead of copying it from input_list, which takes time
    for (i=0;i<input_list.num_elements;i++) {
            if (Volumes[input_list.elements[i]]->geometry.within_function(pos,&Volumes[input_list.elements[i]]->geometry) == 1) {
                printf("The position is inside of volume %d\n",input_list.elements[i]);
                if (Volumes[input_list.elements[i]]->geometry.is_masked_volume == 1) {
                    // if the volume is masked, I need to know if it can be a destination volume from the mask_status_list.
                    // if the masked volume is in ANY mode,
                    this_mask_status=1;
                    //print_1d_int_list(*mask_status_list,"mask status list from within_which_volume");
                    for (mask_index=0;mask_index<Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.num_elements;mask_index++) {
                        //printf("Looking at the mask with global index %d \n",Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.elements[mask_index]);
                        if (mask_status_list->elements[Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.elements[mask_index]] == 1) {
                            if (Volumes[input_list.elements[i]]->geometry.mask_mode == 2) { // ANY (break if any one in)
                                this_mask_status=1;
                                break;
                            }
                            //printf("global index %d had mask status = 1 \n",Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.elements[mask_index]);
                        } else {
                          //printf("global index %d had mask status = 0 \n",Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.elements[mask_index]);
                          this_mask_status = 0;
                          if(Volumes[input_list.elements[i]]->geometry.mask_mode == 1) break; // ALL (break if any one out)
                        }
                    }
                    //printf("This volume is masked, and the mask status is %d\n",this_mask_status);
                } else this_mask_status = 1; // if the volume is not masked
            
                if (Volumes[input_list.elements[i]]->geometry.priority_value > max_priority && this_mask_status == 1) {
                    max_priority = Volumes[input_list.elements[i]]->geometry.priority_value;
                    residing_volume = input_list.elements[i];
                    //printf("residing volume set to %d\n",residing_volume);
                }
                    for (direct_children_index = 0;direct_children_index < Volumes[input_list.elements[i]]->geometry.direct_children.num_elements;direct_children_index++) {
                        if (volume_logic_copy[Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index]] == 1) {
                            ListA[ListA_length++] = Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index];
                            volume_logic_copy[Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index]] = 0;
                        }
                    }
            }
    }
    //printf("Completed first loop, continued in while loop\n");
    if (ListA_length > 0) {
        while (done == 0) {
            for (i=0;i<ListA_length;i++) {
              //printf("checking element number %d of list A which is volume number %d \n",i,ListA[i]);
                if (Volumes[ListA[i]]->geometry.within_function(pos,&Volumes[ListA[i]]->geometry) == 1) {
                  //printf("ray was inside this volume \n");
                    if (Volumes[ListA[i]]->geometry.is_masked_volume == 1) {
                      //printf("it is a mask and thus need check of mask status \n");
                        // if the volume is masked, I need to know if it can be a destination volume from the mask_status_list.
                        // if the masked volume is in ANY mode,
                        this_mask_status=1;
                        for (mask_index=0;mask_index<Volumes[ListA[i]]->geometry.masked_by_mask_index_list.num_elements;mask_index++) {
                            if (mask_status_list->elements[Volumes[ListA[i]]->geometry.masked_by_mask_index_list.elements[mask_index]] == 1) {
                                if (Volumes[ListA[i]]->geometry.mask_mode == 2) { // ANY (break if any one in)
                                    this_mask_status=1;
                                    break;
                                }
                            } else {
                              this_mask_status = 0;
                              if(Volumes[ListA[i]]->geometry.mask_mode == 1) break; // ALL (break if any one out)
                            }
                        }
                    } else this_mask_status = 1;
                    //printf("the mask status is %d \n",this_mask_status);
                    if (Volumes[ListA[i]]->geometry.priority_value > max_priority && this_mask_status == 1) {
                        max_priority = Volumes[ListA[i]]->geometry.priority_value;
                        residing_volume = ListA[i];
                    }
                    //printf("Adding direct children to list B \n");
                        for (direct_children_index = 0;direct_children_index < Volumes[ListA[i]]->geometry.direct_children.num_elements;direct_children_index++) {
                            //printf("Checking direct_child number %d which is %d \n",direct_children_index,Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index]);
                            if (volume_logic_copy[Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index]] == 1) {
                                //printf("It's volume_logic was 1, and it is thus added to listB with index %d \n",ListB_length);
                                ListB[ListB_length++] = Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index];
                                volume_logic_copy[Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index]] = 0;
                            }
                        }
                    //printf("List B is now: ");
                    //for (direct_children_index=0;direct_children_index<ListB_length;direct_children_index++) printf("%d ",ListB[direct_children_index]);
                    //printf("\n");
                    
                    
                }
            }
            if (ListB_length==0) done = 1;
            else {
                
                for (i=0;i<ListB_length;i++) ListA[i] = ListB[i];
                ListA_length = ListB_length;
                ListB_length = 0;
                
                /*
                // Could do this with pointers instead to avoid this for loop (and needless copy)
                // This code block fails on the cluster in rare circumstances
                ListA = temp_pointer;
                ListA = ListB;
                ListB = temp_pointer;
                ListA_length=ListB_length;
                ListB_length=0;
                */
            }
        }
    }
    //printf("residing volume returned %d\n",residing_volume);
    return residing_volume;
};

int within_which_volume_GPU(Coords pos, struct pointer_to_1d_int_list input_list, struct pointer_to_1d_int_list destinations_list, struct Volume_struct **Volumes, struct pointer_to_1d_int_list *mask_status_list, int number_of_volumes, int *volume_logic_copy, int *ListA, int *ListB) {
    // This function identifies in which of the volumes of the input list the position pos lies in.
    // pos: position for which the current volume should be found for
    // input list: list of potential volumes, reduced to the ones without parents (their children will be checked)
    // OLD VERSION: volume logic: A logic list of allowed volumes for lookup, volume 1 3 and 5 in a case of 10 volumes would be [0 1 0 1 0 1 0 0 0 0]
    // destinations_list: list of allowed destinations (the original destinations list)
    // Volumes: Main volumes array
    // volume_logic_copy: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    // ListA: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    // ListB: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    
    // Algorithm description
    // Check all of input list for pos being within them, those that have it within them are:
    //      checked against the current highest priority
    //          if higher, is the new highest priority and the new pick for volume
    //      have all their direct children added to the next list to be checked (but any volume can only be added to that list once)
    // Once a run have been made where the next list to check is empty, the answer for new pick for volume is taken.
    
    // Mask update:
    // Algorithm description
    // Check all of input list for pos being within them, those that have it within them are:
    //      checked against the current highest priority and mask status
    //          if higher, this is the new highest priority and the new pick for volume
    //      have all their direct children added to the next list to be checked (but any volume can only be added to that list once)
    // Once a run have been made where the next list to check is empty, the answer for new pick for volume is taken.
    
    // The advantage of the method is that potentially large numbers of children are skipped when their parents do not contain the position.
    // The overhead cost is low, as all the lists are prealocated.
    // Should be checked which of the two implementations is faster, as this is much more complicated than simply checking all possibilities.
    // No within_function call should be made twice, as the same volume number will not be checked twice because of the properties of the direct_children list and the volume_logic that removes duplicates on each level.
    
    
    // This function uses too much memory, the memory required for the volume logic list is n_volumes^2 ints, or for a MACS monochromator 127000 ints.
    // Instead the original destinations list must be used, and a function for quick lookup in a (sorted) destinations list made.
    // Still need a list of n_volumes length for control to avoid adding the same volume to the list twice.
    
    
    int ListA_length=0,ListB_length=0;
    int done = 0;
    int i,direct_children_index;
    int *temp_pointer;
    double max_priority=-1000000;
    int residing_volume=0; // 0 can be removed from the input list if default is 0
    int this_mask_status,mask_index,mask_global_index;
    
    // volume_logic_copy
    //for (i=0;i<volume_logic.num_elements;i++) volume_logic_copy[i] = volume_logic.elements[i];
    
    // low memory version of volume_logic_copy
    for (i=0;i<number_of_volumes;i++) volume_logic_copy[i] = 0;
    for (i=0;i<destinations_list.num_elements;i++) volume_logic_copy[destinations_list.elements[i]] = 1;
    //printf("within_which_volume debug\n");

    // Does one loop through the algorithm first to set up ListA instead of copying it from input_list, which takes time
    for (i=0;i<input_list.num_elements;i++) {
            if (r_within_function(pos, &Volumes[input_list.elements[i]]->geometry) == 1) {
                //printf("The position is inside of volume %d\n",input_list.elements[i]);
                if (Volumes[input_list.elements[i]]->geometry.is_masked_volume == 1) {
                    // if the volume is masked, I need to know if it can be a destination volume from the mask_status_list.
                    // if the masked volume is in ANY mode,
                    this_mask_status=1;
                    //print_1d_int_list(*mask_status_list,"mask status list from within_which_volume");
                    for (mask_index=0;mask_index<Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.num_elements;mask_index++) {
                        //printf("Looking at the mask with global index %d \n",Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.elements[mask_index]);
                        if (mask_status_list->elements[Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.elements[mask_index]] == 1) {
                            if (Volumes[input_list.elements[i]]->geometry.mask_mode == 2) { // ANY (break if any one in)
                                this_mask_status=1;
                                break;
                            }
                            //printf("global index %d had mask status = 1 \n",Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.elements[mask_index]);
                        } else {
                          //printf("global index %d had mask status = 0 \n",Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.elements[mask_index]);
                          this_mask_status = 0;
                          if(Volumes[input_list.elements[i]]->geometry.mask_mode == 1) break; // ALL (break if any one out)
                        }
                    }
                    //printf("This volume is masked, and the mask status is %d\n",this_mask_status);
                } else this_mask_status = 1; // if the volume is not masked
            
                if (Volumes[input_list.elements[i]]->geometry.priority_value > max_priority && this_mask_status == 1) {
                    max_priority = Volumes[input_list.elements[i]]->geometry.priority_value;
                    residing_volume = input_list.elements[i];
                    //printf("residing volume set to %d\n",residing_volume);
                }
                    for (direct_children_index = 0;direct_children_index < Volumes[input_list.elements[i]]->geometry.direct_children.num_elements;direct_children_index++) {
                        if (volume_logic_copy[Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index]] == 1) {
                            ListA[ListA_length++] = Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index];
                            volume_logic_copy[Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index]] = 0;
                        }
                    }
            }
    }
    //printf("Completed first loop, continued in while loop\n");
    if (ListA_length > 0) {
        while (done == 0) {
            for (i=0;i<ListA_length;i++) {
              //printf("checking element number %d of list A which is volume number %d \n",i,ListA[i]);
                if (r_within_function(pos,&Volumes[ListA[i]]->geometry) == 1) {
                  //printf("ray was inside this volume \n");
                    if (Volumes[ListA[i]]->geometry.is_masked_volume == 1) {
                      //printf("it is a mask and thus need check of mask status \n");
                        // if the volume is masked, I need to know if it can be a destination volume from the mask_status_list.
                        // if the masked volume is in ANY mode,
                        this_mask_status=1;
                        for (mask_index=0;mask_index<Volumes[ListA[i]]->geometry.masked_by_mask_index_list.num_elements;mask_index++) {
                            if (mask_status_list->elements[Volumes[ListA[i]]->geometry.masked_by_mask_index_list.elements[mask_index]] == 1) {
                                if (Volumes[ListA[i]]->geometry.mask_mode == 2) { // ANY (break if any one in)
                                    this_mask_status=1;
                                    break;
                                }
                            } else {
                              this_mask_status = 0;
                              if(Volumes[ListA[i]]->geometry.mask_mode == 1) break; // ALL (break if any one out)
                            }
                        }
                    } else this_mask_status = 1;
                    //printf("the mask status is %d \n",this_mask_status);
                    if (Volumes[ListA[i]]->geometry.priority_value > max_priority && this_mask_status == 1) {
                        max_priority = Volumes[ListA[i]]->geometry.priority_value;
                        residing_volume = ListA[i];
                    }
                    //printf("Adding direct children to list B \n");
                        for (direct_children_index = 0;direct_children_index < Volumes[ListA[i]]->geometry.direct_children.num_elements;direct_children_index++) {
                            //printf("Checking direct_child number %d which is %d \n",direct_children_index,Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index]);
                            if (volume_logic_copy[Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index]] == 1) {
                                //printf("It's volume_logic was 1, and it is thus added to listB with index %d \n",ListB_length);
                                ListB[ListB_length++] = Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index];
                                volume_logic_copy[Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index]] = 0;
                            }
                        }
                    //printf("List B is now: ");
                    //for (direct_children_index=0;direct_children_index<ListB_length;direct_children_index++) printf("%d ",ListB[direct_children_index]);
                    //printf("\n");
                    
                    
                }
            }
            if (ListB_length==0) done = 1;
            else {
                
                for (i=0;i<ListB_length;i++) ListA[i] = ListB[i];
                ListA_length = ListB_length;
                ListB_length = 0;
                
                /*
                // Could do this with pointers instead to avoid this for loop (and needless copy)
                // This code block fails on the cluster in rare circumstances
                ListA = temp_pointer;
                ListA = ListB;
                ListB = temp_pointer;
                ListA_length=ListB_length;
                ListB_length=0;
                */
            }
        }
    }
    //printf("residing volume returned %d\n",residing_volume);
    return residing_volume;
};




int within_which_volume_debug(Coords pos, struct pointer_to_1d_int_list input_list, struct pointer_to_1d_int_list volume_logic, struct Volume_struct **Volumes, int *volume_logic_copy, int *ListA, int *ListB) {
    // This function identifies in which of the volumes of the input list the position pos lies in.
    // pos: position for which the current volume should be found for
    // input list: list of potential volumes, reduced to the ones without parents (their children will be checked)
    // volume logic: A logic list of allowed volumes for lookup, volume 1 3 and 5 in a case of 10 volumes would be [0 1 0 1 0 1 0 0 0 0]
    // Volumes: Main volumes array
    // volume_logic_copy: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    // ListA: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    // ListB: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    
    // Algorithm description
    // Check all of input list for pos being within them, those that have it within them are:
    //      checked against the current highest priority
    //          if higher, is the new highest priority and the new pick for volume
    //      have all their direct children added to the next list to be checked (but any volume can only be added to that list once)
    // Once a run have been made where the next list to check is empty, the answer for new pick for volume is taken.
    
    // The advantage of the method is that potentially large numbers of children are skipped when their parents do not contain the position.
    // The overhead cost is low, as all the lists are prealocated.
    // Should be checked which of the two implementations is faster, as this is much more complicated than simply checking all possibilities.
    // No within_function call should be made twice, as the same volume number will not be checked twice because of the properties of the direct_children list and the volume_logic that removes duplicates on each level.
    
    
    int ListA_length=0,ListB_length=0;
    int done = 0;
    int i,direct_children_index;
    int *temp_pointer;
    double max_priority=-1000000;
    int residing_volume=0; // 0 can be removed from the input list if default is 0
    
    // volume_logic_copy
    for (i=0;i<volume_logic.num_elements;i++) volume_logic_copy[i] = volume_logic.elements[i];
    
    // Does one loop through the algorithm first to set up ListA instead of copying it from input_list, which takes time
    for (i=0;i<input_list.num_elements;i++) {
            if (Volumes[input_list.elements[i]]->geometry.within_function(pos,&Volumes[input_list.elements[i]]->geometry) == 1) {
                if (Volumes[input_list.elements[i]]->geometry.priority_value > max_priority) {
                    max_priority = Volumes[input_list.elements[i]]->geometry.priority_value;
                    residing_volume = input_list.elements[i];
                }
                    for (direct_children_index = 0;direct_children_index < Volumes[input_list.elements[i]]->geometry.direct_children.num_elements;direct_children_index++) {
                        if (volume_logic_copy[Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index]] == 1) {
                            ListA[ListA_length++] = Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index];
                            volume_logic_copy[Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index]] = 0;
                        }
                    }
            }
    }
    if (ListA_length > 0) {
        while (done == 0) {
        
            printf("ListA = [");
            for (i=0;i<ListA_length;i++) {
                printf("%d,",ListA[i]);
            }
            printf("]\n");
            for (i=0;i<ListA_length;i++) {
                if (Volumes[ListA[i]]->geometry.within_function(pos,&Volumes[ListA[i]]->geometry) == 1) {
                    if (Volumes[ListA[i]]->geometry.priority_value > max_priority) {
                        max_priority = Volumes[ListA[i]]->geometry.priority_value;
                        residing_volume = ListA[i];
                    }
                    //if (ListA[i]!=0) {
                        for (direct_children_index = 0;direct_children_index < Volumes[ListA[i]]->geometry.direct_children.num_elements;direct_children_index++) {
                            if (volume_logic_copy[Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index]] == 1) {
                                ListB[ListB_length++] = Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index];
                                volume_logic_copy[Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index]] = 0;
                            }
                        }
                    //}
                }
            }
            if (ListB_length==0) done = 1;
            else {
                ListA = temp_pointer;
                ListA = ListB;
                ListB = temp_pointer;
                ListA_length=ListB_length;
                ListB_length=0;
            }
        }
    }
    printf("Volume number %d had the highest priority of checked volumes\n",residing_volume);
    return residing_volume;
};

int inside_function(struct Volume_struct *parent_volume, struct Volume_struct *child_volume) {
    // Function that calls the correct within function depending on the shapes of the two volumes
    if (strcmp("sphere",parent_volume->geometry.shape) == 0 && strcmp("sphere",child_volume->geometry.shape) == 0) {
        if (sphere_within_sphere(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("cylinder",parent_volume->geometry.shape) == 0 && strcmp("cylinder",child_volume->geometry.shape) == 0) {
        if (cylinder_within_cylinder(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("box",parent_volume->geometry.shape) == 0 && strcmp("box",child_volume->geometry.shape) == 0) {
        if (box_within_box(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("cone",parent_volume->geometry.shape) == 0 && strcmp("cone",child_volume->geometry.shape) == 0) {
        if (cone_within_cone(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("mesh",parent_volume->geometry.shape) == 0 && strcmp("mesh",child_volume->geometry.shape) == 0) {
        if (mesh_within_mesh(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("box",parent_volume->geometry.shape) == 0 && strcmp("cylinder",child_volume->geometry.shape) == 0) {
        if (cylinder_within_box(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("cylinder",parent_volume->geometry.shape) == 0 && strcmp("box",child_volume->geometry.shape) == 0) {
        if (box_within_cylinder(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("box",parent_volume->geometry.shape) == 0 && strcmp("sphere",child_volume->geometry.shape) == 0) {
        if (sphere_within_box(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("sphere",parent_volume->geometry.shape) == 0 && strcmp("box",child_volume->geometry.shape) == 0) {
        if (box_within_sphere(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("sphere",parent_volume->geometry.shape) == 0 && strcmp("cylinder",child_volume->geometry.shape) == 0) {
        if (cylinder_within_sphere(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("cylinder",parent_volume->geometry.shape) == 0 && strcmp("sphere",child_volume->geometry.shape) == 0) {
        if (sphere_within_cylinder(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("cone",parent_volume->geometry.shape) == 0 && strcmp("sphere",child_volume->geometry.shape) == 0) {
        if (sphere_within_cone(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("sphere",parent_volume->geometry.shape) == 0 && strcmp("cone",child_volume->geometry.shape) == 0) {
        if (cone_within_sphere(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("cone",parent_volume->geometry.shape) == 0 && strcmp("cylinder",child_volume->geometry.shape) == 0) {
        if (cylinder_within_cone(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("cylinder",parent_volume->geometry.shape) == 0 && strcmp("cone",child_volume->geometry.shape) == 0) {
        if (cone_within_cylinder(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("cone",parent_volume->geometry.shape) == 0 && strcmp("box",child_volume->geometry.shape) == 0) {
        if (box_within_cone(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("box",parent_volume->geometry.shape) == 0 && strcmp("cone",child_volume->geometry.shape) == 0) {
        if (cone_within_box(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else {
        #ifndef OPENACC
        printf("Need within function for type: ");
        printf("%s",parent_volume->geometry.shape);
        printf(" and type: ");
        printf("%s",child_volume->geometry.shape);
        printf(".\n");
        printf("It is not yet supported to mix mesh geometries with the basic shapes, but several mesh geometries are allowed.\n");
        exit(1);
	#endif
    }
    
    return 0;
};

void generate_children_lists(struct Volume_struct **Volumes, struct pointer_to_1d_int_list **true_children_lists, int number_of_volumes, int verbal) {
  // This function generates a list of children for each volume.
  // A volume m is a child of volume n, if the entire space ocupied by volume m is inside of the space ocupied by volume n
  // A volume m is a true child of volume n, if the entire space coupied by volume m after it's masks are applied is inside the volume ocupied by volume n after it's masks are applied
  
  
  MPI_MASTER(
  if (verbal) printf("\nGenerating children lists --------------------------- \n");
  )

  // Mask update: Creating a temporary list for each volume
  struct pointer_to_1d_int_list *temporary_children_lists;
  temporary_children_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list));

  // The surrounding vacuum, volume 0, done outside of for loop.
  temporary_children_lists[0].num_elements = number_of_volumes - 1;
  temporary_children_lists[0].elements = malloc(temporary_children_lists[0].num_elements*sizeof(int));
  
  int parent;
  for (parent=1;parent<number_of_volumes;parent++) {
      temporary_children_lists[0].elements[parent-1] = parent;
  }
  
  //if (verbal) printf("did temporary children list \n");
  //print_1d_int_list(temporary_children_lists[0],"temp children list [0]");
  
  // Hardcoding that every volume is a child of the surrounding vacuum
  Volumes[0]->geometry.children.num_elements = number_of_volumes-1;
  Volumes[0]->geometry.children.elements = malloc((number_of_volumes-1)*sizeof(int));
  true_children_lists[0] = malloc(sizeof(struct pointer_to_1d_int_list));
  true_children_lists[0]->num_elements = number_of_volumes - 1;
  true_children_lists[0]->elements = malloc((number_of_volumes-1)*sizeof(int));
  
  //if (verbal) printf("allocated true children lists \n");
  
  for (parent=1;parent<number_of_volumes;parent++) {
      Volumes[0]->geometry.children.elements[parent-1] = parent;
      true_children_lists[0]->elements[parent-1] = parent;
  }
  
  char string_output[128];
  MPI_MASTER(
  if (verbal) sprintf(string_output,"Children for Volume %d",0);
  if (verbal) print_1d_int_list(Volumes[0]->geometry.children,string_output);
  )
  
  
  // Generating the children lists for all other volumes using the appropriate geometry functions
  struct pointer_to_1d_int_list temp_list_local;
  temp_list_local.num_elements = number_of_volumes;
  temp_list_local.elements = malloc(number_of_volumes*sizeof(int));
  
  struct pointer_to_1d_int_list true_temp_list_local;
  true_temp_list_local.num_elements = number_of_volumes;
  true_temp_list_local.elements = malloc(number_of_volumes*sizeof(int));
  
  
  int child,used_elements,used_elements_true;
  for (parent=1;parent<number_of_volumes;parent++) {
      used_elements = 0;used_elements_true = 0;
      for (child=1;child<number_of_volumes;child++) {
        if (child != parent) {
          // Call inside_function that selects the proper within function for the two volumes
          if (1 == inside_function(Volumes[parent],Volumes[child])) {
            temp_list_local.elements[used_elements++] = child;
            true_temp_list_local.elements[used_elements_true++] = child;
          }
        } else true_temp_list_local.elements[used_elements_true++] = child; // Needed when children list takes mask into account
      }
      // Temp test
      allocate_list_from_temp(used_elements,temp_list_local,&Volumes[parent]->geometry.children);
      // Assing the children list to a temporary list as the masks have yet to be taken into account
      temporary_children_lists[parent].num_elements=0;
      allocate_list_from_temp(used_elements_true,true_temp_list_local,&temporary_children_lists[parent]);
      
      
      MPI_MASTER(
      if (verbal) sprintf(string_output,"Children for Volume %d (temporary_list)",parent);
      if (verbal) print_1d_int_list(temporary_children_lists[parent],string_output);
      )
      
      MPI_MASTER(
      if (verbal) sprintf(string_output,"Children for Volume %d (permanent_list)",parent);
      if (verbal) print_1d_int_list(Volumes[parent]->geometry.children,string_output);
      )
      
  }
  
  // mask update:
  // The logical expression: (child c parent AND child c parent_mask) OR (child_mask c parent AND child_mask c parent_mask)
  //  needs to be evaluated for each child / parent combination in order to take the masks of each into account
  
  int logic_var1,logic_var2,logic_var_ANY,logic_var_ALL;
  int mask_index,mask_index_child,mask_index_parent;
  int volume_C,volume_P;
  // Loop that takes masks into account
  for (parent=1;parent<number_of_volumes;parent++) {
    used_elements = 0;
    for (child=1;child<number_of_volumes;child++) {
     if (child != parent && 0 == on_int_list(Volumes[parent]->geometry.masked_by_list,child)) {
        // The children list for each volume does not need to contain the volume itself
        //  And a parent masked by it's child can not have that mask as a child
      
      // Here c means within in the sense of a set being part of another set
      // Logical expression to be evaluated: (child c parent AND child c parent_mask) OR (child_mask c parent AND child_mask c parent_mask)
      logic_var1 = on_int_list(temporary_children_lists[parent],child);
      if (logic_var1 == 1 && Volumes[parent]->geometry.is_masked_volume == 1) {
        // if the parent volume is masked, the child also need to be inclosed in these masks to fulfill this side of the logical expression
        logic_var_ANY = 0;
        for (mask_index=0;mask_index<Volumes[parent]->geometry.masked_by_list.num_elements;mask_index++) {
          if (0 == on_int_list(temporary_children_lists[Volumes[parent]->geometry.masked_by_list.elements[mask_index]],child)) {
            if (Volumes[parent]->geometry.mask_mode == 1) {
              logic_var1 = 0;
              break;
            }
          } else logic_var_ANY = 1;
        }
        if (Volumes[parent]->geometry.mask_mode == 2) logic_var1 = logic_var_ANY;
      }
      
      if (logic_var1 == 1) true_temp_list_local.elements[used_elements++] = child;
      else if (Volumes[child]->geometry.is_masked_volume == 1) {
        // If the first side of the logical expression is false, evalute the other side
        // The other side is only relevant if the child volume is masked, otherwise it is ignored
        //printf("Second side of logical expression \n");
        
        logic_var1 = 1; // Assume true
          
        // child_mask c parent
        logic_var_ALL = 0;
        for (mask_index=0;mask_index<Volumes[child]->geometry.masked_by_list.num_elements;mask_index++) {
          if (0 == on_int_list(temporary_children_lists[parent],Volumes[child]->geometry.masked_by_list.elements[mask_index])){
            if (Volumes[child]->geometry.mask_mode == 2) {
              logic_var1 = 0;
              break;
            }
          } else logic_var_ALL = 1;
        }
        if (Volumes[child]->geometry.mask_mode == 1) logic_var1 = logic_var_ALL;
        
        // This line allows the second part of the logical expression to be true in cases where the parent volume is not masked
        if (logic_var1 == 1 && Volumes[parent]->geometry.is_masked_volume == 0) true_temp_list_local.elements[used_elements++] = child;
        
        // There is no reason to check the other part (child_mask c parent_mask) if the first part was not true
        if (logic_var1 == 1 && Volumes[parent]->geometry.is_masked_volume == 1) {
          // This last part requires both the child and the parent to be masked
          // Need to evaluate (child_mask c parent_mask), where both can be a be a list of volume with ALL/ANY modes
          
          if (Volumes[parent]->geometry.mask_mode == 1) {
            logic_var2 = 1; // Assume the logical expression (child_mask c parent_mask) is true
            for (mask_index_parent=0;mask_index_parent<Volumes[parent]->geometry.masked_by_list.num_elements;mask_index_parent++) {
              // As the parent is in ALL mode, the child masks must be within ALL parent masks
              logic_var_ANY = 0; // Assume not a single child is within this parent
              for (mask_index_child=0;mask_index_child<Volumes[child]->geometry.masked_by_list.num_elements;mask_index_child++) {
                volume_P = Volumes[parent]->geometry.masked_by_list.elements[mask_index_parent];
                volume_C = Volumes[child]->geometry.masked_by_list.elements[mask_index_child];
                // Is volume C inside volume P? If yes, volume C must be on volume P's temporary children list
                if (0 == on_int_list(temporary_children_lists[volume_P],volume_C)) {
                  if (Volumes[child]->geometry.mask_mode == 2) {
                    // If child is in ANY mode, any one mask outside is enough to make the expression false
                    logic_var2 = 0;
                    break;
                  }
                } else logic_var_ANY = 1;
              }
              // If child is in ALL mode, then if any one child were within this mask, the logic expression holds true
              if (Volumes[child]->geometry.mask_mode == 1) logic_var2 = logic_var_ANY;
              if (logic_var2 == 0) break; // No need to continue
            }
          } else if (Volumes[parent]->geometry.mask_mode == 2) {
            // If the parent is in ANY mode, it is enough if the child masks are within just 1 of the parent masks
            for (mask_index_parent=0;mask_index_parent<Volumes[parent]->geometry.masked_by_list.num_elements;mask_index_parent++) {
              logic_var2 = 1; // Assume the logical expression (child_mask c parent_mask) is true
              logic_var_ANY = 0; // Assume not a single child is within this parent
              for (mask_index_child=0;mask_index_child<Volumes[child]->geometry.masked_by_list.num_elements;mask_index_child++) {
                volume_P = Volumes[parent]->geometry.masked_by_list.elements[mask_index_parent];
                volume_C = Volumes[child]->geometry.masked_by_list.elements[mask_index_child];
                // Is volume C inside volume P? If yes, volume C must be on volume P's temporary children list
                if (0 == on_int_list(temporary_children_lists[volume_P],volume_C)) {
                  if (Volumes[child]->geometry.mask_mode == 2) {
                    // If child is in ANY mode, any one mask outside is enough to make the expression false
                    logic_var2 = 0;
                    break;
                  }
                } else logic_var_ANY = 1;
              }
              // If child is in ALL mode, then if any one child were within this mask, the logic expression holds true
              if (Volumes[child]->geometry.mask_mode == 1) logic_var2 = logic_var_ANY;
              if (logic_var2 == 1) break; // No need to continue
            }
          }
          
          // if this point is reached, and logic_var2 is true, volume[child] is a child of volume[parent]
          if (logic_var2 == 1) true_temp_list_local.elements[used_elements++] = child;
          
        }
      }
     }
    }
    
    true_children_lists[parent] = malloc(sizeof(struct pointer_to_1d_int_list));
    true_children_lists[parent]->num_elements = 0;
    allocate_list_from_temp(used_elements,true_temp_list_local,true_children_lists[parent]);
    
    MPI_MASTER(
      if (verbal) sprintf(string_output,"True children for Volume (post mask) %d",parent);
      if (verbal) print_1d_int_list(*true_children_lists[parent],string_output);
    )
  }
  
  
  // Clean up of dynamically allocated memory
  
  for(child=0;child<number_of_volumes;child++) free(temporary_children_lists[child].elements);
  free(temporary_children_lists);
  free(true_temp_list_local.elements);
  free(temp_list_local.elements);
};

void generate_overlap_lists(struct pointer_to_1d_int_list **true_overlap_lists, struct pointer_to_1d_int_list **raw_overlap_lists, struct Volume_struct **Volumes, int number_of_volumes, int verbal) {
  // This function generates overlap lists for each volume
  // Volume m overlaps volume n if there is some subset of space they both ocupy (this is called raw overlap)
  // the true overlap list takes mask into account, meaning that the masks are applied before searching for overlap

  MPI_MASTER(
  if (verbal) printf("\nGenerating overlap lists ---------------------------- \n");
  )
  
  // Manually create the overlap list for the surrounding Vacuum, as it overlaps all other volumes
  
  // temporary_overlap_lists are used to save the calculated overlaps to avoid evaluating the heavy functions more than once (twice) for each possible volume pair
  struct pointer_to_1d_int_list *temporary_overlap_lists;
  temporary_overlap_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list));
  
  // Overlap_lists are the final result of the function, and for the surrounding volume it can be set immediatly
  true_overlap_lists[0] = malloc(sizeof(struct pointer_to_1d_int_list));
  true_overlap_lists[0]->num_elements = number_of_volumes-1;
  true_overlap_lists[0]->elements = malloc((number_of_volumes-1)*sizeof(int));
  
  raw_overlap_lists[0] =  malloc(sizeof(struct pointer_to_1d_int_list));
  raw_overlap_lists[0]->num_elements = number_of_volumes;
  raw_overlap_lists[0]->elements = malloc(number_of_volumes*sizeof(int));
  raw_overlap_lists[0]->elements[0] = 0; // Volume 0 overlaps itself
  
  int parent;
  for (parent=1;parent<number_of_volumes;parent++) {
      true_overlap_lists[0]->elements[parent-1] = parent;
      raw_overlap_lists[0]->elements[parent] = parent;
  }
  
  char string_output[128];
  MPI_MASTER(
  if (verbal) sprintf(string_output,"Overlaps for Volume %d",0);
  if (verbal) print_1d_int_list(*true_overlap_lists[0],string_output);
  )
  
  // Generate the overlap lists for the remaining volumes
  struct pointer_to_1d_int_list temp_list_local;
  temp_list_local.num_elements = number_of_volumes;
  temp_list_local.elements = malloc(number_of_volumes*sizeof(int));
  
  int child,used_elements;
  // Create overlap for the remaining volumes
  for (parent=1;parent<number_of_volumes;parent++) {
      true_overlap_lists[parent] = malloc(sizeof(struct pointer_to_1d_int_list));
      used_elements = 0;
      temp_list_local.elements[used_elements++] = 0; // Alwasy overlaps with the surrounding vacuum.
      
      for (child=1;child<number_of_volumes;child++) {
        if (child == parent) temp_list_local.elements[used_elements++] = child;
        else {
        if (strcmp("sphere",Volumes[parent]->geometry.shape) == 0 && strcmp("sphere",Volumes[child]->geometry.shape) == 0) {
            if (sphere_overlaps_sphere(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("cylinder",Volumes[parent]->geometry.shape) == 0 && strcmp("cylinder",Volumes[child]->geometry.shape) == 0) {
            if (cylinder_overlaps_cylinder(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("box",Volumes[parent]->geometry.shape) == 0 && strcmp("box",Volumes[child]->geometry.shape) == 0) {
            if (box_overlaps_box(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("cone",Volumes[parent]->geometry.shape) == 0 && strcmp("cone",Volumes[child]->geometry.shape) == 0) {
            if (cone_overlaps_cone(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("mesh",Volumes[parent]->geometry.shape) == 0 && strcmp("mesh",Volumes[child]->geometry.shape) == 0) {
            if (mesh_overlaps_mesh(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("box",Volumes[parent]->geometry.shape) == 0 && strcmp("cylinder",Volumes[child]->geometry.shape) == 0) {
            if (box_overlaps_cylinder(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("cylinder",Volumes[parent]->geometry.shape) == 0 && strcmp("box",Volumes[child]->geometry.shape) == 0) {
            if (cylinder_overlaps_box(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("box",Volumes[parent]->geometry.shape) == 0 && strcmp("sphere",Volumes[child]->geometry.shape) == 0) {
            if (box_overlaps_sphere(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("sphere",Volumes[parent]->geometry.shape) == 0 && strcmp("box",Volumes[child]->geometry.shape) == 0) {
            if (sphere_overlaps_box(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("sphere",Volumes[parent]->geometry.shape) == 0 && strcmp("cylinder",Volumes[child]->geometry.shape) == 0) {
            if (sphere_overlaps_cylinder(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("cylinder",Volumes[parent]->geometry.shape) == 0 && strcmp("sphere",Volumes[child]->geometry.shape) == 0) {
            if (cylinder_overlaps_sphere(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("cone",Volumes[parent]->geometry.shape) == 0 && strcmp("sphere",Volumes[child]->geometry.shape) == 0) {
            if (cone_overlaps_sphere(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("sphere",Volumes[parent]->geometry.shape) == 0 && strcmp("cone",Volumes[child]->geometry.shape) == 0) {
            if (sphere_overlaps_cone(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("cone",Volumes[parent]->geometry.shape) == 0 && strcmp("cylinder",Volumes[child]->geometry.shape) == 0) {
            if (cone_overlaps_cylinder(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("cylinder",Volumes[parent]->geometry.shape) == 0 && strcmp("cone",Volumes[child]->geometry.shape) == 0) {
            if (cylinder_overlaps_cone(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("cone",Volumes[parent]->geometry.shape) == 0 && strcmp("box",Volumes[child]->geometry.shape) == 0) {
            if (cone_overlaps_box(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("box",Volumes[parent]->geometry.shape) == 0 && strcmp("cone",Volumes[child]->geometry.shape) == 0) {
            if (box_overlaps_cone(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else {
            printf("Need overlap function for type: ");
            printf("%s",Volumes[parent]->geometry.shape);
            printf(" and type: ");
            printf("%s",Volumes[child]->geometry.shape);
            printf(".\n");
            printf("It is not yet supported to mix mesh geometries with the basic shapes, but several mesh geometries are allowed.\n");
            exit(1);
        }
        }
      }
      //allocate_list_from_temp(used_elements,temp_list_local,overlap_lists[parent]);
      allocate_list_from_temp(used_elements,temp_list_local,&temporary_overlap_lists[parent]);
      
      // Save the raw overlap data to the raw_overlap_lists[parent] list
      raw_overlap_lists[parent] = malloc(sizeof(struct pointer_to_1d_int_list));
      raw_overlap_lists[parent]->num_elements = 0;
      allocate_list_from_temp(used_elements,temp_list_local,raw_overlap_lists[parent]);
      
      if (verbal) sprintf(string_output,"Overlaps for Volume (pre mask) %d",parent);
      MPI_MASTER(
      if (verbal) print_1d_int_list(temporary_overlap_lists[parent],string_output);
      )
  }
  
  
  // The temporary_overlap_lists gives the raw overlap data for all volume pairs
  // The next tasks is to take the masks into account, so that a volume is only said to overlap another if all of these statements are true:
  // The volumes overlap each other
  // The mask's of volume 1 overlap volume 2
  // The mask's of volume 2 overlap volume 1
  // The mask's of volume 1 overlap the masks of volume 2
  
  
  int logic_var;
  int overlap_ANY,overlap_ANY_p,overlap_ANY_c;
  int mask_index,mask_index_c,mask_index_p;
  int mask_volume_index,mask_volume_index_p,mask_volume_index_c;

  for (parent=1;parent<number_of_volumes;parent++) {
    used_elements = 0;
    temp_list_local.elements[used_elements++] = 0; // Alwasy overlaps with the surrounding vacuum.
    for (child=1;child<number_of_volumes;child++) {
      if (child != parent) {
        logic_var = 1; // Assume the volumes overlap, and search for evidence that they do not.
      
        // First check if the volumes overlap
        if (0 == on_int_list(temporary_overlap_lists[parent],child)) logic_var = 0;
        
      
        // Check if child overlap with parents masks
        if (logic_var == 1 && Volumes[parent]->geometry.is_masked_volume == 1) {
          overlap_ANY = 0;
          for (mask_index=0;mask_index<Volumes[parent]->geometry.masked_by_list.num_elements;mask_index++) {
            mask_volume_index = Volumes[parent]->geometry.masked_by_list.elements[mask_index];
            if (0 == on_int_list(temporary_overlap_lists[mask_volume_index],child)) {
              if (Volumes[parent]->geometry.mask_mode == 1) {
                logic_var = 0;
                break;
              }
            } else overlap_ANY = 1;
          }
          if (Volumes[parent]->geometry.mask_mode == 2) logic_var = overlap_ANY;
        }
      
        // Check if parent overlap with childs masks
        if (logic_var == 1 && Volumes[child]->geometry.is_masked_volume == 1) {
          overlap_ANY = 0;
          for (mask_index=0;mask_index<Volumes[child]->geometry.masked_by_list.num_elements;mask_index++) {
            mask_volume_index = Volumes[child]->geometry.masked_by_list.elements[mask_index];
            if (0 == on_int_list(temporary_overlap_lists[mask_volume_index],parent)) {
              if (Volumes[child]->geometry.mask_mode == 1) {
                logic_var = 0;
                break;
              }
            } else overlap_ANY = 1;
          }
          if (Volumes[child]->geometry.mask_mode == 2) logic_var = overlap_ANY;
        }
      
        // Check if parents masks overlap childrens masks
        if (logic_var == 1 && Volumes[parent]->geometry.is_masked_volume == 1 && Volumes[child]->geometry.is_masked_volume == 1) {
          overlap_ANY = 0;
          for (mask_index_p=0;mask_index_p<Volumes[parent]->geometry.masked_by_list.num_elements;mask_index_p++) {
            mask_volume_index_p = Volumes[parent]->geometry.masked_by_list.elements[mask_index_p];
            overlap_ANY_p = 1;
            overlap_ANY_c = 0;
            for (mask_index_c=0;mask_index_c<Volumes[child]->geometry.masked_by_list.num_elements;mask_index_c++) {
              mask_volume_index_c = Volumes[child]->geometry.masked_by_list.elements[mask_index_c];
              if (0 == on_int_list(temporary_overlap_lists[mask_volume_index_p],mask_volume_index_c)) {
                if (Volumes[parent]->geometry.mask_mode == 1 && Volumes[child]->geometry.mask_mode == 1) {
                  // If both are in ALL mode and just one combination of masks does not overlap, neither does the common set
                  logic_var = 0;
                  break;
                }
                if (Volumes[parent]->geometry.mask_mode == 2 && Volumes[child]->geometry.mask_mode == 1) {
                  // If the parent is in ANY mode, but the child is in ALL, any one child not overlapping this parent mask, stops the chance for this parent mask
                  overlap_ANY_p = 0;
                  break;
                }
                
              } else {
                // Here because mask_volume_index_p and mask_volume_index_c does overlap
                if (Volumes[parent]->geometry.mask_mode == 1 && Volumes[child]->geometry.mask_mode == 2) {
                  // If the parent is in ALL mode and the child is in ANY mode, stop if a single parent volume does not overlap any child
                  overlap_ANY_c = 1;
                }
                if (Volumes[parent]->geometry.mask_mode == 2 && Volumes[child]->geometry.mask_mode == 2) {
                  // If both parent and child are in any mode, any one overlap between the masks is sufficient
                  overlap_ANY = 1;
                  // Could actually just commit to the overlap list here, and stop all loops.
                }
              }
              
            }
            if (Volumes[parent]->geometry.mask_mode == 1 && Volumes[child]->geometry.mask_mode == 2) logic_var = overlap_ANY_c;
            if (Volumes[parent]->geometry.mask_mode == 2 && Volumes[child]->geometry.mask_mode == 1 && overlap_ANY_p == 1) {
              // When parent is in any mode, and child is in ALL mode, any parent mask that overlaps all children masks is enough to end the parent loop
              logic_var = 1;
              break; // Without this break, only the last parent will matter
            }
            // if (overlap_ANY == 1) break; would speed things up a bit, but only after testing has been started and then it will be repeated
          }
          if (Volumes[parent]->geometry.mask_mode == 2 && Volumes[child]->geometry.mask_mode == 2) logic_var = overlap_ANY;
          // If both volumes have the ANY mode, just one case of overlap is enough.
        }
        
        // If all of the 4 statements above evaluate to true, the two volumes parent and child do overlap and it is added to the list.
        if (logic_var == 1) temp_list_local.elements[used_elements++] = child;
        
      }
    }
    // Allocate the actual overlap list with the new temp_list_local
    allocate_list_from_temp(used_elements,temp_list_local,true_overlap_lists[parent]);
    if (verbal) sprintf(string_output,"Overlaps for Volume (post mask) %d",parent);
    MPI_MASTER(
    if (verbal) print_1d_int_list(*true_overlap_lists[parent],string_output);
    )
  }
  
  // Clean up of dynamically allocated memory
  for(child=1;child<number_of_volumes;child++) free(temporary_overlap_lists[child].elements);
  free(temporary_overlap_lists);
  free(temp_list_local.elements);
  
};

void add_to_mask_intersect_list(struct pointer_to_1d_int_list *mask_intersect_list, int given_volume_index) {
    // A bit simpler than before
    if (on_int_list(*mask_intersect_list,given_volume_index) == 0)
      add_element_to_int_list(mask_intersect_list,given_volume_index);
}


void generate_intersect_check_lists(struct pointer_to_1d_int_list **overlap_lists,struct Volume_struct **Volumes, int number_of_volumes, int verbal) {
  // Generate intersection list for each volume
  MPI_MASTER(
  if (verbal) printf("\nGenerating intersect check lists -------------------- \n");
  )
  // Take overlap list for volume n
  // Remove entries with p < p(n)
  // Remove entries with parents on the list
  
  // Mask update: Mask volumes does not have priorities, do not do step 2 for mask volumes
  // Mask update: Instead of step 3 (Remove entries with parents on the list), move these entries to a Mask intersect list if that parent is masking the entry, otherwise remove
  
  // 1) Mask update: Take overlap list for volume n
  // 2) Mask update: Remove entries with p < p(n), except mask volumes, that are moved to another list A
  // 3) Mask update: Entries with parents on the list are removed (if these parents are masked, only remove the entry if it is a child of their parents masks)
  // 4) Possible optimization: Entries with parents on the A list, are moved to the appropriate mask list for this volume
  // 5) Mask update: Entries on list A are added again, if they are not parents of volume n
  // 6) Mask update: Entries on the main list that are masked are moved to the appropriate mask list for this volume, if their mask is on list A
  
  // Justification of the top algorithm:
  // No need to check intersections with something that does not overlap this volume, so start with intersect list and remove from that.
  // Entries with p < p(n) are "beneath" this volume, and thus doesn't cut it, mask volumes are however always "above" and are treated with care (moved to list A)
  // Entries with parents still on the list is removed, as it is impossible to intersect with them without first going through the parent
  // Entries with parents on the A list can only be seen if the ray is in that particular mask (the parent on the A list), so it can be added to the appropriate mask list for this volume
  // Now all masks that cut the volume are transfered back to the main list, the only masks that overlap and does not cut are parents of this volume, and these are thus not added
  // The entries on the list that are masked are added to the appropriate mask lists of this volume, but only if their mask is on the A list, as only masks on list A can have mask status 1 which is required for this to be relevant (this was true automatically for the ones added to the appropriate lists in the optimization step).
  
  
  // The intersect check list for volume 0 is done manually
  // Declare list for holding logic data for each index in the overlap list of this volume
  struct pointer_to_1d_int_list logic_list;
  logic_list.num_elements = overlap_lists[0]->num_elements;
  logic_list.elements = malloc(logic_list.num_elements * sizeof(int));
  
  // Declare similar list for List A
  struct pointer_to_1d_int_list mask_logic_list;
  mask_logic_list.num_elements = overlap_lists[0]->num_elements;
  mask_logic_list.elements = malloc(mask_logic_list.num_elements * sizeof(int));
  
  
  int iterate;
  int overlap_index;
  
  /*
  // Thise code is an old broken version of intersection_check_list generation only for volume 0, but this case is now included in the loop.
  // Assume all volumes on overlap list should be on intersection check list, and remove the once that should not.
  for (iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 1;
  // Asuume no volumes should go on the mask_logic_list, but add the ones that do.
  for (iterate=0;iterate<mask_logic_list.num_elements;iterate++) mask_logic_list.elements[iterate] = 0;
  
  for (overlap_index = 0;overlap_index < overlap_lists[0]->num_elements;overlap_index++) {
    // No volumes to remove with lower priority, as surrounding vacuum have the lowest.
    
    // Check if this overlap_lists[0]->elements[overlap_index] is a child of another member of the overlap list
    for (iterate = 0;iterate < overlap_lists[0]->num_elements;iterate++) {
        if (iterate != overlap_index) {
            // We are now checking if Volumes[overlap_lists[0]->elements[iterate]]->geometry.children contains overlap_lists[overlap_index]
            if (on_int_list(Volumes[overlap_lists[0]->elements[iterate]]->geometry.children,overlap_lists[0]->elements[overlap_index])) logic_list.elements[overlap_index] = 0;
        }
    }
  }

  Volumes[0]->geometry.intersect_check_list.num_elements = sum_int_list(logic_list);
  Volumes[0]->geometry.intersect_check_list.elements = malloc(Volumes[0]->geometry.intersect_check_list.num_elements * sizeof(int));
  
  iterate = 0;
  for (overlap_index = 0;overlap_index < overlap_lists[0]->num_elements;overlap_index++) {
        if (logic_list.elements[overlap_index])   Volumes[0]->geometry.intersect_check_list.elements[iterate++] = overlap_lists[0]->elements[overlap_index];
  }
  if (logic_list.num_elements > 0) free(logic_list.elements);
  if (mask_logic_list.num_elements > 0) free(mask_logic_list.elements);
  
  MPI_MASTER(
  print_1d_int_list(Volumes[0]->geometry.intersect_check_list,"Intersect check list for Volume 0 (manual)");
  )
  */
  
  // The intersect check lists for the remaining volumes are generated
  int volume_index,mask_volume_number,mask_index;
  //for (volume_index = 1;volume_index < number_of_volumes;volume_index++) {
  for (volume_index = 0;volume_index < number_of_volumes;volume_index++) {
    
      // 1) Take overlap list for volume n
      logic_list.num_elements = overlap_lists[volume_index]->num_elements;
      logic_list.elements = malloc(logic_list.num_elements * sizeof(int));
      mask_logic_list.num_elements = overlap_lists[volume_index]->num_elements;
      mask_logic_list.elements = malloc(mask_logic_list.num_elements * sizeof(int));
      
      
      
      if (Volumes[volume_index]->geometry.is_mask_volume == 1) {
        // Masks do not have any entries on their intersect check list, as they are never the current volume
        for (iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 0;
        for (iterate=0;iterate<mask_logic_list.num_elements;iterate++) mask_logic_list.elements[iterate] = 0;
      } else {
        for (iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 1;
        for (iterate=0;iterate<mask_logic_list.num_elements;iterate++) mask_logic_list.elements[iterate] = 0;
      }
      
      //2) Remove entries with p < p(n), except mask volumes, that are moved to another list A
      for (overlap_index = 0;overlap_index < overlap_lists[volume_index]->num_elements;overlap_index++) {
        
        if (overlap_lists[volume_index]->elements[overlap_index] == 0) logic_list.elements[overlap_index] = 0; // The surrounding vacuum has lower priority (and is not a mask)
        else if (Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.is_mask_volume == 1) {
           logic_list.elements[overlap_index] = 0;      // Remove this volume from the main list
           mask_logic_list.elements[overlap_index] = 1; // Add mask volumes to seperat list
        }
        else if (volume_index != 0) { // Only relevant to remove elements because of priority if the volume_index is different from 0, meaning the surrounding vacuum skips this step
           if (Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.priority_value < Volumes[volume_index]->geometry.priority_value) {
           // If the investigated volume on the overlap_list have lower priority than the volume with volume_index, remove it
           logic_list.elements[overlap_index] = 0; // Remove this volume from the list
           }
        }
      }
      
      // 3) Entries with parents on the list are removed
      for (overlap_index = 0;overlap_index < overlap_lists[volume_index]->num_elements;overlap_index++) {
        // Check if this overlap_lists[0]->elements[overlap_index] is a child of another member of the overlap list
        for (iterate = 0;iterate < overlap_lists[volume_index]->num_elements;iterate++) {
             if (iterate != overlap_index) { // Only necessary if a volume determines that it has itself as child, but a nice safety.
                // We are now checking if Volumes[overlap_lists[volume_index]->elements[iterate]]->geometry.children contains overlap_lists[overlap_index]
                // The && part is needed because we do not remove children of elements that have allready been removed because of their priority
                
                // if (on_int_list(Volumes[overlap_lists[volume_index]->elements[iterate]]->geometry.children,overlap_lists[volume_index]->elements[overlap_index]) && logic_list.elements[overlap_lists[volume_index]->elements[iterate]] == 1) {logic_list.elements[overlap_index] = 0; bug fixed on 3/4 2016
                if (on_int_list(Volumes[overlap_lists[volume_index]->elements[iterate]]->geometry.children,overlap_lists[volume_index]->elements[overlap_index]) && logic_list.elements[iterate] == 1) logic_list.elements[overlap_index] = 0;
                
                /*
                Explanation with simpler notation
                
                Overlap list of volume i = O_i
                Element j of overlap list i = O_i(j)
                
                Children list of volume i = C_i
                Element j on children list i = C_i(j)
                
                Priority of volume i p(i)
                
                for i = volumes
                    for j in O_i(j) 
                        logic(j) = 1;
                        if p(i) > p(O_i(j)) logic(j) = 0; // Remove if lower priority than the currently checked
                        
                        for k in O_i(k)
                            if (O_i(j) is contained on the list C_k && logic(k) == 1) logic(j) = 0
                */
                
                // 4) Entries with parents on the A list, are moved to the appropriate mask list for this volume
                if (on_int_list(Volumes[overlap_lists[volume_index]->elements[iterate]]->geometry.children,overlap_lists[volume_index]->elements[overlap_index]) && mask_logic_list.elements[iterate] == 1) {
                   logic_list.elements[overlap_index] = 0; // Remove from main list (Not strictly needed as it will be removed already if it is on list A)
                   // Add overlap_lists[volume_index]->elements[overlap_index] to volumes mask intersect list for mask with index overlap_lists[volume_index]->elements[iterate]
                   //add_to_mask_intersect_lists(&Volumes[volume_index]->geometry.mask_intersect_lists,overlap_lists[volume_index]->elements[iterate],overlap_lists[volume_index]->elements[overlap_index]);
                   // Simpler method that just collects all the masked intersect parts on a 1d_int list instead of seperating them for each mask
                   add_to_mask_intersect_list(&Volumes[volume_index]->geometry.mask_intersect_list,overlap_lists[volume_index]->elements[overlap_index]);
                }
                
             }
        }
      }
      
      // 5) Entries on list A are added again, if they are not parents of volume n
      // Time to add mask volumes removed back to the intersect list, if they are not parents of the current volume, meaning the current volume should not be a child of the mask
      for (overlap_index = 0;overlap_index < overlap_lists[volume_index]->num_elements;overlap_index++) {
        if (mask_logic_list.elements[overlap_index] == 1) {
          mask_volume_number = overlap_lists[volume_index]->elements[overlap_index];
          if (on_int_list(Volumes[mask_volume_number]->geometry.children,volume_index) == 0) logic_list.elements[overlap_index] = 1; // Add it back to the list
        }
      }
      
      // 6) Entries on the main list that are masked are moved to the appropriate mask list for this volume, if their mask is on list A
      int mask_index,mask_mode,found_index,logic_ALL;
      for (overlap_index = 0;overlap_index < overlap_lists[volume_index]->num_elements;overlap_index++) {
        if (logic_list.elements[overlap_index] == 1 && Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.is_masked_volume == 1) {
          logic_list.elements[overlap_index] = 0; // If the volume is masked, remove it from the intersect check list
          // Could actually keep it on the intersect list if the volume's mask is a parent of the current volume, now it will just be added in all cases
          
          // When ALL setting is used, all masks should overlap the current volume, otherwise the user probably made a mistake, this should be checked in error checking
          // When ANY setting is used, at least one mask should overlap the current volume, otherwise the user probably made a mistake, this should be checked in error checking
          // mask_mode == 1 => ALL / mask_mode == 2 => ANY
          mask_mode = Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.mask_mode;
          
          if (mask_mode == 1) {
            logic_ALL = 1;
            for (iterate=0;iterate<Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.masked_by_list.num_elements;iterate++) {
              mask_index = Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.masked_by_list.elements[iterate];
              
              if (on_int_list(*overlap_lists[volume_index],mask_index) == 0) {
                // If any one of the volumes masks do not overlap with this volume, there is no reason check intersections with the volume regardless of the mask status's
                logic_ALL = 0;
                break;
              }
            }
          if (logic_ALL == 1) {
            // If all of it's masks are overlapping the current volume, add it to all relevant mask intersect lists of this volume
            for (iterate=0;iterate<Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.masked_by_list.num_elements;iterate++) {
              mask_index = Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.masked_by_list.elements[iterate];
              //add_to_mask_intersect_lists(&Volumes[volume_index]->geometry.mask_intersect_lists,mask_index,overlap_lists[volume_index]->elements[overlap_index]);
              // Adding the masked intersect list elements to another list
              add_to_mask_intersect_list(&Volumes[volume_index]->geometry.mask_intersect_list,overlap_lists[volume_index]->elements[overlap_index]);
            }
          }
            
            
          } else if (mask_mode == 2) {
          // When in ANY mode, the problem is easier, as not all masks have to overlap the volume, and we can add each one to the list
            for (iterate=0;iterate<Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.masked_by_list.num_elements;iterate++) {
              mask_index = Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.masked_by_list.elements[iterate];
            
              if (on_int_list(*overlap_lists[volume_index],mask_index) == 1) {
                //add_to_mask_intersect_lists(&Volumes[volume_index]->geometry.mask_intersect_lists,mask_index,overlap_lists[volume_index]->elements[overlap_index]);
                // Adding the masked intersect list elements to another list
                add_to_mask_intersect_list(&Volumes[volume_index]->geometry.mask_intersect_list,overlap_lists[volume_index]->elements[overlap_index]);
              }
            }
            
          }
        }
      }
      
      
      Volumes[volume_index]->geometry.intersect_check_list.num_elements = sum_int_list(logic_list);
      Volumes[volume_index]->geometry.intersect_check_list.elements = malloc(Volumes[volume_index]->geometry.intersect_check_list.num_elements * sizeof(int));
      
      iterate = 0;
      for (overlap_index = 0;overlap_index < overlap_lists[volume_index]->num_elements;overlap_index++) {
            if (logic_list.elements[overlap_index])   Volumes[volume_index]->geometry.intersect_check_list.elements[iterate++] = overlap_lists[volume_index]->elements[overlap_index];
      }
      free(logic_list.elements); // Need to be careful with names for variables to be freed, as they can collide with names in main because of automatic declaration of external variables
      free(mask_logic_list.elements);
      
      
      char string_output[128];
      MPI_MASTER(
      if (verbal) sprintf(string_output,"Intersect check list for Volume %d",volume_index);
      if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.intersect_check_list,string_output);
      if (verbal) sprintf(string_output,"Mask intersect check list for Volume %d",volume_index);
      if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.mask_intersect_list,string_output);
      /*
      if (verbal) {
        for (mask_index=0;mask_index<Volumes[volume_index]->geometry.mask_intersect_lists.number_of_lists;mask_index++) {
          sprintf(string_output," - mask intersect check list for mask %d which is volume %d",mask_index,Volumes[volume_index]->geometry.mask_intersect_lists.mask_indices.elements[mask_index]);
          print_1d_int_list(Volumes[volume_index]->geometry.mask_intersect_lists.lists[mask_index],string_output);
        }
      }
      */
      
      )
  }
};

void generate_parents_lists(struct pointer_to_1d_int_list **parents_lists, struct Volume_struct **Volumes, int number_of_volumes, int verbal, int mask_mode) {
  // Function for generating parent lists for all volumes
  // A volume m has n as a parent, if volume n has volume m as a child
  
  // if mask_mode == 0, masks are ignored, if mask_mode == 1, masks are taken into account.
  
  MPI_MASTER(
  if (verbal) {
    if (mask_mode == 1)
        printf("\nGenerating parents lists ---------------------------- \n");
    else if (mask_mode == 0)
        printf("\nGenerating parents lists (ignoring masks) ----------- \n");
    else {
        printf("Error, the function parents_lists got a non defined mask_mode");
        exit(1);
    }
  }
  
  )
  // Volume iterate has volume p as a parent, if volume p has volume iterate as child.
  
  struct pointer_to_1d_int_list temp_list_local;
  temp_list_local.num_elements = number_of_volumes;
  temp_list_local.elements = malloc(number_of_volumes*sizeof(int));
  
  // Loop over
  int iterate,parent,used_elements;
  for (iterate = 0;iterate < number_of_volumes;iterate++) {
    // clear temp list
    used_elements = 0;
    parents_lists[iterate] = malloc(sizeof(struct pointer_to_1d_int_list));
    for (parent = 0;parent < number_of_volumes;parent++) {
        if (on_int_list(Volumes[parent]->geometry.children,iterate))
          if (mask_mode == 1 || (Volumes[parent]->geometry.is_mask_volume == 0 && Volumes[iterate]->geometry.is_mask_volume == 0))
            temp_list_local.elements[used_elements++] = parent;
    }
      allocate_list_from_temp(used_elements,temp_list_local,parents_lists[iterate]);
      
      char string_output[128];
      MPI_MASTER(
      if (verbal) sprintf(string_output,"Parents for Volume %d",iterate);
      if (verbal) print_1d_int_list(*parents_lists[iterate],string_output);
      )
  }
  free(temp_list_local.elements);

};

void generate_true_parents_lists(struct pointer_to_1d_int_list **parents_lists, struct pointer_to_1d_int_list **true_children_lists, struct Volume_struct **Volumes, int number_of_volumes, int verbal, int mask_mode) {
  // Function for generating parent lists for all volumes
  // A volume m has n as a parent, if volume n has volume m as a child
  
  // if mask_mode == 0, masks are ignored, if mask_mode == 1, masks are taken into account.
  
  MPI_MASTER(
  if (verbal) {
    if (mask_mode == 1)
        printf("\nGenerating parents lists ---------------------------- \n");
    else if (mask_mode == 0)
        printf("\nGenerating parents lists (ignoring masks) ----------- \n");
    else {
        printf("Error, the function parents_lists got a non defined mask_mode");
        exit(1);
    }
  }
  
  )
  // Volume iterate has volume p as a parent, if volume p has volume iterate as child.
  
  struct pointer_to_1d_int_list temp_list_local;
  temp_list_local.num_elements = number_of_volumes;
  temp_list_local.elements = malloc(number_of_volumes*sizeof(int));
  
  // Loop over
  int iterate,parent,used_elements;
  for (iterate = 0;iterate < number_of_volumes;iterate++) {
    // clear temp list
    used_elements = 0;
    parents_lists[iterate] = malloc(sizeof(struct pointer_to_1d_int_list)); // allocate_list_from_temp allocates
    for (parent = 0;parent < number_of_volumes;parent++) {
        //if (on_int_list(Volumes[parent]->geometry.children,iterate))
        if (on_int_list(*true_children_lists[parent],iterate))
          if (mask_mode == 1 || (Volumes[parent]->geometry.is_mask_volume == 0 && Volumes[iterate]->geometry.is_mask_volume == 0))
            temp_list_local.elements[used_elements++] = parent;
    }
      allocate_list_from_temp(used_elements,temp_list_local,parents_lists[iterate]);
      
      char string_output[128];
      MPI_MASTER(
      if (verbal) sprintf(string_output,"Parents for Volume %d",iterate);
      if (verbal) print_1d_int_list(*parents_lists[iterate],string_output);
      )
  }
  free(temp_list_local.elements);

};

void generate_intersect_check_lists_experimental(struct pointer_to_1d_int_list **true_overlap_lists, struct pointer_to_1d_int_list **raw_overlap_lists, struct pointer_to_1d_int_list **parents_lists, struct pointer_to_1d_int_list **true_parents_lists , struct Volume_struct **Volumes, int number_of_volumes, int verbal) {
  // Generates the intersect_check_list and mask_intersect_list for each Volume.
  /*
  Description of needed lists for volume n:
  Children list: List of volumes that is contained within volume n (is stored in the Volumes struct)
  True Children list: List of volumes that when masked by their masks is contained within volume n when masked by it's masks
  
  Parents list: List of volumes that contains volume n
  True parents list: List of volumes that when masked by their masks contains volume n when it is masked by it's masks
  
  raw overlap list: List of volumes whos geometry overlaps the geometry of volume n
  true overlap list: List of volumes whos geometry overlaps the geometry of volume n when the masks of both volumes are applied
  
  
  
  The algorithm:
  
  1) Take the true overlap list for volume n
  2) remove parents of n (normal parent list, with masks)
  3) remove volumes that do not mask n and are true parents of n
  4) remove volumes that are not masks and have lower priority than n
  5) remove volumes that have at least one true parent on the list (in step 4) that is not a mask volume
  6) split the list into two, the intersect_check_list which is all non masked volumes still on the list, and the mask_intersect_list which is the masked volumes
  7) remove volumes on the mask_intersect_list whos mask does not overlap (standard overlap list) n
  
  In step 5 the order in which the volumes are tested and removed may matter, so it is specifically stated that it is as the list looked in step 4.
  */
  
  MPI_MASTER(
  if (verbal) printf("\nGenerating intersect check lists -------------------- \n");
  )
  
  struct pointer_to_1d_int_list work_list;
  struct pointer_to_1d_int_list logic_list;
  
  int volume_index,iterate,parent,mask_index,masked_volume_index,ANY_logic,true_parent_volume_number;
  int *mask_check,*mask_start;
  for (volume_index = 0;volume_index < number_of_volumes;volume_index++) {
    
    // 1) Take the true overlap list for volume n
    // Create copy of true_overlap_lists to work with
    
    if (Volumes[volume_index]->geometry.is_mask_volume == 1) {
      // Bug fixed on 26/11/2016, do not create intersection lists for masks as they are not used, and affects destinations lists in a problematic way
      Volumes[volume_index]->geometry.intersect_check_list.num_elements = 0;
      Volumes[volume_index]->geometry.mask_intersect_list.num_elements = 0;
      
    } else {
      work_list.num_elements = true_overlap_lists[volume_index]->num_elements;
      work_list.elements = malloc(work_list.num_elements * sizeof(int));
      for (iterate=0;iterate<work_list.num_elements;iterate++) work_list.elements[iterate] = true_overlap_lists[volume_index]->elements[iterate];
    
    //if (verbal) print_1d_int_list(work_list,"After 1)");
    
    
    //2) remove parents of n (normal parent list, with masks)
    for (iterate=work_list.num_elements-1;iterate>-1;iterate--) {
      if (on_int_list(*parents_lists[volume_index],work_list.elements[iterate]))
        remove_element_in_list_by_index(&work_list,iterate);
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 2)");
    
    //3) remove volumes that do not mask n and are true parents of n
    for (iterate=work_list.num_elements-1;iterate>-1;iterate--) {
      if (on_int_list(Volumes[volume_index]->geometry.masked_by_list,work_list.elements[iterate]) == 0 && on_int_list(*true_parents_lists[volume_index],work_list.elements[iterate]))
        remove_element_in_list_by_index(&work_list,iterate);
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 3)");
    
    //4) remove volumes that are not masks and have lower priority than n
    for (iterate=work_list.num_elements-1;iterate>-1;iterate--) {
      if (Volumes[work_list.elements[iterate]]->geometry.is_mask_volume == 0 && Volumes[work_list.elements[iterate]]->geometry.priority_value < Volumes[volume_index]->geometry.priority_value)
        remove_element_in_list_by_index(&work_list,iterate);
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 4)");
    
    //5) remove volumes that have at least one true parent on the list (in step 4) that is not a mask volume
    // Here a logic_list is used to not have the order of removal matter
    logic_list.num_elements = work_list.num_elements;
    logic_list.elements = malloc(logic_list.num_elements * sizeof(int));
    for(iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 1;
    
    for (iterate=0;iterate<work_list.num_elements;iterate++) {
      for (parent=0;parent<true_parents_lists[work_list.elements[iterate]]->num_elements;parent++) {
        true_parent_volume_number = true_parents_lists[work_list.elements[iterate]]->elements[parent];
        if (on_int_list(work_list,true_parent_volume_number) && Volumes[true_parent_volume_number]->geometry.is_mask_volume == 0) {
          // Since element number iterate on the work list have a true parent on the work list, it can be removed
          logic_list.elements[iterate] = 0;
          break;
        }
      }
    }
    
    // Now the elements marked for removal can be removed without interfering in the operation
    for (iterate=work_list.num_elements-1;iterate>-1;iterate--) {
      if (logic_list.elements[iterate] == 0) remove_element_in_list_by_index(&work_list,iterate);
    }
    free(logic_list.elements);
    
    //if (verbal) print_1d_int_list(work_list,"After 5)");
      
    //6) split the list into two, the intersect_check_list which is all non masked volumes still on the list, and the mask_intersect_list which is the masked volumes
    
    Volumes[volume_index]->geometry.intersect_check_list.num_elements = 0;
    Volumes[volume_index]->geometry.mask_intersect_list.num_elements = 0;
    
    for (iterate=0;iterate<work_list.num_elements;iterate++) {
      if (Volumes[work_list.elements[iterate]]->geometry.is_masked_volume == 1) {
        add_element_to_int_list(&Volumes[volume_index]->geometry.mask_intersect_list,work_list.elements[iterate]);
      } else {
        add_element_to_int_list(&Volumes[volume_index]->geometry.intersect_check_list,work_list.elements[iterate]);
      }
    }
    
    //if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.intersect_check_list,"After 6) intersect_check_list");
    //if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.mask_intersect_list,"After 6) mask_intersect_list");
    
    //7) remove volumes on the mask_intersect_list whos masks does not overlap (standard overlap list) n
    for (iterate=Volumes[volume_index]->geometry.mask_intersect_list.num_elements-1;iterate>-1;iterate--) {
      // Need to check if the volumes masking Volumes[volume_index]->geometry.mask_intersect_list.elements[iterate] overlaps with n
      masked_volume_index = Volumes[volume_index]->geometry.mask_intersect_list.elements[iterate];
      
      if (Volumes[masked_volume_index]->geometry.mask_mode == 1) {// All mode, if just one does not overlap, remove the element
        for (mask_start=mask_check=Volumes[masked_volume_index]->geometry.masked_by_list.elements;mask_check-mask_start<Volumes[masked_volume_index]->geometry.masked_by_list.num_elements;mask_check++) {
          if (on_int_list(*raw_overlap_lists[volume_index],*mask_check) == 0) {
            remove_element_in_list_by_index(&Volumes[volume_index]->geometry.mask_intersect_list,iterate);
            break;
          }
        }
      } else { // ANY mode, just one need to overlap in order to keep the element
        ANY_logic = 0;
        for (mask_start=mask_check=Volumes[masked_volume_index]->geometry.masked_by_list.elements;mask_check-mask_start<Volumes[masked_volume_index]->geometry.masked_by_list.num_elements;mask_check++) {
          if (on_int_list(*raw_overlap_lists[volume_index],*mask_check) == 1) {
            ANY_logic = 1;
            break;
          }
        }
        if (ANY_logic == 0) remove_element_in_list_by_index(&Volumes[volume_index]->geometry.mask_intersect_list,iterate);
      }
    }
    
    if (work_list.num_elements > 0) free(work_list.elements);
    }
    
    
    //if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.mask_intersect_list,"After 7) mask_intersect_list");
    
    char string_output[128];
    MPI_MASTER(
    if (verbal) sprintf(string_output,"Intersect check list for Volume %d",volume_index);
    if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.intersect_check_list,string_output);
    if (verbal) sprintf(string_output,"Mask intersect check list for Volume %d",volume_index);
    if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.mask_intersect_list,string_output);
    )
    
    
  }
}

void generate_grandparents_lists(struct pointer_to_1d_int_list **grandparents_lists, struct pointer_to_1d_int_list **parents_lists, int number_of_volumes, int verbal) {
  // Function for generating grandparents lists
  // Volume iterate has volume p as a grandparent, if volume p has a parent T, who has volume iterate as parent.
  // Alternertively:
  // Volume iterate has volume p as a grandparent, if volume p have a child that is volume iterate's parent.
  
  MPI_MASTER(
  if (verbal) printf("\nGenerating grandparents lists ----------------------- \n");
  )

  struct pointer_to_1d_int_list common;
  common.num_elements = number_of_volumes;
  common.elements = malloc(common.num_elements*sizeof(int)); // Maximum needed space.
  
  struct pointer_to_1d_int_list temp_list_local;
  temp_list_local.num_elements = number_of_volumes;
  temp_list_local.elements = malloc(number_of_volumes*sizeof(int));
  
  int iterate,reset_int,parent,child,used_elements;
  for (iterate = 0;iterate < number_of_volumes;iterate++) {
    // clear temp list
    used_elements = 0;

    for (reset_int=0; reset_int<number_of_volumes; reset_int++) common.elements[reset_int] = -1; // Initialize to impossible volume ids
    for (reset_int=0; reset_int<number_of_volumes; reset_int++) temp_list_local.elements[reset_int] = -1; // Initialize to impossible volume ids

    grandparents_lists[iterate] = malloc(sizeof(struct pointer_to_1d_int_list));
    
    for (parent = 0; parent<parents_lists[iterate]->num_elements; parent++) {
        // parent number p parents_lists[iterate].elements.[p] in the parent_list for iterate.
        on_both_int_lists(parents_lists[parents_lists[iterate]->elements[parent]], parents_lists[iterate], &common);
        // returns a pointer_to_1d_list, with all the elements that are in common.
        for (child = 0;child < common.num_elements;child++) {
            // Need to make sure the element is not already on the list
            if (0 == on_int_list(temp_list_local, common.elements[child])) {
              temp_list_local.elements[used_elements++] = common.elements[child];
            }
        }
    }
    allocate_list_from_temp(used_elements, temp_list_local, grandparents_lists[iterate]);
  
    char string_output[128];
    MPI_MASTER(
      if (verbal) sprintf(string_output,"Grandparents for Volume %d", iterate);
      if (verbal) print_1d_int_list(*grandparents_lists[iterate], string_output);
    )
  }
  free(temp_list_local.elements);
  free(common.elements);
};


void generate_destinations_lists_experimental(struct pointer_to_1d_int_list **true_overlap_lists, struct pointer_to_1d_int_list **true_children_lists, struct pointer_to_1d_int_list **true_parents_lists, struct pointer_to_1d_int_list **true_grandparents_lists, struct Volume_struct **Volumes, int number_of_volumes, int verbal) {
  // Generates destinations list for for all volumes
  // Current implementation uses true_parents_lists and true_grandparents_lists that are generated as if no masks were defined

  MPI_MASTER(
  if (verbal) printf("\nGenerating destinations lists ----------------------- \n");
  )
  // Volume 0 has an hardcoded empty destinations list
  Volumes[0]->geometry.destinations_list.num_elements = 0;

  struct pointer_to_1d_int_list work_list;
  int volume_index,iterate,iterate2,found_index,I_index,I_volume;
  
  for (volume_index=1;volume_index<number_of_volumes;volume_index++) {
    
    // 1) Take the true overlap list for volume n
    // Create copy of true_overlap_lists to work with
    work_list.num_elements = true_overlap_lists[volume_index]->num_elements;
    work_list.elements = malloc(work_list.num_elements * sizeof(int));
    for (iterate=0;iterate<work_list.num_elements;iterate++) work_list.elements[iterate] = true_overlap_lists[volume_index]->elements[iterate];
    
    //if (verbal) print_1d_int_list(work_list,"After 1)");
    
    // 2) Remove elements from n's intersection list
    for (iterate=work_list.num_elements-1;iterate>-1;iterate--) {
      if (on_int_list(Volumes[volume_index]->geometry.intersect_check_list,work_list.elements[iterate]))
        remove_element_in_list_by_index(&work_list,iterate);
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 2)");
  
    // 3) Remove true children of non-mask elements on n's intersection list
    for (I_index=0;I_index<Volumes[volume_index]->geometry.intersect_check_list.num_elements;I_index++) {
      I_volume = Volumes[volume_index]->geometry.intersect_check_list.elements[I_index];
      if (Volumes[I_volume]->geometry.is_mask_volume == 0) {
        for (iterate=0;iterate<true_children_lists[I_volume]->num_elements;iterate++) {
          remove_element_in_list_by_value(&work_list,true_children_lists[I_volume]->elements[iterate]);
        }
      }
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 3)");
    
    // 4) Remove elements from mask intersection list
    for (iterate=work_list.num_elements-1;iterate>-1;iterate--) {
      if (on_int_list(Volumes[volume_index]->geometry.mask_intersect_list,work_list.elements[iterate]))
        remove_element_in_list_by_index(&work_list,iterate);
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 4)");
    
    // 5) Remove true children of elements on n's mask intersection list
    for (I_index=0;I_index<Volumes[volume_index]->geometry.mask_intersect_list.num_elements;I_index++) {
      I_volume = Volumes[volume_index]->geometry.mask_intersect_list.elements[I_index];
      if (Volumes[I_volume]->geometry.is_mask_volume == 0) {
        for (iterate=0;iterate<true_children_lists[I_volume]->num_elements;iterate++) {
          remove_element_in_list_by_value(&work_list,true_children_lists[I_volume]->elements[iterate]);
        }
      }
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 5)");
    
    // 6) Remove true children of n
    for (iterate=0;iterate<true_children_lists[volume_index]->num_elements;iterate++)
      remove_element_in_list_by_value(&work_list,true_children_lists[volume_index]->elements[iterate]);
      
    //if (verbal) print_1d_int_list(work_list,"After 6)");
      
    // 7) Remove true grandparents of n
    for (iterate=0;iterate<true_grandparents_lists[volume_index]->num_elements;iterate++)
      remove_element_in_list_by_value(&work_list,true_grandparents_lists[volume_index]->elements[iterate]);
      
    //if (verbal) print_1d_int_list(work_list,"After 7)");
      
    // 8) Remove mask volumes
    for (iterate=work_list.num_elements-1;iterate>-1;iterate--) {
      if (Volumes[work_list.elements[iterate]]->geometry.is_mask_volume == 1)
        remove_element_in_list_by_index(&work_list,iterate);
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 8)");
    
    // 9) Remove true parents of n on the list that has other true parents of n on the list with higher priority
    for (iterate=work_list.num_elements-1;iterate>-1;iterate--) {
      if (on_int_list(*true_parents_lists[volume_index],work_list.elements[iterate])){
        // work_list.elements[iterate] is the volume index of a volume that is a true parent of n
        for (iterate2=0;iterate2<work_list.num_elements;iterate2++) {
          if (on_int_list(*true_parents_lists[volume_index],work_list.elements[iterate2]) && iterate != iterate2) {
            if (Volumes[work_list.elements[iterate]]->geometry.priority_value < Volumes[work_list.elements[iterate2]]->geometry.priority_value) {
              //printf("Removing element number %d (V%d) because element number %d (V%d) had higher priority \n",iterate,work_list.elements[iterate],iterate2,work_list.elements[iterate2]);
              remove_element_in_list_by_index(&work_list,iterate);
              break; // Missing break inserted on 14/9/2016
            }
          }
        }
      }
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 9)");
    
    // 10) The remaining list is the destinations_list
    Volumes[volume_index]->geometry.destinations_list.num_elements = work_list.num_elements;
    Volumes[volume_index]->geometry.destinations_list.elements = malloc(Volumes[volume_index]->geometry.destinations_list.num_elements*sizeof(int));
    
    for (iterate=0;iterate<work_list.num_elements;iterate++)
      Volumes[volume_index]->geometry.destinations_list.elements[iterate] = work_list.elements[iterate];
    
    // Clean up after work_list so the next can be allocated
    free(work_list.elements);
  
  
    char string_output[128];
    MPI_MASTER(
    if (verbal) sprintf(string_output,"Destinations list for Volume %d",volume_index);
    if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.destinations_list,string_output);
    )

  }

};

void generate_destinations_list(int N_volume,struct Volume_struct **Volumes,struct pointer_to_1d_int_list original_overlap_list,struct pointer_to_1d_int_list *parent_list, struct pointer_to_1d_int_list *grandparent_list) {
    // This function generates the destinations_list for a single volume index, N_volume
    // The destination list describes which volumes a ray can enter when leaving N_volume
    // Each of the 6 steps for the algorithm is commented individually, and debug print statements are available
    
    // Mask update: Mask volumes are to be removed from all destinations_list as they can not be the current volume
    // It is not that simple, as some volume will then get empty destination lists. Need to revise this algorithm.
    
    // 1) Start with the overlap list of volume N
    // 2) remove all Volumes from the overlap list of N, which is also on the intersect_check_list
    // 3) remove the children of volumes removed in step 2)
    // 4) remove the children of N
    // 5) remove the grandparents of N
    // 6) remove volumes with lower priority than parents of N still on the list
    // 7) The remaing list is the destinations list
    
    
    // The destination list system should run without masks altogether, meaning parent and grandparent lists should not use them,
    //  and all masks should be removed in step 2
    
    // 1) Start with the overlap list of volume N
    // 2) remove all masks on the list
    // 3) remove all Volumes from the overlap list of N, which is also on the intersect_check_list
    // 4) remove the children of volumes removed in step 2) (if the removed volume was not a mask )
    // 5) remove the children of N
    // 6) remove the grandparents of N
    // 7) remove volumes with lower priority than parents of N still on the list
    // 8) The remaing list is the destinations list
    
    

    // 1) Start with the overlap list of volume N
    struct pointer_to_1d_int_list overlap_list;
    overlap_list.num_elements = original_overlap_list.num_elements;
    overlap_list.elements = malloc(overlap_list.num_elements*sizeof(int));
    int iterate;
    for (iterate=0;iterate<overlap_list.num_elements;iterate++) overlap_list.elements[iterate] = original_overlap_list.elements[iterate];
    
    char string_output[124];
    // sprintf(string_output,"Destinations list for Volume %d step 1",N_volume);
    // print_1d_int_list(overlap_list,string_output);
    
    // 2) remove all masks
    for (iterate=overlap_list.num_elements-1;iterate > -1;iterate--) {
        if (Volumes[overlap_list.elements[iterate]]->geometry.is_mask_volume == 1) {
            remove_element_in_list_by_index(&overlap_list,iterate);
        }
    }
    
    // 3) remove all Volumes from the overlap list of N, which is also on the intersect_check_list
    struct pointer_to_1d_int_list removed_under_2;
    removed_under_2.num_elements = 0;
    int to_check;
    removed_under_2.elements = malloc( Volumes[N_volume]->geometry.intersect_check_list.num_elements * sizeof(int));
    for (iterate=0;iterate < Volumes[N_volume]->geometry.intersect_check_list.num_elements;iterate++) {
        to_check = Volumes[N_volume]->geometry.intersect_check_list.elements[iterate];
        if (on_int_list(overlap_list,to_check)) {
            removed_under_2.elements[removed_under_2.num_elements++] = to_check;
            remove_element_in_list_by_value(&overlap_list,to_check);
        }
    }
    
    // sprintf(string_output,"Destinations list for Volume %d step 2",N_volume);
    // print_1d_int_list(overlap_list,string_output);
    
    // 4) remove the children of volumes removed in step 2)
    int children;
    for (iterate=0;iterate<removed_under_2.num_elements;iterate++){
        for (children = 0;children < Volumes[removed_under_2.elements[iterate]]->geometry.children.num_elements;children++) {
          remove_element_in_list_by_value(&overlap_list,Volumes[removed_under_2.elements[iterate]]->geometry.children.elements[children]);
        }
    }
    
    // sprintf(string_output,"Destinations list for Volume %d step 3",N_volume);
    // print_1d_int_list(overlap_list,string_output);
    
    // 5) remove the children of N
    for (children = 0;children < Volumes[N_volume]->geometry.children.num_elements;children++) {
        remove_element_in_list_by_value(&overlap_list,Volumes[N_volume]->geometry.children.elements[children]);
    }
    
    // sprintf(string_output,"Destinations list for Volume %d step 4",N_volume);
    // print_1d_int_list(overlap_list,string_output);
    
    // 6) remove the grandparents of N
    int grandparent;
    for (grandparent = 0;grandparent < grandparent_list->num_elements;grandparent++) {
        remove_element_in_list_by_value(&overlap_list,grandparent_list->elements[grandparent]);
    }
    
    // sprintf(string_output,"Destinations list for Volume %d step 5",N_volume);
    // print_1d_int_list(overlap_list,string_output);
    
    // 7) remove volumes with lower priority than parents of N still on the list
    struct pointer_to_1d_int_list logic_list;
    logic_list.num_elements = overlap_list.num_elements;
    if (logic_list.num_elements>0) logic_list.elements = malloc(logic_list.num_elements*sizeof(int));
    for (iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 1;
    
    int parent;
    for (parent=0;parent<parent_list->num_elements;parent++) {
      if (on_int_list(overlap_list,parent_list->elements[parent])) {
        // Found a parent to N on the list, now check all other elements on the list
        for (iterate=0;iterate<overlap_list.num_elements;iterate++) {
          if (parent_list->elements[parent] != overlap_list.elements[iterate]) {
            // if the element iterate have lower priority than the parent, remove it from the list
            if (Volumes[overlap_list.elements[iterate]]->geometry.priority_value < Volumes[parent_list->elements[parent]]->geometry.priority_value) logic_list.elements[iterate] = 0;
          }
        }
      }
    }
    
    // 8) The remaing list is the destinations list
    Volumes[N_volume]->geometry.destinations_list.num_elements = sum_int_list(logic_list);
    Volumes[N_volume]->geometry.destinations_list.elements = malloc(Volumes[N_volume]->geometry.destinations_list.num_elements * sizeof(int));
    
    int overlap_index,used_elements=0;
    for (overlap_index=0;overlap_index < overlap_list.num_elements;overlap_index++)
      if (logic_list.elements[overlap_index] == 1)
        Volumes[N_volume]->geometry.destinations_list.elements[used_elements++] = overlap_list.elements[overlap_index];
    
    if (overlap_list.num_elements>0) free(overlap_list.elements);
    if (logic_list.num_elements>0) free(logic_list.elements);
    
    
    /*
    // Old version before rule 6 and 2 was added
    // 8) The remaing list is the destinations list
    Volumes[N_volume]->geometry.destinations_list.num_elements = overlap_list.num_elements;
    Volumes[N_volume]->geometry.destinations_list.elements = malloc(Volumes[N_volume]->geometry.destinations_list.num_elements * sizeof(int));
    
    int overlap_index;
    for (overlap_index=0;overlap_index < overlap_list.num_elements;overlap_index++)
        Volumes[N_volume]->geometry.destinations_list.elements[overlap_index] = overlap_list.elements[overlap_index];
    
    // sprintf(string_output,"Destination list for Volume %d step 6",N_volume);
    // print_1d_int_list(Volumes[N_volume]->geometry.destinations_list,string_output);
    
    if (overlap_list.num_elements>0) free(overlap_list.elements);
    */
    
    };

void generate_destinations_lists(struct pointer_to_1d_int_list **grandparents_lists, struct pointer_to_1d_int_list **parents_lists, struct pointer_to_1d_int_list **overlap_lists,struct Volume_struct **Volumes, int number_of_volumes, int verbal) {
    // Because of the complexity of the algortithm for generating the destinations list, the function is made for a single volume at the time to keep the notation simpler
    // This funtion runs the destinations list function for each volume
    MPI_MASTER(
    if (verbal) printf("\nGenerating destinations lists ----------------------- \n");
    )
    
    int volume_index;
    for (volume_index = 0;volume_index < number_of_volumes;volume_index++) {
      generate_destinations_list(volume_index,Volumes,*overlap_lists[volume_index],parents_lists[volume_index],grandparents_lists[volume_index]);
      
      char string_output[128];
      if (verbal) sprintf(string_output,"Destinations list for Volume %d",volume_index);
      MPI_MASTER(
      if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.destinations_list,string_output);
      )
    }
};

/* OBSOLETE
void generate_destinations_logic_lists(struct Volume_struct **Volumes,int number_of_volumes,int verbal) {
  // The destinations logic list is another way to store the destinations list that makes some tasks quicker
  
  MPI_MASTER(
  if (verbal) printf("\nGenerating destinations logic lists ----------------------- \n");
  )

  int volume_index;
  for (volume_index = 0;volume_index < number_of_volumes;volume_index++) {
      allocate_logic_list_from_temp(number_of_volumes,Volumes[volume_index]->geometry.destinations_list,&Volumes[volume_index]->geometry.destinations_logic_list);
      
      char string_output[128];
      MPI_MASTER(
      if (verbal) sprintf(string_output,"Destinations logic list for Volume %d",volume_index);
      if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.destinations_logic_list,string_output);
      )
  }

};
*/

void generate_reduced_destinations_lists(struct pointer_to_1d_int_list **parents_lists, struct Volume_struct **Volumes,int number_of_volumes,int verbal) {
    // The reduced destination list is the destination list of a volume, where each element that has a parent on the same destination list is removed
    // This list is to be fed to the which_volume function, as this funtion will automatically search through the direct children in a tree like manner
    // The optimization reduces the number of calculations of within functions in nested geometries.
    
    MPI_MASTER(
    if (verbal) printf("\nGenerating reduced destination lists ----------------------- \n");
    )
    
    struct pointer_to_1d_int_list logic_list;
  
    int volume_index,checked_dest_index,checked_dest_volume,rest_dest_index,rest_dest_volume,dest_index,iterate;
    for (volume_index = 0;volume_index < number_of_volumes;volume_index++) {
      
      //printf("Generating reduced destinations lists for volume %d\n",volume_index);
      logic_list.num_elements = Volumes[volume_index]->geometry.destinations_list.num_elements;
      logic_list.elements = malloc( (int) logic_list.num_elements * sizeof(int));
      //printf("Sucsessfully allocated space for %d integers in logic list %d\n",logic_list.num_elements,volume_index);
      for (iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 1;
      
      for (checked_dest_index=0;checked_dest_index<Volumes[volume_index]->geometry.destinations_list.num_elements;checked_dest_index++) {
            checked_dest_volume = Volumes[volume_index]->geometry.destinations_list.elements[checked_dest_index];
            for (rest_dest_index=0;rest_dest_index<Volumes[volume_index]->geometry.destinations_list.num_elements;rest_dest_index++) {
                rest_dest_volume = Volumes[volume_index]->geometry.destinations_list.elements[rest_dest_index];
                // As every volume has 0 as a parent, these are ignored. It would work to include this, but would add an extra trivial step to within_which_volume
                if (rest_dest_volume != 0) {
                    if (on_int_list(*parents_lists[checked_dest_volume],rest_dest_volume) == 1) {
                        // In this case, do not include element checked_dest_index on the reduced destinations list
                        // ADD mask check
                        if (Volumes[rest_dest_volume]->geometry.is_masked_volume == 0) {
                          logic_list.elements[checked_dest_index] = 0;
                        }
                    }
                }
            }
      }
      
      Volumes[volume_index]->geometry.reduced_destinations_list.num_elements = sum_int_list(logic_list);
      Volumes[volume_index]->geometry.reduced_destinations_list.elements = malloc((int)Volumes[volume_index]->geometry.reduced_destinations_list.num_elements * sizeof(int));
      
      iterate = 0;
      for (dest_index = 0;dest_index < Volumes[volume_index]->geometry.destinations_list.num_elements;dest_index++) {
            if (logic_list.elements[dest_index] == 1)   Volumes[volume_index]->geometry.reduced_destinations_list.elements[iterate++] = Volumes[volume_index]->geometry.destinations_list.elements[dest_index];
      }
      
      free(logic_list.elements);
      
      // Testing an optimization
      remove_element_in_list_by_value(&Volumes[volume_index]->geometry.reduced_destinations_list,0);
      
      char string_output[128];
      MPI_MASTER(
      if (verbal) sprintf(string_output,"Reduced destinations list for Volume %d",volume_index);
      if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.reduced_destinations_list,string_output);
      )
    }
};

void generate_direct_children_lists(struct pointer_to_1d_int_list **parents_lists, struct Volume_struct **Volumes,int number_of_volumes,int verbal) {

  MPI_MASTER(
  if (verbal) printf("\nGenerating direct children lists ----------------------- \n");
  )
  // A direct children of volume n is a volume that is a child of n, but no other child of n is its parent
  
  // Mask update: Need to check that this step does not bug out when the mask system interferes with child/parent systems
  
  struct pointer_to_1d_int_list logic_list;
  int volume_index,child,parent,iterate;
  for (volume_index = 0;volume_index < number_of_volumes;volume_index++) {
        // Temp elements is used, and its actual number of elements is edited even though the memory allocated is not changed. This is so that the list functions handles it correctly.
        // The free function will free all the allocated memory regardless of the value of the .num_elements structure field, it is just there for convinience.
      
        logic_list.num_elements = Volumes[volume_index]->geometry.children.num_elements;
        logic_list.elements = malloc(logic_list.num_elements * sizeof(int));
        for (iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 1;
      
        // Look through each child of volume n, and for each of those look through all the other children of n and search for one of them being a parent to the child
        for (child=0;child<Volumes[volume_index]->geometry.children.num_elements;child++) {
            for (parent=0;parent<parents_lists[Volumes[volume_index]->geometry.children.elements[child]]->num_elements;parent++) {
                if (on_int_list(Volumes[volume_index]->geometry.children,parents_lists[Volumes[volume_index]->geometry.children.elements[child]]->elements[parent]))
                    // If such a parent is found, remove that child from the list
                    logic_list.elements[child] = 0;
            }
        }
      
      Volumes[volume_index]->geometry.direct_children.num_elements = sum_int_list(logic_list);
      Volumes[volume_index]->geometry.direct_children.elements = malloc(Volumes[volume_index]->geometry.direct_children.num_elements*sizeof(int));
      
      iterate = 0;
      for (child = 0;child < Volumes[volume_index]->geometry.children.num_elements;child++) {
            if (logic_list.elements[child])   Volumes[volume_index]->geometry.direct_children.elements[iterate++] = Volumes[volume_index]->geometry.children.elements[child];
      }
      // Be careful with names in both main and a function, as they are automatically declared as external variables, which would then also free the main.
      free(logic_list.elements);
      
      char string_output[128];
      MPI_MASTER(
      if (verbal) sprintf(string_output,"Children list for Volume        %d",volume_index);
      if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.children,string_output);
      if (verbal) sprintf(string_output,"Direct_children list for Volume %d",volume_index);
      if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.direct_children,string_output);
      )
      
  }

};

void generate_starting_logic_list(struct starting_lists_struct *starting_lists, struct Volume_struct **Volumes, int number_of_volumes, int verbal) {
    // Function for generating logic list of volumes the ray can start in without an error.
    // Start with a list of all vacuum volumes
    // Remove all volumes that are children of non-vacuum volumes
    // It is still possible to have a volume on this list that is surrounded by non-vacuum volumes, but it is hard to detect these situations,
    //  meaning that it is ultimately partly the users responsibility to not send neutrons directly into materials.
    
    int volume_index,*start,*check;
    
    struct pointer_to_1d_int_list temp_list_local;
    temp_list_local.num_elements = number_of_volumes;
    temp_list_local.elements = malloc(number_of_volumes*sizeof(int));
    
    //if (verbal==1) printf("sucessfully allocated temp_list_local.elements, now to enter which volumes are vacuums as a logic list\n");
    temp_list_local.elements[0] = 1; // Volume 0 is a vacuum volume.
    for (volume_index = 1;volume_index < number_of_volumes;volume_index++) {
        if (Volumes[volume_index]->p_physics->is_vacuum == 1) temp_list_local.elements[volume_index] = 1;
        else temp_list_local.elements[volume_index] = 0;
    }
    // temp_list_local is now a logic list of all vacuum volumes
    //if (verbal==1) printf("list of vacuum volumes done, now to remove the children of non-vcauum volumes\n");
    for (volume_index = 1;volume_index < number_of_volumes;volume_index++) { // All volumes ...
        if (temp_list_local.elements[volume_index] == 0) { // ... that are not vacuum ...
            for (start = check = Volumes[volume_index]->geometry.children.elements;check - start < Volumes[volume_index]->geometry.children.num_elements;check++) { // ... have all their children ...
                temp_list_local.elements[*check] = 0; // .. removed from the allowed_start_logic_list
            }
        }
    }
    //if (verbal==1) printf("sucessfully removed children of non-vacuum volumes, now allocate allowed_start_logic_list\n");
    allocate_list_from_temp(number_of_volumes,temp_list_local,&starting_lists->allowed_starting_volume_logic_list);
    //if (verbal==1) printf("sucsessfully allocated allowed_start_logic_list, now freeing temp_list_local.elements. \n");
    
    free(temp_list_local.elements);
    //if (verbal==1) printf("sucessfully freed temp_list_local.elements, generate starting lists done\n");
    
    char string_output[128];
    MPI_MASTER(
    if (verbal) sprintf(string_output,"Allowed starting volume logic list");
    if (verbal) print_1d_int_list(starting_lists->allowed_starting_volume_logic_list,string_output);
    )
    
    
};

void generate_reduced_starting_destinations_list(struct starting_lists_struct *starting_lists, struct pointer_to_1d_int_list **parents_lists, struct Volume_struct **Volumes,int number_of_volumes,int verbal) {
    // The starting_destinations_list is trivial, as it contains all volumes that are not masks.


    struct pointer_to_1d_int_list logic_list;
    //printf("Generating reduced destinations lists for volume %d\n",volume_index);
    logic_list.num_elements = number_of_volumes;
    logic_list.elements = malloc( (int) logic_list.num_elements * sizeof(int));
    int iterate;
    for (iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 0;
    
    
    for (iterate=1;iterate<number_of_volumes;iterate++)
      if (Volumes[iterate]->geometry.is_mask_volume == 0) logic_list.elements[iterate] = 1;
      //else logic_list.elements[iterate] = 0;
    
    starting_lists->starting_destinations_list.num_elements = sum_int_list(logic_list);
    starting_lists->starting_destinations_list.elements = malloc(starting_lists->starting_destinations_list.num_elements*sizeof(int));
    
    int used_elements=0;
    for (iterate=1;iterate<number_of_volumes;iterate++)
      if (logic_list.elements[iterate] == 1) starting_lists->starting_destinations_list.elements[used_elements++] = iterate;
    
    MPI_MASTER(
    if (verbal) printf("\nGenerating start destinations list ------------------------------ \n");
    if (verbal) print_1d_int_list(starting_lists->starting_destinations_list,"Starting destinations list");
    )

    // The reduced starting destination list is used when a ray enters the component in the search for which volume it starts in.
    // It facilitates the same optimization as the regular destination list.
    // The start logic list is also generated, as it is very simple and does not need a seperate function
    
    // Mask update: Need to remove mask volumes from the reduced starting destination list
    
    MPI_MASTER(
    if (verbal) printf("\nGenerating reduced start destination list ----------------------- \n");
    )
    
    int checked_dest_index,checked_dest_volume,rest_dest_index,rest_dest_volume,dest_index;
    
    /* Old version before masks were introduced
    struct pointer_to_1d_int_list starting_dest_list;
    
    starting_dest_list.num_elements = number_of_volumes - 1;
    starting_dest_list.elements = malloc ( starting_dest_list.num_elements * sizeof(int));
    for (iterate=0;iterate<number_of_volumes-1;iterate++) starting_dest_list.elements[iterate] = iterate + 1; // All volumes to be checked for starting
    */
    
    logic_list.num_elements = starting_lists->starting_destinations_list.num_elements;
    free(logic_list.elements);
    logic_list.elements = malloc( (int) logic_list.num_elements * sizeof(int));
    
    //printf("Sucsessfully allocated space for %d integers in logic list %d\n",logic_list.num_elements,volume_index);
    for (iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 1;
  
    for (checked_dest_index=0;checked_dest_index<starting_lists->starting_destinations_list.num_elements;checked_dest_index++) {
        checked_dest_volume = starting_lists->starting_destinations_list.elements[checked_dest_index];
        for (rest_dest_index=0;rest_dest_index<starting_lists->starting_destinations_list.num_elements;rest_dest_index++) {
            rest_dest_volume = starting_lists->starting_destinations_list.elements[rest_dest_index];
            // As every volume has 0 as a parent, these are ignored. It would work to include this, but would add an extra trivial step to within_which_volume
            if (rest_dest_volume != 0) {
                if (on_int_list(*parents_lists[checked_dest_volume],rest_dest_volume) == 1) {
                    // In this case, do not include element checked_dest_index on the reduced destinations list
                    logic_list.elements[checked_dest_index] = 0;
                }
            }
        }
  }
  
  starting_lists->reduced_start_list.num_elements = sum_int_list(logic_list);
  starting_lists->reduced_start_list.elements = malloc((int)starting_lists->reduced_start_list.num_elements * sizeof(int));
  
  iterate = 0;
  for (dest_index = 0;dest_index < starting_lists->starting_destinations_list.num_elements;dest_index++) {
        if (logic_list.elements[dest_index] == 1) starting_lists->reduced_start_list.elements[iterate++] = starting_lists->starting_destinations_list.elements[dest_index];
  }
  
  free(logic_list.elements);
  //free(starting_dest_list.elements);
  
  MPI_MASTER(
  if (verbal) print_1d_int_list(starting_lists->reduced_start_list,"Reduced start destinations list");
  )
    
  // Making the start_logic_list.
  starting_lists->start_logic_list.num_elements = number_of_volumes;
  starting_lists->start_logic_list.elements = malloc ( starting_lists->start_logic_list.num_elements * sizeof(int));
  starting_lists->start_logic_list.elements[0] = 0;
  for (iterate=1;iterate<number_of_volumes;iterate++) starting_lists->start_logic_list.elements[iterate] = 1; // All volumes to be checked for starting volume
  MPI_MASTER(
  if (verbal) print_1d_int_list(starting_lists->start_logic_list,"Start logic list");
  )
};


void generate_next_volume_list(struct Volume_struct **Volumes, int number_of_volumes, int verbal) {
    // Generate list of volumes that can be the next volume which the ray enters. It is used for tagging, not the simulation / propagation

    // Mask update: These lists should be done as if all mask statuses are on, meaning they include all possible next volumes (will include more input to this function)
    
    // bug: next volume list is not complete and contains duplicates

    MPI_MASTER(
    if (verbal) printf("\nGenerating next volume list ------------------------------------- \n");
    )
    // Merge destinations list, intersection list and mask_intersection_list
    int volume_index,iterate,mask_index;
    struct pointer_to_1d_int_list full_intersection_list;
    full_intersection_list.num_elements=0;
    
    for (volume_index=0;volume_index<number_of_volumes;volume_index++) {
        Volumes[volume_index]->geometry.next_volume_list.num_elements = 0;
        // Before mask update
        //merge_lists(&Volumes[volume_index]->geometry.next_volume_list, &Volumes[volume_index]->geometry.destinations_list, &Volumes[volume_index]->geometry.intersect_check_list);
        
        merge_lists(&full_intersection_list, &Volumes[volume_index]->geometry.mask_intersect_list, &Volumes[volume_index]->geometry.intersect_check_list);
        merge_lists(&Volumes[volume_index]->geometry.next_volume_list,&Volumes[volume_index]->geometry.destinations_list,&full_intersection_list);
        
        /* // This complication is taken into account by adding the mask_intersect list instead
        // It is possible that the next volume is still not on this list, as when masks are encountered the next volume can be a volume they mask.
        // For each on the list, add the volumes masked by that mask (do not iterate over this, as masks can not be masked regardless)
        for (iterate=Volumes[volume_index]->geometry.next_volume_list.num_elements-1;iterate>-1;iterate--) {
          if (Volumes[Volumes[volume_index]->geometry.next_volume_list.elements[iterate]]->geometry.is_mask_volume == 1) {
            for (mask_index=0;mask_index<Volumes[Volumes[volume_index]->geometry.next_volume_list.elements[iterate]]->geometry.mask_list.num_elements;mask_index++) {
              add_element_to_int_list(&Volumes[volume_index]->geometry.next_volume_list,Volumes[Volumes[volume_index]->geometry.next_volume_list.elements[iterate]]->geometry.mask_list.elements[mask_index]);
            }
          }
        }
        */
        
        // Remove mask volumes from the next volume list, as they never occur as current_volume, and thus just create dead branches that takes up memory
        for (iterate=Volumes[volume_index]->geometry.next_volume_list.num_elements-1;iterate>-1;iterate--)
          if (Volumes[Volumes[volume_index]->geometry.next_volume_list.elements[iterate]]->geometry.is_mask_volume == 1)
            remove_element_in_list_by_index(&Volumes[volume_index]->geometry.next_volume_list,iterate);
        
        
        if (full_intersection_list.num_elements>0) free(full_intersection_list.elements);
        full_intersection_list.num_elements=0;
        
        char string_output[128];
        MPI_MASTER(
        if (verbal) sprintf(string_output,"Next volume list                %d",volume_index);
        if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.next_volume_list,string_output);
        )
    }
};

void generate_lists(struct Volume_struct **Volumes, struct starting_lists_struct *starting_lists, int number_of_volumes, int verbal) {
    // Function to control the generation of lists
    // Some lists are only needed temporary, and are thus declared here to keep them out of the main scope
    // Others are stored in the volume structs as they are needed in the trace algorithm (or tagging)
    
    
    struct pointer_to_1d_int_list **true_children_lists;
    true_children_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    // generate_children_lists both generate the normal children list for each volume, but also the true children list needed locally.
    generate_children_lists(Volumes, true_children_lists, number_of_volumes,verbal);
    
    
    struct pointer_to_1d_int_list **true_overlap_lists;
    true_overlap_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    struct pointer_to_1d_int_list **raw_overlap_lists;
    raw_overlap_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    
    generate_overlap_lists(true_overlap_lists, raw_overlap_lists, Volumes,number_of_volumes,verbal);
    
    
    //generate_intersect_check_lists(true_overlap_lists, Volumes, number_of_volumes, verbal);
    
    
    struct pointer_to_1d_int_list **parents_lists;
    parents_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    generate_parents_lists(parents_lists,Volumes,number_of_volumes,verbal,1); // The last 1 means masks are taken into account
    
    // Generate version of parent list as it would be without masks
    struct pointer_to_1d_int_list **parents_lists_no_masks;
    parents_lists_no_masks = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    generate_parents_lists(parents_lists_no_masks,Volumes,number_of_volumes,verbal,0); // The last 0 means masks are NOT taken into account
    
    // Generate version of parent list using true_children instead
    struct pointer_to_1d_int_list **true_parents_lists;
    true_parents_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    generate_true_parents_lists(true_parents_lists, true_children_lists, Volumes, number_of_volumes, verbal, 1);
    
    // Generate version of parent list no masks using true_children instead
    struct pointer_to_1d_int_list **true_parents_lists_no_masks;
    true_parents_lists_no_masks = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    generate_true_parents_lists(true_parents_lists_no_masks, true_children_lists, Volumes, number_of_volumes, verbal, 0);
    
    // New version of generate intersect lists
    generate_intersect_check_lists_experimental(true_overlap_lists, raw_overlap_lists, parents_lists, true_parents_lists, Volumes, number_of_volumes, verbal);
    
    struct pointer_to_1d_int_list **grandparents_lists;
    grandparents_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    generate_grandparents_lists(grandparents_lists,parents_lists,number_of_volumes,verbal);
    
    // Generate version of grandparents list as it would have been if no masks were defined
    struct pointer_to_1d_int_list **grandparents_lists_no_masks;
    grandparents_lists_no_masks = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    generate_grandparents_lists(grandparents_lists_no_masks,parents_lists_no_masks,number_of_volumes,verbal);
    
    // Generate true_grandparents_lists
    struct pointer_to_1d_int_list **true_grandparents_lists;
    true_grandparents_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    generate_grandparents_lists(true_grandparents_lists,true_parents_lists,number_of_volumes,verbal);
    
    struct pointer_to_1d_int_list **true_grandparents_lists_no_masks;
    true_grandparents_lists_no_masks = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    generate_grandparents_lists(true_grandparents_lists_no_masks,true_parents_lists_no_masks,number_of_volumes,verbal);
    
    // The destinations lists are generated without taking masks into account (they are removed from the overlap list in an early step)
    //generate_destinations_lists(grandparents_lists_no_masks,parents_lists_no_masks,true_overlap_lists,Volumes,number_of_volumes,verbal);
    generate_destinations_lists_experimental(true_overlap_lists, true_children_lists, true_parents_lists_no_masks, true_grandparents_lists_no_masks, Volumes, number_of_volumes, verbal);
    
    // Obsolete, found a way around them in within_which_volume, but need to test the performance difference
    // generate_destinations_logic_lists(Volumes,number_of_volumes,verbal);
    
    generate_reduced_destinations_lists(parents_lists,Volumes,number_of_volumes,verbal);
    
    generate_direct_children_lists(parents_lists,Volumes,number_of_volumes,verbal);
    
    //generate_starting_list(starting_lists,Volumes,number_of_volumes,verbal);
    generate_starting_logic_list(starting_lists,Volumes,number_of_volumes,verbal);
    
    generate_reduced_starting_destinations_list(starting_lists,parents_lists,Volumes,number_of_volumes,verbal);
    
    // This list is stored with the volumes for convinience, but is only used for tagging
    generate_next_volume_list(Volumes,number_of_volumes,verbal);
    
    // Garbage collection for temporary dynamically allocated lists. (Permanent lists freed from FINALLY)
    int iterate;
    for (iterate=0;iterate<number_of_volumes;iterate++) {
        //printf("freeing for volume nr %d\n",iterate);
        //printf("true_overlap_lists[iterate]->num_elements = %d \n",true_overlap_lists[iterate]->num_elements);
        if (true_overlap_lists[iterate]->num_elements > 0) free(true_overlap_lists[iterate]->elements);
        free(true_overlap_lists[iterate]);
        
        //printf("raw_overlap_lists[iterate]->num_elements = %d \n",raw_overlap_lists[iterate]->num_elements);
        if (raw_overlap_lists[iterate]->num_elements > 0) free(raw_overlap_lists[iterate]->elements);
        free(raw_overlap_lists[iterate]);
        
        //printf("parents_lists[iterate]->num_elements = %d \n",parents_lists[iterate]->num_elements);
        if (parents_lists[iterate]->num_elements > 0) free(parents_lists[iterate]->elements);
        free(parents_lists[iterate]);
        
        //printf("parents_lists_no_masks[iterate]->num_elements = %d \n",parents_lists_no_masks[iterate]->num_elements);
        if (parents_lists_no_masks[iterate]->num_elements > 0) free(parents_lists_no_masks[iterate]->elements);
        free(parents_lists_no_masks[iterate]);
        
        //printf("true_parents_lists[iterate]->num_elements = %d \n",true_parents_lists[iterate]->num_elements);
        if (true_parents_lists[iterate]->num_elements > 0) free(true_parents_lists[iterate]->elements);
        free(true_parents_lists[iterate]);
        
        //printf("true_parents_lists_no_masks[iterate]->num_elements = %d \n",true_parents_lists_no_masks[iterate]->num_elements);
        if (true_parents_lists_no_masks[iterate]->num_elements > 0) free(true_parents_lists_no_masks[iterate]->elements);
        free(true_parents_lists_no_masks[iterate]);
        
        //printf("grandparents_lists[iterate]->num_elements = %d \n",grandparents_lists[iterate]->num_elements);
        if (grandparents_lists[iterate]->num_elements > 0) free(grandparents_lists[iterate]->elements);
        free(grandparents_lists[iterate]);
        
        //printf("true_grandparents_lists[iterate]->num_elements = %d \n",true_grandparents_lists[iterate]->num_elements);
        if (true_grandparents_lists[iterate]->num_elements > 0) free(true_grandparents_lists[iterate]->elements);
        free(true_grandparents_lists[iterate]);
        
        //printf("grandparents_lists_no_masks[iterate]->num_elements = %d \n",grandparents_lists_no_masks[iterate]->num_elements);
        if (grandparents_lists_no_masks[iterate]->num_elements > 0) free(grandparents_lists_no_masks[iterate]->elements);
        free(grandparents_lists_no_masks[iterate]);
        
        //printf("true_grandparents_lists_no_masks[iterate]->num_elements = %d \n",true_grandparents_lists_no_masks[iterate]->num_elements);
        if (true_grandparents_lists_no_masks[iterate]->num_elements > 0) free(true_grandparents_lists_no_masks[iterate]->elements);
        free(true_grandparents_lists_no_masks[iterate]);
        
        //printf("true_children_lists[iterate]->num_elements = %d \n",true_children_lists[iterate]->num_elements);
        if (true_children_lists[iterate]->num_elements > 0) free(true_children_lists[iterate]->elements);
        free(true_children_lists[iterate]);
    }
    //printf("generate lists volume specific free completed\n");
    free(true_overlap_lists);free(raw_overlap_lists);free(parents_lists);free(true_parents_lists);free(true_parents_lists_no_masks);
    free(parents_lists_no_masks);free(true_grandparents_lists);free(grandparents_lists);free(grandparents_lists_no_masks);free(true_grandparents_lists_no_masks);
    free(true_children_lists);
    //printf("generate lists free completed\n");
};

// -------------    Focusing functions   --------------------------------------------------------

// The focusing_data structure is set up by the geometry component, and a pointer to the appropriate
//  focusing function is added to the Volume structure (for this reason the input of all the functions
//  need to be identical, at least in terms of types). In this way there are no if statements to check
//  which of these to be used in the trace, but the focus_data_struct will carry some redundant
//  information, as only the appropriate parameters are set:
// Angular focus on a rectangle (angular_focus_height / angular_focus_width)
// Spatial focus on a rectangle (spatial_focus_height / spatial_focus_width)
// Spatial focus on a disk (sptial_focus_radius)
// No focus (randvec in 4pi) (all set to zero, will select randvec circle as it is slightly faster
//
// When adding a new physical process focusing becomes very easy, as one just calls the master focusing
//  function assosiated with the volume (placed in the geometry struct), using the focus_data_struct
//  also found in the geometry struct, and the process then supports all the focusing modes. It is even
//  possible to add new focusing modes in the future by updating just the geometry components, and this
//  section.

// focus_data_struct definitioon shown here, defined at the start of this file
//struct focus_data_struct {
//Coords Aim;
//double angular_focus_width;
//double angular_focus_height;
//double spatial_focus_width;
//double spatial_focus_height;
//double spatial_focus_radius;
//Rotation absolute_rotation;
//// focusing_function creates a vector per selected criteria of focus_data_struct / selected focus function and returns solid angle
//void (*focusing_function)(Coords*, double*, struct focus_data_struct*);
////                        v_out  , solid_a,
//};

void randvec_target_rect_angular_union(Coords *v_out,double *solid_angle_out, struct focus_data_struct *focus_data) {
    // Calls the standard McStas randvec_target_rect_angular focusing function, but is with the new data input format.
    randvec_target_rect_angular(&v_out->x, &v_out->y, &v_out->z, solid_angle_out, focus_data->Aim.x,focus_data->Aim.y, focus_data->Aim.z, focus_data->angular_focus_width, focus_data->angular_focus_height,focus_data->absolute_rotation);
    //randvec_target_rect_angular(&vx, &vy, &vz, &solid_angle,aim_x, aim_y, aim_z, VarsInc.aw, VarsInc.ah, ROT_A_CURRENT_COMP);
};

void randvec_target_rect_union(Coords *v_out,double *solid_angle_out, struct focus_data_struct *focus_data) {
// Calls the standard McStas randvec_target_rect focusing function, but is with the new data input format.
    randvec_target_rect(&v_out->x, &v_out->y, &v_out->z, solid_angle_out, focus_data->Aim.x,focus_data->Aim.y, focus_data->Aim.z, focus_data->spatial_focus_width, focus_data->spatial_focus_height,focus_data->absolute_rotation);
    // randvec_target_rect(&vx, &vy, &vz, &solid_angle,aim_x, aim_y, aim_z, VarsInc.xw, VarsInc.yh, ROT_A_CURRENT_COMP);
};

void randvec_target_circle_union(Coords *v_out,double *solid_angle_out, struct focus_data_struct *focus_data) {
// Calls the standard McStas randvec_target_circle focusing function, but is with the new data input format.

    // debug input into randvec_target_circle
    //print_position(focus_data->Aim,"Aim vector input for randvec_target_circle");
    //printf("Radius input %f\n",focus_data->spatial_focus_radius);

    randvec_target_circle(&v_out->x, &v_out->y, &v_out->z, solid_angle_out, focus_data->Aim.x,focus_data->Aim.y, focus_data->Aim.z, focus_data->spatial_focus_radius);
    //randvec_target_circle(&vx, &vy, &vz, &solid_angle, aim_x, aim_y, aim_z, focus_r);
};



void focus_initialize(struct geometry_struct *geometry, Coords POS_A_TARGET, Coords POS_A_CURRENT, Rotation ROT_A_CURRENT, int target_index, double target_x, double target_y, double target_z, double angular_focus_width, double angular_focus_height, double spatial_focus_width, double spatial_focus_height, double spatial_focus_radius, char *component_name) {
    // Initialize focusing system
    // target_x/y/z needs to be double setting parameters with default value 0
    // target_index needs to be int setting parameter with default value 0
    // angular_focus_width, angular_focus_height, spatial_focus_width, spatial_focus_height, spatial_focus_radius nneds to be double setting parameters with default value 0
    // When those conditions are met, this code will identify which settings have been entered by the user and select the appropriate focusing parameters, which are loaded into the focus_data struct and the geometry struct.
    // The aim vector in the focus_data struct will be transformed from the local coordinate system of the geometry component to the coordinate system of the master component during the master component initialize

  // Input sanitation
  if (angular_focus_width < 0) {
    printf("\nERROR in Union geometry component named \"%s\", angular focus width focus_aw < 0! \n",component_name);
    exit(1);
  }
  if (angular_focus_height < 0) {
    printf("\nERROR in Union geometry component named \"%s\", angular focus width focus_ah < 0! \n",component_name);
    exit(1);
  }
  if (spatial_focus_width < 0) {
    printf("\nERROR in Union geometry component named \"%s\", spatial focus width focus_xw < 0! \n",component_name);
    exit(1);
  }
  if (spatial_focus_height < 0) {
    printf("\nERROR in Union geometry component named \"%s\", spatial focus height focus_xh < 0! \n",component_name);
    exit(1);
  }
  if (spatial_focus_radius < 0) {
    printf("\nERROR in Union geometry component named \"%s\", spatial focus radius focus_r < 0! \n",component_name);
    exit(1);
  }
  
  struct focus_data_struct focus_data;

  // Initialize focus_data_struct
  /*
  geometry->focus_data.Aim = coords_set(0,0,0);
  geometry->focus_data.angular_focus_width = 0;
  geometry->focus_data.angular_focus_height = 0;
  geometry->focus_data.spatial_focus_width = 0;
  geometry->focus_data.spatial_focus_height = 0;
  geometry->focus_data.spatial_focus_radius = 0;
  rot_copy(geometry->focus_data.absolute_rotation,ROT_A_CURRENT);
  */
  focus_data.Aim = coords_set(0,0,0);
  focus_data.angular_focus_width = 0;
  focus_data.angular_focus_height = 0;
  focus_data.spatial_focus_width = 0;
  focus_data.spatial_focus_height = 0;
  focus_data.spatial_focus_radius = 0;
  rot_copy(focus_data.absolute_rotation,ROT_A_CURRENT);

  // Built on code from Incoherent.comp by Kim Lefmann and Kristian Nielsen
  if (target_index != 0 && !target_x && !target_y && !target_z)
  {
    Coords ToTarget;
    //ToTarget = coords_sub(POS_A_COMP_INDEX(INDEX_CURRENT_COMP+target_index),POS_A_CURRENT_COMP);
    ToTarget = coords_sub(POS_A_TARGET,POS_A_CURRENT);
    //ToTarget = rot_apply(ROT_A_CURRENT_COMP, ToTarget);
    ToTarget = rot_apply(ROT_A_CURRENT, ToTarget);
    coords_get(ToTarget, &focus_data.Aim.x, &focus_data.Aim.y, &focus_data.Aim.z);
  }
  else
  { focus_data.Aim.x = target_x; focus_data.Aim.y = target_y; focus_data.Aim.z = target_z; }
  
  if (!(focus_data.Aim.x || focus_data.Aim.y || focus_data.Aim.z)) {
    // Somehow set a variable to signify scattering into 4pi
    // printf("Union %s: The target is not defined. Using scattering into 4pi.\n",NAME_CURRENT_COMP);
    focus_data.Aim.z=1; // set aim to one so that the randvec output vector has length 1 instead of 0
  }

  int focusing_model_selected = 0;
  if (angular_focus_width != 0 && angular_focus_height != 0) {
    focus_data.focusing_function = &randvec_target_rect_angular_union;
    focus_data.angular_focus_width = DEG2RAD*angular_focus_width; // Convert to radians here
    focus_data.angular_focus_height = DEG2RAD*angular_focus_height;
    focusing_model_selected = 1;
  }
  if (spatial_focus_width != 0 && spatial_focus_height != 0) {
    focus_data.focusing_function = &randvec_target_rect_union;
    focus_data.spatial_focus_width = spatial_focus_width;
    focus_data.spatial_focus_height = spatial_focus_height;
    if (focusing_model_selected) {
        printf("ERROR %s: Select either angular or spatial focusing, not both! Exiting \n",component_name);
        exit(1);
    }
    focusing_model_selected = 1;
  }
  if (spatial_focus_radius != 0) {
    focus_data.focusing_function = &randvec_target_circle_union;
    focus_data.spatial_focus_radius = spatial_focus_radius;
    if (focusing_model_selected) {
        printf("ERROR %s: Select a maximum of one focusing method (spatial rectangle or cicle, or angular rectangle! Exiting \n",component_name);
        exit(1);
    }
    focusing_model_selected = 1;
  }
  if (focusing_model_selected == 0) {
    // Select 4pi focusing
    focus_data.spatial_focus_radius = 0;
    focus_data.focusing_function = &randvec_target_circle_union;
  }
  
  // Allocate the isotropic focus_data struct
  geometry->focus_data_array.num_elements = 0;
  //geometry->focus_data_array.elements = malloc(sizeof(focus_data_struct));
  add_element_to_focus_data_array(&geometry->focus_data_array,focus_data);
};


struct abs_event{
    double time1;
    double position1[3];
    double time2;
    double position2[3];
    double weight_change;
    int volume_index;
    int neutron_id;
};

// Functions for recording absorption
void initialize_absorption_file() {
  FILE *fp;
  fp = fopen("Union_absorption.dat","w");
  fprintf(fp,"r_old x, r_old y, r_old z, old t, r x, r y, r z, new t, weight change, volume index, neutron id \n");
  
  fclose(fp);
}

void write_events_to_file(int last_index, struct abs_event *events) {

  FILE *fp;
  fp = fopen("Union_absorption.dat","a");
  
  struct abs_event *this_event;
  int iterate;
  for (iterate=0; iterate<last_index; iterate++) {
  
    this_event = &events[iterate];
    fprintf(fp,"%g, %g, %g, %g, %g, %g, %g, %g, %e, %i, %i \n",
           this_event->position1[0], this_event->position1[1], this_event->position1[2], this_event->time1,
           this_event->position2[0], this_event->position2[1], this_event->position2[2], this_event->time2,
           this_event->weight_change,
           this_event->volume_index,
           this_event->neutron_id);
  }

  fclose(fp);
}

void record_abs_to_file(double *r, double t1, double *r_old, double t2, double weight_change, int volume, int neutron_id, int *data_index, struct abs_event *events) {
  
  
  
  struct abs_event *this_event;
  
  this_event = &events[(*data_index)++];
  
  //printf("Recording something! %i\n", *data_index);
  
  this_event->position1[0] = r[0];
  this_event->position1[1] = r[1];
  this_event->position1[2] = r[2];
  this_event->time1 = t1;
  this_event->position2[0] = r_old[0];
  this_event->position2[1] = r_old[1];
  this_event->position2[2] = r_old[2];
  this_event->time2 = t2;
  this_event->weight_change = weight_change;
  this_event->volume_index = volume;
  this_event->neutron_id = neutron_id;
  
  
  if (*data_index == 999) {
    
      write_events_to_file(*data_index, events);
      *data_index = 0;

  }

};




/*******************************************************************************
*
*  McStas, neutron ray-tracing package
*  Copyright(C) 2007 Risoe National Laboratory.
*
* %I
* Written by: Mads Bertelsen
* Date: 20.08.15
* Version: $Revision: 0.1 $
* Origin: University of Copenhagen
*
* A sample component to separate geometry and phsysics
*
* %D
* Alpha version, no input system yet
* Hardcode input to geometry engine
* Allows complicated geometry by combination of simple shapes
*
* Algorithm:
* Described elsewhere
*
* %P
* INPUT PARAMETERS:
* radius:  [m] Outer radius of sample in (x,z) plane
*
* OUTPUT PARAMETERS:
* V_rho:  [AA^-3] Atomic density
*
* %L
* The test/example instrument <a href="../examples/Test_Phonon.instr">Test_Phonon.instr</a>.
*
* %E
******************************************************************************/


// This file sets up global lists needed for Union components to communicate with each other
// These all have dynamically allocated memory somewhere in the structure, which is deallocated
//  by the last Union_master.



// Initialize global positions / rotations to transform lists
  // These are lists of pointers to positons / rotations, that will be updated from global frame
  //  to the frame of the master component that uses them in that masters initialize section.
  struct global_positions_to_transform_list_struct global_positions_to_transform_list = {0,NULL};
  struct global_rotations_to_transform_list_struct global_rotations_to_transform_list = {0,NULL};

// Initialize global_process_list
  // Used to facilitate communication between processes and the other types of Union components
  struct pointer_to_global_process_list global_process_list = {0,NULL};

// Initialize global_material_list
  // Used to facilitate communication between materials and the other types of Union components
  struct pointer_to_global_material_list global_material_list = {0,NULL};

// Initialize global_geometry_list
  // Used to facilitate communication between geometries and the other types of Union components
  struct pointer_to_global_geometry_list global_geometry_list = {0,NULL};

// Initialize global_logger_lists
  // Used to facilitate communication between loggers and the other types of Union components
  struct pointer_to_global_logger_list global_all_volume_logger_list = {0,NULL};
  struct pointer_to_global_logger_list global_specific_volumes_logger_list = {0,NULL};

// Initialize global_abs_logger_lists
  struct pointer_to_global_abs_logger_list global_all_volume_abs_logger_list = {0,NULL};
  struct pointer_to_global_abs_logger_list global_specific_volumes_abs_logger_list = {0,NULL};

// Initialize global_tagging_conditional_list
  // Used to facilitate communication between conditionals and the other types of Union components
  struct global_tagging_conditional_list_struct global_tagging_conditional_list = {0,0,NULL};

// Initialize global_master_list
  // Used to facilitate communication between Master components (mainly for deallocation)
  struct pointer_to_global_master_list global_master_list = {0,NULL};

// Initialize global_mantid_min_pixel_id
  // Used for ensuring pixel id's on Mantid monitors do not overlap
  int global_mantid_min_pixel_id = 0;


#endif

// TEST
struct logger_with_data_struct loggers_with_data_array;
struct abs_logger_with_data_struct abs_loggers_with_data_array;

#ifndef MASTER_DETECTOR
    #define MASTER_DETECTOR dummy
#endif


/* Shared user declarations for all components types 'Union_stop'. */
#ifndef Union
#define Union $Revision: 0.8 $

/*******************************************************************************
*
*  McStas, neutron ray-tracing package
*  Copyright(C) 2007 Risoe National Laboratory.
*
* %I
* Written by: Mads Bertelsen
* Date: 20.08.15
* Version: $Revision: 0.1 $
* Origin: University of Copenhagen
*
* Functions and structure definitons for Union components.
*
******************************************************************************/

// -------------    Definition of data structures   ---------------------------------------------
// GPU
enum shape {
  surroundings,
  box,
  sphere,
  cylinder,
  cone,
  mesh
};

enum process {
  Incoherent,
  Powder,
  Single_crystal,
  AF_HB_1D,
  PhononSimple,
  Texture,
  IncoherentPhonon,
  NCrystal,
  Template
};

struct intersection_time_table_struct {
int num_volumes;
int *calculated;
int *n_elements;
double **intersection_times;
};

struct line_segment{
//struct position point1;
//struct position point2;
Coords point1;
Coords point2;
int number_of_dashes;
};

struct pointer_to_1d_int_list {
int num_elements;
int *elements;
#pragma acc shape(elements[0:num_elements]) init_needed(num_elements)
};

struct pointer_to_1d_double_list {
int num_elements;
double *elements;
#pragma acc shape(elements[0:num_elements]) init_needed(num_elements)
};

struct pointer_to_1d_coords_list {
int num_elements;
Coords *elements;
#pragma acc shape(elements[0:num_elements]) init_needed(num_elements)
};

struct lines_to_draw{
int number_of_lines;
struct line_segment *lines;
#pragma acc shape(lines[0:number_of_lines]) init_needed(number_of_lines)
};

// 2D lists not needed anyway
// struct pointer_to_2d_int_list {
// int n_lists;
// struct pointer_to_1d_int_list *lists;
// }

// Todo: see if the union geometry_parameter_union and other geometry structs can be here
union geometry_parameter_union{
    struct sphere_storage   *p_sphere_storage;
    struct cylinder_storage *p_cylinder_storage;
    struct box_storage      *p_box_storage;
    struct cone_storage     *p_cone_storage;
    struct mesh_storage     *p_mesh_storage;
    // add as many pointers to structs as wanted, without increasing memory footprint.
};


struct rotation_struct{
double x;
double y;
double z;
};

struct focus_data_struct {
Coords Aim;
double angular_focus_width;
double angular_focus_height;
double spatial_focus_width;
double spatial_focus_height;
double spatial_focus_radius;
Rotation absolute_rotation;
// focusing_function creates a vector per selected criteria of focus_data_struct / selected focus function and returns solid angle
void (*focusing_function)(Coords*, double*, struct focus_data_struct*);
//                        v_out  , solid_a,
};

struct focus_data_array_struct
{
struct focus_data_struct *elements;
int num_elements;
#pragma acc shape(elements[0:num_elements]) init_needed(num_elements)
};

struct Detector_3D_struct {
  char title_string[256];
  char string_axis_1[256];
  char string_axis_2[256];
  char string_axis_3[256];
  char Filename[256];
  double D1min;
  double D1max;
  double D2min;
  double D2max;
  double D3min;
  double D3max;
  double bins_1; // McStas uses doubles for bin numbers for some reason
  double bins_2;
  double bins_3;
  double ***Array_N; // McStas uses doubles for number of rays in each bin for some reason
  double ***Array_p;
  double ***Array_p2;
};

struct Detector_2D_struct {
  char title_string[256];
  char string_axis_1[256];
  char string_axis_2[256];
  char Filename[256];
  double D1min;
  double D1max;
  double D2min;
  double D2max;
  double bins_1; // McStas uses doubles for bin numbers for some reason
  double bins_2;
  double **Array_N; // McStas uses doubles for number of rays in each bin for some reason
  double **Array_p;
  double **Array_p2;
};

struct Detector_1D_struct {
  char title_string[256];
  char string_axis[256];
  char string_axis_short[64];
  char string_axis_value[256];
  char Filename[256];
  double min;
  double max;
  double bins; // McStas uses doubles for bin numbers for some reason
  double *Array_N; // McStas uses doubles for number of rays in each bin for some reason
  double *Array_p;
  double *Array_p2;
};


union logger_data_union{
  struct a_2DQ_storage_struct *p_2DQ_storage;
  struct a_2DS_storage_struct *p_2DS_storage;
  struct a_3DS_storage_struct *p_3DS_storage;
  struct a_1D_storage_struct *p_1D_storage;
  struct a_2DS_t_storage_struct *p_2DS_t_storage;
  struct a_2D_kf_storage_struct *p_2D_kf_storage;
  struct a_2D_kf_t_storage_struct *p_2D_kf_t_storage;
  // Additional logger storage structs to be addedd
};

struct logger_with_data_struct {
  int used_elements;
  int allocated_elements;
  struct logger_struct **logger_pointers;
};

// logger_pointer_struct
// contains pointers to the different logger functions and it's data union
struct logger_pointer_set_struct {
  // The logger has two record functions, an active and an inactive. Normally the active one will be to permanent storage,
  //  but if a conditional has been defined, it can switch the two, making the active one recording to temporary, which
  //  can then be filtered based on the future path of the ray
  
  // function input Coords position, k[3], k_old[3], p, p_old, NV, NPV, N, logger_data_union, logger_with_data_struct
  void (*active_record_function)(Coords*, double*, double*, double, double, double, int, int, int, struct logger_struct*, struct logger_with_data_struct*);
  void (*inactive_record_function)(Coords*, double*, double*, double, double, double, int, int, int, struct logger_struct*, struct logger_with_data_struct*);
  
  // A clear temporary data function (for new ray)
  void (*clear_temp)(union logger_data_union*);
  
  // Write temporary to permanent is used when the record to temp function is active, and the condition is met.
  void (*temp_to_perm)(union logger_data_union*);
  
  // Write temporary final_p to permanent is used when the record to temp function is active, and the condition is met
  //  and the final weight is given to be used for all stored events in the logger.
  void (*temp_to_perm_final_p)(union logger_data_union*, double);
  
  // Select which temp_to_perm function to use
  int select_t_to_p; // 1: temp_to_perm, 2: temp_to_perm_final_p
  
};

union abs_logger_data_union{
  struct a_2D_abs_storage_struct *p_2D_abs_storage;
  struct a_1D_abs_storage_struct *p_1D_abs_storage;
  struct a_1D_time_abs_storage_struct *p_1D_time_abs_storage;
  struct a_1D_time_to_lambda_abs_storage_struct *p_1D_time_to_lambda_abs_storage;
  struct a_event_abs_storage_struct *p_event_abs_storage;
  struct a_1D_event_abs_storage_struct *p_1D_event_abs_storage;
  // Additional logger storage structs to be addedd
};

struct abs_logger_with_data_struct {
  int used_elements;
  int allocated_elements;
  struct abs_logger_struct **abs_logger_pointers;
};

struct abs_logger_pointer_set_struct {
  // The logger has two record functions, an active and an inactive. Normally the active one will be to permanent storage,
  //  but if a conditional has been defined, it can switch the two, making the active one recording to temporary, which
  //  can then be filtered based on the future path of the ray
  
  // function input Coords position, k[3], p, NV, N, logger_data_union, logger_with_data_struct
  void (*active_record_function)(Coords*, double*, double,  double, int, int, struct abs_logger_struct*, struct abs_logger_with_data_struct*);
  void (*inactive_record_function)(Coords*, double*, double,  double, int, int, struct abs_logger_struct*, struct abs_logger_with_data_struct*);
  
  // A clear temporary data function (for new ray)
  void (*clear_temp)(union abs_logger_data_union*);
  
  // Write temporary to permanent is used when the record to temp function is active, and the condition is met.
  void (*temp_to_perm)(union abs_logger_data_union*);
  
  // Write temporary final_p to permanent is used when the record to temp function is active, and the condition is met
  //  and the final weight is given to be used for all stored events in the logger.
  void (*temp_to_perm_final_p)(union abs_logger_data_union*, double);
  
  // Select which temp_to_perm function to use
  //int select_t_to_p; // 1: temp_to_perm, 2: temp_to_perm_final_p
  
};


struct conditional_standard_struct{
  // Data to be transfered to the conditional function
  double Emax;
  double Emin;
  int E_limit;
  
  double Tmin;
  double Tmax;
  int T_limit;
  
  int volume_index;
  
  double Total_scat_max;
  double Total_scat_min;
  int Total_scat_limit;
  
  double exit_volume_index;
  
  // Test
  Coords test_position;
  Rotation test_rotation;
  Rotation test_t_rotation;
};

struct conditional_PSD_struct{
  double PSD_half_xwidth;
  double PSD_half_yheight;
  
  double Tmin;
  double Tmax;
  int T_limit;
  
  // Position of the PSD
  Coords PSD_position;
  Rotation PSD_rotation;
  Rotation PSD_t_rotation;
};

union conditional_data_union {
  struct conditional_standard_struct *p_standard;
  struct conditional_PSD_struct *p_PSD;
  // Add more as conditional components are made
};

// General input for conditional functions: Position, Velocity/Wavevector, weight, time, total_scat, scattered_flag, scattered_flag_VP,
// Optional extras: data_union? tree base(s)? tree base for current ray?
typedef int (*conditional_function_pointer)(union conditional_data_union*,Coords*, Coords*, double*, double*, int*, int*, int*, int**);
//typedef int (**conditional_function_pointer_array)(union *conditional_data_union,Coords*, Coords*, double*, double*, int*, int*, int**);

struct conditional_list_struct{
  int num_elements;
  
  union conditional_data_union **p_data_unions;
  conditional_function_pointer *conditional_functions;
  //int (**conditional_functions)(Coords*, Coords*, double*, double*, int*, int*, int**);
};

struct logger_struct {
  char name[256];
  // Contains ponters to all the functions assosiated with this logger
  struct logger_pointer_set_struct function_pointers;
  // Contains hard copy of logger_data_union since the size is the same as a pointer.
  union logger_data_union data_union;
  
  int logger_extend_index; // Contain index conditional_extend_array defined in master that can be acsessed from extend section.
  
  struct conditional_list_struct conditional_list;
};


// To be stored in volume, a list of pointers to the relevant loggers corresponding to each process
struct logger_for_each_process_list {
  int num_elements;
  struct logger_struct **p_logger_process;
};

// List of logger_for_each_process_list
struct loggers_struct {
  int num_elements;
  struct logger_for_each_process_list *p_logger_volume;
  #pragma acc shape(p_logger_volume[0:num_elements]) init_needed(num_elements)
};


struct abs_logger_struct {
  char name[256];
  // Contains ponters to all the functions assosiated with this logger
  struct abs_logger_pointer_set_struct function_pointers;
  // Contains hard copy of logger_data_union since the size is the same as a pointer.
  union abs_logger_data_union data_union;
  
  // Position and rotation of the abs_logger
  Coords position;
  Rotation rotation;
  Rotation t_rotation;
  
  int abs_logger_extend_index; // Contain index conditional_extend_array defined in master that can be acsessed from extend section.
  
  struct conditional_list_struct conditional_list;
};

// To be stored in volume, a list of pointers to the relevant abs loggers corresponding to each process
/*
struct abs_logger_for_each_process_list {
  int num_elements;
  struct abs_logger_struct **p_abs_logger_process;
};
*/

// List of abs logger_for_each_process_list
struct abs_loggers_struct {
  int num_elements;
  //struct abs_logger_for_each_process_list *p_abs_logger_volume;
  struct abs_logger_struct **p_abs_logger;
};



struct geometry_struct
{
char shape[64];     // name of shape used (sphere, cylinder, box, off, ...)
enum shape eShape;  // enum with shape for flexible functions GPU
double priority_value;    // priority of the geometry
Coords center;      // Center position of volume, reported by components in global frame, updated to main frame in initialize
// Rotation of this volume
Rotation rotation_matrix; // rotation matrix of volume, reported by component in global frame, updated to main frame in initialize
Rotation transpose_rotation_matrix; // As above
// Array of prrotation matrixes for processes assigned to this volume (indexed by non_isotropic_rot_index in the processes)
Rotation *process_rot_matrix_array;             // matrix that transforms from main coordinate system to local process in this specific volume
Rotation *transpose_process_rot_matrix_array;   // matrix that transforms from local process in this specific volume to main coordinate system
int process_rot_allocated;  // Keeps track of allocation status of rot_matrix_array

struct rotation_struct rotation; // Not used, is the x y and z rotation angles.
int visualization_on; // If visualization_on is true, the volume will be drawn in mcdisplay, otherwise not
int is_exit_volume; // If is exit volume = 1, the ray will exit the component when it enters this volume.
int is_mask_volume; // 1 if volume itself is a mask (masking the ones in it's mask list), otherwise 0
int mask_index;
int is_masked_volume; // 1 if this volume is being masked by another volume, the volumes that mask it is in masked_by_list
int mask_mode; // ALL/ANY 1/2. In ALL mode, only parts covered by all masks is simulated, in ANY mode, area covered by just one mask is simulated
double geometry_p_interact; // fraction of rays that interact with this volume for each scattering (between 0 and 1, 0 for disable)
union geometry_parameter_union geometry_parameters; // relevant parameters for this shape
union geometry_parameter_union (*copy_geometry_parameters)(union geometry_parameter_union*);
struct focus_data_struct focus_data; // Used for focusing from this geometry

// New focus data implementation to remove the focusing bug for non-isotripic processes
struct focus_data_array_struct focus_data_array;
struct pointer_to_1d_int_list focus_array_indices; // Add 1D integer array with indecies for correct focus_data for each process


// intersect_function takes position/velocity of ray and parameters, returns time list
int (*intersect_function)(double*,int*,double*,double*,struct geometry_struct*);
//                        t_array,n_ar,r      ,v

// within_function that checks if the ray origin is within this volume
int (*within_function)(Coords,struct geometry_struct*);
//                     r,      parameters

// mcdisplay function, draws the geometry
//void (*mcdisplay_function)(struct lines_to_draw*,int,struct Volume_struct**,int);
void (*mcdisplay_function)(struct lines_to_draw*,int,struct geometry_struct**,int);
//                                lines          index             Geometries   N

void (*initialize_from_main_function)(struct geometry_struct*);

struct pointer_to_1d_coords_list (*shell_points)(struct geometry_struct*, int maximum_number_of_points);

// List of other volumes to be check when ray starts within this volume.
struct pointer_to_1d_int_list intersect_check_list;
// List of other volumes the ray may enter, if the ray intersects the volume itself.
struct pointer_to_1d_int_list destinations_list;
// The destinations list stored as a logic list which makes some tasks quicker. OBSOLETE
//struct pointer_to_1d_int_list destinations_logic_list;
// Reduced list of other volumes the ray may enter, if the ray intersects the volume itself.
struct pointer_to_1d_int_list reduced_destinations_list;
// List of other volumes that are within this volume
struct pointer_to_1d_int_list children;
// List of other volumes that are within this volume, but does not have any parents that are children of this volume
struct pointer_to_1d_int_list direct_children;
// List of next possible volumes (only used in tagging)
struct pointer_to_1d_int_list next_volume_list;
// List of volumes masked by this volume (usually empty)
struct pointer_to_1d_int_list mask_list;
// List of volumes masking this volume
struct pointer_to_1d_int_list masked_by_list;
// List of masks masking this volume (global mask indices)
struct pointer_to_1d_int_list masked_by_mask_index_list;
// Additional intersect lists dependent on mask status
//struct indexed_mask_lists_struct mask_intersect_lists;
// Simpler way of storing the mask_intersect_lists
struct pointer_to_1d_int_list mask_intersect_list;
};

struct physics_struct
{
char name[256]; // User defined material name
int interact_control;
int is_vacuum;
double my_a;
int number_of_processes;
// pointer to array of pointers to physics_sub structures that each describe a scattering process
struct scattering_process_struct *p_scattering_array;
};

union data_transfer_union{
    // List of pointers to storage structs for all supported physical processes
    struct Incoherent_physics_storage_struct  *pointer_to_a_Incoherent_physics_storage_struct;
    struct Powder_physics_storage_struct *pointer_to_a_Powder_physics_storage_struct;
    struct Single_crystal_physics_storage_struct *pointer_to_a_Single_crystal_physics_storage_struct;
    struct AF_HB_1D_physics_storage_struct *pointer_to_a_AF_HB_1D_physics_storage_struct;
    struct IncoherentPhonon_physics_storage_struct *pointer_to_a_IncoherentPhonon_physics_storage_struct;
    struct PhononSimpleNumeric_physics_storage_struct *pointer_to_a_PhononSimpleNumeric_storage_struct;
    struct PhononSimple_physics_storage_struct *pointer_to_a_PhononSimple_storage_struct;
    struct MagnonSimple_physics_storage_struct *pointer_to_a_MagnonSimple_storage_struct;
    struct Sans_spheres_physics_storage_struct *pointer_to_a_Sans_spheres_physics_storage_struct;
    struct Texture_physics_storage_struct *pointer_to_a_Texture_physics_storage_struct;
    struct NCrystal_physics_storage_struct *pointer_to_a_NCrystal_physics_storage_struct;
    struct Non_physics_storage_struct *pointer_to_a_Non_physics_storage_struct;
    struct Template_physics_storage_struct *pointer_to_a_Template_physics_storage_struct;
    // possible to add as many structs as wanted, without increasing memory footprint.
};



struct scattering_process_struct
{
char name[256];                // User defined process name
enum process eProcess;         // enum value corresponding to this process GPU
double process_p_interact;     // double between 0 and 1 that describes the fraction of events forced to undergo this process. -1 for disable
int non_isotropic_rot_index;   // -1 if process is isotrpic, otherwise is the index of the process rotation matrix in the volume
Rotation rotation_matrix;      // rotation matrix of process, reported by component in local frame, transformed and moved to volume struct in main

union data_transfer_union data_transfer; // The way to reach the storage space allocated for this process (see examples in process.comp files)

// probability_for_scattering_functions calculates this probability given k_i and parameters
int (*probability_for_scattering_function)(double*,double*,union data_transfer_union,struct focus_data_struct*, _class_particle *_particle);
//                                         prop,   k_i,   ,parameters               , focus data / function

// A scattering_function takes k_i and parameters, returns k_f
int (*scattering_function)(double*,double*,double*,union data_transfer_union,struct focus_data_struct*, _class_particle *_particle);
//                         k_f,    k_i,    weight, parameters               , focus data / function
};

struct Volume_struct
{
char name[256]; // User defined volume name
struct geometry_struct geometry;        // Geometry properties (including intersect functions, generated lists)
struct physics_struct *p_physics;       // Physical properties (list of scattering processes, absorption)
struct loggers_struct loggers;          // Loggers assosiated with this volume
struct abs_loggers_struct abs_loggers;  // Loggers assosiated with this volume
};

// example of calling a scattering process
// volume_pointer_list[3]->physics.scattering_process[5].probability_for_scattering_function(input,volume_pointer_list[3]->physics.scattering_process[5])

struct starting_lists_struct
{
struct pointer_to_1d_int_list allowed_starting_volume_logic_list;
struct pointer_to_1d_int_list reduced_start_list;
struct pointer_to_1d_int_list start_logic_list;
struct pointer_to_1d_int_list starting_destinations_list;
};

struct global_positions_to_transform_list_struct {
int num_elements;
Coords **positions;
};

struct global_rotations_to_transform_list_struct {
int num_elements;
Rotation **rotations;
};

struct global_process_element_struct
{
char name[128]; // Name of the process
int component_index;
struct scattering_process_struct *p_scattering_process;
};

struct pointer_to_global_process_list {
int num_elements;
struct global_process_element_struct *elements;
};

struct global_material_element_struct
{
char name[128];
int component_index;
struct physics_struct *physics;
};

struct pointer_to_global_material_list {
int num_elements;
struct global_material_element_struct *elements;
};

struct global_geometry_element_struct
{
char name[128];
int component_index;
int activation_counter;
int stored_copies;
int active;
struct Volume_struct *Volume;
};

struct pointer_to_global_geometry_list {
int num_elements;
struct global_geometry_element_struct *elements;
};

struct global_logger_element_struct {
char name[128];
int component_index;
struct logger_struct *logger;
};

struct pointer_to_global_logger_list {
int num_elements;
struct global_logger_element_struct *elements;
};

struct global_abs_logger_element_struct {
char name[128];
int component_index;
struct abs_logger_struct *abs_logger;
};

struct pointer_to_global_abs_logger_list {
int num_elements;
struct global_abs_logger_element_struct *elements;
};

struct global_tagging_conditional_element_struct {
struct conditional_list_struct conditional_list;
int extend_index;
char name[1024];
int use_status;
};

struct global_tagging_conditional_list_struct {
int num_elements;
int current_index;
struct global_tagging_conditional_element_struct *elements;
};


struct global_master_element_struct {
char name[128];
int component_index;
int stored_number_of_scattering_events; // TEST
struct conditional_list_struct *tagging_conditional_list_pointer;
};

struct pointer_to_global_master_list {
int num_elements;
struct global_master_element_struct *elements;
};


// -------------    Physics functions   ---------------------------------------------------------

//#include "Test_physics.c"
//#include "Incoherent_test.c"

// -------------    General functions   ---------------------------------------------------------
double distance_between(Coords position1,Coords position2) {
    return sqrt((position1.x-position2.x)*(position1.x-position2.x) +
                (position1.y-position2.y)*(position1.y-position2.y) +
                (position1.z-position2.z)*(position1.z-position2.z));
};



double length_of_3vector(double *r) {
        return sqrt(r[0]*r[0]+r[1]*r[1]+r[2]*r[2]);
    };

double length_of_position_vector(Coords point) {
        return sqrt(point.x*point.x+point.y*point.y+point.z*point.z);
    };

Coords make_position(double *r) {
    Coords temp;
    
    temp.x = r[0];temp.y = r[1];temp.z = r[2];
    return temp;
};

Coords coords_scalar_mult(Coords input,double scalar) {
    return coords_set(scalar*input.x,scalar*input.y,scalar*input.z);
};

double union_coords_dot(Coords vector1,Coords vector2) {
    return vector1.x*vector2.x + vector1.y*vector2.y + vector1.z*vector2.z;
}


int sum_int_list(struct pointer_to_1d_int_list list) {
    int iterate,sum = 0;
    for (iterate = 0;iterate < list.num_elements;iterate++) sum += list.elements[iterate];
    return sum;
    };
    
int on_int_list(struct pointer_to_1d_int_list list,int target) {
    int iterate,output=0;
    for (iterate = 0; iterate<list.num_elements ;iterate++) {
        if (list.elements[iterate] == target) output = 1;
    }
    return output;
    };

int find_on_int_list(struct pointer_to_1d_int_list list,int target) {
    int iterate;
    for (iterate = 0;iterate < list.num_elements;iterate++) {
        if (list.elements[iterate] == target) return iterate;
    }
    return -1;
    };
    
void on_both_int_lists(struct pointer_to_1d_int_list *list1, struct pointer_to_1d_int_list *list2, struct pointer_to_1d_int_list *common) {
    // Assume common.elements is allocated to a number of int's large enough to hold the resulting list
    int iterate,used_elements=0;
    for (iterate=0;iterate<list1->num_elements;iterate++) {
        if (on_int_list(*list2,list1->elements[iterate])) common->elements[used_elements++] = list1->elements[iterate];
    }
    common->num_elements = used_elements;
    };

void remove_element_in_list_by_index(struct pointer_to_1d_int_list *list,int index) {
    if (index >= list->num_elements) {
      printf("ERROR(remove_element_in_list_by_index): trying to remove an index that wasn't allocated to begin with");
      exit(1);
    }
    else {
        int iterate;
        int *temp;
        for (iterate = index;iterate < list->num_elements -1;iterate++) {
            list->elements[iterate] = list->elements[iterate+1];
        }
        list->num_elements--;
        //if (list->num_elements==0) printf("Making empty list!\n");
        temp = malloc(list->num_elements * sizeof(int));
        for (iterate = 0;iterate < list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
        free(list->elements);
        list->elements = malloc(list->num_elements * sizeof(int));
        for (iterate = 0;iterate < list->num_elements;iterate++) list->elements[iterate] = temp[iterate];
        free(temp);

    }
    };
    
void remove_element_in_list_by_value(struct pointer_to_1d_int_list *list,int value) {
    int iterate;
    for (iterate = 0;iterate < list->num_elements;iterate++) {
        if (list->elements[iterate] == value) remove_element_in_list_by_index(list,iterate);
    }
    };
    
void merge_lists(struct pointer_to_1d_int_list *result,struct pointer_to_1d_int_list *list1,struct pointer_to_1d_int_list *list2) {
    if (result->num_elements > 0) free(result->elements);
    result->num_elements = list1->num_elements + list2->num_elements;
    if (result->num_elements != 0) {
      result->elements = malloc(result->num_elements*sizeof(int));
      int iterate;
      for (iterate = 0;iterate < list1->num_elements;iterate++)
          result->elements[iterate] = list1->elements[iterate];
      for (iterate = 0;iterate < list2->num_elements;iterate++)
          result->elements[list1->num_elements+iterate] = list2->elements[iterate];
    }
    };

void add_element_to_double_list(struct pointer_to_1d_double_list *list,double value) {
    if (list->num_elements == 0) {
      list->num_elements++;
      list-> elements = malloc(list->num_elements*sizeof(double));
      list-> elements[0] = value;
    } else {
      double temp[list->num_elements];
      int iterate;
      for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
      free(list->elements);
      list->num_elements++;
      list-> elements = malloc(list->num_elements*sizeof(double));
      for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
      list->elements[list->num_elements-1] = value;
    }
    };

void add_element_to_int_list(struct pointer_to_1d_int_list *list,int value) {
    if (list->num_elements == 0) {
      list->num_elements++;
      list-> elements = malloc(list->num_elements*sizeof(int));
      list-> elements[0] = value;
    } else {
      int temp[list->num_elements];
      int iterate;
      for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
      free(list->elements);
      list->num_elements++;
      list-> elements = malloc(list->num_elements*sizeof(int));
      for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
      list->elements[list->num_elements-1] = value;
    }
    };

// Need to check if absolute_rotation is preserved correctly.
void add_element_to_focus_data_array(struct focus_data_array_struct *focus_data_array,struct focus_data_struct focus_data) {
    if (focus_data_array->num_elements == 0) {
      focus_data_array->num_elements++;
      focus_data_array-> elements = malloc(focus_data_array->num_elements*sizeof(struct focus_data_struct));
      focus_data_array-> elements[0] = focus_data;
    } else {
      struct focus_data_struct temp[focus_data_array->num_elements];
      int iterate;
      for (iterate=0;iterate<focus_data_array->num_elements;iterate++) temp[iterate] = focus_data_array->elements[iterate];
      free(focus_data_array->elements);
      focus_data_array->num_elements++;
      focus_data_array-> elements = malloc(focus_data_array->num_elements*sizeof(struct focus_data_struct));
      for (iterate=0;iterate<focus_data_array->num_elements-1;iterate++) focus_data_array->elements[iterate] = temp[iterate];
      focus_data_array->elements[focus_data_array->num_elements-1] = focus_data;
    }
    };


void add_to_logger_with_data(struct logger_with_data_struct *logger_with_data, struct logger_struct *logger) {
    // May reorder the order of the if conditions to avoid checking the == 0 for every single ray
    if (logger_with_data->allocated_elements == 0) {
        logger_with_data->allocated_elements = 5;
        logger_with_data->logger_pointers = malloc(logger_with_data->allocated_elements*sizeof(struct logger_struct*));
        logger_with_data->used_elements = 1;
        logger_with_data->logger_pointers[0] = logger;
    } else if (logger_with_data->used_elements > logger_with_data->allocated_elements-1) {
        struct logger_with_data_struct temp_logger_with_data;
        temp_logger_with_data.logger_pointers = malloc((logger_with_data->used_elements)*sizeof(struct logger_struct*));
        int iterate;
        for (iterate=0;iterate<logger_with_data->used_elements;iterate++) {
            temp_logger_with_data.logger_pointers[iterate]  = logger_with_data->logger_pointers[iterate];
        }
        free(logger_with_data->logger_pointers);
        logger_with_data->allocated_elements = logger_with_data->allocated_elements+5;
        logger_with_data->logger_pointers = malloc(logger_with_data->allocated_elements*sizeof(struct logger_struct*));
        for (iterate=0;iterate<logger_with_data->used_elements;iterate++) {
            logger_with_data->logger_pointers[iterate]  = temp_logger_with_data.logger_pointers[iterate];
        }

        logger_with_data->logger_pointers[logger_with_data->used_elements++] = logger;
        
        
    } else {
        logger_with_data->logger_pointers[logger_with_data->used_elements++] = logger;
    }
    
};

void add_to_abs_logger_with_data(struct abs_logger_with_data_struct *abs_logger_with_data, struct abs_logger_struct *abs_logger) {
    // May reorder the order of the if conditions to avoid checking the == 0 for every single ray
    if (abs_logger_with_data->allocated_elements == 0) {
        abs_logger_with_data->allocated_elements = 5;
        abs_logger_with_data->abs_logger_pointers = malloc(abs_logger_with_data->allocated_elements*sizeof(struct abs_logger_struct*));
        abs_logger_with_data->used_elements = 1;
        abs_logger_with_data->abs_logger_pointers[0] = abs_logger;
    } else if (abs_logger_with_data->used_elements > abs_logger_with_data->allocated_elements-1) {
        struct abs_logger_with_data_struct temp_abs_logger_with_data;
        temp_abs_logger_with_data.abs_logger_pointers = malloc((abs_logger_with_data->used_elements)*sizeof(struct abs_logger_struct*));
        int iterate;
        for (iterate=0;iterate<abs_logger_with_data->used_elements;iterate++) {
            temp_abs_logger_with_data.abs_logger_pointers[iterate]  = abs_logger_with_data->abs_logger_pointers[iterate];
        }
        free(abs_logger_with_data->abs_logger_pointers);
        abs_logger_with_data->allocated_elements = abs_logger_with_data->allocated_elements+5;
        abs_logger_with_data->abs_logger_pointers = malloc(abs_logger_with_data->allocated_elements*sizeof(struct abs_logger_struct*));
        for (iterate=0;iterate<abs_logger_with_data->used_elements;iterate++) {
            abs_logger_with_data->abs_logger_pointers[iterate] = temp_abs_logger_with_data.abs_logger_pointers[iterate];
        }

        abs_logger_with_data->abs_logger_pointers[abs_logger_with_data->used_elements++] = abs_logger;
        
        
    } else {
        abs_logger_with_data->abs_logger_pointers[abs_logger_with_data->used_elements++] = abs_logger;
    }
    
};


// Used typedef to avoid having to change this function later. May update others to use same phillosphy.
void add_function_to_conditional_list(struct conditional_list_struct *list,conditional_function_pointer new, union conditional_data_union *data_union) {
    if (list->num_elements == 0) {
      list->num_elements++;
      list->conditional_functions = malloc(list->num_elements*sizeof(conditional_function_pointer));
      list->p_data_unions = malloc(list->num_elements*sizeof(union conditional_data_union*));
      list->conditional_functions[0] = new;
      list->p_data_unions[0] = data_union;
    }
    else {
    conditional_function_pointer temp_fp[list->num_elements];
    union conditional_data_union *temp_du[list->num_elements];
    int iterate;
    // Could even get away with a shallow copy here instead of the loop, but it is not relevant for performance.
    for (iterate=0;iterate<list->num_elements;iterate++) {
      temp_fp[iterate] = list->conditional_functions[iterate];
      temp_du[iterate] = list->p_data_unions[iterate];
    }
    free(list->conditional_functions);
    free(list->p_data_unions);
    list->num_elements++;
    list->conditional_functions = malloc(list->num_elements*sizeof(conditional_function_pointer));
    list->p_data_unions = malloc(list->num_elements*sizeof(union conditional_data_union*));
    
    for (iterate=0;iterate<list->num_elements-1;iterate++) {
      list->conditional_functions[iterate] = temp_fp[iterate];
      list->p_data_unions[iterate] = temp_du[iterate];
    }
    list->conditional_functions[list->num_elements-1] = new;
    list->p_data_unions[list->num_elements-1] = data_union;
    }
};


// could make function that removes a element from a 1d_int_list, and have each list generation as a function that takes a copy of an overlap list

void print_1d_int_list(struct pointer_to_1d_int_list list,char *name) {
        int iterate;
        printf("LIST: ");printf("%s",name);printf(" = [");
        for (iterate = 0; iterate < list.num_elements; iterate++) {
            printf("%d",list.elements[iterate]);
            if (iterate < list.num_elements - 1) printf(",");
        }
        printf("]\n");
    };

void print_1d_double_list(struct pointer_to_1d_double_list list,char *name) {
        int iterate;
        printf("LIST: ");printf("%s",name);printf(" = [");
        for (iterate = 0; iterate < list.num_elements; iterate++) {
            printf("%f",list.elements[iterate]);
            if (iterate < list.num_elements - 1) printf(",");
        }
        printf("]\n");
    };

void print_position(Coords pos,char *name) {
    printf("POSITION: ");printf("%s",name);printf(" = (%f,%f,%f)\n",pos.x,pos.y,pos.z);
    };

void print_rotation(Rotation rot, char *name) {
    printf("ROT MATRIX: %s \n",name);
    printf("[%f %f %f]\n",rot[0][0],rot[0][1],rot[0][2]);
    printf("[%f %f %f]\n",rot[1][0],rot[1][1],rot[1][2]);
    printf("[%f %f %f]\n\n",rot[2][0],rot[2][1],rot[2][2]);
};
    
void allocate_list_from_temp(int num_elements,struct pointer_to_1d_int_list original,struct pointer_to_1d_int_list *new) {
        int iterate;
    
        new->num_elements = num_elements;
        if (num_elements > 0) {
            new->elements = malloc(num_elements*sizeof(int));
            for (iterate = 0;iterate < num_elements; iterate++) new->elements[iterate] = original.elements[iterate];
        } else new->elements = NULL;
    
    };

void allocate_logic_list_from_temp(int num_elements,struct pointer_to_1d_int_list original, struct pointer_to_1d_int_list *new) {
        // A logic list shares the same structure of a normal list, but instead of listing numbers, it is a list of yes / no (1/0)
        // Giving this function a list of [1 3 5] (and num_elements = 9) would return [0 1 0 1 0 1 0 0 0];
        int iterate;
    
        new->num_elements = num_elements;
        if (num_elements > 0) {
            new->elements = malloc(num_elements*sizeof(int));
            for (iterate = 0;iterate < num_elements;iterate++) new->elements[iterate] = 0;
            for (iterate = 0;iterate < original.num_elements;iterate++) {
                if (original.elements[iterate] < num_elements)
                    new->elements[original.elements[iterate]] = 1;
                else printf("Trying to allocate logical list without enough memory\n");
            }
        } else new->elements = NULL;
    };

/*
struct global_positions_to_transform_list_struct {
int num_elements;
Coords **positions;
}

struct global_rotations_to_transform_list_struct {
int num_elements;
Rotation **rotations;
}
*/

void add_position_pointer_to_list(struct global_positions_to_transform_list_struct *list, Coords *new_position_pointer) {
    if (list->num_elements == 0) {
      list->num_elements++;
      list->positions = malloc(list->num_elements*sizeof(Coords*));
      list->positions[0] = new_position_pointer;
    } else {
      Coords **temp;
      temp = malloc(list->num_elements*sizeof(Coords*));
      if (temp == NULL) printf("malloc failed in add_position_pointer_to_list for temp\n");
      
      int iterate;
      for (iterate=0;iterate<list->num_elements;iterate++)
        temp[iterate] = list->positions[iterate];
      free(list->positions);
      list->num_elements++;
      list->positions = malloc(list->num_elements*sizeof(Coords*));
      if (list->positions == NULL) printf("malloc failed in add_position_pointer_to_list for list->positions\n");
      
      for (iterate=0;iterate<list->num_elements-1;iterate++)
        list->positions[iterate] = temp[iterate];
      free(temp);
      list->positions[list->num_elements-1] = new_position_pointer;
    }
};

void add_rotation_pointer_to_list(struct global_rotations_to_transform_list_struct *list, Rotation *new_rotation_pointer) {
    if (list->num_elements == 0) {
      list->num_elements++;
      list->rotations = malloc(list->num_elements*sizeof(Rotation*));
      list->rotations[0] = new_rotation_pointer;
    } else {
      Rotation **temp;
      temp = malloc(list->num_elements*sizeof(Rotation*));
      int iterate;
      for (iterate=0;iterate<list->num_elements;iterate++)
        temp[iterate] = list->rotations[iterate];
      free(list->rotations);
      list->num_elements++;
      list->rotations = malloc(list->num_elements*sizeof(Rotation*));
      
      for (iterate=0;iterate<list->num_elements-1;iterate++)
        list->rotations[iterate] = temp[iterate];
      free(temp);
      list->rotations[list->num_elements-1] = new_rotation_pointer;
    }
};

void add_element_to_process_list(struct pointer_to_global_process_list *list,struct global_process_element_struct new_element) {
    if (list->num_elements == 0) {
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_process_element_struct));
    list-> elements[0] = new_element;
    }
    else {
    struct global_process_element_struct temp[list->num_elements];
    int iterate;
    for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
    free(list->elements);
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_process_element_struct));
    for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
    list->elements[list->num_elements-1] = new_element;
    }
};

void add_element_to_material_list(struct pointer_to_global_material_list *list,struct global_material_element_struct new_element) {
    if (list->num_elements == 0) {
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_material_element_struct));
    list-> elements[0] = new_element;
    }
    else {
    struct global_material_element_struct temp[list->num_elements];
    int iterate;
    for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
    free(list->elements);
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_material_element_struct));
    for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
    list->elements[list->num_elements-1] = new_element;
    }
};

void add_element_to_geometry_list(struct pointer_to_global_geometry_list *list,struct global_geometry_element_struct new_element) {
    if (list->num_elements == 0) {
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_geometry_element_struct));
    list-> elements[0] = new_element;
    }
    else {
    struct global_geometry_element_struct temp[list->num_elements];
    int iterate;
    for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
    free(list->elements);
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_geometry_element_struct));
    for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
    list->elements[list->num_elements-1] = new_element;
    }
};

void add_element_to_logger_list(struct pointer_to_global_logger_list *list,struct global_logger_element_struct new_element) {
    if (list->num_elements == 0) {
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_logger_element_struct));
    list-> elements[0] = new_element;
    }
    else {
    struct global_logger_element_struct temp[list->num_elements];
    int iterate;
    for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
    free(list->elements);
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_logger_element_struct));
    for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
    list->elements[list->num_elements-1] = new_element;
    }
};

void add_element_to_abs_logger_list(struct pointer_to_global_abs_logger_list *list, struct global_abs_logger_element_struct new_element) {
    if (list->num_elements == 0) {
      list->num_elements++;
      list->elements = malloc(list->num_elements*sizeof(struct global_abs_logger_element_struct));
      list->elements[0] = new_element;
    }
    else {
      struct global_abs_logger_element_struct temp[list->num_elements];
      int iterate;
      for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
      free(list->elements);
      list->num_elements++;
      list-> elements = malloc(list->num_elements*sizeof(struct global_abs_logger_element_struct));
      for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
      list->elements[list->num_elements-1] = new_element;
    }
};

void add_element_to_tagging_conditional_list(struct global_tagging_conditional_list_struct *list,struct global_tagging_conditional_element_struct new_element) {
    if (list->num_elements == 0) {
    list->num_elements++;
    list->elements = malloc(list->num_elements*sizeof(struct global_tagging_conditional_element_struct));
    list->elements[0] = new_element;
    }
    else {
    struct global_tagging_conditional_element_struct temp[list->num_elements];
    int iterate;
    for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
    free(list->elements);
    list->num_elements++;
    list->elements = malloc(list->num_elements*sizeof(struct global_tagging_conditional_element_struct));
    for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
    list->elements[list->num_elements-1] = new_element;
    }
};

void add_element_to_master_list(struct pointer_to_global_master_list *list,struct global_master_element_struct new_element) {
    if (list->num_elements == 0) {
    list->num_elements++;
    list->elements = malloc(list->num_elements*sizeof(struct global_master_element_struct));
    list->elements[0] = new_element;
    }
    else {
    struct global_master_element_struct temp[list->num_elements];
    int iterate;
    for (iterate=0;iterate<list->num_elements;iterate++) temp[iterate] = list->elements[iterate];
    free(list->elements);
    list->num_elements++;
    list-> elements = malloc(list->num_elements*sizeof(struct global_master_element_struct));
    for (iterate=0;iterate<list->num_elements-1;iterate++) list->elements[iterate] = temp[iterate];
    list->elements[list->num_elements-1] = new_element;
    }
};

void add_initialized_logger_in_volume(struct loggers_struct *loggers,int number_of_processes) {
  int iterate;
  if (loggers->num_elements == 0) {
    loggers->num_elements++;
    loggers->p_logger_volume = malloc(loggers->num_elements * sizeof(struct logger_for_each_process_list));
    loggers->p_logger_volume[0].num_elements = number_of_processes;
    loggers->p_logger_volume[0].p_logger_process = malloc(number_of_processes * sizeof(struct logger_struct**));
    for (iterate=0;iterate<number_of_processes;iterate++)
      loggers->p_logger_volume[0].p_logger_process[iterate] = NULL;
  } else {
    // Already some elements, store them in temp, free main, transfer back and add newest.
    struct logger_for_each_process_list temp[loggers->num_elements];
    
    for (iterate=0;iterate<loggers->num_elements;iterate++) temp[iterate] = loggers->p_logger_volume[iterate];
    free(loggers->p_logger_volume);
    loggers->num_elements++;
    loggers->p_logger_volume = malloc(loggers->num_elements*sizeof(struct logger_for_each_process_list));
    for (iterate=0;iterate<loggers->num_elements-1;iterate++) loggers->p_logger_volume[iterate] = temp[iterate];
    loggers->p_logger_volume[loggers->num_elements-1].num_elements = number_of_processes;
    loggers->p_logger_volume[loggers->num_elements-1].p_logger_process = malloc(number_of_processes * sizeof(struct logger_struct**));
    for (iterate=0;iterate<number_of_processes;iterate++) {
      loggers->p_logger_volume[loggers->num_elements-1].p_logger_process[iterate] = NULL;
    }
  }
};

/*
void add_initialized_abs_logger_in_volume(struct abs_loggers_struct *abs_loggers, int number_of_processes) {
  int iterate;
  if (abs_loggers->num_elements == 0) {
    abs_loggers->num_elements++;
    abs_loggers->p_abs_logger_volume = malloc(abs_loggers->num_elements * sizeof(struct abs_loggers_struct));
    abs_loggers->p_abs_logger_volume[0].num_elements = number_of_processes;
    abs_loggers->p_abs_logger_volume[0].p_abs_logger_process = malloc(number_of_processes * sizeof(struct abs_logger_struct**));
    for (iterate=0;iterate<number_of_processes;iterate++)
      abs_loggers->p_abs_logger_volume[0].p_abs_logger_process[iterate] = NULL;
  } else {
    // Already some elements, store them in temp, free main, transfer back and add newest.
    struct abs_logger_for_each_process_list temp[abs_loggers->num_elements];
    
    for (iterate=0;iterate<abs_loggers->num_elements;iterate++) temp[iterate] = abs_loggers->p_abs_logger_volume[iterate];
    free(abs_loggers->p_abs_logger_volume);
    abs_loggers->num_elements++;
    abs_loggers->p_abs_logger_volume = malloc(abs_loggers->num_elements*sizeof(struct abs_logger_for_each_process_list));
    for (iterate=0;iterate<abs_loggers->num_elements-1;iterate++) abs_loggers->p_abs_logger_volume[iterate] = temp[iterate];
    abs_loggers->p_abs_logger_volume[abs_loggers->num_elements-1].num_elements = number_of_processes;
    abs_loggers->p_abs_logger_volume[abs_loggers->num_elements-1].p_abs_logger_process = malloc(number_of_processes * sizeof(struct abs_logger_struct**));
    for (iterate=0;iterate<number_of_processes;iterate++) {
      abs_loggers->p_abs_logger_volume[abs_loggers->num_elements-1].p_abs_logger_process[iterate] = NULL;
    }
  }
};
*/

void add_initialized_abs_logger_in_volume(struct abs_loggers_struct *abs_loggers) {
  int iterate;
  if (abs_loggers->num_elements == 0) {
    abs_loggers->num_elements++;
    abs_loggers->p_abs_logger = malloc(abs_loggers->num_elements * sizeof(struct abs_logger_struct*));
  } else {
    // Already some elements, store them in temp, free main, transfer back and add newest.
    struct abs_logger_struct *temp[abs_loggers->num_elements];
    
    for (iterate=0;iterate<abs_loggers->num_elements;iterate++) temp[iterate] = abs_loggers->p_abs_logger[iterate];
    free(abs_loggers->p_abs_logger);
    
    abs_loggers->num_elements++;
    abs_loggers->p_abs_logger = malloc(abs_loggers->num_elements*sizeof(struct abs_logger_struct*));
    for (iterate=0;iterate<abs_loggers->num_elements-1;iterate++) abs_loggers->p_abs_logger[iterate] = temp[iterate];
    abs_loggers->p_abs_logger[abs_loggers->num_elements-1] = NULL;
    
  }
};


// -------------    Functions used to shorten master trace    ---------------------------------------------


void update_current_mask_intersect_status(struct pointer_to_1d_int_list *current_mask_intersect_list_status, struct pointer_to_1d_int_list *mask_status_list, struct Volume_struct **Volumes, int *current_volume) {
  // This function is to be executed whenever the current volume changes, or the mask status changes
  // It updates the effective mask status for each element of a volumes mask_intersect_list
  // The effective mask takes the ALL/ANY mode assosiated with each volume into account,
  //  meaning a volume needs to be within ALL/ANY of it's masks to have a status of 1
  // In most cases this mask_intersect_list will be empty, and memory operations are avoided
  //printf("Number of elements to be check: %d \n",Volumes[*current_volume]->geometry.mask_intersect_list.num_elements);
  
  if (Volumes[*current_volume]->geometry.mask_intersect_list.num_elements > 0) {
    int iterate,this_element,*mask_start,*mask_check;
    for (iterate=0;iterate<Volumes[*current_volume]->geometry.mask_intersect_list.num_elements;iterate++) {
      this_element = Volumes[*current_volume]->geometry.mask_intersect_list.elements[iterate];
      //printf("We are investigating volume number %d from the mask_intersect_list of volume %d",this_element,*current_volume);
      if (Volumes[this_element]->geometry.mask_mode == 2) { // ANY mask mode
        //printf("We are in ANY mask mode! \n");
        current_mask_intersect_list_status->elements[iterate] = 0; // Assume the mask status is 0, but if any are 1, take that instead
        for (mask_start=mask_check=Volumes[this_element]->geometry.masked_by_mask_index_list.elements;mask_check-mask_start<Volumes[this_element]->geometry.masked_by_mask_index_list.num_elements;mask_check++) {
          //printf("Checking all the mask statuses of volumes masking %d, now mask with global mask index %d ! \n",this_element,*mask_check);
          if (mask_status_list->elements[*mask_check] == 1) {
            //printf("The status was 1, so the effective status is set to 1 and the loop is stopped");
            current_mask_intersect_list_status->elements[iterate] = 1;
            break;
          }
        }
      } else { // ALL mask mode
        //printf("We are in ALL mask mode! \n");
        current_mask_intersect_list_status->elements[iterate] = 1; // Assume the mask status is 1, but if any one is 0, take that instead
        for (mask_start=mask_check=Volumes[this_element]->geometry.masked_by_mask_index_list.elements;mask_check-mask_start<Volumes[this_element]->geometry.masked_by_mask_index_list.num_elements;mask_check++) {
          //printf("Checking all the mask statuses of volumes masking %d, now mask with global mask index %d ! \n",this_element,*mask_check);
          if (mask_status_list->elements[*mask_check] == 0) {
            //printf("The status was 0, so the effective status is set to 0 and the loop is stopped \n");
            current_mask_intersect_list_status->elements[iterate] = 0;
            break;
          }
        }
      }
    }
  }
    
  /* Original version compatible with trace scope
  for (iterate=0;iterate<Volumes[current_volume]->geometry.mask_intersect_list.num_elements;iterate++) {
    this_element = Volumes[current_volume]->geometry.mask_intersect_list.elements[iterate];
    if (Volumes[this_element]->geometry.mask_mode == 2) { // ANY mask mode
      current_mask_intersect_list_status.elements[iterate] = 0; // Assume the mask status is 0, but if any are 1, take that instead
      for (mask_start=mask_check=Volumes[this_element]->geometry.masked_by_mask_index_list.elements;mask_check-mask_start<Volumes[this_element]->geometry.masked_by_mask_index_list.num_elements;mask_check++) {
        if (mask_status_list[*mask_check] == 1) {
          current_mask_intersect_list_status.elements[iterate] = 1;
          break;
        }
      }
    } else { // ALL mask mode
      current_mask_intersect_list_status.elements[iterate] = 1; // Assume the mask status is 1, but if any one is 0, take that instead
      for (mask_start=mask_check=Volumes[this_element]->geometry.masked_by_mask_index_list.elements;mask_check-mask_start<Volumes[this_element]->geometry.masked_by_mask_index_list.num_elements;mask_check++) {
        if (mask_status_list[*mask_check] == 0) {
          current_mask_intersect_list_status.elements[iterate] = 0;
          break;
        }
      }
    }
  }
  */
}

// -------------    Tagging functions    ------------------------------------------------------------------

struct tagging_tree_node_struct {
  // Statistics:
  double intensity;
  int number_of_rays;
  // tree pointers
  struct tagging_tree_node_struct *above;   // Pointer to node above
  struct tagging_tree_node_struct **volume_branches;
  struct tagging_tree_node_struct **process_branches;
};

struct list_of_tagging_tree_node_pointers {
  struct tagging_tree_node_struct **elements;
  int num_elements;
};

struct tagging_tree_node_struct *make_tagging_tree_node(void) {
    return (struct tagging_tree_node_struct *) malloc(sizeof(struct tagging_tree_node_struct));
}


struct tagging_tree_node_struct  *simple_initialize_tagging_tree_node(struct tagging_tree_node_struct *new_node) {
    //struct tagging_tree_node_struct *dummy;
    //dummy = malloc(sizeof(struct tagging_tree_node_struct));
    //new_node = dummy;
    
    //new_node->element = (struct tagging_tree_node_struct *) malloc(sizeof(struct tagging_tree_node_struct));
    //new_node = (struct tagging_tree_node_struct *) malloc(sizeof(struct tagging_tree_node_struct));
    new_node = make_tagging_tree_node();
    
    if (new_node == NULL) printf("ERROR, Union tagging system could not allocate memory\n");
    new_node->intensity = 4.2; // (double) 4.2;
    new_node->number_of_rays = 42; //(int) 42;
    
    printf("new_node->intensity = %f, new_node->number_of_rays = %d \n",new_node->intensity,new_node->number_of_rays);
    return new_node;
};


struct tagging_tree_node_struct *initialize_tagging_tree_node(struct tagging_tree_node_struct *new_node, struct tagging_tree_node_struct *above_node, struct Volume_struct *this_volume) {
    new_node = make_tagging_tree_node();
    
    new_node->intensity = (double) 0;
    new_node->number_of_rays = (int) 0;
    new_node->above = above_node;
    
    int next_volume_list_length = this_volume->geometry.next_volume_list.num_elements;
    new_node->volume_branches = malloc(next_volume_list_length*sizeof(struct tagging_tree_node_struct*));
    int iterate;
    // Initializing pointers so that they can be checked for NULL later. Is this redundant? Does malloc return null pointers?
    for (iterate=0;iterate<next_volume_list_length;iterate++) new_node->volume_branches[iterate] = NULL;
    //new_node->volume_branches.num_elements = dest_list_length; // May be removed
    
    int number_of_processes;
    if (this_volume->p_physics == NULL) number_of_processes = 0;
    else number_of_processes = this_volume->p_physics->number_of_processes;
    
    new_node->process_branches = malloc(number_of_processes*sizeof(struct tagging_tree_node_struct*));
    // Initializing pointers so that they can be checked for NULL later. Is this redundant? Does malloc return null pointers?
    for (iterate=0;iterate<number_of_processes;iterate++) new_node->process_branches[iterate] = NULL;
    //new_node->process_branches.num_elements=number_of_processes; // May be removed
    
    return new_node;
};

struct tagging_tree_node_struct *goto_process_node(struct tagging_tree_node_struct *current_node, int process_index, struct Volume_struct *this_volume, int *stop_tagging_ray, int stop_creating_nodes) {
    // Either create a new node if it has not been created yet, or travel down the tree
    if (current_node->process_branches[process_index] == NULL) {
      if (stop_creating_nodes == 0) {
        current_node->process_branches[process_index] = initialize_tagging_tree_node(current_node->process_branches[process_index],current_node,this_volume);
        return current_node->process_branches[process_index];
      } else {
        // This stops the ray from using more goto node functions and being counted in the statistics.
        // Happens because the history limit is reached, and no new histories should be started.
        *stop_tagging_ray = 1;
        return current_node;
      }
    } else {
        current_node = current_node->process_branches[process_index];
        return current_node;
    }
    
    //return current_node;
};

struct tagging_tree_node_struct *goto_volume_node(struct tagging_tree_node_struct *current_node,int current_volume, int next_volume, struct Volume_struct **Volumes, int *stop_tagging_ray, int stop_creating_nodes) {
    // I have only allocated the number of node branches that corresponds to the current_volumes next_volume_list
    // The problem is to find where on the destination list the current volume is without doing a manual search
    
    // With the new mask system there is a risk of going from and to the same volume, which should be ignored by the tagging system
    if (current_volume == next_volume) return current_node;
    
    struct tagging_tree_node_struct *output;
    int next_volume_list_index = -1;
    // Temporary slow method for finding the correct index on the destination list
    int iterate;
    
    for (iterate=0;iterate<Volumes[current_volume]->geometry.next_volume_list.num_elements;iterate++)
        if (Volumes[current_volume]->geometry.next_volume_list.elements[iterate] == next_volume) {
            next_volume_list_index = iterate;
            break;
        }
    
    // Debug phase
    //printf("Tagging: going from volume %d to volume %d, which is index number %d on it's next volume list \n",current_volume,next_volume,next_volume_list_index);
    #ifndef OPENACC
    if (next_volume_list_index == -1) {
        printf("ERROR in Union component, tagging or destination system failed, next volume was not on next volume list\n");
        printf("current_volume = %d, next_volume = %d \n",current_volume,next_volume);
        print_1d_int_list(Volumes[current_volume]->geometry.destinations_list,"destinations_list for current volume");
        exit(1);
    }
    #endif

    // Either create a new node if it has not been created yet, or travel down the tree
    if (current_node->volume_branches[next_volume_list_index] == NULL) {
      if (stop_creating_nodes == 0) {
        current_node->volume_branches[next_volume_list_index] =  initialize_tagging_tree_node(current_node->volume_branches[next_volume_list_index],current_node,Volumes[next_volume]);
        return current_node->volume_branches[next_volume_list_index];
      } else {
        // This stops the ray from using more goto node functions and being counted in the statistics.
        // Happens because the history limit is reached, and no new histories should be started.
        *stop_tagging_ray = 1;
        return current_node;
      }
    } else {
        //current_node = current_node->volume_branches[next_volume_list_index];
        //return current_node;
        current_node = current_node->volume_branches[next_volume_list_index];
        //printf("used allocated node \n");
        return current_node;
    }
};

void add_statistics_to_node(struct tagging_tree_node_struct *current_node, Coords *r, Coords *v, double *weight, int *counter) {
    if (current_node->number_of_rays == 0) (*counter)++;
    current_node->number_of_rays = current_node->number_of_rays + 1;
    current_node->intensity = current_node->intensity + *weight;
};

struct history_node_struct {
    int volume_index;
    int process_index;
};

struct dynamic_history_list {
  struct history_node_struct *elements;
  int used_elements;
  int allocated_elements;
};

struct saved_history_struct {
    struct history_node_struct *elements;
    int used_elements;
    double intensity;
    int number_of_rays;
};

struct total_history_struct {
    struct saved_history_struct *saved_histories;
    int used_elements;
    int allocated_elements;
};


void add_to_history(struct dynamic_history_list *history, int volume_index, int process_index) {
    //printf("Adding to history[%d]: volume_index = %d, process_index = %d \n",history->used_elements,volume_index,process_index);
    if (history->allocated_elements == 0) {
        history->elements = malloc(5*sizeof(struct history_node_struct));
        history->used_elements = 1;
        history->allocated_elements = 5;
        history->elements[0].volume_index = volume_index;
        history->elements[0].process_index = process_index;
    } else if (history->used_elements > history->allocated_elements-1) {
        struct dynamic_history_list temp_history;
        temp_history.elements = malloc((history->used_elements)*sizeof(struct history_node_struct));
        int iterate;
        for (iterate=0;iterate<history->used_elements;iterate++) {
            temp_history.elements[iterate].volume_index  = history->elements[iterate].volume_index;
            temp_history.elements[iterate].process_index = history->elements[iterate].process_index;
        }
        free(history->elements);
        history->elements = malloc((history->allocated_elements+5)*sizeof(struct history_node_struct));
        for (iterate=0;iterate<history->used_elements;iterate++) {
            history->elements[iterate].volume_index  = temp_history.elements[iterate].volume_index;
            history->elements[iterate].process_index = temp_history.elements[iterate].process_index;
        }
        
        history->allocated_elements = history->allocated_elements+5;
        
        history->elements[history->used_elements].volume_index = volume_index;
        history->elements[history->used_elements].process_index = process_index;
        history->used_elements = history->used_elements+1;
        
    } else {
        history->elements[history->used_elements].volume_index = volume_index;
        history->elements[history->used_elements].process_index = process_index;
        history->used_elements++;
    }
    
};

void printf_history(struct dynamic_history_list *history) {
    int history_iterate;
          //printf("History number %d, intensity = %f, number of rays = %d:",hist_num, search_node->intensity, search_node->number_of_rays);
          for (history_iterate=0;history_iterate<history->used_elements-1;history_iterate++) {
            if (history->elements[history_iterate].process_index == -1) {
                printf(" V%d ->",history->elements[history_iterate].volume_index);
            } else {
                printf(" P%d ->",history->elements[history_iterate].process_index);
            }
          }
          if (history->elements[history_iterate].process_index == -1) {
                printf(" V%d \n",history->elements[history_iterate].volume_index);
          } else {
                printf(" P%d \n",history->elements[history_iterate].process_index);
          }
}

void fprintf_total_history(struct saved_history_struct *history, FILE *fp) {
    fprintf(fp,"%d\t N I=%E \t", history->number_of_rays, history->intensity);
        int history_iterate;
          //printf("History number %d, intensity = %f, number of rays = %d:",hist_num, search_node->intensity, search_node->number_of_rays);
          for (history_iterate=0;history_iterate<history->used_elements-1;history_iterate++) {
            if (history->elements[history_iterate].process_index == -1) {
                fprintf(fp," V%d ->",history->elements[history_iterate].volume_index);
            } else {
                fprintf(fp," P%d ->",history->elements[history_iterate].process_index);
            }
          }
          if (history->elements[history_iterate].process_index == -1) {
                fprintf(fp," V%d \n",history->elements[history_iterate].volume_index);
          } else {
                fprintf(fp," P%d \n",history->elements[history_iterate].process_index);
          }
}

/*
int Sample_compare_doubles (const void *a, const void *b) {
  const double *da = (const double *) a;
  const double *db = (const double *) b;

  return (*da > *db) - (*da < *db);
}
*/


int Sample_compare_history_intensities (const struct saved_history_struct *a, const struct saved_history_struct *b) {
  const double *da = (const double *) &(a->intensity);
  const double *db = (const double *) &(b->intensity);

  return (*da < *db) - (*da > *db);
}

void write_tagging_tree(struct list_of_tagging_tree_node_pointers *master_list, struct Volume_struct **Volumes, int total_history_counter, int number_of_volumes) {
  // Start from top of tree, go to extremeties, take results and add to disk / database, free that node
  int volume_index,done,volume_iterate,process_iterate,current_volume,next_node_found,current_number_of_processes,history_iterate,hist_num;
  
  struct tagging_tree_node_struct *search_node;
  struct tagging_tree_node_struct **kill_candidate;
  struct dynamic_history_list history_data; // Allocate the history list struct
  struct dynamic_history_list *history;     // Use this pointer in the algorithm
  
  struct total_history_struct total_history;
  total_history.saved_histories = malloc(total_history_counter * sizeof(struct saved_history_struct));
  total_history.allocated_elements = total_history_counter;
  total_history.used_elements = 0;
  
  history = &history_data;
  
  history->used_elements = 0;
  history->allocated_elements = 0;
  
  hist_num = 0;
  for (volume_index=0;volume_index<master_list->num_elements;volume_index++) {
    
    search_node = master_list->elements[volume_index];
    
    if (volume_index != 0)
        current_number_of_processes = Volumes[volume_index]->p_physics->number_of_processes;
    else
        current_number_of_processes = 0;
    
    current_volume = volume_index;
    done = 0;
    
    history->used_elements = 0;
    
    add_to_history(history,current_volume,-1);
    while(done == 0) {
        next_node_found=0;
        for (volume_iterate=0;volume_iterate<Volumes[current_volume]->geometry.next_volume_list.num_elements;volume_iterate++) {
            //printf("searc_node->volume_branches[0]->intensity = %f\n",search_node->volume_branches[0]->intensity);
            if (search_node->volume_branches[volume_iterate] != NULL) {
                current_volume = Volumes[current_volume]->geometry.next_volume_list.elements[volume_iterate];
                if (current_volume != 0)
                    current_number_of_processes = Volumes[current_volume]->p_physics->number_of_processes;
                else
                    current_number_of_processes = 0;
                
                //search_node = &(search_node->volume_branches[volume_iterate]);
                kill_candidate = &(search_node->volume_branches[volume_iterate]);
                search_node = search_node->volume_branches[volume_iterate];
                next_node_found = 1;
                add_to_history(history,current_volume,-1);
                //printf_history(history);
                break;
            }
        }
        if (next_node_found == 0) {
          //printf("doing process loop with %d steps \n",current_number_of_processes);
          for (process_iterate=0;process_iterate<current_number_of_processes;process_iterate++) {
            //if (&(search_node->process_branches[volume_iterate]) != NULL) {
            if (search_node->process_branches[process_iterate] != NULL) {
                //printf("was not NULL (process)\n");
                //search_node = &(search_node->process_branches[process_iterate]);
                kill_candidate = &(search_node->process_branches[process_iterate]);
                search_node = search_node->process_branches[process_iterate];
                next_node_found = 1;
                add_to_history(history,current_volume,process_iterate);
                //printf_history(history);
                break;
            }
          }
        }
        
        if (next_node_found == 0) {
          // write this history to disk / memory
          hist_num++;
          //printf("Reached next_node_found == 0 \n");
          if (history->used_elements > 0 && search_node->number_of_rays > 0) {
            //printf("%d rays (I=%E) with history: \t", search_node->number_of_rays, search_node->intensity);
            //printf_history(history);
            
            total_history.saved_histories[total_history.used_elements].used_elements = history->used_elements;
            total_history.saved_histories[total_history.used_elements].elements = malloc(total_history.saved_histories[total_history.used_elements].used_elements*sizeof(struct history_node_struct));
            for (history_iterate = 0;history_iterate<history->used_elements;history_iterate++) {
                total_history.saved_histories[total_history.used_elements].elements[history_iterate] = history->elements[history_iterate];
                //printf("total_history.saved_histories[total_history.used_elements].elements[%d].volume_index \n",history_iterate,total_history.saved_histories[total_history.used_elements].elements[history_iterate].volume_index);
            }
            //total_history.saved_histories[total_history.used_elements].elements = history->elements;
            total_history.saved_histories[total_history.used_elements].intensity = search_node->intensity;
            total_history.saved_histories[total_history.used_elements].number_of_rays = search_node->number_of_rays;
            total_history.used_elements++;
          }
          
          history->used_elements = 0;
          
          // end of tree, no new nodes
          if (search_node->above == NULL) {
            done = 1;
          } else {
            // reset to the root of the tree
            *kill_candidate = NULL;
            free(search_node);
            search_node = master_list->elements[volume_index];
            
            if (volume_index != 0)
              current_number_of_processes = Volumes[volume_index]->p_physics->number_of_processes;
            else
              current_number_of_processes = 0;
    
            current_volume = volume_index;
            add_to_history(history,current_volume,-1);
          }
          
        }
    }
  }
  
  if (history->allocated_elements > 0) free(history->elements);

  qsort(total_history.saved_histories,total_history.used_elements,sizeof (struct saved_history_struct), Sample_compare_history_intensities);
  
  
  
  MPI_MASTER(
  printf("\n\n");
  printf("Top 20 most common histories. Shows the index of volumes entered (VX), and the scattering processes (PX)\n");
  for (history_iterate=0;history_iterate<total_history.used_elements && history_iterate<20;history_iterate++) {
    printf("%d\t N I=%E \t", total_history.saved_histories[history_iterate].number_of_rays, total_history.saved_histories[history_iterate].intensity);
    printf_history(&total_history.saved_histories[history_iterate]);
  }
   
  FILE *fp;
  fp = fopen("union_history.dat","w");
  
  fprintf(fp,"History file written by the McStas component Union_master \n");
  fprintf(fp,"When running with MPI, the results may be from just a single thread, meaning intensities are divided by number of threads\n");
  fprintf(fp,"----- Description of the used volumes -----------------------------------------------------------------------------------\n");
  
  fprintf(fp,"V0: Surrounding vacuum \n");
  for (volume_iterate=1;volume_iterate<number_of_volumes;volume_iterate++) {
    fprintf(fp,"V%d: %s  ",volume_iterate,Volumes[volume_iterate]->name);
    fprintf(fp,"Material: %s  ",Volumes[volume_iterate]->p_physics->name);
    for (process_iterate=0;process_iterate<Volumes[volume_iterate]->p_physics->number_of_processes;process_iterate++) {
      fprintf(fp," P%d: %s",process_iterate,Volumes[volume_iterate]->p_physics->p_scattering_array[process_iterate].name);
    }
    fprintf(fp,"\n");
  }
  fprintf(fp,"----- Histories sorted after intensity ----------------------------------------------------------------------------------\n");
  
  for (history_iterate=0;history_iterate<total_history.used_elements;history_iterate++) {
    fprintf_total_history(&total_history.saved_histories[history_iterate],fp);
    // Garbage collection
    if (total_history.saved_histories[history_iterate].used_elements > 0) free(total_history.saved_histories[history_iterate].elements);
  }
  fclose(fp);
  
  )
  
  // Garbage collection
  if (total_history.allocated_elements > 0) free(total_history.saved_histories);
  
  
};


// -------------    Intersection table functions   --------------------------------------------------------
int clear_intersection_table(struct intersection_time_table_struct *intersection_time_table) {
    // Resets the intersection table when a scattering have occured.
    int iterate_volumes,iterate_solutions;
    
    // Start at one because vacuum (0) does not have a listing in the intersection table
    for (iterate_volumes = 1;iterate_volumes < intersection_time_table->num_volumes;iterate_volumes++) {
        intersection_time_table->calculated[iterate_volumes] = 0;
        // This second loop is added for safty in debugging phase, but can be removed as the information should never be accesed when calculated = 0
        for (iterate_solutions = 0;iterate_solutions < intersection_time_table->n_elements[iterate_volumes];iterate_solutions++) {
            intersection_time_table->intersection_times[iterate_volumes][iterate_solutions] = -1;
        }
    }
    
    return 1;
};

void print_intersection_table(struct intersection_time_table_struct *intersection_time_table) {
    int num_volumes,iterate,solutions;
    int max_number_of_solutions = 0;
    
    num_volumes = intersection_time_table->num_volumes;
    
    for (iterate = 0;iterate < num_volumes;iterate++) {
        if (max_number_of_solutions < intersection_time_table->n_elements[iterate])
            max_number_of_solutions = intersection_time_table->n_elements[iterate];
    }
    
    printf("------------------ INTERSECTION_TIME_TABLE -----------------");
    for (solutions = 2;solutions < max_number_of_solutions;solutions++) printf("------------");
    printf("\n");
    
    // printf("iterate      |");

    printf("           ");
    printf("| CALCULATED  |");
    for (solutions = 0;solutions < max_number_of_solutions;solutions++) {
        printf(" - SOLUTION %d - |",solutions);
    }

    printf("\n");
    for (iterate = 0;iterate < num_volumes;iterate++){
    // print iterate number
    printf("Volume %d   |",iterate);
    printf(" ---- %d ---- |",intersection_time_table->calculated[iterate]);
    
        for (solutions = 0;solutions < max_number_of_solutions;solutions++) {
          if (intersection_time_table->n_elements[iterate] > solutions && intersection_time_table->calculated[iterate] == 1)
            if (intersection_time_table->intersection_times[iterate][solutions] > 0)
             printf("   %1.8f   |",intersection_time_table->intersection_times[iterate][solutions]);
            else
             printf("   %1.7f   |",intersection_time_table->intersection_times[iterate][solutions]);
          else
            printf("                |");
        }
    printf("\n");
    }
    printf("------------------------------------------------------------");
    for (solutions = 2;solutions < max_number_of_solutions;solutions++) printf("------------");
    printf("\n");
    
    
    };
    
// -------------    Drawing functions   --------------------------------------------------------
void merge_lines_to_draw(struct lines_to_draw *lines_master,struct lines_to_draw *lines_new) {
    if (lines_master->number_of_lines == 0) {
    lines_master->number_of_lines = lines_new->number_of_lines;
    lines_master->lines = malloc(lines_master->number_of_lines*sizeof(struct line_segment));
    lines_master->lines = lines_new->lines;
    // One could free lines_new->lines;
    } else {
    int iterate;
    struct line_segment *temp_lines;
    temp_lines = malloc(lines_master->number_of_lines*sizeof(struct line_segment));
    for (iterate = 0;iterate < lines_master->number_of_lines;iterate++) temp_lines[iterate] = lines_master->lines[iterate];
    free(lines_master->lines);
    lines_master->lines = malloc((lines_master->number_of_lines+lines_new->number_of_lines)*sizeof(struct line_segment));
    for (iterate = 0;iterate < lines_master->number_of_lines;iterate++) lines_master->lines[iterate] = temp_lines[iterate];
    for (iterate = 0;iterate < lines_new->number_of_lines;iterate++) lines_master->lines[iterate+lines_master->number_of_lines] = lines_new->lines[iterate];
    lines_master->number_of_lines = lines_master->number_of_lines + lines_new->number_of_lines;
    free(temp_lines);
    }
    };

int r_has_highest_priority(Coords point,int N,struct geometry_struct **Geometries,int number_of_volumes) {
    // Function that test if a point in Volume N has the highest priority.
    // Returns 0 if another volume has higher priority at the point, and it's mask status is 1
    // Returns 1 if no other volume has higher priority at the point
    // (not active) Returns a larger integer if the volume N is a mask, and the point is not in the volume it is masking,
    //  which results in the drawing function making that number of dashes
    
    
    int mask_status,*mask_start,*mask_check;
    // If the volume is a mask, it does not have a priority, and is always on top
    if (Geometries[N]->is_mask_volume == 1) {
      return 1;
      // Can draw parts of the mask that is outside the volume it masks as dashed lines, but it just looks messy
      /*
      mask_status = 1;
      for (mask_check=mask_start=Geometries[N]->mask_list.elements;mask_check-mask_start<Geometries[N]->mask_list.num_elements;mask_check++) {
        if (Geometries[*mask_check]->within_function(point,Geometries[*mask_check]) == 1) {
          return 1; // If the point is within a volume the mask is masking, draw it as a solid line
        }
      }
      // As it was not inside any of the volumes the mask is masking, draw it as a dashed line
      return 5;
      */
    }
    
    // If the volume is a masked volume, check if the point is within it's masks
    if (Geometries[N]->is_masked_volume == 1) {
      if (Geometries[N]->mask_mode == 1) { // ALL mode, need to be within ALL masks
        for (mask_check=mask_start=Geometries[N]->masked_by_list.elements;mask_check-mask_start<Geometries[N]->masked_by_list.num_elements;mask_check++) {
          if (Geometries[*mask_check]->within_function(point,Geometries[*mask_check]) == 0) {
            return 0; // If the point is just outside one mask, the mask status is 0 and the point does not have highest priority
          }
        }
      } else { // ANY mode, need to be within at least one mask
        mask_status = 0;
        for (mask_check=mask_start=Geometries[N]->masked_by_list.elements;mask_check-mask_start<Geometries[N]->masked_by_list.num_elements;mask_check++) {
          if (Geometries[*mask_check]->within_function(point,Geometries[*mask_check]) == 1) {
            mask_status = 1;
            break;
          }
        }
        if (mask_status == 0) {
          return 0; // If it was not in a single of it's masks, the point did not have highest priority
        }
      }
    }
    
    int volume_index;
    double self_priority;
    self_priority = Geometries[N]->priority_value;
    
    
    for (volume_index = 1;volume_index<number_of_volumes;volume_index++) {
      if (volume_index != N && Geometries[volume_index]->is_mask_volume == 0) {
        if (Geometries[volume_index]->within_function(point,Geometries[volume_index])) {
          if (Geometries[volume_index]->is_masked_volume == 1) {
            // Since this volume is masked, the mask status need to be checked
            if (Geometries[volume_index]->mask_mode == 1) { //ALL mode, need to be within ALL masks
              mask_status = 1;
              for (mask_check=mask_start=Geometries[volume_index]->masked_by_list.elements;mask_check-mask_start<Geometries[volume_index]->masked_by_list.num_elements;mask_check++) {
                if (Geometries[*mask_check]->within_function(point,Geometries[*mask_check]) == 0) {
                  mask_status = 0;
                  break;
                }
              }
            } else { // ANY mode, need to be within at least one mask
              mask_status = 0;
              for (mask_check=mask_start=Geometries[volume_index]->masked_by_list.elements;mask_check-mask_start<Geometries[volume_index]->masked_by_list.num_elements;mask_check++) {
                if (Geometries[*mask_check]->within_function(point,Geometries[*mask_check]) == 1) {
                  mask_status = 1;
                  break;
                }
              }
            }
          } else mask_status = 1;
          if (Geometries[volume_index]->priority_value > self_priority && mask_status == 1) {
            // printf("Volume %d did not have highest priority at (%f,%f,%f) (Volume number %d was above)\n",N,point.x,point.y,point.z,volume_index);
            return 0;
          }
        }
      }
    }
    // printf("Volume %d did have highest priority at (%f,%f,%f) \n",N,point.x,point.y,point.z);
    return 1;
    }
    
void draw_line_positions(Coords point1,Coords point2) {
        //line(point1.x,point1.y,point1.z,point2.x,point2.y,point2.z);
        // sigh, can not use line in share. Need to save the information and pass to the mcdisplay part.
    }

int Sample_compare_doubles (const void *a, const void *b) {
  const double *da = (const double *) a;
  const double *db = (const double *) b;

  return (*da > *db) - (*da < *db);
}

struct lines_to_draw draw_line_with_highest_priority(Coords position1,Coords position2,int N,struct geometry_struct **Geometries,int number_of_volumes,int max_number_of_solutions) {
    
    int volume_index,iterate,permanent_list_length = 0;
    int number_of_solutions;
    double temp_intersection[max_number_of_solutions];
    double r1[3],r2[3],direction[3];
    struct pointer_to_1d_double_list intersection_list;
    
    intersection_list.num_elements = 0;
    
    // double *permanent_intersection_list,*storage; // old ways
    
    r1[0] = position1.x;
    r1[1] = position1.y;
    r1[2] = position1.z;
    r2[0] = position2.x;
    r2[1] = position2.y;
    r2[2] = position2.z;
    
    // printf("r1 = (%f,%f,%f) \n",r1[0],r1[1],r1[2]);
    // printf("r2 = (%f,%f,%f) \n",r2[0],r2[1],r2[2]);
    
    direction[0] = r2[0] - r1[0];
    direction[1] = r2[1] - r1[1];
    direction[2] = r2[2] - r1[2];
    int geometry_output;
    
    // Find intersections
    for (volume_index = 1;volume_index < number_of_volumes; volume_index++) {
        if (volume_index != N) {
            geometry_output = Geometries[volume_index]->intersect_function(temp_intersection,&number_of_solutions,r1,direction,Geometries[volume_index]);
             // printf("No solutions for intersection (Volume %d) with %d \n",N,volume_index);
                for (iterate=0;iterate<number_of_solutions;iterate++) {
                    // print_1d_double_list(intersection_list,"intersection_list");
                    if (temp_intersection[iterate] > 0 && temp_intersection[iterate] < 1) {
                        add_element_to_double_list(&intersection_list,temp_intersection[iterate]);
                        // printf("solution taken = %f\n",temp_intersection[iterate]);
                        }                         // printf("solution ignored = %f\n",temp_intersection[iterate]);
                    // print_1d_double_list(intersection_list,"intersection_list");
                
                }
                // printf("First (%d) solutions added to the solution stack, intersection with %d \n",number_of_solutions,volume_index);
                // printf(" Solutions: ");
                // for (iterate = 0;iterate < intersection_list.num_elements;iterate++) printf("%f ",intersection_list.elements[iterate]);
                // printf("\n");
            
        }
    }
    // Now we have a list of intersection distances between r1 and r2 and all volumes.
    // This list needs to be sorted before we continue!
    
    qsort(intersection_list.elements,intersection_list.num_elements,sizeof (double), Sample_compare_doubles);
    // print_1d_double_list(intersection_list,"after sorting");
    // printf(" Solutions (after sorting): ");
    // for (iterate = 0;iterate < intersection_list.num_elements;iterate++) printf("%f ",intersection_list.elements[iterate]);
    // printf("\n");
    
    Coords points[intersection_list.num_elements+2];
    points[0] = coords_set(r1[0],r1[1],r1[2]);
    points[intersection_list.num_elements+1] = coords_set(r2[0],r2[1],r2[2]);
    for (iterate = 1;iterate < intersection_list.num_elements+1;iterate++) {
        points[iterate].x = r1[0] + direction[0]*intersection_list.elements[iterate-1];
        points[iterate].y = r1[1] + direction[1]*intersection_list.elements[iterate-1];
        points[iterate].z = r1[2] + direction[2]*intersection_list.elements[iterate-1];
    }
    
    // printf("Points on the list:\n");
    // for (iterate = 0;iterate < intersection_list.num_elements + 2;iterate++) {
    // //         printf("(%f,%f,%f)\n",points[iterate].x,points[iterate].y,points[iterate].z);
    // }
    // printf("\n");
  
    struct line_segment lines[intersection_list.num_elements+1];
    int draw_logic[intersection_list.num_elements+1];
    //struct lines_to_draw draw_order;
    Coords midpoint;
    struct lines_to_draw draw_order;
    draw_order.number_of_lines = 0;
    
    // printf("test2 in function \n");
    int number_of_dashes;
    
    for (iterate = 0;iterate < intersection_list.num_elements + 1;iterate++) {
        lines[iterate].point1 = points[iterate];
        lines[iterate].point2 = points[iterate+1];
        midpoint.x = 0.5*(lines[iterate].point1.x + lines[iterate].point2.x);
        midpoint.y = 0.5*(lines[iterate].point1.y + lines[iterate].point2.y);
        midpoint.z = 0.5*(lines[iterate].point1.z + lines[iterate].point2.z);
        
        if ((number_of_dashes = r_has_highest_priority(midpoint,N,Geometries,number_of_volumes)) != 0) {
            draw_order.number_of_lines++;
            draw_logic[iterate] = number_of_dashes;
        } else draw_logic[iterate] = 0;
    }
   
    // printf("test3 in function \n");
    
    if (draw_order.number_of_lines > 0) {
        draw_order.lines = malloc(draw_order.number_of_lines*sizeof(struct line_segment));
        draw_order.number_of_lines = 0;
        for (iterate = 0;iterate < intersection_list.num_elements + 1;iterate++) {
            if (draw_logic[iterate] != 0) {
              lines[iterate].number_of_dashes = draw_logic[iterate];
              draw_order.lines[draw_order.number_of_lines++] = lines[iterate];
            }
        }
        if (intersection_list.num_elements > 0) free(intersection_list.elements);
    }

    // printf("function done \n");
    return draw_order;
    }

struct lines_to_draw draw_circle_with_highest_priority(Coords center,Coords vector,double radius,int N,struct geometry_struct **Geometries,int number_of_volumes,int max_number_of_solutions) {
    int number_of_positions = 100;

    // normalize vector
    double vector_length = length_of_position_vector(vector);
    // print_position(vector,"start vector");
    vector.x /= vector_length;
    vector.y /= vector_length;
    vector.z /= vector_length;
    // print_position(vector,"start vector normalized");
    
    // Create a vector from the center of the circle to a point on the circumference by cross product
    double cross_input[3] = {0,1,0};
    // In case the cross input is parallel with the vector, a new is chosen. Both can't be parallel.
    if (scalar_prod(cross_input[0],cross_input[1],cross_input[2],vector.x,vector.y,vector.z) > 0.99) {
        cross_input[0] = 1; cross_input[1] = 0; cross_input[2] = 0;
    }
    // print_position(make_position(cross_input),"cross input");
    
    double cross_product1[3] = {0,0,0};
    vec_prod(cross_product1[0],cross_product1[1],cross_product1[2],vector.x,vector.y,vector.z,cross_input[0],cross_input[1],cross_input[2]);
    
    // print_position(make_position(cross_product1),"cross_product1");
    
    double cross_length = length_of_3vector(cross_product1);
    
    // printf("cross_length = %f \n",cross_length);
    
    cross_product1[0] /= cross_length;
    cross_product1[1] /= cross_length;
    cross_product1[2] /= cross_length;
    
    cross_product1[0] *= radius;
    cross_product1[1] *= radius;
    cross_product1[2] *= radius;
    
    // printf("cross_product1 = (%f,%f,%f) \n",cross_product1[0],cross_product1[1],cross_product1[2]);
    
    int iterate;
    double rotate_angle;
    Coords radial_position,old_radial_position;
    struct lines_to_draw temp_draw_order,return_draw_order;
    return_draw_order.number_of_lines = 0;
    // Generate an array of positions by rotating this vector around the given vector, this corresponds to points on the circle
    old_radial_position.x = center.x + cross_product1[0];
    old_radial_position.y = center.y + cross_product1[1];
    old_radial_position.z = center.z + cross_product1[2];

    // printf("old_radial_position = (%f,%f,%f) \n",old_radial_position.x,old_radial_position.y,old_radial_position.z);
    
    for (iterate = 0;iterate < number_of_positions-1;iterate++) {
        rotate_angle = 2*3.14159*((double) iterate + 1.0)/((double) number_of_positions);
        rotate(radial_position.x,radial_position.y,radial_position.z,cross_product1[0],cross_product1[1],cross_product1[2],rotate_angle,vector.x,vector.y,vector.z);
        radial_position.x += center.x;
        radial_position.y += center.y;
        radial_position.z += center.z;
        
        // Use the draw_lines_with_highest_priority to draw get draw_orders for each line piece
        temp_draw_order = draw_line_with_highest_priority(radial_position,old_radial_position,N,Geometries,number_of_volumes,max_number_of_solutions);
       // Assemble these draw_orders to one large draw order
        merge_lines_to_draw(&return_draw_order,&temp_draw_order);

        old_radial_position.x = radial_position.x;
        old_radial_position.y = radial_position.y;
        old_radial_position.z = radial_position.z;
    }
    
    radial_position.x = center.x + cross_product1[0];
    radial_position.y = center.y + cross_product1[1];
    radial_position.z = center.z + cross_product1[2];
    // Use the draw_lines_with_highest_priority to draw get draw_orders for each line piece
    temp_draw_order = draw_line_with_highest_priority(radial_position,old_radial_position,N,Geometries,number_of_volumes,max_number_of_solutions);
    // Assemble these draw_orders to one large draw order
    merge_lines_to_draw(&return_draw_order,&temp_draw_order);
    
    // clean up
    if (temp_draw_order.number_of_lines > 0) free(temp_draw_order.lines);
    
    // return the large draw order.
    return return_draw_order;
    }

// -------------    Geometry functions   --------------------------------------------------------

#include "Geometry_functions.c"

// -------------    List generator functions   --------------------------------------------------


int within_which_volume(Coords pos, struct pointer_to_1d_int_list input_list, struct pointer_to_1d_int_list destinations_list, struct Volume_struct **Volumes, struct pointer_to_1d_int_list *mask_status_list, int number_of_volumes, int *volume_logic_copy, int *ListA, int *ListB) {
    // This function identifies in which of the volumes of the input list the position pos lies in.
    // pos: position for which the current volume should be found for
    // input list: list of potential volumes, reduced to the ones without parents (their children will be checked)
    // OLD VERSION: volume logic: A logic list of allowed volumes for lookup, volume 1 3 and 5 in a case of 10 volumes would be [0 1 0 1 0 1 0 0 0 0]
    // destinations_list: list of allowed destinations (the original destinations list)
    // Volumes: Main volumes array
    // volume_logic_copy: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    // ListA: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    // ListB: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    
    // Algorithm description
    // Check all of input list for pos being within them, those that have it within them are:
    //      checked against the current highest priority
    //          if higher, is the new highest priority and the new pick for volume
    //      have all their direct children added to the next list to be checked (but any volume can only be added to that list once)
    // Once a run have been made where the next list to check is empty, the answer for new pick for volume is taken.
    
    // Mask update:
    // Algorithm description
    // Check all of input list for pos being within them, those that have it within them are:
    //      checked against the current highest priority and mask status
    //          if higher, this is the new highest priority and the new pick for volume
    //      have all their direct children added to the next list to be checked (but any volume can only be added to that list once)
    // Once a run have been made where the next list to check is empty, the answer for new pick for volume is taken.
    
    // The advantage of the method is that potentially large numbers of children are skipped when their parents do not contain the position.
    // The overhead cost is low, as all the lists are prealocated.
    // Should be checked which of the two implementations is faster, as this is much more complicated than simply checking all possibilities.
    // No within_function call should be made twice, as the same volume number will not be checked twice because of the properties of the direct_children list and the volume_logic that removes duplicates on each level.
    
    
    // This function uses too much memory, the memory required for the volume logic list is n_volumes^2 ints, or for a MACS monochromator 127000 ints.
    // Instead the original destinations list must be used, and a function for quick lookup in a (sorted) destinations list made.
    // Still need a list of n_volumes length for control to avoid adding the same volume to the list twice.
    
    
    int ListA_length=0,ListB_length=0;
    int done = 0;
    int i,direct_children_index;
    int *temp_pointer;
    double max_priority=-1000000;
    int residing_volume=0; // 0 can be removed from the input list if default is 0
    int this_mask_status,mask_index,mask_global_index;
    
    // volume_logic_copy
    //for (i=0;i<volume_logic.num_elements;i++) volume_logic_copy[i] = volume_logic.elements[i];
    
    // low memory version of volume_logic_copy
    for (i=0;i<number_of_volumes;i++) volume_logic_copy[i] = 0;
    for (i=0;i<destinations_list.num_elements;i++) volume_logic_copy[destinations_list.elements[i]] = 1;
    printf("within_which_volume debug %i\n",input_list.num_elements);

    // Does one loop through the algorithm first to set up ListA instead of copying it from input_list, which takes time
    for (i=0;i<input_list.num_elements;i++) {
            if (Volumes[input_list.elements[i]]->geometry.within_function(pos,&Volumes[input_list.elements[i]]->geometry) == 1) {
                printf("The position is inside of volume %d\n",input_list.elements[i]);
                if (Volumes[input_list.elements[i]]->geometry.is_masked_volume == 1) {
                    // if the volume is masked, I need to know if it can be a destination volume from the mask_status_list.
                    // if the masked volume is in ANY mode,
                    this_mask_status=1;
                    //print_1d_int_list(*mask_status_list,"mask status list from within_which_volume");
                    for (mask_index=0;mask_index<Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.num_elements;mask_index++) {
                        //printf("Looking at the mask with global index %d \n",Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.elements[mask_index]);
                        if (mask_status_list->elements[Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.elements[mask_index]] == 1) {
                            if (Volumes[input_list.elements[i]]->geometry.mask_mode == 2) { // ANY (break if any one in)
                                this_mask_status=1;
                                break;
                            }
                            //printf("global index %d had mask status = 1 \n",Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.elements[mask_index]);
                        } else {
                          //printf("global index %d had mask status = 0 \n",Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.elements[mask_index]);
                          this_mask_status = 0;
                          if(Volumes[input_list.elements[i]]->geometry.mask_mode == 1) break; // ALL (break if any one out)
                        }
                    }
                    //printf("This volume is masked, and the mask status is %d\n",this_mask_status);
                } else this_mask_status = 1; // if the volume is not masked
            
                if (Volumes[input_list.elements[i]]->geometry.priority_value > max_priority && this_mask_status == 1) {
                    max_priority = Volumes[input_list.elements[i]]->geometry.priority_value;
                    residing_volume = input_list.elements[i];
                    //printf("residing volume set to %d\n",residing_volume);
                }
                    for (direct_children_index = 0;direct_children_index < Volumes[input_list.elements[i]]->geometry.direct_children.num_elements;direct_children_index++) {
                        if (volume_logic_copy[Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index]] == 1) {
                            ListA[ListA_length++] = Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index];
                            volume_logic_copy[Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index]] = 0;
                        }
                    }
            }
    }
    //printf("Completed first loop, continued in while loop\n");
    if (ListA_length > 0) {
        while (done == 0) {
            for (i=0;i<ListA_length;i++) {
              //printf("checking element number %d of list A which is volume number %d \n",i,ListA[i]);
                if (Volumes[ListA[i]]->geometry.within_function(pos,&Volumes[ListA[i]]->geometry) == 1) {
                  //printf("ray was inside this volume \n");
                    if (Volumes[ListA[i]]->geometry.is_masked_volume == 1) {
                      //printf("it is a mask and thus need check of mask status \n");
                        // if the volume is masked, I need to know if it can be a destination volume from the mask_status_list.
                        // if the masked volume is in ANY mode,
                        this_mask_status=1;
                        for (mask_index=0;mask_index<Volumes[ListA[i]]->geometry.masked_by_mask_index_list.num_elements;mask_index++) {
                            if (mask_status_list->elements[Volumes[ListA[i]]->geometry.masked_by_mask_index_list.elements[mask_index]] == 1) {
                                if (Volumes[ListA[i]]->geometry.mask_mode == 2) { // ANY (break if any one in)
                                    this_mask_status=1;
                                    break;
                                }
                            } else {
                              this_mask_status = 0;
                              if(Volumes[ListA[i]]->geometry.mask_mode == 1) break; // ALL (break if any one out)
                            }
                        }
                    } else this_mask_status = 1;
                    //printf("the mask status is %d \n",this_mask_status);
                    if (Volumes[ListA[i]]->geometry.priority_value > max_priority && this_mask_status == 1) {
                        max_priority = Volumes[ListA[i]]->geometry.priority_value;
                        residing_volume = ListA[i];
                    }
                    //printf("Adding direct children to list B \n");
                        for (direct_children_index = 0;direct_children_index < Volumes[ListA[i]]->geometry.direct_children.num_elements;direct_children_index++) {
                            //printf("Checking direct_child number %d which is %d \n",direct_children_index,Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index]);
                            if (volume_logic_copy[Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index]] == 1) {
                                //printf("It's volume_logic was 1, and it is thus added to listB with index %d \n",ListB_length);
                                ListB[ListB_length++] = Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index];
                                volume_logic_copy[Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index]] = 0;
                            }
                        }
                    //printf("List B is now: ");
                    //for (direct_children_index=0;direct_children_index<ListB_length;direct_children_index++) printf("%d ",ListB[direct_children_index]);
                    //printf("\n");
                    
                    
                }
            }
            if (ListB_length==0) done = 1;
            else {
                
                for (i=0;i<ListB_length;i++) ListA[i] = ListB[i];
                ListA_length = ListB_length;
                ListB_length = 0;
                
                /*
                // Could do this with pointers instead to avoid this for loop (and needless copy)
                // This code block fails on the cluster in rare circumstances
                ListA = temp_pointer;
                ListA = ListB;
                ListB = temp_pointer;
                ListA_length=ListB_length;
                ListB_length=0;
                */
            }
        }
    }
    //printf("residing volume returned %d\n",residing_volume);
    return residing_volume;
};

int within_which_volume_GPU(Coords pos, struct pointer_to_1d_int_list input_list, struct pointer_to_1d_int_list destinations_list, struct Volume_struct **Volumes, struct pointer_to_1d_int_list *mask_status_list, int number_of_volumes, int *volume_logic_copy, int *ListA, int *ListB) {
    // This function identifies in which of the volumes of the input list the position pos lies in.
    // pos: position for which the current volume should be found for
    // input list: list of potential volumes, reduced to the ones without parents (their children will be checked)
    // OLD VERSION: volume logic: A logic list of allowed volumes for lookup, volume 1 3 and 5 in a case of 10 volumes would be [0 1 0 1 0 1 0 0 0 0]
    // destinations_list: list of allowed destinations (the original destinations list)
    // Volumes: Main volumes array
    // volume_logic_copy: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    // ListA: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    // ListB: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    
    // Algorithm description
    // Check all of input list for pos being within them, those that have it within them are:
    //      checked against the current highest priority
    //          if higher, is the new highest priority and the new pick for volume
    //      have all their direct children added to the next list to be checked (but any volume can only be added to that list once)
    // Once a run have been made where the next list to check is empty, the answer for new pick for volume is taken.
    
    // Mask update:
    // Algorithm description
    // Check all of input list for pos being within them, those that have it within them are:
    //      checked against the current highest priority and mask status
    //          if higher, this is the new highest priority and the new pick for volume
    //      have all their direct children added to the next list to be checked (but any volume can only be added to that list once)
    // Once a run have been made where the next list to check is empty, the answer for new pick for volume is taken.
    
    // The advantage of the method is that potentially large numbers of children are skipped when their parents do not contain the position.
    // The overhead cost is low, as all the lists are prealocated.
    // Should be checked which of the two implementations is faster, as this is much more complicated than simply checking all possibilities.
    // No within_function call should be made twice, as the same volume number will not be checked twice because of the properties of the direct_children list and the volume_logic that removes duplicates on each level.
    
    
    // This function uses too much memory, the memory required for the volume logic list is n_volumes^2 ints, or for a MACS monochromator 127000 ints.
    // Instead the original destinations list must be used, and a function for quick lookup in a (sorted) destinations list made.
    // Still need a list of n_volumes length for control to avoid adding the same volume to the list twice.
    
    
    int ListA_length=0,ListB_length=0;
    int done = 0;
    int i,direct_children_index;
    int *temp_pointer;
    double max_priority=-1000000;
    int residing_volume=0; // 0 can be removed from the input list if default is 0
    int this_mask_status,mask_index,mask_global_index;
    
    // volume_logic_copy
    //for (i=0;i<volume_logic.num_elements;i++) volume_logic_copy[i] = volume_logic.elements[i];
    
    // low memory version of volume_logic_copy
    for (i=0;i<number_of_volumes;i++) volume_logic_copy[i] = 0;
    for (i=0;i<destinations_list.num_elements;i++) volume_logic_copy[destinations_list.elements[i]] = 1;
    //printf("within_which_volume debug\n");

    // Does one loop through the algorithm first to set up ListA instead of copying it from input_list, which takes time
    for (i=0;i<input_list.num_elements;i++) {
            if (r_within_function(pos, &Volumes[input_list.elements[i]]->geometry) == 1) {
                //printf("The position is inside of volume %d\n",input_list.elements[i]);
                if (Volumes[input_list.elements[i]]->geometry.is_masked_volume == 1) {
                    // if the volume is masked, I need to know if it can be a destination volume from the mask_status_list.
                    // if the masked volume is in ANY mode,
                    this_mask_status=1;
                    //print_1d_int_list(*mask_status_list,"mask status list from within_which_volume");
                    for (mask_index=0;mask_index<Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.num_elements;mask_index++) {
                        //printf("Looking at the mask with global index %d \n",Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.elements[mask_index]);
                        if (mask_status_list->elements[Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.elements[mask_index]] == 1) {
                            if (Volumes[input_list.elements[i]]->geometry.mask_mode == 2) { // ANY (break if any one in)
                                this_mask_status=1;
                                break;
                            }
                            //printf("global index %d had mask status = 1 \n",Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.elements[mask_index]);
                        } else {
                          //printf("global index %d had mask status = 0 \n",Volumes[input_list.elements[i]]->geometry.masked_by_mask_index_list.elements[mask_index]);
                          this_mask_status = 0;
                          if(Volumes[input_list.elements[i]]->geometry.mask_mode == 1) break; // ALL (break if any one out)
                        }
                    }
                    //printf("This volume is masked, and the mask status is %d\n",this_mask_status);
                } else this_mask_status = 1; // if the volume is not masked
            
                if (Volumes[input_list.elements[i]]->geometry.priority_value > max_priority && this_mask_status == 1) {
                    max_priority = Volumes[input_list.elements[i]]->geometry.priority_value;
                    residing_volume = input_list.elements[i];
                    //printf("residing volume set to %d\n",residing_volume);
                }
                    for (direct_children_index = 0;direct_children_index < Volumes[input_list.elements[i]]->geometry.direct_children.num_elements;direct_children_index++) {
                        if (volume_logic_copy[Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index]] == 1) {
                            ListA[ListA_length++] = Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index];
                            volume_logic_copy[Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index]] = 0;
                        }
                    }
            }
    }
    //printf("Completed first loop, continued in while loop\n");
    if (ListA_length > 0) {
        while (done == 0) {
            for (i=0;i<ListA_length;i++) {
              //printf("checking element number %d of list A which is volume number %d \n",i,ListA[i]);
                if (r_within_function(pos,&Volumes[ListA[i]]->geometry) == 1) {
                  //printf("ray was inside this volume \n");
                    if (Volumes[ListA[i]]->geometry.is_masked_volume == 1) {
                      //printf("it is a mask and thus need check of mask status \n");
                        // if the volume is masked, I need to know if it can be a destination volume from the mask_status_list.
                        // if the masked volume is in ANY mode,
                        this_mask_status=1;
                        for (mask_index=0;mask_index<Volumes[ListA[i]]->geometry.masked_by_mask_index_list.num_elements;mask_index++) {
                            if (mask_status_list->elements[Volumes[ListA[i]]->geometry.masked_by_mask_index_list.elements[mask_index]] == 1) {
                                if (Volumes[ListA[i]]->geometry.mask_mode == 2) { // ANY (break if any one in)
                                    this_mask_status=1;
                                    break;
                                }
                            } else {
                              this_mask_status = 0;
                              if(Volumes[ListA[i]]->geometry.mask_mode == 1) break; // ALL (break if any one out)
                            }
                        }
                    } else this_mask_status = 1;
                    //printf("the mask status is %d \n",this_mask_status);
                    if (Volumes[ListA[i]]->geometry.priority_value > max_priority && this_mask_status == 1) {
                        max_priority = Volumes[ListA[i]]->geometry.priority_value;
                        residing_volume = ListA[i];
                    }
                    //printf("Adding direct children to list B \n");
                        for (direct_children_index = 0;direct_children_index < Volumes[ListA[i]]->geometry.direct_children.num_elements;direct_children_index++) {
                            //printf("Checking direct_child number %d which is %d \n",direct_children_index,Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index]);
                            if (volume_logic_copy[Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index]] == 1) {
                                //printf("It's volume_logic was 1, and it is thus added to listB with index %d \n",ListB_length);
                                ListB[ListB_length++] = Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index];
                                volume_logic_copy[Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index]] = 0;
                            }
                        }
                    //printf("List B is now: ");
                    //for (direct_children_index=0;direct_children_index<ListB_length;direct_children_index++) printf("%d ",ListB[direct_children_index]);
                    //printf("\n");
                    
                    
                }
            }
            if (ListB_length==0) done = 1;
            else {
                
                for (i=0;i<ListB_length;i++) ListA[i] = ListB[i];
                ListA_length = ListB_length;
                ListB_length = 0;
                
                /*
                // Could do this with pointers instead to avoid this for loop (and needless copy)
                // This code block fails on the cluster in rare circumstances
                ListA = temp_pointer;
                ListA = ListB;
                ListB = temp_pointer;
                ListA_length=ListB_length;
                ListB_length=0;
                */
            }
        }
    }
    //printf("residing volume returned %d\n",residing_volume);
    return residing_volume;
};




int within_which_volume_debug(Coords pos, struct pointer_to_1d_int_list input_list, struct pointer_to_1d_int_list volume_logic, struct Volume_struct **Volumes, int *volume_logic_copy, int *ListA, int *ListB) {
    // This function identifies in which of the volumes of the input list the position pos lies in.
    // pos: position for which the current volume should be found for
    // input list: list of potential volumes, reduced to the ones without parents (their children will be checked)
    // volume logic: A logic list of allowed volumes for lookup, volume 1 3 and 5 in a case of 10 volumes would be [0 1 0 1 0 1 0 0 0 0]
    // Volumes: Main volumes array
    // volume_logic_copy: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    // ListA: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    // ListB: A pointer to a integer array with at least length "number_of_volumes" (pre alocated for speed)
    
    // Algorithm description
    // Check all of input list for pos being within them, those that have it within them are:
    //      checked against the current highest priority
    //          if higher, is the new highest priority and the new pick for volume
    //      have all their direct children added to the next list to be checked (but any volume can only be added to that list once)
    // Once a run have been made where the next list to check is empty, the answer for new pick for volume is taken.
    
    // The advantage of the method is that potentially large numbers of children are skipped when their parents do not contain the position.
    // The overhead cost is low, as all the lists are prealocated.
    // Should be checked which of the two implementations is faster, as this is much more complicated than simply checking all possibilities.
    // No within_function call should be made twice, as the same volume number will not be checked twice because of the properties of the direct_children list and the volume_logic that removes duplicates on each level.
    
    
    int ListA_length=0,ListB_length=0;
    int done = 0;
    int i,direct_children_index;
    int *temp_pointer;
    double max_priority=-1000000;
    int residing_volume=0; // 0 can be removed from the input list if default is 0
    
    // volume_logic_copy
    for (i=0;i<volume_logic.num_elements;i++) volume_logic_copy[i] = volume_logic.elements[i];
    
    // Does one loop through the algorithm first to set up ListA instead of copying it from input_list, which takes time
    for (i=0;i<input_list.num_elements;i++) {
            if (Volumes[input_list.elements[i]]->geometry.within_function(pos,&Volumes[input_list.elements[i]]->geometry) == 1) {
                if (Volumes[input_list.elements[i]]->geometry.priority_value > max_priority) {
                    max_priority = Volumes[input_list.elements[i]]->geometry.priority_value;
                    residing_volume = input_list.elements[i];
                }
                    for (direct_children_index = 0;direct_children_index < Volumes[input_list.elements[i]]->geometry.direct_children.num_elements;direct_children_index++) {
                        if (volume_logic_copy[Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index]] == 1) {
                            ListA[ListA_length++] = Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index];
                            volume_logic_copy[Volumes[input_list.elements[i]]->geometry.direct_children.elements[direct_children_index]] = 0;
                        }
                    }
            }
    }
    if (ListA_length > 0) {
        while (done == 0) {
        
            printf("ListA = [");
            for (i=0;i<ListA_length;i++) {
                printf("%d,",ListA[i]);
            }
            printf("]\n");
            for (i=0;i<ListA_length;i++) {
                if (Volumes[ListA[i]]->geometry.within_function(pos,&Volumes[ListA[i]]->geometry) == 1) {
                    if (Volumes[ListA[i]]->geometry.priority_value > max_priority) {
                        max_priority = Volumes[ListA[i]]->geometry.priority_value;
                        residing_volume = ListA[i];
                    }
                    //if (ListA[i]!=0) {
                        for (direct_children_index = 0;direct_children_index < Volumes[ListA[i]]->geometry.direct_children.num_elements;direct_children_index++) {
                            if (volume_logic_copy[Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index]] == 1) {
                                ListB[ListB_length++] = Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index];
                                volume_logic_copy[Volumes[ListA[i]]->geometry.direct_children.elements[direct_children_index]] = 0;
                            }
                        }
                    //}
                }
            }
            if (ListB_length==0) done = 1;
            else {
                ListA = temp_pointer;
                ListA = ListB;
                ListB = temp_pointer;
                ListA_length=ListB_length;
                ListB_length=0;
            }
        }
    }
    printf("Volume number %d had the highest priority of checked volumes\n",residing_volume);
    return residing_volume;
};

int inside_function(struct Volume_struct *parent_volume, struct Volume_struct *child_volume) {
    // Function that calls the correct within function depending on the shapes of the two volumes
    if (strcmp("sphere",parent_volume->geometry.shape) == 0 && strcmp("sphere",child_volume->geometry.shape) == 0) {
        if (sphere_within_sphere(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("cylinder",parent_volume->geometry.shape) == 0 && strcmp("cylinder",child_volume->geometry.shape) == 0) {
        if (cylinder_within_cylinder(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("box",parent_volume->geometry.shape) == 0 && strcmp("box",child_volume->geometry.shape) == 0) {
        if (box_within_box(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("cone",parent_volume->geometry.shape) == 0 && strcmp("cone",child_volume->geometry.shape) == 0) {
        if (cone_within_cone(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("mesh",parent_volume->geometry.shape) == 0 && strcmp("mesh",child_volume->geometry.shape) == 0) {
        if (mesh_within_mesh(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("box",parent_volume->geometry.shape) == 0 && strcmp("cylinder",child_volume->geometry.shape) == 0) {
        if (cylinder_within_box(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("cylinder",parent_volume->geometry.shape) == 0 && strcmp("box",child_volume->geometry.shape) == 0) {
        if (box_within_cylinder(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("box",parent_volume->geometry.shape) == 0 && strcmp("sphere",child_volume->geometry.shape) == 0) {
        if (sphere_within_box(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("sphere",parent_volume->geometry.shape) == 0 && strcmp("box",child_volume->geometry.shape) == 0) {
        if (box_within_sphere(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("sphere",parent_volume->geometry.shape) == 0 && strcmp("cylinder",child_volume->geometry.shape) == 0) {
        if (cylinder_within_sphere(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("cylinder",parent_volume->geometry.shape) == 0 && strcmp("sphere",child_volume->geometry.shape) == 0) {
        if (sphere_within_cylinder(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("cone",parent_volume->geometry.shape) == 0 && strcmp("sphere",child_volume->geometry.shape) == 0) {
        if (sphere_within_cone(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("sphere",parent_volume->geometry.shape) == 0 && strcmp("cone",child_volume->geometry.shape) == 0) {
        if (cone_within_sphere(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("cone",parent_volume->geometry.shape) == 0 && strcmp("cylinder",child_volume->geometry.shape) == 0) {
        if (cylinder_within_cone(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("cylinder",parent_volume->geometry.shape) == 0 && strcmp("cone",child_volume->geometry.shape) == 0) {
        if (cone_within_cylinder(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("cone",parent_volume->geometry.shape) == 0 && strcmp("box",child_volume->geometry.shape) == 0) {
        if (box_within_cone(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else if (strcmp("box",parent_volume->geometry.shape) == 0 && strcmp("cone",child_volume->geometry.shape) == 0) {
        if (cone_within_box(&child_volume->geometry,&parent_volume->geometry)) return 1;
    }
    else {
        #ifndef OPENACC
        printf("Need within function for type: ");
        printf("%s",parent_volume->geometry.shape);
        printf(" and type: ");
        printf("%s",child_volume->geometry.shape);
        printf(".\n");
        printf("It is not yet supported to mix mesh geometries with the basic shapes, but several mesh geometries are allowed.\n");
        exit(1);
	#endif
    }
    
    return 0;
};

void generate_children_lists(struct Volume_struct **Volumes, struct pointer_to_1d_int_list **true_children_lists, int number_of_volumes, int verbal) {
  // This function generates a list of children for each volume.
  // A volume m is a child of volume n, if the entire space ocupied by volume m is inside of the space ocupied by volume n
  // A volume m is a true child of volume n, if the entire space coupied by volume m after it's masks are applied is inside the volume ocupied by volume n after it's masks are applied
  
  
  MPI_MASTER(
  if (verbal) printf("\nGenerating children lists --------------------------- \n");
  )

  // Mask update: Creating a temporary list for each volume
  struct pointer_to_1d_int_list *temporary_children_lists;
  temporary_children_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list));

  // The surrounding vacuum, volume 0, done outside of for loop.
  temporary_children_lists[0].num_elements = number_of_volumes - 1;
  temporary_children_lists[0].elements = malloc(temporary_children_lists[0].num_elements*sizeof(int));
  
  int parent;
  for (parent=1;parent<number_of_volumes;parent++) {
      temporary_children_lists[0].elements[parent-1] = parent;
  }
  
  //if (verbal) printf("did temporary children list \n");
  //print_1d_int_list(temporary_children_lists[0],"temp children list [0]");
  
  // Hardcoding that every volume is a child of the surrounding vacuum
  Volumes[0]->geometry.children.num_elements = number_of_volumes-1;
  Volumes[0]->geometry.children.elements = malloc((number_of_volumes-1)*sizeof(int));
  true_children_lists[0] = malloc(sizeof(struct pointer_to_1d_int_list));
  true_children_lists[0]->num_elements = number_of_volumes - 1;
  true_children_lists[0]->elements = malloc((number_of_volumes-1)*sizeof(int));
  
  //if (verbal) printf("allocated true children lists \n");
  
  for (parent=1;parent<number_of_volumes;parent++) {
      Volumes[0]->geometry.children.elements[parent-1] = parent;
      true_children_lists[0]->elements[parent-1] = parent;
  }
  
  char string_output[128];
  MPI_MASTER(
  if (verbal) sprintf(string_output,"Children for Volume %d",0);
  if (verbal) print_1d_int_list(Volumes[0]->geometry.children,string_output);
  )
  
  
  // Generating the children lists for all other volumes using the appropriate geometry functions
  struct pointer_to_1d_int_list temp_list_local;
  temp_list_local.num_elements = number_of_volumes;
  temp_list_local.elements = malloc(number_of_volumes*sizeof(int));
  
  struct pointer_to_1d_int_list true_temp_list_local;
  true_temp_list_local.num_elements = number_of_volumes;
  true_temp_list_local.elements = malloc(number_of_volumes*sizeof(int));
  
  
  int child,used_elements,used_elements_true;
  for (parent=1;parent<number_of_volumes;parent++) {
      used_elements = 0;used_elements_true = 0;
      for (child=1;child<number_of_volumes;child++) {
        if (child != parent) {
          // Call inside_function that selects the proper within function for the two volumes
          if (1 == inside_function(Volumes[parent],Volumes[child])) {
            temp_list_local.elements[used_elements++] = child;
            true_temp_list_local.elements[used_elements_true++] = child;
          }
        } else true_temp_list_local.elements[used_elements_true++] = child; // Needed when children list takes mask into account
      }
      // Temp test
      allocate_list_from_temp(used_elements,temp_list_local,&Volumes[parent]->geometry.children);
      // Assing the children list to a temporary list as the masks have yet to be taken into account
      temporary_children_lists[parent].num_elements=0;
      allocate_list_from_temp(used_elements_true,true_temp_list_local,&temporary_children_lists[parent]);
      
      
      MPI_MASTER(
      if (verbal) sprintf(string_output,"Children for Volume %d (temporary_list)",parent);
      if (verbal) print_1d_int_list(temporary_children_lists[parent],string_output);
      )
      
      MPI_MASTER(
      if (verbal) sprintf(string_output,"Children for Volume %d (permanent_list)",parent);
      if (verbal) print_1d_int_list(Volumes[parent]->geometry.children,string_output);
      )
      
  }
  
  // mask update:
  // The logical expression: (child c parent AND child c parent_mask) OR (child_mask c parent AND child_mask c parent_mask)
  //  needs to be evaluated for each child / parent combination in order to take the masks of each into account
  
  int logic_var1,logic_var2,logic_var_ANY,logic_var_ALL;
  int mask_index,mask_index_child,mask_index_parent;
  int volume_C,volume_P;
  // Loop that takes masks into account
  for (parent=1;parent<number_of_volumes;parent++) {
    used_elements = 0;
    for (child=1;child<number_of_volumes;child++) {
     if (child != parent && 0 == on_int_list(Volumes[parent]->geometry.masked_by_list,child)) {
        // The children list for each volume does not need to contain the volume itself
        //  And a parent masked by it's child can not have that mask as a child
      
      // Here c means within in the sense of a set being part of another set
      // Logical expression to be evaluated: (child c parent AND child c parent_mask) OR (child_mask c parent AND child_mask c parent_mask)
      logic_var1 = on_int_list(temporary_children_lists[parent],child);
      if (logic_var1 == 1 && Volumes[parent]->geometry.is_masked_volume == 1) {
        // if the parent volume is masked, the child also need to be inclosed in these masks to fulfill this side of the logical expression
        logic_var_ANY = 0;
        for (mask_index=0;mask_index<Volumes[parent]->geometry.masked_by_list.num_elements;mask_index++) {
          if (0 == on_int_list(temporary_children_lists[Volumes[parent]->geometry.masked_by_list.elements[mask_index]],child)) {
            if (Volumes[parent]->geometry.mask_mode == 1) {
              logic_var1 = 0;
              break;
            }
          } else logic_var_ANY = 1;
        }
        if (Volumes[parent]->geometry.mask_mode == 2) logic_var1 = logic_var_ANY;
      }
      
      if (logic_var1 == 1) true_temp_list_local.elements[used_elements++] = child;
      else if (Volumes[child]->geometry.is_masked_volume == 1) {
        // If the first side of the logical expression is false, evalute the other side
        // The other side is only relevant if the child volume is masked, otherwise it is ignored
        //printf("Second side of logical expression \n");
        
        logic_var1 = 1; // Assume true
          
        // child_mask c parent
        logic_var_ALL = 0;
        for (mask_index=0;mask_index<Volumes[child]->geometry.masked_by_list.num_elements;mask_index++) {
          if (0 == on_int_list(temporary_children_lists[parent],Volumes[child]->geometry.masked_by_list.elements[mask_index])){
            if (Volumes[child]->geometry.mask_mode == 2) {
              logic_var1 = 0;
              break;
            }
          } else logic_var_ALL = 1;
        }
        if (Volumes[child]->geometry.mask_mode == 1) logic_var1 = logic_var_ALL;
        
        // This line allows the second part of the logical expression to be true in cases where the parent volume is not masked
        if (logic_var1 == 1 && Volumes[parent]->geometry.is_masked_volume == 0) true_temp_list_local.elements[used_elements++] = child;
        
        // There is no reason to check the other part (child_mask c parent_mask) if the first part was not true
        if (logic_var1 == 1 && Volumes[parent]->geometry.is_masked_volume == 1) {
          // This last part requires both the child and the parent to be masked
          // Need to evaluate (child_mask c parent_mask), where both can be a be a list of volume with ALL/ANY modes
          
          if (Volumes[parent]->geometry.mask_mode == 1) {
            logic_var2 = 1; // Assume the logical expression (child_mask c parent_mask) is true
            for (mask_index_parent=0;mask_index_parent<Volumes[parent]->geometry.masked_by_list.num_elements;mask_index_parent++) {
              // As the parent is in ALL mode, the child masks must be within ALL parent masks
              logic_var_ANY = 0; // Assume not a single child is within this parent
              for (mask_index_child=0;mask_index_child<Volumes[child]->geometry.masked_by_list.num_elements;mask_index_child++) {
                volume_P = Volumes[parent]->geometry.masked_by_list.elements[mask_index_parent];
                volume_C = Volumes[child]->geometry.masked_by_list.elements[mask_index_child];
                // Is volume C inside volume P? If yes, volume C must be on volume P's temporary children list
                if (0 == on_int_list(temporary_children_lists[volume_P],volume_C)) {
                  if (Volumes[child]->geometry.mask_mode == 2) {
                    // If child is in ANY mode, any one mask outside is enough to make the expression false
                    logic_var2 = 0;
                    break;
                  }
                } else logic_var_ANY = 1;
              }
              // If child is in ALL mode, then if any one child were within this mask, the logic expression holds true
              if (Volumes[child]->geometry.mask_mode == 1) logic_var2 = logic_var_ANY;
              if (logic_var2 == 0) break; // No need to continue
            }
          } else if (Volumes[parent]->geometry.mask_mode == 2) {
            // If the parent is in ANY mode, it is enough if the child masks are within just 1 of the parent masks
            for (mask_index_parent=0;mask_index_parent<Volumes[parent]->geometry.masked_by_list.num_elements;mask_index_parent++) {
              logic_var2 = 1; // Assume the logical expression (child_mask c parent_mask) is true
              logic_var_ANY = 0; // Assume not a single child is within this parent
              for (mask_index_child=0;mask_index_child<Volumes[child]->geometry.masked_by_list.num_elements;mask_index_child++) {
                volume_P = Volumes[parent]->geometry.masked_by_list.elements[mask_index_parent];
                volume_C = Volumes[child]->geometry.masked_by_list.elements[mask_index_child];
                // Is volume C inside volume P? If yes, volume C must be on volume P's temporary children list
                if (0 == on_int_list(temporary_children_lists[volume_P],volume_C)) {
                  if (Volumes[child]->geometry.mask_mode == 2) {
                    // If child is in ANY mode, any one mask outside is enough to make the expression false
                    logic_var2 = 0;
                    break;
                  }
                } else logic_var_ANY = 1;
              }
              // If child is in ALL mode, then if any one child were within this mask, the logic expression holds true
              if (Volumes[child]->geometry.mask_mode == 1) logic_var2 = logic_var_ANY;
              if (logic_var2 == 1) break; // No need to continue
            }
          }
          
          // if this point is reached, and logic_var2 is true, volume[child] is a child of volume[parent]
          if (logic_var2 == 1) true_temp_list_local.elements[used_elements++] = child;
          
        }
      }
     }
    }
    
    true_children_lists[parent] = malloc(sizeof(struct pointer_to_1d_int_list));
    true_children_lists[parent]->num_elements = 0;
    allocate_list_from_temp(used_elements,true_temp_list_local,true_children_lists[parent]);
    
    MPI_MASTER(
      if (verbal) sprintf(string_output,"True children for Volume (post mask) %d",parent);
      if (verbal) print_1d_int_list(*true_children_lists[parent],string_output);
    )
  }
  
  
  // Clean up of dynamically allocated memory
  
  for(child=0;child<number_of_volumes;child++) free(temporary_children_lists[child].elements);
  free(temporary_children_lists);
  free(true_temp_list_local.elements);
  free(temp_list_local.elements);
};

void generate_overlap_lists(struct pointer_to_1d_int_list **true_overlap_lists, struct pointer_to_1d_int_list **raw_overlap_lists, struct Volume_struct **Volumes, int number_of_volumes, int verbal) {
  // This function generates overlap lists for each volume
  // Volume m overlaps volume n if there is some subset of space they both ocupy (this is called raw overlap)
  // the true overlap list takes mask into account, meaning that the masks are applied before searching for overlap

  MPI_MASTER(
  if (verbal) printf("\nGenerating overlap lists ---------------------------- \n");
  )
  
  // Manually create the overlap list for the surrounding Vacuum, as it overlaps all other volumes
  
  // temporary_overlap_lists are used to save the calculated overlaps to avoid evaluating the heavy functions more than once (twice) for each possible volume pair
  struct pointer_to_1d_int_list *temporary_overlap_lists;
  temporary_overlap_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list));
  
  // Overlap_lists are the final result of the function, and for the surrounding volume it can be set immediatly
  true_overlap_lists[0] = malloc(sizeof(struct pointer_to_1d_int_list));
  true_overlap_lists[0]->num_elements = number_of_volumes-1;
  true_overlap_lists[0]->elements = malloc((number_of_volumes-1)*sizeof(int));
  
  raw_overlap_lists[0] =  malloc(sizeof(struct pointer_to_1d_int_list));
  raw_overlap_lists[0]->num_elements = number_of_volumes;
  raw_overlap_lists[0]->elements = malloc(number_of_volumes*sizeof(int));
  raw_overlap_lists[0]->elements[0] = 0; // Volume 0 overlaps itself
  
  int parent;
  for (parent=1;parent<number_of_volumes;parent++) {
      true_overlap_lists[0]->elements[parent-1] = parent;
      raw_overlap_lists[0]->elements[parent] = parent;
  }
  
  char string_output[128];
  MPI_MASTER(
  if (verbal) sprintf(string_output,"Overlaps for Volume %d",0);
  if (verbal) print_1d_int_list(*true_overlap_lists[0],string_output);
  )
  
  // Generate the overlap lists for the remaining volumes
  struct pointer_to_1d_int_list temp_list_local;
  temp_list_local.num_elements = number_of_volumes;
  temp_list_local.elements = malloc(number_of_volumes*sizeof(int));
  
  int child,used_elements;
  // Create overlap for the remaining volumes
  for (parent=1;parent<number_of_volumes;parent++) {
      true_overlap_lists[parent] = malloc(sizeof(struct pointer_to_1d_int_list));
      used_elements = 0;
      temp_list_local.elements[used_elements++] = 0; // Alwasy overlaps with the surrounding vacuum.
      
      for (child=1;child<number_of_volumes;child++) {
        if (child == parent) temp_list_local.elements[used_elements++] = child;
        else {
        if (strcmp("sphere",Volumes[parent]->geometry.shape) == 0 && strcmp("sphere",Volumes[child]->geometry.shape) == 0) {
            if (sphere_overlaps_sphere(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("cylinder",Volumes[parent]->geometry.shape) == 0 && strcmp("cylinder",Volumes[child]->geometry.shape) == 0) {
            if (cylinder_overlaps_cylinder(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("box",Volumes[parent]->geometry.shape) == 0 && strcmp("box",Volumes[child]->geometry.shape) == 0) {
            if (box_overlaps_box(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("cone",Volumes[parent]->geometry.shape) == 0 && strcmp("cone",Volumes[child]->geometry.shape) == 0) {
            if (cone_overlaps_cone(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("mesh",Volumes[parent]->geometry.shape) == 0 && strcmp("mesh",Volumes[child]->geometry.shape) == 0) {
            if (mesh_overlaps_mesh(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("box",Volumes[parent]->geometry.shape) == 0 && strcmp("cylinder",Volumes[child]->geometry.shape) == 0) {
            if (box_overlaps_cylinder(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("cylinder",Volumes[parent]->geometry.shape) == 0 && strcmp("box",Volumes[child]->geometry.shape) == 0) {
            if (cylinder_overlaps_box(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("box",Volumes[parent]->geometry.shape) == 0 && strcmp("sphere",Volumes[child]->geometry.shape) == 0) {
            if (box_overlaps_sphere(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("sphere",Volumes[parent]->geometry.shape) == 0 && strcmp("box",Volumes[child]->geometry.shape) == 0) {
            if (sphere_overlaps_box(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("sphere",Volumes[parent]->geometry.shape) == 0 && strcmp("cylinder",Volumes[child]->geometry.shape) == 0) {
            if (sphere_overlaps_cylinder(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("cylinder",Volumes[parent]->geometry.shape) == 0 && strcmp("sphere",Volumes[child]->geometry.shape) == 0) {
            if (cylinder_overlaps_sphere(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("cone",Volumes[parent]->geometry.shape) == 0 && strcmp("sphere",Volumes[child]->geometry.shape) == 0) {
            if (cone_overlaps_sphere(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("sphere",Volumes[parent]->geometry.shape) == 0 && strcmp("cone",Volumes[child]->geometry.shape) == 0) {
            if (sphere_overlaps_cone(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("cone",Volumes[parent]->geometry.shape) == 0 && strcmp("cylinder",Volumes[child]->geometry.shape) == 0) {
            if (cone_overlaps_cylinder(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("cylinder",Volumes[parent]->geometry.shape) == 0 && strcmp("cone",Volumes[child]->geometry.shape) == 0) {
            if (cylinder_overlaps_cone(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("cone",Volumes[parent]->geometry.shape) == 0 && strcmp("box",Volumes[child]->geometry.shape) == 0) {
            if (cone_overlaps_box(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else if (strcmp("box",Volumes[parent]->geometry.shape) == 0 && strcmp("cone",Volumes[child]->geometry.shape) == 0) {
            if (box_overlaps_cone(&Volumes[parent]->geometry,&Volumes[child]->geometry)) temp_list_local.elements[used_elements++] = child;
        }
        else {
            printf("Need overlap function for type: ");
            printf("%s",Volumes[parent]->geometry.shape);
            printf(" and type: ");
            printf("%s",Volumes[child]->geometry.shape);
            printf(".\n");
            printf("It is not yet supported to mix mesh geometries with the basic shapes, but several mesh geometries are allowed.\n");
            exit(1);
        }
        }
      }
      //allocate_list_from_temp(used_elements,temp_list_local,overlap_lists[parent]);
      allocate_list_from_temp(used_elements,temp_list_local,&temporary_overlap_lists[parent]);
      
      // Save the raw overlap data to the raw_overlap_lists[parent] list
      raw_overlap_lists[parent] = malloc(sizeof(struct pointer_to_1d_int_list));
      raw_overlap_lists[parent]->num_elements = 0;
      allocate_list_from_temp(used_elements,temp_list_local,raw_overlap_lists[parent]);
      
      if (verbal) sprintf(string_output,"Overlaps for Volume (pre mask) %d",parent);
      MPI_MASTER(
      if (verbal) print_1d_int_list(temporary_overlap_lists[parent],string_output);
      )
  }
  
  
  // The temporary_overlap_lists gives the raw overlap data for all volume pairs
  // The next tasks is to take the masks into account, so that a volume is only said to overlap another if all of these statements are true:
  // The volumes overlap each other
  // The mask's of volume 1 overlap volume 2
  // The mask's of volume 2 overlap volume 1
  // The mask's of volume 1 overlap the masks of volume 2
  
  
  int logic_var;
  int overlap_ANY,overlap_ANY_p,overlap_ANY_c;
  int mask_index,mask_index_c,mask_index_p;
  int mask_volume_index,mask_volume_index_p,mask_volume_index_c;

  for (parent=1;parent<number_of_volumes;parent++) {
    used_elements = 0;
    temp_list_local.elements[used_elements++] = 0; // Alwasy overlaps with the surrounding vacuum.
    for (child=1;child<number_of_volumes;child++) {
      if (child != parent) {
        logic_var = 1; // Assume the volumes overlap, and search for evidence that they do not.
      
        // First check if the volumes overlap
        if (0 == on_int_list(temporary_overlap_lists[parent],child)) logic_var = 0;
        
      
        // Check if child overlap with parents masks
        if (logic_var == 1 && Volumes[parent]->geometry.is_masked_volume == 1) {
          overlap_ANY = 0;
          for (mask_index=0;mask_index<Volumes[parent]->geometry.masked_by_list.num_elements;mask_index++) {
            mask_volume_index = Volumes[parent]->geometry.masked_by_list.elements[mask_index];
            if (0 == on_int_list(temporary_overlap_lists[mask_volume_index],child)) {
              if (Volumes[parent]->geometry.mask_mode == 1) {
                logic_var = 0;
                break;
              }
            } else overlap_ANY = 1;
          }
          if (Volumes[parent]->geometry.mask_mode == 2) logic_var = overlap_ANY;
        }
      
        // Check if parent overlap with childs masks
        if (logic_var == 1 && Volumes[child]->geometry.is_masked_volume == 1) {
          overlap_ANY = 0;
          for (mask_index=0;mask_index<Volumes[child]->geometry.masked_by_list.num_elements;mask_index++) {
            mask_volume_index = Volumes[child]->geometry.masked_by_list.elements[mask_index];
            if (0 == on_int_list(temporary_overlap_lists[mask_volume_index],parent)) {
              if (Volumes[child]->geometry.mask_mode == 1) {
                logic_var = 0;
                break;
              }
            } else overlap_ANY = 1;
          }
          if (Volumes[child]->geometry.mask_mode == 2) logic_var = overlap_ANY;
        }
      
        // Check if parents masks overlap childrens masks
        if (logic_var == 1 && Volumes[parent]->geometry.is_masked_volume == 1 && Volumes[child]->geometry.is_masked_volume == 1) {
          overlap_ANY = 0;
          for (mask_index_p=0;mask_index_p<Volumes[parent]->geometry.masked_by_list.num_elements;mask_index_p++) {
            mask_volume_index_p = Volumes[parent]->geometry.masked_by_list.elements[mask_index_p];
            overlap_ANY_p = 1;
            overlap_ANY_c = 0;
            for (mask_index_c=0;mask_index_c<Volumes[child]->geometry.masked_by_list.num_elements;mask_index_c++) {
              mask_volume_index_c = Volumes[child]->geometry.masked_by_list.elements[mask_index_c];
              if (0 == on_int_list(temporary_overlap_lists[mask_volume_index_p],mask_volume_index_c)) {
                if (Volumes[parent]->geometry.mask_mode == 1 && Volumes[child]->geometry.mask_mode == 1) {
                  // If both are in ALL mode and just one combination of masks does not overlap, neither does the common set
                  logic_var = 0;
                  break;
                }
                if (Volumes[parent]->geometry.mask_mode == 2 && Volumes[child]->geometry.mask_mode == 1) {
                  // If the parent is in ANY mode, but the child is in ALL, any one child not overlapping this parent mask, stops the chance for this parent mask
                  overlap_ANY_p = 0;
                  break;
                }
                
              } else {
                // Here because mask_volume_index_p and mask_volume_index_c does overlap
                if (Volumes[parent]->geometry.mask_mode == 1 && Volumes[child]->geometry.mask_mode == 2) {
                  // If the parent is in ALL mode and the child is in ANY mode, stop if a single parent volume does not overlap any child
                  overlap_ANY_c = 1;
                }
                if (Volumes[parent]->geometry.mask_mode == 2 && Volumes[child]->geometry.mask_mode == 2) {
                  // If both parent and child are in any mode, any one overlap between the masks is sufficient
                  overlap_ANY = 1;
                  // Could actually just commit to the overlap list here, and stop all loops.
                }
              }
              
            }
            if (Volumes[parent]->geometry.mask_mode == 1 && Volumes[child]->geometry.mask_mode == 2) logic_var = overlap_ANY_c;
            if (Volumes[parent]->geometry.mask_mode == 2 && Volumes[child]->geometry.mask_mode == 1 && overlap_ANY_p == 1) {
              // When parent is in any mode, and child is in ALL mode, any parent mask that overlaps all children masks is enough to end the parent loop
              logic_var = 1;
              break; // Without this break, only the last parent will matter
            }
            // if (overlap_ANY == 1) break; would speed things up a bit, but only after testing has been started and then it will be repeated
          }
          if (Volumes[parent]->geometry.mask_mode == 2 && Volumes[child]->geometry.mask_mode == 2) logic_var = overlap_ANY;
          // If both volumes have the ANY mode, just one case of overlap is enough.
        }
        
        // If all of the 4 statements above evaluate to true, the two volumes parent and child do overlap and it is added to the list.
        if (logic_var == 1) temp_list_local.elements[used_elements++] = child;
        
      }
    }
    // Allocate the actual overlap list with the new temp_list_local
    allocate_list_from_temp(used_elements,temp_list_local,true_overlap_lists[parent]);
    if (verbal) sprintf(string_output,"Overlaps for Volume (post mask) %d",parent);
    MPI_MASTER(
    if (verbal) print_1d_int_list(*true_overlap_lists[parent],string_output);
    )
  }
  
  // Clean up of dynamically allocated memory
  for(child=1;child<number_of_volumes;child++) free(temporary_overlap_lists[child].elements);
  free(temporary_overlap_lists);
  free(temp_list_local.elements);
  
};

void add_to_mask_intersect_list(struct pointer_to_1d_int_list *mask_intersect_list, int given_volume_index) {
    // A bit simpler than before
    if (on_int_list(*mask_intersect_list,given_volume_index) == 0)
      add_element_to_int_list(mask_intersect_list,given_volume_index);
}


void generate_intersect_check_lists(struct pointer_to_1d_int_list **overlap_lists,struct Volume_struct **Volumes, int number_of_volumes, int verbal) {
  // Generate intersection list for each volume
  MPI_MASTER(
  if (verbal) printf("\nGenerating intersect check lists -------------------- \n");
  )
  // Take overlap list for volume n
  // Remove entries with p < p(n)
  // Remove entries with parents on the list
  
  // Mask update: Mask volumes does not have priorities, do not do step 2 for mask volumes
  // Mask update: Instead of step 3 (Remove entries with parents on the list), move these entries to a Mask intersect list if that parent is masking the entry, otherwise remove
  
  // 1) Mask update: Take overlap list for volume n
  // 2) Mask update: Remove entries with p < p(n), except mask volumes, that are moved to another list A
  // 3) Mask update: Entries with parents on the list are removed (if these parents are masked, only remove the entry if it is a child of their parents masks)
  // 4) Possible optimization: Entries with parents on the A list, are moved to the appropriate mask list for this volume
  // 5) Mask update: Entries on list A are added again, if they are not parents of volume n
  // 6) Mask update: Entries on the main list that are masked are moved to the appropriate mask list for this volume, if their mask is on list A
  
  // Justification of the top algorithm:
  // No need to check intersections with something that does not overlap this volume, so start with intersect list and remove from that.
  // Entries with p < p(n) are "beneath" this volume, and thus doesn't cut it, mask volumes are however always "above" and are treated with care (moved to list A)
  // Entries with parents still on the list is removed, as it is impossible to intersect with them without first going through the parent
  // Entries with parents on the A list can only be seen if the ray is in that particular mask (the parent on the A list), so it can be added to the appropriate mask list for this volume
  // Now all masks that cut the volume are transfered back to the main list, the only masks that overlap and does not cut are parents of this volume, and these are thus not added
  // The entries on the list that are masked are added to the appropriate mask lists of this volume, but only if their mask is on the A list, as only masks on list A can have mask status 1 which is required for this to be relevant (this was true automatically for the ones added to the appropriate lists in the optimization step).
  
  
  // The intersect check list for volume 0 is done manually
  // Declare list for holding logic data for each index in the overlap list of this volume
  struct pointer_to_1d_int_list logic_list;
  logic_list.num_elements = overlap_lists[0]->num_elements;
  logic_list.elements = malloc(logic_list.num_elements * sizeof(int));
  
  // Declare similar list for List A
  struct pointer_to_1d_int_list mask_logic_list;
  mask_logic_list.num_elements = overlap_lists[0]->num_elements;
  mask_logic_list.elements = malloc(mask_logic_list.num_elements * sizeof(int));
  
  
  int iterate;
  int overlap_index;
  
  /*
  // Thise code is an old broken version of intersection_check_list generation only for volume 0, but this case is now included in the loop.
  // Assume all volumes on overlap list should be on intersection check list, and remove the once that should not.
  for (iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 1;
  // Asuume no volumes should go on the mask_logic_list, but add the ones that do.
  for (iterate=0;iterate<mask_logic_list.num_elements;iterate++) mask_logic_list.elements[iterate] = 0;
  
  for (overlap_index = 0;overlap_index < overlap_lists[0]->num_elements;overlap_index++) {
    // No volumes to remove with lower priority, as surrounding vacuum have the lowest.
    
    // Check if this overlap_lists[0]->elements[overlap_index] is a child of another member of the overlap list
    for (iterate = 0;iterate < overlap_lists[0]->num_elements;iterate++) {
        if (iterate != overlap_index) {
            // We are now checking if Volumes[overlap_lists[0]->elements[iterate]]->geometry.children contains overlap_lists[overlap_index]
            if (on_int_list(Volumes[overlap_lists[0]->elements[iterate]]->geometry.children,overlap_lists[0]->elements[overlap_index])) logic_list.elements[overlap_index] = 0;
        }
    }
  }

  Volumes[0]->geometry.intersect_check_list.num_elements = sum_int_list(logic_list);
  Volumes[0]->geometry.intersect_check_list.elements = malloc(Volumes[0]->geometry.intersect_check_list.num_elements * sizeof(int));
  
  iterate = 0;
  for (overlap_index = 0;overlap_index < overlap_lists[0]->num_elements;overlap_index++) {
        if (logic_list.elements[overlap_index])   Volumes[0]->geometry.intersect_check_list.elements[iterate++] = overlap_lists[0]->elements[overlap_index];
  }
  if (logic_list.num_elements > 0) free(logic_list.elements);
  if (mask_logic_list.num_elements > 0) free(mask_logic_list.elements);
  
  MPI_MASTER(
  print_1d_int_list(Volumes[0]->geometry.intersect_check_list,"Intersect check list for Volume 0 (manual)");
  )
  */
  
  // The intersect check lists for the remaining volumes are generated
  int volume_index,mask_volume_number,mask_index;
  //for (volume_index = 1;volume_index < number_of_volumes;volume_index++) {
  for (volume_index = 0;volume_index < number_of_volumes;volume_index++) {
    
      // 1) Take overlap list for volume n
      logic_list.num_elements = overlap_lists[volume_index]->num_elements;
      logic_list.elements = malloc(logic_list.num_elements * sizeof(int));
      mask_logic_list.num_elements = overlap_lists[volume_index]->num_elements;
      mask_logic_list.elements = malloc(mask_logic_list.num_elements * sizeof(int));
      
      
      
      if (Volumes[volume_index]->geometry.is_mask_volume == 1) {
        // Masks do not have any entries on their intersect check list, as they are never the current volume
        for (iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 0;
        for (iterate=0;iterate<mask_logic_list.num_elements;iterate++) mask_logic_list.elements[iterate] = 0;
      } else {
        for (iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 1;
        for (iterate=0;iterate<mask_logic_list.num_elements;iterate++) mask_logic_list.elements[iterate] = 0;
      }
      
      //2) Remove entries with p < p(n), except mask volumes, that are moved to another list A
      for (overlap_index = 0;overlap_index < overlap_lists[volume_index]->num_elements;overlap_index++) {
        
        if (overlap_lists[volume_index]->elements[overlap_index] == 0) logic_list.elements[overlap_index] = 0; // The surrounding vacuum has lower priority (and is not a mask)
        else if (Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.is_mask_volume == 1) {
           logic_list.elements[overlap_index] = 0;      // Remove this volume from the main list
           mask_logic_list.elements[overlap_index] = 1; // Add mask volumes to seperat list
        }
        else if (volume_index != 0) { // Only relevant to remove elements because of priority if the volume_index is different from 0, meaning the surrounding vacuum skips this step
           if (Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.priority_value < Volumes[volume_index]->geometry.priority_value) {
           // If the investigated volume on the overlap_list have lower priority than the volume with volume_index, remove it
           logic_list.elements[overlap_index] = 0; // Remove this volume from the list
           }
        }
      }
      
      // 3) Entries with parents on the list are removed
      for (overlap_index = 0;overlap_index < overlap_lists[volume_index]->num_elements;overlap_index++) {
        // Check if this overlap_lists[0]->elements[overlap_index] is a child of another member of the overlap list
        for (iterate = 0;iterate < overlap_lists[volume_index]->num_elements;iterate++) {
             if (iterate != overlap_index) { // Only necessary if a volume determines that it has itself as child, but a nice safety.
                // We are now checking if Volumes[overlap_lists[volume_index]->elements[iterate]]->geometry.children contains overlap_lists[overlap_index]
                // The && part is needed because we do not remove children of elements that have allready been removed because of their priority
                
                // if (on_int_list(Volumes[overlap_lists[volume_index]->elements[iterate]]->geometry.children,overlap_lists[volume_index]->elements[overlap_index]) && logic_list.elements[overlap_lists[volume_index]->elements[iterate]] == 1) {logic_list.elements[overlap_index] = 0; bug fixed on 3/4 2016
                if (on_int_list(Volumes[overlap_lists[volume_index]->elements[iterate]]->geometry.children,overlap_lists[volume_index]->elements[overlap_index]) && logic_list.elements[iterate] == 1) logic_list.elements[overlap_index] = 0;
                
                /*
                Explanation with simpler notation
                
                Overlap list of volume i = O_i
                Element j of overlap list i = O_i(j)
                
                Children list of volume i = C_i
                Element j on children list i = C_i(j)
                
                Priority of volume i p(i)
                
                for i = volumes
                    for j in O_i(j) 
                        logic(j) = 1;
                        if p(i) > p(O_i(j)) logic(j) = 0; // Remove if lower priority than the currently checked
                        
                        for k in O_i(k)
                            if (O_i(j) is contained on the list C_k && logic(k) == 1) logic(j) = 0
                */
                
                // 4) Entries with parents on the A list, are moved to the appropriate mask list for this volume
                if (on_int_list(Volumes[overlap_lists[volume_index]->elements[iterate]]->geometry.children,overlap_lists[volume_index]->elements[overlap_index]) && mask_logic_list.elements[iterate] == 1) {
                   logic_list.elements[overlap_index] = 0; // Remove from main list (Not strictly needed as it will be removed already if it is on list A)
                   // Add overlap_lists[volume_index]->elements[overlap_index] to volumes mask intersect list for mask with index overlap_lists[volume_index]->elements[iterate]
                   //add_to_mask_intersect_lists(&Volumes[volume_index]->geometry.mask_intersect_lists,overlap_lists[volume_index]->elements[iterate],overlap_lists[volume_index]->elements[overlap_index]);
                   // Simpler method that just collects all the masked intersect parts on a 1d_int list instead of seperating them for each mask
                   add_to_mask_intersect_list(&Volumes[volume_index]->geometry.mask_intersect_list,overlap_lists[volume_index]->elements[overlap_index]);
                }
                
             }
        }
      }
      
      // 5) Entries on list A are added again, if they are not parents of volume n
      // Time to add mask volumes removed back to the intersect list, if they are not parents of the current volume, meaning the current volume should not be a child of the mask
      for (overlap_index = 0;overlap_index < overlap_lists[volume_index]->num_elements;overlap_index++) {
        if (mask_logic_list.elements[overlap_index] == 1) {
          mask_volume_number = overlap_lists[volume_index]->elements[overlap_index];
          if (on_int_list(Volumes[mask_volume_number]->geometry.children,volume_index) == 0) logic_list.elements[overlap_index] = 1; // Add it back to the list
        }
      }
      
      // 6) Entries on the main list that are masked are moved to the appropriate mask list for this volume, if their mask is on list A
      int mask_index,mask_mode,found_index,logic_ALL;
      for (overlap_index = 0;overlap_index < overlap_lists[volume_index]->num_elements;overlap_index++) {
        if (logic_list.elements[overlap_index] == 1 && Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.is_masked_volume == 1) {
          logic_list.elements[overlap_index] = 0; // If the volume is masked, remove it from the intersect check list
          // Could actually keep it on the intersect list if the volume's mask is a parent of the current volume, now it will just be added in all cases
          
          // When ALL setting is used, all masks should overlap the current volume, otherwise the user probably made a mistake, this should be checked in error checking
          // When ANY setting is used, at least one mask should overlap the current volume, otherwise the user probably made a mistake, this should be checked in error checking
          // mask_mode == 1 => ALL / mask_mode == 2 => ANY
          mask_mode = Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.mask_mode;
          
          if (mask_mode == 1) {
            logic_ALL = 1;
            for (iterate=0;iterate<Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.masked_by_list.num_elements;iterate++) {
              mask_index = Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.masked_by_list.elements[iterate];
              
              if (on_int_list(*overlap_lists[volume_index],mask_index) == 0) {
                // If any one of the volumes masks do not overlap with this volume, there is no reason check intersections with the volume regardless of the mask status's
                logic_ALL = 0;
                break;
              }
            }
          if (logic_ALL == 1) {
            // If all of it's masks are overlapping the current volume, add it to all relevant mask intersect lists of this volume
            for (iterate=0;iterate<Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.masked_by_list.num_elements;iterate++) {
              mask_index = Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.masked_by_list.elements[iterate];
              //add_to_mask_intersect_lists(&Volumes[volume_index]->geometry.mask_intersect_lists,mask_index,overlap_lists[volume_index]->elements[overlap_index]);
              // Adding the masked intersect list elements to another list
              add_to_mask_intersect_list(&Volumes[volume_index]->geometry.mask_intersect_list,overlap_lists[volume_index]->elements[overlap_index]);
            }
          }
            
            
          } else if (mask_mode == 2) {
          // When in ANY mode, the problem is easier, as not all masks have to overlap the volume, and we can add each one to the list
            for (iterate=0;iterate<Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.masked_by_list.num_elements;iterate++) {
              mask_index = Volumes[overlap_lists[volume_index]->elements[overlap_index]]->geometry.masked_by_list.elements[iterate];
            
              if (on_int_list(*overlap_lists[volume_index],mask_index) == 1) {
                //add_to_mask_intersect_lists(&Volumes[volume_index]->geometry.mask_intersect_lists,mask_index,overlap_lists[volume_index]->elements[overlap_index]);
                // Adding the masked intersect list elements to another list
                add_to_mask_intersect_list(&Volumes[volume_index]->geometry.mask_intersect_list,overlap_lists[volume_index]->elements[overlap_index]);
              }
            }
            
          }
        }
      }
      
      
      Volumes[volume_index]->geometry.intersect_check_list.num_elements = sum_int_list(logic_list);
      Volumes[volume_index]->geometry.intersect_check_list.elements = malloc(Volumes[volume_index]->geometry.intersect_check_list.num_elements * sizeof(int));
      
      iterate = 0;
      for (overlap_index = 0;overlap_index < overlap_lists[volume_index]->num_elements;overlap_index++) {
            if (logic_list.elements[overlap_index])   Volumes[volume_index]->geometry.intersect_check_list.elements[iterate++] = overlap_lists[volume_index]->elements[overlap_index];
      }
      free(logic_list.elements); // Need to be careful with names for variables to be freed, as they can collide with names in main because of automatic declaration of external variables
      free(mask_logic_list.elements);
      
      
      char string_output[128];
      MPI_MASTER(
      if (verbal) sprintf(string_output,"Intersect check list for Volume %d",volume_index);
      if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.intersect_check_list,string_output);
      if (verbal) sprintf(string_output,"Mask intersect check list for Volume %d",volume_index);
      if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.mask_intersect_list,string_output);
      /*
      if (verbal) {
        for (mask_index=0;mask_index<Volumes[volume_index]->geometry.mask_intersect_lists.number_of_lists;mask_index++) {
          sprintf(string_output," - mask intersect check list for mask %d which is volume %d",mask_index,Volumes[volume_index]->geometry.mask_intersect_lists.mask_indices.elements[mask_index]);
          print_1d_int_list(Volumes[volume_index]->geometry.mask_intersect_lists.lists[mask_index],string_output);
        }
      }
      */
      
      )
  }
};

void generate_parents_lists(struct pointer_to_1d_int_list **parents_lists, struct Volume_struct **Volumes, int number_of_volumes, int verbal, int mask_mode) {
  // Function for generating parent lists for all volumes
  // A volume m has n as a parent, if volume n has volume m as a child
  
  // if mask_mode == 0, masks are ignored, if mask_mode == 1, masks are taken into account.
  
  MPI_MASTER(
  if (verbal) {
    if (mask_mode == 1)
        printf("\nGenerating parents lists ---------------------------- \n");
    else if (mask_mode == 0)
        printf("\nGenerating parents lists (ignoring masks) ----------- \n");
    else {
        printf("Error, the function parents_lists got a non defined mask_mode");
        exit(1);
    }
  }
  
  )
  // Volume iterate has volume p as a parent, if volume p has volume iterate as child.
  
  struct pointer_to_1d_int_list temp_list_local;
  temp_list_local.num_elements = number_of_volumes;
  temp_list_local.elements = malloc(number_of_volumes*sizeof(int));
  
  // Loop over
  int iterate,parent,used_elements;
  for (iterate = 0;iterate < number_of_volumes;iterate++) {
    // clear temp list
    used_elements = 0;
    parents_lists[iterate] = malloc(sizeof(struct pointer_to_1d_int_list));
    for (parent = 0;parent < number_of_volumes;parent++) {
        if (on_int_list(Volumes[parent]->geometry.children,iterate))
          if (mask_mode == 1 || (Volumes[parent]->geometry.is_mask_volume == 0 && Volumes[iterate]->geometry.is_mask_volume == 0))
            temp_list_local.elements[used_elements++] = parent;
    }
      allocate_list_from_temp(used_elements,temp_list_local,parents_lists[iterate]);
      
      char string_output[128];
      MPI_MASTER(
      if (verbal) sprintf(string_output,"Parents for Volume %d",iterate);
      if (verbal) print_1d_int_list(*parents_lists[iterate],string_output);
      )
  }
  free(temp_list_local.elements);

};

void generate_true_parents_lists(struct pointer_to_1d_int_list **parents_lists, struct pointer_to_1d_int_list **true_children_lists, struct Volume_struct **Volumes, int number_of_volumes, int verbal, int mask_mode) {
  // Function for generating parent lists for all volumes
  // A volume m has n as a parent, if volume n has volume m as a child
  
  // if mask_mode == 0, masks are ignored, if mask_mode == 1, masks are taken into account.
  
  MPI_MASTER(
  if (verbal) {
    if (mask_mode == 1)
        printf("\nGenerating parents lists ---------------------------- \n");
    else if (mask_mode == 0)
        printf("\nGenerating parents lists (ignoring masks) ----------- \n");
    else {
        printf("Error, the function parents_lists got a non defined mask_mode");
        exit(1);
    }
  }
  
  )
  // Volume iterate has volume p as a parent, if volume p has volume iterate as child.
  
  struct pointer_to_1d_int_list temp_list_local;
  temp_list_local.num_elements = number_of_volumes;
  temp_list_local.elements = malloc(number_of_volumes*sizeof(int));
  
  // Loop over
  int iterate,parent,used_elements;
  for (iterate = 0;iterate < number_of_volumes;iterate++) {
    // clear temp list
    used_elements = 0;
    parents_lists[iterate] = malloc(sizeof(struct pointer_to_1d_int_list)); // allocate_list_from_temp allocates
    for (parent = 0;parent < number_of_volumes;parent++) {
        //if (on_int_list(Volumes[parent]->geometry.children,iterate))
        if (on_int_list(*true_children_lists[parent],iterate))
          if (mask_mode == 1 || (Volumes[parent]->geometry.is_mask_volume == 0 && Volumes[iterate]->geometry.is_mask_volume == 0))
            temp_list_local.elements[used_elements++] = parent;
    }
      allocate_list_from_temp(used_elements,temp_list_local,parents_lists[iterate]);
      
      char string_output[128];
      MPI_MASTER(
      if (verbal) sprintf(string_output,"Parents for Volume %d",iterate);
      if (verbal) print_1d_int_list(*parents_lists[iterate],string_output);
      )
  }
  free(temp_list_local.elements);

};

void generate_intersect_check_lists_experimental(struct pointer_to_1d_int_list **true_overlap_lists, struct pointer_to_1d_int_list **raw_overlap_lists, struct pointer_to_1d_int_list **parents_lists, struct pointer_to_1d_int_list **true_parents_lists , struct Volume_struct **Volumes, int number_of_volumes, int verbal) {
  // Generates the intersect_check_list and mask_intersect_list for each Volume.
  /*
  Description of needed lists for volume n:
  Children list: List of volumes that is contained within volume n (is stored in the Volumes struct)
  True Children list: List of volumes that when masked by their masks is contained within volume n when masked by it's masks
  
  Parents list: List of volumes that contains volume n
  True parents list: List of volumes that when masked by their masks contains volume n when it is masked by it's masks
  
  raw overlap list: List of volumes whos geometry overlaps the geometry of volume n
  true overlap list: List of volumes whos geometry overlaps the geometry of volume n when the masks of both volumes are applied
  
  
  
  The algorithm:
  
  1) Take the true overlap list for volume n
  2) remove parents of n (normal parent list, with masks)
  3) remove volumes that do not mask n and are true parents of n
  4) remove volumes that are not masks and have lower priority than n
  5) remove volumes that have at least one true parent on the list (in step 4) that is not a mask volume
  6) split the list into two, the intersect_check_list which is all non masked volumes still on the list, and the mask_intersect_list which is the masked volumes
  7) remove volumes on the mask_intersect_list whos mask does not overlap (standard overlap list) n
  
  In step 5 the order in which the volumes are tested and removed may matter, so it is specifically stated that it is as the list looked in step 4.
  */
  
  MPI_MASTER(
  if (verbal) printf("\nGenerating intersect check lists -------------------- \n");
  )
  
  struct pointer_to_1d_int_list work_list;
  struct pointer_to_1d_int_list logic_list;
  
  int volume_index,iterate,parent,mask_index,masked_volume_index,ANY_logic,true_parent_volume_number;
  int *mask_check,*mask_start;
  for (volume_index = 0;volume_index < number_of_volumes;volume_index++) {
    
    // 1) Take the true overlap list for volume n
    // Create copy of true_overlap_lists to work with
    
    if (Volumes[volume_index]->geometry.is_mask_volume == 1) {
      // Bug fixed on 26/11/2016, do not create intersection lists for masks as they are not used, and affects destinations lists in a problematic way
      Volumes[volume_index]->geometry.intersect_check_list.num_elements = 0;
      Volumes[volume_index]->geometry.mask_intersect_list.num_elements = 0;
      
    } else {
      work_list.num_elements = true_overlap_lists[volume_index]->num_elements;
      work_list.elements = malloc(work_list.num_elements * sizeof(int));
      for (iterate=0;iterate<work_list.num_elements;iterate++) work_list.elements[iterate] = true_overlap_lists[volume_index]->elements[iterate];
    
    //if (verbal) print_1d_int_list(work_list,"After 1)");
    
    
    //2) remove parents of n (normal parent list, with masks)
    for (iterate=work_list.num_elements-1;iterate>-1;iterate--) {
      if (on_int_list(*parents_lists[volume_index],work_list.elements[iterate]))
        remove_element_in_list_by_index(&work_list,iterate);
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 2)");
    
    //3) remove volumes that do not mask n and are true parents of n
    for (iterate=work_list.num_elements-1;iterate>-1;iterate--) {
      if (on_int_list(Volumes[volume_index]->geometry.masked_by_list,work_list.elements[iterate]) == 0 && on_int_list(*true_parents_lists[volume_index],work_list.elements[iterate]))
        remove_element_in_list_by_index(&work_list,iterate);
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 3)");
    
    //4) remove volumes that are not masks and have lower priority than n
    for (iterate=work_list.num_elements-1;iterate>-1;iterate--) {
      if (Volumes[work_list.elements[iterate]]->geometry.is_mask_volume == 0 && Volumes[work_list.elements[iterate]]->geometry.priority_value < Volumes[volume_index]->geometry.priority_value)
        remove_element_in_list_by_index(&work_list,iterate);
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 4)");
    
    //5) remove volumes that have at least one true parent on the list (in step 4) that is not a mask volume
    // Here a logic_list is used to not have the order of removal matter
    logic_list.num_elements = work_list.num_elements;
    logic_list.elements = malloc(logic_list.num_elements * sizeof(int));
    for(iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 1;
    
    for (iterate=0;iterate<work_list.num_elements;iterate++) {
      for (parent=0;parent<true_parents_lists[work_list.elements[iterate]]->num_elements;parent++) {
        true_parent_volume_number = true_parents_lists[work_list.elements[iterate]]->elements[parent];
        if (on_int_list(work_list,true_parent_volume_number) && Volumes[true_parent_volume_number]->geometry.is_mask_volume == 0) {
          // Since element number iterate on the work list have a true parent on the work list, it can be removed
          logic_list.elements[iterate] = 0;
          break;
        }
      }
    }
    
    // Now the elements marked for removal can be removed without interfering in the operation
    for (iterate=work_list.num_elements-1;iterate>-1;iterate--) {
      if (logic_list.elements[iterate] == 0) remove_element_in_list_by_index(&work_list,iterate);
    }
    free(logic_list.elements);
    
    //if (verbal) print_1d_int_list(work_list,"After 5)");
      
    //6) split the list into two, the intersect_check_list which is all non masked volumes still on the list, and the mask_intersect_list which is the masked volumes
    
    Volumes[volume_index]->geometry.intersect_check_list.num_elements = 0;
    Volumes[volume_index]->geometry.mask_intersect_list.num_elements = 0;
    
    for (iterate=0;iterate<work_list.num_elements;iterate++) {
      if (Volumes[work_list.elements[iterate]]->geometry.is_masked_volume == 1) {
        add_element_to_int_list(&Volumes[volume_index]->geometry.mask_intersect_list,work_list.elements[iterate]);
      } else {
        add_element_to_int_list(&Volumes[volume_index]->geometry.intersect_check_list,work_list.elements[iterate]);
      }
    }
    
    //if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.intersect_check_list,"After 6) intersect_check_list");
    //if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.mask_intersect_list,"After 6) mask_intersect_list");
    
    //7) remove volumes on the mask_intersect_list whos masks does not overlap (standard overlap list) n
    for (iterate=Volumes[volume_index]->geometry.mask_intersect_list.num_elements-1;iterate>-1;iterate--) {
      // Need to check if the volumes masking Volumes[volume_index]->geometry.mask_intersect_list.elements[iterate] overlaps with n
      masked_volume_index = Volumes[volume_index]->geometry.mask_intersect_list.elements[iterate];
      
      if (Volumes[masked_volume_index]->geometry.mask_mode == 1) {// All mode, if just one does not overlap, remove the element
        for (mask_start=mask_check=Volumes[masked_volume_index]->geometry.masked_by_list.elements;mask_check-mask_start<Volumes[masked_volume_index]->geometry.masked_by_list.num_elements;mask_check++) {
          if (on_int_list(*raw_overlap_lists[volume_index],*mask_check) == 0) {
            remove_element_in_list_by_index(&Volumes[volume_index]->geometry.mask_intersect_list,iterate);
            break;
          }
        }
      } else { // ANY mode, just one need to overlap in order to keep the element
        ANY_logic = 0;
        for (mask_start=mask_check=Volumes[masked_volume_index]->geometry.masked_by_list.elements;mask_check-mask_start<Volumes[masked_volume_index]->geometry.masked_by_list.num_elements;mask_check++) {
          if (on_int_list(*raw_overlap_lists[volume_index],*mask_check) == 1) {
            ANY_logic = 1;
            break;
          }
        }
        if (ANY_logic == 0) remove_element_in_list_by_index(&Volumes[volume_index]->geometry.mask_intersect_list,iterate);
      }
    }
    
    if (work_list.num_elements > 0) free(work_list.elements);
    }
    
    
    //if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.mask_intersect_list,"After 7) mask_intersect_list");
    
    char string_output[128];
    MPI_MASTER(
    if (verbal) sprintf(string_output,"Intersect check list for Volume %d",volume_index);
    if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.intersect_check_list,string_output);
    if (verbal) sprintf(string_output,"Mask intersect check list for Volume %d",volume_index);
    if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.mask_intersect_list,string_output);
    )
    
    
  }
}

void generate_grandparents_lists(struct pointer_to_1d_int_list **grandparents_lists, struct pointer_to_1d_int_list **parents_lists, int number_of_volumes, int verbal) {
  // Function for generating grandparents lists
  // Volume iterate has volume p as a grandparent, if volume p has a parent T, who has volume iterate as parent.
  // Alternertively:
  // Volume iterate has volume p as a grandparent, if volume p have a child that is volume iterate's parent.
  
  MPI_MASTER(
  if (verbal) printf("\nGenerating grandparents lists ----------------------- \n");
  )

  struct pointer_to_1d_int_list common;
  common.num_elements = number_of_volumes;
  common.elements = malloc(common.num_elements*sizeof(int)); // Maximum needed space.
  
  struct pointer_to_1d_int_list temp_list_local;
  temp_list_local.num_elements = number_of_volumes;
  temp_list_local.elements = malloc(number_of_volumes*sizeof(int));
  
  int iterate,reset_int,parent,child,used_elements;
  for (iterate = 0;iterate < number_of_volumes;iterate++) {
    // clear temp list
    used_elements = 0;

    for (reset_int=0; reset_int<number_of_volumes; reset_int++) common.elements[reset_int] = -1; // Initialize to impossible volume ids
    for (reset_int=0; reset_int<number_of_volumes; reset_int++) temp_list_local.elements[reset_int] = -1; // Initialize to impossible volume ids

    grandparents_lists[iterate] = malloc(sizeof(struct pointer_to_1d_int_list));
    
    for (parent = 0; parent<parents_lists[iterate]->num_elements; parent++) {
        // parent number p parents_lists[iterate].elements.[p] in the parent_list for iterate.
        on_both_int_lists(parents_lists[parents_lists[iterate]->elements[parent]], parents_lists[iterate], &common);
        // returns a pointer_to_1d_list, with all the elements that are in common.
        for (child = 0;child < common.num_elements;child++) {
            // Need to make sure the element is not already on the list
            if (0 == on_int_list(temp_list_local, common.elements[child])) {
              temp_list_local.elements[used_elements++] = common.elements[child];
            }
        }
    }
    allocate_list_from_temp(used_elements, temp_list_local, grandparents_lists[iterate]);
  
    char string_output[128];
    MPI_MASTER(
      if (verbal) sprintf(string_output,"Grandparents for Volume %d", iterate);
      if (verbal) print_1d_int_list(*grandparents_lists[iterate], string_output);
    )
  }
  free(temp_list_local.elements);
  free(common.elements);
};


void generate_destinations_lists_experimental(struct pointer_to_1d_int_list **true_overlap_lists, struct pointer_to_1d_int_list **true_children_lists, struct pointer_to_1d_int_list **true_parents_lists, struct pointer_to_1d_int_list **true_grandparents_lists, struct Volume_struct **Volumes, int number_of_volumes, int verbal) {
  // Generates destinations list for for all volumes
  // Current implementation uses true_parents_lists and true_grandparents_lists that are generated as if no masks were defined

  MPI_MASTER(
  if (verbal) printf("\nGenerating destinations lists ----------------------- \n");
  )
  // Volume 0 has an hardcoded empty destinations list
  Volumes[0]->geometry.destinations_list.num_elements = 0;

  struct pointer_to_1d_int_list work_list;
  int volume_index,iterate,iterate2,found_index,I_index,I_volume;
  
  for (volume_index=1;volume_index<number_of_volumes;volume_index++) {
    
    // 1) Take the true overlap list for volume n
    // Create copy of true_overlap_lists to work with
    work_list.num_elements = true_overlap_lists[volume_index]->num_elements;
    work_list.elements = malloc(work_list.num_elements * sizeof(int));
    for (iterate=0;iterate<work_list.num_elements;iterate++) work_list.elements[iterate] = true_overlap_lists[volume_index]->elements[iterate];
    
    //if (verbal) print_1d_int_list(work_list,"After 1)");
    
    // 2) Remove elements from n's intersection list
    for (iterate=work_list.num_elements-1;iterate>-1;iterate--) {
      if (on_int_list(Volumes[volume_index]->geometry.intersect_check_list,work_list.elements[iterate]))
        remove_element_in_list_by_index(&work_list,iterate);
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 2)");
  
    // 3) Remove true children of non-mask elements on n's intersection list
    for (I_index=0;I_index<Volumes[volume_index]->geometry.intersect_check_list.num_elements;I_index++) {
      I_volume = Volumes[volume_index]->geometry.intersect_check_list.elements[I_index];
      if (Volumes[I_volume]->geometry.is_mask_volume == 0) {
        for (iterate=0;iterate<true_children_lists[I_volume]->num_elements;iterate++) {
          remove_element_in_list_by_value(&work_list,true_children_lists[I_volume]->elements[iterate]);
        }
      }
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 3)");
    
    // 4) Remove elements from mask intersection list
    for (iterate=work_list.num_elements-1;iterate>-1;iterate--) {
      if (on_int_list(Volumes[volume_index]->geometry.mask_intersect_list,work_list.elements[iterate]))
        remove_element_in_list_by_index(&work_list,iterate);
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 4)");
    
    // 5) Remove true children of elements on n's mask intersection list
    for (I_index=0;I_index<Volumes[volume_index]->geometry.mask_intersect_list.num_elements;I_index++) {
      I_volume = Volumes[volume_index]->geometry.mask_intersect_list.elements[I_index];
      if (Volumes[I_volume]->geometry.is_mask_volume == 0) {
        for (iterate=0;iterate<true_children_lists[I_volume]->num_elements;iterate++) {
          remove_element_in_list_by_value(&work_list,true_children_lists[I_volume]->elements[iterate]);
        }
      }
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 5)");
    
    // 6) Remove true children of n
    for (iterate=0;iterate<true_children_lists[volume_index]->num_elements;iterate++)
      remove_element_in_list_by_value(&work_list,true_children_lists[volume_index]->elements[iterate]);
      
    //if (verbal) print_1d_int_list(work_list,"After 6)");
      
    // 7) Remove true grandparents of n
    for (iterate=0;iterate<true_grandparents_lists[volume_index]->num_elements;iterate++)
      remove_element_in_list_by_value(&work_list,true_grandparents_lists[volume_index]->elements[iterate]);
      
    //if (verbal) print_1d_int_list(work_list,"After 7)");
      
    // 8) Remove mask volumes
    for (iterate=work_list.num_elements-1;iterate>-1;iterate--) {
      if (Volumes[work_list.elements[iterate]]->geometry.is_mask_volume == 1)
        remove_element_in_list_by_index(&work_list,iterate);
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 8)");
    
    // 9) Remove true parents of n on the list that has other true parents of n on the list with higher priority
    for (iterate=work_list.num_elements-1;iterate>-1;iterate--) {
      if (on_int_list(*true_parents_lists[volume_index],work_list.elements[iterate])){
        // work_list.elements[iterate] is the volume index of a volume that is a true parent of n
        for (iterate2=0;iterate2<work_list.num_elements;iterate2++) {
          if (on_int_list(*true_parents_lists[volume_index],work_list.elements[iterate2]) && iterate != iterate2) {
            if (Volumes[work_list.elements[iterate]]->geometry.priority_value < Volumes[work_list.elements[iterate2]]->geometry.priority_value) {
              //printf("Removing element number %d (V%d) because element number %d (V%d) had higher priority \n",iterate,work_list.elements[iterate],iterate2,work_list.elements[iterate2]);
              remove_element_in_list_by_index(&work_list,iterate);
              break; // Missing break inserted on 14/9/2016
            }
          }
        }
      }
    }
    
    //if (verbal) print_1d_int_list(work_list,"After 9)");
    
    // 10) The remaining list is the destinations_list
    Volumes[volume_index]->geometry.destinations_list.num_elements = work_list.num_elements;
    Volumes[volume_index]->geometry.destinations_list.elements = malloc(Volumes[volume_index]->geometry.destinations_list.num_elements*sizeof(int));
    
    for (iterate=0;iterate<work_list.num_elements;iterate++)
      Volumes[volume_index]->geometry.destinations_list.elements[iterate] = work_list.elements[iterate];
    
    // Clean up after work_list so the next can be allocated
    free(work_list.elements);
  
  
    char string_output[128];
    MPI_MASTER(
    if (verbal) sprintf(string_output,"Destinations list for Volume %d",volume_index);
    if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.destinations_list,string_output);
    )

  }

};

void generate_destinations_list(int N_volume,struct Volume_struct **Volumes,struct pointer_to_1d_int_list original_overlap_list,struct pointer_to_1d_int_list *parent_list, struct pointer_to_1d_int_list *grandparent_list) {
    // This function generates the destinations_list for a single volume index, N_volume
    // The destination list describes which volumes a ray can enter when leaving N_volume
    // Each of the 6 steps for the algorithm is commented individually, and debug print statements are available
    
    // Mask update: Mask volumes are to be removed from all destinations_list as they can not be the current volume
    // It is not that simple, as some volume will then get empty destination lists. Need to revise this algorithm.
    
    // 1) Start with the overlap list of volume N
    // 2) remove all Volumes from the overlap list of N, which is also on the intersect_check_list
    // 3) remove the children of volumes removed in step 2)
    // 4) remove the children of N
    // 5) remove the grandparents of N
    // 6) remove volumes with lower priority than parents of N still on the list
    // 7) The remaing list is the destinations list
    
    
    // The destination list system should run without masks altogether, meaning parent and grandparent lists should not use them,
    //  and all masks should be removed in step 2
    
    // 1) Start with the overlap list of volume N
    // 2) remove all masks on the list
    // 3) remove all Volumes from the overlap list of N, which is also on the intersect_check_list
    // 4) remove the children of volumes removed in step 2) (if the removed volume was not a mask )
    // 5) remove the children of N
    // 6) remove the grandparents of N
    // 7) remove volumes with lower priority than parents of N still on the list
    // 8) The remaing list is the destinations list
    
    

    // 1) Start with the overlap list of volume N
    struct pointer_to_1d_int_list overlap_list;
    overlap_list.num_elements = original_overlap_list.num_elements;
    overlap_list.elements = malloc(overlap_list.num_elements*sizeof(int));
    int iterate;
    for (iterate=0;iterate<overlap_list.num_elements;iterate++) overlap_list.elements[iterate] = original_overlap_list.elements[iterate];
    
    char string_output[124];
    // sprintf(string_output,"Destinations list for Volume %d step 1",N_volume);
    // print_1d_int_list(overlap_list,string_output);
    
    // 2) remove all masks
    for (iterate=overlap_list.num_elements-1;iterate > -1;iterate--) {
        if (Volumes[overlap_list.elements[iterate]]->geometry.is_mask_volume == 1) {
            remove_element_in_list_by_index(&overlap_list,iterate);
        }
    }
    
    // 3) remove all Volumes from the overlap list of N, which is also on the intersect_check_list
    struct pointer_to_1d_int_list removed_under_2;
    removed_under_2.num_elements = 0;
    int to_check;
    removed_under_2.elements = malloc( Volumes[N_volume]->geometry.intersect_check_list.num_elements * sizeof(int));
    for (iterate=0;iterate < Volumes[N_volume]->geometry.intersect_check_list.num_elements;iterate++) {
        to_check = Volumes[N_volume]->geometry.intersect_check_list.elements[iterate];
        if (on_int_list(overlap_list,to_check)) {
            removed_under_2.elements[removed_under_2.num_elements++] = to_check;
            remove_element_in_list_by_value(&overlap_list,to_check);
        }
    }
    
    // sprintf(string_output,"Destinations list for Volume %d step 2",N_volume);
    // print_1d_int_list(overlap_list,string_output);
    
    // 4) remove the children of volumes removed in step 2)
    int children;
    for (iterate=0;iterate<removed_under_2.num_elements;iterate++){
        for (children = 0;children < Volumes[removed_under_2.elements[iterate]]->geometry.children.num_elements;children++) {
          remove_element_in_list_by_value(&overlap_list,Volumes[removed_under_2.elements[iterate]]->geometry.children.elements[children]);
        }
    }
    
    // sprintf(string_output,"Destinations list for Volume %d step 3",N_volume);
    // print_1d_int_list(overlap_list,string_output);
    
    // 5) remove the children of N
    for (children = 0;children < Volumes[N_volume]->geometry.children.num_elements;children++) {
        remove_element_in_list_by_value(&overlap_list,Volumes[N_volume]->geometry.children.elements[children]);
    }
    
    // sprintf(string_output,"Destinations list for Volume %d step 4",N_volume);
    // print_1d_int_list(overlap_list,string_output);
    
    // 6) remove the grandparents of N
    int grandparent;
    for (grandparent = 0;grandparent < grandparent_list->num_elements;grandparent++) {
        remove_element_in_list_by_value(&overlap_list,grandparent_list->elements[grandparent]);
    }
    
    // sprintf(string_output,"Destinations list for Volume %d step 5",N_volume);
    // print_1d_int_list(overlap_list,string_output);
    
    // 7) remove volumes with lower priority than parents of N still on the list
    struct pointer_to_1d_int_list logic_list;
    logic_list.num_elements = overlap_list.num_elements;
    if (logic_list.num_elements>0) logic_list.elements = malloc(logic_list.num_elements*sizeof(int));
    for (iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 1;
    
    int parent;
    for (parent=0;parent<parent_list->num_elements;parent++) {
      if (on_int_list(overlap_list,parent_list->elements[parent])) {
        // Found a parent to N on the list, now check all other elements on the list
        for (iterate=0;iterate<overlap_list.num_elements;iterate++) {
          if (parent_list->elements[parent] != overlap_list.elements[iterate]) {
            // if the element iterate have lower priority than the parent, remove it from the list
            if (Volumes[overlap_list.elements[iterate]]->geometry.priority_value < Volumes[parent_list->elements[parent]]->geometry.priority_value) logic_list.elements[iterate] = 0;
          }
        }
      }
    }
    
    // 8) The remaing list is the destinations list
    Volumes[N_volume]->geometry.destinations_list.num_elements = sum_int_list(logic_list);
    Volumes[N_volume]->geometry.destinations_list.elements = malloc(Volumes[N_volume]->geometry.destinations_list.num_elements * sizeof(int));
    
    int overlap_index,used_elements=0;
    for (overlap_index=0;overlap_index < overlap_list.num_elements;overlap_index++)
      if (logic_list.elements[overlap_index] == 1)
        Volumes[N_volume]->geometry.destinations_list.elements[used_elements++] = overlap_list.elements[overlap_index];
    
    if (overlap_list.num_elements>0) free(overlap_list.elements);
    if (logic_list.num_elements>0) free(logic_list.elements);
    
    
    /*
    // Old version before rule 6 and 2 was added
    // 8) The remaing list is the destinations list
    Volumes[N_volume]->geometry.destinations_list.num_elements = overlap_list.num_elements;
    Volumes[N_volume]->geometry.destinations_list.elements = malloc(Volumes[N_volume]->geometry.destinations_list.num_elements * sizeof(int));
    
    int overlap_index;
    for (overlap_index=0;overlap_index < overlap_list.num_elements;overlap_index++)
        Volumes[N_volume]->geometry.destinations_list.elements[overlap_index] = overlap_list.elements[overlap_index];
    
    // sprintf(string_output,"Destination list for Volume %d step 6",N_volume);
    // print_1d_int_list(Volumes[N_volume]->geometry.destinations_list,string_output);
    
    if (overlap_list.num_elements>0) free(overlap_list.elements);
    */
    
    };

void generate_destinations_lists(struct pointer_to_1d_int_list **grandparents_lists, struct pointer_to_1d_int_list **parents_lists, struct pointer_to_1d_int_list **overlap_lists,struct Volume_struct **Volumes, int number_of_volumes, int verbal) {
    // Because of the complexity of the algortithm for generating the destinations list, the function is made for a single volume at the time to keep the notation simpler
    // This funtion runs the destinations list function for each volume
    MPI_MASTER(
    if (verbal) printf("\nGenerating destinations lists ----------------------- \n");
    )
    
    int volume_index;
    for (volume_index = 0;volume_index < number_of_volumes;volume_index++) {
      generate_destinations_list(volume_index,Volumes,*overlap_lists[volume_index],parents_lists[volume_index],grandparents_lists[volume_index]);
      
      char string_output[128];
      if (verbal) sprintf(string_output,"Destinations list for Volume %d",volume_index);
      MPI_MASTER(
      if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.destinations_list,string_output);
      )
    }
};

/* OBSOLETE
void generate_destinations_logic_lists(struct Volume_struct **Volumes,int number_of_volumes,int verbal) {
  // The destinations logic list is another way to store the destinations list that makes some tasks quicker
  
  MPI_MASTER(
  if (verbal) printf("\nGenerating destinations logic lists ----------------------- \n");
  )

  int volume_index;
  for (volume_index = 0;volume_index < number_of_volumes;volume_index++) {
      allocate_logic_list_from_temp(number_of_volumes,Volumes[volume_index]->geometry.destinations_list,&Volumes[volume_index]->geometry.destinations_logic_list);
      
      char string_output[128];
      MPI_MASTER(
      if (verbal) sprintf(string_output,"Destinations logic list for Volume %d",volume_index);
      if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.destinations_logic_list,string_output);
      )
  }

};
*/

void generate_reduced_destinations_lists(struct pointer_to_1d_int_list **parents_lists, struct Volume_struct **Volumes,int number_of_volumes,int verbal) {
    // The reduced destination list is the destination list of a volume, where each element that has a parent on the same destination list is removed
    // This list is to be fed to the which_volume function, as this funtion will automatically search through the direct children in a tree like manner
    // The optimization reduces the number of calculations of within functions in nested geometries.
    
    MPI_MASTER(
    if (verbal) printf("\nGenerating reduced destination lists ----------------------- \n");
    )
    
    struct pointer_to_1d_int_list logic_list;
  
    int volume_index,checked_dest_index,checked_dest_volume,rest_dest_index,rest_dest_volume,dest_index,iterate;
    for (volume_index = 0;volume_index < number_of_volumes;volume_index++) {
      
      //printf("Generating reduced destinations lists for volume %d\n",volume_index);
      logic_list.num_elements = Volumes[volume_index]->geometry.destinations_list.num_elements;
      logic_list.elements = malloc( (int) logic_list.num_elements * sizeof(int));
      //printf("Sucsessfully allocated space for %d integers in logic list %d\n",logic_list.num_elements,volume_index);
      for (iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 1;
      
      for (checked_dest_index=0;checked_dest_index<Volumes[volume_index]->geometry.destinations_list.num_elements;checked_dest_index++) {
            checked_dest_volume = Volumes[volume_index]->geometry.destinations_list.elements[checked_dest_index];
            for (rest_dest_index=0;rest_dest_index<Volumes[volume_index]->geometry.destinations_list.num_elements;rest_dest_index++) {
                rest_dest_volume = Volumes[volume_index]->geometry.destinations_list.elements[rest_dest_index];
                // As every volume has 0 as a parent, these are ignored. It would work to include this, but would add an extra trivial step to within_which_volume
                if (rest_dest_volume != 0) {
                    if (on_int_list(*parents_lists[checked_dest_volume],rest_dest_volume) == 1) {
                        // In this case, do not include element checked_dest_index on the reduced destinations list
                        // ADD mask check
                        if (Volumes[rest_dest_volume]->geometry.is_masked_volume == 0) {
                          logic_list.elements[checked_dest_index] = 0;
                        }
                    }
                }
            }
      }
      
      Volumes[volume_index]->geometry.reduced_destinations_list.num_elements = sum_int_list(logic_list);
      Volumes[volume_index]->geometry.reduced_destinations_list.elements = malloc((int)Volumes[volume_index]->geometry.reduced_destinations_list.num_elements * sizeof(int));
      
      iterate = 0;
      for (dest_index = 0;dest_index < Volumes[volume_index]->geometry.destinations_list.num_elements;dest_index++) {
            if (logic_list.elements[dest_index] == 1)   Volumes[volume_index]->geometry.reduced_destinations_list.elements[iterate++] = Volumes[volume_index]->geometry.destinations_list.elements[dest_index];
      }
      
      free(logic_list.elements);
      
      // Testing an optimization
      remove_element_in_list_by_value(&Volumes[volume_index]->geometry.reduced_destinations_list,0);
      
      char string_output[128];
      MPI_MASTER(
      if (verbal) sprintf(string_output,"Reduced destinations list for Volume %d",volume_index);
      if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.reduced_destinations_list,string_output);
      )
    }
};

void generate_direct_children_lists(struct pointer_to_1d_int_list **parents_lists, struct Volume_struct **Volumes,int number_of_volumes,int verbal) {

  MPI_MASTER(
  if (verbal) printf("\nGenerating direct children lists ----------------------- \n");
  )
  // A direct children of volume n is a volume that is a child of n, but no other child of n is its parent
  
  // Mask update: Need to check that this step does not bug out when the mask system interferes with child/parent systems
  
  struct pointer_to_1d_int_list logic_list;
  int volume_index,child,parent,iterate;
  for (volume_index = 0;volume_index < number_of_volumes;volume_index++) {
        // Temp elements is used, and its actual number of elements is edited even though the memory allocated is not changed. This is so that the list functions handles it correctly.
        // The free function will free all the allocated memory regardless of the value of the .num_elements structure field, it is just there for convinience.
      
        logic_list.num_elements = Volumes[volume_index]->geometry.children.num_elements;
        logic_list.elements = malloc(logic_list.num_elements * sizeof(int));
        for (iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 1;
      
        // Look through each child of volume n, and for each of those look through all the other children of n and search for one of them being a parent to the child
        for (child=0;child<Volumes[volume_index]->geometry.children.num_elements;child++) {
            for (parent=0;parent<parents_lists[Volumes[volume_index]->geometry.children.elements[child]]->num_elements;parent++) {
                if (on_int_list(Volumes[volume_index]->geometry.children,parents_lists[Volumes[volume_index]->geometry.children.elements[child]]->elements[parent]))
                    // If such a parent is found, remove that child from the list
                    logic_list.elements[child] = 0;
            }
        }
      
      Volumes[volume_index]->geometry.direct_children.num_elements = sum_int_list(logic_list);
      Volumes[volume_index]->geometry.direct_children.elements = malloc(Volumes[volume_index]->geometry.direct_children.num_elements*sizeof(int));
      
      iterate = 0;
      for (child = 0;child < Volumes[volume_index]->geometry.children.num_elements;child++) {
            if (logic_list.elements[child])   Volumes[volume_index]->geometry.direct_children.elements[iterate++] = Volumes[volume_index]->geometry.children.elements[child];
      }
      // Be careful with names in both main and a function, as they are automatically declared as external variables, which would then also free the main.
      free(logic_list.elements);
      
      char string_output[128];
      MPI_MASTER(
      if (verbal) sprintf(string_output,"Children list for Volume        %d",volume_index);
      if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.children,string_output);
      if (verbal) sprintf(string_output,"Direct_children list for Volume %d",volume_index);
      if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.direct_children,string_output);
      )
      
  }

};

void generate_starting_logic_list(struct starting_lists_struct *starting_lists, struct Volume_struct **Volumes, int number_of_volumes, int verbal) {
    // Function for generating logic list of volumes the ray can start in without an error.
    // Start with a list of all vacuum volumes
    // Remove all volumes that are children of non-vacuum volumes
    // It is still possible to have a volume on this list that is surrounded by non-vacuum volumes, but it is hard to detect these situations,
    //  meaning that it is ultimately partly the users responsibility to not send neutrons directly into materials.
    
    int volume_index,*start,*check;
    
    struct pointer_to_1d_int_list temp_list_local;
    temp_list_local.num_elements = number_of_volumes;
    temp_list_local.elements = malloc(number_of_volumes*sizeof(int));
    
    //if (verbal==1) printf("sucessfully allocated temp_list_local.elements, now to enter which volumes are vacuums as a logic list\n");
    temp_list_local.elements[0] = 1; // Volume 0 is a vacuum volume.
    for (volume_index = 1;volume_index < number_of_volumes;volume_index++) {
        if (Volumes[volume_index]->p_physics->is_vacuum == 1) temp_list_local.elements[volume_index] = 1;
        else temp_list_local.elements[volume_index] = 0;
    }
    // temp_list_local is now a logic list of all vacuum volumes
    //if (verbal==1) printf("list of vacuum volumes done, now to remove the children of non-vcauum volumes\n");
    for (volume_index = 1;volume_index < number_of_volumes;volume_index++) { // All volumes ...
        if (temp_list_local.elements[volume_index] == 0) { // ... that are not vacuum ...
            for (start = check = Volumes[volume_index]->geometry.children.elements;check - start < Volumes[volume_index]->geometry.children.num_elements;check++) { // ... have all their children ...
                temp_list_local.elements[*check] = 0; // .. removed from the allowed_start_logic_list
            }
        }
    }
    //if (verbal==1) printf("sucessfully removed children of non-vacuum volumes, now allocate allowed_start_logic_list\n");
    allocate_list_from_temp(number_of_volumes,temp_list_local,&starting_lists->allowed_starting_volume_logic_list);
    //if (verbal==1) printf("sucsessfully allocated allowed_start_logic_list, now freeing temp_list_local.elements. \n");
    
    free(temp_list_local.elements);
    //if (verbal==1) printf("sucessfully freed temp_list_local.elements, generate starting lists done\n");
    
    char string_output[128];
    MPI_MASTER(
    if (verbal) sprintf(string_output,"Allowed starting volume logic list");
    if (verbal) print_1d_int_list(starting_lists->allowed_starting_volume_logic_list,string_output);
    )
    
    
};

void generate_reduced_starting_destinations_list(struct starting_lists_struct *starting_lists, struct pointer_to_1d_int_list **parents_lists, struct Volume_struct **Volumes,int number_of_volumes,int verbal) {
    // The starting_destinations_list is trivial, as it contains all volumes that are not masks.


    struct pointer_to_1d_int_list logic_list;
    //printf("Generating reduced destinations lists for volume %d\n",volume_index);
    logic_list.num_elements = number_of_volumes;
    logic_list.elements = malloc( (int) logic_list.num_elements * sizeof(int));
    int iterate;
    for (iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 0;
    
    
    for (iterate=1;iterate<number_of_volumes;iterate++)
      if (Volumes[iterate]->geometry.is_mask_volume == 0) logic_list.elements[iterate] = 1;
      //else logic_list.elements[iterate] = 0;
    
    starting_lists->starting_destinations_list.num_elements = sum_int_list(logic_list);
    starting_lists->starting_destinations_list.elements = malloc(starting_lists->starting_destinations_list.num_elements*sizeof(int));
    
    int used_elements=0;
    for (iterate=1;iterate<number_of_volumes;iterate++)
      if (logic_list.elements[iterate] == 1) starting_lists->starting_destinations_list.elements[used_elements++] = iterate;
    
    MPI_MASTER(
    if (verbal) printf("\nGenerating start destinations list ------------------------------ \n");
    if (verbal) print_1d_int_list(starting_lists->starting_destinations_list,"Starting destinations list");
    )

    // The reduced starting destination list is used when a ray enters the component in the search for which volume it starts in.
    // It facilitates the same optimization as the regular destination list.
    // The start logic list is also generated, as it is very simple and does not need a seperate function
    
    // Mask update: Need to remove mask volumes from the reduced starting destination list
    
    MPI_MASTER(
    if (verbal) printf("\nGenerating reduced start destination list ----------------------- \n");
    )
    
    int checked_dest_index,checked_dest_volume,rest_dest_index,rest_dest_volume,dest_index;
    
    /* Old version before masks were introduced
    struct pointer_to_1d_int_list starting_dest_list;
    
    starting_dest_list.num_elements = number_of_volumes - 1;
    starting_dest_list.elements = malloc ( starting_dest_list.num_elements * sizeof(int));
    for (iterate=0;iterate<number_of_volumes-1;iterate++) starting_dest_list.elements[iterate] = iterate + 1; // All volumes to be checked for starting
    */
    
    logic_list.num_elements = starting_lists->starting_destinations_list.num_elements;
    free(logic_list.elements);
    logic_list.elements = malloc( (int) logic_list.num_elements * sizeof(int));
    
    //printf("Sucsessfully allocated space for %d integers in logic list %d\n",logic_list.num_elements,volume_index);
    for (iterate=0;iterate<logic_list.num_elements;iterate++) logic_list.elements[iterate] = 1;
  
    for (checked_dest_index=0;checked_dest_index<starting_lists->starting_destinations_list.num_elements;checked_dest_index++) {
        checked_dest_volume = starting_lists->starting_destinations_list.elements[checked_dest_index];
        for (rest_dest_index=0;rest_dest_index<starting_lists->starting_destinations_list.num_elements;rest_dest_index++) {
            rest_dest_volume = starting_lists->starting_destinations_list.elements[rest_dest_index];
            // As every volume has 0 as a parent, these are ignored. It would work to include this, but would add an extra trivial step to within_which_volume
            if (rest_dest_volume != 0) {
                if (on_int_list(*parents_lists[checked_dest_volume],rest_dest_volume) == 1) {
                    // In this case, do not include element checked_dest_index on the reduced destinations list
                    logic_list.elements[checked_dest_index] = 0;
                }
            }
        }
  }
  
  starting_lists->reduced_start_list.num_elements = sum_int_list(logic_list);
  starting_lists->reduced_start_list.elements = malloc((int)starting_lists->reduced_start_list.num_elements * sizeof(int));
  
  iterate = 0;
  for (dest_index = 0;dest_index < starting_lists->starting_destinations_list.num_elements;dest_index++) {
        if (logic_list.elements[dest_index] == 1) starting_lists->reduced_start_list.elements[iterate++] = starting_lists->starting_destinations_list.elements[dest_index];
  }
  
  free(logic_list.elements);
  //free(starting_dest_list.elements);
  
  MPI_MASTER(
  if (verbal) print_1d_int_list(starting_lists->reduced_start_list,"Reduced start destinations list");
  )
    
  // Making the start_logic_list.
  starting_lists->start_logic_list.num_elements = number_of_volumes;
  starting_lists->start_logic_list.elements = malloc ( starting_lists->start_logic_list.num_elements * sizeof(int));
  starting_lists->start_logic_list.elements[0] = 0;
  for (iterate=1;iterate<number_of_volumes;iterate++) starting_lists->start_logic_list.elements[iterate] = 1; // All volumes to be checked for starting volume
  MPI_MASTER(
  if (verbal) print_1d_int_list(starting_lists->start_logic_list,"Start logic list");
  )
};


void generate_next_volume_list(struct Volume_struct **Volumes, int number_of_volumes, int verbal) {
    // Generate list of volumes that can be the next volume which the ray enters. It is used for tagging, not the simulation / propagation

    // Mask update: These lists should be done as if all mask statuses are on, meaning they include all possible next volumes (will include more input to this function)
    
    // bug: next volume list is not complete and contains duplicates

    MPI_MASTER(
    if (verbal) printf("\nGenerating next volume list ------------------------------------- \n");
    )
    // Merge destinations list, intersection list and mask_intersection_list
    int volume_index,iterate,mask_index;
    struct pointer_to_1d_int_list full_intersection_list;
    full_intersection_list.num_elements=0;
    
    for (volume_index=0;volume_index<number_of_volumes;volume_index++) {
        Volumes[volume_index]->geometry.next_volume_list.num_elements = 0;
        // Before mask update
        //merge_lists(&Volumes[volume_index]->geometry.next_volume_list, &Volumes[volume_index]->geometry.destinations_list, &Volumes[volume_index]->geometry.intersect_check_list);
        
        merge_lists(&full_intersection_list, &Volumes[volume_index]->geometry.mask_intersect_list, &Volumes[volume_index]->geometry.intersect_check_list);
        merge_lists(&Volumes[volume_index]->geometry.next_volume_list,&Volumes[volume_index]->geometry.destinations_list,&full_intersection_list);
        
        /* // This complication is taken into account by adding the mask_intersect list instead
        // It is possible that the next volume is still not on this list, as when masks are encountered the next volume can be a volume they mask.
        // For each on the list, add the volumes masked by that mask (do not iterate over this, as masks can not be masked regardless)
        for (iterate=Volumes[volume_index]->geometry.next_volume_list.num_elements-1;iterate>-1;iterate--) {
          if (Volumes[Volumes[volume_index]->geometry.next_volume_list.elements[iterate]]->geometry.is_mask_volume == 1) {
            for (mask_index=0;mask_index<Volumes[Volumes[volume_index]->geometry.next_volume_list.elements[iterate]]->geometry.mask_list.num_elements;mask_index++) {
              add_element_to_int_list(&Volumes[volume_index]->geometry.next_volume_list,Volumes[Volumes[volume_index]->geometry.next_volume_list.elements[iterate]]->geometry.mask_list.elements[mask_index]);
            }
          }
        }
        */
        
        // Remove mask volumes from the next volume list, as they never occur as current_volume, and thus just create dead branches that takes up memory
        for (iterate=Volumes[volume_index]->geometry.next_volume_list.num_elements-1;iterate>-1;iterate--)
          if (Volumes[Volumes[volume_index]->geometry.next_volume_list.elements[iterate]]->geometry.is_mask_volume == 1)
            remove_element_in_list_by_index(&Volumes[volume_index]->geometry.next_volume_list,iterate);
        
        
        if (full_intersection_list.num_elements>0) free(full_intersection_list.elements);
        full_intersection_list.num_elements=0;
        
        char string_output[128];
        MPI_MASTER(
        if (verbal) sprintf(string_output,"Next volume list                %d",volume_index);
        if (verbal) print_1d_int_list(Volumes[volume_index]->geometry.next_volume_list,string_output);
        )
    }
};

void generate_lists(struct Volume_struct **Volumes, struct starting_lists_struct *starting_lists, int number_of_volumes, int verbal) {
    // Function to control the generation of lists
    // Some lists are only needed temporary, and are thus declared here to keep them out of the main scope
    // Others are stored in the volume structs as they are needed in the trace algorithm (or tagging)
    
    
    struct pointer_to_1d_int_list **true_children_lists;
    true_children_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    // generate_children_lists both generate the normal children list for each volume, but also the true children list needed locally.
    generate_children_lists(Volumes, true_children_lists, number_of_volumes,verbal);
    
    
    struct pointer_to_1d_int_list **true_overlap_lists;
    true_overlap_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    struct pointer_to_1d_int_list **raw_overlap_lists;
    raw_overlap_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    
    generate_overlap_lists(true_overlap_lists, raw_overlap_lists, Volumes,number_of_volumes,verbal);
    
    
    //generate_intersect_check_lists(true_overlap_lists, Volumes, number_of_volumes, verbal);
    
    
    struct pointer_to_1d_int_list **parents_lists;
    parents_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    generate_parents_lists(parents_lists,Volumes,number_of_volumes,verbal,1); // The last 1 means masks are taken into account
    
    // Generate version of parent list as it would be without masks
    struct pointer_to_1d_int_list **parents_lists_no_masks;
    parents_lists_no_masks = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    generate_parents_lists(parents_lists_no_masks,Volumes,number_of_volumes,verbal,0); // The last 0 means masks are NOT taken into account
    
    // Generate version of parent list using true_children instead
    struct pointer_to_1d_int_list **true_parents_lists;
    true_parents_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    generate_true_parents_lists(true_parents_lists, true_children_lists, Volumes, number_of_volumes, verbal, 1);
    
    // Generate version of parent list no masks using true_children instead
    struct pointer_to_1d_int_list **true_parents_lists_no_masks;
    true_parents_lists_no_masks = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    generate_true_parents_lists(true_parents_lists_no_masks, true_children_lists, Volumes, number_of_volumes, verbal, 0);
    
    // New version of generate intersect lists
    generate_intersect_check_lists_experimental(true_overlap_lists, raw_overlap_lists, parents_lists, true_parents_lists, Volumes, number_of_volumes, verbal);
    
    struct pointer_to_1d_int_list **grandparents_lists;
    grandparents_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    generate_grandparents_lists(grandparents_lists,parents_lists,number_of_volumes,verbal);
    
    // Generate version of grandparents list as it would have been if no masks were defined
    struct pointer_to_1d_int_list **grandparents_lists_no_masks;
    grandparents_lists_no_masks = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    generate_grandparents_lists(grandparents_lists_no_masks,parents_lists_no_masks,number_of_volumes,verbal);
    
    // Generate true_grandparents_lists
    struct pointer_to_1d_int_list **true_grandparents_lists;
    true_grandparents_lists = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    generate_grandparents_lists(true_grandparents_lists,true_parents_lists,number_of_volumes,verbal);
    
    struct pointer_to_1d_int_list **true_grandparents_lists_no_masks;
    true_grandparents_lists_no_masks = malloc(number_of_volumes*sizeof(struct pointer_to_1d_int_list*));
    generate_grandparents_lists(true_grandparents_lists_no_masks,true_parents_lists_no_masks,number_of_volumes,verbal);
    
    // The destinations lists are generated without taking masks into account (they are removed from the overlap list in an early step)
    //generate_destinations_lists(grandparents_lists_no_masks,parents_lists_no_masks,true_overlap_lists,Volumes,number_of_volumes,verbal);
    generate_destinations_lists_experimental(true_overlap_lists, true_children_lists, true_parents_lists_no_masks, true_grandparents_lists_no_masks, Volumes, number_of_volumes, verbal);
    
    // Obsolete, found a way around them in within_which_volume, but need to test the performance difference
    // generate_destinations_logic_lists(Volumes,number_of_volumes,verbal);
    
    generate_reduced_destinations_lists(parents_lists,Volumes,number_of_volumes,verbal);
    
    generate_direct_children_lists(parents_lists,Volumes,number_of_volumes,verbal);
    
    //generate_starting_list(starting_lists,Volumes,number_of_volumes,verbal);
    generate_starting_logic_list(starting_lists,Volumes,number_of_volumes,verbal);
    
    generate_reduced_starting_destinations_list(starting_lists,parents_lists,Volumes,number_of_volumes,verbal);
    
    // This list is stored with the volumes for convinience, but is only used for tagging
    generate_next_volume_list(Volumes,number_of_volumes,verbal);
    
    // Garbage collection for temporary dynamically allocated lists. (Permanent lists freed from FINALLY)
    int iterate;
    for (iterate=0;iterate<number_of_volumes;iterate++) {
        //printf("freeing for volume nr %d\n",iterate);
        //printf("true_overlap_lists[iterate]->num_elements = %d \n",true_overlap_lists[iterate]->num_elements);
        if (true_overlap_lists[iterate]->num_elements > 0) free(true_overlap_lists[iterate]->elements);
        free(true_overlap_lists[iterate]);
        
        //printf("raw_overlap_lists[iterate]->num_elements = %d \n",raw_overlap_lists[iterate]->num_elements);
        if (raw_overlap_lists[iterate]->num_elements > 0) free(raw_overlap_lists[iterate]->elements);
        free(raw_overlap_lists[iterate]);
        
        //printf("parents_lists[iterate]->num_elements = %d \n",parents_lists[iterate]->num_elements);
        if (parents_lists[iterate]->num_elements > 0) free(parents_lists[iterate]->elements);
        free(parents_lists[iterate]);
        
        //printf("parents_lists_no_masks[iterate]->num_elements = %d \n",parents_lists_no_masks[iterate]->num_elements);
        if (parents_lists_no_masks[iterate]->num_elements > 0) free(parents_lists_no_masks[iterate]->elements);
        free(parents_lists_no_masks[iterate]);
        
        //printf("true_parents_lists[iterate]->num_elements = %d \n",true_parents_lists[iterate]->num_elements);
        if (true_parents_lists[iterate]->num_elements > 0) free(true_parents_lists[iterate]->elements);
        free(true_parents_lists[iterate]);
        
        //printf("true_parents_lists_no_masks[iterate]->num_elements = %d \n",true_parents_lists_no_masks[iterate]->num_elements);
        if (true_parents_lists_no_masks[iterate]->num_elements > 0) free(true_parents_lists_no_masks[iterate]->elements);
        free(true_parents_lists_no_masks[iterate]);
        
        //printf("grandparents_lists[iterate]->num_elements = %d \n",grandparents_lists[iterate]->num_elements);
        if (grandparents_lists[iterate]->num_elements > 0) free(grandparents_lists[iterate]->elements);
        free(grandparents_lists[iterate]);
        
        //printf("true_grandparents_lists[iterate]->num_elements = %d \n",true_grandparents_lists[iterate]->num_elements);
        if (true_grandparents_lists[iterate]->num_elements > 0) free(true_grandparents_lists[iterate]->elements);
        free(true_grandparents_lists[iterate]);
        
        //printf("grandparents_lists_no_masks[iterate]->num_elements = %d \n",grandparents_lists_no_masks[iterate]->num_elements);
        if (grandparents_lists_no_masks[iterate]->num_elements > 0) free(grandparents_lists_no_masks[iterate]->elements);
        free(grandparents_lists_no_masks[iterate]);
        
        //printf("true_grandparents_lists_no_masks[iterate]->num_elements = %d \n",true_grandparents_lists_no_masks[iterate]->num_elements);
        if (true_grandparents_lists_no_masks[iterate]->num_elements > 0) free(true_grandparents_lists_no_masks[iterate]->elements);
        free(true_grandparents_lists_no_masks[iterate]);
        
        //printf("true_children_lists[iterate]->num_elements = %d \n",true_children_lists[iterate]->num_elements);
        if (true_children_lists[iterate]->num_elements > 0) free(true_children_lists[iterate]->elements);
        free(true_children_lists[iterate]);
    }
    //printf("generate lists volume specific free completed\n");
    free(true_overlap_lists);free(raw_overlap_lists);free(parents_lists);free(true_parents_lists);free(true_parents_lists_no_masks);
    free(parents_lists_no_masks);free(true_grandparents_lists);free(grandparents_lists);free(grandparents_lists_no_masks);free(true_grandparents_lists_no_masks);
    free(true_children_lists);
    //printf("generate lists free completed\n");
};

// -------------    Focusing functions   --------------------------------------------------------

// The focusing_data structure is set up by the geometry component, and a pointer to the appropriate
//  focusing function is added to the Volume structure (for this reason the input of all the functions
//  need to be identical, at least in terms of types). In this way there are no if statements to check
//  which of these to be used in the trace, but the focus_data_struct will carry some redundant
//  information, as only the appropriate parameters are set:
// Angular focus on a rectangle (angular_focus_height / angular_focus_width)
// Spatial focus on a rectangle (spatial_focus_height / spatial_focus_width)
// Spatial focus on a disk (sptial_focus_radius)
// No focus (randvec in 4pi) (all set to zero, will select randvec circle as it is slightly faster
//
// When adding a new physical process focusing becomes very easy, as one just calls the master focusing
//  function assosiated with the volume (placed in the geometry struct), using the focus_data_struct
//  also found in the geometry struct, and the process then supports all the focusing modes. It is even
//  possible to add new focusing modes in the future by updating just the geometry components, and this
//  section.

// focus_data_struct definitioon shown here, defined at the start of this file
//struct focus_data_struct {
//Coords Aim;
//double angular_focus_width;
//double angular_focus_height;
//double spatial_focus_width;
//double spatial_focus_height;
//double spatial_focus_radius;
//Rotation absolute_rotation;
//// focusing_function creates a vector per selected criteria of focus_data_struct / selected focus function and returns solid angle
//void (*focusing_function)(Coords*, double*, struct focus_data_struct*);
////                        v_out  , solid_a,
//};

void randvec_target_rect_angular_union(Coords *v_out,double *solid_angle_out, struct focus_data_struct *focus_data) {
    // Calls the standard McStas randvec_target_rect_angular focusing function, but is with the new data input format.
    randvec_target_rect_angular(&v_out->x, &v_out->y, &v_out->z, solid_angle_out, focus_data->Aim.x,focus_data->Aim.y, focus_data->Aim.z, focus_data->angular_focus_width, focus_data->angular_focus_height,focus_data->absolute_rotation);
    //randvec_target_rect_angular(&vx, &vy, &vz, &solid_angle,aim_x, aim_y, aim_z, VarsInc.aw, VarsInc.ah, ROT_A_CURRENT_COMP);
};

void randvec_target_rect_union(Coords *v_out,double *solid_angle_out, struct focus_data_struct *focus_data) {
// Calls the standard McStas randvec_target_rect focusing function, but is with the new data input format.
    randvec_target_rect(&v_out->x, &v_out->y, &v_out->z, solid_angle_out, focus_data->Aim.x,focus_data->Aim.y, focus_data->Aim.z, focus_data->spatial_focus_width, focus_data->spatial_focus_height,focus_data->absolute_rotation);
    // randvec_target_rect(&vx, &vy, &vz, &solid_angle,aim_x, aim_y, aim_z, VarsInc.xw, VarsInc.yh, ROT_A_CURRENT_COMP);
};

void randvec_target_circle_union(Coords *v_out,double *solid_angle_out, struct focus_data_struct *focus_data) {
// Calls the standard McStas randvec_target_circle focusing function, but is with the new data input format.

    // debug input into randvec_target_circle
    //print_position(focus_data->Aim,"Aim vector input for randvec_target_circle");
    //printf("Radius input %f\n",focus_data->spatial_focus_radius);

    randvec_target_circle(&v_out->x, &v_out->y, &v_out->z, solid_angle_out, focus_data->Aim.x,focus_data->Aim.y, focus_data->Aim.z, focus_data->spatial_focus_radius);
    //randvec_target_circle(&vx, &vy, &vz, &solid_angle, aim_x, aim_y, aim_z, focus_r);
};



void focus_initialize(struct geometry_struct *geometry, Coords POS_A_TARGET, Coords POS_A_CURRENT, Rotation ROT_A_CURRENT, int target_index, double target_x, double target_y, double target_z, double angular_focus_width, double angular_focus_height, double spatial_focus_width, double spatial_focus_height, double spatial_focus_radius, char *component_name) {
    // Initialize focusing system
    // target_x/y/z needs to be double setting parameters with default value 0
    // target_index needs to be int setting parameter with default value 0
    // angular_focus_width, angular_focus_height, spatial_focus_width, spatial_focus_height, spatial_focus_radius nneds to be double setting parameters with default value 0
    // When those conditions are met, this code will identify which settings have been entered by the user and select the appropriate focusing parameters, which are loaded into the focus_data struct and the geometry struct.
    // The aim vector in the focus_data struct will be transformed from the local coordinate system of the geometry component to the coordinate system of the master component during the master component initialize

  // Input sanitation
  if (angular_focus_width < 0) {
    printf("\nERROR in Union geometry component named \"%s\", angular focus width focus_aw < 0! \n",component_name);
    exit(1);
  }
  if (angular_focus_height < 0) {
    printf("\nERROR in Union geometry component named \"%s\", angular focus width focus_ah < 0! \n",component_name);
    exit(1);
  }
  if (spatial_focus_width < 0) {
    printf("\nERROR in Union geometry component named \"%s\", spatial focus width focus_xw < 0! \n",component_name);
    exit(1);
  }
  if (spatial_focus_height < 0) {
    printf("\nERROR in Union geometry component named \"%s\", spatial focus height focus_xh < 0! \n",component_name);
    exit(1);
  }
  if (spatial_focus_radius < 0) {
    printf("\nERROR in Union geometry component named \"%s\", spatial focus radius focus_r < 0! \n",component_name);
    exit(1);
  }
  
  struct focus_data_struct focus_data;

  // Initialize focus_data_struct
  /*
  geometry->focus_data.Aim = coords_set(0,0,0);
  geometry->focus_data.angular_focus_width = 0;
  geometry->focus_data.angular_focus_height = 0;
  geometry->focus_data.spatial_focus_width = 0;
  geometry->focus_data.spatial_focus_height = 0;
  geometry->focus_data.spatial_focus_radius = 0;
  rot_copy(geometry->focus_data.absolute_rotation,ROT_A_CURRENT);
  */
  focus_data.Aim = coords_set(0,0,0);
  focus_data.angular_focus_width = 0;
  focus_data.angular_focus_height = 0;
  focus_data.spatial_focus_width = 0;
  focus_data.spatial_focus_height = 0;
  focus_data.spatial_focus_radius = 0;
  rot_copy(focus_data.absolute_rotation,ROT_A_CURRENT);

  // Built on code from Incoherent.comp by Kim Lefmann and Kristian Nielsen
  if (target_index != 0 && !target_x && !target_y && !target_z)
  {
    Coords ToTarget;
    //ToTarget = coords_sub(POS_A_COMP_INDEX(INDEX_CURRENT_COMP+target_index),POS_A_CURRENT_COMP);
    ToTarget = coords_sub(POS_A_TARGET,POS_A_CURRENT);
    //ToTarget = rot_apply(ROT_A_CURRENT_COMP, ToTarget);
    ToTarget = rot_apply(ROT_A_CURRENT, ToTarget);
    coords_get(ToTarget, &focus_data.Aim.x, &focus_data.Aim.y, &focus_data.Aim.z);
  }
  else
  { focus_data.Aim.x = target_x; focus_data.Aim.y = target_y; focus_data.Aim.z = target_z; }
  
  if (!(focus_data.Aim.x || focus_data.Aim.y || focus_data.Aim.z)) {
    // Somehow set a variable to signify scattering into 4pi
    // printf("Union %s: The target is not defined. Using scattering into 4pi.\n",NAME_CURRENT_COMP);
    focus_data.Aim.z=1; // set aim to one so that the randvec output vector has length 1 instead of 0
  }

  int focusing_model_selected = 0;
  if (angular_focus_width != 0 && angular_focus_height != 0) {
    focus_data.focusing_function = &randvec_target_rect_angular_union;
    focus_data.angular_focus_width = DEG2RAD*angular_focus_width; // Convert to radians here
    focus_data.angular_focus_height = DEG2RAD*angular_focus_height;
    focusing_model_selected = 1;
  }
  if (spatial_focus_width != 0 && spatial_focus_height != 0) {
    focus_data.focusing_function = &randvec_target_rect_union;
    focus_data.spatial_focus_width = spatial_focus_width;
    focus_data.spatial_focus_height = spatial_focus_height;
    if (focusing_model_selected) {
        printf("ERROR %s: Select either angular or spatial focusing, not both! Exiting \n",component_name);
        exit(1);
    }
    focusing_model_selected = 1;
  }
  if (spatial_focus_radius != 0) {
    focus_data.focusing_function = &randvec_target_circle_union;
    focus_data.spatial_focus_radius = spatial_focus_radius;
    if (focusing_model_selected) {
        printf("ERROR %s: Select a maximum of one focusing method (spatial rectangle or cicle, or angular rectangle! Exiting \n",component_name);
        exit(1);
    }
    focusing_model_selected = 1;
  }
  if (focusing_model_selected == 0) {
    // Select 4pi focusing
    focus_data.spatial_focus_radius = 0;
    focus_data.focusing_function = &randvec_target_circle_union;
  }
  
  // Allocate the isotropic focus_data struct
  geometry->focus_data_array.num_elements = 0;
  //geometry->focus_data_array.elements = malloc(sizeof(focus_data_struct));
  add_element_to_focus_data_array(&geometry->focus_data_array,focus_data);
};


struct abs_event{
    double time1;
    double position1[3];
    double time2;
    double position2[3];
    double weight_change;
    int volume_index;
    int neutron_id;
};

// Functions for recording absorption
void initialize_absorption_file() {
  FILE *fp;
  fp = fopen("Union_absorption.dat","w");
  fprintf(fp,"r_old x, r_old y, r_old z, old t, r x, r y, r z, new t, weight change, volume index, neutron id \n");
  
  fclose(fp);
}

void write_events_to_file(int last_index, struct abs_event *events) {

  FILE *fp;
  fp = fopen("Union_absorption.dat","a");
  
  struct abs_event *this_event;
  int iterate;
  for (iterate=0; iterate<last_index; iterate++) {
  
    this_event = &events[iterate];
    fprintf(fp,"%g, %g, %g, %g, %g, %g, %g, %g, %e, %i, %i \n",
           this_event->position1[0], this_event->position1[1], this_event->position1[2], this_event->time1,
           this_event->position2[0], this_event->position2[1], this_event->position2[2], this_event->time2,
           this_event->weight_change,
           this_event->volume_index,
           this_event->neutron_id);
  }

  fclose(fp);
}

void record_abs_to_file(double *r, double t1, double *r_old, double t2, double weight_change, int volume, int neutron_id, int *data_index, struct abs_event *events) {
  
  
  
  struct abs_event *this_event;
  
  this_event = &events[(*data_index)++];
  
  //printf("Recording something! %i\n", *data_index);
  
  this_event->position1[0] = r[0];
  this_event->position1[1] = r[1];
  this_event->position1[2] = r[2];
  this_event->time1 = t1;
  this_event->position2[0] = r_old[0];
  this_event->position2[1] = r_old[1];
  this_event->position2[2] = r_old[2];
  this_event->time2 = t2;
  this_event->weight_change = weight_change;
  this_event->volume_index = volume;
  this_event->neutron_id = neutron_id;
  
  
  if (*data_index == 999) {
    
      write_events_to_file(*data_index, events);
      *data_index = 0;

  }

};




/*******************************************************************************
*
*  McStas, neutron ray-tracing package
*  Copyright(C) 2007 Risoe National Laboratory.
*
* %I
* Written by: Mads Bertelsen
* Date: 20.08.15
* Version: $Revision: 0.1 $
* Origin: University of Copenhagen
*
* A sample component to separate geometry and phsysics
*
* %D
* Alpha version, no input system yet
* Hardcode input to geometry engine
* Allows complicated geometry by combination of simple shapes
*
* Algorithm:
* Described elsewhere
*
* %P
* INPUT PARAMETERS:
* radius:  [m] Outer radius of sample in (x,z) plane
*
* OUTPUT PARAMETERS:
* V_rho:  [AA^-3] Atomic density
*
* %L
* The test/example instrument <a href="../examples/Test_Phonon.instr">Test_Phonon.instr</a>.
*
* %E
******************************************************************************/


// This file sets up global lists needed for Union components to communicate with each other
// These all have dynamically allocated memory somewhere in the structure, which is deallocated
//  by the last Union_master.



// Initialize global positions / rotations to transform lists
  // These are lists of pointers to positons / rotations, that will be updated from global frame
  //  to the frame of the master component that uses them in that masters initialize section.
  struct global_positions_to_transform_list_struct global_positions_to_transform_list = {0,NULL};
  struct global_rotations_to_transform_list_struct global_rotations_to_transform_list = {0,NULL};

// Initialize global_process_list
  // Used to facilitate communication between processes and the other types of Union components
  struct pointer_to_global_process_list global_process_list = {0,NULL};

// Initialize global_material_list
  // Used to facilitate communication between materials and the other types of Union components
  struct pointer_to_global_material_list global_material_list = {0,NULL};

// Initialize global_geometry_list
  // Used to facilitate communication between geometries and the other types of Union components
  struct pointer_to_global_geometry_list global_geometry_list = {0,NULL};

// Initialize global_logger_lists
  // Used to facilitate communication between loggers and the other types of Union components
  struct pointer_to_global_logger_list global_all_volume_logger_list = {0,NULL};
  struct pointer_to_global_logger_list global_specific_volumes_logger_list = {0,NULL};

// Initialize global_abs_logger_lists
  struct pointer_to_global_abs_logger_list global_all_volume_abs_logger_list = {0,NULL};
  struct pointer_to_global_abs_logger_list global_specific_volumes_abs_logger_list = {0,NULL};

// Initialize global_tagging_conditional_list
  // Used to facilitate communication between conditionals and the other types of Union components
  struct global_tagging_conditional_list_struct global_tagging_conditional_list = {0,0,NULL};

// Initialize global_master_list
  // Used to facilitate communication between Master components (mainly for deallocation)
  struct pointer_to_global_master_list global_master_list = {0,NULL};

// Initialize global_mantid_min_pixel_id
  // Used for ensuring pixel id's on Mantid monitors do not overlap
  int global_mantid_min_pixel_id = 0;


#endif

/*
TODO

update Union master to use the flexible functions

deal with loggers and conditionals
*/

int physics_my(enum process choice, double *my,double *k_initial, union data_transfer_union data_transfer, struct focus_data_struct *focus_data, _class_particle *_particle) {

    int output = 0; // Error return value
    #ifdef PROCESS_DETECTOR
    switch(choice) {
        #ifdef PROCESS_INCOHERENT_DETECTOR
        case Incoherent:
            output = Incoherent_physics_my(my, k_initial, data_transfer, focus_data, _particle);
            break;
        #endif
        #ifdef PROCESS_POWDER_DETECTOR
        case Powder:
            output = Powder_physics_my(my, k_initial, data_transfer, focus_data, _particle);
            break;
        #endif
        #ifdef PROCESS_SINGLE_CRYSTAL_DETECTOR
        case Single_crystal:
            output = Single_crystal_physics_my(my, k_initial, data_transfer, focus_data, _particle);
            break;
        #endif
        #ifdef PROCESS_AF_HB_1D_DETECTOR
        case AF_HB_1D:
            output = AF_HB_1D_physics_my(my, k_initial, data_transfer, focus_data, _particle);
            break;
        #endif
        #ifdef PROCESS_PHONONSIMPLE_DETECTOR
        case PhononSimple:
            output = PhononSimple_physics_my(my, k_initial, data_transfer, focus_data, _particle);
            break;
        #endif
        #ifdef PROCESS_TEXTURE_DETECTOR
        case Texture:
            output = Texture_physics_my(my, k_initial, data_transfer, focus_data, _particle);
            break;
        #endif
        #ifdef PROCESS_INCOHERENTPHONON_DETECTOR
        case IncoherentPhonon:
            output = IncoherentPhonon_physics_my(my, k_initial, data_transfer, focus_data, _particle);
            break;
        #endif
        #ifdef PROCESS_NCRYSTAL_DETECTOR
        case NCrystal:
            output = NCrystal_physics_my(my, k_initial, data_transfer, focus_data, _particle);
            break;
        #endif
        #ifdef PROCESS_TEMPLATE_DETECTOR
        case Template:
            output = Template_physics_my(my, k_initial, data_transfer, focus_data, _particle);
            break;
        #endif
        default:
            printf("physics_my: No scattering process matches input!\n");
            break;
    }
    #endif
    return output;
}
  

int physics_scattering(enum process choice, double *k_final, double *k_initial, double *weight, union data_transfer_union data_transfer, struct focus_data_struct *focus_data, _class_particle *_particle) {

    int output = 0; // Error return value
    #ifdef PROCESS_DETECTOR
    switch(choice) {
        #ifdef PROCESS_INCOHERENT_DETECTOR
        case Incoherent:
            output = Incoherent_physics_scattering(k_final, k_initial, weight, data_transfer, focus_data, _particle);
            break;
        #endif
        #ifdef PROCESS_POWDER_DETECTOR
        case Powder:
            output = Powder_physics_scattering(k_final, k_initial, weight, data_transfer, focus_data, _particle);
            break;
        #endif
        #ifdef PROCESS_SINGLE_CRYSTAL_DETECTOR
        case Single_crystal:
            output = Single_crystal_physics_scattering(k_final, k_initial, weight, data_transfer, focus_data, _particle);
            break;
        #endif
        #ifdef PROCESS_AF_HB_1D_DETECTOR
        case AF_HB_1D:
            output = AF_HB_1D_physics_scattering(k_final, k_initial, weight, data_transfer, focus_data, _particle);
            break;
        #endif
        #ifdef PROCESS_PHONONSIMPLE_DETECTOR
        case PhononSimple:
            output = PhononSimple_physics_scattering(k_final, k_initial, weight, data_transfer, focus_data, _particle);
            break;
        #endif
        #ifdef PROCESS_TEXTURE_DETECTOR
        case Texture:
            output = Texture_physics_scattering(k_final, k_initial, weight, data_transfer, focus_data, _particle);
            break;
        #endif
        #ifdef PROCESS_INCOHERENTPHONON_DETECTOR
        case IncoherentPhonon:
            output = IncoherentPhonon_physics_scattering(k_final, k_initial, weight, data_transfer, focus_data, _particle);
            break;
        #endif
        #ifdef PROCESS_NCRYSTAL_DETECTOR
        case NCrystal:
            output = NCrystal_physics_scattering(k_final, k_initial, weight, data_transfer, focus_data, _particle);
            break;
        #endif
        #ifdef PROCESS_TEMPLATE_DETECTOR
        case Template:
            output = Incoherent_physics_scattering(k_final, k_initial, weight, data_transfer, focus_data, _particle);
            break;
        #endif
        default: printf("physics_scattering: No scattering process matches input!\n");
            break;
    }
    #endif
    return output;
}




/* ************************************************************************** */
/*             End of SHARE user declarations for all components              */
/* ************************************************************************** */


/* ********************** component definition declarations. **************** */

/* component origin=Progress_bar() [1] DECLARE */
/* Parameter definition for component type 'Progress_bar' */
struct _struct_Progress_bar_parameters {
  /* Component type 'Progress_bar' setting parameters */
  char profile[16384];
  MCNUM percent;
  MCNUM flag_save;
  MCNUM minutes;
  /* Component type 'Progress_bar' private parameters */
  double  IntermediateCnts;
  time_t  StartTime;
  time_t  EndTime;
  time_t  CurrentTime;
}; /* _struct_Progress_bar_parameters */
typedef struct _struct_Progress_bar_parameters _class_Progress_bar_parameters;

/* Parameters for component type 'Progress_bar' */
struct _struct_Progress_bar {
  char     _name[256]; /* e.g. origin */
  char     _type[256]; /* Progress_bar */
  long     _index; /* e.g. 2 index in TRACE list */
  Coords   _position_absolute;
  Coords   _position_relative; /* wrt PREVIOUS */
  Rotation _rotation_absolute;
  Rotation _rotation_relative; /* wrt PREVIOUS */
  int      _rotation_is_identity;
  int      _position_relative_is_zero;
  _class_Progress_bar_parameters _parameters;
};
typedef struct _struct_Progress_bar _class_Progress_bar;
_class_Progress_bar _origin_var;
#pragma acc declare create ( _origin_var )

/* component source_maxwell_3=Source_Maxwell_3() [2] DECLARE */
/* Parameter definition for component type 'Source_Maxwell_3' */
struct _struct_Source_Maxwell_3_parameters {
  /* Component type 'Source_Maxwell_3' setting parameters */
  MCNUM size;
  MCNUM yheight;
  MCNUM xwidth;
  MCNUM Lmin;
  MCNUM Lmax;
  MCNUM dist;
  MCNUM focus_xw;
  MCNUM focus_yh;
  MCNUM T1;
  MCNUM T2;
  MCNUM T3;
  MCNUM I1;
  MCNUM I2;
  MCNUM I3;
  long target_index;
  MCNUM lambda0;
  MCNUM dlambda;
  /* Component type 'Source_Maxwell_3' private parameters */
  double  l_range;
  double  w_mult;
  double  w_source;
  double  h_source;
}; /* _struct_Source_Maxwell_3_parameters */
typedef struct _struct_Source_Maxwell_3_parameters _class_Source_Maxwell_3_parameters;

/* Parameters for component type 'Source_Maxwell_3' */
struct _struct_Source_Maxwell_3 {
  char     _name[256]; /* e.g. source_maxwell_3 */
  char     _type[256]; /* Source_Maxwell_3 */
  long     _index; /* e.g. 2 index in TRACE list */
  Coords   _position_absolute;
  Coords   _position_relative; /* wrt PREVIOUS */
  Rotation _rotation_absolute;
  Rotation _rotation_relative; /* wrt PREVIOUS */
  int      _rotation_is_identity;
  int      _position_relative_is_zero;
  _class_Source_Maxwell_3_parameters _parameters;
};
typedef struct _struct_Source_Maxwell_3 _class_Source_Maxwell_3;
_class_Source_Maxwell_3 _source_maxwell_3_var;
#pragma acc declare create ( _source_maxwell_3_var )

/* component guide=Guide() [3] DECLARE */
/* Parameter definition for component type 'Guide' */
struct _struct_Guide_parameters {
  /* Component type 'Guide' setting parameters */
  char reflect[16384];
  MCNUM w1;
  MCNUM h1;
  MCNUM w2;
  MCNUM h2;
  MCNUM l;
  MCNUM R0;
  MCNUM Qc;
  MCNUM alpha;
  MCNUM m;
  MCNUM W;
  /* Component type 'Guide' private parameters */
  t_Table  pTable;
  int  table_present;
}; /* _struct_Guide_parameters */
typedef struct _struct_Guide_parameters _class_Guide_parameters;

/* Parameters for component type 'Guide' */
struct _struct_Guide {
  char     _name[256]; /* e.g. guide */
  char     _type[256]; /* Guide */
  long     _index; /* e.g. 2 index in TRACE list */
  Coords   _position_absolute;
  Coords   _position_relative; /* wrt PREVIOUS */
  Rotation _rotation_absolute;
  Rotation _rotation_relative; /* wrt PREVIOUS */
  int      _rotation_is_identity;
  int      _position_relative_is_zero;
  _class_Guide_parameters _parameters;
};
typedef struct _struct_Guide _class_Guide;
_class_Guide _guide_var;
#pragma acc declare create ( _guide_var )

/* component PSD_before_monochromator=PSD_monitor() [4] DECLARE */
/* Parameter definition for component type 'PSD_monitor' */
struct _struct_PSD_monitor_parameters {
  /* Component type 'PSD_monitor' setting parameters */
  MCNUM nx;
  MCNUM ny;
  char filename[16384];
  MCNUM xmin;
  MCNUM xmax;
  MCNUM ymin;
  MCNUM ymax;
  MCNUM xwidth;
  MCNUM yheight;
  MCNUM restore_neutron;
  long nowritefile;
  /* Component type 'PSD_monitor' private parameters */
  DArray2d  PSD_N;
  DArray2d  PSD_p;
  DArray2d  PSD_p2;
}; /* _struct_PSD_monitor_parameters */
typedef struct _struct_PSD_monitor_parameters _class_PSD_monitor_parameters;

/* Parameters for component type 'PSD_monitor' */
struct _struct_PSD_monitor {
  char     _name[256]; /* e.g. PSD_before_monochromator */
  char     _type[256]; /* PSD_monitor */
  long     _index; /* e.g. 2 index in TRACE list */
  Coords   _position_absolute;
  Coords   _position_relative; /* wrt PREVIOUS */
  Rotation _rotation_absolute;
  Rotation _rotation_relative; /* wrt PREVIOUS */
  int      _rotation_is_identity;
  int      _position_relative_is_zero;
  _class_PSD_monitor_parameters _parameters;
};
typedef struct _struct_PSD_monitor _class_PSD_monitor;
_class_PSD_monitor _PSD_before_monochromator_var;
#pragma acc declare create ( _PSD_before_monochromator_var )

/* component a1=Arm() [5] DECLARE */
/* Parameter definition for component type 'Arm' */
struct _struct_Arm_parameters {
  char Arm_has_no_parameters;
}; /* _struct_Arm_parameters */
typedef struct _struct_Arm_parameters _class_Arm_parameters;

/* Parameters for component type 'Arm' */
struct _struct_Arm {
  char     _name[256]; /* e.g. a1 */
  char     _type[256]; /* Arm */
  long     _index; /* e.g. 2 index in TRACE list */
  Coords   _position_absolute;
  Coords   _position_relative; /* wrt PREVIOUS */
  Rotation _rotation_absolute;
  Rotation _rotation_relative; /* wrt PREVIOUS */
  int      _rotation_is_identity;
  int      _position_relative_is_zero;
  _class_Arm_parameters _parameters;
};
typedef struct _struct_Arm _class_Arm;
_class_Arm _a1_var;
#pragma acc declare create ( _a1_var )

/* component monochromator_flat1=Monochromator_flat() [6] DECLARE */
/* Parameter definition for component type 'Monochromator_flat' */
struct _struct_Monochromator_flat_parameters {
  /* Component type 'Monochromator_flat' setting parameters */
  MCNUM zmin;
  MCNUM zmax;
  MCNUM ymin;
  MCNUM ymax;
  MCNUM zwidth;
  MCNUM yheight;
  MCNUM mosaich;
  MCNUM mosaicv;
  MCNUM r0;
  MCNUM Q;
  MCNUM DM;
  /* Component type 'Monochromator_flat' private parameters */
  double  mos_rms_y;
  double  mos_rms_z;
  double  mos_rms_max;
  double  mono_Q;
}; /* _struct_Monochromator_flat_parameters */
typedef struct _struct_Monochromator_flat_parameters _class_Monochromator_flat_parameters;

/* Parameters for component type 'Monochromator_flat' */
struct _struct_Monochromator_flat {
  char     _name[256]; /* e.g. monochromator_flat1 */
  char     _type[256]; /* Monochromator_flat */
  long     _index; /* e.g. 2 index in TRACE list */
  Coords   _position_absolute;
  Coords   _position_relative; /* wrt PREVIOUS */
  Rotation _rotation_absolute;
  Rotation _rotation_relative; /* wrt PREVIOUS */
  int      _rotation_is_identity;
  int      _position_relative_is_zero;
  _class_Monochromator_flat_parameters _parameters;
};
typedef struct _struct_Monochromator_flat _class_Monochromator_flat;
_class_Monochromator_flat _monochromator_flat1_var;
#pragma acc declare create ( _monochromator_flat1_var )

_class_Monochromator_flat _monochromator_flat2_var;
#pragma acc declare create ( _monochromator_flat2_var )

_class_Monochromator_flat _monochromator_flat3_var;
#pragma acc declare create ( _monochromator_flat3_var )

_class_Monochromator_flat _monochromator_flat4_var;
#pragma acc declare create ( _monochromator_flat4_var )

_class_Monochromator_flat _monochromator_flat5_var;
#pragma acc declare create ( _monochromator_flat5_var )

_class_Arm _a2_var;
#pragma acc declare create ( _a2_var )

_class_PSD_monitor _PSD_after_monochromator_var;
#pragma acc declare create ( _PSD_after_monochromator_var )

/* component collimator_linear=Collimator_linear() [13] DECLARE */
/* Parameter definition for component type 'Collimator_linear' */
struct _struct_Collimator_linear_parameters {
  /* Component type 'Collimator_linear' setting parameters */
  MCNUM xmin;
  MCNUM xmax;
  MCNUM ymin;
  MCNUM ymax;
  MCNUM xwidth;
  MCNUM yheight;
  MCNUM length;
  MCNUM divergence;
  MCNUM transmission;
  MCNUM divergenceV;
  /* Component type 'Collimator_linear' private parameters */
  double  slope;
  double  slopeV;
}; /* _struct_Collimator_linear_parameters */
typedef struct _struct_Collimator_linear_parameters _class_Collimator_linear_parameters;

/* Parameters for component type 'Collimator_linear' */
struct _struct_Collimator_linear {
  char     _name[256]; /* e.g. collimator_linear */
  char     _type[256]; /* Collimator_linear */
  long     _index; /* e.g. 2 index in TRACE list */
  Coords   _position_absolute;
  Coords   _position_relative; /* wrt PREVIOUS */
  Rotation _rotation_absolute;
  Rotation _rotation_relative; /* wrt PREVIOUS */
  int      _rotation_is_identity;
  int      _position_relative_is_zero;
  _class_Collimator_linear_parameters _parameters;
};
typedef struct _struct_Collimator_linear _class_Collimator_linear;
_class_Collimator_linear _collimator_linear_var;
#pragma acc declare create ( _collimator_linear_var )

/* component slit=Slit() [14] DECLARE */
/* Parameter definition for component type 'Slit' */
struct _struct_Slit_parameters {
  /* Component type 'Slit' setting parameters */
  MCNUM xmin;
  MCNUM xmax;
  MCNUM ymin;
  MCNUM ymax;
  MCNUM radius;
  MCNUM xwidth;
  MCNUM yheight;
  /* Component type 'Slit' private parameters */
  char  isradial;
}; /* _struct_Slit_parameters */
typedef struct _struct_Slit_parameters _class_Slit_parameters;

/* Parameters for component type 'Slit' */
struct _struct_Slit {
  char     _name[256]; /* e.g. slit */
  char     _type[256]; /* Slit */
  long     _index; /* e.g. 2 index in TRACE list */
  Coords   _position_absolute;
  Coords   _position_relative; /* wrt PREVIOUS */
  Rotation _rotation_absolute;
  Rotation _rotation_relative; /* wrt PREVIOUS */
  int      _rotation_is_identity;
  int      _position_relative_is_zero;
  _class_Slit_parameters _parameters;
};
typedef struct _struct_Slit _class_Slit;
_class_Slit _slit_var;
#pragma acc declare create ( _slit_var )

_class_PSD_monitor _PSD_before_sampel_var;
#pragma acc declare create ( _PSD_before_sampel_var )

/* component Emon_before_sampel=E_monitor() [16] DECLARE */
/* Parameter definition for component type 'E_monitor' */
struct _struct_E_monitor_parameters {
  /* Component type 'E_monitor' setting parameters */
  MCNUM nE;
  char filename[16384];
  MCNUM xmin;
  MCNUM xmax;
  MCNUM ymin;
  MCNUM ymax;
  long nowritefile;
  MCNUM xwidth;
  MCNUM yheight;
  MCNUM Emin;
  MCNUM Emax;
  MCNUM restore_neutron;
  /* Component type 'E_monitor' private parameters */
  DArray1d  E_N;
  DArray1d  E_p;
  DArray1d  E_p2;
  double  S_p;
  double  S_pE;
  double  S_pE2;
}; /* _struct_E_monitor_parameters */
typedef struct _struct_E_monitor_parameters _class_E_monitor_parameters;

/* Parameters for component type 'E_monitor' */
struct _struct_E_monitor {
  char     _name[256]; /* e.g. Emon_before_sampel */
  char     _type[256]; /* E_monitor */
  long     _index; /* e.g. 2 index in TRACE list */
  Coords   _position_absolute;
  Coords   _position_relative; /* wrt PREVIOUS */
  Rotation _rotation_absolute;
  Rotation _rotation_relative; /* wrt PREVIOUS */
  int      _rotation_is_identity;
  int      _position_relative_is_zero;
  _class_E_monitor_parameters _parameters;
};
typedef struct _struct_E_monitor _class_E_monitor;
_class_E_monitor _Emon_before_sampel_var;
#pragma acc declare create ( _Emon_before_sampel_var )

_class_Arm _a3_var;
#pragma acc declare create ( _a3_var )

_class_Arm _a4_var;
#pragma acc declare create ( _a4_var )

_class_Arm _a5_var;
#pragma acc declare create ( _a5_var )

_class_Arm _sample_position_var;
#pragma acc declare create ( _sample_position_var )

/* component init=Union_init() [21] DECLARE */
/* Parameter definition for component type 'Union_init' */
struct _struct_Union_init_parameters {
  /* Component type 'Union_init' private parameters */
  struct global_positions_to_transform_list_struct  global_positions_to_transform_list;
  struct global_rotations_to_transform_list_struct  global_rotations_to_transform_list;
  struct pointer_to_global_process_list  global_process_list;
  struct pointer_to_global_material_list  global_material_list;
  struct pointer_to_global_geometry_list  global_geometry_list;
  struct pointer_to_global_logger_list  global_all_volume_logger_list;
  struct pointer_to_global_logger_list  global_specific_volumes_logger_list;
  struct pointer_to_global_abs_logger_list  global_all_volume_abs_logger_list;
  struct pointer_to_global_abs_logger_list  global_specific_volumes_abs_logger_list;
  struct global_tagging_conditional_list_struct  global_tagging_conditional_list;
  struct pointer_to_global_master_list  global_master_list;
  int  global_mantid_min_pixel_id;
}; /* _struct_Union_init_parameters */
typedef struct _struct_Union_init_parameters _class_Union_init_parameters;

/* Parameters for component type 'Union_init' */
struct _struct_Union_init {
  char     _name[256]; /* e.g. init */
  char     _type[256]; /* Union_init */
  long     _index; /* e.g. 2 index in TRACE list */
  Coords   _position_absolute;
  Coords   _position_relative; /* wrt PREVIOUS */
  Rotation _rotation_absolute;
  Rotation _rotation_relative; /* wrt PREVIOUS */
  int      _rotation_is_identity;
  int      _position_relative_is_zero;
  _class_Union_init_parameters _parameters;
};
typedef struct _struct_Union_init _class_Union_init;
_class_Union_init _init_var;
#pragma acc declare create ( _init_var )

/* component Vanadium_incoherent_Al=Incoherent_process() [22] DECLARE */
/* Parameter definition for component type 'Incoherent_process' */
struct _struct_Incoherent_process_parameters {
  /* Component type 'Incoherent_process' setting parameters */
  MCNUM sigma;
  MCNUM f_QE;
  MCNUM gamma;
  MCNUM packing_factor;
  MCNUM unit_cell_volume;
  MCNUM interact_fraction;
  char init[16384];
  /* Component type 'Incoherent_process' private parameters */
  struct global_process_element_struct  global_process_element;
  struct scattering_process_struct  This_process;
  struct Incoherent_physics_storage_struct  Incoherent_storage;
  double  effective_my_scattering;
}; /* _struct_Incoherent_process_parameters */
typedef struct _struct_Incoherent_process_parameters _class_Incoherent_process_parameters;

/* Parameters for component type 'Incoherent_process' */
struct _struct_Incoherent_process {
  char     _name[256]; /* e.g. Vanadium_incoherent_Al */
  char     _type[256]; /* Incoherent_process */
  long     _index; /* e.g. 2 index in TRACE list */
  Coords   _position_absolute;
  Coords   _position_relative; /* wrt PREVIOUS */
  Rotation _rotation_absolute;
  Rotation _rotation_relative; /* wrt PREVIOUS */
  int      _rotation_is_identity;
  int      _position_relative_is_zero;
  _class_Incoherent_process_parameters _parameters;
};
typedef struct _struct_Incoherent_process _class_Incoherent_process;
_class_Incoherent_process _Vanadium_incoherent_Al_var;
#pragma acc declare create ( _Vanadium_incoherent_Al_var )

/* component Al_Powder=Powder_process() [23] DECLARE */
/* Parameter definition for component type 'Powder_process' */
struct _struct_Powder_process_parameters {
  /* Component type 'Powder_process' setting parameters */
  char reflections[16384];
  MCNUM packing_factor;
  MCNUM Vc;
  MCNUM delta_d_d;
  MCNUM DW;
  MCNUM nb_atoms;
  MCNUM d_phi;
  MCNUM density;
  MCNUM weight;
  MCNUM barns;
  MCNUM Strain;
  MCNUM interact_fraction;
  MCNUM format[9];
  char init[16384];
  /* Component type 'Powder_process' private parameters */
  struct global_process_element_struct  global_process_element;
  struct scattering_process_struct  This_process;
  struct Powder_physics_storage_struct  Powder_storage;
  struct line_info_struct_union  line_info;
  double  effective_my_scattering;
  double * columns;
}; /* _struct_Powder_process_parameters */
typedef struct _struct_Powder_process_parameters _class_Powder_process_parameters;

/* Parameters for component type 'Powder_process' */
struct _struct_Powder_process {
  char     _name[256]; /* e.g. Al_Powder */
  char     _type[256]; /* Powder_process */
  long     _index; /* e.g. 2 index in TRACE list */
  Coords   _position_absolute;
  Coords   _position_relative; /* wrt PREVIOUS */
  Rotation _rotation_absolute;
  Rotation _rotation_relative; /* wrt PREVIOUS */
  int      _rotation_is_identity;
  int      _position_relative_is_zero;
  _class_Powder_process_parameters _parameters;
};
typedef struct _struct_Powder_process _class_Powder_process;
_class_Powder_process _Al_Powder_var;
#pragma acc declare create ( _Al_Powder_var )

/* component Al=Union_make_material() [24] DECLARE */
/* Parameter definition for component type 'Union_make_material' */
struct _struct_Union_make_material_parameters {
  /* Component type 'Union_make_material' setting parameters */
  char process_string[16384];
  MCNUM my_absorption;
  MCNUM absorber;
  char init[16384];
  /* Component type 'Union_make_material' private parameters */
  struct global_material_element_struct  global_material_element;
  struct physics_struct  this_material;
  int  loop_index;
  int  found_process;
  int  specified_processes;
  char  local_string[256];
  struct pointer_to_1d_int_list  accepted_processes;
}; /* _struct_Union_make_material_parameters */
typedef struct _struct_Union_make_material_parameters _class_Union_make_material_parameters;

/* Parameters for component type 'Union_make_material' */
struct _struct_Union_make_material {
  char     _name[256]; /* e.g. Al */
  char     _type[256]; /* Union_make_material */
  long     _index; /* e.g. 2 index in TRACE list */
  Coords   _position_absolute;
  Coords   _position_relative; /* wrt PREVIOUS */
  Rotation _rotation_absolute;
  Rotation _rotation_relative; /* wrt PREVIOUS */
  int      _rotation_is_identity;
  int      _position_relative_is_zero;
  _class_Union_make_material_parameters _parameters;
};
typedef struct _struct_Union_make_material _class_Union_make_material;
_class_Union_make_material _Al_var;
#pragma acc declare create ( _Al_var )

_class_Incoherent_process _Vanadium_incoherent_Cu_var;
#pragma acc declare create ( _Vanadium_incoherent_Cu_var )

_class_Incoherent_process _Vanadium_incoherent_MnF2_var;
#pragma acc declare create ( _Vanadium_incoherent_MnF2_var )

/* component MnF2_critical=Single_crystal_process() [27] DECLARE */
/* Parameter definition for component type 'Single_crystal_process' */
struct _struct_Single_crystal_process_parameters {
  /* Component type 'Single_crystal_process' setting parameters */
  char reflections[16384];
  MCNUM delta_d_d;
  MCNUM mosaic;
  MCNUM mosaic_a;
  MCNUM mosaic_b;
  MCNUM mosaic_c;
  MCNUM mosaic_AB[8];
  MCNUM recip_cell;
  MCNUM barns;
  MCNUM ax;
  MCNUM ay;
  MCNUM az;
  MCNUM bx;
  MCNUM by;
  MCNUM bz;
  MCNUM cx;
  MCNUM cy;
  MCNUM cz;
  MCNUM aa;
  MCNUM bb;
  MCNUM cc;
  MCNUM order;
  MCNUM RX;
  MCNUM RY;
  MCNUM RZ;
  MCNUM powder;
  MCNUM PG;
  MCNUM interact_fraction;
  MCNUM packing_factor;
  char init[16384];
  /* Component type 'Single_crystal_process' private parameters */
  struct Single_crystal_physics_storage_struct  Single_crystal_storage;
  struct hkl_info_struct_union  hkl_info_union;
  struct global_process_element_struct  global_process_element;
  struct scattering_process_struct  This_process;
}; /* _struct_Single_crystal_process_parameters */
typedef struct _struct_Single_crystal_process_parameters _class_Single_crystal_process_parameters;

/* Parameters for component type 'Single_crystal_process' */
struct _struct_Single_crystal_process {
  char     _name[256]; /* e.g. MnF2_critical */
  char     _type[256]; /* Single_crystal_process */
  long     _index; /* e.g. 2 index in TRACE list */
  Coords   _position_absolute;
  Coords   _position_relative; /* wrt PREVIOUS */
  Rotation _rotation_absolute;
  Rotation _rotation_relative; /* wrt PREVIOUS */
  int      _rotation_is_identity;
  int      _position_relative_is_zero;
  _class_Single_crystal_process_parameters _parameters;
};
typedef struct _struct_Single_crystal_process _class_Single_crystal_process;
_class_Single_crystal_process _MnF2_critical_var;
#pragma acc declare create ( _MnF2_critical_var )

_class_Single_crystal_process _MnF2_single_crystal_var;
#pragma acc declare create ( _MnF2_single_crystal_var )

_class_Single_crystal_process _MnF2_single_crystal_magn_var;
#pragma acc declare create ( _MnF2_single_crystal_magn_var )

_class_Union_make_material _MnF2_var;
#pragma acc declare create ( _MnF2_var )

/* component union_sample=Union_cylinder() [31] DECLARE */
/* Parameter definition for component type 'Union_cylinder' */
struct _struct_Union_cylinder_parameters {
  /* Component type 'Union_cylinder' setting parameters */
  char material_string[16384];
  MCNUM priority;
  MCNUM radius;
  MCNUM yheight;
  MCNUM visualize;
  long target_index;
  MCNUM target_x;
  MCNUM target_y;
  MCNUM target_z;
  MCNUM focus_aw;
  MCNUM focus_ah;
  MCNUM focus_xw;
  MCNUM focus_xh;
  MCNUM focus_r;
  MCNUM p_interact;
  char mask_string[16384];
  char mask_setting[16384];
  MCNUM number_of_activations;
  char init[16384];
  /* Component type 'Union_cylinder' private parameters */
  struct global_geometry_element_struct  global_geometry_element;
  int  loop_index;
  int  loop_2_index;
  int  material_index;
  struct Volume_struct  this_cylinder_volume;
  struct cylinder_storage  this_cylinder_storage;
}; /* _struct_Union_cylinder_parameters */
typedef struct _struct_Union_cylinder_parameters _class_Union_cylinder_parameters;

/* Parameters for component type 'Union_cylinder' */
struct _struct_Union_cylinder {
  char     _name[256]; /* e.g. union_sample */
  char     _type[256]; /* Union_cylinder */
  long     _index; /* e.g. 2 index in TRACE list */
  Coords   _position_absolute;
  Coords   _position_relative; /* wrt PREVIOUS */
  Rotation _rotation_absolute;
  Rotation _rotation_relative; /* wrt PREVIOUS */
  int      _rotation_is_identity;
  int      _position_relative_is_zero;
  _class_Union_cylinder_parameters _parameters;
};
typedef struct _struct_Union_cylinder _class_Union_cylinder;
_class_Union_cylinder _union_sample_var;
#pragma acc declare create ( _union_sample_var )

_class_Union_cylinder _union_stick_var;
#pragma acc declare create ( _union_stick_var )

_class_Union_cylinder _cryostat_wall_var;
#pragma acc declare create ( _cryostat_wall_var )

_class_Union_cylinder _cryostat_vacuum_var;
#pragma acc declare create ( _cryostat_vacuum_var )

/* component Master=Union_master() [35] DECLARE */
/* Parameter definition for component type 'Union_master' */
struct _struct_Union_master_parameters {
  /* Component type 'Union_master' setting parameters */
  MCNUM verbal;
  MCNUM list_verbal;
  MCNUM finally_verbal;
  MCNUM allow_inside_start;
  MCNUM enable_tagging;
  MCNUM history_limit;
  MCNUM enable_conditionals;
  MCNUM inherit_number_of_scattering_events;
  char init[16384];
  /* Component type 'Union_master' private parameters */
  struct global_positions_to_transform_list_struct * global_positions_to_transform_list_master;
  struct global_rotations_to_transform_list_struct * global_rotations_to_transform_list_master;
  struct pointer_to_global_process_list * global_process_list_master;
  struct pointer_to_global_material_list * global_material_list_master;
  struct pointer_to_global_geometry_list * global_geometry_list_master;
  struct pointer_to_global_logger_list * global_all_volume_logger_list_master;
  struct pointer_to_global_logger_list * global_specific_volumes_logger_list_master;
  struct pointer_to_global_abs_logger_list * global_all_volume_abs_logger_list_master;
  struct pointer_to_global_abs_logger_list * global_specific_volumes_abs_logger_list_master;
  struct global_tagging_conditional_list_struct * global_tagging_conditional_list_master;
  struct pointer_to_global_master_list * global_master_list_master;
  int  starting_volume_warning;
  struct global_master_element_struct  global_master_element;
  int  this_global_master_index;
  int  previous_master_index;
  int  geometry_list_index;
  struct intersection_time_table_struct  intersection_time_table;
  struct Volume_struct ** Volumes;
  struct geometry_struct ** Geometries;
  struct Volume_struct ** Volume_copies;
  struct starting_lists_struct  starting_lists;
  struct pointer_to_1d_int_list  Volume_copies_allocated;
  double  r[3];
  double  r_start[3];
  double  v[3];
  int  error_msg;
  int  component_error_msg;
  char  string_output[128];
  int  number_of_volumes;
  int  volume_index;
  int  process_index;
  int  iterator;
  int  solutions;
  int  max_number_of_processes;
  int  limit;
  int  solution;
  int  min_solution;
  int  min_volume;
  int  time_found;
  double  intersection_time;
  double  min_intersection_time;
  struct scattering_process_struct * process;
  struct scattering_process_struct * process_start;
  double * my_trace;
  double * p_my_trace;
  double * my_trace_fraction_control;
  double  k[3];
  double  k_new[3];
  double  k_old[3];
  double  k_rotated[3];
  double  v_length;
  double  my_sum;
  double  my_sum_plus_abs;
  double  culmative_probability;
  double  mc_prop;
  double  time_to_scattering;
  double  length_to_scattering;
  double  length_to_boundery;
  double  time_to_boundery;
  int  selected_process;
  int  scattering_event;
  double  time_propagated_without_scattering;
  int  a_next_volume_found;
  int  next_volume;
  double  next_volume_priority;
  int  done;
  int  current_volume;
  int  ray_sucseeded;
  int * number_of_solutions;
  int  number_of_solutions_static;
  int * check;
  int * start;
  int  intersection_with_children;
  int  geometry_output;
  int  tree_next_volume;
  int * pre_allocated1;
  int * pre_allocated2;
  int * pre_allocated3;
  Coords  ray_position;
  Coords  ray_velocity;
  Coords  ray_velocity_rotated;
  Coords  ray_velocity_final;
  Coords  wavevector;
  Coords  wavevector_rotated;
  int  volume_0_found;
  int * scattered_flag;
  int ** scattered_flag_VP;
  Rotation  master_transposed_rotation_matrix;
  Rotation  temp_rotation_matrix;
  Rotation  temp_transpose_rotation_matrix;
  Coords  non_rotated_position;
  Coords  rotated_position;
  int  non_isotropic_found;
  struct list_of_tagging_tree_node_pointers  master_tagging_node_list;
  struct tagging_tree_node_struct * current_tagging_node;
  int  tagging_leaf_counter;
  int  stop_tagging_ray;
  int  stop_creating_nodes;
  int  number_of_scattering_events;
  double  real_transmission_probability;
  double  mc_transmission_probability;
  int  number_of_process_interacts_set;
  int  index_of_lacking_process;
  double  total_process_interact;
  struct pointer_to_1d_int_list  geometry_component_index_list;
  struct pointer_to_1d_int_list  mask_volume_index_list;
  int  number_of_masks;
  int  number_of_masked_volumes;
  struct pointer_to_1d_int_list  mask_status_list;
  struct pointer_to_1d_int_list  current_mask_intersect_list_status;
  int  mask_index_main;
  int  mask_iterator;
  int * mask_start;
  int * mask_check;
  int  need_to_run_within_which_volume;
  int * number_of_processes_array;
  double  p_old;
  int  log_index;
  int  conditional_status;
  struct logger_struct * this_logger;
  struct abs_logger_struct * this_abs_logger;
  struct conditional_list_struct * tagging_conditional_list;
  int * logger_conditional_extend_array;
  int * abs_logger_conditional_extend_array;
  int  max_conditional_extend_index;
  int  tagging_conditional_extend;
  int  free_tagging_conditioanl_list;
  double  safty_distance;
  double  safty_distance2;
  struct focus_data_struct  temporary_focus_data;
  int  focus_data_index;
  double  r_old[3];
  double  initial_weight;
  double  abs_weight_factor;
  double  time_old;
  int  absorption_index;
  int  abs_weight_factor_set;
  double  my_abs;
  struct abs_event  absorption_event_data[1000];
  Coords  abs_position;
  Coords  transformed_abs_position;
  double  t_abs_propagation;
  double  abs_distance;
  double  abs_max_length;
}; /* _struct_Union_master_parameters */
typedef struct _struct_Union_master_parameters _class_Union_master_parameters;

/* Parameters for component type 'Union_master' */
struct _struct_Union_master {
  char     _name[256]; /* e.g. Master */
  char     _type[256]; /* Union_master */
  long     _index; /* e.g. 2 index in TRACE list */
  Coords   _position_absolute;
  Coords   _position_relative; /* wrt PREVIOUS */
  Rotation _rotation_absolute;
  Rotation _rotation_relative; /* wrt PREVIOUS */
  int      _rotation_is_identity;
  int      _position_relative_is_zero;
  _class_Union_master_parameters _parameters;
};
typedef struct _struct_Union_master _class_Union_master;
_class_Union_master _Master_var;
#pragma acc declare create ( _Master_var )

/* component stop=Union_stop() [36] DECLARE */
/* Parameter definition for component type 'Union_stop' */
struct _struct_Union_stop_parameters {
  char Union_stop_has_no_parameters;
}; /* _struct_Union_stop_parameters */
typedef struct _struct_Union_stop_parameters _class_Union_stop_parameters;

/* Parameters for component type 'Union_stop' */
struct _struct_Union_stop {
  char     _name[256]; /* e.g. stop */
  char     _type[256]; /* Union_stop */
  long     _index; /* e.g. 2 index in TRACE list */
  Coords   _position_absolute;
  Coords   _position_relative; /* wrt PREVIOUS */
  Rotation _rotation_absolute;
  Rotation _rotation_relative; /* wrt PREVIOUS */
  int      _rotation_is_identity;
  int      _position_relative_is_zero;
  _class_Union_stop_parameters _parameters;
};
typedef struct _struct_Union_stop _class_Union_stop;
_class_Union_stop _stop_var;
#pragma acc declare create ( _stop_var )

_class_Collimator_linear _collimator_linear_2_var;
#pragma acc declare create ( _collimator_linear_2_var )

_class_PSD_monitor _PSD_after_sampel_var;
#pragma acc declare create ( _PSD_after_sampel_var )

_class_Monochromator_flat _monochromator_flat6_var;
#pragma acc declare create ( _monochromator_flat6_var )

_class_Arm _a6_var;
#pragma acc declare create ( _a6_var )

_class_PSD_monitor _Detector_var;
#pragma acc declare create ( _Detector_var )

int mcNUMCOMP = 41;

/* User declarations from instrument definition. Can define functions. */
double detector_x;
double detector_y;
double l_cryostat ;
double A1;
double lambda_i;
double lambda_f;
double dm;
double ds;
double ki,kf;
double A5;
double q0;
double ql;
double qm;
double Q;
double DQ;
double A3;
double A4; 
double phi1;
double phi2;
double DeltaA3;
double Magn_order_factor,Tred,TN,beta,mu;
double C_Intensity,C_mosaic;

#undef compcurname
#undef compcurtype
#undef compcurindex
/* end of instrument 'template_body_simple' and components DECLARE */

/* *****************************************************************************
* instrument 'template_body_simple' and components INITIALISE
***************************************************************************** */

/* component origin=Progress_bar() SETTING, POSITION/ROTATION */
int _origin_setpos(void)
{ /* sets initial component parameters, position and rotation */
  SIG_MESSAGE("[_origin_setpos] component origin=Progress_bar() SETTING [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//misc/Progress_bar.comp:57]");
  stracpy(_origin_var._name, "origin", 16384);
  stracpy(_origin_var._type, "Progress_bar", 16384);
  _origin_var._index=1;
  if("NULL" && strlen("NULL"))
    stracpy(_origin_var._parameters.profile, "NULL" ? "NULL" : "", 16384);
  else 
  _origin_var._parameters.profile[0]='\0';
  _origin_var._parameters.percent = 10;
  _origin_var._parameters.flag_save = 0;
  _origin_var._parameters.minutes = 0;


  /* component origin=Progress_bar() AT ROTATED */
  {
    Coords tc1, tc2;
    tc1 = coords_set(0,0,0);
    tc2 = coords_set(0,0,0);
    Rotation tr1;
    rot_set_rotation(tr1,0,0,0);
    rot_set_rotation(_origin_var._rotation_absolute,
      (0.0)*DEG2RAD, (0.0)*DEG2RAD, (0.0)*DEG2RAD);
    rot_copy(_origin_var._rotation_relative, _origin_var._rotation_absolute);
    _origin_var._rotation_is_identity =  rot_test_identity(_origin_var._rotation_relative);
    _origin_var._position_absolute = coords_set(
      0, 0, 0);
    tc1 = coords_neg(_origin_var._position_absolute);
    _origin_var._position_relative = rot_apply(_origin_var._rotation_absolute, tc1);
  } /* origin=Progress_bar() AT ROTATED */
  DEBUG_COMPONENT("origin", _origin_var._position_absolute, _origin_var._rotation_absolute);
  instrument->_position_absolute[1] = _origin_var._position_absolute;
  instrument->_position_relative[1] = _origin_var._position_relative;
    _origin_var._position_relative_is_zero =  coords_test_zero(_origin_var._position_relative);
  instrument->counter_N[1]  = instrument->counter_P[1] = instrument->counter_P2[1] = 0;
  instrument->counter_AbsorbProp[1]= 0;
  return(0);
} /* _origin_setpos */

/* component source_maxwell_3=Source_Maxwell_3() SETTING, POSITION/ROTATION */
int _source_maxwell_3_setpos(void)
{ /* sets initial component parameters, position and rotation */
  SIG_MESSAGE("[_source_maxwell_3_setpos] component source_maxwell_3=Source_Maxwell_3() SETTING [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//sources/Source_Maxwell_3.comp:88]");
  stracpy(_source_maxwell_3_var._name, "source_maxwell_3", 16384);
  stracpy(_source_maxwell_3_var._type, "Source_Maxwell_3", 16384);
  _source_maxwell_3_var._index=2;
  _source_maxwell_3_var._parameters.size = 0;
  _source_maxwell_3_var._parameters.yheight = 0.12;
  _source_maxwell_3_var._parameters.xwidth = 0.03;
  _source_maxwell_3_var._parameters.Lmin = lambda_i * 0.98;
  _source_maxwell_3_var._parameters.Lmax = lambda_i * 1.02;
  _source_maxwell_3_var._parameters.dist = 1.5;
  _source_maxwell_3_var._parameters.focus_xw = 0.03;
  _source_maxwell_3_var._parameters.focus_yh = 0.12;
  _source_maxwell_3_var._parameters.T1 = 150.42;
  _source_maxwell_3_var._parameters.T2 = 38.74;
  _source_maxwell_3_var._parameters.T3 = 14.84;
  _source_maxwell_3_var._parameters.I1 = 3.67E11;
  _source_maxwell_3_var._parameters.I2 = 3.64E11;
  _source_maxwell_3_var._parameters.I3 = 0.95E11;
  _source_maxwell_3_var._parameters.target_index = + 1;
  _source_maxwell_3_var._parameters.lambda0 = 0;
  _source_maxwell_3_var._parameters.dlambda = 0;


  /* component source_maxwell_3=Source_Maxwell_3() AT ROTATED */
  {
    Coords tc1, tc2;
    tc1 = coords_set(0,0,0);
    tc2 = coords_set(0,0,0);
    Rotation tr1;
    rot_set_rotation(tr1,0,0,0);
    rot_set_rotation(tr1,
      (0.0)*DEG2RAD, (0.0)*DEG2RAD, (0.0)*DEG2RAD);
    rot_mul(tr1, _origin_var._rotation_absolute, _source_maxwell_3_var._rotation_absolute);
    rot_transpose(_origin_var._rotation_absolute, tr1);
    rot_mul(_source_maxwell_3_var._rotation_absolute, tr1, _source_maxwell_3_var._rotation_relative);
    _source_maxwell_3_var._rotation_is_identity =  rot_test_identity(_source_maxwell_3_var._rotation_relative);
    tc1 = coords_set(
      0, 0, 0);
    rot_transpose(_origin_var._rotation_absolute, tr1);
    tc2 = rot_apply(tr1, tc1);
    _source_maxwell_3_var._position_absolute = coords_add(_origin_var._position_absolute, tc2);
    tc1 = coords_sub(_origin_var._position_absolute, _source_maxwell_3_var._position_absolute);
    _source_maxwell_3_var._position_relative = rot_apply(_source_maxwell_3_var._rotation_absolute, tc1);
  } /* source_maxwell_3=Source_Maxwell_3() AT ROTATED */
  DEBUG_COMPONENT("source_maxwell_3", _source_maxwell_3_var._position_absolute, _source_maxwell_3_var._rotation_absolute);
  instrument->_position_absolute[2] = _source_maxwell_3_var._position_absolute;
  instrument->_position_relative[2] = _source_maxwell_3_var._position_relative;
    _source_maxwell_3_var._position_relative_is_zero =  coords_test_zero(_source_maxwell_3_var._position_relative);
  instrument->counter_N[2]  = instrument->counter_P[2] = instrument->counter_P2[2] = 0;
  instrument->counter_AbsorbProp[2]= 0;
  return(0);
} /* _source_maxwell_3_setpos */

/* component guide=Guide() SETTING, POSITION/ROTATION */
int _guide_setpos(void)
{ /* sets initial component parameters, position and rotation */
  SIG_MESSAGE("[_guide_setpos] component guide=Guide() SETTING [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//optics/Guide.comp:74]");
  stracpy(_guide_var._name, "guide", 16384);
  stracpy(_guide_var._type, "Guide", 16384);
  _guide_var._index=3;
  _guide_var._parameters.reflect[0]='\0';
  _guide_var._parameters.w1 = 0.03;
  _guide_var._parameters.h1 = 0.12;
  _guide_var._parameters.w2 = 0.03;
  _guide_var._parameters.h2 = 0.12;
  _guide_var._parameters.l = 24.5;
  _guide_var._parameters.R0 = 0.99;
  _guide_var._parameters.Qc = 0.0219;
  _guide_var._parameters.alpha = 6.07;
  _guide_var._parameters.m = 2;
  _guide_var._parameters.W = 0.003;


  /* component guide=Guide() AT ROTATED */
  {
    Coords tc1, tc2;
    tc1 = coords_set(0,0,0);
    tc2 = coords_set(0,0,0);
    Rotation tr1;
    rot_set_rotation(tr1,0,0,0);
    rot_set_rotation(tr1,
      (0.0)*DEG2RAD, (0.0)*DEG2RAD, (0.0)*DEG2RAD);
    rot_mul(tr1, _origin_var._rotation_absolute, _guide_var._rotation_absolute);
    rot_transpose(_source_maxwell_3_var._rotation_absolute, tr1);
    rot_mul(_guide_var._rotation_absolute, tr1, _guide_var._rotation_relative);
    _guide_var._rotation_is_identity =  rot_test_identity(_guide_var._rotation_relative);
    tc1 = coords_set(
      0, 0, 1.5);
    rot_transpose(_origin_var._rotation_absolute, tr1);
    tc2 = rot_apply(tr1, tc1);
    _guide_var._position_absolute = coords_add(_origin_var._position_absolute, tc2);
    tc1 = coords_sub(_source_maxwell_3_var._position_absolute, _guide_var._position_absolute);
    _guide_var._position_relative = rot_apply(_guide_var._rotation_absolute, tc1);
  } /* guide=Guide() AT ROTATED */
  DEBUG_COMPONENT("guide", _guide_var._position_absolute, _guide_var._rotation_absolute);
  instrument->_position_absolute[3] = _guide_var._position_absolute;
  instrument->_position_relative[3] = _guide_var._position_relative;
    _guide_var._position_relative_is_zero =  coords_test_zero(_guide_var._position_relative);
  instrument->counter_N[3]  = instrument->counter_P[3] = instrument->counter_P2[3] = 0;
  instrument->counter_AbsorbProp[3]= 0;
  return(0);
} /* _guide_setpos */

/* component PSD_before_monochromator=PSD_monitor() SETTING, POSITION/ROTATION */
int _PSD_before_monochromator_setpos(void)
{ /* sets initial component parameters, position and rotation */
  SIG_MESSAGE("[_PSD_before_monochromator_setpos] component PSD_before_monochromator=PSD_monitor() SETTING [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//monitors/PSD_monitor.comp:62]");
  stracpy(_PSD_before_monochromator_var._name, "PSD_before_monochromator", 16384);
  stracpy(_PSD_before_monochromator_var._type, "PSD_monitor", 16384);
  _PSD_before_monochromator_var._index=4;
  _PSD_before_monochromator_var._parameters.nx = 90;
  _PSD_before_monochromator_var._parameters.ny = 90;
  if("PSD_before_monochromator" && strlen("PSD_before_monochromator"))
    stracpy(_PSD_before_monochromator_var._parameters.filename, "PSD_before_monochromator" ? "PSD_before_monochromator" : "", 16384);
  else 
  _PSD_before_monochromator_var._parameters.filename[0]='\0';
  _PSD_before_monochromator_var._parameters.xmin = -0.05;
  _PSD_before_monochromator_var._parameters.xmax = 0.05;
  _PSD_before_monochromator_var._parameters.ymin = -0.05;
  _PSD_before_monochromator_var._parameters.ymax = 0.05;
  _PSD_before_monochromator_var._parameters.xwidth = 0.05;
  _PSD_before_monochromator_var._parameters.yheight = 0.15;
  _PSD_before_monochromator_var._parameters.restore_neutron = 1;
  _PSD_before_monochromator_var._parameters.nowritefile = 0;


  /* component PSD_before_monochromator=PSD_monitor() AT ROTATED */
  {
    Coords tc1, tc2;
    tc1 = coords_set(0,0,0);
    tc2 = coords_set(0,0,0);
    Rotation tr1;
    rot_set_rotation(tr1,0,0,0);
    rot_set_rotation(tr1,
      (0.0)*DEG2RAD, (0.0)*DEG2RAD, (0.0)*DEG2RAD);
    rot_mul(tr1, _origin_var._rotation_absolute, _PSD_before_monochromator_var._rotation_absolute);
    rot_transpose(_guide_var._rotation_absolute, tr1);
    rot_mul(_PSD_before_monochromator_var._rotation_absolute, tr1, _PSD_before_monochromator_var._rotation_relative);
    _PSD_before_monochromator_var._rotation_is_identity =  rot_test_identity(_PSD_before_monochromator_var._rotation_relative);
    tc1 = coords_set(
      0, 0, 26.1);
    rot_transpose(_origin_var._rotation_absolute, tr1);
    tc2 = rot_apply(tr1, tc1);
    _PSD_before_monochromator_var._position_absolute = coords_add(_origin_var._position_absolute, tc2);
    tc1 = coords_sub(_guide_var._position_absolute, _PSD_before_monochromator_var._position_absolute);
    _PSD_before_monochromator_var._position_relative = rot_apply(_PSD_before_monochromator_var._rotation_absolute, tc1);
  } /* PSD_before_monochromator=PSD_monitor() AT ROTATED */
  DEBUG_COMPONENT("PSD_before_monochromator", _PSD_before_monochromator_var._position_absolute, _PSD_before_monochromator_var._rotation_absolute);
  instrument->_position_absolute[4] = _PSD_before_monochromator_var._position_absolute;
  instrument->_position_relative[4] = _PSD_before_monochromator_var._position_relative;
    _PSD_before_monochromator_var._position_relative_is_zero =  coords_test_zero(_PSD_before_monochromator_var._position_relative);
  instrument->counter_N[4]  = instrument->counter_P[4] = instrument->counter_P2[4] = 0;
  instrument->counter_AbsorbProp[4]= 0;
  return(0);
} /* _PSD_before_monochromator_setpos */

/* component a1=Arm() SETTING, POSITION/ROTATION */
int _a1_setpos(void)
{ /* sets initial component parameters, position and rotation */
  SIG_MESSAGE("[_a1_setpos] component a1=Arm() SETTING [Arm:0]");
  stracpy(_a1_var._name, "a1", 16384);
  stracpy(_a1_var._type, "Arm", 16384);
  _a1_var._index=5;
  /* component a1=Arm() AT ROTATED */
  {
    Coords tc1, tc2;
    tc1 = coords_set(0,0,0);
    tc2 = coords_set(0,0,0);
    Rotation tr1;
    rot_set_rotation(tr1,0,0,0);
    rot_set_rotation(tr1,
      (0.0)*DEG2RAD, (0.0)*DEG2RAD, (0.0)*DEG2RAD);
    rot_mul(tr1, _origin_var._rotation_absolute, _a1_var._rotation_absolute);
    rot_transpose(_PSD_before_monochromator_var._rotation_absolute, tr1);
    rot_mul(_a1_var._rotation_absolute, tr1, _a1_var._rotation_relative);
    _a1_var._rotation_is_identity =  rot_test_identity(_a1_var._rotation_relative);
    tc1 = coords_set(
      0, 0, 26.4);
    rot_transpose(_origin_var._rotation_absolute, tr1);
    tc2 = rot_apply(tr1, tc1);
    _a1_var._position_absolute = coords_add(_origin_var._position_absolute, tc2);
    tc1 = coords_sub(_PSD_before_monochromator_var._position_absolute, _a1_var._position_absolute);
    _a1_var._position_relative = rot_apply(_a1_var._rotation_absolute, tc1);
  } /* a1=Arm() AT ROTATED */
  DEBUG_COMPONENT("a1", _a1_var._position_absolute, _a1_var._rotation_absolute);
  instrument->_position_absolute[5] = _a1_var._position_absolute;
  instrument->_position_relative[5] = _a1_var._position_relative;
    _a1_var._position_relative_is_zero =  coords_test_zero(_a1_var._position_relative);
  instrument->counter_N[5]  = instrument->counter_P[5] = instrument->counter_P2[5] = 0;
  instrument->counter_AbsorbProp[5]= 0;
  return(0);
} /* _a1_setpos */

/* component monochromator_flat1=Monochromator_flat() SETTING, POSITION/ROTATION */
int _monochromator_flat1_setpos(void)
{ /* sets initial component parameters, position and rotation */
  SIG_MESSAGE("[_monochromator_flat1_setpos] component monochromator_flat1=Monochromator_flat() SETTING [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//optics/Monochromator_flat.comp:103]");
  stracpy(_monochromator_flat1_var._name, "monochromator_flat1", 16384);
  stracpy(_monochromator_flat1_var._type, "Monochromator_flat", 16384);
  _monochromator_flat1_var._index=6;
  _monochromator_flat1_var._parameters.zmin = -0.05;
  _monochromator_flat1_var._parameters.zmax = 0.05;
  _monochromator_flat1_var._parameters.ymin = -0.05;
  _monochromator_flat1_var._parameters.ymax = 0.05;
  _monochromator_flat1_var._parameters.zwidth = 0.15;
  _monochromator_flat1_var._parameters.yheight = 0.025;
  _monochromator_flat1_var._parameters.mosaich = 37;
  _monochromator_flat1_var._parameters.mosaicv = 37;
  _monochromator_flat1_var._parameters.r0 = 0.7;
  _monochromator_flat1_var._parameters.Q = 1.87325;
  _monochromator_flat1_var._parameters.DM = 3.35416;


  /* component monochromator_flat1=Monochromator_flat() AT ROTATED */
  {
    Coords tc1, tc2;
    tc1 = coords_set(0,0,0);
    tc2 = coords_set(0,0,0);
    Rotation tr1;
    rot_set_rotation(tr1,0,0,0);
    rot_set_rotation(tr1,
      (0)*DEG2RAD, (A1)*DEG2RAD, (_instrument_var._parameters.mono_focus * phi2)*DEG2RAD);
    rot_mul(tr1, _a1_var._rotation_absolute, _monochromator_flat1_var._rotation_absolute);
    rot_transpose(_PSD_before_monochromator_var._rotation_absolute, tr1);
    rot_mul(_monochromator_flat1_var._rotation_absolute, tr1, _monochromator_flat1_var._rotation_relative);
    _monochromator_flat1_var._rotation_is_identity =  rot_test_identity(_monochromator_flat1_var._rotation_relative);
    tc1 = coords_set(
      0, -0.052, 0);
    rot_transpose(_a1_var._rotation_absolute, tr1);
    tc2 = rot_apply(tr1, tc1);
    _monochromator_flat1_var._position_absolute = coords_add(_a1_var._position_absolute, tc2);
    tc1 = coords_sub(_PSD_before_monochromator_var._position_absolute, _monochromator_flat1_var._position_absolute);
    _monochromator_flat1_var._position_relative = rot_apply(_monochromator_flat1_var._rotation_absolute, tc1);
  } /* monochromator_flat1=Monochromator_flat() AT ROTATED */
  DEBUG_COMPONENT("monochromator_flat1", _monochromator_flat1_var._position_absolute, _monochromator_flat1_var._rotation_absolute);
  instrument->_position_absolute[6] = _monochromator_flat1_var._position_absolute;
  instrument->_position_relative[6] = _monochromator_flat1_var._position_relative;
    _monochromator_flat1_var._position_relative_is_zero =  coords_test_zero(_monochromator_flat1_var._position_relative);
  instrument->counter_N[6]  = instrument->counter_P[6] = instrument->counter_P2[6] = 0;
  instrument->counter_AbsorbProp[6]= 0;
  return(0);
} /* _monochromator_flat1_setpos */

/* component monochromator_flat2=Monochromator_flat() SETTING, POSITION/ROTATION */
int _monochromator_flat2_setpos(void)
{ /* sets initial component parameters, position and rotation */
  SIG_MESSAGE("[_monochromator_flat2_setpos] component monochromator_flat2=Monochromator_flat() SETTING [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//optics/Monochromator_flat.comp:103]");
  stracpy(_monochromator_flat2_var._name, "monochromator_flat2", 16384);
  stracpy(_monochromator_flat2_var._type, "Monochromator_flat", 16384);
  _monochromator_flat2_var._index=7;
  _monochromator_flat2_var._parameters.zmin = -0.05;
  _monochromator_flat2_var._parameters.zmax = 0.05;
  _monochromator_flat2_var._parameters.ymin = -0.05;
  _monochromator_flat2_var._parameters.ymax = 0.05;
  _monochromator_flat2_var._parameters.zwidth = 0.15;
  _monochromator_flat2_var._parameters.yheight = 0.025;
  _monochromator_flat2_var._parameters.mosaich = 37;
  _monochromator_flat2_var._parameters.mosaicv = 37;
  _monochromator_flat2_var._parameters.r0 = 0.7;
  _monochromator_flat2_var._parameters.Q = 1.87325;
  _monochromator_flat2_var._parameters.DM = 3.35416;


  /* component monochromator_flat2=Monochromator_flat() AT ROTATED */
  {
    Coords tc1, tc2;
    tc1 = coords_set(0,0,0);
    tc2 = coords_set(0,0,0);
    Rotation tr1;
    rot_set_rotation(tr1,0,0,0);
    rot_set_rotation(tr1,
      (0)*DEG2RAD, (A1)*DEG2RAD, (_instrument_var._parameters.mono_focus * phi1)*DEG2RAD);
    rot_mul(tr1, _a1_var._rotation_absolute, _monochromator_flat2_var._rotation_absolute);
    rot_transpose(_monochromator_flat1_var._rotation_absolute, tr1);
    rot_mul(_monochromator_flat2_var._rotation_absolute, tr1, _monochromator_flat2_var._rotation_relative);
    _monochromator_flat2_var._rotation_is_identity =  rot_test_identity(_monochromator_flat2_var._rotation_relative);
    tc1 = coords_set(
      0, 0.026, 0);
    rot_transpose(_monochromator_flat1_var._rotation_absolute, tr1);
    tc2 = rot_apply(tr1, tc1);
    _monochromator_flat2_var._position_absolute = coords_add(_monochromator_flat1_var._position_absolute, tc2);
    tc1 = coords_sub(_monochromator_flat1_var._position_absolute, _monochromator_flat2_var._position_absolute);
    _monochromator_flat2_var._position_relative = rot_apply(_monochromator_flat2_var._rotation_absolute, tc1);
  } /* monochromator_flat2=Monochromator_flat() AT ROTATED */
  DEBUG_COMPONENT("monochromator_flat2", _monochromator_flat2_var._position_absolute, _monochromator_flat2_var._rotation_absolute);
  instrument->_position_absolute[7] = _monochromator_flat2_var._position_absolute;
  instrument->_position_relative[7] = _monochromator_flat2_var._position_relative;
    _monochromator_flat2_var._position_relative_is_zero =  coords_test_zero(_monochromator_flat2_var._position_relative);
  instrument->counter_N[7]  = instrument->counter_P[7] = instrument->counter_P2[7] = 0;
  instrument->counter_AbsorbProp[7]= 0;
  return(0);
} /* _monochromator_flat2_setpos */

/* component monochromator_flat3=Monochromator_flat() SETTING, POSITION/ROTATION */
int _monochromator_flat3_setpos(void)
{ /* sets initial component parameters, position and rotation */
  SIG_MESSAGE("[_monochromator_flat3_setpos] component monochromator_flat3=Monochromator_flat() SETTING [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//optics/Monochromator_flat.comp:103]");
  stracpy(_monochromator_flat3_var._name, "monochromator_flat3", 16384);
  stracpy(_monochromator_flat3_var._type, "Monochromator_flat", 16384);
  _monochromator_flat3_var._index=8;
  _monochromator_flat3_var._parameters.zmin = -0.05;
  _monochromator_flat3_var._parameters.zmax = 0.05;
  _monochromator_flat3_var._parameters.ymin = -0.05;
  _monochromator_flat3_var._parameters.ymax = 0.05;
  _monochromator_flat3_var._parameters.zwidth = 0.15;
  _monochromator_flat3_var._parameters.yheight = 0.025;
  _monochromator_flat3_var._parameters.mosaich = 37;
  _monochromator_flat3_var._parameters.mosaicv = 37;
  _monochromator_flat3_var._parameters.r0 = 0.7;
  _monochromator_flat3_var._parameters.Q = 1.87325;
  _monochromator_flat3_var._parameters.DM = 3.35416;


  /* component monochromator_flat3=Monochromator_flat() AT ROTATED */
  {
    Coords tc1, tc2;
    tc1 = coords_set(0,0,0);
    tc2 = coords_set(0,0,0);
    Rotation tr1;
    rot_set_rotation(tr1,0,0,0);
    rot_set_rotation(tr1,
      (0)*DEG2RAD, (A1)*DEG2RAD, (0)*DEG2RAD);
    rot_mul(tr1, _a1_var._rotation_absolute, _monochromator_flat3_var._rotation_absolute);
    rot_transpose(_monochromator_flat2_var._rotation_absolute, tr1);
    rot_mul(_monochromator_flat3_var._rotation_absolute, tr1, _monochromator_flat3_var._rotation_relative);
    _monochromator_flat3_var._rotation_is_identity =  rot_test_identity(_monochromator_flat3_var._rotation_relative);
    tc1 = coords_set(
      0, 0.026, 0);
    rot_transpose(_monochromator_flat2_var._rotation_absolute, tr1);
    tc2 = rot_apply(tr1, tc1);
    _monochromator_flat3_var._position_absolute = coords_add(_monochromator_flat2_var._position_absolute, tc2);
    tc1 = coords_sub(_monochromator_flat2_var._position_absolute, _monochromator_flat3_var._position_absolute);
    _monochromator_flat3_var._position_relative = rot_apply(_monochromator_flat3_var._rotation_absolute, tc1);
  } /* monochromator_flat3=Monochromator_flat() AT ROTATED */
  DEBUG_COMPONENT("monochromator_flat3", _monochromator_flat3_var._position_absolute, _monochromator_flat3_var._rotation_absolute);
  instrument->_position_absolute[8] = _monochromator_flat3_var._position_absolute;
  instrument->_position_relative[8] = _monochromator_flat3_var._position_relative;
    _monochromator_flat3_var._position_relative_is_zero =  coords_test_zero(_monochromator_flat3_var._position_relative);
  instrument->counter_N[8]  = instrument->counter_P[8] = instrument->counter_P2[8] = 0;
  instrument->counter_AbsorbProp[8]= 0;
  return(0);
} /* _monochromator_flat3_setpos */

/* component monochromator_flat4=Monochromator_flat() SETTING, POSITION/ROTATION */
int _monochromator_flat4_setpos(void)
{ /* sets initial component parameters, position and rotation */
  SIG_MESSAGE("[_monochromator_flat4_setpos] component monochromator_flat4=Monochromator_flat() SETTING [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//optics/Monochromator_flat.comp:103]");
  stracpy(_monochromator_flat4_var._name, "monochromator_flat4", 16384);
  stracpy(_monochromator_flat4_var._type, "Monochromator_flat", 16384);
  _monochromator_flat4_var._index=9;
  _monochromator_flat4_var._parameters.zmin = -0.05;
  _monochromator_flat4_var._parameters.zmax = 0.05;
  _monochromator_flat4_var._parameters.ymin = -0.05;
  _monochromator_flat4_var._parameters.ymax = 0.05;
  _monochromator_flat4_var._parameters.zwidth = 0.15;
  _monochromator_flat4_var._parameters.yheight = 0.025;
  _monochromator_flat4_var._parameters.mosaich = 37;
  _monochromator_flat4_var._parameters.mosaicv = 37;
  _monochromator_flat4_var._parameters.r0 = 0.7;
  _monochromator_flat4_var._parameters.Q = 1.87325;
  _monochromator_flat4_var._parameters.DM = 3.35416;


  /* component monochromator_flat4=Monochromator_flat() AT ROTATED */
  {
    Coords tc1, tc2;
    tc1 = coords_set(0,0,0);
    tc2 = coords_set(0,0,0);
    Rotation tr1;
    rot_set_rotation(tr1,0,0,0);
    rot_set_rotation(tr1,
      (0)*DEG2RAD, (A1)*DEG2RAD, (- _instrument_var._parameters.mono_focus * phi1)*DEG2RAD);
    rot_mul(tr1, _a1_var._rotation_absolute, _monochromator_flat4_var._rotation_absolute);
    rot_transpose(_monochromator_flat3_var._rotation_absolute, tr1);
    rot_mul(_monochromator_flat4_var._rotation_absolute, tr1, _monochromator_flat4_var._rotation_relative);
    _monochromator_flat4_var._rotation_is_identity =  rot_test_identity(_monochromator_flat4_var._rotation_relative);
    tc1 = coords_set(
      0, 0.026, 0);
    rot_transpose(_monochromator_flat3_var._rotation_absolute, tr1);
    tc2 = rot_apply(tr1, tc1);
    _monochromator_flat4_var._position_absolute = coords_add(_monochromator_flat3_var._position_absolute, tc2);
    tc1 = coords_sub(_monochromator_flat3_var._position_absolute, _monochromator_flat4_var._position_absolute);
    _monochromator_flat4_var._position_relative = rot_apply(_monochromator_flat4_var._rotation_absolute, tc1);
  } /* monochromator_flat4=Monochromator_flat() AT ROTATED */
  DEBUG_COMPONENT("monochromator_flat4", _monochromator_flat4_var._position_absolute, _monochromator_flat4_var._rotation_absolute);
  instrument->_position_absolute[9] = _monochromator_flat4_var._position_absolute;
  instrument->_position_relative[9] = _monochromator_flat4_var._position_relative;
    _monochromator_flat4_var._position_relative_is_zero =  coords_test_zero(_monochromator_flat4_var._position_relative);
  instrument->counter_N[9]  = instrument->counter_P[9] = instrument->counter_P2[9] = 0;
  instrument->counter_AbsorbProp[9]= 0;
  return(0);
} /* _monochromator_flat4_setpos */

/* component monochromator_flat5=Monochromator_flat() SETTING, POSITION/ROTATION */
int _monochromator_flat5_setpos(void)
{ /* sets initial component parameters, position and rotation */
  SIG_MESSAGE("[_monochromator_flat5_setpos] component monochromator_flat5=Monochromator_flat() SETTING [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//optics/Monochromator_flat.comp:103]");
  stracpy(_monochromator_flat5_var._name, "monochromator_flat5", 16384);
  stracpy(_monochromator_flat5_var._type, "Monochromator_flat", 16384);
  _monochromator_flat5_var._index=10;
  _monochromator_flat5_var._parameters.zmin = -0.05;
  _monochromator_flat5_var._parameters.zmax = 0.05;
  _monochromator_flat5_var._parameters.ymin = -0.05;
  _monochromator_flat5_var._parameters.ymax = 0.05;
  _monochromator_flat5_var._parameters.zwidth = 0.15;
  _monochromator_flat5_var._parameters.yheight = 0.025;
  _monochromator_flat5_var._parameters.mosaich = 37;
  _monochromator_flat5_var._parameters.mosaicv = 37;
  _monochromator_flat5_var._parameters.r0 = 0.7;
  _monochromator_flat5_var._parameters.Q = 1.87325;
  _monochromator_flat5_var._parameters.DM = 3.35416;


  /* component monochromator_flat5=Monochromator_flat() AT ROTATED */
  {
    Coords tc1, tc2;
    tc1 = coords_set(0,0,0);
    tc2 = coords_set(0,0,0);
    Rotation tr1;
    rot_set_rotation(tr1,0,0,0);
    rot_set_rotation(tr1,
      (0)*DEG2RAD, (A1)*DEG2RAD, (- _instrument_var._parameters.mono_focus * phi2)*DEG2RAD);
    rot_mul(tr1, _a1_var._rotation_absolute, _monochromator_flat5_var._rotation_absolute);
    rot_transpose(_monochromator_flat4_var._rotation_absolute, tr1);
    rot_mul(_monochromator_flat5_var._rotation_absolute, tr1, _monochromator_flat5_var._rotation_relative);
    _monochromator_flat5_var._rotation_is_identity =  rot_test_identity(_monochromator_flat5_var._rotation_relative);
    tc1 = coords_set(
      0, 0.026, 0);
    rot_transpose(_monochromator_flat4_var._rotation_absolute, tr1);
    tc2 = rot_apply(tr1, tc1);
    _monochromator_flat5_var._position_absolute = coords_add(_monochromator_flat4_var._position_absolute, tc2);
    tc1 = coords_sub(_monochromator_flat4_var._position_absolute, _monochromator_flat5_var._position_absolute);
    _monochromator_flat5_var._position_relative = rot_apply(_monochromator_flat5_var._rotation_absolute, tc1);
  } /* monochromator_flat5=Monochromator_flat() AT ROTATED */
  DEBUG_COMPONENT("monochromator_flat5", _monochromator_flat5_var._position_absolute, _monochromator_flat5_var._rotation_absolute);
  instrument->_position_absolute[10] = _monochromator_flat5_var._position_absolute;
  instrument->_position_relative[10] = _monochromator_flat5_var._position_relative;
    _monochromator_flat5_var._position_relative_is_zero =  coords_test_zero(_monochromator_flat5_var._position_relative);
  instrument->counter_N[10]  = instrument->counter_P[10] = instrument->counter_P2[10] = 0;
  instrument->counter_AbsorbProp[10]= 0;
  return(0);
} /* _monochromator_flat5_setpos */

/* component a2=Arm() SETTING, POSITION/ROTATION */
int _a2_setpos(void)
{ /* sets initial component parameters, position and rotation */
  SIG_MESSAGE("[_a2_setpos] component a2=Arm() SETTING [Arm:0]");
  stracpy(_a2_var._name, "a2", 16384);
  stracpy(_a2_var._type, "Arm", 16384);
  _a2_var._index=11;
  /* component a2=Arm() AT ROTATED */
  {
    Coords tc1, tc2;
    tc1 = coords_set(0,0,0);
    tc2 = coords_set(0,0,0);
    Rotation tr1;
    rot_set_rotation(tr1,0,0,0);
    rot_set_rotation(tr1,
      (0)*DEG2RAD, (2 * A1)*DEG2RAD, (0)*DEG2RAD);
    rot_mul(tr1, _a1_var._rotation_absolute, _a2_var._rotation_absolute);
    rot_transpose(_monochromator_flat5_var._rotation_absolute, tr1);
    rot_mul(_a2_var._rotation_absolute, tr1, _a2_var._rotation_relative);
    _a2_var._rotation_is_identity =  rot_test_identity(_a2_var._rotation_relative);
    tc1 = coords_set(
      0, 0, 0);
    rot_transpose(_a1_var._rotation_absolute, tr1);
    tc2 = rot_apply(tr1, tc1);
    _a2_var._position_absolute = coords_add(_a1_var._position_absolute, tc2);
    tc1 = coords_sub(_monochromator_flat5_var._position_absolute, _a2_var._position_absolute);
    _a2_var._position_relative = rot_apply(_a2_var._rotation_absolute, tc1);
  } /* a2=Arm() AT ROTATED */
  DEBUG_COMPONENT("a2", _a2_var._position_absolute, _a2_var._rotation_absolute);
  instrument->_position_absolute[11] = _a2_var._position_absolute;
  instrument->_position_relative[11] = _a2_var._position_relative;
    _a2_var._position_relative_is_zero =  coords_test_zero(_a2_var._position_relative);
  instrument->counter_N[11]  = instrument->counter_P[11] = instrument->counter_P2[11] = 0;
  instrument->counter_AbsorbProp[11]= 0;
  return(0);
} /* _a2_setpos */

/* component PSD_after_monochromator=PSD_monitor() SETTING, POSITION/ROTATION */
int _PSD_after_monochromator_setpos(void)
{ /* sets initial component parameters, position and rotation */
  SIG_MESSAGE("[_PSD_after_monochromator_setpos] component PSD_after_monochromator=PSD_monitor() SETTING [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//monitors/PSD_monitor.comp:62]");
  stracpy(_PSD_after_monochromator_var._name, "PSD_after_monochromator", 16384);
  stracpy(_PSD_after_monochromator_var._type, "PSD_monitor", 16384);
  _PSD_after_monochromator_var._index=12;
  _PSD_after_monochromator_var._parameters.nx = 90;
  _PSD_after_monochromator_var._parameters.ny = 90;
  if("PSD_after_monochromator" && strlen("PSD_after_monochromator"))
    stracpy(_PSD_after_monochromator_var._parameters.filename, "PSD_after_monochromator" ? "PSD_after_monochromator" : "", 16384);
  else 
  _PSD_after_monochromator_var._parameters.filename[0]='\0';
  _PSD_after_monochromator_var._parameters.xmin = -0.05;
  _PSD_after_monochromator_var._parameters.xmax = 0.05;
  _PSD_after_monochromator_var._parameters.ymin = -0.05;
  _PSD_after_monochromator_var._parameters.ymax = 0.05;
  _PSD_after_monochromator_var._parameters.xwidth = 0.1;
  _PSD_after_monochromator_var._parameters.yheight = 0.1;
  _PSD_after_monochromator_var._parameters.restore_neutron = 1;
  _PSD_after_monochromator_var._parameters.nowritefile = 0;


  /* component PSD_after_monochromator=PSD_monitor() AT ROTATED */
  {
    Coords tc1, tc2;
    tc1 = coords_set(0,0,0);
    tc2 = coords_set(0,0,0);
    Rotation tr1;
    rot_set_rotation(tr1,0,0,0);
    rot_set_rotation(tr1,
      (0.0)*DEG2RAD, (0.0)*DEG2RAD, (0.0)*DEG2RAD);
    rot_mul(tr1, _a2_var._rotation_absolute, _PSD_after_monochromator_var._rotation_absolute);
    rot_transpose(_monochromator_flat5_var._rotation_absolute, tr1);
    rot_mul(_PSD_after_monochromator_var._rotation_absolute, tr1, _PSD_after_monochromator_var._rotation_relative);
    _PSD_after_monochromator_var._rotation_is_identity =  rot_test_identity(_PSD_after_monochromator_var._rotation_relative);
    tc1 = coords_set(
      0, 0, 0.999);
    rot_transpose(_a2_var._rotation_absolute, tr1);
    tc2 = rot_apply(tr1, tc1);
    _PSD_after_monochromator_var._position_absolute = coords_add(_a2_var._position_absolute, tc2);
    tc1 = coords_sub(_monochromator_flat5_var._position_absolute, _PSD_after_monochromator_var._position_absolute);
    _PSD_after_monochromator_var._position_relative = rot_apply(_PSD_after_monochromator_var._rotation_absolute, tc1);
  } /* PSD_after_monochromator=PSD_monitor() AT ROTATED */
  DEBUG_COMPONENT("PSD_after_monochromator", _PSD_after_monochromator_var._position_absolute, _PSD_after_monochromator_var._rotation_absolute);
  instrument->_position_absolute[12] = _PSD_after_monochromator_var._position_absolute;
  instrument->_position_relative[12] = _PSD_after_monochromator_var._position_relative;
    _PSD_after_monochromator_var._position_relative_is_zero =  coords_test_zero(_PSD_after_monochromator_var._position_relative);
  instrument->counter_N[12]  = instrument->counter_P[12] = instrument->counter_P2[12] = 0;
  instrument->counter_AbsorbProp[12]= 0;
  return(0);
} /* _PSD_after_monochromator_setpos */

/* component collimator_linear=Collimator_linear() SETTING, POSITION/ROTATION */
int _collimator_linear_setpos(void)
{ /* sets initial component parameters, position and rotation */
  SIG_MESSAGE("[_collimator_linear_setpos] component collimator_linear=Collimator_linear() SETTING [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//optics/Collimator_linear.comp:55]");
  stracpy(_collimator_linear_var._name, "collimator_linear", 16384);
  stracpy(_collimator_linear_var._type, "Collimator_linear", 16384);
  _collimator_linear_var._index=13;
  _collimator_linear_var._parameters.xmin = -0.02;
  _collimator_linear_var._parameters.xmax = 0.02;
  _collimator_linear_var._parameters.ymin = -0.05;
  _collimator_linear_var._parameters.ymax = 0.05;
  _collimator_linear_var._parameters.xwidth = 0.08;
  _collimator_linear_var._parameters.yheight = 0.20;
  _collimator_linear_var._parameters.length = 0.20;
  _collimator_linear_var._parameters.divergence = _instrument_var._parameters.col_div_1;
  _collimator_linear_var._parameters.transmission = 1;
  _collimator_linear_var._parameters.divergenceV = 0;


  /* component collimator_linear=Collimator_linear() AT ROTATED */
  {
    Coords tc1, tc2;
    tc1 = coords_set(0,0,0);
    tc2 = coords_set(0,0,0);
    Rotation tr1;
    rot_set_rotation(tr1,0,0,0);
    rot_set_rotation(tr1,
      (0.0)*DEG2RAD, (0.0)*DEG2RAD, (0.0)*DEG2RAD);
    rot_mul(tr1, _a2_var._rotation_absolute, _collimator_linear_var._rotation_absolute);
    rot_transpose(_PSD_after_monochromator_var._rotation_absolute, tr1);
    rot_mul(_collimator_linear_var._rotation_absolute, tr1, _collimator_linear_var._rotation_relative);
    _collimator_linear_var._rotation_is_identity =  rot_test_identity(_collimator_linear_var._rotation_relative);
    tc1 = coords_set(
      0, 0, 1.0);
    rot_transpose(_a2_var._rotation_absolute, tr1);
    tc2 = rot_apply(tr1, tc1);
    _collimator_linear_var._position_absolute = coords_add(_a2_var._position_absolute, tc2);
    tc1 = coords_sub(_PSD_after_monochromator_var._position_absolute, _collimator_linear_var._position_absolute);
    _collimator_linear_var._position_relative = rot_apply(_collimator_linear_var._rotation_absolute, tc1);
  } /* collimator_linear=Collimator_linear() AT ROTATED */
  DEBUG_COMPONENT("collimator_linear", _collimator_linear_var._position_absolute, _collimator_linear_var._rotation_absolute);
  instrument->_position_absolute[13] = _collimator_linear_var._position_absolute;
  instrument->_position_relative[13] = _collimator_linear_var._position_relative;
    _collimator_linear_var._position_relative_is_zero =  coords_test_zero(_collimator_linear_var._position_relative);
  instrument->counter_N[13]  = instrument->counter_P[13] = instrument->counter_P2[13] = 0;
  instrument->counter_AbsorbProp[13]= 0;
  return(0);
} /* _collimator_linear_setpos */

/* component slit=Slit() SETTING, POSITION/ROTATION */
int _slit_setpos(void)
{ /* sets initial component parameters, position and rotation */
  SIG_MESSAGE("[_slit_setpos] component slit=Slit() SETTING [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//optics/Slit.comp:66]");
  stracpy(_slit_var._name, "slit", 16384);
  stracpy(_slit_var._type, "Slit", 16384);
  _slit_var._index=14;
  _slit_var._parameters.xmin = - _instrument_var._parameters.slit_left;
  _slit_var._parameters.xmax = _instrument_var._parameters.slit_right;
  _slit_var._parameters.ymin = - _instrument_var._parameters.slit_bottom;
  _slit_var._parameters.ymax = _instrument_var._parameters.slit_top;
  _slit_var._parameters.radius = UNSET;
  _slit_var._parameters.xwidth = UNSET;
  _slit_var._parameters.yheight = UNSET;


  /* component slit=Slit() AT ROTATED */
  {
    Coords tc1, tc2;
    tc1 = coords_set(0,0,0);
    tc2 = coords_set(0,0,0);
    Rotation tr1;
    rot_set_rotation(tr1,0,0,0);
    rot_set_rotation(tr1,
      (0.0)*DEG2RAD, (0.0)*DEG2RAD, (0.0)*DEG2RAD);
    rot_mul(tr1, _a2_var._rotation_absolute, _slit_var._rotation_absolute);
    rot_transpose(_collimator_linear_var._rotation_absolute, tr1);
    rot_mul(_slit_var._rotation_absolute, tr1, _slit_var._rotation_relative);
    _slit_var._rotation_is_identity =  rot_test_identity(_slit_var._rotation_relative);
    tc1 = coords_set(
      0, 0, 1.54 -0.05 - l_cryostat);
    rot_transpose(_a2_var._rotation_absolute, tr1);
    tc2 = rot_apply(tr1, tc1);
    _slit_var._position_absolute = coords_add(_a2_var._position_absolute, tc2);
    tc1 = coords_sub(_collimator_linear_var._position_absolute, _slit_var._position_absolute);
    _slit_var._position_relative = rot_apply(_slit_var._rotation_absolute, tc1);
  } /* slit=Slit() AT ROTATED */
  DEBUG_COMPONENT("slit", _slit_var._position_absolute, _slit_var._rotation_absolute);
  instrument->_position_absolute[14] = _slit_var._position_absolute;
  instrument->_position_relative[14] = _slit_var._position_relative;
    _slit_var._position_relative_is_zero =  coords_test_zero(_slit_var._position_relative);
  instrument->counter_N[14]  = instrument->counter_P[14] = instrument->counter_P2[14] = 0;
  instrument->counter_AbsorbProp[14]= 0;
  return(0);
} /* _slit_setpos */

/* component PSD_before_sampel=PSD_monitor() SETTING, POSITION/ROTATION */
int _PSD_before_sampel_setpos(void)
{ /* sets initial component parameters, position and rotation */
  SIG_MESSAGE("[_PSD_before_sampel_setpos] component PSD_before_sampel=PSD_monitor() SETTING [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//monitors/PSD_monitor.comp:62]");
  stracpy(_PSD_before_sampel_var._name, "PSD_before_sampel", 16384);
  stracpy(_PSD_before_sampel_var._type, "PSD_monitor", 16384);
  _PSD_before_sampel_var._index=15;
  _PSD_before_sampel_var._parameters.nx = 90;
  _PSD_before_sampel_var._parameters.ny = 90;
  if("PSD_before_sampel" && strlen("PSD_before_sampel"))
    stracpy(_PSD_before_sampel_var._parameters.filename, "PSD_before_sampel" ? "PSD_before_sampel" : "", 16384);
  else 
  _PSD_before_sampel_var._parameters.filename[0]='\0';
  _PSD_before_sampel_var._parameters.xmin = -0.05;
  _PSD_before_sampel_var._parameters.xmax = 0.05;
  _PSD_before_sampel_var._parameters.ymin = -0.05;
  _PSD_before_sampel_var._parameters.ymax = 0.05;
  _PSD_before_sampel_var._parameters.xwidth = 0.1;
  _PSD_before_sampel_var._parameters.yheight = 0.1;
  _PSD_before_sampel_var._parameters.restore_neutron = 1;
  _PSD_before_sampel_var._parameters.nowritefile = 0;


  /* component PSD_before_sampel=PSD_monitor() AT ROTATED */
  {
    Coords tc1, tc2;
    tc1 = coords_set(0,0,0);
    tc2 = coords_set(0,0,0);
    Rotation tr1;
    rot_set_rotation(tr1,0,0,0);
    rot_set_rotation(tr1,
      (0.0)*DEG2RAD, (0.0)*DEG2RAD, (0.0)*DEG2RAD);
    rot_mul(tr1, _slit_var._rotation_absolute, _PSD_before_sampel_var._rotation_absolute);
    rot_transpose(_slit_var._rotation_absolute, tr1);
    rot_mul(_PSD_before_sampel_var._rotation_absolute, tr1, _PSD_before_sampel_var._rotation_relative);
    _PSD_before_sampel_var._rotation_is_identity =  rot_test_identity(_PSD_before_sampel_var._rotation_relative);
    tc1 = coords_set(
      0, 0, 0.001);
    rot_transpose(_slit_var._rotation_absolute, tr1);
    tc2 = rot_apply(tr1, tc1);
    _PSD_before_sampel_var._position_absolute = coords_add(_slit_var._position_absolute, tc2);
    tc1 = coords_sub(_slit_var._position_absolute, _PSD_before_sampel_var._position_absolute);
    _PSD_before_sampel_var._position_relative = rot_apply(_PSD_before_sampel_var._rotation_absolute, tc1);
  } /* PSD_before_sampel=PSD_monitor() AT ROTATED */
  DEBUG_COMPONENT("PSD_before_sampel", _PSD_before_sampel_var._position_absolute, _PSD_before_sampel_var._rotation_absolute);
  instrument->_position_absolute[15] = _PSD_before_sampel_var._position_absolute;
  instrument->_position_relative[15] = _PSD_before_sampel_var._position_relative;
    _PSD_before_sampel_var._position_relative_is_zero =  coords_test_zero(_PSD_before_sampel_var._position_relative);
  instrument->counter_N[15]  = instrument->counter_P[15] = instrument->counter_P2[15] = 0;
  instrument->counter_AbsorbProp[15]= 0;
  return(0);
} /* _PSD_before_sampel_setpos */

/* component Emon_before_sampel=E_monitor() SETTING, POSITION/ROTATION */
int _Emon_before_sampel_setpos(void)
{ /* sets initial component parameters, position and rotation */
  SIG_MESSAGE("[_Emon_before_sampel_setpos] component Emon_before_sampel=E_monitor() SETTING [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//monitors/E_monitor.comp:69]");
  stracpy(_Emon_before_sampel_var._name, "Emon_before_sampel", 16384);
  stracpy(_Emon_before_sampel_var._type, "E_monitor", 16384);
  _Emon_before_sampel_var._index=16;
  _Emon_before_sampel_var._parameters.nE = 200;
  if("Emon_before_sampel" && strlen("Emon_before_sampel"))
    stracpy(_Emon_before_sampel_var._parameters.filename, "Emon_before_sampel" ? "Emon_before_sampel" : "", 16384);
  else 
  _Emon_before_sampel_var._parameters.filename[0]='\0';
  _Emon_before_sampel_var._parameters.xmin = -0.05;
  _Emon_before_sampel_var._parameters.xmax = 0.05;
  _Emon_before_sampel_var._parameters.ymin = -0.05;
  _Emon_before_sampel_var._parameters.ymax = 0.05;
  _Emon_before_sampel_var._parameters.nowritefile = 0;
  _Emon_before_sampel_var._parameters.xwidth = 0.1;
  _Emon_before_sampel_var._parameters.yheight = 0.1;
  _Emon_before_sampel_var._parameters.Emin = 4.5;
  _Emon_before_sampel_var._parameters.Emax = 5.5;
  _Emon_before_sampel_var._parameters.restore_neutron = 1;


  /* component Emon_before_sampel=E_monitor() AT ROTATED */
  {
    Coords tc1, tc2;
    tc1 = coords_set(0,0,0);
    tc2 = coords_set(0,0,0);
    Rotation tr1;
    rot_set_rotation(tr1,0,0,0);
    rot_set_rotation(tr1,
      (0.0)*DEG2RAD, (0.0)*DEG2RAD, (0.0)*DEG2RAD);
    rot_mul(tr1, _PSD_before_sampel_var._rotation_absolute, _Emon_before_sampel_var._rotation_absolute);
    rot_transpose(_PSD_before_sampel_var._rotation_absolute, tr1);
    rot_mul(_Emon_before_sampel_var._rotation_absolute, tr1, _Emon_before_sampel_var._rotation_relative);
    _Emon_before_sampel_var._rotation_is_identity =  rot_test_identity(_Emon_before_sampel_var._rotation_relative);
    tc1 = coords_set(
      0, 0, 0.001);
    rot_transpose(_PSD_before_sampel_var._rotation_absolute, tr1);
    tc2 = rot_apply(tr1, tc1);
    _Emon_before_sampel_var._position_absolute = coords_add(_PSD_before_sampel_var._position_absolute, tc2);
    tc1 = coords_sub(_PSD_before_sampel_var._position_absolute, _Emon_before_sampel_var._position_absolute);
    _Emon_before_sampel_var._position_relative = rot_apply(_Emon_before_sampel_var._rotation_absolute, tc1);
  } /* Emon_before_sampel=E_monitor() AT ROTATED */
  DEBUG_COMPONENT("Emon_before_sampel", _Emon_before_sampel_var._position_absolute, _Emon_before_sampel_var._rotation_absolute);
  instrument->_position_absolute[16] = _Emon_before_sampel_var._position_absolute;
  instrument->_position_relative[16] = _Emon_before_sampel_var._position_relative;
    _Emon_before_sampel_var._position_relative_is_zero =  coords_test_zero(_Emon_before_sampel_var._position_relative);
  instrument->counter_N[16]  = instrument->counter_P[16] = instrument->counter_P2[16] = 0;
  instrument->counter_AbsorbProp[16]= 0;
  return(0);
} /* _Emon_before_sampel_setpos */

/* component a3=Arm() SETTING, POSITION/ROTATION */
int _a3_setpos(void)
{ /* sets initial component parameters, position and rotation */
  SIG_MESSAGE("[_a3_setpos] component a3=Arm() SETTING [Arm:0]");
  stracpy(_a3_var._name, "a3", 16384);
  stracpy(_a3_var._type, "Arm", 16384);
  _a3_var._index=17;
  /* component a3=Arm() AT ROTATED */
  {
    Coords tc1, tc2;
    tc1 = coords_set(0,0,0);
    tc2 = coords_set(0,0,0);
    Rotation tr1;
    rot_set_rotation(tr1,0,0,0);
    rot_set_rotation(tr1,
      (0.0)*DEG2RAD, (0.0)*DEG2RAD, (0.0)*DEG2RAD);
    rot_mul(tr1, _a2_var._rotation_absolute, _a3_var._rotation_absolute);
    rot_transpose(_Emon_before_sampel_var._rotation_absolute, tr1);
    rot_mul(_a3_var._rotation_absolute, tr1, _a3_var._rotation_relative);
    _a3_var._rotation_is_identity =  rot_test_identity(_a3_var._rotation_relative);
    tc1 = coords_set(
      0, 0, 1.54);
    rot_transpose(_a2_var._rotation_absolute, tr1);
    tc2 = rot_apply(tr1, tc1);
    _a3_var._position_absolute = coords_add(_a2_var._position_absolute, tc2);
    tc1 = coords_sub(_Emon_before_sampel_var._position_absolute, _a3_var._position_absolute);
    _a3_var._position_relative = rot_apply(_a3_var._rotation_absolute, tc1);
  } /* a3=Arm() AT ROTATED */
  DEBUG_COMPONENT("a3", _a3_var._position_absolute, _a3_var._rotation_absolute);
  instrument->_position_absolute[17] = _a3_var._position_absolute;
  instrument->_position_relative[17] = _a3_var._position_relative;
    _a3_var._position_relative_is_zero =  coords_test_zero(_a3_var._position_relative);
  instrument->counter_N[17]  = instrument->counter_P[17] = instrument->counter_P2[17] = 0;
  instrument->counter_AbsorbProp[17]= 0;
  return(0);
} /* _a3_setpos */

/* component a4=Arm() SETTING, POSITION/ROTATION */
int _a4_setpos(void)
{ /* sets initial component parameters, position and rotation */
  SIG_MESSAGE("[_a4_setpos] component a4=Arm() SETTING [Arm:0]");
  stracpy(_a4_var._name, "a4", 16384);
  stracpy(_a4_var._type, "Arm", 16384);
  _a4_var._index=18;
  /* component a4=Arm() AT ROTATED */
  {
    Coords tc1, tc2;
    tc1 = coords_set(0,0,0);
    tc2 = coords_set(0,0,0);
    Rotation tr1;
    rot_set_rotation(tr1,0,0,0);
    rot_set_rotation(tr1,
      (0)*DEG2RAD, (A4)*DEG2RAD, (0)*DEG2RAD);
    rot_mul(tr1, _a3_var._rotation_absolute, _a4_var._rotation_absolute);
    rot_transpose(_Emon_before_sampel_var._rotation_absolute, tr1);
    rot_mul(_a4_var._rotation_absolute, tr1, _a4_var._rotation_relative);
    _a4_var._rotation_is_identity =  rot_test_identity(_a4_var._rotation_relative);
    tc1 = coords_set(
      0, 0, 0);
    rot_transpose(_a3_var._rotation_absolute, tr1);
    tc2 = rot_apply(tr1, tc1);
    _a4_var._position_absolute = coords_add(_a3_var._position_absolute, tc2);
    tc1 = coords_sub(_Emon_before_sampel_var._position_absolute, _a4_var._position_absolute);
    _a4_var._position_relative = rot_apply(_a4_var._rotation_absolute, tc1);
  } /* a4=Arm() AT ROTATED */
  DEBUG_COMPONENT("a4", _a4_var._position_absolute, _a4_var._rotation_absolute);
  instrument->_position_absolute[18] = _a4_var._position_absolute;
  instrument->_position_relative[18] = _a4_var._position_relative;
    _a4_var._position_relative_is_zero =  coords_test_zero(_a4_var._position_relative);
  instrument->counter_N[18]  = instrument->counter_P[18] = instrument->counter_P2[18] = 0;
  instrument->counter_AbsorbProp[18]= 0;
  return(0);
} /* _a4_setpos */

/* component a5=Arm() SETTING, POSITION/ROTATION */
int _a5_setpos(void)
{ /* sets initial component parameters, position and rotation */
  SIG_MESSAGE("[_a5_setpos] component a5=Arm() SETTING [Arm:0]");
  stracpy(_a5_var._name, "a5", 16384);
  stracpy(_a5_var._type, "Arm", 16384);
  _a5_var._index=19;
  /* component a5=Arm() AT ROTATED */
  {
    Coords tc1, tc2;
    tc1 = coords_set(0,0,0);
    tc2 = coords_set(0,0,0);
    Rotation tr1;
    rot_set_rotation(tr1,0,0,0);
    rot_set_rotation(tr1,
      (0.0)*DEG2RAD, (0.0)*DEG2RAD, (0.0)*DEG2RAD);
    rot_mul(tr1, _a4_var._rotation_absolute, _a5_var._rotation_absolute);
    rot_transpose(_Emon_before_sampel_var._rotation_absolute, tr1);
    rot_mul(_a5_var._rotation_absolute, tr1, _a5_var._rotation_relative);
    _a5_var._rotation_is_identity =  rot_test_identity(_a5_var._rotation_relative);
    tc1 = coords_set(
      0, 0, 1.26);
    rot_transpose(_a4_var._rotation_absolute, tr1);
    tc2 = rot_apply(tr1, tc1);
    _a5_var._position_absolute = coords_add(_a4_var._position_absolute, tc2);
    tc1 = coords_sub(_Emon_before_sampel_var._position_absolute, _a5_var._position_absolute);
    _a5_var._position_relative = rot_apply(_a5_var._rotation_absolute, tc1);
  } /* a5=Arm() AT ROTATED */
  DEBUG_COMPONENT("a5", _a5_var._position_absolute, _a5_var._rotation_absolute);
  instrument->_position_absolute[19] = _a5_var._position_absolute;
  instrument->_position_relative[19] = _a5_var._position_relative;
    _a5_var._position_relative_is_zero =  coords_test_zero(_a5_var._position_relative);
  instrument->counter_N[19]  = instrument->counter_P[19] = instrument->counter_P2[19] = 0;
  instrument->counter_AbsorbProp[19]= 0;
  return(0);
} /* _a5_setpos */

/* component sample_position=Arm() SETTING, POSITION/ROTATION */
int _sample_position_setpos(void)
{ /* sets initial component parameters, position and rotation */
  SIG_MESSAGE("[_sample_position_setpos] component sample_position=Arm() SETTING [Arm:0]");
  stracpy(_sample_position_var._name, "sample_position", 16384);
  stracpy(_sample_position_var._type, "Arm", 16384);
  _sample_position_var._index=20;
  /* component sample_position=Arm() AT ROTATED */
  {
    Coords tc1, tc2;
    tc1 = coords_set(0,0,0);
    tc2 = coords_set(0,0,0);
    Rotation tr1;
    rot_set_rotation(tr1,0,0,0);
    rot_set_rotation(tr1,
      (0)*DEG2RAD, (- A3 + DeltaA3)*DEG2RAD, (0)*DEG2RAD);
    rot_mul(tr1, _a3_var._rotation_absolute, _sample_position_var._rotation_absolute);
    rot_transpose(_Emon_before_sampel_var._rotation_absolute, tr1);
    rot_mul(_sample_position_var._rotation_absolute, tr1, _sample_position_var._rotation_relative);
    _sample_position_var._rotation_is_identity =  rot_test_identity(_sample_position_var._rotation_relative);
    tc1 = coords_set(
      0, 0, 0);
    rot_transpose(_a3_var._rotation_absolute, tr1);
    tc2 = rot_apply(tr1, tc1);
    _sample_position_var._position_absolute = coords_add(_a3_var._position_absolute, tc2);
    tc1 = coords_sub(_Emon_before_sampel_var._position_absolute, _sample_position_var._position_absolute);
    _sample_position_var._position_relative = rot_apply(_sample_position_var._rotation_absolute, tc1);
  } /* sample_position=Arm() AT ROTATED */
  DEBUG_COMPONENT("sample_position", _sample_position_var._position_absolute, _sample_position_var._rotation_absolute);
  instrument->_position_absolute[20] = _sample_position_var._position_absolute;
  instrument->_position_relative[20] = _sample_position_var._position_relative;
    _sample_position_var._position_relative_is_zero =  coords_test_zero(_sample_position_var._position_relative);
  instrument->counter_N[20]  = instrument->counter_P[20] = instrument->counter_P2[20] = 0;
  instrument->counter_AbsorbProp[20]= 0;
  return(0);
} /* _sample_position_setpos */

/* component init=Union_init() SETTING, POSITION/ROTATION */
int _init_setpos(void)
{ /* sets initial component parameters, position and rotation */
  SIG_MESSAGE("[_init_setpos] component init=Union_init() SETTING [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//union/Union_init.comp:113]");
  stracpy(_init_var._name, "init", 16384);
  stracpy(_init_var._type, "Union_init", 16384);
  _init_var._index=21;

  /* component init=Union_init() AT ROTATED */
  {
    Coords tc1, tc2;
    tc1 = coords_set(0,0,0);
    tc2 = coords_set(0,0,0);
    Rotation tr1;
    rot_set_rotation(tr1,0,0,0);
    rot_set_rotation(_init_var._rotation_absolute,
      (0.0)*DEG2RAD, (0.0)*DEG2RAD, (0.0)*DEG2RAD);
    rot_transpose(_Emon_before_sampel_var._rotation_absolute, tr1);
    rot_mul(_init_var._rotation_absolute, tr1, _init_var._rotation_relative);
    _init_var._rotation_is_identity =  rot_test_identity(_init_var._rotation_relative);
    _init_var._position_absolute = coords_set(
      0, 0, 0);
    tc1 = coords_sub(_Emon_before_sampel_var._position_absolute, _init_var._position_absolute);
    _init_var._position_relative = rot_apply(_init_var._rotation_absolute, tc1);
  } /* init=Union_init() AT ROTATED */
  DEBUG_COMPONENT("init", _init_var._position_absolute, _init_var._rotation_absolute);
  instrument->_position_absolute[21] = _init_var._position_absolute;
  instrument->_position_relative[21] = _init_var._position_relative;
    _init_var._position_relative_is_zero =  coords_test_zero(_init_var._position_relative);
  instrument->counter_N[21]  = instrument->counter_P[21] = instrument->counter_P2[21] = 0;
  instrument->counter_AbsorbProp[21]= 0;
  return(0);
} /* _init_setpos */

/* component Vanadium_incoherent_Al=Incoherent_process() SETTING, POSITION/ROTATION */
int _Vanadium_incoherent_Al_setpos(void)
{ /* sets initial component parameters, position and rotation */
  SIG_MESSAGE("[_Vanadium_incoherent_Al_setpos] component Vanadium_incoherent_Al=Incoherent_process() SETTING [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//union/Incoherent_process.comp:142]");
  stracpy(_Vanadium_incoherent_Al_var._name, "Vanadium_incoherent_Al", 16384);
  stracpy(_Vanadium_incoherent_Al_var._type, "Incoherent_process", 16384);
  _Vanadium_incoherent_Al_var._index=22;
  _Vanadium_incoherent_Al_var._parameters.sigma = 0.0082;
  _Vanadium_incoherent_Al_var._parameters.f_QE = 0;
  _Vanadium_incoherent_Al_var._parameters.gamma = 0;
  _Vanadium_incoherent_Al_var._parameters.packing_factor = 1;
  _Vanadium_incoherent_Al_var._parameters.unit_cell_volume = 66.3967 / 4;
  _Vanadium_incoherent_Al_var._parameters.interact_fraction = -1;
  if("init" && strlen("init"))
    stracpy(_Vanadium_incoherent_Al_var._parameters.init, "init" ? "init" : "", 16384);
  else 
  _Vanadium_incoherent_Al_var._parameters.init[0]='\0';


  /* component Vanadium_incoherent_Al=Incoherent_process() AT ROTATED */
  {
    Coords tc1, tc2;
    tc1 = coords_set(0,0,0);
    tc2 = coords_set(0,0,0);
    Rotation tr1;
    rot_set_rotation(tr1,0,0,0);
    rot_set_rotation(_Vanadium_incoherent_Al_var._rotation_absolute,
      (0.0)*DEG2RAD, (0.0)*DEG2RAD, (0.0)*DEG2RAD);
    rot_transpose(_Emon_before_sampel_var._rotation_absolute, tr1);
    rot_mul(_Vanadium_incoherent_Al_var._rotation_absolute, tr1, _Vanadium_incoherent_Al_var._rotation_relative);
    _Vanadium_incoherent_Al_var._rotation_is_identity =  rot_test_identity(_Vanadium_incoherent_Al_var._rotation_relative);
    _Vanadium_incoherent_Al_var._position_absolute = coords_set(
      0, 0, 0);
    tc1 = coords_sub(_Emon_before_sampel_var._position_absolute, _Vanadium_incoherent_Al_var._position_absolute);
    _Vanadium_incoherent_Al_var._position_relative = rot_apply(_Vanadium_incoherent_Al_var._rotation_absolute, tc1);
  } /* Vanadium_incoherent_Al=Incoherent_process() AT ROTATED */
  DEBUG_COMPONENT("Vanadium_incoherent_Al", _Vanadium_incoherent_Al_var._position_absolute, _Vanadium_incoherent_Al_var._rotation_absolute);
  instrument->_position_absolute[22] = _Vanadium_incoherent_Al_var._position_absolute;
  instrument->_position_relative[22] = _Vanadium_incoherent_Al_var._position_relative;
    _Vanadium_incoherent_Al_var._position_relative_is_zero =  coords_test_zero(_Vanadium_incoherent_Al_var._position_relative);
  instrument->counter_N[22]  = instrument->counter_P[22] = instrument->counter_P2[22] = 0;
  instrument->counter_AbsorbProp[22]= 0;
  return(0);
} /* _Vanadium_incoherent_Al_setpos */

/* component Al_Powder=Powder_process() SETTING, POSITION/ROTATION */
int _Al_Powder_setpos(void)
{ /* sets initial component parameters, position and rotation */
  SIG_MESSAGE("[_Al_Powder_setpos] component Al_Powder=Powder_process() SETTING [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//union/Powder_process.comp:650]");
  stracpy(_Al_Powder_var._name, "Al_Powder", 16384);
  stracpy(_Al_Powder_var._type, "Powder_process", 16384);
  _Al_Powder_var._index=23;
  if("Al.laz" && strlen("Al.laz"))
    stracpy(_Al_Powder_var._parameters.reflections, "Al.laz" ? "Al.laz" : "", 16384);
  else 
  _Al_Powder_var._parameters.reflections[0]='\0';
  _Al_Powder_var._parameters.packing_factor = 1;
  _Al_Powder_var._parameters.Vc = 0;
  _Al_Powder_var._parameters.delta_d_d = 0;
  _Al_Powder_var._parameters.DW = 0;
  _Al_Powder_var._parameters.nb_atoms = 1;
  _Al_Powder_var._parameters.d_phi = 0;
  _Al_Powder_var._parameters.density = 0;
  _Al_Powder_var._parameters.weight = 0;
  _Al_Powder_var._parameters.barns = 1;
  _Al_Powder_var._parameters.Strain = 0;
  _Al_Powder_var._parameters.interact_fraction = -1;
  _Al_Powder_var._parameters.format[0] = 0;
  _Al_Powder_var._parameters.format[1] = 0;
  _Al_Powder_var._parameters.format[2] = 0;
  _Al_Powder_var._parameters.format[3] = 0;
  _Al_Powder_var._parameters.format[4] = 0;
  _Al_Powder_var._parameters.format[5] = 0;
  _Al_Powder_var._parameters.format[6] = 0;
  _Al_Powder_var._parameters.format[7] = 0;
  _Al_Powder_var._parameters.format[8] = 0;
  if("init" && strlen("init"))
    stracpy(_Al_Powder_var._parameters.init, "init" ? "init" : "", 16384);
  else 
  _Al_Powder_var._parameters.init[0]='\0';


  /* component Al_Powder=Powder_process() AT ROTATED */
  {
    Coords tc1, tc2;
    tc1 = coords_set(0,0,0);
    tc2 = coords_set(0,0,0);
    Rotation tr1;
    rot_set_rotation(tr1,0,0,0);
    rot_set_rotation(_Al_Powder_var._rotation_absolute,
      (0.0)*DEG2RAD, (0.0)*DEG2RAD, (0.0)*DEG2RAD);
    rot_transpose(_Emon_before_sampel_var._rotation_absolute, tr1);
    rot_mul(_Al_Powder_var._rotation_absolute, tr1, _Al_Powder_var._rotation_relative);
    _Al_Powder_var._rotation_is_identity =  rot_test_identity(_Al_Powder_var._rotation_relative);
    _Al_Powder_var._position_absolute = coords_set(
      0, 0, 0);
    tc1 = coords_sub(_Emon_before_sampel_var._position_absolute, _Al_Powder_var._position_absolute);
    _Al_Powder_var._position_relative = rot_apply(_Al_Powder_var._rotation_absolute, tc1);
  } /* Al_Powder=Powder_process() AT ROTATED */
  DEBUG_COMPONENT("Al_Powder", _Al_Powder_var._position_absolute, _Al_Powder_var._rotation_absolute);
  instrument->_position_absolute[23] = _Al_Powder_var._position_absolute;
  instrument->_position_relative[23] = _Al_Powder_var._position_relative;
    _Al_Powder_var._position_relative_is_zero =  coords_test_zero(_Al_Powder_var._position_relative);
  instrument->counter_N[23]  = instrument->counter_P[23] = instrument->counter_P2[23] = 0;
  instrument->counter_AbsorbProp[23]= 0;
  return(0);
} /* _Al_Powder_setpos */

/* component Al=Union_make_material() SETTING, POSITION/ROTATION */
int _Al_setpos(void)
{ /* sets initial component parameters, position and rotation */
  SIG_MESSAGE("[_Al_setpos] component Al=Union_make_material() SETTING [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//union/Union_make_material.comp:180]");
  stracpy(_Al_var._name, "Al", 16384);
  stracpy(_Al_var._type, "Union_make_material", 16384);
  _Al_var._index=24;
  if("Al_Powder,Vanadium_incoherent_Al" && strlen("Al_Powder,Vanadium_incoherent_Al"))
    stracpy(_Al_var._parameters.process_string, "Al_Powder,Vanadium_incoherent_Al" ? "Al_Powder,Vanadium_incoherent_Al" : "", 16384);
  else 
  _Al_var._parameters.process_string[0]='\0';
  _Al_var._parameters.my_absorption = 100 * 0.231 / 16.7;
  _Al_var._parameters.absorber = 0;
  if("init" && strlen("init"))
    stracpy(_Al_var._parameters.init, "init" ? "init" : "", 16384);
  else 
  _Al_var._parameters.init[0]='\0';


  /* component Al=Union_make_material() AT ROTATED */
  {
    Coords tc1, tc2;
    tc1 = coords_set(0,0,0);
    tc2 = coords_set(0,0,0);
    Rotation tr1;
    rot_set_rotation(tr1,0,0,0);
    rot_set_rotation(tr1,
      (0.0)*DEG2RAD, (0.0)*DEG2RAD, (0.0)*DEG2RAD);
    rot_mul(tr1, _sample_position_var._rotation_absolute, _Al_var._rotation_absolute);
    rot_transpose(_Emon_before_sampel_var._rotation_absolute, tr1);
    rot_mul(_Al_var._rotation_absolute, tr1, _Al_var._rotation_relative);
    _Al_var._rotation_is_identity =  rot_test_identity(_Al_var._rotation_relative);
    tc1 = coords_set(
      0, 0, 0);
    rot_transpose(_sample_position_var._rotation_absolute, tr1);
    tc2 = rot_apply(tr1, tc1);
    _Al_var._position_absolute = coords_add(_sample_position_var._position_absolute, tc2);
    tc1 = coords_sub(_Emon_before_sampel_var._position_absolute, _Al_var._position_absolute);
    _Al_var._position_relative = rot_apply(_Al_var._rotation_absolute, tc1);
  } /* Al=Union_make_material() AT ROTATED */
  DEBUG_COMPONENT("Al", _Al_var._position_absolute, _Al_var._rotation_absolute);
  instrument->_position_absolute[24] = _Al_var._position_absolute;
  instrument->_position_relative[24] = _Al_var._position_relative;
    _Al_var._position_relative_is_zero =  coords_test_zero(_Al_var._position_relative);
  instrument->counter_N[24]  = instrument->counter_P[24] = instrument->counter_P2[24] = 0;
  instrument->counter_AbsorbProp[24]= 0;
  return(0);
} /* _Al_setpos */

/* component Vanadium_incoherent_Cu=Incoherent_process() SETTING, POSITION/ROTATION */
int _Vanadium_incoherent_Cu_setpos(void)
{ /* sets initial component parameters, position and rotation */
  SIG_MESSAGE("[_Vanadium_incoherent_Cu_setpos] component Vanadium_incoherent_Cu=Incoherent_process() SETTING [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//union/Incoherent_process.comp:142]");
  stracpy(_Vanadium_incoherent_Cu_var._name, "Vanadium_incoherent_Cu", 16384);
  stracpy(_Vanadium_incoherent_Cu_var._type, "Incoherent_process", 16384);
  _Vanadium_incoherent_Cu_var._index=25;
  _Vanadium_incoherent_Cu_var._parameters.sigma = 0.55;
  _Vanadium_incoherent_Cu_var._parameters.f_QE = 0;
  _Vanadium_incoherent_Cu_var._parameters.gamma = 0;
  _Vanadium_incoherent_Cu_var._parameters.packing_factor = 1;
  _Vanadium_incoherent_Cu_var._parameters.unit_cell_volume = 47.24360 / 4;
  _Vanadium_incoherent_Cu_var._parameters.interact_fraction = -1;
  if("init" && strlen("init"))
    stracpy(_Vanadium_incoherent_Cu_var._parameters.init, "init" ? "init" : "", 16384);
  else 
  _Vanadium_incoherent_Cu_var._parameters.init[0]='\0';


  /* component Vanadium_incoherent_Cu=Incoherent_process() AT ROTATED */
  {
    Coords tc1, tc2;
    tc1 = coords_set(0,0,0);
    tc2 = coords_set(0,0,0);
    Rotation tr1;
    rot_set_rotation(tr1,0,0,0);
    rot_set_rotation(_Vanadium_incoherent_Cu_var._rotation_absolute,
      (0.0)*DEG2RAD, (0.0)*DEG2RAD, (0.0)*DEG2RAD);
    rot_transpose(_Emon_before_sampel_var._rotation_absolute, tr1);
    rot_mul(_Vanadium_incoherent_Cu_var._rotation_absolute, tr1, _Vanadium_incoherent_Cu_var._rotation_relative);
    _Vanadium_incoherent_Cu_var._rotation_is_identity =  rot_test_identity(_Vanadium_incoherent_Cu_var._rotation_relative);
    _Vanadium_incoherent_Cu_var._position_absolute = coords_set(
      0, 0, 0);
    tc1 = coords_sub(_Emon_before_sampel_var._position_absolute, _Vanadium_incoherent_Cu_var._position_absolute);
    _Vanadium_incoherent_Cu_var._position_relative = rot_apply(_Vanadium_incoherent_Cu_var._rotation_absolute, tc1);
  } /* Vanadium_incoherent_Cu=Incoherent_process() AT ROTATED */
  DEBUG_COMPONENT("Vanadium_incoherent_Cu", _Vanadium_incoherent_Cu_var._position_absolute, _Vanadium_incoherent_Cu_var._rotation_absolute);
  instrument->_position_absolute[25] = _Vanadium_incoherent_Cu_var._position_absolute;
  instrument->_position_relative[25] = _Vanadium_incoherent_Cu_var._position_relative;
    _Vanadium_incoherent_Cu_var._position_relative_is_zero =  coords_test_zero(_Vanadium_incoherent_Cu_var._position_relative);
  instrument->counter_N[25]  = instrument->counter_P[25] = instrument->counter_P2[25] = 0;
  instrument->counter_AbsorbProp[25]= 0;
  return(0);
} /* _Vanadium_incoherent_Cu_setpos */

/* component Vanadium_incoherent_MnF2=Incoherent_process() SETTING, POSITION/ROTATION */
int _Vanadium_incoherent_MnF2_setpos(void)
{ /* sets initial component parameters, position and rotation */
  SIG_MESSAGE("[_Vanadium_incoherent_MnF2_setpos] component Vanadium_incoherent_MnF2=Incoherent_process() SETTING [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//union/Incoherent_process.comp:142]");
  stracpy(_Vanadium_incoherent_MnF2_var._name, "Vanadium_incoherent_MnF2", 16384);
  stracpy(_Vanadium_incoherent_MnF2_var._type, "Incoherent_process", 16384);
  _Vanadium_incoherent_MnF2_var._index=26;
  _Vanadium_incoherent_MnF2_var._parameters.sigma = 0.40 * 2 + 0.0008 * 4;
  _Vanadium_incoherent_MnF2_var._parameters.f_QE = 0;
  _Vanadium_incoherent_MnF2_var._parameters.gamma = 0;
  _Vanadium_incoherent_MnF2_var._parameters.packing_factor = 1;
  _Vanadium_incoherent_MnF2_var._parameters.unit_cell_volume = 74.32539;
  _Vanadium_incoherent_MnF2_var._parameters.interact_fraction = -1;
  if("init" && strlen("init"))
    stracpy(_Vanadium_incoherent_MnF2_var._parameters.init, "init" ? "init" : "", 16384);
  else 
  _Vanadium_incoherent_MnF2_var._parameters.init[0]='\0';


  /* component Vanadium_incoherent_MnF2=Incoherent_process() AT ROTATED */
  {
    Coords tc1, tc2;
    tc1 = coords_set(0,0,0);
    tc2 = coords_set(0,0,0);
    Rotation tr1;
    rot_set_rotation(tr1,0,0,0);
    rot_set_rotation(_Vanadium_incoherent_MnF2_var._rotation_absolute,
      (0.0)*DEG2RAD, (0.0)*DEG2RAD, (0.0)*DEG2RAD);
    rot_transpose(_Emon_before_sampel_var._rotation_absolute, tr1);
    rot_mul(_Vanadium_incoherent_MnF2_var._rotation_absolute, tr1, _Vanadium_incoherent_MnF2_var._rotation_relative);
    _Vanadium_incoherent_MnF2_var._rotation_is_identity =  rot_test_identity(_Vanadium_incoherent_MnF2_var._rotation_relative);
    _Vanadium_incoherent_MnF2_var._position_absolute = coords_set(
      0, 0, 0);
    tc1 = coords_sub(_Emon_before_sampel_var._position_absolute, _Vanadium_incoherent_MnF2_var._position_absolute);
    _Vanadium_incoherent_MnF2_var._position_relative = rot_apply(_Vanadium_incoherent_MnF2_var._rotation_absolute, tc1);
  } /* Vanadium_incoherent_MnF2=Incoherent_process() AT ROTATED */
  DEBUG_COMPONENT("Vanadium_incoherent_MnF2", _Vanadium_incoherent_MnF2_var._position_absolute, _Vanadium_incoherent_MnF2_var._rotation_absolute);
  instrument->_position_absolute[26] = _Vanadium_incoherent_MnF2_var._position_absolute;
  instrument->_position_relative[26] = _Vanadium_incoherent_MnF2_var._position_relative;
    _Vanadium_incoherent_MnF2_var._position_relative_is_zero =  coords_test_zero(_Vanadium_incoherent_MnF2_var._position_relative);
  instrument->counter_N[26]  = instrument->counter_P[26] = instrument->counter_P2[26] = 0;
  instrument->counter_AbsorbProp[26]= 0;
  return(0);
} /* _Vanadium_incoherent_MnF2_setpos */

/* component MnF2_critical=Single_crystal_process() SETTING, POSITION/ROTATION */
int _MnF2_critical_setpos(void)
{ /* sets initial component parameters, position and rotation */
  SIG_MESSAGE("[_MnF2_critical_setpos] component MnF2_critical=Single_crystal_process() SETTING [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//union/Single_crystal_process.comp:968]");
  stracpy(_MnF2_critical_var._name, "MnF2_critical", 16384);
  stracpy(_MnF2_critical_var._type, "Single_crystal_process", 16384);
  _MnF2_critical_var._index=27;
  if("MnF2_short_magn.lau" && strlen("MnF2_short_magn.lau"))
    stracpy(_MnF2_critical_var._parameters.reflections, "MnF2_short_magn.lau" ? "MnF2_short_magn.lau" : "", 16384);
  else 
  _MnF2_critical_var._parameters.reflections[0]='\0';
  _MnF2_critical_var._parameters.delta_d_d = DQ / q0;
  _MnF2_critical_var._parameters.mosaic = C_mosaic;
  _MnF2_critical_var._parameters.mosaic_a = -1;
  _MnF2_critical_var._parameters.mosaic_b = -1;
  _MnF2_critical_var._parameters.mosaic_c = -1;
  _MnF2_critical_var._parameters.mosaic_AB[0] = 0;
  _MnF2_critical_var._parameters.mosaic_AB[1] = 0;
  _MnF2_critical_var._parameters.mosaic_AB[2] = 0;
  _MnF2_critical_var._parameters.mosaic_AB[3] = 0;
  _MnF2_critical_var._parameters.mosaic_AB[4] = 0;
  _MnF2_critical_var._parameters.mosaic_AB[5] = 0;
  _MnF2_critical_var._parameters.mosaic_AB[6] = 0;
  _MnF2_critical_var._parameters.mosaic_AB[7] = 0;
  _MnF2_critical_var._parameters.recip_cell = 0;
  _MnF2_critical_var._parameters.barns = 1;
  _MnF2_critical_var._parameters.ax = 4.87300;
  _MnF2_critical_var._parameters.ay = 0;
  _MnF2_critical_var._parameters.az = 0;
  _MnF2_critical_var._parameters.bx = 0;
  _MnF2_critical_var._parameters.by = 4.87300;
  _MnF2_critical_var._parameters.bz = 0;
  _MnF2_critical_var._parameters.cx = 0;
  _MnF2_critical_var._parameters.cy = 0;
  _MnF2_critical_var._parameters.cz = 3.13000;
  _MnF2_critical_var._parameters.aa = 0;
  _MnF2_critical_var._parameters.bb = 0;
  _MnF2_critical_var._parameters.cc = 0;
  _MnF2_critical_var._parameters.order = 0;
  _MnF2_critical_var._parameters.RX = 0;
  _MnF2_critical_var._parameters.RY = 0;
  _MnF2_critical_var._parameters.RZ = 0;
  _MnF2_critical_var._parameters.powder = 0;
  _MnF2_critical_var._parameters.PG = 0;
  _MnF2_critical_var._parameters.interact_fraction = -1;
  _MnF2_critical_var._parameters.packing_factor = C_Intensity;
  if("init" && strlen("init"))
    stracpy(_MnF2_critical_var._parameters.init, "init" ? "init" : "", 16384);
  else 
  _MnF2_critical_var._parameters.init[0]='\0';


  /* component MnF2_critical=Single_crystal_process() AT ROTATED */
  {
    Coords tc1, tc2;
    tc1 = coords_set(0,0,0);
    tc2 = coords_set(0,0,0);
    Rotation tr1;
    rot_set_rotation(tr1,0,0,0);
    rot_set_rotation(_MnF2_critical_var._rotation_absolute,
      (0)*DEG2RAD, (0)*DEG2RAD, (0)*DEG2RAD);
    rot_transpose(_Emon_before_sampel_var._rotation_absolute, tr1);
    rot_mul(_MnF2_critical_var._rotation_absolute, tr1, _MnF2_critical_var._rotation_relative);
    _MnF2_critical_var._rotation_is_identity =  rot_test_identity(_MnF2_critical_var._rotation_relative);
    _MnF2_critical_var._position_absolute = coords_set(
      0, 0, 0);
    tc1 = coords_sub(_Emon_before_sampel_var._position_absolute, _MnF2_critical_var._position_absolute);
    _MnF2_critical_var._position_relative = rot_apply(_MnF2_critical_var._rotation_absolute, tc1);
  } /* MnF2_critical=Single_crystal_process() AT ROTATED */
  DEBUG_COMPONENT("MnF2_critical", _MnF2_critical_var._position_absolute, _MnF2_critical_var._rotation_absolute);
  instrument->_position_absolute[27] = _MnF2_critical_var._position_absolute;
  instrument->_position_relative[27] = _MnF2_critical_var._position_relative;
    _MnF2_critical_var._position_relative_is_zero =  coords_test_zero(_MnF2_critical_var._position_relative);
  instrument->counter_N[27]  = instrument->counter_P[27] = instrument->counter_P2[27] = 0;
  instrument->counter_AbsorbProp[27]= 0;
  return(0);
} /* _MnF2_critical_setpos */

/* component MnF2_single_crystal=Single_crystal_process() SETTING, POSITION/ROTATION */
int _MnF2_single_crystal_setpos(void)
{ /* sets initial component parameters, position and rotation */
  SIG_MESSAGE("[_MnF2_single_crystal_setpos] component MnF2_single_crystal=Single_crystal_process() SETTING [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//union/Single_crystal_process.comp:968]");
  stracpy(_MnF2_single_crystal_var._name, "MnF2_single_crystal", 16384);
  stracpy(_MnF2_single_crystal_var._type, "Single_crystal_process", 16384);
  _MnF2_single_crystal_var._index=28;
  if("MnF2_short.lau" && strlen("MnF2_short.lau"))
    stracpy(_MnF2_single_crystal_var._parameters.reflections, "MnF2_short.lau" ? "MnF2_short.lau" : "", 16384);
  else 
  _MnF2_single_crystal_var._parameters.reflections[0]='\0';
  _MnF2_single_crystal_var._parameters.delta_d_d = 1E-4;
  _MnF2_single_crystal_var._parameters.mosaic = 20;
  _MnF2_single_crystal_var._parameters.mosaic_a = -1;
  _MnF2_single_crystal_var._parameters.mosaic_b = -1;
  _MnF2_single_crystal_var._parameters.mosaic_c = -1;
  _MnF2_single_crystal_var._parameters.mosaic_AB[0] = 0;
  _MnF2_single_crystal_var._parameters.mosaic_AB[1] = 0;
  _MnF2_single_crystal_var._parameters.mosaic_AB[2] = 0;
  _MnF2_single_crystal_var._parameters.mosaic_AB[3] = 0;
  _MnF2_single_crystal_var._parameters.mosaic_AB[4] = 0;
  _MnF2_single_crystal_var._parameters.mosaic_AB[5] = 0;
  _MnF2_single_crystal_var._parameters.mosaic_AB[6] = 0;
  _MnF2_single_crystal_var._parameters.mosaic_AB[7] = 0;
  _MnF2_single_crystal_var._parameters.recip_cell = 0;
  _MnF2_single_crystal_var._parameters.barns = 1;
  _MnF2_single_crystal_var._parameters.ax = 4.87300;
  _MnF2_single_crystal_var._parameters.ay = 0;
  _MnF2_single_crystal_var._parameters.az = 0;
  _MnF2_single_crystal_var._parameters.bx = 0;
  _MnF2_single_crystal_var._parameters.by = 4.87300;
  _MnF2_single_crystal_var._parameters.bz = 0;
  _MnF2_single_crystal_var._parameters.cx = 0;
  _MnF2_single_crystal_var._parameters.cy = 0;
  _MnF2_single_crystal_var._parameters.cz = 3.13000;
  _MnF2_single_crystal_var._parameters.aa = 0;
  _MnF2_single_crystal_var._parameters.bb = 0;
  _MnF2_single_crystal_var._parameters.cc = 0;
  _MnF2_single_crystal_var._parameters.order = 0;
  _MnF2_single_crystal_var._parameters.RX = 0;
  _MnF2_single_crystal_var._parameters.RY = 0;
  _MnF2_single_crystal_var._parameters.RZ = 0;
  _MnF2_single_crystal_var._parameters.powder = 0;
  _MnF2_single_crystal_var._parameters.PG = 0;
  _MnF2_single_crystal_var._parameters.interact_fraction = -1;
  _MnF2_single_crystal_var._parameters.packing_factor = 1;
  if("init" && strlen("init"))
    stracpy(_MnF2_single_crystal_var._parameters.init, "init" ? "init" : "", 16384);
  else 
  _MnF2_single_crystal_var._parameters.init[0]='\0';


  /* component MnF2_single_crystal=Single_crystal_process() AT ROTATED */
  {
    Coords tc1, tc2;
    tc1 = coords_set(0,0,0);
    tc2 = coords_set(0,0,0);
    Rotation tr1;
    rot_set_rotation(tr1,0,0,0);
    rot_set_rotation(_MnF2_single_crystal_var._rotation_absolute,
      (0)*DEG2RAD, (0)*DEG2RAD, (0)*DEG2RAD);
    rot_transpose(_MnF2_critical_var._rotation_absolute, tr1);
    rot_mul(_MnF2_single_crystal_var._rotation_absolute, tr1, _MnF2_single_crystal_var._rotation_relative);
    _MnF2_single_crystal_var._rotation_is_identity =  rot_test_identity(_MnF2_single_crystal_var._rotation_relative);
    _MnF2_single_crystal_var._position_absolute = coords_set(
      0, 0, 0);
    tc1 = coords_sub(_MnF2_critical_var._position_absolute, _MnF2_single_crystal_var._position_absolute);
    _MnF2_single_crystal_var._position_relative = rot_apply(_MnF2_single_crystal_var._rotation_absolute, tc1);
  } /* MnF2_single_crystal=Single_crystal_process() AT ROTATED */
  DEBUG_COMPONENT("MnF2_single_crystal", _MnF2_single_crystal_var._position_absolute, _MnF2_single_crystal_var._rotation_absolute);
  instrument->_position_absolute[28] = _MnF2_single_crystal_var._position_absolute;
  instrument->_position_relative[28] = _MnF2_single_crystal_var._position_relative;
    _MnF2_single_crystal_var._position_relative_is_zero =  coords_test_zero(_MnF2_single_crystal_var._position_relative);
  instrument->counter_N[28]  = instrument->counter_P[28] = instrument->counter_P2[28] = 0;
  instrument->counter_AbsorbProp[28]= 0;
  return(0);
} /* _MnF2_single_crystal_setpos */

/* component MnF2_single_crystal_magn=Single_crystal_process() SETTING, POSITION/ROTATION */
int _MnF2_single_crystal_magn_setpos(void)
{ /* sets initial component parameters, position and rotation */
  SIG_MESSAGE("[_MnF2_single_crystal_magn_setpos] component MnF2_single_crystal_magn=Single_crystal_process() SETTING [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//union/Single_crystal_process.comp:968]");
  stracpy(_MnF2_single_crystal_magn_var._name, "MnF2_single_crystal_magn", 16384);
  stracpy(_MnF2_single_crystal_magn_var._type, "Single_crystal_process", 16384);
  _MnF2_single_crystal_magn_var._index=29;
  if("MnF2_short_magn.lau" && strlen("MnF2_short_magn.lau"))
    stracpy(_MnF2_single_crystal_magn_var._parameters.reflections, "MnF2_short_magn.lau" ? "MnF2_short_magn.lau" : "", 16384);
  else 
  _MnF2_single_crystal_magn_var._parameters.reflections[0]='\0';
  _MnF2_single_crystal_magn_var._parameters.delta_d_d = 1E-4;
  _MnF2_single_crystal_magn_var._parameters.mosaic = 20;
  _MnF2_single_crystal_magn_var._parameters.mosaic_a = -1;
  _MnF2_single_crystal_magn_var._parameters.mosaic_b = -1;
  _MnF2_single_crystal_magn_var._parameters.mosaic_c = -1;
  _MnF2_single_crystal_magn_var._parameters.mosaic_AB[0] = 0;
  _MnF2_single_crystal_magn_var._parameters.mosaic_AB[1] = 0;
  _MnF2_single_crystal_magn_var._parameters.mosaic_AB[2] = 0;
  _MnF2_single_crystal_magn_var._parameters.mosaic_AB[3] = 0;
  _MnF2_single_crystal_magn_var._parameters.mosaic_AB[4] = 0;
  _MnF2_single_crystal_magn_var._parameters.mosaic_AB[5] = 0;
  _MnF2_single_crystal_magn_var._parameters.mosaic_AB[6] = 0;
  _MnF2_single_crystal_magn_var._parameters.mosaic_AB[7] = 0;
  _MnF2_single_crystal_magn_var._parameters.recip_cell = 0;
  _MnF2_single_crystal_magn_var._parameters.barns = 1;
  _MnF2_single_crystal_magn_var._parameters.ax = 4.87300;
  _MnF2_single_crystal_magn_var._parameters.ay = 0;
  _MnF2_single_crystal_magn_var._parameters.az = 0;
  _MnF2_single_crystal_magn_var._parameters.bx = 0;
  _MnF2_single_crystal_magn_var._parameters.by = 4.87300;
  _MnF2_single_crystal_magn_var._parameters.bz = 0;
  _MnF2_single_crystal_magn_var._parameters.cx = 0;
  _MnF2_single_crystal_magn_var._parameters.cy = 0;
  _MnF2_single_crystal_magn_var._parameters.cz = 3.13000;
  _MnF2_single_crystal_magn_var._parameters.aa = 0;
  _MnF2_single_crystal_magn_var._parameters.bb = 0;
  _MnF2_single_crystal_magn_var._parameters.cc = 0;
  _MnF2_single_crystal_magn_var._parameters.order = 0;
  _MnF2_single_crystal_magn_var._parameters.RX = 0;
  _MnF2_single_crystal_magn_var._parameters.RY = 0;
  _MnF2_single_crystal_magn_var._parameters.RZ = 0;
  _MnF2_single_crystal_magn_var._parameters.powder = 0;
  _MnF2_single_crystal_magn_var._parameters.PG = 0;
  _MnF2_single_crystal_magn_var._parameters.interact_fraction = -1;
  _MnF2_single_crystal_magn_var._parameters.packing_factor = Magn_order_factor;
  if("init" && strlen("init"))
    stracpy(_MnF2_single_crystal_magn_var._parameters.init, "init" ? "init" : "", 16384);
  else 
  _MnF2_single_crystal_magn_var._parameters.init[0]='\0';


  /* component MnF2_single_crystal_magn=Single_crystal_process() AT ROTATED */
  {
    Coords tc1, tc2;
    tc1 = coords_set(0,0,0);
    tc2 = coords_set(0,0,0);
    Rotation tr1;
    rot_set_rotation(tr1,0,0,0);
    rot_set_rotation(_MnF2_single_crystal_magn_var._rotation_absolute,
      (0)*DEG2RAD, (0)*DEG2RAD, (0)*DEG2RAD);
    rot_transpose(_MnF2_single_crystal_var._rotation_absolute, tr1);
    rot_mul(_MnF2_single_crystal_magn_var._rotation_absolute, tr1, _MnF2_single_crystal_magn_var._rotation_relative);
    _MnF2_single_crystal_magn_var._rotation_is_identity =  rot_test_identity(_MnF2_single_crystal_magn_var._rotation_relative);
    _MnF2_single_crystal_magn_var._position_absolute = coords_set(
      0, 0, 0);
    tc1 = coords_sub(_MnF2_single_crystal_var._position_absolute, _MnF2_single_crystal_magn_var._position_absolute);
    _MnF2_single_crystal_magn_var._position_relative = rot_apply(_MnF2_single_crystal_magn_var._rotation_absolute, tc1);
  } /* MnF2_single_crystal_magn=Single_crystal_process() AT ROTATED */
  DEBUG_COMPONENT("MnF2_single_crystal_magn", _MnF2_single_crystal_magn_var._position_absolute, _MnF2_single_crystal_magn_var._rotation_absolute);
  instrument->_position_absolute[29] = _MnF2_single_crystal_magn_var._position_absolute;
  instrument->_position_relative[29] = _MnF2_single_crystal_magn_var._position_relative;
    _MnF2_single_crystal_magn_var._position_relative_is_zero =  coords_test_zero(_MnF2_single_crystal_magn_var._position_relative);
  instrument->counter_N[29]  = instrument->counter_P[29] = instrument->counter_P2[29] = 0;
  instrument->counter_AbsorbProp[29]= 0;
  return(0);
} /* _MnF2_single_crystal_magn_setpos */

/* component MnF2=Union_make_material() SETTING, POSITION/ROTATION */
int _MnF2_setpos(void)
{ /* sets initial component parameters, position and rotation */
  SIG_MESSAGE("[_MnF2_setpos] component MnF2=Union_make_material() SETTING [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//union/Union_make_material.comp:180]");
  stracpy(_MnF2_var._name, "MnF2", 16384);
  stracpy(_MnF2_var._type, "Union_make_material", 16384);
  _MnF2_var._index=30;
  if("Vanadium_incoherent_MnF2,MnF2_critical,MnF2_single_crystal,MnF2_single_crystal_magn" && strlen("Vanadium_incoherent_MnF2,MnF2_critical,MnF2_single_crystal,MnF2_single_crystal_magn"))
    stracpy(_MnF2_var._parameters.process_string, "Vanadium_incoherent_MnF2,MnF2_critical,MnF2_single_crystal,MnF2_single_crystal_magn" ? "Vanadium_incoherent_MnF2,MnF2_critical,MnF2_single_crystal,MnF2_single_crystal_magn" : "", 16384);
  else 
  _MnF2_var._parameters.process_string[0]='\0';
  _MnF2_var._parameters.my_absorption = 100 * ( 4 * 0.0096 + 2 * 13.3 ) / 74.325;
  _MnF2_var._parameters.absorber = 0;
  if("init" && strlen("init"))
    stracpy(_MnF2_var._parameters.init, "init" ? "init" : "", 16384);
  else 
  _MnF2_var._parameters.init[0]='\0';


  /* component MnF2=Union_make_material() AT ROTATED */
  {
    Coords tc1, tc2;
    tc1 = coords_set(0,0,0);
    tc2 = coords_set(0,0,0);
    Rotation tr1;
    rot_set_rotation(tr1,0,0,0);
    rot_set_rotation(tr1,
      (0)*DEG2RAD, (0)*DEG2RAD, (0)*DEG2RAD);
    rot_mul(tr1, _sample_position_var._rotation_absolute, _MnF2_var._rotation_absolute);
    rot_transpose(_MnF2_single_crystal_magn_var._rotation_absolute, tr1);
    rot_mul(_MnF2_var._rotation_absolute, tr1, _MnF2_var._rotation_relative);
    _MnF2_var._rotation_is_identity =  rot_test_identity(_MnF2_var._rotation_relative);
    tc1 = coords_set(
      0, 0, 0);
    rot_transpose(_sample_position_var._rotation_absolute, tr1);
    tc2 = rot_apply(tr1, tc1);
    _MnF2_var._position_absolute = coords_add(_sample_position_var._position_absolute, tc2);
    tc1 = coords_sub(_MnF2_single_crystal_magn_var._position_absolute, _MnF2_var._position_absolute);
    _MnF2_var._position_relative = rot_apply(_MnF2_var._rotation_absolute, tc1);
  } /* MnF2=Union_make_material() AT ROTATED */
  DEBUG_COMPONENT("MnF2", _MnF2_var._position_absolute, _MnF2_var._rotation_absolute);
  instrument->_position_absolute[30] = _MnF2_var._position_absolute;
  instrument->_position_relative[30] = _MnF2_var._position_relative;
    _MnF2_var._position_relative_is_zero =  coords_test_zero(_MnF2_var._position_relative);
  instrument->counter_N[30]  = instrument->counter_P[30] = instrument->counter_P2[30] = 0;
  instrument->counter_AbsorbProp[30]= 0;
  return(0);
} /* _MnF2_setpos */

/* component union_sample=Union_cylinder() SETTING, POSITION/ROTATION */
int _union_sample_setpos(void)
{ /* sets initial component parameters, position and rotation */
  SIG_MESSAGE("[_union_sample_setpos] component union_sample=Union_cylinder() SETTING [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//union/Union_cylinder.comp:182]");
  stracpy(_union_sample_var._name, "union_sample", 16384);
  stracpy(_union_sample_var._type, "Union_cylinder", 16384);
  _union_sample_var._index=31;
  if("MnF2" && strlen("MnF2"))
    stracpy(_union_sample_var._parameters.material_string, "MnF2" ? "MnF2" : "", 16384);
  else 
  _union_sample_var._parameters.material_string[0]='\0';
  _union_sample_var._parameters.priority = 10;
  _union_sample_var._parameters.radius = 0.005;
  _union_sample_var._parameters.yheight = 0.05;
  _union_sample_var._parameters.visualize = 1;
  _union_sample_var._parameters.target_index = 0;
  _union_sample_var._parameters.target_x = 0;
  _union_sample_var._parameters.target_y = 0;
  _union_sample_var._parameters.target_z = 0;
  _union_sample_var._parameters.focus_aw = 0;
  _union_sample_var._parameters.focus_ah = 0;
  _union_sample_var._parameters.focus_xw = 0;
  _union_sample_var._parameters.focus_xh = 0;
  _union_sample_var._parameters.focus_r = 0;
  _union_sample_var._parameters.p_interact = 0;
  _union_sample_var._parameters.mask_string[0]='\0';
  _union_sample_var._parameters.mask_setting[0]='\0';
  _union_sample_var._parameters.number_of_activations = 1;
  if("init" && strlen("init"))
    stracpy(_union_sample_var._parameters.init, "init" ? "init" : "", 16384);
  else 
  _union_sample_var._parameters.init[0]='\0';


  /* component union_sample=Union_cylinder() AT ROTATED */
  {
    Coords tc1, tc2;
    tc1 = coords_set(0,0,0);
    tc2 = coords_set(0,0,0);
    Rotation tr1;
    rot_set_rotation(tr1,0,0,0);
    rot_set_rotation(tr1,
      (0)*DEG2RAD, (0)*DEG2RAD, (90)*DEG2RAD);
    rot_mul(tr1, _sample_position_var._rotation_absolute, _union_sample_var._rotation_absolute);
    rot_transpose(_MnF2_single_crystal_magn_var._rotation_absolute, tr1);
    rot_mul(_union_sample_var._rotation_absolute, tr1, _union_sample_var._rotation_relative);
    _union_sample_var._rotation_is_identity =  rot_test_identity(_union_sample_var._rotation_relative);
    tc1 = coords_set(
      0, 0, 0);
    rot_transpose(_sample_position_var._rotation_absolute, tr1);
    tc2 = rot_apply(tr1, tc1);
    _union_sample_var._position_absolute = coords_add(_sample_position_var._position_absolute, tc2);
    tc1 = coords_sub(_MnF2_single_crystal_magn_var._position_absolute, _union_sample_var._position_absolute);
    _union_sample_var._position_relative = rot_apply(_union_sample_var._rotation_absolute, tc1);
  } /* union_sample=Union_cylinder() AT ROTATED */
  DEBUG_COMPONENT("union_sample", _union_sample_var._position_absolute, _union_sample_var._rotation_absolute);
  instrument->_position_absolute[31] = _union_sample_var._position_absolute;
  instrument->_position_relative[31] = _union_sample_var._position_relative;
    _union_sample_var._position_relative_is_zero =  coords_test_zero(_union_sample_var._position_relative);
  instrument->counter_N[31]  = instrument->counter_P[31] = instrument->counter_P2[31] = 0;
  instrument->counter_AbsorbProp[31]= 0;
  return(0);
} /* _union_sample_setpos */

/* component union_stick=Union_cylinder() SETTING, POSITION/ROTATION */
int _union_stick_setpos(void)
{ /* sets initial component parameters, position and rotation */
  SIG_MESSAGE("[_union_stick_setpos] component union_stick=Union_cylinder() SETTING [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//union/Union_cylinder.comp:182]");
  stracpy(_union_stick_var._name, "union_stick", 16384);
  stracpy(_union_stick_var._type, "Union_cylinder", 16384);
  _union_stick_var._index=32;
  if("Al" && strlen("Al"))
    stracpy(_union_stick_var._parameters.material_string, "Al" ? "Al" : "", 16384);
  else 
  _union_stick_var._parameters.material_string[0]='\0';
  _union_stick_var._parameters.priority = 8;
  _union_stick_var._parameters.radius = 0.005;
  _union_stick_var._parameters.yheight = 0.052;
  _union_stick_var._parameters.visualize = 1;
  _union_stick_var._parameters.target_index = 0;
  _union_stick_var._parameters.target_x = 0;
  _union_stick_var._parameters.target_y = 0;
  _union_stick_var._parameters.target_z = 0;
  _union_stick_var._parameters.focus_aw = 0;
  _union_stick_var._parameters.focus_ah = 0;
  _union_stick_var._parameters.focus_xw = 0;
  _union_stick_var._parameters.focus_xh = 0;
  _union_stick_var._parameters.focus_r = 0;
  _union_stick_var._parameters.p_interact = 0;
  _union_stick_var._parameters.mask_string[0]='\0';
  _union_stick_var._parameters.mask_setting[0]='\0';
  _union_stick_var._parameters.number_of_activations = 1;
  if("init" && strlen("init"))
    stracpy(_union_stick_var._parameters.init, "init" ? "init" : "", 16384);
  else 
  _union_stick_var._parameters.init[0]='\0';


  /* component union_stick=Union_cylinder() AT ROTATED */
  {
    Coords tc1, tc2;
    tc1 = coords_set(0,0,0);
    tc2 = coords_set(0,0,0);
    Rotation tr1;
    rot_set_rotation(tr1,0,0,0);
    rot_set_rotation(tr1,
      (0)*DEG2RAD, (0)*DEG2RAD, (0)*DEG2RAD);
    rot_mul(tr1, _sample_position_var._rotation_absolute, _union_stick_var._rotation_absolute);
    rot_transpose(_MnF2_single_crystal_magn_var._rotation_absolute, tr1);
    rot_mul(_union_stick_var._rotation_absolute, tr1, _union_stick_var._rotation_relative);
    _union_stick_var._rotation_is_identity =  rot_test_identity(_union_stick_var._rotation_relative);
    tc1 = coords_set(
      0, 0.03, 0);
    rot_transpose(_sample_position_var._rotation_absolute, tr1);
    tc2 = rot_apply(tr1, tc1);
    _union_stick_var._position_absolute = coords_add(_sample_position_var._position_absolute, tc2);
    tc1 = coords_sub(_MnF2_single_crystal_magn_var._position_absolute, _union_stick_var._position_absolute);
    _union_stick_var._position_relative = rot_apply(_union_stick_var._rotation_absolute, tc1);
  } /* union_stick=Union_cylinder() AT ROTATED */
  DEBUG_COMPONENT("union_stick", _union_stick_var._position_absolute, _union_stick_var._rotation_absolute);
  instrument->_position_absolute[32] = _union_stick_var._position_absolute;
  instrument->_position_relative[32] = _union_stick_var._position_relative;
    _union_stick_var._position_relative_is_zero =  coords_test_zero(_union_stick_var._position_relative);
  instrument->counter_N[32]  = instrument->counter_P[32] = instrument->counter_P2[32] = 0;
  instrument->counter_AbsorbProp[32]= 0;
  return(0);
} /* _union_stick_setpos */

/* component cryostat_wall=Union_cylinder() SETTING, POSITION/ROTATION */
int _cryostat_wall_setpos(void)
{ /* sets initial component parameters, position and rotation */
  SIG_MESSAGE("[_cryostat_wall_setpos] component cryostat_wall=Union_cylinder() SETTING [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//union/Union_cylinder.comp:182]");
  stracpy(_cryostat_wall_var._name, "cryostat_wall", 16384);
  stracpy(_cryostat_wall_var._type, "Union_cylinder", 16384);
  _cryostat_wall_var._index=33;
  if("Al" && strlen("Al"))
    stracpy(_cryostat_wall_var._parameters.material_string, "Al" ? "Al" : "", 16384);
  else 
  _cryostat_wall_var._parameters.material_string[0]='\0';
  _cryostat_wall_var._parameters.priority = 6;
  _cryostat_wall_var._parameters.radius = 0.14;
  _cryostat_wall_var._parameters.yheight = 0.181;
  _cryostat_wall_var._parameters.visualize = 1;
  _cryostat_wall_var._parameters.target_index = 0;
  _cryostat_wall_var._parameters.target_x = 0;
  _cryostat_wall_var._parameters.target_y = 0;
  _cryostat_wall_var._parameters.target_z = 0;
  _cryostat_wall_var._parameters.focus_aw = 0;
  _cryostat_wall_var._parameters.focus_ah = 0;
  _cryostat_wall_var._parameters.focus_xw = 0;
  _cryostat_wall_var._parameters.focus_xh = 0;
  _cryostat_wall_var._parameters.focus_r = 0;
  _cryostat_wall_var._parameters.p_interact = 0.1;
  _cryostat_wall_var._parameters.mask_string[0]='\0';
  _cryostat_wall_var._parameters.mask_setting[0]='\0';
  _cryostat_wall_var._parameters.number_of_activations = 1;
  if("init" && strlen("init"))
    stracpy(_cryostat_wall_var._parameters.init, "init" ? "init" : "", 16384);
  else 
  _cryostat_wall_var._parameters.init[0]='\0';


  /* component cryostat_wall=Union_cylinder() AT ROTATED */
  {
    Coords tc1, tc2;
    tc1 = coords_set(0,0,0);
    tc2 = coords_set(0,0,0);
    Rotation tr1;
    rot_set_rotation(tr1,0,0,0);
    rot_set_rotation(tr1,
      (0)*DEG2RAD, (0)*DEG2RAD, (0)*DEG2RAD);
    rot_mul(tr1, _sample_position_var._rotation_absolute, _cryostat_wall_var._rotation_absolute);
    rot_transpose(_MnF2_single_crystal_magn_var._rotation_absolute, tr1);
    rot_mul(_cryostat_wall_var._rotation_absolute, tr1, _cryostat_wall_var._rotation_relative);
    _cryostat_wall_var._rotation_is_identity =  rot_test_identity(_cryostat_wall_var._rotation_relative);
    tc1 = coords_set(
      0, 0.02, 0);
    rot_transpose(_sample_position_var._rotation_absolute, tr1);
    tc2 = rot_apply(tr1, tc1);
    _cryostat_wall_var._position_absolute = coords_add(_sample_position_var._position_absolute, tc2);
    tc1 = coords_sub(_MnF2_single_crystal_magn_var._position_absolute, _cryostat_wall_var._position_absolute);
    _cryostat_wall_var._position_relative = rot_apply(_cryostat_wall_var._rotation_absolute, tc1);
  } /* cryostat_wall=Union_cylinder() AT ROTATED */
  DEBUG_COMPONENT("cryostat_wall", _cryostat_wall_var._position_absolute, _cryostat_wall_var._rotation_absolute);
  instrument->_position_absolute[33] = _cryostat_wall_var._position_absolute;
  instrument->_position_relative[33] = _cryostat_wall_var._position_relative;
    _cryostat_wall_var._position_relative_is_zero =  coords_test_zero(_cryostat_wall_var._position_relative);
  instrument->counter_N[33]  = instrument->counter_P[33] = instrument->counter_P2[33] = 0;
  instrument->counter_AbsorbProp[33]= 0;
  return(0);
} /* _cryostat_wall_setpos */

/* component cryostat_vacuum=Union_cylinder() SETTING, POSITION/ROTATION */
int _cryostat_vacuum_setpos(void)
{ /* sets initial component parameters, position and rotation */
  SIG_MESSAGE("[_cryostat_vacuum_setpos] component cryostat_vacuum=Union_cylinder() SETTING [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//union/Union_cylinder.comp:182]");
  stracpy(_cryostat_vacuum_var._name, "cryostat_vacuum", 16384);
  stracpy(_cryostat_vacuum_var._type, "Union_cylinder", 16384);
  _cryostat_vacuum_var._index=34;
  if("Vacuum" && strlen("Vacuum"))
    stracpy(_cryostat_vacuum_var._parameters.material_string, "Vacuum" ? "Vacuum" : "", 16384);
  else 
  _cryostat_vacuum_var._parameters.material_string[0]='\0';
  _cryostat_vacuum_var._parameters.priority = 7;
  _cryostat_vacuum_var._parameters.radius = 0.115;
  _cryostat_vacuum_var._parameters.yheight = 0.15;
  _cryostat_vacuum_var._parameters.visualize = 1;
  _cryostat_vacuum_var._parameters.target_index = 0;
  _cryostat_vacuum_var._parameters.target_x = 0;
  _cryostat_vacuum_var._parameters.target_y = 0;
  _cryostat_vacuum_var._parameters.target_z = 0;
  _cryostat_vacuum_var._parameters.focus_aw = 0;
  _cryostat_vacuum_var._parameters.focus_ah = 0;
  _cryostat_vacuum_var._parameters.focus_xw = 0;
  _cryostat_vacuum_var._parameters.focus_xh = 0;
  _cryostat_vacuum_var._parameters.focus_r = 0;
  _cryostat_vacuum_var._parameters.p_interact = 0;
  _cryostat_vacuum_var._parameters.mask_string[0]='\0';
  _cryostat_vacuum_var._parameters.mask_setting[0]='\0';
  _cryostat_vacuum_var._parameters.number_of_activations = 1;
  if("init" && strlen("init"))
    stracpy(_cryostat_vacuum_var._parameters.init, "init" ? "init" : "", 16384);
  else 
  _cryostat_vacuum_var._parameters.init[0]='\0';


  /* component cryostat_vacuum=Union_cylinder() AT ROTATED */
  {
    Coords tc1, tc2;
    tc1 = coords_set(0,0,0);
    tc2 = coords_set(0,0,0);
    Rotation tr1;
    rot_set_rotation(tr1,0,0,0);
    rot_set_rotation(tr1,
      (0)*DEG2RAD, (0)*DEG2RAD, (0)*DEG2RAD);
    rot_mul(tr1, _sample_position_var._rotation_absolute, _cryostat_vacuum_var._rotation_absolute);
    rot_transpose(_MnF2_single_crystal_magn_var._rotation_absolute, tr1);
    rot_mul(_cryostat_vacuum_var._rotation_absolute, tr1, _cryostat_vacuum_var._rotation_relative);
    _cryostat_vacuum_var._rotation_is_identity =  rot_test_identity(_cryostat_vacuum_var._rotation_relative);
    tc1 = coords_set(
      0, 0.02, 0);
    rot_transpose(_sample_position_var._rotation_absolute, tr1);
    tc2 = rot_apply(tr1, tc1);
    _cryostat_vacuum_var._position_absolute = coords_add(_sample_position_var._position_absolute, tc2);
    tc1 = coords_sub(_MnF2_single_crystal_magn_var._position_absolute, _cryostat_vacuum_var._position_absolute);
    _cryostat_vacuum_var._position_relative = rot_apply(_cryostat_vacuum_var._rotation_absolute, tc1);
  } /* cryostat_vacuum=Union_cylinder() AT ROTATED */
  DEBUG_COMPONENT("cryostat_vacuum", _cryostat_vacuum_var._position_absolute, _cryostat_vacuum_var._rotation_absolute);
  instrument->_position_absolute[34] = _cryostat_vacuum_var._position_absolute;
  instrument->_position_relative[34] = _cryostat_vacuum_var._position_relative;
    _cryostat_vacuum_var._position_relative_is_zero =  coords_test_zero(_cryostat_vacuum_var._position_relative);
  instrument->counter_N[34]  = instrument->counter_P[34] = instrument->counter_P2[34] = 0;
  instrument->counter_AbsorbProp[34]= 0;
  return(0);
} /* _cryostat_vacuum_setpos */

/* component Master=Union_master() SETTING, POSITION/ROTATION */
int _Master_setpos(void)
{ /* sets initial component parameters, position and rotation */
  SIG_MESSAGE("[_Master_setpos] component Master=Union_master() SETTING [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//union/Union_master.comp:297]");
  stracpy(_Master_var._name, "Master", 16384);
  stracpy(_Master_var._type, "Union_master", 16384);
  _Master_var._index=35;
  _Master_var._parameters.verbal = 1;
  _Master_var._parameters.list_verbal = 0;
  _Master_var._parameters.finally_verbal = 0;
  _Master_var._parameters.allow_inside_start = 0;
  _Master_var._parameters.enable_tagging = 0;
  _Master_var._parameters.history_limit = 300000;
  _Master_var._parameters.enable_conditionals = 1;
  _Master_var._parameters.inherit_number_of_scattering_events = 0;
  if("init" && strlen("init"))
    stracpy(_Master_var._parameters.init, "init" ? "init" : "", 16384);
  else 
  _Master_var._parameters.init[0]='\0';


  /* component Master=Union_master() AT ROTATED */
  {
    Coords tc1, tc2;
    tc1 = coords_set(0,0,0);
    tc2 = coords_set(0,0,0);
    Rotation tr1;
    rot_set_rotation(tr1,0,0,0);
    rot_set_rotation(tr1,
      (0.0)*DEG2RAD, (0.0)*DEG2RAD, (0.0)*DEG2RAD);
    rot_mul(tr1, _sample_position_var._rotation_absolute, _Master_var._rotation_absolute);
    rot_transpose(_MnF2_single_crystal_magn_var._rotation_absolute, tr1);
    rot_mul(_Master_var._rotation_absolute, tr1, _Master_var._rotation_relative);
    _Master_var._rotation_is_identity =  rot_test_identity(_Master_var._rotation_relative);
    tc1 = coords_set(
      0, 0, 0);
    rot_transpose(_sample_position_var._rotation_absolute, tr1);
    tc2 = rot_apply(tr1, tc1);
    _Master_var._position_absolute = coords_add(_sample_position_var._position_absolute, tc2);
    tc1 = coords_sub(_MnF2_single_crystal_magn_var._position_absolute, _Master_var._position_absolute);
    _Master_var._position_relative = rot_apply(_Master_var._rotation_absolute, tc1);
  } /* Master=Union_master() AT ROTATED */
  DEBUG_COMPONENT("Master", _Master_var._position_absolute, _Master_var._rotation_absolute);
  instrument->_position_absolute[35] = _Master_var._position_absolute;
  instrument->_position_relative[35] = _Master_var._position_relative;
    _Master_var._position_relative_is_zero =  coords_test_zero(_Master_var._position_relative);
  instrument->counter_N[35]  = instrument->counter_P[35] = instrument->counter_P2[35] = 0;
  instrument->counter_AbsorbProp[35]= 0;
  return(0);
} /* _Master_setpos */

/* component stop=Union_stop() SETTING, POSITION/ROTATION */
int _stop_setpos(void)
{ /* sets initial component parameters, position and rotation */
  SIG_MESSAGE("[_stop_setpos] component stop=Union_stop() SETTING [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//union/Union_stop.comp:69]");
  stracpy(_stop_var._name, "stop", 16384);
  stracpy(_stop_var._type, "Union_stop", 16384);
  _stop_var._index=36;
  /* component stop=Union_stop() AT ROTATED */
  {
    Coords tc1, tc2;
    tc1 = coords_set(0,0,0);
    tc2 = coords_set(0,0,0);
    Rotation tr1;
    rot_set_rotation(tr1,0,0,0);
    rot_set_rotation(_stop_var._rotation_absolute,
      (0.0)*DEG2RAD, (0.0)*DEG2RAD, (0.0)*DEG2RAD);
    rot_transpose(_Master_var._rotation_absolute, tr1);
    rot_mul(_stop_var._rotation_absolute, tr1, _stop_var._rotation_relative);
    _stop_var._rotation_is_identity =  rot_test_identity(_stop_var._rotation_relative);
    _stop_var._position_absolute = coords_set(
      0, 0, 0);
    tc1 = coords_sub(_Master_var._position_absolute, _stop_var._position_absolute);
    _stop_var._position_relative = rot_apply(_stop_var._rotation_absolute, tc1);
  } /* stop=Union_stop() AT ROTATED */
  DEBUG_COMPONENT("stop", _stop_var._position_absolute, _stop_var._rotation_absolute);
  instrument->_position_absolute[36] = _stop_var._position_absolute;
  instrument->_position_relative[36] = _stop_var._position_relative;
    _stop_var._position_relative_is_zero =  coords_test_zero(_stop_var._position_relative);
  instrument->counter_N[36]  = instrument->counter_P[36] = instrument->counter_P2[36] = 0;
  instrument->counter_AbsorbProp[36]= 0;
  return(0);
} /* _stop_setpos */

/* component collimator_linear_2=Collimator_linear() SETTING, POSITION/ROTATION */
int _collimator_linear_2_setpos(void)
{ /* sets initial component parameters, position and rotation */
  SIG_MESSAGE("[_collimator_linear_2_setpos] component collimator_linear_2=Collimator_linear() SETTING [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//optics/Collimator_linear.comp:55]");
  stracpy(_collimator_linear_2_var._name, "collimator_linear_2", 16384);
  stracpy(_collimator_linear_2_var._type, "Collimator_linear", 16384);
  _collimator_linear_2_var._index=37;
  _collimator_linear_2_var._parameters.xmin = -0.02;
  _collimator_linear_2_var._parameters.xmax = 0.02;
  _collimator_linear_2_var._parameters.ymin = -0.05;
  _collimator_linear_2_var._parameters.ymax = 0.05;
  _collimator_linear_2_var._parameters.xwidth = 0.08;
  _collimator_linear_2_var._parameters.yheight = 0.2;
  _collimator_linear_2_var._parameters.length = 0.2;
  _collimator_linear_2_var._parameters.divergence = _instrument_var._parameters.col_div_2;
  _collimator_linear_2_var._parameters.transmission = 1;
  _collimator_linear_2_var._parameters.divergenceV = 0;


  /* component collimator_linear_2=Collimator_linear() AT ROTATED */
  {
    Coords tc1, tc2;
    tc1 = coords_set(0,0,0);
    tc2 = coords_set(0,0,0);
    Rotation tr1;
    rot_set_rotation(tr1,0,0,0);
    rot_set_rotation(tr1,
      (0.0)*DEG2RAD, (0.0)*DEG2RAD, (0.0)*DEG2RAD);
    rot_mul(tr1, _a4_var._rotation_absolute, _collimator_linear_2_var._rotation_absolute);
    rot_transpose(_Master_var._rotation_absolute, tr1);
    rot_mul(_collimator_linear_2_var._rotation_absolute, tr1, _collimator_linear_2_var._rotation_relative);
    _collimator_linear_2_var._rotation_is_identity =  rot_test_identity(_collimator_linear_2_var._rotation_relative);
    tc1 = coords_set(
      0, 0, 0.8);
    rot_transpose(_a4_var._rotation_absolute, tr1);
    tc2 = rot_apply(tr1, tc1);
    _collimator_linear_2_var._position_absolute = coords_add(_a4_var._position_absolute, tc2);
    tc1 = coords_sub(_Master_var._position_absolute, _collimator_linear_2_var._position_absolute);
    _collimator_linear_2_var._position_relative = rot_apply(_collimator_linear_2_var._rotation_absolute, tc1);
  } /* collimator_linear_2=Collimator_linear() AT ROTATED */
  DEBUG_COMPONENT("collimator_linear_2", _collimator_linear_2_var._position_absolute, _collimator_linear_2_var._rotation_absolute);
  instrument->_position_absolute[37] = _collimator_linear_2_var._position_absolute;
  instrument->_position_relative[37] = _collimator_linear_2_var._position_relative;
    _collimator_linear_2_var._position_relative_is_zero =  coords_test_zero(_collimator_linear_2_var._position_relative);
  instrument->counter_N[37]  = instrument->counter_P[37] = instrument->counter_P2[37] = 0;
  instrument->counter_AbsorbProp[37]= 0;
  return(0);
} /* _collimator_linear_2_setpos */

/* component PSD_after_sampel=PSD_monitor() SETTING, POSITION/ROTATION */
int _PSD_after_sampel_setpos(void)
{ /* sets initial component parameters, position and rotation */
  SIG_MESSAGE("[_PSD_after_sampel_setpos] component PSD_after_sampel=PSD_monitor() SETTING [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//monitors/PSD_monitor.comp:62]");
  stracpy(_PSD_after_sampel_var._name, "PSD_after_sampel", 16384);
  stracpy(_PSD_after_sampel_var._type, "PSD_monitor", 16384);
  _PSD_after_sampel_var._index=38;
  _PSD_after_sampel_var._parameters.nx = 128;
  _PSD_after_sampel_var._parameters.ny = 128;
  if("PSD_after_sampel" && strlen("PSD_after_sampel"))
    stracpy(_PSD_after_sampel_var._parameters.filename, "PSD_after_sampel" ? "PSD_after_sampel" : "", 16384);
  else 
  _PSD_after_sampel_var._parameters.filename[0]='\0';
  _PSD_after_sampel_var._parameters.xmin = -0.05;
  _PSD_after_sampel_var._parameters.xmax = 0.05;
  _PSD_after_sampel_var._parameters.ymin = -0.05;
  _PSD_after_sampel_var._parameters.ymax = 0.05;
  _PSD_after_sampel_var._parameters.xwidth = 0.1;
  _PSD_after_sampel_var._parameters.yheight = 0.2;
  _PSD_after_sampel_var._parameters.restore_neutron = 1;
  _PSD_after_sampel_var._parameters.nowritefile = 0;


  /* component PSD_after_sampel=PSD_monitor() AT ROTATED */
  {
    Coords tc1, tc2;
    tc1 = coords_set(0,0,0);
    tc2 = coords_set(0,0,0);
    Rotation tr1;
    rot_set_rotation(tr1,0,0,0);
    rot_set_rotation(tr1,
      (0.0)*DEG2RAD, (0.0)*DEG2RAD, (0.0)*DEG2RAD);
    rot_mul(tr1, _a4_var._rotation_absolute, _PSD_after_sampel_var._rotation_absolute);
    rot_transpose(_collimator_linear_2_var._rotation_absolute, tr1);
    rot_mul(_PSD_after_sampel_var._rotation_absolute, tr1, _PSD_after_sampel_var._rotation_relative);
    _PSD_after_sampel_var._rotation_is_identity =  rot_test_identity(_PSD_after_sampel_var._rotation_relative);
    tc1 = coords_set(
      0, 0, 1.16);
    rot_transpose(_a4_var._rotation_absolute, tr1);
    tc2 = rot_apply(tr1, tc1);
    _PSD_after_sampel_var._position_absolute = coords_add(_a4_var._position_absolute, tc2);
    tc1 = coords_sub(_collimator_linear_2_var._position_absolute, _PSD_after_sampel_var._position_absolute);
    _PSD_after_sampel_var._position_relative = rot_apply(_PSD_after_sampel_var._rotation_absolute, tc1);
  } /* PSD_after_sampel=PSD_monitor() AT ROTATED */
  DEBUG_COMPONENT("PSD_after_sampel", _PSD_after_sampel_var._position_absolute, _PSD_after_sampel_var._rotation_absolute);
  instrument->_position_absolute[38] = _PSD_after_sampel_var._position_absolute;
  instrument->_position_relative[38] = _PSD_after_sampel_var._position_relative;
    _PSD_after_sampel_var._position_relative_is_zero =  coords_test_zero(_PSD_after_sampel_var._position_relative);
  instrument->counter_N[38]  = instrument->counter_P[38] = instrument->counter_P2[38] = 0;
  instrument->counter_AbsorbProp[38]= 0;
  return(0);
} /* _PSD_after_sampel_setpos */

/* component monochromator_flat6=Monochromator_flat() SETTING, POSITION/ROTATION */
int _monochromator_flat6_setpos(void)
{ /* sets initial component parameters, position and rotation */
  SIG_MESSAGE("[_monochromator_flat6_setpos] component monochromator_flat6=Monochromator_flat() SETTING [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//optics/Monochromator_flat.comp:103]");
  stracpy(_monochromator_flat6_var._name, "monochromator_flat6", 16384);
  stracpy(_monochromator_flat6_var._type, "Monochromator_flat", 16384);
  _monochromator_flat6_var._index=39;
  _monochromator_flat6_var._parameters.zmin = -0.05;
  _monochromator_flat6_var._parameters.zmax = 0.05;
  _monochromator_flat6_var._parameters.ymin = -0.05;
  _monochromator_flat6_var._parameters.ymax = 0.05;
  _monochromator_flat6_var._parameters.zwidth = 0.15;
  _monochromator_flat6_var._parameters.yheight = 0.12;
  _monochromator_flat6_var._parameters.mosaich = 37;
  _monochromator_flat6_var._parameters.mosaicv = 37;
  _monochromator_flat6_var._parameters.r0 = 0.7;
  _monochromator_flat6_var._parameters.Q = 1.87325;
  _monochromator_flat6_var._parameters.DM = 3.35416;


  /* component monochromator_flat6=Monochromator_flat() AT ROTATED */
  {
    Coords tc1, tc2;
    tc1 = coords_set(0,0,0);
    tc2 = coords_set(0,0,0);
    Rotation tr1;
    rot_set_rotation(tr1,0,0,0);
    rot_set_rotation(tr1,
      (0)*DEG2RAD, (A5)*DEG2RAD, (0)*DEG2RAD);
    rot_mul(tr1, _a5_var._rotation_absolute, _monochromator_flat6_var._rotation_absolute);
    rot_transpose(_PSD_after_sampel_var._rotation_absolute, tr1);
    rot_mul(_monochromator_flat6_var._rotation_absolute, tr1, _monochromator_flat6_var._rotation_relative);
    _monochromator_flat6_var._rotation_is_identity =  rot_test_identity(_monochromator_flat6_var._rotation_relative);
    tc1 = coords_set(
      0, 0, 0);
    rot_transpose(_a5_var._rotation_absolute, tr1);
    tc2 = rot_apply(tr1, tc1);
    _monochromator_flat6_var._position_absolute = coords_add(_a5_var._position_absolute, tc2);
    tc1 = coords_sub(_PSD_after_sampel_var._position_absolute, _monochromator_flat6_var._position_absolute);
    _monochromator_flat6_var._position_relative = rot_apply(_monochromator_flat6_var._rotation_absolute, tc1);
  } /* monochromator_flat6=Monochromator_flat() AT ROTATED */
  DEBUG_COMPONENT("monochromator_flat6", _monochromator_flat6_var._position_absolute, _monochromator_flat6_var._rotation_absolute);
  instrument->_position_absolute[39] = _monochromator_flat6_var._position_absolute;
  instrument->_position_relative[39] = _monochromator_flat6_var._position_relative;
    _monochromator_flat6_var._position_relative_is_zero =  coords_test_zero(_monochromator_flat6_var._position_relative);
  instrument->counter_N[39]  = instrument->counter_P[39] = instrument->counter_P2[39] = 0;
  instrument->counter_AbsorbProp[39]= 0;
  return(0);
} /* _monochromator_flat6_setpos */

/* component a6=Arm() SETTING, POSITION/ROTATION */
int _a6_setpos(void)
{ /* sets initial component parameters, position and rotation */
  SIG_MESSAGE("[_a6_setpos] component a6=Arm() SETTING [Arm:0]");
  stracpy(_a6_var._name, "a6", 16384);
  stracpy(_a6_var._type, "Arm", 16384);
  _a6_var._index=40;
  /* component a6=Arm() AT ROTATED */
  {
    Coords tc1, tc2;
    tc1 = coords_set(0,0,0);
    tc2 = coords_set(0,0,0);
    Rotation tr1;
    rot_set_rotation(tr1,0,0,0);
    rot_set_rotation(tr1,
      (0)*DEG2RAD, (2 * A5)*DEG2RAD, (0)*DEG2RAD);
    rot_mul(tr1, _a5_var._rotation_absolute, _a6_var._rotation_absolute);
    rot_transpose(_monochromator_flat6_var._rotation_absolute, tr1);
    rot_mul(_a6_var._rotation_absolute, tr1, _a6_var._rotation_relative);
    _a6_var._rotation_is_identity =  rot_test_identity(_a6_var._rotation_relative);
    tc1 = coords_set(
      0, 0, 0);
    rot_transpose(_a5_var._rotation_absolute, tr1);
    tc2 = rot_apply(tr1, tc1);
    _a6_var._position_absolute = coords_add(_a5_var._position_absolute, tc2);
    tc1 = coords_sub(_monochromator_flat6_var._position_absolute, _a6_var._position_absolute);
    _a6_var._position_relative = rot_apply(_a6_var._rotation_absolute, tc1);
  } /* a6=Arm() AT ROTATED */
  DEBUG_COMPONENT("a6", _a6_var._position_absolute, _a6_var._rotation_absolute);
  instrument->_position_absolute[40] = _a6_var._position_absolute;
  instrument->_position_relative[40] = _a6_var._position_relative;
    _a6_var._position_relative_is_zero =  coords_test_zero(_a6_var._position_relative);
  instrument->counter_N[40]  = instrument->counter_P[40] = instrument->counter_P2[40] = 0;
  instrument->counter_AbsorbProp[40]= 0;
  return(0);
} /* _a6_setpos */

/* component Detector=PSD_monitor() SETTING, POSITION/ROTATION */
int _Detector_setpos(void)
{ /* sets initial component parameters, position and rotation */
  SIG_MESSAGE("[_Detector_setpos] component Detector=PSD_monitor() SETTING [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//monitors/PSD_monitor.comp:62]");
  stracpy(_Detector_var._name, "Detector", 16384);
  stracpy(_Detector_var._type, "PSD_monitor", 16384);
  _Detector_var._index=41;
  _Detector_var._parameters.nx = 128;
  _Detector_var._parameters.ny = 128;
  if("Detector" && strlen("Detector"))
    stracpy(_Detector_var._parameters.filename, "Detector" ? "Detector" : "", 16384);
  else 
  _Detector_var._parameters.filename[0]='\0';
  _Detector_var._parameters.xmin = -0.05;
  _Detector_var._parameters.xmax = 0.05;
  _Detector_var._parameters.ymin = -0.05;
  _Detector_var._parameters.ymax = 0.05;
  _Detector_var._parameters.xwidth = detector_x;
  _Detector_var._parameters.yheight = detector_y;
  _Detector_var._parameters.restore_neutron = 0;
  _Detector_var._parameters.nowritefile = 0;


  /* component Detector=PSD_monitor() AT ROTATED */
  {
    Coords tc1, tc2;
    tc1 = coords_set(0,0,0);
    tc2 = coords_set(0,0,0);
    Rotation tr1;
    rot_set_rotation(tr1,0,0,0);
    rot_set_rotation(tr1,
      (0.0)*DEG2RAD, (0.0)*DEG2RAD, (0.0)*DEG2RAD);
    rot_mul(tr1, _a6_var._rotation_absolute, _Detector_var._rotation_absolute);
    rot_transpose(_monochromator_flat6_var._rotation_absolute, tr1);
    rot_mul(_Detector_var._rotation_absolute, tr1, _Detector_var._rotation_relative);
    _Detector_var._rotation_is_identity =  rot_test_identity(_Detector_var._rotation_relative);
    tc1 = coords_set(
      0, 0, 0.35);
    rot_transpose(_a6_var._rotation_absolute, tr1);
    tc2 = rot_apply(tr1, tc1);
    _Detector_var._position_absolute = coords_add(_a6_var._position_absolute, tc2);
    tc1 = coords_sub(_monochromator_flat6_var._position_absolute, _Detector_var._position_absolute);
    _Detector_var._position_relative = rot_apply(_Detector_var._rotation_absolute, tc1);
  } /* Detector=PSD_monitor() AT ROTATED */
  DEBUG_COMPONENT("Detector", _Detector_var._position_absolute, _Detector_var._rotation_absolute);
  instrument->_position_absolute[41] = _Detector_var._position_absolute;
  instrument->_position_relative[41] = _Detector_var._position_relative;
    _Detector_var._position_relative_is_zero =  coords_test_zero(_Detector_var._position_relative);
  instrument->counter_N[41]  = instrument->counter_P[41] = instrument->counter_P2[41] = 0;
  instrument->counter_AbsorbProp[41]= 0;
  return(0);
} /* _Detector_setpos */

_class_Progress_bar *class_Progress_bar_init(_class_Progress_bar *_comp
) {
  #define profile (_comp->_parameters.profile)
  #define percent (_comp->_parameters.percent)
  #define flag_save (_comp->_parameters.flag_save)
  #define minutes (_comp->_parameters.minutes)
  #define IntermediateCnts (_comp->_parameters.IntermediateCnts)
  #define StartTime (_comp->_parameters.StartTime)
  #define EndTime (_comp->_parameters.EndTime)
  #define CurrentTime (_comp->_parameters.CurrentTime)
  SIG_MESSAGE("[_origin_init] component origin=Progress_bar() INITIALISE [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//misc/Progress_bar.comp:57]");

IntermediateCnts=0;
StartTime=0;
EndTime=0;
CurrentTime=0;

fprintf(stdout, "[%s] Initialize\n", instrument_name);
  if (percent*mcget_ncount()/100 < 1e5) {
    percent=1e5*100.0/mcget_ncount();
  }
  #ifdef OPENACC
  time(&StartTime);
  #endif
  #undef profile
  #undef percent
  #undef flag_save
  #undef minutes
  #undef IntermediateCnts
  #undef StartTime
  #undef EndTime
  #undef CurrentTime
  return(_comp);
} /* class_Progress_bar_init */

_class_Source_Maxwell_3 *class_Source_Maxwell_3_init(_class_Source_Maxwell_3 *_comp
) {
  #define size (_comp->_parameters.size)
  #define yheight (_comp->_parameters.yheight)
  #define xwidth (_comp->_parameters.xwidth)
  #define Lmin (_comp->_parameters.Lmin)
  #define Lmax (_comp->_parameters.Lmax)
  #define dist (_comp->_parameters.dist)
  #define focus_xw (_comp->_parameters.focus_xw)
  #define focus_yh (_comp->_parameters.focus_yh)
  #define T1 (_comp->_parameters.T1)
  #define T2 (_comp->_parameters.T2)
  #define T3 (_comp->_parameters.T3)
  #define I1 (_comp->_parameters.I1)
  #define I2 (_comp->_parameters.I2)
  #define I3 (_comp->_parameters.I3)
  #define target_index (_comp->_parameters.target_index)
  #define lambda0 (_comp->_parameters.lambda0)
  #define dlambda (_comp->_parameters.dlambda)
  #define l_range (_comp->_parameters.l_range)
  #define w_mult (_comp->_parameters.w_mult)
  #define w_source (_comp->_parameters.w_source)
  #define h_source (_comp->_parameters.h_source)
  SIG_MESSAGE("[_source_maxwell_3_init] component source_maxwell_3=Source_Maxwell_3() INITIALISE [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//sources/Source_Maxwell_3.comp:88]");

  if (target_index && !dist)
  {
    Coords ToTarget;
    double tx,ty,tz;
    ToTarget = coords_sub(POS_A_COMP_INDEX(INDEX_CURRENT_COMP+target_index),POS_A_CURRENT_COMP);
    ToTarget = rot_apply(ROT_A_CURRENT_COMP, ToTarget);
    coords_get(ToTarget, &tx, &ty, &tz);
    dist=sqrt(tx*tx+ty*ty+tz*tz);
  }

  if (size>0) {
    w_source = h_source = size;
  } else {
    w_source = xwidth;
    h_source = yheight;
  }
  if (lambda0) {
    Lmin=lambda0-dlambda;
    Lmax=lambda0+dlambda;
  }
  l_range = Lmax-Lmin;
  w_mult = w_source*h_source*1.0e4;     /* source area correction */
  w_mult *= l_range;            /* wavelength range correction */
  w_mult *= 1.0/mcget_ncount();   /* correct for # neutron rays */

  if (w_source <0 || h_source < 0 || Lmin <= 0 || Lmax <= 0 || dist <= 0 || T1 <= 0 || T2 <= 0|| T3 <= 0 || Lmax<=Lmin) {
      printf("Source_Maxwell_3: %s: Error in input parameter values!\n"
             "ERROR          Exiting\n",
           NAME_CURRENT_COMP);
      exit(0);
  }

  #undef size
  #undef yheight
  #undef xwidth
  #undef Lmin
  #undef Lmax
  #undef dist
  #undef focus_xw
  #undef focus_yh
  #undef T1
  #undef T2
  #undef T3
  #undef I1
  #undef I2
  #undef I3
  #undef target_index
  #undef lambda0
  #undef dlambda
  #undef l_range
  #undef w_mult
  #undef w_source
  #undef h_source
  return(_comp);
} /* class_Source_Maxwell_3_init */

_class_Guide *class_Guide_init(_class_Guide *_comp
) {
  #define reflect (_comp->_parameters.reflect)
  #define w1 (_comp->_parameters.w1)
  #define h1 (_comp->_parameters.h1)
  #define w2 (_comp->_parameters.w2)
  #define h2 (_comp->_parameters.h2)
  #define l (_comp->_parameters.l)
  #define R0 (_comp->_parameters.R0)
  #define Qc (_comp->_parameters.Qc)
  #define alpha (_comp->_parameters.alpha)
  #define m (_comp->_parameters.m)
  #define W (_comp->_parameters.W)
  #define pTable (_comp->_parameters.pTable)
  #define table_present (_comp->_parameters.table_present)
  SIG_MESSAGE("[_guide_init] component guide=Guide() INITIALISE [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//optics/Guide.comp:74]");

if (mcgravitation) fprintf(stderr,"WARNING: Guide: %s: "
    "This component produces wrong results with gravitation !\n"
    "Use Guide_gravity.\n",
    NAME_CURRENT_COMP);

  if (!w2) w2=w1;
  if (!h2) h2=h1;

  if (reflect && strlen(reflect) && strcmp(reflect,"NULL") && strcmp(reflect,"0")) {
    if (Table_Read(&pTable, reflect, 1) <= 0) /* read 1st block data from file into pTable */
      exit(fprintf(stderr,"Guide: %s: can not read file %s\n", NAME_CURRENT_COMP, reflect));
    table_present=1;
  } else {
    table_present=0;
    if (W < 0 || R0 < 0 || Qc < 0 || m < 0)
    { fprintf(stderr,"Guide: %s: W R0 Qc must be >0.\n", NAME_CURRENT_COMP);
      exit(-1); }
  }
  #undef reflect
  #undef w1
  #undef h1
  #undef w2
  #undef h2
  #undef l
  #undef R0
  #undef Qc
  #undef alpha
  #undef m
  #undef W
  #undef pTable
  #undef table_present
  return(_comp);
} /* class_Guide_init */

_class_PSD_monitor *class_PSD_monitor_init(_class_PSD_monitor *_comp
) {
  #define nx (_comp->_parameters.nx)
  #define ny (_comp->_parameters.ny)
  #define filename (_comp->_parameters.filename)
  #define xmin (_comp->_parameters.xmin)
  #define xmax (_comp->_parameters.xmax)
  #define ymin (_comp->_parameters.ymin)
  #define ymax (_comp->_parameters.ymax)
  #define xwidth (_comp->_parameters.xwidth)
  #define yheight (_comp->_parameters.yheight)
  #define restore_neutron (_comp->_parameters.restore_neutron)
  #define nowritefile (_comp->_parameters.nowritefile)
  #define PSD_N (_comp->_parameters.PSD_N)
  #define PSD_p (_comp->_parameters.PSD_p)
  #define PSD_p2 (_comp->_parameters.PSD_p2)
  SIG_MESSAGE("[_PSD_before_monochromator_init] component PSD_before_monochromator=PSD_monitor() INITIALISE [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//monitors/PSD_monitor.comp:62]");

  if (xwidth  > 0) { xmax = xwidth/2;  xmin = -xmax; }
  if (yheight > 0) { ymax = yheight/2; ymin = -ymax; }

  if ((xmin >= xmax) || (ymin >= ymax)){
    printf("PSD_monitor: %s: Null detection area !\n"
           "ERROR        (xwidth,yheight,xmin,xmax,ymin,ymax). Exiting",
    NAME_CURRENT_COMP);
    exit(0);
  }

  PSD_N = create_darr2d(nx, ny);
  PSD_p = create_darr2d(nx, ny);
  PSD_p2 = create_darr2d(nx, ny);

  // Use instance name for monitor output if no input was given
  if (!strcmp(filename,"\0")) sprintf(filename,NAME_CURRENT_COMP);
  #undef nx
  #undef ny
  #undef filename
  #undef xmin
  #undef xmax
  #undef ymin
  #undef ymax
  #undef xwidth
  #undef yheight
  #undef restore_neutron
  #undef nowritefile
  #undef PSD_N
  #undef PSD_p
  #undef PSD_p2
  return(_comp);
} /* class_PSD_monitor_init */

_class_Monochromator_flat *class_Monochromator_flat_init(_class_Monochromator_flat *_comp
) {
  #define zmin (_comp->_parameters.zmin)
  #define zmax (_comp->_parameters.zmax)
  #define ymin (_comp->_parameters.ymin)
  #define ymax (_comp->_parameters.ymax)
  #define zwidth (_comp->_parameters.zwidth)
  #define yheight (_comp->_parameters.yheight)
  #define mosaich (_comp->_parameters.mosaich)
  #define mosaicv (_comp->_parameters.mosaicv)
  #define r0 (_comp->_parameters.r0)
  #define Q (_comp->_parameters.Q)
  #define DM (_comp->_parameters.DM)
  #define mos_rms_y (_comp->_parameters.mos_rms_y)
  #define mos_rms_z (_comp->_parameters.mos_rms_z)
  #define mos_rms_max (_comp->_parameters.mos_rms_max)
  #define mono_Q (_comp->_parameters.mono_Q)
  SIG_MESSAGE("[_monochromator_flat1_init] component monochromator_flat1=Monochromator_flat() INITIALISE [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//optics/Monochromator_flat.comp:103]");

  mos_rms_y = MIN2RAD*mosaicv/sqrt(8*log(2));
  mos_rms_z = MIN2RAD*mosaich/sqrt(8*log(2));
  mos_rms_max = mos_rms_y > mos_rms_z ? mos_rms_y : mos_rms_z;

  mono_Q = Q;
  if (DM != 0) mono_Q = 2*PI/DM;

  if (zwidth>0)  { zmax = zwidth/2;  zmin=-zmax; }
  if (yheight>0) { ymax = yheight/2; ymin=-ymax; }

  if (zmin==zmax || ymin==ymax)
    exit(fprintf(stderr, "Monochromator_flat: %s : Surface is null (zmin,zmax,ymin,ymax)\n", NAME_CURRENT_COMP));
  #undef zmin
  #undef zmax
  #undef ymin
  #undef ymax
  #undef zwidth
  #undef yheight
  #undef mosaich
  #undef mosaicv
  #undef r0
  #undef Q
  #undef DM
  #undef mos_rms_y
  #undef mos_rms_z
  #undef mos_rms_max
  #undef mono_Q
  return(_comp);
} /* class_Monochromator_flat_init */

_class_Collimator_linear *class_Collimator_linear_init(_class_Collimator_linear *_comp
) {
  #define xmin (_comp->_parameters.xmin)
  #define xmax (_comp->_parameters.xmax)
  #define ymin (_comp->_parameters.ymin)
  #define ymax (_comp->_parameters.ymax)
  #define xwidth (_comp->_parameters.xwidth)
  #define yheight (_comp->_parameters.yheight)
  #define length (_comp->_parameters.length)
  #define divergence (_comp->_parameters.divergence)
  #define transmission (_comp->_parameters.transmission)
  #define divergenceV (_comp->_parameters.divergenceV)
  #define slope (_comp->_parameters.slope)
  #define slopeV (_comp->_parameters.slopeV)
  SIG_MESSAGE("[_collimator_linear_init] component collimator_linear=Collimator_linear() INITIALISE [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//optics/Collimator_linear.comp:55]");

slope = tan(MIN2RAD*divergence);
  slopeV= tan(MIN2RAD*divergenceV);
  if (xwidth  > 0) { xmax = xwidth/2;  xmin = -xmax; }
  if (yheight > 0) { ymax = yheight/2; ymin = -ymax; }

  if ((xmin >= xmax) || (ymin >= ymax)) {
    printf("Collimator_linear: %s: Null slit opening area !\n"
	         "ERROR              (xwidth,yheight,xmin,xmax,ymin,ymax). Exiting",
           NAME_CURRENT_COMP);
    exit(0);
  }

  #undef xmin
  #undef xmax
  #undef ymin
  #undef ymax
  #undef xwidth
  #undef yheight
  #undef length
  #undef divergence
  #undef transmission
  #undef divergenceV
  #undef slope
  #undef slopeV
  return(_comp);
} /* class_Collimator_linear_init */

_class_Slit *class_Slit_init(_class_Slit *_comp
) {
  #define xmin (_comp->_parameters.xmin)
  #define xmax (_comp->_parameters.xmax)
  #define ymin (_comp->_parameters.ymin)
  #define ymax (_comp->_parameters.ymax)
  #define radius (_comp->_parameters.radius)
  #define xwidth (_comp->_parameters.xwidth)
  #define yheight (_comp->_parameters.yheight)
  #define isradial (_comp->_parameters.isradial)
  SIG_MESSAGE("[_slit_init] component slit=Slit() INITIALISE [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//optics/Slit.comp:66]");

if (is_unset(radius)){
  isradial=0;
  if (all_set(3, xwidth, xmin, xmax)){
    slit_error_if(xwidth != xmax - xmin, "specifying xwidth, xmin and xmax requires consistent parameters", NAME_CURRENT_COMP);
  } else {
    slit_error_if(is_unset(xwidth) && any_unset(2, xmin, xmax), "specify either xwidth or xmin & xmax", NAME_CURRENT_COMP);
  }
  if (all_set(3, yheight, ymin, ymax)){
    slit_error_if(yheight != ymax - ymin, "specifying yheight, ymin and ymax requires consistent parameters", NAME_CURRENT_COMP);
  } else {
    slit_error_if(is_unset(yheight) && any_unset(2, ymin, ymax), "specify either yheight or ymin & ymax", NAME_CURRENT_COMP);
  }
  if (is_unset(xmin)) { // xmax also unset but xwidth *is* set
    xmax = xwidth/2;
    xmin = -xmax;
  }
  if (is_unset(ymin)) { // ymax also unset but yheight *is* set
    ymax = yheight/2;
    ymin = -ymax;
  }
  slit_warning_if(xmin == xmax || ymin == ymax, "Running with CLOSED rectangular slit - is this intentional?", NAME_CURRENT_COMP);
} else {
  isradial=1;
  slit_error_if(any_set(6, xwidth, xmin, xmax, yheight, ymin, ymax), 
                "specify radius OR width and height parameters", NAME_CURRENT_COMP);
  slit_warning_if(radius == 0., "Running with CLOSED radial slit - is this intentional?", NAME_CURRENT_COMP);
}

  #undef xmin
  #undef xmax
  #undef ymin
  #undef ymax
  #undef radius
  #undef xwidth
  #undef yheight
  #undef isradial
  return(_comp);
} /* class_Slit_init */

_class_E_monitor *class_E_monitor_init(_class_E_monitor *_comp
) {
  #define nE (_comp->_parameters.nE)
  #define filename (_comp->_parameters.filename)
  #define xmin (_comp->_parameters.xmin)
  #define xmax (_comp->_parameters.xmax)
  #define ymin (_comp->_parameters.ymin)
  #define ymax (_comp->_parameters.ymax)
  #define nowritefile (_comp->_parameters.nowritefile)
  #define xwidth (_comp->_parameters.xwidth)
  #define yheight (_comp->_parameters.yheight)
  #define Emin (_comp->_parameters.Emin)
  #define Emax (_comp->_parameters.Emax)
  #define restore_neutron (_comp->_parameters.restore_neutron)
  #define E_N (_comp->_parameters.E_N)
  #define E_p (_comp->_parameters.E_p)
  #define E_p2 (_comp->_parameters.E_p2)
  #define S_p (_comp->_parameters.S_p)
  #define S_pE (_comp->_parameters.S_pE)
  #define S_pE2 (_comp->_parameters.S_pE2)
  SIG_MESSAGE("[_Emon_before_sampel_init] component Emon_before_sampel=E_monitor() INITIALISE [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//monitors/E_monitor.comp:69]");

  int i;

  if (xwidth  > 0) { xmax = xwidth/2;  xmin = -xmax; }
  if (yheight > 0) { ymax = yheight/2; ymin = -ymax; }

  if ((xmin >= xmax) || (ymin >= ymax)) {
    printf("E_monitor: %s: Null detection area !\n"
           "ERROR      (xwidth,yheight,xmin,xmax,ymin,ymax). Exiting",
           NAME_CURRENT_COMP);
    exit(0);
  }

  E_N = create_darr1d(nE);
  E_p = create_darr1d(nE);
  E_p2 = create_darr1d(nE);

  for (i=0; i<nE; i++)
  {
    E_N[i] = 0;
    E_p[i] = 0;
    E_p2[i] = 0;
  }
  S_p = S_pE = S_pE2 = 0;

  // Use instance name for monitor output if no input was given
  if (!strcmp(filename,"\0")) sprintf(filename,NAME_CURRENT_COMP);
  #undef nE
  #undef filename
  #undef xmin
  #undef xmax
  #undef ymin
  #undef ymax
  #undef nowritefile
  #undef xwidth
  #undef yheight
  #undef Emin
  #undef Emax
  #undef restore_neutron
  #undef E_N
  #undef E_p
  #undef E_p2
  #undef S_p
  #undef S_pE
  #undef S_pE2
  return(_comp);
} /* class_E_monitor_init */

_class_Union_init *class_Union_init_init(_class_Union_init *_comp
) {
  #define global_positions_to_transform_list (_comp->_parameters.global_positions_to_transform_list)
  #define global_rotations_to_transform_list (_comp->_parameters.global_rotations_to_transform_list)
  #define global_process_list (_comp->_parameters.global_process_list)
  #define global_material_list (_comp->_parameters.global_material_list)
  #define global_geometry_list (_comp->_parameters.global_geometry_list)
  #define global_all_volume_logger_list (_comp->_parameters.global_all_volume_logger_list)
  #define global_specific_volumes_logger_list (_comp->_parameters.global_specific_volumes_logger_list)
  #define global_all_volume_abs_logger_list (_comp->_parameters.global_all_volume_abs_logger_list)
  #define global_specific_volumes_abs_logger_list (_comp->_parameters.global_specific_volumes_abs_logger_list)
  #define global_tagging_conditional_list (_comp->_parameters.global_tagging_conditional_list)
  #define global_master_list (_comp->_parameters.global_master_list)
  #define global_mantid_min_pixel_id (_comp->_parameters.global_mantid_min_pixel_id)
  SIG_MESSAGE("[_init_init] component init=Union_init() INITIALISE [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//union/Union_init.comp:113]");

global_positions_to_transform_list.num_elements = 0;
global_positions_to_transform_list.positions = NULL;

global_rotations_to_transform_list.num_elements = 0;
global_rotations_to_transform_list.rotations = NULL;

global_process_list.num_elements = 0;
global_process_list.elements = NULL;

global_material_list.num_elements = 0;
global_material_list.elements = NULL;

global_geometry_list.num_elements = 0;
global_geometry_list.elements = NULL;

global_all_volume_logger_list.num_elements = 0;
global_all_volume_logger_list.elements = NULL;

global_specific_volumes_logger_list.num_elements = 0;
global_specific_volumes_logger_list.elements = NULL;

global_all_volume_abs_logger_list.num_elements = 0;
global_all_volume_abs_logger_list.elements = NULL;

global_specific_volumes_abs_logger_list.num_elements = 0;
global_specific_volumes_abs_logger_list.elements = NULL;

global_tagging_conditional_list.num_elements = 0;
global_tagging_conditional_list.elements = NULL;

global_master_list.num_elements = 0;
global_master_list.elements = NULL;

global_mantid_min_pixel_id = 0;
  #undef global_positions_to_transform_list
  #undef global_rotations_to_transform_list
  #undef global_process_list
  #undef global_material_list
  #undef global_geometry_list
  #undef global_all_volume_logger_list
  #undef global_specific_volumes_logger_list
  #undef global_all_volume_abs_logger_list
  #undef global_specific_volumes_abs_logger_list
  #undef global_tagging_conditional_list
  #undef global_master_list
  #undef global_mantid_min_pixel_id
  return(_comp);
} /* class_Union_init_init */

_class_Incoherent_process *class_Incoherent_process_init(_class_Incoherent_process *_comp
) {
  #define sigma (_comp->_parameters.sigma)
  #define f_QE (_comp->_parameters.f_QE)
  #define gamma (_comp->_parameters.gamma)
  #define packing_factor (_comp->_parameters.packing_factor)
  #define unit_cell_volume (_comp->_parameters.unit_cell_volume)
  #define interact_fraction (_comp->_parameters.interact_fraction)
  #define init (_comp->_parameters.init)
  #define global_process_element (_comp->_parameters.global_process_element)
  #define This_process (_comp->_parameters.This_process)
  #define Incoherent_storage (_comp->_parameters.Incoherent_storage)
  #define effective_my_scattering (_comp->_parameters.effective_my_scattering)
  SIG_MESSAGE("[_Vanadium_incoherent_Al_init] component Vanadium_incoherent_Al=Incoherent_process() INITIALISE [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//union/Incoherent_process.comp:142]");

  // Initialize done in the component
  effective_my_scattering = ((packing_factor/unit_cell_volume) * 100 * sigma);
  Incoherent_storage.my_scattering = effective_my_scattering;
  
  Incoherent_storage.QE_sampling_frequency = f_QE;
  Incoherent_storage.lorentzian_width = gamma;

  // Need to specify if this process is isotropic
  This_process.non_isotropic_rot_index = -1; // Yes (powder)
  //This_process.non_isotropic_rot_index =  1;  // No (single crystal)

  // The type of the process must be saved in the global enum process
  This_process.eProcess = Incoherent;

  // Packing the data into a structure that is transported to the main component
  sprintf(This_process.name,NAME_CURRENT_COMP);
  This_process.process_p_interact = interact_fraction;
  This_process.data_transfer.pointer_to_a_Incoherent_physics_storage_struct = &Incoherent_storage;
  //This_process.data_transfer.pointer_to_a_Incoherent_physics_storage_struct->my_scattering = effective_my_scattering;
  This_process.probability_for_scattering_function = &Incoherent_physics_my;
  This_process.scattering_function = &Incoherent_physics_scattering;

  // This will be the same for all process's, and can thus be moved to an include.
  sprintf(global_process_element.name,NAME_CURRENT_COMP);
  global_process_element.component_index = INDEX_CURRENT_COMP;
  global_process_element.p_scattering_process = &This_process;

  if (_getcomp_index(init) < 0) {
    fprintf(stderr,"Incoherent_process:%s: Error identifying Union_init component, %s is not a known component name.\n",
            NAME_CURRENT_COMP, init);
    exit(-1);
  }

  struct pointer_to_global_process_list *global_process_list = COMP_GETPAR3(Union_init, init, global_process_list);
  add_element_to_process_list(global_process_list, global_process_element);
  #undef sigma
  #undef f_QE
  #undef gamma
  #undef packing_factor
  #undef unit_cell_volume
  #undef interact_fraction
  #undef init
  #undef global_process_element
  #undef This_process
  #undef Incoherent_storage
  #undef effective_my_scattering
  return(_comp);
} /* class_Incoherent_process_init */

_class_Powder_process *class_Powder_process_init(_class_Powder_process *_comp
) {
  #define reflections (_comp->_parameters.reflections)
  #define packing_factor (_comp->_parameters.packing_factor)
  #define Vc (_comp->_parameters.Vc)
  #define delta_d_d (_comp->_parameters.delta_d_d)
  #define DW (_comp->_parameters.DW)
  #define nb_atoms (_comp->_parameters.nb_atoms)
  #define d_phi (_comp->_parameters.d_phi)
  #define density (_comp->_parameters.density)
  #define weight (_comp->_parameters.weight)
  #define barns (_comp->_parameters.barns)
  #define Strain (_comp->_parameters.Strain)
  #define interact_fraction (_comp->_parameters.interact_fraction)
  #define format (_comp->_parameters.format)
  #define init (_comp->_parameters.init)
  #define global_process_element (_comp->_parameters.global_process_element)
  #define This_process (_comp->_parameters.This_process)
  #define Powder_storage (_comp->_parameters.Powder_storage)
  #define line_info (_comp->_parameters.line_info)
  #define effective_my_scattering (_comp->_parameters.effective_my_scattering)
  #define columns (_comp->_parameters.columns)
  SIG_MESSAGE("[_Al_Powder_init] component Al_Powder=Powder_process() INITIALISE [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//union/Powder_process.comp:650]");


  // Initialize done in the component
  columns = format;
  
  // Copy from PowderN component
  int i=0;
  struct line_data_union *L;
  line_info.Dd       = delta_d_d;
  line_info.DWfactor = DW;
  line_info.V_0      = Vc;
  line_info.rho      = density;
  line_info.at_weight= weight;
  line_info.at_nb    = nb_atoms;
  line_info.sigma_a  = 0; // This inputs are not needed, as absorption is handled elsewhere
  line_info.sigma_i  = 0; // This input is not needed, as incoherent scattering is handled elsewhere
  line_info.flag_barns=barns;
  //line_info.shape    = 0;
  line_info.flag_warning=0;
  line_info.Epsilon  = Strain;
  line_info.radius_i =line_info.xwidth_i=line_info.yheight_i=line_info.zdepth_i=0;
  line_info.v  = 0;
  line_info.Nq = 0;
  //line_info.v_min = FLT_MAX; line_info.v_max = 0;
  line_info.v_min = 10000000000; line_info.v_max = 0;
  line_info.neutron_passed=0;
  line_info.nb_reuses = line_info.nb_refl = line_info.nb_refl_count = 0;
  line_info.xs_compute= line_info.xs_reuse= line_info.xs_calls =0;
  for (i=0; i< 9; i++) line_info.column_order[i] = columns[i];
  strncpy(line_info.compname, NAME_CURRENT_COMP, 256);

  // p_interact handled elsewhere
  //if (p_interact) {
  //  if (p_interact < p_inc) { double tmp=p_interact; p_interact=p_inc; p_inc=tmp; }
  //  p_transmit = 1-p_interact-p_inc;
  //}

  if (reflections && strlen(reflections) && strcmp(reflections, "NULL") && strcmp(reflections, "0")) {
    i = read_line_data_union(reflections, &line_info);
    if (i == 0)
      exit(fprintf(stderr,"PowderN: %s: reflection file %s is not valid.\n"
                          "ERROR    Please check file format (laz or lau).\n", NAME_CURRENT_COMP, reflections));
  }

  /* compute the scattering unit density from material weight and density */
  /* the weight of the scattering element is the chemical formula molecular weight
   * times the nb of chemical formulae in the scattering element (nb_atoms) */
  if (!line_info.V_0 && line_info.at_nb > 0
    && line_info.at_weight > 0 && line_info.rho > 0) {
    /* molar volume [cm^3/mol] = weight [g/mol] / density [g/cm^3] */
    /* atom density per Angs^3 = [mol/cm^3] * N_Avogadro *(1e-8)^3 */
    line_info.V_0 = line_info.at_nb
      /(line_info.rho/line_info.at_weight/1e24*6.02214199e23);
  }

  /* the scattering unit cross sections are the chemical formula onces
   * times the nb of chemical formulae in the scattering element */
  if (line_info.at_nb > 0) {
    line_info.sigma_a *= line_info.at_nb; line_info.sigma_i *= line_info.at_nb;
  }

  if (line_info.V_0 <= 0)
    fprintf(stderr,"PowderN: %s: density/unit cell volume is NULL (Vc). Unactivating component.\n", NAME_CURRENT_COMP);


  if (line_info.flag_barns) { /* Factor 100 to convert from barns to fm^2 */
    line_info.XsectionFactor = 100;
  } else {
    line_info.XsectionFactor = 1;
  }

  if (line_info.V_0 && i) {
    L = line_info.list;

    line_info.q_v = malloc(line_info.count*sizeof(double));
    line_info.w_v = malloc(line_info.count*sizeof(double));
    line_info.my_s_v2 = malloc(line_info.count*sizeof(double));
    if (!line_info.q_v || !line_info.w_v || !line_info.my_s_v2)
      exit(fprintf(stderr,"PowderN: %s: ERROR allocating memory (init)\n", NAME_CURRENT_COMP));
    for(i=0; i<line_info.count; i++)
    {
      line_info.my_s_v2[i] = 4*PI*PI*PI*packing_factor*(L[i].DWfactor ? L[i].DWfactor : 1)
                 /(line_info.V_0*line_info.V_0*V2K*V2K)
                 *(L[i].j * L[i].F2 / L[i].q)*line_info.XsectionFactor;
      /* Is not yet divided by v^2 */
      /* Squires [3.103] */
      line_info.q_v[i] = L[i].q*K2V;
      line_info.w_v[i] = L[i].w;
    }
  }
  if (line_info.V_0) {
    /* Is not yet divided by v */
    line_info.my_a_v = packing_factor*line_info.sigma_a/line_info.V_0*2200*100;   // Factor 100 to convert from barns to fm^2
    line_info.my_inc = packing_factor*line_info.sigma_i/line_info.V_0*100;   // Factor 100 to convert from barns to fm^2

    printf("PowderN: %s: Vc=%g [Angs] sigma_abs=%g [barn] sigma_inc=%g [barn] reflections=%s\n",
      NAME_CURRENT_COMP, line_info.V_0, line_info.sigma_a, line_info.sigma_i, reflections && strlen(reflections) ? reflections : "NULL");
  }

  //printf("INTIALIZE line_info.v = %f, line_info.v_min = %f, line_info.v_max = %f, line_info.neutron_passed = %f\n",line_info.v,line_info.v_min,line_info.v_max,line_info.neutron_passed);

  Powder_storage.line_info_storage = &line_info;
  Powder_storage.vertical_angular_limit = d_phi;
  
  // Need to specify if this process is isotropic
  This_process.non_isotropic_rot_index = -1; // Yes (powder)
  //This_process.non_isotropic_rot_index =  1;  // No (single crystal)

  // The type of the process must be saved in the global enum process
  This_process.eProcess = Powder;

  // Packing the data into a structure that is transported to the main component
  This_process.data_transfer.pointer_to_a_Powder_physics_storage_struct = &Powder_storage;
  This_process.data_transfer.pointer_to_a_Powder_physics_storage_struct->my_scattering = effective_my_scattering;
  This_process.probability_for_scattering_function = &Powder_physics_my;
  This_process.scattering_function = &Powder_physics_scattering;
    
  // This will be the same for all process's, and can thus be moved to an include.
  This_process.process_p_interact = interact_fraction;
  sprintf(This_process.name,NAME_CURRENT_COMP);
  rot_copy(This_process.rotation_matrix,ROT_A_CURRENT_COMP);
  sprintf(global_process_element.name,NAME_CURRENT_COMP);
  global_process_element.component_index = INDEX_CURRENT_COMP;
  global_process_element.p_scattering_process = &This_process;

if (_getcomp_index(init) < 0) {
fprintf(stderr,"Powder_process:%s: Error identifying Union_init component, %s is not a known component name.\n",
NAME_CURRENT_COMP, init);
exit(-1);
}


struct pointer_to_global_process_list *global_process_list = COMP_GETPAR3(Union_init, init, global_process_list);
  add_element_to_process_list(global_process_list,global_process_element);
  #undef reflections
  #undef packing_factor
  #undef Vc
  #undef delta_d_d
  #undef DW
  #undef nb_atoms
  #undef d_phi
  #undef density
  #undef weight
  #undef barns
  #undef Strain
  #undef interact_fraction
  #undef format
  #undef init
  #undef global_process_element
  #undef This_process
  #undef Powder_storage
  #undef line_info
  #undef effective_my_scattering
  #undef columns
  return(_comp);
} /* class_Powder_process_init */

_class_Union_make_material *class_Union_make_material_init(_class_Union_make_material *_comp
) {
  #define process_string (_comp->_parameters.process_string)
  #define my_absorption (_comp->_parameters.my_absorption)
  #define absorber (_comp->_parameters.absorber)
  #define init (_comp->_parameters.init)
  #define global_material_element (_comp->_parameters.global_material_element)
  #define this_material (_comp->_parameters.this_material)
  #define loop_index (_comp->_parameters.loop_index)
  #define found_process (_comp->_parameters.found_process)
  #define specified_processes (_comp->_parameters.specified_processes)
  #define local_string (_comp->_parameters.local_string)
  #define accepted_processes (_comp->_parameters.accepted_processes)
  SIG_MESSAGE("[_Al_init] component Al=Union_make_material() INITIALISE [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//union/Union_make_material.comp:180]");


  accepted_processes.num_elements = 0;
  accepted_processes.elements = NULL;

  /*
  // Comma test
  printf("Starting comma test on string: %s \n",process_string);
  printf("Number of commas in string: %d \n",count_commas(process_string));
  exit(1);
  */
  

  if (0 == strcmp(NAME_CURRENT_COMP,"vacuum") || 0 == strcmp(NAME_CURRENT_COMP,"Vacuum")) {
    printf("ERROR, a Union material may not be called Vacuum. A vacuum volume may be created by material=\"Vacuum\" in a geometry component.\n");
    exit(1);
  }
  if (0 == strcmp(NAME_CURRENT_COMP,"exit") || 0 == strcmp(NAME_CURRENT_COMP,"Exit")) {
    printf("ERROR, a Union material may not be called Exit. A exit volume may be created by material=\"Exit\" in a geometry component.\n");
    exit(1);
  }
  if (my_absorption < 0) {
    printf("ERROR, Union make material named %s have a negative absorption cross section!.\n",NAME_CURRENT_COMP);
    exit(1);
  }

if (_getcomp_index(init) < 0) {
fprintf(stderr,"Union_make_material:%s: Error identifying Union_init component, %s is not a known component name.\n",
NAME_CURRENT_COMP, init);
exit(-1);
}

struct pointer_to_global_process_list *global_process_list = COMP_GETPAR3(Union_init, init, global_process_list);
  struct pointer_to_global_material_list *global_material_list = COMP_GETPAR3(Union_init, init, global_material_list);
  
  if (absorber == 0) {
    if (process_string && strlen(process_string) && strcmp(process_string,"NULL") && strcmp(process_string, "0")) {
        manual_linking_function_material(process_string, global_process_list, &accepted_processes, NAME_CURRENT_COMP);
    } else {
      for (loop_index=0;loop_index<global_process_list->num_elements;loop_index++) {
        // printf("Automatic linking chosen [loop index = %d] with process_string = %s  \n",loop_index,process_string);
        // automatic linking
        // accept a process if index is between current and former index of make_material components
          if (1 == automatic_linking_materials_function(global_process_list->elements[loop_index], *global_material_list, INDEX_CURRENT_COMP))
              add_element_to_int_list(&accepted_processes,loop_index);
      }
    }
  }

  this_material.number_of_processes = accepted_processes.num_elements; // Add number of processes
  this_material.is_vacuum = 0; // This material is not vacuum
  
  if (this_material.number_of_processes == 0 && my_absorption == 0) {
    printf("ERROR, the material named %s has no processes assigned and no absorption cross section, making it eqvialent to vacuum. Vacuums are assigned by setting material=\"Vacuum\" in a geometry component.\n",NAME_CURRENT_COMP);
    exit(1);
  }
  
  // add process element to this_material, building an array of processes called p_scattering_array
  if (this_material.number_of_processes > 0) this_material.p_scattering_array = malloc(this_material.number_of_processes * sizeof(struct scattering_process_struct));
  for (loop_index=0;loop_index<accepted_processes.num_elements;loop_index++) {
        this_material.p_scattering_array[loop_index]=*global_process_list->elements[accepted_processes.elements[loop_index]].p_scattering_process;
  }
  
  this_material.my_a = my_absorption;  // add the absorption to this material
  sprintf(this_material.name,NAME_CURRENT_COMP);
  
  // packing the information into the global_material_element, which is then included in the global_material_list.
  sprintf(global_material_element.name,NAME_CURRENT_COMP);
  global_material_element.component_index = INDEX_CURRENT_COMP;
  global_material_element.physics = &this_material; // Would be nicer if this material was a pointer, now we have the (small) data two places
  add_element_to_material_list(global_material_list, global_material_element);
  
  #undef process_string
  #undef my_absorption
  #undef absorber
  #undef init
  #undef global_material_element
  #undef this_material
  #undef loop_index
  #undef found_process
  #undef specified_processes
  #undef local_string
  #undef accepted_processes
  return(_comp);
} /* class_Union_make_material_init */

_class_Single_crystal_process *class_Single_crystal_process_init(_class_Single_crystal_process *_comp
) {
  #define reflections (_comp->_parameters.reflections)
  #define delta_d_d (_comp->_parameters.delta_d_d)
  #define mosaic (_comp->_parameters.mosaic)
  #define mosaic_a (_comp->_parameters.mosaic_a)
  #define mosaic_b (_comp->_parameters.mosaic_b)
  #define mosaic_c (_comp->_parameters.mosaic_c)
  #define mosaic_AB (_comp->_parameters.mosaic_AB)
  #define recip_cell (_comp->_parameters.recip_cell)
  #define barns (_comp->_parameters.barns)
  #define ax (_comp->_parameters.ax)
  #define ay (_comp->_parameters.ay)
  #define az (_comp->_parameters.az)
  #define bx (_comp->_parameters.bx)
  #define by (_comp->_parameters.by)
  #define bz (_comp->_parameters.bz)
  #define cx (_comp->_parameters.cx)
  #define cy (_comp->_parameters.cy)
  #define cz (_comp->_parameters.cz)
  #define aa (_comp->_parameters.aa)
  #define bb (_comp->_parameters.bb)
  #define cc (_comp->_parameters.cc)
  #define order (_comp->_parameters.order)
  #define RX (_comp->_parameters.RX)
  #define RY (_comp->_parameters.RY)
  #define RZ (_comp->_parameters.RZ)
  #define powder (_comp->_parameters.powder)
  #define PG (_comp->_parameters.PG)
  #define interact_fraction (_comp->_parameters.interact_fraction)
  #define packing_factor (_comp->_parameters.packing_factor)
  #define init (_comp->_parameters.init)
  #define Single_crystal_storage (_comp->_parameters.Single_crystal_storage)
  #define hkl_info_union (_comp->_parameters.hkl_info_union)
  #define global_process_element (_comp->_parameters.global_process_element)
  #define This_process (_comp->_parameters.This_process)
  SIG_MESSAGE("[_MnF2_critical_init] component MnF2_critical=Single_crystal_process() INITIALISE [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//union/Single_crystal_process.comp:968]");

  // Single crystal initialize
  double as, bs, cs;
  int i=0;

  /* transfer input parameters */
  hkl_info_union.m_delta_d_d = delta_d_d;
  hkl_info_union.m_a  = 0;
  hkl_info_union.m_b  = 0;
  hkl_info_union.m_c  = 0;
  hkl_info_union.m_aa = aa;
  hkl_info_union.m_bb = bb;
  hkl_info_union.m_cc = cc;
  hkl_info_union.m_ax = ax;
  hkl_info_union.m_ay = ay;
  hkl_info_union.m_az = az;
  hkl_info_union.m_bx = bx;
  hkl_info_union.m_by = by;
  hkl_info_union.m_bz = bz;
  hkl_info_union.m_cx = cx;
  hkl_info_union.m_cy = cy;
  hkl_info_union.m_cz = cz;
  //hkl_info_union.sigma_a = sigma_abs;
  //hkl_info_union.sigma_i = sigma_inc;
  hkl_info_union.recip   = recip_cell;

  /* default format h,k,l,F,F2  */
  hkl_info_union.column_order[0]=1;
  hkl_info_union.column_order[1]=2;
  hkl_info_union.column_order[2]=3;
  hkl_info_union.column_order[3]=0;
  hkl_info_union.column_order[4]=7;
  hkl_info_union.kix = hkl_info_union.kiy = hkl_info_union.kiz = 0;
  hkl_info_union.nb_reuses = hkl_info_union.nb_refl = hkl_info_union.nb_refl_count = 0;
  hkl_info_union.tau_count = 0;

  /* ought to be cleaned up as mosaic_AB now is a proper vector/array and not a define */
  double* mosaic_ABin = mosaic_AB;
  /* Read in structure factors, and do some pre-calculations. */
  if (!read_hkl_data_union(reflections, &hkl_info_union, mosaic, mosaic_a, mosaic_b, mosaic_c, mosaic_ABin)) {
    printf("Single_crystal_process: %s: Error: Aborting.\n", NAME_CURRENT_COMP);
    exit(0);
  }
    
  if (hkl_info_union.sigma_a<0) hkl_info_union.sigma_a=0;
  if (hkl_info_union.sigma_i<0) hkl_info_union.sigma_i=0;
  
  if (hkl_info_union.count)
    printf("Single_crystal_process: %s: Read %d reflections from file '%s'\n",
      NAME_CURRENT_COMP, hkl_info_union.count, reflections);
  else printf("Single_crystal_process: %s: Using incoherent elastic scattering only sigma=%g.\n",
      NAME_CURRENT_COMP, hkl_info_union.sigma_i);
  
  /*
  hkl_info.shape=-1; // -1:no shape, 0:cyl, 1:box, 2:sphere, 3:any-shape
  if (geometry && strlen(geometry) && strcmp(geometry, "NULL") && strcmp(geometry, "0")) {
	  if (off_init(geometry, xwidth, yheight, zdepth, 0, &offdata)) {
      hkl_info.shape=3; 
    }
  }
  else if (xwidth && yheight && zdepth)  hkl_info.shape=1; // box
  else if (radius > 0 && yheight)        hkl_info.shape=0; // cylinder
  else if (radius > 0 && !yheight)       hkl_info.shape=2; // sphere

  if (hkl_info.shape < 0) 
    exit(fprintf(stderr,"Single_crystal: %s: sample has invalid dimensions.\n"
                        "ERROR           Please check parameter values (xwidth, yheight, zdepth, radius).\n", NAME_CURRENT_COMP));
  */
  
  printf("Single_crystal: %s: Vc=%g [Angs] sigma_abs=%g [barn] sigma_inc=%g [barn] reflections=%s\n",
      NAME_CURRENT_COMP, hkl_info_union.V0, hkl_info_union.sigma_a, hkl_info_union.sigma_i,
      reflections && strlen(reflections) ? reflections : "NULL");

  if (powder && PG) 
    exit(fprintf(stderr,"Single_crystal_process: %s: powder and PG modes can not be used together!\n"
	     "ERROR           Please use EITHER powder or PG mode.\n", NAME_CURRENT_COMP));

  if (powder && !(order==1)) {
    fprintf(stderr,"Single_crystal_process: %s: powder mode means implicit choice of no multiple scattering!\n"
	    "WARNING setting order=1\n", NAME_CURRENT_COMP);
    order=1;
  } 
  
  if (PG && !(order==1)) {
    fprintf(stderr,"Single_crystal_process: %s: PG mode means implicit choice of no multiple scattering!\n"
	    "WARNING setting order=1\n", NAME_CURRENT_COMP);
    order=1;
  } 

  // Temporary errors untill these features are either added or removed from input
  

  if (powder)
    exit(fprintf(stderr,"Single_crystal_process: %s: powder mode not supported yet!\n"
	     "ERROR           Please disable powder mode.\n", NAME_CURRENT_COMP));
  

  if (PG)
    exit(fprintf(stderr,"Single_crystal_process: %s: PG mode not supported yet!\n"
	     "ERROR           Please disable PG mode.\n", NAME_CURRENT_COMP));
   
  if (order)
    exit(fprintf(stderr,"Single_crystal_process: %s: Order control not supported yet!\n"
	     "ERROR           Please set order to zero.\n", NAME_CURRENT_COMP));
  
  // Initialize done in the component
  // Added for single crystal
  Single_crystal_storage.PG_setting = PG;
  Single_crystal_storage.powder_setting = powder;
  Single_crystal_storage.barns_setting = barns;
  Single_crystal_storage.pack = packing_factor;
  Single_crystal_storage.hkl_info_storage = &hkl_info_union;

  // Need to specify if this process is isotropic
  //This_process.non_isotropic_rot_index = -1; // Yes (powder)
  This_process.non_isotropic_rot_index =  1;  // No (single crystal)

  // The type of the process must be saved in the global enum process
  This_process.eProcess = Single_crystal;

  // Packing the data into a structure that is transported to the main component
  This_process.data_transfer.pointer_to_a_Single_crystal_physics_storage_struct = &Single_crystal_storage;
  This_process.probability_for_scattering_function = &Single_crystal_physics_my;
  This_process.scattering_function = &Single_crystal_physics_scattering;

  // This will be the same for all process's, and can thus be moved to an include.
  This_process.process_p_interact = interact_fraction;
  sprintf(This_process.name,NAME_CURRENT_COMP);
  rot_copy(This_process.rotation_matrix,ROT_A_CURRENT_COMP);
  sprintf(global_process_element.name,NAME_CURRENT_COMP);
  global_process_element.component_index = INDEX_CURRENT_COMP;
  global_process_element.p_scattering_process = &This_process;

if (_getcomp_index(init) < 0) {
fprintf(stderr,"Single_crystal_process:%s: Error identifying Union_init component, %s is not a known component name.\n",
NAME_CURRENT_COMP, init);
exit(-1);
}


struct pointer_to_global_process_list *global_process_list = COMP_GETPAR3(Union_init, init, global_process_list);
  add_element_to_process_list(global_process_list, global_process_element);
  
  #undef reflections
  #undef delta_d_d
  #undef mosaic
  #undef mosaic_a
  #undef mosaic_b
  #undef mosaic_c
  #undef mosaic_AB
  #undef recip_cell
  #undef barns
  #undef ax
  #undef ay
  #undef az
  #undef bx
  #undef by
  #undef bz
  #undef cx
  #undef cy
  #undef cz
  #undef aa
  #undef bb
  #undef cc
  #undef order
  #undef RX
  #undef RY
  #undef RZ
  #undef powder
  #undef PG
  #undef interact_fraction
  #undef packing_factor
  #undef init
  #undef Single_crystal_storage
  #undef hkl_info_union
  #undef global_process_element
  #undef This_process
  return(_comp);
} /* class_Single_crystal_process_init */

_class_Union_cylinder *class_Union_cylinder_init(_class_Union_cylinder *_comp
) {
  #define material_string (_comp->_parameters.material_string)
  #define priority (_comp->_parameters.priority)
  #define radius (_comp->_parameters.radius)
  #define yheight (_comp->_parameters.yheight)
  #define visualize (_comp->_parameters.visualize)
  #define target_index (_comp->_parameters.target_index)
  #define target_x (_comp->_parameters.target_x)
  #define target_y (_comp->_parameters.target_y)
  #define target_z (_comp->_parameters.target_z)
  #define focus_aw (_comp->_parameters.focus_aw)
  #define focus_ah (_comp->_parameters.focus_ah)
  #define focus_xw (_comp->_parameters.focus_xw)
  #define focus_xh (_comp->_parameters.focus_xh)
  #define focus_r (_comp->_parameters.focus_r)
  #define p_interact (_comp->_parameters.p_interact)
  #define mask_string (_comp->_parameters.mask_string)
  #define mask_setting (_comp->_parameters.mask_setting)
  #define number_of_activations (_comp->_parameters.number_of_activations)
  #define init (_comp->_parameters.init)
  #define global_geometry_element (_comp->_parameters.global_geometry_element)
  #define loop_index (_comp->_parameters.loop_index)
  #define loop_2_index (_comp->_parameters.loop_2_index)
  #define material_index (_comp->_parameters.material_index)
  #define this_cylinder_volume (_comp->_parameters.this_cylinder_volume)
  #define this_cylinder_storage (_comp->_parameters.this_cylinder_storage)
  SIG_MESSAGE("[_union_sample_init] component union_sample=Union_cylinder() INITIALISE [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//union/Union_cylinder.comp:182]");

// Initializes the focusing system for this volume including input sanitation.
focus_initialize(&this_cylinder_volume.geometry, POS_A_COMP_INDEX(INDEX_CURRENT_COMP+target_index), POS_A_CURRENT_COMP, ROT_A_CURRENT_COMP, target_index, target_x, target_y, target_z, focus_aw, focus_ah, focus_xw, focus_xh, focus_r, NAME_CURRENT_COMP);

// Input sanitation for this geometry
if (radius <= 0) {
  printf("\nERROR in Union_cylinder named %s, the radius is <= 0. \n",NAME_CURRENT_COMP);
  exit(1);
}

if (yheight <= 0) {
  printf("\nERROR in Union_cylinder named %s, yheight is <= 0. \n",NAME_CURRENT_COMP);
  exit(1);
}

if (_getcomp_index(init) < 0) {
fprintf(stderr,"Union_cylinder:%s: Error identifying Union_init component, %s is not a known component name.\n",
NAME_CURRENT_COMP, init);
exit(-1);
}

struct pointer_to_global_material_list *global_material_list = COMP_GETPAR3(Union_init, init, global_material_list);
// Use sanitation
#ifdef MATERIAL_DETECTOR
if (global_material_list->num_elements == 0) {
  // Here if the user have defined a material, but only after this material
  printf("\nERROR: Need to define a material using Union_make_material before using a Union geometry component. \n");
  printf("       %s was defined before first use of Union_make_material.\n",NAME_CURRENT_COMP);
  exit(1);
}
#endif
#ifndef MATERIAL_DETECTOR
  printf("\nERROR: Need to define a material using Union_make_material before using a Union geometry component. \n");
  exit(1);
#endif


this_cylinder_volume.geometry.is_masked_volume = 0;
this_cylinder_volume.geometry.is_exit_volume = 0;
this_cylinder_volume.geometry.is_mask_volume = 0;
struct pointer_to_global_geometry_list *global_geometry_list = COMP_GETPAR3(Union_init, init, global_geometry_list);

// Read the material input, or if it lacks, use automatic linking.
if (mask_string && strlen(mask_string) && strcmp(mask_string, "NULL") && strcmp(mask_string, "0")) {
    // A mask volume is used to limit the extend of other volumes, called the masked volumes. These are specified in the mask_string.
    // In order for a ray to enter a masked volume, it needs to be both in the region covered by that volume AND the mask volume.
    // When more than
    this_cylinder_volume.geometry.mask_mode = 1; // Default is mask mode is ALL
    if (mask_setting && strlen(mask_setting) && strcmp(mask_setting, "NULL") && strcmp(mask_setting, "0")) {
        if (strcmp(mask_setting,"ALL") == 0 || strcmp(mask_setting,"All") == 0) this_cylinder_volume.geometry.mask_mode = 1;
        else if (strcmp(mask_setting,"ANY") == 0 || strcmp(mask_setting,"Any") == 0) this_cylinder_volume.geometry.mask_mode = 2;
        else {
            printf("The mask_mode of component %s is set to %s, but must be either ALL or ANY.\n",NAME_CURRENT_COMP,mask_setting);
            exit(1);
        }
    }
    
    int found_geometries = 0;
    for (loop_index=0;loop_index<global_geometry_list->num_elements;loop_index++) {
        // Add mask list
        if (1 == manual_linking_function(global_geometry_list->elements[loop_index].name,mask_string)) {
            add_element_to_int_list(&this_cylinder_volume.geometry.mask_list,global_geometry_list->elements[loop_index].component_index);
            add_element_to_int_list(&global_geometry_list->elements[loop_index].Volume->geometry.masked_by_list,INDEX_CURRENT_COMP);
            global_geometry_list->elements[loop_index].Volume->geometry.is_masked_volume = 1;
            if (this_cylinder_volume.geometry.mask_mode == 2)
                global_geometry_list->elements[loop_index].Volume->geometry.mask_mode = 2;
            if (this_cylinder_volume.geometry.mask_mode == 1) {
                if (global_geometry_list->elements[loop_index].Volume->geometry.is_masked_volume == 1 && global_geometry_list->elements[loop_index].Volume->geometry.mask_mode != 2)
                    // If more than one mask is added to one volume, the ANY mode overwrites the (default) ALL mode.
                    global_geometry_list->elements[loop_index].Volume->geometry.mask_mode = 1;
            }
            
            found_geometries = 1;
        }
    }
    if (found_geometries == 0) {
        printf("The mask_string in geometry: %s did not find any of the specified volumes in the mask_string %s \n",NAME_CURRENT_COMP,mask_string);
        exit(1);
    }
    this_cylinder_volume.p_physics = malloc(sizeof(struct physics_struct));
    this_cylinder_volume.p_physics->is_vacuum = 0; // Makes this volume a vacuum
    this_cylinder_volume.p_physics->number_of_processes = (int) 0; // Should not be used.
    this_cylinder_volume.p_physics->my_a = 0; // Should not be used.
    sprintf(this_cylinder_volume.p_physics->name,"Mask");
    this_cylinder_volume.geometry.is_mask_volume = 1;
    
    
// Read the material input, or if it lacks, use automatic linking.
} else if (material_string && strlen(material_string) && strcmp(material_string, "NULL") && strcmp(material_string, "0")) {
    // A geometry string was given, use it to determine which material
    if (0 == strcmp(material_string,"vacuum") || 0 == strcmp(material_string,"Vacuum")) {
        // One could have a global physics struct for vacuum instead of creating one for each
        this_cylinder_volume.p_physics = malloc(sizeof(struct physics_struct));
        this_cylinder_volume.p_physics->is_vacuum = 1; // Makes this volume a vacuum
        this_cylinder_volume.p_physics->number_of_processes = (int) 0;
        this_cylinder_volume.p_physics->my_a = 0; // Should not be used.
        sprintf(this_cylinder_volume.p_physics->name,"Vacuum");
    } else if (0 == strcmp(material_string,"exit") || 0 == strcmp(material_string,"Exit")) {
        // One could have a global physics struct for exit instead of creating one for each
        this_cylinder_volume.p_physics = malloc(sizeof(struct physics_struct));
        this_cylinder_volume.p_physics->is_vacuum = 1; // Makes this volume a vacuum
        this_cylinder_volume.p_physics->number_of_processes = (int) 0;
        this_cylinder_volume.p_physics->my_a = 0; // Should not be used.
        this_cylinder_volume.geometry.is_exit_volume = 1;
        sprintf(this_cylinder_volume.p_physics->name,"Exit");
    } else {
        for (loop_index=0;loop_index<global_material_list->num_elements;loop_index++) {
          if (0 == strcmp(material_string,global_material_list->elements[loop_index].name)) {
            this_cylinder_volume.p_physics = global_material_list->elements[loop_index].physics;
            break;
          }
          if (loop_index == global_material_list->num_elements-1) {
            printf("\n");
            printf("ERROR: The material string \"%s\" in Union geometry \"%s\" did not match a specified material. \n",material_string,NAME_CURRENT_COMP);
            printf("       The materials available at this point (need to be defined before the geometry): \n");
            for (loop_index=0;loop_index<global_material_list->num_elements;loop_index++)
              printf("         %s\n",global_material_list->elements[loop_index].name);
            printf("\n");
            printf("       It is also possible to use one of the defualt materials avaiable: \n");
            printf("           Vacuum (for a Volume without scattering or absorption)\n");
            printf("           Exit (for a Volume where the ray exits the component if it enters)\n");
            printf("           Mask (for a Volume that masks existing volumes specified in the mask_string\n");
            exit(1);
          }
        }
    }
} else {
    // Automatic linking, simply using the last defined material.
    #ifndef MATERIAL_DETECTOR
        printf("Need to define a material before the geometry to use automatic linking %s.\n",NAME_CURRENT_COMP);
        exit(1);
    #endif
    this_cylinder_volume.p_physics = global_material_list->elements[global_material_list->num_elements-1].physics;
}

sprintf(this_cylinder_volume.name,NAME_CURRENT_COMP);
sprintf(this_cylinder_volume.geometry.shape,"cylinder");
this_cylinder_volume.geometry.eShape = cylinder;
this_cylinder_volume.geometry.priority_value = priority;
// Currently the coordinates will be in absolute space.
this_cylinder_volume.geometry.center = POS_A_CURRENT_COMP;

this_cylinder_volume.geometry.geometry_p_interact = p_interact;
this_cylinder_storage.cyl_radius = radius;
this_cylinder_storage.height = yheight;
this_cylinder_volume.geometry.visualization_on = visualize;
this_cylinder_volume.geometry.geometry_parameters.p_cylinder_storage = &this_cylinder_storage;
this_cylinder_volume.geometry.within_function = &r_within_cylinder;
this_cylinder_volume.geometry.intersect_function = &sample_cylinder_intersect;
this_cylinder_volume.geometry.mcdisplay_function = &mcdisplay_cylinder_function;
this_cylinder_volume.geometry.shell_points = &cylinder_shell_points;
this_cylinder_volume.geometry.initialize_from_main_function = &initialize_cylinder_geometry_from_main_component;
this_cylinder_volume.geometry.process_rot_allocated = 0;
this_cylinder_volume.geometry.copy_geometry_parameters = &allocate_cylinder_storage_copy;

rot_copy(this_cylinder_volume.geometry.rotation_matrix,ROT_A_CURRENT_COMP);
rot_transpose(ROT_A_CURRENT_COMP,this_cylinder_volume.geometry.transpose_rotation_matrix);

// Initialize loggers
this_cylinder_volume.loggers.num_elements = 0;
this_cylinder_volume.abs_loggers.num_elements = 0;

// packing the information into the global_geometry_element, which is then included in the global_geometry_list.
sprintf(global_geometry_element.name,NAME_CURRENT_COMP);
global_geometry_element.activation_counter = number_of_activations;
global_geometry_element.component_index = INDEX_CURRENT_COMP;
global_geometry_element.Volume = &this_cylinder_volume; // Would be nicer if this m was a pointer, now we have the (small) data two places
add_element_to_geometry_list(global_geometry_list,global_geometry_element);
  #undef material_string
  #undef priority
  #undef radius
  #undef yheight
  #undef visualize
  #undef target_index
  #undef target_x
  #undef target_y
  #undef target_z
  #undef focus_aw
  #undef focus_ah
  #undef focus_xw
  #undef focus_xh
  #undef focus_r
  #undef p_interact
  #undef mask_string
  #undef mask_setting
  #undef number_of_activations
  #undef init
  #undef global_geometry_element
  #undef loop_index
  #undef loop_2_index
  #undef material_index
  #undef this_cylinder_volume
  #undef this_cylinder_storage
  return(_comp);
} /* class_Union_cylinder_init */

_class_Union_master *class_Union_master_init(_class_Union_master *_comp
) {
  #define verbal (_comp->_parameters.verbal)
  #define list_verbal (_comp->_parameters.list_verbal)
  #define finally_verbal (_comp->_parameters.finally_verbal)
  #define allow_inside_start (_comp->_parameters.allow_inside_start)
  #define enable_tagging (_comp->_parameters.enable_tagging)
  #define history_limit (_comp->_parameters.history_limit)
  #define enable_conditionals (_comp->_parameters.enable_conditionals)
  #define inherit_number_of_scattering_events (_comp->_parameters.inherit_number_of_scattering_events)
  #define init (_comp->_parameters.init)
  #define global_positions_to_transform_list_master (_comp->_parameters.global_positions_to_transform_list_master)
  #define global_rotations_to_transform_list_master (_comp->_parameters.global_rotations_to_transform_list_master)
  #define global_process_list_master (_comp->_parameters.global_process_list_master)
  #define global_material_list_master (_comp->_parameters.global_material_list_master)
  #define global_geometry_list_master (_comp->_parameters.global_geometry_list_master)
  #define global_all_volume_logger_list_master (_comp->_parameters.global_all_volume_logger_list_master)
  #define global_specific_volumes_logger_list_master (_comp->_parameters.global_specific_volumes_logger_list_master)
  #define global_all_volume_abs_logger_list_master (_comp->_parameters.global_all_volume_abs_logger_list_master)
  #define global_specific_volumes_abs_logger_list_master (_comp->_parameters.global_specific_volumes_abs_logger_list_master)
  #define global_tagging_conditional_list_master (_comp->_parameters.global_tagging_conditional_list_master)
  #define global_master_list_master (_comp->_parameters.global_master_list_master)
  #define starting_volume_warning (_comp->_parameters.starting_volume_warning)
  #define global_master_element (_comp->_parameters.global_master_element)
  #define this_global_master_index (_comp->_parameters.this_global_master_index)
  #define previous_master_index (_comp->_parameters.previous_master_index)
  #define geometry_list_index (_comp->_parameters.geometry_list_index)
  #define intersection_time_table (_comp->_parameters.intersection_time_table)
  #define Volumes (_comp->_parameters.Volumes)
  #define Geometries (_comp->_parameters.Geometries)
  #define Volume_copies (_comp->_parameters.Volume_copies)
  #define starting_lists (_comp->_parameters.starting_lists)
  #define Volume_copies_allocated (_comp->_parameters.Volume_copies_allocated)
  #define r (_comp->_parameters.r)
  #define r_start (_comp->_parameters.r_start)
  #define v (_comp->_parameters.v)
  #define error_msg (_comp->_parameters.error_msg)
  #define component_error_msg (_comp->_parameters.component_error_msg)
  #define string_output (_comp->_parameters.string_output)
  #define number_of_volumes (_comp->_parameters.number_of_volumes)
  #define volume_index (_comp->_parameters.volume_index)
  #define process_index (_comp->_parameters.process_index)
  #define iterator (_comp->_parameters.iterator)
  #define solutions (_comp->_parameters.solutions)
  #define max_number_of_processes (_comp->_parameters.max_number_of_processes)
  #define limit (_comp->_parameters.limit)
  #define solution (_comp->_parameters.solution)
  #define min_solution (_comp->_parameters.min_solution)
  #define min_volume (_comp->_parameters.min_volume)
  #define time_found (_comp->_parameters.time_found)
  #define intersection_time (_comp->_parameters.intersection_time)
  #define min_intersection_time (_comp->_parameters.min_intersection_time)
  #define process (_comp->_parameters.process)
  #define process_start (_comp->_parameters.process_start)
  #define my_trace (_comp->_parameters.my_trace)
  #define p_my_trace (_comp->_parameters.p_my_trace)
  #define my_trace_fraction_control (_comp->_parameters.my_trace_fraction_control)
  #define k (_comp->_parameters.k)
  #define k_new (_comp->_parameters.k_new)
  #define k_old (_comp->_parameters.k_old)
  #define k_rotated (_comp->_parameters.k_rotated)
  #define v_length (_comp->_parameters.v_length)
  #define my_sum (_comp->_parameters.my_sum)
  #define my_sum_plus_abs (_comp->_parameters.my_sum_plus_abs)
  #define culmative_probability (_comp->_parameters.culmative_probability)
  #define mc_prop (_comp->_parameters.mc_prop)
  #define time_to_scattering (_comp->_parameters.time_to_scattering)
  #define length_to_scattering (_comp->_parameters.length_to_scattering)
  #define length_to_boundery (_comp->_parameters.length_to_boundery)
  #define time_to_boundery (_comp->_parameters.time_to_boundery)
  #define selected_process (_comp->_parameters.selected_process)
  #define scattering_event (_comp->_parameters.scattering_event)
  #define time_propagated_without_scattering (_comp->_parameters.time_propagated_without_scattering)
  #define a_next_volume_found (_comp->_parameters.a_next_volume_found)
  #define next_volume (_comp->_parameters.next_volume)
  #define next_volume_priority (_comp->_parameters.next_volume_priority)
  #define done (_comp->_parameters.done)
  #define current_volume (_comp->_parameters.current_volume)
  #define ray_sucseeded (_comp->_parameters.ray_sucseeded)
  #define number_of_solutions (_comp->_parameters.number_of_solutions)
  #define number_of_solutions_static (_comp->_parameters.number_of_solutions_static)
  #define check (_comp->_parameters.check)
  #define start (_comp->_parameters.start)
  #define intersection_with_children (_comp->_parameters.intersection_with_children)
  #define geometry_output (_comp->_parameters.geometry_output)
  #define tree_next_volume (_comp->_parameters.tree_next_volume)
  #define pre_allocated1 (_comp->_parameters.pre_allocated1)
  #define pre_allocated2 (_comp->_parameters.pre_allocated2)
  #define pre_allocated3 (_comp->_parameters.pre_allocated3)
  #define ray_position (_comp->_parameters.ray_position)
  #define ray_velocity (_comp->_parameters.ray_velocity)
  #define ray_velocity_rotated (_comp->_parameters.ray_velocity_rotated)
  #define ray_velocity_final (_comp->_parameters.ray_velocity_final)
  #define wavevector (_comp->_parameters.wavevector)
  #define wavevector_rotated (_comp->_parameters.wavevector_rotated)
  #define volume_0_found (_comp->_parameters.volume_0_found)
  #define scattered_flag (_comp->_parameters.scattered_flag)
  #define scattered_flag_VP (_comp->_parameters.scattered_flag_VP)
  #define master_transposed_rotation_matrix (_comp->_parameters.master_transposed_rotation_matrix)
  #define temp_rotation_matrix (_comp->_parameters.temp_rotation_matrix)
  #define temp_transpose_rotation_matrix (_comp->_parameters.temp_transpose_rotation_matrix)
  #define non_rotated_position (_comp->_parameters.non_rotated_position)
  #define rotated_position (_comp->_parameters.rotated_position)
  #define non_isotropic_found (_comp->_parameters.non_isotropic_found)
  #define master_tagging_node_list (_comp->_parameters.master_tagging_node_list)
  #define current_tagging_node (_comp->_parameters.current_tagging_node)
  #define tagging_leaf_counter (_comp->_parameters.tagging_leaf_counter)
  #define stop_tagging_ray (_comp->_parameters.stop_tagging_ray)
  #define stop_creating_nodes (_comp->_parameters.stop_creating_nodes)
  #define number_of_scattering_events (_comp->_parameters.number_of_scattering_events)
  #define real_transmission_probability (_comp->_parameters.real_transmission_probability)
  #define mc_transmission_probability (_comp->_parameters.mc_transmission_probability)
  #define number_of_process_interacts_set (_comp->_parameters.number_of_process_interacts_set)
  #define index_of_lacking_process (_comp->_parameters.index_of_lacking_process)
  #define total_process_interact (_comp->_parameters.total_process_interact)
  #define geometry_component_index_list (_comp->_parameters.geometry_component_index_list)
  #define mask_volume_index_list (_comp->_parameters.mask_volume_index_list)
  #define number_of_masks (_comp->_parameters.number_of_masks)
  #define number_of_masked_volumes (_comp->_parameters.number_of_masked_volumes)
  #define mask_status_list (_comp->_parameters.mask_status_list)
  #define current_mask_intersect_list_status (_comp->_parameters.current_mask_intersect_list_status)
  #define mask_index_main (_comp->_parameters.mask_index_main)
  #define mask_iterator (_comp->_parameters.mask_iterator)
  #define mask_start (_comp->_parameters.mask_start)
  #define mask_check (_comp->_parameters.mask_check)
  #define need_to_run_within_which_volume (_comp->_parameters.need_to_run_within_which_volume)
  #define number_of_processes_array (_comp->_parameters.number_of_processes_array)
  #define p_old (_comp->_parameters.p_old)
  #define log_index (_comp->_parameters.log_index)
  #define conditional_status (_comp->_parameters.conditional_status)
  #define this_logger (_comp->_parameters.this_logger)
  #define this_abs_logger (_comp->_parameters.this_abs_logger)
  #define tagging_conditional_list (_comp->_parameters.tagging_conditional_list)
  #define logger_conditional_extend_array (_comp->_parameters.logger_conditional_extend_array)
  #define abs_logger_conditional_extend_array (_comp->_parameters.abs_logger_conditional_extend_array)
  #define max_conditional_extend_index (_comp->_parameters.max_conditional_extend_index)
  #define tagging_conditional_extend (_comp->_parameters.tagging_conditional_extend)
  #define free_tagging_conditioanl_list (_comp->_parameters.free_tagging_conditioanl_list)
  #define safty_distance (_comp->_parameters.safty_distance)
  #define safty_distance2 (_comp->_parameters.safty_distance2)
  #define temporary_focus_data (_comp->_parameters.temporary_focus_data)
  #define focus_data_index (_comp->_parameters.focus_data_index)
  #define r_old (_comp->_parameters.r_old)
  #define initial_weight (_comp->_parameters.initial_weight)
  #define abs_weight_factor (_comp->_parameters.abs_weight_factor)
  #define time_old (_comp->_parameters.time_old)
  #define absorption_index (_comp->_parameters.absorption_index)
  #define abs_weight_factor_set (_comp->_parameters.abs_weight_factor_set)
  #define my_abs (_comp->_parameters.my_abs)
  #define absorption_event_data (_comp->_parameters.absorption_event_data)
  #define abs_position (_comp->_parameters.abs_position)
  #define transformed_abs_position (_comp->_parameters.transformed_abs_position)
  #define t_abs_propagation (_comp->_parameters.t_abs_propagation)
  #define abs_distance (_comp->_parameters.abs_distance)
  #define abs_max_length (_comp->_parameters.abs_max_length)
  SIG_MESSAGE("[_Master_init] component Master=Union_master() INITIALISE [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//union/Union_master.comp:297]");

if (_getcomp_index(init) < 0) {
fprintf(stderr,"Union_master:%s: Error identifying Union_init component, %s is not a known component name.\n",
NAME_CURRENT_COMP, init);
exit(-1);
}
  // Unpack global lists
  global_positions_to_transform_list_master = COMP_GETPAR3(Union_init, init, global_positions_to_transform_list);
  global_rotations_to_transform_list_master = COMP_GETPAR3(Union_init, init, global_rotations_to_transform_list);
  global_process_list_master = COMP_GETPAR3(Union_init, init, global_process_list);
  global_material_list_master = COMP_GETPAR3(Union_init, init, global_material_list);
  global_geometry_list_master = COMP_GETPAR3(Union_init, init, global_geometry_list);
  global_all_volume_logger_list_master = COMP_GETPAR3(Union_init, init, global_all_volume_logger_list);
  global_specific_volumes_logger_list_master = COMP_GETPAR3(Union_init, init, global_specific_volumes_logger_list);
  global_all_volume_abs_logger_list_master = COMP_GETPAR3(Union_init, init, global_all_volume_abs_logger_list);
  global_specific_volumes_abs_logger_list_master = COMP_GETPAR3(Union_init, init, global_specific_volumes_abs_logger_list);
  global_tagging_conditional_list_master = COMP_GETPAR3(Union_init, init, global_tagging_conditional_list);
  global_master_list_master = COMP_GETPAR3(Union_init, init, global_master_list);

  // It is possible to surpress warnings on starting volume by setting this to 1
  starting_volume_warning = 0;
  
  // Start at 0 error messages, quit after 100.
  component_error_msg = 0;
  
  // For within_which_volume
  volume_0_found = 0;
  
  // For tagging
  tagging_leaf_counter=0;
  
  // For masks
  number_of_masks = 0;
  number_of_masked_volumes = 0;

  // Use sanitation
  #ifndef ANY_GEOMETRY_DETECTOR_DECLARE
    printf("\nERROR: Need to define at least one Volume using Union_cylinder or Union_box before using the Union_master component. \n");
    exit(1);
  #endif
  #ifdef ANY_GEOMETRY_DETECTOR_DECLARE
    if (global_geometry_list_master->num_elements == 0) {
      printf("\nERROR: Need to define at least one Volume using Union_cylinder or Union_box before using the Union_master component. \n");
      printf("       Union_master component named \"%s\" is before any Volumes in the instrument file. At least one Volume need to be defined before\n",NAME_CURRENT_COMP);
    
      exit(1);
    }
  #endif
  
  // Parameters describing the safety distances close to surfaces, as scattering should not occur closer to a surface than the
  //  accuracy of the intersection calculation.
  safty_distance = 1E-11;
  safty_distance2 = safty_distance*2;
  
  // Write information to the global_master_list_master about the current Union_master
  sprintf(global_master_element.name,NAME_CURRENT_COMP);
  global_master_element.component_index = INDEX_CURRENT_COMP;
  add_element_to_master_list(global_master_list_master, global_master_element);
  if (inherit_number_of_scattering_events == 1 && global_master_list_master->num_elements == 1) {
     printf("ERROR in Union_master with name %s. Inherit_number_of_scattering_events set to 1 for first Union_master component, but there is no preceeding Union_master component. Aborting.\n",NAME_CURRENT_COMP);
     exit(1);
  }
  this_global_master_index = global_master_list_master->num_elements - 1; // Save the index for this master in global master list
  
  // Set the component index of the previous Union_master component if one exists
  if (global_master_list_master->num_elements == 1) previous_master_index = 0; // no previous index
  else previous_master_index = global_master_list_master->elements[global_master_list_master->num_elements-2].component_index; // -2 because of zero indexing and needing the previous index.
  //printf("Assigned previous_master_index = %d \n",previous_master_index);
  
  // All volumes in the global_geometry_list_master is being check for activity using the number_of_activations input made for each geometry (default is 1)
  // In addition it is counted how many volumes, mask volumes and masked volumes are active in this Union_master.
  number_of_volumes = 1; // Starting with 1 as the surrounding vacuum is considered a volume
  number_of_masks = 0;   // Starting with 0 mask volumes
  number_of_masked_volumes = 0; // Starting with 0 masked volumes
  for (iterator=0;iterator<global_geometry_list_master->num_elements;iterator++) {
    if (global_geometry_list_master->elements[iterator].component_index < INDEX_CURRENT_COMP && global_geometry_list_master->elements[iterator].activation_counter > 0) {
        global_geometry_list_master->elements[iterator].active = 1;
        global_geometry_list_master->elements[iterator].activation_counter--;
        number_of_volumes++;
        if (global_geometry_list_master->elements[iterator].Volume->geometry.is_mask_volume == 1) number_of_masks++;
        if (global_geometry_list_master->elements[iterator].Volume->geometry.is_masked_volume == 1) number_of_masked_volumes++;
    } else global_geometry_list_master->elements[iterator].active = 0;
  }
  
  // Allocation of global lists
  geometry_component_index_list.num_elements = number_of_volumes;
  geometry_component_index_list.elements = malloc( geometry_component_index_list.num_elements * sizeof(int));
  mask_volume_index_list.num_elements = number_of_masks;
  if (number_of_masks >0) mask_volume_index_list.elements = malloc( number_of_masks * sizeof(int));
  mask_status_list.num_elements = number_of_masks;
  if (number_of_masks >0) mask_status_list.elements = malloc( number_of_masks * sizeof(int));
  current_mask_intersect_list_status.num_elements = number_of_masked_volumes;
  if (number_of_masked_volumes >0) current_mask_intersect_list_status.elements = malloc( number_of_masked_volumes * sizeof(int));
  
  // Make a list of component index from each volume index
  volume_index = 0;
  for (iterator=0;iterator<global_geometry_list_master->num_elements;iterator++) {
    if (global_geometry_list_master->elements[iterator].active == 1)
        geometry_component_index_list.elements[++volume_index] = global_geometry_list_master->elements[iterator].component_index;
      
  }
  geometry_component_index_list.elements[0] = 0; // Volume 0 is never set in the above code, but should never be used.
  
  // The input for this component is done through a series of input components
  // All information needed is stored in global lists, some of which is printed here for an overview to the user.
  MPI_MASTER( // MPI_MASTER ensures just one thread output this information to the user
  if (verbal == 1) {
      printf("---------------------------------------------------------------------\n");
      printf("global_process_list_master->num_elements: %d\n",global_process_list_master->num_elements);
      for (iterator=0;iterator<global_process_list_master->num_elements;iterator++) {
      printf("name of process [%d]: %s \n",iterator,global_process_list_master->elements[iterator].name);
      printf("component index [%d]: %d \n",iterator,global_process_list_master->elements[iterator].component_index);
      }
      
      printf("---------------------------------------------------------------------\n");
      printf("global_material_list_master->num_elements: %d\n",global_material_list_master->num_elements);
      for (iterator=0;iterator<global_material_list_master->num_elements;iterator++) {
      printf("name of material    [%d]: %s \n",iterator,global_material_list_master->elements[iterator].name);
      printf("component index     [%d]: %d \n",iterator,global_material_list_master->elements[iterator].component_index);
      printf("my_absoprtion       [%d]: %f \n",iterator,global_material_list_master->elements[iterator].physics->my_a);
      printf("number of processes [%d]: %d \n",iterator,global_material_list_master->elements[iterator].physics->number_of_processes);
      }

      printf("---------------------------------------------------------------------\n");
      printf("global_geometry_list_master->num_elements: %d\n",global_material_list_master->num_elements);
      for (iterator=0;iterator<global_geometry_list_master->num_elements;iterator++) {
        if (global_geometry_list_master->elements[iterator].active == 1) {
          printf("\n");
          printf("name of geometry    [%d]: %s \n",iterator,global_geometry_list_master->elements[iterator].name);
          printf("component index     [%d]: %d \n",iterator,global_geometry_list_master->elements[iterator].component_index);
          printf("Volume.name         [%d]: %s \n",iterator,global_geometry_list_master->elements[iterator].Volume->name);
          if (global_geometry_list_master->elements[iterator].Volume->geometry.is_mask_volume == 0) {
          printf("Volume.p_physics.is_vacuum           [%d]: %d \n",iterator,global_geometry_list_master->elements[iterator].Volume->p_physics->is_vacuum);
          printf("Volume.p_physics.my_absorption       [%d]: %f \n",iterator,global_geometry_list_master->elements[iterator].Volume->p_physics->my_a);
          printf("Volume.p_physics.number of processes [%d]: %d \n",iterator,global_geometry_list_master->elements[iterator].Volume->p_physics->number_of_processes);
          }
          printf("Volume.geometry.shape                [%d]: %s \n",iterator,global_geometry_list_master->elements[iterator].Volume->geometry.shape);
          printf("Volume.geometry.center.x             [%d]: %f \n",iterator,global_geometry_list_master->elements[iterator].Volume->geometry.center.x);
          printf("Volume.geometry.center.y             [%d]: %f \n",iterator,global_geometry_list_master->elements[iterator].Volume->geometry.center.y);
          printf("Volume.geometry.center.z             [%d]: %f \n",iterator,global_geometry_list_master->elements[iterator].Volume->geometry.center.z);
          printf("Volume.geometry.rotation_matrix[0]           [%d]: [%f %f %f] \n",iterator,global_geometry_list_master->elements[iterator].Volume->geometry.rotation_matrix[0][0],global_geometry_list_master->elements[iterator].Volume->geometry.rotation_matrix[0][1],global_geometry_list_master->elements[iterator].Volume->geometry.rotation_matrix[0][2]);
          printf("Volume.geometry.rotation_matrix[1]           [%d]: [%f %f %f] \n",iterator,global_geometry_list_master->elements[iterator].Volume->geometry.rotation_matrix[1][0],global_geometry_list_master->elements[iterator].Volume->geometry.rotation_matrix[1][1],global_geometry_list_master->elements[iterator].Volume->geometry.rotation_matrix[1][2]);
          printf("Volume.geometry.rotation_matrix[2]           [%d]: [%f %f %f] \n",iterator,global_geometry_list_master->elements[iterator].Volume->geometry.rotation_matrix[2][0],global_geometry_list_master->elements[iterator].Volume->geometry.rotation_matrix[2][1],global_geometry_list_master->elements[iterator].Volume->geometry.rotation_matrix[2][2]);
          if (strcmp(global_geometry_list_master->elements[iterator].Volume->geometry.shape,"cylinder") == 0) {
          printf("Volume.geometry.geometry_parameters.cyl_radius [%d]: %f \n",iterator,global_geometry_list_master->elements[iterator].Volume->geometry.geometry_parameters.p_cylinder_storage->cyl_radius);
          printf("Volume.geometry.geometry_parameters.height [%d]: %f \n",iterator,global_geometry_list_master->elements[iterator].Volume->geometry.geometry_parameters.p_cylinder_storage->height);
          }
          printf("Volume.geometry.focus_data_array.elements[0].Aim             [%d]: [%f %f %f] \n",iterator,global_geometry_list_master->elements[iterator].Volume->geometry.focus_data_array.elements[0].Aim.x,global_geometry_list_master->elements[iterator].Volume->geometry.focus_data_array.elements[0].Aim.y,global_geometry_list_master->elements[iterator].Volume->geometry.focus_data_array.elements[0].Aim.z);
        }
      }
      printf("---------------------------------------------------------------------\n");
      printf("number_of_volumes = %d\n",number_of_volumes);
      printf("number_of_masks = %d\n",number_of_masks);
      printf("number_of_masked_volumes = %d\n",number_of_masked_volumes);
  }
  
  ); // End MPI_MASTER
  
  
  // --- Initialization tasks independent of volume stucture -----------------------

  // Store a pointer to the conditional list and update the current index in that structure
  // If no tagging_conditionals were defined between this and the previous master, a dummy is allocated instead
  if (global_tagging_conditional_list_master->num_elements == global_tagging_conditional_list_master->current_index + 1) {
    tagging_conditional_list = &global_tagging_conditional_list_master->elements[global_tagging_conditional_list_master->current_index++].conditional_list;
    free_tagging_conditioanl_list = 0;
  } else {
    tagging_conditional_list = malloc(sizeof(struct conditional_list_struct));
    tagging_conditional_list->num_elements = 0;
    free_tagging_conditioanl_list = 1;
  }
  
  // Find the maximum logger extend index so that the correct memory allocation can be performed later
  // Here the loggers applied to all volumes are searched, later this result is compared to volume specific loggers and updated
  max_conditional_extend_index = -1;
  for (iterator=0;iterator<global_all_volume_logger_list_master->num_elements;iterator++) {
    if (global_all_volume_logger_list_master->elements[iterator].logger->logger_extend_index > max_conditional_extend_index) {
      max_conditional_extend_index = global_all_volume_logger_list_master->elements[iterator].logger->logger_extend_index;
    }
  }
  
  // The absolute rotation of this component is saved for use in initialization
  rot_transpose(ROT_A_CURRENT_COMP,master_transposed_rotation_matrix);
  
  // Preceeding componnets can add coordinates and rotations to global_positions_to_transform and global_rotations_to_transform
  //  in order to have these transformed into the coordinate system of the next master compoent in the instrument file.
  // Here these transformations are performed, and the lists are cleared so no transformed information is further altered by
  //  next master components.
  
  // Position transformation
  for (iterator=0;iterator<global_positions_to_transform_list_master->num_elements;iterator++) {
    non_rotated_position = coords_sub(*(global_positions_to_transform_list_master->positions[iterator]),POS_A_CURRENT_COMP);
    *(global_positions_to_transform_list_master->positions[iterator]) = rot_apply(ROT_A_CURRENT_COMP,non_rotated_position);
  }
  if (global_positions_to_transform_list_master->num_elements > 0) {
    global_positions_to_transform_list_master->num_elements = 0;
    free(global_positions_to_transform_list_master->positions);
  }
  // Rotation transformation
  for (iterator=0;iterator<global_rotations_to_transform_list_master->num_elements;iterator++) {
    //print_rotation(*(global_rotations_to_transform_list_master->rotations[iterator]),"rotation matrix to be updated");
    rot_mul(master_transposed_rotation_matrix,*(global_rotations_to_transform_list_master->rotations[iterator]),temp_rotation_matrix);
    rot_copy(*(global_rotations_to_transform_list_master->rotations[iterator]),temp_rotation_matrix);
  }
  if (global_rotations_to_transform_list_master->num_elements > 0) {
    global_rotations_to_transform_list_master->num_elements = 0;
    free(global_rotations_to_transform_list_master->rotations);
  }

  
  // --- Definition of volumes and loading of appropriate data  -----------------------
  
  // The information stored in global lists is to be stored in one array of structures that is allocated here
  Volumes = malloc(number_of_volumes * sizeof(struct Volume_struct*));
  scattered_flag = malloc(number_of_volumes*sizeof(int));
  scattered_flag_VP = (int**) malloc(number_of_volumes * sizeof(int*));
  number_of_processes_array = malloc(number_of_volumes*sizeof(int));
  
  // The mcdisplay functions need access to the other geomtries, but can not use the Volumes struct because of order of definition.
  // A separate list of pointers to the geometry structures is thus allocated
  Geometries = malloc(number_of_volumes * sizeof(struct geometry_struct *));
  
  // When activation counter is used to have several copies of one volume, it can become necessary to have soft copies of volumes
  // Not all of these will necessarily be allocated or used.
  Volume_copies = malloc(number_of_volumes * sizeof(struct Volume_struct *));
  Volume_copies_allocated.num_elements = 0;
  
  // The central structure is called a "Volume", it describes a region in space with certain scattering processes and absorption cross section

  // ---  Volume 0 ------------------------------------------------------------------------------------------------
  // Volume 0 is the vacuum surrounding the experiment (infinite, everywhere) and its properties are hardcoded here
  Volumes[0] = malloc(sizeof(struct Volume_struct));
  strcpy(Volumes[0]->name,"Surrounding vacuum");
  // Assign geometry
  
  // This information is meaningless for volume 0, and is never be acsessed in the logic.
  Volumes[0]->geometry.priority_value = 0.0;
  Volumes[0]->geometry.center.x = 0;
  Volumes[0]->geometry.center.y = 0;
  Volumes[0]->geometry.center.z = 0;
  strcpy(Volumes[0]->geometry.shape,"vacuum");
  Volumes[0]->geometry.eShape = surroundings;
  Volumes[0]->geometry.within_function = &r_within_surroundings; // Always returns 1
  // No physics struct allocated
  Volumes[0]->p_physics = NULL;
  number_of_processes_array[volume_index] = 0;
  
  // These are never used for volume 0, but by setting the length to 0 it is automatically skipped in many forloops without the need for an if statement
  Volumes[0]->geometry.children.num_elements=0;
  Volumes[0]->geometry.direct_children.num_elements=0;
  Volumes[0]->geometry.destinations_list.num_elements=0;
  Volumes[0]->geometry.reduced_destinations_list.num_elements=0;
  
  Volumes[0]->geometry.is_exit_volume = 0;
  Volumes[0]->geometry.masked_by_list.num_elements = 0;
  Volumes[0]->geometry.mask_list.num_elements = 0;
  Volumes[0]->geometry.masked_by_mask_index_list.num_elements = 0;
  Volumes[0]->geometry.mask_mode=0;
  Volumes[0]->geometry.is_mask_volume=0;
  Volumes[0]->geometry.is_masked_volume=0;
  
  // A pointer to the geometry structure
  Geometries[0] = &Volumes[0]->geometry;
  
  // Logging initialization
  Volumes[0]->loggers.num_elements = 0;
  Volumes[0]->abs_loggers.num_elements = 0;
  
  
  // --- Loop over user defined volumes ------------------------------------------------------------------------
  // Here the user defined volumes are loaded into the volume structure that is used in the ray-tracing
  //  algorithm. Not all user defined volumes are used, some could be used by a previous master, some
  //  could be used by the previous master, this one, and perhaps more. This is controlled by the
  //  activation counter input for geometries, and is here condensed to the active variable.
  // Volumes that were used before
  
  
  max_number_of_processes = 0; // The maximum number of processes in a volume is assumed 0 and updated during the following loop
  
  volume_index = 0;
  mask_index_main = 0;
  for (geometry_list_index=0;geometry_list_index<global_geometry_list_master->num_elements;geometry_list_index++) {
    if (global_geometry_list_master->elements[geometry_list_index].active == 1) { // Only include the volume if it is active
      volume_index++;
      // Connect a volume for each of the geometry.comp instances in the McStas instrument files
      if (global_geometry_list_master->elements[geometry_list_index].activation_counter == 0) {
        // This is the last time this volume is used, use the hard copy from the geometry component
        Volumes[volume_index] = global_geometry_list_master->elements[geometry_list_index].Volume;
      } else {
        // Since this volume is still needed more than this once, we need to make a shallow copy and use instead
        
        Volume_copies[volume_index] = malloc(sizeof(struct Volume_struct));
        *(Volume_copies[volume_index]) = *global_geometry_list_master->elements[geometry_list_index].Volume; // Makes shallow copy
        Volumes[volume_index] = Volume_copies[volume_index];
        add_element_to_int_list(&Volume_copies_allocated,volume_index); // Keep track of dynamically allocated volumes in order to free them in FINALLY.
        
        // The geometry storage needs a shallow copy as well (hard copy not necessary for any current geometries), may need changes in future
        // A simple copy_geometry_parameters function is added to the geometry in each geometry component
        Volumes[volume_index]->geometry.geometry_parameters = Volumes[volume_index]->geometry.copy_geometry_parameters(&global_geometry_list_master->elements[geometry_list_index].Volume->geometry.geometry_parameters);
          
      }
      
      // This section identifies the different non isotropic processes in the current volume and give them appropriate transformation matrices
      // Identify the number of non isotropic processes in a material (this code can be safely executed for the same material many times)
      // A setting of -1 means no transformation necessary, other settings are assigned a unique identifier instead
      non_isotropic_found = 0;
      for (iterator=0;iterator<Volumes[volume_index]->p_physics->number_of_processes;iterator++) {
        if (Volumes[volume_index]->p_physics->p_scattering_array[iterator].non_isotropic_rot_index != -1) {
            Volumes[volume_index]->p_physics->p_scattering_array[iterator].non_isotropic_rot_index = non_isotropic_found;
            non_isotropic_found++;
        }
      }
      
      Volumes[volume_index]->geometry.focus_array_indices.num_elements=0;
      // For the non_isotropic volumes found, rotation matrices need to be allocated and calculated
      if (non_isotropic_found > 0) {
        // Allocation of rotation and transpose rotation matrices
        if (Volumes[volume_index]->geometry.process_rot_allocated == 0) {
          Volumes[volume_index]->geometry.process_rot_matrix_array = malloc(non_isotropic_found * sizeof(Rotation));
          Volumes[volume_index]->geometry.transpose_process_rot_matrix_array = malloc(non_isotropic_found * sizeof(Rotation));
          Volumes[volume_index]->geometry.process_rot_allocated = 1;
        }
      
        // Calculation of the appropriate rotation matrices for transformation between Union_master and the process in a given volume.
        non_isotropic_found = 0;
        for (iterator=0;iterator<Volumes[volume_index]->p_physics->number_of_processes;iterator++) {
          if (Volumes[volume_index]->p_physics->p_scattering_array[iterator].non_isotropic_rot_index != -1) {
            // Transformation for each process / geometry combination
            
            // The focus vector is given in relation to the geometry and needs to be transformed to the process
            // Work on temporary_focus_data_element which is added to the focus_data_array_at the end
            temporary_focus_data = Volumes[volume_index]->geometry.focus_data_array.elements[0];
            
            // Correct for process rotation
            temporary_focus_data.Aim = rot_apply(Volumes[volume_index]->p_physics->p_scattering_array[iterator].rotation_matrix,temporary_focus_data.Aim);
            
            // Add element to focus_array_indices
            // focus_array_indices refers to the correct element in focus_data_array for this volume/process combination
            // focus_data_array[0] is the isotropic version in all cases, so the first non_isotropic goes to focus_data_array[1]
            //  and so forth. When a process is isotropic, this array is appended with a zero.
            // The focus_array_indices maps process numbers to the correct focus_data_array index.
            add_element_to_int_list(&Volumes[volume_index]->geometry.focus_array_indices,non_isotropic_found+1);
            
            // Add the new focus_data element to this volumes focus_data_array.
            add_element_to_focus_data_array(&Volumes[volume_index]->geometry.focus_data_array,temporary_focus_data);
            
            // Quick error check to see the length is correct which indirectly confirms the indices are correct
            if (Volumes[volume_index]->geometry.focus_data_array.num_elements != non_isotropic_found + 2) {
              printf("ERROR, focus_data_array length for volume %s inconsistent with number of non isotropic processes found!\n",Volumes[volume_index]->name);
              exit(1);
            }
            
            // Create rotation matrix for this specific volume / process combination to transform from master coordinate system to the non-isotropics process coordinate system
            // This is done by multipling the transpose master component roration matrix, the volume rotation, and then the process rotation matrix onto the velocity / wavevector
            rot_mul(Volumes[volume_index]->geometry.rotation_matrix,master_transposed_rotation_matrix,temp_rotation_matrix);
            rot_mul(Volumes[volume_index]->p_physics->p_scattering_array[iterator].rotation_matrix,temp_rotation_matrix,Volumes[volume_index]->geometry.process_rot_matrix_array[non_isotropic_found]);
            
            // Need to transpose as well to transform back to the master coordinate system
            rot_transpose(Volumes[volume_index]->geometry.process_rot_matrix_array[non_isotropic_found],Volumes[volume_index]->geometry.transpose_process_rot_matrix_array[non_isotropic_found]);

            // Debug print
            //print_rotation(Volumes[volume_index]->geometry.process_rot_matrix_array[non_isotropic_found],"Process rotation matrix");
            //print_rotation(Volumes[volume_index]->geometry.transpose_process_rot_matrix_array[non_isotropic_found],"Transpose process rotation matrix");
            
            non_isotropic_found++;
          } else {
            // This process can use the standard isotropic focus_data_array which is indexed zero.
            add_element_to_int_list(&Volumes[volume_index]->geometry.focus_array_indices,0);
          }
        }
      } else {
      // No non isotropic volumes found, focus_array_indices should just be a list of 0's of same length as the number of processes.
      // In this way all processes use the isotropic focus_data structure
        Volumes[volume_index]->geometry.focus_array_indices.elements = malloc(Volumes[volume_index]->p_physics->number_of_processes * sizeof(int));
        for (iterator=0;iterator<Volumes[volume_index]->p_physics->number_of_processes;iterator++)
          Volumes[volume_index]->geometry.focus_array_indices.elements[iterator] = 0;
          
      }
      
      // This component works in its local coordinate system, and thus all information from the input components should be transformed to its coordinate system.
      // All the input components saved their absolute rotation/position into their Volume structure, and the absolute rotation of the current component is known.
      // The next section finds the relative rotation and translation of all the volumes and the master component.
      
      // Transform the rotation matrices for each volume
      rot_mul(ROT_A_CURRENT_COMP,Volumes[volume_index]->geometry.transpose_rotation_matrix,temp_rotation_matrix);
      // Copy the result back to the volumes structure
      rot_copy(Volumes[volume_index]->geometry.rotation_matrix,temp_rotation_matrix);
      // Now update the transpose as well
      rot_transpose(Volumes[volume_index]->geometry.rotation_matrix,temp_rotation_matrix);
      rot_copy(Volumes[volume_index]->geometry.transpose_rotation_matrix,temp_rotation_matrix);
        
      // Transform the position for each volume
      non_rotated_position.x = Volumes[volume_index]->geometry.center.x - POS_A_CURRENT_COMP.x;
      non_rotated_position.y = Volumes[volume_index]->geometry.center.y - POS_A_CURRENT_COMP.y;
      non_rotated_position.z = Volumes[volume_index]->geometry.center.z - POS_A_CURRENT_COMP.z;

      rot_transpose(ROT_A_CURRENT_COMP,temp_rotation_matrix); // REVIEW LINE
      rotated_position = rot_apply(ROT_A_CURRENT_COMP, non_rotated_position);

      Volumes[volume_index]->geometry.center.x = rotated_position.x;
      Volumes[volume_index]->geometry.center.y = rotated_position.y;
      Volumes[volume_index]->geometry.center.z = rotated_position.z;
      
      // The focus_data information need to be updated as well
      rot_mul(ROT_A_CURRENT_COMP,Volumes[volume_index]->geometry.focus_data_array.elements[0].absolute_rotation,temp_rotation_matrix);
      // Copy the result back to the volumes structure
      rot_copy(Volumes[volume_index]->geometry.focus_data_array.elements[0].absolute_rotation,temp_rotation_matrix);
      
      // Use same rotation on the aim vector of the isotropic focus_data element
      Volumes[volume_index]->geometry.focus_data_array.elements[0].Aim = rot_apply(Volumes[volume_index]->geometry.rotation_matrix,Volumes[volume_index]->geometry.focus_data_array.elements[0].Aim);
      
      // To allocate enough memory to hold information on all processes, the maximum of these is updated if this volume has more
      if (Volumes[volume_index]->p_physics->number_of_processes > max_number_of_processes)
          max_number_of_processes = Volumes[volume_index]->p_physics->number_of_processes;
        
      // Allocate memory to scattered_flag_VP (holds statistics for scatterings in each process of the volume)
      scattered_flag_VP[volume_index] = malloc(Volumes[volume_index]->p_physics->number_of_processes * sizeof(int));
      number_of_processes_array[volume_index] = Volumes[volume_index]->p_physics->number_of_processes;
      
      // Normalizing and error checking process interact fraction
      number_of_process_interacts_set = 0; total_process_interact=0;
      for (process_index=0;process_index<Volumes[volume_index]->p_physics->number_of_processes;process_index++) {
        if (Volumes[volume_index]->p_physics->p_scattering_array[process_index].process_p_interact != -1) {
          number_of_process_interacts_set++;
          total_process_interact += Volumes[volume_index]->p_physics->p_scattering_array[process_index].process_p_interact;
        } else {
          index_of_lacking_process = process_index;
        }
      }
      
      if (number_of_process_interacts_set == 0) Volumes[volume_index]->p_physics->interact_control = 0;
      else Volumes[volume_index]->p_physics->interact_control = 1;
      
      // If all are set, check if they need renormalization so that the sum is one.
      if (number_of_process_interacts_set == Volumes[volume_index]->p_physics->number_of_processes) {
        if (total_process_interact > 1.001 || total_process_interact < 0.999) {
          for (process_index=0;process_index<Volumes[volume_index]->p_physics->number_of_processes;process_index++) {
            Volumes[volume_index]->p_physics->p_scattering_array[process_index].process_p_interact = Volumes[volume_index]->p_physics->p_scattering_array[process_index].process_p_interact/total_process_interact;
          }
        }
      } else if ( number_of_process_interacts_set != 0) {
        if (number_of_process_interacts_set == Volumes[volume_index]->p_physics->number_of_processes - 1) {// If all but one is set, it is an easy fix
          Volumes[volume_index]->p_physics->p_scattering_array[index_of_lacking_process].process_p_interact = 1 - total_process_interact;
          if (total_process_interact >= 1) {
            printf("ERROR, material %s has a total interact_fraction above 1 and a process without an interact_fraction. Either set all so they can be renormalized, or have a sum below 1, so that the last can have 1 - sum.\n",Volumes[volume_index]->p_physics->name);
            exit(1);
          }
        } else {
            printf("ERROR, material %s needs to have all, all minus one or none of its processes with an interact_fraction \n",Volumes[volume_index]->p_physics->name);
            exit(1);
        }
      }
        
      // Some initialization can only happen after the rotation matrix relative to the master is known
      // Such initialization is placed in the geometry component, and executed here through a function pointer
      Volumes[volume_index]->geometry.initialize_from_main_function(&Volumes[volume_index]->geometry);
      
      // Add pointer to geometry to Geometries
      Geometries[volume_index] = &Volumes[volume_index]->geometry;
      
      // Initialize mask intersect list
      Volumes[volume_index]->geometry.mask_intersect_list.num_elements = 0;
      
      // Here the mask_list and masked_by_list for the volume is updated from component index values to volume indexes
      for (iterator=0;iterator<Volumes[volume_index]->geometry.mask_list.num_elements;iterator++)
        Volumes[volume_index]->geometry.mask_list.elements[iterator] = find_on_int_list(geometry_component_index_list,Volumes[volume_index]->geometry.mask_list.elements[iterator]);
      
      for (iterator=0;iterator<Volumes[volume_index]->geometry.masked_by_list.num_elements;iterator++)
        Volumes[volume_index]->geometry.masked_by_list.elements[iterator] = find_on_int_list(geometry_component_index_list,Volumes[volume_index]->geometry.masked_by_list.elements[iterator]);
      
      // If the volume is a mask, its volume number is added to the mask_volume_index list so volume index can be converted to mask_index.
      if (Volumes[volume_index]->geometry.is_mask_volume == 1) Volumes[volume_index]->geometry.mask_index = mask_index_main;
      if (Volumes[volume_index]->geometry.is_mask_volume == 1) mask_volume_index_list.elements[mask_index_main++] = volume_index;
     
      // Check all loggers assosiated with this volume and update the max_conditional_extend_index if necessary
      for (iterator=0;iterator<Volumes[volume_index]->loggers.num_elements;iterator++) {
        for (process_index=0;process_index<Volumes[volume_index]->loggers.p_logger_volume[iterator].num_elements;process_index++) {
          if (Volumes[volume_index]->loggers.p_logger_volume[iterator].p_logger_process[process_index] != NULL) {
            if (Volumes[volume_index]->loggers.p_logger_volume[iterator].p_logger_process[process_index]->logger_extend_index > max_conditional_extend_index)
              max_conditional_extend_index = Volumes[volume_index]->loggers.p_logger_volume[iterator].p_logger_process[process_index]->logger_extend_index;
          }
        }
      }
      
    
    }
  } // Initialization for each volume done
  
  // ------- Initialization of ray-tracing algorithm ------------------------------------
   
  my_trace = malloc(max_number_of_processes*sizeof(double));
  my_trace_fraction_control = malloc(max_number_of_processes*sizeof(double));
  
  // All geometries can have 2 intersections currently, when this changes the maximum number of solutions need to be reported to the Union_master.
  number_of_solutions = &number_of_solutions_static;
  component_error_msg = 0;
  
  // Pre allocated memory for destination list search
  pre_allocated1 = malloc(number_of_volumes * sizeof(int));
  pre_allocated2 = malloc(number_of_volumes * sizeof(int));
  pre_allocated3 = malloc(number_of_volumes * sizeof(int));
  
  // Allocate memory for logger_conditional_extend_array used in the extend section of the master component, if it is needed.
  if (max_conditional_extend_index > -1) {
    logger_conditional_extend_array = malloc((max_conditional_extend_index + 1)*sizeof(int));
  }
  
  // In this function different lists of volume indecies are generated. They are the key to the speed of the component and central for the logic.
  // They use simple set algebra to generate these lists for each volume:
  // Children list for volume n: Indicies of volumes that are entirely within the set described by volume n
  // Overlap list for volume n: Indicies of volume that contains some of the set described by volume n (excluding volume n)
  // Intersect check list for volume n: Indicies of volumes to check for intersection if a ray originates from volume n (is generated from the children and overlap lists)
  // Parents list for volume n: Indicies of volumes that contain the entire set of volume n
  // Grandparents lists for volume n: Indicies of volumes that contain the entire set of at least one parent of volume n
  // Destination list for volume n: Indicies of volumes that could be the destination volume when a ray leaves volume n
  // The overlap, parents and grandparents lists are local variables in the function, and not in the main scope.
  
  generate_lists(Volumes, &starting_lists, number_of_volumes, list_verbal);
  
  // Generate "safe starting list", which contains all volumes that the ray may enter from other components
  // These are all volumes without scattering or absorption
  
  // Updating mask lists from volume index to global_mask_indices
  // Filling out the masked_by list that uses mask indices
  for (volume_index=0;volume_index<number_of_volumes;volume_index++) {
    Volumes[volume_index]->geometry.masked_by_mask_index_list.num_elements = Volumes[volume_index]->geometry.masked_by_list.num_elements;
    Volumes[volume_index]->geometry.masked_by_mask_index_list.elements = malloc(Volumes[volume_index]->geometry.masked_by_mask_index_list.num_elements * sizeof(int));
    for (iterator=0;iterator<Volumes[volume_index]->geometry.masked_by_list.num_elements;iterator++)
        Volumes[volume_index]->geometry.masked_by_mask_index_list.elements[iterator] = find_on_int_list(mask_volume_index_list,Volumes[volume_index]->geometry.masked_by_list.elements[iterator]);
  }
  
  int volume_index_main;
  
  // Checking for equal priorities in order to alert the user to a potential input error
  for (volume_index_main=0;volume_index_main<number_of_volumes;volume_index_main++) {
    for (volume_index=0;volume_index<number_of_volumes;volume_index++)
        if (Volumes[volume_index_main]->geometry.priority_value == Volumes[volume_index]->geometry.priority_value && volume_index_main != volume_index) {
            if (Volumes[volume_index_main]->geometry.is_mask_volume == 0 && Volumes[volume_index]->geometry.is_mask_volume == 0) {
              // Priority of masks do not matter
              printf("ERROR in Union_master with name %s. The volumes named %s and %s have the same priority. Change the priorities so the one present in case of overlap has highest priority.\n",NAME_CURRENT_COMP,Volumes[volume_index_main]->name,Volumes[volume_index]->name);
              exit(1);
            }
        }
  }
  
  
  // Printing the generated lists for all volumes.
  MPI_MASTER(
  if (verbal) printf("\n ---- Overview of the lists generated for each volume ---- \n");
  
  
  printf("List overview for surrounding vacuum\n");
  for (volume_index_main=0;volume_index_main<number_of_volumes;volume_index_main++) {
      
      if (verbal) {
        if (volume_index_main != 0) {
          if (volume_index_main,Volumes[volume_index_main]->geometry.is_mask_volume == 0 ||
              volume_index_main,Volumes[volume_index_main]->geometry.is_masked_volume == 0 ||
              volume_index_main,Volumes[volume_index_main]->geometry.is_exit_volume == 0) {
            printf("List overview for %s with %s shape made of %s\n",
                   Volumes[volume_index_main]->name,
                   Volumes[volume_index_main]->geometry.shape,
                   Volumes[volume_index_main]->p_physics->name);
          } else {
            printf("List overview for %s with shape %s\n",
                   Volumes[volume_index_main]->name,
                   Volumes[volume_index_main]->geometry.shape);
          }
        }
      }
  
      if (verbal) sprintf(string_output,"Children for Volume                  %d",volume_index_main);
      if (verbal) print_1d_int_list(Volumes[volume_index_main]->geometry.children,string_output);
      
      if (verbal) sprintf(string_output,"Direct_children for Volume           %d",volume_index_main);
      if (verbal) print_1d_int_list(Volumes[volume_index_main]->geometry.direct_children,string_output);
      
      if (verbal) sprintf(string_output,"Intersect_check_list for Volume      %d",volume_index_main);
      if (verbal) print_1d_int_list(Volumes[volume_index_main]->geometry.intersect_check_list,string_output);
      
      if (verbal) sprintf(string_output,"Mask_intersect_list for Volume       %d",volume_index_main);
      if (verbal) print_1d_int_list(Volumes[volume_index_main]->geometry.mask_intersect_list,string_output);
      
      if (verbal) sprintf(string_output,"Destinations_list for Volume         %d",volume_index_main);
      if (verbal) print_1d_int_list(Volumes[volume_index_main]->geometry.destinations_list,string_output);
      
      //if (verbal) sprintf(string_output,"Destinations_logic_list for Volume   %d",volume_index_main);
      //if (verbal) print_1d_int_list(Volumes[volume_index_main]->geometry.destinations_logic_list,string_output);
      
      if (verbal) sprintf(string_output,"Reduced_destinations_list for Volume %d",volume_index_main);
      if (verbal) print_1d_int_list(Volumes[volume_index_main]->geometry.reduced_destinations_list,string_output);
      
      if (verbal) sprintf(string_output,"Next_volume_list for Volume          %d",volume_index_main);
      if (verbal) print_1d_int_list(Volumes[volume_index_main]->geometry.next_volume_list,string_output);
      
      if (verbal) {
        if (volume_index_main != 0)
                           printf("      Is_vacuum for Volume                 %d = %d\n",volume_index_main,Volumes[volume_index_main]->p_physics->is_vacuum);
      }
      if (verbal) {
        if (volume_index_main != 0)
                           printf("      is_mask_volume for Volume            %d = %d\n",volume_index_main,Volumes[volume_index_main]->geometry.is_mask_volume);
      }
      if (verbal) {
        if (volume_index_main != 0)
                           printf("      is_masked_volume for Volume          %d = %d\n",volume_index_main,Volumes[volume_index_main]->geometry.is_masked_volume);
      }
      if (verbal) {
        if (volume_index_main != 0)
                           printf("      is_exit_volume for Volume            %d = %d\n",volume_index_main,Volumes[volume_index_main]->geometry.is_exit_volume);
      }
      
      if (verbal) sprintf(string_output,"mask_list for Volume                 %d",volume_index_main);
      if (verbal) print_1d_int_list(Volumes[volume_index_main]->geometry.mask_list,string_output);
      
      if (verbal) sprintf(string_output,"masked_by_list for Volume            %d",volume_index_main);
      if (verbal) print_1d_int_list(Volumes[volume_index_main]->geometry.masked_by_list,string_output);
      
      if (verbal) sprintf(string_output,"masked_by_mask_index_list for Volume %d",volume_index_main);
      if (verbal) print_1d_int_list(Volumes[volume_index_main]->geometry.masked_by_mask_index_list,string_output);
      
      if (verbal)          printf("      mask_mode for Volume                 %d = %d\n",volume_index_main,Volumes[volume_index_main]->geometry.mask_mode);
      if (verbal) printf("\n");
  }
  ) // End of MPI_MASTER
  

  // Initializing intersection_time_table
  // The intersection time table contains all information on intersection times for the current position/direction, and is cleared everytime a ray changes direction.
  // Not all entries needs to be calculated, so there is a variable that keeps track of which intersection times have been calculated in order to avoid redoing that.
  // When the intersections times are calculated for a volume, all future intersections are kept in the time table.
  // Thus the memory allocation have to take into account how many intersections there can be with each volume, but it is currently set to 2, but can easily be changed. This may need to be reported by individual geometry components in the future.
  
  intersection_time_table.num_volumes = number_of_volumes;
  
  intersection_time_table.n_elements = (int*) malloc(intersection_time_table.num_volumes * sizeof(int));
  intersection_time_table.calculated = (int*) malloc(intersection_time_table.num_volumes * sizeof(int));
  intersection_time_table.intersection_times = (double**) malloc(intersection_time_table.num_volumes * sizeof(double));
  for (iterator = 0;iterator < intersection_time_table.num_volumes;iterator++){
      if (strcmp(Volumes[iterator]->geometry.shape, "mesh") == 0) {
        intersection_time_table.n_elements[iterator] = (int) 100; // Meshes can have any number of intersections, here we allocate room for 100
      } else {
        intersection_time_table.n_elements[iterator] = (int) 2; // number of intersection for all other geometries
      }
      if (iterator == 0) intersection_time_table.n_elements[iterator] = (int) 0; // number of intersection solutions
      intersection_time_table.calculated[iterator] = (int) 0; // Initializing calculated logic
      
      if (iterator == 0) {
           intersection_time_table.intersection_times[0] = NULL;
      }
      else {
        intersection_time_table.intersection_times[iterator] = (double*) malloc(intersection_time_table.n_elements[iterator]*sizeof(double));
        for (solutions = 0;solutions < intersection_time_table.n_elements[iterator];solutions++) {
              intersection_time_table.intersection_times[iterator][solutions] = -1.0;
        }
      }
  }
  
  // If enabled, the tagging system tracks all different histories sampled by the program.

  // Initialize the tagging tree
  // Allocate a list of host nodes with the same length as the number of volumes
  
  stop_creating_nodes = 0; stop_tagging_ray = 0; tagging_leaf_counter = 0;
  if (enable_tagging) {
    master_tagging_node_list.num_elements = number_of_volumes;
    master_tagging_node_list.elements = malloc(master_tagging_node_list.num_elements * sizeof(struct tagging_tree_node_struct*));
  
    // Initialize
    for (volume_index=0;volume_index<number_of_volumes;volume_index++) {
      //if (verbal) printf("Allocating master tagging node for volume number %d \n",volume_index);
      master_tagging_node_list.elements[volume_index] = initialize_tagging_tree_node(master_tagging_node_list.elements[volume_index], NULL, Volumes[volume_index]);
      //if (verbal) printf("Allocated master tagging node for volume number %d \n",volume_index);
    }
  }
  
  // Initialize loggers
  loggers_with_data_array.allocated_elements = 0;
  loggers_with_data_array.used_elements = 0;
  
  abs_loggers_with_data_array.allocated_elements = 0;
  abs_loggers_with_data_array.used_elements = 0;
  
  
  // Signal initialization complete
  MPI_MASTER(
    printf("Union_master component %s initialized sucessfully\n",NAME_CURRENT_COMP);
  )

  #undef verbal
  #undef list_verbal
  #undef finally_verbal
  #undef allow_inside_start
  #undef enable_tagging
  #undef history_limit
  #undef enable_conditionals
  #undef inherit_number_of_scattering_events
  #undef init
  #undef global_positions_to_transform_list_master
  #undef global_rotations_to_transform_list_master
  #undef global_process_list_master
  #undef global_material_list_master
  #undef global_geometry_list_master
  #undef global_all_volume_logger_list_master
  #undef global_specific_volumes_logger_list_master
  #undef global_all_volume_abs_logger_list_master
  #undef global_specific_volumes_abs_logger_list_master
  #undef global_tagging_conditional_list_master
  #undef global_master_list_master
  #undef starting_volume_warning
  #undef global_master_element
  #undef this_global_master_index
  #undef previous_master_index
  #undef geometry_list_index
  #undef intersection_time_table
  #undef Volumes
  #undef Geometries
  #undef Volume_copies
  #undef starting_lists
  #undef Volume_copies_allocated
  #undef r
  #undef r_start
  #undef v
  #undef error_msg
  #undef component_error_msg
  #undef string_output
  #undef number_of_volumes
  #undef volume_index
  #undef process_index
  #undef iterator
  #undef solutions
  #undef max_number_of_processes
  #undef limit
  #undef solution
  #undef min_solution
  #undef min_volume
  #undef time_found
  #undef intersection_time
  #undef min_intersection_time
  #undef process
  #undef process_start
  #undef my_trace
  #undef p_my_trace
  #undef my_trace_fraction_control
  #undef k
  #undef k_new
  #undef k_old
  #undef k_rotated
  #undef v_length
  #undef my_sum
  #undef my_sum_plus_abs
  #undef culmative_probability
  #undef mc_prop
  #undef time_to_scattering
  #undef length_to_scattering
  #undef length_to_boundery
  #undef time_to_boundery
  #undef selected_process
  #undef scattering_event
  #undef time_propagated_without_scattering
  #undef a_next_volume_found
  #undef next_volume
  #undef next_volume_priority
  #undef done
  #undef current_volume
  #undef ray_sucseeded
  #undef number_of_solutions
  #undef number_of_solutions_static
  #undef check
  #undef start
  #undef intersection_with_children
  #undef geometry_output
  #undef tree_next_volume
  #undef pre_allocated1
  #undef pre_allocated2
  #undef pre_allocated3
  #undef ray_position
  #undef ray_velocity
  #undef ray_velocity_rotated
  #undef ray_velocity_final
  #undef wavevector
  #undef wavevector_rotated
  #undef volume_0_found
  #undef scattered_flag
  #undef scattered_flag_VP
  #undef master_transposed_rotation_matrix
  #undef temp_rotation_matrix
  #undef temp_transpose_rotation_matrix
  #undef non_rotated_position
  #undef rotated_position
  #undef non_isotropic_found
  #undef master_tagging_node_list
  #undef current_tagging_node
  #undef tagging_leaf_counter
  #undef stop_tagging_ray
  #undef stop_creating_nodes
  #undef number_of_scattering_events
  #undef real_transmission_probability
  #undef mc_transmission_probability
  #undef number_of_process_interacts_set
  #undef index_of_lacking_process
  #undef total_process_interact
  #undef geometry_component_index_list
  #undef mask_volume_index_list
  #undef number_of_masks
  #undef number_of_masked_volumes
  #undef mask_status_list
  #undef current_mask_intersect_list_status
  #undef mask_index_main
  #undef mask_iterator
  #undef mask_start
  #undef mask_check
  #undef need_to_run_within_which_volume
  #undef number_of_processes_array
  #undef p_old
  #undef log_index
  #undef conditional_status
  #undef this_logger
  #undef this_abs_logger
  #undef tagging_conditional_list
  #undef logger_conditional_extend_array
  #undef abs_logger_conditional_extend_array
  #undef max_conditional_extend_index
  #undef tagging_conditional_extend
  #undef free_tagging_conditioanl_list
  #undef safty_distance
  #undef safty_distance2
  #undef temporary_focus_data
  #undef focus_data_index
  #undef r_old
  #undef initial_weight
  #undef abs_weight_factor
  #undef time_old
  #undef absorption_index
  #undef abs_weight_factor_set
  #undef my_abs
  #undef absorption_event_data
  #undef abs_position
  #undef transformed_abs_position
  #undef t_abs_propagation
  #undef abs_distance
  #undef abs_max_length
  return(_comp);
} /* class_Union_master_init */



int init(void) { /* called by mccode_main for template_body_simple:INITIALISE */
  DEBUG_INSTR();

  /* code_main/parseoptions/readparams sets instrument parameters value */
  stracpy(instrument->_name, "template_body_simple", 256);

  /* Instrument 'template_body_simple' INITIALISE */
  SIG_MESSAGE("[template_body_simple] INITIALISE [RITA-2-Kristine-instrument.instr:59]");
  #define Ei (instrument->_parameters.Ei)
  #define Ef (instrument->_parameters.Ef)
  #define mono_focus (instrument->_parameters.mono_focus)
  #define h (instrument->_parameters.h)
  #define k (instrument->_parameters.k)
  #define col_div_1 (instrument->_parameters.col_div_1)
  #define col_div_2 (instrument->_parameters.col_div_2)
  #define slit_left (instrument->_parameters.slit_left)
  #define slit_right (instrument->_parameters.slit_right)
  #define slit_top (instrument->_parameters.slit_top)
  #define slit_bottom (instrument->_parameters.slit_bottom)
  #define sym (instrument->_parameters.sym)
  #define dA3 (instrument->_parameters.dA3)
  #define T (instrument->_parameters.T)
{

detector_x=0.03;
detector_y=0.20;
l_cryostat = 0.15;

dm=3.355;
ds= 4.873;

// Basics of the scattering

qm = 2*PI/dm;

lambda_i = (2.0*PI)/(sqrt(Ei)*0.694692);

A1 = asin((lambda_i)/(2.0*dm))*180.0/PI;

lambda_f = (2.0*PI)/(sqrt(Ef)*0.694692);

A5 = asin((lambda_f)/(2.0*dm))*180.0/PI;

q0 = 2.0*PI/ds;

ql = sqrt(h*h+k*k);

ki = sqrt(Ei)*0.694692;

kf = sqrt(Ef)*0.694692;

Q = q0*ql;

// Misalignment

DeltaA3 =-7.58;

// Calculate magnetic critical scattering

TN = 67.13; // The "secret" Neel temperature

Tred = T/TN;

beta = 0.365;
mu = 0.700;

Magn_order_factor=0;

if ((Tred < 1.0) && (Tred >= 0.8))
  Magn_order_factor = 2.2*0.8835*exp(2*beta*log(1-Tred)); //pow(1-Tred,2*beta); // 0.8 < Tred < 1

if ((0.3 <= Tred) && (Tred < 0.8))
Magn_order_factor = 1.00962 - 0.10*Tred - 0.1*pow(Tred,2) - 0.2*pow(Tred,4) - 0.7*pow(Tred,6); // 0.3 < Tred < 0.8
 
if (Tred < 0.3)  
   Magn_order_factor = 1-0.35*pow(Tred,2); // Tred < 0.3

Magn_order_factor *= 0.01; // The intensity looks too high...

C_Intensity = 2e-3*exp(0.2*log(fabs(Tred-1)));

DQ = 1*exp(mu*log(fabs(1-Tred)));

C_mosaic = 180/PI*60*DQ/q0; 
 
// Calculate angles from rlu
if (sym==0){
	A4 = acos(-(Q*Q-ki*ki-kf*kf)/(2.0*ki*kf))*180.0/PI;
	A3 = acos(-(kf*kf-ki*ki-Q*Q)/(2.0*ki*Q))*180.0/PI+atan2(k,h)*180.0/PI+dA3;
}

if (sym==1){
	A4 = -acos(-(Q*Q-ki*ki-kf*kf)/(2.0*ki*kf))*180.0/PI;
	A3 = -acos(-(kf*kf-ki*ki-Q*Q)/(2.0*ki*Q))*180.0/PI+atan2(k,h)*180.0/PI+dA3;
}

// Calculate angles for focusing monochromator
phi1 = 180/PI*(0.026/1.54)*(ki/qm);
phi2 = 180/PI*(0.052/1.54)*(ki/qm);

}
  #undef Ei
  #undef Ef
  #undef mono_focus
  #undef h
  #undef k
  #undef col_div_1
  #undef col_div_2
  #undef slit_left
  #undef slit_right
  #undef slit_top
  #undef slit_bottom
  #undef sym
  #undef dA3
  #undef T
  _origin_setpos(); /* type Progress_bar */
  _source_maxwell_3_setpos(); /* type Source_Maxwell_3 */
  _guide_setpos(); /* type Guide */
  _PSD_before_monochromator_setpos(); /* type PSD_monitor */
  _a1_setpos(); /* type Arm */
  _monochromator_flat1_setpos(); /* type Monochromator_flat */
  _monochromator_flat2_setpos(); /* type Monochromator_flat */
  _monochromator_flat3_setpos(); /* type Monochromator_flat */
  _monochromator_flat4_setpos(); /* type Monochromator_flat */
  _monochromator_flat5_setpos(); /* type Monochromator_flat */
  _a2_setpos(); /* type Arm */
  _PSD_after_monochromator_setpos(); /* type PSD_monitor */
  _collimator_linear_setpos(); /* type Collimator_linear */
  _slit_setpos(); /* type Slit */
  _PSD_before_sampel_setpos(); /* type PSD_monitor */
  _Emon_before_sampel_setpos(); /* type E_monitor */
  _a3_setpos(); /* type Arm */
  _a4_setpos(); /* type Arm */
  _a5_setpos(); /* type Arm */
  _sample_position_setpos(); /* type Arm */
  _init_setpos(); /* type Union_init */
  _Vanadium_incoherent_Al_setpos(); /* type Incoherent_process */
  _Al_Powder_setpos(); /* type Powder_process */
  _Al_setpos(); /* type Union_make_material */
  _Vanadium_incoherent_Cu_setpos(); /* type Incoherent_process */
  _Vanadium_incoherent_MnF2_setpos(); /* type Incoherent_process */
  _MnF2_critical_setpos(); /* type Single_crystal_process */
  _MnF2_single_crystal_setpos(); /* type Single_crystal_process */
  _MnF2_single_crystal_magn_setpos(); /* type Single_crystal_process */
  _MnF2_setpos(); /* type Union_make_material */
  _union_sample_setpos(); /* type Union_cylinder */
  _union_stick_setpos(); /* type Union_cylinder */
  _cryostat_wall_setpos(); /* type Union_cylinder */
  _cryostat_vacuum_setpos(); /* type Union_cylinder */
  _Master_setpos(); /* type Union_master */
  _stop_setpos(); /* type Union_stop */
  _collimator_linear_2_setpos(); /* type Collimator_linear */
  _PSD_after_sampel_setpos(); /* type PSD_monitor */
  _monochromator_flat6_setpos(); /* type Monochromator_flat */
  _a6_setpos(); /* type Arm */
  _Detector_setpos(); /* type PSD_monitor */

  /* call iteratively all components INITIALISE */
  class_Progress_bar_init(&_origin_var);

  class_Source_Maxwell_3_init(&_source_maxwell_3_var);

  class_Guide_init(&_guide_var);

  class_PSD_monitor_init(&_PSD_before_monochromator_var);


  class_Monochromator_flat_init(&_monochromator_flat1_var);

  class_Monochromator_flat_init(&_monochromator_flat2_var);

  class_Monochromator_flat_init(&_monochromator_flat3_var);

  class_Monochromator_flat_init(&_monochromator_flat4_var);

  class_Monochromator_flat_init(&_monochromator_flat5_var);


  class_PSD_monitor_init(&_PSD_after_monochromator_var);

  class_Collimator_linear_init(&_collimator_linear_var);

  class_Slit_init(&_slit_var);

  class_PSD_monitor_init(&_PSD_before_sampel_var);

  class_E_monitor_init(&_Emon_before_sampel_var);





  class_Union_init_init(&_init_var);

  class_Incoherent_process_init(&_Vanadium_incoherent_Al_var);

  class_Powder_process_init(&_Al_Powder_var);

  class_Union_make_material_init(&_Al_var);

  class_Incoherent_process_init(&_Vanadium_incoherent_Cu_var);

  class_Incoherent_process_init(&_Vanadium_incoherent_MnF2_var);

  class_Single_crystal_process_init(&_MnF2_critical_var);

  class_Single_crystal_process_init(&_MnF2_single_crystal_var);

  class_Single_crystal_process_init(&_MnF2_single_crystal_magn_var);

  class_Union_make_material_init(&_MnF2_var);

  class_Union_cylinder_init(&_union_sample_var);

  class_Union_cylinder_init(&_union_stick_var);

  class_Union_cylinder_init(&_cryostat_wall_var);

  class_Union_cylinder_init(&_cryostat_vacuum_var);

  class_Union_master_init(&_Master_var);


  class_Collimator_linear_init(&_collimator_linear_2_var);

  class_PSD_monitor_init(&_PSD_after_sampel_var);

  class_Monochromator_flat_init(&_monochromator_flat6_var);


  class_PSD_monitor_init(&_Detector_var);

  if (mcdotrace) display();
  DEBUG_INSTR_END();

#ifdef OPENACC
#include <openacc.h>
#pragma acc update device(_origin_var)
#pragma acc update device(_source_maxwell_3_var)
#pragma acc update device(_guide_var)
#pragma acc update device(_PSD_before_monochromator_var)
#pragma acc update device(_a1_var)
#pragma acc update device(_monochromator_flat1_var)
#pragma acc update device(_monochromator_flat2_var)
#pragma acc update device(_monochromator_flat3_var)
#pragma acc update device(_monochromator_flat4_var)
#pragma acc update device(_monochromator_flat5_var)
#pragma acc update device(_a2_var)
#pragma acc update device(_PSD_after_monochromator_var)
#pragma acc update device(_collimator_linear_var)
#pragma acc update device(_slit_var)
#pragma acc update device(_PSD_before_sampel_var)
#pragma acc update device(_Emon_before_sampel_var)
#pragma acc update device(_a3_var)
#pragma acc update device(_a4_var)
#pragma acc update device(_a5_var)
#pragma acc update device(_sample_position_var)
#pragma acc update device(_init_var)
#pragma acc update device(_Vanadium_incoherent_Al_var)
#pragma acc update device(_Al_Powder_var)
#pragma acc update device(_Al_var)
#pragma acc update device(_Vanadium_incoherent_Cu_var)
#pragma acc update device(_Vanadium_incoherent_MnF2_var)
#pragma acc update device(_MnF2_critical_var)
#pragma acc update device(_MnF2_single_crystal_var)
#pragma acc update device(_MnF2_single_crystal_magn_var)
#pragma acc update device(_MnF2_var)
#pragma acc update device(_union_sample_var)
#pragma acc update device(_union_stick_var)
#pragma acc update device(_cryostat_wall_var)
#pragma acc update device(_cryostat_vacuum_var)
#pragma acc update device(_Master_var)
#pragma acc update device(_stop_var)
#pragma acc update device(_collimator_linear_2_var)
#pragma acc update device(_PSD_after_sampel_var)
#pragma acc update device(_monochromator_flat6_var)
#pragma acc update device(_a6_var)
#pragma acc update device(_Detector_var)
#pragma acc update device(_instrument_var)
#endif

  return(0);
} /* init */

/*******************************************************************************
* components TRACE
*******************************************************************************/

#define x (_particle->x)
#define y (_particle->y)
#define z (_particle->z)
#define vx (_particle->vx)
#define vy (_particle->vy)
#define vz (_particle->vz)
#define t (_particle->t)
#define sx (_particle->sx)
#define sy (_particle->sy)
#define sz (_particle->sz)
#define p (_particle->p)
#define mcgravitation (_particle->mcgravitation)
#define mcMagnet (_particle->mcMagnet)
#define allow_backprop (_particle->allow_backprop)
/* if on GPU, globally nullify sprintf,fprintf,printfs   */
/* (Similar defines are available in each comp trace but */
/*  those are not enough to handle external libs etc. )  */
#ifdef OPENACC
#ifndef MULTICORE
#define fprintf(stderr,...) printf(__VA_ARGS__)
#define sprintf(string,...) printf(__VA_ARGS__)
#define exit(...) noprintf()
#define strcmp(a,b) str_comp(a,b)
#define strlen(a) str_len(a)
#endif
#endif
#define SCATTERED (_particle->_scattered)
#define RESTORE (_particle->_restore)
#define RESTORE_NEUTRON(_index, ...) _particle->_restore = _index;
#define ABSORBED (_particle->_absorbed)
#define mcget_run_num() _particle->_uid
#define ABSORB0 do { DEBUG_STATE(); DEBUG_ABSORB(); MAGNET_OFF; ABSORBED++; return(_comp); } while(0)
#define ABSORB ABSORB0
#pragma acc routine
_class_Progress_bar *class_Progress_bar_trace(_class_Progress_bar *_comp
  , _class_particle *_particle) {
  ABSORBED=SCATTERED=RESTORE=0;
  #define profile (_comp->_parameters.profile)
  #define percent (_comp->_parameters.percent)
  #define flag_save (_comp->_parameters.flag_save)
  #define minutes (_comp->_parameters.minutes)
  #define IntermediateCnts (_comp->_parameters.IntermediateCnts)
  #define StartTime (_comp->_parameters.StartTime)
  #define EndTime (_comp->_parameters.EndTime)
  #define CurrentTime (_comp->_parameters.CurrentTime)
  SIG_MESSAGE("[_origin_trace] component origin=Progress_bar() TRACE [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//misc/Progress_bar.comp:73]");

#ifndef OPENACC
  double ncount;
  ncount = mcget_run_num();
  if (!StartTime) {
    time(&StartTime); /* compute starting time */
    IntermediateCnts = 1e3;
  }
  time_t NowTime;
  time(&NowTime);
  /* compute initial estimate of computation duration */
  if (!EndTime && ncount >= IntermediateCnts) {
    CurrentTime = NowTime;
    if (difftime(NowTime,StartTime) > 10 && ncount) { /* wait 10 sec before writing ETA */
      EndTime = StartTime + (time_t)(difftime(NowTime,StartTime)
				     *(double)mcget_ncount()/ncount);
      IntermediateCnts = 0;
      fprintf(stdout, "\nTrace ETA ");
      if (difftime(EndTime,StartTime) < 60.0)
        fprintf(stdout, "%g [s] %% ", difftime(EndTime,StartTime));
      else if (difftime(EndTime,StartTime) > 3600.0)
        fprintf(stdout, "%g [h] %% ", difftime(EndTime,StartTime)/3600.0);
      else
        fprintf(stdout, "%g [min] %% ", difftime(EndTime,StartTime)/60.0);
    } else IntermediateCnts += 1e3;
    fflush(stdout);
  }

  /* display percentage when percent or minutes have reached step */
  if (EndTime && mcget_ncount() &&
    (    (minutes && difftime(NowTime,CurrentTime) > minutes*60)
      || (percent && !minutes && ncount >= IntermediateCnts))   )
  {
    fprintf(stdout, "%d ", (int)(ncount*100.0/mcget_ncount())); fflush(stdout);
    CurrentTime = NowTime;

    IntermediateCnts = ncount + percent*mcget_ncount()/100;
    /* check that next intermediate ncount check is a multiple of the desired percentage */
    IntermediateCnts = floor(IntermediateCnts*100/percent/mcget_ncount())*percent*mcget_ncount()/100;
    /* raise flag to indicate that we did something */
    SCATTER;
    if (flag_save) save(NULL);
  }
#endif
#ifndef NOABSORB_INF_NAN
  /* Check for nan or inf particle parms */ 
  if(isnan(p)  ||  isinf(p)) ABSORB;
  if(isnan(t)  ||  isinf(t)) ABSORB;
  if(isnan(vx) || isinf(vx)) ABSORB;
  if(isnan(vy) || isinf(vy)) ABSORB;
  if(isnan(vz) || isinf(vz)) ABSORB;
  if(isnan(x)  ||  isinf(x)) ABSORB;
  if(isnan(y)  ||  isinf(y)) ABSORB;
  if(isnan(z)  ||  isinf(z)) ABSORB;
#else
  if(isnan(p)  ||  isinf(p)) printf("NAN or INF found in p,  %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(t)  ||  isinf(t)) printf("NAN or INF found in t,  %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(vx) || isinf(vx)) printf("NAN or INF found in vx, %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(vy) || isinf(vy)) printf("NAN or INF found in vy, %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(vz) || isinf(vz)) printf("NAN or INF found in vz, %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(x)  ||  isinf(x)) printf("NAN or INF found in x,  %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(y)  ||  isinf(y)) printf("NAN or INF found in y,  %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(z)  ||  isinf(z)) printf("NAN or INF found in z,  %s (particle %lld)\n",_comp->_name,_particle->_uid);
#endif
  #undef profile
  #undef percent
  #undef flag_save
  #undef minutes
  #undef IntermediateCnts
  #undef StartTime
  #undef EndTime
  #undef CurrentTime
  return(_comp);
} /* class_Progress_bar_trace */

#pragma acc routine
_class_Source_Maxwell_3 *class_Source_Maxwell_3_trace(_class_Source_Maxwell_3 *_comp
  , _class_particle *_particle) {
  ABSORBED=SCATTERED=RESTORE=0;
  #define size (_comp->_parameters.size)
  #define yheight (_comp->_parameters.yheight)
  #define xwidth (_comp->_parameters.xwidth)
  #define Lmin (_comp->_parameters.Lmin)
  #define Lmax (_comp->_parameters.Lmax)
  #define dist (_comp->_parameters.dist)
  #define focus_xw (_comp->_parameters.focus_xw)
  #define focus_yh (_comp->_parameters.focus_yh)
  #define T1 (_comp->_parameters.T1)
  #define T2 (_comp->_parameters.T2)
  #define T3 (_comp->_parameters.T3)
  #define I1 (_comp->_parameters.I1)
  #define I2 (_comp->_parameters.I2)
  #define I3 (_comp->_parameters.I3)
  #define target_index (_comp->_parameters.target_index)
  #define lambda0 (_comp->_parameters.lambda0)
  #define dlambda (_comp->_parameters.dlambda)
  #define l_range (_comp->_parameters.l_range)
  #define w_mult (_comp->_parameters.w_mult)
  #define w_source (_comp->_parameters.w_source)
  #define h_source (_comp->_parameters.h_source)
  SIG_MESSAGE("[_source_maxwell_3_trace] component source_maxwell_3=Source_Maxwell_3() TRACE [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//sources/Source_Maxwell_3.comp:124]");

  double v,tau_l,E,lambda,k,r,xf,yf,dx,dy,w_focus;

  t=0;
  z=0;
  x = 0.5*w_source*randpm1();
  y = 0.5*h_source*randpm1();         /* Choose initial position */

  randvec_target_rect_real(&xf, &yf, &r, &w_focus,
		      0, 0, dist, focus_xw, focus_yh, ROT_A_CURRENT_COMP, x, y, z, 2);

  dx = xf-x;
  dy = yf-y;
  r = sqrt(dx*dx+dy*dy+dist*dist);

  lambda = Lmin+l_range*rand01();    /* Choose from uniform distribution */
  k = 2*PI/lambda;
  v = K2V*k;

  vz = v*dist/r;
  vy = v*dy/r;
  vx = v*dx/r;


/*  printf("pos0 (%g %g %g), pos1 (%g %g %g), r: %g, v (%g %g %g), v %g\n",
  x,y,z,xf,yf,dist,r,vx,vy,vz, v);
  printf("l %g, w_focus %g \n", lambda, w_focus);  */

  p *= w_mult*w_focus;                /* Correct for target focusing etc */
  p *= I1*SM3_Maxwell(lambda,T1)+I2*SM3_Maxwell(lambda,T2)+I3*SM3_Maxwell(lambda,T3);
                                        /* Calculate true intensity */
#ifndef NOABSORB_INF_NAN
  /* Check for nan or inf particle parms */ 
  if(isnan(p)  ||  isinf(p)) ABSORB;
  if(isnan(t)  ||  isinf(t)) ABSORB;
  if(isnan(vx) || isinf(vx)) ABSORB;
  if(isnan(vy) || isinf(vy)) ABSORB;
  if(isnan(vz) || isinf(vz)) ABSORB;
  if(isnan(x)  ||  isinf(x)) ABSORB;
  if(isnan(y)  ||  isinf(y)) ABSORB;
  if(isnan(z)  ||  isinf(z)) ABSORB;
#else
  if(isnan(p)  ||  isinf(p)) printf("NAN or INF found in p,  %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(t)  ||  isinf(t)) printf("NAN or INF found in t,  %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(vx) || isinf(vx)) printf("NAN or INF found in vx, %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(vy) || isinf(vy)) printf("NAN or INF found in vy, %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(vz) || isinf(vz)) printf("NAN or INF found in vz, %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(x)  ||  isinf(x)) printf("NAN or INF found in x,  %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(y)  ||  isinf(y)) printf("NAN or INF found in y,  %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(z)  ||  isinf(z)) printf("NAN or INF found in z,  %s (particle %lld)\n",_comp->_name,_particle->_uid);
#endif
  #undef size
  #undef yheight
  #undef xwidth
  #undef Lmin
  #undef Lmax
  #undef dist
  #undef focus_xw
  #undef focus_yh
  #undef T1
  #undef T2
  #undef T3
  #undef I1
  #undef I2
  #undef I3
  #undef target_index
  #undef lambda0
  #undef dlambda
  #undef l_range
  #undef w_mult
  #undef w_source
  #undef h_source
  return(_comp);
} /* class_Source_Maxwell_3_trace */

#pragma acc routine
_class_Guide *class_Guide_trace(_class_Guide *_comp
  , _class_particle *_particle) {
  ABSORBED=SCATTERED=RESTORE=0;
  #define reflect (_comp->_parameters.reflect)
  #define w1 (_comp->_parameters.w1)
  #define h1 (_comp->_parameters.h1)
  #define w2 (_comp->_parameters.w2)
  #define h2 (_comp->_parameters.h2)
  #define l (_comp->_parameters.l)
  #define R0 (_comp->_parameters.R0)
  #define Qc (_comp->_parameters.Qc)
  #define alpha (_comp->_parameters.alpha)
  #define m (_comp->_parameters.m)
  #define W (_comp->_parameters.W)
  #define pTable (_comp->_parameters.pTable)
  #define table_present (_comp->_parameters.table_present)
  SIG_MESSAGE("[_guide_trace] component guide=Guide() TRACE [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//optics/Guide.comp:96]");

  double t1,t2;                                 /* Intersection times. */
  double av,ah,bv,bh,cv1,cv2,ch1,ch2,d;         /* Intermediate values */
  double weight;                                /* Internal probability weight */
  double vdotn_v1,vdotn_v2,vdotn_h1,vdotn_h2;   /* Dot products. */
  int i;                                        /* Which mirror hit? */
  double q;                                     /* Q [1/AA] of reflection */
  double nlen2;                                 /* Vector lengths squared */
  double par[5] = {R0, Qc, alpha, m, W};
  
  /* ToDo: These could be precalculated. */
  double ww = .5*(w2 - w1), hh = .5*(h2 - h1);
  double whalf = .5*w1, hhalf = .5*h1;

  /* Propagate neutron to guide entrance. */
  PROP_Z0;
  /* Scatter here to ensure that fully transmitted neutrons will not be
     absorbed in a GROUP construction, e.g. all neutrons - even the
     later absorbed ones are scattered at the guide entry. */
  SCATTER;
  if(x <= -whalf || x >= whalf || y <= -hhalf || y >= hhalf)
    ABSORB;
  for(;;)
  {
    /* Compute the dot products of v and n for the four mirrors. */
    av = l*vx; bv = ww*vz;
    ah = l*vy; bh = hh*vz;
    vdotn_v1 = bv + av;         /* Left vertical */
    vdotn_v2 = bv - av;         /* Right vertical */
    vdotn_h1 = bh + ah;         /* Lower horizontal */
    vdotn_h2 = bh - ah;         /* Upper horizontal */
    /* Compute the dot products of (O - r) and n as c1+c2 and c1-c2 */
    cv1 = -whalf*l - z*ww; cv2 = x*l;
    ch1 = -hhalf*l - z*hh; ch2 = y*l;
    /* Compute intersection times. */
    t1 = (l - z)/vz;
    i = 0;
    if(vdotn_v1 < 0 && (t2 = (cv1 - cv2)/vdotn_v1) < t1)
    {
      t1 = t2;
      i = 1;
    }
    if(vdotn_v2 < 0 && (t2 = (cv1 + cv2)/vdotn_v2) < t1)
    {
      t1 = t2;
      i = 2;
    }
    if(vdotn_h1 < 0 && (t2 = (ch1 - ch2)/vdotn_h1) < t1)
    {
      t1 = t2;
      i = 3;
    }
    if(vdotn_h2 < 0 && (t2 = (ch1 + ch2)/vdotn_h2) < t1)
    {
      t1 = t2;
      i = 4;
    }
    if(i == 0)
      break;                    /* Neutron left guide. */
    PROP_DT(t1);
    switch(i)
    {
      case 1:                   /* Left vertical mirror */
        nlen2 = l*l + ww*ww;
        q = V2Q*(-2)*vdotn_v1/sqrt(nlen2);
        d = 2*vdotn_v1/nlen2;
        vx = vx - d*l;
        vz = vz - d*ww;
        break;
      case 2:                   /* Right vertical mirror */
        nlen2 = l*l + ww*ww;
        q = V2Q*(-2)*vdotn_v2/sqrt(nlen2);
        d = 2*vdotn_v2/nlen2;
        vx = vx + d*l;
        vz = vz - d*ww;
        break;
      case 3:                   /* Lower horizontal mirror */
        nlen2 = l*l + hh*hh;
        q = V2Q*(-2)*vdotn_h1/sqrt(nlen2);
        d = 2*vdotn_h1/nlen2;
        vy = vy - d*l;
        vz = vz - d*hh;
        break;
      case 4:                   /* Upper horizontal mirror */
        nlen2 = l*l + hh*hh;
        q = V2Q*(-2)*vdotn_h2/sqrt(nlen2);
        d = 2*vdotn_h2/nlen2;
        vy = vy + d*l;
        vz = vz - d*hh;
        break;
    }
    /* Now compute reflectivity. */
    weight = 1.0; /* Initial internal weight factor */
    if(m == 0)
      ABSORB;
    if (reflect && table_present==1)
       TableReflecFunc(q, &pTable, &weight);
    else {
      StdReflecFunc(q, par, &weight);
    }
    if (weight > 0)
      p *= weight;
    else ABSORB;
    SCATTER;
  }
#ifndef NOABSORB_INF_NAN
  /* Check for nan or inf particle parms */ 
  if(isnan(p)  ||  isinf(p)) ABSORB;
  if(isnan(t)  ||  isinf(t)) ABSORB;
  if(isnan(vx) || isinf(vx)) ABSORB;
  if(isnan(vy) || isinf(vy)) ABSORB;
  if(isnan(vz) || isinf(vz)) ABSORB;
  if(isnan(x)  ||  isinf(x)) ABSORB;
  if(isnan(y)  ||  isinf(y)) ABSORB;
  if(isnan(z)  ||  isinf(z)) ABSORB;
#else
  if(isnan(p)  ||  isinf(p)) printf("NAN or INF found in p,  %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(t)  ||  isinf(t)) printf("NAN or INF found in t,  %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(vx) || isinf(vx)) printf("NAN or INF found in vx, %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(vy) || isinf(vy)) printf("NAN or INF found in vy, %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(vz) || isinf(vz)) printf("NAN or INF found in vz, %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(x)  ||  isinf(x)) printf("NAN or INF found in x,  %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(y)  ||  isinf(y)) printf("NAN or INF found in y,  %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(z)  ||  isinf(z)) printf("NAN or INF found in z,  %s (particle %lld)\n",_comp->_name,_particle->_uid);
#endif
  #undef reflect
  #undef w1
  #undef h1
  #undef w2
  #undef h2
  #undef l
  #undef R0
  #undef Qc
  #undef alpha
  #undef m
  #undef W
  #undef pTable
  #undef table_present
  return(_comp);
} /* class_Guide_trace */

#pragma acc routine
_class_PSD_monitor *class_PSD_monitor_trace(_class_PSD_monitor *_comp
  , _class_particle *_particle) {
  ABSORBED=SCATTERED=RESTORE=0;
  #define nx (_comp->_parameters.nx)
  #define ny (_comp->_parameters.ny)
  #define filename (_comp->_parameters.filename)
  #define xmin (_comp->_parameters.xmin)
  #define xmax (_comp->_parameters.xmax)
  #define ymin (_comp->_parameters.ymin)
  #define ymax (_comp->_parameters.ymax)
  #define xwidth (_comp->_parameters.xwidth)
  #define yheight (_comp->_parameters.yheight)
  #define restore_neutron (_comp->_parameters.restore_neutron)
  #define nowritefile (_comp->_parameters.nowritefile)
  #define PSD_N (_comp->_parameters.PSD_N)
  #define PSD_p (_comp->_parameters.PSD_p)
  #define PSD_p2 (_comp->_parameters.PSD_p2)
  SIG_MESSAGE("[_PSD_before_monochromator_trace] component PSD_before_monochromator=PSD_monitor() TRACE [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//monitors/PSD_monitor.comp:82]");

  PROP_Z0;
  if (x>xmin && x<xmax && y>ymin && y<ymax){
    int i = floor((x - xmin)*nx/(xmax - xmin));
    int j = floor((y - ymin)*ny/(ymax - ymin));

    double p2 = p*p;
    #pragma acc atomic
    PSD_N[i][j] = PSD_N[i][j]+1;

    #pragma acc atomic
    PSD_p[i][j] = PSD_p[i][j]+p;
    
    #pragma acc atomic
    PSD_p2[i][j] = PSD_p2[i][j] + p2;
    
    SCATTER;
  }
  if (restore_neutron) {
    RESTORE_NEUTRON(INDEX_CURRENT_COMP, x, y, z, vx, vy, vz, t, sx, sy, sz, p);
  }
#ifndef NOABSORB_INF_NAN
  /* Check for nan or inf particle parms */ 
  if(isnan(p)  ||  isinf(p)) ABSORB;
  if(isnan(t)  ||  isinf(t)) ABSORB;
  if(isnan(vx) || isinf(vx)) ABSORB;
  if(isnan(vy) || isinf(vy)) ABSORB;
  if(isnan(vz) || isinf(vz)) ABSORB;
  if(isnan(x)  ||  isinf(x)) ABSORB;
  if(isnan(y)  ||  isinf(y)) ABSORB;
  if(isnan(z)  ||  isinf(z)) ABSORB;
#else
  if(isnan(p)  ||  isinf(p)) printf("NAN or INF found in p,  %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(t)  ||  isinf(t)) printf("NAN or INF found in t,  %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(vx) || isinf(vx)) printf("NAN or INF found in vx, %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(vy) || isinf(vy)) printf("NAN or INF found in vy, %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(vz) || isinf(vz)) printf("NAN or INF found in vz, %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(x)  ||  isinf(x)) printf("NAN or INF found in x,  %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(y)  ||  isinf(y)) printf("NAN or INF found in y,  %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(z)  ||  isinf(z)) printf("NAN or INF found in z,  %s (particle %lld)\n",_comp->_name,_particle->_uid);
#endif
  #undef nx
  #undef ny
  #undef filename
  #undef xmin
  #undef xmax
  #undef ymin
  #undef ymax
  #undef xwidth
  #undef yheight
  #undef restore_neutron
  #undef nowritefile
  #undef PSD_N
  #undef PSD_p
  #undef PSD_p2
  return(_comp);
} /* class_PSD_monitor_trace */

#pragma acc routine
_class_Monochromator_flat *class_Monochromator_flat_trace(_class_Monochromator_flat *_comp
  , _class_particle *_particle) {
  ABSORBED=SCATTERED=RESTORE=0;
  #define zmin (_comp->_parameters.zmin)
  #define zmax (_comp->_parameters.zmax)
  #define ymin (_comp->_parameters.ymin)
  #define ymax (_comp->_parameters.ymax)
  #define zwidth (_comp->_parameters.zwidth)
  #define yheight (_comp->_parameters.yheight)
  #define mosaich (_comp->_parameters.mosaich)
  #define mosaicv (_comp->_parameters.mosaicv)
  #define r0 (_comp->_parameters.r0)
  #define Q (_comp->_parameters.Q)
  #define DM (_comp->_parameters.DM)
  #define mos_rms_y (_comp->_parameters.mos_rms_y)
  #define mos_rms_z (_comp->_parameters.mos_rms_z)
  #define mos_rms_max (_comp->_parameters.mos_rms_max)
  #define mono_Q (_comp->_parameters.mono_Q)
  SIG_MESSAGE("[_monochromator_flat1_trace] component monochromator_flat1=Monochromator_flat() TRACE [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//optics/Monochromator_flat.comp:119]");

  double y1,z1,t1,dt,kix,kiy,kiz,ratio,order,q0x,k,q0,theta;
  double bx,by,bz,kux,kuy,kuz,ax,ay,az,phi;
  double cos_2theta,k_sin_2theta,cos_phi,sin_phi,q_x,q_y,q_z;
  double delta,p_reflect,total,c1x,c1y,c1z,width,mos_sample;
  int i;

  if(vx != 0.0 && (dt = -x/vx) >= 0.0)
  {                             /* Moving towards crystal? */
    y1 = y + vy*dt;             /* Propagate to crystal plane */
    z1 = z + vz*dt;
    t1 = t + dt;
    if (z1>zmin && z1<zmax && y1>ymin && y1<ymax)
    {                           /* Intersect the crystal? */
      kix = V2K*vx;             /* Initial wave vector */
      kiy = V2K*vy;
      kiz = V2K*vz;
      /* Get reflection order and corresponding nominal scattering vector q0
         of correct length and direction. Only the order with the closest
         scattering vector is considered */
      ratio = -2*kix/mono_Q;
      order = floor(ratio + .5);
      if(order == 0.0)
        order = ratio < 0 ? -1 : 1;
      /* Order will be negative when the neutron enters from the back, in
         which case the direction of Q0 is flipped. */
      if(order < 0)
        order = -order;
      /* Make sure the order is small enough to allow Bragg scattering at the
         given neutron wavelength */
      k = sqrt(kix*kix + kiy*kiy + kiz*kiz);
      kux = kix/k;              /* Unit vector along ki */
      kuy = kiy/k;
      kuz = kiz/k;
      if(order > 2*k/mono_Q)
        order--;
      if(order > 0)             /* Bragg scattering possible? (Kristine_made_correction) */
      {
        q0 = order*mono_Q;
        q0x = ratio < 0 ? -q0 : q0;
        theta = asin(q0/(2*k)); /* Actual bragg angle */
        /* Make MC choice: reflect or transmit? */
        delta = asin(fabs(kux)) - theta;
        p_reflect = r0*exp(-kiy*kiy/(kiy*kiy + kiz*kiz)*(delta*delta)/
                           (2*mos_rms_y*mos_rms_y))*
                       exp(-kiz*kiz/(kiy*kiy + kiz*kiz)*(delta*delta)/
                           (2*mos_rms_z*mos_rms_z));
        if(rand01() < p_reflect)
        {                       /* Reflect */
          cos_2theta = cos(2*theta);
          k_sin_2theta = k*sin(2*theta);
          /* Get unit normal to plane containing ki and most probable kf */
          vec_prod(bx, by, bz, kix, kiy, kiz, q0x, 0, 0);
          NORM(bx,by,bz);
          bx *= k_sin_2theta;
          by *= k_sin_2theta;
          bz *= k_sin_2theta;
          /* Get unit vector normal to ki and b */
          vec_prod(ax, ay, az, bx, by, bz, kux, kuy, kuz);
          /* Compute the total scattering probability at this ki */
          total = 0;
          /* Choose width of Gaussian distribution to sample the angle
           * phi on the Debye-Scherrer cone for the scattered neutron.
           * The radius of the Debye-Scherrer cone is smaller by a
           * factor 1/cos(theta) than the radius of the (partial) sphere
           * describing the possible orientations of Q due to mosaicity, so we
           * start with a width 1/cos(theta) greater than the largest of
           * the two mosaics. */
          mos_sample = mos_rms_max/cos(theta);
          c1x = kix*(cos_2theta-1);
          c1y = kiy*(cos_2theta-1);
          c1z = kiz*(cos_2theta-1);
          /* Loop, repeatedly reducing the sample width until it is small
           * enough to avoid sampling scattering directions with
           * ridiculously low scattering probability.
           * Use a cut-off at 5 times the gauss width for considering
           * scattering probability as well as for integration limits
           * when integrating the sampled distribution below. */
          for(i=0; i<100; i++) {
            width = 5*mos_sample;
            cos_phi = cos(width);
            sin_phi = sin(width);
            q_x = c1x + cos_phi*ax + sin_phi*bx;
            q_y = (c1y + cos_phi*ay + sin_phi*by)/mos_rms_y;
            q_z = (c1z + cos_phi*az + sin_phi*bz)/mos_rms_z;
            /* Stop when we get near a factor of 25=5^2. */
            if(q_z*q_z + q_y*q_y < (25/(2.0/3.0))*(q_x*q_x))
              break;
            mos_sample *= (2.0/3.0);
          }
          /* Now integrate the chosen sampling distribution, using a
           * cut-off at five times sigma. */
          for(i = 0; i < (sizeof(Gauss_X)/sizeof(double)); i++)
          {
            phi = width*Gauss_X[i];
            cos_phi = cos(phi);
            sin_phi = sin(phi);
            q_x = c1x + cos_phi*ax + sin_phi*bx;
            q_y = c1y + cos_phi*ay + sin_phi*by;
            q_z = c1z + cos_phi*az + sin_phi*bz;
            p_reflect = GAUSS((q_y/q_x),0,mos_rms_y)*
                        GAUSS((q_z/q_x),0,mos_rms_z);
            total += Gauss_W[i]*p_reflect;
          }
          total *= width;
          /* Choose point on Debye-Scherrer cone. Sample from a Gaussian of
           * width 1/cos(theta) greater than the mosaic and correct for any
           * error by adjusting the neutron weight later. */
          phi = mos_sample*randnorm();
          /* Compute final wave vector kf and scattering vector q = ki - kf */
          cos_phi = cos(phi);
          sin_phi = sin(phi);
          q_x = c1x + cos_phi*ax + sin_phi*bx;
          q_y = c1y + cos_phi*ay + sin_phi*by;
          q_z = c1z + cos_phi*az + sin_phi*bz;
          p_reflect = GAUSS((q_y/q_x),0,mos_rms_y)*
                      GAUSS((q_z/q_x),0,mos_rms_z);
          x = 0;
          y = y1;
          z = z1;
          t = t1;
          vx = K2V*(kix+q_x);
          vy = K2V*(kiy+q_y);
          vz = K2V*(kiz+q_z);
          p_reflect /= total*GAUSS(phi,0,mos_sample);
          if (p_reflect <= 0) ABSORB;
          if (p_reflect > 1)  p_reflect = 1;
          p *= p_reflect;
          SCATTER;
        } /* End MC choice to reflect or transmit neutron */
      } /* End bragg scattering possible */
    } /* End intersect the crystal */
  } /* End neutron moving towards crystal */
#ifndef NOABSORB_INF_NAN
  /* Check for nan or inf particle parms */ 
  if(isnan(p)  ||  isinf(p)) ABSORB;
  if(isnan(t)  ||  isinf(t)) ABSORB;
  if(isnan(vx) || isinf(vx)) ABSORB;
  if(isnan(vy) || isinf(vy)) ABSORB;
  if(isnan(vz) || isinf(vz)) ABSORB;
  if(isnan(x)  ||  isinf(x)) ABSORB;
  if(isnan(y)  ||  isinf(y)) ABSORB;
  if(isnan(z)  ||  isinf(z)) ABSORB;
#else
  if(isnan(p)  ||  isinf(p)) printf("NAN or INF found in p,  %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(t)  ||  isinf(t)) printf("NAN or INF found in t,  %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(vx) || isinf(vx)) printf("NAN or INF found in vx, %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(vy) || isinf(vy)) printf("NAN or INF found in vy, %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(vz) || isinf(vz)) printf("NAN or INF found in vz, %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(x)  ||  isinf(x)) printf("NAN or INF found in x,  %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(y)  ||  isinf(y)) printf("NAN or INF found in y,  %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(z)  ||  isinf(z)) printf("NAN or INF found in z,  %s (particle %lld)\n",_comp->_name,_particle->_uid);
#endif
  #undef zmin
  #undef zmax
  #undef ymin
  #undef ymax
  #undef zwidth
  #undef yheight
  #undef mosaich
  #undef mosaicv
  #undef r0
  #undef Q
  #undef DM
  #undef mos_rms_y
  #undef mos_rms_z
  #undef mos_rms_max
  #undef mono_Q
  return(_comp);
} /* class_Monochromator_flat_trace */

#pragma acc routine
_class_Collimator_linear *class_Collimator_linear_trace(_class_Collimator_linear *_comp
  , _class_particle *_particle) {
  ABSORBED=SCATTERED=RESTORE=0;
  #define xmin (_comp->_parameters.xmin)
  #define xmax (_comp->_parameters.xmax)
  #define ymin (_comp->_parameters.ymin)
  #define ymax (_comp->_parameters.ymax)
  #define xwidth (_comp->_parameters.xwidth)
  #define yheight (_comp->_parameters.yheight)
  #define length (_comp->_parameters.length)
  #define divergence (_comp->_parameters.divergence)
  #define transmission (_comp->_parameters.transmission)
  #define divergenceV (_comp->_parameters.divergenceV)
  #define slope (_comp->_parameters.slope)
  #define slopeV (_comp->_parameters.slopeV)
  SIG_MESSAGE("[_collimator_linear_trace] component collimator_linear=Collimator_linear() TRACE [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//optics/Collimator_linear.comp:70]");

    double phi, dt;

    PROP_Z0;
    if (x<xmin || x>xmax || y<ymin || y>ymax)
      ABSORB;
    dt = length/vz;
    PROP_DT(dt);
    if (x<xmin || x>xmax || y<ymin || y>ymax)
      ABSORB;

    if(slope > 0.0)
    {
      phi = fabs(vx/vz);
      if (phi > slope)
        ABSORB;
      else
        p *= transmission*(1.0 - phi/slope);
      SCATTER;
    }
    if (slopeV > 0) {
      phi = fabs(vy/vz);
      if (phi > slopeV)
        ABSORB;
      else
        p *= transmission*(1.0 - phi/slopeV);
      SCATTER;
    }
#ifndef NOABSORB_INF_NAN
  /* Check for nan or inf particle parms */ 
  if(isnan(p)  ||  isinf(p)) ABSORB;
  if(isnan(t)  ||  isinf(t)) ABSORB;
  if(isnan(vx) || isinf(vx)) ABSORB;
  if(isnan(vy) || isinf(vy)) ABSORB;
  if(isnan(vz) || isinf(vz)) ABSORB;
  if(isnan(x)  ||  isinf(x)) ABSORB;
  if(isnan(y)  ||  isinf(y)) ABSORB;
  if(isnan(z)  ||  isinf(z)) ABSORB;
#else
  if(isnan(p)  ||  isinf(p)) printf("NAN or INF found in p,  %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(t)  ||  isinf(t)) printf("NAN or INF found in t,  %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(vx) || isinf(vx)) printf("NAN or INF found in vx, %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(vy) || isinf(vy)) printf("NAN or INF found in vy, %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(vz) || isinf(vz)) printf("NAN or INF found in vz, %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(x)  ||  isinf(x)) printf("NAN or INF found in x,  %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(y)  ||  isinf(y)) printf("NAN or INF found in y,  %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(z)  ||  isinf(z)) printf("NAN or INF found in z,  %s (particle %lld)\n",_comp->_name,_particle->_uid);
#endif
  #undef xmin
  #undef xmax
  #undef ymin
  #undef ymax
  #undef xwidth
  #undef yheight
  #undef length
  #undef divergence
  #undef transmission
  #undef divergenceV
  #undef slope
  #undef slopeV
  return(_comp);
} /* class_Collimator_linear_trace */

#pragma acc routine
_class_Slit *class_Slit_trace(_class_Slit *_comp
  , _class_particle *_particle) {
  ABSORBED=SCATTERED=RESTORE=0;
  #define xmin (_comp->_parameters.xmin)
  #define xmax (_comp->_parameters.xmax)
  #define ymin (_comp->_parameters.ymin)
  #define ymax (_comp->_parameters.ymax)
  #define radius (_comp->_parameters.radius)
  #define xwidth (_comp->_parameters.xwidth)
  #define yheight (_comp->_parameters.yheight)
  #define isradial (_comp->_parameters.isradial)
  SIG_MESSAGE("[_slit_trace] component slit=Slit() TRACE [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//optics/Slit.comp:98]");

    PROP_Z0;
    if (!isradial ? (x < xmin || x > xmax || y < ymin || y > ymax) : (x * x + y * y > radius * radius))
      ABSORB;
    else
      SCATTER;
#ifndef NOABSORB_INF_NAN
  /* Check for nan or inf particle parms */ 
  if(isnan(p)  ||  isinf(p)) ABSORB;
  if(isnan(t)  ||  isinf(t)) ABSORB;
  if(isnan(vx) || isinf(vx)) ABSORB;
  if(isnan(vy) || isinf(vy)) ABSORB;
  if(isnan(vz) || isinf(vz)) ABSORB;
  if(isnan(x)  ||  isinf(x)) ABSORB;
  if(isnan(y)  ||  isinf(y)) ABSORB;
  if(isnan(z)  ||  isinf(z)) ABSORB;
#else
  if(isnan(p)  ||  isinf(p)) printf("NAN or INF found in p,  %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(t)  ||  isinf(t)) printf("NAN or INF found in t,  %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(vx) || isinf(vx)) printf("NAN or INF found in vx, %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(vy) || isinf(vy)) printf("NAN or INF found in vy, %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(vz) || isinf(vz)) printf("NAN or INF found in vz, %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(x)  ||  isinf(x)) printf("NAN or INF found in x,  %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(y)  ||  isinf(y)) printf("NAN or INF found in y,  %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(z)  ||  isinf(z)) printf("NAN or INF found in z,  %s (particle %lld)\n",_comp->_name,_particle->_uid);
#endif
  #undef xmin
  #undef xmax
  #undef ymin
  #undef ymax
  #undef radius
  #undef xwidth
  #undef yheight
  #undef isradial
  return(_comp);
} /* class_Slit_trace */

#pragma acc routine
_class_E_monitor *class_E_monitor_trace(_class_E_monitor *_comp
  , _class_particle *_particle) {
  ABSORBED=SCATTERED=RESTORE=0;
  #define nE (_comp->_parameters.nE)
  #define filename (_comp->_parameters.filename)
  #define xmin (_comp->_parameters.xmin)
  #define xmax (_comp->_parameters.xmax)
  #define ymin (_comp->_parameters.ymin)
  #define ymax (_comp->_parameters.ymax)
  #define nowritefile (_comp->_parameters.nowritefile)
  #define xwidth (_comp->_parameters.xwidth)
  #define yheight (_comp->_parameters.yheight)
  #define Emin (_comp->_parameters.Emin)
  #define Emax (_comp->_parameters.Emax)
  #define restore_neutron (_comp->_parameters.restore_neutron)
  #define E_N (_comp->_parameters.E_N)
  #define E_p (_comp->_parameters.E_p)
  #define E_p2 (_comp->_parameters.E_p2)
  #define S_p (_comp->_parameters.S_p)
  #define S_pE (_comp->_parameters.S_pE)
  #define S_pE2 (_comp->_parameters.S_pE2)
  SIG_MESSAGE("[_Emon_before_sampel_trace] component Emon_before_sampel=E_monitor() TRACE [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//monitors/E_monitor.comp:99]");

  int i;
  double E;

  PROP_Z0;
  if (x>xmin && x<xmax && y>ymin && y<ymax)
  {
    E = VS2E*(vx*vx + vy*vy + vz*vz);

    S_p += p;
    S_pE += p*E;
    S_pE2 += p*E*E;

    i = floor((E-Emin)*nE/(Emax-Emin));
    if(i >= 0 && i < nE)
    {
      double p2 = p*p;
      #pragma acc atomic
      E_N[i] = E_N[i] +1;
      #pragma acc atomic
      E_p[i] = E_p[i] + p;
      #pragma acc atomic
      E_p2[i] = E_p2[i] + p2;
      SCATTER;
    }
  }
  if (restore_neutron) {
    RESTORE_NEUTRON(INDEX_CURRENT_COMP, x, y, z, vx, vy, vz, t, sx, sy, sz, p);
  }
#ifndef NOABSORB_INF_NAN
  /* Check for nan or inf particle parms */ 
  if(isnan(p)  ||  isinf(p)) ABSORB;
  if(isnan(t)  ||  isinf(t)) ABSORB;
  if(isnan(vx) || isinf(vx)) ABSORB;
  if(isnan(vy) || isinf(vy)) ABSORB;
  if(isnan(vz) || isinf(vz)) ABSORB;
  if(isnan(x)  ||  isinf(x)) ABSORB;
  if(isnan(y)  ||  isinf(y)) ABSORB;
  if(isnan(z)  ||  isinf(z)) ABSORB;
#else
  if(isnan(p)  ||  isinf(p)) printf("NAN or INF found in p,  %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(t)  ||  isinf(t)) printf("NAN or INF found in t,  %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(vx) || isinf(vx)) printf("NAN or INF found in vx, %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(vy) || isinf(vy)) printf("NAN or INF found in vy, %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(vz) || isinf(vz)) printf("NAN or INF found in vz, %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(x)  ||  isinf(x)) printf("NAN or INF found in x,  %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(y)  ||  isinf(y)) printf("NAN or INF found in y,  %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(z)  ||  isinf(z)) printf("NAN or INF found in z,  %s (particle %lld)\n",_comp->_name,_particle->_uid);
#endif
  #undef nE
  #undef filename
  #undef xmin
  #undef xmax
  #undef ymin
  #undef ymax
  #undef nowritefile
  #undef xwidth
  #undef yheight
  #undef Emin
  #undef Emax
  #undef restore_neutron
  #undef E_N
  #undef E_p
  #undef E_p2
  #undef S_p
  #undef S_pE
  #undef S_pE2
  return(_comp);
} /* class_E_monitor_trace */

#pragma acc routine
_class_Single_crystal_process *class_Single_crystal_process_trace(_class_Single_crystal_process *_comp
  , _class_particle *_particle) {
  ABSORBED=SCATTERED=RESTORE=0;
  #define reflections (_comp->_parameters.reflections)
  #define delta_d_d (_comp->_parameters.delta_d_d)
  #define mosaic (_comp->_parameters.mosaic)
  #define mosaic_a (_comp->_parameters.mosaic_a)
  #define mosaic_b (_comp->_parameters.mosaic_b)
  #define mosaic_c (_comp->_parameters.mosaic_c)
  #define mosaic_AB (_comp->_parameters.mosaic_AB)
  #define recip_cell (_comp->_parameters.recip_cell)
  #define barns (_comp->_parameters.barns)
  #define ax (_comp->_parameters.ax)
  #define ay (_comp->_parameters.ay)
  #define az (_comp->_parameters.az)
  #define bx (_comp->_parameters.bx)
  #define by (_comp->_parameters.by)
  #define bz (_comp->_parameters.bz)
  #define cx (_comp->_parameters.cx)
  #define cy (_comp->_parameters.cy)
  #define cz (_comp->_parameters.cz)
  #define aa (_comp->_parameters.aa)
  #define bb (_comp->_parameters.bb)
  #define cc (_comp->_parameters.cc)
  #define order (_comp->_parameters.order)
  #define RX (_comp->_parameters.RX)
  #define RY (_comp->_parameters.RY)
  #define RZ (_comp->_parameters.RZ)
  #define powder (_comp->_parameters.powder)
  #define PG (_comp->_parameters.PG)
  #define interact_fraction (_comp->_parameters.interact_fraction)
  #define packing_factor (_comp->_parameters.packing_factor)
  #define init (_comp->_parameters.init)
  #define Single_crystal_storage (_comp->_parameters.Single_crystal_storage)
  #define hkl_info_union (_comp->_parameters.hkl_info_union)
  #define global_process_element (_comp->_parameters.global_process_element)
  #define This_process (_comp->_parameters.This_process)
  SIG_MESSAGE("[_MnF2_critical_trace] component MnF2_critical=Single_crystal_process() TRACE [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//union/Single_crystal_process.comp:1114]");

    // Trace should be empty, the simulation is done in Union_master
#ifndef NOABSORB_INF_NAN
  /* Check for nan or inf particle parms */ 
  if(isnan(p)  ||  isinf(p)) ABSORB;
  if(isnan(t)  ||  isinf(t)) ABSORB;
  if(isnan(vx) || isinf(vx)) ABSORB;
  if(isnan(vy) || isinf(vy)) ABSORB;
  if(isnan(vz) || isinf(vz)) ABSORB;
  if(isnan(x)  ||  isinf(x)) ABSORB;
  if(isnan(y)  ||  isinf(y)) ABSORB;
  if(isnan(z)  ||  isinf(z)) ABSORB;
#else
  if(isnan(p)  ||  isinf(p)) printf("NAN or INF found in p,  %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(t)  ||  isinf(t)) printf("NAN or INF found in t,  %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(vx) || isinf(vx)) printf("NAN or INF found in vx, %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(vy) || isinf(vy)) printf("NAN or INF found in vy, %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(vz) || isinf(vz)) printf("NAN or INF found in vz, %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(x)  ||  isinf(x)) printf("NAN or INF found in x,  %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(y)  ||  isinf(y)) printf("NAN or INF found in y,  %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(z)  ||  isinf(z)) printf("NAN or INF found in z,  %s (particle %lld)\n",_comp->_name,_particle->_uid);
#endif
  #undef reflections
  #undef delta_d_d
  #undef mosaic
  #undef mosaic_a
  #undef mosaic_b
  #undef mosaic_c
  #undef mosaic_AB
  #undef recip_cell
  #undef barns
  #undef ax
  #undef ay
  #undef az
  #undef bx
  #undef by
  #undef bz
  #undef cx
  #undef cy
  #undef cz
  #undef aa
  #undef bb
  #undef cc
  #undef order
  #undef RX
  #undef RY
  #undef RZ
  #undef powder
  #undef PG
  #undef interact_fraction
  #undef packing_factor
  #undef init
  #undef Single_crystal_storage
  #undef hkl_info_union
  #undef global_process_element
  #undef This_process
  return(_comp);
} /* class_Single_crystal_process_trace */

_class_Union_master *class_Union_master_trace(_class_Union_master *_comp
  , _class_particle *_particle) {
  ABSORBED=SCATTERED=RESTORE=0;
  #define verbal (_comp->_parameters.verbal)
  #define list_verbal (_comp->_parameters.list_verbal)
  #define finally_verbal (_comp->_parameters.finally_verbal)
  #define allow_inside_start (_comp->_parameters.allow_inside_start)
  #define enable_tagging (_comp->_parameters.enable_tagging)
  #define history_limit (_comp->_parameters.history_limit)
  #define enable_conditionals (_comp->_parameters.enable_conditionals)
  #define inherit_number_of_scattering_events (_comp->_parameters.inherit_number_of_scattering_events)
  #define init (_comp->_parameters.init)
  #define global_positions_to_transform_list_master (_comp->_parameters.global_positions_to_transform_list_master)
  #define global_rotations_to_transform_list_master (_comp->_parameters.global_rotations_to_transform_list_master)
  #define global_process_list_master (_comp->_parameters.global_process_list_master)
  #define global_material_list_master (_comp->_parameters.global_material_list_master)
  #define global_geometry_list_master (_comp->_parameters.global_geometry_list_master)
  #define global_all_volume_logger_list_master (_comp->_parameters.global_all_volume_logger_list_master)
  #define global_specific_volumes_logger_list_master (_comp->_parameters.global_specific_volumes_logger_list_master)
  #define global_all_volume_abs_logger_list_master (_comp->_parameters.global_all_volume_abs_logger_list_master)
  #define global_specific_volumes_abs_logger_list_master (_comp->_parameters.global_specific_volumes_abs_logger_list_master)
  #define global_tagging_conditional_list_master (_comp->_parameters.global_tagging_conditional_list_master)
  #define global_master_list_master (_comp->_parameters.global_master_list_master)
  #define starting_volume_warning (_comp->_parameters.starting_volume_warning)
  #define global_master_element (_comp->_parameters.global_master_element)
  #define this_global_master_index (_comp->_parameters.this_global_master_index)
  #define previous_master_index (_comp->_parameters.previous_master_index)
  #define geometry_list_index (_comp->_parameters.geometry_list_index)
  #define intersection_time_table (_comp->_parameters.intersection_time_table)
  #define Volumes (_comp->_parameters.Volumes)
  #define Geometries (_comp->_parameters.Geometries)
  #define Volume_copies (_comp->_parameters.Volume_copies)
  #define starting_lists (_comp->_parameters.starting_lists)
  #define Volume_copies_allocated (_comp->_parameters.Volume_copies_allocated)
  #define r (_comp->_parameters.r)
  #define r_start (_comp->_parameters.r_start)
  #define v (_comp->_parameters.v)
  #define error_msg (_comp->_parameters.error_msg)
  #define component_error_msg (_comp->_parameters.component_error_msg)
  #define string_output (_comp->_parameters.string_output)
  #define number_of_volumes (_comp->_parameters.number_of_volumes)
  #define volume_index (_comp->_parameters.volume_index)
  #define process_index (_comp->_parameters.process_index)
  #define iterator (_comp->_parameters.iterator)
  #define solutions (_comp->_parameters.solutions)
  #define max_number_of_processes (_comp->_parameters.max_number_of_processes)
  #define limit (_comp->_parameters.limit)
  #define solution (_comp->_parameters.solution)
  #define min_solution (_comp->_parameters.min_solution)
  #define min_volume (_comp->_parameters.min_volume)
  #define time_found (_comp->_parameters.time_found)
  #define intersection_time (_comp->_parameters.intersection_time)
  #define min_intersection_time (_comp->_parameters.min_intersection_time)
  #define process (_comp->_parameters.process)
  #define process_start (_comp->_parameters.process_start)
  #define my_trace (_comp->_parameters.my_trace)
  #define p_my_trace (_comp->_parameters.p_my_trace)
  #define my_trace_fraction_control (_comp->_parameters.my_trace_fraction_control)
  #define k (_comp->_parameters.k)
  #define k_new (_comp->_parameters.k_new)
  #define k_old (_comp->_parameters.k_old)
  #define k_rotated (_comp->_parameters.k_rotated)
  #define v_length (_comp->_parameters.v_length)
  #define my_sum (_comp->_parameters.my_sum)
  #define my_sum_plus_abs (_comp->_parameters.my_sum_plus_abs)
  #define culmative_probability (_comp->_parameters.culmative_probability)
  #define mc_prop (_comp->_parameters.mc_prop)
  #define time_to_scattering (_comp->_parameters.time_to_scattering)
  #define length_to_scattering (_comp->_parameters.length_to_scattering)
  #define length_to_boundery (_comp->_parameters.length_to_boundery)
  #define time_to_boundery (_comp->_parameters.time_to_boundery)
  #define selected_process (_comp->_parameters.selected_process)
  #define scattering_event (_comp->_parameters.scattering_event)
  #define time_propagated_without_scattering (_comp->_parameters.time_propagated_without_scattering)
  #define a_next_volume_found (_comp->_parameters.a_next_volume_found)
  #define next_volume (_comp->_parameters.next_volume)
  #define next_volume_priority (_comp->_parameters.next_volume_priority)
  #define done (_comp->_parameters.done)
  #define current_volume (_comp->_parameters.current_volume)
  #define ray_sucseeded (_comp->_parameters.ray_sucseeded)
  #define number_of_solutions (_comp->_parameters.number_of_solutions)
  #define number_of_solutions_static (_comp->_parameters.number_of_solutions_static)
  #define check (_comp->_parameters.check)
  #define start (_comp->_parameters.start)
  #define intersection_with_children (_comp->_parameters.intersection_with_children)
  #define geometry_output (_comp->_parameters.geometry_output)
  #define tree_next_volume (_comp->_parameters.tree_next_volume)
  #define pre_allocated1 (_comp->_parameters.pre_allocated1)
  #define pre_allocated2 (_comp->_parameters.pre_allocated2)
  #define pre_allocated3 (_comp->_parameters.pre_allocated3)
  #define ray_position (_comp->_parameters.ray_position)
  #define ray_velocity (_comp->_parameters.ray_velocity)
  #define ray_velocity_rotated (_comp->_parameters.ray_velocity_rotated)
  #define ray_velocity_final (_comp->_parameters.ray_velocity_final)
  #define wavevector (_comp->_parameters.wavevector)
  #define wavevector_rotated (_comp->_parameters.wavevector_rotated)
  #define volume_0_found (_comp->_parameters.volume_0_found)
  #define scattered_flag (_comp->_parameters.scattered_flag)
  #define scattered_flag_VP (_comp->_parameters.scattered_flag_VP)
  #define master_transposed_rotation_matrix (_comp->_parameters.master_transposed_rotation_matrix)
  #define temp_rotation_matrix (_comp->_parameters.temp_rotation_matrix)
  #define temp_transpose_rotation_matrix (_comp->_parameters.temp_transpose_rotation_matrix)
  #define non_rotated_position (_comp->_parameters.non_rotated_position)
  #define rotated_position (_comp->_parameters.rotated_position)
  #define non_isotropic_found (_comp->_parameters.non_isotropic_found)
  #define master_tagging_node_list (_comp->_parameters.master_tagging_node_list)
  #define current_tagging_node (_comp->_parameters.current_tagging_node)
  #define tagging_leaf_counter (_comp->_parameters.tagging_leaf_counter)
  #define stop_tagging_ray (_comp->_parameters.stop_tagging_ray)
  #define stop_creating_nodes (_comp->_parameters.stop_creating_nodes)
  #define number_of_scattering_events (_comp->_parameters.number_of_scattering_events)
  #define real_transmission_probability (_comp->_parameters.real_transmission_probability)
  #define mc_transmission_probability (_comp->_parameters.mc_transmission_probability)
  #define number_of_process_interacts_set (_comp->_parameters.number_of_process_interacts_set)
  #define index_of_lacking_process (_comp->_parameters.index_of_lacking_process)
  #define total_process_interact (_comp->_parameters.total_process_interact)
  #define geometry_component_index_list (_comp->_parameters.geometry_component_index_list)
  #define mask_volume_index_list (_comp->_parameters.mask_volume_index_list)
  #define number_of_masks (_comp->_parameters.number_of_masks)
  #define number_of_masked_volumes (_comp->_parameters.number_of_masked_volumes)
  #define mask_status_list (_comp->_parameters.mask_status_list)
  #define current_mask_intersect_list_status (_comp->_parameters.current_mask_intersect_list_status)
  #define mask_index_main (_comp->_parameters.mask_index_main)
  #define mask_iterator (_comp->_parameters.mask_iterator)
  #define mask_start (_comp->_parameters.mask_start)
  #define mask_check (_comp->_parameters.mask_check)
  #define need_to_run_within_which_volume (_comp->_parameters.need_to_run_within_which_volume)
  #define number_of_processes_array (_comp->_parameters.number_of_processes_array)
  #define p_old (_comp->_parameters.p_old)
  #define log_index (_comp->_parameters.log_index)
  #define conditional_status (_comp->_parameters.conditional_status)
  #define this_logger (_comp->_parameters.this_logger)
  #define this_abs_logger (_comp->_parameters.this_abs_logger)
  #define tagging_conditional_list (_comp->_parameters.tagging_conditional_list)
  #define logger_conditional_extend_array (_comp->_parameters.logger_conditional_extend_array)
  #define abs_logger_conditional_extend_array (_comp->_parameters.abs_logger_conditional_extend_array)
  #define max_conditional_extend_index (_comp->_parameters.max_conditional_extend_index)
  #define tagging_conditional_extend (_comp->_parameters.tagging_conditional_extend)
  #define free_tagging_conditioanl_list (_comp->_parameters.free_tagging_conditioanl_list)
  #define safty_distance (_comp->_parameters.safty_distance)
  #define safty_distance2 (_comp->_parameters.safty_distance2)
  #define temporary_focus_data (_comp->_parameters.temporary_focus_data)
  #define focus_data_index (_comp->_parameters.focus_data_index)
  #define r_old (_comp->_parameters.r_old)
  #define initial_weight (_comp->_parameters.initial_weight)
  #define abs_weight_factor (_comp->_parameters.abs_weight_factor)
  #define time_old (_comp->_parameters.time_old)
  #define absorption_index (_comp->_parameters.absorption_index)
  #define abs_weight_factor_set (_comp->_parameters.abs_weight_factor_set)
  #define my_abs (_comp->_parameters.my_abs)
  #define absorption_event_data (_comp->_parameters.absorption_event_data)
  #define abs_position (_comp->_parameters.abs_position)
  #define transformed_abs_position (_comp->_parameters.transformed_abs_position)
  #define t_abs_propagation (_comp->_parameters.t_abs_propagation)
  #define abs_distance (_comp->_parameters.abs_distance)
  #define abs_max_length (_comp->_parameters.abs_max_length)
  SIG_MESSAGE("[_Master_trace] component Master=Union_master() TRACE [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//union/Union_master.comp:989]");

  
  #ifdef Union_trace_verbal_setting
    printf("\n\n\n\n\n----------- NEW RAY -------------------------------------------------\n");
    printf("Union_master component name: %s \n \n",NAME_CURRENT_COMP);
  #endif
  
  // Initialize logic
  done = 0;
  error_msg = 0;
  clear_intersection_table(&intersection_time_table);
  
  time_propagated_without_scattering = 0;
  v_length = sqrt(vx*vx+vy*vy+vz*vz);
  
  // Initialize logger system / Statistics
  number_of_scattering_events = 0;
  
  if (inherit_number_of_scattering_events==1) // Continue number of scattering from previous Union_master
    number_of_scattering_events = global_master_list_master->elements[this_global_master_index-1].stored_number_of_scattering_events;
  
  // Zero scattered_flag_VP data
  for (volume_index = 1;volume_index<number_of_volumes;volume_index++) { // No reason to update volume 0, as scattering doesn't happen there
    scattered_flag[volume_index] = 0;
    for (process_index=0;process_index<number_of_processes_array[volume_index];process_index++)
      scattered_flag_VP[volume_index][process_index] = 0;
  }
  
  // If first Union_master in instrument, reset loggers_with_data_array and clean unused data.
  // Unused data happens when logging data is passed to the next Union_master, but the ray is absorbed on the way.
  // Could be improved by using the precompiler instead as ncount times the number of Union_masters could be avoided.
  if (global_master_list_master->elements[0].component_index == INDEX_CURRENT_COMP) {
    // If this is the first Union master, clean up logger data for rays that did not make it through Union components
    for (log_index=loggers_with_data_array.used_elements-1;log_index>-1;log_index--) {
      loggers_with_data_array.logger_pointers[log_index]->function_pointers.clear_temp(&loggers_with_data_array.logger_pointers[log_index]->data_union);
    }
    loggers_with_data_array.used_elements = 0;
    for (log_index=abs_loggers_with_data_array.used_elements-1;log_index>-1;log_index--) {
      abs_loggers_with_data_array.abs_logger_pointers[log_index]->function_pointers.clear_temp(&abs_loggers_with_data_array.abs_logger_pointers[log_index]->data_union);
    }
    abs_loggers_with_data_array.used_elements = 0;
  }
  tagging_conditional_extend = 0;
  for (iterator=0;iterator<max_conditional_extend_index+1;iterator++) {
    logger_conditional_extend_array[iterator] = 0;
  }

  // Need to clean up the double notation for position and velocity. // REVIEW_LINE
  r_start[0] = x; r_start[1] = y; r_start[2] = z;
  r[0]=x;r[1]=y;r[2]=z;v[0]=vx;v[1]=vy;v[2]=vz; // REVIEW_LINE r and v are bad names
  k[0] = V2K*vx; k[1] = V2K*vy; k[2] = V2K*vz;
  
  ray_position = coords_set(x,y,z);
  ray_velocity = coords_set(vx,vy,vz);
  
  // Mask update: need to check the mask status for the initial position
  // mask status for a mask is 1 if the ray position is inside, 0 if it is outside
  for (iterator=0;iterator<number_of_masks;iterator++) {
    // CPU Only
    //if(Volumes[mask_volume_index_list.elements[iterator]]->geometry.within_function(ray_position,&Volumes[mask_volume_index_list.elements[iterator]]->geometry) == 1) {
    // GPU
    if (r_within_function(ray_position, &Volumes[mask_volume_index_list.elements[iterator]]->geometry) == 1) {
      mask_status_list.elements[iterator] = 1;
    } else {
      mask_status_list.elements[iterator] = 0;
    }
  }
  
  #ifdef Union_trace_verbal_setting
    print_1d_int_list(mask_status_list,"Initial mask status list");
  #endif
  
  // Now the initial current_volume can be found, which requires the up to date mask_status_list
  current_volume = within_which_volume_GPU(ray_position, starting_lists.reduced_start_list, starting_lists.starting_destinations_list, Volumes, &mask_status_list, number_of_volumes, pre_allocated1, pre_allocated2, pre_allocated3);
  
  // Using the mask_status_list and the current volume, the current_mask_intersect_list_status can be made
  //  it contains the effective mask status of all volumes on the current volumes mask intersect list, which needs to be calculated,
  //  but only when the current volume or mask status changes, not under for example scattering inside the current volume
  update_current_mask_intersect_status(&current_mask_intersect_list_status, &mask_status_list, Volumes, &current_volume);
  
  #ifdef Union_trace_verbal_setting
    printf("Starting current_volume = %d\n",current_volume);
  #endif
  
  // Check if the ray appeared in an allowed starting volume, unless this check is disabled by the user for advanced cases
  if (allow_inside_start == 0 && starting_lists.allowed_starting_volume_logic_list.elements[current_volume] == 0) {
    printf("ERROR, ray ''teleported'' into Union component %s, if intentional, set allow_inside_start=1\n", NAME_CURRENT_COMP);
    // NEED ERROR FLAG: Need to set an error flag that is read in finally to warn user of problem.
    exit(1);
  }
  // Warn the user that rays have appeared inside a volume instead of outside as expected
  if (starting_volume_warning == 0 && current_volume != 0) {
        printf("WARNING: Ray started in volume ''%s'' rather than the surrounding vacuum in component %s. This warning is only shown once.\n",Volumes[current_volume]->name,NAME_CURRENT_COMP);
        starting_volume_warning = 1;
  }
  
  // Placing the new ray at the start of the tagging tree corresponding to current volume
  // A history limit can be imposed so that no new nodes are created after this limit (may be necessary to fit in memory)
  // Rays can still follow the nodes created before even when no additional nodes are created, but if a situation that
  //  requires a new node is encountered, stop_tagging_ray is set to 1, stopping further tagging and preventing the data
  //  for that ray to be used further.
  if (enable_tagging) {
    current_tagging_node = master_tagging_node_list.elements[current_volume];
    stop_tagging_ray = 0; // Allow this ray to be tracked
    if (tagging_leaf_counter > history_limit) stop_creating_nodes = 1;
  }
  
  #ifdef Union_trace_verbal_setting
    if (enable_tagging) printf("current_tagging_node->intensity = %f\n",current_tagging_node->intensity);
    if (enable_tagging) printf("current_tagging_node->number_of_rays = %d \n",current_tagging_node->number_of_rays);
  #endif
  
  // Propagation loop including scattering
  // This while loop continues until the ray leaves the ensamble of user defined volumes either through volume 0
  //  or a dedicated exit volume. The loop is cancelled after a large number of iterations as a failsafe for errors.
  // A single run of the loop will either be a propagation to the next volume along the path of the ray, or a
  //  scattering event at some point along the path of the ray in the current volume.
  limit = 100000;
  while (done == 0) {
    limit--;
    
    #ifdef Union_trace_verbal_setting
      printf("----------- START OF WHILE LOOP --------------------------------------\n");
      print_intersection_table(&intersection_time_table);
      printf("current_volume = %d \n",current_volume);
    #endif
    
    // Calculating intersections with the necessary volumes. The relevant set of volumes depend on the current volume and the mask status array.
    // First the volumes on the current volumes intersect list is checked, then its mask interset list. Before checking the volume itself, it is
    //  checked if any children of the current volume is intersected, in which case the intersection calculation with the current volume can be
    //  skipped.
    
    // Checking intersections for all volumes in the intersect list.
    for (start=check=Volumes[current_volume]->geometry.intersect_check_list.elements;check-start<Volumes[current_volume]->geometry.intersect_check_list.num_elements;check++) {
    // This will leave check as a pointer to the intergers in the intersect_check_list and iccrement nicely
        #ifdef Union_trace_verbal_setting
          printf("Intersect_list = %d being checked \n",*check);
        #endif
    
        if (intersection_time_table.calculated[*check] == 0) {
             #ifdef Union_trace_verbal_setting
               printf("running intersection for intersect_list with *check = %d \n",*check);
             #endif
            // Calculate intersections using intersect function imbedded in the relevant volume structure using parameters that are also imbedded in the structure.

            // GPU Flexible intersect_function call
            geometry_output = intersect_function(intersection_time_table.intersection_times[*check], number_of_solutions, r_start, v, &Volumes[*check]->geometry);
            
            intersection_time_table.calculated[*check] = 1;
        }
    }
    
    // Mask update: add additional loop for checking intersections with masked volumes depending on mask statuses
    for (mask_iterator=0;mask_iterator<Volumes[current_volume]->geometry.mask_intersect_list.num_elements;mask_iterator++) {
      if (current_mask_intersect_list_status.elements[mask_iterator] == 1) { // Only check if the mask is active
        #ifdef Union_trace_verbal_setting
          printf("Mask Intersect_list = %d being checked \n",Volumes[current_volume]->geometry.mask_intersect_list.elements[mask_iterator]);
        #endif
        if (intersection_time_table.calculated[Volumes[current_volume]->geometry.mask_intersect_list.elements[mask_iterator]] == 0) {
          #ifdef Union_trace_verbal_setting
            printf("running intersection for mask_intersect_list element = %d \n",Volumes[current_volume]->geometry.mask_intersect_list.elements[mask_iterator]);
          //  printf("r = (%f,%f,%f) v = (%f,%f,%f) \n",r[0],r[1],r[2],v[0],v[1],v[2]);
          #endif
          // Calculate intersections using intersect function imbedded in the relevant volume structure using parameters
            //  that are also imbedded in the structure.
          // CPU Only
          //geometry_output = Volumes[Volumes[current_volume]->geometry.mask_intersect_list.elements[mask_iterator]]->geometry.intersect_function(intersection_time_table.intersection_times[Volumes[current_volume]->geometry.mask_intersect_list.elements[mask_iterator]],number_of_solutions,r_start,v,&Volumes[Volumes[current_volume]->geometry.mask_intersect_list.elements[mask_iterator]]->geometry);
          
          // GPU allowed
          int selected_index;
          selected_index = Volumes[current_volume]->geometry.mask_intersect_list.elements[mask_iterator];
          geometry_output = intersect_function(intersection_time_table.intersection_times[selected_index], number_of_solutions, r_start, v, &Volumes[selected_index]->geometry);
          
          intersection_time_table.calculated[Volumes[current_volume]->geometry.mask_intersect_list.elements[mask_iterator]] = 1;
          // if printf("succesfully calculated intersection times for volume *check = %d \n",*check);
        }
      }
    }
    
    // Checking if there are intersections with children of current volume, which means there is an intersection before current_volume, and thus can be skipped. But only if they have not been overwritten. In case current_volume is 0, there is no need to do this regardless.
    if (current_volume != 0 && intersection_time_table.calculated[current_volume] == 0) {
        #ifdef Union_trace_verbal_setting
          printf("Checking if children of current_volume = %d have intersections. \n",current_volume);
        #endif
        intersection_with_children = 0;
        //for (start = check = Volumes[current_volume]->geometry.direct_children.elements;check - start < Volumes[current_volume]->geometry.children.num_elements;check++) { // REVIEW LINE. Caused bug with masks.
        for (start = check = Volumes[current_volume]->geometry.children.elements;check - start < Volumes[current_volume]->geometry.children.num_elements;check++) {
            #ifdef Union_trace_verbal_setting
              printf("Checking if child %d of current_volume = %d have intersections. \n",*check,current_volume);
            #endif
            // Only check the first of the two results in the intersection table, as they are ordered, and the second is of no interest
            if (intersection_time_table.calculated[*check] == 1 && intersection_time_table.intersection_times[*check][0] > time_propagated_without_scattering) {
                // If this child is masked, its mask status need to be 1 in order to be taken into account
                if (Volumes[*check]->geometry.is_masked_volume == 0) {
                  #ifdef Union_trace_verbal_setting
                    printf("Found an child of current_volume with an intersection. Skips calculating for current_volume \n");
                  #endif
                  intersection_with_children = 1;
                  break; // No need to check more, if there is just one it is not necessary to calculate intersection with current_volume yet
                } else {
                  #ifdef Union_trace_verbal_setting
                    printf("Found an child of current_volume with an intersection, but it is masked. Check to see if it can skip calculating for current_volume \n");
                  #endif
                  
                  if (Volumes[*check]->geometry.mask_mode == 2) { // ANY mask mode
                    tree_next_volume = 0;
                    for (mask_start=mask_check=Volumes[*check]->geometry.masked_by_mask_index_list.elements;mask_check-mask_start<Volumes[*check]->geometry.masked_by_mask_index_list.num_elements;mask_check++) {
                       if (mask_status_list.elements[*mask_check] == 1) {
                         intersection_with_children = 1;
                         break;
                       }
                    }
                  } else { // ALL mask mode
                    intersection_with_children = 1;
                    for (mask_start=mask_check=Volumes[*check]->geometry.masked_by_mask_index_list.elements;mask_check-mask_start<Volumes[*check]->geometry.masked_by_mask_index_list.num_elements;mask_check++) {
                      if (mask_status_list.elements[*mask_check] == 0) {
                        intersection_with_children = 0;
                        break;
                      }
                    }
                  }
                  #ifdef Union_trace_verbal_setting
                    printf("The mask status was 1, can actually skip intersection calculation for current volume \n");
                  #endif
                  if (intersection_with_children == 1) break;
                }
            }
        }
        #ifdef Union_trace_verbal_setting
          printf("intersection_with_children = %d \n",intersection_with_children);
        #endif
        if (intersection_with_children == 0) {
            // GPU Allowed
            geometry_output = intersect_function(intersection_time_table.intersection_times[current_volume], number_of_solutions, r_start, v, &Volumes[current_volume]->geometry);
            intersection_time_table.calculated[current_volume] = 1;
        }
    }

    // At this point, intersection_time_table is updated with intersection times of all possible intersections.
    #ifdef Union_trace_verbal_setting
      print_intersection_table(&intersection_time_table);
    #endif
    
    // Next task is to find the next intersection time. The next intersection must be greater than the time_propagated_without_scattering (0 at start of loop)
    // Loops are eqvialent to the 3 intersection calculation loops already completed
    
    // First loop for checking intersect_check_list
    #ifdef Union_trace_verbal_setting
      min_intersection_time=0;
      printf("Incoming value of MIN_intersection_time=%g\n",min_intersection_time);
    #endif
    time_found = 0;
    for (start=check=Volumes[current_volume]->geometry.intersect_check_list.elements;check-start<Volumes[current_volume]->geometry.intersect_check_list.num_elements;check++) {
        for (solution = 0;solution<intersection_time_table.n_elements[*check];solution++) {
            if (time_found) {
                if ((intersection_time = intersection_time_table.intersection_times[*check][solution]) > time_propagated_without_scattering &&  intersection_time < min_intersection_time) {
                    min_intersection_time = intersection_time;min_solution = solution;min_volume = *check;
                   #ifdef Union_trace_verbal_setting
		    printf("found A at %i x %i\n",*check,solution);
		    #endif
                }
            } else {
                if ((intersection_time = intersection_time_table.intersection_times[*check][solution]) > time_propagated_without_scattering) {
                    min_intersection_time = intersection_time;min_solution = solution;min_volume = *check;
                    time_found = 1;
                    #ifdef Union_trace_verbal_setting
		     printf("found B at %i x %i\n",*check,solution);
                    #endif
                }
            }
        }
    }
    #ifdef Union_trace_verbal_setting
    printf("min_intersection_time=%g min_solution=%i\n",min_intersection_time,min_solution);
    #endif

    // Now check the masked_intersect_list, but only the ones that are currently active
    for (mask_iterator=0;mask_iterator<Volumes[current_volume]->geometry.mask_intersect_list.num_elements;mask_iterator++) {
      if (current_mask_intersect_list_status.elements[mask_iterator] == 1) {
        for (solution = 0;solution<intersection_time_table.n_elements[Volumes[current_volume]->geometry.mask_intersect_list.elements[mask_iterator]];solution++) {
            if (time_found) {
                if ((intersection_time = intersection_time_table.intersection_times[Volumes[current_volume]->geometry.mask_intersect_list.elements[mask_iterator]][solution]) > time_propagated_without_scattering &&  intersection_time < min_intersection_time) {
                    min_intersection_time = intersection_time;min_solution = solution;min_volume = Volumes[current_volume]->geometry.mask_intersect_list.elements[mask_iterator];
                }
            } else {
                if ((intersection_time = intersection_time_table.intersection_times[Volumes[current_volume]->geometry.mask_intersect_list.elements[mask_iterator]][solution]) > time_propagated_without_scattering) {
                    min_intersection_time = intersection_time;min_solution = solution;min_volume = Volumes[current_volume]->geometry.mask_intersect_list.elements[mask_iterator];
                    time_found = 1;
                }
            }
        }
      }
    }
    
    // And check the current_volume
    for (solution = 0;solution<intersection_time_table.n_elements[current_volume];solution++) {
        if (time_found) {
            if ((intersection_time = intersection_time_table.intersection_times[current_volume][solution]) > time_propagated_without_scattering && intersection_time < min_intersection_time) {
                min_intersection_time = intersection_time;min_solution = solution;min_volume = current_volume;
            }
        } else {
            if ((intersection_time = intersection_time_table.intersection_times[current_volume][solution]) > time_propagated_without_scattering) {
                min_intersection_time = intersection_time;min_solution = solution;min_volume = current_volume;
                time_found = 1;
            }
        }
    }
    
    #ifdef Union_trace_verbal_setting
      printf("min_intersection_time = %f \n",min_intersection_time);
      printf("min_solution          = %d \n",min_solution);
      printf("min_volume            = %d \n",min_volume);
      printf("time_found            = %d \n",time_found);
    #endif

    abs_weight_factor = 1.0;
    abs_weight_factor_set = 0;

    // If a time is found, propagation continues, and it will be checked if a scattering occurs before the next intersection.
    // If a time is not found, the ray must be leaving the ensamble of volumes and the loop will be concluded
    if (time_found) {
        time_to_boundery = min_intersection_time - time_propagated_without_scattering; // calculate the time remaining before the next intersection
        scattering_event = 0; // Assume a scattering event will not occur
        
        // Check if a scattering event should occur
        if (current_volume != 0) { // Volume 0 is always vacuum, and if this is the current volume, an event will not occur
          if (Volumes[current_volume]->p_physics->number_of_processes == 0) { // If there are no processes, the volume could be vacuum or an absorber
            if (Volumes[current_volume]->p_physics->is_vacuum == 0) {
              // This volume does not have physical processes but does have an absorption cross section, so the ray weight is reduced accordingly
              
              my_sum_plus_abs = Volumes[current_volume]->p_physics->my_a*(2200/v_length);
              length_to_boundery = time_to_boundery * v_length;
              
              abs_weight_factor = exp(-Volumes[current_volume]->p_physics->my_a*2200*time_to_boundery);
              abs_weight_factor_set = 1;
    
              #ifdef Union_trace_verbal_setting
                printf("name of material: %s \n",Volumes[current_volume]->name);
                printf("length to boundery  = %f\n",length_to_boundery);
                printf("absorption cross section  = %f\n",Volumes[current_volume]->p_physics->my_a);
                printf("chance to get through this length of absorber: %f \%\n", 100*exp(-Volumes[current_volume]->p_physics->my_a*length_to_boundery));
              #endif
            }
          } else {
            // Since there is a non-zero number of processes in this material, all the scattering cross section for these are calculated
            my_sum = 0; k[0] = V2K*vx; k[1] = V2K*vy; k[2] = V2K*vz; p_my_trace = my_trace; wavevector = coords_set(k[0],k[1],k[2]);
            //for (process_start = process = Volumes[current_volume]->p_physics->p_scattering_array;process - process_start < Volumes[current_volume]->p_physics->number_of_processes;process++) {
            int p_index;
            for (p_index=0; p_index < Volumes[current_volume]->p_physics->number_of_processes; p_index++ ){ // GPU
            
              if (Volumes[current_volume]->p_physics->p_scattering_array[p_index].non_isotropic_rot_index != -1) {
                // If the process is not isotropic, the wavevector is transformed into the local coordinate system of the process
                wavevector_rotated = rot_apply(Volumes[current_volume]->geometry.process_rot_matrix_array[Volumes[current_volume]->p_physics->p_scattering_array[p_index].non_isotropic_rot_index],wavevector);
                
                coords_get(wavevector_rotated,&k_rotated[0],&k_rotated[1],&k_rotated[2]);

              } else {
                k_rotated[0] = k[0]; k_rotated[1] = k[1]; k_rotated[2] = k[2];
              }
              
              // Find correct focus_data_array index for this volume/process
              focus_data_index = Volumes[current_volume]->geometry.focus_array_indices.elements[p_index];
              
              // Call the probability for scattering function assighed to this specific procress (the process pointer is updated in the for loop)
              process = &Volumes[current_volume]->p_physics->p_scattering_array[p_index]; // GPU Allowed
              
              int physics_output;
              physics_output = physics_my(process->eProcess, p_my_trace, k_rotated, process->data_transfer,&Volumes[current_volume]->geometry.focus_data_array.elements[focus_data_index], _particle);
              
              my_sum += *p_my_trace;
              #ifdef Union_trace_verbal_setting
                printf("my_trace = %f, my_sum = %f\n",*p_my_trace,my_sum);
              #endif
              p_my_trace++; // increment the pointer so that it point to the next element (max number of process in any material is allocated)
            }
            
            #ifdef Union_trace_verbal_setting
              printf("time_propagated_without_scattering = %f.\n",time_propagated_without_scattering);
              printf("v_length                           = %f.\n",v_length);
            #endif
            
            length_to_boundery = time_to_boundery * v_length;
            
            #ifdef Union_trace_verbal_setting
              printf("exp(- length_to_boundery*my_sum) = %f. length_to_boundery = %f. my_sum = %f.\n",exp(-length_to_boundery*my_sum),length_to_boundery,my_sum);
            #endif
            
            // Selecting if a scattering takes place, and what scattering process.
            // This section have too many if statements, and unessecary calculations
            // Could make seperate functions for p_interact on/off and interact_fraction on/off,
            //   and set function pointers to these in initialize, thus avoiding many unessecary if statements and calculations of x/x.
            
            my_sum_plus_abs = my_sum + Volumes[current_volume]->p_physics->my_a*(2200/v_length);
            
            if (my_sum < 1E-18) {
                // The scattering cross section is basicly zero, no scattering should occur.
                scattering_event = 0;
                abs_weight_factor =  exp(-length_to_boundery*my_sum_plus_abs); // Correct for absorption and the almost zero scattering
                abs_weight_factor_set = 1;
            } else if (length_to_boundery < safty_distance2) {
                // Too close to boundery to safly make another scattering, attenuate
                scattering_event = 0;
                abs_weight_factor = exp(-length_to_boundery*my_sum_plus_abs); // Attentuate the beam for the small distance
                
            } else {
                // The scattering cross section is above zero and the distance to the boundery is sufficient for a scattering
                if (Volumes[current_volume]->geometry.geometry_p_interact != 0) {
                    // a fraction of the beam (geometry_p_interact) is forced to scatter
                    real_transmission_probability = exp(-length_to_boundery*my_sum_plus_abs);
                    mc_transmission_probability = (1.0 - Volumes[current_volume]->geometry.geometry_p_interact);
                    if ((scattering_event = (rand01() > mc_transmission_probability))) {
                        // Scattering event happens, this is the correction for the weight
                        p *= (1.0-real_transmission_probability)/(1.0-mc_transmission_probability); // Absorption simulated in weight

                        // Find length to next scattering knowing the ray will scatter.
                        length_to_scattering = safty_distance -log(1.0 - rand0max((1.0 - exp(-my_sum_plus_abs*(length_to_boundery-safty_distance2))))) / my_sum_plus_abs;
                    } else {
                        // Scattering event does not happen, this is the appropriate correction
                        p *= real_transmission_probability/mc_transmission_probability; // Absorption simulated in weight
                        
                    }
                } else {
                    // probability to scatter is the natural value
                    if(my_sum*length_to_boundery < 1e-6) { // Scattering probability very small, linear method is used as exponential is unreliable
                      if (length_to_boundery > safty_distance2) {
                        if (rand01() < exp(-length_to_boundery*my_sum_plus_abs)) {
                          // Scattering happens, use linear description to select scattering position
                          length_to_scattering = safty_distance + rand0max(length_to_boundery - safty_distance2);
                          // Weight factor necessary to correct for using the linear scattering position distribution
                          abs_weight_factor = length_to_boundery*my_sum*exp(-length_to_scattering*my_sum_plus_abs); // Absorption simulated in weight
                          abs_weight_factor_set = 1;
                          
                          scattering_event = 1;
                        } else scattering_event = 0;
                      } else {
                        // The distance is too short to reliably make a scattering event (in comparison to accuraccy of intersect functions)
                        abs_weight_factor = exp(-length_to_boundery*my_sum_plus_abs); // Attentuate the beam for the small distance
                        scattering_event = 0;
                      }
                    } else {
                        // Strong scattering, use exponential description to select scattering position between safetydistance and infinity
                        length_to_scattering = safty_distance -log(1 - rand01() ) / my_sum_plus_abs;
                        // Scattering happens if the scattering position is before the boundery (and safty distance)
                        if (length_to_scattering < length_to_boundery - safty_distance) scattering_event = 1;
                        else scattering_event = 0;
                    }
                }
                
                if (scattering_event == 1) {
                  // Adjust weight for absorption
                  abs_weight_factor *= my_sum/my_sum_plus_abs;
                  abs_weight_factor_set = 1;
                  // Safety feature, alert in case of nonsense my results / negative absorption
                  if (my_sum/my_sum_plus_abs > 1.0) printf("WARNING: Absorption weight factor above 1! Should not happen! \n");
                  // Select process
                  if (Volumes[current_volume]->p_physics->number_of_processes == 1) { // trivial case
                    // Select the only available process, which will always have index 0
                    selected_process = 0;
                  } else {
                    if (Volumes[current_volume]->p_physics->interact_control == 1) {
                      // Interact_fraction is used to influence the choice of process in this material
                      mc_prop = rand01();culmative_probability=0;total_process_interact=1.0;
                  
                      // If any of the processes have probability 0, they are excluded from the selection
                      for (iterator = 0;iterator < Volumes[current_volume]->p_physics->number_of_processes;iterator++) {
                        if (my_trace[iterator] < 1E-18) {
                          // When this happens, the total force probability is corrected and the probability for this particular instance is set to 0
                          total_process_interact -= Volumes[current_volume]->p_physics->p_scattering_array[iterator].process_p_interact;
                          my_trace_fraction_control[iterator] = 0;
                          // In cases where my_trace is not zero, the forced fraction is still used.
                        } else my_trace_fraction_control[iterator] = Volumes[current_volume]->p_physics->p_scattering_array[iterator].process_p_interact;
                      }
                      // Randomly select a process using the weights stored in my_trace_fraction_control divided by total_process_interact
                      for (iterator = 0;iterator < Volumes[current_volume]->p_physics->number_of_processes;iterator++) {
                        culmative_probability += my_trace_fraction_control[iterator]/total_process_interact;
                        if (culmative_probability > mc_prop) {
                          selected_process = iterator;
                          p *= (my_trace[iterator]/my_sum)*(total_process_interact/my_trace_fraction_control[iterator]);
                          break;
                        }
                      }
                    
                    } else {
                      // Select a process based on their relative attenuations factors
                      mc_prop = rand01();culmative_probability=0;
                      for (iterator = 0;iterator < Volumes[current_volume]->p_physics->number_of_processes;iterator++) {
                        culmative_probability += my_trace[iterator]/my_sum;
                        if (culmative_probability > mc_prop) {
                          selected_process = iterator;
                          break;
                        }
                      }
                    }
                  }
                } // end of select process
            }
            
          }
          
        } // Done checking for scttering event and in case of scattering selecting a process
     
        // Record initial weight, absorption weight factor and initial position
        
        initial_weight = p;
        r_old[0] = r[0]; r_old[1] = r[1]; r_old[2] = r[2]; time_old = t;
        // Apply absorption
        p *= abs_weight_factor;
        
        // Create event for absorption loggers
        // Need to use start position and length travelled to sample that trajectory for absorption event. Could do several, here just one.
        
        // min length: 0, max length: length_to_scattering if scattering, else length to boundary
        
        // Avoid logging absorption when the ray is in vacuum.
        if (current_volume != 0 && abs_weight_factor_set == 1) { // Volume 0 is always vacuum, and if this is the current volume, an event will not occur
          if (Volumes[current_volume]->p_physics->is_vacuum == 0) { // No absorption in vacuum
            
            if (scattering_event == 1) {
                // When scattering events occur, place the absoprtion the same place (the total cross section is used to place it)
                abs_distance = length_to_scattering;
            } else {
                // When the ray exits a volume, the absorption position should be exponentially distributed using the total cross section
                my_abs = Volumes[current_volume]->p_physics->my_a*(2200/v_length);
                abs_distance = -log(1.0 - rand0max(1.0 - exp(-my_sum_plus_abs*length_to_boundery)) ) / my_sum_plus_abs;
            }

            t_abs_propagation = abs_distance/v_length;

            abs_position = coords_set(x + t_abs_propagation*vx, y + t_abs_propagation*vy, z + t_abs_propagation*vz);
        
            // This info needs to be loaded into the absorption loggers
        
            // Need to run through relevant absorption loggers here
            #ifdef Union_trace_verbal_setting
            printf("Running abs_logger system for specific volumes \n");
            #endif
        
            // Logging for detector components assosiated with this volume
            for (log_index=0;log_index<Volumes[current_volume]->abs_loggers.num_elements;log_index++) {
              // Make transformation according to the individual position of the abs_logger? This would require position / rotation for all abs_loggers
              transformed_abs_position = coords_sub(abs_position, Volumes[current_volume]->abs_loggers.p_abs_logger[log_index]->position);
              transformed_abs_position = rot_apply(Volumes[current_volume]->abs_loggers.p_abs_logger[log_index]->rotation, transformed_abs_position);
              
                // This function calls a logger function which in turn stores some data among the passed, and possibly performs some basic data analysis
              Volumes[current_volume]->abs_loggers.p_abs_logger[log_index]->function_pointers.active_record_function(&transformed_abs_position, k_new, initial_weight*(1.0-abs_weight_factor), t + t_abs_propagation, scattered_flag[current_volume], number_of_scattering_events, Volumes[current_volume]->abs_loggers.p_abs_logger[log_index], &abs_loggers_with_data_array);
                // If the logging component have a conditional attatched, the collected data will be written to a temporary place
                // At the end of the rays life, it will be checked if the condition is met
                //  if it is met, the temporary data is transfered to permanent, and temp is cleared.
                //  if it is not met, the temporary data is cleared.
            }
        
            #ifdef Union_trace_verbal_setting
              printf("Running abs_logger system for all volumes \n");
            #endif
            for (log_index=0;log_index<global_all_volume_abs_logger_list_master->num_elements;log_index++) {
              // As above, but on a global scale, meaning scattering in all volumes are logged
              
              // Problems with VN, PV, as there is no assosiated volume or process. The functions however need to have the same input to make the logger components general.
              // Could be interesting to have a monitor that just globally measurres the second scattering event in any volume (must be two in the same). Weird but not meaningless.
            
              // Make transformation according to the individual position of the abs_logger? This would require position / rotation for all abs_loggers
              transformed_abs_position = coords_sub(abs_position, global_all_volume_abs_logger_list_master->elements[log_index].abs_logger->position);
              transformed_abs_position = rot_apply(global_all_volume_abs_logger_list_master->elements[log_index].abs_logger->rotation, transformed_abs_position);
              
              // Above version includes scattered_flag_VP, but selected_process may be undefined at this point.
              global_all_volume_abs_logger_list_master->elements[log_index].abs_logger->function_pointers.active_record_function(&transformed_abs_position, k_new, initial_weight*(1.0-abs_weight_factor), t+t_abs_propagation, scattered_flag[current_volume], number_of_scattering_events, global_all_volume_abs_logger_list_master->elements[log_index].abs_logger, &abs_loggers_with_data_array);
            }
          }
        }
        
        if (scattering_event == 1) {
            #ifdef Union_trace_verbal_setting
              printf("SCATTERING EVENT \n");
              printf("current_volume            = %d \n", current_volume);
              printf("r = (%f,%f,%f) v = (%f,%f,%f) \n", r[0], r[1], r[2], v[0], v[1], v[2]);
            #endif
            
            // Calculate the time to scattering
            time_to_scattering = length_to_scattering/v_length;
            
            #ifdef Union_trace_verbal_setting
              printf("time to scattering        = %2.20f \n",time_to_scattering);
              printf("length to boundery = %f, length to scattering = %f \n",length_to_boundery,length_to_scattering);
            #endif
            
            // May be replace by version without gravity
            //PROP_DT(time_to_scattering);
            
            // Reduce the double book keeping done here
            x += time_to_scattering*vx; y += time_to_scattering*vy; z += time_to_scattering*vz; t += time_to_scattering;
            r_start[0] = x; r_start[1] = y; r_start[2] = z;
            r[0] = x; r[1] = y; r[2] = z;
            ray_position = coords_set(x,y,z);
            ray_velocity = coords_set(vx,vy,vz);
            
            // Safe check that should be unecessary. Used to fine tune how close to the edge of a volume a scattering event is allowed to take place (1E-14 m away currently).
            if (r_within_function(ray_position, &Volumes[current_volume]->geometry) == 0) {
              printf("\nERROR, propagated out of current volume instead of to a point within!\n");
              printf("length_to_scattering_specified = %2.20f\n             length propagated = %2.20f\n            length_to_boundery = %2.20f \n   current_position = (%lf,%lf,%lf) \n",length_to_scattering,sqrt(time_to_scattering*time_to_scattering*vx*vx+time_to_scattering*time_to_scattering*vy*vy+time_to_scattering*time_to_scattering*vz*vz),length_to_boundery,x,y,z);
              
              volume_index = within_which_volume_GPU(ray_position,starting_lists.reduced_start_list,starting_lists.starting_destinations_list,Volumes,&mask_status_list,number_of_volumes,pre_allocated1,pre_allocated2,pre_allocated3);
              
              printf("Debug info: Volumes[current_volume]->name = %s, but now inside volume number %d named %s.\n",Volumes[current_volume]->name,volume_index,Volumes[volume_index]->name);
              printf("Ray absorbed \n");
              ABSORB;
            }
            
            // Save information before scattering event needed in logging section
            p_old = p;
            k_old[0] = k[0];k_old[1] = k[1];k_old[2] = k[2];
            
            // Find correct focus_data_array index for this volume/process
            focus_data_index = Volumes[current_volume]->geometry.focus_array_indices.elements[selected_process];
        
            // Rotation to local process coordinate system (for non isotropic processes)
            if (Volumes[current_volume]->p_physics->p_scattering_array[selected_process].non_isotropic_rot_index != -1) {
                ray_velocity_rotated = rot_apply(Volumes[current_volume]->geometry.process_rot_matrix_array[Volumes[current_volume]->p_physics->p_scattering_array[selected_process].non_isotropic_rot_index],ray_velocity);
            } else {
                ray_velocity_rotated = ray_velocity;
            }
	    #ifdef Union_trace_verbal_setting
            printf("Kin: %g %g %g, selected_process: %i %i\n",k[0],k[1],k[2],selected_process,current_volume);
	    coords_print(Volumes[current_volume]->geometry.focus_data_array.elements[0].Aim);
            #endif
            // test_physics_scattering(double *k_final, double *k_initial, union data_transfer_union data_transfer) {
            coords_get(coords_scalar_mult(ray_velocity_rotated,V2K), &k[0], &k[1], &k[2]);
            
            // I may replace a intial and final k with one instance that serves as both input and output
            process = &Volumes[current_volume]->p_physics->p_scattering_array[selected_process]; // CPU Only
            if (0 == physics_scattering(process->eProcess, k_new, k, &p, process->data_transfer, &Volumes[current_volume]->geometry.focus_data_array.elements[0], _particle)) {
              /*
              // PowderN and Single_crystal requires the option of absorbing the neutron, which is weird. If there is a scattering probability, there should be a new direction.
              // It can arise from need to simplify sampling process and end up in cases where weight factor is 0, and the ray should be absorbed in these cases
              printf("ERROR: Union_master: %s.Absorbed ray because scattering function returned 0 (error/absorb)\n",NAME_CURRENT_COMP);
              component_error_msg++;
              if (component_error_msg > 100) {
                printf("To many errors encountered, exiting. \n");
                exit(1);
              }
              */
              ABSORB;
            }
            #ifdef Union_trace_verbal_setting
            printf("Kout: %g %g %g\n", k_new[0],k_new[1],k_new[2]);
            #endif
            // Update velocity using k
            ray_velocity_rotated = coords_set(K2V*k_new[0],K2V*k_new[1],K2V*k_new[2]);
            
            // Transformation back to main coordinate system (maybe one should only do this when multiple scattering in that volume was over, especially if there is only one non isotropic frame)
            if (Volumes[current_volume]->p_physics->p_scattering_array[selected_process].non_isotropic_rot_index != -1) {
                ray_velocity_final = rot_apply(Volumes[current_volume]->geometry.transpose_process_rot_matrix_array[Volumes[current_volume]->p_physics->p_scattering_array[selected_process].non_isotropic_rot_index],ray_velocity_rotated);
            } else {
               ray_velocity_final = ray_velocity_rotated;
            }
            #ifdef Union_trace_verbal_setting
	    printf("Final velocity vector "); coords_print(ray_velocity_final);
	    #endif
            // Write velocity to global variable (temp, only really necessary at final)
            coords_get(ray_velocity_final, &vx, &vy, &vz);
            
            // Write velocity in array format as it is still used by intersect functions (temp, they need to be updated to ray_position / ray_velocity)
            v[0] = vx; v[1] = vy; v[2] = vz;
            v_length = sqrt(vx*vx+vy*vy+vz*vz);
            k_new[0] = V2K*vx; k_new[1] = V2K*vy; k_new[2] = V2K*vz;
            if (verbal) if (v_length < 1) printf("velocity set to less than 1\n");
            
            #ifdef Union_trace_verbal_setting
              printf("Running logger system for specific volumes \n");
            #endif
            // Logging for detector components assosiated with this volume
            for (log_index=0;log_index<Volumes[current_volume]->loggers.num_elements;log_index++) {
              if (Volumes[current_volume]->loggers.p_logger_volume[log_index].p_logger_process[selected_process] != NULL) {
                // Technically the scattering function could edit k, the wavevector before the scattering, even though there would be little point to doing that.
                // Could save a secure copy and pass that instead to be certain that no scattering process accidently tampers with the logging.
                
                // This function calls a logger function which in turn stores some data among the passed, and possibly performs some basic data analysis
                Volumes[current_volume]->loggers.p_logger_volume[log_index].p_logger_process[selected_process]->function_pointers.active_record_function(&ray_position, k_new, k_old, p, p_old, t, scattered_flag[current_volume], scattered_flag_VP[current_volume][selected_process], number_of_scattering_events, Volumes[current_volume]->loggers.p_logger_volume[log_index].p_logger_process[selected_process], &loggers_with_data_array);
                // If the logging component have a conditional attatched, the collected data will be written to a temporary place
                // At the end of the rays life, it will be checked if the condition is met
                //  if it is met, the temporary data is transfered to permanent, and temp is cleared.
                //  if it is not met, the temporary data is cleared.
              }
            }
            
            #ifdef Union_trace_verbal_setting
              printf("Running logger system for all volumes \n");
            #endif
            for (log_index=0;log_index<global_all_volume_logger_list_master->num_elements;log_index++) {
              // As above, but on a global scale, meaning scattering in all volumes are logged
              
              // Problems with VN, PV, as there is no assosiated volume or process. The functions however need to have the same input to make the logger components general.
              // Could be interesting to have a monitor that just globally measurres the second scattering event in any volume (must be two in the same). Weird but not meaningless.
              global_all_volume_logger_list_master->elements[log_index].logger->function_pointers.active_record_function(&ray_position, k_new, k_old, p, p_old, t, scattered_flag[current_volume], scattered_flag_VP[current_volume][selected_process], number_of_scattering_events, global_all_volume_logger_list_master->elements[log_index].logger, &loggers_with_data_array);
            }
            #ifdef Union_trace_verbal_setting
	    printf("Outgoing event: %g %g %g // %g %g %g\n",x,y,z,vx,vy,vz);
            #endif
            SCATTER;
            ++number_of_scattering_events;
            ++scattered_flag[current_volume];
            ++scattered_flag_VP[current_volume][selected_process];
            
            // Clear intersection time lists as the direction of the ray has changed
            clear_intersection_table(&intersection_time_table);
            time_propagated_without_scattering = 0.0;
            #ifdef Union_trace_verbal_setting
              printf("SCATTERED SUCSSESFULLY \n");
              printf("r = (%f,%f,%f) v = (%f,%f,%f) \n",x,y,z,vx,vy,vz);
            
	      if (enable_tagging && stop_tagging_ray == 0) printf("Before new process node: current_tagging_node->intensity = %f\n",current_tagging_node->intensity);
              if (enable_tagging && stop_tagging_ray == 0) printf("Before new process node: current_tagging_node->number_of_rays = %d\n", current_tagging_node->number_of_rays);
            #endif
            
	    if (enable_tagging && stop_tagging_ray == 0)
                current_tagging_node = goto_process_node(current_tagging_node, selected_process,Volumes[current_volume], &stop_tagging_ray,stop_creating_nodes);
                        
            #ifdef Union_trace_verbal_setting
            
              if (enable_tagging && stop_tagging_ray == 0) printf("After new process node: current_tagging_node->intensity = %f\n", current_tagging_node->intensity);
              if (enable_tagging && stop_tagging_ray == 0) printf("After new process node: current_tagging_node->number_of_rays = %d\n", current_tagging_node->number_of_rays);
            #endif
            
        } else {
            #ifdef Union_trace_verbal_setting
              printf("Propagate out of volume %d\n", current_volume);
              printf("r = (%f,%f,%f) v = (%f,%f,%f) \n", x, y, z, vx, vy, vz);
            #endif
            // Propagate neutron to found minimum time
            // PROP_DT(time_to_boundery);
            x += time_to_boundery*vx;
            y += time_to_boundery*vy;
            z += time_to_boundery*vz;
            t += time_to_boundery;
            r[0] = x; r[1] = y; r[2] = z;
            ray_position = coords_set(x,y,z);
            ray_velocity = coords_set(vx,vy,vz);
            
            time_propagated_without_scattering = min_intersection_time;
            SCATTER; // For debugging purposes
            #ifdef Union_trace_verbal_setting
              printf("r = (%f,%f,%f) v = (%f,%f,%f) \n",x,y,z,vx,vy,vz);
            #endif
            // Remove this entry from the intersection_time_table
            intersection_time_table.intersection_times[min_volume][min_solution] = -1;
            
            // Use destination list for corresponding intersection entry n,i) to find next volume
            #ifdef Union_trace_verbal_setting
              printf("PROPAGATION FROM VOLUME %d \n",current_volume);
            #endif
            if (min_volume == current_volume) {
                #ifdef Union_trace_verbal_setting
                  printf("min_volume == current_volume \n");
                #endif
                // List approach to finding the next volume.
                // When the ray intersects the current volume, the next volume must be on the destination list of the current volume
                // However, the reduced_destination_list can be investigated first, and depending on the results, the
                //  direct children of the volumes on the reduced destination list are investigated.
                // In the worst case, all direct children are investigated, which is eqvivalent to the entire destination list.
                // There is however a certain overhead in the logic needed to set up this tree, avoid duplicates of direct children, and so on.
                // This method is only faster than just checking the destination list when there are direct children (nested structures),
                //  but in general the tree method scales better with complexity, and is only slightly slower in simple cases.
                
                if (Volumes[current_volume]->geometry.destinations_list.num_elements == 1)
                    tree_next_volume = Volumes[current_volume]->geometry.destinations_list.elements[0];
                else {
                    ray_position = coords_set(x,y,z);
                    ray_velocity = coords_set(vx,vy,vz);
                    tree_next_volume = within_which_volume_GPU(ray_position,Volumes[current_volume]->geometry.reduced_destinations_list,Volumes[current_volume]->geometry.destinations_list,Volumes,&mask_status_list,number_of_volumes,pre_allocated1,pre_allocated2,pre_allocated3);
                }

                #ifdef Union_trace_verbal_setting
                  if (enable_tagging) printf("tree method moves from %d to %d\n",current_volume,tree_next_volume);
                
                  if (enable_tagging && stop_tagging_ray == 0) printf("Before new tree volume node: current_tagging_node->intensity = %f\n",current_tagging_node->intensity);
                  if (enable_tagging && stop_tagging_ray == 0) printf("Before new tree volume node: current_tagging_node->number_of_rays = %d\n",current_tagging_node->number_of_rays);
                #endif
                
                if (enable_tagging && stop_tagging_ray == 0)
                    current_tagging_node = goto_volume_node(current_tagging_node, current_volume, tree_next_volume, Volumes,&stop_tagging_ray,stop_creating_nodes);
                
                #ifdef Union_trace_verbal_setting
                  if (enable_tagging && stop_tagging_ray == 0) printf("After new tree volume node: current_tagging_node->intensity = %f\n",current_tagging_node->intensity);
                  if (enable_tagging && stop_tagging_ray == 0) printf("After new tree volume node: current_tagging_node->number_of_rays = %d\n",current_tagging_node->number_of_rays);
                #endif
                
                // Set next volume to the solution found in the tree method
                current_volume = tree_next_volume;
                update_current_mask_intersect_status(&current_mask_intersect_list_status, &mask_status_list, Volumes, &current_volume);
                #ifdef Union_trace_verbal_setting
                  print_1d_int_list(current_mask_intersect_list_status,"Updated current_mask_intersect_list_status");
                #endif
                
            } else {
                #ifdef Union_trace_verbal_setting
                  if (enable_tagging && stop_tagging_ray == 0) printf("Before new intersection volume node: current_tagging_node->intensity = %f\n",current_tagging_node->intensity);
                  if (enable_tagging && stop_tagging_ray == 0) printf("Before new intersection volume node: current_tagging_node->number_of_rays = %d\n",current_tagging_node->number_of_rays);
                #endif
                
                // Mask update: If the min_volume is not a mask, things are simple, current_volume = min_volume.
                //               however, if it is a mask, the mask status will switch.
                //               if the mask status becomes one, the masked volumes inside may be the next volume (unless they are children of the mask)
                //               if the mask status becomes zero (and the current volume is masked by min_volume), the destinations list of the mask is searched
                //               if the mask status becomes zero (and the current volume is NOT masked by min volume), the current volume doesn't change
                
                if (Volumes[min_volume]->geometry.is_mask_volume == 0) {
                  #ifdef Union_trace_verbal_setting
                    printf("Min volume is not a mask, next volume = min volume\n");
                  #endif
                  if (enable_tagging && stop_tagging_ray == 0) {
                    current_tagging_node = goto_volume_node(current_tagging_node, current_volume, min_volume, Volumes,&stop_tagging_ray,stop_creating_nodes);
                  }
                  current_volume = min_volume;
                } else {
                  #ifdef Union_trace_verbal_setting
                    printf("Current volume is not a mask, complex decision tree\n");
                  #endif
                  if (mask_status_list.elements[Volumes[min_volume]->geometry.mask_index] == 1) {
                    // We are leaving the mask, change the status
                    #ifdef Union_trace_verbal_setting
                      printf("mask status changed from 1 to 0 as a mask is left\n");
                    #endif
                    mask_status_list.elements[Volumes[min_volume]->geometry.mask_index] = 0;
                    // If the current volume is masked by this mask, run within_which_volume using the masks destination list, otherwise keep the current volume
                    if (on_int_list(Volumes[current_volume]->geometry.masked_by_list,min_volume) == 1) {
                      #ifdef Union_trace_verbal_setting
                        printf("The current volume was masked by this mask, and my need updating\n");
                      #endif
                      // In case of ANY mode, need to see if another mask on the masked_by list of the current volume is active, and if so, nothing happens
                      need_to_run_within_which_volume = 1;
                      if (Volumes[current_volume]->geometry.mask_mode == 2) {
                        for (mask_start=mask_check=Volumes[current_volume]->geometry.masked_by_mask_index_list.elements; mask_check-mask_start<Volumes[current_volume]->geometry.masked_by_mask_index_list.num_elements; mask_check++) {
                          if (mask_status_list.elements[*mask_check] == 1) {
                            // Nothing needs to be done, the effective mask status of the current volume is still 1
                            need_to_run_within_which_volume = 0;
                            break;
                          }
                        }
                      }
                      if (need_to_run_within_which_volume == 1) {
                        #ifdef Union_trace_verbal_setting
                          printf("The current volume was masked by this mask, and does need updating\n");
                        #endif
                        if (Volumes[min_volume]->geometry.destinations_list.num_elements == 1) {
                          #ifdef Union_trace_verbal_setting
                            printf("Only one element in the destination tree of the mask\n");
                          #endif
                          // If there is only one element on the destinations list (quite common) there is no reason to run within_which_volume
                          // Instead the mask status is calculated here
                          if (Volumes[Volumes[min_volume]->geometry.destinations_list.elements[0]]->geometry.is_masked_volume == 1) {
                            #ifdef Union_trace_verbal_setting
                              printf("The one element is however masked, so the mask status need to be calculated\n");
                            #endif
                            // figure out the effective mask status of this volume
                            if (Volumes[Volumes[min_volume]->geometry.destinations_list.elements[0]]->geometry.mask_mode == 2) { // ANY mask mode
                              tree_next_volume = 0;
                              for (mask_start=mask_check=Volumes[Volumes[min_volume]->geometry.destinations_list.elements[0]]->geometry.masked_by_mask_index_list.elements; mask_check-mask_start<Volumes[Volumes[min_volume]->geometry.destinations_list.elements[0]]->geometry.masked_by_mask_index_list.num_elements; mask_check++) {
                                if (mask_status_list.elements[*mask_check] == 1) {
                                  tree_next_volume = Volumes[min_volume]->geometry.destinations_list.elements[0];
                                  break;
                                }
                              }
                            } else { // ALL mask mode
                              tree_next_volume = Volumes[min_volume]->geometry.destinations_list.elements[0];
                              for (mask_start=mask_check=Volumes[Volumes[min_volume]->geometry.destinations_list.elements[0]]->geometry.masked_by_mask_index_list.elements;mask_check-mask_start<Volumes[Volumes[min_volume]->geometry.destinations_list.elements[0]]->geometry.masked_by_mask_index_list.num_elements;mask_check++) {
                                if (mask_status_list.elements[*mask_check] == 0) {
                                  tree_next_volume = 0;
                                  break;
                                }
                              }
                            }
                          } else tree_next_volume = Volumes[min_volume]->geometry.destinations_list.elements[0];
                          #ifdef Union_trace_verbal_setting
                            printf("The method found the next tree volume to be %d\n",tree_next_volume);
                          #endif
                          if (enable_tagging && stop_tagging_ray == 0) current_tagging_node = goto_volume_node(current_tagging_node, current_volume, tree_next_volume, Volumes, &stop_tagging_ray, stop_creating_nodes);
                          current_volume = tree_next_volume;
                        } else {
                          #ifdef Union_trace_verbal_setting
                            printf("Many elements in destinations list, use within_which_volume\n");
                          #endif
                          ray_position = coords_set(x,y,z);
                          ray_velocity = coords_set(vx,vy,vz);
                          tree_next_volume = within_which_volume_GPU(ray_position, Volumes[min_volume]->geometry.reduced_destinations_list, Volumes[min_volume]->geometry.destinations_list, Volumes, &mask_status_list, number_of_volumes, pre_allocated1, pre_allocated2, pre_allocated3);

                          if (enable_tagging && stop_tagging_ray == 0) current_tagging_node = goto_volume_node(current_tagging_node, current_volume, tree_next_volume, Volumes,&stop_tagging_ray,stop_creating_nodes);
                          current_volume = tree_next_volume;
                          #ifdef Union_trace_verbal_setting
                            printf("Set new new volume to %d\n",tree_next_volume);
                          #endif
                        }
                      } else {
                        #ifdef Union_trace_verbal_setting
                          printf("Did not need updating, as another mask was covering the volume\n");
                        #endif
                      }
                    }

                  } else {
                    // Here beccause the mask status of the mask that is intersected was 0, and it is thus switched to 1
                    mask_status_list.elements[Volumes[min_volume]->geometry.mask_index] = 1;
                    // When entering a mask, the new highest priority volume may be one of the masked volumes, if not we keep the current volume
                    ray_position = coords_set(x,y,z);
                    ray_velocity = coords_set(vx,vy,vz);
                    // Bug found on the 2/9 2016, the destinations_list of a mask does not contain the volumes inside it. Could make an additional list for this.
                    // The temporary fix will be to use the mask list for both reduced destinations list and destinations list.
                    tree_next_volume = within_which_volume_GPU(ray_position, Volumes[min_volume]->geometry.mask_list, Volumes[min_volume]->geometry.mask_list, Volumes, &mask_status_list, number_of_volumes, pre_allocated1, pre_allocated2, pre_allocated3);
                    // if within_which_volume returns 0, no result was found (volume 0 can not be masked, so it could not be on the mask list)
                    if (tree_next_volume != 0) {
                      if (Volumes[tree_next_volume]->geometry.priority_value > Volumes[current_volume]->geometry.priority_value) {
                        // In case the current volume has a higher priority, nothing happens, otherwise change current volume
                        if (enable_tagging && stop_tagging_ray == 0) current_tagging_node = goto_volume_node(current_tagging_node, current_volume, tree_next_volume, Volumes, &stop_tagging_ray, stop_creating_nodes);
                        current_volume = tree_next_volume;
                      }
                    }
                  }
                }
                
                // Regardless of the outcome of the above code, either the mask status or current volume have changed, and thus a effective mask update is needed.
                update_current_mask_intersect_status(&current_mask_intersect_list_status, &mask_status_list, Volumes, &current_volume);
                #ifdef Union_trace_verbal_setting
                  print_1d_int_list(mask_status_list,"Updated mask status list");
                  print_1d_int_list(current_mask_intersect_list_status,"Updated current_mask_intersect_list_status");
                  if (enable_tagging) printf("After new intersection volume node: current_tagging_node->intensity = %f\n",current_tagging_node->intensity);
                  if (enable_tagging) printf("After new intersection volume node: current_tagging_node->number_of_rays = %d\n",current_tagging_node->number_of_rays);
                #endif
                
            }
            if (Volumes[current_volume]->geometry.is_exit_volume==1) {
                    done = 1; // Exit volumes allow the ray to escape the component
                    ray_sucseeded = 1; // Allows the ray to
            }
            #ifdef Union_trace_verbal_setting
              printf(" TO VOLUME %d \n",current_volume);
            #endif
        }
        
    } else { // Here because a shortest time is not found
        if (current_volume == 0) {
            done = 1;
            ray_sucseeded = 1;
            
        } else { // Check for errors (debugging phase)
            if (error_msg == 0) {
              component_error_msg++;
              ray_sucseeded = 0;
              done = 1; // stop the loop
              printf("\n----------------------------------------------------------------------------------------------------\n");
              printf("Union_master %s: Somehow reached a situation with no intersection time found, but still inside volume %d instead of 0\n",NAME_CURRENT_COMP,current_volume);
              for (volume_index = 1; volume_index < number_of_volumes; volume_index++) {
                if (r_within_function(ray_position,&Volumes[volume_index]->geometry) == 1)
                    printf("The ray is in volume       %d\n",volume_index);
              }
              
              print_1d_int_list(mask_status_list,"mask status list");
              for (iterator=0;iterator<number_of_volumes;iterator++)
                 printf("%d:%d - ",iterator,scattered_flag[iterator]);
              printf("\n");
              printf("r = (%f,%f,%f) v = (%f,%f,%f) \n",x,y,z,vx,vy,vz);
              
              printf("Trace error number (%d/100) \n",component_error_msg);
            }
            error_msg++;
            
            if (component_error_msg > 100) {
                printf("To many errors encountered, exiting. \n");
                // need ERROR FLAG to be read in finally which can warn the user of problems!
                exit(1);
            }
        }
    }
    
    if (limit == 0) {done = 1; ray_sucseeded = 0; printf("Reached limit on number of interactions, and discarded the neutron, was in volume %d\n", current_volume); ABSORB;}
    #ifdef Union_trace_verbal_setting
      printf("----------- END OF WHILE LOOP --------------------------------------\n");
    #endif
    
  }
  // Could move all add_statistics and similar to this point, but need to filter for failed rays
  if (ray_sucseeded == 1) {
    
    // Ray sucseeded, need to check status of conditionals
    #ifdef Union_trace_verbal_setting
      printf("----------- logger loop --------------------------------------\n");
    #endif
    // Loggers attatched to specific volumes need to be handled with care to avoid looping over all loggers for every ray
    if (enable_conditionals == 1) {
      for (log_index=loggers_with_data_array.used_elements-1; log_index>-1; log_index--) {
        // Check all conditionals attatched to the current logger
        this_logger = loggers_with_data_array.logger_pointers[log_index];
        conditional_status = 1;
        for (iterator=0;iterator<loggers_with_data_array.logger_pointers[log_index]->conditional_list.num_elements;iterator++) {
          // Call this particular conditional. If it fails, report the status and break
          #ifdef Union_trace_verbal_setting
            printf("Checking conditional number %d for logger named %s \n",iterator,loggers_with_data_array.logger_pointers[log_index]->name);
          #endif
          if (0 == this_logger->conditional_list.conditional_functions[iterator](
                         this_logger->conditional_list.p_data_unions[iterator],
                         &ray_position, &ray_velocity, &p, &t, &current_volume,
                         &number_of_scattering_events, scattered_flag,scattered_flag_VP)) {
            conditional_status = 0;
            break;
          }
        }
        if (conditional_status == 1) {
          // If a logger does not have a conditional, it will write directly to perm, and not even add it to the loggers_with_data_array, thus we know the temp_to_perm function needs to be called
          // The input for the temp_to_perm function is a pointer to the logger_data_union for the appropriate logger
          
          if (loggers_with_data_array.logger_pointers[log_index]->function_pointers.select_t_to_p == 1) {
            loggers_with_data_array.logger_pointers[log_index]->function_pointers.temp_to_perm(&loggers_with_data_array.logger_pointers[log_index]->data_union);
          }
          else if (loggers_with_data_array.logger_pointers[log_index]->function_pointers.select_t_to_p == 2) {
            loggers_with_data_array.logger_pointers[log_index]->function_pointers.temp_to_perm_final_p(&loggers_with_data_array.logger_pointers[log_index]->data_union,p);
          }
        
            // The user can set a condtional_extend_index, so that the evaluation of this specific conditional can be taken easily from extend
          if (loggers_with_data_array.logger_pointers[log_index]->logger_extend_index != -1) {
            #ifdef Union_trace_verbal_setting
              printf("Updating logger_conditional_extend_array[%d] to 1 (max length = %d)\n", loggers_with_data_array.logger_pointers[log_index]->logger_extend_index, max_conditional_extend_index);
            #endif
            logger_conditional_extend_array[loggers_with_data_array.logger_pointers[log_index]->logger_extend_index] = 1; // Can be reached from EXTEND
            // Are all reset to 0 for each new ray
            #ifdef Union_trace_verbal_setting
              printf("Updated extend index sucessfully\n");
            #endif
          }
        
          // Need to remove the current element from logger_with_data as it has been cleared and written to disk
          // The remaining elements is passed on to the next Union_master as it may fulfill the conditional after that master
          if (global_master_list_master->elements[global_master_list_master->num_elements-1].component_index != INDEX_CURRENT_COMP) {
            // Move current logger pointer in logger_with_data to end position
            loggers_with_data_array.logger_pointers[log_index] = loggers_with_data_array.logger_pointers[loggers_with_data_array.used_elements-1];
            // Decrease logger_with_data.used_elements with 1
            loggers_with_data_array.used_elements--;
          }
        }
      }
        
      // Perform the same loop with abs_loggers and their conditionals
      for (log_index=abs_loggers_with_data_array.used_elements-1; log_index>-1; log_index--) {
        // Check all conditionals attatched to the current logger
        this_abs_logger = abs_loggers_with_data_array.abs_logger_pointers[log_index];
        conditional_status = 1;
        for (iterator=0;iterator<abs_loggers_with_data_array.abs_logger_pointers[log_index]->conditional_list.num_elements;iterator++) {
          // Call this particular conditional. If it fails, report the status and break
          #ifdef Union_trace_verbal_setting
            printf("Checking conditional number %d for abs logger named %s \n",iterator, abs_loggers_with_data_array.abs_logger_pointers[log_index]->name);
          #endif
          if (0 == this_abs_logger->conditional_list.conditional_functions[iterator](
                         this_abs_logger->conditional_list.p_data_unions[iterator],
                         &ray_position, &ray_velocity, &p, &t, &current_volume,
                         &number_of_scattering_events, scattered_flag, scattered_flag_VP)) {
            conditional_status = 0;
            break;
          }
        }
        if (conditional_status == 1) {
          // If a logger does not have a conditional, it will write directly to perm, and not even add it to the loggers_with_data_array, thus we know the temp_to_perm function needs to be called
          // The input for the temp_to_perm function is a pointer to the logger_data_union for the appropriate logger
          abs_loggers_with_data_array.abs_logger_pointers[log_index]->function_pointers.temp_to_perm(&abs_loggers_with_data_array.abs_logger_pointers[log_index]->data_union);
        
          // The user can set a condtional_extend_index, so that the evaluation of this specific conditional can be taken easily from extend
          if (abs_loggers_with_data_array.abs_logger_pointers[log_index]->abs_logger_extend_index != -1) {
            #ifdef Union_trace_verbal_setting
              printf("Updating logger_conditional_extend_array[%d] to 1 (max length = %d)\n",abs_loggers_with_data_array.abs_logger_pointers[log_index]->abs_logger_extend_index,max_conditional_extend_index);
            #endif
            abs_logger_conditional_extend_array[abs_loggers_with_data_array.abs_logger_pointers[log_index]->abs_logger_extend_index] = 1; // Can be reached from EXTEND
            // Are all reset to 0 for each new ray
            #ifdef Union_trace_verbal_setting
              printf("Updated extend index sucessfully\n");
            #endif
          }
        
          // Need to remove the current element from logger_with_data as it has been cleared and written to disk
          // The remaining elements is passed on to the next Union_master as it may fulfill the conditional after that master
          if (global_master_list_master->elements[global_master_list_master->num_elements-1].component_index != INDEX_CURRENT_COMP) {
            // Move current logger pointer in logger_with_data to end position
            abs_loggers_with_data_array.abs_logger_pointers[log_index] = abs_loggers_with_data_array.abs_logger_pointers[abs_loggers_with_data_array.used_elements-1];
            // Decrease logger_with_data.used_elements with 1
            abs_loggers_with_data_array.used_elements--;
          }
        
        }
      }
    }
        
    if (enable_tagging && stop_tagging_ray == 0) {
      conditional_status = 1;
      for (iterator=0; iterator<tagging_conditional_list->num_elements; iterator++) {
        // Call this particular conditional. If it fails, report the status and break
        // Since a conditional can work for a logger and master_tagging at the same time, it may be evaluated twice
        #ifdef Union_trace_verbal_setting
          printf("Checking tagging conditional number %d\n",iterator);
        #endif
        if (0 == tagging_conditional_list->conditional_functions[iterator](
                         tagging_conditional_list->p_data_unions[iterator],
                         &ray_position, &ray_velocity, &p, &t, &current_volume,
                         &number_of_scattering_events, scattered_flag,scattered_flag_VP)) {
          conditional_status = 0;
          break;
        }
      }
      if (conditional_status == 1) {
        tagging_conditional_extend = 1;
        #ifdef Union_trace_verbal_setting
          printf("Before adding statistics to node: current_tagging_nodbe->intensity = %f\n",current_tagging_node->intensity);
          printf("Before adding statistics to node: current_tagging_node->number_of_rays = %d\n",current_tagging_node->number_of_rays);
        #endif
        
          add_statistics_to_node(current_tagging_node,&ray_position, &ray_velocity, &p, &tagging_leaf_counter);
        
        #ifdef Union_trace_verbal_setting
          printf("After adding statistics to node: current_tagging_node->intensity = %f\n",current_tagging_node->intensity);
          printf("After adding statistics to node: current_tagging_node->number_of_rays = %d\n",current_tagging_node->number_of_rays);
        #endif
      }
    }
        
    // Move the rays a nano meter away from the surface it left, in case activation counter > 1, this will prevent the ray from starting on a volume boundery
    x += vx*1E-9; y += vy*1E-9; z += vz*1E-9; t += 1E-9;
        
  } else {
    ABSORB; // Absorb rays that didn't exit correctly for whatever reason
    // Could error log here
  }
  
  // Stores nubmer of scattering events in global master list so that another master with inherit_number_of_scattering_events can continue
  global_master_list_master->elements[this_global_master_index].stored_number_of_scattering_events = number_of_scattering_events;
  
#ifndef NOABSORB_INF_NAN
  /* Check for nan or inf particle parms */ 
  if(isnan(p)  ||  isinf(p)) ABSORB;
  if(isnan(t)  ||  isinf(t)) ABSORB;
  if(isnan(vx) || isinf(vx)) ABSORB;
  if(isnan(vy) || isinf(vy)) ABSORB;
  if(isnan(vz) || isinf(vz)) ABSORB;
  if(isnan(x)  ||  isinf(x)) ABSORB;
  if(isnan(y)  ||  isinf(y)) ABSORB;
  if(isnan(z)  ||  isinf(z)) ABSORB;
#else
  if(isnan(p)  ||  isinf(p)) printf("NAN or INF found in p,  %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(t)  ||  isinf(t)) printf("NAN or INF found in t,  %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(vx) || isinf(vx)) printf("NAN or INF found in vx, %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(vy) || isinf(vy)) printf("NAN or INF found in vy, %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(vz) || isinf(vz)) printf("NAN or INF found in vz, %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(x)  ||  isinf(x)) printf("NAN or INF found in x,  %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(y)  ||  isinf(y)) printf("NAN or INF found in y,  %s (particle %lld)\n",_comp->_name,_particle->_uid);
  if(isnan(z)  ||  isinf(z)) printf("NAN or INF found in z,  %s (particle %lld)\n",_comp->_name,_particle->_uid);
#endif
  #undef verbal
  #undef list_verbal
  #undef finally_verbal
  #undef allow_inside_start
  #undef enable_tagging
  #undef history_limit
  #undef enable_conditionals
  #undef inherit_number_of_scattering_events
  #undef init
  #undef global_positions_to_transform_list_master
  #undef global_rotations_to_transform_list_master
  #undef global_process_list_master
  #undef global_material_list_master
  #undef global_geometry_list_master
  #undef global_all_volume_logger_list_master
  #undef global_specific_volumes_logger_list_master
  #undef global_all_volume_abs_logger_list_master
  #undef global_specific_volumes_abs_logger_list_master
  #undef global_tagging_conditional_list_master
  #undef global_master_list_master
  #undef starting_volume_warning
  #undef global_master_element
  #undef this_global_master_index
  #undef previous_master_index
  #undef geometry_list_index
  #undef intersection_time_table
  #undef Volumes
  #undef Geometries
  #undef Volume_copies
  #undef starting_lists
  #undef Volume_copies_allocated
  #undef r
  #undef r_start
  #undef v
  #undef error_msg
  #undef component_error_msg
  #undef string_output
  #undef number_of_volumes
  #undef volume_index
  #undef process_index
  #undef iterator
  #undef solutions
  #undef max_number_of_processes
  #undef limit
  #undef solution
  #undef min_solution
  #undef min_volume
  #undef time_found
  #undef intersection_time
  #undef min_intersection_time
  #undef process
  #undef process_start
  #undef my_trace
  #undef p_my_trace
  #undef my_trace_fraction_control
  #undef k
  #undef k_new
  #undef k_old
  #undef k_rotated
  #undef v_length
  #undef my_sum
  #undef my_sum_plus_abs
  #undef culmative_probability
  #undef mc_prop
  #undef time_to_scattering
  #undef length_to_scattering
  #undef length_to_boundery
  #undef time_to_boundery
  #undef selected_process
  #undef scattering_event
  #undef time_propagated_without_scattering
  #undef a_next_volume_found
  #undef next_volume
  #undef next_volume_priority
  #undef done
  #undef current_volume
  #undef ray_sucseeded
  #undef number_of_solutions
  #undef number_of_solutions_static
  #undef check
  #undef start
  #undef intersection_with_children
  #undef geometry_output
  #undef tree_next_volume
  #undef pre_allocated1
  #undef pre_allocated2
  #undef pre_allocated3
  #undef ray_position
  #undef ray_velocity
  #undef ray_velocity_rotated
  #undef ray_velocity_final
  #undef wavevector
  #undef wavevector_rotated
  #undef volume_0_found
  #undef scattered_flag
  #undef scattered_flag_VP
  #undef master_transposed_rotation_matrix
  #undef temp_rotation_matrix
  #undef temp_transpose_rotation_matrix
  #undef non_rotated_position
  #undef rotated_position
  #undef non_isotropic_found
  #undef master_tagging_node_list
  #undef current_tagging_node
  #undef tagging_leaf_counter
  #undef stop_tagging_ray
  #undef stop_creating_nodes
  #undef number_of_scattering_events
  #undef real_transmission_probability
  #undef mc_transmission_probability
  #undef number_of_process_interacts_set
  #undef index_of_lacking_process
  #undef total_process_interact
  #undef geometry_component_index_list
  #undef mask_volume_index_list
  #undef number_of_masks
  #undef number_of_masked_volumes
  #undef mask_status_list
  #undef current_mask_intersect_list_status
  #undef mask_index_main
  #undef mask_iterator
  #undef mask_start
  #undef mask_check
  #undef need_to_run_within_which_volume
  #undef number_of_processes_array
  #undef p_old
  #undef log_index
  #undef conditional_status
  #undef this_logger
  #undef this_abs_logger
  #undef tagging_conditional_list
  #undef logger_conditional_extend_array
  #undef abs_logger_conditional_extend_array
  #undef max_conditional_extend_index
  #undef tagging_conditional_extend
  #undef free_tagging_conditioanl_list
  #undef safty_distance
  #undef safty_distance2
  #undef temporary_focus_data
  #undef focus_data_index
  #undef r_old
  #undef initial_weight
  #undef abs_weight_factor
  #undef time_old
  #undef absorption_index
  #undef abs_weight_factor_set
  #undef my_abs
  #undef absorption_event_data
  #undef abs_position
  #undef transformed_abs_position
  #undef t_abs_propagation
  #undef abs_distance
  #undef abs_max_length
  return(_comp);
} /* class_Union_master_trace */

/* *****************************************************************************
* instrument 'template_body_simple' TRACE
***************************************************************************** */

#ifndef FUNNEL
#pragma acc routine
int raytrace(_class_particle* _particle) { /* single event propagation, called by mccode_main for template_body_simple:TRACE */

  /* init variables and counters for TRACE */
  #undef ABSORB0
  #undef ABSORB
  #define ABSORB0 do { DEBUG_ABSORB(); MAGNET_OFF; ABSORBED++; return(ABSORBED);} while(0)
  #define ABSORB ABSORB0
  DEBUG_ENTER();
  DEBUG_STATE();
  _particle->flag_nocoordschange=0; /* Init */
  _class_particle _particle_save;
  /* the main iteration loop for one incoming event */
  while (!ABSORBED) { /* iterate event until absorbed */
    /* send particle event to component instance, one after the other */
    /* begin component origin=Progress_bar() [1] */
    if (!_particle->flag_nocoordschange) { // flag activated by JUMP to pass coords change
      if (_origin_var._rotation_is_identity) {
        if(!_origin_var._position_relative_is_zero) {
          coords_get(coords_add(coords_set(x,y,z), _origin_var._position_relative),&x, &y, &z);
        }
      } else {
          mccoordschange(_origin_var._position_relative, _origin_var._rotation_relative, _particle);
      }
    }
    if (!ABSORBED && _particle->_index == 1) {
      _particle->flag_nocoordschange=0; /* Reset if we came here from a JUMP */
      _particle_save = *_particle;
      DEBUG_COMP(_origin_var._name);
      DEBUG_STATE();
      class_Progress_bar_trace(&_origin_var, _particle);
      if (_particle->_restore)
        particle_restore(_particle, &_particle_save);
      _particle->_index++;
      if (!ABSORBED) { DEBUG_STATE(); }
    } /* end component origin [1] */
    /* begin component source_maxwell_3=Source_Maxwell_3() [2] */
    if (!_particle->flag_nocoordschange) { // flag activated by JUMP to pass coords change
      if (_source_maxwell_3_var._rotation_is_identity) {
        if(!_source_maxwell_3_var._position_relative_is_zero) {
          coords_get(coords_add(coords_set(x,y,z), _source_maxwell_3_var._position_relative),&x, &y, &z);
        }
      } else {
          mccoordschange(_source_maxwell_3_var._position_relative, _source_maxwell_3_var._rotation_relative, _particle);
      }
    }
    if (!ABSORBED && _particle->_index == 2) {
      _particle->flag_nocoordschange=0; /* Reset if we came here from a JUMP */
      _particle_save = *_particle;
      DEBUG_COMP(_source_maxwell_3_var._name);
      DEBUG_STATE();
      class_Source_Maxwell_3_trace(&_source_maxwell_3_var, _particle);
      if (_particle->_restore)
        particle_restore(_particle, &_particle_save);
      _particle->_index++;
      if (!ABSORBED) { DEBUG_STATE(); }
    } /* end component source_maxwell_3 [2] */
    /* begin component guide=Guide() [3] */
    if (!_particle->flag_nocoordschange) { // flag activated by JUMP to pass coords change
      if (_guide_var._rotation_is_identity) {
        if(!_guide_var._position_relative_is_zero) {
          coords_get(coords_add(coords_set(x,y,z), _guide_var._position_relative),&x, &y, &z);
        }
      } else {
          mccoordschange(_guide_var._position_relative, _guide_var._rotation_relative, _particle);
      }
    }
    if (!ABSORBED && _particle->_index == 3) {
      _particle->flag_nocoordschange=0; /* Reset if we came here from a JUMP */
      _particle_save = *_particle;
      DEBUG_COMP(_guide_var._name);
      DEBUG_STATE();
      class_Guide_trace(&_guide_var, _particle);
      if (_particle->_restore)
        particle_restore(_particle, &_particle_save);
      _particle->_index++;
      if (!ABSORBED) { DEBUG_STATE(); }
    } /* end component guide [3] */
    /* begin component PSD_before_monochromator=PSD_monitor() [4] */
    if (!_particle->flag_nocoordschange) { // flag activated by JUMP to pass coords change
      if (_PSD_before_monochromator_var._rotation_is_identity) {
        if(!_PSD_before_monochromator_var._position_relative_is_zero) {
          coords_get(coords_add(coords_set(x,y,z), _PSD_before_monochromator_var._position_relative),&x, &y, &z);
        }
      } else {
          mccoordschange(_PSD_before_monochromator_var._position_relative, _PSD_before_monochromator_var._rotation_relative, _particle);
      }
    }
    if (!ABSORBED && _particle->_index == 4) {
      _particle->flag_nocoordschange=0; /* Reset if we came here from a JUMP */
      _particle_save = *_particle;
      DEBUG_COMP(_PSD_before_monochromator_var._name);
      DEBUG_STATE();
      class_PSD_monitor_trace(&_PSD_before_monochromator_var, _particle);
      if (_particle->_restore)
        particle_restore(_particle, &_particle_save);
      _particle->_index++;
      if (!ABSORBED) { DEBUG_STATE(); }
    } /* end component PSD_before_monochromator [4] */
    /* begin component a1=Arm() [5] */
    if (!ABSORBED && _particle->_index == 5) {
      _particle->flag_nocoordschange=0; /* Reset if we came here from a JUMP */
      _particle->_index++;
    } /* end component a1 [5] */
    /* begin component monochromator_flat1=Monochromator_flat() [6] */
    if (!_particle->flag_nocoordschange) { // flag activated by JUMP to pass coords change
      if (_monochromator_flat1_var._rotation_is_identity) {
        if(!_monochromator_flat1_var._position_relative_is_zero) {
          coords_get(coords_add(coords_set(x,y,z), _monochromator_flat1_var._position_relative),&x, &y, &z);
        }
      } else {
          mccoordschange(_monochromator_flat1_var._position_relative, _monochromator_flat1_var._rotation_relative, _particle);
      }
    }
    if (!ABSORBED && _particle->_index == 6) {
      _particle->flag_nocoordschange=0; /* Reset if we came here from a JUMP */
      _particle_save = *_particle;
      DEBUG_COMP(_monochromator_flat1_var._name);
      DEBUG_STATE();
      class_Monochromator_flat_trace(&_monochromator_flat1_var, _particle);
      if (_particle->_restore)
        particle_restore(_particle, &_particle_save);
      _particle->_index++;
      if (!ABSORBED) { DEBUG_STATE(); }
    } /* end component monochromator_flat1 [6] */
    /* begin component monochromator_flat2=Monochromator_flat() [7] */
    if (!_particle->flag_nocoordschange) { // flag activated by JUMP to pass coords change
      if (_monochromator_flat2_var._rotation_is_identity) {
        if(!_monochromator_flat2_var._position_relative_is_zero) {
          coords_get(coords_add(coords_set(x,y,z), _monochromator_flat2_var._position_relative),&x, &y, &z);
        }
      } else {
          mccoordschange(_monochromator_flat2_var._position_relative, _monochromator_flat2_var._rotation_relative, _particle);
      }
    }
    if (!ABSORBED && _particle->_index == 7) {
      _particle->flag_nocoordschange=0; /* Reset if we came here from a JUMP */
      _particle_save = *_particle;
      DEBUG_COMP(_monochromator_flat2_var._name);
      DEBUG_STATE();
      class_Monochromator_flat_trace(&_monochromator_flat2_var, _particle);
      if (_particle->_restore)
        particle_restore(_particle, &_particle_save);
      _particle->_index++;
      if (!ABSORBED) { DEBUG_STATE(); }
    } /* end component monochromator_flat2 [7] */
    /* begin component monochromator_flat3=Monochromator_flat() [8] */
    if (!_particle->flag_nocoordschange) { // flag activated by JUMP to pass coords change
      if (_monochromator_flat3_var._rotation_is_identity) {
        if(!_monochromator_flat3_var._position_relative_is_zero) {
          coords_get(coords_add(coords_set(x,y,z), _monochromator_flat3_var._position_relative),&x, &y, &z);
        }
      } else {
          mccoordschange(_monochromator_flat3_var._position_relative, _monochromator_flat3_var._rotation_relative, _particle);
      }
    }
    if (!ABSORBED && _particle->_index == 8) {
      _particle->flag_nocoordschange=0; /* Reset if we came here from a JUMP */
      _particle_save = *_particle;
      DEBUG_COMP(_monochromator_flat3_var._name);
      DEBUG_STATE();
      class_Monochromator_flat_trace(&_monochromator_flat3_var, _particle);
      if (_particle->_restore)
        particle_restore(_particle, &_particle_save);
      _particle->_index++;
      if (!ABSORBED) { DEBUG_STATE(); }
    } /* end component monochromator_flat3 [8] */
    /* begin component monochromator_flat4=Monochromator_flat() [9] */
    if (!_particle->flag_nocoordschange) { // flag activated by JUMP to pass coords change
      if (_monochromator_flat4_var._rotation_is_identity) {
        if(!_monochromator_flat4_var._position_relative_is_zero) {
          coords_get(coords_add(coords_set(x,y,z), _monochromator_flat4_var._position_relative),&x, &y, &z);
        }
      } else {
          mccoordschange(_monochromator_flat4_var._position_relative, _monochromator_flat4_var._rotation_relative, _particle);
      }
    }
    if (!ABSORBED && _particle->_index == 9) {
      _particle->flag_nocoordschange=0; /* Reset if we came here from a JUMP */
      _particle_save = *_particle;
      DEBUG_COMP(_monochromator_flat4_var._name);
      DEBUG_STATE();
      class_Monochromator_flat_trace(&_monochromator_flat4_var, _particle);
      if (_particle->_restore)
        particle_restore(_particle, &_particle_save);
      _particle->_index++;
      if (!ABSORBED) { DEBUG_STATE(); }
    } /* end component monochromator_flat4 [9] */
    /* begin component monochromator_flat5=Monochromator_flat() [10] */
    if (!_particle->flag_nocoordschange) { // flag activated by JUMP to pass coords change
      if (_monochromator_flat5_var._rotation_is_identity) {
        if(!_monochromator_flat5_var._position_relative_is_zero) {
          coords_get(coords_add(coords_set(x,y,z), _monochromator_flat5_var._position_relative),&x, &y, &z);
        }
      } else {
          mccoordschange(_monochromator_flat5_var._position_relative, _monochromator_flat5_var._rotation_relative, _particle);
      }
    }
    if (!ABSORBED && _particle->_index == 10) {
      _particle->flag_nocoordschange=0; /* Reset if we came here from a JUMP */
      _particle_save = *_particle;
      DEBUG_COMP(_monochromator_flat5_var._name);
      DEBUG_STATE();
      class_Monochromator_flat_trace(&_monochromator_flat5_var, _particle);
      if (_particle->_restore)
        particle_restore(_particle, &_particle_save);
      _particle->_index++;
      if (!ABSORBED) { DEBUG_STATE(); }
    } /* end component monochromator_flat5 [10] */
    /* begin component a2=Arm() [11] */
    if (!ABSORBED && _particle->_index == 11) {
      _particle->flag_nocoordschange=0; /* Reset if we came here from a JUMP */
      _particle->_index++;
    } /* end component a2 [11] */
    /* begin component PSD_after_monochromator=PSD_monitor() [12] */
    if (!_particle->flag_nocoordschange) { // flag activated by JUMP to pass coords change
      if (_PSD_after_monochromator_var._rotation_is_identity) {
        if(!_PSD_after_monochromator_var._position_relative_is_zero) {
          coords_get(coords_add(coords_set(x,y,z), _PSD_after_monochromator_var._position_relative),&x, &y, &z);
        }
      } else {
          mccoordschange(_PSD_after_monochromator_var._position_relative, _PSD_after_monochromator_var._rotation_relative, _particle);
      }
    }
    if (!ABSORBED && _particle->_index == 12) {
      _particle->flag_nocoordschange=0; /* Reset if we came here from a JUMP */
      _particle_save = *_particle;
      DEBUG_COMP(_PSD_after_monochromator_var._name);
      DEBUG_STATE();
      class_PSD_monitor_trace(&_PSD_after_monochromator_var, _particle);
      if (_particle->_restore)
        particle_restore(_particle, &_particle_save);
      _particle->_index++;
      if (!ABSORBED) { DEBUG_STATE(); }
    } /* end component PSD_after_monochromator [12] */
    /* begin component collimator_linear=Collimator_linear() [13] */
    if (!_particle->flag_nocoordschange) { // flag activated by JUMP to pass coords change
      if (_collimator_linear_var._rotation_is_identity) {
        if(!_collimator_linear_var._position_relative_is_zero) {
          coords_get(coords_add(coords_set(x,y,z), _collimator_linear_var._position_relative),&x, &y, &z);
        }
      } else {
          mccoordschange(_collimator_linear_var._position_relative, _collimator_linear_var._rotation_relative, _particle);
      }
    }
    if (!ABSORBED && _particle->_index == 13) {
      _particle->flag_nocoordschange=0; /* Reset if we came here from a JUMP */
      _particle_save = *_particle;
      DEBUG_COMP(_collimator_linear_var._name);
      DEBUG_STATE();
      class_Collimator_linear_trace(&_collimator_linear_var, _particle);
      if (_particle->_restore)
        particle_restore(_particle, &_particle_save);
      _particle->_index++;
      if (!ABSORBED) { DEBUG_STATE(); }
    } /* end component collimator_linear [13] */
    /* begin component slit=Slit() [14] */
    if (!_particle->flag_nocoordschange) { // flag activated by JUMP to pass coords change
      if (_slit_var._rotation_is_identity) {
        if(!_slit_var._position_relative_is_zero) {
          coords_get(coords_add(coords_set(x,y,z), _slit_var._position_relative),&x, &y, &z);
        }
      } else {
          mccoordschange(_slit_var._position_relative, _slit_var._rotation_relative, _particle);
      }
    }
    if (!ABSORBED && _particle->_index == 14) {
      _particle->flag_nocoordschange=0; /* Reset if we came here from a JUMP */
      _particle_save = *_particle;
      DEBUG_COMP(_slit_var._name);
      DEBUG_STATE();
      class_Slit_trace(&_slit_var, _particle);
      if (_particle->_restore)
        particle_restore(_particle, &_particle_save);
      _particle->_index++;
      if (!ABSORBED) { DEBUG_STATE(); }
    } /* end component slit [14] */
    /* begin component PSD_before_sampel=PSD_monitor() [15] */
    if (!_particle->flag_nocoordschange) { // flag activated by JUMP to pass coords change
      if (_PSD_before_sampel_var._rotation_is_identity) {
        if(!_PSD_before_sampel_var._position_relative_is_zero) {
          coords_get(coords_add(coords_set(x,y,z), _PSD_before_sampel_var._position_relative),&x, &y, &z);
        }
      } else {
          mccoordschange(_PSD_before_sampel_var._position_relative, _PSD_before_sampel_var._rotation_relative, _particle);
      }
    }
    if (!ABSORBED && _particle->_index == 15) {
      _particle->flag_nocoordschange=0; /* Reset if we came here from a JUMP */
      _particle_save = *_particle;
      DEBUG_COMP(_PSD_before_sampel_var._name);
      DEBUG_STATE();
      class_PSD_monitor_trace(&_PSD_before_sampel_var, _particle);
      if (_particle->_restore)
        particle_restore(_particle, &_particle_save);
      _particle->_index++;
      if (!ABSORBED) { DEBUG_STATE(); }
    } /* end component PSD_before_sampel [15] */
    /* begin component Emon_before_sampel=E_monitor() [16] */
    if (!_particle->flag_nocoordschange) { // flag activated by JUMP to pass coords change
      if (_Emon_before_sampel_var._rotation_is_identity) {
        if(!_Emon_before_sampel_var._position_relative_is_zero) {
          coords_get(coords_add(coords_set(x,y,z), _Emon_before_sampel_var._position_relative),&x, &y, &z);
        }
      } else {
          mccoordschange(_Emon_before_sampel_var._position_relative, _Emon_before_sampel_var._rotation_relative, _particle);
      }
    }
    if (!ABSORBED && _particle->_index == 16) {
      _particle->flag_nocoordschange=0; /* Reset if we came here from a JUMP */
      _particle_save = *_particle;
      DEBUG_COMP(_Emon_before_sampel_var._name);
      DEBUG_STATE();
      class_E_monitor_trace(&_Emon_before_sampel_var, _particle);
      if (_particle->_restore)
        particle_restore(_particle, &_particle_save);
      _particle->_index++;
      if (!ABSORBED) { DEBUG_STATE(); }
    } /* end component Emon_before_sampel [16] */
    /* begin component a3=Arm() [17] */
    if (!ABSORBED && _particle->_index == 17) {
      _particle->flag_nocoordschange=0; /* Reset if we came here from a JUMP */
      _particle->_index++;
    } /* end component a3 [17] */
#ifndef NOSPLIT
    /* start SPLIT at a4 */
    if (!ABSORBED) {
    _class_particle Split_a4_particle=*_particle;
    int Split_a4_counter;
    int SplitS_a4 = 10;
    for (Split_a4_counter = 0; Split_a4_counter< SplitS_a4; Split_a4_counter++) {
      randstate_t randbackup = *_particle->randstate;
      *_particle=Split_a4_particle;
      *_particle->randstate = randbackup;
      p /= SplitS_a4 > 0 ? SplitS_a4 : 1;
#endif
    /* begin component a4=Arm() [18] */
    if (!ABSORBED && _particle->_index == 18) {
      _particle->flag_nocoordschange=0; /* Reset if we came here from a JUMP */
      _particle->_index++;
    } /* end component a4 [18] */
    /* begin component a5=Arm() [19] */
    if (!ABSORBED && _particle->_index == 19) {
      _particle->flag_nocoordschange=0; /* Reset if we came here from a JUMP */
      _particle->_index++;
    } /* end component a5 [19] */
    /* begin component sample_position=Arm() [20] */
    if (!ABSORBED && _particle->_index == 20) {
      _particle->flag_nocoordschange=0; /* Reset if we came here from a JUMP */
      _particle->_index++;
    } /* end component sample_position [20] */
    /* begin component init=Union_init() [21] */
    if (!ABSORBED && _particle->_index == 21) {
      _particle->flag_nocoordschange=0; /* Reset if we came here from a JUMP */
      _particle->_index++;
    } /* end component init [21] */
    /* begin component Vanadium_incoherent_Al=Incoherent_process() [22] */
    if (!ABSORBED && _particle->_index == 22) {
      _particle->flag_nocoordschange=0; /* Reset if we came here from a JUMP */
      _particle->_index++;
    } /* end component Vanadium_incoherent_Al [22] */
    /* begin component Al_Powder=Powder_process() [23] */
    if (!ABSORBED && _particle->_index == 23) {
      _particle->flag_nocoordschange=0; /* Reset if we came here from a JUMP */
      _particle->_index++;
    } /* end component Al_Powder [23] */
    /* begin component Al=Union_make_material() [24] */
    if (!ABSORBED && _particle->_index == 24) {
      _particle->flag_nocoordschange=0; /* Reset if we came here from a JUMP */
      _particle->_index++;
    } /* end component Al [24] */
    /* begin component Vanadium_incoherent_Cu=Incoherent_process() [25] */
    if (!ABSORBED && _particle->_index == 25) {
      _particle->flag_nocoordschange=0; /* Reset if we came here from a JUMP */
      _particle->_index++;
    } /* end component Vanadium_incoherent_Cu [25] */
    /* begin component Vanadium_incoherent_MnF2=Incoherent_process() [26] */
    if (!ABSORBED && _particle->_index == 26) {
      _particle->flag_nocoordschange=0; /* Reset if we came here from a JUMP */
      _particle->_index++;
    } /* end component Vanadium_incoherent_MnF2 [26] */
    /* begin component MnF2_critical=Single_crystal_process() [27] */
    if (!_particle->flag_nocoordschange) { // flag activated by JUMP to pass coords change
      if (_MnF2_critical_var._rotation_is_identity) {
        if(!_MnF2_critical_var._position_relative_is_zero) {
          coords_get(coords_add(coords_set(x,y,z), _MnF2_critical_var._position_relative),&x, &y, &z);
        }
      } else {
          mccoordschange(_MnF2_critical_var._position_relative, _MnF2_critical_var._rotation_relative, _particle);
      }
    }
    if (!ABSORBED && _particle->_index == 27) {
      _particle->flag_nocoordschange=0; /* Reset if we came here from a JUMP */
      _particle_save = *_particle;
      DEBUG_COMP(_MnF2_critical_var._name);
      DEBUG_STATE();
      class_Single_crystal_process_trace(&_MnF2_critical_var, _particle);
      if (_particle->_restore)
        particle_restore(_particle, &_particle_save);
      _particle->_index++;
      if (!ABSORBED) { DEBUG_STATE(); }
    } /* end component MnF2_critical [27] */
    /* begin component MnF2_single_crystal=Single_crystal_process() [28] */
    if (!_particle->flag_nocoordschange) { // flag activated by JUMP to pass coords change
      if (_MnF2_single_crystal_var._rotation_is_identity) {
        if(!_MnF2_single_crystal_var._position_relative_is_zero) {
          coords_get(coords_add(coords_set(x,y,z), _MnF2_single_crystal_var._position_relative),&x, &y, &z);
        }
      } else {
          mccoordschange(_MnF2_single_crystal_var._position_relative, _MnF2_single_crystal_var._rotation_relative, _particle);
      }
    }
    if (!ABSORBED && _particle->_index == 28) {
      _particle->flag_nocoordschange=0; /* Reset if we came here from a JUMP */
      _particle_save = *_particle;
      DEBUG_COMP(_MnF2_single_crystal_var._name);
      DEBUG_STATE();
      class_Single_crystal_process_trace(&_MnF2_single_crystal_var, _particle);
      if (_particle->_restore)
        particle_restore(_particle, &_particle_save);
      _particle->_index++;
      if (!ABSORBED) { DEBUG_STATE(); }
    } /* end component MnF2_single_crystal [28] */
    /* begin component MnF2_single_crystal_magn=Single_crystal_process() [29] */
    if (!_particle->flag_nocoordschange) { // flag activated by JUMP to pass coords change
      if (_MnF2_single_crystal_magn_var._rotation_is_identity) {
        if(!_MnF2_single_crystal_magn_var._position_relative_is_zero) {
          coords_get(coords_add(coords_set(x,y,z), _MnF2_single_crystal_magn_var._position_relative),&x, &y, &z);
        }
      } else {
          mccoordschange(_MnF2_single_crystal_magn_var._position_relative, _MnF2_single_crystal_magn_var._rotation_relative, _particle);
      }
    }
    if (!ABSORBED && _particle->_index == 29) {
      _particle->flag_nocoordschange=0; /* Reset if we came here from a JUMP */
      _particle_save = *_particle;
      DEBUG_COMP(_MnF2_single_crystal_magn_var._name);
      DEBUG_STATE();
      class_Single_crystal_process_trace(&_MnF2_single_crystal_magn_var, _particle);
      if (_particle->_restore)
        particle_restore(_particle, &_particle_save);
      _particle->_index++;
      if (!ABSORBED) { DEBUG_STATE(); }
    } /* end component MnF2_single_crystal_magn [29] */
    /* begin component MnF2=Union_make_material() [30] */
    if (!ABSORBED && _particle->_index == 30) {
      _particle->flag_nocoordschange=0; /* Reset if we came here from a JUMP */
      _particle->_index++;
    } /* end component MnF2 [30] */
    /* begin component union_sample=Union_cylinder() [31] */
    if (!ABSORBED && _particle->_index == 31) {
      _particle->flag_nocoordschange=0; /* Reset if we came here from a JUMP */
      _particle->_index++;
    } /* end component union_sample [31] */
    /* begin component union_stick=Union_cylinder() [32] */
    if (!ABSORBED && _particle->_index == 32) {
      _particle->flag_nocoordschange=0; /* Reset if we came here from a JUMP */
      _particle->_index++;
    } /* end component union_stick [32] */
    /* begin component cryostat_wall=Union_cylinder() [33] */
    if (!ABSORBED && _particle->_index == 33) {
      _particle->flag_nocoordschange=0; /* Reset if we came here from a JUMP */
      _particle->_index++;
    } /* end component cryostat_wall [33] */
    /* begin component cryostat_vacuum=Union_cylinder() [34] */
    if (!ABSORBED && _particle->_index == 34) {
      _particle->flag_nocoordschange=0; /* Reset if we came here from a JUMP */
      _particle->_index++;
    } /* end component cryostat_vacuum [34] */
    /* begin component Master=Union_master() [35] */
    if (!_particle->flag_nocoordschange) { // flag activated by JUMP to pass coords change
      if (_Master_var._rotation_is_identity) {
        if(!_Master_var._position_relative_is_zero) {
          coords_get(coords_add(coords_set(x,y,z), _Master_var._position_relative),&x, &y, &z);
        }
      } else {
          mccoordschange(_Master_var._position_relative, _Master_var._rotation_relative, _particle);
      }
    }
    if (!ABSORBED && _particle->_index == 35) {
      _particle->flag_nocoordschange=0; /* Reset if we came here from a JUMP */
      _particle_save = *_particle;
      DEBUG_COMP(_Master_var._name);
      DEBUG_STATE();
      class_Union_master_trace(&_Master_var, _particle);
      if (_particle->_restore)
        particle_restore(_particle, &_particle_save);
      _particle->_index++;
      if (!ABSORBED) { DEBUG_STATE(); }
    } /* end component Master [35] */
    /* begin component stop=Union_stop() [36] */
    if (!ABSORBED && _particle->_index == 36) {
      _particle->flag_nocoordschange=0; /* Reset if we came here from a JUMP */
      _particle->_index++;
    } /* end component stop [36] */
    /* begin component collimator_linear_2=Collimator_linear() [37] */
    if (!_particle->flag_nocoordschange) { // flag activated by JUMP to pass coords change
      if (_collimator_linear_2_var._rotation_is_identity) {
        if(!_collimator_linear_2_var._position_relative_is_zero) {
          coords_get(coords_add(coords_set(x,y,z), _collimator_linear_2_var._position_relative),&x, &y, &z);
        }
      } else {
          mccoordschange(_collimator_linear_2_var._position_relative, _collimator_linear_2_var._rotation_relative, _particle);
      }
    }
    if (!ABSORBED && _particle->_index == 37) {
      _particle->flag_nocoordschange=0; /* Reset if we came here from a JUMP */
      _particle_save = *_particle;
      DEBUG_COMP(_collimator_linear_2_var._name);
      DEBUG_STATE();
      class_Collimator_linear_trace(&_collimator_linear_2_var, _particle);
      if (_particle->_restore)
        particle_restore(_particle, &_particle_save);
      _particle->_index++;
      if (!ABSORBED) { DEBUG_STATE(); }
    } /* end component collimator_linear_2 [37] */
    /* begin component PSD_after_sampel=PSD_monitor() [38] */
    if (!_particle->flag_nocoordschange) { // flag activated by JUMP to pass coords change
      if (_PSD_after_sampel_var._rotation_is_identity) {
        if(!_PSD_after_sampel_var._position_relative_is_zero) {
          coords_get(coords_add(coords_set(x,y,z), _PSD_after_sampel_var._position_relative),&x, &y, &z);
        }
      } else {
          mccoordschange(_PSD_after_sampel_var._position_relative, _PSD_after_sampel_var._rotation_relative, _particle);
      }
    }
    if (!ABSORBED && _particle->_index == 38) {
      _particle->flag_nocoordschange=0; /* Reset if we came here from a JUMP */
      _particle_save = *_particle;
      DEBUG_COMP(_PSD_after_sampel_var._name);
      DEBUG_STATE();
      class_PSD_monitor_trace(&_PSD_after_sampel_var, _particle);
      if (_particle->_restore)
        particle_restore(_particle, &_particle_save);
      _particle->_index++;
      if (!ABSORBED) { DEBUG_STATE(); }
    } /* end component PSD_after_sampel [38] */
    /* begin component monochromator_flat6=Monochromator_flat() [39] */
    if (!_particle->flag_nocoordschange) { // flag activated by JUMP to pass coords change
      if (_monochromator_flat6_var._rotation_is_identity) {
        if(!_monochromator_flat6_var._position_relative_is_zero) {
          coords_get(coords_add(coords_set(x,y,z), _monochromator_flat6_var._position_relative),&x, &y, &z);
        }
      } else {
          mccoordschange(_monochromator_flat6_var._position_relative, _monochromator_flat6_var._rotation_relative, _particle);
      }
    }
    if (!ABSORBED && _particle->_index == 39) {
      _particle->flag_nocoordschange=0; /* Reset if we came here from a JUMP */
      _particle_save = *_particle;
      DEBUG_COMP(_monochromator_flat6_var._name);
      DEBUG_STATE();
      class_Monochromator_flat_trace(&_monochromator_flat6_var, _particle);
      if (_particle->_restore)
        particle_restore(_particle, &_particle_save);
      _particle->_index++;
      if (!ABSORBED) { DEBUG_STATE(); }
    } /* end component monochromator_flat6 [39] */
    /* begin component a6=Arm() [40] */
    if (!ABSORBED && _particle->_index == 40) {
      _particle->flag_nocoordschange=0; /* Reset if we came here from a JUMP */
      _particle->_index++;
    } /* end component a6 [40] */
    /* begin component Detector=PSD_monitor() [41] */
    if (!_particle->flag_nocoordschange) { // flag activated by JUMP to pass coords change
      if (_Detector_var._rotation_is_identity) {
        if(!_Detector_var._position_relative_is_zero) {
          coords_get(coords_add(coords_set(x,y,z), _Detector_var._position_relative),&x, &y, &z);
        }
      } else {
          mccoordschange(_Detector_var._position_relative, _Detector_var._rotation_relative, _particle);
      }
    }
    if (!ABSORBED && _particle->_index == 41) {
      _particle->flag_nocoordschange=0; /* Reset if we came here from a JUMP */
      _particle_save = *_particle;
      DEBUG_COMP(_Detector_var._name);
      DEBUG_STATE();
      class_PSD_monitor_trace(&_Detector_var, _particle);
      if (_particle->_restore)
        particle_restore(_particle, &_particle_save);
      _particle->_index++;
      if (!ABSORBED) { DEBUG_STATE(); }
    } /* end component Detector [41] */
#ifndef NOSPLIT
    } /* end SPLIT at a4 */
    } /* if (!ABSORBED) relating to SPLIT at a4 */
#endif
    if (_particle->_index > 41)
      ABSORBED++; /* absorbed when passed all components */
  } /* while !ABSORBED */

  DEBUG_LEAVE()
  particle_restore(_particle, &_particle_save);
  DEBUG_STATE()

  return(_particle->_index);
} /* raytrace */

/* loop to generate events and call raytrace() propagate them */
void raytrace_all(unsigned long long ncount, unsigned long seed) {

  /* CPU-loop */
  unsigned long long loops;
  loops = ceil((double)ncount/gpu_innerloop);
  /* if on GPU, printf has been globally nullified, re-enable here */
  #ifdef OPENACC
  #ifndef MULTICORE
  #undef printf
  #endif
  #endif

  #ifdef OPENACC
  if (ncount>gpu_innerloop) {
    printf("Defining %llu CPU loops around GPU kernel and adjusting ncount\n",loops);
    mcset_ncount(loops*gpu_innerloop);
  } else {
    #endif
    loops=1;
    gpu_innerloop = ncount;
    #ifdef OPENACC
  }
    #endif

  for (unsigned long long cloop=0; cloop<loops; cloop++) {
    #ifdef OPENACC
    if (loops>1) fprintf(stdout, "%d..", (int)cloop); fflush(stdout);
    #endif

    /* if on GPU, re-nullify printf */
    #ifdef OPENACC
    #ifndef MULTICORE
    #define printf(...) noprintf()
    #endif
    #endif

    #pragma acc parallel loop num_gangs(numgangs) vector_length(vecsize)
    for (unsigned long pidx=0 ; pidx < gpu_innerloop ; pidx++) {
      _class_particle particleN = mcgenstate(); // initial particle
      _class_particle* _particle = &particleN;
      particleN._uid = pidx;

      srandom(_hash((pidx+1)*(seed+1)));
      particle_uservar_init(_particle);

      raytrace(_particle);
    } /* inner for */
    seed = seed+gpu_innerloop;
    mcncount += gpu_innerloop;
  } /* CPU for */
  /* if on GPU, printf has been globally nullified, re-enable here */
  #ifdef OPENACC
  #ifndef MULTICORE
  #undef printf
  #endif
  #endif
  MPI_MASTER(
  printf("*** TRACE end *** \n");
  );
} /* raytrace_all */

#endif //no-FUNNEL

#ifdef FUNNEL
// Alternative raytrace algorithm which iterates all particles through
// one component at the time, can remove absorbs from the next loop and
// switch between cpu/gpu.
void raytrace_all_funnel(unsigned long long ncount, unsigned long seed) {

  // set up outer (CPU) loop / particle batches
  unsigned long long loops;

  /* if on GPU, printf has been globally nullified, re-enable here */
  #ifdef OPENACC
  #ifndef MULTICORE
  #undef printf
  #endif
  #endif

  #ifdef OPENACC
  loops = ceil((double)ncount/gpu_innerloop);
  if (ncount>gpu_innerloop) {
    printf("Defining %llu CPU loops around kernel and adjusting ncount\n",loops);
    mcset_ncount(loops*gpu_innerloop);
  } else {
  #endif
    loops=1;
    gpu_innerloop = ncount;
  #ifdef OPENACC
  }
  #endif

  // create particles struct and pointer arrays (same memory used by all batches)
  _class_particle* particles = malloc(gpu_innerloop*sizeof(_class_particle));
  _class_particle* pbuffer = malloc(gpu_innerloop*sizeof(_class_particle));
  long livebatchsize = gpu_innerloop;

  #undef ABSORB0
  #undef ABSORB
  #define ABSORB0 do { DEBUG_ABSORB(); MAGNET_OFF; ABSORBED++; } while(0)
  #define ABSORB ABSORB0
  // outer loop / particle batches
  for (unsigned long long cloop=0; cloop<loops; cloop++) {
    if (loops>1) fprintf(stdout, "%d..", (int)cloop); fflush(stdout);

    // init particles
    #pragma acc parallel loop present(particles)
    for (unsigned long pidx=0 ; pidx < livebatchsize ; pidx++) {
      // generate particle state, set loop index and seed
      particles[pidx] = mcgenstate();
      _class_particle* _particle = particles + pidx;
      _particle->_uid = pidx;
      srandom(_hash((pidx+1)*(seed+1))); // _particle->state usage built into srandom macro
      particle_uservar_init(_particle);
    }

    // iterate components

    #pragma acc parallel loop present(particles)
    for (unsigned long pidx=0 ; pidx < livebatchsize ; pidx++) {
      _class_particle* _particle = &particles[pidx];
      _class_particle _particle_save;

      // origin
    if (!ABSORBED && _particle->_index == 1) {
        if (_origin_var._rotation_is_identity)
          coords_get(coords_add(coords_set(x,y,z), _origin_var._position_relative),&x, &y, &z);
        else
          mccoordschange(_origin_var._position_relative, _origin_var._rotation_relative, _particle);
        _particle_save = *_particle;
        class_Progress_bar_trace(&_origin_var, _particle);
        if (_particle->_restore)
        particle_restore(_particle, &_particle_save);
        _particle->_index++;
      }

      // source_maxwell_3
    if (!ABSORBED && _particle->_index == 2) {
        if (_source_maxwell_3_var._rotation_is_identity)
          coords_get(coords_add(coords_set(x,y,z), _source_maxwell_3_var._position_relative),&x, &y, &z);
        else
          mccoordschange(_source_maxwell_3_var._position_relative, _source_maxwell_3_var._rotation_relative, _particle);
        _particle_save = *_particle;
        class_Source_Maxwell_3_trace(&_source_maxwell_3_var, _particle);
        if (_particle->_restore)
        particle_restore(_particle, &_particle_save);
        _particle->_index++;
      }

      // guide
    if (!ABSORBED && _particle->_index == 3) {
        if (_guide_var._rotation_is_identity)
          coords_get(coords_add(coords_set(x,y,z), _guide_var._position_relative),&x, &y, &z);
        else
          mccoordschange(_guide_var._position_relative, _guide_var._rotation_relative, _particle);
        _particle_save = *_particle;
        class_Guide_trace(&_guide_var, _particle);
        if (_particle->_restore)
        particle_restore(_particle, &_particle_save);
        _particle->_index++;
      }

      // PSD_before_monochromator
    if (!ABSORBED && _particle->_index == 4) {
        if (_PSD_before_monochromator_var._rotation_is_identity)
          coords_get(coords_add(coords_set(x,y,z), _PSD_before_monochromator_var._position_relative),&x, &y, &z);
        else
          mccoordschange(_PSD_before_monochromator_var._position_relative, _PSD_before_monochromator_var._rotation_relative, _particle);
        _particle_save = *_particle;
        class_PSD_monitor_trace(&_PSD_before_monochromator_var, _particle);
        if (_particle->_restore)
        particle_restore(_particle, &_particle_save);
        _particle->_index++;
      }

      // a1
    if (!ABSORBED && _particle->_index == 5) {
        _particle->_index++;
      }

      // monochromator_flat1
    if (!ABSORBED && _particle->_index == 6) {
        if (_monochromator_flat1_var._rotation_is_identity)
          coords_get(coords_add(coords_set(x,y,z), _monochromator_flat1_var._position_relative),&x, &y, &z);
        else
          mccoordschange(_monochromator_flat1_var._position_relative, _monochromator_flat1_var._rotation_relative, _particle);
        _particle_save = *_particle;
        class_Monochromator_flat_trace(&_monochromator_flat1_var, _particle);
        if (_particle->_restore)
        particle_restore(_particle, &_particle_save);
        _particle->_index++;
      }

      // monochromator_flat2
    if (!ABSORBED && _particle->_index == 7) {
        if (_monochromator_flat2_var._rotation_is_identity)
          coords_get(coords_add(coords_set(x,y,z), _monochromator_flat2_var._position_relative),&x, &y, &z);
        else
          mccoordschange(_monochromator_flat2_var._position_relative, _monochromator_flat2_var._rotation_relative, _particle);
        _particle_save = *_particle;
        class_Monochromator_flat_trace(&_monochromator_flat2_var, _particle);
        if (_particle->_restore)
        particle_restore(_particle, &_particle_save);
        _particle->_index++;
      }

      // monochromator_flat3
    if (!ABSORBED && _particle->_index == 8) {
        if (_monochromator_flat3_var._rotation_is_identity)
          coords_get(coords_add(coords_set(x,y,z), _monochromator_flat3_var._position_relative),&x, &y, &z);
        else
          mccoordschange(_monochromator_flat3_var._position_relative, _monochromator_flat3_var._rotation_relative, _particle);
        _particle_save = *_particle;
        class_Monochromator_flat_trace(&_monochromator_flat3_var, _particle);
        if (_particle->_restore)
        particle_restore(_particle, &_particle_save);
        _particle->_index++;
      }

      // monochromator_flat4
    if (!ABSORBED && _particle->_index == 9) {
        if (_monochromator_flat4_var._rotation_is_identity)
          coords_get(coords_add(coords_set(x,y,z), _monochromator_flat4_var._position_relative),&x, &y, &z);
        else
          mccoordschange(_monochromator_flat4_var._position_relative, _monochromator_flat4_var._rotation_relative, _particle);
        _particle_save = *_particle;
        class_Monochromator_flat_trace(&_monochromator_flat4_var, _particle);
        if (_particle->_restore)
        particle_restore(_particle, &_particle_save);
        _particle->_index++;
      }

      // monochromator_flat5
    if (!ABSORBED && _particle->_index == 10) {
        if (_monochromator_flat5_var._rotation_is_identity)
          coords_get(coords_add(coords_set(x,y,z), _monochromator_flat5_var._position_relative),&x, &y, &z);
        else
          mccoordschange(_monochromator_flat5_var._position_relative, _monochromator_flat5_var._rotation_relative, _particle);
        _particle_save = *_particle;
        class_Monochromator_flat_trace(&_monochromator_flat5_var, _particle);
        if (_particle->_restore)
        particle_restore(_particle, &_particle_save);
        _particle->_index++;
      }

      // a2
    if (!ABSORBED && _particle->_index == 11) {
        _particle->_index++;
      }

      // PSD_after_monochromator
    if (!ABSORBED && _particle->_index == 12) {
        if (_PSD_after_monochromator_var._rotation_is_identity)
          coords_get(coords_add(coords_set(x,y,z), _PSD_after_monochromator_var._position_relative),&x, &y, &z);
        else
          mccoordschange(_PSD_after_monochromator_var._position_relative, _PSD_after_monochromator_var._rotation_relative, _particle);
        _particle_save = *_particle;
        class_PSD_monitor_trace(&_PSD_after_monochromator_var, _particle);
        if (_particle->_restore)
        particle_restore(_particle, &_particle_save);
        _particle->_index++;
      }

      // collimator_linear
    if (!ABSORBED && _particle->_index == 13) {
        if (_collimator_linear_var._rotation_is_identity)
          coords_get(coords_add(coords_set(x,y,z), _collimator_linear_var._position_relative),&x, &y, &z);
        else
          mccoordschange(_collimator_linear_var._position_relative, _collimator_linear_var._rotation_relative, _particle);
        _particle_save = *_particle;
        class_Collimator_linear_trace(&_collimator_linear_var, _particle);
        if (_particle->_restore)
        particle_restore(_particle, &_particle_save);
        _particle->_index++;
      }

      // slit
    if (!ABSORBED && _particle->_index == 14) {
        if (_slit_var._rotation_is_identity)
          coords_get(coords_add(coords_set(x,y,z), _slit_var._position_relative),&x, &y, &z);
        else
          mccoordschange(_slit_var._position_relative, _slit_var._rotation_relative, _particle);
        _particle_save = *_particle;
        class_Slit_trace(&_slit_var, _particle);
        if (_particle->_restore)
        particle_restore(_particle, &_particle_save);
        _particle->_index++;
      }

      // PSD_before_sampel
    if (!ABSORBED && _particle->_index == 15) {
        if (_PSD_before_sampel_var._rotation_is_identity)
          coords_get(coords_add(coords_set(x,y,z), _PSD_before_sampel_var._position_relative),&x, &y, &z);
        else
          mccoordschange(_PSD_before_sampel_var._position_relative, _PSD_before_sampel_var._rotation_relative, _particle);
        _particle_save = *_particle;
        class_PSD_monitor_trace(&_PSD_before_sampel_var, _particle);
        if (_particle->_restore)
        particle_restore(_particle, &_particle_save);
        _particle->_index++;
      }

      // Emon_before_sampel
    if (!ABSORBED && _particle->_index == 16) {
        if (_Emon_before_sampel_var._rotation_is_identity)
          coords_get(coords_add(coords_set(x,y,z), _Emon_before_sampel_var._position_relative),&x, &y, &z);
        else
          mccoordschange(_Emon_before_sampel_var._position_relative, _Emon_before_sampel_var._rotation_relative, _particle);
        _particle_save = *_particle;
        class_E_monitor_trace(&_Emon_before_sampel_var, _particle);
        if (_particle->_restore)
        particle_restore(_particle, &_particle_save);
        _particle->_index++;
      }

      // a3
    if (!ABSORBED && _particle->_index == 17) {
        _particle->_index++;
      }
    }

    // SPLIT with available livebatchsize 
    long mult_a4;
    livebatchsize = sort_absorb_last(particles, pbuffer, livebatchsize, gpu_innerloop, 1, &mult_a4);
    //printf("livebatchsize: %ld, split: %ld\n",  livebatchsize, mult);

    #pragma acc parallel loop present(particles)
    for (unsigned long pidx=0 ; pidx < livebatchsize ; pidx++) {
      _class_particle* _particle = &particles[pidx];
      _class_particle _particle_save;

      // a4
    if (!ABSORBED && _particle->_index == 18) {
        _particle->_index++;
      }

      // a5
    if (!ABSORBED && _particle->_index == 19) {
        _particle->_index++;
      }

      // sample_position
    if (!ABSORBED && _particle->_index == 20) {
        _particle->_index++;
      }

      // init
    if (!ABSORBED && _particle->_index == 21) {
        _particle->_index++;
      }

      // Vanadium_incoherent_Al
    if (!ABSORBED && _particle->_index == 22) {
        _particle->_index++;
      }

      // Al_Powder
    if (!ABSORBED && _particle->_index == 23) {
        _particle->_index++;
      }

      // Al
    if (!ABSORBED && _particle->_index == 24) {
        _particle->_index++;
      }

      // Vanadium_incoherent_Cu
    if (!ABSORBED && _particle->_index == 25) {
        _particle->_index++;
      }

      // Vanadium_incoherent_MnF2
    if (!ABSORBED && _particle->_index == 26) {
        _particle->_index++;
      }

      // MnF2_critical
    if (!ABSORBED && _particle->_index == 27) {
        if (_MnF2_critical_var._rotation_is_identity)
          coords_get(coords_add(coords_set(x,y,z), _MnF2_critical_var._position_relative),&x, &y, &z);
        else
          mccoordschange(_MnF2_critical_var._position_relative, _MnF2_critical_var._rotation_relative, _particle);
        _particle_save = *_particle;
        class_Single_crystal_process_trace(&_MnF2_critical_var, _particle);
        if (_particle->_restore)
        particle_restore(_particle, &_particle_save);
        _particle->_index++;
      }

      // MnF2_single_crystal
    if (!ABSORBED && _particle->_index == 28) {
        if (_MnF2_single_crystal_var._rotation_is_identity)
          coords_get(coords_add(coords_set(x,y,z), _MnF2_single_crystal_var._position_relative),&x, &y, &z);
        else
          mccoordschange(_MnF2_single_crystal_var._position_relative, _MnF2_single_crystal_var._rotation_relative, _particle);
        _particle_save = *_particle;
        class_Single_crystal_process_trace(&_MnF2_single_crystal_var, _particle);
        if (_particle->_restore)
        particle_restore(_particle, &_particle_save);
        _particle->_index++;
      }

      // MnF2_single_crystal_magn
    if (!ABSORBED && _particle->_index == 29) {
        if (_MnF2_single_crystal_magn_var._rotation_is_identity)
          coords_get(coords_add(coords_set(x,y,z), _MnF2_single_crystal_magn_var._position_relative),&x, &y, &z);
        else
          mccoordschange(_MnF2_single_crystal_magn_var._position_relative, _MnF2_single_crystal_magn_var._rotation_relative, _particle);
        _particle_save = *_particle;
        class_Single_crystal_process_trace(&_MnF2_single_crystal_magn_var, _particle);
        if (_particle->_restore)
        particle_restore(_particle, &_particle_save);
        _particle->_index++;
      }

      // MnF2
    if (!ABSORBED && _particle->_index == 30) {
        _particle->_index++;
      }

      // union_sample
    if (!ABSORBED && _particle->_index == 31) {
        _particle->_index++;
      }

      // union_stick
    if (!ABSORBED && _particle->_index == 32) {
        _particle->_index++;
      }

      // cryostat_wall
    if (!ABSORBED && _particle->_index == 33) {
        _particle->_index++;
      }

      // cryostat_vacuum
    if (!ABSORBED && _particle->_index == 34) {
        _particle->_index++;
      }
        #define JUMP_FUNNEL
    }

    for (unsigned long pidx=0 ; pidx < livebatchsize ; pidx++) {
      _class_particle* _particle = &particles[pidx];
      _class_particle _particle_save;

      // Master
    if (!ABSORBED && _particle->_index == 35) {
        if (_Master_var._rotation_is_identity)
          coords_get(coords_add(coords_set(x,y,z), _Master_var._position_relative),&x, &y, &z);
        else
          mccoordschange(_Master_var._position_relative, _Master_var._rotation_relative, _particle);
        _particle_save = *_particle;
        class_Union_master_trace(&_Master_var, _particle);
        if (_particle->_restore)
        particle_restore(_particle, &_particle_save);
        _particle->_index++;
      }
    }

    #pragma acc parallel loop present(particles)
    for (unsigned long pidx=0 ; pidx < livebatchsize ; pidx++) {
      _class_particle* _particle = &particles[pidx];
      _class_particle _particle_save;

      // stop
    if (!ABSORBED && _particle->_index == 36) {
        _particle->_index++;
      }

      // collimator_linear_2
    if (!ABSORBED && _particle->_index == 37) {
        if (_collimator_linear_2_var._rotation_is_identity)
          coords_get(coords_add(coords_set(x,y,z), _collimator_linear_2_var._position_relative),&x, &y, &z);
        else
          mccoordschange(_collimator_linear_2_var._position_relative, _collimator_linear_2_var._rotation_relative, _particle);
        _particle_save = *_particle;
        class_Collimator_linear_trace(&_collimator_linear_2_var, _particle);
        if (_particle->_restore)
        particle_restore(_particle, &_particle_save);
        _particle->_index++;
      }

      // PSD_after_sampel
    if (!ABSORBED && _particle->_index == 38) {
        if (_PSD_after_sampel_var._rotation_is_identity)
          coords_get(coords_add(coords_set(x,y,z), _PSD_after_sampel_var._position_relative),&x, &y, &z);
        else
          mccoordschange(_PSD_after_sampel_var._position_relative, _PSD_after_sampel_var._rotation_relative, _particle);
        _particle_save = *_particle;
        class_PSD_monitor_trace(&_PSD_after_sampel_var, _particle);
        if (_particle->_restore)
        particle_restore(_particle, &_particle_save);
        _particle->_index++;
      }

      // monochromator_flat6
    if (!ABSORBED && _particle->_index == 39) {
        if (_monochromator_flat6_var._rotation_is_identity)
          coords_get(coords_add(coords_set(x,y,z), _monochromator_flat6_var._position_relative),&x, &y, &z);
        else
          mccoordschange(_monochromator_flat6_var._position_relative, _monochromator_flat6_var._rotation_relative, _particle);
        _particle_save = *_particle;
        class_Monochromator_flat_trace(&_monochromator_flat6_var, _particle);
        if (_particle->_restore)
        particle_restore(_particle, &_particle_save);
        _particle->_index++;
      }

      // a6
    if (!ABSORBED && _particle->_index == 40) {
        _particle->_index++;
      }

      // Detector
    if (!ABSORBED && _particle->_index == 41) {
        if (_Detector_var._rotation_is_identity)
          coords_get(coords_add(coords_set(x,y,z), _Detector_var._position_relative),&x, &y, &z);
        else
          mccoordschange(_Detector_var._position_relative, _Detector_var._rotation_relative, _particle);
        _particle_save = *_particle;
        class_PSD_monitor_trace(&_Detector_var, _particle);
        if (_particle->_restore)
        particle_restore(_particle, &_particle_save);
        _particle->_index++;
      }

    }

    // jump to next viable seed
    seed = seed + gpu_innerloop;
  } // outer loop / particle batches

  free(particles);
  free(pbuffer);

  printf("\n");
} /* raytrace_all_funnel */
#endif // FUNNEL

#undef x
#undef y
#undef z
#undef vx
#undef vy
#undef vz
#undef t
#undef sx
#undef sy
#undef sz
#undef p
#undef mcgravitation
#undef mcMagnet
#undef allow_backprop
#ifdef OPENACC
#ifndef MULTICORE
#undef strlen
#undef strcmp
#undef exit
#undef printf
#undef sprintf
#undef fprintf
#endif
#endif
#undef SCATTERED
#undef RESTORE
#undef RESTORE_NEUTRON
#undef STORE_NEUTRON
#undef ABSORBED
#undef ABSORB
#undef ABSORB0
/* *****************************************************************************
* instrument 'template_body_simple' and components SAVE
***************************************************************************** */

_class_Progress_bar *class_Progress_bar_save(_class_Progress_bar *_comp
) {
  #define profile (_comp->_parameters.profile)
  #define percent (_comp->_parameters.percent)
  #define flag_save (_comp->_parameters.flag_save)
  #define minutes (_comp->_parameters.minutes)
  #define IntermediateCnts (_comp->_parameters.IntermediateCnts)
  #define StartTime (_comp->_parameters.StartTime)
  #define EndTime (_comp->_parameters.EndTime)
  #define CurrentTime (_comp->_parameters.CurrentTime)
  SIG_MESSAGE("[_origin_save] component origin=Progress_bar() SAVE [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//misc/Progress_bar.comp:120]");

  MPI_MASTER(fprintf(stdout, "\nSave [%s]\n", instrument_name););
  if (profile && strlen(profile) && strcmp(profile,"NULL") && strcmp(profile,"0")) {
    char filename[256];
    if (!strlen(profile) || !strcmp(profile,"NULL") || !strcmp(profile,"0")) strcpy(filename, instrument_name);
    else strcpy(filename, profile);
    DETECTOR_OUT_1D(
        "Intensity profiler",
        "Component index [1]",
        "Intensity",
        "prof", 1, mcNUMCOMP, mcNUMCOMP-1,
        &(instrument->counter_N[1]),&(instrument->counter_P[1]),&(instrument->counter_P2[1]),
        filename);

  }
  #undef profile
  #undef percent
  #undef flag_save
  #undef minutes
  #undef IntermediateCnts
  #undef StartTime
  #undef EndTime
  #undef CurrentTime
  return(_comp);
} /* class_Progress_bar_save */

_class_PSD_monitor *class_PSD_monitor_save(_class_PSD_monitor *_comp
) {
  #define nx (_comp->_parameters.nx)
  #define ny (_comp->_parameters.ny)
  #define filename (_comp->_parameters.filename)
  #define xmin (_comp->_parameters.xmin)
  #define xmax (_comp->_parameters.xmax)
  #define ymin (_comp->_parameters.ymin)
  #define ymax (_comp->_parameters.ymax)
  #define xwidth (_comp->_parameters.xwidth)
  #define yheight (_comp->_parameters.yheight)
  #define restore_neutron (_comp->_parameters.restore_neutron)
  #define nowritefile (_comp->_parameters.nowritefile)
  #define PSD_N (_comp->_parameters.PSD_N)
  #define PSD_p (_comp->_parameters.PSD_p)
  #define PSD_p2 (_comp->_parameters.PSD_p2)
  SIG_MESSAGE("[_PSD_before_monochromator_save] component PSD_before_monochromator=PSD_monitor() SAVE [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//monitors/PSD_monitor.comp:106]");

    if (!nowritefile) {
      DETECTOR_OUT_2D(
          "PSD monitor",
          "X position [cm]",
          "Y position [cm]",
          xmin*100.0, xmax*100.0, ymin*100.0, ymax*100.0,
          nx, ny,
          &PSD_N[0][0],&PSD_p[0][0],&PSD_p2[0][0],
          filename);
    }
  #undef nx
  #undef ny
  #undef filename
  #undef xmin
  #undef xmax
  #undef ymin
  #undef ymax
  #undef xwidth
  #undef yheight
  #undef restore_neutron
  #undef nowritefile
  #undef PSD_N
  #undef PSD_p
  #undef PSD_p2
  return(_comp);
} /* class_PSD_monitor_save */

_class_E_monitor *class_E_monitor_save(_class_E_monitor *_comp
) {
  #define nE (_comp->_parameters.nE)
  #define filename (_comp->_parameters.filename)
  #define xmin (_comp->_parameters.xmin)
  #define xmax (_comp->_parameters.xmax)
  #define ymin (_comp->_parameters.ymin)
  #define ymax (_comp->_parameters.ymax)
  #define nowritefile (_comp->_parameters.nowritefile)
  #define xwidth (_comp->_parameters.xwidth)
  #define yheight (_comp->_parameters.yheight)
  #define Emin (_comp->_parameters.Emin)
  #define Emax (_comp->_parameters.Emax)
  #define restore_neutron (_comp->_parameters.restore_neutron)
  #define E_N (_comp->_parameters.E_N)
  #define E_p (_comp->_parameters.E_p)
  #define E_p2 (_comp->_parameters.E_p2)
  #define S_p (_comp->_parameters.S_p)
  #define S_pE (_comp->_parameters.S_pE)
  #define S_pE2 (_comp->_parameters.S_pE2)
  SIG_MESSAGE("[_Emon_before_sampel_save] component Emon_before_sampel=E_monitor() SAVE [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//monitors/E_monitor.comp:131]");

if (!nowritefile) {
  DETECTOR_OUT_1D(
      "Energy monitor",
      "Energy [meV]",
      "Intensity",
      "E", Emin, Emax, nE,
      &E_N[0],&E_p[0],&E_p2[0],
      filename);
  if (S_p) printf("<E> : %g meV , E-width : %g meV \n",
   S_pE/S_p,sqrt(S_pE2/S_p - S_pE*S_pE/(S_p*S_p)) );
}
  #undef nE
  #undef filename
  #undef xmin
  #undef xmax
  #undef ymin
  #undef ymax
  #undef nowritefile
  #undef xwidth
  #undef yheight
  #undef Emin
  #undef Emax
  #undef restore_neutron
  #undef E_N
  #undef E_p
  #undef E_p2
  #undef S_p
  #undef S_pE
  #undef S_pE2
  return(_comp);
} /* class_E_monitor_save */



int save(FILE *handle) { /* called by mccode_main for template_body_simple:SAVE */
  if (!handle) siminfo_init(NULL);

  /* call iteratively all components SAVE */
  class_Progress_bar_save(&_origin_var);



  class_PSD_monitor_save(&_PSD_before_monochromator_var);








  class_PSD_monitor_save(&_PSD_after_monochromator_var);



  class_PSD_monitor_save(&_PSD_before_sampel_var);

  class_E_monitor_save(&_Emon_before_sampel_var);






















  class_PSD_monitor_save(&_PSD_after_sampel_var);



  class_PSD_monitor_save(&_Detector_var);

  if (!handle) siminfo_close(); 

  return(0);
} /* save */

/* *****************************************************************************
* instrument 'template_body_simple' and components FINALLY
***************************************************************************** */

_class_Progress_bar *class_Progress_bar_finally(_class_Progress_bar *_comp
) {
  #define profile (_comp->_parameters.profile)
  #define percent (_comp->_parameters.percent)
  #define flag_save (_comp->_parameters.flag_save)
  #define minutes (_comp->_parameters.minutes)
  #define IntermediateCnts (_comp->_parameters.IntermediateCnts)
  #define StartTime (_comp->_parameters.StartTime)
  #define EndTime (_comp->_parameters.EndTime)
  #define CurrentTime (_comp->_parameters.CurrentTime)
  SIG_MESSAGE("[_origin_finally] component origin=Progress_bar() FINALLY [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//misc/Progress_bar.comp:138]");

  time_t NowTime;
  time(&NowTime);
  fprintf(stdout, "\nFinally [%s: %s]. Time: ", instrument_name, dirname ? dirname : ".");
  if (difftime(NowTime,StartTime) < 60.0)
    fprintf(stdout, "%g [s] ", difftime(NowTime,StartTime));
  else if (difftime(NowTime,StartTime) > 3600.0)
    fprintf(stdout, "%g [h] ", difftime(NowTime,StartTime)/3600.0);
  else
    fprintf(stdout, "%g [min] ", difftime(NowTime,StartTime)/60.0);
  fprintf(stdout, "\n");
  #undef profile
  #undef percent
  #undef flag_save
  #undef minutes
  #undef IntermediateCnts
  #undef StartTime
  #undef EndTime
  #undef CurrentTime
  return(_comp);
} /* class_Progress_bar_finally */

_class_PSD_monitor *class_PSD_monitor_finally(_class_PSD_monitor *_comp
) {
  #define nx (_comp->_parameters.nx)
  #define ny (_comp->_parameters.ny)
  #define filename (_comp->_parameters.filename)
  #define xmin (_comp->_parameters.xmin)
  #define xmax (_comp->_parameters.xmax)
  #define ymin (_comp->_parameters.ymin)
  #define ymax (_comp->_parameters.ymax)
  #define xwidth (_comp->_parameters.xwidth)
  #define yheight (_comp->_parameters.yheight)
  #define restore_neutron (_comp->_parameters.restore_neutron)
  #define nowritefile (_comp->_parameters.nowritefile)
  #define PSD_N (_comp->_parameters.PSD_N)
  #define PSD_p (_comp->_parameters.PSD_p)
  #define PSD_p2 (_comp->_parameters.PSD_p2)
  SIG_MESSAGE("[_PSD_before_monochromator_finally] component PSD_before_monochromator=PSD_monitor() FINALLY [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//monitors/PSD_monitor.comp:119]");

  destroy_darr2d(PSD_N);
  destroy_darr2d(PSD_p);
  destroy_darr2d(PSD_p2);
  #undef nx
  #undef ny
  #undef filename
  #undef xmin
  #undef xmax
  #undef ymin
  #undef ymax
  #undef xwidth
  #undef yheight
  #undef restore_neutron
  #undef nowritefile
  #undef PSD_N
  #undef PSD_p
  #undef PSD_p2
  return(_comp);
} /* class_PSD_monitor_finally */

_class_E_monitor *class_E_monitor_finally(_class_E_monitor *_comp
) {
  #define nE (_comp->_parameters.nE)
  #define filename (_comp->_parameters.filename)
  #define xmin (_comp->_parameters.xmin)
  #define xmax (_comp->_parameters.xmax)
  #define ymin (_comp->_parameters.ymin)
  #define ymax (_comp->_parameters.ymax)
  #define nowritefile (_comp->_parameters.nowritefile)
  #define xwidth (_comp->_parameters.xwidth)
  #define yheight (_comp->_parameters.yheight)
  #define Emin (_comp->_parameters.Emin)
  #define Emax (_comp->_parameters.Emax)
  #define restore_neutron (_comp->_parameters.restore_neutron)
  #define E_N (_comp->_parameters.E_N)
  #define E_p (_comp->_parameters.E_p)
  #define E_p2 (_comp->_parameters.E_p2)
  #define S_p (_comp->_parameters.S_p)
  #define S_pE (_comp->_parameters.S_pE)
  #define S_pE2 (_comp->_parameters.S_pE2)
  SIG_MESSAGE("[_Emon_before_sampel_finally] component Emon_before_sampel=E_monitor() FINALLY [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//monitors/E_monitor.comp:146]");

  destroy_darr1d(E_N);
  destroy_darr1d(E_p);
  destroy_darr1d(E_p2);
  #undef nE
  #undef filename
  #undef xmin
  #undef xmax
  #undef ymin
  #undef ymax
  #undef nowritefile
  #undef xwidth
  #undef yheight
  #undef Emin
  #undef Emax
  #undef restore_neutron
  #undef E_N
  #undef E_p
  #undef E_p2
  #undef S_p
  #undef S_pE
  #undef S_pE2
  return(_comp);
} /* class_E_monitor_finally */

_class_Incoherent_process *class_Incoherent_process_finally(_class_Incoherent_process *_comp
) {
  #define sigma (_comp->_parameters.sigma)
  #define f_QE (_comp->_parameters.f_QE)
  #define gamma (_comp->_parameters.gamma)
  #define packing_factor (_comp->_parameters.packing_factor)
  #define unit_cell_volume (_comp->_parameters.unit_cell_volume)
  #define interact_fraction (_comp->_parameters.interact_fraction)
  #define init (_comp->_parameters.init)
  #define global_process_element (_comp->_parameters.global_process_element)
  #define This_process (_comp->_parameters.This_process)
  #define Incoherent_storage (_comp->_parameters.Incoherent_storage)
  #define effective_my_scattering (_comp->_parameters.effective_my_scattering)
  SIG_MESSAGE("[_Vanadium_incoherent_Al_finally] component Vanadium_incoherent_Al=Incoherent_process() FINALLY [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//union/Incoherent_process.comp:185]");

// Since the process and it's storage is a static allocation, there is nothing to deallocate

  #undef sigma
  #undef f_QE
  #undef gamma
  #undef packing_factor
  #undef unit_cell_volume
  #undef interact_fraction
  #undef init
  #undef global_process_element
  #undef This_process
  #undef Incoherent_storage
  #undef effective_my_scattering
  return(_comp);
} /* class_Incoherent_process_finally */

_class_Powder_process *class_Powder_process_finally(_class_Powder_process *_comp
) {
  #define reflections (_comp->_parameters.reflections)
  #define packing_factor (_comp->_parameters.packing_factor)
  #define Vc (_comp->_parameters.Vc)
  #define delta_d_d (_comp->_parameters.delta_d_d)
  #define DW (_comp->_parameters.DW)
  #define nb_atoms (_comp->_parameters.nb_atoms)
  #define d_phi (_comp->_parameters.d_phi)
  #define density (_comp->_parameters.density)
  #define weight (_comp->_parameters.weight)
  #define barns (_comp->_parameters.barns)
  #define Strain (_comp->_parameters.Strain)
  #define interact_fraction (_comp->_parameters.interact_fraction)
  #define format (_comp->_parameters.format)
  #define init (_comp->_parameters.init)
  #define global_process_element (_comp->_parameters.global_process_element)
  #define This_process (_comp->_parameters.This_process)
  #define Powder_storage (_comp->_parameters.Powder_storage)
  #define line_info (_comp->_parameters.line_info)
  #define effective_my_scattering (_comp->_parameters.effective_my_scattering)
  #define columns (_comp->_parameters.columns)
  SIG_MESSAGE("[_Al_Powder_finally] component Al_Powder=Powder_process() FINALLY [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//union/Powder_process.comp:791]");

  free(line_info.list);
  free(line_info.q_v);
  free(line_info.w_v);
  free(line_info.my_s_v2);
  #undef reflections
  #undef packing_factor
  #undef Vc
  #undef delta_d_d
  #undef DW
  #undef nb_atoms
  #undef d_phi
  #undef density
  #undef weight
  #undef barns
  #undef Strain
  #undef interact_fraction
  #undef format
  #undef init
  #undef global_process_element
  #undef This_process
  #undef Powder_storage
  #undef line_info
  #undef effective_my_scattering
  #undef columns
  return(_comp);
} /* class_Powder_process_finally */

_class_Union_make_material *class_Union_make_material_finally(_class_Union_make_material *_comp
) {
  #define process_string (_comp->_parameters.process_string)
  #define my_absorption (_comp->_parameters.my_absorption)
  #define absorber (_comp->_parameters.absorber)
  #define init (_comp->_parameters.init)
  #define global_material_element (_comp->_parameters.global_material_element)
  #define this_material (_comp->_parameters.this_material)
  #define loop_index (_comp->_parameters.loop_index)
  #define found_process (_comp->_parameters.found_process)
  #define specified_processes (_comp->_parameters.specified_processes)
  #define local_string (_comp->_parameters.local_string)
  #define accepted_processes (_comp->_parameters.accepted_processes)
  SIG_MESSAGE("[_Al_finally] component Al=Union_make_material() FINALLY [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//union/Union_make_material.comp:259]");

// The elements of the scattering array used static allocation and is thus deallocated automatically
if (this_material.number_of_processes > 0) free(this_material.p_scattering_array);
if (accepted_processes.num_elements > 0) free(accepted_processes.elements);

struct pointer_to_global_geometry_list *global_geometry_list = COMP_GETPAR3(Union_init, init, global_geometry_list);
struct pointer_to_global_master_list *global_master_list = COMP_GETPAR3(Union_init, init, global_master_list);

// Checking if any Union volumes are defined after the master component
#ifdef MASTER_DETECTOR
  #ifdef ANY_GEOMETRY_DETECTOR_DECLARE
    #ifndef MASTER_DETECTOR_WARNING
    
      for (loop_index=0;loop_index<global_geometry_list->num_elements;loop_index++) {
        if (global_geometry_list->elements[loop_index].component_index > global_master_list->elements[global_master_list->num_elements-1].component_index) {
          printf("WARNING: No Union_master component defined after Union volume named %s, this components did not affect the simulation in any way.\n",global_geometry_list->elements[loop_index].name);
        }
      }
      // Decided to have this as a warning without exiting the simulation
      // In order to only show this warning once, the MASTER_DETECTOR_WARNING is defined
      #define MASTER_DETECTOR_WARNING dummy
    #endif
  #endif
#endif

// Checking if the user remembered to put in a Union_master
#ifndef MASTER_DETECTOR
  #ifdef ANY_GEOMETRY_DETECTOR_DECLARE
    #ifndef MASTER_DETECTOR_WARNING
      printf("\nWARNING: No Union_master component used, these components did not affect the simulation in any way:\n");
      for (loop_index=0;loop_index<global_geometry_list->num_elements;loop_index++)
        printf("  %s\n",global_geometry_list->elements[loop_index].name);
      printf("\n");
      // Decided to have this as a warning without exiting the simulation
      // In order to only show this warning once, the MASTER_DETECTOR_WARNING is defined
      #define MASTER_DETECTOR_WARNING dummy
    #endif
  #endif
#endif


  #undef process_string
  #undef my_absorption
  #undef absorber
  #undef init
  #undef global_material_element
  #undef this_material
  #undef loop_index
  #undef found_process
  #undef specified_processes
  #undef local_string
  #undef accepted_processes
  return(_comp);
} /* class_Union_make_material_finally */

_class_Union_master *class_Union_master_finally(_class_Union_master *_comp
) {
  #define verbal (_comp->_parameters.verbal)
  #define list_verbal (_comp->_parameters.list_verbal)
  #define finally_verbal (_comp->_parameters.finally_verbal)
  #define allow_inside_start (_comp->_parameters.allow_inside_start)
  #define enable_tagging (_comp->_parameters.enable_tagging)
  #define history_limit (_comp->_parameters.history_limit)
  #define enable_conditionals (_comp->_parameters.enable_conditionals)
  #define inherit_number_of_scattering_events (_comp->_parameters.inherit_number_of_scattering_events)
  #define init (_comp->_parameters.init)
  #define global_positions_to_transform_list_master (_comp->_parameters.global_positions_to_transform_list_master)
  #define global_rotations_to_transform_list_master (_comp->_parameters.global_rotations_to_transform_list_master)
  #define global_process_list_master (_comp->_parameters.global_process_list_master)
  #define global_material_list_master (_comp->_parameters.global_material_list_master)
  #define global_geometry_list_master (_comp->_parameters.global_geometry_list_master)
  #define global_all_volume_logger_list_master (_comp->_parameters.global_all_volume_logger_list_master)
  #define global_specific_volumes_logger_list_master (_comp->_parameters.global_specific_volumes_logger_list_master)
  #define global_all_volume_abs_logger_list_master (_comp->_parameters.global_all_volume_abs_logger_list_master)
  #define global_specific_volumes_abs_logger_list_master (_comp->_parameters.global_specific_volumes_abs_logger_list_master)
  #define global_tagging_conditional_list_master (_comp->_parameters.global_tagging_conditional_list_master)
  #define global_master_list_master (_comp->_parameters.global_master_list_master)
  #define starting_volume_warning (_comp->_parameters.starting_volume_warning)
  #define global_master_element (_comp->_parameters.global_master_element)
  #define this_global_master_index (_comp->_parameters.this_global_master_index)
  #define previous_master_index (_comp->_parameters.previous_master_index)
  #define geometry_list_index (_comp->_parameters.geometry_list_index)
  #define intersection_time_table (_comp->_parameters.intersection_time_table)
  #define Volumes (_comp->_parameters.Volumes)
  #define Geometries (_comp->_parameters.Geometries)
  #define Volume_copies (_comp->_parameters.Volume_copies)
  #define starting_lists (_comp->_parameters.starting_lists)
  #define Volume_copies_allocated (_comp->_parameters.Volume_copies_allocated)
  #define r (_comp->_parameters.r)
  #define r_start (_comp->_parameters.r_start)
  #define v (_comp->_parameters.v)
  #define error_msg (_comp->_parameters.error_msg)
  #define component_error_msg (_comp->_parameters.component_error_msg)
  #define string_output (_comp->_parameters.string_output)
  #define number_of_volumes (_comp->_parameters.number_of_volumes)
  #define volume_index (_comp->_parameters.volume_index)
  #define process_index (_comp->_parameters.process_index)
  #define iterator (_comp->_parameters.iterator)
  #define solutions (_comp->_parameters.solutions)
  #define max_number_of_processes (_comp->_parameters.max_number_of_processes)
  #define limit (_comp->_parameters.limit)
  #define solution (_comp->_parameters.solution)
  #define min_solution (_comp->_parameters.min_solution)
  #define min_volume (_comp->_parameters.min_volume)
  #define time_found (_comp->_parameters.time_found)
  #define intersection_time (_comp->_parameters.intersection_time)
  #define min_intersection_time (_comp->_parameters.min_intersection_time)
  #define process (_comp->_parameters.process)
  #define process_start (_comp->_parameters.process_start)
  #define my_trace (_comp->_parameters.my_trace)
  #define p_my_trace (_comp->_parameters.p_my_trace)
  #define my_trace_fraction_control (_comp->_parameters.my_trace_fraction_control)
  #define k (_comp->_parameters.k)
  #define k_new (_comp->_parameters.k_new)
  #define k_old (_comp->_parameters.k_old)
  #define k_rotated (_comp->_parameters.k_rotated)
  #define v_length (_comp->_parameters.v_length)
  #define my_sum (_comp->_parameters.my_sum)
  #define my_sum_plus_abs (_comp->_parameters.my_sum_plus_abs)
  #define culmative_probability (_comp->_parameters.culmative_probability)
  #define mc_prop (_comp->_parameters.mc_prop)
  #define time_to_scattering (_comp->_parameters.time_to_scattering)
  #define length_to_scattering (_comp->_parameters.length_to_scattering)
  #define length_to_boundery (_comp->_parameters.length_to_boundery)
  #define time_to_boundery (_comp->_parameters.time_to_boundery)
  #define selected_process (_comp->_parameters.selected_process)
  #define scattering_event (_comp->_parameters.scattering_event)
  #define time_propagated_without_scattering (_comp->_parameters.time_propagated_without_scattering)
  #define a_next_volume_found (_comp->_parameters.a_next_volume_found)
  #define next_volume (_comp->_parameters.next_volume)
  #define next_volume_priority (_comp->_parameters.next_volume_priority)
  #define done (_comp->_parameters.done)
  #define current_volume (_comp->_parameters.current_volume)
  #define ray_sucseeded (_comp->_parameters.ray_sucseeded)
  #define number_of_solutions (_comp->_parameters.number_of_solutions)
  #define number_of_solutions_static (_comp->_parameters.number_of_solutions_static)
  #define check (_comp->_parameters.check)
  #define start (_comp->_parameters.start)
  #define intersection_with_children (_comp->_parameters.intersection_with_children)
  #define geometry_output (_comp->_parameters.geometry_output)
  #define tree_next_volume (_comp->_parameters.tree_next_volume)
  #define pre_allocated1 (_comp->_parameters.pre_allocated1)
  #define pre_allocated2 (_comp->_parameters.pre_allocated2)
  #define pre_allocated3 (_comp->_parameters.pre_allocated3)
  #define ray_position (_comp->_parameters.ray_position)
  #define ray_velocity (_comp->_parameters.ray_velocity)
  #define ray_velocity_rotated (_comp->_parameters.ray_velocity_rotated)
  #define ray_velocity_final (_comp->_parameters.ray_velocity_final)
  #define wavevector (_comp->_parameters.wavevector)
  #define wavevector_rotated (_comp->_parameters.wavevector_rotated)
  #define volume_0_found (_comp->_parameters.volume_0_found)
  #define scattered_flag (_comp->_parameters.scattered_flag)
  #define scattered_flag_VP (_comp->_parameters.scattered_flag_VP)
  #define master_transposed_rotation_matrix (_comp->_parameters.master_transposed_rotation_matrix)
  #define temp_rotation_matrix (_comp->_parameters.temp_rotation_matrix)
  #define temp_transpose_rotation_matrix (_comp->_parameters.temp_transpose_rotation_matrix)
  #define non_rotated_position (_comp->_parameters.non_rotated_position)
  #define rotated_position (_comp->_parameters.rotated_position)
  #define non_isotropic_found (_comp->_parameters.non_isotropic_found)
  #define master_tagging_node_list (_comp->_parameters.master_tagging_node_list)
  #define current_tagging_node (_comp->_parameters.current_tagging_node)
  #define tagging_leaf_counter (_comp->_parameters.tagging_leaf_counter)
  #define stop_tagging_ray (_comp->_parameters.stop_tagging_ray)
  #define stop_creating_nodes (_comp->_parameters.stop_creating_nodes)
  #define number_of_scattering_events (_comp->_parameters.number_of_scattering_events)
  #define real_transmission_probability (_comp->_parameters.real_transmission_probability)
  #define mc_transmission_probability (_comp->_parameters.mc_transmission_probability)
  #define number_of_process_interacts_set (_comp->_parameters.number_of_process_interacts_set)
  #define index_of_lacking_process (_comp->_parameters.index_of_lacking_process)
  #define total_process_interact (_comp->_parameters.total_process_interact)
  #define geometry_component_index_list (_comp->_parameters.geometry_component_index_list)
  #define mask_volume_index_list (_comp->_parameters.mask_volume_index_list)
  #define number_of_masks (_comp->_parameters.number_of_masks)
  #define number_of_masked_volumes (_comp->_parameters.number_of_masked_volumes)
  #define mask_status_list (_comp->_parameters.mask_status_list)
  #define current_mask_intersect_list_status (_comp->_parameters.current_mask_intersect_list_status)
  #define mask_index_main (_comp->_parameters.mask_index_main)
  #define mask_iterator (_comp->_parameters.mask_iterator)
  #define mask_start (_comp->_parameters.mask_start)
  #define mask_check (_comp->_parameters.mask_check)
  #define need_to_run_within_which_volume (_comp->_parameters.need_to_run_within_which_volume)
  #define number_of_processes_array (_comp->_parameters.number_of_processes_array)
  #define p_old (_comp->_parameters.p_old)
  #define log_index (_comp->_parameters.log_index)
  #define conditional_status (_comp->_parameters.conditional_status)
  #define this_logger (_comp->_parameters.this_logger)
  #define this_abs_logger (_comp->_parameters.this_abs_logger)
  #define tagging_conditional_list (_comp->_parameters.tagging_conditional_list)
  #define logger_conditional_extend_array (_comp->_parameters.logger_conditional_extend_array)
  #define abs_logger_conditional_extend_array (_comp->_parameters.abs_logger_conditional_extend_array)
  #define max_conditional_extend_index (_comp->_parameters.max_conditional_extend_index)
  #define tagging_conditional_extend (_comp->_parameters.tagging_conditional_extend)
  #define free_tagging_conditioanl_list (_comp->_parameters.free_tagging_conditioanl_list)
  #define safty_distance (_comp->_parameters.safty_distance)
  #define safty_distance2 (_comp->_parameters.safty_distance2)
  #define temporary_focus_data (_comp->_parameters.temporary_focus_data)
  #define focus_data_index (_comp->_parameters.focus_data_index)
  #define r_old (_comp->_parameters.r_old)
  #define initial_weight (_comp->_parameters.initial_weight)
  #define abs_weight_factor (_comp->_parameters.abs_weight_factor)
  #define time_old (_comp->_parameters.time_old)
  #define absorption_index (_comp->_parameters.absorption_index)
  #define abs_weight_factor_set (_comp->_parameters.abs_weight_factor_set)
  #define my_abs (_comp->_parameters.my_abs)
  #define absorption_event_data (_comp->_parameters.absorption_event_data)
  #define abs_position (_comp->_parameters.abs_position)
  #define transformed_abs_position (_comp->_parameters.transformed_abs_position)
  #define t_abs_propagation (_comp->_parameters.t_abs_propagation)
  #define abs_distance (_comp->_parameters.abs_distance)
  #define abs_max_length (_comp->_parameters.abs_max_length)
  SIG_MESSAGE("[_Master_finally] component Master=Union_master() FINALLY [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//union/Union_master.comp:2126]");

// write out histories from tagging system if enabled
if (enable_tagging) {
    if (finally_verbal) printf("Writing tagging tree to disk \n");
    if (finally_verbal) printf("Number of leafs = %d \n",tagging_leaf_counter);
    // While writing the tagging tree to disk, all the leafs are deallocated
    write_tagging_tree(&master_tagging_node_list, Volumes, tagging_leaf_counter, number_of_volumes);
}
if (master_tagging_node_list.num_elements > 0) free(master_tagging_node_list.elements);


if (finally_verbal) printf("Freeing variables which are always allocated \n");
// free allocated arrays specific to this master union component
free(scattered_flag);
free(my_trace);
free(pre_allocated1);
free(pre_allocated2);
free(pre_allocated3);
free(number_of_processes_array);
free(Geometries);

if (finally_verbal) printf("Freeing intersection_time_table \n");
for (iterator = 1;iterator < intersection_time_table.num_volumes;iterator++){
    free(intersection_time_table.intersection_times[iterator]);
}

free(intersection_time_table.n_elements);
free(intersection_time_table.calculated);
free(intersection_time_table.intersection_times);

if (free_tagging_conditioanl_list == 1) free(tagging_conditional_list);

if (finally_verbal) printf("Freeing lists for individual volumes \n");
for (volume_index=0;volume_index<number_of_volumes;volume_index++) {
  
  if (finally_verbal) printf("  Freeing geometry\n");
  if (Volumes[volume_index]->geometry.intersect_check_list.num_elements > 0) free(Volumes[volume_index]->geometry.intersect_check_list.elements);
  if (Volumes[volume_index]->geometry.destinations_list.num_elements > 0) free(Volumes[volume_index]->geometry.destinations_list.elements);
  if (Volumes[volume_index]->geometry.reduced_destinations_list.num_elements > 0) free(Volumes[volume_index]->geometry.reduced_destinations_list.elements);
  if (Volumes[volume_index]->geometry.children.num_elements > 0) free(Volumes[volume_index]->geometry.children.elements);
  if (Volumes[volume_index]->geometry.direct_children.num_elements > 0) free(Volumes[volume_index]->geometry.direct_children.elements);
  if (Volumes[volume_index]->geometry.masked_by_list.num_elements > 0) free(Volumes[volume_index]->geometry.masked_by_list.elements);
  if (Volumes[volume_index]->geometry.masked_by_mask_index_list.num_elements > 0) free(Volumes[volume_index]->geometry.masked_by_mask_index_list.elements);
  if (Volumes[volume_index]->geometry.mask_list.num_elements > 0) free(Volumes[volume_index]->geometry.mask_list.elements);
  if (Volumes[volume_index]->geometry.mask_intersect_list.num_elements > 0) free(Volumes[volume_index]->geometry.mask_intersect_list.elements);
  if (Volumes[volume_index]->geometry.next_volume_list.num_elements > 0) free(Volumes[volume_index]->geometry.next_volume_list.elements);
  
  if (finally_verbal) printf("  Freeing physics\n");
  if (volume_index > 0) { // Volume 0 does not have physical properties allocated
    free(scattered_flag_VP[volume_index]);
    if (Volumes[volume_index]->geometry.process_rot_allocated == 1) {
          free(Volumes[volume_index]->geometry.process_rot_matrix_array);
          free(Volumes[volume_index]->geometry.transpose_process_rot_matrix_array);
          Volumes[volume_index]->geometry.process_rot_allocated = 0;
    }
    if (on_int_list(Volume_copies_allocated,volume_index)) {
      // This is a local copy of a volume, deallocate that local copy (all the allocated memory attachted to it was just deallocated, so this should not leave any leaks)
      free(Volumes[volume_index]);
    } else {
      // Only free p_physics for vacuum volumes for the original at the end (there is a p_physics allocated for each vacuum volume)
      if (Volumes[volume_index]->p_physics->is_vacuum == 1 ) free(Volumes[volume_index]->p_physics);
    }
  }
  
  if (finally_verbal) printf("  Freeing loggers\n");
  if (Volumes[volume_index]->loggers.num_elements >0) {
    for (iterator=0;iterator<Volumes[volume_index]->loggers.num_elements;iterator++) {
      free(Volumes[volume_index]->loggers.p_logger_volume[iterator].p_logger_process);
    }
    free(Volumes[volume_index]->loggers.p_logger_volume);
  }
  
  if (finally_verbal) printf("  Freeing abs_loggers\n");
  if (Volumes[volume_index]->abs_loggers.num_elements > 0) {
    free(Volumes[volume_index]->abs_loggers.p_abs_logger);
  }
  if (finally_verbal) printf("  Freeing Volumes[index]\n");
  //free(Volumes[volume_index]); // Not able to free
  //if (finally_verbal) printf("  Managed to free Volumes[index]\n");
}

free(scattered_flag_VP);

if (finally_verbal) printf("Freeing starting lists \n");
if (starting_lists.allowed_starting_volume_logic_list.num_elements > 0) free(starting_lists.allowed_starting_volume_logic_list.elements);
if (starting_lists.reduced_start_list.num_elements > 0) free(starting_lists.reduced_start_list.elements);
if (starting_lists.start_logic_list.num_elements > 0) free(starting_lists.start_logic_list.elements);

if (finally_verbal) printf("Freeing mask lists \n");
if (mask_status_list.num_elements>0) free(mask_status_list.elements);
if (current_mask_intersect_list_status.num_elements>0) free(current_mask_intersect_list_status.elements);
if (mask_volume_index_list.num_elements>0) free(mask_volume_index_list.elements);

if (finally_verbal) printf("Freeing component index list \n");
if (geometry_component_index_list.num_elements>0) free(geometry_component_index_list.elements);


if (finally_verbal) printf("Freeing Volumes \n");
free(Volumes);

// Free global allocated arrays if this is the last master union component in the instrument file

if (global_master_list_master->elements[global_master_list_master->num_elements-1].component_index == INDEX_CURRENT_COMP) {
    if (finally_verbal) printf("Freeing global arrays because this is the last Union master component\n");
    
    // Freeing lists allocated in Union_initialization
    
    if (finally_verbal) printf("Freeing global process list \n");
    if (global_process_list_master->num_elements > 0) free(global_process_list_master->elements);

    if (finally_verbal) printf("Freeing global material list \n");
    if (global_material_list_master->num_elements > 0) free(global_material_list_master->elements);

    if (finally_verbal) printf("Freeing global geometry list \n");
    if (global_geometry_list_master->num_elements > 0) free(global_geometry_list_master->elements);

    if (finally_verbal) printf("Freeing global master list \n");
    if (global_master_list_master->num_elements > 0) free(global_master_list_master->elements);
    
    if (finally_verbal) printf("Freeing global logger lists \n");
    for (iterator=0;iterator<global_all_volume_logger_list_master->num_elements;iterator++) {
      if (global_all_volume_logger_list_master->elements[iterator].logger->conditional_list.num_elements > 0) {
        free(global_all_volume_logger_list_master->elements[iterator].logger->conditional_list.conditional_functions);
        free(global_all_volume_logger_list_master->elements[iterator].logger->conditional_list.p_data_unions);
      }
    }
    if (global_all_volume_logger_list_master->num_elements > 0) free(global_all_volume_logger_list_master->elements);
        
    for (iterator=0;iterator<global_specific_volumes_logger_list_master->num_elements;iterator++) {
      if (global_specific_volumes_logger_list_master->elements[iterator].logger->conditional_list.num_elements > 0) {
        free(global_specific_volumes_logger_list_master->elements[iterator].logger->conditional_list.conditional_functions);
        free(global_specific_volumes_logger_list_master->elements[iterator].logger->conditional_list.p_data_unions);
      }
    }
    if (global_specific_volumes_logger_list_master->num_elements > 0) free(global_specific_volumes_logger_list_master->elements);
    
    if (finally_verbal) printf("Freeing global abs logger lists \n");
    for (iterator=0;iterator<global_all_volume_abs_logger_list_master->num_elements;iterator++) {
      if (global_all_volume_abs_logger_list_master->elements[iterator].abs_logger->conditional_list.num_elements > 0) {
        free(global_all_volume_abs_logger_list_master->elements[iterator].abs_logger->conditional_list.conditional_functions);
        free(global_all_volume_abs_logger_list_master->elements[iterator].abs_logger->conditional_list.p_data_unions);
      }
    }
    if (global_all_volume_abs_logger_list_master->num_elements > 0) free(global_all_volume_abs_logger_list_master->elements);
    
    for (iterator=0;iterator<global_specific_volumes_abs_logger_list_master->num_elements;iterator++) {
      if (global_specific_volumes_abs_logger_list_master->elements[iterator].abs_logger->conditional_list.num_elements > 0) {
        free(global_specific_volumes_abs_logger_list_master->elements[iterator].abs_logger->conditional_list.conditional_functions);
        free(global_specific_volumes_abs_logger_list_master->elements[iterator].abs_logger->conditional_list.p_data_unions);
      }
    }
    if (global_specific_volumes_abs_logger_list_master->num_elements > 0) free(global_specific_volumes_abs_logger_list_master->elements);

    if (finally_verbal) printf("Freeing global tagging conditional lists \n");
    for (iterator=0;iterator<global_tagging_conditional_list_master->num_elements;iterator++) {
      if (global_tagging_conditional_list_master->elements[iterator].conditional_list.num_elements > 0) {
        free(global_tagging_conditional_list_master->elements[iterator].conditional_list.conditional_functions);
        free(global_tagging_conditional_list_master->elements[iterator].conditional_list.p_data_unions);
      }
    }
    if (global_tagging_conditional_list_master->num_elements>0) free(global_tagging_conditional_list_master->elements);
}

  #undef verbal
  #undef list_verbal
  #undef finally_verbal
  #undef allow_inside_start
  #undef enable_tagging
  #undef history_limit
  #undef enable_conditionals
  #undef inherit_number_of_scattering_events
  #undef init
  #undef global_positions_to_transform_list_master
  #undef global_rotations_to_transform_list_master
  #undef global_process_list_master
  #undef global_material_list_master
  #undef global_geometry_list_master
  #undef global_all_volume_logger_list_master
  #undef global_specific_volumes_logger_list_master
  #undef global_all_volume_abs_logger_list_master
  #undef global_specific_volumes_abs_logger_list_master
  #undef global_tagging_conditional_list_master
  #undef global_master_list_master
  #undef starting_volume_warning
  #undef global_master_element
  #undef this_global_master_index
  #undef previous_master_index
  #undef geometry_list_index
  #undef intersection_time_table
  #undef Volumes
  #undef Geometries
  #undef Volume_copies
  #undef starting_lists
  #undef Volume_copies_allocated
  #undef r
  #undef r_start
  #undef v
  #undef error_msg
  #undef component_error_msg
  #undef string_output
  #undef number_of_volumes
  #undef volume_index
  #undef process_index
  #undef iterator
  #undef solutions
  #undef max_number_of_processes
  #undef limit
  #undef solution
  #undef min_solution
  #undef min_volume
  #undef time_found
  #undef intersection_time
  #undef min_intersection_time
  #undef process
  #undef process_start
  #undef my_trace
  #undef p_my_trace
  #undef my_trace_fraction_control
  #undef k
  #undef k_new
  #undef k_old
  #undef k_rotated
  #undef v_length
  #undef my_sum
  #undef my_sum_plus_abs
  #undef culmative_probability
  #undef mc_prop
  #undef time_to_scattering
  #undef length_to_scattering
  #undef length_to_boundery
  #undef time_to_boundery
  #undef selected_process
  #undef scattering_event
  #undef time_propagated_without_scattering
  #undef a_next_volume_found
  #undef next_volume
  #undef next_volume_priority
  #undef done
  #undef current_volume
  #undef ray_sucseeded
  #undef number_of_solutions
  #undef number_of_solutions_static
  #undef check
  #undef start
  #undef intersection_with_children
  #undef geometry_output
  #undef tree_next_volume
  #undef pre_allocated1
  #undef pre_allocated2
  #undef pre_allocated3
  #undef ray_position
  #undef ray_velocity
  #undef ray_velocity_rotated
  #undef ray_velocity_final
  #undef wavevector
  #undef wavevector_rotated
  #undef volume_0_found
  #undef scattered_flag
  #undef scattered_flag_VP
  #undef master_transposed_rotation_matrix
  #undef temp_rotation_matrix
  #undef temp_transpose_rotation_matrix
  #undef non_rotated_position
  #undef rotated_position
  #undef non_isotropic_found
  #undef master_tagging_node_list
  #undef current_tagging_node
  #undef tagging_leaf_counter
  #undef stop_tagging_ray
  #undef stop_creating_nodes
  #undef number_of_scattering_events
  #undef real_transmission_probability
  #undef mc_transmission_probability
  #undef number_of_process_interacts_set
  #undef index_of_lacking_process
  #undef total_process_interact
  #undef geometry_component_index_list
  #undef mask_volume_index_list
  #undef number_of_masks
  #undef number_of_masked_volumes
  #undef mask_status_list
  #undef current_mask_intersect_list_status
  #undef mask_index_main
  #undef mask_iterator
  #undef mask_start
  #undef mask_check
  #undef need_to_run_within_which_volume
  #undef number_of_processes_array
  #undef p_old
  #undef log_index
  #undef conditional_status
  #undef this_logger
  #undef this_abs_logger
  #undef tagging_conditional_list
  #undef logger_conditional_extend_array
  #undef abs_logger_conditional_extend_array
  #undef max_conditional_extend_index
  #undef tagging_conditional_extend
  #undef free_tagging_conditioanl_list
  #undef safty_distance
  #undef safty_distance2
  #undef temporary_focus_data
  #undef focus_data_index
  #undef r_old
  #undef initial_weight
  #undef abs_weight_factor
  #undef time_old
  #undef absorption_index
  #undef abs_weight_factor_set
  #undef my_abs
  #undef absorption_event_data
  #undef abs_position
  #undef transformed_abs_position
  #undef t_abs_propagation
  #undef abs_distance
  #undef abs_max_length
  return(_comp);
} /* class_Union_master_finally */



int finally(void) { /* called by mccode_main for template_body_simple:FINALLY */
#pragma acc update host(_origin_var)
#pragma acc update host(_source_maxwell_3_var)
#pragma acc update host(_guide_var)
#pragma acc update host(_PSD_before_monochromator_var)
#pragma acc update host(_a1_var)
#pragma acc update host(_monochromator_flat1_var)
#pragma acc update host(_monochromator_flat2_var)
#pragma acc update host(_monochromator_flat3_var)
#pragma acc update host(_monochromator_flat4_var)
#pragma acc update host(_monochromator_flat5_var)
#pragma acc update host(_a2_var)
#pragma acc update host(_PSD_after_monochromator_var)
#pragma acc update host(_collimator_linear_var)
#pragma acc update host(_slit_var)
#pragma acc update host(_PSD_before_sampel_var)
#pragma acc update host(_Emon_before_sampel_var)
#pragma acc update host(_a3_var)
#pragma acc update host(_a4_var)
#pragma acc update host(_a5_var)
#pragma acc update host(_sample_position_var)
#pragma acc update host(_init_var)
#pragma acc update host(_Vanadium_incoherent_Al_var)
#pragma acc update host(_Al_Powder_var)
#pragma acc update host(_Al_var)
#pragma acc update host(_Vanadium_incoherent_Cu_var)
#pragma acc update host(_Vanadium_incoherent_MnF2_var)
#pragma acc update host(_MnF2_critical_var)
#pragma acc update host(_MnF2_single_crystal_var)
#pragma acc update host(_MnF2_single_crystal_magn_var)
#pragma acc update host(_MnF2_var)
#pragma acc update host(_union_sample_var)
#pragma acc update host(_union_stick_var)
#pragma acc update host(_cryostat_wall_var)
#pragma acc update host(_cryostat_vacuum_var)
#pragma acc update host(_Master_var)
#pragma acc update host(_stop_var)
#pragma acc update host(_collimator_linear_2_var)
#pragma acc update host(_PSD_after_sampel_var)
#pragma acc update host(_monochromator_flat6_var)
#pragma acc update host(_a6_var)
#pragma acc update host(_Detector_var)
#pragma acc update host(_instrument_var)

  siminfo_init(NULL);
  save(siminfo_file); /* save data when simulation ends */

  /* call iteratively all components FINALLY */
  class_Progress_bar_finally(&_origin_var);



  class_PSD_monitor_finally(&_PSD_before_monochromator_var);








  class_PSD_monitor_finally(&_PSD_after_monochromator_var);



  class_PSD_monitor_finally(&_PSD_before_sampel_var);

  class_E_monitor_finally(&_Emon_before_sampel_var);






  class_Incoherent_process_finally(&_Vanadium_incoherent_Al_var);

  class_Powder_process_finally(&_Al_Powder_var);

  class_Union_make_material_finally(&_Al_var);

  class_Incoherent_process_finally(&_Vanadium_incoherent_Cu_var);

  class_Incoherent_process_finally(&_Vanadium_incoherent_MnF2_var);




  class_Union_make_material_finally(&_MnF2_var);





  class_Union_master_finally(&_Master_var);



  class_PSD_monitor_finally(&_PSD_after_sampel_var);



  class_PSD_monitor_finally(&_Detector_var);

  siminfo_close(); 

  return(0);
} /* finally */

/* *****************************************************************************
* instrument 'template_body_simple' and components DISPLAY
***************************************************************************** */

  #define magnify     mcdis_magnify
  #define line        mcdis_line
  #define dashed_line mcdis_dashed_line
  #define multiline   mcdis_multiline
  #define rectangle   mcdis_rectangle
  #define box         mcdis_box
  #define circle      mcdis_circle
  #define cylinder    mcdis_cylinder
  #define sphere      mcdis_sphere
_class_Progress_bar *class_Progress_bar_display(_class_Progress_bar *_comp
) {
  #define profile (_comp->_parameters.profile)
  #define percent (_comp->_parameters.percent)
  #define flag_save (_comp->_parameters.flag_save)
  #define minutes (_comp->_parameters.minutes)
  #define IntermediateCnts (_comp->_parameters.IntermediateCnts)
  #define StartTime (_comp->_parameters.StartTime)
  #define EndTime (_comp->_parameters.EndTime)
  #define CurrentTime (_comp->_parameters.CurrentTime)
  SIG_MESSAGE("[_origin_display] component origin=Progress_bar() DISPLAY [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//misc/Progress_bar.comp:152]");

  printf("MCDISPLAY: component %s\n", _comp->_name);

  #undef profile
  #undef percent
  #undef flag_save
  #undef minutes
  #undef IntermediateCnts
  #undef StartTime
  #undef EndTime
  #undef CurrentTime
  return(_comp);
} /* class_Progress_bar_display */

_class_Source_Maxwell_3 *class_Source_Maxwell_3_display(_class_Source_Maxwell_3 *_comp
) {
  #define size (_comp->_parameters.size)
  #define yheight (_comp->_parameters.yheight)
  #define xwidth (_comp->_parameters.xwidth)
  #define Lmin (_comp->_parameters.Lmin)
  #define Lmax (_comp->_parameters.Lmax)
  #define dist (_comp->_parameters.dist)
  #define focus_xw (_comp->_parameters.focus_xw)
  #define focus_yh (_comp->_parameters.focus_yh)
  #define T1 (_comp->_parameters.T1)
  #define T2 (_comp->_parameters.T2)
  #define T3 (_comp->_parameters.T3)
  #define I1 (_comp->_parameters.I1)
  #define I2 (_comp->_parameters.I2)
  #define I3 (_comp->_parameters.I3)
  #define target_index (_comp->_parameters.target_index)
  #define lambda0 (_comp->_parameters.lambda0)
  #define dlambda (_comp->_parameters.dlambda)
  #define l_range (_comp->_parameters.l_range)
  #define w_mult (_comp->_parameters.w_mult)
  #define w_source (_comp->_parameters.w_source)
  #define h_source (_comp->_parameters.h_source)
  SIG_MESSAGE("[_source_maxwell_3_display] component source_maxwell_3=Source_Maxwell_3() DISPLAY [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//sources/Source_Maxwell_3.comp:158]");

  printf("MCDISPLAY: component %s\n", _comp->_name);
  
  multiline(5, -(double)focus_xw/2.0, -(double)focus_yh/2.0, 0.0,
                (double)focus_xw/2.0, -(double)focus_yh/2.0, 0.0,
                (double)focus_xw/2.0,  (double)focus_yh/2.0, 0.0,
               -(double)focus_xw/2.0,  (double)focus_yh/2.0, 0.0,
               -(double)focus_xw/2.0, -(double)focus_yh/2.0, 0.0);
  if (dist) {
    dashed_line(0,0,0, -focus_xw/2,-focus_yh/2,dist, 4);
    dashed_line(0,0,0,  focus_xw/2,-focus_yh/2,dist, 4);
    dashed_line(0,0,0,  focus_xw/2, focus_yh/2,dist, 4);
    dashed_line(0,0,0, -focus_xw/2, focus_yh/2,dist, 4);
  }
  #undef size
  #undef yheight
  #undef xwidth
  #undef Lmin
  #undef Lmax
  #undef dist
  #undef focus_xw
  #undef focus_yh
  #undef T1
  #undef T2
  #undef T3
  #undef I1
  #undef I2
  #undef I3
  #undef target_index
  #undef lambda0
  #undef dlambda
  #undef l_range
  #undef w_mult
  #undef w_source
  #undef h_source
  return(_comp);
} /* class_Source_Maxwell_3_display */

_class_Guide *class_Guide_display(_class_Guide *_comp
) {
  #define reflect (_comp->_parameters.reflect)
  #define w1 (_comp->_parameters.w1)
  #define h1 (_comp->_parameters.h1)
  #define w2 (_comp->_parameters.w2)
  #define h2 (_comp->_parameters.h2)
  #define l (_comp->_parameters.l)
  #define R0 (_comp->_parameters.R0)
  #define Qc (_comp->_parameters.Qc)
  #define alpha (_comp->_parameters.alpha)
  #define m (_comp->_parameters.m)
  #define W (_comp->_parameters.W)
  #define pTable (_comp->_parameters.pTable)
  #define table_present (_comp->_parameters.table_present)
  SIG_MESSAGE("[_guide_display] component guide=Guide() DISPLAY [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//optics/Guide.comp:204]");

  printf("MCDISPLAY: component %s\n", _comp->_name);
  
  multiline(5,
            -w1/2.0, -h1/2.0, 0.0,
             w1/2.0, -h1/2.0, 0.0,
             w1/2.0,  h1/2.0, 0.0,
            -w1/2.0,  h1/2.0, 0.0,
            -w1/2.0, -h1/2.0, 0.0);
  multiline(5,
            -w2/2.0, -h2/2.0, (double)l,
             w2/2.0, -h2/2.0, (double)l,
             w2/2.0,  h2/2.0, (double)l,
            -w2/2.0,  h2/2.0, (double)l,
            -w2/2.0, -h2/2.0, (double)l);
  line(-w1/2.0, -h1/2.0, 0, -w2/2.0, -h2/2.0, (double)l);
  line( w1/2.0, -h1/2.0, 0,  w2/2.0, -h2/2.0, (double)l);
  line( w1/2.0,  h1/2.0, 0,  w2/2.0,  h2/2.0, (double)l);
  line(-w1/2.0,  h1/2.0, 0, -w2/2.0,  h2/2.0, (double)l);
  #undef reflect
  #undef w1
  #undef h1
  #undef w2
  #undef h2
  #undef l
  #undef R0
  #undef Qc
  #undef alpha
  #undef m
  #undef W
  #undef pTable
  #undef table_present
  return(_comp);
} /* class_Guide_display */

_class_PSD_monitor *class_PSD_monitor_display(_class_PSD_monitor *_comp
) {
  #define nx (_comp->_parameters.nx)
  #define ny (_comp->_parameters.ny)
  #define filename (_comp->_parameters.filename)
  #define xmin (_comp->_parameters.xmin)
  #define xmax (_comp->_parameters.xmax)
  #define ymin (_comp->_parameters.ymin)
  #define ymax (_comp->_parameters.ymax)
  #define xwidth (_comp->_parameters.xwidth)
  #define yheight (_comp->_parameters.yheight)
  #define restore_neutron (_comp->_parameters.restore_neutron)
  #define nowritefile (_comp->_parameters.nowritefile)
  #define PSD_N (_comp->_parameters.PSD_N)
  #define PSD_p (_comp->_parameters.PSD_p)
  #define PSD_p2 (_comp->_parameters.PSD_p2)
  SIG_MESSAGE("[_PSD_before_monochromator_display] component PSD_before_monochromator=PSD_monitor() DISPLAY [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//monitors/PSD_monitor.comp:126]");

  printf("MCDISPLAY: component %s\n", _comp->_name);
  
  multiline(5, (double)xmin, (double)ymin, 0.0,
               (double)xmax, (double)ymin, 0.0,
               (double)xmax, (double)ymax, 0.0,
               (double)xmin, (double)ymax, 0.0,
               (double)xmin, (double)ymin, 0.0);
  #undef nx
  #undef ny
  #undef filename
  #undef xmin
  #undef xmax
  #undef ymin
  #undef ymax
  #undef xwidth
  #undef yheight
  #undef restore_neutron
  #undef nowritefile
  #undef PSD_N
  #undef PSD_p
  #undef PSD_p2
  return(_comp);
} /* class_PSD_monitor_display */

_class_Arm *class_Arm_display(_class_Arm *_comp
) {
  SIG_MESSAGE("[_a1_display] component a1=Arm() DISPLAY [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//optics/Arm.comp:40]");

  printf("MCDISPLAY: component %s\n", _comp->_name);
  /* A bit ugly; hard-coded dimensions. */
  
  line(0,0,0,0.2,0,0);
  line(0,0,0,0,0.2,0);
  line(0,0,0,0,0,0.2);
  return(_comp);
} /* class_Arm_display */

_class_Monochromator_flat *class_Monochromator_flat_display(_class_Monochromator_flat *_comp
) {
  #define zmin (_comp->_parameters.zmin)
  #define zmax (_comp->_parameters.zmax)
  #define ymin (_comp->_parameters.ymin)
  #define ymax (_comp->_parameters.ymax)
  #define zwidth (_comp->_parameters.zwidth)
  #define yheight (_comp->_parameters.yheight)
  #define mosaich (_comp->_parameters.mosaich)
  #define mosaicv (_comp->_parameters.mosaicv)
  #define r0 (_comp->_parameters.r0)
  #define Q (_comp->_parameters.Q)
  #define DM (_comp->_parameters.DM)
  #define mos_rms_y (_comp->_parameters.mos_rms_y)
  #define mos_rms_z (_comp->_parameters.mos_rms_z)
  #define mos_rms_max (_comp->_parameters.mos_rms_max)
  #define mono_Q (_comp->_parameters.mono_Q)
  SIG_MESSAGE("[_monochromator_flat1_display] component monochromator_flat1=Monochromator_flat() DISPLAY [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//optics/Monochromator_flat.comp:255]");

  printf("MCDISPLAY: component %s\n", _comp->_name);
  
  multiline(5, 0.0, (double)ymin, (double)zmin,
               0.0, (double)ymax, (double)zmin,
               0.0, (double)ymax, (double)zmax,
               0.0, (double)ymin, (double)zmax,
               0.0, (double)ymin, (double)zmin);
  #undef zmin
  #undef zmax
  #undef ymin
  #undef ymax
  #undef zwidth
  #undef yheight
  #undef mosaich
  #undef mosaicv
  #undef r0
  #undef Q
  #undef DM
  #undef mos_rms_y
  #undef mos_rms_z
  #undef mos_rms_max
  #undef mono_Q
  return(_comp);
} /* class_Monochromator_flat_display */

_class_Collimator_linear *class_Collimator_linear_display(_class_Collimator_linear *_comp
) {
  #define xmin (_comp->_parameters.xmin)
  #define xmax (_comp->_parameters.xmax)
  #define ymin (_comp->_parameters.ymin)
  #define ymax (_comp->_parameters.ymax)
  #define xwidth (_comp->_parameters.xwidth)
  #define yheight (_comp->_parameters.yheight)
  #define length (_comp->_parameters.length)
  #define divergence (_comp->_parameters.divergence)
  #define transmission (_comp->_parameters.transmission)
  #define divergenceV (_comp->_parameters.divergenceV)
  #define slope (_comp->_parameters.slope)
  #define slopeV (_comp->_parameters.slopeV)
  SIG_MESSAGE("[_collimator_linear_display] component collimator_linear=Collimator_linear() DISPLAY [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//optics/Collimator_linear.comp:101]");

  printf("MCDISPLAY: component %s\n", _comp->_name);
  double x;
  int i;

  
  for(x = xmin, i = 0; i <= 3; i++, x += (xmax - xmin)/3.0)
    multiline(5, x, (double)ymin, 0.0, x, (double)ymax, 0.0,
              x, (double)ymax, (double)length, x, (double)ymin, (double)length,
              x, (double)ymin, 0.0);
  line(xmin, ymin, 0,   xmax, ymin, 0);
  line(xmin, ymax, 0,   xmax, ymax, 0);
  line(xmin, ymin, length, xmax, ymin, length);
  line(xmin, ymax, length, xmax, ymax, length);
  #undef xmin
  #undef xmax
  #undef ymin
  #undef ymax
  #undef xwidth
  #undef yheight
  #undef length
  #undef divergence
  #undef transmission
  #undef divergenceV
  #undef slope
  #undef slopeV
  return(_comp);
} /* class_Collimator_linear_display */

_class_Slit *class_Slit_display(_class_Slit *_comp
) {
  #define xmin (_comp->_parameters.xmin)
  #define xmax (_comp->_parameters.xmax)
  #define ymin (_comp->_parameters.ymin)
  #define ymax (_comp->_parameters.ymax)
  #define radius (_comp->_parameters.radius)
  #define xwidth (_comp->_parameters.xwidth)
  #define yheight (_comp->_parameters.yheight)
  #define isradial (_comp->_parameters.isradial)
  SIG_MESSAGE("[_slit_display] component slit=Slit() DISPLAY [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//optics/Slit.comp:107]");

  printf("MCDISPLAY: component %s\n", _comp->_name);
  
  if (is_unset(radius)) {
    double xw, yh;
    xw = (xmax - xmin)/2.0;
    yh = (ymax - ymin)/2.0;
    multiline(3, xmin-xw, (double)ymax, 0.0,
              (double)xmin, (double)ymax, 0.0,
              (double)xmin, ymax+yh, 0.0);
    multiline(3, xmax+xw, (double)ymax, 0.0,
              (double)xmax, (double)ymax, 0.0,
              (double)xmax, ymax+yh, 0.0);
    multiline(3, xmin-xw, (double)ymin, 0.0,
              (double)xmin, (double)ymin, 0.0,
              (double)xmin, ymin-yh, 0.0);
    multiline(3, xmax+xw, (double)ymin, 0.0,
              (double)xmax, (double)ymin, 0.0,
              (double)xmax, ymin-yh, 0.0);
  } else {
    circle("xy",0,0,0,radius);
  }
  #undef xmin
  #undef xmax
  #undef ymin
  #undef ymax
  #undef radius
  #undef xwidth
  #undef yheight
  #undef isradial
  return(_comp);
} /* class_Slit_display */

_class_E_monitor *class_E_monitor_display(_class_E_monitor *_comp
) {
  #define nE (_comp->_parameters.nE)
  #define filename (_comp->_parameters.filename)
  #define xmin (_comp->_parameters.xmin)
  #define xmax (_comp->_parameters.xmax)
  #define ymin (_comp->_parameters.ymin)
  #define ymax (_comp->_parameters.ymax)
  #define nowritefile (_comp->_parameters.nowritefile)
  #define xwidth (_comp->_parameters.xwidth)
  #define yheight (_comp->_parameters.yheight)
  #define Emin (_comp->_parameters.Emin)
  #define Emax (_comp->_parameters.Emax)
  #define restore_neutron (_comp->_parameters.restore_neutron)
  #define E_N (_comp->_parameters.E_N)
  #define E_p (_comp->_parameters.E_p)
  #define E_p2 (_comp->_parameters.E_p2)
  #define S_p (_comp->_parameters.S_p)
  #define S_pE (_comp->_parameters.S_pE)
  #define S_pE2 (_comp->_parameters.S_pE2)
  SIG_MESSAGE("[_Emon_before_sampel_display] component Emon_before_sampel=E_monitor() DISPLAY [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//monitors/E_monitor.comp:153]");

  printf("MCDISPLAY: component %s\n", _comp->_name);

  multiline(5, (double)xmin, (double)ymin, 0.0,
               (double)xmax, (double)ymin, 0.0,
               (double)xmax, (double)ymax, 0.0,
               (double)xmin, (double)ymax, 0.0,
               (double)xmin, (double)ymin, 0.0);
  #undef nE
  #undef filename
  #undef xmin
  #undef xmax
  #undef ymin
  #undef ymax
  #undef nowritefile
  #undef xwidth
  #undef yheight
  #undef Emin
  #undef Emax
  #undef restore_neutron
  #undef E_N
  #undef E_p
  #undef E_p2
  #undef S_p
  #undef S_pE
  #undef S_pE2
  return(_comp);
} /* class_E_monitor_display */

_class_Union_master *class_Union_master_display(_class_Union_master *_comp
) {
  #define verbal (_comp->_parameters.verbal)
  #define list_verbal (_comp->_parameters.list_verbal)
  #define finally_verbal (_comp->_parameters.finally_verbal)
  #define allow_inside_start (_comp->_parameters.allow_inside_start)
  #define enable_tagging (_comp->_parameters.enable_tagging)
  #define history_limit (_comp->_parameters.history_limit)
  #define enable_conditionals (_comp->_parameters.enable_conditionals)
  #define inherit_number_of_scattering_events (_comp->_parameters.inherit_number_of_scattering_events)
  #define init (_comp->_parameters.init)
  #define global_positions_to_transform_list_master (_comp->_parameters.global_positions_to_transform_list_master)
  #define global_rotations_to_transform_list_master (_comp->_parameters.global_rotations_to_transform_list_master)
  #define global_process_list_master (_comp->_parameters.global_process_list_master)
  #define global_material_list_master (_comp->_parameters.global_material_list_master)
  #define global_geometry_list_master (_comp->_parameters.global_geometry_list_master)
  #define global_all_volume_logger_list_master (_comp->_parameters.global_all_volume_logger_list_master)
  #define global_specific_volumes_logger_list_master (_comp->_parameters.global_specific_volumes_logger_list_master)
  #define global_all_volume_abs_logger_list_master (_comp->_parameters.global_all_volume_abs_logger_list_master)
  #define global_specific_volumes_abs_logger_list_master (_comp->_parameters.global_specific_volumes_abs_logger_list_master)
  #define global_tagging_conditional_list_master (_comp->_parameters.global_tagging_conditional_list_master)
  #define global_master_list_master (_comp->_parameters.global_master_list_master)
  #define starting_volume_warning (_comp->_parameters.starting_volume_warning)
  #define global_master_element (_comp->_parameters.global_master_element)
  #define this_global_master_index (_comp->_parameters.this_global_master_index)
  #define previous_master_index (_comp->_parameters.previous_master_index)
  #define geometry_list_index (_comp->_parameters.geometry_list_index)
  #define intersection_time_table (_comp->_parameters.intersection_time_table)
  #define Volumes (_comp->_parameters.Volumes)
  #define Geometries (_comp->_parameters.Geometries)
  #define Volume_copies (_comp->_parameters.Volume_copies)
  #define starting_lists (_comp->_parameters.starting_lists)
  #define Volume_copies_allocated (_comp->_parameters.Volume_copies_allocated)
  #define r (_comp->_parameters.r)
  #define r_start (_comp->_parameters.r_start)
  #define v (_comp->_parameters.v)
  #define error_msg (_comp->_parameters.error_msg)
  #define component_error_msg (_comp->_parameters.component_error_msg)
  #define string_output (_comp->_parameters.string_output)
  #define number_of_volumes (_comp->_parameters.number_of_volumes)
  #define volume_index (_comp->_parameters.volume_index)
  #define process_index (_comp->_parameters.process_index)
  #define iterator (_comp->_parameters.iterator)
  #define solutions (_comp->_parameters.solutions)
  #define max_number_of_processes (_comp->_parameters.max_number_of_processes)
  #define limit (_comp->_parameters.limit)
  #define solution (_comp->_parameters.solution)
  #define min_solution (_comp->_parameters.min_solution)
  #define min_volume (_comp->_parameters.min_volume)
  #define time_found (_comp->_parameters.time_found)
  #define intersection_time (_comp->_parameters.intersection_time)
  #define min_intersection_time (_comp->_parameters.min_intersection_time)
  #define process (_comp->_parameters.process)
  #define process_start (_comp->_parameters.process_start)
  #define my_trace (_comp->_parameters.my_trace)
  #define p_my_trace (_comp->_parameters.p_my_trace)
  #define my_trace_fraction_control (_comp->_parameters.my_trace_fraction_control)
  #define k (_comp->_parameters.k)
  #define k_new (_comp->_parameters.k_new)
  #define k_old (_comp->_parameters.k_old)
  #define k_rotated (_comp->_parameters.k_rotated)
  #define v_length (_comp->_parameters.v_length)
  #define my_sum (_comp->_parameters.my_sum)
  #define my_sum_plus_abs (_comp->_parameters.my_sum_plus_abs)
  #define culmative_probability (_comp->_parameters.culmative_probability)
  #define mc_prop (_comp->_parameters.mc_prop)
  #define time_to_scattering (_comp->_parameters.time_to_scattering)
  #define length_to_scattering (_comp->_parameters.length_to_scattering)
  #define length_to_boundery (_comp->_parameters.length_to_boundery)
  #define time_to_boundery (_comp->_parameters.time_to_boundery)
  #define selected_process (_comp->_parameters.selected_process)
  #define scattering_event (_comp->_parameters.scattering_event)
  #define time_propagated_without_scattering (_comp->_parameters.time_propagated_without_scattering)
  #define a_next_volume_found (_comp->_parameters.a_next_volume_found)
  #define next_volume (_comp->_parameters.next_volume)
  #define next_volume_priority (_comp->_parameters.next_volume_priority)
  #define done (_comp->_parameters.done)
  #define current_volume (_comp->_parameters.current_volume)
  #define ray_sucseeded (_comp->_parameters.ray_sucseeded)
  #define number_of_solutions (_comp->_parameters.number_of_solutions)
  #define number_of_solutions_static (_comp->_parameters.number_of_solutions_static)
  #define check (_comp->_parameters.check)
  #define start (_comp->_parameters.start)
  #define intersection_with_children (_comp->_parameters.intersection_with_children)
  #define geometry_output (_comp->_parameters.geometry_output)
  #define tree_next_volume (_comp->_parameters.tree_next_volume)
  #define pre_allocated1 (_comp->_parameters.pre_allocated1)
  #define pre_allocated2 (_comp->_parameters.pre_allocated2)
  #define pre_allocated3 (_comp->_parameters.pre_allocated3)
  #define ray_position (_comp->_parameters.ray_position)
  #define ray_velocity (_comp->_parameters.ray_velocity)
  #define ray_velocity_rotated (_comp->_parameters.ray_velocity_rotated)
  #define ray_velocity_final (_comp->_parameters.ray_velocity_final)
  #define wavevector (_comp->_parameters.wavevector)
  #define wavevector_rotated (_comp->_parameters.wavevector_rotated)
  #define volume_0_found (_comp->_parameters.volume_0_found)
  #define scattered_flag (_comp->_parameters.scattered_flag)
  #define scattered_flag_VP (_comp->_parameters.scattered_flag_VP)
  #define master_transposed_rotation_matrix (_comp->_parameters.master_transposed_rotation_matrix)
  #define temp_rotation_matrix (_comp->_parameters.temp_rotation_matrix)
  #define temp_transpose_rotation_matrix (_comp->_parameters.temp_transpose_rotation_matrix)
  #define non_rotated_position (_comp->_parameters.non_rotated_position)
  #define rotated_position (_comp->_parameters.rotated_position)
  #define non_isotropic_found (_comp->_parameters.non_isotropic_found)
  #define master_tagging_node_list (_comp->_parameters.master_tagging_node_list)
  #define current_tagging_node (_comp->_parameters.current_tagging_node)
  #define tagging_leaf_counter (_comp->_parameters.tagging_leaf_counter)
  #define stop_tagging_ray (_comp->_parameters.stop_tagging_ray)
  #define stop_creating_nodes (_comp->_parameters.stop_creating_nodes)
  #define number_of_scattering_events (_comp->_parameters.number_of_scattering_events)
  #define real_transmission_probability (_comp->_parameters.real_transmission_probability)
  #define mc_transmission_probability (_comp->_parameters.mc_transmission_probability)
  #define number_of_process_interacts_set (_comp->_parameters.number_of_process_interacts_set)
  #define index_of_lacking_process (_comp->_parameters.index_of_lacking_process)
  #define total_process_interact (_comp->_parameters.total_process_interact)
  #define geometry_component_index_list (_comp->_parameters.geometry_component_index_list)
  #define mask_volume_index_list (_comp->_parameters.mask_volume_index_list)
  #define number_of_masks (_comp->_parameters.number_of_masks)
  #define number_of_masked_volumes (_comp->_parameters.number_of_masked_volumes)
  #define mask_status_list (_comp->_parameters.mask_status_list)
  #define current_mask_intersect_list_status (_comp->_parameters.current_mask_intersect_list_status)
  #define mask_index_main (_comp->_parameters.mask_index_main)
  #define mask_iterator (_comp->_parameters.mask_iterator)
  #define mask_start (_comp->_parameters.mask_start)
  #define mask_check (_comp->_parameters.mask_check)
  #define need_to_run_within_which_volume (_comp->_parameters.need_to_run_within_which_volume)
  #define number_of_processes_array (_comp->_parameters.number_of_processes_array)
  #define p_old (_comp->_parameters.p_old)
  #define log_index (_comp->_parameters.log_index)
  #define conditional_status (_comp->_parameters.conditional_status)
  #define this_logger (_comp->_parameters.this_logger)
  #define this_abs_logger (_comp->_parameters.this_abs_logger)
  #define tagging_conditional_list (_comp->_parameters.tagging_conditional_list)
  #define logger_conditional_extend_array (_comp->_parameters.logger_conditional_extend_array)
  #define abs_logger_conditional_extend_array (_comp->_parameters.abs_logger_conditional_extend_array)
  #define max_conditional_extend_index (_comp->_parameters.max_conditional_extend_index)
  #define tagging_conditional_extend (_comp->_parameters.tagging_conditional_extend)
  #define free_tagging_conditioanl_list (_comp->_parameters.free_tagging_conditioanl_list)
  #define safty_distance (_comp->_parameters.safty_distance)
  #define safty_distance2 (_comp->_parameters.safty_distance2)
  #define temporary_focus_data (_comp->_parameters.temporary_focus_data)
  #define focus_data_index (_comp->_parameters.focus_data_index)
  #define r_old (_comp->_parameters.r_old)
  #define initial_weight (_comp->_parameters.initial_weight)
  #define abs_weight_factor (_comp->_parameters.abs_weight_factor)
  #define time_old (_comp->_parameters.time_old)
  #define absorption_index (_comp->_parameters.absorption_index)
  #define abs_weight_factor_set (_comp->_parameters.abs_weight_factor_set)
  #define my_abs (_comp->_parameters.my_abs)
  #define absorption_event_data (_comp->_parameters.absorption_event_data)
  #define abs_position (_comp->_parameters.abs_position)
  #define transformed_abs_position (_comp->_parameters.transformed_abs_position)
  #define t_abs_propagation (_comp->_parameters.t_abs_propagation)
  #define abs_distance (_comp->_parameters.abs_distance)
  #define abs_max_length (_comp->_parameters.abs_max_length)
  SIG_MESSAGE("[_Master_display] component Master=Union_master() DISPLAY [/Applications/McStas-3.3.app/Contents/Resources/mcstas/3.3//union/Union_master.comp:2293]");

  printf("MCDISPLAY: component %s\n", _comp->_name);
  // mcdisplay is handled in the component files for each geometry and called here. The line function is only available in this section, and not through functions,
  //   so all the lines to be drawn for each volume are collected in a structure that is then drawn here.
  magnify("xyz");
  struct lines_to_draw lines_to_draw_master;
  for (volume_index=1; volume_index<number_of_volumes; volume_index++) {
        if (Volumes[volume_index]->geometry.visualization_on == 1) {
            lines_to_draw_master.number_of_lines = 0;
            
            Volumes[volume_index]->geometry.mcdisplay_function(&lines_to_draw_master,volume_index,Geometries,number_of_volumes);
            
            for (iterator = 0;iterator<lines_to_draw_master.number_of_lines;iterator++) {
               if (lines_to_draw_master.lines[iterator].number_of_dashes == 1) {
                 line(lines_to_draw_master.lines[iterator].point1.x, lines_to_draw_master.lines[iterator].point1.y, lines_to_draw_master.lines[iterator].point1.z, lines_to_draw_master.lines[iterator].point2.x, lines_to_draw_master.lines[iterator].point2.y, lines_to_draw_master.lines[iterator].point2.z);
               }
               else {
                 dashed_line(lines_to_draw_master.lines[iterator].point1.x, lines_to_draw_master.lines[iterator].point1.y, lines_to_draw_master.lines[iterator].point1.z, lines_to_draw_master.lines[iterator].point2.x, lines_to_draw_master.lines[iterator].point2.y, lines_to_draw_master.lines[iterator].point2.z, lines_to_draw_master.lines[iterator].number_of_dashes);
               }
            }
            if (lines_to_draw_master.number_of_lines>0) free(lines_to_draw_master.lines);
        }
   }

  #undef verbal
  #undef list_verbal
  #undef finally_verbal
  #undef allow_inside_start
  #undef enable_tagging
  #undef history_limit
  #undef enable_conditionals
  #undef inherit_number_of_scattering_events
  #undef init
  #undef global_positions_to_transform_list_master
  #undef global_rotations_to_transform_list_master
  #undef global_process_list_master
  #undef global_material_list_master
  #undef global_geometry_list_master
  #undef global_all_volume_logger_list_master
  #undef global_specific_volumes_logger_list_master
  #undef global_all_volume_abs_logger_list_master
  #undef global_specific_volumes_abs_logger_list_master
  #undef global_tagging_conditional_list_master
  #undef global_master_list_master
  #undef starting_volume_warning
  #undef global_master_element
  #undef this_global_master_index
  #undef previous_master_index
  #undef geometry_list_index
  #undef intersection_time_table
  #undef Volumes
  #undef Geometries
  #undef Volume_copies
  #undef starting_lists
  #undef Volume_copies_allocated
  #undef r
  #undef r_start
  #undef v
  #undef error_msg
  #undef component_error_msg
  #undef string_output
  #undef number_of_volumes
  #undef volume_index
  #undef process_index
  #undef iterator
  #undef solutions
  #undef max_number_of_processes
  #undef limit
  #undef solution
  #undef min_solution
  #undef min_volume
  #undef time_found
  #undef intersection_time
  #undef min_intersection_time
  #undef process
  #undef process_start
  #undef my_trace
  #undef p_my_trace
  #undef my_trace_fraction_control
  #undef k
  #undef k_new
  #undef k_old
  #undef k_rotated
  #undef v_length
  #undef my_sum
  #undef my_sum_plus_abs
  #undef culmative_probability
  #undef mc_prop
  #undef time_to_scattering
  #undef length_to_scattering
  #undef length_to_boundery
  #undef time_to_boundery
  #undef selected_process
  #undef scattering_event
  #undef time_propagated_without_scattering
  #undef a_next_volume_found
  #undef next_volume
  #undef next_volume_priority
  #undef done
  #undef current_volume
  #undef ray_sucseeded
  #undef number_of_solutions
  #undef number_of_solutions_static
  #undef check
  #undef start
  #undef intersection_with_children
  #undef geometry_output
  #undef tree_next_volume
  #undef pre_allocated1
  #undef pre_allocated2
  #undef pre_allocated3
  #undef ray_position
  #undef ray_velocity
  #undef ray_velocity_rotated
  #undef ray_velocity_final
  #undef wavevector
  #undef wavevector_rotated
  #undef volume_0_found
  #undef scattered_flag
  #undef scattered_flag_VP
  #undef master_transposed_rotation_matrix
  #undef temp_rotation_matrix
  #undef temp_transpose_rotation_matrix
  #undef non_rotated_position
  #undef rotated_position
  #undef non_isotropic_found
  #undef master_tagging_node_list
  #undef current_tagging_node
  #undef tagging_leaf_counter
  #undef stop_tagging_ray
  #undef stop_creating_nodes
  #undef number_of_scattering_events
  #undef real_transmission_probability
  #undef mc_transmission_probability
  #undef number_of_process_interacts_set
  #undef index_of_lacking_process
  #undef total_process_interact
  #undef geometry_component_index_list
  #undef mask_volume_index_list
  #undef number_of_masks
  #undef number_of_masked_volumes
  #undef mask_status_list
  #undef current_mask_intersect_list_status
  #undef mask_index_main
  #undef mask_iterator
  #undef mask_start
  #undef mask_check
  #undef need_to_run_within_which_volume
  #undef number_of_processes_array
  #undef p_old
  #undef log_index
  #undef conditional_status
  #undef this_logger
  #undef this_abs_logger
  #undef tagging_conditional_list
  #undef logger_conditional_extend_array
  #undef abs_logger_conditional_extend_array
  #undef max_conditional_extend_index
  #undef tagging_conditional_extend
  #undef free_tagging_conditioanl_list
  #undef safty_distance
  #undef safty_distance2
  #undef temporary_focus_data
  #undef focus_data_index
  #undef r_old
  #undef initial_weight
  #undef abs_weight_factor
  #undef time_old
  #undef absorption_index
  #undef abs_weight_factor_set
  #undef my_abs
  #undef absorption_event_data
  #undef abs_position
  #undef transformed_abs_position
  #undef t_abs_propagation
  #undef abs_distance
  #undef abs_max_length
  return(_comp);
} /* class_Union_master_display */


  #undef magnify
  #undef line
  #undef dashed_line
  #undef multiline
  #undef rectangle
  #undef box
  #undef circle
  #undef cylinder
  #undef sphere

int display(void) { /* called by mccode_main for template_body_simple:DISPLAY */
  printf("MCDISPLAY: start\n");

  /* call iteratively all components DISPLAY */
  class_Progress_bar_display(&_origin_var);

  class_Source_Maxwell_3_display(&_source_maxwell_3_var);

  class_Guide_display(&_guide_var);

  class_PSD_monitor_display(&_PSD_before_monochromator_var);

  class_Arm_display(&_a1_var);

  class_Monochromator_flat_display(&_monochromator_flat1_var);

  class_Monochromator_flat_display(&_monochromator_flat2_var);

  class_Monochromator_flat_display(&_monochromator_flat3_var);

  class_Monochromator_flat_display(&_monochromator_flat4_var);

  class_Monochromator_flat_display(&_monochromator_flat5_var);

  class_Arm_display(&_a2_var);

  class_PSD_monitor_display(&_PSD_after_monochromator_var);

  class_Collimator_linear_display(&_collimator_linear_var);

  class_Slit_display(&_slit_var);

  class_PSD_monitor_display(&_PSD_before_sampel_var);

  class_E_monitor_display(&_Emon_before_sampel_var);

  class_Arm_display(&_a3_var);

  class_Arm_display(&_a4_var);

  class_Arm_display(&_a5_var);

  class_Arm_display(&_sample_position_var);















  class_Union_master_display(&_Master_var);


  class_Collimator_linear_display(&_collimator_linear_2_var);

  class_PSD_monitor_display(&_PSD_after_sampel_var);

  class_Monochromator_flat_display(&_monochromator_flat6_var);

  class_Arm_display(&_a6_var);

  class_PSD_monitor_display(&_Detector_var);

  printf("MCDISPLAY: end\n");

  return(0);
} /* display */

void* _getvar_parameters(char* compname)
/* enables settings parameters based use of the GETPAR macro */
{
  #ifdef OPENACC
    #define strcmp(a,b) str_comp(a,b)
  #endif
  if (!strcmp(compname, "origin")) return (void *) &(_origin_var._parameters);
  if (!strcmp(compname, "source_maxwell_3")) return (void *) &(_source_maxwell_3_var._parameters);
  if (!strcmp(compname, "guide")) return (void *) &(_guide_var._parameters);
  if (!strcmp(compname, "PSD_before_monochromator")) return (void *) &(_PSD_before_monochromator_var._parameters);
  if (!strcmp(compname, "a1")) return (void *) &(_a1_var._parameters);
  if (!strcmp(compname, "monochromator_flat1")) return (void *) &(_monochromator_flat1_var._parameters);
  if (!strcmp(compname, "monochromator_flat2")) return (void *) &(_monochromator_flat2_var._parameters);
  if (!strcmp(compname, "monochromator_flat3")) return (void *) &(_monochromator_flat3_var._parameters);
  if (!strcmp(compname, "monochromator_flat4")) return (void *) &(_monochromator_flat4_var._parameters);
  if (!strcmp(compname, "monochromator_flat5")) return (void *) &(_monochromator_flat5_var._parameters);
  if (!strcmp(compname, "a2")) return (void *) &(_a2_var._parameters);
  if (!strcmp(compname, "PSD_after_monochromator")) return (void *) &(_PSD_after_monochromator_var._parameters);
  if (!strcmp(compname, "collimator_linear")) return (void *) &(_collimator_linear_var._parameters);
  if (!strcmp(compname, "slit")) return (void *) &(_slit_var._parameters);
  if (!strcmp(compname, "PSD_before_sampel")) return (void *) &(_PSD_before_sampel_var._parameters);
  if (!strcmp(compname, "Emon_before_sampel")) return (void *) &(_Emon_before_sampel_var._parameters);
  if (!strcmp(compname, "a3")) return (void *) &(_a3_var._parameters);
  if (!strcmp(compname, "a4")) return (void *) &(_a4_var._parameters);
  if (!strcmp(compname, "a5")) return (void *) &(_a5_var._parameters);
  if (!strcmp(compname, "sample_position")) return (void *) &(_sample_position_var._parameters);
  if (!strcmp(compname, "init")) return (void *) &(_init_var._parameters);
  if (!strcmp(compname, "Vanadium_incoherent_Al")) return (void *) &(_Vanadium_incoherent_Al_var._parameters);
  if (!strcmp(compname, "Al_Powder")) return (void *) &(_Al_Powder_var._parameters);
  if (!strcmp(compname, "Al")) return (void *) &(_Al_var._parameters);
  if (!strcmp(compname, "Vanadium_incoherent_Cu")) return (void *) &(_Vanadium_incoherent_Cu_var._parameters);
  if (!strcmp(compname, "Vanadium_incoherent_MnF2")) return (void *) &(_Vanadium_incoherent_MnF2_var._parameters);
  if (!strcmp(compname, "MnF2_critical")) return (void *) &(_MnF2_critical_var._parameters);
  if (!strcmp(compname, "MnF2_single_crystal")) return (void *) &(_MnF2_single_crystal_var._parameters);
  if (!strcmp(compname, "MnF2_single_crystal_magn")) return (void *) &(_MnF2_single_crystal_magn_var._parameters);
  if (!strcmp(compname, "MnF2")) return (void *) &(_MnF2_var._parameters);
  if (!strcmp(compname, "union_sample")) return (void *) &(_union_sample_var._parameters);
  if (!strcmp(compname, "union_stick")) return (void *) &(_union_stick_var._parameters);
  if (!strcmp(compname, "cryostat_wall")) return (void *) &(_cryostat_wall_var._parameters);
  if (!strcmp(compname, "cryostat_vacuum")) return (void *) &(_cryostat_vacuum_var._parameters);
  if (!strcmp(compname, "Master")) return (void *) &(_Master_var._parameters);
  if (!strcmp(compname, "stop")) return (void *) &(_stop_var._parameters);
  if (!strcmp(compname, "collimator_linear_2")) return (void *) &(_collimator_linear_2_var._parameters);
  if (!strcmp(compname, "PSD_after_sampel")) return (void *) &(_PSD_after_sampel_var._parameters);
  if (!strcmp(compname, "monochromator_flat6")) return (void *) &(_monochromator_flat6_var._parameters);
  if (!strcmp(compname, "a6")) return (void *) &(_a6_var._parameters);
  if (!strcmp(compname, "Detector")) return (void *) &(_Detector_var._parameters);
  return 0;
}

void* _get_particle_var(char *token, _class_particle *p)
/* enables setpars based use of GET_PARTICLE_DVAR macro and similar */
{
  return 0;
}

int _getcomp_index(char* compname)
/* Enables retrieving the component position & rotation when the index is not known.
 * Component indexing into MACROS, e.g., POS_A_COMP_INDEX, are 1-based! */
{
  if (!strcmp(compname, "origin")) return 1;
  if (!strcmp(compname, "source_maxwell_3")) return 2;
  if (!strcmp(compname, "guide")) return 3;
  if (!strcmp(compname, "PSD_before_monochromator")) return 4;
  if (!strcmp(compname, "a1")) return 5;
  if (!strcmp(compname, "monochromator_flat1")) return 6;
  if (!strcmp(compname, "monochromator_flat2")) return 7;
  if (!strcmp(compname, "monochromator_flat3")) return 8;
  if (!strcmp(compname, "monochromator_flat4")) return 9;
  if (!strcmp(compname, "monochromator_flat5")) return 10;
  if (!strcmp(compname, "a2")) return 11;
  if (!strcmp(compname, "PSD_after_monochromator")) return 12;
  if (!strcmp(compname, "collimator_linear")) return 13;
  if (!strcmp(compname, "slit")) return 14;
  if (!strcmp(compname, "PSD_before_sampel")) return 15;
  if (!strcmp(compname, "Emon_before_sampel")) return 16;
  if (!strcmp(compname, "a3")) return 17;
  if (!strcmp(compname, "a4")) return 18;
  if (!strcmp(compname, "a5")) return 19;
  if (!strcmp(compname, "sample_position")) return 20;
  if (!strcmp(compname, "init")) return 21;
  if (!strcmp(compname, "Vanadium_incoherent_Al")) return 22;
  if (!strcmp(compname, "Al_Powder")) return 23;
  if (!strcmp(compname, "Al")) return 24;
  if (!strcmp(compname, "Vanadium_incoherent_Cu")) return 25;
  if (!strcmp(compname, "Vanadium_incoherent_MnF2")) return 26;
  if (!strcmp(compname, "MnF2_critical")) return 27;
  if (!strcmp(compname, "MnF2_single_crystal")) return 28;
  if (!strcmp(compname, "MnF2_single_crystal_magn")) return 29;
  if (!strcmp(compname, "MnF2")) return 30;
  if (!strcmp(compname, "union_sample")) return 31;
  if (!strcmp(compname, "union_stick")) return 32;
  if (!strcmp(compname, "cryostat_wall")) return 33;
  if (!strcmp(compname, "cryostat_vacuum")) return 34;
  if (!strcmp(compname, "Master")) return 35;
  if (!strcmp(compname, "stop")) return 36;
  if (!strcmp(compname, "collimator_linear_2")) return 37;
  if (!strcmp(compname, "PSD_after_sampel")) return 38;
  if (!strcmp(compname, "monochromator_flat6")) return 39;
  if (!strcmp(compname, "a6")) return 40;
  if (!strcmp(compname, "Detector")) return 41;
  return -1;
}

/* embedding file "mccode_main.c" */

/*******************************************************************************
* mccode_main: McCode main() function.
*******************************************************************************/
int mccode_main(int argc, char *argv[])
{
  /*  double run_num = 0; */
  time_t  t;
  clock_t ct;

#ifdef USE_MPI
  char mpi_node_name[MPI_MAX_PROCESSOR_NAME];
  int  mpi_node_name_len;
#endif /* USE_MPI */

#ifdef MAC
  argc = ccommand(&argv);
#endif

#ifdef USE_MPI
  MPI_Init(&argc,&argv);
  MPI_Comm_size(MPI_COMM_WORLD, &mpi_node_count); /* get number of nodes */
  MPI_Comm_rank(MPI_COMM_WORLD, &mpi_node_rank);
  MPI_Comm_set_name(MPI_COMM_WORLD, instrument_name);
  MPI_Get_processor_name(mpi_node_name, &mpi_node_name_len);
#endif /* USE_MPI */

  ct = clock();

  // device and host functional RNG seed
  struct timeval tm;
  gettimeofday(&tm, NULL);
  mcseed = (long) tm.tv_sec*1000000 + tm.tv_usec;
  mcstartdate = (long)tm.tv_sec;  /* set start date before parsing options and creating sim file */
  // init global _particle.randstate for random number use
  // during init(), finally() and display(). NOTE: during trace, a local
  // "_particle" variable is present and thus used instead.
  srandom(_hash(mcseed-1));

#ifdef USE_MPI
  /* *** print number of nodes *********************************************** */
  if (mpi_node_count > 1) {
    MPI_MASTER(
    printf("Simulation '%s' (%s): running on %i nodes (master is '%s', MPI version %i.%i).\n",
      instrument_name, instrument_source, mpi_node_count, mpi_node_name, MPI_VERSION, MPI_SUBVERSION);
    );
    /* share the same seed, then adapt random seed for each node */
    MPI_Bcast(&mcseed, 1, MPI_LONG, 0, MPI_COMM_WORLD); /* root sends its seed to slaves */
    mcseed += mpi_node_rank; /* make sure we use different seeds per noe */
  }
#endif /* USE_MPI */

#ifdef OPENACC
#ifdef USE_MPI
  int num_devices = acc_get_num_devices(acc_device_nvidia);
  if(num_devices>0){
    int my_device = mpi_node_rank % num_devices;
    acc_set_device_num( my_device, acc_device_nvidia );
    printf("Have found %d GPU devices on rank %d. Will use device %d.\n", num_devices, mpi_node_rank, my_device);
  }else{
    printf("There was an issue probing acc_get_num_devices, fallback to host\n");
    acc_set_device_type( acc_device_host );
  }
#endif
#endif

  /* *** parse options ******************************************************* */
  SIG_MESSAGE("[" __FILE__ "] main START");
  mcformat = getenv(FLAVOR_UPPER "_FORMAT") ?
             getenv(FLAVOR_UPPER "_FORMAT") : FLAVOR_UPPER;
  instrument_exe = argv[0]; /* store the executable path */
  /* read simulation parameters and options */
  mcparseoptions(argc, argv); /* sets output dir and format */


#ifdef USE_MPI
  if (mpi_node_count > 1) {
    /* share the same seed, then adapt random seed for each node */
    MPI_Bcast(&mcseed, 1, MPI_LONG, 0, MPI_COMM_WORLD); /* root sends its seed to slaves */
    mcseed += mpi_node_rank; /* make sure we use different seeds per node */
  }
#endif


/* *** install sig handler, but only once !! after parameters parsing ******* */
#ifndef NOSIGNALS
#ifdef SIGQUIT
  if (signal( SIGQUIT ,sighandler) == SIG_IGN)
    signal( SIGQUIT,SIG_IGN);   /* quit (ASCII FS) */
#endif
#ifdef SIGABRT
  if (signal( SIGABRT ,sighandler) == SIG_IGN)
    signal( SIGABRT,SIG_IGN);   /* used by abort, replace SIGIOT in the future */
#endif
#ifdef SIGTERM
  if (signal( SIGTERM ,sighandler) == SIG_IGN)
    signal( SIGTERM,SIG_IGN);   /* software termination signal from kill */
#endif
#ifdef SIGUSR1
  if (signal( SIGUSR1 ,sighandler) == SIG_IGN)
    signal( SIGUSR1,SIG_IGN);   /* display simulation status */
#endif
#ifdef SIGUSR2
  if (signal( SIGUSR2 ,sighandler) == SIG_IGN)
    signal( SIGUSR2,SIG_IGN);
#endif
#ifdef SIGHUP
  if (signal( SIGHUP ,sighandler) == SIG_IGN)
    signal( SIGHUP,SIG_IGN);
#endif
#ifdef SIGILL
  if (signal( SIGILL ,sighandler) == SIG_IGN)
    signal( SIGILL,SIG_IGN);    /* illegal instruction (not reset when caught) */
#endif
#ifdef SIGFPE
  if (signal( SIGFPE ,sighandler) == SIG_IGN)
    signal( SIGSEGV,SIG_IGN);    /* floating point exception */
#endif
#ifdef SIGBUS
  if (signal( SIGBUS ,sighandler) == SIG_IGN)
    signal( SIGSEGV,SIG_IGN);    /* bus error */
#endif
#ifdef SIGSEGV
  if (signal( SIGSEGV ,sighandler) == SIG_IGN)
    signal( SIGSEGV,SIG_IGN);   /* segmentation violation */
#endif
#endif /* !NOSIGNALS */


  // init executed by master/host
  siminfo_init(NULL); /* open SIM */
  SIG_MESSAGE("[" __FILE__ "] main INITIALISE");
  init();


#ifndef NOSIGNALS
#ifdef SIGINT
  if (signal( SIGINT ,sighandler) == SIG_IGN)
    signal( SIGINT,SIG_IGN);    /* interrupt (rubout) only after INIT */
#endif
#endif /* !NOSIGNALS */

/* ================ main particle generation/propagation loop ================ */
#ifdef USE_MPI
  /* sliced Ncount on each MPI node */
  mcncount = mpi_node_count > 1 ?
    floor(mcncount / mpi_node_count) :
    mcncount; /* number of rays per node */
#endif

// MT specific init, note that per-ray init is empty
#if RNG_ALG == 2
  mt_srandom(mcseed);
#endif


// main raytrace work loop
#ifndef FUNNEL
  // legacy version
  raytrace_all(mcncount, mcseed);
#else
  MPI_MASTER(
  // "funneled" version in which propagation is more parallelizable
  printf("\nNOTE: CPU COMPONENT grammar activated:\n 1) \"FUNNEL\" raytrace algorithm enabled.\n 2) Any SPLIT's are dynamically allocated based on available buffer size. \n");
	     );
  raytrace_all_funnel(mcncount, mcseed);
#endif


#ifdef USE_MPI
 /* merge run_num from MPI nodes */
  if (mpi_node_count > 1) {
  double mcrun_num_double = (double)mcrun_num;
  mc_MPI_Sum(&mcrun_num_double, 1);
  mcrun_num = (unsigned long long)mcrun_num_double;
  }
#endif


  // save/finally executed by master node/thread/host
  finally();


#ifdef USE_MPI
  MPI_Finalize();
#endif /* USE_MPI */


  return 0;
} /* mccode_main */
/* End of file "mccode_main.c". */

/* end of generated C code ./RITA-2-Kristine-instrument.c */
