Ejemplo n.º 1
0
int main(int argc, char *argv[]) {
    int     n_search;
    int     n_files;
    int     k_read;
    int     max_n_groups;
    int     l_read;
    int *   n_particles_i;
    int *   n_particles_j;
    int     n_groups_i;
    int     n_groups_j;
    int *   match_ids;
    float * match_score;
    size_t *match_index;
    int     j_halo;
    int     match;
    int     i_read;
    int     i_read_start;
    int     i_read_stop;
    SID_fp  fp_in;

    SID_Init(&argc, &argv, NULL);

    // Fetch user inputs
    if(argc != 12)
        SID_exit_error("Invalid Syntax.", SID_ERROR_SYNTAX);
    char filename_SSimPL_root[SID_MAX_FILENAME_LENGTH];
    char filename_halos_root[SID_MAX_FILENAME_LENGTH];
    char filename_trees_root[SID_MAX_FILENAME_LENGTH];
    char filename_out_root[SID_MAX_FILENAME_LENGTH];
    strcpy(filename_SSimPL_root, argv[1]);
    strcpy(filename_halos_root, argv[2]);
    strcpy(filename_trees_root, argv[3]);
    double x_cen    = (double)atof(argv[4]);
    double y_cen    = (double)atof(argv[5]);
    double z_cen    = (double)atof(argv[6]);
    double radius   = (double)atof(argv[7]);
    double z_min_in = (double)atof(argv[8]);
    double z_max_in = (double)atof(argv[9]);
    double M_min    = (double)atof(argv[10]);
    strcpy(filename_out_root, argv[11]);
    double radius2 = radius * radius;

    SID_log("Query trees for sphere (x,y,z,r)=(%.2lf,%.2lf,%.2lf,%.2lf) between z=%.2lf and z=%.2lf...",
            SID_LOG_OPEN,
            x_cen,
            y_cen,
            z_cen,
            radius,
            z_min_in,
            z_max_in);

    char filename_catalog_root[SID_MAX_FILENAME_LENGTH];
    sprintf(filename_catalog_root, "%s/catalogs/%s", filename_SSimPL_root, filename_halos_root);

    // Read tree header information
    tree_info *trees;
    char       filename_file_root[SID_MAX_FILENAME_LENGTH];
    sprintf(filename_file_root, "%s/trees/%s", filename_SSimPL_root, filename_trees_root);
    SID_set_verbosity(SID_SET_VERBOSITY_RELATIVE, 0);
    init_trees_read(filename_SSimPL_root, filename_halos_root, filename_trees_root, TREE_READ_HEADER_ONLY, &trees);
    SID_set_verbosity(SID_SET_VERBOSITY_DEFAULT);

    // Turn given redshift range into snapshot range
    int i_snap_min_z = find_treesnap_z(trees, z_max_in);
    int i_snap_max_z = find_treesnap_z(trees, z_min_in);
    SID_log("z=%.2lf -> snapshot=%d", SID_LOG_COMMENT, z_min_in, trees->snap_list[i_snap_max_z]);
    SID_log("z=%.2lf -> snapshot=%d", SID_LOG_COMMENT, z_max_in, trees->snap_list[i_snap_min_z]);

    // Perform query
    int  n_groups_list    = 0;
    int  n_subgroups_list = 0;
    int *group_list       = NULL;
    int *subgroup_list    = NULL;
    for(int i_pass = 0; i_pass < 3; i_pass++) {
        if(i_pass == 0)
            SID_log("Counting halos to be queried...", SID_LOG_OPEN | SID_LOG_TIMER);
        else if(i_pass == 1)
            SID_log("Identifying halos to be queried...", SID_LOG_OPEN | SID_LOG_TIMER);
        else if(i_pass == 2)
            SID_log("Performing query...", SID_LOG_OPEN | SID_LOG_TIMER);
        // Write headers
        if(i_pass == 2) {
            for(int i_type = 0; i_type < 2; i_type++) {
                int  n_list    = 0;
                int *halo_list = NULL;
                if(i_type == 0) {
                    n_list    = n_groups_list;
                    halo_list = group_list;
                } else {
                    n_list    = n_subgroups_list;
                    halo_list = subgroup_list;
                }
                for(int i_list = 0; i_list < n_list; i_list++) {
                    int  i_column = 1;
                    char filename_out[SID_MAX_FILENAME_LENGTH];
                    if(i_type == 0)
                        sprintf(filename_out, "%s_group_%09d.txt", filename_out_root, halo_list[i_list]);
                    else
                        sprintf(filename_out, "%s_subgroup_%09d.txt", filename_out_root, halo_list[i_list]);
                    FILE *fp_out = fopen(filename_out, "w");
                    fprintf(fp_out, "# Column (%02d): Halo expansion factor\n", i_column++);
                    fprintf(fp_out, "#        (%02d): Halo redshift\n", i_column++);
                    fprintf(fp_out, "#        (%02d): Halo snapshot\n", i_column++);
                    fprintf(fp_out, "#        (%02d): Halo index\n", i_column++);
                    fprintf(fp_out, "#        (%02d): Halo ID\n", i_column++);
                    fprintf(fp_out, "#        (%02d): Halo log10(M_vir [M_sol/h])\n", i_column++);
                    fprintf(fp_out, "#        (%02d): Halo n_particles\n", i_column++);
                    fprintf(fp_out, "#        (%02d): Halo n_particles_peak\n", i_column++);
                    fprintf(fp_out, "#        (%02d): Halo x [Mpc/h])\n", i_column++);
                    fprintf(fp_out, "#        (%02d): Halo y [Mpc/h])\n", i_column++);
                    fprintf(fp_out, "#        (%02d): Halo z [Mpc/h])\n", i_column++);
                    fprintf(fp_out, "#        (%02d): Halo radius [Mpc/h])\n", i_column++);
                    fprintf(fp_out, "#        (%02d): Halo tree ID\n", i_column++);
                    fprintf(fp_out, "#        (%02d): Descendant file offset\n", i_column++);
                    fprintf(fp_out, "#        (%02d): Descendant snapshot\n", i_column++);
                    fprintf(fp_out, "#        (%02d): Descendant index\n", i_column++);
                    fprintf(fp_out, "#        (%02d): Descendant ID\n", i_column++);
                    fprintf(fp_out, "#        (%02d): Bridge forematch snapshot\n", i_column++);
                    fprintf(fp_out, "#        (%02d): Bridge forematch index\n", i_column++);
                    fprintf(fp_out, "#        (%02d): Bridge backmatch snapshot\n", i_column++);
                    fprintf(fp_out, "#        (%02d): Bridge backmatch index\n", i_column++);
                    fprintf(fp_out, "#        (%02d): Halo type\n", i_column++);
                    fprintf(fp_out, "#        (%02d): Halo type string\n", i_column++);
                    if(i_type == 1) {
                        fprintf(fp_out, "#        (%02d): Group index\n", i_column++);
                        fprintf(fp_out, "#        (%02d): Group ID\n", i_column++);
                        fprintf(fp_out, "#        (%02d): Subgroup index\n", i_column++);
                        fprintf(fp_out, "#        (%02d): FoF Centre dx [Mpc/h])\n", i_column++);
                        fprintf(fp_out, "#        (%02d): FoF Centre dy [Mpc/h])\n", i_column++);
                        fprintf(fp_out, "#        (%02d): FoF Centre dz [Mpc/h])\n", i_column++);
                    }
                    fclose(fp_out);
                }
            }
        }

        // Set the snapshot range we need to scan
        int i_snap_start;
        int i_snap_stop;
        if(i_pass < 2) {
            i_snap_start = i_snap_min_z;
            i_snap_stop  = i_snap_max_z;
        } else {
            i_snap_start = 0;
            i_snap_stop  = trees->n_snaps - 1;
        }

        // Loop over the range of snapshots
        int i_list = 0;
        for(int i_snap = i_snap_start; i_snap <= i_snap_stop; i_snap++) {
            // Get the snapshot
            int snapshot = trees->snap_list[i_snap];
            if(i_pass == 2)
                SID_log("Processing snapshot %03d...", SID_LOG_OPEN, snapshot);

            // Open properties for this snapshot
            fp_catalog_info      fp_properties_groups;
            fp_catalog_info      fp_properties_subgroups;
            halo_properties_info properties_groups;
            halo_properties_info properties_subgroups;
            fopen_catalog(filename_catalog_root, snapshot, READ_CATALOG_GROUPS | READ_CATALOG_PROPERTIES, &fp_properties_groups);
            fopen_catalog(filename_catalog_root, snapshot, READ_CATALOG_SUBGROUPS | READ_CATALOG_PROPERTIES, &fp_properties_subgroups);

            // Open horizontal trees for this snapshot
            SID_fp fp_in_trees;
            SID_fp fp_in_bridge_forematch;
            SID_fp fp_in_bridge_backmatch;
            char   filename_in[SID_MAX_FILENAME_LENGTH];
            sprintf(filename_in, "%s/trees/%s/horizontal/trees/horizontal_trees_%03d.dat", filename_SSimPL_root, filename_trees_root, snapshot);
            SID_fopen(filename_in, "r", &fp_in_trees);
            sprintf(filename_in,
                    "%s/trees/%s/horizontal/trees/horizontal_trees_forematch_pointers_%03d.dat",
                    filename_SSimPL_root,
                    filename_trees_root,
                    snapshot);
            SID_fopen(filename_in, "r", &fp_in_bridge_forematch);
            sprintf(filename_in,
                    "%s/trees/%s/horizontal/trees/horizontal_trees_backmatch_pointers_%03d.dat",
                    filename_SSimPL_root,
                    filename_trees_root,
                    snapshot);
            SID_fopen(filename_in, "r", &fp_in_bridge_backmatch);

            // Read trees header
            int n_step_in;
            int n_search_in;
            int n_groups;
            int n_subgroups;
            int n_groups_max_in;
            int n_subgroups_max_in;
            int n_trees_subgroup_in;
            int n_trees_group_in;
            SID_fread_all(&n_step_in, sizeof(int), 1, &fp_in_trees);
            SID_fread_all(&n_search_in, sizeof(int), 1, &fp_in_trees);
            SID_fread_all(&n_groups, sizeof(int), 1, &fp_in_trees);
            SID_fread_all(&n_subgroups, sizeof(int), 1, &fp_in_trees);
            SID_fread_all(&n_groups_max_in, sizeof(int), 1, &fp_in_trees);
            SID_fread_all(&n_subgroups_max_in, sizeof(int), 1, &fp_in_trees);
            SID_fread_all(&n_trees_subgroup_in, sizeof(int), 1, &fp_in_trees);
            SID_fread_all(&n_trees_group_in, sizeof(int), 1, &fp_in_trees);
            SID_fskip(sizeof(int), 8, &fp_in_bridge_forematch);
            SID_fskip(sizeof(int), 8, &fp_in_bridge_backmatch);

            // Scan through the trees
            int i_subgroup = 0;
            for(int i_group = 0; i_group < n_groups; i_group++) {
                // Read group
                int   group_id;
                int   group_type;
                int   group_descendant_id;
                int   group_tree_id;
                int   group_file_offset;
                int   group_index;
                int   group_n_particles_peak;
                int   n_subgroups_group;
                int   group_forematch_id;
                int   group_forematch_first_file;
                int   group_forematch_first_index;
                float group_forematch_first_score;
                int   group_forematch_default_file;
                int   group_forematch_default_index;
                float group_forematch_default_score;
                int   group_forematch_best_file;
                int   group_forematch_best_index;
                float group_forematch_best_score;
                float group_forematch_score_prog;
                int   group_backmatch_id;
                int   group_backmatch_file;
                int   group_backmatch_index;
                float group_backmatch_score;
                float group_backmatch_score_prog;
                SID_fread_all(&group_id, sizeof(int), 1, &fp_in_trees);
                SID_fread_all(&group_type, sizeof(int), 1, &fp_in_trees);
                SID_fread_all(&group_descendant_id, sizeof(int), 1, &fp_in_trees);
                SID_fread_all(&group_tree_id, sizeof(int), 1, &fp_in_trees);
                SID_fread_all(&group_file_offset, sizeof(int), 1, &fp_in_trees);
                SID_fread_all(&group_index, sizeof(int), 1, &fp_in_trees);
                SID_fread_all(&group_n_particles_peak, sizeof(int), 1, &fp_in_trees);
                SID_fread_all(&n_subgroups_group, sizeof(int), 1, &fp_in_trees);
                SID_fread_all(&group_forematch_id, sizeof(int), 1, &fp_in_bridge_forematch);
                SID_fread_all(&group_forematch_first_file, sizeof(int), 1, &fp_in_bridge_forematch);
                SID_fread_all(&group_forematch_first_index, sizeof(int), 1, &fp_in_bridge_forematch);
                SID_fread_all(&group_forematch_first_score, sizeof(float), 1, &fp_in_bridge_forematch);
                SID_fread_all(&group_forematch_default_file, sizeof(int), 1, &fp_in_bridge_forematch);
                SID_fread_all(&group_forematch_default_index, sizeof(int), 1, &fp_in_bridge_forematch);
                SID_fread_all(&group_forematch_default_score, sizeof(float), 1, &fp_in_bridge_forematch);
                SID_fread_all(&group_forematch_best_file, sizeof(int), 1, &fp_in_bridge_forematch);
                SID_fread_all(&group_forematch_best_index, sizeof(int), 1, &fp_in_bridge_forematch);
                SID_fread_all(&group_forematch_best_score, sizeof(float), 1, &fp_in_bridge_forematch);
                SID_fread_all(&group_forematch_score_prog, sizeof(float), 1, &fp_in_bridge_forematch);
                SID_fread_all(&group_backmatch_id, sizeof(int), 1, &fp_in_bridge_backmatch);
                SID_fread_all(&group_backmatch_file, sizeof(int), 1, &fp_in_bridge_backmatch);
                SID_fread_all(&group_backmatch_index, sizeof(int), 1, &fp_in_bridge_backmatch);
                SID_fread_all(&group_backmatch_score, sizeof(float), 1, &fp_in_bridge_backmatch);
                SID_fread_all(&group_backmatch_score_prog, sizeof(float), 1, &fp_in_bridge_backmatch);
                SID_fskip(sizeof(int), 1, &fp_in_bridge_forematch); // skip subhalo count
                SID_fskip(sizeof(int), 1, &fp_in_bridge_backmatch); // skip subhalo count
                fread_catalog_file(&fp_properties_groups, NULL, NULL, &properties_groups, NULL, i_group);

                // Process group
                process_local(trees,
                              i_pass,
                              filename_out_root,
                              radius2,
                              M_min,
                              i_snap,
                              i_group,
                              0,
                              i_group,
                              group_id,
                              group_file_offset,
                              group_type,
                              group_n_particles_peak,
                              group_tree_id,
                              group_index,
                              group_descendant_id,
                              group_forematch_first_index,
                              group_forematch_first_file,
                              group_backmatch_index,
                              group_backmatch_file,
                              group_id,
                              &properties_groups,
                              NULL,
                              &n_groups_list,
                              group_list,
                              x_cen,
                              y_cen,
                              z_cen);

                for(int j_subgroup = 0; j_subgroup < n_subgroups_group; i_subgroup++, j_subgroup++) {
                    // Read subgroup
                    int   subgroup_id;
                    int   subgroup_type;
                    int   subgroup_descendant_id;
                    int   subgroup_tree_id;
                    int   subgroup_file_offset;
                    int   subgroup_index;
                    int   subgroup_n_particles_peak;
                    int   subgroup_forematch_id;
                    int   subgroup_forematch_first_file;
                    int   subgroup_forematch_first_index;
                    float subgroup_forematch_first_score;
                    int   subgroup_forematch_default_file;
                    int   subgroup_forematch_default_index;
                    float subgroup_forematch_default_score;
                    int   subgroup_forematch_best_file;
                    int   subgroup_forematch_best_index;
                    float subgroup_forematch_best_score;
                    float subgroup_forematch_score_prog;
                    int   subgroup_backmatch_id;
                    int   subgroup_backmatch_file;
                    int   subgroup_backmatch_index;
                    float subgroup_backmatch_score;
                    float subgroup_backmatch_score_prog;
                    SID_fread_all(&subgroup_id, sizeof(int), 1, &fp_in_trees);
                    SID_fread_all(&subgroup_type, sizeof(int), 1, &fp_in_trees);
                    SID_fread_all(&subgroup_descendant_id, sizeof(int), 1, &fp_in_trees);
                    SID_fread_all(&subgroup_tree_id, sizeof(int), 1, &fp_in_trees);
                    SID_fread_all(&subgroup_file_offset, sizeof(int), 1, &fp_in_trees);
                    SID_fread_all(&subgroup_index, sizeof(int), 1, &fp_in_trees);
                    SID_fread_all(&subgroup_n_particles_peak, sizeof(int), 1, &fp_in_trees);
                    SID_fread_all(&subgroup_forematch_id, sizeof(int), 1, &fp_in_bridge_forematch);
                    SID_fread_all(&subgroup_forematch_first_file, sizeof(int), 1, &fp_in_bridge_forematch);
                    SID_fread_all(&subgroup_forematch_first_index, sizeof(int), 1, &fp_in_bridge_forematch);
                    SID_fread_all(&subgroup_forematch_first_score, sizeof(float), 1, &fp_in_bridge_forematch);
                    SID_fread_all(&subgroup_forematch_default_file, sizeof(int), 1, &fp_in_bridge_forematch);
                    SID_fread_all(&subgroup_forematch_default_index, sizeof(int), 1, &fp_in_bridge_forematch);
                    SID_fread_all(&subgroup_forematch_default_score, sizeof(float), 1, &fp_in_bridge_forematch);
                    SID_fread_all(&subgroup_forematch_best_file, sizeof(int), 1, &fp_in_bridge_forematch);
                    SID_fread_all(&subgroup_forematch_best_index, sizeof(int), 1, &fp_in_bridge_forematch);
                    SID_fread_all(&subgroup_forematch_best_score, sizeof(float), 1, &fp_in_bridge_forematch);
                    SID_fread_all(&subgroup_forematch_score_prog, sizeof(float), 1, &fp_in_bridge_forematch);
                    SID_fread_all(&subgroup_backmatch_id, sizeof(int), 1, &fp_in_bridge_backmatch);
                    SID_fread_all(&subgroup_backmatch_file, sizeof(int), 1, &fp_in_bridge_backmatch);
                    SID_fread_all(&subgroup_backmatch_index, sizeof(int), 1, &fp_in_bridge_backmatch);
                    SID_fread_all(&subgroup_backmatch_score, sizeof(float), 1, &fp_in_bridge_backmatch);
                    SID_fread_all(&subgroup_backmatch_score_prog, sizeof(float), 1, &fp_in_bridge_backmatch);
                    fread_catalog_file(&fp_properties_subgroups, NULL, NULL, &properties_subgroups, NULL, i_subgroup);

                    // Process subgroup
                    process_local(trees,
                                  i_pass,
                                  filename_out_root,
                                  radius2,
                                  M_min,
                                  i_snap,
                                  i_subgroup,
                                  j_subgroup,
                                  i_group,
                                  subgroup_id,
                                  subgroup_file_offset,
                                  subgroup_type,
                                  subgroup_n_particles_peak,
                                  subgroup_tree_id,
                                  subgroup_index,
                                  subgroup_descendant_id,
                                  subgroup_forematch_first_index,
                                  subgroup_forematch_first_file,
                                  subgroup_backmatch_index,
                                  subgroup_backmatch_file,
                                  group_id,
                                  &properties_subgroups,
                                  &properties_groups,
                                  &n_subgroups_list,
                                  subgroup_list,
                                  x_cen,
                                  y_cen,
                                  z_cen);
                }
            }
            fclose_catalog(&fp_properties_groups);
            fclose_catalog(&fp_properties_subgroups);
            SID_fclose(&fp_in_trees);
            SID_fclose(&fp_in_bridge_forematch);
            SID_fclose(&fp_in_bridge_backmatch);
            if(i_pass == 2)
                SID_log("Done.", SID_LOG_CLOSE);
        } // i_snap
        if(i_pass == 0) {
            group_list       = (int *)SID_malloc(sizeof(int) * n_groups_list);
            subgroup_list    = (int *)SID_malloc(sizeof(int) * n_subgroups_list);
            n_groups_list    = 0;
            n_subgroups_list = 0;
        } else if(i_pass == 1) {
            SID_log("(%d groups and %d subgroups found)...", SID_LOG_CONTINUE, n_groups_list, n_subgroups_list);
            // Write list files
            for(int i_type = 0; i_type < 2; i_type++) {
                char filename_out[SID_MAX_FILENAME_LENGTH];
                int  n_list    = 0;
                int *halo_list = NULL;
                if(i_type == 0) {
                    sprintf(filename_out, "%s_group_list.txt", filename_out_root);
                    n_list    = n_groups_list;
                    halo_list = group_list;
                } else {
                    sprintf(filename_out, "%s_subgroup_list.txt", filename_out_root);
                    n_list    = n_subgroups_list;
                    halo_list = subgroup_list;
                }
                FILE *fp_out = fopen(filename_out, "w");
                fprintf(fp_out, "# Halo IDs in list of tree tracks with base {%s}\n", filename_out_root);
                for(int i_list = 0; i_list < n_list; i_list++)
                    fprintf(fp_out, "%d\n", halo_list[i_list]);
                fclose(fp_out);
            }
        }
        SID_log("Done.", SID_LOG_CLOSE);
    }

    // Clean-up
    SID_free(SID_FARG group_list);
    SID_free(SID_FARG subgroup_list);
    free_trees(&trees);

    SID_log("Done.", SID_LOG_CLOSE);
    SID_Finalize();
}
Ejemplo n.º 2
0
int main(int argc, char *argv[]){

  SID_init(&argc,&argv,NULL,NULL);

  // Fetch user inputs
  char filename_SSimPL_dir[MAX_FILENAME_LENGTH];
  char filename_halo_version_root[MAX_FILENAME_LENGTH];
  char filename_trees_root[MAX_FILENAME_LENGTH];
  char filename_trees_name[MAX_FILENAME_LENGTH];
  char filename_halos_root[MAX_FILENAME_LENGTH];
  char filename_out_root[MAX_FILENAME_LENGTH];
  strcpy(filename_SSimPL_dir,       argv[1]);
  strcpy(filename_halo_version_root,argv[2]);
  strcpy(filename_trees_name,       argv[3]);
  double z_lo         =(double)atof(argv[4]);
  double z_hi         =(double)atof(argv[5]);
  double M_cut_min    =(double)atof(argv[6]);
  strcpy(filename_out_root,         argv[7]);

  // Set some filenames
  sprintf(filename_trees_root,"%s/trees/%s",filename_SSimPL_dir,filename_trees_name);
  sprintf(filename_halos_root,"%s/halos/%s",filename_SSimPL_dir,filename_halo_version_root);

  SID_log("Creating a catalog matched across redshifts z_lo~%lf and z_hi~%lf...",SID_LOG_OPEN|SID_LOG_TIMER,z_lo,z_hi);

  // Perform analysis
  tree_info *trees;
  read_trees(filename_SSimPL_dir,
             filename_halo_version_root,
             filename_trees_name,
             TREE_MODE_DEFAULT|TREE_READ_EXTENDED_POINTERS,
             &trees);

  // Read ancillary data
  read_trees_catalogs(trees,READ_TREES_CATALOGS_GROUPS|READ_TREES_CATALOGS_SUBGROUPS);

  // Determine which snapshots best match the given redshifts
  int i_z_lo=find_treesnap_z(trees,z_lo);
  int i_z_hi=find_treesnap_z(trees,z_hi);

  // Generate two catalogs
  SID_log("Compiling catalogs...",SID_LOG_OPEN|SID_LOG_TIMER);
  for(int i_cat=0;i_cat<2;i_cat++){
     char filename_out[MAX_FILENAME_LENGTH];
     switch(i_cat){
        case 0:
           SID_log("Processing group catalog...",SID_LOG_OPEN);
           sprintf(filename_out,"%s_groups.txt",filename_out_root);
           break;
        case 1:
           SID_log("Processing subgroup catalog...",SID_LOG_OPEN);
           sprintf(filename_out,"%s_subgroups.txt",filename_out_root);
           break;
     }

     // Open file and write header
     FILE *fp_out  =fopen(filename_out,"w");
     int   i_column=1;
     fprintf(fp_out,"# Catalog matched from z_hi=%lf to z_lo=%lf\n",trees->z_list[i_z_hi],trees->z_list[i_z_lo]);
     fprintf(fp_out,"#   SSiMPL_dir  ={%s}\n",filename_SSimPL_dir);
     fprintf(fp_out,"#   halo_version={%s}\n",filename_halo_version_root);
     fprintf(fp_out,"#   tree_version={%s}\n",filename_trees_name);
     fprintf(fp_out,"#\n");
     if(i_cat==0){
        fprintf(fp_out,"# Column (%02d): FoF      index   (z_hi)\n",i_column++);
        fprintf(fp_out,"#        (%02d): FoF      index   (z_lo)\n",i_column++);
     }
     else{
        fprintf(fp_out,"# Column (%02d): subgroup index   (z_hi)\n",i_column++);
        fprintf(fp_out,"#        (%02d): subgroup index   (z_lo)\n",i_column++);
        fprintf(fp_out,"#        (%02d): FoF      index   (z_hi)\n",i_column++);
        fprintf(fp_out,"#        (%02d): FoF      index   (z_lo)\n",i_column++);
     }
     fprintf(fp_out,"#        (%02d): n_particles      (z_hi)\n",i_column++);
     if(i_cat==0){
        fprintf(fp_out,"#        (%02d): n_subgroups      (z_hi)\n",i_column++);
        fprintf(fp_out,"#        (%02d): n_subgroups      (z_lo)\n",i_column++);
        fprintf(fp_out,"#        (%02d): sig_v_1D  [km/s] (z_hi)\n",i_column++);
        fprintf(fp_out,"#        (%02d): sig_v_1D  [km/s] (z_lo)\n",i_column++);
        fprintf(fp_out,"#        (%02d): sig_v_1Dp [km/s] (z_hi)\n",i_column++);
        fprintf(fp_out,"#        (%02d): sig_v_1Dp [km/s] (z_lo)\n",i_column++);
     }
     else{
        fprintf(fp_out,"#        (%02d): M_vir_sub      (z_hi)\n",i_column++);
        fprintf(fp_out,"#        (%02d): M_vir_sub      (z_lo)\n",i_column++);
     }
     fprintf(fp_out,"#        (%02d): M_vir_FoF        (z_hi)\n",i_column++);
     fprintf(fp_out,"#        (%02d): M_vir_FoF        (z_lo)\n",i_column++);
     fprintf(fp_out,"#        (%02d): x                (z_hi)\n",i_column++);
     fprintf(fp_out,"#        (%02d): y                (z_hi)\n",i_column++);
     fprintf(fp_out,"#        (%02d): z                (z_hi)\n",i_column++);
     fprintf(fp_out,"#        (%02d): x                (z_lo)\n",i_column++);
     fprintf(fp_out,"#        (%02d): y                (z_lo)\n",i_column++);
     fprintf(fp_out,"#        (%02d): z                (z_lo)\n",i_column++);
     fprintf(fp_out,"#        (%02d): v_x              (z_hi)\n",i_column++);
     fprintf(fp_out,"#        (%02d): v_y              (z_hi)\n",i_column++);
     fprintf(fp_out,"#        (%02d): v_z              (z_hi)\n",i_column++);
     fprintf(fp_out,"#        (%02d): v_x              (z_lo)\n",i_column++);
     fprintf(fp_out,"#        (%02d): v_y              (z_lo)\n",i_column++);
     fprintf(fp_out,"#        (%02d): v_z              (z_lo)\n",i_column++);

     // Write catalog
     tree_node_info        *current;
     halo_properties_info **group_properties   =(halo_properties_info **)ADaPS_fetch(trees->data,"properties_groups");
     halo_properties_info **subgroup_properties=(halo_properties_info **)ADaPS_fetch(trees->data,"properties_subgroups");
     if(i_cat==0)
        current=trees->first_neighbour_groups[i_z_hi];
     else
        current=trees->first_neighbour_subgroups[i_z_hi];
     while(current!=NULL){
        tree_node_info       *current_subgroup;
        tree_node_info       *current_group;
        halo_properties_info *current_properties;
        halo_properties_info *current_group_properties;
        halo_properties_info *current_subgroup_properties;
        if(i_cat==0){
           current_subgroup           =NULL;
           current_group              =current;
           current_group_properties   =&(group_properties[current_group->snap_tree][current_group->neighbour_index]);
           current_subgroup_properties=&(subgroup_properties[current->snap_tree][current->neighbour_index]);
           current_properties         =current_group_properties;
        }
        else{
           current_subgroup           =current;
           current_group              =current->parent_top;
           current_group_properties   =&(group_properties[current_group->snap_tree][current_group->neighbour_index]);
           current_subgroup_properties=&(subgroup_properties[current->snap_tree][current->neighbour_index]);
           current_properties         =current_subgroup_properties;
        }
        if(current_properties->M_vir>=M_cut_min){
           tree_node_info *matched;
           int             flag_exact=find_treenode_snap_equals_given(trees,current,i_z_lo,&matched,TREE_PROGENITOR_ORDER_N_PARTICLES_PEAK);
           if(matched!=NULL && flag_exact){
              int n_sub_lo;
              int n_sub_hi;
              tree_node_info *matched_group;
              tree_node_info *matched_subgroup;
              halo_properties_info *matched_group_properties;
              halo_properties_info *matched_subgroup_properties;
              double current_group_sigma_v=0.;
              double matched_group_sigma_v=0.;
              if(i_cat==0){
                 double v_mean;
                 matched_subgroup           =NULL;
                 matched_group              =matched;
                 matched_subgroup_properties=NULL;
                 matched_group_properties   =&(group_properties[matched_group->snap_tree][matched_group->neighbour_index]);
                 tree_node_info *current_j;

                 // Compute 1D velocity dispersion for current group                
                 current_j=current_group->substructure_first;
                 v_mean   =0.;
                 n_sub_hi =0;
                 while(current_j!=NULL){
                    double M_i=subgroup_properties[current_j->snap_tree][current_j->neighbour_index].M_vir;
                    if(M_i>M_cut_min){
                       float  v_i=subgroup_properties[current_j->snap_tree][current_j->neighbour_index].velocity_COM[0];
                       v_mean+=v_i;
                       n_sub_hi++;
                    }
                    current_j=current_j->substructure_next;
                 }
                 current_j=current_group->substructure_first;
                 current_group_sigma_v=0.;
                 if(n_sub_hi>1){
                    v_mean/=(double)n_sub_hi;
                    while(current_j!=NULL){
                       double M_i=subgroup_properties[current_j->snap_tree][current_j->neighbour_index].M_vir;
                       if(M_i>M_cut_min){
                          float v_i=subgroup_properties[current_j->snap_tree][current_j->neighbour_index].velocity_COM[0];
                          current_group_sigma_v+=pow(v_i-v_mean,2.);
                       }
                       current_j=current_j->substructure_next;
                    }
                    current_group_sigma_v=sqrt(current_group_sigma_v/(double)(n_sub_hi-1));
                 }

                 // Compute 1D velocity dispersion for matched group                
                 current_j=matched_group->substructure_first;
                 v_mean   =0.;
                 n_sub_lo =0;
                 while(current_j!=NULL){
                    double M_i=subgroup_properties[current_j->snap_tree][current_j->neighbour_index].M_vir;
                    if(M_i>M_cut_min){
                       float v_i=subgroup_properties[current_j->snap_tree][current_j->neighbour_index].velocity_COM[0];
                       v_mean+=v_i;
                       n_sub_lo++;
                    }
                    current_j=current_j->substructure_next;
                 }
                 current_j=matched_group->substructure_first;
                 matched_group_sigma_v=0.;
                 if(n_sub_lo>1){
                    v_mean/=(double)n_sub_lo;
                    while(current_j!=NULL){
                       double M_i=subgroup_properties[current_j->snap_tree][current_j->neighbour_index].M_vir;
                       if(M_i>M_cut_min){
                          float v_i=subgroup_properties[current_j->snap_tree][current_j->neighbour_index].velocity_COM[0];
                          matched_group_sigma_v+=pow(v_i-v_mean,2.);
                       }
                       current_j=current_j->substructure_next;
                    }
                    matched_group_sigma_v=sqrt(matched_group_sigma_v/(double)(n_sub_lo-1));
                 }

              }
              else{
                 matched_subgroup           =matched;
                 matched_group              =matched_subgroup->parent_top;
                 matched_subgroup_properties=&(subgroup_properties[matched_subgroup->snap_tree][matched_subgroup->neighbour_index]);
                 matched_group_properties   =&(group_properties[matched_group->snap_tree][matched_group->neighbour_index]);
              }
              if(i_cat==0)
                 fprintf(fp_out,"%7d %7d %6d %5d %5d %10.3le %10.3le %10.3le %10.3le %10.3le %10.3le %10.3le %10.3le %10.3le %10.3le %10.3le %10.3le %10.3le %10.3le %10.3le %10.3le %10.3le %10.3le\n",
                    current_group->neighbour_index,
                    matched_group->neighbour_index,
                    current_group_properties->n_particles,
                    n_sub_hi,
                    n_sub_lo,
                    current_group_sigma_v,
                    matched_group_sigma_v,
                    current_group_properties->sigma_v,
                    matched_group_properties->sigma_v,
                    current_group_properties->M_vir,
                    matched_group_properties->M_vir,
                    current_group_properties->position_MBP[0],
                    current_group_properties->position_MBP[1],
                    current_group_properties->position_MBP[2],
                    matched_group_properties->position_MBP[0],
                    matched_group_properties->position_MBP[1],
                    matched_group_properties->position_MBP[2],
                    current_group_properties->velocity_COM[0],
                    current_group_properties->velocity_COM[1],
                    current_group_properties->velocity_COM[2],
                    matched_group_properties->velocity_COM[0],
                    matched_group_properties->velocity_COM[1],
                    matched_group_properties->velocity_COM[2]);
              else
                 fprintf(fp_out,"%7d %7d %7d %7d %6d %10.3le %10.3le %10.3le %10.3le %10.3le %10.3le %10.3le %10.3le %10.3le %10.3le %10.3le %10.3le %10.3le %10.3le %10.3le %10.3le\n",
                    current_subgroup->neighbour_index,
                    matched_subgroup->neighbour_index,
                    current_group->neighbour_index,
                    matched_group->neighbour_index,
                    current_subgroup_properties->n_particles,
                    current_subgroup_properties->M_vir,
                    matched_subgroup_properties->M_vir,
                    current_group_properties->M_vir,
                    matched_group_properties->M_vir,
                    current_subgroup_properties->position_MBP[0],
                    current_subgroup_properties->position_MBP[1],
                    current_subgroup_properties->position_MBP[2],
                    matched_subgroup_properties->position_MBP[0],
                    matched_subgroup_properties->position_MBP[1],
                    matched_subgroup_properties->position_MBP[2],
                    current_subgroup_properties->velocity_COM[0],
                    current_subgroup_properties->velocity_COM[1],
                    current_subgroup_properties->velocity_COM[2],
                    matched_subgroup_properties->velocity_COM[0],
                    matched_subgroup_properties->velocity_COM[1],
                    matched_subgroup_properties->velocity_COM[2]);
           }
        }
        current=current->next_neighbour;
     }
     fclose(fp_out);
     SID_log("Done.",SID_LOG_CLOSE);
  }
  SID_log("Done.",SID_LOG_CLOSE);

  // Clean-up
  free_trees(&trees);

  SID_log("Done.",SID_LOG_CLOSE);
  SID_exit(ERROR_NONE);
}
void analyze_halos_and_N_subhalos(tree_info  *trees,
                                  const char *filename_out_root,
                                  const char *catalog_root,
                                  double      z_select_exact,
                                  double      M_cut_lo,
                                  double      M_cut_hi,
                                  int         n_subgroups_track_max){

  // Compute merger rates ...
  SID_log("Constructing catalogs...",SID_LOG_OPEN|SID_LOG_TIMER);

  // Fetch halo properties
  halo_properties_info **group_properties   =(halo_properties_info **)ADaPS_fetch(trees->data,"properties_groups");
  halo_properties_info **subgroup_properties=(halo_properties_info **)ADaPS_fetch(trees->data,"properties_subgroups");

  // Determine the best z_select snapshot
  int    i_z_select=find_treesnap_z(trees,z_select_exact);
  int    i_z_0     =trees->n_snaps-1;
  double z_select  =trees->z_list[i_z_select];
  double t_select  =trees->t_list[i_z_select];
  SID_log("The snapshot corresponding best to z=%4.2f is #%03d (z=%4.2f).",SID_LOG_COMMENT,z_select_exact,trees->snap_list[i_z_select],z_select);

  // Allocate the list arrays
  tree_node_info  **list_groups       =(tree_node_info  **)SID_malloc(sizeof(tree_node_info  *)*trees->n_groups_snap_local[i_z_select]);
  tree_node_info  **list_subgroups_all=(tree_node_info  **)SID_malloc(sizeof(tree_node_info  *)*trees->n_groups_snap_local[i_z_select]);
  tree_node_info ***list_subgroups    =(tree_node_info ***)SID_malloc(sizeof(tree_node_info **)*n_subgroups_track_max);
  for(int i_track=0;i_track<n_subgroups_track_max;i_track++)
     list_subgroups[i_track]=(tree_node_info **)SID_malloc(sizeof(tree_node_info *)*trees->n_groups_snap_local[i_z_select]);

  // Select z_select systems
  tree_node_info *current_group;
  int  n_list_groups       =0;
  int  n_list_subgroups_all=0;
  int *n_list_subgroups    =(int *)SID_calloc(sizeof(int)*n_subgroups_track_max);
  SID_log("Selecting z=%4.2f systems...",SID_LOG_OPEN,trees->z_list[i_z_select]);
  current_group=trees->first_neighbour_groups[i_z_select];
  while(current_group!=NULL){
     halo_properties_info *current_group_properties=&(group_properties[current_group->snap_tree][current_group->neighbour_index]);
     if(current_group_properties->M_vir>=M_cut_lo && current_group_properties->M_vir<=M_cut_hi){
        list_groups[n_list_groups++]=current_group;
        tree_node_info *current_subgroup=current_group->substructure_first;
        int i_subgroup       =0;
        int i_subgroups_track=0;
        while(current_subgroup!=NULL && i_subgroups_track<n_subgroups_track_max){
           if(!check_treenode_if_fragmented(current_subgroup)){
              if(i_subgroup>0)
                 list_subgroups_all[n_list_subgroups_all++]=current_subgroup;
              list_subgroups[i_subgroups_track][n_list_subgroups[i_subgroups_track]++]=current_subgroup;
              i_subgroups_track++;
           }
           i_subgroup++;
           current_subgroup=current_subgroup->substructure_next;
        }
     }
     current_group=current_group->next_neighbour;
  }
  SID_log("%d groups found...",SID_LOG_CONTINUE,n_list_groups);
  SID_log("Done.",SID_LOG_CLOSE);

  // Write properties and tracks for selected z_select groups and subgroups
  char catalog_name[32];
  sprintf(catalog_name,"%s_groups",catalog_root);
  write_tree_branches(trees,list_groups,n_list_groups,TRUE,filename_out_root,catalog_name);
  average_tree_branches(catalog_name);
  for(int i_track=0;i_track<n_subgroups_track_max;i_track++){
     if(i_track==0){
        sprintf(catalog_name,"%s_subgroups_all",catalog_root);
        write_tree_branches(trees,list_subgroups_all,n_list_subgroups_all,FALSE,filename_out_root,catalog_name);
        average_tree_branches(catalog_name);
     }
     sprintf(catalog_name,"%s_subgroups_%02d",catalog_root,i_track);
     write_tree_branches(trees,list_subgroups[i_track],n_list_subgroups[i_track],FALSE,filename_out_root,catalog_name);
     average_tree_branches(catalog_name);
  }

  // Clean-up
  SID_free(SID_FARG n_list_subgroups);
  for(int i_track=0;i_track<n_subgroups_track_max;i_track++)
     SID_free(SID_FARG list_subgroups[i_track]);
  SID_free(SID_FARG list_subgroups);
  SID_free(SID_FARG list_subgroups_all);
  SID_free(SID_FARG list_groups);

  SID_log("Done.",SID_LOG_CLOSE);
}