/*=============================================================================*
 * HaloTracker
 *
 *  - reads in one particular *_particles files (and the corresponding *_halos file)
 *
 *  - uses the particle id's of a specified halo and locates these particles
 *    in a series of user defined AMIGA binaries
 *
 *  - based upon the bdm_halos.c routines (cut-and-paste into HaloTracker.c though!)
 *    the tracker re-calculates the physical properties of that halo using those particle id's
 *
 * useful tips:
 *  - TRK_MINPART should be smaller than the MINPART value used for the actual AHF/BDM analysis
 *
 *=============================================================================*/
/*
 * Extensions by Kristin Warnick, AIP, Nov. 2005:
 *	- option TRACKER_DEBRIS: save unbound particles and their properties
 */
/* Added in Jan. & Feb. 2006:
 *	- write distance, energy, ang. mom. for every particle (after its id)
 *	- use x_fac for conversion of dist
 *	- add output of the mass of the particle, option ADDMASS
 *	- sort particles after get_centre (update distances), option NEWDIST
 *	- correct for periodic boundary conditions in get_centre, 
 *		option PERIODCENTER
 * NOTE: Do NOT use TRACKER_DEBRIS option for host too lightly: too much data would be written!
 *
 */
/*
 * Further changes (Kristin Riebe, AIP, early 2009):
 * 	- 21.01.2009: allow reading of multiple gadget-files
 *	  		=> use libio's-input() and read_gadget(), 
 *	  		remove read_gadget() from this file
 *			=> seems to work now! :-) 
 *	- adapt to new particles-file format (first line = number of halos!)
 * 	- 27.01.2009: remove options ADDMASS and SAVE_DISKSPACE, adapt to use routines
 *		from ahf-library, i.e.:
 *			- use the same halo, profile and particle structures! 
 *			- Attention: AHF's rem_unbound cannot (yet) deal with debris particles!!
 *	- 28.01.2009: use init_... routines for initializing the global structures,
 *			similar to startrun.c
 *	- 06.02.2009: working with ahf-routines now, doing the same analysis,
 *			=> gives similar results! :-)
 *			- BUT: non-converged radii are not yet marked properly
 *			       debris-option still not fully implemented 
 *				(needs changes in ahf_halos.c!) 
 *			- profiles are computed in the same way as for AHF, 
 *				NOT going out until the last bound particle
 *	- 08.02.2009: TRACKER_DEBRIS works again! :-) (yet still not for gas&stars)
 */ 

#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>

/*=========================================================================
* AMIGA stuff
*=========================================================================*/
#include "../src/param.h"
#include "../src/tdef.h"
#include "../src/common.c"

#include "../src/libutility/utility.h"
#include "../src/libahf/ahf_halos.h"

#include "../src/libgrids/grids.h"
#include "../src/libparticles/particles.h"
#include "../src/libio_serial/io_serial.h"


/* TRACKER_DEBRIS = flag for tracking and writing bound+unbound particles 
 * 	Please do not define it here but within the main Makefile!
 * 	(some other routines need this flag as well)
 */


/* mandatory switches: */
#define NEWDIST		/* additional sort after getting the centre */
#define PERIODCENTER	/* take care of periodic boundary conditions when
			 * determining the centre */

/*=========================================================================
 * important parameters for HaloTracker
 *=========================================================================*/
#define CENTREPARTS   25	/* inner particles for new centre */
/* #define TRK_MINPART   10	--- see param.h */
/* #define TRK_VTUNE     1.5    --- see param.h */
#define SAVE_IDS      /* always start with all initially present particles */
#define BINLOG        /* use logarithmic binning for profiles              */
#define TRACKER_LOG   /* write a logfile                                   */
//#define STEREO2
//#define AHFabsangmom

/*=========================================================================
 * COMMON structures (not defined in tdef.h)
 *=========================================================================*/
/* "halo particle" */
/*typedef struct P *Pptr;
typedef struct P {
   long unsigned id;
   double        dist;
   double        Ekin;
   double        Epot;
   double        Lx;
   double        Ly;
   double        Lz;
#ifdef ADDMASS
   double 	 weight;
#endif
} P;
*/

/*=========================================================================
 * all the routines that are going to be called...
 * (a lot of the routines are copies from src/ to allow for adjustments!)
 *=========================================================================*/
void    read_simu       (char *filename);
void    read_particles  (char *prefix);
void    read_halos      (char *prefix);
void    update_halo     (HALO *cur_halo);
void    write_halo      (HALO *cur_halo);
void    get_centre      (HALO *cur_halo);
/*void    rem_unbound     (HALO *cur_halo);	//new
void    sort_halo_particles  (HALO *cur_halo);	//new
int	HaloProfiles           (HALO *);
void    rem_outsideRvir (HALO *, int);		//new
//Pptr    create_P        (long unsigned npart);
//void    copy_P          (Pptr part1,  Pptr part2);
*/
HALO   *create_halo     (long block_size);
void    dest_halo       (HALO *cur_halo);
/*void    c_profile  	(HALO *cur_halo, int nbins);
void    dest_profile    (HALO *cur_halo);
*/
void    calc_Pvir       (HALO *cur_halo);

/* functions for proper input/reading */
void input(char *infile_name);
void set_user_data(uparam *user_data, char *outprefix);

/*=========================================================================
* COMMON variables (other than those defined in common.c)
*=========================================================================*/
double 	      x_fac, v_fac, m_fac, rho_fac, phi_fac, r_fac, Hubble;
double        x_fac_inv, v_fac_inv, m_fac_inv, rho_fac_inv, phi_fac_inv;
long          jhalo;
HALO          *fst_halo;
FILE	      *fphalos, *fpprof, *fpinfo;
#ifdef TRACKER_DEBRIS
FILE	      *fpparticles, *fpuparticles, *fpoparticles;
#endif
uparam        user_dummy;   /* structure needed by the init-routines */




