/******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2021 John Helly (j.c.helly@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * ******************************************************************************/ #include "lightcone/lightcone_replications.h" #include "align.h" #include "cell.h" #include "error.h" #include "memuse.h" #include #include #include /** * @brief Comparison function for sorting replications * * a The first replication * b The second replication * */ static int compare_replication_rmin(const void *a, const void *b) { const struct replication *rep_a = (struct replication *)a; const struct replication *rep_b = (struct replication *)b; if (rep_a->rmin2 < rep_b->rmin2) return -1; else if (rep_a->rmin2 > rep_b->rmin2) return 1; else return 0; } /** * @brief Make a list of periodic box replications which overlap * the specified distance range from an observer. * * @param boxsize Size of the cubic simulation box. * @param observer_position Location of the observer. * @param lightcone_rmin Minimum distance from the observer. * @param lightcone_rmax Maximum distance from the observer. * of particles. * @param replication_list Pointer to the struct to initialise. */ void replication_list_init(struct replication_list *replication_list, double boxsize, double cell_width, double observer_position[3], double lightcone_rmin, double lightcone_rmax) { /* Find range of replications to examine in each dimension */ int rep_min[3]; int rep_max[3]; for (int i = 0; i < 3; i += 1) { rep_min[i] = (int)floor( (observer_position[i] - lightcone_rmax - 0.5 * cell_width) / boxsize); rep_max[i] = (int)floor( (observer_position[i] + lightcone_rmax + 0.5 * cell_width) / boxsize); } /* On first pass just count replications */ for (int ipass = 0; ipass < 2; ipass += 1) { replication_list->nrep = 0; /* Loop over periodic replications */ for (int i = rep_min[0]; i <= rep_max[0]; i += 1) { for (int j = rep_min[1]; j <= rep_max[1]; j += 1) { for (int k = rep_min[2]; k <= rep_max[2]; k += 1) { /* Find centre of this replication relative to observer */ double cx = boxsize * i + 0.5 * boxsize - observer_position[0]; double cy = boxsize * j + 0.5 * boxsize - observer_position[1]; double cz = boxsize * k + 0.5 * boxsize - observer_position[2]; /* Find distance to closest possible particle in this replication */ double dx, dy, dz; dx = fabs(cx) - 0.5 * boxsize - 0.5 * cell_width; if (dx < 0) dx = 0; dy = fabs(cy) - 0.5 * boxsize - 0.5 * cell_width; if (dy < 0) dy = 0; dz = fabs(cz) - 0.5 * boxsize - 0.5 * cell_width; if (dz < 0) dz = 0; double rep_rmin = sqrt(dx * dx + dy * dy + dz * dz); /* Find distance to most distant possible particle in this replication */ dx = fabs(cx) + 0.5 * boxsize + 0.5 * cell_width; dy = fabs(cy) + 0.5 * boxsize + 0.5 * cell_width; dz = fabs(cz) + 0.5 * boxsize + 0.5 * cell_width; double rep_rmax = sqrt(dx * dx + dy * dy + dz * dz); /* Flag if any point in this replication could be in the lightcone */ int in_lightcone = 1; /* Check distance limits */ if (rep_rmax < lightcone_rmin || rep_rmin > lightcone_rmax) in_lightcone = 0; if (in_lightcone) { /* Store replications on second pass */ if (ipass == 1) { /* Get a pointer to the next replication */ const int nrep = replication_list->nrep; struct replication *rep = replication_list->replication + nrep; /* Store info about this replication */ rep->rmin2 = pow(rep_rmin, 2.0); rep->rmax2 = pow(rep_rmax, 2.0); rep->coord[0] = i * boxsize; rep->coord[1] = j * boxsize; rep->coord[2] = k * boxsize; } replication_list->nrep += 1; } } /* Next replication in z */ } /* Next replication in y */ } /* Next replication in x */ /* Allocate storage after first pass */ if (ipass == 0) { const int nrep = replication_list->nrep; if (swift_memalign( "lightcone_replications", (void **)&replication_list->replication, SWIFT_STRUCT_ALIGNMENT, sizeof(struct replication) * nrep) != 0) { error("Failed to allocate lightcone replication list"); } } } /* Next pass */ /* Now sort replications by minimum distance */ qsort(replication_list->replication, (size_t)replication_list->nrep, sizeof(struct replication), compare_replication_rmin); /* Record the distance limits we used - may need these to refine the list * later */ replication_list->lightcone_rmin = lightcone_rmin; replication_list->lightcone_rmax = lightcone_rmax; } /** * @brief Make an empty replication list * * @param replication_list Pointer to the struct to initialise. */ void replication_list_init_empty(struct replication_list *replication_list) { const int nrep = 0; if (swift_memalign( "lightcone_replications", (void **)&replication_list->replication, SWIFT_STRUCT_ALIGNMENT, sizeof(struct replication) * nrep) != 0) { error("Failed to allocate lightcone replication list"); } replication_list->lightcone_rmin = 0.0; replication_list->lightcone_rmax = 0.0; replication_list->nrep = 0; } /** * @brief Deallocate a replication list * * @param replication_list Pointer to the struct to deallocate. */ void replication_list_clean(struct replication_list *replication_list) { swift_free("lightcone_replications", replication_list->replication); replication_list->replication = NULL; replication_list->nrep = 0; } /** * @brief Write a replication list to a file as text * * @param replication_list The replication list * @param fd The file to write to */ void replication_list_write(struct replication_list *replication_list, FILE *fd) { for (int i = 0; i < replication_list->nrep; i += 1) { fprintf(fd, "%e, %e, %e, %e, %e\n", replication_list->replication[i].coord[0], replication_list->replication[i].coord[1], replication_list->replication[i].coord[2], sqrt(replication_list->replication[i].rmin2), sqrt(replication_list->replication[i].rmax2)); } } /** * Determine subset of replications which overlap a #cell * * @param rep_in The input replication list * @param cell The input cell * @param rep_out The output replication list * * Initializes rep_out, which must then be freed with * replication_list_clean(). * */ void replication_list_subset_for_cell(const struct replication_list *rep_in, const struct cell *cell, const double observer_position[3], struct replication_list *rep_out) { /* Find centre coordinates of this cell */ const double cell_centre[] = {cell->loc[0] + 0.5 * cell->width[0], cell->loc[1] + 0.5 * cell->width[1], cell->loc[2] + 0.5 * cell->width[2]}; /* Find 'effective' width of this cell - particles can wander out of the cell * by up to half a cell width */ const double cell_eff_width[] = {2.0 * cell->width[0], 2.0 * cell->width[1], 2.0 * cell->width[2]}; /* Allocate array of replications for the new list */ const int nrep_max = rep_in->nrep; if (swift_memalign("lightcone_replications", (void **)&rep_out->replication, SWIFT_STRUCT_ALIGNMENT, sizeof(struct replication) * nrep_max) != 0) { error("Failed to allocate pruned lightcone replication list"); } /* Get distance limits (squared) used to make the input list */ const double lightcone_rmin2 = pow(rep_in->lightcone_rmin, 2.0); const double lightcone_rmax2 = pow(rep_in->lightcone_rmax, 2.0); /* Loop over all replications */ rep_out->nrep = 0; for (int i = 0; i < nrep_max; i += 1) { /* Get a pointer to this input replication */ const struct replication *rep = rep_in->replication + i; /* Find coordinates of centre of this replication of the cell relative to * the observer */ double cell_rep_centre[3]; for (int j = 0; j < 3; j += 1) { cell_rep_centre[j] = rep->coord[j] + cell_centre[j] - observer_position[j]; } /* Compute minimum possible distance squared from observer to this * replication of this cell */ double cell_rmin2 = 0.0; for (int j = 0; j < 3; j += 1) { double dx = fabs(cell_rep_centre[j]) - 0.5 * cell_eff_width[j]; if (dx < 0.0) dx = 0.0; cell_rmin2 += dx * dx; } /* Compute maximum possible distance squared from observer to this * replication of this cell */ double cell_rmax2 = 0.0; for (int j = 0; j < 3; j += 1) { double dx = fabs(cell_rep_centre[j]) + 0.5 * cell_eff_width[j]; cell_rmax2 += dx * dx; } /* Decide whether this cell could contribute to this replication */ if (cell_rmax2 >= lightcone_rmin2 && cell_rmin2 <= lightcone_rmax2) { memcpy(rep_out->replication + rep_out->nrep, rep, sizeof(struct replication)); rep_out->nrep += 1; } /* Next input replication */ } /* Not used currently */ rep_out->lightcone_rmin = rep_in->lightcone_rmin; rep_out->lightcone_rmax = rep_in->lightcone_rmax; }