Beispiel #1
0
double bias_model_BPR_integral(cosmo_info **cosmo, double z) {
    double       z_max = 10000.;
    interp_info *interp;
    if(!ADaPS_exist(*cosmo, "bias_model_BPR_Iz_interp")) {
        int     n_int;
        int     i_int;
        double  dz;
        double  Omega_M, Omega_k, Omega_Lambda;
        double  z_temp;
        double *x_int;
        double *y_int;
        double  log_z;
        double  dlog_z;
        n_int        = 250;
        Omega_M      = ((double *)ADaPS_fetch(*cosmo, "Omega_M"))[0];
        Omega_k      = ((double *)ADaPS_fetch(*cosmo, "Omega_k"))[0];
        Omega_Lambda = ((double *)ADaPS_fetch(*cosmo, "Omega_Lambda"))[0];
        x_int        = (double *)SID_malloc(sizeof(double) * n_int);
        y_int        = (double *)SID_malloc(sizeof(double) * n_int);
        i_int        = 0;
        x_int[i_int] = 0.;
        y_int[i_int] = pow((1. + x_int[i_int]) / E_z(Omega_M, Omega_k, Omega_Lambda, x_int[i_int]), 3.);
        i_int++;
        x_int[i_int] = take_log10(z_max) / (double)(n_int - 1);
        y_int[i_int] = pow((1. + x_int[i_int]) / E_z(Omega_M, Omega_k, Omega_Lambda, x_int[i_int]), 3.);
        log_z        = take_log10(x_int[i_int]);
        dlog_z       = (take_log10(z_max) - log_z) / (double)(n_int - 2);
        for(i_int++, log_z += dlog_z; i_int < (n_int - 1); i_int++, log_z += dlog_z) {
            x_int[i_int] = take_alog10(log_z);
            y_int[i_int] = pow((1. + x_int[i_int]) / E_z(Omega_M, Omega_k, Omega_Lambda, x_int[i_int]), 3.);
        }
        x_int[i_int] = z_max;
        y_int[i_int] = pow((1. + x_int[i_int]) / E_z(Omega_M, Omega_k, Omega_Lambda, x_int[i_int]), 3.);
        init_interpolate(x_int, y_int, (size_t)n_int, gsl_interp_cspline, &interp);
        SID_free(SID_FARG x_int);
        SID_free(SID_FARG y_int);
        ADaPS_store_interp(cosmo, (void *)(interp), "bias_model_BPR_Iz_interp");

    } else
        interp = (interp_info *)ADaPS_fetch(*cosmo, "bias_model_BPR_Iz_interp");
    return (interpolate_integral(interp, z, z_max));
}
Beispiel #2
0
void map_to_grid(size_t      n_particles_local,
                 GBPREAL *   x_particles_local,
                 GBPREAL *   y_particles_local,
                 GBPREAL *   z_particles_local,
                 GBPREAL *   v_particles_local,
                 GBPREAL *   w_particles_local,
                 cosmo_info *cosmo,
                 double      redshift,
                 int         distribution_scheme,
                 double      normalization_constant,
                 field_info *field,
                 field_info *field_norm,
                 int         mode) {
    size_t       i_p;
    int          i_k;
    size_t       i_b;
    size_t       i_grid;
    int          i_coord;
    int          i_i[3];
    int          j_i[3];
    int          k_i[3];
    size_t       n_particles;
    double       v_p;
    double       w_p;
    int          flag_valued_particles;
    int          flag_weight_particles;
    int          flag_weight;
    int          flag_active;
    int          flag_viable;
    double       k_mag;
    double       dk;
    int          n_powspec;
    int          mode_powspec;
    size_t *     n_mode_powspec;
    double *     k_powspec;
    double *     kmin_powspec;
    double *     kmax_powspec;
    double *     k_powspec_bin;
    double *     P_powspec;
    double *     dP_powspec;
    double       k_min;
    double       k_max;
    double       norm_local;
    double       normalization;
    GBPREAL      x_i;
    GBPREAL      x_particle_i;
    GBPREAL      y_particle_i;
    GBPREAL      z_particle_i;
    double       kernal_offset;
    int          W_search_lo;
    int          W_search_hi;
    size_t       receive_left_size  = 0;
    size_t       receive_right_size = 0;
    size_t       index_best;
    int          n_buffer[3];
    size_t       n_send_left;
    size_t       n_send_right;
    size_t       send_size_left;
    size_t       send_size_right;
    GBPREAL *    send_left          = NULL;
    GBPREAL *    send_right         = NULL;
    GBPREAL *    receive_left       = NULL;
    GBPREAL *    receive_right      = NULL;
    GBPREAL *    send_left_norm     = NULL;
    GBPREAL *    send_right_norm    = NULL;
    GBPREAL *    receive_left_norm  = NULL;
    GBPREAL *    receive_right_norm = NULL;
    double       r_i, r_min, r_i_max = 0;
    double       W_i;
    int          index_i;
    interp_info *P_k_interp;
    double *     r_Daub;
    double *     W_Daub;
    double       h_Hubble;
    int          n_Daub;
    interp_info *W_r_Daub_interp = NULL;
    int          i_rank;
    size_t       buffer_index;
    int          i_test;
    double       accumulator;

    // Compute the total poulation size and print a status message
    calc_sum_global(&n_particles_local, &n_particles, 1, SID_SIZE_T, CALC_MODE_DEFAULT, SID_COMM_WORLD);
    SID_log("Distributing %zu items onto a %dx%dx%d grid...", SID_LOG_OPEN, n_particles, field->n[0], field->n[1], field->n[2]);

    // If we've been given a normalization field, make sure it's got the same geometry as the results field
    if(field_norm != NULL) {
        if(field->n_d != field_norm->n_d)
            SID_exit_error("grid dimension counts don't match (ie. %d!=%d)", SID_ERROR_LOGIC, field->n_d,
                           field_norm->n_d);
        int i_d;
        for(i_d = 0; i_d < field->n_d; i_d++) {
            if(field->n[i_d] != field_norm->n[i_d])
                SID_exit_error("grid dimension No. %d's sizes don't match (ie. %d!=%d)", SID_ERROR_LOGIC, i_d,
                               field->n[i_d], field_norm->n[i_d]);
            if(field->n_R_local[i_d] != field_norm->n_R_local[i_d])
                SID_exit_error("grid dimension No. %d's slab sizes don't match (ie. %d!=%d)", SID_ERROR_LOGIC, i_d,
                               field->n_R_local[i_d], field_norm->n_R_local[i_d]);
            if(field->i_R_start_local[i_d] != field_norm->i_R_start_local[i_d])
                SID_exit_error("grid dimension No. %d's start positions don't match (ie. %le!=%le)", SID_ERROR_LOGIC,
                               i_d, field->i_R_start_local[i_d], field_norm->i_R_start_local[i_d]);
            if(field->i_R_stop_local[i_d] != field_norm->i_R_stop_local[i_d])
                SID_exit_error("grid dimension No. %d's stop positions don't match (ie. %le!=%le)", SID_ERROR_LOGIC,
                               i_d, field->i_R_stop_local[i_d], field_norm->i_R_stop_local[i_d]);
        }
        if(field->n_field != field_norm->n_field)
            SID_exit_error("grid field sizes don't match (ie. %d!=%d)", SID_ERROR_LOGIC, field->n_field,
                           field_norm->n_field);
        if(field->n_field_R_local != field_norm->n_field_R_local)
            SID_exit_error("grid local field sizes don't match (ie. %d!=%d)", SID_ERROR_LOGIC, field->n_field_R_local,
                           field_norm->n_field_R_local);
        if(field->total_local_size != field_norm->total_local_size)
            SID_exit_error("grid total local sizes don't match (ie. %d!=%d)", SID_ERROR_LOGIC, field->total_local_size,
                           field_norm->total_local_size);
    }

    // Set some variables
    if(v_particles_local != NULL)
        flag_valued_particles = GBP_TRUE;
    else {
        flag_valued_particles = GBP_FALSE;
        v_p                   = 1.;
    }
    if(w_particles_local != NULL)
        flag_weight_particles = GBP_TRUE;
    else {
        flag_weight_particles = GBP_FALSE;
        w_p                   = 1.;
    }
    h_Hubble = ((double *)ADaPS_fetch(cosmo, "h_Hubble"))[0];

    // Initializing the mass assignment scheme
    switch(distribution_scheme) {
        case MAP2GRID_DIST_DWT20:
            W_search_lo   = 2;
            W_search_hi   = 7;
            kernal_offset = 2.5;
            compute_Daubechies_scaling_fctns(20, 5, &r_Daub, &W_Daub, &n_Daub);
            init_interpolate(r_Daub, W_Daub, n_Daub, gsl_interp_cspline, &W_r_Daub_interp);
            SID_free(SID_FARG r_Daub);
            SID_free(SID_FARG W_Daub);
            SID_log("(using D20 scale function kernal)...", SID_LOG_CONTINUE);
            break;
        case MAP2GRID_DIST_DWT12:
            W_search_lo   = 1;
            W_search_hi   = 6;
            kernal_offset = 1.75;
            compute_Daubechies_scaling_fctns(12, 5, &r_Daub, &W_Daub, &n_Daub);
            init_interpolate(r_Daub, W_Daub, (size_t)n_Daub, gsl_interp_cspline, &W_r_Daub_interp);
            SID_free(SID_FARG r_Daub);
            SID_free(SID_FARG W_Daub);
            SID_log("(using D12 scale function kernal)...", SID_LOG_CONTINUE);
            break;
        case MAP2GRID_DIST_TSC:
            W_search_lo = 2;
            W_search_hi = 2;
            SID_log("(using triangular shaped function kernal)...", SID_LOG_CONTINUE);
            break;
        case MAP2GRID_DIST_CIC:
            SID_log("(using cloud-in-cell kernal)...", SID_LOG_CONTINUE);
        case MAP2GRID_DIST_NGP:
        default:
            W_search_lo = 1;
            W_search_hi = 1;
            SID_log("(using nearest grid point kernal)...", SID_LOG_CONTINUE);
            break;
    }

    // Initializing slab buffers
    n_send_left     = (size_t)(field->n[0] * field->n[1] * W_search_lo);
    n_send_right    = (size_t)(field->n[0] * field->n[1] * W_search_hi);
    send_size_left  = n_send_left * sizeof(GBPREAL);
    send_size_right = n_send_right * sizeof(GBPREAL);
    send_left       = (GBPREAL *)SID_calloc(send_size_left);
    send_right      = (GBPREAL *)SID_calloc(send_size_right);
    receive_left    = (GBPREAL *)SID_calloc(send_size_right);
    receive_right   = (GBPREAL *)SID_calloc(send_size_left);
    if(field_norm != NULL) {
        send_left_norm     = (GBPREAL *)SID_calloc(send_size_left);
        send_right_norm    = (GBPREAL *)SID_calloc(send_size_right);
        receive_left_norm  = (GBPREAL *)SID_calloc(send_size_right);
        receive_right_norm = (GBPREAL *)SID_calloc(send_size_left);
    }

    // Clear the field
    if(!SID_CHECK_BITFIELD_SWITCH(mode, MAP2GRID_MODE_NOCLEAN)) {
        SID_log("Clearing fields...", SID_LOG_OPEN);
        clear_field(field);
        if(field_norm != NULL)
            clear_field(field);
        SID_log("Done.", SID_LOG_CLOSE);
    }

    // It is essential that we not pad the field for the simple way that we add-in the boundary buffers below
    set_FFT_padding_state(field, GBP_FALSE);
    if(field_norm != NULL)
        set_FFT_padding_state(field_norm, GBP_FALSE);

    // Create the mass distribution
    SID_log("Performing grid assignment...", SID_LOG_OPEN | SID_LOG_TIMER);

    // Loop over all the objects
    pcounter_info pcounter;
    SID_Init_pcounter(&pcounter, n_particles_local, 10);
    for(i_p = 0, norm_local = 0.; i_p < n_particles_local; i_p++) {
        double norm_i;
        double value_i;
        if(flag_valued_particles)
            v_p = (double)(v_particles_local[i_p]);
        if(flag_weight_particles)
            w_p = (double)(w_particles_local[i_p]);
        norm_i  = w_p;
        value_i = v_p * norm_i;

        // Particle's position
        x_particle_i = (GBPREAL)x_particles_local[i_p];
        y_particle_i = (GBPREAL)y_particles_local[i_p];
        z_particle_i = (GBPREAL)z_particles_local[i_p];

        // Quantize it onto the grid
        x_particle_i /= (GBPREAL)field->dR[0];
        y_particle_i /= (GBPREAL)field->dR[1];
        z_particle_i /= (GBPREAL)field->dR[2];
        i_i[0] = (int)x_particle_i; // position in grid-coordinates
        i_i[1] = (int)y_particle_i; // position in grid-coordinates
        i_i[2] = (int)z_particle_i; // position in grid-coordinates

        // Apply the kernel
        flag_viable = GBP_TRUE;
        double x_i_effective;
        for(j_i[0] = -W_search_lo; j_i[0] <= W_search_hi; j_i[0]++) {
            for(j_i[1] = -W_search_lo; j_i[1] <= W_search_hi; j_i[1]++) {
                for(j_i[2] = -W_search_lo; j_i[2] <= W_search_hi; j_i[2]++) {
                    // Compute distance to each grid point being searched against ...
                    flag_active = GBP_TRUE;
                    for(i_coord = 0, W_i = 1.; i_coord < 3; i_coord++) {
                        switch(i_coord) {
                            case 0:
                                x_i = (GBPREAL)(i_i[0] + j_i[0]) - x_particle_i;
                                break;
                            case 1:
                                x_i = (GBPREAL)(i_i[1] + j_i[1]) - y_particle_i;
                                break;
                            case 2:
                                x_i = (GBPREAL)(i_i[2] + j_i[2]) - z_particle_i;
                                break;
                        }
                        switch(distribution_scheme) {
                                // Distribute with a Daubechies wavelet transform of 12th or 20th order a la Cui et al '08
                            case MAP2GRID_DIST_DWT12:
                            case MAP2GRID_DIST_DWT20:
                                x_i_effective = x_i + kernal_offset;
                                if(x_i_effective > 0.)
                                    W_i *= interpolate(W_r_Daub_interp, x_i_effective);
                                else
                                    flag_active = GBP_FALSE;
                                break;
                                // Distribute using the triangular shaped cloud (TSC) method
                            case MAP2GRID_DIST_TSC:
                                if(x_i < 0.5)
                                    W_i *= (0.75 - x_i * x_i);
                                else if(x_i < 1.5)
                                    W_i *= 0.5 * (1.5 - fabs(x_i)) * (1.5 - fabs(x_i));
                                else
                                    flag_active = GBP_FALSE;
                                break;
                                // Distribute using the cloud-in-cell (CIC) method
                            case MAP2GRID_DIST_CIC:
                                if(fabs(x_i) < 1.)
                                    W_i *= (1. - fabs(x_i));
                                else
                                    flag_active = GBP_FALSE;
                                break;
                                // Distribute using "nearest grid point" (NGP; ie. the simplest and default) method
                            case MAP2GRID_DIST_NGP:
                            default:
                                if(fabs(x_i) <= 0.5 && flag_viable)
                                    W_i *= 1.;
                                else
                                    flag_active = GBP_FALSE;
                                break;
                        }
                    }
                    if(flag_active) { // This flags-out regions of the kernal with no support to save some time
                        // Set the grid indices (enforce periodic BCs; do x-coordinate last) ...
                        //   ... y-coordinate ...
                        k_i[1] = (i_i[1] + j_i[1]);
                        if(k_i[1] < 0)
                            k_i[1] += field->n[1];
                        else
                            k_i[1] = k_i[1] % field->n[1];
                        //   ... z-coordinate ...
                        k_i[2] = i_i[2] + j_i[2];
                        if(k_i[2] < 0)
                            k_i[2] += field->n[2];
                        else
                            k_i[2] = k_i[2] % field->n[2];
                        //   ... x-coordinate ...
                        //     Depending on x-index, add contribution to the
                        //     local array or to the slab buffers.
                        k_i[0] = (i_i[0] + j_i[0]);
                        if(k_i[0] < field->i_R_start_local[0]) {
                            k_i[0] -= (field->i_R_start_local[0] - W_search_lo);
                            if(k_i[0] < 0)
                                SID_exit_error("Left slab buffer limit exceeded by %d element(s).", SID_ERROR_LOGIC,
                                               -k_i[0]);
                            send_left[index_FFT_R(field, k_i)] += W_i * value_i;
                            if(field_norm != NULL)
                                send_left_norm[index_FFT_R(field_norm, k_i)] += W_i * norm_i;
                        } else if(k_i[0] > field->i_R_stop_local[0]) {
                            k_i[0] -= (field->i_R_stop_local[0] + 1);
                            if(k_i[0] >= W_search_hi)
                                SID_exit_error("Right slab buffer limit exceeded by %d element(s).", SID_ERROR_LOGIC,
                                               k_i[0] - W_search_hi + 1);
                            else {
                                send_right[index_FFT_R(field, k_i)] += W_i * value_i;
                                if(field_norm != NULL)
                                    send_right_norm[index_FFT_R(field_norm, k_i)] += W_i * norm_i;
                            }
                        } else {
                            field->field_local[index_local_FFT_R(field, k_i)] += W_i * value_i;
                            if(field_norm != NULL)
                                field_norm->field_local[index_local_FFT_R(field_norm, k_i)] += W_i * norm_i;
                        }
                        flag_viable = GBP_FALSE;
                    }
                }
            }
        }
        // Report the calculation's progress
        SID_check_pcounter(&pcounter, i_p);
    }
    SID_log("Done.", SID_LOG_CLOSE);

    // Perform exchange of slab buffers and add them to the local mass distribution.
    //    Note: it's important that the FFT field not be padded (see above, where
    //          this is set) for this to work the way it's done.
    SID_log("Adding-in the slab buffers...", SID_LOG_OPEN | SID_LOG_TIMER);
    // Numerator first ...
    exchange_slab_buffer_left(send_left, send_size_left, receive_right, &receive_right_size, &(field->slab));
    exchange_slab_buffer_right(send_right, send_size_right, receive_left, &receive_left_size, &(field->slab));
    for(i_b = 0; i_b < n_send_right; i_b++)
        field->field_local[i_b] += receive_left[i_b];
    for(i_b = 0; i_b < n_send_left; i_b++)
        field->field_local[field->n_field_R_local - n_send_left + i_b] += receive_right[i_b];
    // ... then denominator (if it's being used)
    if(field_norm != NULL) {
        exchange_slab_buffer_left(send_left_norm, send_size_left, receive_right_norm, &receive_right_size, &(field_norm->slab));
        exchange_slab_buffer_right(send_right_norm, send_size_right, receive_left_norm, &receive_left_size, &(field_norm->slab));
        for(i_b = 0; i_b < n_send_right; i_b++)
            field_norm->field_local[i_b] += receive_left_norm[i_b];
        for(i_b = 0; i_b < n_send_left; i_b++)
            field_norm->field_local[field_norm->n_field_R_local - n_send_left + i_b] += receive_right[i_b];
    }
    SID_free(SID_FARG send_left);
    SID_free(SID_FARG send_right);
    SID_free(SID_FARG receive_left);
    SID_free(SID_FARG receive_right);
    if(field_norm != NULL) {
        SID_free(SID_FARG send_left_norm);
        SID_free(SID_FARG send_right_norm);
        SID_free(SID_FARG receive_left_norm);
        SID_free(SID_FARG receive_right_norm);
    }
    SID_log("Done.", SID_LOG_CLOSE);

    // Recompute local normalization (more accurate for large sample sizes)
    if(!SID_CHECK_BITFIELD_SWITCH(mode, MAP2GRID_MODE_NONORM)) {
        SID_log("Applying normalization...", SID_LOG_OPEN);
        if(field_norm != NULL) {
            for(i_grid = 0; i_grid < field->n_field_R_local; i_grid++) {
                if(field_norm->field_local[i_grid] != 0)
                    field->field_local[i_grid] /= field_norm->field_local[i_grid];
            }
        }
        if(SID_CHECK_BITFIELD_SWITCH(mode, MAP2GRID_MODE_APPLYFACTOR)) {
            for(i_grid = 0; i_grid < field->n_field_R_local; i_grid++)
                field->field_local[i_grid] *= normalization_constant;
        }
        if(SID_CHECK_BITFIELD_SWITCH(mode, MAP2GRID_MODE_FORCENORM)) {
            norm_local = 0;
            for(i_grid = 0; i_grid < field->n_field_R_local; i_grid++)
                norm_local += (double)field->field_local[i_grid];
            calc_sum_global(&norm_local, &normalization, 1, SID_DOUBLE, CALC_MODE_DEFAULT, SID_COMM_WORLD);
            double normalization_factor;
            normalization_factor = normalization_constant / normalization;
            for(i_grid = 0; i_grid < field->n_field_R_local; i_grid++)
                field->field_local[i_grid] *= normalization_factor;
        }
        SID_log("Done.", SID_LOG_CLOSE, normalization);
    }

    if(W_r_Daub_interp != NULL)
        free_interpolate(SID_FARG W_r_Daub_interp, NULL);

    SID_log("Done.", SID_LOG_CLOSE);
}
Beispiel #3
0
/*
Manage the whole stuff.
*/
void	makeit()

  {
   checkstruct		*check;
   picstruct		*dfield, *field,*pffield[MAXFLAG], *wfield,*dwfield;
   catstruct		*imacat;
   tabstruct		*imatab;
   patternstruct	*pattern;
   static time_t        thetime1, thetime2;
   struct tm		*tm;
   unsigned int		modeltype;
   int			nflag[MAXFLAG], nparam2[2],
			i, nok, ntab, next, ntabmax, forcextflag,
			nima0,nima1, nweight0,nweight1, npsf0,npsf1, npat,npat0;

   next = 0;
   nok = 1;

/* Processing start date and time */
  dtime = counter_seconds();
  thetimet = time(NULL);
  tm = localtime(&thetimet);
  sprintf(prefs.sdate_start,"%04d-%02d-%02d",
        tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday);
  sprintf(prefs.stime_start,"%02d:%02d:%02d",
        tm->tm_hour, tm->tm_min, tm->tm_sec);

  NFPRINTF(OUTPUT, "");
  QPRINTF(OUTPUT, "----- %s %s started on %s at %s with %d thread%s\n\n",
		BANNER,
		MYVERSION,
		prefs.sdate_start,
		prefs.stime_start,
		prefs.nthreads,
		prefs.nthreads>1? "s":"");

/* Initialize globals variables */
  initglob();

  NFPRINTF(OUTPUT, "Setting catalog parameters");
  readcatparams(prefs.param_name);
  useprefs();			/* update things accor. to prefs parameters */

/* Check if a specific extension should be loaded */
/* Never true for an NDF, although we could through all NDFs in a container, */
/* so we make selectext go away. */
  nima0 = -1;
  forcextflag = 0;

/* Do the same for other data (but do not force single extension mode) */
  nima1 = -1;    /* selectext(prefs.image_name[1]) */
  nweight0 = -1; /* selectext(prefs.wimage_name[0]) */
  nweight1 = -1; /* selectext(prefs.wimage_name[1]) */
  if (prefs.dpsf_flag)
    {
    npsf0 = -1; /* selectext(prefs.psf_name[0]) */
    npsf1 = -1; /* selectext(prefs.psf_name[1]) */
    }
  else
    npsf0 = -1; /* selectext(prefs.psf_name[0]) */
  for (i=0; i<prefs.nfimage_name; i++)
    nflag[i] = -1; /* selectext(prefs.fimage_name[i]) */

  if (prefs.psf_flag)
    {
/*-- Read the first PSF extension to set up stuff such as context parameters */
    NFPRINTF(OUTPUT, "Reading PSF information");
    if (prefs.dpsf_flag)
      {
      thedpsf = psf_load(prefs.psf_name[0],nima0<0? 1 :(npsf0<0? 1:npsf0)); 
      thepsf = psf_load(prefs.psf_name[1], nima1<0? 1 :(npsf1<0? 1:npsf1));
      }
    else
      thepsf = psf_load(prefs.psf_name[0], nima0<0? 1 :(npsf0<0? 1:npsf0)); 
 /*-- Need to check things up because of PSF context parameters */
    updateparamflags();
    useprefs();
    }

  if (prefs.prof_flag)
    {
#ifdef USE_MODEL
    fft_init(prefs.nthreads);
/* Create profiles at full resolution */
    NFPRINTF(OUTPUT, "Preparing profile models");
    modeltype = (FLAG(obj2.prof_offset_flux)? MODEL_BACK : MODEL_NONE)
	|(FLAG(obj2.prof_dirac_flux)? MODEL_DIRAC : MODEL_NONE)
	|(FLAG(obj2.prof_spheroid_flux)?
		(FLAG(obj2.prof_spheroid_sersicn)?
			MODEL_SERSIC : MODEL_DEVAUCOULEURS) : MODEL_NONE)
	|(FLAG(obj2.prof_disk_flux)? MODEL_EXPONENTIAL : MODEL_NONE)
	|(FLAG(obj2.prof_bar_flux)? MODEL_BAR : MODEL_NONE)
	|(FLAG(obj2.prof_arms_flux)? MODEL_ARMS : MODEL_NONE);
    theprofit = profit_init(thepsf, modeltype);
    changecatparamarrays("VECTOR_MODEL", &theprofit->nparam, 1);
    changecatparamarrays("VECTOR_MODELERR", &theprofit->nparam, 1);
    nparam2[0] = nparam2[1] = theprofit->nparam;
    changecatparamarrays("MATRIX_MODELERR", nparam2, 2);
    if (prefs.dprof_flag)
      thedprofit = profit_init(thedpsf, modeltype);
    if (prefs.pattern_flag)
      {
      npat0 = prefs.prof_disk_patternvectorsize;
      if (npat0<prefs.prof_disk_patternmodvectorsize)
        npat0 = prefs.prof_disk_patternmodvectorsize;
      if (npat0<prefs.prof_disk_patternargvectorsize)
        npat0 = prefs.prof_disk_patternargvectorsize;
/*---- Do a copy of the original number of pattern components */
      prefs.prof_disk_patternncomp = npat0;
      pattern = pattern_init(theprofit, prefs.pattern_type, npat0);
      if (FLAG(obj2.prof_disk_patternvector))
        {
        npat = pattern->size[2];
        changecatparamarrays("DISK_PATTERN_VECTOR", &npat, 1);
        }
      if (FLAG(obj2.prof_disk_patternmodvector))
        {
        npat = pattern->ncomp*pattern->nfreq;
        changecatparamarrays("DISK_PATTERNMOD_VECTOR", &npat, 1);
        }
      if (FLAG(obj2.prof_disk_patternargvector))
        {
        npat = pattern->ncomp*pattern->nfreq;
        changecatparamarrays("DISK_PATTERNARG_VECTOR", &npat, 1);
        }
      pattern_end(pattern);
      }
    QPRINTF(OUTPUT, "Fitting model: ");
    for (i=0; i<theprofit->nprof; i++)
      {
      if (i)
        QPRINTF(OUTPUT, "+");
      QPRINTF(OUTPUT, "%s", theprofit->prof[i]->name);
      }
    QPRINTF(OUTPUT, "\n");
    if (FLAG(obj2.prof_concentration)|FLAG(obj2.prof_concentration))
      {
      thepprofit = profit_init(thepsf, MODEL_DIRAC);
      theqprofit = profit_init(thepsf, MODEL_EXPONENTIAL);
      }
#else
    error(EXIT_FAILURE,
		"*Error*: model-fitting is not supported in this build.\n",
			" Please check your configure options");
#endif
    }

  if (prefs.filter_flag)
    {
    NFPRINTF(OUTPUT, "Reading detection filter");
    getfilter(prefs.filter_name);	/* get the detection filter */
    }

  if (FLAG(obj2.sprob))
    {
    NFPRINTF(OUTPUT, "Initializing Neural Network");
    neurinit();
    NFPRINTF(OUTPUT, "Reading Neural Network Weights");
    getnnw(); 
    }

  if (prefs.somfit_flag)
    {
     int	margin;

    thesom = som_load(prefs.som_name);
    if ((margin=(thesom->inputsize[1]+1)/2) > prefs.cleanmargin)
      prefs.cleanmargin = margin;
    if (prefs.somfit_vectorsize>thesom->neurdim)
      {
      prefs.somfit_vectorsize = thesom->neurdim;
      sprintf(gstr,"%d", prefs.somfit_vectorsize);
      warning("Dimensionality of the SOM-fit vector limited to ", gstr);
      }
    }

/* Prepare growth-curve buffer */
  if (prefs.growth_flag)
    initgrowth();

/* Allocate memory for multidimensional catalog parameter arrays */
  alloccatparams();
  useprefs();

/*-- Init the CHECK-images */
  if (prefs.check_flag)
    {
      checkenum	c;
      
      NFPRINTF(OUTPUT, "Initializing check-image(s)");
      for (i=0; i<prefs.ncheck_type; i++)
        if ((c=prefs.check_type[i]) != CHECK_NONE)
          {
            if (prefs.check[c])
              error(EXIT_FAILURE,"*Error*: 2 CHECK_IMAGEs cannot have the same ",
                        " CHECK_IMAGE_TYPE");
            prefs.check[c] = initcheck(prefs.check_name[i], prefs.check_type[i],
                                       next);
            free(prefs.check_name[i]);
          }
    }

  NFPRINTF(OUTPUT, "Initializing catalog");
  initcat();
  
/* Initialize XML data */
  if (prefs.xml_flag || prefs.cat_type==ASCII_VO)
    init_xml(next);

/* Go through all images */
/* for all images in an MEF */

/*---- Initial time measurement*/
  time(&thetime1);
  thecat.currext = nok+1;
  
  dfield = field = wfield = dwfield = NULL;

/*---- Init the Detection and Measurement-images */
  if (prefs.dimage_flag)
  {
      dfield = newfield(prefs.image_name[0], DETECT_FIELD, nok);
      field = newfield(prefs.image_name[1], MEASURE_FIELD, nok);
      if ((field->width!=dfield->width) || (field->height!=dfield->height))
          error(EXIT_FAILURE, "*Error*: Frames have different sizes","");
/*---- Prepare interpolation */
      if (prefs.dweight_flag && prefs.interp_type[0] == INTERP_ALL)
          init_interpolate(dfield, -1, -1);
      if (prefs.interp_type[1] == INTERP_ALL)
          init_interpolate(field, -1, -1);
  }
  else
  {
      field = newfield(prefs.image_name[0], DETECT_FIELD | MEASURE_FIELD, nok);
/*-- Prepare interpolation */
      if ((prefs.dweight_flag || prefs.weight_flag)
          && prefs.interp_type[0] == INTERP_ALL)
          init_interpolate(field, -1, -1);       /* 0.0 or anything else */
  }

/*-- Init the WEIGHT-images */
  if (prefs.dweight_flag || prefs.weight_flag) 
  {
      weightenum	wtype;
      PIXTYPE	interpthresh;

      if (prefs.nweight_type>1)
      {
/*------ Double-weight-map mode */
          if (prefs.weight_type[1] != WEIGHT_NONE)
          {
/*-------- First: the "measurement" weights */
              wfield = newweight(prefs.wimage_name[1],field,prefs.weight_type[1],
                                 nok);
              wtype = prefs.weight_type[1];
              interpthresh = prefs.weight_thresh[1];
/*-------- Convert the interpolation threshold to variance units */
              weight_to_var(wfield, &interpthresh, 1);
              wfield->weight_thresh = interpthresh;
              if (prefs.interp_type[1] != INTERP_NONE)
                  init_interpolate(wfield,
                                   prefs.interp_xtimeout[1], prefs.interp_ytimeout[1]);
          }
/*------ The "detection" weights */
          if (prefs.weight_type[0] != WEIGHT_NONE)
          {
              interpthresh = prefs.weight_thresh[0];
              if (prefs.weight_type[0] == WEIGHT_FROMINTERP)
              {
                  dwfield=newweight(prefs.wimage_name[0],wfield,prefs.weight_type[0], 
                                    nok);
                  weight_to_var(wfield, &interpthresh, 1);
              }
              else
              {
                  dwfield = newweight(prefs.wimage_name[0], dfield?dfield:field,
                                      prefs.weight_type[0], nok);
                  weight_to_var(dwfield, &interpthresh, 1);
              }
              dwfield->weight_thresh = interpthresh;
              if (prefs.interp_type[0] != INTERP_NONE)
                  init_interpolate(dwfield,
                                   prefs.interp_xtimeout[0], prefs.interp_ytimeout[0]);
          }
      }
      else
      {
/*------ Single-weight-map mode */
          wfield = newweight(prefs.wimage_name[0], dfield?dfield:field,
                             prefs.weight_type[0], nok);
          wtype = prefs.weight_type[0];
          interpthresh = prefs.weight_thresh[0];
/*------ Convert the interpolation threshold to variance units */
          weight_to_var(wfield, &interpthresh, 1);
          wfield->weight_thresh = interpthresh;
          if (prefs.interp_type[0] != INTERP_NONE)
              init_interpolate(wfield,
                               prefs.interp_xtimeout[0], prefs.interp_ytimeout[0]);
      }
  }

/*-- Init the FLAG-images */
  for (i=0; i<prefs.nimaflag; i++)
  {
      pffield[i] = newfield(prefs.fimage_name[i], FLAG_FIELD, nok);
      if ((pffield[i]->width!=field->width)
          || (pffield[i]->height!=field->height))
          error(EXIT_FAILURE,
                "*Error*: Incompatible FLAG-map size in ", prefs.fimage_name[i]);
  }

/*-- Compute background maps for `standard' fields */
  QPRINTF(OUTPUT, dfield? "Measurement image:"
          : "Detection+Measurement image: ");
  makeback(field, wfield, prefs.wscale_flag[1]);
  QPRINTF(OUTPUT, (dfield || (dwfield&&dwfield->flags^INTERP_FIELD))? "(M)   "
          "Background: %-10g RMS: %-10g / Threshold: %-10g \n"
          : "(M+D) "
          "Background: %-10g RMS: %-10g / Threshold: %-10g \n",
          field->backmean, field->backsig, (field->flags & DETECT_FIELD)?
          field->dthresh: field->thresh);
  if (dfield)
  {
      QPRINTF(OUTPUT, "Detection image: ");
      makeback(dfield, dwfield? dwfield
                        : (prefs.weight_type[0] == WEIGHT_NONE?NULL:wfield),
                prefs.wscale_flag[0]);
      QPRINTF(OUTPUT, "(D)   "
              "Background: %-10g RMS: %-10g / Threshold: %-10g \n",
              dfield->backmean, dfield->backsig, dfield->dthresh);
  }
  else if (dwfield && dwfield->flags^INTERP_FIELD)
  {
      makeback(field, dwfield, prefs.wscale_flag[0]);
      QPRINTF(OUTPUT, "(D)   "
              "Background: %-10g RMS: %-10g / Threshold: %-10g \n",
              field->backmean, field->backsig, field->dthresh);
  }

/*-- For interpolated weight-maps, copy the background structure */
  if (dwfield && dwfield->flags&(INTERP_FIELD|BACKRMS_FIELD))
      copyback(dwfield->reffield, dwfield);
  if (wfield && wfield->flags&(INTERP_FIELD|BACKRMS_FIELD))
      copyback(wfield->reffield, wfield);

/*-- Prepare learn and/or associations */
  if (prefs.assoc_flag)
      init_assoc(field);                  /* initialize assoc tasks */

/*-- Update the CHECK-images */
  if (prefs.check_flag)
      for (i=0; i<MAXCHECK; i++)
        if ((check=prefs.check[i]))
          reinitcheck(field, check);

    if (!forcextflag && nok>1)
      {
      if (prefs.psf_flag)
        {
/*------ Read other PSF extensions */
        NFPRINTF(OUTPUT, "Reading PSF information");
        psf_end(thepsf, thepsfit);
        if (prefs.dpsf_flag)
          {
          psf_end(thedpsf, thedpsfit);
          thedpsf = psf_load(prefs.psf_name[0], nok);
          thepsf = psf_load(prefs.psf_name[1], nok);
          }
        else
          thepsf = psf_load(prefs.psf_name[0], nok); 
        }

#ifdef USE_MODEL
      if (prefs.prof_flag)
        {
/*------ Create profiles at full resolution */
        profit_end(theprofit);
        theprofit = profit_init(thepsf, modeltype);
        if (prefs.dprof_flag)
          {
          profit_end(thedprofit);
          thedprofit = profit_init(thedpsf, modeltype);
          }
        if (prefs.pattern_flag)
          {
          pattern = pattern_init(theprofit, prefs.pattern_type, npat0);
          pattern_end(pattern);
          }
        if (FLAG(obj2.prof_concentration)|FLAG(obj2.prof_concentration))
          {
          profit_end(thepprofit);
          profit_end(theqprofit);
          thepprofit = profit_init(thepsf, MODEL_DIRAC);
          theqprofit = profit_init(thepsf, MODEL_EXPONENTIAL);
          }
        }
#endif
      }

/*-- Initialize PSF contexts and workspace */
  if (prefs.psf_flag)
  {
      psf_readcontext(thepsf, field);
      psf_init();
      if (prefs.dpsf_flag)
        {
        psf_readcontext(thepsf, dfield);
        psf_init();
        }
  }

/*-- Copy field structures to static ones (for catalog info) */
  if (dfield)
  {
      thefield1 = *field;
      thefield2 = *dfield;
  }
  else
      thefield1 = thefield2 = *field;

  if (wfield)
  {
      thewfield1 = *wfield;
      thewfield2 = dwfield? *dwfield: *wfield;
  }
  else if (dwfield)
      thewfield2 = *dwfield;

  reinitcat(field);

/*-- Start the extraction pipeline */
  NFPRINTF(OUTPUT, "Scanning image");
  scanimage(field, dfield, pffield, prefs.nimaflag, wfield, dwfield);

  NFPRINTF(OUTPUT, "Closing files");

/*-- Finish the current CHECK-image processing */
  if (prefs.check_flag)
      for (i=0; i<MAXCHECK; i++)
        if ((check=prefs.check[i]))
          reendcheck(field, check);

/*-- Final time measurements*/
    if (time(&thetime2)!=-1)
      {
      if (!strftime(thecat.ext_date, 12, "%d/%m/%Y", localtime(&thetime2)))
        error(EXIT_FAILURE, "*Internal Error*: Date string too long ","");
      if (!strftime(thecat.ext_time, 10, "%H:%M:%S", localtime(&thetime2)))
          error(EXIT_FAILURE, "*Internal Error*: Time/date string too long ","");
      thecat.ext_elapsed = difftime(thetime2, thetime1);
      
  }

  reendcat();

/* Update XML data */
    if (prefs.xml_flag || prefs.cat_type==ASCII_VO)
      update_xml(&thecat, dfield? dfield:field, field,
	dwfield? dwfield:wfield, wfield);


/*-- Close ASSOC routines */
  end_assoc(field);

  for (i=0; i<prefs.nimaflag; i++)
      endfield(pffield[i]);
  endfield(field);
  if (dfield)
      endfield(dfield);
  if (wfield)
      endfield(wfield);
  if (dwfield)
      endfield(dwfield);

    QPRINTF(OUTPUT, "      Objects: detected %-8d / sextracted %-8d        \n\n",
	thecat.ndetect, thecat.ntotal);
/* End look around all images in an MEF */

  if (nok<0)
     error(EXIT_FAILURE, "Not enough valid FITS image extensions in ",
           prefs.image_name[0]);

  NFPRINTF(OUTPUT, "Closing files");

/* End CHECK-image processing */
  if (prefs.check_flag)
    for (i=0; i<MAXCHECK; i++)
      {
      if ((check=prefs.check[i]))
        endcheck(check);
      prefs.check[i] = NULL;
      }

  if (prefs.filter_flag)
    endfilter();

  if (prefs.somfit_flag)
    som_end(thesom);

  if (prefs.growth_flag)
    endgrowth();

#ifdef USE_MODEL
  if (prefs.prof_flag)
    {
    profit_end(theprofit);
    if (prefs.dprof_flag)
      profit_end(thedprofit);
    if (FLAG(obj2.prof_concentration)|FLAG(obj2.prof_concentration))
      {
      profit_end(thepprofit);
      profit_end(theqprofit);
      }
    fft_end();
    }
#endif

  if (prefs.psf_flag)
    psf_end(thepsf, thepsfit);

  if (prefs.dpsf_flag)
    psf_end(thedpsf, thedpsfit);

  if (FLAG(obj2.sprob))
    neurclose();

/* Processing end date and time */
  thetimet2 = time(NULL);
  tm = localtime(&thetimet2);
  sprintf(prefs.sdate_end,"%04d-%02d-%02d",
	tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday);
  sprintf(prefs.stime_end,"%02d:%02d:%02d",
	tm->tm_hour, tm->tm_min, tm->tm_sec);
  prefs.time_diff = counter_seconds() - dtime;

/* Write XML */
  if (prefs.xml_flag)
    write_xml(prefs.xml_name);

  endcat((char *)NULL);

  if (prefs.xml_flag || prefs.cat_type==ASCII_VO)
    end_xml();

/* Free FITS headers (now catalogues are closed). */
  if (field->fitsheadsize > 0) {
      free(field->fitshead);
  }

  return;
  }