/*=============================   MAIN   ==================================*/
int main(argc,argv)
/*=========================================================================*/
int argc;
char **argv;
{   
   long     halo_id, ntimes, itime, ihalo;
   char   **SIMUfile, haloprefix[MAXSTRING];
   char     outfile[MAXSTRING], outprefix[MAXSTRING], file_no[MAXSTRING];
   HALO     *cur_halo;
#ifdef TRACKER_DEBRIS
   long     jpart;
   partptr  cur_part;
#endif


   
   /*==================================================================*
    *                          USER INTERFACE                          *
    *==================================================================*/
   printf("==================================================================\n");
#ifdef MLAPM
   printf("  track individual halo(s) through a whole set of MLAPM binaries\n");
#else
   printf("  track individual halo(s) through a whole set of AMIGA binaries\n");
#endif
#ifdef TRACKER_DEBRIS
   printf("              additionally: save the debris field\n");
#endif   
   printf("==================================================================\n");
   printf("\nPlease give id of halo to track (-1=all):      ");
   scanf("%ld", &jhalo);
   printf("%ld\n",jhalo);
   printf("Please give prefix of _particles/_halos file:  ");
   scanf("%s",haloprefix);
   printf("%s\n",haloprefix);
   printf("Please give number of AMIGA binaries:          ");
   scanf("%ld", &ntimes);
   printf("%ld\n",ntimes);
   SIMUfile = (char **) calloc(ntimes, sizeof(char*));
   for(itime=0; itime < ntimes; itime++)
     {
      SIMUfile[itime] = (char *) calloc(MAXSTRING, sizeof(char));
      printf("Please give name of %5ld. AMIGA binary:       ",itime+1);
      scanf("%s", SIMUfile[itime]);
      printf("%s\n",SIMUfile[itime]);      
     }
   printf("Please give prefix for output files            ");
   scanf("%s",outprefix);
   printf("%s\n\n",outprefix);      


   /* initialize io-structure and amiga-logfile */
   /*===========================================*/
   set_user_data(&user_dummy, outprefix);
   init_io_structure(user_dummy);
   init_logfile(user_dummy);
   

   
#ifdef TRACKER_LOG
   /* open tracker logfile (necessary for routines in bdm_halos.c) */
   fpinfo = fopen("HaloTracker.log","w");
   fprintf(fpinfo,"=========================================\n");
   fprintf(fpinfo,"             HaloTracker\n");
   fprintf(fpinfo,"=========================================\n");
   fflush(fpinfo);   
#endif
   
   /* open single output file */   
   if(jhalo != -1)
     {
      /* open .TRK_halos */
      sprintf(outfile,"%s.TRK_halos",outprefix);
      if((fphalos=fopen(outfile,"w"))==NULL)
        {
         printf("I cannot open %s\n", outfile);
         exit(1);
        }
#ifdef GAS_PARTICLES
      fprintf(fphalos,"# zred(1)   npart(1)  nvpart(2)      Xc(3)          Yc(4)          Zc(5)      VXc(6)   VYc(7)   VZc(8)    Mvir(9)      Rvir(10) Vmax(11)   Rmax(12) sigV(13) lambda(14)      Lx(15)       Ly(16)       Lz(17)     a(18)     Eax(19)    Eay(20)    Eaz(21)     b(22)     Ebx(23)    Eby(24)    Ebz(25)     c(26)     Ecx(27)    Ecy(28)    Ecz(29) ovdens(30)    Redge(31) nbins(32)     Ekin(33)       Epot(34)   mbp_offset(35)  com_offset(36)   r2(37)      lambdaE(38)     n_gas(39)   M_gas(40)   lambda_gas(41)  Lx_gas(42)  Ly_gas(43)  Lz_gas(44)   a_gas(45)  Eax_gas(46) Eay_gas(47) Eaz_gas(48)   b_gas(49)  Ebx_gas(50) Eby_gas(51) Ebz_gas(52)   c_gas(53)  Ecx_gas(54) Ecy_gas(55) Ecz_gas(56)  Ekin_gas(57)  Epot_gas(58)  lambdaE_gas(59)   phi0(60)\n");
#else
      fprintf(fphalos,"# zred(1)   npart(1)  nvpart(2)      Xc(3)          Yc(4)          Zc(5)      VXc(6)   VYc(7)   VZc(8)    Mvir(9)      Rvir(10) Vmax(11)   Rmax(12) sigV(13) lambda(14)      Lx(15)       Ly(16)       Lz(17)     a(18)     Eax(19)    Eay(20)    Eaz(21)     b(22)     Ebx(23)    Eby(24)    Ebz(25)     c(26)     Ecx(27)    Ecy(28)    Ecz(29) ovdens(30)    Redge(31) nbins(32)     Ekin(33)       Epot(34)   mbp_offset(35)  com_offset(36)   r2(37)      lambdaE(38)\n");
#endif
      fflush(fphalos);

      /* open .TRK_profiles */
      sprintf(outfile,"%s.TRK_profiles",outprefix);
      if((fpprof=fopen(outfile,"w"))==NULL)
        {
         printf("I cannot open %s\n", outfile);
         exit(1);
        }
      fprintf(fpprof,"#  r(1)    npart(2)    nvpart(3)    ovdens(4)     dens(5)   vcirc(6)  sigv(7)     Lx(8)        Ly(9)        Lz(10)      a(11)      Eax(12)    Eay(13)    Eaz(14)    b(15)      Ebx(16)    Eby(17)    Ebz(18)    c(19)      Ecx(20)    Ecy(21)    Ecz(22)      Ekin(23)       Epot(24)\n");
      fflush(fpprof);

#ifdef TRACKER_DEBRIS      
      /* open TRK_...particles-files */
      sprintf(outfile,"%s.TRK_particles",outprefix);
      fprintf(stderr," o writing to output file %s\n",outfile);
      fpparticles=fopen(outfile,"w");
      
      sprintf(outfile,"%s.TRK_uparticles",outprefix);
      fprintf(stderr," o writing to output file %s\n",outfile);
      fpuparticles=fopen(outfile,"w");
      
      sprintf(outfile,"%s.TRK_oparticles",outprefix);
      fprintf(stderr," o writing to output file %s\n",outfile);
      fpoparticles=fopen(outfile,"w");
#endif
     }	/* jhalo !=-1 */
   
   /* create timeline (needed by, for instance, calc_virial()) */
   create_timeline(0.001, 1.0, &simu.timeline);


   /*==================================================================*
    *                          INITIAL TIME                            *
    *==================================================================*/
   fprintf(stderr,"INTIAL TIME:\n");
   fprintf(stderr,"============\n");


   /* read raw simulation data, wrapper for input() */
   read_simu(SIMUfile[0]);
   
   /* open multi output files (after read_simu as we need the redshift global.z!) */
   if(jhalo == -1)
     {
      /* open .TRK_halos */
      strcpy(outfile,outprefix);
      sprintf(file_no,".z%.3f.TRK_halos",fabs(global.z));
      strcat(outfile, file_no);
      fprintf(stderr," o writing to output file %s\n",outfile);
      if((fphalos=fopen(outfile,"w"))==NULL)
        {
         printf("I cannot open %s\n", outfile);
         exit(1);
        }
#ifdef GAS_PARTICLES
      fprintf(fphalos,"#  npart(1)  nvpart(2)      Xc(3)          Yc(4)          Zc(5)      VXc(6)   VYc(7)   VZc(8)    Mvir(9)      Rvir(10) Vmax(11)   Rmax(12) sigV(13) lambda(14)      Lx(15)       Ly(16)       Lz(17)     a(18)     Eax(19)    Eay(20)    Eaz(21)     b(22)     Ebx(23)    Eby(24)    Ebz(25)     c(26)     Ecx(27)    Ecy(28)    Ecz(29) ovdens(30)    Redge(31) nbins(32)     Ekin(33)       Epot(34)   mbp_offset(35)  com_offset(36)   r2(37)      lambdaE(38)     n_gas(39)   M_gas(40)   lambda_gas(41)  Lx_gas(42)  Ly_gas(43)  Lz_gas(44)   a_gas(45)  Eax_gas(46) Eay_gas(47) Eaz_gas(48)   b_gas(49)  Ebx_gas(50) Eby_gas(51) Ebz_gas(52)   c_gas(53)  Ecx_gas(54) Ecy_gas(55) Ecz_gas(56)  Ekin_gas(57)  Epot_gas(58)  lambdaE_gas(59)   phi0(60)\n");
#else
      fprintf(fphalos,"#  npart(1)  nvpart(2)      Xc(3)          Yc(4)          Zc(5)      VXc(6)   VYc(7)   VZc(8)    Mvir(9)      Rvir(10) Vmax(11)   Rmax(12) sigV(13) lambda(14)      Lx(15)       Ly(16)       Lz(17)     a(18)     Eax(19)    Eay(20)    Eaz(21)     b(22)     Ebx(23)    Eby(24)    Ebz(25)     c(26)     Ecx(27)    Ecy(28)    Ecz(29) ovdens(30)    Redge(31) nbins(32)     Ekin(33)       Epot(34)   mbp_offset(35)  com_offset(36)   r2(37)      lambdaE(38)\n");
#endif
      fflush(fphalos);      

      /* open .TRK_profiles */
      strcpy(outfile,outprefix);
      sprintf(file_no,".z%.3f.TRK_profiles",fabs(global.z));
      strcat(outfile, file_no);
      fprintf(stderr," o writing to output file %s\n",outfile);
      if((fpprof=fopen(outfile,"w"))==NULL)
        {
         printf("I cannot open %s\n", outfile);
         exit(1);
        }
      fprintf(fpprof,"#  r(1)    npart(2)    nvpart(3)    ovdens(4)     dens(5)   vcirc(6)  sigv(7)     Lx(8)        Ly(9)        Lz(10)      a(11)      Eax(12)    Eay(13)    Eaz(14)    b(15)      Ebx(16)    Eby(17)    Ebz(18)    c(19)      Ecx(20)    Ecy(21)    Ecz(22)      Ekin(23)       Epot(24)\n");
      fflush(fpprof);

#ifdef TRACKER_DEBRIS      
      /* open TRK_...particles-files */
      strcpy(outfile,outprefix);
      sprintf(file_no,".z%.3f.TRK_particles",fabs(global.z));
      strcat(outfile, file_no);
      fprintf(stderr," o writing to output file %s\n",outfile);
      fpparticles=fopen(outfile,"w");
      
      strcpy(outfile,outprefix);
      sprintf(file_no,".z%.3f.TRK_uparticles",fabs(global.z));
      strcat(outfile, file_no);
      fprintf(stderr," o writing to output file %s\n",outfile);
      fpuparticles=fopen(outfile,"w");
      
      strcpy(outfile,outprefix);
      sprintf(file_no,".z%.3f.TRK_oparticles",fabs(global.z));
      strcat(outfile, file_no);
      fprintf(stderr," o writing to output file %s\n",outfile);
      fpoparticles=fopen(outfile,"w");
#endif
     }
   /* read in initial halo particles from _particles file */
   read_particles(haloprefix);	/* reads particle ids into cur_halo->ipart[] */
   
   /* read initial halo centres from _halos file */
   read_halos(haloprefix);

   /* sort particles with respect to distance from centre */
   for(ihalo=0; ihalo<simu.no_halos; ihalo++)
     {
      /* set current halo */
      cur_halo = fst_halo + ihalo;

      fprintf(stderr,"  o working on halo %ld (%ld):\n",ihalo,cur_halo->npart);
      
      fprintf(stderr,"   o sorting particles with respect to initial centre ... ");
      sort_halo_particles(cur_halo);	/* should be sorted already ...*/
      fprintf(stderr,"done\n");

      /* even though we know all about the halos from _halos, let's recalculate their properties... */
      update_halo(cur_halo);
     }

   /* close multi output file */
   if(jhalo == -1)
     {
      fclose(fphalos);
      fclose(fpprof);
     }

   /* free particle arrays */
   if (global.fst_part) free(global.fst_part);
   if (global.fst_gas)  free(global.fst_gas);
   if (global.fst_star) free(global.fst_star);
   
   /*============================================================*
    *                TRACKING THROUGH TIME                       *
    *============================================================*/
   fprintf(stderr,"\nTRACKING THROUGH TIME:\n");
   fprintf(stderr,"======================\n");

   /* loop over all successive AMIGA outputs */
   for(itime=1; itime<ntimes; itime++)
     {
      /* read raw simulation data */
      read_simu(SIMUfile[itime]);
      
      /* open multi output files (after read_simu as we need the redshift global.z!) */
      if(jhalo == -1)
        {
         /* open .TRK_halos */
         strcpy(outfile,outprefix);
         sprintf(file_no,".z%.3f.TRK_halos",fabs(global.z));
         strcat(outfile, file_no);
         fprintf(stderr," o writing to output file %s\n",outfile);
         if((fphalos=fopen(outfile,"w"))==NULL)
           {
            printf("I cannot open %s\n", outfile);
            exit(1);
           }
         fprintf(fphalos,"#  npart(1)  nvpart(2)      Xc(3)          Yc(4)          Zc(5)      VXc(6)   VYc(7)   VZc(8)    Mvir(9)      Rvir(10) Vmax(11)   Rmax(12) sigV(13) lambda(14)    Lx(15)       Ly(16)       Lz(17)       a(18)     E1x(19)    E1y(20)    E1z(21)     b(22)     E2x(23)    E2y(24)    E2z(25)     c(26)     E3x(27)    E3y(28)    E3z(29) ovdens(30)  Redge(31) nbins(32)     Ekin(33)       Epot(34)   mbp_offset(35)  com_offset(36)   r2(37)    lambdaE(38)\n");
         fflush(fphalos);      

         /* open .TRK_profiles */
         strcpy(outfile,outprefix);
         sprintf(file_no,".z%.3f.TRK_profiles",fabs(global.z));
         strcat(outfile, file_no);
         fprintf(stderr," o writing to output file %s\n",outfile);
         if((fpprof=fopen(outfile,"w"))==NULL)
           {
            printf("I cannot open %s\n", outfile);
            exit(1);
           }
         fprintf(fpprof,"#  r(1)    npart(2)    nvpart(3)    ovdens(4)     dens(5)   vcirc(6)  sigv(7)     Lx(8)        Ly(9)        Lz(10)      a(11)      Eax(12)    Eay(13)    Eaz(14)    b(15)      Ebx(16)    Eby(17)    Ebz(18)    c(19)      Ecx(20)    Ecy(21)    Ecz(22)      Ekin(23)       Epot(24)\n");
         fflush(fpprof);

#ifdef TRACKER_DEBRIS      
         /* open TRK_...particles-files */
         strcpy(outfile,outprefix);
         sprintf(file_no,".z%.3f.TRK_particles",fabs(global.z));
         strcat(outfile, file_no);
         fprintf(stderr," o writing to output file %s\n",outfile);
         fpparticles=fopen(outfile,"w");
      
         strcpy(outfile,outprefix);
         sprintf(file_no,".z%.3f.TRK_uparticles",fabs(global.z));
         strcat(outfile, file_no);
         fprintf(stderr," o writing to output file %s\n",outfile);
         fpuparticles=fopen(outfile,"w");
      
         strcpy(outfile,outprefix);
         sprintf(file_no,".z%.3f.TRK_oparticles",fabs(global.z));
         strcat(outfile, file_no);
         fprintf(stderr," o writing to output file %s\n",outfile);
         fpoparticles=fopen(outfile,"w");
#endif
        }


      /* loop over all halos */
      for(ihalo=0; ihalo<simu.no_halos; ihalo++)
        {
         /* set current halo */
         cur_halo = fst_halo + ihalo;
         fprintf(stderr,"  o working on halo %ld (%ld):\n",ihalo,cur_halo->npart);

         /* update halo properties using particles in SIMUfile[itime] */
         update_halo(cur_halo);
        } /* ihalo */
      
      if(jhalo == -1)
        {
         fclose(fphalos);
         fclose(fpprof);
#ifdef TRACKER_DEBRIS
         fclose(fpparticles);
         fclose(fpuparticles);
         fclose(fpoparticles);
#endif
        }

      /* free particle array */
      if (global.fst_part)  free(global.fst_part);
      if (global.fst_gas)   free(global.fst_gas);
      if (global.fst_star)  free(global.fst_star);
      
     } /* itime */
   
   /* close single output file */
   if(jhalo != -1)
     {
      fclose(fphalos);
#ifdef TRACKER_DEBRIS
      fclose(fpparticles);
      fclose(fpuparticles);
      fclose(fpoparticles);
#endif
     }

         
#ifdef TRACKER_LOG
   /* close logfile */
   fclose(fpinfo);
#endif

#ifdef LIGHTCONE 
   /* close the file if it exists. */
   if (io.lightcone!=NULL) 
   {
      fclose(io.lightcone);
      io.lightcone=NULL;
   }
#endif /* LIGHTCONE */

   if (io.a_out != NULL)
      free(io.a_out);
   
   free(io.icfile_name);
   free(io.dumpfile_name);
   free(io.logfile_name);
   free(io.outfile_prefix);

   
   fprintf(io.logfile,
           "==========================================================\n");
   fprintf(io.logfile,
           "                       FINISHED (v%3.1f/%d)\n",VERSION,BUILT);
   fprintf(io.logfile,
           "==========================================================\n");
   fclose(io.logfile);

}

