From 41f2ac04f5fa9bb1fc2ebdc223894a6b689a8a47 Mon Sep 17 00:00:00 2001 From: Loic Hausammann <loic.hausammann@protonmail.ch> Date: Mon, 16 Mar 2020 12:24:48 +0000 Subject: [PATCH] Gear star formation split --- examples/main.c | 5 + src/cell.c | 250 +++++++++++++++++- src/cell.h | 7 + src/engine.h | 2 + src/runner_others.c | 19 +- src/space.c | 19 +- src/space.h | 3 + src/star_formation/EAGLE/star_formation.h | 34 ++- src/star_formation/GEAR/star_formation.h | 100 ++++++- src/star_formation/GEAR/star_formation_io.h | 13 + .../GEAR/star_formation_struct.h | 15 +- src/star_formation/QLA/star_formation.h | 34 ++- src/star_formation/none/star_formation.h | 35 ++- src/stars/GEAR/stars_io.h | 6 +- src/stars/GEAR/stars_part.h | 3 + 15 files changed, 521 insertions(+), 24 deletions(-) diff --git a/examples/main.c b/examples/main.c index 4784d532f4..2528ce589e 100644 --- a/examples/main.c +++ b/examples/main.c @@ -1204,6 +1204,11 @@ int main(int argc, char *argv[]) { fflush(stdout); } + /* Compute some stats for the star formation */ + if (with_star_formation) { + star_formation_first_init_stats(&starform, &e); + } + /* Get some info to the user. */ if (myrank == 0) { const long long N_DM = N_total[swift_type_dark_matter] + diff --git a/src/cell.c b/src/cell.c index d5b0499d00..65d0c1ad1b 100644 --- a/src/cell.c +++ b/src/cell.c @@ -5480,10 +5480,44 @@ void cell_recursively_shift_sparts(struct cell *c, } } +/** + * @brief Recursively update the pointer and counter for #gpart after the + * addition of a new particle. + * + * @param c The cell we are working on. + * @param progeny_list The list of the progeny index at each level for the + * leaf-cell where the particle was added. + * @param main_branch Are we in a cell directly above the leaf where the new + * particle was added? + */ +void cell_recursively_shift_gparts(struct cell *c, + const int progeny_list[space_cell_maxdepth], + const int main_branch) { + if (c->split) { + /* No need to recurse in progenies located before the insestion point */ + const int first_progeny = main_branch ? progeny_list[(int)c->depth] : 0; + + for (int k = first_progeny; k < 8; ++k) { + if (c->progeny[k] != NULL) + cell_recursively_shift_gparts(c->progeny[k], progeny_list, + main_branch && (k == first_progeny)); + } + } + + /* When directly above the leaf with the new particle: increase the particle + * count */ + /* When after the leaf with the new particle: shift by one position */ + if (main_branch) { + c->grav.count++; + } else { + c->grav.parts++; + } +} + /** * @brief "Add" a #spart in a given #cell. * - * This function will a a #spart at the start of the current cell's array by + * This function will add a #spart at the start of the current cell's array by * shifting all the #spart in the top-level cell by one position. All the * pointers and cell counts are updated accordingly. * @@ -5557,7 +5591,7 @@ struct spart *cell_add_spart(struct engine *e, struct cell *const c) { memmove(&c->stars.parts[1], &c->stars.parts[0], n_copy * sizeof(struct spart)); - /* Update the gpart->spart links (shift by 1) */ + /* Update the spart->gpart links (shift by 1) */ for (size_t i = 0; i < n_copy; ++i) { #ifdef SWIFT_DEBUG_CHECKS if (c->stars.parts[i + 1].gpart == NULL) { @@ -5610,6 +5644,140 @@ struct spart *cell_add_spart(struct engine *e, struct cell *const c) { return sp; } +/** + * @brief "Add" a #gpart in a given #cell. + * + * This function will add a #gpart at the start of the current cell's array by + * shifting all the #gpart in the top-level cell by one position. All the + * pointers and cell counts are updated accordingly. + * + * @param e The #engine. + * @param c The leaf-cell in which to add the #gpart. + * + * @return A pointer to the newly added #gpart. The gpart has a been zeroed + * and given a position within the cell as well as set to the minimal active + * time bin. + */ +struct gpart *cell_add_gpart(struct engine *e, struct cell *c) { + /* Perform some basic consitency checks */ + if (c->nodeID != engine_rank) error("Adding gpart on a foreign node"); + if (c->grav.ti_old_part != e->ti_current) error("Undrifted cell!"); + if (c->split) error("Addition of gpart performed above the leaf level"); + + struct space *s = e->s; + + /* Progeny number at each level */ + int progeny[space_cell_maxdepth]; +#ifdef SWIFT_DEBUG_CHECKS + for (int i = 0; i < space_cell_maxdepth; ++i) progeny[i] = -1; +#endif + + /* Get the top-level this leaf cell is in and compute the progeny indices at + each level */ + struct cell *top = c; + while (top->parent != NULL) { + /* What is the progeny index of the cell? */ + for (int k = 0; k < 8; ++k) { + if (top->parent->progeny[k] == top) { + progeny[(int)top->parent->depth] = k; + } + } + + /* Check that the cell was indeed drifted to this point to avoid future + * issues */ +#ifdef SWIFT_DEBUG_CHECKS + if (top->grav.super != NULL && top->grav.count > 0 && + top->grav.ti_old_part != e->ti_current) { + error("Cell had not been correctly drifted before adding a gpart"); + } +#endif + + /* Climb up */ + top = top->parent; + } + + /* Lock the top-level cell as we are going to operate on it */ + lock_lock(&top->grav.star_formation_lock); + + /* Are there any extra particles left? */ + if (top->grav.count == top->grav.count_total - 1) { + /* Release the local lock before exiting. */ + if (lock_unlock(&top->grav.star_formation_lock) != 0) + error("Failed to unlock the top-level cell."); + message("We ran out of gravity particles!"); + atomic_inc(&e->forcerebuild); + return NULL; + } + + /* Number of particles to shift in order to get a free space. */ + const size_t n_copy = &top->grav.parts[top->grav.count] - c->grav.parts; + +#ifdef SWIFT_DEBUG_CHECKS + if (c->grav.parts + n_copy > top->grav.parts + top->grav.count) + error("Copying beyond the allowed range"); +#endif + + if (n_copy > 0) { + // MATTHIEU: This can be improved. We don't need to copy everything, just + // need to swap a few particles. + memmove(&c->grav.parts[1], &c->grav.parts[0], + n_copy * sizeof(struct gpart)); + + /* Update the gpart->spart links (shift by 1) */ + struct gpart *gparts = c->grav.parts; + for (size_t i = 0; i < n_copy; ++i) { + if (gparts[i + 1].type == swift_type_gas) { + s->parts[-gparts[i + 1].id_or_neg_offset].gpart++; + } else if (gparts[i + 1].type == swift_type_stars) { + s->sparts[-gparts[i + 1].id_or_neg_offset].gpart++; + } else if (gparts[i + 1].type == swift_type_black_hole) { + s->bparts[-gparts[i + 1].id_or_neg_offset].gpart++; + } + } + } + + /* Recursively shift all the gpart to get a free spot at the start of the + * current cell*/ + cell_recursively_shift_gparts(top, progeny, /* main_branch=*/1); + + /* Make sure the gravity will be recomputed for this particle in the next + * step + */ + struct cell *top2 = c; + while (top2->parent != NULL) { + top2->grav.ti_old_part = e->ti_current; + top2 = top2->parent; + } + top2->grav.ti_old_part = e->ti_current; + + /* Release the lock */ + if (lock_unlock(&top->grav.star_formation_lock) != 0) + error("Failed to unlock the top-level cell."); + + /* We now have an empty gpart as the first particle in that cell */ + struct gpart *gp = &c->grav.parts[0]; + bzero(gp, sizeof(struct gpart)); + + /* Give it a decent position */ + gp->x[0] = c->loc[0] + 0.5 * c->width[0]; + gp->x[1] = c->loc[1] + 0.5 * c->width[1]; + gp->x[2] = c->loc[2] + 0.5 * c->width[2]; + + /* Set it to the current time-bin */ + gp->time_bin = e->min_active_bin; + +#ifdef SWIFT_DEBUG_CHECKS + /* Specify it was drifted to this point */ + gp->ti_drift = e->ti_current; +#endif + + /* Register that we used one of the free slots. */ + const size_t one = 1; + atomic_sub(&e->s->nr_extra_gparts, one); + + return gp; +} + /** * @brief "Remove" a gas particle from the calculation. * @@ -5935,6 +6103,84 @@ struct spart *cell_convert_part_to_spart(struct engine *e, struct cell *c, return sp; } +/** + * @brief Add a new #spart based on a #part and link it to a new #gpart. + * The part and xpart are not changed. + * + * @param e The #engine. + * @param c The #cell from which to remove the #part. + * @param p The #part to remove (must be inside c). + * @param xp The extended data of the #part. + * + * @return A fresh #spart with the same ID, position, velocity and + * time-bin as the original #part. + */ +struct spart *cell_spawn_new_spart_from_part(struct engine *e, struct cell *c, + const struct part *p, + const struct xpart *xp) { + /* Quick cross-check */ + if (c->nodeID != e->nodeID) + error("Can't spawn a particle in a foreign cell."); + + if (p->gpart == NULL) + error("Trying to create a new spart from a part without gpart friend!"); + + /* Create a fresh (empty) spart */ + struct spart *sp = cell_add_spart(e, c); + + /* Did we run out of free spart slots? */ + if (sp == NULL) return NULL; + + /* Copy over the distance since rebuild */ + sp->x_diff[0] = xp->x_diff[0]; + sp->x_diff[1] = xp->x_diff[1]; + sp->x_diff[2] = xp->x_diff[2]; + + /* Create a new gpart */ + struct gpart *gp = cell_add_gpart(e, c); + + /* Did we run out of free gpart slots? */ + if (gp == NULL) { + /* Remove the particle created */ + cell_remove_spart(e, c, sp); + return NULL; + } + + /* Copy the gpart */ + *gp = *p->gpart; + + /* Assign the ID back */ + sp->id = p->id; + gp->type = swift_type_stars; + + /* Re-link things */ + sp->gpart = gp; + gp->id_or_neg_offset = -(sp - e->s->sparts); + + /* Synchronize clocks */ + gp->time_bin = sp->time_bin; + + /* Synchronize masses, positions and velocities */ + sp->mass = p->mass; + sp->x[0] = p->x[0]; + sp->x[1] = p->x[1]; + sp->x[2] = p->x[2]; + sp->v[0] = xp->v_full[0]; + sp->v[1] = xp->v_full[1]; + sp->v[2] = xp->v_full[2]; + +#ifdef SWIFT_DEBUG_CHECKS + sp->ti_kick = p->ti_kick; + sp->ti_drift = p->ti_drift; +#endif + + /* Set a smoothing length */ + sp->h = p->h; + + /* Here comes the Sun! */ + return sp; +} + /** * @brief Re-arrange the #part in a top-level cell such that all the extra * ones for on-the-fly creation are located at the end of the array. diff --git a/src/cell.h b/src/cell.h index b4242bf1f5..f449b92907 100644 --- a/src/cell.h +++ b/src/cell.h @@ -518,6 +518,9 @@ struct cell { /*! Spin lock for various uses (#multipole case). */ swift_lock_type mlock; + /*! Spin lock for star formation use. */ + swift_lock_type star_formation_lock; + /*! Nr of #gpart in this cell. */ int count; @@ -932,6 +935,10 @@ void cell_remove_spart(const struct engine *e, struct cell *c, void cell_remove_bpart(const struct engine *e, struct cell *c, struct bpart *bp); struct spart *cell_add_spart(struct engine *e, struct cell *c); +struct gpart *cell_add_gpart(struct engine *e, struct cell *c); +struct spart *cell_spawn_new_spart_from_part(struct engine *e, struct cell *c, + const struct part *p, + const struct xpart *xp); struct gpart *cell_convert_part_to_gpart(const struct engine *e, struct cell *c, struct part *p, struct xpart *xp); struct gpart *cell_convert_spart_to_gpart(const struct engine *e, diff --git a/src/engine.h b/src/engine.h index 27cfd8dd0f..5c3ccc7980 100644 --- a/src/engine.h +++ b/src/engine.h @@ -547,6 +547,8 @@ void engine_launch(struct engine *e, const char *call); void engine_prepare(struct engine *e); void engine_init_particles(struct engine *e, int flag_entropy_ICs, int clean_h_values); +void engine_compute_star_formation_stats(struct engine *e, + struct star_formation *starform); void engine_step(struct engine *e); void engine_split(struct engine *e, struct partition *initial_partition); void engine_exchange_strays(struct engine *e, const size_t offset_parts, diff --git a/src/runner_others.c b/src/runner_others.c index f6d5d6bd5e..6e4f353e4e 100644 --- a/src/runner_others.c +++ b/src/runner_others.c @@ -330,9 +330,17 @@ void runner_do_star_formation(struct runner *r, struct cell *c, int timer) { /* Convert the gas particle to a star particle */ struct spart *sp = NULL; + const int spawn_spart = + star_formation_should_spawn_spart(p, xp, sf_props); if (swift_star_formation_model_creates_stars) { - sp = cell_convert_part_to_spart(e, c, p, xp); + /* Check if we should create a new particle or transform one */ + if (spawn_spart) { + sp = cell_spawn_new_spart_from_part(e, c, p, xp); + } else { + /* Convert the gas particle to a star particle */ + sp = cell_convert_part_to_spart(e, c, p, xp); + } } else { cell_convert_part_to_gpart(e, c, p, xp); } @@ -344,9 +352,9 @@ void runner_do_star_formation(struct runner *r, struct cell *c, int timer) { * c->cellID); */ /* Copy the properties of the gas particle to the star particle */ - star_formation_copy_properties(p, xp, sp, e, sf_props, cosmo, - with_cosmology, phys_const, - hydro_props, us, cooling); + star_formation_copy_properties( + p, xp, sp, e, sf_props, cosmo, with_cosmology, phys_const, + hydro_props, us, cooling, spawn_spart); /* Update the Star formation history */ star_formation_logger_log_new_spart(sp, &c->stars.sfh); @@ -550,7 +558,8 @@ void runner_do_end_grav_force(struct runner *r, struct cell *c, int timer) { #ifdef SWIFT_DEBUG_CHECKS if ((e->policy & engine_policy_self_gravity) && - !(e->policy & engine_policy_black_holes)) { + !(e->policy & engine_policy_black_holes) && + !(e->policy & engine_policy_star_formation)) { /* Let's add a self interaction to simplify the count */ gp->num_interacted++; diff --git a/src/space.c b/src/space.c index b63a261aab..b6d38f88f1 100644 --- a/src/space.c +++ b/src/space.c @@ -577,12 +577,14 @@ void space_regrid(struct space *s, int verbose) { error("Failed to init spinlock for gravity."); if (lock_init(&s->cells_top[k].grav.mlock) != 0) error("Failed to init spinlock for multipoles."); + if (lock_init(&s->cells_top[k].grav.star_formation_lock) != 0) + error("Failed to init spinlock for star formation (gpart)."); if (lock_init(&s->cells_top[k].stars.lock) != 0) error("Failed to init spinlock for stars."); if (lock_init(&s->cells_top[k].black_holes.lock) != 0) error("Failed to init spinlock for black holes."); if (lock_init(&s->cells_top[k].stars.star_formation_lock) != 0) - error("Failed to init spinlock for star formation."); + error("Failed to init spinlock for star formation (spart)."); } /* Set the cell location and sizes. */ @@ -781,7 +783,7 @@ void space_allocate_extras(struct space *s, int verbose) { /* Do we have enough space for the extra gparts (i.e. we haven't used up any) * ? */ - if (nr_gparts + expected_num_extra_gparts > size_gparts) { + if (nr_actual_gparts + expected_num_extra_gparts > nr_gparts) { /* Ok... need to put some more in the game */ @@ -817,6 +819,10 @@ void space_allocate_extras(struct space *s, int verbose) { if (s->sparts[i].time_bin != time_bin_not_created) s->sparts[i].gpart += delta; } + for (size_t i = 0; i < nr_bparts; ++i) { + if (s->bparts[i].time_bin != time_bin_not_created) + s->bparts[i].gpart += delta; + } } /* Turn some of the allocated spares into particles we can use */ @@ -3869,7 +3875,8 @@ void space_recycle(struct space *s, struct cell *c) { if (lock_destroy(&c->hydro.lock) != 0 || lock_destroy(&c->grav.plock) != 0 || lock_destroy(&c->grav.mlock) != 0 || lock_destroy(&c->stars.lock) != 0 || lock_destroy(&c->black_holes.lock) != 0 || - lock_destroy(&c->stars.star_formation_lock)) + lock_destroy(&c->grav.star_formation_lock) != 0 || + lock_destroy(&c->stars.star_formation_lock) != 0) error("Failed to destroy spinlocks."); /* Lock the space. */ @@ -3922,7 +3929,8 @@ void space_recycle_list(struct space *s, struct cell *cell_list_begin, lock_destroy(&c->grav.mlock) != 0 || lock_destroy(&c->stars.lock) != 0 || lock_destroy(&c->black_holes.lock) != 0 || - lock_destroy(&c->stars.star_formation_lock)) + lock_destroy(&c->stars.star_formation_lock) != 0 || + lock_destroy(&c->grav.star_formation_lock) != 0) error("Failed to destroy spinlocks."); /* Count this cell. */ @@ -4023,7 +4031,8 @@ void space_getcells(struct space *s, int nr_cells, struct cell **cells) { lock_init(&cells[j]->grav.mlock) != 0 || lock_init(&cells[j]->stars.lock) != 0 || lock_init(&cells[j]->black_holes.lock) != 0 || - lock_init(&cells[j]->stars.star_formation_lock) != 0) + lock_init(&cells[j]->stars.star_formation_lock) != 0 || + lock_init(&cells[j]->grav.star_formation_lock) != 0) error("Failed to initialize cell spinlocks."); } } diff --git a/src/space.h b/src/space.h index 885f9ba85e..e98b1b1a0e 100644 --- a/src/space.h +++ b/src/space.h @@ -40,6 +40,7 @@ struct cell; struct cosmology; struct gravity_props; +struct star_formation; /* Some constants. */ #define space_cellallocchunk 1000 @@ -384,5 +385,7 @@ void space_free_foreign_parts(struct space *s, const int clear_cell_pointers); void space_struct_dump(struct space *s, FILE *stream); void space_struct_restore(struct space *s, FILE *stream); void space_write_cell_hierarchy(const struct space *s, int j); +void space_compute_star_formation_stats(const struct space *s, + struct star_formation *star_form); #endif /* SWIFT_SPACE_H */ diff --git a/src/star_formation/EAGLE/star_formation.h b/src/star_formation/EAGLE/star_formation.h index 77b3acf3b0..15be7ea6b7 100644 --- a/src/star_formation/EAGLE/star_formation.h +++ b/src/star_formation/EAGLE/star_formation.h @@ -368,6 +368,21 @@ INLINE static int star_formation_should_convert_to_star( return (prob > random_number); } +/** + * @brief Decides whether a new particle should be created or if the hydro + * particle needs to be transformed. + * + * @param p The #part. + * @param xp The #xpart. + * @param starform The properties of the star formation model. + * + * @return 1 if a new spart needs to be created. + */ +INLINE static int star_formation_should_spawn_spart( + struct part* p, struct xpart* xp, const struct star_formation* starform) { + return 0; +} + /** * @brief Update the SF properties of a particle that is not star forming. * @@ -409,6 +424,7 @@ INLINE static void star_formation_update_part_not_SFR( * @param hydro_props The properties of the hydro scheme. * @param us The internal system of units. * @param cooling The cooling data struct. + * @param convert_part Did we convert a part (or spawned one)? */ INLINE static void star_formation_copy_properties( const struct part* p, const struct xpart* xp, struct spart* sp, @@ -417,7 +433,8 @@ INLINE static void star_formation_copy_properties( const struct phys_const* phys_const, const struct hydro_props* restrict hydro_props, const struct unit_system* restrict us, - const struct cooling_function_data* restrict cooling) { + const struct cooling_function_data* restrict cooling, + const int convert_part) { /* Store the current mass */ sp->mass = hydro_get_mass(p); @@ -752,4 +769,19 @@ star_formation_no_spart_available(const struct engine* e, const struct part* p, /* Nothing to do, we just skip it and deal with it next step */ } +/** + * @brief Compute some information for the star formation model based + * on all the particles that were read in. + * + * This is called once on start-up of the code. + * + * Nothing to do here for EAGLE. + * + * @param star_form The #star_formation structure. + * @param e The #engine. + */ +__attribute__((always_inline)) INLINE static void +star_formation_first_init_stats(struct star_formation* star_form, + const struct engine* e) {} + #endif /* SWIFT_EAGLE_STAR_FORMATION_H */ diff --git a/src/star_formation/GEAR/star_formation.h b/src/star_formation/GEAR/star_formation.h index f2afc77f9d..27980271b3 100644 --- a/src/star_formation/GEAR/star_formation.h +++ b/src/star_formation/GEAR/star_formation.h @@ -145,11 +145,24 @@ INLINE static int star_formation_should_convert_to_star( const float G = phys_const->const_newton_G; const float density = hydro_get_physical_density(p, cosmo); + /* Get the mass of the future possible star */ + const float mass_gas = hydro_get_mass(p); + /* Compute the probability */ const float inv_free_fall_time = sqrtf(density * 32.f * G * 0.33333333f * M_1_PI); - const float prob = 1.f - exp(-starform->star_formation_efficiency * - inv_free_fall_time * dt_star); + float prob = 1.f - expf(-starform->star_formation_efficiency * + inv_free_fall_time * dt_star); + + /* Add the mass factor */ + if (starform->n_stars_per_part != 1) { + const float min_mass = + starform->mass_stars * starform->min_mass_frac_plus_one; + const float mass_star = + mass_gas > min_mass ? starform->mass_stars : mass_gas; + + prob *= mass_gas / mass_star; + } /* Roll the dice... */ const float random_number = @@ -159,6 +172,29 @@ INLINE static int star_formation_should_convert_to_star( return random_number < prob; } +/** + * @brief Decides whether a new particle should be created or if the hydro + * particle needs to be transformed. + * + * @param p The #part. + * @param xp The #xpart. + * @param starform The properties of the star formation model. + * + * @return 1 if a new spart needs to be created. + */ +INLINE static int star_formation_should_spawn_spart( + struct part* p, struct xpart* xp, const struct star_formation* starform) { + + /* Check if we are splitting the particles or not */ + if (starform->n_stars_per_part == 1) { + return 0; + } + + const float mass_min = + starform->min_mass_frac_plus_one * starform->mass_stars; + return hydro_get_mass(p) > mass_min; +} + /** * @brief Update the SF properties of a particle that is not star forming. * @@ -184,18 +220,35 @@ INLINE static void star_formation_update_part_not_SFR( * @param phys_const the physical constants in internal units. * @param cosmo the cosmological parameters and properties. * @param with_cosmology if we run with cosmology. + * @param convert_part Did we convert a part (or spawned one)? */ INLINE static void star_formation_copy_properties( - const struct part* p, const struct xpart* xp, struct spart* sp, + struct part* p, const struct xpart* xp, struct spart* sp, const struct engine* e, const struct star_formation* starform, const struct cosmology* cosmo, const int with_cosmology, const struct phys_const* phys_const, const struct hydro_props* restrict hydro_props, const struct unit_system* restrict us, - const struct cooling_function_data* restrict cooling) { + const struct cooling_function_data* restrict cooling, + const int convert_part) { /* Store the current mass */ - sp->mass = hydro_get_mass(p); + const float mass_gas = hydro_get_mass(p); + if (!convert_part) { + /* Update the spart */ + const float min_mass = + starform->mass_stars * starform->min_mass_frac_plus_one; + const float mass_star = + mass_gas > min_mass ? starform->mass_stars : mass_gas; + sp->mass = mass_star; + sp->gpart->mass = mass_star; + + /* Update the part */ + hydro_set_mass(p, mass_gas - mass_star); + p->gpart->mass = mass_gas - mass_star; + } else { + sp->mass = mass_gas; + } sp->birth.mass = sp->mass; /* Store either the birth_scale_factor or birth_time depending */ @@ -217,6 +270,9 @@ INLINE static void star_formation_copy_properties( /* Copy the chemistry properties */ chemistry_copy_star_formation_properties(p, xp, sp); + + /* Copy the progenitor id */ + sp->prog_id = p->id; } /** @@ -325,4 +381,38 @@ star_formation_no_spart_available(const struct engine* e, const struct part* p, "or Scheduler:cell_extra_gparts"); } +/** + * @brief Compute some information for the star formation model based + * on all the particles that were read in. + * + * This is called once on start-up of the code. + * + * @param star_form The #star_formation structure. + * @param e The #engine. + */ +__attribute__((always_inline)) INLINE static void +star_formation_first_init_stats(struct star_formation* star_form, + const struct engine* e) { + + const struct space* s = e->s; + double avg_mass = 0; + + /* Sum the mass over all the particles. */ + for (size_t i = 0; i < s->nr_parts; i++) { + avg_mass += hydro_get_mass(&s->parts[i]); + } + +#ifdef WITH_MPI + /* Compute the contribution from the other nodes. */ + MPI_Allreduce(MPI_IN_PLACE, &avg_mass, 1, MPI_DOUBLE, MPI_SUM, + MPI_COMM_WORLD); +#endif + + star_form->mass_stars = avg_mass / e->total_nr_parts; + + if (e->nodeID == 0) { + message("Average hydro mass: %g", star_form->mass_stars); + } +} + #endif /* SWIFT_GEAR_STAR_FORMATION_H */ diff --git a/src/star_formation/GEAR/star_formation_io.h b/src/star_formation/GEAR/star_formation_io.h index 7e6dcefce6..f99458a931 100644 --- a/src/star_formation/GEAR/star_formation_io.h +++ b/src/star_formation/GEAR/star_formation_io.h @@ -64,12 +64,25 @@ INLINE static void starformation_init_backend( starform->maximal_temperature = parser_get_param_double( parameter_file, "GEARStarFormation:maximal_temperature"); + /* Number of stars per particles */ + starform->n_stars_per_part = parser_get_param_double( + parameter_file, "GEARStarFormation:n_stars_per_particle"); + + /* Minimal fraction of mass for the last star formed. */ + starform->min_mass_frac_plus_one = parser_get_param_double( + parameter_file, "GEARStarFormation:min_mass_frac"); + /* Avoid generating gas particle with mass below the fraction => + 1. */ + starform->min_mass_frac_plus_one += 1.; + /* Get the jeans factor */ starform->n_jeans_2_3 = pow(pressure_floor_props.n_jeans, 2. / 3.); /* Apply unit change */ starform->maximal_temperature *= units_cgs_conversion_factor(us, UNIT_CONV_TEMPERATURE); + + /* Initialize the mass of the stars to 0 for the stats computation */ + starform->mass_stars = 0; } #endif /* SWIFT_STAR_FORMATION_GEAR_IO_H */ diff --git a/src/star_formation/GEAR/star_formation_struct.h b/src/star_formation/GEAR/star_formation_struct.h index 9d6d78df5c..c03a8ae0a4 100644 --- a/src/star_formation/GEAR/star_formation_struct.h +++ b/src/star_formation/GEAR/star_formation_struct.h @@ -34,14 +34,23 @@ struct star_formation_xpart_data { struct star_formation { /*! Number of particle required to resolved the - * Jeans criterion (at power 2/3) */ + * Jeans criterion (at power 2/3). */ float n_jeans_2_3; - /*! Maximal temperature for forming a star */ + /*! Maximal temperature for forming a star. */ float maximal_temperature; - /*! Star formation efficiency */ + /*! Star formation efficiency. */ float star_formation_efficiency; + + /*! Number of possible stars per particle. */ + int n_stars_per_part; + + /*! Mass of a star. */ + float mass_stars; + + /*! Minimal fraction of mass_stars for the last star formed by a part. */ + float min_mass_frac_plus_one; }; #endif /* SWIFT_GEAR_STAR_FORMATION_STRUCT_H */ diff --git a/src/star_formation/QLA/star_formation.h b/src/star_formation/QLA/star_formation.h index a0ffa22707..aa94212208 100644 --- a/src/star_formation/QLA/star_formation.h +++ b/src/star_formation/QLA/star_formation.h @@ -151,6 +151,7 @@ INLINE static void star_formation_update_part_not_SFR( * @param hydro_props The properties of the hydro scheme. * @param us The internal system of units. * @param cooling The cooling data struct. + * @param convert_part Did we convert a part(or spawned one)? */ INLINE static void star_formation_copy_properties( const struct part* p, const struct xpart* xp, struct spart* sp, @@ -159,7 +160,8 @@ INLINE static void star_formation_copy_properties( const struct phys_const* phys_const, const struct hydro_props* restrict hydro_props, const struct unit_system* restrict us, - const struct cooling_function_data* restrict cooling) { + const struct cooling_function_data* restrict cooling, + const int convert_part) { /* Store the current mass */ sp->mass = hydro_get_mass(p); @@ -297,4 +299,34 @@ star_formation_no_spart_available(const struct engine* e, const struct part* p, /* Nothing to do since we just turn gas particles into DM. */ } +/** + * @brief Decides whether a new particle should be created or if the hydro + * particle needs to be transformed. + * + * @param p The #part. + * @param xp The #xpart. + * @param starform The properties of the star formation model. + * + * @return 1 if a new spart needs to be created. + */ +INLINE static int star_formation_should_spawn_spart( + struct part* p, struct xpart* xp, const struct star_formation* starform) { + return 0; +} + +/** + * @brief Compute some information for the star formation model based + * on all the particles that were read in. + * + * This is called once on start-up of the code. + * + * Nothing to do here for the quick Lyman-alpha model. + * + * @param star_form The #star_formation structure. + * @param e The #engine. + */ +__attribute__((always_inline)) INLINE static void +star_formation_first_init_stats(struct star_formation* star_form, + const struct engine* e) {} + #endif /* SWIFT_QLA_STAR_FORMATION_H */ diff --git a/src/star_formation/none/star_formation.h b/src/star_formation/none/star_formation.h index 589cd0f108..78f8a7c617 100644 --- a/src/star_formation/none/star_formation.h +++ b/src/star_formation/none/star_formation.h @@ -101,6 +101,21 @@ INLINE static int star_formation_should_convert_to_star( return 0; } +/** + * @brief Decides whether a new particle should be created or if the hydro + * particle needs to be transformed. + * + * @param p The #part. + * @param xp The #xpart. + * @param starform The properties of the star formation model. + * + * @return 1 if a new spart needs to be created. + */ +INLINE static int star_formation_should_spawn_spart( + struct part* p, struct xpart* xp, const struct star_formation* starform) { + return 0; +} + /** * @brief Update the SF properties of a particle that is not star forming. * @@ -130,6 +145,7 @@ INLINE static void star_formation_update_part_not_SFR( * @param phys_const the physical constants in internal units. * @param cosmo the cosmological parameters and properties. * @param with_cosmology if we run with cosmology. + * @param convert_part Did we convert a part (or spawned one)? */ INLINE static void star_formation_copy_properties( const struct part* p, const struct xpart* xp, struct spart* sp, @@ -138,7 +154,8 @@ INLINE static void star_formation_copy_properties( const struct phys_const* phys_const, const struct hydro_props* restrict hydro_props, const struct unit_system* restrict us, - const struct cooling_function_data* restrict cooling) {} + const struct cooling_function_data* restrict cooling, + const int convert_part) {} /** * @brief initialization of the star formation law @@ -250,4 +267,20 @@ star_formation_no_spart_available(const struct engine* e, const struct part* p, const struct xpart* xp) { /* Nothing to do, we just skip it and deal with it next step */ } + +/** + * @brief Compute some information for the star formation model based + * on all the particles that were read in. + * + * This is called once on start-up of the code. + * + * Nothing to do here. + * + * @param star_form The #star_formation structure. + * @param e The #engine. + */ +__attribute__((always_inline)) INLINE static void +star_formation_first_init_stats(struct star_formation* star_form, + const struct engine* e) {} + #endif /* SWIFT_NONE_STAR_FORMATION_H */ diff --git a/src/stars/GEAR/stars_io.h b/src/stars/GEAR/stars_io.h index a5a6e7368b..83d3f8e418 100644 --- a/src/stars/GEAR/stars_io.h +++ b/src/stars/GEAR/stars_io.h @@ -119,7 +119,7 @@ INLINE static void stars_write_particles(const struct spart *sparts, const int with_cosmology) { /* Say how much we want to write */ - *num_fields = 9; + *num_fields = 10; /* List what we want to write */ list[0] = io_make_output_field_convert_spart( @@ -169,6 +169,10 @@ INLINE static void stars_write_particles(const struct spart *sparts, sparts, birth.mass, "Masses of the star particles at birth time"); + list[9] = io_make_output_field("ProgenitorIDs", LONGLONG, 1, + UNIT_CONV_NO_UNITS, 0.f, sparts, prog_id, + "Unique IDs of the progenitor particle"); + #ifdef DEBUG_INTERACTIONS_STARS list += *num_fields; diff --git a/src/stars/GEAR/stars_part.h b/src/stars/GEAR/stars_part.h index bee9b5c6a3..0e1e8a983b 100644 --- a/src/stars/GEAR/stars_part.h +++ b/src/stars/GEAR/stars_part.h @@ -37,6 +37,9 @@ struct spart { /*! Particle ID. */ long long id; + /*! Progenitor ID */ + long long prog_id; + /*! Pointer to corresponding gravity part. */ struct gpart* gpart; -- GitLab