int main(int argc, char *argv[]) {
    SID_Init(&argc, &argv, NULL);

    // Parse arguments
    char filename_cosmo[SID_MAX_FILENAME_LENGTH];
    char filename_list_in[SID_MAX_FILENAME_LENGTH];
    strcpy(filename_cosmo, argv[1]);
    strcpy(filename_list_in, argv[2]);

    // Read input list
    char *  line        = NULL;
    size_t  line_length = 0;
    FILE *  fp_in       = fopen(filename_list_in, "r");
    int     n_list      = count_lines_data(fp_in);
    double *a_list      = (double *)SID_malloc(sizeof(double) * n_list);
    for(int i_list = 0; i_list < n_list; i_list++) {
        grab_next_line_data(fp_in, &line, &line_length);
        grab_double(line, 1, &(a_list[i_list]));
    }
    SID_free(SID_FARG line);

    double a_lo_table = a_list[0];
    double a_hi_table = a_list[n_list - 1];
    for(int i_list = 0; i_list < n_list; i_list++) {
        a_lo_table = GBP_MIN(a_lo_table, a_list[i_list]);
        a_hi_table = GBP_MAX(a_hi_table, a_list[i_list]);
    }
    a_lo_table = GBP_MIN(a_lo_table, 1e-4);

    SID_log("Creating extended snapshot list...", SID_LOG_OPEN);

    // Initialize cosmology
    cosmo_info *cosmo = NULL;
    read_gbpCosmo_file(&cosmo, filename_cosmo);

    // Creeate interpolation tables
    int     n_table     = 500;
    double *a_table     = (double *)SID_malloc(n_table * sizeof(double));
    double *t_table     = (double *)SID_malloc(n_table * sizeof(double));
    double *n_dyn_table = (double *)SID_malloc(n_table * sizeof(double));
    double  da_table    = (a_hi_table - a_lo_table) / (double)(n_table - 1);
    for(int i_table = 0; i_table < n_table; i_table++) {
        // Expansion factor
        if(i_table == 0)
            a_table[i_table] = a_lo_table;
        else if(i_table == (n_table - 1))
            a_table[i_table] = a_hi_table;
        else
            a_table[i_table] = a_table[0] + da_table * (double)i_table;
        // Cosmic time
        t_table[i_table] = t_age_a(a_table[i_table], &cosmo);
        // Number of dynamical times
        if(i_table == 0)
            n_dyn_table[i_table] = 0.;
        else
            n_dyn_table[i_table] = delta_n_dyn(a_table[0], a_table[i_table], &cosmo);
    }
    interp_info *n_of_a_interp = NULL;
    init_interpolate(a_table, n_dyn_table, n_table, gsl_interp_akima, &n_of_a_interp);

    // Generate desired table
    double a_last   = 0.;
    double z_last   = 0.;
    double t_last   = 0.;
    double n_last   = 0.;
    double a_i      = 0.;
    double z_i      = 1.e22;
    double t_i      = 0.;
    double n_i      = 0.;
    double da       = 0.;
    double dz       = 0.;
    double dt       = 0.;
    double dn       = 0.;
    int    i_column = 1;
    printf("# Table of times, etc. generated from expansion factors in {%s}\n", filename_list_in);
    printf("# Column (%02d): snapshot number\n", i_column++);
    printf("#        (%02d): a:=expansion factor\n", i_column++);
    printf("#        (%02d): da\n", i_column++);
    printf("#        (%02d): z:=redshift\n", i_column++);
    printf("#        (%02d): dz\n", i_column++);
    printf("#        (%02d): t:=cosmic time  [years]\n", i_column++);
    printf("#        (%02d): dt\n", i_column++);
    printf("#        (%02d): n_dyn:=No. of dynamical times\n", i_column++);
    printf("#        (%02d): dn_dyn\n", i_column++);
    for(int i_list = 0; i_list < n_list; i_list++) {
        a_last = a_i;
        z_last = z_i;
        t_last = t_i;
        n_last = n_i;
        a_i    = a_list[i_list];
        z_i    = z_of_a(a_i);
        t_i    = t_age_a(a_i, &cosmo) / S_PER_YEAR;
        n_i    = interpolate(n_of_a_interp, a_i);
        da     = a_i - a_last;
        dz     = fabs(z_i - z_last);
        dt     = t_i - t_last;
        dn     = n_i - n_last;
        printf("%03d %le %le %le %le %le %le %le %le\n", i_list, a_i, da, z_i, dz, t_i, dt, n_i, dn);
    }

    // Clean-up
    SID_free(SID_FARG a_list);
    SID_free(SID_FARG a_table);
    SID_free(SID_FARG t_table);
    SID_free(SID_FARG n_dyn_table);
    free_interpolate(SID_FARG n_of_a_interp, NULL);
    free_cosmo(&cosmo);

    SID_log("Done.", SID_LOG_CLOSE);
    SID_Finalize();
}
int compute_group_analysis(halo_properties_info  *properties,
                           halo_profile_info     *profile,
                           double (*p_i_fctn) (void *,int,int), 
                           double (*v_i_fctn) (void *,int,int), 
                           size_t (*id_i_fctn)(void *,int), 
                           void   *params,
                           double                 box_size,
                           double                 particle_mass,
                           int                    n_particles,
                           double                 expansion_factor,
                           double                *x,
                           double                *y,
                           double                *z,
                           double                *vx,
                           double                *vy,
                           double                *vz,
                           double                *R,
                           size_t               **R_index_in,
                           int                    flag_manual_centre,
                           int                    flag_compute_shapes,
                           cosmo_info            *cosmo){
  size_t      *R_index;
  int          i,j;
  int          i_profile;
  int          j_profile;
  int          k_profile;
  int          n_profile;
  size_t       i_particle;
  size_t       j_particle;
  size_t       k_particle;
  int          next_bin_particle;
  interp_info *V_R_interpolate;
  interp_info *vir_interpolate;
  int          i_bin;
  int          n_in_bin;
  double       n_per_bin;
  int          n_cumulative;
  double       V1;
  double       V2;
  double       dV;
  double       dM;
  double       sigma_r_mean;
  double       sigma_t_mean;
  double       sigma_T_mean;
  double       sigma_P_mean;
  double       sigma_mean;
  double       x_COM_accumulator;
  double       y_COM_accumulator;
  double       z_COM_accumulator;
  double       vx_COM_accumulator;
  double       vy_COM_accumulator;
  double       vz_COM_accumulator;
  double       spin_x_accumulator;
  double       spin_y_accumulator;
  double       spin_z_accumulator;
  double       r_xy;
  double       v_tot,v_rad,v_tan;
  double       v_x_mean,v_y_mean,v_z_mean,v_rad_mean;
  double       shape_eigen_values[3];
  double       shape_eigen_vectors[3][3];
  double       x_COM,y_COM,z_COM,R_COM;
  double       r_c[MAX_PROFILE_BINS_P1];
  double       v_c[MAX_PROFILE_BINS_P1];
  double       r_interp[MAX_PROFILE_BINS];
  double       y_interp[MAX_PROFILE_BINS];
  size_t       n_bins_temp;
  double       V_max,R_max;
  double       Delta,Omega;
  double       norm;
  int          flag_interpolated=FALSE;
  const gsl_interp_type  *interp_type;
  double sigma_cor,sigma_halo;
  double M_cor,M_halo;
  double x_vir,gamma;

  double h_Hubble=((double *)ADaPS_fetch(cosmo,"h_Hubble"))[0];
  double Omega_M =((double *)ADaPS_fetch(cosmo,"Omega_M"))[0];
  double redshift=z_of_a(expansion_factor);

  Delta=Delta_vir(redshift,cosmo);
  Omega=1.;

  // Initialize properties
  properties->id_MBP         =id_i_fctn(params,0);//id_array[index_MBP];
  properties->n_particles    =n_particles;
  properties->position_COM[0]=0.;
  properties->position_COM[1]=0.;
  properties->position_COM[2]=0.;
  properties->position_MBP[0]=(float)p_i_fctn(params,0,0);//(x_array[index_MBP]);
  properties->position_MBP[1]=(float)p_i_fctn(params,1,0);//(y_array[index_MBP]);
  properties->position_MBP[2]=(float)p_i_fctn(params,2,0);//(z_array[index_MBP]);
  properties->velocity_COM[0]=0.;
  properties->velocity_COM[1]=0.;
  properties->velocity_COM[2]=0.;
  properties->velocity_MBP[0]=(float)v_i_fctn(params,0,0);//(vx_array[index_MBP]);
  properties->velocity_MBP[1]=(float)v_i_fctn(params,1,0);//(vy_array[index_MBP]);
  properties->velocity_MBP[2]=(float)v_i_fctn(params,2,0);//(vz_array[index_MBP]);
  properties->M_vir          =0.;
  properties->R_vir          =0.;
  properties->R_halo         =0.;
  properties->R_max          =0.;
  properties->V_max          =0.;
  properties->sigma_v        =0.;
  properties->spin[0]        =0.;
  properties->spin[1]        =0.;
  properties->spin[2]        =0.;
  properties->q_triaxial     =1.;
  properties->s_triaxial     =1.;
  for(i=0;i<3;i++){
    for(j=0;j<3;j++)
      properties->shape_eigen_vectors[i][j]=0.;
    properties->shape_eigen_vectors[i][i]=1.;
  }

  // Set the number of profile bins and the number of particles per bin
  profile->n_bins=MAX(MIN_PROFILE_BINS,MIN((int)((6.2*log10((double)n_particles)-3.5)+((double)n_particles/1000.)+1),MAX_PROFILE_BINS));
  n_per_bin      =(double)(n_particles)/(double)profile->n_bins;

  // There's nothing to do if there are no particles
  if(n_particles==0)
    profile->n_bins=0;
  else{
    // Create a v_c(0)=0 bin
    r_c[0]=0.;
    v_c[0]=0.;

    // Initialize profiles
    for(i_bin=0;i_bin<profile->n_bins;i_bin++){
      profile->bins[i_bin].r_med           =0.;
      profile->bins[i_bin].r_max           =0.;
      profile->bins[i_bin].n_particles     =0;
      profile->bins[i_bin].M_r             =0.;
      profile->bins[i_bin].rho             =0.;
      profile->bins[i_bin].overdensity     =0.;
      profile->bins[i_bin].position_COM[0] =0.;
      profile->bins[i_bin].position_COM[1] =0.;
      profile->bins[i_bin].position_COM[2] =0.;
      profile->bins[i_bin].velocity_COM[0] =0.;
      profile->bins[i_bin].velocity_COM[1] =0.;
      profile->bins[i_bin].velocity_COM[2] =0.;
      profile->bins[i_bin].sigma_rad       =0.;
      profile->bins[i_bin].sigma_tan       =0.;
      profile->bins[i_bin].sigma_tot       =0.;
      profile->bins[i_bin].spin[0]         =0.;
      profile->bins[i_bin].spin[1]         =0.;
      profile->bins[i_bin].spin[2]         =0.;
      profile->bins[i_bin].q_triaxial      =1.;
      profile->bins[i_bin].s_triaxial      =1.;
      for(i=0;i<3;i++){
        for(j=0;j<3;j++)
          profile->bins[i_bin].shape_eigen_vectors[i][j]=0.;
        profile->bins[i_bin].shape_eigen_vectors[i][i]=1.;
      }
    }

    // Fill temporary arrays for particle positions, radii (all w.r.t MBP) and velocities
    //   Also, enforce periodic box on particle positions
    double x_cen;
    double y_cen;
    double z_cen;
    x_cen=(double)properties->position_MBP[0];
    y_cen=(double)properties->position_MBP[1];
    z_cen=(double)properties->position_MBP[2];
    if(flag_manual_centre){
       double x_cen_manual;
       double y_cen_manual;
       double z_cen_manual;
       // Compute a rough comoving centre
       for(j_particle=0;j_particle<n_particles;j_particle++){
         x[j_particle]=d_periodic(p_i_fctn(params,0,j_particle)-x_cen,box_size);//(double)(x_array[k_particle])-x_cen,box_size);
         y[j_particle]=d_periodic(p_i_fctn(params,1,j_particle)-y_cen,box_size);//(double)(y_array[k_particle])-y_cen,box_size);
         z[j_particle]=d_periodic(p_i_fctn(params,2,j_particle)-z_cen,box_size);//(double)(z_array[k_particle])-z_cen,box_size);
       }
       // Refine it with shrinking spheres
       int n_iterations;
       n_iterations=compute_centroid3D(NULL,
                                       x,
                                       y,
                                       z,
                                       n_particles,
                                       1e-3, // 1 kpc
                                       0.75,
                                       30,
                                       CENTROID3D_MODE_FACTOR|CENTROID3D_MODE_INPLACE,
                                       &x_cen_manual,
                                       &y_cen_manual,
                                       &z_cen_manual);
       x_cen+=x_cen_manual;
       y_cen+=y_cen_manual;
       z_cen+=z_cen_manual;
       properties->position_MBP[0]=x_cen;
       properties->position_MBP[1]=y_cen;
       properties->position_MBP[2]=z_cen;
       if(properties->position_MBP[0]< box_size) properties->position_MBP[0]+=box_size;
       if(properties->position_MBP[1]< box_size) properties->position_MBP[1]+=box_size;
       if(properties->position_MBP[2]< box_size) properties->position_MBP[2]+=box_size;
       if(properties->position_MBP[0]>=box_size) properties->position_MBP[0]-=box_size;
       if(properties->position_MBP[1]>=box_size) properties->position_MBP[1]-=box_size;
       if(properties->position_MBP[2]>=box_size) properties->position_MBP[2]-=box_size;
    }
    
    for(j_particle=0;j_particle<n_particles;j_particle++){
      // ... halo-centric particle positions ...
      x[j_particle]=expansion_factor*d_periodic(p_i_fctn(params,0,j_particle)-x_cen,box_size);//((double)x_array[k_particle])-x_cen,box_size);
      y[j_particle]=expansion_factor*d_periodic(p_i_fctn(params,1,j_particle)-y_cen,box_size);//((double)y_array[k_particle])-y_cen,box_size);
      z[j_particle]=expansion_factor*d_periodic(p_i_fctn(params,2,j_particle)-z_cen,box_size);//((double)z_array[k_particle])-z_cen,box_size);

      // ... velocities ...
      vx[j_particle]=v_i_fctn(params,0,j_particle);//(double)(vx_array[k_particle]);
      vy[j_particle]=v_i_fctn(params,1,j_particle);//(double)(vy_array[k_particle]);
      vz[j_particle]=v_i_fctn(params,2,j_particle);//(double)(vz_array[k_particle]);

      // ... particle radii ...
      R[j_particle]=sqrt(x[j_particle]*x[j_particle]+y[j_particle]*y[j_particle]+z[j_particle]*z[j_particle]);
    }
    
    // Sort particles by radius
    merge_sort((void *)R,(size_t)n_particles,R_index_in,SID_DOUBLE,SORT_COMPUTE_INDEX,SORT_COMPUTE_NOT_INPLACE);
    R_index=(*R_index_in);

    // Use the average of the central 30 particles for the MBP velocity if we are
    //    manually computing centres
    if(flag_manual_centre){
       double vx_cen_temp=0.;
       double vy_cen_temp=0.;
       double vz_cen_temp=0.;
       int    n_cen =0;
       for(i_particle=0;i_particle<MIN(30,n_particles);i_particle++){
          vx_cen_temp+=vx[i_particle];
          vy_cen_temp+=vy[i_particle];
          vz_cen_temp+=vz[i_particle];
          n_cen++;
       }
       properties->velocity_MBP[0]=vx_cen_temp/(double)n_cen;
       properties->velocity_MBP[1]=vy_cen_temp/(double)n_cen;
       properties->velocity_MBP[2]=vz_cen_temp/(double)n_cen;
    }

    // We need the COM velocity at R_vir before we can get halo centric velocities.  Thus,
    //   we need the overdensity profile first
    x_COM_accumulator  =0.;
    y_COM_accumulator  =0.;
    z_COM_accumulator  =0.;
    vx_COM_accumulator =0.;
    vy_COM_accumulator =0.;
    vz_COM_accumulator =0.;
    V2                 =0.;
    n_cumulative       =0;
    for(i_bin=0,i_particle=0;i_bin<profile->n_bins;i_bin++,i_particle+=n_in_bin){

      V1=V2; // Volumes

      // ... particle numbers ...
      if(i_bin<profile->n_bins-1)
        n_in_bin=(int)((double)(i_bin+1)*n_per_bin)-i_particle;
      else
        n_in_bin=n_particles-i_particle;
      n_cumulative                    +=n_in_bin;
      profile->bins[i_bin].n_particles =n_in_bin;

      // ... mass profile ...
      profile->bins[i_bin].M_r=particle_mass*(double)n_cumulative;

      // ... binning radii ...
      if(n_in_bin%2==1)
        profile->bins[i_bin].r_med=(float)R[R_index[i_particle+n_in_bin/2]];
      else
        profile->bins[i_bin].r_med=0.5*(float)(R[R_index[i_particle+n_in_bin/2-1]]+R[R_index[i_particle+n_in_bin/2]]);
      profile->bins[i_bin].r_max=(float)R[R_index[i_particle+n_in_bin-1]];

      // ... COM positions and velocities ...
      for(j_particle=0;j_particle<n_in_bin;j_particle++){
        k_particle=R_index[i_particle+j_particle];
        x_COM_accumulator += x[k_particle];
        y_COM_accumulator += y[k_particle];
        z_COM_accumulator += z[k_particle];
        vx_COM_accumulator+=vx[k_particle];
        vy_COM_accumulator+=vy[k_particle];
        vz_COM_accumulator+=vz[k_particle];
      }
      profile->bins[i_bin].position_COM[0]=(float)(x_COM_accumulator/(double)n_cumulative);
      profile->bins[i_bin].position_COM[1]=(float)(y_COM_accumulator/(double)n_cumulative);
      profile->bins[i_bin].position_COM[2]=(float)(z_COM_accumulator/(double)n_cumulative);
      profile->bins[i_bin].velocity_COM[0]=(float)(vx_COM_accumulator/(double)n_cumulative);
      profile->bins[i_bin].velocity_COM[1]=(float)(vy_COM_accumulator/(double)n_cumulative);
      profile->bins[i_bin].velocity_COM[2]=(float)(vz_COM_accumulator/(double)n_cumulative);

      // ... density ...
      V2=FOUR_THIRDS_PI*profile->bins[i_bin].r_max*profile->bins[i_bin].r_max*profile->bins[i_bin].r_max; // Volume
      dV=V2-V1;
      dM=particle_mass*(double)n_in_bin;
      profile->bins[i_bin].rho        =(float)(dM/dV);
      profile->bins[i_bin].overdensity=(float)(profile->bins[i_bin].M_r/(V2*Omega*rho_crit_z(redshift,cosmo)));

      /// ... triaxiality ...
      if(flag_compute_shapes){
         compute_triaxiality(x,
                             y,
                             z,
                             (double)profile->bins[i_bin].position_COM[0],
                             (double)profile->bins[i_bin].position_COM[1],
                             (double)profile->bins[i_bin].position_COM[2],
                             box_size,
                             n_cumulative,
                             R_index,
                             shape_eigen_values,
                             shape_eigen_vectors);
         profile->bins[i_bin].q_triaxial=(float)(shape_eigen_values[1]/shape_eigen_values[0]);
         profile->bins[i_bin].s_triaxial=(float)(shape_eigen_values[2]/shape_eigen_values[0]);
         for(i=0;i<3;i++)
           for(j=0;j<3;j++)
             profile->bins[i_bin].shape_eigen_vectors[i][j]=(float)shape_eigen_vectors[i][j];
      }
    }

    // Interpolate to get R_vir
    flag_interpolated=FALSE;
    properties->R_halo=profile->bins[profile->n_bins-1].r_max;
    if(profile->n_bins>1){

      // Remove any small-radius monotonic increases from the interpolation interval
      j_profile=0;
      while(profile->bins[j_profile].overdensity<=profile->bins[j_profile+1].overdensity && j_profile<profile->n_bins-2)
        j_profile++;

      // Only keep decreasing bins
      n_bins_temp=0;
      r_interp[n_bins_temp]=take_log10((double)profile->bins[j_profile].r_max);
      y_interp[n_bins_temp]=take_log10((double)profile->bins[j_profile].overdensity);
      n_bins_temp++;
      for(i_profile=j_profile+1;i_profile<profile->n_bins;i_profile++){
        if(take_log10((double)profile->bins[i_profile].overdensity)<y_interp[n_bins_temp-1]){
          r_interp[n_bins_temp]=take_log10((double)profile->bins[i_profile].r_max);
          y_interp[n_bins_temp]=take_log10((double)profile->bins[i_profile].overdensity);
          n_bins_temp++;
        }
      }

      if(n_bins_temp>1){
        // Perform interpolation
        if(y_interp[0]>=take_log10(Delta) && y_interp[n_bins_temp-1]<=take_log10(Delta)){
          if(n_bins_temp>9)
            interp_type=gsl_interp_cspline;
          else
            interp_type=gsl_interp_linear;
          interp_type=gsl_interp_linear;
          init_interpolate(y_interp,r_interp,n_bins_temp,interp_type,&vir_interpolate);
          properties->R_vir       =(float)take_alog10(interpolate(vir_interpolate,take_log10(Delta)));
          free_interpolate(SID_FARG vir_interpolate,NULL);
          flag_interpolated=TRUE;
        }
        else if(y_interp[0]<take_log10(Delta)){
          properties->R_vir=(float)take_alog10(r_interp[0]);
          flag_interpolated=FLAG_INTERP_MIN_BIN;
        }
        else{
          properties->R_vir=(float)take_alog10(r_interp[n_bins_temp-1]);
          flag_interpolated=FLAG_INTERP_MAX_BIN;
        }
      }
      else{
        properties->R_vir=(float)take_alog10(r_interp[0]);
        flag_interpolated=FLAG_INTERP_MIN_BIN;
      }
    }
    else{
      properties->R_vir=profile->bins[0].r_max; 
      flag_interpolated=FLAG_INTERP_MIN_BIN;
    }

    // Set the interpolation method
    if(n_bins_temp>9)
      interp_type=gsl_interp_cspline;
    else
      interp_type=gsl_interp_linear;
    interp_type=gsl_interp_linear;

    // Compute v_COM(R_vir)
    for(i_profile=0;i_profile<profile->n_bins;i_profile++)
      r_interp[i_profile]=(double)profile->bins[i_profile].r_max;
    if(flag_interpolated==TRUE){
      for(i_profile=0;i_profile<profile->n_bins;i_profile++)
        y_interp[i_profile]=(double)profile->bins[i_profile].velocity_COM[0];
      init_interpolate(r_interp,y_interp,profile->n_bins,interp_type,&vir_interpolate);
      properties->velocity_COM[0]=(float)interpolate(vir_interpolate,properties->R_vir);
      free_interpolate(SID_FARG vir_interpolate,NULL);
      for(i_profile=0;i_profile<profile->n_bins;i_profile++)
        y_interp[i_profile]=(double)profile->bins[i_profile].velocity_COM[1];
      init_interpolate(r_interp,y_interp,profile->n_bins,interp_type,&vir_interpolate);
      properties->velocity_COM[1]=(float)interpolate(vir_interpolate,properties->R_vir);
      free_interpolate(SID_FARG vir_interpolate,NULL);
      for(i_profile=0;i_profile<profile->n_bins;i_profile++)
        y_interp[i_profile]=(double)profile->bins[i_profile].velocity_COM[2];
      init_interpolate(r_interp,y_interp,profile->n_bins,interp_type,&vir_interpolate);
      properties->velocity_COM[2]=(float)interpolate(vir_interpolate,properties->R_vir);
      free_interpolate(SID_FARG vir_interpolate,NULL);
    }
    else{
      if(flag_interpolated==FLAG_INTERP_MIN_BIN)      i_profile=0;
      else if(flag_interpolated==FLAG_INTERP_MAX_BIN) i_profile=profile->n_bins-1;
      else SID_trap_error("Unrecognized value for flag_interpolated {%d}.",flag_interpolated);
      properties->velocity_COM[0]=(float)profile->bins[i_profile].velocity_COM[0];
      properties->velocity_COM[1]=(float)profile->bins[i_profile].velocity_COM[1];
      properties->velocity_COM[2]=(float)profile->bins[i_profile].velocity_COM[2];
    }

    // Compute halo-centric particle velocities
    //   Subtract COM mean and add Hubble flow
    for(j_particle=0;j_particle<n_particles;j_particle++){
      vx[j_particle]+=x[j_particle]*H_convert(H_z(redshift,cosmo))-(double)properties->velocity_COM[0];
      vy[j_particle]+=y[j_particle]*H_convert(H_z(redshift,cosmo))-(double)properties->velocity_COM[1];
      vz[j_particle]+=z[j_particle]*H_convert(H_z(redshift,cosmo))-(double)properties->velocity_COM[2];
    }

    // Compute remaining profiles ...
    spin_x_accumulator=0.;
    spin_y_accumulator=0.;
    spin_z_accumulator=0.;
    V2                =0.;
    n_cumulative      =0;
    for(i_bin=0,i_particle=0;i_bin<profile->n_bins;i_bin++,i_particle+=n_in_bin){

      V1=V2; // Volumes

      // ... particle numbers ...
      if(i_bin<profile->n_bins-1)
        n_in_bin=(int)((double)(i_bin+1)*n_per_bin)-i_particle;
      else
        n_in_bin=n_particles-i_particle;
      n_cumulative+=n_in_bin;

      // ... spins and mean velocities ...
      v_x_mean  =0.;
      v_y_mean  =0.;
      v_z_mean  =0.;
      v_rad_mean=0.;
      for(j_particle=0;j_particle<n_in_bin;j_particle++){
        k_particle=R_index[i_particle+j_particle];
      
        // ... spins ...
        spin_x_accumulator+=(double)(y[k_particle]*vz[k_particle]-z[k_particle]*vy[k_particle]);
        spin_y_accumulator+=(double)(z[k_particle]*vx[k_particle]-x[k_particle]*vz[k_particle]);
        spin_z_accumulator+=(double)(x[k_particle]*vy[k_particle]-y[k_particle]*vx[k_particle]);

        // ... mean velocities (needed below for velocity dispersions) ...
        if(R[k_particle]>0.){
          v_rad      =(x[k_particle]*vx[k_particle]+y[k_particle]*vy[k_particle]+z[k_particle]*vz[k_particle])/R[k_particle];
          v_x_mean  +=vx[k_particle];
          v_y_mean  +=vy[k_particle];
          v_z_mean  +=vz[k_particle];
          v_rad_mean+=v_rad;
        }
      }
      profile->bins[i_bin].spin[0]=(float)(spin_x_accumulator)/n_cumulative;
      profile->bins[i_bin].spin[1]=(float)(spin_y_accumulator)/n_cumulative;
      profile->bins[i_bin].spin[2]=(float)(spin_z_accumulator)/n_cumulative;
      v_x_mean  /=(double)n_in_bin;
      v_y_mean  /=(double)n_in_bin;
      v_z_mean  /=(double)n_in_bin;
      v_rad_mean/=(double)n_in_bin;

      // ... velocity dispersions ...
      for(j_particle=0;j_particle<n_in_bin;j_particle++){
        k_particle=R_index[i_particle+j_particle];
        if(R[k_particle]>0.){
          v_tot=sqrt(pow(vx[k_particle]-v_x_mean,2.)+pow(vy[k_particle]-v_y_mean,2.)+pow(vz[k_particle]-v_z_mean,2.));
          v_rad=(x[k_particle]*(vx[k_particle]-v_x_mean)+y[k_particle]*(vy[k_particle]-v_y_mean)+z[k_particle]*(vz[k_particle]-v_z_mean))/R[k_particle];
          v_tan=sqrt(v_tot*v_tot-v_rad*v_rad);
          profile->bins[i_bin].sigma_tot+=(float)((v_tot)*(v_tot));
          profile->bins[i_bin].sigma_rad+=(float)((v_rad-v_rad_mean)*(v_rad-v_rad_mean));
          profile->bins[i_bin].sigma_tan+=(float)((v_tan)*(v_tan));
        }
      }
      profile->bins[i_bin].sigma_tot=(float)sqrt((double)profile->bins[i_bin].sigma_tot/(double)n_in_bin);
      profile->bins[i_bin].sigma_rad=(float)sqrt((double)profile->bins[i_bin].sigma_rad/(double)n_in_bin);
      profile->bins[i_bin].sigma_tan=(float)sqrt((double)profile->bins[i_bin].sigma_tan/(double)n_in_bin);
    
      // ... circular velocity; v_c(R) ...
      r_c[i_bin+1]=profile->bins[i_bin].r_max;
      v_c[i_bin+1]=sqrt(G_NEWTON*profile->bins[i_bin].M_r/(double)profile->bins[i_bin].r_max);
    }
    
    // Determine R_max and V_max from v_c(R)...
    R_max=(double)r_c[1]; // default for a monotonically increasing V_c(r)
    V_max=(double)v_c[1]; // default for a monotonically increasing V_c(r)
    if(profile->n_bins>1){

      // Remove any large-radius monotonic increases from the interpolation interval
      k_profile=profile->n_bins;
      while(v_c[k_profile-1]<=v_c[k_profile] && k_profile>1)
        k_profile--;
      if(v_c[0]<=v_c[1] && k_profile==1)
        k_profile--;

      // If the profile is not monotonically increasing ...
      if(k_profile>0){
        n_bins_temp=k_profile+1;
      
        // ...find the maximum (call its index j_profile)
        for(i_profile=0,j_profile=0;i_profile<n_bins_temp;i_profile++){
          if(v_c[i_profile]>v_c[j_profile])
            j_profile=i_profile;
        }

        // ...find bottom of range in which to search for maximum (call its index i_profile)
        i_profile=j_profile-1;
        while(v_c[i_profile]>=v_c[j_profile] && i_profile>0)
          i_profile--;
    
        // ...find top of range in which to search for maximum (call its index k_profile)
        k_profile=j_profile+1;
        while(v_c[k_profile]>=v_c[j_profile] && k_profile<n_bins_temp-1)
          k_profile++;

        //   ... perform interpolation
        V_max=(double)v_c[j_profile];
        R_max=(double)r_c[j_profile];
        if(i_profile<j_profile && j_profile<k_profile){
          if(n_bins_temp>9)
            interp_type=gsl_interp_cspline;
          else
            interp_type=gsl_interp_linear;
          interp_type=gsl_interp_linear;
          init_interpolate(r_c,v_c,n_bins_temp,gsl_interp_cspline,&V_R_interpolate);
          interpolate_maximum(V_R_interpolate,
                              r_c[i_profile],
                              r_c[j_profile],
                              r_c[k_profile],
                              0.05,
                              &R_max,
                              &V_max);
          free_interpolate(SID_FARG V_R_interpolate,NULL);
        }
      }
    }
    properties->R_max=(float)R_max;
    properties->V_max=(float)V_max;

    if(profile->n_bins>9)
      interp_type=gsl_interp_cspline;
    else
      interp_type=gsl_interp_linear;
    interp_type=gsl_interp_linear;

    // Perform normal interpolation from profiles to get the rest of the global quantities
    if(flag_interpolated==TRUE){
      //  ... COM positions ...
      for(i_profile=0;i_profile<profile->n_bins;i_profile++)
        y_interp[i_profile]=(double)profile->bins[i_profile].position_COM[0]/expansion_factor;
      init_interpolate(r_interp,y_interp,profile->n_bins,interp_type,&vir_interpolate);
      properties->position_COM[0]=(float)interpolate(vir_interpolate,properties->R_vir);
      free_interpolate(SID_FARG vir_interpolate,NULL);
      for(i_profile=0;i_profile<profile->n_bins;i_profile++)
        y_interp[i_profile]=(double)profile->bins[i_profile].position_COM[1]/expansion_factor;
      init_interpolate(r_interp,y_interp,profile->n_bins,interp_type,&vir_interpolate);
      properties->position_COM[1]=(float)interpolate(vir_interpolate,properties->R_vir);
      free_interpolate(SID_FARG vir_interpolate,NULL);
      for(i_profile=0;i_profile<profile->n_bins;i_profile++)
        y_interp[i_profile]=(double)profile->bins[i_profile].position_COM[2]/expansion_factor;
      init_interpolate(r_interp,y_interp,profile->n_bins,interp_type,&vir_interpolate);
      properties->position_COM[2]=(float)interpolate(vir_interpolate,properties->R_vir);
      free_interpolate(SID_FARG vir_interpolate,NULL);
      //  ... M_vir ...
      for(i_profile=0;i_profile<profile->n_bins;i_profile++)
        y_interp[i_profile]=(double)profile->bins[i_profile].M_r;
      init_interpolate(r_interp,y_interp,profile->n_bins,interp_type,&vir_interpolate);
      properties->M_vir=interpolate(vir_interpolate,properties->R_vir);
      free_interpolate(SID_FARG vir_interpolate,NULL);
      //  ... sigma_v ...
      for(i_profile=0;i_profile<profile->n_bins;i_profile++)
        y_interp[i_profile]=(double)profile->bins[i_profile].sigma_tot;
      init_interpolate(r_interp,y_interp,profile->n_bins,interp_type,&vir_interpolate);
      properties->sigma_v=(float)interpolate(vir_interpolate,properties->R_vir);
      free_interpolate(SID_FARG vir_interpolate,NULL);
      //   ... spin ...
      for(i_profile=0;i_profile<profile->n_bins;i_profile++)
        y_interp[i_profile]=(double)profile->bins[i_profile].spin[0];
      init_interpolate(r_interp,y_interp,profile->n_bins,interp_type,&vir_interpolate);
      properties->spin[0]=(float)interpolate(vir_interpolate,properties->R_vir);
      free_interpolate(SID_FARG vir_interpolate,NULL);
      for(i_profile=0;i_profile<profile->n_bins;i_profile++)
        y_interp[i_profile]=(double)profile->bins[i_profile].spin[1];
      init_interpolate(r_interp,y_interp,profile->n_bins,interp_type,&vir_interpolate);
      properties->spin[1]=(float)interpolate(vir_interpolate,properties->R_vir);
      free_interpolate(SID_FARG vir_interpolate,NULL);
      for(i_profile=0;i_profile<profile->n_bins;i_profile++)
        y_interp[i_profile]=(double)profile->bins[i_profile].spin[2];
      init_interpolate(r_interp,y_interp,profile->n_bins,interp_type,&vir_interpolate);
      properties->spin[2]=(float)interpolate(vir_interpolate,properties->R_vir);
      free_interpolate(SID_FARG vir_interpolate,NULL);
      //  ... triaxial axes ratios ...
      for(i_profile=0;i_profile<profile->n_bins;i_profile++)
        y_interp[i_profile]=(double)profile->bins[i_profile].q_triaxial;
      init_interpolate(r_interp,y_interp,profile->n_bins,interp_type,&vir_interpolate);
      properties->q_triaxial=(float)interpolate(vir_interpolate,properties->R_vir);
      free_interpolate(SID_FARG vir_interpolate,NULL);
      for(i_profile=0;i_profile<profile->n_bins;i_profile++)
        y_interp[i_profile]=(double)profile->bins[i_profile].s_triaxial;
      init_interpolate(r_interp,y_interp,profile->n_bins,interp_type,&vir_interpolate);
      properties->s_triaxial=(float)interpolate(vir_interpolate,properties->R_vir);
      free_interpolate(SID_FARG vir_interpolate,NULL);
      // ... shape eigen vectors ...
      for(i=0;i<3;i++){
        for(j=0;j<3;j++){
          for(i_profile=0;i_profile<profile->n_bins;i_profile++)
            y_interp[i_profile]=(double)cos(profile->bins[i_profile].shape_eigen_vectors[i][j]);
          init_interpolate(r_interp,y_interp,profile->n_bins,interp_type,&vir_interpolate);
          properties->shape_eigen_vectors[i][j]=(float)acos(MAX(0,MIN(1.,interpolate(vir_interpolate,properties->R_vir))));
          free_interpolate(SID_FARG vir_interpolate,NULL);
        }
        norm=sqrt(properties->shape_eigen_vectors[i][0]*properties->shape_eigen_vectors[i][0]+
                  properties->shape_eigen_vectors[i][1]*properties->shape_eigen_vectors[i][1]+
                  properties->shape_eigen_vectors[i][2]*properties->shape_eigen_vectors[i][2]);
        for(j=0;j<3;j++)
          properties->shape_eigen_vectors[i][j]/=norm;
      }
    }
    // ... else apply defaults to faulty cases.
    else{ 
      if(flag_interpolated==FLAG_INTERP_MIN_BIN)      i_profile=0;
      else if(flag_interpolated==FLAG_INTERP_MAX_BIN) i_profile=profile->n_bins-1;
      else SID_trap_error("Unrecognized value for flag_interpolated {%d}.",flag_interpolated);

      //  ... COM positions ...
      properties->position_COM[0]=(double)(profile->bins[i_profile].position_COM[0])/expansion_factor;
      properties->position_COM[1]=(double)(profile->bins[i_profile].position_COM[1])/expansion_factor;
      properties->position_COM[2]=(double)(profile->bins[i_profile].position_COM[2])/expansion_factor;
      //  ... M_vir ...
      properties->M_vir=(double)profile->bins[i_profile].M_r;
      //  ... sigma_v ...
      properties->sigma_v=(float)profile->bins[i_profile].sigma_tot;
      //   ... spin ...
      properties->spin[0]=(float)profile->bins[i_profile].spin[0];
      properties->spin[1]=(float)profile->bins[i_profile].spin[1];
      properties->spin[2]=(float)profile->bins[i_profile].spin[2];
      //  ... triaxial axes ratios ...
      properties->q_triaxial=(float)profile->bins[i_profile].q_triaxial;
      properties->s_triaxial=(float)profile->bins[i_profile].s_triaxial;
      // ... shape eigen vectors ...
      for(i=0;i<3;i++){
        for(j=0;j<3;j++)
          properties->shape_eigen_vectors[i][j]=(float)profile->bins[i_profile].shape_eigen_vectors[i][j];
        norm=sqrt(properties->shape_eigen_vectors[i][0]*properties->shape_eigen_vectors[i][0]+
                  properties->shape_eigen_vectors[i][1]*properties->shape_eigen_vectors[i][1]+
                  properties->shape_eigen_vectors[i][2]*properties->shape_eigen_vectors[i][2]);
        for(j=0;j<3;j++)
          properties->shape_eigen_vectors[i][j]/=norm;
      }
    }

    // Enforce periodic box on COM position
    properties->position_COM[0]+=x_cen;
    properties->position_COM[1]+=y_cen;
    properties->position_COM[2]+=z_cen;
    if(properties->position_COM[0]< box_size) properties->position_COM[0]+=box_size;
    if(properties->position_COM[1]< box_size) properties->position_COM[1]+=box_size;
    if(properties->position_COM[2]< box_size) properties->position_COM[2]+=box_size;
    if(properties->position_COM[0]>=box_size) properties->position_COM[0]-=box_size;
    if(properties->position_COM[1]>=box_size) properties->position_COM[1]-=box_size;
    if(properties->position_COM[2]>=box_size) properties->position_COM[2]-=box_size;

    // Perform unit conversions
    //   ... properties first ...
    properties->position_COM[0]*=h_Hubble/M_PER_MPC;
    properties->position_COM[1]*=h_Hubble/M_PER_MPC;
    properties->position_COM[2]*=h_Hubble/M_PER_MPC;
    properties->position_MBP[0]*=h_Hubble/M_PER_MPC;
    properties->position_MBP[1]*=h_Hubble/M_PER_MPC;
    properties->position_MBP[2]*=h_Hubble/M_PER_MPC;
    properties->velocity_COM[0]*=1e-3;
    properties->velocity_COM[1]*=1e-3;
    properties->velocity_COM[2]*=1e-3;
    properties->velocity_MBP[0]*=1e-3;
    properties->velocity_MBP[1]*=1e-3;
    properties->velocity_MBP[2]*=1e-3;
    properties->M_vir          *=h_Hubble/M_SOL;
    properties->R_vir          *=h_Hubble/M_PER_MPC;
    properties->R_halo         *=h_Hubble/M_PER_MPC;
    properties->R_max          *=h_Hubble/M_PER_MPC;
    properties->V_max          *=1e-3;
    properties->sigma_v        *=1e-3;
    properties->spin[0]        *=1e-3*h_Hubble/M_PER_MPC;
    properties->spin[1]        *=1e-3*h_Hubble/M_PER_MPC;
    properties->spin[2]        *=1e-3*h_Hubble/M_PER_MPC;

    //   ... then profiles ...
    for(i_bin=0;i_bin<profile->n_bins;i_bin++){
      profile->bins[i_bin].r_med          *=h_Hubble/M_PER_MPC;
      profile->bins[i_bin].r_max          *=h_Hubble/M_PER_MPC;
      profile->bins[i_bin].M_r            *=h_Hubble/M_SOL;
      profile->bins[i_bin].rho            *=M_PER_MPC*M_PER_MPC*M_PER_MPC/(h_Hubble*h_Hubble*M_SOL);
      profile->bins[i_bin].position_COM[0]*=h_Hubble/M_PER_MPC;
      profile->bins[i_bin].position_COM[1]*=h_Hubble/M_PER_MPC;
      profile->bins[i_bin].position_COM[2]*=h_Hubble/M_PER_MPC;
      profile->bins[i_bin].velocity_COM[0]*=1e-3;
      profile->bins[i_bin].velocity_COM[1]*=1e-3;
      profile->bins[i_bin].velocity_COM[2]*=1e-3;
      profile->bins[i_bin].sigma_rad      *=1e-3;
      profile->bins[i_bin].sigma_tan      *=1e-3;
      profile->bins[i_bin].sigma_tot      *=1e-3;
      profile->bins[i_bin].spin[0]        *=1e-3*h_Hubble/M_PER_MPC;
      profile->bins[i_bin].spin[1]        *=1e-3*h_Hubble/M_PER_MPC;
      profile->bins[i_bin].spin[2]        *=1e-3*h_Hubble/M_PER_MPC;
    }
  
  }

  return(flag_interpolated);
}