/*====================================================================================
* the actual halo tracker:
*   1. guess a new centre based upon the innermost particles from the previous step
*   2. remove unbound particles
*   3. re-calculate halo properties based upon bound particles
*   4. sort particles with respects to new centre for next step
*
*   most of these routines are actually identical to the ones in bdm_halos.c,
*   however, they required minor modifications regarding the information dumped
*   into an _info file in bdm_halos.c
*=====================================================================================*/
void update_halo(HALO *cur_halo)
{
   long          jpart;
   long unsigned all_npart, *all_id;
   partptr       cur_part;
   /* get halo centre */
   fprintf(stderr,"   o getting centre ... ");
   get_centre(cur_halo);
   fprintf(stderr,"done\n");

#ifdef NEWDIST
   /* update all particle distances according to the new centre and sort them */
   /* here _all_ the particles, no matter whether unbound or not, are sorted */
   fprintf(stderr,"   o sorting particles with respect to new centre ... ");
   sort_halo_particles(cur_halo);
   fprintf(stderr,"done (%ld)\n",cur_halo->npart);
#endif   
   
#ifdef SAVE_IDS
   /* store particle id's in temporary array before any of them are getting removed */
   fprintf(stderr,"   o saving original particle id's ... ");
   all_npart = cur_halo->npart;
   all_id    = (long unsigned*) calloc(cur_halo->npart, sizeof(long unsigned));
   for(jpart=0; jpart<cur_halo->npart; jpart++)
      all_id[jpart] = cur_halo->ipart[jpart];		/*Kristin: can be done faster!!! */
   fprintf(stderr,"done (%ld)\n",cur_halo->npart);
#endif

   rem_outsideRvir(cur_halo,0);	/* already remove beforehand, for proper Phi0-det. in rem_unb. */

   /* remove unbound particles */
   fprintf(stderr,"   o removing unbound particles ... ");
   rem_unbound(cur_halo);
   fprintf(stderr,"done (%ld %g)\n",cur_halo->npart, cur_halo->R_vir*x_fac);

   rem_outsideRvir(cur_halo, 1);	/* sim. to ahf_halos => don't calc. profile
   					 * up to last boudn particle! 
					 * May want to change this later!!! */

   /* create density, mass, sigV, ... profiles (NO update of R_vir!!!) */
   fprintf(stderr,"   o calculating profiles ... ");
   
   HaloProfiles(cur_halo);
   //calc_profiles(cur_halo);
   fprintf(stderr,"done (%ld %g)\n",cur_halo->npart, cur_halo->R_vir*x_fac);

   /* update halo properties */
   //fprintf(stderr,"   o updating halo properties ... ");
   //calc_Pvir(cur_halo); 
   //if(cur_halo->R_vir < 0.) cur_halo->R_vir = -1./(x_fac*1000.);
   //fprintf(stderr,"done (%ld)\n",cur_halo->npart);
   
   
   /* write properties into output file (_halos-file) */
   write_halo(cur_halo);


#ifdef STEREO2
   {
      FILE *fpstereo;

      fpstereo = fopen("stereo2.geom","w");

      for(jpart=0; jpart<cur_halo->npart; jpart++)
        {
         cur_part = global.fst_part + cur_halo->ipart[jpart];
         fprintf(fpstereo,"p %g %g %g 1 0 0\n",cur_part->pos[X],cur_part->pos[Y],cur_part->pos[Z]);
        }
      fclose(fpstereo);
      exit(1);
   }
#endif


#ifdef TRACKER_DEBRIS
   /* unit for dist: r_fac or x_fac (like for all other lengths) ? */
   /* write particles of this halo */
   fprintf(stderr,"       Write bound inner particles of this halo ...\n");
   fprintf(stderr,"          cur_halo->npart: %ld\n",cur_halo->npart );

   fprintf(fpparticles," %ld\n",cur_halo->npart);
   //fprintf(fpparticles,"%g   %ld\n",global.z,cur_halo->npart);

   for(jpart=0; jpart<cur_halo->npart; jpart++)
   {
      cur_part = global.fst_part + cur_halo->ipart[jpart];

      fprintf(fpparticles,"%10ld\n", cur_halo->ipart[jpart]);

   }
   fflush(fpparticles);

   /* unbound */
   fprintf(stderr,"       Write unbound particles of this halo ...\n");
   fprintf(stderr,"          cur_halo->nupart: %ld\n",cur_halo->nupart );
   fprintf(fpuparticles," %ld\n",cur_halo->nupart);
   for(jpart=0; jpart<cur_halo->nupart; jpart++)
   {
      cur_part = global.fst_part + cur_halo->iupart[jpart];
      fprintf(fpuparticles,"%10ld\n", cur_halo->iupart[jpart]);
   }
   fflush(fpuparticles);

   /* outliers */
   fprintf(stderr,"       Write outlying particles of this halo ...\n");
   fprintf(stderr,"          cur_halo->nopart: %ld\n",cur_halo->nopart );
   fprintf(fpoparticles," %ld\n",cur_halo->nopart);
   for(jpart=0; jpart<cur_halo->nopart; jpart++)
   {
      cur_part = global.fst_part + cur_halo->iopart[jpart];
      fprintf(fpoparticles,"%10ld\n", cur_halo->iopart[jpart]);
   }
   fflush(fpoparticles);
#endif /* TRACKER_DEBRIS */


#ifdef SAVE_IDS
   /* restore original particle id's from temporary array */
   fprintf(stderr,"   o restoring original particle id's ... ");
   cur_halo->npart = all_npart;
   free(cur_halo->ipart);

   cur_halo->ipart = (long unsigned *) calloc(cur_halo->npart, sizeof(long unsigned));
   if (!cur_halo->ipart) {fprintf(stderr,"Error in calloc for ipart. Exit.\n"); exit(1);}

   for(jpart=0; jpart<cur_halo->npart; jpart++)
      cur_halo->ipart[jpart] = all_id[jpart];
   free(all_id);

   fprintf(stderr,"done (%ld)\n",cur_halo->npart);
#endif

   /* sort particles with respect to distance from centre for next call to update_halo */
   /* here _all_ the particles, no matter whether unbound or not, are sorted */
   fprintf(stderr,"   o sorting particles with respect to new centre ... ");
   sort_halo_particles(cur_halo);
   fprintf(stderr,"done (%ld)\n",cur_halo->npart);
   
   /* profile not needed anymore */
   dest_profile(cur_halo);

#ifdef TRACKER_DEBRIS
   /* free some more memory 
    * (iupart and iopart, allocated in rem_unbound()
    *  and rem_outsideRvir() are not used anymore) */
   if (cur_halo->iopart) { free(cur_halo->iopart); cur_halo->iopart = NULL; }
   if (cur_halo->iupart) { free(cur_halo->iupart); cur_halo->iupart = NULL; }
#endif
}

/*==============================================================================
 * read in particle id's for halo(s) from AHF_particles
 *==============================================================================*/
void read_particles(char *prefix)
{
   char          infile[MAXSTRING];
   FILE         *fpin;
   long          npart, ihalo, jpart;
   long unsigned id;
   HALO          *cur_halo;
   char 	 line[100];
   
   /* prepare filename */
   strcpy(infile,prefix);
   strcat(infile,"_particles");
   
   fprintf(stderr," o reading _particles file %s ... ",infile);
   
   if((fpin=fopen(infile,"r"))==NULL)
     {
      printf("I cannot open %s\n", infile);
      exit(1);
     }

   /* ATTENTION! in some AHF_particles, the first number gives the number of halos!!! */   
#ifndef MORE_PARTICLES_INFO     
   /* count total number of halos in AHF_particles file */
   fprintf(stderr,"\n   - count total number of halos ... ");
   simu.no_halos = 0;
      
   while(1)
     {
      /* read number of particles in halo */
      fscanf(fpin,"%ld",&npart);
      
      /* did we actually read anything */
      if(feof(fpin)) 
         break;
      
      /* increase counter for number of halos */
      (simu.no_halos)++;
      
      /* read npart particle id's */
      for(jpart=0; jpart<npart; jpart++)
         fscanf(fpin,"%ld",&id);      
     }
   fclose(fpin);
   fprintf(stderr,"    %ld halos found.\n", simu.no_halos);
#else
   fgets(line, sizeof(line), fpin);
   sscanf(line,"%ld",&simu.no_halos);
        
   fprintf(stderr,"\n  - There are %ld halos.\n", simu.no_halos);
#endif     
   
   
   /* check jhalo */
   
   /* we only need one halo structure in this case */
   if(jhalo != -1)
     {
      if(simu.no_halos < jhalo)
        {
         fprintf(stderr,"simu.no_halos = %ld  vs.  jhalo = %ld\n",
                 simu.no_halos, jhalo);
         exit(1);
        }
      
      simu.no_halos = 1;
     }

#ifndef MORE_PARTICLES_INFO   
   /* open file for actual reading */
   if((fpin=fopen(infile,"r"))==NULL)
     {
      printf("I cannot open %s\n", infile);
      exit(1);
     }
#endif
   /* else: file is already open! */

   /* create only one halo structure though */
   /*   simu.no_halos = 1; */
   
   fst_halo = create_halo(simu.no_halos);
   cur_halo = fst_halo;
   
   /* read AHF_particles until jhalo has been reached */
   if(jhalo == -1)
     {
      for(ihalo=0; ihalo<simu.no_halos; ihalo++)
        {
         cur_halo = fst_halo + ihalo;
         
         /* read number of particles in halo */
         fgets(line, sizeof(line), fpin);
         sscanf(line,"%ld",&npart);
                  
         /* read npart particle id's */
         cur_halo->npart = npart;
         //cur_halo->part  = (Pptr) create_P(npart);
	 cur_halo->ipart = (long unsigned *) calloc(cur_halo->npart, sizeof(long unsigned));
	 
         //fprintf(stderr,"   - read %ld particles ...\n", npart);
	 for(jpart=0; jpart<npart; jpart++)
	 {
            fgets(line, sizeof(line), fpin);
            sscanf(line,"%ld",&(cur_halo->ipart[jpart]));
            //if ((jpart+1)%10000 == 0) fprintf(stderr,"     %d particles read.\n", jpart+1);   
	 }
        }
     }
   else
     {
      ihalo = -1;
      while(ihalo != jhalo)
        {
         /* read number of particles in halo */
         fgets(line, sizeof(line), fpin);
         sscanf(line,"%ld",&npart);
         
         /* read npart particle id's */
         cur_halo->npart = npart;
         //cur_halo->part  = (Pptr) create_P(npart);
	 cur_halo->ipart = (long unsigned *) calloc(cur_halo->npart, sizeof(long unsigned));
         for(jpart=0; jpart<npart; jpart++)
	 {
            fgets(line, sizeof(line), fpin);
	    sscanf(line,"%ld",&(cur_halo->ipart[jpart]));
         }
         /* increase halo counter */
         ihalo++;
	 
	 if (ihalo%100 == 0) fprintf(stderr,"  - particles for %d halos read.\n", ihalo);
	 
        }
     }
   
   fclose(fpin);
   
/*   fprintf(stderr,"Kristin: Check, if useful things have been read or not: \n");
   fprintf(stderr,"Kristin: halo 0,1,2 npart and 1. id: %ld %ld, %ld %ld, %ld %ld\n",
   	fst_halo->npart, fst_halo->ipart[0], 
	(fst_halo+1)->npart, (fst_halo+1)->ipart[0], 
	(fst_halo+2)->npart, (fst_halo+2)->ipart[0]);
	=> ok. :-)   
*/
   
   fprintf(stderr,"done\n");   
}

