MP-Gadget  5.0.1.dev1-76bc7d4726-dirty
metal_return.c
Go to the documentation of this file.
1 #include <mpi.h>
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <math.h>
6 #include <gsl/gsl_math.h>
7 #include <gsl/gsl_integration.h>
8 #include <gsl/gsl_interp2d.h>
9 #include <gsl/gsl_roots.h>
10 #include <gsl/gsl_errno.h>
11 #include <omp.h>
12 
13 #include "physconst.h"
14 #include "walltime.h"
15 #include "slotsmanager.h"
16 #include "treewalk.h"
17 #include "metal_return.h"
18 #include "densitykernel.h"
19 #include "density.h"
20 #include "cosmology.h"
21 #include "winds.h"
22 #include "utils/spinlocks.h"
23 #include "metal_tables.h"
24 
43 #if NMETALS != NSPECIES
44  #pragma error " Inconsistency in metal number between slots and metals"
45 #endif
46 
47 static struct metal_return_params
48 {
49  double Sn1aN0;
53 
54 /* For tests*/
55 void set_metal_params(double Sn1aN0)
56 {
57  MetalParams.Sn1aN0 = Sn1aN0;
58 }
59 
60 /*Set the parameters of the hydro module*/
61 void
63 {
64  int ThisTask;
65  MPI_Comm_rank(MPI_COMM_WORLD, &ThisTask);
66  if(ThisTask == 0) {
67  MetalParams.Sn1aN0 = param_get_double(ps, "MetalsSn1aN0");
68  MetalParams.SPHWeighting = param_get_int(ps, "MetalsSPHWeighting");
69  MetalParams.MaxNgbDeviation = param_get_double(ps, "MetalsMaxNgbDeviation");
70  }
71  MPI_Bcast(&MetalParams, sizeof(struct metal_return_params), MPI_BYTE, 0, MPI_COMM_WORLD);
72 }
73 
74 /* Build the interpolators for each yield table. We use bilinear interpolation
75  * so there is no extra memory allocation and we never free the tables*/
77 {
78  interp->lifetime_interp = gsl_interp2d_alloc(gsl_interp2d_bilinear, LIFE_NMET, LIFE_NMASS);
79  gsl_interp2d_init(interp->lifetime_interp, lifetime_metallicity, lifetime_masses, lifetime, LIFE_NMET, LIFE_NMASS);
80  interp->agb_mass_interp = gsl_interp2d_alloc(gsl_interp2d_bilinear, AGB_NMET, AGB_NMASS);
81  gsl_interp2d_init(interp->agb_mass_interp, agb_metallicities, agb_masses, agb_total_mass, AGB_NMET, AGB_NMASS);
82  interp->agb_metallicity_interp = gsl_interp2d_alloc(gsl_interp2d_bilinear, AGB_NMET, AGB_NMASS);
83  gsl_interp2d_init(interp->agb_metallicity_interp, agb_metallicities, agb_masses, agb_total_metals, AGB_NMET, AGB_NMASS);
84  int i;
85  for(i=0; i<NMETALS; i++) {
86  interp->agb_metals_interp[i] = gsl_interp2d_alloc(gsl_interp2d_bilinear, AGB_NMET, AGB_NMASS);
87  gsl_interp2d_init(interp->agb_metals_interp[i], agb_metallicities, agb_masses, agb_yield[i], AGB_NMET, AGB_NMASS);
88  }
89  interp->snii_mass_interp = gsl_interp2d_alloc(gsl_interp2d_bilinear, SNII_NMET, SNII_NMASS);
90  gsl_interp2d_init(interp->snii_mass_interp, snii_metallicities, snii_masses, snii_total_mass, SNII_NMET, SNII_NMASS);
91  interp->snii_metallicity_interp = gsl_interp2d_alloc(gsl_interp2d_bilinear, SNII_NMET, SNII_NMASS);
92  gsl_interp2d_init(interp->snii_metallicity_interp, snii_metallicities, snii_masses, snii_total_metals, SNII_NMET, SNII_NMASS);
93  for(i=0; i<NMETALS; i++) {
94  interp->snii_metals_interp[i] = gsl_interp2d_alloc(gsl_interp2d_bilinear, SNII_NMET, SNII_NMASS);
95  gsl_interp2d_init(interp->snii_metals_interp[i], snii_metallicities, snii_masses, snii_yield[i], SNII_NMET, SNII_NMASS);
96  }
97 }
98 
99 #define METALS_GET_PRIV(tw) ((struct MetalReturnPriv*) ((tw)->priv))
100 
101 typedef struct {
107  /* This is the metal/mass generated this timestep.*/
108  MyFloat MetalSpeciesGenerated[NMETALS];
112 
113 typedef struct {
115  /* This is the total mass returned to
116  * the surrounding gas particles, for mass conservation.*/
119 
120 typedef struct {
124 
125 static int
126 metal_return_haswork(int n, TreeWalk * tw);
127 
128 static void
132  TreeWalkNgbIterMetals * iter,
133  LocalTreeWalk * lv
134  );
135 
136 static void
137 metal_return_copy(int place, TreeWalkQueryMetals * input, TreeWalk * tw);
138 
139 static void
140 metal_return_postprocess(int place, TreeWalk * tw);
141 
142 /* The Chabrier IMF used for computing SnII and AGB yields.
143  * See 1305.2913 eq 3*/
144 static double chabrier_imf(double mass)
145 {
146  if(mass <= 1) {
147  return 0.852464 / mass * exp(- pow(log(mass / 0.079)/ 0.69, 2)/2);
148  }
149  else {
150  return 0.237912 * pow(mass, -2.3);
151  }
152 }
153 
154 double atime_integ(double atime, void * params)
155 {
156  Cosmology * CP = (Cosmology *) params;
157  return 1/(hubble_function(CP, atime) * atime);
158 }
159 
160 /* Compute the difference in internal time units between two scale factors.*/
161 static double atime_to_myr(Cosmology *CP, double atime1, double atime2, gsl_integration_workspace * gsl_work)
162 {
163  /* t = dt/da da = 1/(Ha) da*/
164  /* Approximate hubble function as constant here: we only care
165  * about metal return over a single timestep*/
166  gsl_function ff = {atime_integ, CP};
167  double tmyr, abserr;
168  gsl_integration_qag(&ff, atime1, atime2, 1e-4, 0, GSL_WORKSPACE, GSL_INTEG_GAUSS61, gsl_work, &tmyr, &abserr);
169  return tmyr * CP->UnitTime_in_s / SEC_PER_MEGAYEAR;
170 }
171 
172 /* Functions for the root finder*/
174 {
175  double dtfind;
176  double stellarmetal;
177  gsl_interp2d * lifetime_tables;
178  gsl_interp_accel * metalacc;
179  gsl_interp_accel * massacc;
180 };
181 
182 /* This is the inverse of the lifetime function from the tables.
183  * Need to find the stars with a given lifetime*/
184 double
185 massendlife (double mass, void *params)
186 {
187  struct massbin_find_params *p = (struct massbin_find_params *) params;
188  double tlife = gsl_interp2d_eval(p->lifetime_tables, lifetime_metallicity, lifetime_masses, lifetime, p->stellarmetal, mass, p->metalacc, p->massacc);
189  double tlifemyr = tlife/1e6;
190  return tlifemyr - p->dtfind;
191 }
192 
193 /* Solve the lifetime function to find the lowest and highest mass bin that dies this timestep*/
194 double do_rootfinding(struct massbin_find_params *p, double mass_low, double mass_high)
195 {
196  int iter = 0;
197  gsl_function F;
198 
199  F.function = &massendlife;
200  F.params = p;
201 
202  const gsl_root_fsolver_type *T = gsl_root_fsolver_falsepos;
203  gsl_root_fsolver * s = gsl_root_fsolver_alloc (T);
204  gsl_root_fsolver_set (s, &F, mass_low, mass_high);
205 
206  /* Iterate until we have an idea of the mass bins dying this timestep.
207  * No check is done for success, but it should always be close enough.*/
208  for(iter = 0; iter < MAXITER; iter++)
209  {
210  gsl_root_fsolver_iterate (s);
211  mass_low = gsl_root_fsolver_x_lower (s);
212  mass_high = gsl_root_fsolver_x_upper (s);
213  int status = gsl_root_test_interval (mass_low, mass_high,
214  0, 0.005);
215  //message(4, "lo %g hi %g root %g val %g\n", mass_low, mass_high, gsl_root_fsolver_root(s), massendlife(gsl_root_fsolver_root(s), p));
216  if (status == GSL_SUCCESS)
217  break;
218  }
219  double root = gsl_root_fsolver_root(s);
220  gsl_root_fsolver_free (s);
221  return root;
222 }
223 
224 /* Find the mass bins which die in this timestep using the lifetime table.
225  * dtstart, dtend - time at start and end of timestep in Myr.
226  * stellarmetal - metallicity of the star.
227  * lifetime_tables - 2D interpolation table of the lifetime.
228  * masshigh, masslow - pointers in which to store the high and low lifetime limits
229  */
230 void find_mass_bin_limits(double * masslow, double * masshigh, const double dtstart, const double dtend, double stellarmetal, gsl_interp2d * lifetime_tables)
231 {
232  /* Clamp metallicities to the table values.*/
237 
238  /* Find the root with GSL routines. */
239  struct massbin_find_params p = {0};
240  p.metalacc = gsl_interp_accel_alloc();
241  p.massacc = gsl_interp_accel_alloc();
244  /* First find stars that died before the end of this timebin*/
245  p.dtfind = dtend;
246  /* If no stars have died yet*/
247  if(massendlife (MAXMASS, &p) >= 0)
248  {
249  *masslow = MAXMASS;
250  *masshigh = MAXMASS;
251  return;
252  }
253  /* All stars die before the end of this timestep*/
254  if(massendlife (agb_masses[0], &p) <= 0)
255  *masslow = lifetime_masses[0];
256  else
257  *masslow = do_rootfinding(&p, agb_masses[0], MAXMASS);
258 
259  /* Now find stars that died before the start of this timebin*/
260  p.dtfind = dtstart;
261  /* Now we know that life(masslow) = dtend, so life(masslow) > dtstart, so life(masslow) - dtstart > 0
262  * This is when no stars have died at the beginning of this timestep.*/
263  if(massendlife (MAXMASS, &p) >= 0)
264  *masshigh = MAXMASS;
265  /* This can sometimes happen due to root finding inaccuracy.
266  * Just do this star next timestep.*/
267  else if(massendlife (*masslow, &p) <= 0)
268  *masshigh = *masslow;
269  else
270  *masshigh = do_rootfinding(&p, *masslow, MAXMASS);
271  gsl_interp_accel_free(p.metalacc);
272  gsl_interp_accel_free(p.massacc);
273 }
274 
275 /* Parameters of the interpolator
276  * to hand to the imf integral.
277  * Use different interpolation structures
278  * for mass return, metal return and yield.*/
280 {
281  gsl_interp2d * interp;
282  const double * masses;
283  const double * metallicities;
284  const double * weights;
285  double metallicity;
286 };
287 
288 /* Integrand for a function which computes a Chabrier IMF weighted quantity.*/
289 double chabrier_imf_integ (double mass, void * params)
290 {
291  struct imf_integ_params * para = (struct imf_integ_params * ) params;
292  /* This is needed so that the yield for SNII with masses between 8 and 13 Msun
293  * are the same as the smallest mass in the table, 13 Msun,
294  * but they still contribute their number density to the IMF.*/
295  double intpmass = mass;
296  if(mass < para->masses[0])
297  intpmass = para->masses[0];
298  if(mass > para->masses[para->interp->ysize-1])
299  intpmass = para->masses[para->interp->ysize-1];
300  double weight = gsl_interp2d_eval(para->interp, para->metallicities, para->masses, para->weights, para->metallicity, intpmass, NULL, NULL);
301  /* This rescales the return by the original mass of the star, if it was outside the table.
302  * It means that, for example, an 8 Msun star does not return more than 8 Msun. */
303  weight *= (mass/intpmass);
304  return weight * chabrier_imf(mass);
305 }
306 
307 /* Helper for the IMF normalisation*/
308 double chabrier_mass(double mass, void * params)
309 {
310  return mass * chabrier_imf(mass);
311 }
312 
313 /* Compute factor to normalise the total mass in the IMF to unity.*/
314 double compute_imf_norm(gsl_integration_workspace * gsl_work)
315 {
316  double norm, abserr;
317  gsl_function ff = {chabrier_mass, NULL};
318  gsl_integration_qag(&ff, MINMASS, MAXMASS, 1e-4, 1e-3, GSL_WORKSPACE, GSL_INTEG_GAUSS61, gsl_work, &norm, &abserr);
319  return norm;
320 }
321 
322 /* Compute number of Sn1a: has units of N0 = 1.3e-3, which is SN1A/(unit initial mass in M_sun).
323  * Zero for age < 40 Myr. */
324 double sn1a_number(double dtmyrstart, double dtmyrend, double hub)
325 {
326  /* Number of Sn1a events follows a delay time distribution (1305.2913, eq. 10) */
327  const double sn1aindex = 1.12;
328  const double tau8msun = 40;
329  if(dtmyrend < tau8msun)
330  return 0;
331  /* Lower integration limit modelling formation time of WDs*/
332  if(dtmyrstart < tau8msun)
333  dtmyrstart = tau8msun;
334  /* Total number of Sn1a events from this star: integral evaluated from t=tau8msun to t=hubble time.*/
335  const double totalSN1a = 1- pow(1/(hub*HUBBLE * SEC_PER_MEGAYEAR)/tau8msun, 1-sn1aindex);
336  /* This is the integral of the DTD, normalised to the N0 rate which is in SN/M_sun.*/
337  double Nsn1a = MetalParams.Sn1aN0 /totalSN1a * (pow(dtmyrstart / tau8msun, 1-sn1aindex) - pow(dtmyrend / tau8msun, 1-sn1aindex));
338  return Nsn1a;
339 }
340 
341 /* Compute yield of AGB stars: this is normalised to the yield which has units of Msun / (unit Msun in the initial SSP and so is really dimensionless.)*/
342 double compute_agb_yield(gsl_interp2d * agb_interp, const double * agb_weights, double stellarmetal, double masslow, double masshigh, gsl_integration_workspace * gsl_work )
343 {
344  struct imf_integ_params para;
345  gsl_function ff = {chabrier_imf_integ, &para};
346  double agbyield = 0, abserr;
347  /* Only return AGB metals for the range of AGB stars*/
348  if (masshigh > SNAGBSWITCH)
349  masshigh = SNAGBSWITCH;
350  if (masslow < agb_masses[0])
351  masslow = agb_masses[0];
352  if (stellarmetal > agb_metallicities[AGB_NMET-1])
353  stellarmetal = agb_metallicities[AGB_NMET-1];
354  if (stellarmetal < agb_metallicities[0])
355  stellarmetal = agb_metallicities[0];
356  /* This happens if no bins in range had dying stars this timestep*/
357  if(masslow >= masshigh)
358  return 0;
359  para.interp = agb_interp;
360  para.masses = agb_masses;
362  para.metallicity = stellarmetal;
363  para.weights = agb_weights;
364  gsl_integration_qag(&ff, masslow, masshigh, 1e-7, 1e-3, GSL_WORKSPACE, GSL_INTEG_GAUSS61, gsl_work, &agbyield, &abserr);
365  return agbyield;
366 }
367 
368 double compute_snii_yield(gsl_interp2d * snii_interp, const double * snii_weights, double stellarmetal, double masslow, double masshigh, gsl_integration_workspace * gsl_work )
369 {
370  struct imf_integ_params para;
371  gsl_function ff = {chabrier_imf_integ, &para};
372  double yield = 0, abserr;
373  /* Only return metals for the range of SNII stars.*/
374  if (masshigh > snii_masses[SNII_NMASS-1])
375  masshigh = snii_masses[SNII_NMASS-1];
376  if (masslow < SNAGBSWITCH)
377  masslow = SNAGBSWITCH;
378  if (stellarmetal > snii_metallicities[SNII_NMET-1])
379  stellarmetal = snii_metallicities[SNII_NMET-1];
380  if (stellarmetal < snii_metallicities[0])
381  stellarmetal = snii_metallicities[0];
382  para.interp = snii_interp;
383  para.masses = snii_masses;
385  para.metallicity = stellarmetal;
386  para.weights = snii_weights;
387  /* This happens if no bins in range had dying stars this timestep*/
388  if(masslow >= masshigh)
389  return 0;
390  gsl_integration_qag(&ff, masslow, masshigh, 1e-7, 1e-3, GSL_WORKSPACE, GSL_INTEG_GAUSS61, gsl_work, &yield, &abserr);
391  return yield;
392 }
393 
394 /* Compute the total mass yield for this star in this timestep*/
395 static double mass_yield(double dtmyrstart, double dtmyrend, double stellarmetal, double hub, struct interps * interp, double imf_norm, gsl_integration_workspace * gsl_work, double masslow, double masshigh)
396 {
397  /* Number of AGB stars/SnII by integrating the IMF*/
398  double agbyield = compute_agb_yield(interp->agb_mass_interp, agb_total_mass, stellarmetal, masslow, masshigh, gsl_work);
399  double sniiyield = compute_snii_yield(interp->snii_mass_interp, snii_total_mass, stellarmetal, masslow, masshigh, gsl_work);
400  /* Fraction of the IMF which goes off this timestep. Normalised by the total IMF so we get a fraction of the SSP.*/
401  double massyield = (agbyield + sniiyield)/imf_norm;
402  /* Mass yield from Sn1a*/
403  double Nsn1a = sn1a_number(dtmyrstart, dtmyrend, hub);
404  massyield += Nsn1a * sn1a_total_metals;
405  //message(3, "masslow %g masshigh %g stellarmetal %g dystart %g dtend %g agb %g snii %g sn1a %g imf_norm %g\n",
406  // masslow, masshigh, stellarmetal, dtmyrstart, dtmyrend, agbyield, sniiyield, Nsn1a * sn1a_total_metals, imf_norm);
407  return massyield;
408 }
409 
410 /* Compute the total metal yield for this star in this timestep*/
411 static double metal_yield(double dtmyrstart, double dtmyrend, double stellarmetal, double hub, struct interps * interp, MyFloat * MetalYields, double imf_norm, gsl_integration_workspace * gsl_work, double masslow, double masshigh)
412 {
413  double MetalGenerated = 0;
414  /* Number of AGB stars/SnII by integrating the IMF*/
415  MetalGenerated += compute_agb_yield(interp->agb_metallicity_interp, agb_total_metals, stellarmetal, masslow, masshigh, gsl_work);
416  MetalGenerated += compute_snii_yield(interp->snii_metallicity_interp, snii_total_metals, stellarmetal, masslow, masshigh, gsl_work);
417  MetalGenerated /= imf_norm;
418 
419  int i;
420  for(i = 0; i < NMETALS; i++)
421  {
422  MetalYields[i] = 0;
423  MetalYields[i] += compute_agb_yield(interp->agb_metals_interp[i], agb_yield[i], stellarmetal, masslow, masshigh, gsl_work);
424  MetalYields[i] += compute_snii_yield(interp->snii_metals_interp[i], snii_yield[i], stellarmetal, masslow, masshigh, gsl_work);
425  MetalYields[i] /= imf_norm;
426  }
427  double Nsn1a = sn1a_number(dtmyrstart, dtmyrend, hub);
428  for(i = 0; i < NMETALS; i++)
429  MetalYields[i] += Nsn1a * sn1a_yields[i];
430  MetalGenerated += Nsn1a * sn1a_total_metals;
431 
432  return MetalGenerated;
433 }
434 
435 /* Initialise the private structure, finding stellar mass return and ages*/
436 int64_t
437 metal_return_init(const ActiveParticles * act, Cosmology * CP, struct MetalReturnPriv * priv, const double atime)
438 {
439  int nthread = omp_get_max_threads();
440  priv->gsl_work = ta_malloc("gsl_work", gsl_integration_workspace *, nthread);
441  int i;
442  /* Allocate a workspace for each thread*/
443  for(i=0; i < nthread; i++)
444  priv->gsl_work[i] = gsl_integration_workspace_alloc(GSL_WORKSPACE);
445  priv->hub = CP->HubbleParam;
446 
447  /* Initialize*/
449  priv->StellarAges = (MyFloat *) mymalloc("StellarAges", SlotsManager->info[4].size * sizeof(MyFloat));
450  priv->MassReturn = (MyFloat *) mymalloc("MassReturn", SlotsManager->info[4].size * sizeof(MyFloat));
451  priv->LowDyingMass = (MyFloat *) mymalloc("LowDyingMass", SlotsManager->info[4].size * sizeof(MyFloat));
452  priv->HighDyingMass = (MyFloat *) mymalloc("HighDyingMass", SlotsManager->info[4].size * sizeof(MyFloat));
453  priv->StarVolumeSPH = (MyFloat *) mymalloc("StarVolumeSPH", SlotsManager->info[4].size * sizeof(MyFloat));
454 
455  priv->imf_norm = compute_imf_norm(priv->gsl_work[0]);
456  /* Maximum possible mass return for below*/
457  double maxmassfrac = mass_yield(0, 1/(CP->HubbleParam*HUBBLE * SEC_PER_MEGAYEAR), snii_metallicities[SNII_NMET-1], CP->HubbleParam, &priv->interp, priv->imf_norm, priv->gsl_work[0],agb_masses[0], MAXMASS);
458 
459  int64_t haswork = 0;
460  /* First find the mass return as a fraction of the total mass and the age of the star.
461  * This is done first so we can skip density computation for not active stars*/
462  #pragma omp parallel for reduction(+: haswork)
463  for(i=0; i < act->NumActiveParticle;i++)
464  {
465  int p_i = act->ActiveParticle ? act->ActiveParticle[i] : i;
466  if(P[p_i].Type != 4)
467  continue;
468  int tid = omp_get_thread_num();
469  const int slot = P[p_i].PI;
470  priv->StellarAges[slot] = atime_to_myr(CP, STARP(p_i).FormationTime, atime, priv->gsl_work[tid]);
471  /* Note this takes care of units*/
472  double initialmass = P[p_i].Mass + STARP(p_i).TotalMassReturned;
473  find_mass_bin_limits(&priv->LowDyingMass[slot], &priv->HighDyingMass[slot], STARP(p_i).LastEnrichmentMyr, priv->StellarAges[P[p_i].PI], STARP(p_i).Metallicity, priv->interp.lifetime_interp);
474 
475  priv->MassReturn[slot] = initialmass * mass_yield(STARP(p_i).LastEnrichmentMyr, priv->StellarAges[P[p_i].PI], STARP(p_i).Metallicity, CP->HubbleParam, &priv->interp, priv->imf_norm, priv->gsl_work[tid],priv->LowDyingMass[slot], priv->HighDyingMass[slot]);
476  //message(3, "Particle %d PI %d massgen %g mass %g initmass %g\n", p_i, P[p_i].PI, priv->MassReturn[P[p_i].PI], P[p_i].Mass, initialmass);
477  /* Guard against making a zero mass particle and warn since this should not happen.*/
478  if(STARP(p_i).TotalMassReturned + priv->MassReturn[slot] > initialmass * maxmassfrac) {
479  if(priv->MassReturn[slot] / STARP(p_i).TotalMassReturned > 0.01)
480  message(1, "Large mass return id %ld %g from %d mass %g initial %g (maxfrac %g) age %g lastenrich %g metal %g dymass %g %g\n",
481  P[p_i].ID, priv->MassReturn[slot], p_i, STARP(p_i).TotalMassReturned, initialmass, maxmassfrac, priv->StellarAges[P[p_i].PI], STARP(p_i).LastEnrichmentMyr, STARP(p_i).Metallicity, priv->LowDyingMass[slot], priv->HighDyingMass[slot]);
482  priv->MassReturn[slot] = initialmass * maxmassfrac - STARP(p_i).TotalMassReturned;
483  if(priv->MassReturn[slot] < 0) {
484  priv->MassReturn[slot] = 0;
485  }
486  /* Ensure that we skip this step*/
487  if(!metals_haswork(p_i, priv->MassReturn))
488  STARP(p_i).LastEnrichmentMyr = priv->StellarAges[P[p_i].PI];
489 
490  }
491  /* Keep count of how much work we need to do*/
492  if(metals_haswork(p_i, priv->MassReturn))
493  haswork++;
494  }
495  return haswork;
496 }
497 
498 /* Free memory allocated by metal_return_init */
499 void
501 {
502  myfree(priv->StarVolumeSPH);
503  myfree(priv->HighDyingMass);
504  myfree(priv->LowDyingMass);
505  myfree(priv->MassReturn);
506  myfree(priv->StellarAges);
507 
508  int i;
509  for(i=0; i < omp_get_max_threads(); i++)
510  gsl_integration_workspace_free(priv->gsl_work[i]);
511 
512  ta_free(priv->gsl_work);
513 }
514 
516 void
517 metal_return(const ActiveParticles * act, DomainDecomp * const ddecomp, Cosmology * CP, const double atime, const double AvgGasMass)
518 {
519  /* Do nothing if no stars yet*/
520  int64_t totstar;
521  MPI_Allreduce(&SlotsManager->info[4].size, &totstar, 1, MPI_INT64, MPI_SUM, MPI_COMM_WORLD);
522  if(totstar == 0)
523  return;
524 
525  struct MetalReturnPriv priv[1];
526 
527  int64_t nwork = metal_return_init(act, CP, priv, atime);
528 
529  /* Maximum mass of a gas particle after enrichment: cap it at a few times the initial mass.
530  * FIXME: Ideally we should here fork a new particle with a smaller gas mass. We should
531  * figure out then how set the gas entropy. A possibly better idea is to add
532  * a generic routine to split gas particles into the density code.*/
533  priv->MaxGasMass = 4* AvgGasMass;
534 
535  int64_t totwork;
536  MPI_Allreduce(&nwork, &totwork, 1, MPI_INT64, MPI_SUM, MPI_COMM_WORLD);
537 
538  walltime_measure("/SPH/Metals/Init");
539 
540  if(totwork == 0) {
542  return;
543  }
544 
545  ForceTree gasTree = {0};
546  /* Just gas, no moments*/
547  force_tree_rebuild_mask(&gasTree, ddecomp, GASMASK, 0, NULL);
548 
549  /* Compute total number of weights around each star for actively returning stars*/
550  stellar_density(act, priv->StarVolumeSPH, priv->MassReturn, &gasTree);
551 
552  /* Do the metal return*/
553  TreeWalk tw[1] = {{0}};
554 
555  tw->ev_label = "METALS";
561  tw->reduce = NULL;
565  tw->repeatdisallowed = 1;
566  tw->tree = &gasTree;
567  tw->priv = priv;
568 
571  free_spinlocks(priv->spin);
572 
573  force_tree_free(&gasTree);
575 
576  /* collect some timing information */
577  walltime_measure("/SPH/Metals/Yield");
578 }
579 
580 /* This function is unusually important:
581  * it computes the total amount of metals to be returned in this timestep.*/
582 static void
584 {
585  input->Metallicity = STARP(place).Metallicity;
586  input->Mass = P[place].Mass;
587  input->Hsml = P[place].Hsml;
588  int pi = P[place].PI;
589  input->StarVolumeSPH = METALS_GET_PRIV(tw)->StarVolumeSPH[pi];
590  double InitialMass = P[place].Mass + STARP(place).TotalMassReturned;
591  double dtmyrend = METALS_GET_PRIV(tw)->StellarAges[pi];
592  double dtmyrstart = STARP(place).LastEnrichmentMyr;
593  int tid = omp_get_thread_num();
594  /* This is the total mass returned from this stellar population this timestep. Note this is already in the desired units.*/
595  input->MassGenerated = METALS_GET_PRIV(tw)->MassReturn[pi];
596  /* This returns the total amount of metal produced this timestep, and also fills out MetalSpeciesGenerated, which is an
597  * element by element table of the metal produced by dying stars this timestep.*/
598  double total_z_yield = metal_yield(dtmyrstart, dtmyrend, input->Metallicity, METALS_GET_PRIV(tw)->hub, &METALS_GET_PRIV(tw)->interp, input->MetalSpeciesGenerated, METALS_GET_PRIV(tw)->imf_norm, METALS_GET_PRIV(tw)->gsl_work[tid], METALS_GET_PRIV(tw)->LowDyingMass[pi], METALS_GET_PRIV(tw)->HighDyingMass[pi]);
599  /* The total metal returned is the metal created this timestep, plus the metal which was already in the mass returned by the dying stars.*/
600  input->MetalGenerated = InitialMass * total_z_yield + STARP(place).Metallicity * input->MassGenerated;
601  //message(3, "Particle %d PI %d z %g massgen %g metallicity %g\n", pi, P[pi].PI, total_z_yield, METALS_GET_PRIV(tw)->MassReturn[pi], STARP(place).Metallicity);
602  /* It should be positive! If it is not, this is some integration error
603  * in the yield table as we cannot destroy metal which is not present.*/
604  if(input->MetalGenerated < 0)
605  input->MetalGenerated = 0;
606  /* Similarly for all the other metal species*/
607  int i;
608  for(i = 0; i < NMETALS; i++) {
609  input->MetalSpeciesGenerated[i] = InitialMass * input->MetalSpeciesGenerated[i] + STARP(place).Metals[i] * input->MassGenerated;
610  if(input->MetalSpeciesGenerated[i] < 0)
611  input->MetalSpeciesGenerated[i] = 0;
612  }
613 }
614 
615 /* Update the mass and enrichment variables for the star.
616  * Note that the stellar metallicity is not updated, as the
617  * metal-forming stars are now dead and their metals in the gas.*/
618 static void
620 {
621  /* Conserve mass returned*/
622  P[place].Mass -= METALS_GET_PRIV(tw)->MassReturn[P[place].PI];
623  STARP(place).TotalMassReturned += METALS_GET_PRIV(tw)->MassReturn[P[place].PI];
624  /* Update the last enrichment time*/
625  STARP(place).LastEnrichmentMyr = METALS_GET_PRIV(tw)->StellarAges[P[place].PI];
626 }
627 
632 static void
636  TreeWalkNgbIterMetals * iter,
637  LocalTreeWalk * lv
638  )
639 {
640  if(iter->base.other == -1) {
641  /* Only return metals to gas*/
642  iter->base.mask = 1;
643  iter->base.Hsml = I->Hsml;
645  /* Initialise the mass lost by this star in this timestep*/
646  O->MassReturn = 0;
648  return;
649  }
650 
651  const int other = iter->base.other;
652  const double r2 = iter->base.r2;
653  const double r = iter->base.r;
654 
655  if(r2 > 0 && r2 < iter->kernel.HH)
656  {
657  double wk = 1;
658  const double u = r * iter->kernel.Hinv;
659 
661  wk = density_kernel_wk(&iter->kernel, u);
662  double ThisMetals[NMETALS];
663  if(I->StarVolumeSPH ==0)
664  endrun(3, "StarVolumeSPH %g hsml %g\n", I->StarVolumeSPH, I->Hsml);
665  double newmass;
666  int pi = P[other].PI;
667  lock_spinlock(pi, METALS_GET_PRIV(lv->tw)->spin);
668  /* Volume of particle weighted by the SPH kernel*/
669  double volume = P[other].Mass / SPHP(other).Density;
670  double returnfraction = wk * volume / I->StarVolumeSPH;
671  int i;
672  for(i = 0; i < NMETALS; i++)
673  ThisMetals[i] = returnfraction * I->MetalSpeciesGenerated[i];
674  /* Keep track of how much was returned for conservation purposes*/
675  double thismass = returnfraction * I->MassGenerated;
676  O->MassReturn += thismass;
677  /* Add metals weighted by SPH kernel*/
678  double thismetal = returnfraction * I->MetalGenerated;
679  /* Add the metals to the particle.*/
680  for(i = 0; i < NMETALS; i++)
681  SPHP(other).Metals[i] = (SPHP(other).Metals[i] * P[other].Mass + ThisMetals[i])/(P[other].Mass + thismass);
682  /* Update total metallicity*/
683  SPHP(other).Metallicity = (SPHP(other).Metallicity * P[other].Mass + thismetal)/(P[other].Mass + thismass);
684  /* Update mass*/
685  double massfrac = (P[other].Mass + thismass) / P[other].Mass;
686  /* Ensure that the gas particles don't become overweight.
687  * If there are few gas particles around, the star clusters
688  * will hold onto their metals.*/
689  if(P[other].Mass + thismass < METALS_GET_PRIV(lv->tw)->MaxGasMass) {
690  P[other].Mass *= massfrac;
691  /* Density also needs a correction so the volume fraction is unchanged.
692  * This ensures that volume = Mass/Density is unchanged for the next particle
693  * and thus the weighting still sums to unity.*/
694  SPHP(other).Density *= massfrac;
695  }
696  newmass = P[other].Mass;
697  unlock_spinlock(pi, METALS_GET_PRIV(lv->tw)->spin);
698  if(newmass <= 0)
699  endrun(3, "New mass %g new metal %g in particle %d id %ld from star mass %g metallicity %g\n",
700  newmass, SPHP(other).Metallicity, other, P[other].ID, I->Mass, I->Metallicity);
701  }
702 }
703 
704 /* Find stars returning enough metals to the gas.
705  * This is a wrapper function to allow for
706  * different private structs in different treewalks*/
707 int
709 {
710  if(P[i].Type != 4)
711  return 0;
712  int pi = P[i].PI;
713  /* Don't do enrichment from all stars, just those with significant enrichment*/
714  if(MassReturn[pi] < 1e-3 * (P[i].Mass + STARP(i).TotalMassReturned))
715  return 0;
716  return 1;
717 }
718 
719 static int
721 {
722  return metals_haswork(i, METALS_GET_PRIV(tw)->MassReturn);
723 }
724 
725 /* Number of densities to evaluate simultaneously*/
726 #define NHSML 10
727 
728 typedef struct {
731  double kernel_volume[NHSML];
733 
734 typedef struct
735 {
737  MyFloat Hsml[NHSML];
739 
740 typedef struct {
742  MyFloat VolumeSPH[NHSML];
744  int maxcmpte;
747 
749  /* Current number of neighbours*/
751  /* Lower and upper bounds on smoothing length*/
754  /* For haswork*/
757  double DesNumNgb;
758  /* Maximum index where NumNgb is valid. */
759  int * maxcmpte;
760 };
761 
762 #define STELLAR_DENSITY_GET_PRIV(tw) ((struct StellarDensityPriv*) ((tw)->priv))
763 
764 static int
766 {
767  return metals_haswork(i, STELLAR_DENSITY_GET_PRIV(tw)->MassReturn);
768 }
769 
770 /* Get Hsml for one of the evaluations*/
771 static inline double
772 effhsml(int place, int i, TreeWalk * tw)
773 {
774  int pi = P[place].PI;
775  double left = STELLAR_DENSITY_GET_PRIV(tw)->Left[pi];
776  double right = STELLAR_DENSITY_GET_PRIV(tw)->Right[pi];
777  /* If somehow Hsml has become zero through underflow, use something non-zero
778  * to make sure we converge. */
779  if(left == 0 && right > 0.99*tw->tree->BoxSize && P[place].Hsml == 0) {
780  int fat = force_get_father(place, tw->tree);
781  P[place].Hsml = tw->tree->Nodes[fat].len;
782  if(P[place].Hsml == 0)
783  P[place].Hsml = tw->tree->BoxSize / pow(PartManager->NumPart, 1./3)/4.;
784  }
785  /* Use slightly past the current Hsml as the right most boundary*/
786  if(right > 0.99*tw->tree->BoxSize)
787  right = P[place].Hsml * ((1.+NHSML)/NHSML);
788  /* Use 1/2 of current Hsml for left. The asymmetry is because it is free
789  * to compute extra densities for h < Hsml, but not for h > Hsml.*/
790  if(left == 0)
791  left = 0.1 * P[place].Hsml;
792  /* From left + 1/N to right - 1/N, evenly spaced in volume,
793  * since NumNgb ~ h^3.*/
794  double rvol = pow(right, 3);
795  double lvol = pow(left, 3);
796  return pow((1.*i+1)/(1.*NHSML+1) * (rvol - lvol) + lvol, 1./3);
797 }
798 
799 static void
801 {
802  int i;
803  for(i = 0; i < NHSML; i++)
804  I->Hsml[i] = effhsml(place, i, tw);
805 }
806 
807 static void
809 {
810  int pi = P[place].PI;
811  int i;
812  if(mode == 0 || STELLAR_DENSITY_GET_PRIV(tw)->maxcmpte[pi] > remote->maxcmpte)
813  STELLAR_DENSITY_GET_PRIV(tw)->maxcmpte[pi] = remote->maxcmpte;
814  for(i = 0; i < remote->maxcmpte; i++) {
815  TREEWALK_REDUCE(STELLAR_DENSITY_GET_PRIV(tw)->NumNgb[pi][i], remote->Ngb[i]);
816  TREEWALK_REDUCE(STELLAR_DENSITY_GET_PRIV(tw)->VolumeSPH[pi][i], remote->VolumeSPH[i]);
817  }
818 }
819 
821 {
822  MyFloat * Left = STELLAR_DENSITY_GET_PRIV(tw)->Left;
823  MyFloat * Right = STELLAR_DENSITY_GET_PRIV(tw)->Right;
824 
825  int pi = P[i].PI;
826  int tid = omp_get_thread_num();
827  double desnumngb = STELLAR_DENSITY_GET_PRIV(tw)->DesNumNgb;
828 
829  const int maxcmpt = STELLAR_DENSITY_GET_PRIV(tw)->maxcmpte[pi];
830  int j;
831  double evalhsml[NHSML];
832  for(j = 0; j < maxcmpt; j++)
833  evalhsml[j] = effhsml(i, j, tw);
834 
835  int close = 0;
836  P[i].Hsml = ngb_narrow_down(&Right[pi],&Left[pi],evalhsml,STELLAR_DENSITY_GET_PRIV(tw)->NumNgb[pi],maxcmpt,desnumngb,&close,tw->tree->BoxSize);
837  double numngb = STELLAR_DENSITY_GET_PRIV(tw)->NumNgb[pi][close];
838 
839  /* Save VolumeSPH*/
840  STELLAR_DENSITY_GET_PRIV(tw)->VolumeSPH[pi][0] = STELLAR_DENSITY_GET_PRIV(tw)->VolumeSPH[pi][close];
841 
842  /* now check whether we had enough neighbours */
843  if(numngb < (desnumngb - MetalParams.MaxNgbDeviation) ||
844  (numngb > (desnumngb + MetalParams.MaxNgbDeviation)))
845  {
846  /* This condition is here to prevent the density code looping forever if it encounters
847  * multiple particles at the same position. If this happens you likely have worse
848  * problems anyway, so warn also. */
849  if((Right[pi] - Left[pi]) < 1.0e-4 * Left[pi])
850  {
851  /* If this happens probably the exchange is screwed up and all your particles have moved to (0,0,0)*/
852  message(1, "Very tight Hsml bounds for i=%d ID=%lu type %d Hsml=%g Left=%g Right=%g Ngbs=%g des = %g Right-Left=%g pos=(%g|%g|%g)\n",
853  i, P[i].ID, P[i].Type, effhsml, Left[pi], Right[pi], numngb, desnumngb, Right[pi] - Left[pi], P[i].Pos[0], P[i].Pos[1], P[i].Pos[2]);
854  return;
855  }
856  /* More work needed: add this particle to the redo queue*/
857  tw->NPRedo[tid][tw->NPLeft[tid]] = i;
858  tw->NPLeft[tid] ++;
859  if(tw->Niteration >= 10)
860  message(1, "i=%d ID=%lu Hsml=%g lastdhsml=%g Left=%g Right=%g Ngbs=%g Right-Left=%g pos=(%g|%g|%g) fac = %g\n",
861  i, P[i].ID, P[i].Hsml, evalhsml[close], Left[pi], Right[pi], numngb, Right[pi] - Left[pi], P[i].Pos[0], P[i].Pos[1], P[i].Pos[2]);
862 
863  }
864  if(tw->maxnumngb[tid] < numngb)
865  tw->maxnumngb[tid] = numngb;
866  if(tw->minnumngb[tid] > numngb)
867  tw->minnumngb[tid] = numngb;
868 
869 }
870 
871 static void
876  LocalTreeWalk * lv)
877 {
878  if(iter->base.other == -1) {
879  int i;
880  for(i = 0; i < NHSML; i++) {
882  iter->kernel_volume[i] = density_kernel_volume(&iter->kernel[i]);
883  }
884  iter->base.Hsml = I->Hsml[NHSML-1];
885  iter->base.mask = 1; /* gas only */
887  O->maxcmpte = NHSML;
888  return;
889  }
890  const int other = iter->base.other;
891  const double r = iter->base.r;
892  const double r2 = iter->base.r2;
893 
894  int i;
895  for(i = 0; i < O->maxcmpte; i++) {
896  if(r2 < iter->kernel[i].HH)
897  {
898  const double u = r * iter->kernel[i].Hinv;
899  double wk = density_kernel_wk(&iter->kernel[i], u);
900  O->Ngb[i] += wk * iter->kernel_volume[i];
901  /* For stars we need the total weighting, sum(w_k m_k / rho_k).*/
902  double thisvol = P[other].Mass / SPHP(other).Density;
904  thisvol *= wk;
905  O->VolumeSPH[i] += thisvol;
906  }
907  }
908  double desnumngb = STELLAR_DENSITY_GET_PRIV(lv->tw)->DesNumNgb;
909  /* If there is an entry which is above desired DesNumNgb,
910  * we don't need to search past it. After this point
911  * all entries in the Ngb table above O->Ngb are invalid.*/
912  for(i = 0; i < NHSML; i++) {
913  if(O->Ngb[i] > desnumngb) {
914  O->maxcmpte = i+1;
915  iter->base.Hsml = I->Hsml[i];
916  break;
917  }
918  }
919 
920 }
921 
922 void
923 stellar_density(const ActiveParticles * act, MyFloat * StarVolumeSPH, MyFloat * MassReturn, const ForceTree * const tree)
924 {
925  TreeWalk tw[1] = {{0}};
926  struct StellarDensityPriv priv[1];
927 
928  tw->ev_label = "STELLAR_DENSITY";
930  tw->NoNgblist = 1;
939  tw->priv = priv;
940  tw->tree = tree;
941 
942  int i;
943 
944  priv->MassReturn = MassReturn;
945 
946  priv->Left = (MyFloat *) mymalloc("DENS_PRIV->Left", SlotsManager->info[4].size * sizeof(MyFloat));
947  priv->Right = (MyFloat *) mymalloc("DENS_PRIV->Right", SlotsManager->info[4].size * sizeof(MyFloat));
948  priv->NumNgb = (MyFloat (*) [NHSML]) mymalloc("DENS_PRIV->NumNgb", SlotsManager->info[4].size * sizeof(priv->NumNgb[0]));
949  priv->VolumeSPH = (MyFloat (*) [NHSML]) mymalloc("DENS_PRIV->VolumeSPH", SlotsManager->info[4].size * sizeof(priv->VolumeSPH[0]));
950  priv->maxcmpte = (int *) mymalloc("maxcmpte", SlotsManager->info[4].size * sizeof(int));
951 
953 
954  #pragma omp parallel for
955  for(i = 0; i < act->NumActiveParticle; i++) {
956  int a = act->ActiveParticle ? act->ActiveParticle[i] : i;
957  /* Skip the garbage particles */
958  if(P[a].IsGarbage)
959  continue;
960  if(!stellar_density_haswork(a, tw))
961  continue;
962  int pi = P[a].PI;
963  priv->Left[pi] = 0;
964  priv->Right[pi] = tree->BoxSize;
965  }
966 
967  /* allocate buffers to arrange communication */
968 
970  #pragma omp parallel for
971  for(i = 0; i < act->NumActiveParticle; i++) {
972  int a = act->ActiveParticle ? act->ActiveParticle[i] : i;
973  /* Skip the garbage particles */
974  if(P[a].IsGarbage)
975  continue;
976  if(!stellar_density_haswork(a, tw))
977  continue;
978  /* Copy the Star Volume SPH*/
979  StarVolumeSPH[P[a].PI] = priv->VolumeSPH[P[a].PI][0];
980  if(priv->VolumeSPH[P[a].PI][0] == 0)
981  endrun(3, "i = %d pi = %d StarVolumeSPH %g hsml %g\n", a, P[a].PI, priv->VolumeSPH[P[a].PI][0], P[a].Hsml);
982  }
983 
984  myfree(priv->maxcmpte);
985  myfree(priv->VolumeSPH);
986  myfree(priv->NumNgb);
987  myfree(priv->Right);
988  myfree(priv->Left);
989 
990  double timeall = walltime_measure(WALLTIME_IGNORE);
991 
992  double timecomp = tw->timecomp3 + tw->timecomp1 + tw->timecomp2;
993  double timewait = tw->timewait1 + tw->timewait2;
994  double timecomm = tw->timecommsumm1 + tw->timecommsumm2;
995  walltime_add("/SPH/Metals/Density/Compute", timecomp);
996  walltime_add("/SPH/Metals/Density/Wait", timewait);
997  walltime_add("/SPH/Metals/Density/Comm", timecomm);
998  walltime_add("/SPH/Metals/Density/Misc", timeall - (timecomp + timewait + timecomm));
999 
1000  return;
1001 }
#define MAXITER
Definition: cooling.c:46
Interp interp
double hubble_function(const Cosmology *CP, double a)
Definition: cosmology.c:58
double GetNumNgb(enum DensityKernelType KernelType)
Definition: density.c:55
enum DensityKernelType GetDensityKernelType(void)
Definition: density.c:63
double(* wk)(DensityKernel *kernel, double q)
Definition: densitykernel.c:94
double density_kernel_volume(DensityKernel *kernel)
void density_kernel_init(DensityKernel *kernel, double H, enum DensityKernelType type)
double density_kernel_wk(DensityKernel *kernel, double u)
void message(int where, const char *fmt,...)
Definition: endrun.c:175
void endrun(int where, const char *fmt,...)
Definition: endrun.c:147
void force_tree_rebuild_mask(ForceTree *tree, DomainDecomp *ddecomp, int mask, const int HybridNuGrav, const char *EmergencyOutputDir)
Definition: forcetree.c:161
void force_tree_free(ForceTree *tree)
Definition: forcetree.c:1409
int force_get_father(int no, const ForceTree *tree)
Definition: forcetree.c:905
#define GASMASK
Definition: forcetree.h:21
static double kernel(double loga, void *params)
Definition: lightcone.c:59
static double mass_yield(double dtmyrstart, double dtmyrend, double stellarmetal, double hub, struct interps *interp, double imf_norm, gsl_integration_workspace *gsl_work, double masslow, double masshigh)
Definition: metal_return.c:395
void set_metal_params(double Sn1aN0)
Definition: metal_return.c:55
void find_mass_bin_limits(double *masslow, double *masshigh, const double dtstart, const double dtend, double stellarmetal, gsl_interp2d *lifetime_tables)
Definition: metal_return.c:230
void stellar_density_check_neighbours(int i, TreeWalk *tw)
Definition: metal_return.c:820
static void stellar_density_reduce(int place, TreeWalkResultStellarDensity *remote, enum TreeWalkReduceMode mode, TreeWalk *tw)
Definition: metal_return.c:808
double atime_integ(double atime, void *params)
Definition: metal_return.c:154
#define STELLAR_DENSITY_GET_PRIV(tw)
Definition: metal_return.c:762
int64_t metal_return_init(const ActiveParticles *act, Cosmology *CP, struct MetalReturnPriv *priv, const double atime)
Definition: metal_return.c:437
#define NHSML
Definition: metal_return.c:726
void setup_metal_table_interp(struct interps *interp)
Definition: metal_return.c:76
double do_rootfinding(struct massbin_find_params *p, double mass_low, double mass_high)
Definition: metal_return.c:194
static int stellar_density_haswork(int i, TreeWalk *tw)
Definition: metal_return.c:765
static double effhsml(int place, int i, TreeWalk *tw)
Definition: metal_return.c:772
double massendlife(double mass, void *params)
Definition: metal_return.c:185
static void metal_return_postprocess(int place, TreeWalk *tw)
Definition: metal_return.c:619
static void stellar_density_ngbiter(TreeWalkQueryStellarDensity *I, TreeWalkResultStellarDensity *O, TreeWalkNgbIterStellarDensity *iter, LocalTreeWalk *lv)
Definition: metal_return.c:872
static struct metal_return_params MetalParams
void metal_return_priv_free(struct MetalReturnPriv *priv)
Definition: metal_return.c:500
double sn1a_number(double dtmyrstart, double dtmyrend, double hub)
Definition: metal_return.c:324
void metal_return(const ActiveParticles *act, DomainDecomp *const ddecomp, Cosmology *CP, const double atime, const double AvgGasMass)
Definition: metal_return.c:517
void set_metal_return_params(ParameterSet *ps)
Definition: metal_return.c:62
static void stellar_density_copy(int place, TreeWalkQueryStellarDensity *I, TreeWalk *tw)
Definition: metal_return.c:800
double compute_snii_yield(gsl_interp2d *snii_interp, const double *snii_weights, double stellarmetal, double masslow, double masshigh, gsl_integration_workspace *gsl_work)
Definition: metal_return.c:368
#define METALS_GET_PRIV(tw)
Definition: metal_return.c:99
static int metal_return_haswork(int n, TreeWalk *tw)
Definition: metal_return.c:720
int metals_haswork(int i, MyFloat *MassReturn)
Definition: metal_return.c:708
double chabrier_imf_integ(double mass, void *params)
Definition: metal_return.c:289
void stellar_density(const ActiveParticles *act, MyFloat *StarVolumeSPH, MyFloat *MassReturn, const ForceTree *const tree)
Definition: metal_return.c:923
static void metal_return_copy(int place, TreeWalkQueryMetals *input, TreeWalk *tw)
Definition: metal_return.c:583
double compute_agb_yield(gsl_interp2d *agb_interp, const double *agb_weights, double stellarmetal, double masslow, double masshigh, gsl_integration_workspace *gsl_work)
Definition: metal_return.c:342
double compute_imf_norm(gsl_integration_workspace *gsl_work)
Definition: metal_return.c:314
static double chabrier_imf(double mass)
Definition: metal_return.c:144
static double atime_to_myr(Cosmology *CP, double atime1, double atime2, gsl_integration_workspace *gsl_work)
Definition: metal_return.c:161
static void metal_return_ngbiter(TreeWalkQueryMetals *I, TreeWalkResultMetals *O, TreeWalkNgbIterMetals *iter, LocalTreeWalk *lv)
Definition: metal_return.c:633
static double metal_yield(double dtmyrstart, double dtmyrend, double stellarmetal, double hub, struct interps *interp, MyFloat *MetalYields, double imf_norm, gsl_integration_workspace *gsl_work, double masslow, double masshigh)
Definition: metal_return.c:411
double chabrier_mass(double mass, void *params)
Definition: metal_return.c:308
#define LIFE_NMET
Definition: metal_tables.h:16
static const double snii_yield[NSPECIES][SNII_NMET *SNII_NMASS]
Definition: metal_tables.h:334
#define AGB_NMET
Definition: metal_tables.h:68
static const double snii_metallicities[SNII_NMET]
Definition: metal_tables.h:311
#define LIFE_NMASS
Definition: metal_tables.h:17
#define SNII_NMASS
Definition: metal_tables.h:309
static const double agb_masses[AGB_NMASS]
Definition: metal_tables.h:70
#define SNAGBSWITCH
Definition: metal_tables.h:13
#define GSL_WORKSPACE
Definition: metal_tables.h:426
#define AGB_NMASS
Definition: metal_tables.h:69
static const double snii_total_mass[SNII_NMET *SNII_NMASS]
Definition: metal_tables.h:312
static const double agb_total_mass[AGB_NMET *AGB_NMASS]
Definition: metal_tables.h:72
#define MINMASS
Definition: metal_tables.h:11
static const double sn1a_yields[NSPECIES]
Definition: metal_tables.h:61
static const double agb_total_metals[AGB_NMET *AGB_NMASS]
Definition: metal_tables.h:94
static const double lifetime_metallicity[LIFE_NMET]
Definition: metal_tables.h:18
#define SNII_NMET
Definition: metal_tables.h:308
static const double sn1a_total_metals
Definition: metal_tables.h:60
static const double lifetime[LIFE_NMASS *LIFE_NMET]
Definition: metal_tables.h:24
#define MAXMASS
Definition: metal_tables.h:9
static const double agb_yield[NSPECIES][AGB_NMET *AGB_NMASS]
Definition: metal_tables.h:116
static const double snii_masses[SNII_NMASS]
Definition: metal_tables.h:310
static const double lifetime_masses[LIFE_NMASS]
Definition: metal_tables.h:20
static const double snii_total_metals[SNII_NMET *SNII_NMASS]
Definition: metal_tables.h:323
static const double agb_metallicities[AGB_NMET]
Definition: metal_tables.h:71
#define mymalloc(name, size)
Definition: mymalloc.h:15
#define ta_malloc(name, type, nele)
Definition: mymalloc.h:25
#define ta_free(p)
Definition: mymalloc.h:28
#define myfree(x)
Definition: mymalloc.h:19
double param_get_double(ParameterSet *ps, const char *name)
Definition: paramset.c:336
int param_get_int(ParameterSet *ps, const char *name)
Definition: paramset.c:368
struct part_manager_type PartManager[1]
Definition: partmanager.c:11
#define P
Definition: partmanager.h:88
#define HUBBLE
Definition: physconst.h:25
#define SEC_PER_MEGAYEAR
Definition: physconst.h:31
static Cosmology * CP
Definition: power.c:27
struct slots_manager_type SlotsManager[1]
Definition: slotsmanager.c:7
#define SPHP(i)
Definition: slotsmanager.h:124
#define STARP(i)
Definition: slotsmanager.h:126
#define NMETALS
Definition: slotsmanager.h:60
void free_spinlocks(struct SpinLocks *spin)
Definition: spinlocks.c:70
void lock_spinlock(int i, struct SpinLocks *spin)
Definition: spinlocks.c:23
struct SpinLocks * init_spinlocks(int NumLock)
Definition: spinlocks.c:49
void unlock_spinlock(int i, struct SpinLocks *spin)
Definition: spinlocks.c:30
int * ActiveParticle
Definition: timestep.h:13
int64_t NumActiveParticle
Definition: timestep.h:12
double HubbleParam
Definition: cosmology.h:20
double UnitTime_in_s
Definition: cosmology.h:22
double BoxSize
Definition: forcetree.h:106
struct NODE * Nodes
Definition: forcetree.h:97
TreeWalk * tw
Definition: treewalk.h:47
gsl_integration_workspace ** gsl_work
Definition: metal_return.h:27
MyFloat * StarVolumeSPH
Definition: metal_return.h:37
MyFloat * HighDyingMass
Definition: metal_return.h:31
MyFloat * StellarAges
Definition: metal_return.h:28
struct interps interp
Definition: metal_return.h:38
MyFloat * MassReturn
Definition: metal_return.h:29
struct SpinLocks * spin
Definition: metal_return.h:39
MyFloat * LowDyingMass
Definition: metal_return.h:30
MyFloat len
Definition: forcetree.h:42
MyFloat(* NumNgb)[NHSML]
Definition: metal_return.c:750
MyFloat * MassReturn
Definition: metal_return.c:755
MyFloat(* VolumeSPH)[NHSML]
Definition: metal_return.c:753
enum NgbTreeFindSymmetric symmetric
Definition: treewalk.h:37
TreeWalkNgbIterBase base
Definition: metal_return.c:121
DensityKernel kernel
Definition: metal_return.c:122
DensityKernel kernel[NHSML]
Definition: metal_return.c:730
TreeWalkNgbIterBase base
Definition: metal_return.c:729
TreeWalkQueryBase base
Definition: metal_return.c:102
MyFloat MetalSpeciesGenerated[NMETALS]
Definition: metal_return.c:108
TreeWalkQueryBase base
Definition: metal_return.c:736
TreeWalkResultBase base
Definition: metal_return.c:114
TreeWalkResultBase base
Definition: metal_return.c:741
int64_t Niteration
Definition: treewalk.h:142
double * minnumngb
Definition: treewalk.h:172
void * priv
Definition: treewalk.h:85
TreeWalkHasWorkFunction haswork
Definition: treewalk.h:100
size_t ngbiter_type_elsize
Definition: treewalk.h:97
double timecomp2
Definition: treewalk.h:124
double * maxnumngb
Definition: treewalk.h:171
double timecomp3
Definition: treewalk.h:125
int ** NPRedo
Definition: treewalk.h:168
double timecommsumm1
Definition: treewalk.h:126
const ForceTree * tree
Definition: treewalk.h:88
TreeWalkProcessFunction postprocess
Definition: treewalk.h:104
TreeWalkNgbIterFunction ngbiter
Definition: treewalk.h:103
TreeWalkReduceResultFunction reduce
Definition: treewalk.h:102
const char * ev_label
Definition: treewalk.h:91
double timewait2
Definition: treewalk.h:122
int NoNgblist
Definition: treewalk.h:156
TreeWalkVisitFunction visit
Definition: treewalk.h:99
int repeatdisallowed
Definition: treewalk.h:117
size_t result_type_elsize
Definition: treewalk.h:96
TreeWalkFillQueryFunction fill
Definition: treewalk.h:101
size_t * NPLeft
Definition: treewalk.h:167
double timecomp1
Definition: treewalk.h:123
double timewait1
Definition: treewalk.h:121
double timecommsumm2
Definition: treewalk.h:127
size_t query_type_elsize
Definition: treewalk.h:95
const double * metallicities
Definition: metal_return.c:283
const double * weights
Definition: metal_return.c:284
gsl_interp2d * interp
Definition: metal_return.c:281
const double * masses
Definition: metal_return.c:282
gsl_interp2d * lifetime_interp
Definition: metal_return.h:13
gsl_interp_accel * massacc
Definition: metal_return.c:179
gsl_interp_accel * metalacc
Definition: metal_return.c:178
gsl_interp2d * lifetime_tables
Definition: metal_return.c:177
int64_t size
Definition: slotsmanager.h:12
struct slot_info info[6]
Definition: slotsmanager.h:112
#define MPI_INT64
Definition: system.h:12
int ThisTask
Definition: test_exchange.c:23
int treewalk_visit_ngbiter(TreeWalkQueryBase *I, TreeWalkResultBase *O, LocalTreeWalk *lv)
Definition: treewalk.c:924
int treewalk_visit_nolist_ngbiter(TreeWalkQueryBase *I, TreeWalkResultBase *O, LocalTreeWalk *lv)
Definition: treewalk.c:1143
void treewalk_run(TreeWalk *tw, int *active_set, size_t size)
Definition: treewalk.c:619
double ngb_narrow_down(double *right, double *left, const double *radius, const double *numNgb, int maxcmpt, int desnumngb, int *closeidx, double BoxSize)
Definition: treewalk.c:1366
void treewalk_do_hsml_loop(TreeWalk *tw, int *queue, int64_t queuesize, int update_hsml)
Definition: treewalk.c:1254
int(* TreeWalkVisitFunction)(TreeWalkQueryBase *input, TreeWalkResultBase *output, LocalTreeWalk *lv)
Definition: treewalk.h:68
void(* TreeWalkReduceResultFunction)(const int j, TreeWalkResultBase *result, const enum TreeWalkReduceMode mode, TreeWalk *tw)
Definition: treewalk.h:76
TreeWalkReduceMode
Definition: treewalk.h:15
void(* TreeWalkProcessFunction)(const int i, TreeWalk *tw)
Definition: treewalk.h:73
void(* TreeWalkNgbIterFunction)(TreeWalkQueryBase *input, TreeWalkResultBase *output, TreeWalkNgbIterBase *iter, LocalTreeWalk *lv)
Definition: treewalk.h:70
void(* TreeWalkFillQueryFunction)(const int j, TreeWalkQueryBase *query, TreeWalk *tw)
Definition: treewalk.h:75
#define TREEWALK_REDUCE(A, B)
Definition: treewalk.h:189
@ NGB_TREEFIND_ASYMMETRIC
Definition: treewalk.h:12
LOW_PRECISION MyFloat
Definition: types.h:19
#define walltime_measure(name)
Definition: walltime.h:8
#define WALLTIME_IGNORE
Definition: walltime.h:6
#define walltime_add(name, dt)
Definition: walltime.h:9