From ff8b655cc43f23e1b82178283f63c253f9e5d4a3 Mon Sep 17 00:00:00 2001 From: Loic Hausammann <loic.hausammann@protonmail.ch> Date: Fri, 24 Jan 2020 13:06:59 +0000 Subject: [PATCH] Gear --- configure.ac | 23 +- examples/GEAR/AgoraDisk/agora_disk.yml | 67 +- examples/GEAR/AgoraDisk/cleanupSwift.py | 18 +- examples/GEAR/AgoraDisk/getIC.sh | 2 +- examples/GEAR/AgoraDisk/getSolution.sh | 12 +- examples/GEAR/AgoraDisk/plotSolution.py | 45 +- examples/GEAR/AgoraDisk/run.sh | 12 +- examples/GEAR/DwarfGalaxy/dwarf_galaxy.yml | 28 + examples/GEAR/DwarfGalaxy/run.sh | 2 +- examples/GEAR/ZoomIn/run.sh | 2 +- examples/GEAR/ZoomIn/zoom_in.yml | 27 + examples/GEAR/getChemistryTable.sh | 2 + .../SupernovaeFeedback/SN_feedback.yml | 44 - .../SupernovaeFeedback/getGlass.sh | 2 - .../SubgridTests/SupernovaeFeedback/makeIC.py | 117 -- .../SubgridTests/SupernovaeFeedback/run.sh | 19 - examples/parameter_example.yml | 34 +- src/Makefile.am | 25 +- src/chemistry/GEAR/chemistry.h | 205 +++- src/chemistry/GEAR/chemistry_iact.h | 4 +- src/chemistry/GEAR/chemistry_io.h | 67 +- src/chemistry/GEAR/chemistry_struct.h | 36 +- src/common_io.c | 142 ++- src/common_io.h | 7 + src/cooling/Compton/cooling.h | 7 +- src/cooling/EAGLE/cooling.c | 7 +- src/cooling/EAGLE/cooling.h | 3 +- src/cooling/const_du/cooling.h | 7 +- src/cooling/const_lambda/cooling.h | 7 +- src/cooling/grackle/cooling.c | 1038 +++++++++++++++++ src/cooling/grackle/cooling.h | 959 ++------------- src/cooling/grackle/cooling_io.h | 46 +- src/cooling/grackle/cooling_struct.h | 37 +- src/cooling/none/cooling.h | 7 +- src/engine.c | 4 +- src/feedback.h | 3 + src/feedback/EAGLE/feedback.h | 31 +- src/feedback/EAGLE/feedback_iact.h | 2 +- src/feedback/EAGLE/feedback_struct.h | 5 + src/feedback/GEAR/feedback.c | 279 +++++ src/feedback/GEAR/feedback.h | 85 ++ src/feedback/GEAR/feedback_iact.h | 148 +++ src/feedback/GEAR/feedback_properties.h | 102 ++ src/feedback/GEAR/feedback_struct.h | 63 + src/feedback/GEAR/hdf5_functions.h | 135 +++ src/feedback/GEAR/initial_mass_function.c | 543 +++++++++ src/feedback/GEAR/initial_mass_function.h | 60 + src/feedback/GEAR/interpolation.h | 207 ++++ src/feedback/GEAR/lifetime.h | 242 ++++ src/feedback/GEAR/stellar_evolution.c | 502 ++++++++ src/feedback/GEAR/stellar_evolution.h | 70 ++ src/feedback/GEAR/stellar_evolution_struct.h | 163 +++ src/feedback/GEAR/supernovae_ia.c | 418 +++++++ src/feedback/GEAR/supernovae_ia.h | 63 + src/feedback/GEAR/supernovae_ii.c | 432 +++++++ src/feedback/GEAR/supernovae_ii.h | 67 ++ src/feedback/none/feedback.h | 31 +- src/feedback/none/feedback_struct.h | 5 + src/feedback_properties.h | 2 + src/feedback_struct.h | 2 + src/hydro/AnarchyPU/hydro_part.h | 4 + src/hydro/Default/hydro_part.h | 4 + src/hydro/Gadget2/hydro_part.h | 4 + src/hydro/Gizmo/hydro_part.h | 4 + src/hydro/Minimal/hydro_part.h | 4 + src/hydro/Planetary/hydro_part.h | 4 + src/hydro/PressureEnergy/hydro.h | 4 + src/hydro/PressureEnergy/hydro_part.h | 4 + .../hydro_part.h | 4 + src/hydro/PressureEntropy/hydro_part.h | 4 + src/hydro/SPHENIX/hydro_part.h | 4 + src/hydro/Shadowswift/hydro_part.h | 4 + src/parallel_io.c | 2 + src/pressure_floor/GEAR/pressure_floor.h | 2 +- src/random.h | 5 +- src/runner_ghost.c | 11 +- src/runner_others.c | 23 +- src/runner_recv.c | 2 +- src/serial_io.c | 2 + src/single_io.c | 2 + src/space.c | 3 +- src/star_formation/EAGLE/star_formation.h | 5 +- src/star_formation/GEAR/star_formation.h | 23 +- .../GEAR/star_formation_struct.h | 5 +- src/star_formation/none/star_formation.h | 5 +- src/stars/EAGLE/stars.h | 6 +- src/stars/EAGLE/stars_part.h | 8 +- src/stars/GEAR/stars.h | 75 +- src/stars/GEAR/stars_iact.h | 9 +- src/stars/GEAR/stars_io.h | 11 +- src/stars/GEAR/stars_part.h | 38 +- src/task_order.h | 3 + src/task_order/EAGLE/task_order.h | 8 + src/task_order/GEAR/task_order.h | 8 + src/task_order/none/task_order.h | 14 + 95 files changed, 5704 insertions(+), 1338 deletions(-) create mode 100644 examples/GEAR/getChemistryTable.sh delete mode 100644 examples/SubgridTests/SupernovaeFeedback/SN_feedback.yml delete mode 100755 examples/SubgridTests/SupernovaeFeedback/getGlass.sh delete mode 100644 examples/SubgridTests/SupernovaeFeedback/makeIC.py delete mode 100644 examples/SubgridTests/SupernovaeFeedback/run.sh create mode 100644 src/cooling/grackle/cooling.c create mode 100644 src/feedback/GEAR/feedback.c create mode 100644 src/feedback/GEAR/feedback.h create mode 100644 src/feedback/GEAR/feedback_iact.h create mode 100644 src/feedback/GEAR/feedback_properties.h create mode 100644 src/feedback/GEAR/feedback_struct.h create mode 100644 src/feedback/GEAR/hdf5_functions.h create mode 100644 src/feedback/GEAR/initial_mass_function.c create mode 100644 src/feedback/GEAR/initial_mass_function.h create mode 100644 src/feedback/GEAR/interpolation.h create mode 100644 src/feedback/GEAR/lifetime.h create mode 100644 src/feedback/GEAR/stellar_evolution.c create mode 100644 src/feedback/GEAR/stellar_evolution.h create mode 100644 src/feedback/GEAR/stellar_evolution_struct.h create mode 100644 src/feedback/GEAR/supernovae_ia.c create mode 100644 src/feedback/GEAR/supernovae_ia.h create mode 100644 src/feedback/GEAR/supernovae_ii.c create mode 100644 src/feedback/GEAR/supernovae_ii.h diff --git a/configure.ac b/configure.ac index f91c2bf710..9f50ba4563 100644 --- a/configure.ac +++ b/configure.ac @@ -1439,13 +1439,14 @@ case "$with_subgrid" in ;; GEAR) with_subgrid_cooling=grackle_0 - with_subgrid_chemistry=GEAR + with_subgrid_chemistry=GEAR_10 with_subgrid_pressure_floor=GEAR with_subgrid_stars=GEAR with_subgrid_star_formation=GEAR - with_subgrid_feedback=none + with_subgrid_feedback=GEAR with_subgrid_black_holes=none - with_subgrid_task_order=GEAR + # GEAR's order is not used anymore + with_subgrid_task_order=none enable_fof=no ;; EAGLE) @@ -1791,7 +1792,8 @@ esac # chemistry function AC_ARG_WITH([chemistry], [AS_HELP_STRING([--with-chemistry=<function>], - [chemistry function @<:@none, GEAR, EAGLE default: none@:>@] + [chemistry function @<:@none, GEAR_*, EAGLE default: none@:>@ + For GEAR, you need to provide the number of elements (e.g. GEAR_10)] )], [with_chemistry="$withval"], [with_chemistry="none"] @@ -1809,8 +1811,10 @@ case "$with_chemistry" in none) AC_DEFINE([CHEMISTRY_NONE], [1], [No chemistry function]) ;; - GEAR) + GEAR_*) AC_DEFINE([CHEMISTRY_GEAR], [1], [Chemistry taken from the GEAR model]) + number_element=${with_chemistry:5} + AC_DEFINE_UNQUOTED([GEAR_CHEMISTRY_ELEMENT_COUNT], [$number_element], [Number of element to follow]) ;; EAGLE) AC_DEFINE([CHEMISTRY_EAGLE], [1], [Chemistry taken from the EAGLE model]) @@ -1913,6 +1917,9 @@ case "$with_feedback" in EAGLE) AC_DEFINE([FEEDBACK_EAGLE], [1], [EAGLE stellar feedback and evolution model]) ;; + GEAR) + AC_DEFINE([FEEDBACK_GEAR], [1], [GEAR stellar feedback and evolution model]) + ;; none) AC_DEFINE([FEEDBACK_NONE], [1], [No feedback]) ;; @@ -2139,6 +2146,12 @@ AM_CONDITIONAL([HAVEEAGLECOOLING], [test $with_cooling = "EAGLE"]) # Check if using EAGLE feedback AM_CONDITIONAL([HAVEEAGLEFEEDBACK], [test $with_feedback = "EAGLE"]) +# check if using grackle cooling +AM_CONDITIONAL([HAVEGRACKLECOOLING], [test ${with_cooling:0:7} == "grackle"]) + +# check if using gear feedback +AM_CONDITIONAL([HAVEGEARFEEDBACK], [test $with_feedback == "GEAR"]) + # Handle .in files. AC_CONFIG_FILES([Makefile src/Makefile examples/Makefile examples/Cooling/CoolingRates/Makefile doc/Makefile doc/Doxyfile tests/Makefile]) AC_CONFIG_FILES([argparse/Makefile tools/Makefile logger/Makefile logger/tests/Makefile]) diff --git a/examples/GEAR/AgoraDisk/agora_disk.yml b/examples/GEAR/AgoraDisk/agora_disk.yml index 92f2532b31..839e969a74 100644 --- a/examples/GEAR/AgoraDisk/agora_disk.yml +++ b/examples/GEAR/AgoraDisk/agora_disk.yml @@ -7,15 +7,25 @@ InternalUnitSystem: UnitTemp_in_cgs: 1 # Kelvin Scheduler: - max_top_level_cells: 8 + max_top_level_cells: 16 + cell_extra_sparts: 5000 # (Optional) Number of spare sparts per top-level allocated at rebuild time for on-the-fly creation. + cell_max_size: 8000 # (Optional) Maximal number of interactions per task if we force the split (this is the default value). + cell_sub_size_pair_hydro: 2560 # (Optional) Maximal number of hydro-hydro interactions per sub-pair hydro/star task (this is the default value). + cell_sub_size_self_hydro: 3200 # (Optional) Maximal number of hydro-hydro interactions per sub-self hydro/star task (this is the default value). + cell_sub_size_pair_stars: 2560 # (Optional) Maximal number of hydro-star interactions per sub-pair hydro/star task (this is the default value). + cell_sub_size_self_stars: 3200 # (Optional) Maximal number of hydro-star interactions per sub-self hydro/star task (this is the default value). + cell_sub_size_pair_grav: 2560 # (Optional) Maximal number of interactions per sub-pair gravity task (this is the default value). + cell_sub_size_self_grav: 3200 # (Optional) Maximal number of interactions per sub-self gravity task (this is the default value). + cell_split_size: 100 # (Optional) Maximal number of particles per cell (this is the default value). # Parameters governing the time integration TimeIntegration: time_begin: 0. # The starting time of the simulation (in internal units). time_end: 0.5 # The end time of the simulation (in internal units). dt_min: 1e-10 # The minimal time-step size of the simulation (in internal units). - dt_max: 1e-4 # The maximal time-step size of the simulation (in internal units). - + dt_max: 0.1 # The maximal time-step size of the simulation (in internal units). + max_dt_RMS_factor: 0.25 # (Optional) Dimensionless factor for the maximal displacement allowed based on the RMS velocities. + # Parameters governing the snapshots Snapshots: basename: agora_disk # Common part of the name of output files @@ -29,10 +39,12 @@ Statistics: # Parameters for the self-gravity scheme Gravity: - eta: 0.025 # Constant dimensionless multiplier for time integration. + eta: 0.05 # Constant dimensionless multiplier for time integration. theta: 0.7 # Opening angle (Multipole acceptance criterion) - comoving_softening: 0.08 # Comoving softening length (in internal units). - max_physical_softening: 0.08 # Physical softening length (in internal units). + comoving_DM_softening: 0.08 # Comoving softening length (in internal units). + max_physical_DM_softening: 0.08 # Physical softening length (in internal units). + comoving_baryon_softening: 0.08 # Comoving softening length (in internal units). + max_physical_baryon_softening: 0.08 # Physical softening length (in internal units). mesh_side_length: 32 # Number of cells along each axis for the periodic gravity mesh. # Parameters for the hydrodynamics scheme @@ -40,6 +52,8 @@ SPH: resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. minimal_temperature: 10. # Kelvin + h_min_ratio: 0.1095 # (Optional) Minimal allowed smoothing length in units of the softening. Defaults to 0 if unspecified. + h_max: 10. # (Optional) Maximal allowed smoothing length in internal units. Defaults to FLT_MAX if unspecified. # Parameters related to the initial conditions InitialConditions: @@ -55,13 +69,34 @@ LambdaCooling: # Cooling with Grackle 2.0 GrackleCooling: - CloudyTable: CloudyData_UVB=HM2012.h5 # Name of the Cloudy Table (available on the grackle bitbucket repository) - WithUVbackground: 1 # Enable or not the UV background - Redshift: 0 # Redshift to use (-1 means time based redshift) - WithMetalCooling: 1 # Enable or not the metal cooling - ProvideVolumetricHeatingRates: 0 # User provide volumetric heating rates - ProvideSpecificHeatingRates: 0 # User provide specific heating rates - SelfShieldingMethod: 0 # Grackle (<= 3) or Gear self shielding method - OutputMode: 1 # Write in output corresponding primordial chemistry mode - MaxSteps: 1000 - ConvergenceLimit: 1e-2 + cloudy_table: CloudyData_UVB=HM2012.h5 # Name of the Cloudy Table (available on the grackle bitbucket repository) + with_UV_background: 1 # Enable or not the UV background + redshift: 0 # Redshift to use (-1 means time based redshift) + with_metal_cooling: 1 # Enable or not the metal cooling + provide_volumetric_heating_rates: 0 # User provide volumetric heating rates + provide_specific_heating_rates: 0 # User provide specific heating rates + self_shielding_method: -1 # Grackle (<= 3) or Gear self shielding method + self_shielding_threshold_atom_per_cm3: 0.007 # Required only with GEAR's self shielding. Density threshold of the self shielding + max_steps: 1000 + convergence_limit: 1e-2 + thermal_time_myr: 5 + + +GEARStarFormation: + star_formation_efficiency: 0.01 # star formation efficiency (c_*) + maximal_temperature: 1e10 # Upper limit to the temperature of a star forming particle + +GEARPressureFloor: + jeans_factor: 10 + +GEARFeedback: + supernovae_energy_erg: 0.1e51 + yields_table: chemistry-AGB+OMgSFeZnSrYBaEu-16072013.h5 + discrete_yields: 0 + +GEARChemistry: + initial_metallicity: 1 + scale_initial_metallicity: 1 + +Restarts: + delta_hours: 72 # (Optional) decimal hours between dumps of restart files. diff --git a/examples/GEAR/AgoraDisk/cleanupSwift.py b/examples/GEAR/AgoraDisk/cleanupSwift.py index 31e6ed8ab0..865b0cd6d7 100644 --- a/examples/GEAR/AgoraDisk/cleanupSwift.py +++ b/examples/GEAR/AgoraDisk/cleanupSwift.py @@ -14,7 +14,7 @@ copyfile(filename, out) f = File(out) for i in range(6): - name = "PartType{}/ElementAbundance".format(i) + name = "PartType{}/ElementAbundances".format(i) if name in f: del f[name] @@ -23,13 +23,27 @@ for i in range(NPartType): if name not in f: continue - grp = f[name + "/SmoothingLength"] + grp = f[name + "/SmoothingLengths"] grp[:] *= 1.823 + # fix issue due to the name of densities + fields = [("Density", "Densities"), + ("Entropies", "Entropies"), + ("InternalEnergy", "InternalEnergies"), + ("SmoothingLength", "SmoothingLengths")] + + for field in fields: + if field[1] in f[name] and field[0] not in f[name]: + f[name + "/" + field[0]] = f[name + "/" + field[1]] + + + + cosmo = f["Cosmology"].attrs head = f["Header"].attrs head["OmegaLambda"] = cosmo["Omega_lambda"] head["Omega0"] = cosmo["Omega_b"] head["HubbleParam"] = cosmo["H0 [internal units]"] +head["Time"] = head["Time"][0] f.close() diff --git a/examples/GEAR/AgoraDisk/getIC.sh b/examples/GEAR/AgoraDisk/getIC.sh index c234b52b94..ca7fe3b251 100755 --- a/examples/GEAR/AgoraDisk/getIC.sh +++ b/examples/GEAR/AgoraDisk/getIC.sh @@ -6,4 +6,4 @@ if [ "$#" -ne 1 ]; then exit fi -wget https://obswww.unige.ch/~lhausamm/swift/IC/AgoraDisk/$1.hdf5 +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/AgoraDisk/$1.hdf5 diff --git a/examples/GEAR/AgoraDisk/getSolution.sh b/examples/GEAR/AgoraDisk/getSolution.sh index 37b7f8031b..681786a76c 100755 --- a/examples/GEAR/AgoraDisk/getSolution.sh +++ b/examples/GEAR/AgoraDisk/getSolution.sh @@ -1,12 +1,4 @@ #!/bin/bash -# cleanup work space -rm snapshot_0000.hdf5 snapshot_0500.hdf5 - -wget https://obswww.unige.ch/~lhausamm/swift/IC/AgoraDisk/Gear/with_cooling/snapshot_0000.hdf5 -wget https://obswww.unige.ch/~lhausamm/swift/IC/AgoraDisk/Gear/with_cooling/snapshot_0500.hdf5 - -# wget https://obswww.unige.ch/~lhausamm/swift/IC/AgoraDisk/Gear/without_cooling/snapshot_0000.hdf5 -# wget https://obswww.unige.ch/~lhausamm/swift/IC/AgoraDisk/Gear/without_cooling/snapshot_0500.hdf5 - - +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/AgoraDisk/Gear/snapshot_0000.hdf5 +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/AgoraDisk/Gear/snapshot_0050.hdf5 diff --git a/examples/GEAR/AgoraDisk/plotSolution.py b/examples/GEAR/AgoraDisk/plotSolution.py index 8db2c98dbc..b3a831b2d9 100644 --- a/examples/GEAR/AgoraDisk/plotSolution.py +++ b/examples/GEAR/AgoraDisk/plotSolution.py @@ -34,7 +34,7 @@ from yt.analysis_modules.halo_analysis.api import * from yt.data_objects.particle_filters import add_particle_filter from scipy.stats import kde from subprocess import call -#mylog.setLevel(1) +# mylog.setLevel(1) from yt.utilities.math_utils import get_cyl_r, get_cyl_theta, get_cyl_z @@ -53,7 +53,7 @@ draw_PDF = 2###2 # 0/1/2/3 = OFF/ON/ON with draw_pos_vel_PDF = 4##4 # 0/1/2/3/4 = OFF/ON/ON with 1D profile/ON also with 1D dispersion profile/ON also with separate 1D vertical dispersion profiles draw_star_pos_vel_PDF = 4##4 # 0/1/2/3/4 = OFF/ON/ON with 1D profile/ON also with 1D dispersion profile/ON also with separate 1D vertical dispersion profiles draw_rad_height_PDF = 2##2 # 0/1/2/3 = OFF/ON/ON with 1D profile/ON with analytic ftn subtracted -draw_metal_PDF = 0##0 # 0/1 = OFF/ON +draw_metal_PDF = 1##1 # 0/1 = OFF/ON draw_density_DF = 2#2 # 0/1/2 = OFF/ON/ON with difference plot between 1st and 2nd datasets (when 2, dataset_num should be set to 2) draw_radius_DF = 1#1 # 0/1 = OFF/ON draw_star_radius_DF = 2#2 # 0/1/2 = OFF/ON/ON with SFR profile and K-S plot (when 2, this automatically turns on draw_radius_DF) @@ -110,9 +110,9 @@ marker_names = ['s', 'o', 'p', 'v', '^', '<', '>', 'h', '*'] # [file_location[1]+'GIZMO/snapshot_temp_000', file_location[1]+'GIZMO/snapshot_temp_100']]] codes = ['SWIFT', 'GEAR'] filenames = [[["./agora_disk_IC.hdf5", "./agora_disk_500Myr.hdf5"], # Sim-noSFF - ["./snapshot_0000.hdf5", "./snapshot_0500.hdf5"]], + ["./snapshot_0000.hdf5", "./snapshot_0050.hdf5"]], [["./agora_disk_IC.hdf5", "./agora_disk_500Myr.hdf5"], # Sim-SFF (with star formation and feedback) - ["./snapshot_0000.hdf5", "./snapshot_0500.hdf5"]]] # I did not check the order, they can be switched + ["./snapshot_0000.hdf5", "./snapshot_0050.hdf5"]]] # I did not check the order, they can be switched # codes = ["SWIFT"] # filenames = [[["./agora_disk_0000.hdf5", "./agora_disk_0050.hdf5"]], @@ -139,8 +139,8 @@ filenames = [[["./agora_disk_IC.hdf5", "./agora_disk_500Myr.hdf5"], # Sim-noSFF # filenames = [[[file_location[0]+'GADGET-3/AGORA_ISO_LOW_DRY/snap_iso_dry_000.hdf5', file_location[0]+'GADGET-3/AGORA_ISO_LOW_DRY/snap_iso_dry_010.hdf5']], # [[file_location[1]+'GADGET-3/AGORA_ISO_LOW_SF_SNII_Thermal_Chevalier_SFT10/snap_iso_sf_000.hdf5', file_location[1]+'GADGET-3/AGORA_ISO_LOW_SF_SNII_Thermal_Chevalier_SFT10/snap_iso_sf_010.hdf5']]] # codes = ['GEAR'] -# filenames = [[['snapshot_0000', 'snapshot_0500']], -# [['snapshot_0000', 'snapshot_0500']]] +# filenames = [[['snapshot_0000.hdf5', 'snapshot_0500.hdf5']], +# [['snapshot_0000.hdf5', 'snapshot_0500']]] # codes = ['GIZMO'] # filenames = [[[file_location[0]+'GIZMO/snapshot_temp_000', file_location[0]+'GIZMO/snapshot_temp_100']], # [[file_location[1]+'GIZMO/snapshot_temp_000', file_location[1]+'GIZMO/snapshot_temp_100']]] @@ -439,7 +439,7 @@ for time in range(len(times)): yield line if draw_PDF >= 1: fig_PDF += [plt.figure(figsize=(50, 80))] - grid_PDF += [AxesGrid(fig_PDF[time], (0.01,0.01,0.99,0.99), nrows_ncols = (3, int(math.ceil(len(codes)/3.0))), axes_pad = 0.05, add_all = True, share_all = True, + grid_PDF += [AxesGrid(fig_PDF[time], (0.01,0.01,0.99,0.99), nrows_ncols = (2, int(math.ceil(len(codes)/2.0))), axes_pad = 0.05, add_all = True, share_all = True, label_mode = "1", cbar_mode = "single", cbar_location = "right", cbar_size = "2%", cbar_pad = 0.05, aspect = False)] if draw_pos_vel_PDF >= 1: fig_pos_vel_PDF += [plt.figure(figsize=(50, 80))] @@ -469,7 +469,7 @@ for time in range(len(times)): rad_height_profiles.append([]) if draw_metal_PDF == 1: fig_metal_PDF += [plt.figure(figsize=(50, 80))] - grid_metal_PDF += [AxesGrid(fig_metal_PDF[time], (0.01,0.01,0.99,0.99), nrows_ncols = (3, int(math.ceil(len(codes)/3.0))), axes_pad = 0.05, add_all = True, share_all = True, + grid_metal_PDF += [AxesGrid(fig_metal_PDF[time], (0.01,0.01,0.99,0.99), nrows_ncols = (2, int(math.ceil(len(codes)/2.0))), axes_pad = 0.05, add_all = True, share_all = True, label_mode = "1", cbar_mode = "single", cbar_location = "right", cbar_size = "2%", cbar_pad = 0.05, aspect = False)] if draw_density_DF >= 1: density_DF_xs.append([]) @@ -576,9 +576,9 @@ for time in range(len(times)): PartType_StarBeforeFiltered_to_use = "PartType4" if time != 0: def _FormationTime(field, data): - return pf.arr(data["PartType4", "BirthTime"].d, 'code_time') + return pf.arr(data["PartType4", "BirthTimes"].d, 'code_time') pf.add_field(("PartType4", FormationTimeType_to_use), function=_FormationTime, particle_type=True, take_log=False, units="code_time") - + MassType_to_use = "Masses" elif codes[code] == "GEAR": PartType_Gas_to_use = "PartType0" @@ -715,6 +715,20 @@ for time in range(len(times)): def _metallicity_3(field, data): return data["deposit", PartType_Gas_to_use+"_smoothed_"+MetallicityType_to_use] pf.add_field(("gas", "metallicity"), function=_metallicity_3, force_override=True, display_name="Metallicity", particle_type=False, take_log=True, units="") + elif codes[code] == 'SWIFT': # "Metals" in SWIFT is 10-species field ([:,9] is the total metal fraction), so requires a change in _vector_fields in frontends/gadget/io.py: added ("Metals", 10) + def _metallicity_2(field, data): + if len(data[PartType_Gas_to_use, "SmoothedElementAbundances"].shape) == 1: + return data[PartType_Gas_to_use, "SmoothedElementAbundances"] + else: + return data[PartType_Gas_to_use, "SmoothedElementAbundances"][:,9].in_units("") # in_units("") turned out to be crucial!; otherwise code_metallicity will be used and it will mess things up + # We are creating ("Gas", "Metallicity") here, different from ("Gas", "metallicity") which is auto-generated by yt but doesn't work properly + pf.add_field((PartType_Gas_to_use, MetallicityType_to_use), function=_metallicity_2, display_name="Metallicity", particle_type=True, take_log=True, units="") + # Also creating smoothed field following an example in yt-project.org/docs/dev/cookbook/calculating_information.html; use hardcoded num_neighbors as in frontends/gadget/fields.py + fn = add_volume_weighted_smoothed_field(PartType_Gas_to_use, "Coordinates", MassType_to_use, "SmoothingLength", "Density", MetallicityType_to_use, pf.field_info, nneighbors=64) + # Alias doesn't work -- e.g. pf.field_info.alias(("gas", "metallicity"), fn[0]) -- probably because pf=GadgetDataset()?, not load()?; so I add and replace existing ("gas", "metallicity") + def _metallicity_3(field, data): + return data["deposit", PartType_Gas_to_use+"_smoothed_"+MetallicityType_to_use] + pf.add_field(("gas", "metallicity"), function=_metallicity_3, force_override=True, display_name="Metallicity", particle_type=False, take_log=True, units="") if draw_metal_map >= 2: def _metal_mass(field, data): return data["gas", "cell_mass"] * data["gas", "metallicity"] @@ -745,7 +759,7 @@ for time in range(len(times)): if draw_metal_map >= 1 or draw_metal_PDF == 1: def _Metallicity_2(field, data): return data[(PartType_Gas_to_use, MetallicityType_to_use)] - pf.add_field((PartType_Gas_to_use, "Metallicity_2"), function=_Metallicity_2, take_log=True, particle_type=False, display_name="Metallicity", units="") + pf.add_field((PartType_Gas_to_use, "Metallicity_2"), function=_Metallicity_2, take_log=True, particle_type=True, display_name="Metallicity", units="") def _Density_2_minus_analytic(field, data): return data[(PartType_Gas_to_use, "density")] - rho_agora_disk(data[(PartType_Gas_to_use, "radius")], data[(PartType_Gas_to_use, "particle_position_cylindrical_z_abs")]) pf.add_field((PartType_Gas_to_use, "Density_2_minus_analytic"), function=_Density_2_minus_analytic, take_log=False, particle_type=False, display_name="Density Residual", units="g/cm**3") @@ -1565,13 +1579,18 @@ for time in range(len(times)): else: # Because ParticlePhasePlot doesn't yet work for a log-log PDF for some reason, I will do the following trick. pf.field_info[(PartType_Gas_to_use, "Metallicity_2")].take_log = False - p3 = PhasePlot(sp, (PartType_Gas_to_use, "density"), (PartType_Gas_to_use, "temperature"), (PartType_Gas_to_use, "Metallicity_2"), weight_field=(PartType_Gas_to_use, MassType_to_use), fontsize=12, x_bins=300, y_bins=300) + p3 = ParticlePhasePlot(sp, (PartType_Gas_to_use, "density"), (PartType_Gas_to_use, "temperature"), (PartType_Gas_to_use, "Metallicity_2"), + weight_field=(PartType_Gas_to_use, MassType_to_use), fontsize=12, x_bins=300, y_bins=300) p3.set_zlim((PartType_Gas_to_use, "Metallicity_2"), 0.01, 0.04) p3.set_cmap((PartType_Gas_to_use, "Metallicity_2"), my_cmap2) plot3 = p3.plots[(PartType_Gas_to_use, "Metallicity_2")] + p3.set_unit("density", "g/cm**3") p3.set_xlim(1e-29, 1e-21) + p3.set_unit("temperature", "K") p3.set_ylim(10, 1e7) + p3.set_log("density", True) + p3.set_log("temperature", True) plot3.figure = fig_metal_PDF[time] plot3.axes = grid_metal_PDF[time][code].axes @@ -2271,7 +2290,7 @@ for time in range(len(times)): if draw_metal_PDF == 1: fig_metal_PDF[time].savefig("metal_PDF_%dMyr" % times[time], bbox_inches='tight', pad_inches=0.03, dpi=300) if draw_density_DF >= 1: - plt.clf() +# plt.clf() # plt.subplot(111, aspect=1) fig = plt.figure(figsize=(8, 8)) gridspec.GridSpec(4, 1) diff --git a/examples/GEAR/AgoraDisk/run.sh b/examples/GEAR/AgoraDisk/run.sh index 8284098a1a..b2956e086d 100755 --- a/examples/GEAR/AgoraDisk/run.sh +++ b/examples/GEAR/AgoraDisk/run.sh @@ -5,6 +5,8 @@ # currently only the low resolution is available sim=low +rm agora_disk_0*.hdf5 + # make run.sh fail if a subcommand fails set -e @@ -19,7 +21,13 @@ fi if [ ! -e CloudyData_UVB=HM2012.h5 ] then echo "Fetching the Cloudy tables required by Grackle..." - ../../Cooling/getGrackleCoolingTable.sh + ../../Cooling/getGrackleCoolingTable.sh +fi + +if [ ! -e chemistry-AGB+OMgSFeZnSrYBaEu-16072013.h5 ] +then + echo "Fetching the chemistry tables..." + ../getChemistryTable.sh fi # copy the initial conditions @@ -28,7 +36,7 @@ cp $sim.hdf5 agora_disk.hdf5 python3 changeType.py agora_disk.hdf5 # Run SWIFT -../../swift --cooling --hydro --self-gravity --threads=4 agora_disk.yml 2>&1 | tee output.log +../../swift --sync --limiter --cooling --hydro --self-gravity --star-formation --feedback --stars --threads=8 agora_disk.yml 2>&1 | tee output.log echo "Changing smoothing length to be Gadget compatible" diff --git a/examples/GEAR/DwarfGalaxy/dwarf_galaxy.yml b/examples/GEAR/DwarfGalaxy/dwarf_galaxy.yml index 00fd889d4f..d377031585 100644 --- a/examples/GEAR/DwarfGalaxy/dwarf_galaxy.yml +++ b/examples/GEAR/DwarfGalaxy/dwarf_galaxy.yml @@ -68,3 +68,31 @@ InitialConditions: cleanup_h_factors: 1 # Remove the h-factors inherited from Gadget cleanup_velocity_factors: 1 # Remove the sqrt(a) factor in the velocities inherited from Gadget + +# Cooling with Grackle 3.0 +GrackleCooling: + cloudy_table: CloudyData_UVB=HM2012.h5 # Name of the Cloudy Table (available on the grackle bitbucket repository) + with_UV_background: 1 # Enable or not the UV background + redshift: -1 # Redshift to use (-1 means time based redshift) + with_metal_cooling: 1 # Enable or not the metal cooling + provide_volumetric_heating_rates: 0 # (optional) User provide volumetric heating rates + provide_specific_heating_rates: 0 # (optional) User provide specific heating rates + self_shielding_method: 0 # (optional) Grackle (<= 3) or Gear self shielding method + max_steps: 10000 # (optional) Max number of step when computing the initial composition + convergence_limit: 1e-2 # (optional) Convergence threshold (relative) for initial composition + thermal_time_Myr: 5 + +GearChemistry: + initial_metallicity: 0.01295 + +GEARFeedback: + supernovae_energy_erg: 1e50 + yields_table: chemistry-AGB+OMgSFeZnSrYBaEu-16072013.h5 + +GearPressureFloor: + jeans_factor: 10. # Number of particles required to suppose a resolved clump and avoid the pressure floor. + + +GEARStarFormation: + star_formation_efficiency: 0.01 # star formation efficiency (c_*) + maximal_temperature: 3e4 # Upper limit to the temperature of a star forming particle diff --git a/examples/GEAR/DwarfGalaxy/run.sh b/examples/GEAR/DwarfGalaxy/run.sh index 1f63556e7f..11be3465d2 100755 --- a/examples/GEAR/DwarfGalaxy/run.sh +++ b/examples/GEAR/DwarfGalaxy/run.sh @@ -7,5 +7,5 @@ then ./getIC.sh fi -../../swift --feedback --self-gravity --hydro --stars --threads=8 $@ dwarf_galaxy.yml 2>&1 | tee output.log +../../swift --feedback --limiter --sync --self-gravity --hydro --stars --cooling --star-formation --threads=8 $@ dwarf_galaxy.yml 2>&1 | tee output.log diff --git a/examples/GEAR/ZoomIn/run.sh b/examples/GEAR/ZoomIn/run.sh index 59c8ff0d63..58961e0253 100755 --- a/examples/GEAR/ZoomIn/run.sh +++ b/examples/GEAR/ZoomIn/run.sh @@ -7,5 +7,5 @@ then ./getIC.sh fi -../../swift --feedback --cosmology --self-gravity --hydro --stars --threads=8 zoom_in.yml 2>&1 | tee output.log +../../swift --feedback --cosmology --limiter --sync --self-gravity --hydro --stars --star-formation --threads=8 zoom_in.yml 2>&1 | tee output.log diff --git a/examples/GEAR/ZoomIn/zoom_in.yml b/examples/GEAR/ZoomIn/zoom_in.yml index 8e5763c4af..1d01e6abd3 100644 --- a/examples/GEAR/ZoomIn/zoom_in.yml +++ b/examples/GEAR/ZoomIn/zoom_in.yml @@ -60,3 +60,30 @@ InitialConditions: cleanup_h_factors: 1 # Remove the h-factors inherited from Gadget cleanup_velocity_factors: 1 # Remove the sqrt(a) factor in the velocities inherited from Gadget + +# Cooling with Grackle 3.0 +GrackleCooling: + cloudy_table: CloudyData_UVB=HM2012.h5 # Name of the Cloudy Table (available on the grackle bitbucket repository) + with_UV_background: 1 # Enable or not the UV background + redshift: -1 # Redshift to use (-1 means time based redshift) + with_metal_cooling: 1 # Enable or not the metal cooling + provide_volumetric_heating_rates: 0 # (optional) User provide volumetric heating rates + provide_specific_heating_rates: 0 # (optional) User provide specific heating rates + self_shielding_method: 0 # (optional) Grackle (<= 3) or Gear self shielding method + max_steps: 10000 # (optional) Max number of step when computing the initial composition + convergence_limit: 1e-2 # (optional) Convergence threshold (relative) for initial composition + thermal_time_Myr: 5 + +GearChemistry: + initial_metallicity: 0.01295 + +GEARFeedback: + supernovae_energy_erg: 1e50 + yields_table: chemistry-AGB+OMgSFeZnSrYBaEu-16072013.h5 + +GEARStarFormation: + star_formation_efficiency: 0.01 # star formation efficiency (c_*) + maximal_temperature: 3e4 # Upper limit to the temperature of a star forming particle + +GEARPressureFloor: + jeans_factor: 10. # Number of particles required to suppose a resolved clump and avoid the pressure floor. diff --git a/examples/GEAR/getChemistryTable.sh b/examples/GEAR/getChemistryTable.sh new file mode 100644 index 0000000000..b8f8ac7886 --- /dev/null +++ b/examples/GEAR/getChemistryTable.sh @@ -0,0 +1,2 @@ +#!/bin/bash +wget https://obswww.unige.ch/lastro/projects/Clastro/PySWIFTsim/chemistry-AGB+OMgSFeZnSrYBaEu-16072013.h5 diff --git a/examples/SubgridTests/SupernovaeFeedback/SN_feedback.yml b/examples/SubgridTests/SupernovaeFeedback/SN_feedback.yml deleted file mode 100644 index a59ae302ff..0000000000 --- a/examples/SubgridTests/SupernovaeFeedback/SN_feedback.yml +++ /dev/null @@ -1,44 +0,0 @@ -# Define the system of units to use internally. -InternalUnitSystem: - UnitMass_in_cgs: 1 # Grams - UnitLength_in_cgs: 1 # Centimeters - UnitVelocity_in_cgs: 1 # Centimeters per second - UnitCurrent_in_cgs: 1 # Amperes - UnitTemp_in_cgs: 1 # Kelvin - -# Values of some physical constants -PhysicalConstants: - G: 0 # (Optional) Overwrite the value of Newton's constant used internally by the code. - -# Parameters governing the time integration -TimeIntegration: - time_begin: 0. # The starting time of the simulation (in internal units). - time_end: 5e-2 # The end time of the simulation (in internal units). - dt_min: 1e-7 # The minimal time-step size of the simulation (in internal units). - dt_max: 1e-4 # The maximal time-step size of the simulation (in internal units). - -# Parameters governing the snapshots -Snapshots: - basename: SN_feedback # Common part of the name of output files - time_first: 0. # Time of the first output (in internal units) - delta_time: 1e-2 # Time difference between consecutive outputs (in internal units) - compression: 1 - -# Parameters governing the conserved quantities statistics -Statistics: - delta_time: 1e-3 # Time between statistics output - -# Parameters for the hydrodynamics scheme -SPH: - resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). - CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. - -# Parameters related to the initial conditions -InitialConditions: - file_name: ./SN_feedback.hdf5 - smoothing_length_scaling: 1. - periodic: 1 # Are we running with periodic ICs? - -# Parameters for the stellar models -Stars: - resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). diff --git a/examples/SubgridTests/SupernovaeFeedback/getGlass.sh b/examples/SubgridTests/SupernovaeFeedback/getGlass.sh deleted file mode 100755 index d5c5f590ac..0000000000 --- a/examples/SubgridTests/SupernovaeFeedback/getGlass.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/glassCube_64.hdf5 diff --git a/examples/SubgridTests/SupernovaeFeedback/makeIC.py b/examples/SubgridTests/SupernovaeFeedback/makeIC.py deleted file mode 100644 index 8a0fca2bfd..0000000000 --- a/examples/SubgridTests/SupernovaeFeedback/makeIC.py +++ /dev/null @@ -1,117 +0,0 @@ -############################################################################### - # This file is part of SWIFT. - # Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@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 <http://www.gnu.org/licenses/>. - # - ############################################################################## - -import h5py -from numpy import * - -# Generates a swift IC file for the Sedov blast test in a periodic cubic box - -# Parameters -gamma = 5./3. # Gas adiabatic index -rho0 = 1. # Background density -P0 = 1.e-6 # Background pressure -E0= 1. # Energy of the explosion -N_inject = 15 # Number of particles in which to inject energy -fileName = "SN_feedback.hdf5" - -#--------------------------------------------------- -glass = h5py.File("glassCube_64.hdf5", "r") - -# Read particle positions and h from the glass -pos = glass["/PartType0/Coordinates"][:,:] -eps = 1e-6 -pos = (pos - pos.min()) / (pos.max() - pos.min() + eps) -h = glass["/PartType0/SmoothingLength"][:] * 0.3 * 3.3 - -numPart = size(h) -vol = 1. -Boxsize = 1. - -# Generate extra arrays -v = zeros((numPart, 3)) -ids = linspace(1, numPart, numPart) -m = zeros(numPart) -u = zeros(numPart) -r = zeros(numPart) - -r = sqrt((pos[:,0] - 0.5)**2 + (pos[:,1] - 0.5)**2 + (pos[:,2] - 0.5)**2) -m[:] = rho0 * vol / numPart -u[:] = P0 / (rho0 * (gamma - 1)) - -#-------------------------------------------------- - -star_pos = zeros((1, 3)) -star_pos[:,:] = 0.5 * Boxsize - -star_v = zeros((1, 3)) -star_v[:,:] = 0. - -# increase mass to keep it at center -star_m = 1e3 * array([rho0 * vol / numPart]) -star_ids = array([numPart + 1]) -star_h = array([h.max()]) - -#-------------------------------------------------- - -#File -file = h5py.File(fileName, 'w') - -# Header -grp = file.create_group("/Header") -grp.attrs["BoxSize"] = [Boxsize]*3 -grp.attrs["NumPart_Total"] = [numPart, 0, 0, 0, 1, 0] -grp.attrs["NumPart_Total_HighWord"] = [0, 0, 0, 0, 0, 0] -grp.attrs["NumPart_ThisFile"] = [numPart, 0, 0, 0, 1, 0] -grp.attrs["Time"] = 0.0 -grp.attrs["NumFilesPerSnapshot"] = 1 -grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] -grp.attrs["Flag_Entropy_ICs"] = 0 -grp.attrs["Dimension"] = 3 - -#Runtime parameters -grp = file.create_group("/RuntimePars") -grp.attrs["PeriodicBoundariesOn"] = 0 - -#Units -grp = file.create_group("/Units") -grp.attrs["Unit length in cgs (U_L)"] = 1. -grp.attrs["Unit mass in cgs (U_M)"] = 1. -grp.attrs["Unit time in cgs (U_t)"] = 1. -grp.attrs["Unit current in cgs (U_I)"] = 1. -grp.attrs["Unit temperature in cgs (U_T)"] = 1. - -#Particle group -grp = file.create_group("/PartType0") -grp.create_dataset('Coordinates', data=pos, dtype='d') -grp.create_dataset('Velocities', data=v, dtype='f') -grp.create_dataset('Masses', data=m, dtype='f') -grp.create_dataset('SmoothingLength', data=h, dtype='f') -grp.create_dataset('InternalEnergy', data=u, dtype='f') -grp.create_dataset('ParticleIDs', data=ids, dtype='L') - -# stellar group -grp = file.create_group("/PartType4") -grp.create_dataset("Coordinates", data=star_pos, dtype="d") -grp.create_dataset('Velocities', data=star_v, dtype='f') -grp.create_dataset('Masses', data=star_m, dtype='f') -grp.create_dataset('SmoothingLength', data=star_h, dtype='f') -grp.create_dataset('ParticleIDs', data=star_ids, dtype='L') - - -file.close() diff --git a/examples/SubgridTests/SupernovaeFeedback/run.sh b/examples/SubgridTests/SupernovaeFeedback/run.sh deleted file mode 100644 index af8802164c..0000000000 --- a/examples/SubgridTests/SupernovaeFeedback/run.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash - - # Generate the initial conditions if they are not present. -if [ ! -e glassCube_64.hdf5 ] -then - echo "Fetching initial glass file for the Supernovae feedback example..." - ./getGlass.sh -fi -if [ ! -e SN_feedback.hdf5 ] -then - echo "Generating initial conditions for the Supernovae feedback example..." - python makeIC.py -fi - -# Run SWIFT -../../swift --external-gravity --feedback --hydro --stars --threads=4 SN_feedback.yml 2>&1 | tee output.log - -# Plot the solution -# TODO diff --git a/examples/parameter_example.yml b/examples/parameter_example.yml index 754b0d9986..4d43ae30f5 100644 --- a/examples/parameter_example.yml +++ b/examples/parameter_example.yml @@ -307,7 +307,7 @@ EAGLEEntropyFloor: # Parameters related to pressure floors ---------------------------------------------- GEARPressureFloor: - Jeans_factor: 10. # Number of particles required to suppose a resolved clump and avoid the pressure floor. + jeans_factor: 10. # Number of particles required to suppose a resolved clump and avoid the pressure floor. # Parameters related to cooling function ---------------------------------------------- @@ -335,15 +335,17 @@ EAGLECooling: # Cooling with Grackle 3.0 GrackleCooling: - CloudyTable: CloudyData_UVB=HM2012.h5 # Name of the Cloudy Table (available on the grackle bitbucket repository) - WithUVbackground: 1 # Enable or not the UV background - Redshift: 0 # Redshift to use (-1 means time based redshift) - WithMetalCooling: 1 # Enable or not the metal cooling - ProvideVolumetricHeatingRates: 0 # (optional) User provide volumetric heating rates - ProvideSpecificHeatingRates: 0 # (optional) User provide specific heating rates - SelfShieldingMethod: 0 # (optional) Grackle (<= 3) or Gear self shielding method - MaxSteps: 10000 # (optional) Max number of step when computing the initial composition - ConvergenceLimit: 1e-2 # (optional) Convergence threshold (relative) for initial composition + cloudy_table: CloudyData_UVB=HM2012.h5 # Name of the Cloudy Table (available on the grackle bitbucket repository) + with_UV_background: 1 # Enable or not the UV background + redshift: 0 # Redshift to use (-1 means time based redshift) + with_metal_cooling: 1 # Enable or not the metal cooling + provide_volumetric_heating_rates: 0 # (optional) User provide volumetric heating rates + provide_specific_heating_rates: 0 # (optional) User provide specific heating rates + max_steps: 10000 # (optional) Max number of step when computing the initial composition + convergence_limit: 1e-2 # (optional) Convergence threshold (relative) for initial composition + thermal_time_myr: 5 # (optional) Time (in Myr) for adiabatic cooling after a feedback event. + self_shielding_method: -1 # (optional) Grackle (1->3 for Grackle's ones, 0 for none and -1 for GEAR) + self_shielding_threshold_atom_per_cm3: 0.007 # Required only with GEAR's self shielding. Density threshold of the self shielding # Parameters related to chemistry models ----------------------------------------------- @@ -360,6 +362,12 @@ EAGLEChemistry: init_abundance_Silicon: 0.000 # Inital fraction of particle mass in Silicon init_abundance_Iron: 0.000 # Inital fraction of particle mass in Iron +# GEAR chemistry model (Revaz and Jablonka 2018) +GEARChemistry: + initial_metallicity: 1 # Initial metallicity of the gas (mass fraction) + scale_initial_metallicity: 1 # Should we scale the initial metallicity with the solar one? + + # Parameters related to star formation models ----------------------------------------------- # GEAR star formation model (Revaz and Jablonka 2018) @@ -428,6 +436,12 @@ EAGLEFeedback: SNII_yield_factor_Silicon: 1.0 # (Optional) Correction factor to apply to the Silicon yield from the SNII channel. SNII_yield_factor_Iron: 0.5 # (Optional) Correction factor to apply to the Iron yield from the SNII channel. +# GEAR feedback model +GEARFeedback: + supernovae_energy_erg: 0.1e51 # Energy released by a single supernovae. + yields_table: chemistry-AGB+OMgSFeZnSrYBaEu-16072013.h5 # Table containing the yields. + discrete_yields: 0 # Should we use discrete yields or the IMF integrated one? + # Parameters related to AGN models ----------------------------------------------- # EAGLE AGN model diff --git a/src/Makefile.am b/src/Makefile.am index 54b766c46e..7144496b68 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -71,6 +71,19 @@ if HAVEEAGLEFEEDBACK EAGLE_FEEDBACK_SOURCES += feedback/EAGLE/feedback.c endif +# source files for GRACKLE cooling +GRACKLE_COOLING_SOURCES = +if HAVEGRACKLECOOLING +GRACKLE_COOLING_SOURCES += cooling/grackle/cooling.c +endif + +# source files for GRACKLE cooling +GEAR_FEEDBACK_SOURCES = +if HAVEGEARFEEDBACK +GEAR_FEEDBACK_SOURCES += feedback/GEAR/stellar_evolution.c feedback/GEAR/feedback.c \ + feedback/GEAR/initial_mass_function.c feedback/GEAR/supernovae_ia.c feedback/GEAR/supernovae_ii.c +endif + # Common source files AM_SOURCES = space.c runner_main.c runner_doiact_hydro.c runner_doiact_limiter.c \ runner_doiact_stars.c runner_doiact_black_holes.c runner_ghost.c runner_recv.c \ @@ -89,7 +102,8 @@ AM_SOURCES = space.c runner_main.c runner_doiact_hydro.c runner_doiact_limiter.c chemistry.c cosmology.c restart.c mesh_gravity.c velociraptor_interface.c \ outputlist.c velociraptor_dummy.c logger_io.c memuse.c mpiuse.c memuse_rnodes.c fof.c \ hashmap.c pressure_floor.c \ - $(EAGLE_COOLING_SOURCES) $(EAGLE_FEEDBACK_SOURCES) + $(EAGLE_COOLING_SOURCES) $(EAGLE_FEEDBACK_SOURCES) $(GRACKLE_COOLING_SOURCES) \ + $(GEAR_FEEDBACK_SOURCES) # Include files for distribution, not installation. nobase_noinst_HEADERS = align.h approx_math.h atomic.h barrier.h cycle.h error.h inline.h kernel_hydro.h kernel_gravity.h \ @@ -182,6 +196,8 @@ nobase_noinst_HEADERS = align.h approx_math.h atomic.h barrier.h cycle.h error.h stars/Default/stars_debug.h stars/Default/stars_part.h \ stars/EAGLE/stars.h stars/EAGLE/stars_iact.h stars/EAGLE/stars_io.h \ stars/EAGLE/stars_debug.h stars/EAGLE/stars_part.h \ + stars/GEAR/stars.h stars/GEAR/stars_iact.h stars/GEAR/stars_io.h \ + stars/GEAR/stars_debug.h stars/GEAR/stars_part.h \ potential/none/potential.h potential/point_mass/potential.h \ potential/isothermal/potential.h potential/disc_patch/potential.h \ potential/sine_wave/potential.h \ @@ -229,6 +245,11 @@ nobase_noinst_HEADERS = align.h approx_math.h atomic.h barrier.h cycle.h error.h feedback/EAGLE/feedback.h feedback/EAGLE/feedback_struct.h feedback/EAGLE/feedback_iact.h \ feedback/EAGLE/feedback_properties.h feedback/EAGLE/imf.h feedback/EAGLE/interpolate.h \ feedback/EAGLE/yield_tables.h \ + feedback/GEAR/stellar_evolution_struct.h feedback/GEAR/stellar_evolution.h \ + feedback/GEAR/feedback.h feedback/GEAR/feedback_iact.h \ + feedback/GEAR/feedback_properties.h feedback/GEAR/feedback_struct.h \ + feedback/GEAR/initial_mass_function.h feedback/GEAR/supernovae_ia.h feedback/GEAR/supernovae_ii.h \ + feedback/GEAR/lifetime.h feedback/GEAR/hdf5_functions.h feedback/GEAR/interpolation.h \ black_holes/Default/black_holes.h black_holes/Default/black_holes_io.h \ black_holes/Default/black_holes_part.h black_holes/Default/black_holes_iact.h \ black_holes/Default/black_holes_properties.h \ @@ -245,7 +266,7 @@ nobase_noinst_HEADERS = align.h approx_math.h atomic.h barrier.h cycle.h error.h # Sources and special flags for the gravity library libgrav_la_SOURCES = runner_doiact_grav.c -libgrav_la_CFLAGS = $(AM_CFLAGS) $(GRAVITY_CFLAGS) +libgrav_la_CFLAGS = $(AM_CFLAGS) $(GRAVITY_CFLAGS) libgrav_la_LDFLAGS = $(AM_LDFLAGS) $(EXTRA_LIBS) # Sources and special flags for the gravity MPI library diff --git a/src/chemistry/GEAR/chemistry.h b/src/chemistry/GEAR/chemistry.h index d301661a96..ebeb41b3ba 100644 --- a/src/chemistry/GEAR/chemistry.h +++ b/src/chemistry/GEAR/chemistry.h @@ -27,6 +27,7 @@ /* Some standard headers. */ #include <float.h> #include <math.h> +#include <string.h> /* Local includes. */ #include "chemistry_struct.h" @@ -38,16 +39,21 @@ #include "units.h" /** - * @brief Compute the metal mass fraction + * @brief Copies the chemistry properties of the gas particle over to the + * star particle. * - * @param p Pointer to the particle data. - * @param xp Pointer to the extended particle data. - * @param data The global chemistry information. + * @param p the gas particles. + * @param xp the additional properties of the gas particles. + * @param sp the new created star particle with its properties. */ -__attribute__((always_inline)) INLINE static float -chemistry_metal_mass_fraction(const struct part* restrict p, - const struct xpart* restrict xp) { - return p->chemistry_data.Z; +INLINE static void chemistry_copy_star_formation_properties( + const struct part* p, const struct xpart* xp, struct spart* sp) { + + /* Store the chemistry struct in the star particle */ + for (int i = 0; i < GEAR_CHEMISTRY_ELEMENT_COUNT; i++) { + sp->chemistry_data.metal_mass_fraction[i] = + p->chemistry_data.smoothed_metal_mass_fraction[i]; + } } /** @@ -62,6 +68,60 @@ static INLINE void chemistry_print_backend( message("Chemistry function is 'Gear'."); } +/** + * @brief Read the solar abundances and scale with them the initial + * metallicities. + * + * @param parameter_file The parsed parameter file. + * @param data The properties to initialise. + */ +static INLINE void chemistry_scale_initial_metallicities( + struct swift_params* parameter_file, struct chemistry_global_data* data) { +#ifdef HAVE_HDF5 + + /* Get the yields table */ + char filename[DESCRIPTION_BUFFER_SIZE]; + parser_get_param_string(parameter_file, "GEARFeedback:yields_table", + filename); + + /* Open file. */ + hid_t file_id = H5Fopen(filename, H5F_ACC_RDONLY, H5P_DEFAULT); + if (file_id < 0) error("unable to open file %s.\n", filename); + + /* Open group. */ + hid_t group_id = H5Gopen(file_id, "Data", H5P_DEFAULT); + if (group_id < 0) error("unable to open group Data.\n"); + + /* Read the data */ + float* sol_ab = (float*)malloc(sizeof(float) * GEAR_CHEMISTRY_ELEMENT_COUNT); + io_read_array_attribute(group_id, "SolarMassAbundances", FLOAT, sol_ab, + GEAR_CHEMISTRY_ELEMENT_COUNT); + + /* Close group */ + hid_t status = H5Gclose(group_id); + if (status < 0) error("error closing group."); + + /* Close file */ + status = H5Fclose(file_id); + if (status < 0) error("error closing file."); + + /* Scale the initial metallicities */ + char txt[DESCRIPTION_BUFFER_SIZE] = "Scaling initial metallicities by:"; + for (int i = 0; i < GEAR_CHEMISTRY_ELEMENT_COUNT; i++) { + data->initial_metallicities[i] *= sol_ab[i]; + char tmp[10]; + sprintf(tmp, " %.2g", sol_ab[i]); + strcat(txt, tmp); + } + + if (engine_rank == 0) { + message("%s", txt); + } +#else + error("Cannot scale the solar abundances without HDF5"); +#endif +} + /** * @brief Initialises the chemistry properties. * @@ -78,8 +138,22 @@ static INLINE void chemistry_init_backend(struct swift_params* parameter_file, struct chemistry_global_data* data) { /* read parameters */ - data->initial_metallicity = parser_get_opt_param_float( - parameter_file, "GearChemistry:InitialMetallicity", -1); + const float initial_metallicity = parser_get_param_float( + parameter_file, "GEARChemistry:initial_metallicity"); + + /* Set the initial metallicities */ + for (int i = 0; i < GEAR_CHEMISTRY_ELEMENT_COUNT; i++) { + data->initial_metallicities[i] = initial_metallicity; + } + + /* Check if need to scale the initial metallicity */ + const int scale_metallicity = parser_get_opt_param_int( + parameter_file, "GEARChemistry:scale_initial_metallicity", 0); + + /* Scale the metallicities if required */ + if (scale_metallicity) { + chemistry_scale_initial_metallicities(parameter_file, data); + } } /** @@ -96,8 +170,12 @@ __attribute__((always_inline)) INLINE static void chemistry_init_part( struct chemistry_part_data* cpd = &p->chemistry_data; - for (int i = 0; i < chemistry_element_count; i++) { + for (int i = 0; i < GEAR_CHEMISTRY_ELEMENT_COUNT; i++) { + /* Reset the smoothed metallicity */ cpd->smoothed_metal_mass_fraction[i] = 0.f; + + /* Convert the total mass into mass fraction */ + cpd->metal_mass_fraction[i] = cpd->metal_mass[i] / p->mass; } } @@ -125,13 +203,16 @@ __attribute__((always_inline)) INLINE static void chemistry_end_density( struct chemistry_part_data* cpd = &p->chemistry_data; - for (int i = 0; i < chemistry_element_count; i++) { + for (int i = 0; i < GEAR_CHEMISTRY_ELEMENT_COUNT; i++) { /* Final operation on the density (add self-contribution). */ cpd->smoothed_metal_mass_fraction[i] += m * cpd->metal_mass_fraction[i] * kernel_root; /* Finish the calculation by inserting the missing h-factors */ cpd->smoothed_metal_mass_fraction[i] *= factor; + + /* Convert the mass fraction into a total mass */ + cpd->metal_mass[i] = m * cpd->metal_mass_fraction[i]; } } @@ -157,7 +238,14 @@ chemistry_part_has_no_neighbours(struct part* restrict p, struct xpart* restrict xp, const struct chemistry_global_data* cd, const struct cosmology* cosmo) { - error("Needs implementing!"); + + /* Set the smoothed fractions with the non smoothed fractions */ + for (int i = 0; i < GEAR_CHEMISTRY_ELEMENT_COUNT; i++) { + p->chemistry_data.smoothed_metal_mass_fraction[i] = + p->chemistry_data.metal_mass_fraction[i]; + p->chemistry_data.metal_mass[i] = + p->chemistry_data.metal_mass_fraction[i] * p->mass; + } } /** @@ -198,7 +286,10 @@ __attribute__((always_inline)) INLINE static void chemistry_first_init_part( const struct chemistry_global_data* data, struct part* restrict p, struct xpart* restrict xp) { - p->chemistry_data.Z = data->initial_metallicity; + for (int i = 0; i < GEAR_CHEMISTRY_ELEMENT_COUNT; i++) { + p->chemistry_data.metal_mass[i] = data->initial_metallicities[i] * p->mass; + } + chemistry_init_part(p, data); } @@ -212,9 +303,9 @@ __attribute__((always_inline)) INLINE static void chemistry_first_init_part( __attribute__((always_inline)) INLINE static void chemistry_first_init_spart( const struct chemistry_global_data* data, struct spart* restrict sp) { - error( - "MATTHIEU: Loic this is a new function. I don't know whether you" - " want something here."); + for (int i = 0; i < GEAR_CHEMISTRY_ELEMENT_COUNT; i++) { + sp->chemistry_data.metal_mass_fraction[i] = data->initial_metallicities[i]; + } } /** @@ -273,4 +364,84 @@ __attribute__((always_inline)) INLINE static void chemistry_split_part( error("Loic: to be implemented"); } +/** + * @brief Returns the total metallicity (metal mass fraction) of the + * star particle to be used in feedback/enrichment related routines. + * + * @param sp Pointer to the particle data. + */ +__attribute__((always_inline)) INLINE static float +chemistry_get_total_metal_mass_fraction_for_feedback( + const struct spart* restrict sp) { + + return sp->chemistry_data + .metal_mass_fraction[GEAR_CHEMISTRY_ELEMENT_COUNT - 1]; +} + +/** + * @brief Returns the abundances (metal mass fraction) of the + * star particle to be used in feedback/enrichment related routines. + * + * @param sp Pointer to the particle data. + */ +__attribute__((always_inline)) INLINE static float const* +chemistry_get_metal_mass_fraction_for_feedback( + const struct spart* restrict sp) { + + return sp->chemistry_data.metal_mass_fraction; +} + +/** + * @brief Returns the total metallicity (metal mass fraction) of the + * gas particle to be used in cooling related routines. + * + * @param p Pointer to the particle data. + */ +__attribute__((always_inline)) INLINE static float +chemistry_get_total_metal_mass_fraction_for_cooling( + const struct part* restrict p) { + + return p->chemistry_data + .smoothed_metal_mass_fraction[GEAR_CHEMISTRY_ELEMENT_COUNT - 1]; +} + +/** + * @brief Returns the abundance array (metal mass fractions) of the + * gas particle to be used in cooling related routines. + * + * @param p Pointer to the particle data. + */ +__attribute__((always_inline)) INLINE static float const* +chemistry_get_metal_mass_fraction_for_cooling(const struct part* restrict p) { + + return p->chemistry_data.smoothed_metal_mass_fraction; +} + +/** + * @brief Returns the total metallicity (metal mass fraction) of the + * gas particle to be used in star formation related routines. + * + * @param p Pointer to the particle data. + */ +__attribute__((always_inline)) INLINE static float +chemistry_get_total_metal_mass_fraction_for_star_formation( + const struct part* restrict p) { + + return p->chemistry_data + .smoothed_metal_mass_fraction[GEAR_CHEMISTRY_ELEMENT_COUNT - 1]; +} + +/** + * @brief Returns the abundance array (metal mass fractions) of the + * gas particle to be used in star formation related routines. + * + * @param p Pointer to the particle data. + */ +__attribute__((always_inline)) INLINE static float const* +chemistry_get_metal_mass_fraction_for_star_formation( + const struct part* restrict p) { + + return p->chemistry_data.smoothed_metal_mass_fraction; +} + #endif /* SWIFT_CHEMISTRY_GEAR_H */ diff --git a/src/chemistry/GEAR/chemistry_iact.h b/src/chemistry/GEAR/chemistry_iact.h index f1d724b680..ff49af4abb 100644 --- a/src/chemistry/GEAR/chemistry_iact.h +++ b/src/chemistry/GEAR/chemistry_iact.h @@ -68,7 +68,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_chemistry( kernel_deval(uj, &wj, &wj_dx); /* Compute contribution to the smooth metallicity */ - for (int i = 0; i < chemistry_element_count; i++) { + for (int i = 0; i < GEAR_CHEMISTRY_ELEMENT_COUNT; i++) { chi->smoothed_metal_mass_fraction[i] += mj * chj->metal_mass_fraction[i] * wi; chj->smoothed_metal_mass_fraction[i] += @@ -109,7 +109,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_chemistry( kernel_deval(ui, &wi, &wi_dx); /* Compute contribution to the smooth metallicity */ - for (int i = 0; i < chemistry_element_count; i++) { + for (int i = 0; i < GEAR_CHEMISTRY_ELEMENT_COUNT; i++) { chi->smoothed_metal_mass_fraction[i] += mj * chj->metal_mass_fraction[i] * wi; } diff --git a/src/chemistry/GEAR/chemistry_io.h b/src/chemistry/GEAR/chemistry_io.h index 008268657f..0943f369b1 100644 --- a/src/chemistry/GEAR/chemistry_io.h +++ b/src/chemistry/GEAR/chemistry_io.h @@ -27,19 +27,6 @@ #include "physical_constants.h" #include "units.h" -/** - * @brief Return a string containing the name of a given #chemistry_element. - */ -__attribute__((always_inline)) INLINE static const char* -chemistry_get_element_name(enum chemistry_element elem) { - - static const char* chemistry_element_names[chemistry_element_count] = { - "Oxygen", "Magnesium", "Sulfur", "Iron", "Zinc", - "Strontium", "Yttrium", "Barium", "Europium"}; - - return chemistry_element_names[elem]; -} - /** * @brief Specifies which particle fields to read from a dataset * @@ -53,12 +40,10 @@ INLINE static int chemistry_read_particles(struct part* parts, /* List what we want to read */ list[0] = io_make_input_field( - "ElementAbundance", FLOAT, chemistry_element_count, OPTIONAL, + "ElementAbundance", FLOAT, GEAR_CHEMISTRY_ELEMENT_COUNT, OPTIONAL, UNIT_CONV_NO_UNITS, parts, chemistry_data.metal_mass_fraction); - list[1] = io_make_input_field("Z", FLOAT, 1, OPTIONAL, UNIT_CONV_NO_UNITS, - parts, chemistry_data.Z); - return 2; + return 1; } /** @@ -73,21 +58,18 @@ INLINE static int chemistry_write_particles(const struct part* parts, struct io_props* list) { /* List what we want to write */ - list[0] = - io_make_output_field("SmoothedElementAbundances", FLOAT, - chemistry_element_count, UNIT_CONV_NO_UNITS, 0.f, - parts, chemistry_data.smoothed_metal_mass_fraction, - "Element abundances smoothed over the neighbors"); - - list[1] = io_make_output_field("Z", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, parts, - chemistry_data.Z, "Temporary field"); - - list[2] = io_make_output_field("ElementAbundances", FLOAT, - chemistry_element_count, UNIT_CONV_NO_UNITS, - 0.f, parts, chemistry_data.metal_mass_fraction, - "Mass fraction of each element"); + list[0] = io_make_output_field( + "SmoothedElementAbundances", FLOAT, GEAR_CHEMISTRY_ELEMENT_COUNT, + UNIT_CONV_NO_UNITS, 0.f, parts, + chemistry_data.smoothed_metal_mass_fraction, + "Element abundances smoothed over the neighbors"); + + list[1] = io_make_output_field( + "ElementAbundances", FLOAT, GEAR_CHEMISTRY_ELEMENT_COUNT, + UNIT_CONV_NO_UNITS, 0.f, parts, chemistry_data.metal_mass_fraction, + "Mass fraction of each element"); - return 3; + return 2; } /** @@ -102,21 +84,12 @@ INLINE static int chemistry_write_sparticles(const struct spart* sparts, struct io_props* list) { /* List what we want to write */ - list[0] = - io_make_output_field("SmoothedElementAbundances", FLOAT, - chemistry_element_count, UNIT_CONV_NO_UNITS, 0.f, - sparts, chemistry_data.smoothed_metal_mass_fraction, - "Element abundances smoothed over the neighbors"); - - list[1] = io_make_output_field("Z", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, sparts, - chemistry_data.Z, "Temporary field"); - - list[2] = io_make_output_field( - "ElementAbundance", FLOAT, chemistry_element_count, UNIT_CONV_NO_UNITS, - 0.f, sparts, chemistry_data.metal_mass_fraction, + list[0] = io_make_output_field( + "ElementAbundances", FLOAT, GEAR_CHEMISTRY_ELEMENT_COUNT, + UNIT_CONV_NO_UNITS, 0.f, sparts, chemistry_data.metal_mass_fraction, "Mass fraction of each element"); - return 3; + return 1; } /** @@ -143,12 +116,6 @@ INLINE static int chemistry_write_bparticles(const struct bpart* bparts, INLINE static void chemistry_write_flavour(hid_t h_grp) { io_write_attribute_s(h_grp, "Chemistry Model", "GEAR"); - for (enum chemistry_element i = chemistry_element_O; - i < chemistry_element_count; i++) { - char buffer[20]; - sprintf(buffer, "Element %d", (int)i); - io_write_attribute_s(h_grp, buffer, chemistry_get_element_name(i)); - } } #endif diff --git a/src/chemistry/GEAR/chemistry_struct.h b/src/chemistry/GEAR/chemistry_struct.h index 105b72d7fd..ae108bf271 100644 --- a/src/chemistry/GEAR/chemistry_struct.h +++ b/src/chemistry/GEAR/chemistry_struct.h @@ -19,43 +19,37 @@ #ifndef SWIFT_CHEMISTRY_STRUCT_GEAR_H #define SWIFT_CHEMISTRY_STRUCT_GEAR_H -/** - * @brief The individual elements traced in the model. - */ -enum chemistry_element { - chemistry_element_O = 0, - chemistry_element_Mg, - chemistry_element_S, - chemistry_element_Fe, - chemistry_element_Zn, - chemistry_element_Sr, - chemistry_element_Y, - chemistry_element_Ba, - chemistry_element_Eu, - chemistry_element_count -}; - /** * @brief Global chemical abundance information. */ struct chemistry_global_data { /* Initial metallicity Z */ - float initial_metallicity; + float initial_metallicities[GEAR_CHEMISTRY_ELEMENT_COUNT]; }; /** - * @brief Properties of the chemistry function. + * @brief Properties of the chemistry function for #part. */ struct chemistry_part_data { /*! Fraction of the particle mass in a given element */ - float metal_mass_fraction[chemistry_element_count]; + float metal_mass_fraction[GEAR_CHEMISTRY_ELEMENT_COUNT]; + + /*! Total mass of element in a particle */ + float metal_mass[GEAR_CHEMISTRY_ELEMENT_COUNT]; /*! Smoothed fraction of the particle mass in a given element */ - float smoothed_metal_mass_fraction[chemistry_element_count]; + float smoothed_metal_mass_fraction[GEAR_CHEMISTRY_ELEMENT_COUNT]; +}; - float Z; +/** + * @brief Properties of the chemistry function for #spart. + */ +struct chemistry_spart_data { + + /*! Fraction of the particle mass in a given element */ + float metal_mass_fraction[GEAR_CHEMISTRY_ELEMENT_COUNT]; }; /** diff --git a/src/common_io.c b/src/common_io.c index 67b011098c..f1238482f4 100644 --- a/src/common_io.c +++ b/src/common_io.c @@ -120,7 +120,7 @@ int io_is_double_precision(enum IO_DATA_TYPE type) { } /** - * @brief Reads an attribute from a given HDF5 group. + * @brief Reads an attribute (scalar) from a given HDF5 group. * * @param grp The group from which to read. * @param name The name of the attribute to read. @@ -220,6 +220,146 @@ void io_assert_valid_header_cosmology(hid_t h_grp, double a) { } } +/** + * @brief Reads the number of elements in a HDF5 attribute. + * + * @param attr The attribute from which to read. + * + * @return The number of elements. + * + * Calls #error() if an error occurs. + */ +hsize_t io_get_number_element_in_attribute(hid_t attr) { + /* Get the dataspace */ + hid_t space = H5Aget_space(attr); + if (space < 0) error("Failed to get data space"); + + /* Read the number of dimensions */ + const int ndims = H5Sget_simple_extent_ndims(space); + + /* Read the dimensions */ + hsize_t* dims = (hsize_t*)malloc(sizeof(hsize_t) * ndims); + H5Sget_simple_extent_dims(space, dims, NULL); + + /* Compute number of elements */ + hsize_t count = 1; + for (int i = 0; i < ndims; i++) { + count *= dims[i]; + } + + /* Cleanup */ + free(dims); + H5Sclose(space); + return count; +}; + +/** + * @brief Reads an attribute (array) from a given HDF5 group. + * @param data (output) The attribute read from the HDF5 group (need to be + * already allocated). + * @param number_element Number of elements in the attribute. + * + * Calls #error() if an error occurs. + */ +void io_read_array_attribute(hid_t grp, const char* name, + enum IO_DATA_TYPE type, void* data, + hsize_t number_element) { + + /* Open attribute */ + const hid_t h_attr = H5Aopen(grp, name, H5P_DEFAULT); + if (h_attr < 0) error("Error while opening attribute '%s'", name); + + /* Get the number of elements */ + hsize_t count = io_get_number_element_in_attribute(h_attr); + + /* Check if correct number of element */ + if (count != number_element) { + error( + "Error found a different number of elements than expected (%lli != " + "%lli) in attribute %s", + count, number_element, name); + } + + /* Read attribute */ + const hid_t h_err = H5Aread(h_attr, io_hdf5_type(type), data); + if (h_err < 0) error("Error while reading attribute '%s'", name); + + /* Cleanup */ + H5Aclose(h_attr); +} + +/** + * @brief Reads the number of elements in a HDF5 dataset. + * + * @param dataset The dataset from which to read. + * + * @return The number of elements. + * + * Calls #error() if an error occurs. + */ +hsize_t io_get_number_element_in_dataset(hid_t dataset) { + /* Get the dataspace */ + hid_t space = H5Dget_space(dataset); + if (space < 0) error("Failed to get data space"); + + /* Read the number of dimensions */ + const int ndims = H5Sget_simple_extent_ndims(space); + + /* Read the dimensions */ + hsize_t* dims = (hsize_t*)malloc(sizeof(hsize_t) * ndims); + H5Sget_simple_extent_dims(space, dims, NULL); + + /* Compute number of elements */ + hsize_t count = 1; + for (int i = 0; i < ndims; i++) { + count *= dims[i]; + } + + /* Cleanup */ + free(dims); + H5Sclose(space); + return count; +}; + +/** + * @brief Reads a dataset (array) from a given HDF5 group. + * + * @param grp The group from which to read. + * @param name The name of the dataset to read. + * @param type The #IO_DATA_TYPE of the attribute. + * @param data (output) The attribute read from the HDF5 group (need to be + * already allocated). + * @param number_element Number of elements in the attribute. + * + * Calls #error() if an error occurs. + */ +void io_read_array_dataset(hid_t grp, const char* name, enum IO_DATA_TYPE type, + void* data, hsize_t number_element) { + + /* Open dataset */ + const hid_t h_dataset = H5Dopen(grp, name, H5P_DEFAULT); + if (h_dataset < 0) error("Error while opening attribute '%s'", name); + + /* Get the number of elements */ + hsize_t count = io_get_number_element_in_dataset(h_dataset); + + /* Check if correct number of element */ + if (count != number_element) { + error( + "Error found a different number of elements than expected (%lli != " + "%lli) in dataset %s", + count, number_element, name); + } + + /* Read dataset */ + const hid_t h_err = H5Dread(h_dataset, io_hdf5_type(type), H5S_ALL, H5S_ALL, + H5P_DEFAULT, data); + if (h_err < 0) error("Error while reading dataset '%s'", name); + + /* Cleanup */ + H5Dclose(h_dataset); +} + /** * @brief Write an attribute to a given HDF5 group. * diff --git a/src/common_io.h b/src/common_io.h index 024d6027c5..3d160d6778 100644 --- a/src/common_io.h +++ b/src/common_io.h @@ -68,12 +68,19 @@ enum IO_DATA_TYPE { hid_t io_hdf5_type(enum IO_DATA_TYPE type); +hsize_t io_get_number_element_in_attribute(hid_t attr); +hsize_t io_get_number_element_in_dataset(hid_t dataset); void io_read_attribute(hid_t grp, const char* name, enum IO_DATA_TYPE type, void* data); void io_read_attribute_graceful(hid_t grp, const char* name, enum IO_DATA_TYPE type, void* data); void io_assert_valid_header_cosmology(hid_t h_grp, double a); +void io_read_array_attribute(hid_t grp, const char* name, + enum IO_DATA_TYPE type, void* data, + hsize_t number_element); +void io_read_array_dataset(hid_t grp, const char* name, enum IO_DATA_TYPE type, + void* data, hsize_t number_element); void io_write_attribute(hid_t grp, const char* name, enum IO_DATA_TYPE type, const void* data, int num); diff --git a/src/cooling/Compton/cooling.h b/src/cooling/Compton/cooling.h index 3e2c0ea8a3..a594cdca69 100644 --- a/src/cooling/Compton/cooling.h +++ b/src/cooling/Compton/cooling.h @@ -136,6 +136,7 @@ __attribute__((always_inline)) INLINE static double Compton_cooling_rate_cgs( * @param cooling The #cooling_function_data used in the run. * @param p Pointer to the particle data. * @param xp Pointer to the particle' extended data. + * @param time The current time. * @param dt The time-step of this particle. * @param dt_therm The time-step operator used for thermal quantities. */ @@ -146,8 +147,8 @@ __attribute__((always_inline)) INLINE static void cooling_cool_part( const struct hydro_props* hydro_props, const struct entropy_floor_properties* floor_props, const struct cooling_function_data* restrict cooling, - struct part* restrict p, struct xpart* restrict xp, const float dt, - const float dt_therm) { + struct part* restrict p, struct xpart* restrict xp, const double time, + const float dt, const float dt_therm) { /* Nothing to do here? */ if (dt == 0.) return; @@ -240,6 +241,7 @@ __attribute__((always_inline)) INLINE static float cooling_timestep( * @param phys_const The physical constants in internal units. * @param cooling The properties of the cooling function. * @param us The internal system of units. + * @param hydro_props The properties of the hydro scheme. * @param cosmo The current cosmological model. * @param p Pointer to the particle data. * @param xp Pointer to the extended particle data. @@ -247,6 +249,7 @@ __attribute__((always_inline)) INLINE static float cooling_timestep( __attribute__((always_inline)) INLINE static void cooling_first_init_part( const struct phys_const* restrict phys_const, const struct unit_system* restrict us, + const struct hydro_props* hydro_props, const struct cosmology* restrict cosmo, const struct cooling_function_data* restrict cooling, const struct part* restrict p, struct xpart* restrict xp) { diff --git a/src/cooling/EAGLE/cooling.c b/src/cooling/EAGLE/cooling.c index 694d429db0..3a6fb523b8 100644 --- a/src/cooling/EAGLE/cooling.c +++ b/src/cooling/EAGLE/cooling.c @@ -372,6 +372,8 @@ INLINE static double bisection_iter( * @param cooling The #cooling_function_data used in the run. * @param p Pointer to the particle data. * @param xp Pointer to the extended particle data. + * @param time The current time (since the Big Bang or start of the run) in + * internal units. * @param dt The cooling time-step of this particle. * @param dt_therm The hydro time-step of this particle. */ @@ -382,7 +384,8 @@ void cooling_cool_part(const struct phys_const *phys_const, const struct entropy_floor_properties *floor_props, const struct cooling_function_data *cooling, struct part *restrict p, struct xpart *restrict xp, - const float dt, const float dt_therm) { + const double time, const float dt, + const float dt_therm) { /* No cooling happens over zero time */ if (dt == 0.) return; @@ -548,6 +551,7 @@ __attribute__((always_inline)) INLINE float cooling_timestep( * * @param phys_const #phys_const data structure. * @param us The internal system of units. + * @param hydro_props The properties of the hydro scheme. * @param cosmo #cosmology data structure. * @param cooling #cooling_function_data struct. * @param p #part data. @@ -556,6 +560,7 @@ __attribute__((always_inline)) INLINE float cooling_timestep( __attribute__((always_inline)) INLINE void cooling_first_init_part( const struct phys_const *restrict phys_const, const struct unit_system *restrict us, + const struct hydro_props *hydro_props, const struct cosmology *restrict cosmo, const struct cooling_function_data *restrict cooling, const struct part *restrict p, struct xpart *restrict xp) { diff --git a/src/cooling/EAGLE/cooling.h b/src/cooling/EAGLE/cooling.h index c115a8ce3f..90c1c01e82 100644 --- a/src/cooling/EAGLE/cooling.h +++ b/src/cooling/EAGLE/cooling.h @@ -44,7 +44,7 @@ void cooling_cool_part(const struct phys_const *phys_const, const struct entropy_floor_properties *floor_props, const struct cooling_function_data *cooling, struct part *restrict p, struct xpart *restrict xp, - const float dt, const float dt_therm); + const double time, const float dt, const float dt_therm); float cooling_timestep(const struct cooling_function_data *restrict cooling, const struct phys_const *restrict phys_const, @@ -57,6 +57,7 @@ float cooling_timestep(const struct cooling_function_data *restrict cooling, void cooling_first_init_part( const struct phys_const *restrict phys_const, const struct unit_system *restrict us, + const struct hydro_props *hydro_props, const struct cosmology *restrict cosmo, const struct cooling_function_data *restrict cooling, const struct part *restrict p, struct xpart *restrict xp); diff --git a/src/cooling/const_du/cooling.h b/src/cooling/const_du/cooling.h index e78badfa85..de5164e594 100644 --- a/src/cooling/const_du/cooling.h +++ b/src/cooling/const_du/cooling.h @@ -76,6 +76,7 @@ INLINE static void cooling_update(const struct cosmology* cosmo, * @param cooling The #cooling_function_data used in the run. * @param p Pointer to the particle data. * @param xp Pointer to the extended particle data. + * @param time The current time. * @param dt The time-step of this particle. * @param dt_therm The time-step operator used for thermal quantities. */ @@ -86,8 +87,8 @@ __attribute__((always_inline)) INLINE static void cooling_cool_part( const struct hydro_props* hydro_props, const struct entropy_floor_properties* floor_props, const struct cooling_function_data* restrict cooling, - struct part* restrict p, struct xpart* restrict xp, const float dt, - const float dt_therm) { + struct part* restrict p, struct xpart* restrict xp, const double time, + const float dt, const float dt_therm) { /* Internal energy floor */ const float u_floor = cooling->min_energy; @@ -155,6 +156,7 @@ __attribute__((always_inline)) INLINE static float cooling_timestep( * @param p Pointer to the particle data. * @param xp Pointer to the extended particle data. * @param phys_const The physical constants in internal units. + * @param hydro_props The properties of the hydro scheme. * @param cooling The properties of the cooling function. * @param us The internal system of units. * @param cosmo The current cosmological model. @@ -162,6 +164,7 @@ __attribute__((always_inline)) INLINE static float cooling_timestep( __attribute__((always_inline)) INLINE static void cooling_first_init_part( const struct phys_const* restrict phys_const, const struct unit_system* restrict us, + const struct hydro_props* hydro_props, const struct cosmology* restrict cosmo, const struct cooling_function_data* restrict cooling, const struct part* restrict p, struct xpart* restrict xp) { diff --git a/src/cooling/const_lambda/cooling.h b/src/cooling/const_lambda/cooling.h index 7820a3a5e4..ef56b6e357 100644 --- a/src/cooling/const_lambda/cooling.h +++ b/src/cooling/const_lambda/cooling.h @@ -106,6 +106,7 @@ __attribute__((always_inline)) INLINE static double cooling_rate_cgs( * @param cooling The #cooling_function_data used in the run. * @param p Pointer to the particle data. * @param xp Pointer to the particle' extended data. + * @param time The current time. * @param dt The time-step of this particle. * @param dt_therm The time-step operator used for thermal quantities. */ @@ -116,8 +117,8 @@ __attribute__((always_inline)) INLINE static void cooling_cool_part( const struct hydro_props* hydro_props, const struct entropy_floor_properties* floor_props, const struct cooling_function_data* restrict cooling, - struct part* restrict p, struct xpart* restrict xp, const float dt, - const float dt_therm) { + struct part* restrict p, struct xpart* restrict xp, const double time, + const float dt, const float dt_therm) { /* Nothing to do here? */ if (dt == 0.) return; @@ -234,6 +235,7 @@ __attribute__((always_inline)) INLINE static float cooling_timestep( * @param phys_const The physical constants in internal units. * @param cooling The properties of the cooling function. * @param us The internal system of units. + * @param hydro_props The properties of the hydro scheme. * @param cosmo The current cosmological model. * @param p Pointer to the particle data. * @param xp Pointer to the extended particle data. @@ -241,6 +243,7 @@ __attribute__((always_inline)) INLINE static float cooling_timestep( __attribute__((always_inline)) INLINE static void cooling_first_init_part( const struct phys_const* restrict phys_const, const struct unit_system* restrict us, + const struct hydro_props* hydro_props, const struct cosmology* restrict cosmo, const struct cooling_function_data* restrict cooling, const struct part* restrict p, struct xpart* restrict xp) { diff --git a/src/cooling/grackle/cooling.c b/src/cooling/grackle/cooling.c new file mode 100644 index 0000000000..6b74b72aef --- /dev/null +++ b/src/cooling/grackle/cooling.c @@ -0,0 +1,1038 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@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 <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +/** + * @file src/cooling/grackle/cooling.c + * @brief Cooling using the GRACKLE 3.0 library. + */ + +#include "../config.h" + +/* Include header */ +#include "cooling.h" + +/* Some standard headers. */ +#include <fenv.h> +#include <float.h> +#include <math.h> + +/* The grackle library itself */ +#include <grackle.h> + +/* Local includes. */ +#include "chemistry.h" +#include "cooling_io.h" +#include "entropy_floor.h" +#include "error.h" +#include "hydro.h" +#include "parser.h" +#include "part.h" +#include "physical_constants.h" +#include "units.h" + +/* need to rework (and check) code if changed */ +#define GRACKLE_NPART 1 +#define GRACKLE_RANK 3 + +/** + * @brief Common operations performed on the cooling function at a + * given time-step or redshift. + * + * @param cosmo The current cosmological model. + * @param cooling The #cooling_function_data used in the run. + * @param s The #space containing all the particles. + */ +void cooling_update(const struct cosmology* cosmo, + struct cooling_function_data* cooling, struct space* s) { + /* set current time */ + if (cooling->redshift == -1) + cooling->units.a_value = cosmo->a; + else + cooling->units.a_value = 1. / (1. + cooling->redshift); +} + +/** + * @brief Print the chemical network + * + * @param xp The #xpart to print + */ +void cooling_print_fractions(const struct xpart* restrict xp) { + + const struct cooling_xpart_data tmp = xp->cooling_data; +#if COOLING_GRACKLE_MODE > 0 + message("HI %g, HII %g, HeI %g, HeII %g, HeIII %g, e %g", tmp.HI_frac, + tmp.HII_frac, tmp.HeI_frac, tmp.HeII_frac, tmp.HeIII_frac, + tmp.e_frac); +#endif + +#if COOLING_GRACKLE_MODE > 1 + message("HM %g, H2I %g, H2II %g", tmp.HM_frac, tmp.H2I_frac, tmp.H2II_frac); +#endif + +#if COOLING_GRACKLE_MODE > 2 + message("DI %g, DII %g, HDI %g", tmp.DI_frac, tmp.DII_frac, tmp.HDI_frac); +#endif + message("Metal: %g", tmp.metal_frac); +} + +/** + * @brief Check if the difference of a given field is lower than limit + * + * @param xp First #xpart + * @param old Second #xpart + * @param field The field to check + * @param limit Difference limit + * + * @return 0 if diff > limit + */ +#define cooling_check_field(xp, old, field, limit) \ + ({ \ + float tmp = xp->cooling_data.field - old->cooling_data.field; \ + tmp = fabs(tmp) / xp->cooling_data.field; \ + if (tmp > limit) return 0; \ + }) + +/** + * @brief Check if difference between two particles is lower than a given value + * + * @param xp One of the #xpart + * @param old The other #xpart + * @param limit The difference limit + */ +int cooling_converged(const struct xpart* restrict xp, + const struct xpart* restrict old, const float limit) { + +#if COOLING_GRACKLE_MODE > 0 + cooling_check_field(xp, old, HI_frac, limit); + cooling_check_field(xp, old, HII_frac, limit); + cooling_check_field(xp, old, HeI_frac, limit); + cooling_check_field(xp, old, HeII_frac, limit); + cooling_check_field(xp, old, HeIII_frac, limit); + cooling_check_field(xp, old, e_frac, limit); +#endif +#if COOLING_GRACKLE_MODE > 1 + cooling_check_field(xp, old, HM_frac, limit); + cooling_check_field(xp, old, H2I_frac, limit); + cooling_check_field(xp, old, H2II_frac, limit); +#endif + +#if COOLING_GRACKLE_MODE > 2 + cooling_check_field(xp, old, DI_frac, limit); + cooling_check_field(xp, old, DII_frac, limit); + cooling_check_field(xp, old, HDI_frac, limit); +#endif + + return 1; +} + +/** + * @brief Compute equilibrium fraction + * + * @param p Pointer to the particle data. + * @param xp Pointer to the extended particle data. + * @param cooling The properties of the cooling function. + */ +void cooling_compute_equilibrium( + const struct phys_const* restrict phys_const, + const struct unit_system* restrict us, + const struct hydro_props* hydro_properties, + const struct cosmology* restrict cosmo, + const struct cooling_function_data* restrict cooling, + const struct part* restrict p, struct xpart* restrict xp) { + + /* get temporary data */ + struct part p_tmp = *p; + struct cooling_function_data cooling_tmp = *cooling; + cooling_tmp.chemistry.with_radiative_cooling = 0; + /* need density for computation, therefore quick estimate */ + p_tmp.rho = 0.2387 * p_tmp.mass / pow(p_tmp.h, 3); + + /* compute time step */ + const double alpha = 0.01; + double dt = fabs(cooling_time(phys_const, us, hydro_properties, cosmo, + &cooling_tmp, &p_tmp, xp)); + cooling_new_energy(phys_const, us, cosmo, hydro_properties, &cooling_tmp, + &p_tmp, xp, dt); + dt = alpha * fabs(cooling_time(phys_const, us, hydro_properties, cosmo, + &cooling_tmp, &p_tmp, xp)); + + /* init simple variables */ + int step = 0; + const int max_step = cooling_tmp.max_step; + const float conv_limit = cooling_tmp.convergence_limit; + struct xpart old; + + do { + /* update variables */ + step += 1; + old = *xp; + + /* update chemistry */ + cooling_new_energy(phys_const, us, cosmo, hydro_properties, &cooling_tmp, + &p_tmp, xp, dt); + } while (step < max_step && !cooling_converged(xp, &old, conv_limit)); + + if (step == max_step) + error( + "A particle element fraction failed to converge." + "You can change 'GrackleCooling:MaxSteps' or " + "'GrackleCooling:ConvergenceLimit' to avoid this problem"); +} + +/** + * @brief Sets the cooling properties of the (x-)particles to a valid start + * state. + * + * @param p Pointer to the particle data. + * @param xp Pointer to the extended particle data. + * @param cooling The properties of the cooling function. + */ +void cooling_first_init_part(const struct phys_const* restrict phys_const, + const struct unit_system* restrict us, + const struct hydro_props* hydro_props, + const struct cosmology* restrict cosmo, + const struct cooling_function_data* cooling, + const struct part* restrict p, + struct xpart* restrict xp) { + + xp->cooling_data.radiated_energy = 0.f; + xp->cooling_data.time_last_event = -cooling->thermal_time; + +#if COOLING_GRACKLE_MODE >= 1 + gr_float zero = 1.e-20; + + /* primordial chemistry >= 1 */ + xp->cooling_data.HI_frac = zero; + xp->cooling_data.HII_frac = grackle_data->HydrogenFractionByMass; + xp->cooling_data.HeI_frac = 1. - grackle_data->HydrogenFractionByMass; + xp->cooling_data.HeII_frac = zero; + xp->cooling_data.HeIII_frac = zero; + xp->cooling_data.e_frac = xp->cooling_data.HII_frac + + 0.25 * xp->cooling_data.HeII_frac + + 0.5 * xp->cooling_data.HeIII_frac; +#endif // MODE >= 1 + +#if COOLING_GRACKLE_MODE >= 2 + /* primordial chemistry >= 2 */ + xp->cooling_data.HM_frac = zero; + xp->cooling_data.H2I_frac = zero; + xp->cooling_data.H2II_frac = zero; +#endif // MODE >= 2 + +#if COOLING_GRACKLE_MODE >= 3 + /* primordial chemistry >= 3 */ + xp->cooling_data.DI_frac = grackle_data->DeuteriumToHydrogenRatio * + grackle_data->HydrogenFractionByMass; + xp->cooling_data.DII_frac = zero; + xp->cooling_data.HDI_frac = zero; +#endif // MODE >= 3 + +#if COOLING_GRACKLE_MODE > 0 + cooling_compute_equilibrium(phys_const, us, hydro_props, cosmo, cooling, p, + xp); +#endif +} + +/** + * @brief Returns the total radiated energy by this particle. + * + * @param xp The extended particle data + */ +float cooling_get_radiated_energy(const struct xpart* restrict xp) { + + return xp->cooling_data.radiated_energy; +} + +/** + * @brief Prints the properties of the cooling model to stdout. + * + * @param cooling The properties of the cooling function. + */ +void cooling_print_backend(const struct cooling_function_data* cooling) { + + message("Cooling function is 'Grackle'."); + message("Using Grackle = %i", cooling->chemistry.use_grackle); + message("Chemical network = %i", cooling->chemistry.primordial_chemistry); + message("CloudyTable = %s", cooling->cloudy_table); + message("Redshift = %g", cooling->redshift); + message("UV background = %d", cooling->with_uv_background); + message("Metal cooling = %i", cooling->chemistry.metal_cooling); + message("Self Shielding = %i", cooling->self_shielding_method); + if (cooling->self_shielding_method == -1) { + message("Self Shelding density = %g", cooling->self_shielding_threshold); + } + message("Specific Heating Rates = %i", + cooling->provide_specific_heating_rates); + message("Volumetric Heating Rates = %i", + cooling->provide_volumetric_heating_rates); + message("Units:"); + message("\tComoving = %i", cooling->units.comoving_coordinates); + message("\tLength = %g", cooling->units.length_units); + message("\tDensity = %g", cooling->units.density_units); + message("\tTime = %g", cooling->units.time_units); + message("\tScale Factor = %g (units: %g)", cooling->units.a_value, + cooling->units.a_units); +} + +/** + * @brief copy a #xpart to the grackle data + * + * @param data The #grackle_field_data + * @param p The #part + * @param xp The #xpart + * @param rho Particle density + */ +#if COOLING_GRACKLE_MODE > 0 +void cooling_copy_to_grackle1(grackle_field_data* data, const struct part* p, + struct xpart* xp, gr_float rho) { + /* HI */ + xp->cooling_data.HI_frac *= rho; + data->HI_density = &xp->cooling_data.HI_frac; + /* HII */ + xp->cooling_data.HII_frac *= rho; + data->HII_density = &xp->cooling_data.HII_frac; + + /* HeI */ + xp->cooling_data.HeI_frac *= rho; + data->HeI_density = &xp->cooling_data.HeI_frac; + + /* HeII */ + xp->cooling_data.HeII_frac *= rho; + data->HeII_density = &xp->cooling_data.HeII_frac; + + /* HeIII */ + xp->cooling_data.HeIII_frac *= rho; + data->HeIII_density = &xp->cooling_data.HeIII_frac; + + /* HeII */ + xp->cooling_data.e_frac *= rho; + data->e_density = &xp->cooling_data.e_frac; +} +#else +void cooling_copy_to_grackle1(grackle_field_data* data, const struct part* p, + struct xpart* xp, gr_float rho) { + data->HI_density = NULL; + data->HII_density = NULL; + data->HeI_density = NULL; + data->HeII_density = NULL; + data->HeIII_density = NULL; + data->e_density = NULL; +} +#endif + +/** + * @brief copy a #xpart to the grackle data + * + * @param data The #grackle_field_data + * @param p The #part + * @param xp The #xpart + * @param rho Particle density + */ +#if COOLING_GRACKLE_MODE > 1 +void cooling_copy_to_grackle2(grackle_field_data* data, const struct part* p, + struct xpart* xp, gr_float rho) { + /* HM */ + xp->cooling_data.HM_frac *= rho; + data->HM_density = &xp->cooling_data.HM_frac; + + /* H2I */ + xp->cooling_data.H2I_frac *= rho; + data->H2I_density = &xp->cooling_data.H2I_frac; + + /* H2II */ + xp->cooling_data.H2II_frac *= rho; + data->H2II_density = &xp->cooling_data.H2II_frac; +} +#else +void cooling_copy_to_grackle2(grackle_field_data* data, const struct part* p, + struct xpart* xp, gr_float rho) { + data->HM_density = NULL; + data->H2I_density = NULL; + data->H2II_density = NULL; +} +#endif + +/** + * @brief copy a #xpart to the grackle data + * + * @param data The #grackle_field_data + * @param p The #part + * @param xp The #xpart + * @param rho Particle density + */ +#if COOLING_GRACKLE_MODE > 2 +void cooling_copy_to_grackle3(grackle_field_data* data, const struct part* p, + struct xpart* xp, gr_float rho) { + /* DI */ + xp->cooling_data.DI_frac *= rho; + data->DI_density = &xp->cooling_data.DI_frac; + + /* DII */ + xp->cooling_data.DII_frac *= rho; + data->DII_density = &xp->cooling_data.DII_frac; + + /* HDI */ + xp->cooling_data.HDI_frac *= rho; + data->HDI_density = &xp->cooling_data.HDI_frac; +} +#else +void cooling_copy_to_grackle3(grackle_field_data* data, const struct part* p, + struct xpart* xp, gr_float rho) { + data->DI_density = NULL; + data->DII_density = NULL; + data->HDI_density = NULL; +} +#endif + +/** + * @brief copy the grackle data to a #xpart + * + * @param data The #grackle_field_data + * @param p The #part + * @param xp The #xpart + * @param rho Particle density + */ +#if COOLING_GRACKLE_MODE > 0 +void cooling_copy_from_grackle1(grackle_field_data* data, const struct part* p, + struct xpart* xp, gr_float rho) { + + /* HI */ + xp->cooling_data.HI_frac = *data->HI_density / rho; + + /* HII */ + xp->cooling_data.HII_frac = *data->HII_density / rho; + + /* HeI */ + xp->cooling_data.HeI_frac = *data->HeI_density / rho; + + /* HeII */ + xp->cooling_data.HeII_frac = *data->HeII_density / rho; + + /* HeIII */ + xp->cooling_data.HeIII_frac = *data->HeIII_density / rho; + + /* e */ + xp->cooling_data.e_frac = *data->e_density / rho; +} +#else +void cooling_copy_from_grackle1(grackle_field_data* data, const struct part* p, + struct xpart* xp, gr_float rho) {} +#endif + +/** + * @brief copy the grackle data to a #xpart + * + * @param data The #grackle_field_data + * @param p The #part + * @param xp The #xpart + * @param rho Particle density + */ +#if COOLING_GRACKLE_MODE > 1 +void cooling_copy_from_grackle2(grackle_field_data* data, const struct part* p, + struct xpart* xp, gr_float rho) { + /* HM */ + xp->cooling_data.HM_frac = *data->HM_density / rho; + /* H2I */ + xp->cooling_data.H2I_frac = *data->H2I_density / rho; + /* H2II */ + xp->cooling_data.H2II_frac = *data->H2II_density / rho; +} +#else +void cooling_copy_from_grackle2(grackle_field_data* data, const struct part* p, + struct xpart* xp, gr_float rho) {} +#endif + +/** + * @brief copy the grackle data to a #xpart + * + * @param data The #grackle_field_data + * @param p The #part + * @param xp The #xpart + * @param rho Particle density + */ +#if COOLING_GRACKLE_MODE > 2 +void cooling_copy_from_grackle3(grackle_field_data* data, const struct part* p, + struct xpart* xp, gr_float rho) { + + /* DI */ + xp->cooling_data.DI_frac = *data->DI_density / rho; + + /* DII */ + xp->cooling_data.DII_frac = *data->DII_density / rho; + + /* HDI */ + xp->cooling_data.HDI_frac = *data->HDI_density / rho; +} +#else +void cooling_copy_from_grackle3(grackle_field_data* data, const struct part* p, + struct xpart* xp, gr_float rho) {} +#endif + +/** + * @brief copy a #xpart to the grackle data + * + * Warning this function creates some variable, therefore the grackle call + * should be in a block that still has the variables. + * + * @param data The #grackle_field_data + * @param p The #part + * @param xp The #xpart + * @param rho Particle density + */ +void cooling_copy_to_grackle(grackle_field_data* data, const struct part* p, + struct xpart* xp, gr_float rho) { + + cooling_copy_to_grackle1(data, p, xp, rho); + cooling_copy_to_grackle2(data, p, xp, rho); + cooling_copy_to_grackle3(data, p, xp, rho); + + data->volumetric_heating_rate = NULL; + data->specific_heating_rate = NULL; + data->RT_heating_rate = NULL; + data->RT_HI_ionization_rate = NULL; + data->RT_HeI_ionization_rate = NULL; + data->RT_HeII_ionization_rate = NULL; + data->RT_H2_dissociation_rate = NULL; + + gr_float* metal_density = (gr_float*)malloc(sizeof(gr_float)); + *metal_density = chemistry_get_total_metal_mass_fraction_for_cooling(p) * rho; + data->metal_density = metal_density; +} + +/** + * @brief copy a #xpart to the grackle data + * + * Warning this function creates some variable, therefore the grackle call + * should be in a block that still has the variables. + * + * @param data The #grackle_field_data + * @param p The #part + * @param xp The #xpart + * @param rho Particle density + */ +void cooling_copy_from_grackle(grackle_field_data* data, const struct part* p, + struct xpart* xp, gr_float rho) { + cooling_copy_from_grackle1(data, p, xp, rho); + cooling_copy_from_grackle2(data, p, xp, rho); + cooling_copy_from_grackle3(data, p, xp, rho); + + free(data->metal_density); +} + +/** + * @brief Apply the self shielding (if needed) by turning on/off the UV + * background. + * + * @param cooling The #cooling_function_data used in the run. + * @param chemistry The #chemistry_data from grackle. + * @param p Pointer to the particle data. + * + */ +void cooling_apply_self_shielding( + const struct cooling_function_data* restrict cooling, + chemistry_data* restrict chemistry, const struct part* restrict p, + const struct cosmology* cosmo) { + + /* Are we using self shielding or UV background? */ + if (!cooling->with_uv_background || cooling->self_shielding_method >= 0) { + return; + } + + /* Are we in a self shielding regime? */ + const float rho = hydro_get_physical_density(p, cosmo); + if (rho > cooling->self_shielding_threshold) { + chemistry->UVbackground = 0; + } else { + chemistry->UVbackground = 1; + } +} + +/** + * @brief Compute the energy of a particle after dt and update the particle + * chemistry data + * + * @param phys_const The physical constants in internal units. + * @param us The internal system of units. + * @param cooling The #cooling_function_data used in the run. + * @param p Pointer to the particle data. + * @param xp Pointer to the particle extra data + * @param dt The time-step of this particle. + * + * @return du / dt + */ +gr_float cooling_new_energy( + const struct phys_const* restrict phys_const, + const struct unit_system* restrict us, + const struct cosmology* restrict cosmo, + const struct hydro_props* hydro_props, + const struct cooling_function_data* restrict cooling, + const struct part* restrict p, struct xpart* restrict xp, double dt) { + + /* set current time */ + code_units units = cooling->units; + chemistry_data chemistry_grackle = cooling->chemistry; + + /* initialize data */ + grackle_field_data data; + + /* set values */ + /* grid */ + int grid_dimension[GRACKLE_RANK] = {GRACKLE_NPART, 1, 1}; + int grid_start[GRACKLE_RANK] = {0, 0, 0}; + int grid_end[GRACKLE_RANK] = {GRACKLE_NPART - 1, 0, 0}; + + data.grid_dx = 0.; + data.grid_rank = GRACKLE_RANK; + data.grid_dimension = grid_dimension; + data.grid_start = grid_start; + data.grid_end = grid_end; + + /* general particle data */ + gr_float density = hydro_get_physical_density(p, cosmo); + gr_float energy = hydro_get_physical_internal_energy(p, xp, cosmo) + + dt * hydro_get_physical_internal_energy_dt(p, cosmo); + + /* We now need to check that we are not going to go below any of the limits */ + const double u_minimal = hydro_props->minimal_internal_energy; + energy = max(energy, u_minimal); + + /* initialize density */ + data.density = &density; + + /* initialize energy */ + data.internal_energy = &energy; + + /* grackle 3.0 doc: "Currently not used" */ + data.x_velocity = NULL; + data.y_velocity = NULL; + data.z_velocity = NULL; + + /* copy to grackle structure */ + cooling_copy_to_grackle(&data, p, xp, density); + + /* Apply the self shielding if requested */ + cooling_apply_self_shielding(cooling, &chemistry_grackle, p, cosmo); + + /* solve chemistry */ + if (local_solve_chemistry(&chemistry_grackle, &grackle_rates, &units, &data, + dt) == 0) { + error("Error in solve_chemistry."); + } + + /* copy from grackle data to particle */ + cooling_copy_from_grackle(&data, p, xp, density); + + return energy; +} + +/** + * @brief Compute the cooling time + * + * @param cooling The #cooling_function_data used in the run. + * @param p Pointer to the particle data. + * @param xp Pointer to the particle extra data + * + * @return cooling time + */ +gr_float cooling_time(const struct phys_const* restrict phys_const, + const struct unit_system* restrict us, + const struct hydro_props* hydro_props, + const struct cosmology* restrict cosmo, + const struct cooling_function_data* restrict cooling, + const struct part* restrict p, + struct xpart* restrict xp) { + + error("TODO: use energy after adiabatic cooling"); + + /* set current time */ + code_units units = cooling->units; + + /* initialize data */ + grackle_field_data data; + chemistry_data chemistry_grackle = cooling->chemistry; + + /* set values */ + /* grid */ + int grid_dimension[GRACKLE_RANK] = {GRACKLE_NPART, 1, 1}; + int grid_start[GRACKLE_RANK] = {0, 0, 0}; + int grid_end[GRACKLE_RANK] = {GRACKLE_NPART - 1, 0, 0}; + + data.grid_rank = GRACKLE_RANK; + data.grid_dimension = grid_dimension; + data.grid_start = grid_start; + data.grid_end = grid_end; + + /* general particle data */ + gr_float density = hydro_get_physical_density(p, cosmo); + gr_float energy = hydro_get_physical_internal_energy(p, xp, cosmo); + + /* initialize density */ + data.density = &density; + + /* initialize energy */ + data.internal_energy = &energy; + + /* grackle 3.0 doc: "Currently not used" */ + data.x_velocity = NULL; + data.y_velocity = NULL; + data.z_velocity = NULL; + + /* copy data from particle to grackle data */ + cooling_copy_to_grackle(&data, p, xp, density); + + /* Apply the self shielding if requested */ + cooling_apply_self_shielding(cooling, &chemistry_grackle, p, cosmo); + + /* Compute cooling time */ + gr_float cooling_time; + chemistry_data_storage chemistry_rates = grackle_rates; + if (local_calculate_cooling_time(&chemistry_grackle, &chemistry_rates, &units, + &data, &cooling_time) == 0) { + error("Error in calculate_cooling_time."); + } + + /* copy from grackle data to particle */ + cooling_copy_from_grackle(&data, p, xp, density); + + /* compute rate */ + return cooling_time; +} + +/** + * @brief Apply the cooling to a particle. + * + * Depending on the task order, you may wish to either + * cool down the particle immediately or do it during the drift. + * + * @param p Pointer to the particle data. + * @param xp Pointer to the xparticle data. + * @param cosmo The current cosmological model. + * @param cooling_du_dt Time derivative of the cooling. + * @param u_new Internal energy after the cooling. + */ +void cooling_apply(struct part* restrict p, struct xpart* restrict xp, + const struct cosmology* restrict cosmo, float cooling_du_dt, + gr_float u_new) { + +#ifdef TASK_ORDER_GEAR + /* Cannot use du / dt as it will be erased before being used */ + hydro_set_physical_internal_energy(p, xp, cosmo, u_new); + hydro_set_drifted_physical_internal_energy(p, cosmo, u_new); +#else + hydro_set_physical_internal_energy_dt(p, cosmo, cooling_du_dt); +#endif +} + +/** + * @brief Apply the cooling function to a particle. + * + * @param phys_const The physical constants in internal units. + * @param us The internal system of units. + * @param cosmo The current cosmological model. + * @param floor_props Properties of the entropy floor. + * @param cooling The #cooling_function_data used in the run. + * @param p Pointer to the particle data. + * @param xp Pointer to the particle' extended data. + * @param time The current time. + * @param dt The time-step of this particle. + * @param dt_therm The time-step operator used for thermal quantities. + */ +void cooling_cool_part(const struct phys_const* restrict phys_const, + const struct unit_system* restrict us, + const struct cosmology* restrict cosmo, + const struct hydro_props* hydro_props, + const struct entropy_floor_properties* floor_props, + const struct cooling_function_data* restrict cooling, + struct part* restrict p, struct xpart* restrict xp, + const double time, const double dt, + const double dt_therm) { + + /* Nothing to do here? */ + if (dt == 0.) return; + + /* Is the cooling turn off */ + if (time - xp->cooling_data.time_last_event < cooling->thermal_time) { + return; + } + + /* Get the change in internal energy due to hydro forces */ + const float hydro_du_dt = hydro_get_physical_internal_energy_dt(p, cosmo); + + /* Current energy */ + const float u_old = hydro_get_physical_internal_energy(p, xp, cosmo); + + /* Calculate energy after dt */ + gr_float u_new = cooling_new_energy(phys_const, us, cosmo, hydro_props, + cooling, p, xp, dt); + + /* We now need to check that we are not going to go below any of the limits */ + const double u_minimal = hydro_props->minimal_internal_energy; + u_new = max(u_new, u_minimal); + + /* Expected change in energy over the next kick step + (assuming no change in dt) */ + const double delta_u = u_new - u_old; + + /* Turn this into a rate of change (including cosmology term) */ + const float cooling_du_dt = delta_u / dt_therm; + + /* Update the internal energy time derivative */ + cooling_apply(p, xp, cosmo, cooling_du_dt, u_new); + + /* Store the radiated energy */ + xp->cooling_data.radiated_energy -= + hydro_get_mass(p) * (cooling_du_dt - hydro_du_dt) * dt; +} + +/** + * @brief Compute the temperature of a #part based on the cooling function. + * + * @param phys_const #phys_const data structure. + * @param hydro_props The properties of the hydro scheme. + * @param us The internal system of units. + * @param cosmo #cosmology data structure. + * @param cooling #cooling_function_data struct. + * @param p #part data. + * @param xp Pointer to the #xpart data. + */ +float cooling_get_temperature( + const struct phys_const* restrict phys_const, + const struct hydro_props* restrict hydro_props, + const struct unit_system* restrict us, + const struct cosmology* restrict cosmo, + const struct cooling_function_data* restrict cooling, + const struct part* restrict p, const struct xpart* restrict xp) { + // TODO use the grackle library + + /* Physical constants */ + const double m_H = phys_const->const_proton_mass; + const double k_B = phys_const->const_boltzmann_k; + + /* Gas properties */ + const double T_transition = hydro_props->hydrogen_ionization_temperature; + const double mu_neutral = hydro_props->mu_neutral; + const double mu_ionised = hydro_props->mu_ionised; + + /* Particle temperature */ + const double u = hydro_get_drifted_physical_internal_energy(p, cosmo); + + /* Temperature over mean molecular weight */ + const double T_over_mu = hydro_gamma_minus_one * u * m_H / k_B; + + /* Are we above or below the HII -> HI transition? */ + if (T_over_mu > (T_transition + 1.) / mu_ionised) + return T_over_mu * mu_ionised; + else if (T_over_mu < (T_transition - 1.) / mu_neutral) + return T_over_mu * mu_neutral; + else + return T_transition; +} + +/** + * @brief Computes the cooling time-step. + * + * We return FLT_MAX so as to impose no limit on the time-step. + * + * @param cooling The #cooling_function_data used in the run. + * @param phys_const The physical constants in internal units. + * @param cosmo The current cosmological model. + * @param us The internal system of units. + * @param p Pointer to the particle data. + */ +float cooling_timestep(const struct cooling_function_data* restrict cooling, + const struct phys_const* restrict phys_const, + const struct cosmology* restrict cosmo, + const struct unit_system* restrict us, + const struct hydro_props* hydro_props, + const struct part* restrict p, + const struct xpart* restrict xp) { + + return FLT_MAX; +} + +/** + * @brief Split the coolong content of a particle into n pieces + * + * @param p The #part. + * @param xp The #xpart. + * @param n The number of pieces to split into. + */ +void cooling_split_part(struct part* p, struct xpart* xp, double n) { + + error("Loic: to be implemented"); +} + +/** + * @brief Initialises the cooling unit system. + * + * @param us The current internal system of units. + * @param cooling The cooling properties to initialize + */ +void cooling_init_units(const struct unit_system* us, + const struct phys_const* phys_const, + struct cooling_function_data* cooling) { + + /* These are conversions from code units to cgs. */ + + /* first cosmo */ + cooling->units.a_units = 1.0; // units for the expansion factor + cooling->units.a_value = 1.0; + + /* We assume here all physical quantities to + be in proper coordinate (not comobile) */ + cooling->units.comoving_coordinates = 0; + + /* then units */ + cooling->units.density_units = + units_cgs_conversion_factor(us, UNIT_CONV_DENSITY); + cooling->units.length_units = + units_cgs_conversion_factor(us, UNIT_CONV_LENGTH); + cooling->units.time_units = units_cgs_conversion_factor(us, UNIT_CONV_TIME); + cooling->units.velocity_units = + units_cgs_conversion_factor(us, UNIT_CONV_VELOCITY); + + /* Self shielding */ + if (cooling->self_shielding_method == -1) { + cooling->self_shielding_threshold *= + phys_const->const_proton_mass * + pow(units_cgs_conversion_factor(us, UNIT_CONV_LENGTH), 3.); + } +} + +/** + * @brief Initialises Grackle. + * + * @param cooling The cooling properties to initialize + */ +void cooling_init_grackle(struct cooling_function_data* cooling) { + +#ifdef SWIFT_DEBUG_CHECKS + /* enable verbose for grackle */ + grackle_verbose = 1; +#endif + + chemistry_data* chemistry = &cooling->chemistry; + + /* Create a chemistry object for parameters and rate data. */ + if (set_default_chemistry_parameters(chemistry) == 0) { + error("Error in set_default_chemistry_parameters."); + } + + // Set parameter values for chemistry. + chemistry->use_grackle = 1; + chemistry->with_radiative_cooling = 1; + + /* molecular network with H, He, D + From Cloudy table */ + chemistry->primordial_chemistry = cooling->primordial_chemistry; + chemistry->metal_cooling = cooling->with_metal_cooling; + chemistry->UVbackground = cooling->with_uv_background; + chemistry->grackle_data_file = cooling->cloudy_table; + + /* radiative transfer */ + chemistry->use_radiative_transfer = cooling->provide_specific_heating_rates || + cooling->provide_volumetric_heating_rates; + chemistry->use_volumetric_heating_rate = + cooling->provide_volumetric_heating_rates; + chemistry->use_specific_heating_rate = + cooling->provide_specific_heating_rates; + + if (cooling->provide_specific_heating_rates && + cooling->provide_volumetric_heating_rates) + message( + "WARNING: You should specified either the specific or the volumetric " + "heating rates, not both"); + + /* self shielding */ + chemistry->self_shielding_method = cooling->self_shielding_method; + + /* Initialize the chemistry object. */ + if (initialize_chemistry_data(&cooling->units) == 0) { + error("Error in initialize_chemistry_data."); + } +} + +/** + * @brief Initialises the cooling properties. + * + * @param parameter_file The parsed parameter file. + * @param us The current internal system of units. + * @param phys_const The physical constants in internal units. + * @param hydro_props The properties of the hydro scheme. + * @param cooling The cooling properties to initialize + */ +void cooling_init_backend(struct swift_params* parameter_file, + const struct unit_system* us, + const struct phys_const* phys_const, + const struct hydro_props* hydro_props, + struct cooling_function_data* cooling) { + + if (GRACKLE_NPART != 1) + error("Grackle with multiple particles not implemented"); + + /* read parameters */ + cooling_read_parameters(parameter_file, cooling, phys_const); + + /* Set up the units system. */ + cooling_init_units(us, phys_const, cooling); + + /* Set up grackle */ + cooling_init_grackle(cooling); +} + +/** + * @brief Clean-up the memory allocated for the cooling routines + * + * @param cooling the cooling data structure. + */ +void cooling_clean(struct cooling_function_data* cooling) { + _free_chemistry_data(&cooling->chemistry, &grackle_rates); +} + +/** + * @brief Write a cooling struct to the given FILE as a stream of bytes. + * + * Nothing to do beyond writing the structure from the stream. + * + * @param cooling the struct + * @param stream the file stream + */ +void cooling_struct_dump(const struct cooling_function_data* cooling, + FILE* stream) { + restart_write_blocks((void*)cooling, sizeof(struct cooling_function_data), 1, + stream, "cooling", "cooling function"); +} + +/** + * @brief Restore a hydro_props struct from the given FILE as a stream of + * bytes. + * + * Nothing to do beyond reading the structure from the stream. + * + * @param cooling the struct + * @param stream the file stream + * @param cosmo #cosmology structure + */ +void cooling_struct_restore(struct cooling_function_data* cooling, FILE* stream, + const struct cosmology* cosmo) { + restart_read_blocks((void*)cooling, sizeof(struct cooling_function_data), 1, + stream, NULL, "cooling function"); + + /* Set up grackle */ + cooling_init_grackle(cooling); +} diff --git a/src/cooling/grackle/cooling.h b/src/cooling/grackle/cooling.h index 2b646aedfe..209b986c6f 100644 --- a/src/cooling/grackle/cooling.h +++ b/src/cooling/grackle/cooling.h @@ -25,6 +25,7 @@ */ /* Some standard headers. */ +#include <fenv.h> #include <float.h> #include <math.h> @@ -46,888 +47,108 @@ #define GRACKLE_NPART 1 #define GRACKLE_RANK 3 -/* prototype */ -static gr_float cooling_time( +void cooling_update(const struct cosmology* cosmo, + struct cooling_function_data* cooling, struct space* s); +void cooling_print_fractions(const struct xpart* restrict xp); +int cooling_converged(const struct xpart* restrict xp, + const struct xpart* restrict old, const float limit); +void cooling_compute_equilibrium( const struct phys_const* restrict phys_const, const struct unit_system* restrict us, + const struct hydro_props* hydro_properties, const struct cosmology* restrict cosmo, const struct cooling_function_data* restrict cooling, const struct part* restrict p, struct xpart* restrict xp); -static gr_float cooling_new_energy( - const struct phys_const* restrict phys_const, - const struct unit_system* restrict us, - const struct cosmology* restrict cosmo, +void cooling_first_init_part(const struct phys_const* restrict phys_const, + const struct unit_system* restrict us, + const struct hydro_props* hydro_properties, + const struct cosmology* restrict cosmo, + const struct cooling_function_data* cooling, + const struct part* restrict p, + struct xpart* restrict xp); + +float cooling_get_radiated_energy(const struct xpart* restrict xp); +void cooling_print_backend(const struct cooling_function_data* cooling); + +void cooling_copy_to_grackle1(grackle_field_data* data, const struct part* p, + struct xpart* xp, gr_float rho); +void cooling_copy_to_grackle2(grackle_field_data* data, const struct part* p, + struct xpart* xp, gr_float rho); +void cooling_copy_to_grackle3(grackle_field_data* data, const struct part* p, + struct xpart* xp, gr_float rho); +void cooling_copy_from_grackle1(grackle_field_data* data, const struct part* p, + struct xpart* xp, gr_float rho); +void cooling_copy_from_grackle2(grackle_field_data* data, const struct part* p, + struct xpart* xp, gr_float rho); +void cooling_copy_from_grackle3(grackle_field_data* data, const struct part* p, + struct xpart* xp, gr_float rho); +void cooling_copy_to_grackle(grackle_field_data* data, const struct part* p, + struct xpart* xp, gr_float rho); +void cooling_copy_from_grackle(grackle_field_data* data, const struct part* p, + struct xpart* xp, gr_float rho); +void cooling_apply_self_shielding( const struct cooling_function_data* restrict cooling, - const struct part* restrict p, struct xpart* restrict xp, double dt); - -/** - * @brief Common operations performed on the cooling function at a - * given time-step or redshift. - * - * @param cosmo The current cosmological model. - * @param cooling The #cooling_function_data used in the run. - * @param s The #space containing all the particles. - */ -INLINE static void cooling_update(const struct cosmology* cosmo, - struct cooling_function_data* cooling, - struct space* s) { - /* set current time */ - if (cooling->redshift == -1) - cooling->units.a_value = cosmo->a; - else - cooling->units.a_value = 1. / (1. + cooling->redshift); -} - -/** - * @brief Print the chemical network - * - * @param xp The #xpart to print - */ -__attribute__((always_inline)) INLINE static void cooling_print_fractions( - const struct xpart* restrict xp) { - - const struct cooling_xpart_data tmp = xp->cooling_data; -#if COOLING_GRACKLE_MODE > 0 - message("HI %g, HII %g, HeI %g, HeII %g, HeIII %g, e %g", tmp.HI_frac, - tmp.HII_frac, tmp.HeI_frac, tmp.HeII_frac, tmp.HeIII_frac, - tmp.e_frac); -#endif - -#if COOLING_GRACKLE_MODE > 1 - message("HM %g, H2I %g, H2II %g", tmp.HM_frac, tmp.H2I_frac, tmp.H2II_frac); -#endif - -#if COOLING_GRACKLE_MODE > 2 - message("DI %g, DII %g, HDI %g", tmp.DI_frac, tmp.DII_frac, tmp.HDI_frac); -#endif - message("Metal: %g", tmp.metal_frac); -} - -/** - * @brief Check if the difference of a given field is lower than limit - * - * @param xp First #xpart - * @param old Second #xpart - * @param field The field to check - * @param limit Difference limit - * - * @return 0 if diff > limit - */ -#define cooling_check_field(xp, old, field, limit) \ - ({ \ - float tmp = xp->cooling_data.field - old->cooling_data.field; \ - tmp = fabs(tmp) / xp->cooling_data.field; \ - if (tmp > limit) return 0; \ - }) - -/** - * @brief Check if difference between two particles is lower than a given value - * - * @param xp One of the #xpart - * @param old The other #xpart - * @param limit The difference limit - */ -__attribute__((always_inline)) INLINE static int cooling_converged( - const struct xpart* restrict xp, const struct xpart* restrict old, - const float limit) { - -#if COOLING_GRACKLE_MODE > 0 - cooling_check_field(xp, old, HI_frac, limit); - cooling_check_field(xp, old, HII_frac, limit); - cooling_check_field(xp, old, HeI_frac, limit); - cooling_check_field(xp, old, HeII_frac, limit); - cooling_check_field(xp, old, HeIII_frac, limit); - cooling_check_field(xp, old, e_frac, limit); -#endif -#if COOLING_GRACKLE_MODE > 1 - cooling_check_field(xp, old, HM_frac, limit); - cooling_check_field(xp, old, H2I_frac, limit); - cooling_check_field(xp, old, H2II_frac, limit); -#endif - -#if COOLING_GRACKLE_MODE > 2 - cooling_check_field(xp, old, DI_frac, limit); - cooling_check_field(xp, old, DII_frac, limit); - cooling_check_field(xp, old, HDI_frac, limit); -#endif - - return 1; -} - -/** - * @brief Compute equilibrium fraction - * - * @param p Pointer to the particle data. - * @param xp Pointer to the extended particle data. - * @param cooling The properties of the cooling function. - */ -__attribute__((always_inline)) INLINE static void cooling_compute_equilibrium( + chemistry_data* restrict chemistry, const struct part* restrict p, + const struct cosmology* cosmo); +gr_float cooling_new_energy( const struct phys_const* restrict phys_const, const struct unit_system* restrict us, const struct cosmology* restrict cosmo, + const struct hydro_props* hydro_properties, const struct cooling_function_data* restrict cooling, - const struct part* restrict p, struct xpart* restrict xp) { - - /* get temporary data */ - struct part p_tmp = *p; - struct cooling_function_data cooling_tmp = *cooling; - cooling_tmp.chemistry.with_radiative_cooling = 0; - /* need density for computation, therefore quick estimate */ - p_tmp.rho = 0.2387 * p_tmp.mass / pow(p_tmp.h, 3); - - /* compute time step */ - const double alpha = 0.01; - double dt = - fabs(cooling_time(phys_const, us, cosmo, &cooling_tmp, &p_tmp, xp)); - cooling_new_energy(phys_const, us, cosmo, &cooling_tmp, &p_tmp, xp, dt); - dt = alpha * - fabs(cooling_time(phys_const, us, cosmo, &cooling_tmp, &p_tmp, xp)); - - /* init simple variables */ - int step = 0; - const int max_step = cooling_tmp.max_step; - const float conv_limit = cooling_tmp.convergence_limit; - struct xpart old; - - do { - /* update variables */ - step += 1; - old = *xp; - - /* update chemistry */ - cooling_new_energy(phys_const, us, cosmo, &cooling_tmp, &p_tmp, xp, dt); - } while (step < max_step && !cooling_converged(xp, &old, conv_limit)); - - if (step == max_step) - error( - "A particle element fraction failed to converge." - "You can change 'GrackleCooling:MaxSteps' or " - "'GrackleCooling:ConvergenceLimit' to avoid this problem"); -} - -/** - * @brief Sets the cooling properties of the (x-)particles to a valid start - * state. - * - * @param p Pointer to the particle data. - * @param xp Pointer to the extended particle data. - * @param cooling The properties of the cooling function. - */ -__attribute__((always_inline)) INLINE static void cooling_first_init_part( - const struct phys_const* restrict phys_const, - const struct unit_system* restrict us, - const struct cosmology* restrict cosmo, - const struct cooling_function_data* cooling, const struct part* restrict p, - struct xpart* restrict xp) { - - xp->cooling_data.radiated_energy = 0.f; - -#if COOLING_GRACKLE_MODE >= 1 - gr_float zero = 1.e-20; - - /* primordial chemistry >= 1 */ - xp->cooling_data.HI_frac = zero; - xp->cooling_data.HII_frac = grackle_data->HydrogenFractionByMass; - xp->cooling_data.HeI_frac = 1. - grackle_data->HydrogenFractionByMass; - xp->cooling_data.HeII_frac = zero; - xp->cooling_data.HeIII_frac = zero; - xp->cooling_data.e_frac = xp->cooling_data.HII_frac + - 0.25 * xp->cooling_data.HeII_frac + - 0.5 * xp->cooling_data.HeIII_frac; -#endif // MODE >= 1 - -#if COOLING_GRACKLE_MODE >= 2 - /* primordial chemistry >= 2 */ - xp->cooling_data.HM_frac = zero; - xp->cooling_data.H2I_frac = zero; - xp->cooling_data.H2II_frac = zero; -#endif // MODE >= 2 - -#if COOLING_GRACKLE_MODE >= 3 - /* primordial chemistry >= 3 */ - xp->cooling_data.DI_frac = grackle_data->DeuteriumToHydrogenRatio * - grackle_data->HydrogenFractionByMass; - xp->cooling_data.DII_frac = zero; - xp->cooling_data.HDI_frac = zero; -#endif // MODE >= 3 - -#if COOLING_GRACKLE_MODE > 0 - cooling_compute_equilibrium(phys_const, us, cosmo, cooling, p, xp); -#endif -} - -/** - * @brief Returns the total radiated energy by this particle. - * - * @param xp The extended particle data - */ -__attribute__((always_inline)) INLINE static float cooling_get_radiated_energy( - const struct xpart* restrict xp) { - - return xp->cooling_data.radiated_energy; -} - -/** - * @brief Prints the properties of the cooling model to stdout. - * - * @param cooling The properties of the cooling function. - */ -__attribute__((always_inline)) INLINE static void cooling_print_backend( - const struct cooling_function_data* cooling) { - - message("Cooling function is 'Grackle'."); - message("Using Grackle = %i", cooling->chemistry.use_grackle); - message("Chemical network = %i", cooling->chemistry.primordial_chemistry); - message("CloudyTable = %s", cooling->cloudy_table); - message("Redshift = %g", cooling->redshift); - message("UV background = %d", cooling->with_uv_background); - message("Metal cooling = %i", cooling->chemistry.metal_cooling); - message("Self Shielding = %i", cooling->self_shielding_method); - message("Specific Heating Rates = %i", - cooling->provide_specific_heating_rates); - message("Volumetric Heating Rates = %i", - cooling->provide_volumetric_heating_rates); - message("Units:"); - message("\tComoving = %i", cooling->units.comoving_coordinates); - message("\tLength = %g", cooling->units.length_units); - message("\tDensity = %g", cooling->units.density_units); - message("\tTime = %g", cooling->units.time_units); - message("\tScale Factor = %g (units: %g)", cooling->units.a_value, - cooling->units.a_units); -} - -/** - * @brief copy a single field from the grackle data to a #xpart - * - * @param data The #grackle_field_data - * @param xp The #xpart - * @param rho Particle density - * @param field The field to copy - */ -#define cooling_copy_field_from_grackle(data, xp, rho, field) \ - xp->cooling_data.field##_frac = *data.field##_density / rho; - -/** - * @brief copy a single field from a #xpart to the grackle data - * - * @param data The #grackle_field_data - * @param xp The #xpart - * @param rho Particle density - * @param field The field to copy - */ -#define cooling_copy_field_to_grackle(data, xp, rho, field) \ - gr_float grackle_##field = xp->cooling_data.field##_frac * rho; \ - data.field##_density = &grackle_##field; - -/** - * @brief copy a #xpart to the grackle data - * - * Warning this function creates some variable, therefore the grackle call - * should be in a block that still has the variables. - * - * @param data The #grackle_field_data - * @param p The #part - * @param xp The #xpart - * @param rho Particle density - */ -#if COOLING_GRACKLE_MODE > 0 -#define cooling_copy_to_grackle1(data, p, xp, rho) \ - cooling_copy_field_to_grackle(data, xp, rho, HI); \ - cooling_copy_field_to_grackle(data, xp, rho, HII); \ - cooling_copy_field_to_grackle(data, xp, rho, HeI); \ - cooling_copy_field_to_grackle(data, xp, rho, HeII); \ - cooling_copy_field_to_grackle(data, xp, rho, HeIII); \ - cooling_copy_field_to_grackle(data, xp, rho, e); -#else -#define cooling_copy_to_grackle1(data, p, xp, rho) \ - data.HI_density = NULL; \ - data.HII_density = NULL; \ - data.HeI_density = NULL; \ - data.HeII_density = NULL; \ - data.HeIII_density = NULL; \ - data.e_density = NULL; -#endif - -/** - * @brief copy a #xpart to the grackle data - * - * Warning this function creates some variable, therefore the grackle call - * should be in a block that still has the variables. - * - * @param data The #grackle_field_data - * @param p The #part - * @param xp The #xpart - * @param rho Particle density - */ -#if COOLING_GRACKLE_MODE > 1 -#define cooling_copy_to_grackle2(data, p, xp, rho) \ - cooling_copy_field_to_grackle(data, xp, rho, HM); \ - cooling_copy_field_to_grackle(data, xp, rho, H2I); \ - cooling_copy_field_to_grackle(data, xp, rho, H2II); -#else -#define cooling_copy_to_grackle2(data, p, xp, rho) \ - data.HM_density = NULL; \ - data.H2I_density = NULL; \ - data.H2II_density = NULL; -#endif - -/** - * @brief copy a #xpart to the grackle data - * - * Warning this function creates some variable, therefore the grackle call - * should be in a block that still has the variables. - * - * @param data The #grackle_field_data - * @param p The #part - * @param xp The #xpart - * @param rho Particle density - */ -#if COOLING_GRACKLE_MODE > 2 -#define cooling_copy_to_grackle3(data, p, xp, rho) \ - cooling_copy_field_to_grackle(data, xp, rho, DI); \ - cooling_copy_field_to_grackle(data, xp, rho, DII); \ - cooling_copy_field_to_grackle(data, xp, rho, HDI); -#else -#define cooling_copy_to_grackle3(data, p, xp, rho) \ - data.DI_density = NULL; \ - data.DII_density = NULL; \ - data.HDI_density = NULL; -#endif - -/** - * @brief copy the grackle data to a #xpart - * - * @param data The #grackle_field_data - * @param p The #part - * @param xp The #xpart - * @param rho Particle density - */ -#if COOLING_GRACKLE_MODE > 0 -#define cooling_copy_from_grackle1(data, p, xp, rho) \ - cooling_copy_field_from_grackle(data, xp, rho, HI); \ - cooling_copy_field_from_grackle(data, xp, rho, HII); \ - cooling_copy_field_from_grackle(data, xp, rho, HeI); \ - cooling_copy_field_from_grackle(data, xp, rho, HeII); \ - cooling_copy_field_from_grackle(data, xp, rho, HeIII); \ - cooling_copy_field_from_grackle(data, xp, rho, e); -#else -#define cooling_copy_from_grackle1(data, p, xp, rho) -#endif - -/** - * @brief copy the grackle data to a #xpart - * - * @param data The #grackle_field_data - * @param p The #part - * @param xp The #xpart - * @param rho Particle density - */ -#if COOLING_GRACKLE_MODE > 1 -#define cooling_copy_from_grackle2(data, p, xp, rho) \ - cooling_copy_field_from_grackle(data, xp, rho, HM); \ - cooling_copy_field_from_grackle(data, xp, rho, H2I); \ - cooling_copy_field_from_grackle(data, xp, rho, H2II); -#else -#define cooling_copy_from_grackle2(data, p, xp, rho) -#endif - -/** - * @brief copy the grackle data to a #xpart - * - * @param data The #grackle_field_data - * @param p The #part - * @param xp The #xpart - * @param rho Particle density - */ -#if COOLING_GRACKLE_MODE > 2 -#define cooling_copy_from_grackle3(data, p, xp, rho) \ - cooling_copy_field_from_grackle(data, xp, rho, DI); \ - cooling_copy_field_from_grackle(data, xp, rho, DII); \ - cooling_copy_field_from_grackle(data, xp, rho, HDI); -#else -#define cooling_copy_from_grackle3(data, p, xp, rho) -#endif - -/** - * @brief copy a #xpart to the grackle data - * - * Warning this function creates some variable, therefore the grackle call - * should be in a block that still has the variables. - * - * @param data The #grackle_field_data - * @param p The #part - * @param xp The #xpart - * @param rho Particle density - */ -#define cooling_copy_to_grackle(data, p, xp, rho) \ - cooling_copy_to_grackle1(data, p, xp, rho); \ - cooling_copy_to_grackle2(data, p, xp, rho); \ - cooling_copy_to_grackle3(data, p, xp, rho); \ - data.volumetric_heating_rate = NULL; \ - data.specific_heating_rate = NULL; \ - data.RT_heating_rate = NULL; \ - data.RT_HI_ionization_rate = NULL; \ - data.RT_HeI_ionization_rate = NULL; \ - data.RT_HeII_ionization_rate = NULL; \ - data.RT_H2_dissociation_rate = NULL; \ - gr_float metal_density = chemistry_metal_mass_fraction(p, xp) * rho; \ - data.metal_density = &metal_density; - -/** - * @brief copy a #xpart to the grackle data - * - * Warning this function creates some variable, therefore the grackle call - * should be in a block that still has the variables. - * - * @param data The #grackle_field_data - * @param p The #part - * @param xp The #xpart - * @param rho Particle density - */ -#define cooling_copy_from_grackle(data, p, xp, rho) \ - cooling_copy_from_grackle1(data, p, xp, rho); \ - cooling_copy_from_grackle2(data, p, xp, rho); \ - cooling_copy_from_grackle3(data, p, xp, rho); - -/** - * @brief Compute the energy of a particle after dt and update the particle - * chemistry data - * - * @param phys_const The physical constants in internal units. - * @param us The internal system of units. - * @param cooling The #cooling_function_data used in the run. - * @param p Pointer to the particle data. - * @param xp Pointer to the particle extra data - * @param dt The time-step of this particle. - * - * @return du / dt - */ -__attribute__((always_inline)) INLINE static gr_float cooling_new_energy( - const struct phys_const* restrict phys_const, - const struct unit_system* restrict us, - const struct cosmology* restrict cosmo, - const struct cooling_function_data* restrict cooling, - const struct part* restrict p, struct xpart* restrict xp, double dt) { - - /* set current time */ - code_units units = cooling->units; - - /* initialize data */ - grackle_field_data data; - - /* set values */ - /* grid */ - int grid_dimension[GRACKLE_RANK] = {GRACKLE_NPART, 1, 1}; - int grid_start[GRACKLE_RANK] = {0, 0, 0}; - int grid_end[GRACKLE_RANK] = {GRACKLE_NPART - 1, 0, 0}; - - data.grid_dx = 0.; - data.grid_rank = GRACKLE_RANK; - data.grid_dimension = grid_dimension; - data.grid_start = grid_start; - data.grid_end = grid_end; - - /* general particle data */ - gr_float density = hydro_get_physical_density(p, cosmo); - const float energy_before = hydro_get_physical_internal_energy(p, xp, cosmo); - gr_float energy = energy_before; - - /* initialize density */ - data.density = &density; - - /* initialize energy */ - data.internal_energy = &energy; - - /* grackle 3.0 doc: "Currently not used" */ - data.x_velocity = NULL; - data.y_velocity = NULL; - data.z_velocity = NULL; - - /* copy to grackle structure */ - cooling_copy_to_grackle(data, p, xp, density); - - /* solve chemistry */ - chemistry_data chemistry_grackle = cooling->chemistry; - if (local_solve_chemistry(&chemistry_grackle, &grackle_rates, &units, &data, - dt) == 0) { - error("Error in solve_chemistry."); - } - - /* copy from grackle data to particle */ - cooling_copy_from_grackle(data, p, xp, density); - - return energy; -} - -/** - * @brief Compute the cooling time - * - * @param cooling The #cooling_function_data used in the run. - * @param p Pointer to the particle data. - * @param xp Pointer to the particle extra data - * - * @return cooling time - */ -__attribute__((always_inline)) INLINE static gr_float cooling_time( - const struct phys_const* restrict phys_const, - const struct unit_system* restrict us, - const struct cosmology* restrict cosmo, - const struct cooling_function_data* restrict cooling, - const struct part* restrict p, struct xpart* restrict xp) { - - /* set current time */ - code_units units = cooling->units; - - /* initialize data */ - grackle_field_data data; - - /* set values */ - /* grid */ - int grid_dimension[GRACKLE_RANK] = {GRACKLE_NPART, 1, 1}; - int grid_start[GRACKLE_RANK] = {0, 0, 0}; - int grid_end[GRACKLE_RANK] = {GRACKLE_NPART - 1, 0, 0}; - - data.grid_rank = GRACKLE_RANK; - data.grid_dimension = grid_dimension; - data.grid_start = grid_start; - data.grid_end = grid_end; - - /* general particle data */ - const gr_float energy_before = - hydro_get_physical_internal_energy(p, xp, cosmo); - gr_float density = hydro_get_physical_density(p, cosmo); - gr_float energy = energy_before; - - /* initialize density */ - data.density = &density; - - /* initialize energy */ - data.internal_energy = &energy; - - /* grackle 3.0 doc: "Currently not used" */ - data.x_velocity = NULL; - data.y_velocity = NULL; - data.z_velocity = NULL; - - /* copy data from particle to grackle data */ - cooling_copy_to_grackle(data, p, xp, density); - - /* Compute cooling time */ - gr_float cooling_time; - chemistry_data chemistry_grackle = cooling->chemistry; - chemistry_data_storage chemistry_rates = grackle_rates; - if (local_calculate_cooling_time(&chemistry_grackle, &chemistry_rates, &units, - &data, &cooling_time) == 0) { - error("Error in calculate_cooling_time."); - } - - /* copy from grackle data to particle */ - cooling_copy_from_grackle(data, p, xp, density); - - /* compute rate */ - return cooling_time; -} - -/** - * @brief Apply the cooling function to a particle. - * - * @param phys_const The physical constants in internal units. - * @param us The internal system of units. - * @param cosmo The current cosmological model. - * @param cooling The #cooling_function_data used in the run. - * @param p Pointer to the particle data. - * @param dt The time-step of this particle. - * @param hydro_properties the hydro_props struct, used for - * getting the minimal internal energy allowed in by SWIFT. - * Read from yml file into engine struct. - */ -__attribute__((always_inline)) INLINE static void cooling_cool_part( - const struct phys_const* restrict phys_const, - const struct unit_system* restrict us, - const struct cosmology* restrict cosmo, - const struct hydro_props* hydro_props, - const struct entropy_floor_properties* floor_props, - const struct cooling_function_data* restrict cooling, - struct part* restrict p, struct xpart* restrict xp, double dt, - double dt_therm) { - - /* Nothing to do here? */ - if (dt == 0.) return; - - /* Current energy */ - const float u_old = hydro_get_physical_internal_energy(p, xp, cosmo); - - /* Current du_dt in physical coordinates (internal units) */ - const float hydro_du_dt = hydro_get_physical_internal_energy_dt(p, cosmo); - - /* Calculate energy after dt */ - gr_float u_new = - cooling_new_energy(phys_const, us, cosmo, cooling, p, xp, dt); - - float delta_u = u_new - u_old + hydro_du_dt * dt_therm; - - /* We now need to check that we are not going to go below any of the limits */ - - /* First, check whether we may end up below the minimal energy after - * this step 1/2 kick + another 1/2 kick that could potentially be for - * a time-step twice as big. We hence check for 1.5 delta_u. */ - if (u_old + 1.5 * delta_u < hydro_props->minimal_internal_energy) { - delta_u = (hydro_props->minimal_internal_energy - u_old) / 1.5; - } - - /* Second, check whether the energy used in the prediction could get negative. - * We need to check for the 1/2 dt kick followed by a full time-step drift - * that could potentially be for a time-step twice as big. We hence check - * for 2.5 delta_u but this time against 0 energy not the minimum. - * To avoid numerical rounding bringing us below 0., we add a tiny tolerance. - */ - const float rounding_tolerance = 1.0e-4; - - if (u_old + 2.5 * delta_u < 0.) { - delta_u = -u_old / (2.5 + rounding_tolerance); - } - - /* Turn this into a rate of change (including cosmology term) */ - const float cooling_du_dt = delta_u / dt_therm; - - /* Update the internal energy time derivative */ - hydro_set_physical_internal_energy_dt(p, cosmo, cooling_du_dt); - - /* Store the radiated energy */ - xp->cooling_data.radiated_energy -= hydro_get_mass(p) * cooling_du_dt * dt; -} + const struct part* restrict p, struct xpart* restrict xp, double dt); -/** - * @brief Compute the temperature of a #part based on the cooling function. - * - * @param phys_const #phys_const data structure. - * @param hydro_props The properties of the hydro scheme. - * @param us The internal system of units. - * @param cosmo #cosmology data structure. - * @param cooling #cooling_function_data struct. - * @param p #part data. - * @param xp Pointer to the #xpart data. - */ -static INLINE float cooling_get_temperature( +gr_float cooling_time(const struct phys_const* restrict phys_const, + const struct unit_system* restrict us, + const struct hydro_props* hydro_properties, + const struct cosmology* restrict cosmo, + const struct cooling_function_data* restrict cooling, + const struct part* restrict p, struct xpart* restrict xp); +void cooling_apply(struct part* restrict p, struct xpart* restrict xp, + const struct cosmology* restrict cosmo, float cooling_du_dt, + gr_float u_new); + +void cooling_cool_part(const struct phys_const* restrict phys_const, + const struct unit_system* restrict us, + const struct cosmology* restrict cosmo, + const struct hydro_props* hydro_properties, + const struct entropy_floor_properties* floor_props, + const struct cooling_function_data* restrict cooling, + struct part* restrict p, struct xpart* restrict xp, + const double time, const double dt, + const double dt_therm); + +float cooling_get_temperature( const struct phys_const* restrict phys_const, - const struct hydro_props* restrict hydro_props, + const struct hydro_props* hydro_properties, const struct unit_system* restrict us, const struct cosmology* restrict cosmo, const struct cooling_function_data* restrict cooling, - const struct part* restrict p, const struct xpart* restrict xp) { - // TODO use the grackle library - - /* Physical constants */ - const double m_H = phys_const->const_proton_mass; - const double k_B = phys_const->const_boltzmann_k; - - /* Gas properties */ - const double T_transition = hydro_props->hydrogen_ionization_temperature; - const double mu_neutral = hydro_props->mu_neutral; - const double mu_ionised = hydro_props->mu_ionised; - - /* Particle temperature */ - const double u = hydro_get_physical_internal_energy(p, xp, cosmo); - - /* Temperature over mean molecular weight */ - const double T_over_mu = hydro_gamma_minus_one * u * m_H / k_B; - - /* Are we above or below the HII -> HI transition? */ - if (T_over_mu > (T_transition + 1.) / mu_ionised) - return T_over_mu * mu_ionised; - else if (T_over_mu < (T_transition - 1.) / mu_neutral) - return T_over_mu * mu_neutral; - else - return T_transition; -} - -/** - * @brief Computes the cooling time-step. - * - * We return FLT_MAX so as to impose no limit on the time-step. - * - * @param cooling The #cooling_function_data used in the run. - * @param phys_const The physical constants in internal units. - * @param cosmo The current cosmological model. - * @param us The internal system of units. - * @param p Pointer to the particle data. - */ -__attribute__((always_inline)) INLINE static float cooling_timestep( - const struct cooling_function_data* restrict cooling, - const struct phys_const* restrict phys_const, - const struct cosmology* restrict cosmo, - const struct unit_system* restrict us, - const struct hydro_props* hydro_props, const struct part* restrict p, - const struct xpart* restrict xp) { - - return FLT_MAX; -} - -/** - * @brief Split the coolong content of a particle into n pieces - * - * @param p The #part. - * @param xp The #xpart. - * @param n The number of pieces to split into. - */ -INLINE static void cooling_split_part(struct part* p, struct xpart* xp, - double n) { - - error("Loic: to be implemented"); -} - -/** - * @brief Initialises the cooling unit system. - * - * @param us The current internal system of units. - * @param cooling The cooling properties to initialize - */ -__attribute__((always_inline)) INLINE static void cooling_init_units( - const struct unit_system* us, struct cooling_function_data* cooling) { - - /* These are conversions from code units to cgs. */ - - /* first cosmo */ - cooling->units.a_units = 1.0; // units for the expansion factor - cooling->units.a_value = 1.0; - - /* We assume here all physical quantities to - be in proper coordinate (not comobile) */ - cooling->units.comoving_coordinates = 0; - - /* then units */ - cooling->units.density_units = - units_cgs_conversion_factor(us, UNIT_CONV_DENSITY); - cooling->units.length_units = - units_cgs_conversion_factor(us, UNIT_CONV_LENGTH); - cooling->units.time_units = units_cgs_conversion_factor(us, UNIT_CONV_TIME); - cooling->units.velocity_units = - units_cgs_conversion_factor(us, UNIT_CONV_VELOCITY); -} - -/** - * @brief Initialises Grackle. - * - * @param cooling The cooling properties to initialize - */ -__attribute__((always_inline)) INLINE static void cooling_init_grackle( - struct cooling_function_data* cooling) { - -#ifdef SWIFT_DEBUG_CHECKS - /* enable verbose for grackle */ - grackle_verbose = 1; -#endif - - chemistry_data* chemistry = &cooling->chemistry; - - /* Create a chemistry object for parameters and rate data. */ - if (set_default_chemistry_parameters(chemistry) == 0) { - error("Error in set_default_chemistry_parameters."); - } - - // Set parameter values for chemistry. - chemistry->use_grackle = 1; - chemistry->with_radiative_cooling = 1; - - /* molecular network with H, He, D - From Cloudy table */ - chemistry->primordial_chemistry = COOLING_GRACKLE_MODE; - chemistry->metal_cooling = cooling->with_metal_cooling; - chemistry->UVbackground = cooling->with_uv_background; - chemistry->grackle_data_file = cooling->cloudy_table; - - /* radiative transfer */ - chemistry->use_radiative_transfer = cooling->provide_specific_heating_rates || - cooling->provide_volumetric_heating_rates; - chemistry->use_volumetric_heating_rate = - cooling->provide_volumetric_heating_rates; - chemistry->use_specific_heating_rate = - cooling->provide_specific_heating_rates; - - if (cooling->provide_specific_heating_rates && - cooling->provide_volumetric_heating_rates) - message( - "WARNING: You should specified either the specific or the volumetric " - "heating rates, not both"); - - /* self shielding */ - chemistry->self_shielding_method = cooling->self_shielding_method; - - /* Initialize the chemistry object. */ - if (initialize_chemistry_data(&cooling->units) == 0) { - error("Error in initialize_chemistry_data."); - } -} - -/** - * @brief Initialises the cooling properties. - * - * @param parameter_file The parsed parameter file. - * @param us The current internal system of units. - * @param phys_const The physical constants in internal units. - * @param hydro_props The properties of the hydro scheme. - * @param cooling The cooling properties to initialize - */ -__attribute__((always_inline)) INLINE static void cooling_init_backend( - struct swift_params* parameter_file, const struct unit_system* us, - const struct phys_const* phys_const, const struct hydro_props* hydro_props, - struct cooling_function_data* cooling) { - - if (GRACKLE_NPART != 1) - error("Grackle with multiple particles not implemented"); - - /* read parameters */ - cooling_read_parameters(parameter_file, cooling); - - /* Set up the units system. */ - cooling_init_units(us, cooling); - - /* Set up grackle */ - cooling_init_grackle(cooling); -} - -/** - * @brief Clean-up the memory allocated for the cooling routines - * - * @param cooling the cooling data structure. - */ -static INLINE void cooling_clean(struct cooling_function_data* cooling) { - - // MATTHIEU: To do: free stuff here -} - -/** - * @brief Write a cooling struct to the given FILE as a stream of bytes. - * - * Nothing to do beyond writing the structure from the stream. - * - * @param cooling the struct - * @param stream the file stream - */ -static INLINE void cooling_struct_dump( - const struct cooling_function_data* cooling, FILE* stream) { - restart_write_blocks((void*)cooling, sizeof(struct cooling_function_data), 1, - stream, "cooling", "cooling function"); -} - -/** - * @brief Restore a hydro_props struct from the given FILE as a stream of - * bytes. - * - * Nothing to do beyond reading the structure from the stream. - * - * @param cooling the struct - * @param stream the file stream - * @param cosmo #cosmology structure - */ -static INLINE void cooling_struct_restore(struct cooling_function_data* cooling, - FILE* stream, - const struct cosmology* cosmo) { - restart_read_blocks((void*)cooling, sizeof(struct cooling_function_data), 1, - stream, NULL, "cooling function"); - - /* Set up grackle */ - cooling_init_grackle(cooling); -} - + const struct part* restrict p, const struct xpart* restrict xp); +float cooling_timestep(const struct cooling_function_data* restrict cooling, + const struct phys_const* restrict phys_const, + const struct cosmology* restrict cosmo, + const struct unit_system* restrict us, + const struct hydro_props* hydro_properties, + const struct part* restrict p, + const struct xpart* restrict xp); + +void cooling_split_part(struct part* p, struct xpart* xp, double n); + +void cooling_init_units(const struct unit_system* us, + const struct phys_const* phys_const, + struct cooling_function_data* cooling); +void cooling_init_grackle(struct cooling_function_data* cooling); + +void cooling_init_backend(struct swift_params* parameter_file, + const struct unit_system* us, + const struct phys_const* phys_const, + const struct hydro_props* hydro_props, + struct cooling_function_data* cooling); + +void cooling_clean(struct cooling_function_data* cooling); +void cooling_struct_dump(const struct cooling_function_data* cooling, + FILE* stream); +void cooling_struct_restore(struct cooling_function_data* cooling, FILE* stream, + const struct cosmology* cosmo); #endif /* SWIFT_COOLING_GRACKLE_H */ diff --git a/src/cooling/grackle/cooling_io.h b/src/cooling/grackle/cooling_io.h index 18712f4cf6..613f507868 100644 --- a/src/cooling/grackle/cooling_io.h +++ b/src/cooling/grackle/cooling_io.h @@ -136,34 +136,58 @@ __attribute__((always_inline)) INLINE static int cooling_write_particles( * @param cooling The cooling properties to initialize */ __attribute__((always_inline)) INLINE static void cooling_read_parameters( - struct swift_params* parameter_file, - struct cooling_function_data* cooling) { + struct swift_params* parameter_file, struct cooling_function_data* cooling, + const struct phys_const* phys_const) { - parser_get_param_string(parameter_file, "GrackleCooling:CloudyTable", + parser_get_param_string(parameter_file, "GrackleCooling:cloudy_table", cooling->cloudy_table); + + cooling->primordial_chemistry = parser_get_opt_param_int( + parameter_file, "GrackleCooling:primordial_chemistry", + COOLING_GRACKLE_MODE); + + if (cooling->primordial_chemistry < 0) + error("Primordial chemistry cannot be below 0"); + + if (cooling->primordial_chemistry > COOLING_GRACKLE_MODE) + error("Cannot run primordial chemistry %i when compiled with %i", + cooling->primordial_chemistry, COOLING_GRACKLE_MODE); + cooling->with_uv_background = - parser_get_param_int(parameter_file, "GrackleCooling:WithUVbackground"); + parser_get_param_int(parameter_file, "GrackleCooling:with_UV_background"); cooling->redshift = - parser_get_param_double(parameter_file, "GrackleCooling:Redshift"); + parser_get_param_double(parameter_file, "GrackleCooling:redshift"); cooling->with_metal_cooling = - parser_get_param_int(parameter_file, "GrackleCooling:WithMetalCooling"); + parser_get_param_int(parameter_file, "GrackleCooling:with_metal_cooling"); cooling->provide_volumetric_heating_rates = parser_get_opt_param_int( - parameter_file, "GrackleCooling:ProvideVolumetricHeatingRates", 0); + parameter_file, "GrackleCooling:provide_volumetric_heating_rates", 0); cooling->provide_specific_heating_rates = parser_get_opt_param_int( - parameter_file, "GrackleCooling:ProvideSpecificHeatingRates", 0); + parameter_file, "GrackleCooling:provide_specific_heating_rates", 0); + /* Self shielding */ cooling->self_shielding_method = parser_get_opt_param_int( - parameter_file, "GrackleCooling:SelfShieldingMethod", 0); + parameter_file, "GrackleCooling:self_shielding_method", 0); + + if (cooling->self_shielding_method == -1) { + cooling->self_shielding_threshold = parser_get_param_float( + parameter_file, "GrackleCooling:self_shielding_threshold_atom_per_cm3"); + } + /* Initial step convergence */ cooling->max_step = parser_get_opt_param_int( - parameter_file, "GrackleCooling:MaxSteps", 10000); + parameter_file, "GrackleCooling:max_steps", 10000); cooling->convergence_limit = parser_get_opt_param_double( - parameter_file, "GrackleCooling:ConvergenceLimit", 1e-2); + parameter_file, "GrackleCooling:convergence_limit", 1e-2); + + /* Thermal time */ + cooling->thermal_time = + parser_get_param_float(parameter_file, "GrackleCooling:thermal_time_myr"); + cooling->thermal_time *= phys_const->const_year * 1e6; } #endif /* SWIFT_COOLING_GRACKLE_IO_H */ diff --git a/src/cooling/grackle/cooling_struct.h b/src/cooling/grackle/cooling_struct.h index 66c385234c..fb1e745242 100644 --- a/src/cooling/grackle/cooling_struct.h +++ b/src/cooling/grackle/cooling_struct.h @@ -32,41 +32,51 @@ */ struct cooling_function_data { - /* Filename of the Cloudy Table */ + /*! Filename of the Cloudy Table */ char cloudy_table[200]; - /* Enable/Disable UV backgroud */ + /*! Enable/Disable UV backgroud */ int with_uv_background; - /* Redshift to use for the UV backgroud (-1 to use cosmological one) */ + /*! Chemistry network */ + int primordial_chemistry; + + /*! Redshift to use for the UV backgroud (-1 to use cosmological one) */ double redshift; - /* unit system */ + /*! unit system */ code_units units; - /* grackle chemistry data */ + /*! grackle chemistry data */ chemistry_data chemistry; - /* Enable/Disable metal cooling */ + /*! Enable/Disable metal cooling */ int with_metal_cooling; - /* User provide volumetric heating rates */ + /*! User provide volumetric heating rates */ int provide_volumetric_heating_rates; - /* User provide specific heating rates */ + /*! User provide specific heating rates */ int provide_specific_heating_rates; - /* Self shielding method (<= 3) means grackle modes */ + /*! Self shielding method (1 -> 3 for grackle's ones, 0 for none and -1 for + * GEAR) */ int self_shielding_method; - /* convergence limit for first init */ + /*! Self shielding threshold */ + float self_shielding_threshold; + + /*! convergence limit for first init */ float convergence_limit; - /* number of step max for first init */ + /*! number of step max for first init */ int max_step; - /* over relaxation parameter */ + /*! over relaxation parameter */ float omega; + + /*! Duration for switching off cooling after an event (e.g. supernovae) */ + float thermal_time; }; /** @@ -77,6 +87,9 @@ struct cooling_xpart_data { /*! Energy radiated away by this particle since the start of the run */ float radiated_energy; + /*! Last time the cooling was switch off */ + float time_last_event; + /* here all fractions are mass fraction */ #if COOLING_GRACKLE_MODE >= 1 /* primordial chemistry >= 1 */ diff --git a/src/cooling/none/cooling.h b/src/cooling/none/cooling.h index 27c7451517..107ea4b88a 100644 --- a/src/cooling/none/cooling.h +++ b/src/cooling/none/cooling.h @@ -63,6 +63,7 @@ INLINE static void cooling_update(const struct cosmology* cosmo, * @param cooling The #cooling_function_data used in the run. * @param p Pointer to the particle data. * @param xp Pointer to the extended particle data. + * @param time The current time. * @param dt The time-step of this particle. * @param dt_therm The time-step operator used for thermal quantities. */ @@ -73,8 +74,8 @@ __attribute__((always_inline)) INLINE static void cooling_cool_part( const struct hydro_props* hydro_props, const struct entropy_floor_properties* floor_props, const struct cooling_function_data* restrict cooling, - struct part* restrict p, struct xpart* restrict xp, const float dt, - const float dt_therm) {} + struct part* restrict p, struct xpart* restrict xp, const double time, + const float dt, const float dt_therm) {} /** * @brief Computes the cooling time-step. @@ -108,6 +109,7 @@ __attribute__((always_inline)) INLINE static float cooling_timestep( * * @param phys_const The physical constant in internal units. * @param us The unit system. + * @param hydro_props The properties of the hydro scheme. * @param cosmo The current cosmological model. * @param data The properties of the cooling function. * @param p Pointer to the particle data. @@ -116,6 +118,7 @@ __attribute__((always_inline)) INLINE static float cooling_timestep( __attribute__((always_inline)) INLINE static void cooling_first_init_part( const struct phys_const* restrict phys_const, const struct unit_system* restrict us, + const struct hydro_props* hydro_props, const struct cosmology* restrict cosmo, const struct cooling_function_data* data, const struct part* restrict p, struct xpart* restrict xp) {} diff --git a/src/engine.c b/src/engine.c index b38738b9f5..c1e294a442 100644 --- a/src/engine.c +++ b/src/engine.c @@ -3173,7 +3173,7 @@ void engine_collect_stars_counter(struct engine *e) { /* Reset counters */ for (size_t i = 0; i < e->s->nr_sparts_foreign; i++) { - e->s->sparts_foreign[i].num_ngb_force = 0; + e->s->sparts_foreign[i].num_ngb_feedback = 0; } /* Update counters */ @@ -3193,7 +3193,7 @@ void engine_collect_stars_counter(struct engine *e) { id_j, j, displs[engine_rank], n_sparts_int[engine_rank]); } - local_sparts[i].num_ngb_force += sparts[j].num_ngb_force; + local_sparts[i].num_ngb_feedback += sparts[j].num_ngb_feedback; } } } diff --git a/src/feedback.h b/src/feedback.h index ee2934c764..0b44e41bcd 100644 --- a/src/feedback.h +++ b/src/feedback.h @@ -29,6 +29,9 @@ #elif defined(FEEDBACK_EAGLE) #include "./feedback/EAGLE/feedback.h" #include "./feedback/EAGLE/feedback_iact.h" +#elif defined(FEEDBACK_GEAR) +#include "./feedback/GEAR/feedback.h" +#include "./feedback/GEAR/feedback_iact.h" #else #error "Invalid choice of feedback model" #endif diff --git a/src/feedback/EAGLE/feedback.h b/src/feedback/EAGLE/feedback.h index 03144fcc2d..c3e7c76df1 100644 --- a/src/feedback/EAGLE/feedback.h +++ b/src/feedback/EAGLE/feedback.h @@ -33,6 +33,20 @@ void compute_stellar_evolution(const struct feedback_props* feedback_props, const struct unit_system* us, const double age, const double dt); +/** + * @brief Update the properties of a particle fue to feedback effects after + * the cooling was applied. + * + * Nothing to do here in the EAGLE model. + * + * @param p The #part to consider. + * @param xp The #xpart to consider. + * @param cosmo The #cosmology. + */ +__attribute__((always_inline)) INLINE static void feedback_update_part( + struct part* restrict p, struct xpart* restrict xp, + const struct engine* restrict e) {} + /** * @brief Should this particle be doing any feedback-related operation? * @@ -157,16 +171,19 @@ __attribute__((always_inline)) INLINE static void feedback_prepare_spart( * @param feedback_props The #feedback_props structure. * @param cosmo The current cosmological model. * @param us The unit system. + * @param phys_const The physical constants in internal units. * @param star_age_beg_step The age of the star at the star of the time-step in * internal units. * @param dt The time-step size of this star in internal units. * @param time The physical time in internal units. + * @param ti_begin The integer time at the beginning of the step. * @param with_cosmology Are we running with cosmology on? */ __attribute__((always_inline)) INLINE static void feedback_evolve_spart( struct spart* restrict sp, const struct feedback_props* feedback_props, const struct cosmology* cosmo, const struct unit_system* us, - const double star_age_beg_step, const double dt, const double time, + const struct phys_const* phys_const, const double star_age_beg_step, + const double dt, const double time, const integertime_t ti_begin, const int with_cosmology) { #ifdef SWIFT_DEBUG_CHECKS @@ -269,4 +286,16 @@ void feedback_struct_dump(const struct feedback_props* feedback, FILE* stream); void feedback_struct_restore(struct feedback_props* feedback, FILE* stream); +#ifdef HAVE_HDF5 +/** + * @brief Writes the current model of feedback to the file + * @param h_grpsph The HDF5 group in which to write + */ +INLINE static void feedback_write_flavour(struct feedback_props* feedback, + hid_t h_grp) { + + io_write_attribute_s(h_grp, "Feedback Model", "EAGLE"); +}; +#endif // HAVE_HDF5 + #endif /* SWIFT_FEEDBACK_EAGLE_H */ diff --git a/src/feedback/EAGLE/feedback_iact.h b/src/feedback/EAGLE/feedback_iact.h index a7da4b2267..7715e3a8b3 100644 --- a/src/feedback/EAGLE/feedback_iact.h +++ b/src/feedback/EAGLE/feedback_iact.h @@ -280,7 +280,7 @@ runner_iact_nonsym_feedback_apply(const float r2, const float *dx, /* Draw a random number (Note mixing both IDs) */ const float rand = random_unit_interval(si->id + pj->id, ti_current, - random_number_stellar_feedback); + random_number_stellar_feedback_1); /* Are we lucky? */ if (rand < prob) { diff --git a/src/feedback/EAGLE/feedback_struct.h b/src/feedback/EAGLE/feedback_struct.h index ad6d46da46..000a144719 100644 --- a/src/feedback/EAGLE/feedback_struct.h +++ b/src/feedback/EAGLE/feedback_struct.h @@ -21,6 +21,11 @@ #include "chemistry_struct.h" +/** + * @brief Feedback fields carried by each hydro particles + */ +struct feedback_part_data {}; + /** * @brief Feedback fields carried by each star particles */ diff --git a/src/feedback/GEAR/feedback.c b/src/feedback/GEAR/feedback.c new file mode 100644 index 0000000000..7b498fd2ca --- /dev/null +++ b/src/feedback/GEAR/feedback.c @@ -0,0 +1,279 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2018 Loic Hausammann (loic.hausammann@epfl.ch) + * + * 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 <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ + +/* Include header */ +#include "feedback.h" + +/* Local includes */ +#include "cosmology.h" +#include "engine.h" +#include "error.h" +#include "feedback_properties.h" +#include "hydro_properties.h" +#include "part.h" +#include "stellar_evolution.h" +#include "units.h" + +#include <strings.h> + +/** + * @brief Update the properties of the particle due to a supernovae. + * + * @param p The #part to consider. + * @param xp The #xpart to consider. + * @param cosmo The #cosmology. + */ +void feedback_update_part(struct part* restrict p, struct xpart* restrict xp, + const struct engine* restrict e) { + + /* Did the particle receive a supernovae */ + if (xp->feedback_data.delta_mass == 0) return; + + const struct cosmology* cosmo = e->cosmology; + + /* Turn off the cooling */ + xp->cooling_data.time_last_event = e->time; + + /* Update mass */ + const float old_mass = hydro_get_mass(p); + const float new_mass = old_mass + xp->feedback_data.delta_mass; + + if (xp->feedback_data.delta_mass < 0.) { + error("Delta mass smaller than 0"); + } + + hydro_set_mass(p, new_mass); + + xp->feedback_data.delta_mass = 0; + + /* Update the density */ + p->rho *= new_mass / old_mass; + + /* Update internal energy */ + const float u = hydro_get_physical_internal_energy(p, xp, cosmo); + const float u_new = u + xp->feedback_data.delta_u; + + hydro_set_physical_internal_energy(p, xp, cosmo, u_new); + hydro_set_drifted_physical_internal_energy(p, cosmo, u_new); + + xp->feedback_data.delta_u = 0.; + + /* Update the velocities */ + for (int i = 0; i < 3; i++) { + const float dv = xp->feedback_data.delta_p[i] / new_mass; + + xp->v_full[i] += dv; + p->v[i] += dv; + + xp->feedback_data.delta_p[i] = 0; + } +} + +/** + * @Brief Should we do feedback for this star? + * + * @param sp The star to consider. + */ +int feedback_will_do_feedback(const struct spart* sp, + const struct feedback_props* feedback_props, + const int with_cosmology, + const struct cosmology* cosmo, + const double time) { + + return (sp->birth_time != -1.); +} + +/** + * @brief Should this particle be doing any feedback-related operation? + * + * @param sp The #spart. + * @param time The current simulation time (Non-cosmological runs). + * @param cosmo The cosmological model (cosmological runs). + * @param with_cosmology Are we doing a cosmological run? + */ +int feedback_is_active(const struct spart* sp, const double time, + const struct cosmology* cosmo, + const int with_cosmology) { + + if (sp->birth_time == -1.) return 0; + + if (with_cosmology) { + return ((double)cosmo->a) > sp->birth_scale_factor; + } else { + return time > sp->birth_time; + } +} + +/** + * @brief Returns the length of time since the particle last did + * enrichment/feedback. + * + * @param sp The #spart. + * @param with_cosmology Are we running with cosmological time integration on? + * @param cosmo The cosmological model. + * @param time The current time (since the Big Bang / start of the run) in + * internal units. + * @param dt_star the length of this particle's time-step in internal units. + * @return The length of the enrichment step in internal units. + */ +double feedback_get_enrichment_timestep(const struct spart* sp, + const int with_cosmology, + const struct cosmology* cosmo, + const double time, + const double dt_star) { + return dt_star; +} + +/** + * @brief Prepares a s-particle for its feedback interactions + * + * @param sp The particle to act upon + */ +void feedback_init_spart(struct spart* sp) { + + sp->feedback_data.enrichment_weight = 0.f; +} + +/** + * @brief Prepares a star's feedback field before computing what + * needs to be distributed. + */ +void feedback_reset_feedback(struct spart* sp, + const struct feedback_props* feedback_props) { + + /* Zero the energy of supernovae */ + sp->feedback_data.energy_ejected = 0; +} + +/** + * @brief Initialises the s-particles feedback props for the first time + * + * This function is called only once just after the ICs have been + * read in to do some conversions. + * + * @param sp The particle to act upon. + * @param feedback_props The properties of the feedback model. + */ +void feedback_first_init_spart(struct spart* sp, + const struct feedback_props* feedback_props) { + + feedback_init_spart(sp); + + feedback_reset_feedback(sp, feedback_props); +} + +/** + * @brief Initialises the s-particles feedback props for the first time + * + * This function is called only once just after the ICs have been + * read in to do some conversions. + * + * @param sp The particle to act upon. + * @param feedback_props The properties of the feedback model. + */ +void feedback_prepare_spart(struct spart* sp, + const struct feedback_props* feedback_props) {} + +/** + * @brief Evolve the stellar properties of a #spart. + * + * This function compute the SN rate and yields before sending + * this information to a different MPI rank. + * + * @param sp The particle to act upon + * @param feedback_props The #feedback_props structure. + * @param cosmo The current cosmological model. + * @param us The unit system. + * @param phys_const The #phys_const. + * @param star_age_beg_step The age of the star at the star of the time-step in + * internal units. + * @param dt The time-step size of this star in internal units. + * @param time The physical time in internal units. + * @param ti_begin The integer time at the beginning of the step. + * @param with_cosmology Are we running with cosmology on? + */ +void feedback_evolve_spart(struct spart* restrict sp, + const struct feedback_props* feedback_props, + const struct cosmology* cosmo, + const struct unit_system* us, + const struct phys_const* phys_const, + const double star_age_beg_step, const double dt, + const double time, const integertime_t ti_begin, + const int with_cosmology) { + +#ifdef SWIFT_DEBUG_CHECKS + if (sp->birth_time == -1.) error("Evolving a star particle that should not!"); +#endif + + /* Reset the feedback */ + feedback_reset_feedback(sp, feedback_props); + + /* Add missing h factor */ + const float hi_inv = 1.f / sp->h; + const float hi_inv_dim = pow_dimension(hi_inv); /* 1/h^d */ + + sp->feedback_data.enrichment_weight *= hi_inv_dim; + + /* Compute the stellar evolution */ + stellar_evolution_evolve_spart(sp, &feedback_props->stellar_model, cosmo, us, + phys_const, ti_begin, star_age_beg_step, dt); + + /* Transform the number of SN to the energy */ + sp->feedback_data.energy_ejected = + sp->feedback_data.number_sn * feedback_props->energy_per_supernovae; +} + +/** + * @brief Write a feedback struct to the given FILE as a stream of bytes. + * + * @param feedback the struct + * @param stream the file stream + */ +void feedback_struct_dump(const struct feedback_props* feedback, FILE* stream) { + + restart_write_blocks((void*)feedback, sizeof(struct feedback_props), 1, + stream, "feedback", "feedback function"); + + stellar_evolution_dump(&feedback->stellar_model, stream); +} + +/** + * @brief Restore a feedback struct from the given FILE as a stream of + * bytes. + * + * @param feedback the struct + * @param stream the file stream + */ +void feedback_struct_restore(struct feedback_props* feedback, FILE* stream) { + + restart_read_blocks((void*)feedback, sizeof(struct feedback_props), 1, stream, + NULL, "feedback function"); + + stellar_evolution_restore(&feedback->stellar_model, stream); +} + +/** + * @brief Clean the allocated memory. + * + * @param feedback the #feedback_props. + */ +void feedback_clean(struct feedback_props* feedback) { + + stellar_evolution_clean(&feedback->stellar_model); +} diff --git a/src/feedback/GEAR/feedback.h b/src/feedback/GEAR/feedback.h new file mode 100644 index 0000000000..1f333b39f7 --- /dev/null +++ b/src/feedback/GEAR/feedback.h @@ -0,0 +1,85 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2018 Loic Hausammann (loic.hausammann@epfl.ch) + * + * 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 <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_FEEDBACK_GEAR_H +#define SWIFT_FEEDBACK_GEAR_H + +#include "cosmology.h" +#include "error.h" +#include "feedback_properties.h" +#include "hydro_properties.h" +#include "part.h" +#include "stellar_evolution.h" +#include "units.h" + +#include <strings.h> + +void feedback_update_part(struct part* restrict p, struct xpart* restrict xp, + const struct engine* restrict e); + +int feedback_will_do_feedback(const struct spart* sp, + const struct feedback_props* feedback_props, + const int with_cosmology, + const struct cosmology* cosmo, const double time); + +int feedback_is_active(const struct spart* sp, const double time, + const struct cosmology* cosmo, const int with_cosmology); +double feedback_get_enrichment_timestep(const struct spart* sp, + const int with_cosmology, + const struct cosmology* cosmo, + const double time, + const double dt_star); +void feedback_init_spart(struct spart* sp); + +void feedback_reset_feedback(struct spart* sp, + const struct feedback_props* feedback_props); +void feedback_first_init_spart(struct spart* sp, + const struct feedback_props* feedback_props); +void feedback_prepare_spart(struct spart* sp, + const struct feedback_props* feedback_props); +void feedback_evolve_spart(struct spart* restrict sp, + const struct feedback_props* feedback_props, + const struct cosmology* cosmo, + const struct unit_system* us, + const struct phys_const* phys_const, + const double star_age_beg_step, const double dt, + const double time, const integertime_t ti_begin, + const int with_cosmology); +void feedback_struct_dump(const struct feedback_props* feedback, FILE* stream); +void feedback_struct_restore(struct feedback_props* feedback, FILE* stream); +void feedback_clean(struct feedback_props* feedback); + +/** + * @brief Writes the current model of feedback to the file + * @param h_grpsph The HDF5 group in which to write + */ +INLINE static void feedback_write_flavour(struct feedback_props* feedback, + hid_t h_grp) { + + io_write_attribute_s(h_grp, "Feedback Model", "GEAR"); + + for (int i = 0; i < GEAR_CHEMISTRY_ELEMENT_COUNT; i++) { + char buffer[20]; + sprintf(buffer, "Element %d", (int)i); + io_write_attribute_s( + h_grp, buffer, + stellar_evolution_get_element_name(&feedback->stellar_model, i)); + } +}; + +#endif /* SWIFT_FEEDBACK_GEAR_H */ diff --git a/src/feedback/GEAR/feedback_iact.h b/src/feedback/GEAR/feedback_iact.h new file mode 100644 index 0000000000..93c37cadc2 --- /dev/null +++ b/src/feedback/GEAR/feedback_iact.h @@ -0,0 +1,148 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Coypright (c) 2018 Loic Hausammann (loic.hausammann@epfl.ch) + * + * 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 <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_GEAR_FEEDBACK_IACT_H +#define SWIFT_GEAR_FEEDBACK_IACT_H + +/* Local includes */ +#include "hydro.h" +#include "random.h" +#include "timestep_sync_part.h" + +/** + * @brief Density interaction between two particles (non-symmetric). + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param si First sparticle. + * @param pj Second particle (not updated). + * @param xpj Extra particle data (not updated). + * @param cosmo The cosmological model. + * @param ti_current Current integer time value + */ +__attribute__((always_inline)) INLINE static void +runner_iact_nonsym_feedback_density(const float r2, const float *dx, + const float hi, const float hj, + struct spart *restrict si, + const struct part *restrict pj, + const struct xpart *restrict xpj, + const struct cosmology *restrict cosmo, + const integertime_t ti_current) { + + /* Get the gas mass. */ + const float mj = hydro_get_mass(pj); + + /* Get r and 1/r. */ + const float r_inv = 1.0f / sqrtf(r2); + const float r = r2 * r_inv; + + /* Compute the kernel function */ + const float hi_inv = 1.0f / hi; + const float ui = r * hi_inv; + float wi; + kernel_eval(ui, &wi); + + /* Add contribution of pj to normalisation of density weighted fraction + * which determines how much mass to distribute to neighbouring + * gas particles */ + + /* The normalization by 1 / h^d is done in feedback.h */ + si->feedback_data.enrichment_weight += mj * wi; +} + +/** + * @brief Feedback interaction between two particles (non-symmetric). + * Used for updating properties of gas particles neighbouring a star particle + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (si - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param si First (star) particle (not updated). + * @param pj Second (gas) particle. + * @param xpj Extra particle data + * @param cosmo The cosmological model. + * @param ti_current Current integer time used value for seeding random number + * generator + */ +__attribute__((always_inline)) INLINE static void +runner_iact_nonsym_feedback_apply(const float r2, const float *dx, + const float hi, const float hj, + struct spart *si, struct part *pj, + struct xpart *xpj, + const struct cosmology *cosmo, + const integertime_t ti_current) { + + const double e_sn = si->feedback_data.energy_ejected; + + /* Do we have supernovae? */ + if (e_sn == 0) { + return; + } + + const float mj = hydro_get_mass(pj); + const float r = sqrtf(r2); + + /* Get the kernel for hi. */ + float hi_inv = 1.0f / hi; + float hi_inv_dim = pow_dimension(hi_inv); /* 1/h^d */ + float xi = r * hi_inv; + float wi, wi_dx; + kernel_deval(xi, &wi, &wi_dx); + wi *= hi_inv_dim; + + /* Compute inverse enrichment weight */ + const double si_inv_weight = si->feedback_data.enrichment_weight == 0 + ? 0. + : 1. / si->feedback_data.enrichment_weight; + + /* Mass received */ + const double m_ej = si->feedback_data.mass_ejected; + // TODO compute inverse before feedback loop + const double weight = mj * wi * si_inv_weight; + const double dm = m_ej * weight; + const double new_mass = mj + dm; + + /* Energy received */ + // TODO compute inverse before feedback loop + const double du = e_sn * weight / new_mass; + + xpj->feedback_data.delta_mass += dm; + xpj->feedback_data.delta_u += du; + + /* Compute momentum received. */ + for (int i = 0; i < 3; i++) { + xpj->feedback_data.delta_p[i] += dm * (si->v[i] - xpj->v_full[i]); + } + + /* Add the metals */ + for (int i = 0; i < GEAR_CHEMISTRY_ELEMENT_COUNT; i++) { + pj->chemistry_data.metal_mass[i] += + weight * si->feedback_data.metal_mass_ejected[i]; + } + + /* Impose maximal viscosity */ + hydro_diffusive_feedback_reset(pj); + + /* Synchronize the particle on the timeline */ + timestep_sync_part(pj); +} + +#endif /* SWIFT_GEAR_FEEDBACK_IACT_H */ diff --git a/src/feedback/GEAR/feedback_properties.h b/src/feedback/GEAR/feedback_properties.h new file mode 100644 index 0000000000..4b2d4f207b --- /dev/null +++ b/src/feedback/GEAR/feedback_properties.h @@ -0,0 +1,102 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Coypright (c) 2018 Loic Hausammann (loic.hausammann@epfl.ch) + * + * 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 <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_GEAR_FEEDBACK_PROPERTIES_H +#define SWIFT_GEAR_FEEDBACK_PROPERTIES_H + +#include "chemistry.h" +#include "hydro_properties.h" +#include "stellar_evolution.h" +#include "stellar_evolution_struct.h" + +/** + * @brief Properties of the GEAR feedback model. + */ +struct feedback_props { + /*! Energy per supernovae */ + float energy_per_supernovae; + + /*! filename of the chemistry table */ + char filename[PARSER_MAX_LINE_SIZE]; + + /*! The stellar model */ + struct stellar_model stellar_model; +}; + +/** + * @brief Print the feedback model. + * + * @param feedback_props The #feedback_props + */ +__attribute__((always_inline)) INLINE static void feedback_props_print( + const struct feedback_props* feedback_props) { + + /* Only the master print */ + if (engine_rank != 0) { + return; + } + + /* Print the feedback properties */ + message("Energy per supernovae = %.2g", + feedback_props->energy_per_supernovae); + message("Yields table = %s", feedback_props->filename); + + /* Print the stellar model */ + stellar_model_print(&feedback_props->stellar_model); +} + +/** + * @brief Initialize the global properties of the feedback scheme. + * + * By default, takes the values provided by the hydro. + * + * @param fp The #feedback_props. + * @param phys_const The physical constants in the internal unit system. + * @param us The internal unit system. + * @param params The parsed parameters. + * @param hydro_props The already read-in properties of the hydro scheme. + * @param cosmo The cosmological model. + */ +__attribute__((always_inline)) INLINE static void feedback_props_init( + struct feedback_props* fp, const struct phys_const* phys_const, + const struct unit_system* us, struct swift_params* params, + const struct hydro_props* hydro_props, const struct cosmology* cosmo) { + + /* Supernovae energy */ + double e_feedback = + parser_get_param_double(params, "GEARFeedback:supernovae_energy_erg"); + e_feedback /= units_cgs_conversion_factor(us, UNIT_CONV_ENERGY); + fp->energy_per_supernovae = e_feedback; + + /* filename of the chemistry table */ + parser_get_param_string(params, "GEARFeedback:yields_table", fp->filename); + + /* Initialize the stellar model */ + stellar_evolution_props_init(&fp->stellar_model, phys_const, us, params, + cosmo); + + /* Print the stellar properties */ + feedback_props_print(fp); + + /* Print a final message. */ + if (engine_rank == 0) { + message("Stellar feedback initialized"); + } +} + +#endif /* SWIFT_GEAR_FEEDBACK_PROPERTIES_H */ diff --git a/src/feedback/GEAR/feedback_struct.h b/src/feedback/GEAR/feedback_struct.h new file mode 100644 index 0000000000..cd6e336ed7 --- /dev/null +++ b/src/feedback/GEAR/feedback_struct.h @@ -0,0 +1,63 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) + * + * 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 <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_FEEDBACK_STRUCT_GEAR_H +#define SWIFT_FEEDBACK_STRUCT_GEAR_H + +#include "chemistry_struct.h" + +/** + * @brief Feedback fields carried by each hydro particles + */ + +struct feedback_part_data { + /*! mass received from supernovae */ + float delta_mass; + + /*! specific energy received from supernovae */ + float delta_u; + + /*! Momemtum received from a supernovae */ + float delta_p[3]; +}; + +/** + * @brief Feedback fields carried by each star particles + */ +struct feedback_spart_data { + + /*! Inverse of normalisation factor used for the enrichment. */ + float enrichment_weight; + + /* Assign two different names for clarification */ + union { + /*! Number of supernovae */ + float number_sn; + + /*! Energy injected in the surrounding particles */ + float energy_ejected; + }; + + /*! Total mass ejected by the supernovae */ + float mass_ejected; + + /*! Chemical composition of the mass ejected */ + float metal_mass_ejected[GEAR_CHEMISTRY_ELEMENT_COUNT]; +}; + +#endif /* SWIFT_FEEDBACK_STRUCT_GEAR_H */ diff --git a/src/feedback/GEAR/hdf5_functions.h b/src/feedback/GEAR/hdf5_functions.h new file mode 100644 index 0000000000..3d7e9befa0 --- /dev/null +++ b/src/feedback/GEAR/hdf5_functions.h @@ -0,0 +1,135 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2019 Loic Hausammann (loic.hausammann@epfl.ch) + * + * 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 <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_HDF5_FUNCTIONS_GEAR_H +#define SWIFT_HDF5_FUNCTIONS_GEAR_H + +/* Local includes. */ +#include "chemistry.h" +#include "inline.h" + +/** + * @brief Reads a string attribute (array) from a given HDF5 group. + * + * @param grp The group from which to read. + * @param name The name of the attribute to read. + * @param data (output) The attribute read from the HDF5 group (need to be + * allocated). + * @param number_element Number of elements in the attribute. + * @param size_per_element Maximal size per element in data. + * + * Calls #error() if an error occurs. + */ +__attribute__((always_inline)) INLINE static void +io_read_string_array_attribute(hid_t grp, const char *name, void *data, + hsize_t number_element, + hsize_t size_per_element) { + + /* Open attribute */ + const hid_t h_attr = H5Aopen(grp, name, H5P_DEFAULT); + if (h_attr < 0) error("Error while opening attribute '%s'", name); + + /* Get the number of elements */ + hsize_t count = io_get_number_element_in_attribute(h_attr); + + /* Check if correct number of element */ + if (count != number_element) { + error( + "Error found a different number of elements than expected (%lli != " + "%lli) in attribute %s", + count, number_element, name); + } + + /* Get the string length */ + const hid_t type = H5Aget_type(h_attr); + if (type < 0) error("Failed to get attribute type"); + + size_t sdim = H5Tget_size(type); + + /* Check if the size is correct */ + if (sdim > size_per_element) { + error("Cannot read string longer than %lli in %s", size_per_element, name); + } + + /* Allocate the temporary array */ + char *tmp = malloc(sizeof(char) * sdim * number_element); + if (tmp == NULL) { + error("Failed to allocate the temporary array."); + } + + /* Read attribute */ + const hid_t h_err = H5Aread(h_attr, type, tmp); + if (h_err < 0) error("Error while reading attribute '%s'", name); + + /* Copy the attribute correctly */ + for (hsize_t i = 0; i < number_element; i++) { + char *src = tmp + i * sdim; + char *dest = data + i * size_per_element; + memcpy(dest, src, sdim); + } + + /* Cleanup */ + free(tmp); + H5Aclose(h_attr); +} + +/** + * @brief Open a group in the yields table (#h5_close_group needs to be called). + * + * @param params The @swift_params. + * @param group_name The name of the group to open. + * @param file_id (output) The id of the file opened. + * @param group_id (output) The id of the group opened. + * + */ +__attribute__((always_inline)) INLINE static void h5_open_group( + struct swift_params *params, char *group_name, hid_t *file_id, + hid_t *group_id) { + + /* Get filename. */ + char filename[256]; + parser_get_param_string(params, "GEARFeedback:yields_table", filename); + + /* Open file. */ + *file_id = H5Fopen(filename, H5F_ACC_RDONLY, H5P_DEFAULT); + if (*file_id < 0) error("unable to open file %s.\n", filename); + + /* Open group. */ + *group_id = H5Gopen(*file_id, group_name, H5P_DEFAULT); + if (*group_id < 0) error("unable to open group %s.\n", group_name); +} + +/** + * @brief Close a group in the yields table. + * + * @param file_id The id of the file opened. + * @param group_id The id of the group opened. + * + */ +__attribute__((always_inline)) INLINE static void h5_close_group( + hid_t file_id, hid_t group_id) { + + /* Close group */ + hid_t status = H5Gclose(group_id); + if (status < 0) error("error closing group."); + + /* Close file */ + status = H5Fclose(file_id); + if (status < 0) error("error closing file."); +} +#endif // SWIFT_HDF5_FUNCTIONS_GEAR_H diff --git a/src/feedback/GEAR/initial_mass_function.c b/src/feedback/GEAR/initial_mass_function.c new file mode 100644 index 0000000000..cb165648d2 --- /dev/null +++ b/src/feedback/GEAR/initial_mass_function.c @@ -0,0 +1,543 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2019 Loic Hausammann (loic.hausammann@epfl.ch) + * + * 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 <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ + +/* Include header */ +#include "initial_mass_function.h" + +/* local headers */ +#include "hdf5_functions.h" +#include "stellar_evolution_struct.h" + +/** + * @brief Get the IMF exponent in between mass_min and mass_max. + */ +float initial_mass_function_get_exponent( + const struct initial_mass_function *imf, float mass_min, float mass_max) { + +#ifdef SWIFT_DEBUG_CHECKS + if (mass_max > imf->mass_max) + error("Cannot have mass larger than the largest one in the IMF"); + if (mass_min < imf->mass_min) + error("Cannot have mass smaller than the smallest one in the IMF"); + if (mass_max < mass_min) error("Cannot have mass_min larger than mass_max"); +#endif + + for (int i = 0; i < imf->n_parts; i++) { + + /* Check if in the correct part of the IMF */ + if (mass_min < imf->mass_limits[i + 1]) { + + /* Check if in only one segment */ + if (mass_max > imf->mass_limits[i + 1]) { + error("Cannot get a single exponent for the interval [%g, %g]", + mass_min, mass_max); + } + + return imf->exp[i]; + } + } + + error("Masses outside IMF ranges"); + + return -1; +} + +/** @brief Print the initial mass function */ +void initial_mass_function_print(const struct initial_mass_function *imf) { + + message("Number of parts: %i", imf->n_parts); + message("Mass interval: [%g, %g]", imf->mass_min, imf->mass_max); + for (int i = 0; i < imf->n_parts; i++) { + message("[%g, %g]: %.2g * m^{%g}", imf->mass_limits[i], + imf->mass_limits[i + 1], imf->coef[i], imf->exp[i]); + } +} + +/** + * @brief Integrate the #interpolation_1d data with the initial mass function. + * + * The x are supposed to be linear in log. + * + * @param imf The #initial_mass_function. + * @param interp The #interpolation_1d. + */ +void initial_mass_function_integrate(const struct initial_mass_function *imf, + struct interpolation_1d *interp) { + + /* Index in the data */ + int j = 1; + const float mass_min = pow(10, interp->xmin); + const float mass_max = pow(10, interp->xmin + (interp->N - 1) * interp->dx); + + float m = mass_min; + + float *tmp = (float *)malloc(sizeof(float) * interp->N); + + /* Set lower limit */ + tmp[0] = 0; + for (int i = 0; i < imf->n_parts; i++) { + + /* Check if already in the correct part */ + if (mass_min > imf->mass_limits[i + 1]) { + continue; + } + + /* Check if already above the maximal mass */ + if (mass_max < imf->mass_limits[i]) { + break; + } + + /* Integrate the data */ + while (m < imf->mass_limits[i + 1] && j < interp->N) { + + /* Compute the masses */ + const float log_m1 = interp->xmin + (j - 1) * interp->dx; + const float m1 = pow(10, log_m1); + const float log_m2 = interp->xmin + j * interp->dx; + const float m2 = pow(10, log_m2); + const float dm = m2 - m1; + const float imf_1 = imf->coef[i] * pow(m1, imf->exp[i]); + + /* Get the imf of the upper limit */ + float imf_2; + if (m2 > imf->mass_limits[i + 1]) { + imf_2 = imf->coef[i + 1] * pow(m2, imf->exp[i + 1]); + } else { + imf_2 = imf->coef[i] * pow(m2, imf->exp[i]); + } + + /* Compute the integral */ + tmp[j] = + tmp[j - 1] + + 0.5 * (imf_1 * interp->data[j - 1] + imf_2 * interp->data[j]) * dm; + + /* Update j and m */ + j += 1; + m = m2; + } + } + + /* The rest is extrapolated with 0 */ + for (int k = j; k < interp->N; k++) { + tmp[k] = tmp[k - 1]; + } + + /* Copy temporary array */ + memcpy(interp->data, tmp, interp->N * sizeof(float)); + + /* Update the boundary conditions */ + interp->boundary_condition = boundary_condition_zero_const; + + /* clean everything */ + free(tmp); +} + +/** + * @brief Get the IMF coefficient in between mass_min and mass_max. + * + * @param imf The #initial_mass_function. + * @param mass_min The minimal mass of the requested interval. + * @param mass_max The maximal mass of the requested interval. + * + * @return The imf's coefficient of the interval. + */ +float initial_mass_function_get_coefficient( + const struct initial_mass_function *imf, float mass_min, float mass_max) { + + for (int i = 0; i < imf->n_parts; i++) { + + /* Check if in the correct part of the IMF */ + if (mass_min < imf->mass_limits[i + 1]) { + + /* Check if in only one segment */ + if (mass_max > imf->mass_limits[i + 1]) { + error("Cannot get a single coefficient for the interval [%g, %g]", + mass_min, mass_max); + } + + return imf->coef[i]; + } + } + + error("Masses outside IMF ranges"); + + return -1; +} + +/** + * @brief Compute the integral of the fraction number of the initial mass + * function. + * + * @param imf The #initial_mass_function. + * @param m1 The lower mass to evaluate. + * @param m2 The upper mass to evaluate. + * + * @return The number fraction. + */ +float initial_mass_function_get_integral_xi( + const struct initial_mass_function *imf, float m1, float m2) { + + int k = -1; + /* Find the correct part */ + for (int i = 0; i < imf->n_parts; i++) { + if (m1 <= imf->mass_limits[i + 1]) { + k = i; + break; + } + } + + /* Check if found a part */ + if (k == -1) { + error("Failed to find the correct function part: %g %g", m1, m2); + } + + /* Check if m2 is inside the part */ + if (m2 < imf->mass_limits[k] || m2 > imf->mass_limits[k + 1]) { + error("This function is not able to integrate in two different parts %g %g", + m1, m2); + } + + /* Compute the integral */ + const float int_xi1 = pow(m1, imf->exp[k]); + const float int_xi2 = pow(m2, imf->exp[k]); + + return imf->coef[k] * (int_xi2 - int_xi1) / imf->exp[k]; +}; + +/** + * @brief Compute the mass fraction of the initial mass function. + * + * @param imf The #initial_mass_function. + * @param m The mass to evaluate. + * + * @return The mass fraction. + */ +float initial_mass_function_get_imf(const struct initial_mass_function *imf, + float m) { + +#ifdef SWIFT_DEBUG_CHECKS + if (m > imf->mass_max || m < imf->mass_min) + error("Mass below or above limits expecting %g < %g < %g.", imf->mass_min, + m, imf->mass_max); +#endif + + for (int i = 0; i < imf->n_parts; i++) { + if (m <= imf->mass_limits[i + 1]) { + return imf->coef[i] * pow(m, imf->exp[i]); + } + } + + error("Failed to find correct function part: %g larger than mass max %g.", m, + imf->mass_max); + return 0.; +}; + +/** + * @brief Compute the integral of the mass fraction of the initial mass + * function. + * + * @param imf The #initial_mass_function. + * @param m1 The lower mass to evaluate. + * @param m2 The upper mass to evaluate. + * + * @return The integral of the mass fraction. + */ +float initial_mass_function_get_integral_imf( + const struct initial_mass_function *imf, const float m1, const float m2) { + +#ifdef SWIFT_DEBUG_CHECKS + if (m1 > imf->mass_max || m1 < imf->mass_min) + error("Mass 1 below or above limits expecting %g < %g < %g.", imf->mass_min, + m1, imf->mass_max); + if (m2 > imf->mass_max || m2 < imf->mass_min) + error("Mass 2 below or above limits expecting %g < %g < %g.", imf->mass_min, + m2, imf->mass_max); +#endif + + for (int i = 0; i < imf->n_parts; i++) { + if (m1 <= imf->mass_limits[i + 1]) { + if (m2 < imf->mass_limits[i] || m2 > imf->mass_limits[i + 1]) { + error( + "The code does not support the integration over multiple parts of " + "the IMF"); + } + const float exp = imf->exp[i] + 1.; + return imf->coef[i] * (pow(m2, exp) - pow(m1, exp)) / exp; + } + } + + error("Failed to find correct function part: %g, %g larger than mass max %g.", + m1, m2, imf->mass_max); + return 0.; +}; + +/** + * @brief Compute the coefficients of the initial mass function. + * + * @param imf The #initial_mass_function. + */ +void initial_mass_function_compute_coefficients( + struct initial_mass_function *imf) { + + /* Allocate memory */ + if ((imf->coef = (float *)malloc(sizeof(float) * imf->n_parts)) == NULL) + error("Failed to allocate the IMF coefficients."); + + /* Suppose that the first coefficients is 1 (will be corrected later) */ + imf->coef[0] = 1.; + + /* Use the criterion of continuity for the IMF */ + for (int i = 1; i < imf->n_parts; i++) { + float exp = imf->exp[i - 1] - imf->exp[i]; + imf->coef[i] = imf->coef[i - 1] * pow(imf->mass_limits[i], exp); + } + + /* Use the criterion on the integral = 1 */ + float integral = 0; + for (int i = 0; i < imf->n_parts; i++) { + const float exp = imf->exp[i] + 1.; + const float m_i = pow(imf->mass_limits[i], exp); + const float m_i1 = pow(imf->mass_limits[i + 1], exp); + integral += imf->coef[i] * (m_i1 - m_i) / exp; + } + + /* Normalize the coefficients (fix initial supposition) */ + for (int i = 0; i < imf->n_parts; i++) { + imf->coef[i] /= integral; + } +} + +/** + * @brief Reads the initial mass function parameters from the tables. + * + * @param imf The #initial_mass_function. + * @param params The #swift_params. + */ +void initial_mass_function_read_from_table(struct initial_mass_function *imf, + struct swift_params *params) { + + hid_t file_id, group_id; + + /* Open IMF group */ + h5_open_group(params, "Data/IMF", &file_id, &group_id); + + /* Read number of parts */ + io_read_attribute(group_id, "n", INT, &imf->n_parts); + + /* The tables have a different definition of n */ + imf->n_parts += 1; + + /* Allocate the memory for the exponents */ + if ((imf->exp = (float *)malloc(sizeof(float) * imf->n_parts)) == NULL) + error("Failed to allocate the IMF exponents."); + + /* Read the exponents */ + io_read_array_attribute(group_id, "as", FLOAT, imf->exp, imf->n_parts); + + /* Allocate the memory for the temporary mass limits */ + if ((imf->mass_limits = + (float *)malloc(sizeof(float) * (imf->n_parts + 1))) == NULL) + error("Failed to allocate the IMF masses."); + + /* Read the mass limits */ + io_read_array_attribute(group_id, "ms", FLOAT, imf->mass_limits, + imf->n_parts - 1); + + /* Copy the data (need to shift for mass_min) */ + for (int i = imf->n_parts - 1; i > 0; i--) { + imf->mass_limits[i] = imf->mass_limits[i - 1]; + } + + /* Read the minimal mass limit */ + io_read_attribute(group_id, "Mmin", FLOAT, &imf->mass_limits[0]); + + /* Read the maximal mass limit */ + io_read_attribute(group_id, "Mmax", FLOAT, &imf->mass_limits[imf->n_parts]); + + /* Close everything */ + h5_close_group(file_id, group_id); +} + +/** + * @brief Reads the parameters file and if required overwrites the parameters + * found in the yields table. + * + * @param imf The #initial_mass_function. + * @param params The #swift_params. + */ +void initial_mass_function_read_from_params(struct initial_mass_function *imf, + struct swift_params *params) { + + /* Read the number of elements */ + const int n_parts = parser_get_opt_param_int( + params, "GEARInitialMassFunction:number_function_part", imf->n_parts); + + const int n_parts_changed = n_parts != imf->n_parts; + imf->n_parts = n_parts; + + /* Reallocate the exponent memory */ + if (n_parts_changed) { + free(imf->exp); + if ((imf->exp = (float *)malloc(sizeof(float) * imf->n_parts)) == NULL) + error("Failed to allocate the IMF exponents."); + } + + /* Read the exponents */ + const char *exponent_name = "GEARInitialMassFunction:exponents"; + if (n_parts_changed) { + parser_get_param_float_array(params, exponent_name, imf->n_parts, imf->exp); + } else { + parser_get_opt_param_float_array(params, exponent_name, imf->n_parts, + imf->exp); + } + + /* Reallocate the mass limits memory */ + if (n_parts_changed) { + free(imf->mass_limits); + if ((imf->mass_limits = + (float *)malloc(sizeof(float) * (imf->n_parts + 1))) == NULL) + error("Failed to allocate the IMF masses."); + } + + /* Read the mass limits */ + const char *mass_limits_name = "GEARInitialMassFunction:mass_limits_msun"; + if (n_parts_changed) { + parser_get_param_float_array(params, mass_limits_name, imf->n_parts + 1, + imf->mass_limits); + } else { + parser_get_opt_param_float_array(params, mass_limits_name, imf->n_parts + 1, + imf->mass_limits); + } +} + +/** + * @brief Initialize the initial mass function. + * + * @param imf The #initial_mass_function. + * @param phys_const The #phys_const. + * @param us The #unit_system. + * @param params The #swift_params. + */ +void initial_mass_function_init(struct initial_mass_function *imf, + const struct phys_const *phys_const, + const struct unit_system *us, + struct swift_params *params) { + + /* Read the parameters from the yields table */ + initial_mass_function_read_from_table(imf, params); + + /* Overwrites the parameters if found in the params file */ + initial_mass_function_read_from_params(imf, params); + + /* Write the masses in the correct attributes */ + imf->mass_min = imf->mass_limits[0]; + imf->mass_max = imf->mass_limits[imf->n_parts]; + + /* Compute the coefficients */ + initial_mass_function_compute_coefficients(imf); +} + +/** + * @brief Write a initial_mass_function struct to the given FILE as a stream of + * bytes. + * + * Here we are only writing the arrays, everything else has been copied in the + * feedback. + * + * @param imf the struct + * @param stream the file stream + * @param sm The #stellar_model. + */ +void initial_mass_function_dump(const struct initial_mass_function *imf, + FILE *stream, const struct stellar_model *sm) { + + /* Dump the mass limits. */ + if (imf->mass_limits != NULL) { + restart_write_blocks((void *)imf->mass_limits, sizeof(float), + imf->n_parts + 1, stream, "imf_mass_limits", + "imf_mass_limits"); + } + + /*! Dump the exponents. */ + if (imf->exp != NULL) { + restart_write_blocks((void *)imf->exp, sizeof(float), imf->n_parts, stream, + "imf_exponents", "imf_exponents"); + } + + /*! Dump the coefficients. */ + if (imf->coef != NULL) { + restart_write_blocks((void *)imf->coef, sizeof(float), imf->n_parts, stream, + "imf_coef", "imf_coef"); + } +} + +/** + * @brief Restore a initial_mass_function struct from the given FILE as a stream + * of bytes. + * + * Here we are only writing the arrays, everything else has been copied in the + * feedback. + * + * @param imf the struct + * @param stream the file stream + * @param sm The #stellar_model. + */ +void initial_mass_function_restore(struct initial_mass_function *imf, + FILE *stream, + const struct stellar_model *sm) { + + /* Restore the mass limits */ + if (imf->mass_limits != NULL) { + imf->mass_limits = (float *)malloc(sizeof(float) * imf->n_parts + 1); + restart_read_blocks((void *)imf->mass_limits, sizeof(float), + imf->n_parts + 1, stream, NULL, "imf_mass_limits"); + } + + /* Restore the exponents */ + if (imf->exp != NULL) { + imf->exp = (float *)malloc(sizeof(float) * imf->n_parts); + restart_read_blocks((void *)imf->exp, sizeof(float), imf->n_parts, stream, + NULL, "imf_exponents"); + } + + /* Restore the coefficients */ + if (imf->coef != NULL) { + imf->coef = (float *)malloc(sizeof(float) * imf->n_parts); + restart_read_blocks((void *)imf->coef, sizeof(float), imf->n_parts, stream, + NULL, "imf_coef"); + } +} + +/** + * @brief Clean the allocated memory. + * + * @param imf the #initial_mass_function. + */ +void initial_mass_function_clean(struct initial_mass_function *imf) { + + /* Free the pointers */ + free(imf->mass_limits); + imf->mass_limits = NULL; + + free(imf->exp); + imf->exp = NULL; + + free(imf->coef); + imf->coef = NULL; +} diff --git a/src/feedback/GEAR/initial_mass_function.h b/src/feedback/GEAR/initial_mass_function.h new file mode 100644 index 0000000000..bf29e7bada --- /dev/null +++ b/src/feedback/GEAR/initial_mass_function.h @@ -0,0 +1,60 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2019 Loic Hausammann (loic.hausammann@epfl.ch) + * + * 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 <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_INITIAL_MASS_FUNCTION_GEAR_H +#define SWIFT_INITIAL_MASS_FUNCTION_GEAR_H + +#include "hdf5_functions.h" +#include "stellar_evolution_struct.h" + +float initial_mass_function_get_exponent( + const struct initial_mass_function *imf, float mass_min, float mass_max); +void initial_mass_function_print(const struct initial_mass_function *imf); + +void initial_mass_function_integrate(const struct initial_mass_function *imf, + struct interpolation_1d *interp); +float initial_mass_function_get_coefficient( + const struct initial_mass_function *imf, float mass_min, float mass_max); +float initial_mass_function_get_integral_xi( + const struct initial_mass_function *imf, float m1, float m2); +float initial_mass_function_get_imf(const struct initial_mass_function *imf, + float m); +float initial_mass_function_get_integral_imf( + const struct initial_mass_function *imf, const float m1, const float m2); +void initial_mass_function_compute_coefficients( + struct initial_mass_function *imf); + +void initial_mass_function_read_from_table(struct initial_mass_function *imf, + struct swift_params *params); +void initial_mass_function_read_from_params(struct initial_mass_function *imf, + struct swift_params *params); + +void initial_mass_function_init(struct initial_mass_function *imf, + const struct phys_const *phys_const, + const struct unit_system *us, + struct swift_params *params); + +void initial_mass_function_dump(const struct initial_mass_function *imf, + FILE *stream, const struct stellar_model *sm); + +void initial_mass_function_restore(struct initial_mass_function *imf, + FILE *stream, + const struct stellar_model *sm); + +void initial_mass_function_clean(struct initial_mass_function *imf); +#endif // SWIFT_INITIAL_MASS_FUNCTION_GEAR_H diff --git a/src/feedback/GEAR/interpolation.h b/src/feedback/GEAR/interpolation.h new file mode 100644 index 0000000000..37706cda73 --- /dev/null +++ b/src/feedback/GEAR/interpolation.h @@ -0,0 +1,207 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2019 Loic Hausammann (loic.hausammann@epfl.ch) + * + * 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 <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_GEAR_INTERPOLATION_H +#define SWIFT_GEAR_INTERPOLATION_H + +enum interpolate_boundary_condition { + /* No extrapolation => raise errors */ + boundary_condition_error, + + /* Zero as boundary conditions */ + boundary_condition_zero, + + /* Zero (left boundary) and constant (right boundary) boundary conditions */ + boundary_condition_zero_const, +}; + +struct interpolation_1d { + /* Data to interpolate */ + float *data; + + /* Minimal x */ + float xmin; + + /* Step size between x points */ + float dx; + + /* Number of element in the data */ + int N; + + /* Type of boundary conditions. */ + enum interpolate_boundary_condition boundary_condition; +}; + +/** + * @brief Initialize the #interpolation_1d. + * + * Assumes x are linear in log. + * + * @params interp The #interpolation_1d. + * @params xmin Minimal value of x (in log). + * @params xmax Maximal value of x (in log). + * @params N Requested number of values. + * @params log_data_xmin The minimal value of the data (in log). + * @params step_size The size of the x steps (in log). + * @params N_data The number of element in the data. + * @params data The data to interpolate (y). + * @params N The number of element in data. + * @params boundary_condition The type of #interpolate_boundary_condition. + */ +__attribute__((always_inline)) static INLINE void interpolate_1d_init( + struct interpolation_1d *interp, float xmin, float xmax, int N, + float log_data_xmin, float step_size, int N_data, const float *data, + enum interpolate_boundary_condition boundary_condition) { + + /* Save the variables */ + interp->N = N; + interp->xmin = xmin; + interp->dx = (xmax - xmin) / (N - 1.f); + interp->boundary_condition = boundary_condition; + + /* Allocate the memory */ + interp->data = malloc(sizeof(float) * N); + if (interp->data == NULL) + error("Failed to allocate memory for the interpolation"); + + /* Interpolate the data */ + for (int i = 0; i < N; i++) { + const float log_x = xmin + i * interp->dx; + const float x_j = (log_x - log_data_xmin) / step_size; + + /* Check boundaries */ + if (x_j < 0) { + switch (boundary_condition) { + case boundary_condition_error: + error("Cannot extrapolate"); + break; + case boundary_condition_zero: + interp->data[i] = 0; + break; + case boundary_condition_zero_const: + interp->data[i] = 0; + break; + default: + error("Interpolation type not implemented"); + } + continue; + } else if (x_j >= N_data) { + switch (boundary_condition) { + case boundary_condition_error: + error("Cannot extrapolate"); + break; + case boundary_condition_zero: + interp->data[i] = 0; + break; + case boundary_condition_zero_const: + interp->data[i] = interp->data[i - 1]; + break; + default: + error("Interpolation type not implemented"); + } + continue; + } + + /* Interpolate i */ + const int j = x_j; + const float f = x_j - j; + interp->data[i] = (1. - f) * data[j] + f * data[j + 1]; + } +} + +/** + * @brief Interpolate the data. + * + * @params interp The #interpolation_1d. + * @params x The x value where to interpolate. + * + * @return The interpolated value y. + */ +__attribute__((always_inline)) static INLINE float interpolate_1d( + const struct interpolation_1d *interp, float x) { + + /* Find indice */ + const float i = (x - interp->xmin) / interp->dx; + const int idx = i; + const float dx = i - idx; + + /* Should we extrapolate? */ + if (i < 0) { + switch (interp->boundary_condition) { + case boundary_condition_error: + error("Cannot extrapolate"); + break; + case boundary_condition_zero: + case boundary_condition_zero_const: + return 0; + default: + error("Interpolation type not implemented"); + } + } else if (i >= interp->N - 1) { + switch (interp->boundary_condition) { + case boundary_condition_error: + error("Cannot extrapolate"); + break; + case boundary_condition_zero: + return 0; + case boundary_condition_zero_const: + return interp->data[interp->N - 1]; + default: + error("Interpolation type not implemented"); + } + } + + /* interpolate */ + return interp->data[idx] * (1. - dx) + interp->data[idx + 1] * dx; +} + +/** + * @brief Print the data. + * + * @params interp The #interpolation_1d. + */ +__attribute__((always_inline)) static INLINE void interpolate_1d_print( + const struct interpolation_1d *interp) { + + message("Interpolation between %g and %g", interp->xmin, + interp->xmin + interp->dx * interp->N); + + message("Contains %i values and use the boundary condition %i", interp->N, + interp->boundary_condition); + + /* Print values */ + for (int i = 0; i < interp->N; i++) { + float x = interp->xmin + i * interp->dx; + message("%.2g: %g", x, interp->data[i]); + } +} + +/** + * @brief Cleanup the #interpolation_1d structure. + * + * @params interp The #interpolation_1d. + */ +__attribute__((always_inline)) static INLINE void interpolate_1d_free( + struct interpolation_1d *interp) { + + /* Free the allocated memory */ + free(interp->data); + interp->data = NULL; +} + +#endif // SWIFT_GEAR_INTERPOLATION_H diff --git a/src/feedback/GEAR/lifetime.h b/src/feedback/GEAR/lifetime.h new file mode 100644 index 0000000000..78a7ac16b2 --- /dev/null +++ b/src/feedback/GEAR/lifetime.h @@ -0,0 +1,242 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2019 Loic Hausammann (loic.hausammann@epfl.ch) + * + * 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 <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_LIFETIME_GEAR_H +#define SWIFT_LIFETIME_GEAR_H + +#include "hdf5_functions.h" +#include "stellar_evolution_struct.h" + +/** + * @brief Print the lifetime model. + * + * @param lf The #lifetime. + */ +__attribute__((always_inline)) INLINE static void lifetime_print( + const struct lifetime* lf) { + + /* Only the master print */ + if (engine_rank != 0) { + return; + } + + message("Quadratic terms: %.2g %.2g %.2g", lf->quadratic[0], lf->quadratic[1], + lf->quadratic[2]); + + message("Linear terms: %.2g %.2g %.2g", lf->linear[0], lf->linear[1], + lf->linear[2]); + + message("Constant terms: %.2g %.2g %.2g", lf->constant[0], lf->constant[1], + lf->constant[2]); +} + +/** + * @brief Compute the lifetime of a star. + * + * @param life The #lifetime model. + * @param log_mass The star's mass (in log10(solMass)). + * @param metallicity The star's metallicity. + * + * @return The star's lifetime (in log10(Myr)). + */ +__attribute__((always_inline)) INLINE static float +lifetime_get_log_lifetime_from_mass(const struct lifetime* life, float log_mass, + float metallicity) { + + /* Compute quadratic term */ + const float quadratic = + (life->quadratic[0] * metallicity + life->quadratic[1]) * metallicity + + life->quadratic[2]; + /* Compute linear term */ + const float linear = + (life->linear[0] * metallicity + life->linear[1]) * metallicity + + life->linear[2]; + /* Compute constant term */ + const float constant = + (life->constant[0] * metallicity + life->constant[1]) * metallicity + + life->constant[2]; + + /* Compute lifetime */ + return (quadratic * log_mass + linear) * log_mass + constant; +} + +/** + * @brief Compute the mass of a star with a given lifetime + * + * @param life The #lifetime model. + * @param log_time The star's lifetime (in log10(Myr)). + * @param metallicity The star's metallicity. + * + * @return The star's mass (in log10(solMass)) + */ +__attribute__((always_inline)) INLINE static float +lifetime_get_log_mass_from_lifetime(const struct lifetime* life, float log_time, + float metallicity) { + + /* Compute quadratic term */ + const float quadratic = + (life->quadratic[0] * metallicity + life->quadratic[1]) * metallicity + + life->quadratic[2]; + /* Compute linear term */ + const float linear = + (life->linear[0] * metallicity + life->linear[1]) * metallicity + + life->linear[2]; + /* Compute constant term */ + const float constant = + (life->constant[0] * metallicity + life->constant[1]) * metallicity + + life->constant[2]; + + /* Compute the "c" with the time */ + const float c_t = constant - log_time; + + /* Use the quadratic formula to find the mass */ + if (quadratic != 0) { + const float delta = linear * linear - 4 * quadratic * c_t; + + /* Avoid complex number should not happen in real simulation */ + if (delta < 0) { + return -linear / (2. * quadratic); + } else { + return (-linear - sqrt(delta)) / (2. * quadratic); + } + } else { + return -c_t / linear; + } +} + +/** + * @brief Read lifetime parameters from tables. + * + * @param lt The #lifetime. + * @param params The #swift_params. + */ +__attribute__((always_inline)) INLINE static void lifetime_read_from_tables( + struct lifetime* lt, struct swift_params* params) { + + hid_t file_id, group_id; + + /* Open IMF group */ + h5_open_group(params, "Data/LiveTimes", &file_id, &group_id); + + /* Allocate the temporary array */ + float* tmp; + if ((tmp = (float*)malloc(sizeof(float) * 9)) == NULL) + error("Failed to allocate the temporary array."); + + /* Read the coefficients */ + io_read_array_dataset(group_id, "coeff_z", FLOAT, tmp, 9); + + /* Copy the coefficents */ + const int dim = 3; + for (int i = 0; i < dim; i++) { + lt->quadratic[i] = tmp[i]; + lt->linear[i] = tmp[i + dim]; + lt->constant[i] = tmp[i + 2 * dim]; + } + + /* Change units from yr into Myr */ + lt->constant[dim - 1] -= 6; + + /* Cleanup everything */ + free(tmp); + h5_close_group(file_id, group_id); +} + +/** + * @brief Read lifetime parameters from params. + * + * @param lt The #lifetime. + * @param params The #swift_params. + */ +__attribute__((always_inline)) INLINE static void lifetime_read_from_params( + struct lifetime* lt, struct swift_params* params) { + + /* Read quadratic terms */ + parser_get_opt_param_float_array(params, "GEARLifetime:quadratic", 3, + lt->quadratic); + + /* Read linear terms */ + parser_get_opt_param_float_array(params, "GEARLifetime:linear", 3, + lt->linear); + + /* Read constant terms */ + parser_get_opt_param_float_array(params, "GEARLifetime:constant", 3, + lt->constant); +} + +/** + * @brief Inititialize the Lifetime. + * + * @param lt The #lifetime. + * @param phys_const The #phys_const. + * @param us The #unit_system. + * @param params The #swift_params. + */ +__attribute__((always_inline)) INLINE static void lifetime_init( + struct lifetime* lt, const struct phys_const* phys_const, + const struct unit_system* us, struct swift_params* params) { + + /* Read params from yields table */ + lifetime_read_from_tables(lt, params); + + /* overwrite the parameters if found in the params */ + lifetime_read_from_params(lt, params); +} + +/** + * @brief Write a lifetime struct to the given FILE as a stream of bytes. + * + * Here we are only writing the arrays, everything else has been copied in the + * feedback. + * + * @param lt the struct + * @param stream the file stream + * @param sm The #stellar_model. + */ +__attribute__((always_inline)) INLINE static void lifetime_dump( + const struct lifetime* lt, FILE* stream, const struct stellar_model* sm) { + + /* Nothing to do here */ +} + +/** + * @brief Restore a lifetime struct from the given FILE as a stream of + * bytes. + * + * Here we are only writing the arrays, everything else has been copied in the + * feedback. + * + * @param lt the struct + * @param stream the file stream + * @param sm The #stellar_model. + */ +__attribute__((always_inline)) INLINE static void lifetime_restore( + struct lifetime* lt, FILE* stream, const struct stellar_model* sm) { + + /* Nothing to do here */ +} + +/** + * @brief Clean the allocated memory. + * + * @param lifetime the #lifetime. + */ +__attribute__((always_inline)) INLINE static void lifetime_clean( + struct lifetime* lifetime) {} + +#endif // SWIFT_LIFETIME_GEAR_H diff --git a/src/feedback/GEAR/stellar_evolution.c b/src/feedback/GEAR/stellar_evolution.c new file mode 100644 index 0000000000..15853d0ff9 --- /dev/null +++ b/src/feedback/GEAR/stellar_evolution.c @@ -0,0 +1,502 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2019 Loic Hausammann (loic.hausammann@epfl.ch) + * + * 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 <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ + +/* Include header */ +#include "stellar_evolution.h" + +/* Include local headers */ +#include "hdf5_functions.h" +#include "initial_mass_function.h" +#include "lifetime.h" +#include "random.h" +#include "stellar_evolution_struct.h" +#include "supernovae_ia.h" +#include "supernovae_ii.h" + +#include <math.h> +#include <stddef.h> + +/** + * @brief Print the stellar model. + * + * @param sm The #stellar_model. + */ +void stellar_model_print(const struct stellar_model* sm) { + + /* Only the master print */ + if (engine_rank != 0) { + return; + } + + /* Print the type of yields */ + message("Discrete yields? %i", sm->discrete_yields); + + /* Print the sub properties */ + initial_mass_function_print(&sm->imf); + lifetime_print(&sm->lifetime); + supernovae_ia_print(&sm->snia); + supernovae_ii_print(&sm->snii); +} + +/** + * @brief Compute the integer number of supernovae from the floating number. + * + * @param sp The particle to act upon + * @param number_supernovae_f Floating number of supernovae during this step. + * @param ti_begin The #integertime_t at the begining of the step. + * @param random_type The categorie of random. + * + * @return The integer number of supernovae. + */ +int stellar_evolution_compute_integer_number_supernovae( + struct spart* restrict sp, float number_supernovae_f, + const integertime_t ti_begin, enum random_number_type random_type) { + + const int number_supernovae_i = floor(number_supernovae_f); + + /* Get the random number for the decimal part */ + const float rand_sn = random_unit_interval(sp->id, ti_begin, random_type); + + /* Get the fraction part */ + const float frac_sn = number_supernovae_f - number_supernovae_i; + + /* Get the integer number of SN */ + return number_supernovae_i + ((rand_sn < frac_sn) ? 1 : 0); +} + +/** + * @brief Compute the feedback properties. + * + * @param sp The particle to act upon + * @param sm The #stellar_model structure. + * @param phys_const The physical constants in the internal unit system. + * @param log_m_beg_step Mass of a star ending its life at the begining of the + * step (log10(solMass)) + * @param log_m_end_step Mass of a star ending its life at the end of the step + * (log10(solMass)) + * @param m_beg_step Mass of a star ending its life at the begining of the step + * (solMass) + * @param m_end_step Mass of a star ending its life at the end of the step + * (solMass) + * @param number_snia Number of SNIa produced by the stellar particle. + * @param number_snii Number of SNII produced by the stellar particle. + * + */ +void stellar_evolution_compute_continuous_feedback_properties( + struct spart* restrict sp, const struct stellar_model* sm, + const struct phys_const* phys_const, const float log_m_beg_step, + const float log_m_end_step, const float m_beg_step, const float m_end_step, + const float m_init, const int number_snia, const int number_snii) { + + /* Compute the mass ejected */ + /* SNIa */ + const float mass_frac_snia = + supernovae_ia_get_ejected_mass_processed(&sm->snia) * + supernovae_ia_get_number(&sm->snia, m_end_step, m_beg_step); + + /* SNII */ + const float mass_frac_snii = + supernovae_ii_get_ejected_mass_fraction_processed( + &sm->snii, log_m_end_step, log_m_beg_step); + + sp->feedback_data.mass_ejected = (mass_frac_snia + mass_frac_snii) * m_init; + + /* Transform into internal units */ + sp->feedback_data.mass_ejected *= phys_const->const_solar_mass; + + if (sp->mass <= sp->feedback_data.mass_ejected) { + error("Stars cannot have negative mass. (%g <= %g). Initial mass = %g", + sp->mass, sp->feedback_data.mass_ejected, sp->birth.mass); + } + + /* Update the mass */ + sp->mass -= sp->feedback_data.mass_ejected; + + /* Now deal with the metals */ + + /* Get the SNIa yields */ + const float* snia_yields = supernovae_ia_get_yields(&sm->snia); + + /* Compute the SNII yields */ + float snii_yields[GEAR_CHEMISTRY_ELEMENT_COUNT]; + supernovae_ii_get_yields(&sm->snii, log_m_end_step, log_m_beg_step, + snii_yields); + + /* Compute the mass fraction of non processed elements */ + const float non_processed = supernovae_ii_get_ejected_mass_fraction( + &sm->snii, log_m_end_step, log_m_beg_step); + + /* Set the yields */ + for (int i = 0; i < GEAR_CHEMISTRY_ELEMENT_COUNT; i++) { + /* Compute the mass fraction of metals */ + sp->feedback_data.metal_mass_ejected[i] = + /* Supernovae Ia yields */ + snia_yields[i] * number_snia + + /* Supernovae II yields */ + snii_yields[i] + + /* Gas contained in stars initial metallicity */ + chemistry_get_metal_mass_fraction_for_feedback(sp)[i] * non_processed; + + /* Convert it to total mass */ + sp->feedback_data.metal_mass_ejected[i] *= sp->birth.mass; + } +} + +/** + * @brief Compute the feedback properties. + * + * @param sp The particle to act upon + * @param sm The #stellar_model structure. + * @param phys_const The physical constants in the internal unit system. + * @param log_m_beg_step Mass of a star ending its life at the begining of the + * step (log10(solMass)) + * @param log_m_end_step Mass of a star ending its life at the end of the step + * (log10(solMass)) + * @param m_beg_step Mass of a star ending its life at the begining of the step + * (solMass) + * @param m_end_step Mass of a star ending its life at the end of the step + * (solMass) + * @param number_snia Number of SNIa produced by the stellar particle. + * @param number_snii Number of SNII produced by the stellar particle. + * + */ +void stellar_evolution_compute_discrete_feedback_properties( + struct spart* restrict sp, const struct stellar_model* sm, + const struct phys_const* phys_const, const float log_m_beg_step, + const float log_m_end_step, const float m_beg_step, const float m_end_step, + const float m_init, const int number_snia, const int number_snii) { + + /* Get the normalization to the average */ + const float normalization = + number_snii == 0 + ? 0. + : number_snii / + (supernovae_ii_get_number(&sm->snii, m_end_step, m_beg_step) * + m_init); + + /* Compute the mass ejected */ + /* SNIa */ + const float mass_snia = + (number_snia == 0) + ? 0 + : (supernovae_ia_get_ejected_mass_processed(&sm->snia) * number_snia); + + /* SNII */ + const float mass_snii = + normalization * supernovae_ii_get_ejected_mass_fraction_processed( + &sm->snii, log_m_end_step, log_m_beg_step); + + sp->feedback_data.mass_ejected = mass_snia + mass_snii; + + /* Transform into internal units */ + sp->feedback_data.mass_ejected *= phys_const->const_solar_mass; + + if (sp->mass <= sp->feedback_data.mass_ejected) { + error("Stars cannot have negative mass. (%g <= %g). Initial mass = %g", + sp->mass, sp->feedback_data.mass_ejected, sp->birth.mass); + } + + /* Update the mass */ + sp->mass -= sp->feedback_data.mass_ejected; + + /* Get the SNIa yields */ + const float* snia_yields = supernovae_ia_get_yields(&sm->snia); + + /* Compute the SNII yields (without the normalization) */ + float snii_yields[GEAR_CHEMISTRY_ELEMENT_COUNT]; + supernovae_ii_get_yields(&sm->snii, log_m_end_step, log_m_beg_step, + snii_yields); + + /* Compute the mass fraction of non processed elements */ + const float non_processed = + normalization * supernovae_ii_get_ejected_mass_fraction( + &sm->snii, log_m_end_step, log_m_beg_step); + + /* Set the yields */ + for (int i = 0; i < GEAR_CHEMISTRY_ELEMENT_COUNT; i++) { + /* Compute the mass fraction of metals */ + sp->feedback_data.metal_mass_ejected[i] = + /* Supernovae Ia yields */ + snia_yields[i] * number_snia + + /* Supernovae II yields */ + normalization * snii_yields[i] + + /* Gas contained in stars initial metallicity */ + chemistry_get_metal_mass_fraction_for_feedback(sp)[i] * non_processed; + + /* Convert it to total mass */ + sp->feedback_data.metal_mass_ejected[i] *= sp->birth.mass; + } +} + +/** + * @brief Evolve the stellar properties of a #spart. + * + * This function compute the SN rate and yields before sending + * this information to a different MPI rank. + * + * Here I am using Myr-solar mass units internally in order to + * avoid numerical errors. + * + * @param sp The particle to act upon + * @param sm The #stellar_model structure. + * @param cosmo The current cosmological model. + * @param us The unit system. + * @param phys_const The physical constants in the internal unit system. + * @param ti_begin The #integertime_t at the begining of the step. + * @param star_age_beg_step The age of the star at the star of the time-step in + * internal units. + * @param dt The time-step size of this star in internal units. + */ +void stellar_evolution_evolve_spart( + struct spart* restrict sp, const struct stellar_model* sm, + const struct cosmology* cosmo, const struct unit_system* us, + const struct phys_const* phys_const, const integertime_t ti_begin, + const double star_age_beg_step, const double dt) { + + /* Convert the inputs */ + const double conversion_to_myr = phys_const->const_year * 1e6; + const double star_age_beg_step_myr = star_age_beg_step / conversion_to_myr; + const double dt_myr = dt / conversion_to_myr; + + /* Get the metallicity */ + const float metallicity = + chemistry_get_total_metal_mass_fraction_for_feedback(sp); + + /* Compute masses range */ + const float log_m_beg_step = + star_age_beg_step == 0. + ? FLT_MAX + : lifetime_get_log_mass_from_lifetime( + &sm->lifetime, log10(star_age_beg_step_myr), metallicity); + const float log_m_end_step = lifetime_get_log_mass_from_lifetime( + &sm->lifetime, log10(star_age_beg_step_myr + dt_myr), metallicity); + + const float m_beg_step = + star_age_beg_step == 0. ? FLT_MAX : pow(10, log_m_beg_step); + const float m_end_step = pow(10, log_m_end_step); + + /* Check if the star can produce a supernovae */ + const int can_produce_snia = + supernovae_ia_can_explode(&sm->snia, m_end_step, m_beg_step); + const int can_produce_snii = + supernovae_ii_can_explode(&sm->snii, m_end_step, m_beg_step); + + /* Is it possible to generate a supernovae? */ + if (!can_produce_snia && !can_produce_snii) return; + + /* Compute the initial mass */ + const float m_init = sp->birth.mass / phys_const->const_solar_mass; + + /* Compute number of SNIa */ + int number_snia = 0; + if (can_produce_snia) { + /* Compute rates */ + const float number_snia_f = + supernovae_ia_get_number(&sm->snia, m_end_step, m_beg_step) * m_init; + + /* Get the integer number of supernovae */ + number_snia = stellar_evolution_compute_integer_number_supernovae( + sp, number_snia_f, ti_begin, random_number_stellar_feedback_1); + } + + /* Compute number of SNII */ + int number_snii = 0; + if (can_produce_snii) { + /* Compute rates */ + const float number_snii_f = + supernovae_ii_get_number(&sm->snii, m_end_step, m_beg_step) * m_init; + + /* Get the integer number of supernovae */ + number_snii = stellar_evolution_compute_integer_number_supernovae( + sp, number_snii_f, ti_begin, random_number_stellar_feedback_2); + } + + /* Does this star produce a supernovae? */ + if (number_snia == 0 && number_snii == 0) return; + + sp->feedback_data.number_sn = number_snia + number_snii; + + /* Compute the properties of the feedback (e.g. yields) */ + if (sm->discrete_yields) { + stellar_evolution_compute_discrete_feedback_properties( + sp, sm, phys_const, log_m_beg_step, log_m_end_step, m_beg_step, + m_end_step, m_init, number_snia, number_snii); + } else { + stellar_evolution_compute_continuous_feedback_properties( + sp, sm, phys_const, log_m_beg_step, log_m_end_step, m_beg_step, + m_end_step, m_init, number_snia, number_snii); + } +} + +/** + * @brief Get the name of the element i. + * + * @param sm The #stellar_model. + * @param i The element indice. + */ +const char* stellar_evolution_get_element_name(const struct stellar_model* sm, + int i) { + + return sm->elements_name + i * GEAR_LABELS_SIZE; +} + +/** + * @brief Read the name of all the elements present in the tables. + * + * @param sm The #stellar_model. + */ +void stellar_evolution_read_elements(struct stellar_model* sm, + struct swift_params* params) { + + hid_t file_id, group_id; + + /* Open IMF group */ + h5_open_group(params, "Data", &file_id, &group_id); + + /* Read the elements */ + io_read_string_array_attribute(group_id, "elts", sm->elements_name, + GEAR_CHEMISTRY_ELEMENT_COUNT, + GEAR_LABELS_SIZE); + + /* Check that we received correctly the metals */ + if (strcmp(stellar_evolution_get_element_name( + sm, GEAR_CHEMISTRY_ELEMENT_COUNT - 1), + "Metals") != 0) { + error( + "The chemistry table should contain the metals in the last column " + "(found %s)", + stellar_evolution_get_element_name(sm, + GEAR_CHEMISTRY_ELEMENT_COUNT - 1)); + } + + /* Print the name of the elements */ + char txt[GEAR_CHEMISTRY_ELEMENT_COUNT * (GEAR_LABELS_SIZE + 2)] = ""; + for (int i = 0; i < GEAR_CHEMISTRY_ELEMENT_COUNT; i++) { + if (i != 0) { + strcat(txt, ", "); + } + strcat(txt, stellar_evolution_get_element_name(sm, i)); + } + + if (engine_rank == 0) { + message("Chemistry elements: %s", txt); + } + + /* Cleanup everything */ + h5_close_group(file_id, group_id); +} + +/** + * @brief Initialize the global properties of the stellar evolution scheme. + * + * @param sm The #stellar_model. + * @param phys_const The physical constants in the internal unit system. + * @param us The internal unit system. + * @param params The parsed parameters. + * @param hydro_props The already read-in properties of the hydro scheme. + * @param cosmo The cosmological model. + */ +void stellar_evolution_props_init(struct stellar_model* sm, + const struct phys_const* phys_const, + const struct unit_system* us, + struct swift_params* params, + const struct cosmology* cosmo) { + + /* Read the list of elements */ + stellar_evolution_read_elements(sm, params); + + /* Use the discrete yields approach? */ + sm->discrete_yields = + parser_get_param_int(params, "GEARFeedback:discrete_yields"); + + /* Initialize the initial mass function */ + initial_mass_function_init(&sm->imf, phys_const, us, params); + + /* Initialize the lifetime model */ + lifetime_init(&sm->lifetime, phys_const, us, params); + + /* Initialize the supernovae Ia model */ + supernovae_ia_init(&sm->snia, phys_const, us, params, sm); + + /* Initialize the supernovae II model */ + supernovae_ii_init(&sm->snii, phys_const, us, params, sm); +} + +/** + * @brief Write a stellar_evolution struct to the given FILE as a stream of + * bytes. + * + * Here we are only writing the arrays, everything has been copied in the + * feedback. + * + * @param sm the struct + * @param stream the file stream + */ +void stellar_evolution_dump(const struct stellar_model* sm, FILE* stream) { + + /* Dump the initial mass function */ + initial_mass_function_dump(&sm->imf, stream, sm); + + /* Dump the lifetime model */ + lifetime_dump(&sm->lifetime, stream, sm); + + /* Dump the supernovae Ia model */ + supernovae_ia_dump(&sm->snia, stream, sm); + + /* Dump the supernovae II model */ + supernovae_ii_dump(&sm->snii, stream, sm); +} + +/** + * @brief Restore a stellar_evolution struct from the given FILE as a stream of + * bytes. + * + * Here we are only writing the arrays, everything has been copied in the + * feedback. + * + * @param sm the struct + * @param stream the file stream + */ +void stellar_evolution_restore(struct stellar_model* sm, FILE* stream) { + + /* Restore the initial mass function */ + initial_mass_function_restore(&sm->imf, stream, sm); + + /* Restore the lifetime model */ + lifetime_restore(&sm->lifetime, stream, sm); + + /* Restore the supernovae Ia model */ + supernovae_ia_restore(&sm->snia, stream, sm); + + /* Restore the supernovae II model */ + supernovae_ii_restore(&sm->snii, stream, sm); +} + +/** + * @brief Clean the allocated memory. + * + * @param sm the #stellar_model. + */ +void stellar_evolution_clean(struct stellar_model* sm) { + + initial_mass_function_clean(&sm->imf); + lifetime_clean(&sm->lifetime); + supernovae_ia_clean(&sm->snia); + supernovae_ii_clean(&sm->snii); +} diff --git a/src/feedback/GEAR/stellar_evolution.h b/src/feedback/GEAR/stellar_evolution.h new file mode 100644 index 0000000000..9ffda5ff67 --- /dev/null +++ b/src/feedback/GEAR/stellar_evolution.h @@ -0,0 +1,70 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2019 Loic Hausammann (loic.hausammann@epfl.ch) + * + * 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 <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_STELLAR_EVOLUTION_GEAR_H +#define SWIFT_STELLAR_EVOLUTION_GEAR_H + +#include "hdf5_functions.h" +#include "initial_mass_function.h" +#include "lifetime.h" +#include "random.h" +#include "stellar_evolution_struct.h" +#include "supernovae_ia.h" +#include "supernovae_ii.h" + +#include <math.h> +#include <stddef.h> + +void stellar_model_print(const struct stellar_model* sm); +int stellar_evolution_compute_integer_number_supernovae( + struct spart* restrict sp, float number_supernovae_f, + const integertime_t ti_begin, enum random_number_type random_type); + +void stellar_evolution_compute_continuous_feedback_properties( + struct spart* restrict sp, const struct stellar_model* sm, + const struct phys_const* phys_const, const float log_m_beg_step, + const float log_m_end_step, const float m_beg_step, const float m_end_step, + const float m_init, const int number_snia, const int number_snii); +void stellar_evolution_compute_discrete_feedback_properties( + struct spart* restrict sp, const struct stellar_model* sm, + const struct phys_const* phys_const, const float log_m_beg_step, + const float log_m_end_step, const float m_beg_step, const float m_end_step, + const float m_init, const int number_snia, const int number_snii); + +void stellar_evolution_evolve_spart( + struct spart* restrict sp, const struct stellar_model* sm, + const struct cosmology* cosmo, const struct unit_system* us, + const struct phys_const* phys_const, const integertime_t ti_begin, + const double star_age_beg_step, const double dt); + +const char* stellar_evolution_get_element_name(const struct stellar_model* sm, + int i); +void stellar_evolution_read_elements(struct stellar_model* sm, + struct swift_params* params); +void stellar_evolution_props_init(struct stellar_model* sm, + const struct phys_const* phys_const, + const struct unit_system* us, + struct swift_params* params, + const struct cosmology* cosmo); + +void stellar_evolution_dump(const struct stellar_model* sm, FILE* stream); +void stellar_evolution_restore(struct stellar_model* sm, FILE* stream); + +void stellar_evolution_clean(struct stellar_model* sm); + +#endif // SWIFT_STELLAR_EVOLUTION_GEAR_H diff --git a/src/feedback/GEAR/stellar_evolution_struct.h b/src/feedback/GEAR/stellar_evolution_struct.h new file mode 100644 index 0000000000..cb6280c65c --- /dev/null +++ b/src/feedback/GEAR/stellar_evolution_struct.h @@ -0,0 +1,163 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2018 Loic Hausammann (loic.hausammann@epfl.ch) + * + * 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 <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_STELLAR_EVOLUTION_STRUCT_GEAR_H +#define SWIFT_STELLAR_EVOLUTION_STRUCT_GEAR_H + +#include "interpolation.h" + +/* Number of different type of companion. + If changed, the IO needs to be updated. + */ +#define GEAR_NUMBER_TYPE_OF_COMPANION 2 +#define GEAR_LABELS_SIZE 10 + +/** + * @brief Model for the initial mass function. + * + * Describe a model such as Kroupa 2001: + * + * f(m) = coef[i] * pow(m, exp[i]) + */ +struct initial_mass_function { + + /*! Mass limits between IMF parts (n_parts + 1 elements). */ + float *mass_limits; + + /*! Exponent of each IMF parts (n_parts elements). */ + float *exp; + + /*! Coefficient of each IMF parts (n_parts elements). */ + float *coef; + + /*! Number of parts in the function. */ + int n_parts; + + /*! Minimal mass contained in mass_limits, copied for more clarity. */ + float mass_min; + + /*! Maximal mass contained in mass_limits, copied for more clarity. */ + float mass_max; +}; + +/** + * @brief Model for the stellar lifetime. + */ +struct lifetime { + + /*! Coefficients for the log10(m)^2 term */ + float quadratic[3]; + + /*! Coefficients for the log10(m) term */ + float linear[3]; + + /*! Coefficients for the constant term */ + float constant[3]; +}; + +/** + * @brief Model for SNIa. + */ +struct supernovae_ia { + /*! Mass of each element ejected by a single supernovae */ + float yields[GEAR_CHEMISTRY_ELEMENT_COUNT]; + + /*! White dwarf's mass */ + float mass_white_dwarf; + + /*! Minimal mass of the progenitor */ + float mass_min_progenitor; + + /*! Maximal mass of the progenitor */ + float mass_max_progenitor; + + /*! coefficient of the initial mass function for progenitor divided by + * progenitor_exponent */ + float progenitor_coef_exp; + + /*! exponent of the initial mass function for progenitor */ + float progenitor_exponent; + + /*! exponent of the initial mass function for binaries */ + float companion_exponent; + + struct { + /*! Initial mass function's coeffcients */ + float coef; + + /*! Maximal mass of the companion */ + float mass_max; + + /*! Minimal mass of the companion */ + float mass_min; + } companion[GEAR_NUMBER_TYPE_OF_COMPANION]; +}; + +/** + * @brief Model for SNII. + */ +struct supernovae_ii { + + /*! Integrated (over the IMF) mass fraction of metals ejected by a supernovae + */ + struct interpolation_1d integrated_yields[GEAR_CHEMISTRY_ELEMENT_COUNT]; + + /*! Total mass fraction ejected (integrated over the IMF) */ + struct interpolation_1d integrated_ejected_mass_processed; + + /*! Mass fraction ejected and not processed (=> with the star metallicity) */ + struct interpolation_1d integrated_ejected_mass; + + /*! Minimal mass for a SNII */ + float mass_min; + + /*! Maximal mass for a SNII */ + float mass_max; + + /*! exponent of the IMF */ + float exponent; + + /*! coefficient of the IMF over the exponent */ + float coef_exp; +}; + +/** + * @brief The complete stellar model. + */ +struct stellar_model { + + /*! Name of the different elements */ + char elements_name[GEAR_CHEMISTRY_ELEMENT_COUNT * GEAR_LABELS_SIZE]; + + /*! The initial mass function */ + struct initial_mass_function imf; + + /*! The stellar lifetime */ + struct lifetime lifetime; + + /*! The supernovae type Ia */ + struct supernovae_ia snia; + + /*! The supernovae type II */ + struct supernovae_ii snii; + + /*! Use a discrete yields approach */ + char discrete_yields; +}; + +#endif // SWIFT_STELLAR_EVOLUTION_STRUCT_GEAR_H diff --git a/src/feedback/GEAR/supernovae_ia.c b/src/feedback/GEAR/supernovae_ia.c new file mode 100644 index 0000000000..7d42694747 --- /dev/null +++ b/src/feedback/GEAR/supernovae_ia.c @@ -0,0 +1,418 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2019 Loic Hausammann (loic.hausammann@epfl.ch) + * + * 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 <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ + +/* Include header */ +#include "supernovae_ia.h" + +/* Local headers */ +#include "hdf5_functions.h" +#include "stellar_evolution.h" +#include "stellar_evolution_struct.h" + +/** + * @brief Print the supernovae Ia model. + * + * @param snia The #supernovae_ia. + */ +void supernovae_ia_print(const struct supernovae_ia *snia) { + + /* Only the master print */ + if (engine_rank != 0) { + return; + } + + message("Mass of the white dwarf = %g", snia->mass_white_dwarf); + message("Mass range of the progenitor = [%g, %g]", snia->mass_min_progenitor, + snia->mass_max_progenitor); + + for (int i = 0; i < GEAR_NUMBER_TYPE_OF_COMPANION; i++) { + message("Mass range of the companion %i = [%g, %g]", i, + snia->companion[i].mass_min, snia->companion[i].mass_max); + } +} + +/** + * @brief Check if the given mass is able to produce a SNIa. + * + * @param snia The #supernovae_ia model. + * @param m_low The lower mass. + * @param m_high The higher mass. + * + * @return If the mass is in the range of SNIa. + */ +int supernovae_ia_can_explode(const struct supernovae_ia *snia, float m_low, + float m_high) { + + if (m_low > snia->mass_max_progenitor) return 0; + + for (int i = 0; i < GEAR_NUMBER_TYPE_OF_COMPANION; i++) { + if (m_low < snia->companion[i].mass_max && + m_high > snia->companion[i].mass_min) { + return 1; + } + } + + return 0; +} + +/** + * @brief Get the yields of a supernovae Ia. + * + * @param snia The #supernovae_ia model. + */ +const float *supernovae_ia_get_yields(const struct supernovae_ia *snia) { + return snia->yields; +} + +/** + * @brief Get the processed mass ejected of a supernovae Ia. + * + * @param snia The #supernovae_ia model. + */ +float supernovae_ia_get_ejected_mass_processed( + const struct supernovae_ia *snia) { + return snia->mass_white_dwarf; +} + +/** + * @brief Compute the companion integral (second integral in equation 3.46 in + * Poirier 2004) + * + * @param snia The #supernovae_ia model. + * @param m1 The lower mass limit. + * @param m2 The upper mass limit. + * @param companion_type The type of companion (e.g. index of snia->companion). + * + * @return The fraction of companion. + */ +float supernovae_ia_get_companion_fraction(const struct supernovae_ia *snia, + float m1, float m2, + int companion_type) { +#ifdef SWIFT_DEBUG_CHECKS + if (m1 > m2) error("Mass 1 larger than mass 2 %g > %g.", m1, m2); +#endif + + const float tmp = + pow(m2, snia->companion_exponent) - pow(m1, snia->companion_exponent); + return snia->companion[companion_type].coef * tmp / snia->companion_exponent; +} + +/** + * @brief Compute the number of supernovae Ia per unit of mass (equation 3.46 in + * Poirier 2004). + * + * @param snia The #supernovae_ia model. + * @param m1 The lower mass limit. + * @param m2 The upper mass limit. + * + * @return The number of supernovae Ia per unit of mass. + */ +float supernovae_ia_get_number(const struct supernovae_ia *snia, float m1, + float m2) { + +#ifdef SWIFT_DEBUG_CHECKS + if (m1 > m2) error("Mass 1 larger than mass 2 %g > %g.", m1, m2); +#endif + + /* Do we have white dwarf? */ + if (m1 > snia->mass_max_progenitor) { + return 0.; + } + + float number_companion = 0.; + for (int i = 0; i < GEAR_NUMBER_TYPE_OF_COMPANION; i++) { + /* Check if we are in the possible interval */ + if (m1 > snia->companion[i].mass_max || m2 < snia->companion[i].mass_min) + continue; + + /* Get mass limits */ + const float mass_min = max(m1, snia->companion[i].mass_min); + const float mass_max = min(m2, snia->companion[i].mass_max); + + /* Compute number of companions */ + number_companion += + supernovae_ia_get_companion_fraction(snia, mass_min, mass_max, i); + } + + /* Use only the white dwarf already created */ + const float mass_min = max(m1, snia->mass_min_progenitor); + + /* Compute number of white dwarf */ + float number_white_dwarf = + pow(snia->mass_max_progenitor, snia->progenitor_exponent); + number_white_dwarf -= pow(mass_min, snia->progenitor_exponent); + number_white_dwarf *= snia->progenitor_coef_exp; + + return number_companion * number_white_dwarf; +}; + +/** + * @brief Read the SNIa yields from the table. + * + * @param snia The #supernovae_ia model. + * @param params The #swift_params. + * @param sm The #stellar_model. + */ +void supernovae_ia_read_yields(struct supernovae_ia *snia, + struct swift_params *params, + const struct stellar_model *sm) { + + hid_t file_id, group_id; + const int number_labels = GEAR_CHEMISTRY_ELEMENT_COUNT + 2; + + /* Open IMF group */ + h5_open_group(params, "Data/SNIa/Metals", &file_id, &group_id); + + /* Read the yields */ + float *yields = (float *)malloc(sizeof(float) * number_labels); + io_read_array_attribute(group_id, "data", FLOAT, yields, number_labels); + + /* Read the labels */ + char labels[(GEAR_CHEMISTRY_ELEMENT_COUNT + 2) * GEAR_LABELS_SIZE] = ""; + io_read_string_array_attribute(group_id, "elts", labels, number_labels, + GEAR_LABELS_SIZE); + + /* Save the yields */ + /* Loop over the elements in sm */ + for (int i = 0; i < GEAR_CHEMISTRY_ELEMENT_COUNT; i++) { + int found = 0; + /* Loop over SNIa yields labels */ + for (int j = 0; j < number_labels; j++) { + const char *s1 = labels + j * GEAR_LABELS_SIZE; + const char *s2 = stellar_evolution_get_element_name(sm, i); + if (strcmp(s1, s2) == 0) { + found = 1; + snia->yields[i] = yields[j]; + break; + } + } + + /* Check if found an element */ + if (!found) { + error("Cannot find element %s in SNIa yields", + stellar_evolution_get_element_name(sm, i)); + } + } + + /* Cleanup everything */ + free(yields); + h5_close_group(file_id, group_id); +}; + +/** + * @brief Initialize the companion structure in the #supernovae_ia. + */ +void supernovae_ia_init_companion(struct supernovae_ia *snia) { + + for (int i = 0; i < GEAR_NUMBER_TYPE_OF_COMPANION; i++) { + /* Compute the integral */ + float integral = supernovae_ia_get_companion_fraction( + snia, snia->companion[i].mass_min, snia->companion[i].mass_max, i); + + /* Update the coefficient for a normalization to 1 of the IMF */ + snia->companion[i].coef *= snia->companion[i].coef / integral; + } +} + +/** + * @brief Reads the supernovae Ia parameters from the tables. + * + * @param snia The #supernovae_ia model. + * @param params The simulation parameters. + */ +void supernovae_ia_read_from_tables(struct supernovae_ia *snia, + struct swift_params *params) { + + hid_t file_id, group_id; + + /* Open IMF group */ + h5_open_group(params, "Data/SNIa", &file_id, &group_id); + + /* Read the exponent of the IMF for companion */ + io_read_attribute(group_id, "a", FLOAT, &snia->companion_exponent); + + /* Read the minimal mass for a white dwarf progenitor */ + io_read_attribute(group_id, "Mpl", FLOAT, &snia->mass_min_progenitor); + + /* Read the maximal mass for a white dwarf */ + io_read_attribute(group_id, "Mpu", FLOAT, &snia->mass_max_progenitor); + + /* Read the maximal mass of a red giant companion */ + io_read_attribute(group_id, "Mdu1", FLOAT, &snia->companion[0].mass_max); + + /* Read the minimal mass of a red giant companion */ + io_read_attribute(group_id, "Mdl1", FLOAT, &snia->companion[0].mass_min); + + /* Read the coefficient of the main sequence companion */ + io_read_attribute(group_id, "bb1", FLOAT, &snia->companion[0].coef); + + /* Read the maximal mass of a main sequence companion */ + io_read_attribute(group_id, "Mdu2", FLOAT, &snia->companion[1].mass_max); + + /* Read the minimal mass of a main sequence companion */ + io_read_attribute(group_id, "Mdl2", FLOAT, &snia->companion[1].mass_min); + + /* Read the coefficient of the main sequence companion */ + io_read_attribute(group_id, "bb2", FLOAT, &snia->companion[1].coef); + + /* Cleanup everything */ + h5_close_group(file_id, group_id); + + /* Read the white dwarf mass */ + + /* Open IMF group */ + h5_open_group(params, "Data", &file_id, &group_id); + + /* Read the white dwarf mass */ + io_read_attribute(group_id, "MeanWDMass", FLOAT, &snia->mass_white_dwarf); + + /* Cleanup everything */ + h5_close_group(file_id, group_id); +} + +/** + * @brief Reads the supernovae Ia parameters from the parameters file. + * + * @param snia The #supernovae_ia model. + * @param params The simulation parameters. + */ +void supernovae_ia_read_from_params(struct supernovae_ia *snia, + struct swift_params *params) { + + /* Read the exponent of the IMF for companion */ + snia->companion_exponent = parser_get_opt_param_float( + params, "GEARSupernovaeIa:exponent", snia->companion_exponent); + + /* Read the minimal mass for a white dwarf */ + snia->mass_min_progenitor = parser_get_opt_param_float( + params, "GEARSupernovaeIa:min_mass_white_dwarf_progenitor", + snia->mass_min_progenitor); + + /* Read the maximal mass for a white dwarf */ + snia->mass_max_progenitor = parser_get_opt_param_float( + params, "GEARSupernovaeIa:max_mass_white_dwarf_progenitor", + snia->mass_max_progenitor); + + /* Read the maximal mass of a red giant companion */ + snia->companion[0].mass_max = + parser_get_opt_param_float(params, "GEARSupernovaeIa:max_mass_red_giant", + snia->companion[0].mass_max); + + /* Read the minimal mass of a red giant companion */ + snia->companion[0].mass_min = + parser_get_opt_param_float(params, "GEARSupernovaeIa:min_mass_red_giant", + snia->companion[0].mass_min); + + /* Read the coefficient of the main sequence companion */ + snia->companion[0].coef = parser_get_opt_param_float( + params, "GEARSupernovaeIa:coef_red_giant", snia->companion[0].coef); + + /* Read the maximal mass of a main sequence companion */ + snia->companion[1].mass_max = parser_get_opt_param_float( + params, "GEARSupernovaeIa:max_mass_main_sequence", + snia->companion[1].mass_max); + + /* Read the minimal mass of a main sequence companion */ + snia->companion[1].mass_min = parser_get_opt_param_float( + params, "GEARSupernovaeIa:min_mass_main_sequence", + snia->companion[1].mass_min); + + /* Read the coefficient of the main sequence companion */ + snia->companion[1].coef = parser_get_opt_param_float( + params, "GEARSupernovaeIa:coef_main_sequence", snia->companion[1].coef); + + /* Read the mass of a white dwarf */ + snia->mass_white_dwarf = parser_get_opt_param_float( + params, "GEARSupernovaeIa:white_dwarf_mass", snia->mass_white_dwarf); +} + +/** + * @brief Initialize the #supernovae_ia structure. + * + * @param snia The #supernovae_ia model. + * @param phys_const The #phys_const. + * @param us The #unit_system. + * @param params The simulation parameters. + * @param sm The #stellar_model. + */ +void supernovae_ia_init(struct supernovae_ia *snia, + const struct phys_const *phys_const, + const struct unit_system *us, + struct swift_params *params, + const struct stellar_model *sm) { + + /* Read the parameters from the tables */ + supernovae_ia_read_from_tables(snia, params); + + /* Read the parameters from the params file */ + supernovae_ia_read_from_params(snia, params); + + /* Read the yields */ + supernovae_ia_read_yields(snia, params, sm); + + /* Get the IMF parameters */ + snia->progenitor_exponent = initial_mass_function_get_exponent( + &sm->imf, snia->mass_min_progenitor, snia->mass_max_progenitor); + snia->progenitor_coef_exp = initial_mass_function_get_coefficient( + &sm->imf, snia->mass_min_progenitor, snia->mass_max_progenitor); + snia->progenitor_coef_exp /= snia->progenitor_exponent; + + /* Compute the normalization coefficients of the companion IMF */ + supernovae_ia_init_companion(snia); +} + +/** + * @brief Write a supernovae_ia struct to the given FILE as a stream of bytes. + * + * Here we are only writing the arrays, everything else has been copied in the + * feedback. + * + * @param snia the struct + * @param stream the file stream + * @param sm The #stellar_model. + */ +void supernovae_ia_dump(const struct supernovae_ia *snia, FILE *stream, + const struct stellar_model *sm) { + + /* Nothing to do here */ +} + +/** + * @brief Restore a supernovae_ia struct from the given FILE as a stream of + * bytes. + * + * Here we are only writing the arrays, everything else has been copied in the + * feedback. + * + * @param snia the struct + * @param stream the file stream + * @param sm The #stellar_model. + */ +void supernovae_ia_restore(struct supernovae_ia *snia, FILE *stream, + const struct stellar_model *sm) { + + /* Nothing to do here */ +} + +/** + * @brief Clean the allocated memory. + * + * @param snia the #supernovae_ia. + */ +void supernovae_ia_clean(struct supernovae_ia *snia) {} diff --git a/src/feedback/GEAR/supernovae_ia.h b/src/feedback/GEAR/supernovae_ia.h new file mode 100644 index 0000000000..19a6923c2d --- /dev/null +++ b/src/feedback/GEAR/supernovae_ia.h @@ -0,0 +1,63 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2019 Loic Hausammann (loic.hausammann@epfl.ch) + * + * 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 <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_SUPERNOVAE_IA_GEAR_H +#define SWIFT_SUPERNOVAE_IA_GEAR_H + +#include "hdf5_functions.h" +#include "stellar_evolution.h" +#include "stellar_evolution_struct.h" + +void supernovae_ia_print(const struct supernovae_ia *snia); +int supernovae_ia_can_explode(const struct supernovae_ia *snia, float m_low, + float m_high); +const float *supernovae_ia_get_yields(const struct supernovae_ia *snia); +float supernovae_ia_get_ejected_mass_processed( + const struct supernovae_ia *snia); +float supernovae_ia_get_companion_fraction(const struct supernovae_ia *snia, + float m1, float m2, + int companion_type); +float supernovae_ia_get_number(const struct supernovae_ia *snia, float m1, + float m2); + +void supernovae_ia_read_yields(struct supernovae_ia *snia, + struct swift_params *params, + const struct stellar_model *sm); + +void supernovae_ia_init_companion(struct supernovae_ia *snia); + +void supernovae_ia_read_from_tables(struct supernovae_ia *snia, + struct swift_params *params); + +void supernovae_ia_read_from_params(struct supernovae_ia *snia, + struct swift_params *params); + +void supernovae_ia_init(struct supernovae_ia *snia, + const struct phys_const *phys_const, + const struct unit_system *us, + struct swift_params *params, + const struct stellar_model *sm); + +void supernovae_ia_dump(const struct supernovae_ia *snia, FILE *stream, + const struct stellar_model *sm); + +void supernovae_ia_restore(struct supernovae_ia *snia, FILE *stream, + const struct stellar_model *sm); +void supernovae_ia_clean(struct supernovae_ia *snia); + +#endif // SWIFT_SUPERNOVAE_IA_GEAR_H diff --git a/src/feedback/GEAR/supernovae_ii.c b/src/feedback/GEAR/supernovae_ii.c new file mode 100644 index 0000000000..8b44afe834 --- /dev/null +++ b/src/feedback/GEAR/supernovae_ii.c @@ -0,0 +1,432 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2019 Loic Hausammann (loic.hausammann@epfl.ch) + * + * 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 <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ + +/* Include header */ +#include "supernovae_ii.h" + +/* Local headers */ +#include "hdf5_functions.h" +#include "interpolation.h" +#include "stellar_evolution.h" +#include "stellar_evolution_struct.h" + +/** + * @brief Print the supernovae II model. + * + * @param snii The #supernovae_ii. + */ +void supernovae_ii_print(const struct supernovae_ii *snii) { + + /* Only the master print */ + if (engine_rank != 0) { + return; + } + + message("Mass range for SNII = [%g, %g]", snii->mass_min, snii->mass_max); +} + +/** + * @brief Check if the given mass is able to produce a SNII. + * + * @param snii The #supernovae_ii model. + * @param m_low The lower mass. + * @param m_high The higher mass + * + * @return If the mass is in the range of SNII. + */ +int supernovae_ii_can_explode(const struct supernovae_ii *snii, float m_low, + float m_high) { + + if (m_high < snii->mass_min || m_low > snii->mass_max) return 0; + + return 1; +} + +/** + * @brief Compute the number of supernovae II per unit of mass (equation 3.47 in + * Poirier 2004). + * + * @param snii The #supernovae_ii model. + * @param m1 The lower mass limit. + * @param m2 The upper mass limit. + * + * @return The number of supernovae II per unit of mass. + */ +float supernovae_ii_get_number(const struct supernovae_ii *snii, float m1, + float m2) { +#ifdef SWIFT_DEBUG_CHECKS + if (m1 > m2) error("Mass 1 larger than mass 2 %g > %g.", m1, m2); +#endif + + /* Can we explode SNII? */ + if (!supernovae_ii_can_explode(snii, m1, m2)) { + return 0.; + } + + const float mass_min = max(m1, snii->mass_min); + const float mass_max = min(m2, snii->mass_max); + + const float pow_mass = + pow(mass_max, snii->exponent) - pow(mass_min, snii->exponent); + + return snii->coef_exp * pow_mass; +}; + +/** + * @brief Get the SNII yields per mass (Poirier version). + * + * @param snii The #supernovae_ii model. + * @param m1 The lower mass in log. + * @param m2 The upper mass in log. + * @param yields The elements ejected (needs to be allocated). + */ +void supernovae_ii_get_yields(const struct supernovae_ii *snii, float log_m1, + float log_m2, float *yields) { + + for (int i = 0; i < GEAR_CHEMISTRY_ELEMENT_COUNT; i++) { + float yields_1 = interpolate_1d(&snii->integrated_yields[i], log_m1); + float yields_2 = interpolate_1d(&snii->integrated_yields[i], log_m2); + + yields[i] = yields_2 - yields_1; + } +}; + +/** + * @brief Get the ejected mass per mass unit. + * + * @param snii The #supernovae_ii model. + * @param m1 The lower mass in log. + * @param m2 The upper mass in log. + * + * @return mass_ejected_processed The mass of processsed elements. + */ +float supernovae_ii_get_ejected_mass_fraction(const struct supernovae_ii *snii, + float log_m1, float log_m2) { + + float mass_ejected_1 = interpolate_1d(&snii->integrated_ejected_mass, log_m1); + float mass_ejected_2 = interpolate_1d(&snii->integrated_ejected_mass, log_m2); + + return mass_ejected_2 - mass_ejected_1; +}; + +/** + * @brief Get the ejected mass (processed) per mass. + * + * @param snii The #supernovae_ii model. + * @param log_m1 The lower mass in log. + * @param log_m2 The upper mass in log. + * + * @return mass_ejected The mass of non processsed elements. + */ +float supernovae_ii_get_ejected_mass_fraction_processed( + const struct supernovae_ii *snii, float log_m1, float log_m2) { + + float mass_ejected_1 = + interpolate_1d(&snii->integrated_ejected_mass_processed, log_m1); + float mass_ejected_2 = + interpolate_1d(&snii->integrated_ejected_mass_processed, log_m2); + + return mass_ejected_2 - mass_ejected_1; +}; + +/** + * @brief Read an array of SNII yields from the table. + * + * @param snii The #supernovae_ii model. + * @param sm The #stellar_model. + */ +void supernovae_ii_read_yields_array( + struct supernovae_ii *snii, struct interpolation_1d *interp, + const struct phys_const *phys_const, const struct stellar_model *sm, + hid_t group_id, const char *hdf5_dataset_name, hsize_t *previous_count, + int interpolation_size) { + + /* Now let's get the number of elements */ + /* Open attribute */ + const hid_t h_dataset = H5Dopen(group_id, hdf5_dataset_name, H5P_DEFAULT); + if (h_dataset < 0) + error("Error while opening attribute '%s'", hdf5_dataset_name); + + /* Get the number of elements */ + hsize_t count = io_get_number_element_in_dataset(h_dataset); + + /* Check that all the arrays have the same size */ + if (*previous_count != 0 && count != *previous_count) { + error("The code is not able to deal with yields arrays of different size"); + } + *previous_count = count; + + /* Read the minimal mass (in log) */ + float log_mass_min = 0; + io_read_attribute(h_dataset, "min", FLOAT, &log_mass_min); + + /* Read the step size (log step) */ + float step_size = 0; + io_read_attribute(h_dataset, "step", FLOAT, &step_size); + + /* Close the attribute */ + H5Dclose(h_dataset); + + /* Allocate the memory */ + float *data = (float *)malloc(sizeof(float) * count); + if (data == NULL) + error("Failed to allocate the SNII yields for %s.", hdf5_dataset_name); + + /* Read the dataset */ + io_read_array_dataset(group_id, hdf5_dataset_name, FLOAT, data, count); + + /* Initialize the interpolation */ + interpolate_1d_init(interp, log10(snii->mass_min), log10(snii->mass_max), + interpolation_size, log_mass_min, step_size, count, data, + boundary_condition_zero); + + /* Integrate the yields */ + initial_mass_function_integrate(&sm->imf, interp); + + /* Cleanup the memory */ + free(data); +} + +/** + * @brief Read the SNII yields from the table. + * + * The tables are in internal units at the end of this function. + * + * @param snii The #supernovae_ii model. + * @param params The simulation parameters. + * @param sm The #stellar_model. + */ +void supernovae_ii_read_yields(struct supernovae_ii *snii, + struct swift_params *params, + const struct phys_const *phys_const, + const struct stellar_model *sm) { + + hid_t file_id, group_id; + + hsize_t previous_count = 0; + + const int interpolation_size = parser_get_opt_param_int( + params, "GEARSupernovaeII:interpolation_size", 200); + + /* Open IMF group */ + h5_open_group(params, "Data/SNII", &file_id, &group_id); + + /* Do all the elements */ + for (int i = 0; i < GEAR_CHEMISTRY_ELEMENT_COUNT; i++) { + + /* Get the element name */ + const char *name = stellar_evolution_get_element_name(sm, i); + + /* Read the array */ + supernovae_ii_read_yields_array(snii, &snii->integrated_yields[i], + phys_const, sm, group_id, name, + &previous_count, interpolation_size); + } + + /* Read the mass ejected */ + supernovae_ii_read_yields_array( + snii, &snii->integrated_ejected_mass_processed, phys_const, sm, group_id, + "Ej", &previous_count, interpolation_size); + + /* Read the mass ejected of non processed gas */ + supernovae_ii_read_yields_array(snii, &snii->integrated_ejected_mass, + phys_const, sm, group_id, "Ejnp", + &previous_count, interpolation_size); + + /* Cleanup everything */ + h5_close_group(file_id, group_id); +}; + +/** + * @brief Reads the supernovae II parameters from parameters file. + * + * @param snii The #supernovae_ii model. + * @param params The simulation parameters. + */ +void supernovae_ii_read_from_params(struct supernovae_ii *snii, + struct swift_params *params) { + + /* Read the minimal mass of a supernovae */ + snii->mass_min = parser_get_opt_param_float( + params, "GEARSupernovaeII:min_mass", snii->mass_min); + + /* Read the maximal mass of a supernovae */ + snii->mass_max = parser_get_opt_param_float( + params, "GEARSupernovaeII:max_mass", snii->mass_max); +} + +/** + * @brief Reads the supernovae II parameters from tables. + * + * @param snii The #supernovae_ii model. + * @param params The simulation parameters. + */ +void supernovae_ii_read_from_tables(struct supernovae_ii *snii, + struct swift_params *params) { + + hid_t file_id, group_id; + + /* Open IMF group */ + h5_open_group(params, "Data/SNII", &file_id, &group_id); + + /* Read the minimal mass of a supernovae */ + io_read_attribute(group_id, "Mmin", FLOAT, &snii->mass_min); + + /* Read the maximal mass of a supernovae */ + io_read_attribute(group_id, "Mmax", FLOAT, &snii->mass_max); + + /* Cleanup everything */ + h5_close_group(file_id, group_id); +} + +/** + * @brief Initialize the #supernovae_ii structure. + * + * @param snii The #supernovae_ii model. + * @param phys_const The #phys_const. + * @param us The #unit_system. + * @param params The simulation parameters. + * @param sm The #stellar_model. + */ +void supernovae_ii_init(struct supernovae_ii *snii, + const struct phys_const *phys_const, + const struct unit_system *us, + struct swift_params *params, + const struct stellar_model *sm) { + + /* Read the parameters from the tables */ + supernovae_ii_read_from_tables(snii, params); + + /* Read the parameters from the params file */ + supernovae_ii_read_from_tables(snii, params); + + /* Read the supernovae yields (and apply the units) */ + supernovae_ii_read_yields(snii, params, phys_const, sm); + + /* Get the IMF parameters */ + snii->exponent = initial_mass_function_get_exponent(&sm->imf, snii->mass_min, + snii->mass_max); + snii->coef_exp = initial_mass_function_get_coefficient( + &sm->imf, snii->mass_min, snii->mass_max); + snii->coef_exp /= snii->exponent; +} + +/** + * @brief Write a supernovae_ii struct to the given FILE as a stream of bytes. + * + * Here we are only writing the arrays, everything else has been copied in the + * feedback. + * + * @param snii the struct + * @param stream the file stream + */ +void supernovae_ii_dump(const struct supernovae_ii *snii, FILE *stream, + const struct stellar_model *sm) { + + /* Dump the yields. */ + for (int i = 0; i < GEAR_CHEMISTRY_ELEMENT_COUNT; i++) { + if (snii->integrated_yields[i].data == NULL) { + continue; + } + + const char *name = stellar_evolution_get_element_name(sm, i); + restart_write_blocks((void *)snii->integrated_yields[i].data, sizeof(float), + snii->integrated_yields[i].N, stream, name, name); + } + + /*! Dump the processed mass. */ + if (snii->integrated_ejected_mass_processed.data != NULL) { + restart_write_blocks((void *)snii->integrated_ejected_mass_processed.data, + sizeof(float), + snii->integrated_ejected_mass_processed.N, stream, + "processed_mass", "processed_mass"); + } + + /*! Dump the non processed mass. */ + if (snii->integrated_ejected_mass.data != NULL) { + restart_write_blocks((void *)snii->integrated_ejected_mass.data, + sizeof(float), snii->integrated_ejected_mass.N, stream, + "non_processed_mass", "non_processed_mass"); + } +} + +/** + * @brief Restore a supernovae_ii struct from the given FILE as a stream of + * bytes. + * + * Here we are only writing the arrays, everything else has been copied in the + * feedback. + * + * @param snii the struct + * @param stream the file stream + */ +void supernovae_ii_restore(struct supernovae_ii *snii, FILE *stream, + const struct stellar_model *sm) { + + /* Restore the yields */ + for (int i = 0; i < GEAR_CHEMISTRY_ELEMENT_COUNT; i++) { + if (snii->integrated_yields[i].data == NULL) { + continue; + } + + const char *name = stellar_evolution_get_element_name(sm, i); + snii->integrated_yields[i].data = + (float *)malloc(sizeof(float) * snii->integrated_yields[i].N); + + restart_read_blocks((void *)snii->integrated_yields[i].data, sizeof(float), + snii->integrated_yields[i].N, stream, NULL, name); + } + + /* Restore the processed mass */ + + if (snii->integrated_ejected_mass_processed.data != NULL) { + snii->integrated_ejected_mass_processed.data = (float *)malloc( + sizeof(float) * snii->integrated_ejected_mass_processed.N); + + restart_read_blocks((void *)snii->integrated_ejected_mass_processed.data, + sizeof(float), + snii->integrated_ejected_mass_processed.N, stream, NULL, + "processed_mass"); + } + + /* Restore the non processed mass */ + if (snii->integrated_ejected_mass.data != NULL) { + snii->integrated_ejected_mass.data = + (float *)malloc(sizeof(float) * snii->integrated_ejected_mass.N); + + restart_read_blocks((void *)snii->integrated_ejected_mass.data, + sizeof(float), snii->integrated_ejected_mass.N, stream, + NULL, "non_processed_mass"); + } +} + +/** + * @brief Clean the allocated memory. + * + * @param snii the #supernovae_ii. + */ +void supernovae_ii_clean(struct supernovae_ii *snii) { + + for (int i = 0; i < GEAR_CHEMISTRY_ELEMENT_COUNT; i++) { + interpolate_1d_free(&snii->integrated_yields[i]); + } + + interpolate_1d_free(&snii->integrated_ejected_mass_processed); + interpolate_1d_free(&snii->integrated_ejected_mass); +} diff --git a/src/feedback/GEAR/supernovae_ii.h b/src/feedback/GEAR/supernovae_ii.h new file mode 100644 index 0000000000..515e320b58 --- /dev/null +++ b/src/feedback/GEAR/supernovae_ii.h @@ -0,0 +1,67 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2019 Loic Hausammann (loic.hausammann@epfl.ch) + * + * 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 <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_SUPERNOVAE_II_GEAR_H +#define SWIFT_SUPERNOVAE_II_GEAR_H + +#include "hdf5_functions.h" +#include "interpolation.h" +#include "stellar_evolution_struct.h" + +/** + * @brief Print the supernovae II model. + * + * @param snii The #supernovae_ii. + */ +void supernovae_ii_print(const struct supernovae_ii *snii); +int supernovae_ii_can_explode(const struct supernovae_ii *snii, float m_low, + float m_high); +float supernovae_ii_get_number(const struct supernovae_ii *snii, float m1, + float m2); +void supernovae_ii_get_yields(const struct supernovae_ii *snii, float log_m1, + float log_m2, float *yields); +float supernovae_ii_get_ejected_mass_fraction(const struct supernovae_ii *snii, + float log_m1, float log_m2); + +float supernovae_ii_get_ejected_mass_fraction_processed( + const struct supernovae_ii *snii, float log_m1, float log_m2); +void supernovae_ii_read_yields_array( + struct supernovae_ii *snii, struct interpolation_1d *interp, + const struct phys_const *phys_const, const struct stellar_model *sm, + hid_t group_id, const char *hdf5_dataset_name, hsize_t *previous_count, + int interpolation_size); +void supernovae_ii_read_yields(struct supernovae_ii *snii, + struct swift_params *params, + const struct phys_const *phys_const, + const struct stellar_model *sm); +void supernovae_ii_read_from_params(struct supernovae_ii *snii, + struct swift_params *params); + +void supernovae_ii_read_from_tables(struct supernovae_ii *snii, + struct swift_params *params); +void supernovae_ii_init(struct supernovae_ii *snii, + const struct phys_const *phys_const, + const struct unit_system *us, + struct swift_params *params, + const struct stellar_model *sm); +void supernovae_ii_dump(const struct supernovae_ii *snii, FILE *stream, + const struct stellar_model *sm); +void supernovae_ii_restore(struct supernovae_ii *snii, FILE *stream, + const struct stellar_model *sm); +void supernovae_ii_clean(struct supernovae_ii *snii); +#endif // SWIFT_SUPERNOVAE_II_GEAR_H diff --git a/src/feedback/none/feedback.h b/src/feedback/none/feedback.h index 85a8cf9566..7c83e9ed5d 100644 --- a/src/feedback/none/feedback.h +++ b/src/feedback/none/feedback.h @@ -26,6 +26,20 @@ #include "part.h" #include "units.h" +/** + * @brief Update the properties of a particle fue to feedback effects after + * the cooling was applied. + * + * Nothing to do here. + * + * @param p The #part to consider. + * @param xp The #xpart to consider. + * @param cosmo The #cosmology. + */ +__attribute__((always_inline)) INLINE static void feedback_update_part( + struct part* restrict p, struct xpart* restrict xp, + const struct engine* restrict e) {} + /** * @brief Prepares a s-particle for its feedback interactions * @@ -128,16 +142,19 @@ __attribute__((always_inline)) INLINE static void feedback_prepare_spart( * @param feedback_props The #feedback_props structure. * @param cosmo The current cosmological model. * @param us The unit system. + * @param phys_const The #phys_const. * @param star_age_beg_step The age of the star at the star of the time-step in * internal units. * @param dt The time-step size of this star in internal units. * @param time The physical time in internal units. + * @param ti_begin The integer time at the beginning of the step. * @param with_cosmology Are we running with cosmology on? */ __attribute__((always_inline)) INLINE static void feedback_evolve_spart( struct spart* restrict sp, const struct feedback_props* feedback_props, const struct cosmology* cosmo, const struct unit_system* us, - const double star_age_beg_step, const double dt, const double time, + const struct phys_const* phys_const, const double star_age_beg_step, + const double dt, const double time, const integertime_t ti_begin, const int with_cosmology) {} /** @@ -185,4 +202,16 @@ static INLINE void feedback_struct_dump(const struct feedback_props* feedback, static INLINE void feedback_struct_restore(struct feedback_props* feedback, FILE* stream) {} +#ifdef HAVE_HDF5 +/** + * @brief Writes the current model of feedback to the file + * @param h_grpsph The HDF5 group in which to write + */ +INLINE static void feedback_write_flavour(struct feedback_props* feedback, + hid_t h_grp) { + + io_write_attribute_s(h_grp, "Feedback Model", "None"); +}; +#endif + #endif /* SWIFT_FEEDBACK_NONE_H */ diff --git a/src/feedback/none/feedback_struct.h b/src/feedback/none/feedback_struct.h index 0dde77bbd7..23506c34ac 100644 --- a/src/feedback/none/feedback_struct.h +++ b/src/feedback/none/feedback_struct.h @@ -21,6 +21,11 @@ #include "chemistry_struct.h" +/** + * @brief Feedback fields carried by each hydro particles + */ +struct feedback_part_data {}; + /** * @brief Feedback fields carried by each star particles * diff --git a/src/feedback_properties.h b/src/feedback_properties.h index 83afbb1eea..32729203a4 100644 --- a/src/feedback_properties.h +++ b/src/feedback_properties.h @@ -27,6 +27,8 @@ #include "./feedback/none/feedback_properties.h" #elif defined(FEEDBACK_EAGLE) #include "./feedback/EAGLE/feedback_properties.h" +#elif defined(FEEDBACK_GEAR) +#include "./feedback/GEAR/feedback_properties.h" #else #error "Invalid choice of feedback model" #endif diff --git a/src/feedback_struct.h b/src/feedback_struct.h index be6d480e7a..09009b2ba6 100644 --- a/src/feedback_struct.h +++ b/src/feedback_struct.h @@ -32,6 +32,8 @@ #include "./feedback/none/feedback_struct.h" #elif defined(FEEDBACK_EAGLE) #include "./feedback/EAGLE/feedback_struct.h" +#elif defined(FEEDBACK_GEAR) +#include "./feedback/GEAR/feedback_struct.h" #else #error "Invalid choice of feedback function." #endif diff --git a/src/hydro/AnarchyPU/hydro_part.h b/src/hydro/AnarchyPU/hydro_part.h index 87113504c9..c0b300e3f5 100644 --- a/src/hydro/AnarchyPU/hydro_part.h +++ b/src/hydro/AnarchyPU/hydro_part.h @@ -32,6 +32,7 @@ #include "black_holes_struct.h" #include "chemistry_struct.h" #include "cooling_struct.h" +#include "feedback_struct.h" #include "star_formation_struct.h" #include "timestep_limiter_struct.h" #include "tracers_struct.h" @@ -69,6 +70,9 @@ struct xpart { /* Additional data used by the tracers */ struct star_formation_xpart_data sf_data; + /* Additional data used by the feedback */ + struct feedback_part_data feedback_data; + } SWIFT_STRUCT_ALIGN; /** diff --git a/src/hydro/Default/hydro_part.h b/src/hydro/Default/hydro_part.h index a236b14534..6437e5c5c4 100644 --- a/src/hydro/Default/hydro_part.h +++ b/src/hydro/Default/hydro_part.h @@ -30,6 +30,7 @@ #include "black_holes_struct.h" #include "chemistry_struct.h" #include "cooling_struct.h" +#include "feedback_struct.h" #include "star_formation_struct.h" #include "timestep_limiter_struct.h" #include "tracers_struct.h" @@ -67,6 +68,9 @@ struct xpart { /* Additional data used by the tracers */ struct star_formation_xpart_data sf_data; + /* Additional data used by the feedback */ + struct feedback_part_data feedback_data; + } SWIFT_STRUCT_ALIGN; /** diff --git a/src/hydro/Gadget2/hydro_part.h b/src/hydro/Gadget2/hydro_part.h index d34d51a603..1916410885 100644 --- a/src/hydro/Gadget2/hydro_part.h +++ b/src/hydro/Gadget2/hydro_part.h @@ -34,6 +34,7 @@ #include "black_holes_struct.h" #include "chemistry_struct.h" #include "cooling_struct.h" +#include "feedback_struct.h" #include "logger.h" #include "pressure_floor_struct.h" #include "star_formation_struct.h" @@ -67,6 +68,9 @@ struct xpart { /* Additional data used by the star formation */ struct star_formation_xpart_data sf_data; + /* Additional data used by the feedback */ + struct feedback_part_data feedback_data; + #ifdef WITH_LOGGER /* Additional data for the particle logger */ struct logger_part_data logger_data; diff --git a/src/hydro/Gizmo/hydro_part.h b/src/hydro/Gizmo/hydro_part.h index 7003396da3..c1228f576e 100644 --- a/src/hydro/Gizmo/hydro_part.h +++ b/src/hydro/Gizmo/hydro_part.h @@ -22,6 +22,7 @@ #include "black_holes_struct.h" #include "chemistry_struct.h" #include "cooling_struct.h" +#include "feedback_struct.h" #include "star_formation_struct.h" #include "timestep_limiter_struct.h" #include "tracers_struct.h" @@ -50,6 +51,9 @@ struct xpart { /* Additional data used by the star formation */ struct star_formation_xpart_data sf_data; + /* Additional data used by the feedback */ + struct feedback_part_data feedback_data; + } SWIFT_STRUCT_ALIGN; /* Import the right hydro particle definition */ diff --git a/src/hydro/Minimal/hydro_part.h b/src/hydro/Minimal/hydro_part.h index 4d4b1a09f4..9b31c4976d 100644 --- a/src/hydro/Minimal/hydro_part.h +++ b/src/hydro/Minimal/hydro_part.h @@ -35,6 +35,7 @@ #include "black_holes_struct.h" #include "chemistry_struct.h" #include "cooling_struct.h" +#include "feedback_struct.h" #include "star_formation_struct.h" #include "timestep_limiter_struct.h" #include "tracers_struct.h" @@ -72,6 +73,9 @@ struct xpart { /* Additional data used by the tracers */ struct star_formation_xpart_data sf_data; + /* Additional data used by the feedback */ + struct feedback_part_data feedback_data; + } SWIFT_STRUCT_ALIGN; /** diff --git a/src/hydro/Planetary/hydro_part.h b/src/hydro/Planetary/hydro_part.h index c3cdf3879c..4c0c4b786c 100644 --- a/src/hydro/Planetary/hydro_part.h +++ b/src/hydro/Planetary/hydro_part.h @@ -37,6 +37,7 @@ #include "chemistry_struct.h" #include "cooling_struct.h" #include "equation_of_state.h" // For enum material_id +#include "feedback_struct.h" #include "star_formation_struct.h" #include "timestep_limiter_struct.h" #include "tracers_struct.h" @@ -74,6 +75,9 @@ struct xpart { /* Additional data used by the star formation */ struct star_formation_xpart_data sf_data; + /* Additional data used by the feedback */ + struct feedback_part_data feedback_data; + } SWIFT_STRUCT_ALIGN; /** diff --git a/src/hydro/PressureEnergy/hydro.h b/src/hydro/PressureEnergy/hydro.h index 2ed22ac672..fddafa52a9 100644 --- a/src/hydro/PressureEnergy/hydro.h +++ b/src/hydro/PressureEnergy/hydro.h @@ -442,6 +442,10 @@ hydro_set_drifted_physical_internal_energy(struct part *p, /* Update variables. */ hydro_update_soundspeed(p, cosmo); + + const float comoving_pressure_with_floor = + pressure_floor_get_comoving_pressure(p, p->pressure_bar, cosmo); + p->force.pressure_bar_with_floor = comoving_pressure_with_floor; } /** diff --git a/src/hydro/PressureEnergy/hydro_part.h b/src/hydro/PressureEnergy/hydro_part.h index 0f19f968b8..82a6275f9c 100644 --- a/src/hydro/PressureEnergy/hydro_part.h +++ b/src/hydro/PressureEnergy/hydro_part.h @@ -34,6 +34,7 @@ #include "black_holes_struct.h" #include "chemistry_struct.h" #include "cooling_struct.h" +#include "feedback_struct.h" #include "pressure_floor_struct.h" #include "star_formation_struct.h" #include "timestep_limiter_struct.h" @@ -72,6 +73,9 @@ struct xpart { /*! Additional data used by the star formation */ struct star_formation_xpart_data sf_data; + /* Additional data used by the feedback */ + struct feedback_part_data feedback_data; + } SWIFT_STRUCT_ALIGN; /** diff --git a/src/hydro/PressureEnergyMorrisMonaghanAV/hydro_part.h b/src/hydro/PressureEnergyMorrisMonaghanAV/hydro_part.h index 09b838b65e..b21dbc6e81 100644 --- a/src/hydro/PressureEnergyMorrisMonaghanAV/hydro_part.h +++ b/src/hydro/PressureEnergyMorrisMonaghanAV/hydro_part.h @@ -35,6 +35,7 @@ #include "black_holes_struct.h" #include "chemistry_struct.h" #include "cooling_struct.h" +#include "feedback_struct.h" #include "star_formation_struct.h" #include "timestep_limiter_struct.h" #include "tracers_struct.h" @@ -72,6 +73,9 @@ struct xpart { /*! Additional data used by the star formation */ struct star_formation_xpart_data sf_data; + /* Additional data used by the feedback */ + struct feedback_part_data feedback_data; + } SWIFT_STRUCT_ALIGN; /** diff --git a/src/hydro/PressureEntropy/hydro_part.h b/src/hydro/PressureEntropy/hydro_part.h index 586976e4dc..7c77bf88a7 100644 --- a/src/hydro/PressureEntropy/hydro_part.h +++ b/src/hydro/PressureEntropy/hydro_part.h @@ -33,6 +33,7 @@ #include "black_holes_struct.h" #include "chemistry_struct.h" #include "cooling_struct.h" +#include "feedback_struct.h" #include "star_formation_struct.h" #include "timestep_limiter_struct.h" #include "tracers_struct.h" @@ -64,6 +65,9 @@ struct xpart { /*! Additional data used by the star formation */ struct star_formation_xpart_data sf_data; + /* Additional data used by the feedback */ + struct feedback_part_data feedback_data; + } SWIFT_STRUCT_ALIGN; /* Data of a single particle. */ diff --git a/src/hydro/SPHENIX/hydro_part.h b/src/hydro/SPHENIX/hydro_part.h index 3865ad539f..e7f9a60545 100644 --- a/src/hydro/SPHENIX/hydro_part.h +++ b/src/hydro/SPHENIX/hydro_part.h @@ -29,6 +29,7 @@ #include "black_holes_struct.h" #include "chemistry_struct.h" #include "cooling_struct.h" +#include "feedback_struct.h" #include "pressure_floor_struct.h" #include "star_formation_struct.h" #include "timestep_limiter_struct.h" @@ -67,6 +68,9 @@ struct xpart { /* Additional data used by the tracers */ struct star_formation_xpart_data sf_data; + /* Additional data used by the feedback */ + struct feedback_part_data feedback_data; + } SWIFT_STRUCT_ALIGN; /** diff --git a/src/hydro/Shadowswift/hydro_part.h b/src/hydro/Shadowswift/hydro_part.h index 3bd1a6af2e..bd43ab43db 100644 --- a/src/hydro/Shadowswift/hydro_part.h +++ b/src/hydro/Shadowswift/hydro_part.h @@ -24,6 +24,7 @@ #include "black_holes_struct.h" #include "chemistry_struct.h" #include "cooling_struct.h" +#include "feedback_struct.h" #include "timestep_limiter_struct.h" #include "tracers_struct.h" #include "voronoi_cell.h" @@ -49,6 +50,9 @@ struct xpart { /* Additional data used by the tracers */ struct tracers_xpart_data tracers_data; + /* Additional data used by the feedback */ + struct feedback_part_data feedback_data; + } SWIFT_STRUCT_ALIGN; /* Data of a single particle. */ diff --git a/src/parallel_io.c b/src/parallel_io.c index f073104e89..5a8605dbcd 100644 --- a/src/parallel_io.c +++ b/src/parallel_io.c @@ -45,6 +45,7 @@ #include "engine.h" #include "entropy_floor.h" #include "error.h" +#include "feedback.h" #include "fof_io.h" #include "gravity_io.h" #include "gravity_properties.h" @@ -1170,6 +1171,7 @@ void prepare_file(struct engine* e, const char* baseName, long long N_total[6], cooling_write_flavour(h_grp, e->cooling_func); chemistry_write_flavour(h_grp); tracers_write_flavour(h_grp); + feedback_write_flavour(e->feedback_props, h_grp); H5Gclose(h_grp); /* Print the gravity parameters */ diff --git a/src/pressure_floor/GEAR/pressure_floor.h b/src/pressure_floor/GEAR/pressure_floor.h index cb88659cb6..2d00b9b6c2 100644 --- a/src/pressure_floor/GEAR/pressure_floor.h +++ b/src/pressure_floor/GEAR/pressure_floor.h @@ -130,7 +130,7 @@ __attribute__((always_inline)) static INLINE void pressure_floor_init( /* Read the Jeans factor */ props->n_jeans = - parser_get_param_float(params, "GEARPressureFloor:Jeans_factor"); + parser_get_param_float(params, "GEARPressureFloor:jeans_factor"); /* Compute the constants */ props->constants = diff --git a/src/random.h b/src/random.h index 8618f77ff7..afe0b99b2c 100644 --- a/src/random.h +++ b/src/random.h @@ -42,11 +42,12 @@ * generator. * In case new numbers need to be added other possible * numbers could be: - * 5947309451, 6977309513 + * 5947309451 */ enum random_number_type { random_number_star_formation = 0LL, - random_number_stellar_feedback = 3947008991LL, + random_number_stellar_feedback_1 = 3947008991LL, + random_number_stellar_feedback_2 = 6977309513LL, random_number_stellar_enrichment = 2936881973LL, random_number_BH_feedback = 1640531371LL, random_number_BH_swallow = 4947009007LL diff --git a/src/runner_ghost.c b/src/runner_ghost.c index a4113b5b89..2aaf172ea4 100644 --- a/src/runner_ghost.c +++ b/src/runner_ghost.c @@ -74,6 +74,7 @@ void runner_do_stars_ghost(struct runner *r, struct cell *c, int timer) { struct spart *restrict sparts = c->stars.parts; const struct engine *e = r->e; const struct unit_system *us = e->internal_units; + const struct phys_const *phys_const = e->physical_constants; const int with_cosmology = (e->policy & engine_policy_cosmology); const struct cosmology *cosmo = e->cosmology; const struct feedback_props *feedback_props = e->feedback_props; @@ -239,9 +240,9 @@ void runner_do_stars_ghost(struct runner *r, struct cell *c, int timer) { star_age_end_of_step - dt_enrichment; /* Compute the stellar evolution */ - feedback_evolve_spart(sp, feedback_props, cosmo, us, + feedback_evolve_spart(sp, feedback_props, cosmo, us, phys_const, star_age_beg_of_step, dt_enrichment, - e->time, with_cosmology); + e->time, ti_begin, with_cosmology); } else { /* Reset the feedback fields of the star particle */ @@ -382,9 +383,9 @@ void runner_do_stars_ghost(struct runner *r, struct cell *c, int timer) { star_age_end_of_step - dt_enrichment; /* Compute the stellar evolution */ - feedback_evolve_spart(sp, feedback_props, cosmo, us, + feedback_evolve_spart(sp, feedback_props, cosmo, us, phys_const, star_age_beg_of_step, dt_enrichment, e->time, - with_cosmology); + ti_begin, with_cosmology); } else { /* Reset the feedback fields of the star particle */ @@ -1054,7 +1055,7 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) { hydro_end_density(p, cosmo); chemistry_end_density(p, chemistry, cosmo); pressure_floor_end_density(p, cosmo); - star_formation_end_density(p, star_formation, cosmo); + star_formation_end_density(p, xp, star_formation, cosmo); /* Are we using the alternative definition of the number of neighbours? */ diff --git a/src/runner_others.c b/src/runner_others.c index 60e86bc039..2f7a82e2ff 100644 --- a/src/runner_others.c +++ b/src/runner_others.c @@ -44,6 +44,7 @@ #include "cooling.h" #include "engine.h" #include "error.h" +#include "feedback.h" #include "gravity.h" #include "hydro.h" #include "logger.h" @@ -52,6 +53,7 @@ #include "star_formation.h" #include "star_formation_logger.h" #include "stars.h" +#include "task_order.h" #include "timers.h" #include "timestep_limiter.h" #include "tracers.h" @@ -143,7 +145,6 @@ void runner_do_grav_mesh(struct runner *r, struct cell *c, int timer) { * @param timer 1 if the time is to be recorded. */ void runner_do_cooling(struct runner *r, struct cell *c, int timer) { - const struct engine *e = r->e; const struct cosmology *cosmo = e->cosmology; const int with_cosmology = (e->policy & engine_policy_cosmology); @@ -157,11 +158,14 @@ void runner_do_cooling(struct runner *r, struct cell *c, int timer) { struct part *restrict parts = c->hydro.parts; struct xpart *restrict xparts = c->hydro.xparts; const int count = c->hydro.count; + const double time = e->time; TIMER_TIC; - /* Anything to do here? */ - if (!cell_is_active_hydro(c, e)) return; + /* Anything to do here? (i.e. does this cell need updating?) */ + if (!((task_order_cooling_after_timestep && cell_is_starting_hydro(c, e)) || + (!task_order_cooling_after_timestep && cell_is_active_hydro(c, e)))) + return; /* Recurse? */ if (c->split) { @@ -176,7 +180,9 @@ void runner_do_cooling(struct runner *r, struct cell *c, int timer) { struct part *restrict p = &parts[i]; struct xpart *restrict xp = &xparts[i]; - if (part_is_active(p, e)) { + /* Anything to do here? (i.e. does this particle need updating?) */ + if ((task_order_cooling_after_timestep && part_is_starting(p, e)) || + (!task_order_cooling_after_timestep && part_is_active(p, e))) { double dt_cool, dt_therm; if (with_cosmology) { @@ -196,8 +202,13 @@ void runner_do_cooling(struct runner *r, struct cell *c, int timer) { /* Let's cool ! */ cooling_cool_part(constants, us, cosmo, hydro_props, - entropy_floor_props, cooling_func, p, xp, dt_cool, - dt_therm); + entropy_floor_props, cooling_func, p, xp, time, + dt_cool, dt_therm); + + /* Apply the effects of feedback on this particle + * (Note: Only used in schemes that have a delayed feedback mechanism + * otherwise just an empty function) */ + feedback_update_part(p, xp, e); } } } diff --git a/src/runner_recv.c b/src/runner_recv.c index 6486f7621c..a00e3d2dc5 100644 --- a/src/runner_recv.c +++ b/src/runner_recv.c @@ -232,7 +232,7 @@ void runner_do_recv_spart(struct runner *r, struct cell *c, int clear_sorts, /* Collect everything... */ for (size_t k = 0; k < nr_sparts; k++) { #ifdef DEBUG_INTERACTIONS_STARS - sparts[k].num_ngb_force = 0; + sparts[k].num_ngb_feedback = 0; #endif if (sparts[k].time_bin == time_bin_inhibited) continue; time_bin_min = min(time_bin_min, sparts[k].time_bin); diff --git a/src/serial_io.c b/src/serial_io.c index dc033b95c4..d028d79dc2 100644 --- a/src/serial_io.c +++ b/src/serial_io.c @@ -45,6 +45,7 @@ #include "engine.h" #include "entropy_floor.h" #include "error.h" +#include "feedback.h" #include "fof_io.h" #include "gravity_io.h" #include "gravity_properties.h" @@ -1035,6 +1036,7 @@ void write_output_serial(struct engine* e, const char* baseName, cooling_write_flavour(h_grp, e->cooling_func); chemistry_write_flavour(h_grp); tracers_write_flavour(h_grp); + feedback_write_flavour(e->feedback_props, h_grp); H5Gclose(h_grp); /* Print the gravity parameters */ diff --git a/src/single_io.c b/src/single_io.c index 05ccfa8fde..2a8a589757 100644 --- a/src/single_io.c +++ b/src/single_io.c @@ -44,6 +44,7 @@ #include "engine.h" #include "entropy_floor.h" #include "error.h" +#include "feedback.h" #include "fof_io.h" #include "gravity_io.h" #include "gravity_properties.h" @@ -880,6 +881,7 @@ void write_output_single(struct engine* e, const char* baseName, cooling_write_flavour(h_grp, e->cooling_func); chemistry_write_flavour(h_grp); tracers_write_flavour(h_grp); + feedback_write_flavour(e->feedback_props, h_grp); H5Gclose(h_grp); /* Print the gravity parameters */ diff --git a/src/space.c b/src/space.c index 7596ed637d..3fffafc4b7 100644 --- a/src/space.c +++ b/src/space.c @@ -4286,7 +4286,8 @@ void space_first_init_parts_mapper(void *restrict map_data, int count, &xp[k]); /* And the cooling */ - cooling_first_init_part(phys_const, us, cosmo, cool_func, &p[k], &xp[k]); + cooling_first_init_part(phys_const, us, hydro_props, cosmo, cool_func, + &p[k], &xp[k]); /* And the tracers */ tracers_first_init_xpart(&p[k], &xp[k], us, phys_const, cosmo, hydro_props, diff --git a/src/star_formation/EAGLE/star_formation.h b/src/star_formation/EAGLE/star_formation.h index 143984ebf8..ce680d4ec1 100644 --- a/src/star_formation/EAGLE/star_formation.h +++ b/src/star_formation/EAGLE/star_formation.h @@ -664,12 +664,13 @@ INLINE static void starformation_print_backend( * density loop for the EAGLE star formation model. * * @param p The particle to act upon + * @param xp The extra particle to act upon * @param cd The global star_formation information. * @param cosmo The current cosmological model. */ __attribute__((always_inline)) INLINE static void star_formation_end_density( - struct part* restrict p, const struct star_formation* cd, - const struct cosmology* cosmo) {} + struct part* restrict p, struct xpart* restrict xp, + const struct star_formation* cd, const struct cosmology* cosmo) {} /** * @brief Sets all particle fields to sensible values when the #part has 0 ngbs. diff --git a/src/star_formation/GEAR/star_formation.h b/src/star_formation/GEAR/star_formation.h index c2682f1643..8eb60bf38f 100644 --- a/src/star_formation/GEAR/star_formation.h +++ b/src/star_formation/GEAR/star_formation.h @@ -59,6 +59,11 @@ INLINE static int star_formation_is_star_forming( const struct cooling_function_data* restrict cooling, const struct entropy_floor_properties* restrict entropy_floor) { + /* Check if collapsing particles */ + if (xp->sf_data.div_v > 0) { + return 0; + } + const float temperature = cooling_get_temperature(phys_const, hydro_props, us, cosmo, cooling, p, xp); @@ -73,7 +78,7 @@ INLINE static int star_formation_is_star_forming( const float sigma2 = p->pressure_floor_data.sigma2 * cosmo->a * cosmo->a; const float n_jeans_2_3 = starform->n_jeans_2_3; - const float h = p->h; + const float h = p->h * kernel_gamma; const float density = hydro_get_physical_density(p, cosmo); // TODO use GRACKLE */ @@ -200,10 +205,6 @@ INLINE static void star_formation_copy_properties( sp->birth_time = e->time; } - // TODO copy only metals - /* Store the chemistry struct in the star particle */ - sp->chemistry_data = p->chemistry_data; - /* Store the tracers data */ sp->tracers_data = xp->tracers_data; @@ -213,6 +214,9 @@ INLINE static void star_formation_copy_properties( /* Store the birth temperature*/ sp->birth.temperature = cooling_get_temperature(phys_const, hydro_props, us, cosmo, cooling, p, xp); + + /* Copy the chemistry properties */ + chemistry_copy_star_formation_properties(p, xp, sp); } /** @@ -231,12 +235,17 @@ INLINE static void starformation_print_backend( * Nothing to do here. * * @param p The particle to act upon + * @param xp The extra particle to act upon * @param cd The global star_formation information. * @param cosmo The current cosmological model. */ __attribute__((always_inline)) INLINE static void star_formation_end_density( - struct part* restrict p, const struct star_formation* cd, - const struct cosmology* cosmo) {} + struct part* restrict p, struct xpart* restrict xp, + const struct star_formation* cd, const struct cosmology* cosmo) { + + /* Copy the velocity divergence */ + xp->sf_data.div_v = p->density.div_v; +} /** * @brief Sets all particle fields to sensible values when the #part has 0 ngbs. diff --git a/src/star_formation/GEAR/star_formation_struct.h b/src/star_formation/GEAR/star_formation_struct.h index 50a735ff45..9d6d78df5c 100644 --- a/src/star_formation/GEAR/star_formation_struct.h +++ b/src/star_formation/GEAR/star_formation_struct.h @@ -23,7 +23,10 @@ * @brief Star-formation-related properties stored in the extended particle * data. */ -struct star_formation_xpart_data {}; +struct star_formation_xpart_data { + /*! Particle velocity divergence. */ + float div_v; +}; /** * @brief Global star formation properties diff --git a/src/star_formation/none/star_formation.h b/src/star_formation/none/star_formation.h index a48155b05b..6155d41564 100644 --- a/src/star_formation/none/star_formation.h +++ b/src/star_formation/none/star_formation.h @@ -171,12 +171,13 @@ INLINE static void starformation_print_backend( * Nothing to do here. * * @param p The particle to act upon + * @param xp The extra particle to act upon * @param cd The global star_formation information. * @param cosmo The current cosmological model. */ __attribute__((always_inline)) INLINE static void star_formation_end_density( - struct part* restrict p, const struct star_formation* cd, - const struct cosmology* cosmo) {} + struct part* restrict p, struct xpart* restrict xp, + const struct star_formation* cd, const struct cosmology* cosmo) {} /** * @brief Sets all particle fields to sensible values when the #part has 0 ngbs. diff --git a/src/stars/EAGLE/stars.h b/src/stars/EAGLE/stars.h index 644acfcbf7..9567af1bce 100644 --- a/src/stars/EAGLE/stars.h +++ b/src/stars/EAGLE/stars.h @@ -167,7 +167,7 @@ __attribute__((always_inline)) INLINE static void stars_reset_acceleration( struct spart* restrict p) { #ifdef DEBUG_INTERACTIONS_STARS - p->num_ngb_force = 0; + p->num_ngb_feedback = 0; #endif } @@ -184,8 +184,8 @@ __attribute__((always_inline)) INLINE static void stars_reset_feedback( #ifdef DEBUG_INTERACTIONS_STARS for (int i = 0; i < MAX_NUM_OF_NEIGHBOURS_STARS; ++i) - p->ids_ngbs_force[i] = -1; - p->num_ngb_force = 0; + p->ids_ngbs_feedback[i] = -1; + p->num_ngb_feedback = 0; #endif } diff --git a/src/stars/EAGLE/stars_part.h b/src/stars/EAGLE/stars_part.h index 13f0783674..15613d077a 100644 --- a/src/stars/EAGLE/stars_part.h +++ b/src/stars/EAGLE/stars_part.h @@ -132,11 +132,11 @@ struct spart { /*! List of interacting particles in the density SELF and PAIR */ long long ids_ngbs_density[MAX_NUM_OF_NEIGHBOURS_STARS]; - /*! Number of interactions in the force SELF and PAIR */ - int num_ngb_force; + /*! Number of interactions in the feedback SELF and PAIR */ + int num_ngb_feedback; - /*! List of interacting particles in the force SELF and PAIR */ - long long ids_ngbs_force[MAX_NUM_OF_NEIGHBOURS_STARS]; + /*! List of interacting particles in the feedback SELF and PAIR */ + long long ids_ngbs_feedback[MAX_NUM_OF_NEIGHBOURS_STARS]; #endif } SWIFT_STRUCT_ALIGN; diff --git a/src/stars/GEAR/stars.h b/src/stars/GEAR/stars.h index 2fe4428b81..1c5bdf5916 100644 --- a/src/stars/GEAR/stars.h +++ b/src/stars/GEAR/stars.h @@ -33,26 +33,6 @@ __attribute__((always_inline)) INLINE static float stars_compute_timestep( return FLT_MAX; } -/** - * @brief Initialises the s-particles for the first time - * - * This function is called only once just after the ICs have been - * read in to do some conversions. - * - * @param sp The particle to act upon - * @param stars_properties The properties of the stellar model. - * @param with_cosmology Are we running with cosmological time integration. - * @param scale_factor The current scale-factor (used if running with - * cosmology). - * @param time The current time (used if running without cosmology). - */ -__attribute__((always_inline)) INLINE static void stars_first_init_spart( - struct spart* sp, const struct stars_props* stars_properties, - const int with_cosmology, const double scale_factor, const double time) { - - sp->time_bin = 0; -} - /** * @brief Prepares a s-particle for its interactions * @@ -71,6 +51,26 @@ __attribute__((always_inline)) INLINE static void stars_init_spart( sp->density.wcount_dh = 0.f; } +/** + * @brief Initialises the s-particles for the first time + * + * This function is called only once just after the ICs have been + * read in to do some conversions. + * + * @param sp The particle to act upon. + * @param stars_properties Properties of the stars model. + */ +__attribute__((always_inline)) INLINE static void stars_first_init_spart( + struct spart* sp, const struct stars_props* stars_properties, + const int with_cosmology, const double scale_factor, const double time) { + + sp->time_bin = 0; + sp->birth.density = 0.f; + // sp->birth_time = 0.; + + stars_init_spart(sp); +} + /** * @brief Predict additional particle fields forward in time when drifting * @@ -92,6 +92,8 @@ __attribute__((always_inline)) INLINE static void stars_reset_predicted_values( /** * @brief Finishes the calculation of (non-gravity) forces acting on stars * + * Multiplies the forces and accelerations by the appropiate constants + * * @param sp The particle to act upon */ __attribute__((always_inline)) INLINE static void stars_end_feedback( @@ -136,13 +138,8 @@ __attribute__((always_inline)) INLINE static void stars_end_density( __attribute__((always_inline)) INLINE static void stars_spart_has_no_neighbours( struct spart* restrict sp, const struct cosmology* cosmo) { - /* Some smoothing length multiples. */ - const float h = sp->h; - const float h_inv = 1.0f / h; /* 1/h */ - const float h_inv_dim = pow_dimension(h_inv); /* 1/h^d */ - /* Re-set problematic values */ - sp->density.wcount = kernel_root * h_inv_dim; + sp->density.wcount = 0.f; sp->density.wcount_dh = 0.f; } @@ -154,24 +151,30 @@ __attribute__((always_inline)) INLINE static void stars_spart_has_no_neighbours( * * @param p The particle to act upon */ -__attribute__((always_inline)) INLINE static void stars_reset_feedback( +__attribute__((always_inline)) INLINE static void stars_reset_acceleration( struct spart* restrict p) { #ifdef DEBUG_INTERACTIONS_STARS - for (int i = 0; i < MAX_NUM_OF_NEIGHBOURS_STARS; ++i) - p->ids_ngbs_force[i] = -1; - p->num_ngb_force = 0; + p->num_ngb_feedback = 0; #endif } /** - * @brief Initializes constants related to stellar evolution, initializes imf, - * reads and processes yield tables + * @brief Reset acceleration fields of a particle + * + * This is the equivalent of hydro_reset_acceleration. + * We do not compute the acceleration on star, therefore no need to use it. * - * @param params swift_params parameters structure - * @param stars stars_props data structure + * @param p The particle to act upon */ -inline static void stars_evolve_init(struct swift_params* params, - struct stars_props* restrict stars) {} +__attribute__((always_inline)) INLINE static void stars_reset_feedback( + struct spart* restrict p) { + +#ifdef DEBUG_INTERACTIONS_STARS + for (int i = 0; i < MAX_NUM_OF_NEIGHBOURS_STARS; ++i) + p->ids_ngbs_feedback[i] = -1; + p->num_ngb_feedback = 0; +#endif +} #endif /* SWIFT_GEAR_STARS_H */ diff --git a/src/stars/GEAR/stars_iact.h b/src/stars/GEAR/stars_iact.h index c7bda43fc0..024ac7a9b9 100644 --- a/src/stars/GEAR/stars_iact.h +++ b/src/stars/GEAR/stars_iact.h @@ -65,13 +65,14 @@ runner_iact_nonsym_stars_density(const float r2, const float *dx, /** * @brief Feedback interaction between two particles (non-symmetric). + * Used for updating properties of gas particles neighbouring a star particle * * @param r2 Comoving square distance between the two particles. - * @param dx Comoving vector separating both particles (pi - pj). + * @param dx Comoving vector separating both particles (si - pj). * @param hi Comoving smoothing-length of particle i. * @param hj Comoving smoothing-length of particle j. - * @param si First sparticle. - * @param pj Second particle (not updated). + * @param si First (star) particle (not updated). + * @param pj Second (gas) particle. * @param a Current scale factor. * @param H Current Hubble parameter. */ @@ -91,4 +92,4 @@ runner_iact_nonsym_stars_feedback(const float r2, const float *dx, #endif } -#endif +#endif /* SWIFT_GEAR_STARS_IACT_H */ diff --git a/src/stars/GEAR/stars_io.h b/src/stars/GEAR/stars_io.h index addde2424b..a5a6e7368b 100644 --- a/src/stars/GEAR/stars_io.h +++ b/src/stars/GEAR/stars_io.h @@ -34,7 +34,7 @@ INLINE static void stars_read_particles(struct spart *sparts, int *num_fields) { /* Say how much we want to read */ - *num_fields = 5; + *num_fields = 7; /* List what we want to read */ list[0] = io_make_input_field("Coordinates", DOUBLE, 3, COMPULSORY, @@ -47,6 +47,15 @@ INLINE static void stars_read_particles(struct spart *sparts, UNIT_CONV_NO_UNITS, sparts, id); list[4] = io_make_input_field("SmoothingLength", FLOAT, 1, OPTIONAL, UNIT_CONV_LENGTH, sparts, h); + // TODO take it from initial mass + list[5] = io_make_input_field("BirthMass", FLOAT, 1, COMPULSORY, + UNIT_CONV_MASS, sparts, birth.mass); + + // TODO make it optional + list[6] = io_make_input_field("BirthTime", FLOAT, 1, OPTIONAL, UNIT_CONV_MASS, + sparts, birth_time); + + // TODO read birth density } INLINE static void convert_spart_pos(const struct engine *e, diff --git a/src/stars/GEAR/stars_part.h b/src/stars/GEAR/stars_part.h index bf68a580ef..bee9b5c6a3 100644 --- a/src/stars/GEAR/stars_part.h +++ b/src/stars/GEAR/stars_part.h @@ -55,22 +55,9 @@ struct spart { /*! Star mass */ float mass; - /* Particle cutoff radius. */ + /*! Particle smoothing length. */ float h; - /*! Union for the birth time and birth scale factor */ - union { - - /*! Birth time */ - float birth_time; - - /*! Birth scale factor */ - float birth_scale_factor; - }; - - /*! Particle time bin */ - timebin_t time_bin; - struct { /* Number of neighbours. */ @@ -92,6 +79,16 @@ struct spart { float mass; } birth; + /*! Union for the birth time and birth scale factor */ + union { + + /*! Birth time */ + float birth_time; + + /*! Birth scale factor */ + float birth_scale_factor; + }; + /*! Feedback structure */ struct feedback_spart_data feedback_data; @@ -99,7 +96,10 @@ struct spart { struct tracers_xpart_data tracers_data; /*! Chemistry structure */ - struct chemistry_part_data chemistry_data; + struct chemistry_spart_data chemistry_data; + + /*! Particle time bin */ + timebin_t time_bin; #ifdef SWIFT_DEBUG_CHECKS @@ -118,11 +118,11 @@ struct spart { /*! List of interacting particles in the density SELF and PAIR */ long long ids_ngbs_density[MAX_NUM_OF_NEIGHBOURS_STARS]; - /*! Number of interactions in the force SELF and PAIR */ - int num_ngb_force; + /*! Number of interactions in the feedback SELF and PAIR */ + int num_ngb_feedback; /*! List of interacting particles in the force SELF and PAIR */ - long long ids_ngbs_force[MAX_NUM_OF_NEIGHBOURS_STARS]; + long long ids_ngbs_feedback[MAX_NUM_OF_NEIGHBOURS_STARS]; #endif } SWIFT_STRUCT_ALIGN; @@ -135,7 +135,7 @@ struct stars_props { /*! Resolution parameter */ float eta_neighbours; - /*! Target weightd number of neighbours (for info only)*/ + /*! Target weighted number of neighbours (for info only)*/ float target_neighbours; /*! Smoothing length tolerance */ diff --git a/src/task_order.h b/src/task_order.h index da1f8032a3..7924bc2ee3 100644 --- a/src/task_order.h +++ b/src/task_order.h @@ -21,6 +21,9 @@ #include "../config.h" +/* Local includes */ +#include "scheduler.h" + #ifdef TASK_ORDER_NONE #include "task_order/none/task_order.h" #elif TASK_ORDER_GEAR diff --git a/src/task_order/EAGLE/task_order.h b/src/task_order/EAGLE/task_order.h index e6dff8610e..b3769d65e8 100644 --- a/src/task_order/EAGLE/task_order.h +++ b/src/task_order/EAGLE/task_order.h @@ -19,8 +19,16 @@ #ifndef SWIFT_TASK_ORDER_EAGLE_H #define SWIFT_TASK_ORDER_EAGLE_H +/** + * Is the star-formation task running before the feedback task? + */ #define task_order_star_formation_before_feedback 1 +/** + * Is the cooling task running after the time-step calculation task? + */ +#define task_order_cooling_after_timestep 0 + /** * @brief Place the star formation cell at the right place in the dependency * graph. diff --git a/src/task_order/GEAR/task_order.h b/src/task_order/GEAR/task_order.h index 3708881db6..461068c6e4 100644 --- a/src/task_order/GEAR/task_order.h +++ b/src/task_order/GEAR/task_order.h @@ -19,8 +19,16 @@ #ifndef SWIFT_TASK_ORDER_GEAR_H #define SWIFT_TASK_ORDER_GEAR_H +/** + * Is the star-formation task running before the feedback task? + */ #define task_order_star_formation_before_feedback 0 +/** + * Is the cooling task running after the time-step calculation task? + */ +#define task_order_cooling_after_timestep 1 + /** * @brief Place the star formation cell at the right place in the dependency * graph. diff --git a/src/task_order/none/task_order.h b/src/task_order/none/task_order.h index 3bb7975f43..826999f9dc 100644 --- a/src/task_order/none/task_order.h +++ b/src/task_order/none/task_order.h @@ -19,8 +19,22 @@ #ifndef SWIFT_TASK_ORDER_NONE_H #define SWIFT_TASK_ORDER_NONE_H +/** + * @file task_order/none/task_order.h + * @brief Defines the order of the subgrid tasks when no subgrid model has + * been specified. Defaults to the EAGLE model. + */ + +/** + * Is the star-formation task running before the feedback task? + */ #define task_order_star_formation_before_feedback 1 +/** + * Is the cooling task running after the time-step calculation task? + */ +#define task_order_cooling_after_timestep 0 + /** * @brief Place the star formation cell at the right place in the dependency * graph. -- GitLab