/*==============================================================================
 * read in initial halo positions from AHF_halos
 *==============================================================================*/
void read_halos(char *prefix)
{
   char    infile[MAXSTRING];
   FILE   *fpin;
   HALO   *cur_halo;
   char    dummyline[MAXSTRING];
   int     i, j, ilen;
   long    ihalo, last_halo, npart;
   double  Xc, Yc, Zc, VXc, VYc, VZc, M_vir;
      
   
   /* prepare filename */
   strcpy(infile,prefix);
   strcat(infile,"_halos");
   
   fprintf(stderr," o reading _halos file %s ... ",infile);
   
   /* open AHF_halos file */
   if((fpin=fopen(infile,"r"))==NULL)
     {
      printf("I cannot open %s\n", infile);
      exit(1);
     }
   
   /* there is only one halo so far ... */
   cur_halo = fst_halo;
   
   /* overread header line */
   fgets(dummyline,MAXSTRING,fpin);
   
   /* all halos or just one */
   if(jhalo == -1)
      last_halo = simu.no_halos-1;
   else
      last_halo = jhalo;
   
   for(ihalo=0; ihalo<=last_halo; ihalo++)
     {
      /* read info from file */
      fgets(dummyline,MAXSTRING,fpin);
      
      /* extract information from last read dummyline */
      sscanf(dummyline,"%ld %lf %lf %lf %lf",&npart,&M_vir,&Xc,&Yc,&Zc,&VXc,&VYc,&VZc);

      /* transfer info to halo structure */
      cur_halo->pos.x = Xc   * x_fac_inv;
      cur_halo->pos.y = Yc   * x_fac_inv;
      cur_halo->pos.z = Zc   * x_fac_inv;
      cur_halo->vel.x = VXc  * v_fac_inv;
      cur_halo->vel.y = VYc  * v_fac_inv;
      cur_halo->vel.z = VZc  * v_fac_inv;
      
      /* next halo structure */
      if(jhalo == -1)
         cur_halo++;
     }
   
   fclose(fpin);
   
   /* fprintf(stderr,"Kristin: halo[0].x, halo[10].x,y,z: %lf %lf %lf %lf\n", fst_halo[0].pos.x*x_fac, fst_halo[10].pos.x*x_fac, fst_halo[10].pos.y*x_fac, fst_halo[10].pos.z*x_fac);
   => ok. */
   
   fprintf(stderr,"done\n");
}

/*==============================================================================
 * get a rough estimate for the halo centre using the innermost particles
 *==============================================================================*/
void get_centre(HALO *cur_halo)
{
   double  Xc, Yc, Zc;
   int     i;
   long    ipart;
   partptr cur_part;

#ifdef PERIODCENTER
   double Xtmp, Ytmp, Ztmp;
   double dX, dY, dZ;
#endif
   
   if(cur_halo->npart < CENTREPARTS) return;
   
   Xc = 0.0;
   Yc = 0.0;
   Zc = 0.0;
   
   for(i=0; i<CENTREPARTS; i++)
     {
      /* the particles are sorted with respect to their distance */
      ipart    = cur_halo->ipart[i];
      cur_part = global.fst_part + ipart;

#ifdef PERIODCENTER
      /* how to get the new centre, when halo lies a the border of the box? */
      /* determine distance to old centre: */
      Xtmp = cur_part->pos[X];
      Ytmp = cur_part->pos[Y];
      Ztmp = cur_part->pos[Z];

      dX = Xtmp - cur_halo->pos.x;
      dY = Ytmp - cur_halo->pos.y;
      dZ = Ztmp - cur_halo->pos.z;
      
      /* shift the position by 1.0 (Boxsize), if necessary (periodic boundary!) */
      if (dX >  0.5) Xtmp = Xtmp - 1.0;
      if (dY >  0.5) Ytmp = Ytmp - 1.0;
      if (dZ >  0.5) Ztmp = Ztmp - 1.0;
      if (dX < -0.5) Xtmp = Xtmp + 1.0;
      if (dY < -0.5) Ytmp = Ytmp + 1.0;
      if (dZ < -0.5) Ztmp = Ztmp + 1.0;
   
      /* determine new centre with shifted positions */   
      Xc += Xtmp;
      Yc += Ytmp;
      Zc += Ztmp;
#else      
      Xc += cur_part->pos.x;
      Yc += cur_part->pos.y;
      Zc += cur_part->pos.z;
#endif
     }
   
   Xc /= (double)CENTREPARTS;
   Yc /= (double)CENTREPARTS;
   Zc /= (double)CENTREPARTS;

#ifdef PERIODCENTER
   /* shift the centre back to the box, if necessary */
   if ( Xc >  1.0 ) Xc = Xc - 1.0;
   if ( Yc >  1.0 ) Yc = Yc - 1.0;
   if ( Zc >  1.0 ) Zc = Zc - 1.0;
   if ( Xc <  0.0 ) Xc = Xc + 1.0;
   if ( Yc <  0.0 ) Yc = Yc + 1.0;
   if ( Zc <  0.0 ) Zc = Zc + 1.0;
#endif
      
   fprintf(stderr,"(new=%g %g %g - old=%g %g %g) ",
           Xc*x_fac,Yc*x_fac,Zc*x_fac,
           cur_halo->pos.x*x_fac,cur_halo->pos.y*x_fac,cur_halo->pos.z*x_fac);

   cur_halo->pos.x = Xc;
   cur_halo->pos.y = Yc;
   cur_halo->pos.z = Zc;
}

/*==============================================================================
* little routine dumping relevant info into relevant file
*==============================================================================*/
void write_halo(HALO *cur_halo)
{
   int    ibin, r_conv_i;
   double rad, t_relax, age;
   
   if(jhalo == -1)
     {
      /* write new values into output file */
#ifdef GAS_PARTICLES
      fprintf(fphalos,"%10ld %10.0f %14.8f %14.8f %14.8f %8.2f %8.2f %8.2f %12.6g %10.2f %8.2f %10.2f %8.2f %10.6f %12.4g %12.4g %12.4g %10.6f %10.6f %10.6f %10.6f %10.6f %10.6f %10.6f %10.6f %10.6f %10.6f %10.6f %10.6f %8.2f %12.4g %6d       %12.6g   %12.6g  %10.5f      %10.5f     %10.5f  %12.6f   %10ld   %12.6g   %10.6f   %10.6f  %10.6f  %10.6f   %10.6f   %10.6f  %10.6f  %10.6f  %10.6f   %10.6f  %10.6f  %10.6f  %10.6f   %10.6f  %10.6f  %10.6f  %12.6g   %12.6g   %10.6f    %12.6g\n",
#else
      fprintf(fphalos,"%10ld %10.0f %14.8f %14.8f %14.8f %8.2f %8.2f %8.2f %12.6g %10.2f %8.2f %10.2f %8.2f %10.6f %12.4g %12.4g %12.4g %10.6f %10.6f %10.6f %10.6f %10.6f %10.6f %10.6f %10.6f %10.6f %10.6f %10.6f %10.6f %8.2f %12.4g %6d       %12.6g   %12.6g  %10.5f      %10.5f     %10.5f  %12.6f\n",
#endif
              cur_halo->npart,                     /* number of particles         */
              cur_halo->M_vir,                     /* number of virtual particles */
              cur_halo->pos.x*x_fac, 
              cur_halo->pos.y*x_fac,              /* comoving Mpc/h  */
              cur_halo->pos.z*x_fac,
              cur_halo->vel.x*v_fac, 
              cur_halo->vel.y*v_fac,              /* comoving peculiar km/sec */
              cur_halo->vel.z*v_fac,
              cur_halo->M_vir*m_fac,               /* Msun/h */
              cur_halo->R_vir*x_fac*1000.,         /* kpc/h  */
              sqrt(cur_halo->V2_max*phi_fac),      /* km/sec */
              cur_halo->R_max*x_fac*1000.,         /* kpc/h  */
              cur_halo->sigV*v_fac,               /* km/sec */
              cur_halo->lambda*v_fac*sqrt(r_fac/(Grav*m_fac)),
#ifdef AHFabsangmom
              cur_halo->AngMom.x*r_fac*v_fac*m_fac,
              cur_halo->AngMom.y*r_fac*v_fac*m_fac,
              cur_halo->AngMom.z*r_fac*v_fac*m_fac,
#else
              cur_halo->AngMom.x,
              cur_halo->AngMom.y,
              cur_halo->AngMom.z,
#endif
              cur_halo->axis.x,
              cur_halo->E1.x,
              cur_halo->E1.y,
              cur_halo->E1.z,
              cur_halo->axis.y,
              cur_halo->E2.x,
              cur_halo->E2.y,
              cur_halo->E2.z,
              cur_halo->axis.z,
              cur_halo->E3.x,
              cur_halo->E3.y,
              cur_halo->E3.z,
              cur_halo->ovdens,
	      cur_halo->R_edge*x_fac*1000.,  
              /* cur_halo->R_bnd*x_fac*1000., */
              cur_halo->prof.nbins,
              cur_halo->Ekin*m_fac*pow2(v_fac),
              cur_halo->Epot*m_fac*phi_fac,
              cur_halo->mbp_offset*x_fac*1000.,
              cur_halo->com_offset*x_fac*1000.,
              cur_halo->r2*x_fac*1000.,
              cur_halo->lambdaE
#ifdef GAS_PARTICLES
              ,
	      cur_halo->gas.npart,
              cur_halo->gas.M_vir*m_fac,
              cur_halo->gas.lambda,
              cur_halo->gas.AngMom.x,
              cur_halo->gas.AngMom.y,
              cur_halo->gas.AngMom.z,
              cur_halo->gas.axis.x,
              cur_halo->gas.E1.x,
              cur_halo->gas.E1.y,
              cur_halo->gas.E1.z,		  
              cur_halo->gas.axis.y,
              cur_halo->gas.E2.x,
              cur_halo->gas.E2.y,
              cur_halo->gas.E2.z,		  
              cur_halo->gas.axis.z,
              cur_halo->gas.E3.x,
              cur_halo->gas.E3.y,
              cur_halo->gas.E3.z,
              cur_halo->gas.Ekin*m_fac*pow2(v_fac),
              cur_halo->gas.Epot*m_fac*phi_fac,
              cur_halo->gas.lambdaE,
              cur_halo->Phi0*m_fac*phi_fac
#endif
              );      
     }
   else
     {
      /* write new values into output file */
#ifdef GAS_PARTICLES
      fprintf(fphalos,"%8.4g %10ld %10.0f %14.8f %14.8f %14.8f %8.2f %8.2f %8.2f %12.6g %10.2f %8.2f %10.2f %8.2f %10.6f %12.4g %12.4g %12.4g %10.6f %10.6f %10.6f %10.6f %10.6f %10.6f %10.6f %10.6f %10.6f %10.6f %10.6f %10.6f %8.2f %12.4g %6d       %12.6g   %12.6g  %10.5f      %10.5f     %10.5f  %12.6f   %10ld   %12.6g   %10.6f   %10.6f  %10.6f  %10.6f   %10.6f   %10.6f  %10.6f  %10.6f  %10.6f   %10.6f  %10.6f  %10.6f  %10.6f   %10.6f  %10.6f  %10.6f  %12.6g   %12.6g   %10.6f    %12.6g\n",
#else
      fprintf(fphalos,"%8.4g %10ld %10.0f %14.8f %14.8f %14.8f %8.2f %8.2f %8.2f %12.6g %10.2f %8.2f %10.2f %8.2f %10.6f %12.4g %12.4g %12.4g %10.6f %10.6f %10.6f %10.6f %10.6f %10.6f %10.6f %10.6f %10.6f %10.6f %10.6f %10.6f %8.2f %12.4g %6d       %12.6g   %12.6g  %10.5f      %10.5f     %10.5f  %12.6f\n",
#endif
              global.z,
              cur_halo->npart,                     /* number of particles         */
              cur_halo->M_vir,                     /* number of virtual particles */
              cur_halo->pos.x*x_fac, 
              cur_halo->pos.y*x_fac,              /* comoving Mpc/h  */
              cur_halo->pos.z*x_fac,
              cur_halo->vel.x*v_fac, 
              cur_halo->vel.y*v_fac,              /* comoving peculiar km/sec */
              cur_halo->vel.z*v_fac,
              cur_halo->M_vir*m_fac,               /* Msun/h */
              cur_halo->R_vir*x_fac*1000.,         /* kpc/h  */
              sqrt(cur_halo->V2_max*phi_fac),      /* km/sec */
              cur_halo->R_max*x_fac*1000.,         /* kpc/h  */
              cur_halo->sigV*v_fac,               /* km/sec */
              cur_halo->lambda*v_fac*sqrt(r_fac/(Grav*m_fac)),
#ifdef AHFabsangmom
              cur_halo->AngMom.x*r_fac*v_fac*m_fac,
              cur_halo->AngMom.y*r_fac*v_fac*m_fac,
              cur_halo->AngMom.z*r_fac*v_fac*m_fac,
#else
              cur_halo->AngMom.x,
              cur_halo->AngMom.y,
              cur_halo->AngMom.z,
#endif
              cur_halo->axis.x,
              cur_halo->E1.x,
              cur_halo->E1.y,
              cur_halo->E1.z,
              cur_halo->axis.y,
              cur_halo->E2.x,
              cur_halo->E2.y,
              cur_halo->E2.z,
              cur_halo->axis.z,
              cur_halo->E3.x,
              cur_halo->E3.y,
              cur_halo->E3.z,
              cur_halo->ovdens,
	      cur_halo->R_edge*x_fac*1000.,  
              /* cur_halo->R_bnd*x_fac*1000., */
              cur_halo->prof.nbins,
              cur_halo->Ekin*m_fac*pow2(v_fac),
              cur_halo->Epot*m_fac*phi_fac,
              cur_halo->mbp_offset*x_fac*1000.,
              cur_halo->com_offset*x_fac*1000.,
              cur_halo->r2*x_fac*1000.,
              cur_halo->lambdaE
#ifdef GAS_PARTICLES
              ,
	      cur_halo->gas.npart,
              cur_halo->gas.M_vir*m_fac,
              cur_halo->gas.lambda,
              cur_halo->gas.AngMom.x,
              cur_halo->gas.AngMom.y,
              cur_halo->gas.AngMom.z,
              cur_halo->gas.axis.x,
              cur_halo->gas.E1.x,
              cur_halo->gas.E1.y,
              cur_halo->gas.E1.z,		  
              cur_halo->gas.axis.y,
              cur_halo->gas.E2.x,
              cur_halo->gas.E2.y,
              cur_halo->gas.E2.z,		  
              cur_halo->gas.axis.z,
              cur_halo->gas.E3.x,
              cur_halo->gas.E3.y,
              cur_halo->gas.E3.z,
              cur_halo->gas.Ekin*m_fac*pow2(v_fac),
              cur_halo->gas.Epot*m_fac*phi_fac,
              cur_halo->gas.lambdaE,
              cur_halo->Phi0*m_fac*phi_fac
#endif
              );   
     }
   fflush(fphalos);
   
   for(r_conv_i=0, ibin=0; ibin<cur_halo->prof.nbins; ibin++)
   {
      /* check for converged radius (Power et al. 2003) */
      rad      = cur_halo->prof.r[ibin];
      //t_relax  = cur_halo->prof.npart[ibin]/log(rad/cur_halo->spaRes)
      //dunno spaRes -> simply use 2nd profile bin and don't rely on criterion too much ;-)
      t_relax  = cur_halo->prof.npart[ibin]/log(rad/cur_halo->prof.r[1])
         * rad / sqrt(cur_halo->prof.v2_circ[ibin]);
      
      /* convert to (Mpc/h) / (km/sec) ~ (Mpc/h) / (kpc/Gyr) */
      t_relax *= r_fac/sqrt(phi_fac);
      
      /* convert to Gyr/h */
      t_relax *= 1E3;
      
      /* age of the Universe in Gyr/h */
      age      = calc_t(global.a)*Mpc/H0/Gyr;
      
      /* if not converged, write negative radius into .AHF_profiles */
      if(t_relax < 0.9*age)
         /* The +1 is merely to keep the name consistent:
         * r_conv_i should give the smallest converged radius, not
         * the bin before it. Hence to check for converged bin or
         * not, i<r_conv_i is the thing to do. */
         r_conv_i = ibin+1;
   }

   for(ibin=0; ibin<cur_halo->prof.nbins; ibin++)
     {      
      /* If the radius is not converged, print the radius negative
      * to the .AHF_profiles */
      rad = (ibin<r_conv_i) ?  -cur_halo->prof.r[ibin] : cur_halo->prof.r[ibin];

      fprintf(fpprof,"%8.4f %10ld %12.0f %12.2f %12.2f %8.2f %8.2f %12.4g %12.4g %12.4g %10.6f %10.6f %10.6f %10.6f %10.6f %10.6f %10.6f %10.6f %10.6f %10.6f %10.6f %10.6f    %10.6g   %10.6g\n",
              cur_halo->prof.r[ibin]            * x_fac*1000.,
              cur_halo->prof.npart[ibin],
              cur_halo->prof.nvpart[ibin],
              cur_halo->prof.ovdens[ibin]	* rho_fac/global.rho_b,
              cur_halo->prof.dens[ibin]		* rho_fac/global.rho_b,
              sqrt(cur_halo->prof.v2_circ[ibin] * phi_fac),
              cur_halo->prof.sig_v[ibin]        * v_fac,
              cur_halo->prof.Lx[ibin]           * m_fac*r_fac*v_fac,	/* check: x_fac or r_fac??*/
              cur_halo->prof.Ly[ibin]           * m_fac*r_fac*v_fac,
              cur_halo->prof.Lz[ibin]           * m_fac*r_fac*v_fac,
              cur_halo->prof.axis1[ibin],
              cur_halo->prof.E1x[ibin],
              cur_halo->prof.E1y[ibin],
              cur_halo->prof.E1z[ibin],
              cur_halo->prof.axis2[ibin],
              cur_halo->prof.E2x[ibin],
              cur_halo->prof.E2y[ibin],
              cur_halo->prof.E2z[ibin],
              cur_halo->prof.axis3[ibin],
              cur_halo->prof.E3x[ibin],
              cur_halo->prof.E3y[ibin],
              cur_halo->prof.E3z[ibin],
              cur_halo->prof.Ekin[ibin]		*m_fac*pow2(v_fac),
              cur_halo->prof.Epot[ibin]		*m_fac*phi_fac
              );
     }
   
#ifdef TRACKER_LOG
   fprintf(fpinfo,"%8.4f %10ld %10.0f %8.4f %8.4f %8.4f %8.2f %8.2f %8.2f %12.6g %10.2f %8.2f %10.2f %8.2f %10.6f %12.4g %12.4g %12.4g %10.6f %10.6f %10.6f %10.6f %10.6f %10.6f %10.6f %10.6f %10.6f %10.6f %10.6f %10.6f %8.2f %10.2f %6d       %12.6g   %12.6g  %10.5f      %10.5f   %10.5f     %12.6f\n",
           global.z,
           cur_halo->npart,                     /* number of particles         */
           cur_halo->M_vir,                     /* number of virtual particles */
           cur_halo->pos.x*x_fac, 
           cur_halo->pos.y*x_fac,              /* comoving Mpc/h  */
           cur_halo->pos.z*x_fac,
           cur_halo->vel.x*v_fac, 
           cur_halo->vel.y*v_fac,              /* comoving peculiar km/sec */
           cur_halo->vel.z*v_fac,
           cur_halo->M_vir*m_fac,               /* Msun/h */
           cur_halo->R_vir*x_fac*1000.,         /* kpc/h  */
           sqrt(cur_halo->V2_max*phi_fac),      /* km/sec */
           cur_halo->R_max*x_fac*1000.,         /* kpc/h  */
           cur_halo->sigV*v_fac,               /* km/sec */
           cur_halo->lambda*v_fac*sqrt(r_fac/(Grav*m_fac)),
#ifdef AHFabsangmom
           cur_halo->AngMom.x*r_fac*v_fac*m_fac,
           cur_halo->AngMom.y*r_fac*v_fac*m_fac,
           cur_halo->AngMom.z*r_fac*v_fac*m_fac,
#else
           cur_halo->AngMom.x,
           cur_halo->AngMom.y,
           cur_halo->AngMom.z,
#endif
           cur_halo->axis.x,
           cur_halo->E1.x,
           cur_halo->E1.y,
           cur_halo->E1.z,
           cur_halo->axis.y,
           cur_halo->E2.x,
           cur_halo->E2.y,
           cur_halo->E2.z,
           cur_halo->axis.z,
           cur_halo->E3.x,
           cur_halo->E3.y,
           cur_halo->E3.z,
           cur_halo->ovdens,
           cur_halo->R_edge*x_fac*1000.,
           cur_halo->prof.nbins,
           cur_halo->Ekin*m_fac*pow2(v_fac),
           cur_halo->Epot*m_fac*phi_fac,
           cur_halo->mbp_offset*x_fac*1000.,
           cur_halo->com_offset*x_fac*1000.,
           cur_halo->r2*x_fac*1000.,
           cur_halo->lambdaE
           );
   fflush(fpinfo);
#endif 
}

/*==============================================================================
* wrapper for call to input for appropriate binary reading routine
*==============================================================================*/
void read_simu(char *filename)
{
   double omega,lambda,ovlim,rho_crit,rho_b,a,a2,a3,rho_vir;

   input(filename);

   /* copy things to AMIGA common blocks */
   /*------------------------------------*/
   /* simu. */
   init_simu_structure(user_dummy);
   /* global. */
   init_global_structure(user_dummy);

   /* declare conversion factors */
   x_fac       = simu.boxsize;
   r_fac       = simu.boxsize*global.a;
   v_fac       = simu.boxsize/simu.t_unit/global.a;
   m_fac       = simu.pmass;
   rho_fac     = simu.pmass/pow3(simu.boxsize);
   phi_fac     = Grav*simu.pmass/(simu.boxsize*global.a);
   Hubble      = calc_Hubble(global.a);
   x_fac_inv   = (double)1.0/x_fac;
   v_fac_inv   = (double)1.0/v_fac;
   m_fac_inv   = (double)1.0/m_fac;
   rho_fac_inv = (double)1.0/rho_fac;
   phi_fac_inv = (double)1.0/phi_fac;
      
   
   fprintf(stderr,"Kristin: conversion factors: %lf %lf %g %lf\n", x_fac, v_fac, m_fac, r_fac); 
   fprintf(stderr,"Kristin: simu.no_gas: %ld\n", simu.no_gas); 
   
   /*====================================== AMIGA particles ======================================*/
   /* transfer to AMIGA particle arrays */

      
   /*================================
    * define virial overdensity etc.
    *================================*/
   fprintf(stderr,"a=%g\n",global.a);
   a        = global.a;
   a2       = pow2(global.a);
   a3       = pow3(global.a);
   rho_crit = rhoc0 * (simu.lambda0*(1.-1./a2) + simu.omega0*(1./a3-1./a2) + 1./a2);
   omega    =    simu.omega0  / (a+simu.omega0*(1.-a)+simu.lambda0*(a3-a));
   lambda   = a3*simu.lambda0 / (a+simu.omega0*(1.-a)+simu.lambda0*(a3-a));
   
   ovlim    = calc_virial(a);
   rho_b    =         a3 * omega * rho_crit;  /* comoving(!) background density */
   rho_vir  = ovlim * a3 * omega * rho_crit;  /* comoving(!)   virial   density */
   
   global.ovlim   = ovlim;
   global.rho_b   = rho_b;
   global.rho_vir = rho_vir;

#ifdef MULTIMASS
#ifdef AHFmmfocus

/* remove those massive particles just like it is done in startrun.c */
  if(simu.no_species > 1)
    {
     long unsigned no_part;
     double        no_vpart;
     partptr       fst_part, cur_part, new_part;
      

     /* determine simu.min_weight */
     simu.min_weight = 100000000000000;
     for(cur_part=global.fst_part; cur_part<(global.fst_part+simu.no_part); cur_part++)
	 if(cur_part->weight < simu.min_weight)
	     simu.min_weight = cur_part->weight;

     no_part  = 0;
     no_vpart = 0.0;
     for(cur_part=global.fst_part; cur_part<(global.fst_part+simu.no_part); cur_part++)
       {
        if(fabs(cur_part->weight-simu.min_weight) < ZERO)
        //if(cur_part->weight < 10)
          {
           no_part++;
           no_vpart += cur_part->weight;
          }
       }
     
     /* allocate memory for particles */
     fst_part = c_part(no_part);
     
     /* shift particles to new array */
     new_part = fst_part;
     for(cur_part=global.fst_part; cur_part<(global.fst_part+simu.no_part); cur_part++)
       {
        if(fabs(cur_part->weight-simu.min_weight) < ZERO)
        //if(cur_part->weight < 10)
          {
           new_part->pos[X] = cur_part->pos[X];
           new_part->pos[Y] = cur_part->pos[Y];
           new_part->pos[Z] = cur_part->pos[Z];
           new_part->mom[X] = cur_part->mom[X];
           new_part->mom[Y] = cur_part->mom[Y];
           new_part->mom[Z] = cur_part->mom[Z];
           new_part->weight = cur_part->weight;
           new_part++;
          }
       }
     
     /* erase old particle list and store new one */
     free(global.fst_part);
     global.fst_part = fst_part;
     
     /* update simulation parameter */
     simu.no_part       = no_part;
     simu.no_vpart      = no_vpart;
     io.header.no_part  = no_part;
     io.header.no_vpart = no_vpart;
     
     fprintf(stderr,"\nAHFmmfocus analysis:\n");
     fprintf(stderr,"====================\n");
     fprintf(stderr,"min_weight  = %g\n",  simu.min_weight);
     fprintf(stderr,"no_part     = %ld\n", simu.no_part);
     fprintf(stderr,"no_vpart    = %g\n",  simu.no_vpart);
     
     
    }

#endif
#endif
   
}

/*==============================================================================
*  check rotation curve for maximum
*==============================================================================*/
void get_RmaxVmax(double *Vcirc2, double *r, int nbins, int ismooth, double RV_max[2])
{
   int    ibin, ibin_max[2];
   double V2max;

   smooth3(Vcirc2,  nbins, ismooth);

   V2max	       = -10.0;
   ibin_max[0]    = nbins-1;
   ibin_max[1]    = nbins-1;

   /* approach from left */
   for(ibin=0; ibin<nbins-1; ibin++)
   {
      if(Vcirc2[ibin] > V2max)
      {
         V2max	    = Vcirc2[ibin];
         ibin_max[0] = ibin;
      }
   }

   /* approch from right */
   for(ibin=(nbins-1+ibin_max[0])/2; ibin>ibin_max[0]; ibin--)
   {
      if(Vcirc2[ibin] > V2max)
      {
         V2max	    = Vcirc2[ibin];
         ibin_max[1] = ibin;
      }
   }

   RV_max[0] = (	  r[ibin_max[0]] +	r[ibin_max[1]])/2;
   RV_max[1] = (Vcirc2[ibin_max[0]] + Vcirc2[ibin_max[1]])/2;   
}

/*==============================================================================
 *  check dens_r2=dens*r2 for maximum
 *==============================================================================*/
double get_r2(double *dens_r2, double *r, int nbins, int ismooth)
{
   int    ibin, ibin_r2max;
   double dens_r2_max[2], r2_max[2];

   smooth3(dens_r2, nbins, ismooth);

   dens_r2_max[0] = -10.0;
   dens_r2_max[1] = -10.0;
   r2_max[0]      = -10.0;
   r2_max[1]      = -10.0;
   ibin_r2max     = nbins-1;

   /* approch from left */
   for(ibin=0; ibin<nbins-1; ibin++)
   {
      if(dens_r2[ibin] > dens_r2_max[0])
      {
         dens_r2_max[0] = dens_r2[ibin];
         r2_max[0]      = (r[ibin]+r[ibin+1])/2;
         ibin_r2max     = ibin;
      }
   }

   /* approch from right */
   for(ibin=(nbins-1+ibin_r2max)/2; ibin>0; ibin--)
   {	  
      if(dens_r2[ibin] > dens_r2_max[1])
      {
         dens_r2_max[1] = dens_r2[ibin];
         r2_max[1]      = (r[ibin]+r[ibin+1])/2;
      }
   }

   return((r2_max[0]+r2_max[1])/2);
}

/*----------------------------------------------------------------------------
* create_halo:
* same as c_part but this time for a halo structure...
*----------------------------------------------------------------------------*/
HALO *create_halo(long block_size)
{
   HALO     *HALOblock;
   HALO     *tmp_halo;
   int       i;        /* coordinate index (X,Y,Z)                     */
   
   /* generate block_size halos */
   if((HALOblock = (HALO *) calloc(block_size, sizeof(HALO))) == NULL)
     {
      fprintf(io.logfile,"create_halo: error callocing halos\n");
      fflush(io.logfile);
      fclose(io.logfile);
      exit(1);
     }
   
   /*
    * loop over halos filling values with 0.0 and setting pointers to NULL.
    */
   
   for(tmp_halo = HALOblock; tmp_halo < HALOblock + block_size; tmp_halo++)
     {
      tmp_halo->pos.x     = 0.0;
      tmp_halo->pos.y     = 0.0;
      tmp_halo->pos.z     = 0.0;
      tmp_halo->vel.x     = 0.0;
      tmp_halo->vel.y     = 0.0;
      tmp_halo->vel.z     = 0.0;
      tmp_halo->AngMom.x  = 0.0;
      tmp_halo->AngMom.y  = 0.0;
      tmp_halo->AngMom.z  = 0.0;
      
      tmp_halo->ovdens    = 0.0;
      tmp_halo->M_vir     = 0.0;
      tmp_halo->R_vir     = 0.0;
//      tmp_halo->M_bnd     = 0.0;
//      tmp_halo->R_bnd     = 0.0;
      tmp_halo->R_max     = 0.0;
      tmp_halo->V2_max    = 0.0;
      tmp_halo->sigV    = 0.0;
      tmp_halo->lambda    = 0.0;
      tmp_halo->axis.x     = 0.0;
      tmp_halo->axis.y     = 0.0;
      tmp_halo->axis.z     = 0.0;
//      tmp_halo->ref_level = 0;
      
      tmp_halo->prof.nbins   = 0;
      tmp_halo->prof.r       = NULL;
      tmp_halo->prof.npart   = NULL;
      tmp_halo->prof.nvpart  = NULL;
      tmp_halo->prof.ovdens  = NULL;
      tmp_halo->prof.dens    = NULL;
      tmp_halo->prof.v2_circ = NULL;
      tmp_halo->prof.sig_v   = NULL;
      tmp_halo->prof.Lx      = NULL;
      tmp_halo->prof.Ly      = NULL;
      tmp_halo->prof.Lz      = NULL;
      tmp_halo->prof.axis1   = NULL;
      tmp_halo->prof.E1x     = NULL;
      tmp_halo->prof.E1y     = NULL;
      tmp_halo->prof.E1z     = NULL;
      tmp_halo->prof.axis2   = NULL;
      tmp_halo->prof.E2x     = NULL;
      tmp_halo->prof.E2y     = NULL;
      tmp_halo->prof.E2z     = NULL;
      tmp_halo->prof.axis3   = NULL;
      tmp_halo->prof.E3x     = NULL;
      tmp_halo->prof.E3y     = NULL;
      tmp_halo->prof.E3z     = NULL;
      
      /* initialize all bound particle storage */
      tmp_halo->npart  = 0;
      tmp_halo->ipart   = NULL;
      
#ifdef TRACKER_DEBRIS
      /* initialize all unbound particle storage */
      tmp_halo->nupart = 0;
      tmp_halo->iupart  = NULL;

      /* initialize all outlying particle storage */
      tmp_halo->nopart = 0;
      tmp_halo->iopart  = NULL;
#endif
     }
   return HALOblock;
   
}

/*----------------------------------------------------------------------------
* dest_halo:
* destroy all pointers associated with halo
*----------------------------------------------------------------------------*/
void dest_halo(HALO *cur_halo)
{
   free(cur_halo->ipart);
   cur_halo->ipart  = NULL;
   
#ifdef TRACKER_DEBRIS
   if (cur_halo->iupart) free(cur_halo->iupart);
   cur_halo->iupart = NULL;

   if (cur_halo->iopart) free(cur_halo->iopart);
   cur_halo->iopart = NULL;
#endif

   dest_profile(cur_halo);
}

/*==============================================================================
*  calculate all relevant integral (i.e. "virial") properties
*==============================================================================*/
void calc_Pvir(HALO *cur_halo)
{
   double        Rleft, Rright, ODleft, ODright, Dleft, Dright;
   double        mom[NDIM], weight, M_vir;
   double        sigV, lambda, axis1, axis2, axis3, Lx, Ly, Lz, absAngMom;
   double        a11, a22, a33, a12, a13, a23, itensor[3][3];
   double        Xc, Yc, Zc, VXc, VYc, VZc;
   double        Xp, Yp, Zp, VXp, VYp, VZp;
   double        dX, dY, dZ, dVX, dVY, dVZ;
   long unsigned jpart;
   int           ibin;
   partptr       cur_part, most_bound;
   double        pre_dist,I_mid,I_now,I_prev,Phi0,Phi,Epot,Ekin,Epart,Tpart,Upart,Emin,dist;
   double        CoM[NDIM], tmp1, tmp2, tmp3;
   
#ifdef GAS_PARTICLES
   gasptr        cur_gas;
   long unsigned ngas;
   double        M_gas;
   double        Lx_gas, Ly_gas, Lz_gas;
   double        Ekin_gas, Epot_gas, v_therm;
   double        a11_gas,a22_gas,a33_gas,a12_gas,a13_gas,a23_gas;
#endif
   
   /* halo too small ? */
   if(cur_halo->npart < TRK_MINPART) return;
   
#ifdef TRACKER_LOG
   fprintf(fpinfo,"         getting integral properties...");
   fflush(fpinfo);
#endif
   
   /* use cur_halo->profile to determine virial/truncation radius */
   if(cur_halo->prof.ovdens[cur_halo->prof.nbins-1] < global.ovlim)
     {
      ibin = MAX(0,cur_halo->prof.nbins-2);
      while(cur_halo->prof.ovdens[ibin] < global.ovlim && ibin > 0)
         ibin--;
      
      Rleft   = cur_halo->prof.r[ibin];
      Rright  = cur_halo->prof.r[ibin+1];
      ODleft  = cur_halo->prof.ovdens[ibin];
      ODright = cur_halo->prof.ovdens[ibin+1];
      Dleft   = cur_halo->prof.dens[ibin];
      Dright  = cur_halo->prof.dens[ibin+1];
      
      /* get interpolated virial radius */
      cur_halo->R_vir  = (Rleft*(global.ovlim-ODright)+Rright*(ODleft-global.ovlim))
         /                  (ODleft-ODright);
      
      /* spot on virial overdensity... */
      cur_halo->ovdens = global.ovlim;
      
#ifdef TRACKER_LOG
      fprintf(fpinfo,"new R_vir=%g (ibin=%d)...\n",
              1000.*cur_halo->R_vir*x_fac,ibin);
      fflush(fpinfo);
#endif
      
     }
   else
     {
      /* stick to current radius, simply nothing to be done...  */
      cur_halo->ovdens = cur_halo->prof.ovdens[cur_halo->prof.nbins-1];
      
#ifdef TRACKER_LOG
      fprintf(fpinfo,"\n");
      fflush(fpinfo);
#endif
     }
   
   /* get rid of all particles outside R_vir (updates M_vir/npart) */
   rem_outsideRvir(cur_halo,1);
   
   /* check halo credibility */
   if(cur_halo->npart < TRK_MINPART) return;
   
   /* loop over all particles calculating mean halo velocity */
   mom[X] = 0.0;
   mom[Y] = 0.0;
   mom[Z] = 0.0;
   M_vir  = 0.0;
   for(jpart=0; jpart<cur_halo->npart; jpart++)
     {
      cur_part = global.fst_part + cur_halo->ipart[jpart];
#ifdef MULTIMASS
      weight   = cur_part->weight;
#else
      weight   = 1.0;
#endif
      M_vir  += weight;
      mom[X] += weight*cur_part->mom[X];
      mom[Y] += weight*cur_part->mom[Y];
      mom[Z] += weight*cur_part->mom[Z];
     }
   
   /* store newly calculated halo velocity */
   cur_halo->M_vir  = M_vir;
   cur_halo->vel.x = mom[X]/cur_halo->M_vir;
   cur_halo->vel.y = mom[Y]/cur_halo->M_vir;
   cur_halo->vel.z = mom[Z]/cur_halo->M_vir;
   
   
   /* halo position and velocity */
   Xc     = cur_halo->pos.x;
   Yc     = cur_halo->pos.y;
   Zc     = cur_halo->pos.z;
   VXc    = cur_halo->vel.x;
   VYc    = cur_halo->vel.y;
   VZc    = cur_halo->vel.z;
   
   
   /* loop over all particles calculating integral halo properties */
   sigV   = 0.0;
   Lx     = 0.0;
   Ly     = 0.0;
   Lz     = 0.0;
   a11    = 0.0;
   a22    = 0.0;
   a33    = 0.0;
   a12    = 0.0;
   a13    = 0.0;
   a23    = 0.0;
   
   Phi0       = cur_halo->Phi0;
   Phi        = 0.0;
   Epot       = 0.0;
   Ekin       = 0.0;
   pre_dist   = 0.0;
   I_prev     = 0.0;
   M_vir      = 0.0;
   Emin       = 1e30;
   most_bound = NULL;
   
   CoM[X]    = 0.0;
   CoM[Y]    = 0.0;
   CoM[Z]    = 0.0;

#ifdef GAS_PARTICLES
   ngas       = 0;
   M_gas      = 0.0;
   Lx_gas     = 0.0;
   Ly_gas     = 0.0;
   Lz_gas     = 0.0;
   Ekin_gas   = 0.0;
   Epot_gas   = 0.0;
   a11_gas    = 0.0;
   a22_gas    = 0.0;
   a33_gas    = 0.0;
   a12_gas    = 0.0;
   a13_gas    = 0.0;
   a23_gas    = 0.0;
#endif

   for(jpart=0; jpart<cur_halo->npart; jpart++)
     {
      cur_part = global.fst_part + cur_halo->ipart[jpart];
#ifdef MULTIMASS
      weight   = cur_part->weight;
#else
      weight   = 1.0;
#endif
      
      M_vir += weight;
      
      /* particle position */
      Xp  = (double)cur_part->pos[X];
      Yp  = (double)cur_part->pos[Y];
      Zp  = (double)cur_part->pos[Z];
      
      /* put particle into halo rest frame */
      dX  = (Xp - Xc);
      dY  = (Yp - Yc);
      dZ  = (Zp - Zc);
      
      /* take care of periodic boundary conditions */
      if(dX >  0.5) dX -= 1.0;
      if(dY >  0.5) dY -= 1.0;
      if(dZ >  0.5) dZ -= 1.0;
      if(dX < -0.5) dX += 1.0;
      if(dY < -0.5) dY += 1.0;
      if(dZ < -0.5) dZ += 1.0;
      
      /* centre of mass with correct boundaries... */
      CoM[X] += weight*(cur_halo->pos.x+dX);
      CoM[Y] += weight*(cur_halo->pos.y+dY);
      CoM[Z] += weight*(cur_halo->pos.z+dZ);
      
      /* current distance */
      dist = sqrt(pow2(dX) + pow2(dY) + pow2(dZ));

      /* particle velocity in halo rest frame */
      VXp = (double)cur_part->mom[X];
      VYp = (double)cur_part->mom[Y];
      VZp = (double)cur_part->mom[Z];
      dVX = (VXp - VXc);
      dVY = (VYp - VYc);
      dVZ = (VZp - VZc);
      
      /* angular momentum of particles in shell */
      Lx += weight*(dY*dVZ-dZ*dVY);
      Ly += weight*(dZ*dVX-dX*dVZ);
      Lz += weight*(dX*dVY-dY*dVX);
      
      /* velocity dispersion of particles in shell */
      dVX   +=  Hubble * dX * r_fac/v_fac;
      dVY   +=  Hubble * dY * r_fac/v_fac;
      dVZ   +=  Hubble * dZ * r_fac/v_fac;
      
      Tpart  = weight*(pow2(dVX)+pow2(dVY)+pow2(dVZ));
      sigV  += Tpart;
      Ekin  += Tpart;
      
#ifdef AHFreducedinertiatensor
      /* inertia tensor of all particles within sphere(!) */
      a11 += weight*dX*dX/pow2(dist);
      a22 += weight*dY*dY/pow2(dist);
      a33 += weight*dZ*dZ/pow2(dist);
      a12 += weight*dX*dY/pow2(dist);
      a13 += weight*dX*dZ/pow2(dist);
      a23 += weight*dY*dZ/pow2(dist);
#else
      /* inertia tensor of all particles within sphere(!) */
      a11 += weight*dX*dX;
      a22 += weight*dY*dY;
      a33 += weight*dZ*dZ;
      a12 += weight*dX*dY;
      a13 += weight*dX*dZ;
      a23 += weight*dY*dZ;
#endif

      /* get potential energy */
      if(dist > ZERO)
        {
         I_now = M_vir/pow2(dist);
         I_mid = (I_now + I_prev)/2.;
         Phi  += I_mid * (dist - pre_dist);
         
         Upart = (Phi-Phi0)*weight;
         Epot += Upart;
        }
      else
        {
         /* simply use old Phi value... */
         Upart = (Phi-Phi0)*weight;
         Epot += Upart;
        }
      I_prev   = I_now;
      pre_dist = dist;
      
      /* energy of current particle */
      Epart = (0.5*Tpart + Upart);
      
      if(Epart < Emin)
        {
         Emin       = Epart;
         most_bound = cur_part;
        }
      
#ifdef GAS_PARTICLES
      /* current == gas particle => add thermal velocity */
      if(cur_part-global.fst_part-global.offset_gas < global.no_gas && cur_part-global.fst_part-global.offset_gas >= 0)
        {
         cur_gas = global.fst_gas + (cur_part-global.fst_part-global.offset_gas);
         
         /* potential energy of gas alone */
         Epot_gas += Upart;
         
         /* calculate kinetic+internal gas energy */
         Ekin_gas += Tpart;
         
         /* u = 3/2  kT/m  = 1/2  v_therm^2 ?! */
         v_therm   = weight * 2*cur_gas->u/pow2(v_fac);
         
         /* account for thermal energy */
         sigV     += v_therm;
         Ekin     += v_therm;
         Ekin_gas += v_therm;
         
         /* calculate integral properties of gas alone */
         ngas++;
         M_gas   += weight;
         Lx_gas  += weight*(dY*dVZ-dZ*dVY);
         Ly_gas  += weight*(dZ*dVX-dX*dVZ);
         Lz_gas  += weight*(dX*dVY-dY*dVX);
#ifdef AHFreducedinertiatensor
         a11_gas += weight*dX*dX/pow2(dist);
         a22_gas += weight*dY*dY/pow2(dist);
         a33_gas += weight*dZ*dZ/pow2(dist);
         a12_gas += weight*dX*dY/pow2(dist);
         a13_gas += weight*dX*dZ/pow2(dist);
         a23_gas += weight*dY*dZ/pow2(dist);
#else
         a11_gas += weight*dX*dX;
         a22_gas += weight*dY*dY;
         a33_gas += weight*dZ*dZ;
         a12_gas += weight*dX*dY;
         a13_gas += weight*dX*dZ;
         a23_gas += weight*dY*dZ;
#endif
        }
#endif

     }  /* jpart */
   
   /* centre of mass */
   CoM[X] /= M_vir;
   CoM[Y] /= M_vir;
   CoM[Z] /= M_vir;
   CoM[X]  = fmod(CoM[X]+1.,1.);
   CoM[Y]  = fmod(CoM[Y]+1.,1.);
   CoM[Z]  = fmod(CoM[Z]+1.,1.);
   
  /* get eigenavalues of inertia tensor */
  itensor[0][0] = a11;
  itensor[1][1] = a22;
  itensor[2][2] = a33;
  itensor[0][1] = a12;
  itensor[1][0] = a12;
  itensor[0][2] = a13;
  itensor[2][0] = a13;
  itensor[1][2] = a23;
  itensor[2][1] = a23;
  get_axes(itensor, &axis1, &axis2, &axis3);
   
  cur_halo->sigV   = sqrt(sigV/cur_halo->M_vir);
  cur_halo->Ekin   = 0.5*Ekin;
  cur_halo->Epot   = 0.5*Epot;

  absAngMom        = sqrt(pow2(Lx)+pow2(Ly)+pow2(Lz));
#ifdef AHFabsangmom
  cur_halo->AngMom.x = Lx;
  cur_halo->AngMom.y = Ly;
  cur_halo->AngMom.z = Lz;
#else
  cur_halo->AngMom.x = Lx/absAngMom;
  cur_halo->AngMom.y = Ly/absAngMom;
  cur_halo->AngMom.z = Lz/absAngMom;
#endif
  cur_halo->axis.x = 1.0;
  cur_halo->axis.y = sqrt(axis2/axis1);
  cur_halo->axis.z = sqrt(axis3/axis1);
  cur_halo->E1.x  = itensor[0][0];
  cur_halo->E1.y  = itensor[1][0];
  cur_halo->E1.z  = itensor[2][0];
  cur_halo->E2.x  = itensor[0][1];
  cur_halo->E2.y  = itensor[1][1];
  cur_halo->E2.z  = itensor[2][1];
  cur_halo->E3.x  = itensor[0][2];
  cur_halo->E3.y  = itensor[1][2];
  cur_halo->E3.z  = itensor[2][2];
  cur_halo->lambda = sqrt( (pow2(Lx)+pow2(Ly)+pow2(Lz))
                           / (2. * pow3(cur_halo->M_vir) * cur_halo->R_vir )
                           );

   /* energy-based lambda: careful with numerics... */
   tmp1 = sqrt(m_fac*cur_halo->M_vir);      /* tmp1 = sqrt(M)         */
   tmp1 = pow5(tmp1);                       /* tmp1 = M^5/2           */
   tmp2 = cur_halo->Ekin*m_fac*pow2(v_fac); /* tmp2 = Ekin            */
   tmp3 = cur_halo->Epot*m_fac*phi_fac;     /* tmp3 = Epot            */
   tmp2 = fabs(tmp2+tmp3);                  /* tmp2 = E               */
   tmp2 = sqrt(tmp2);                       /* tmp2 = sqrt(|E|)       */
   tmp1 = tmp2/tmp1;                        /* tmp1 = sqrt(|E|)/M^5/2 */
   tmp2 = m_fac*r_fac*v_fac*absAngMom;      /* tmp2 = L               */
   tmp3 = tmp1*tmp2;
   cur_halo->lambdaE  = tmp3/Grav;


   /* calculate offsets */
   dX = most_bound->pos[X]-cur_halo->pos.x;
   dY = most_bound->pos[Y]-cur_halo->pos.y;
   dZ = most_bound->pos[Z]-cur_halo->pos.z;
   if(dX >  0.5) dX -= 1.0;
   if(dY >  0.5) dY -= 1.0;
   if(dZ >  0.5) dZ -= 1.0;
   if(dX < -0.5) dX += 1.0;
   if(dY < -0.5) dY += 1.0;
   if(dZ < -0.5) dZ += 1.0;      
   cur_halo->mbp_offset = sqrt(pow2(dX) + pow2(dY) + pow2(dZ));
   
   dX = CoM[X]-cur_halo->pos.x;
   dY = CoM[Y]-cur_halo->pos.y;
   dZ = CoM[Z]-cur_halo->pos.z;
   if(dX >  0.5) dX -= 1.0;
   if(dY >  0.5) dY -= 1.0;
   if(dZ >  0.5) dZ -= 1.0;
   if(dX < -0.5) dX += 1.0;
   if(dY < -0.5) dY += 1.0;
   if(dZ < -0.5) dZ += 1.0;      
   cur_halo->com_offset = sqrt(pow2(dX) + pow2(dY) + pow2(dZ));
   
#ifdef GAS_PARTICLES
   cur_halo->gas.npart = ngas;
   cur_halo->gas.M_vir = M_gas;
   
   if(ngas > 0)
     {
      /* get spin parameter of gas particles (all gas part's within sphere!) */
      absAngMom               = sqrt(pow2(Lx_gas)+pow2(Ly_gas)+pow2(Lz_gas));
      cur_halo->gas.AngMom.x  = Lx_gas/absAngMom;
      cur_halo->gas.AngMom.y  = Ly_gas/absAngMom;
      cur_halo->gas.AngMom.z  = Lz_gas/absAngMom;
      cur_halo->gas.Ekin      = 0.5*Ekin_gas;
      cur_halo->gas.Epot      = 0.5*Epot_gas;
      cur_halo->gas.lambda    = absAngMom/M_gas/sqrt(2. * cur_halo->M_vir * cur_halo->R_vir);
      cur_halo->gas.lambda   *= v_fac*sqrt(r_fac/(Grav*m_fac));
      /* energy-based lambda: careful with numerics... */
      {
         tmp1 = sqrt(m_fac*cur_halo->M_vir);          /* tmp1 = sqrt(M_tot)             */
         tmp1 = pow3(tmp1);                           /* tmp1 = M_tot^3/2               */
         tmp2 = cur_halo->gas.Ekin*m_fac*pow2(v_fac); /* tmp2 = Ekin_gas                */
         tmp3 = cur_halo->gas.Epot*m_fac*phi_fac;     /* tmp3 = Epot_gas                */
         tmp2 = fabs(tmp2+tmp3);                      /* tmp2 = E_gas                   */
         tmp2 = sqrt(tmp2);                           /* tmp2 = sqrt(|E_gas|)           */
         tmp1 = tmp2/tmp1;                            /* tmp1 = sqrt(|E_gas|)/M_tot^3/2 */
         tmp2 = m_fac*r_fac*v_fac*absAngMom;          /* tmp2 = L_gas                   */
         tmp2 = tmp2/(m_fac*cur_halo->gas.M_vir);      /* tmp2 = L_gas/M_gas             */
         tmp3 = tmp1*tmp2;
         
         cur_halo->gas.lambdaE = tmp3/Grav;
      }
      
      /* get shape of gas particles (all gas part's within sphere!) */
      itensor[0][0] = a11_gas;
      itensor[1][1] = a22_gas;
      itensor[2][2] = a33_gas;
      itensor[0][1] = a12_gas;
      itensor[1][0] = a12_gas;
      itensor[0][2] = a13_gas;
      itensor[2][0] = a13_gas;
      itensor[1][2] = a23_gas;
      itensor[2][1] = a23_gas;
      get_axes(itensor, &axis1, &axis2, &axis3);
      cur_halo->gas.axis.x  = 1.0;
      cur_halo->gas.axis.y  = sqrt(axis2/axis1);
      cur_halo->gas.axis.z  = sqrt(axis3/axis1);
      cur_halo->gas.E1.x  = itensor[0][0];
      cur_halo->gas.E1.y  = itensor[1][0];
      cur_halo->gas.E1.z  = itensor[2][0];
      cur_halo->gas.E2.x  = itensor[0][1];
      cur_halo->gas.E2.y  = itensor[1][1];
      cur_halo->gas.E2.z  = itensor[2][1];
      cur_halo->gas.E3.x  = itensor[0][2];
      cur_halo->gas.E3.y  = itensor[1][2];
      cur_halo->gas.E3.z  = itensor[2][2];
     }
   else
     {
      cur_halo->gas.AngMom.x = 0.0;
      cur_halo->gas.AngMom.y = 0.0;
      cur_halo->gas.AngMom.z = 0.0;
      cur_halo->gas.lambda   = 0.0;
      cur_halo->gas.lambdaE  = 0.0;
      cur_halo->gas.Ekin     = 0.0;
      cur_halo->gas.Epot     = 0.0;
      cur_halo->gas.axis.x   = 0.0;
      cur_halo->gas.axis.y   = 0.0;
      cur_halo->gas.axis.z   = 0.0;
      cur_halo->gas.E1.x     = 0.0;
      cur_halo->gas.E1.y     = 0.0;
      cur_halo->gas.E1.z     = 0.0;
      cur_halo->gas.E2.x     = 0.0;
      cur_halo->gas.E2.y     = 0.0;
      cur_halo->gas.E2.z     = 0.0;
      cur_halo->gas.E3.x     = 0.0;
      cur_halo->gas.E3.y     = 0.0;
      cur_halo->gas.E3.z     = 0.0;
     }
#endif

}

              
              
              
void set_user_data(uparam *user_data, char *outprefix)
{
  sprintf(user_data->icfile_name," ");
  sprintf(user_data->dumpfile_name, " ");
  sprintf(user_data->outfile_prefix, "%s.", outprefix);
#ifdef LIGHTCONE
  sprintf(user_data->lightcone_prefix, " ");
#endif /* LIGHTCONE */

  user_data->NGRID_DOM      = 0;

  user_data->Nth_dom        = 0.0;
  user_data->Nth_ref        = 0.0;

  user_data->final_z        = 0.0;

  user_data->out_dumps      = 0;
  user_data->no_outputs     = 0;
//  double *user_data->z_out;

#ifdef LIGHTCONE
  user_data->lightcone_z    = 0.0;
  user_data->lightcone_type = 0;
  user_data->sa  		 = 0;
  user_data->sb 		 = 0;
  user_data->sg 		 = 0;   
  user_data->dcx		 = 0.0;
  user_data->dcy		 = 0.0;
#endif /* LIGHTCONE */

#ifdef MOND
  user_data->g0 	         = 0.0;  // cm/s^2
  user_data->h0	         = 0.0;  // km/sec/Mpc
#endif

#if (defined ART && defined HYDRO)
  /* the hydro-parameters are not stored in the ART binary */
  user_data->omegab         = 0.0;
  user_data->gamma	         = 0.0;
  user_data->H_frac         = 0.0;
  user_data->T_init         = 0.0;
  user_data->B_init         = 0.0;
#endif
}
