diff --git a/configure.ac b/configure.ac index 82382447fde7c411f61dbd62f7db388a6a8d9cf9..977a86a03d5e4c82139dfd56d3f1ee7afe3ea6e8 100644 --- a/configure.ac +++ b/configure.ac @@ -155,6 +155,21 @@ LT_INIT AC_PROG_CC_C99 AC_C_INLINE +# If debugging try to show inlined functions. +if test "x$enable_debug" = "xyes"; then + # Show inlined functions. + if test "$ax_cv_c_compiler_vendor" = "gnu"; then + # Would like to use -gdwarf and let the compiler pick a good version + # but that doesn't always work. + AX_CHECK_COMPILE_FLAG([-gdwarf -fvar-tracking-assignments], + [inline_EXTRA_FLAGS="-gdwarf -fvar-tracking-assignments"], + [inline_EXTRA_FLAGS="-gdwarf-2 -fvar-tracking-assignments"]) + CFLAGS="$CFLAGS $inline_EXTRA_FLAGS" + elif test "$ax_cv_c_compiler_vendor" = "intel"; then + CFLAGS="$CFLAGS -debug inline-debug-info" + fi +fi + # Define HAVE_POSIX_MEMALIGN if it works. AX_FUNC_POSIX_MEMALIGN @@ -451,7 +466,7 @@ AC_LINK_IFELSE([AC_LANG_PROGRAM( AC_MSG_RESULT($rtc_ok) # Add warning flags by default, if these can be used. Option =error adds -# -Werror to GCC, clang and Intel. Note do this last as compiler tests may +# -Werror to GCC, clang and Intel. Note do this last as compiler tests may # become errors, if that's an issue don't use CFLAGS for these, use an AC_SUBST(). AC_ARG_ENABLE([compiler-warnings], [AS_HELP_STRING([--enable-compiler-warnings], @@ -461,7 +476,7 @@ AC_ARG_ENABLE([compiler-warnings], [enable_warn="error"] ) if test "$enable_warn" != "no"; then - + # AX_CFLAGS_WARN_ALL does not give good warning flags for the Intel compiler # We will do this by hand instead and only default to the macro for unknown compilers case "$ax_cv_c_compiler_vendor" in @@ -475,7 +490,7 @@ if test "$enable_warn" != "no"; then AX_CFLAGS_WARN_ALL ;; esac - + # Add a "choke on warning" flag if it exists if test "$enable_warn" = "error"; then case "$ax_cv_c_compiler_vendor" in diff --git a/examples/DiscPatch/HydroStatic/dynamic.pro b/examples/DiscPatch/HydroStatic/dynamic.pro index c02c65fe418e84cdd62978dbddcf5a641fa4c156..00ee3f7a8d2dc435be2093af959efd2c49903637 100644 --- a/examples/DiscPatch/HydroStatic/dynamic.pro +++ b/examples/DiscPatch/HydroStatic/dynamic.pro @@ -8,7 +8,8 @@ iplot = 1 ; if iplot = 1, make plot of E/Lz conservation, else, simply compare f @physunits indir = './' -basefile = 'Disk-Patch-dynamic_' +;basefile = 'Disc-Patch-dynamic_' +basefile = 'Disc-Patch_' ; set properties of potential uL = phys.pc ; unit of length @@ -16,18 +17,27 @@ uM = phys.msun ; unit of mass uV = 1d5 ; unit of velocity ; properties of patch -surface_density = 10. +surface_density = 100. ; surface density of all mass, which generates the gravitational potential scale_height = 100. -z_disk = 200.; +z_disk = 200. ; +fgas = 0.1 ; gas fraction gamma = 5./3. ; derived units constG = 10.^(alog10(phys.g)+alog10(uM)-2d0*alog10(uV)-alog10(uL)) ; pcentre = [0.,0.,z_disk] * pc / uL utherm = !pi * constG * surface_density * scale_height / (gamma-1.) +temp = (utherm*uV^2)*phys.m_h/phys.kb soundspeed = sqrt(gamma * (gamma-1.) * utherm) t_dyn = sqrt(scale_height / (constG * surface_density)) - +rho0 = fgas*(surface_density)/(2.*scale_height) +print,' dynamical time = ',t_dyn,' = ',t_dyn*UL/uV/(1d6*phys.yr),' Myr' +print,' thermal energy per unit mass = ',utherm +print,' central density = ',rho0,' = ',rho0*uM/uL^3/m_h,' particles/cm^3' +print,' central temperature = ',temp +lambda = 2 * !pi * phys.G^1.5 * (scale_height*uL)^1.5 * (surface_density * uM/uL^2)^0.5 * phys.m_h^2 / (gamma-1) / fgas +print,' lambda = ',lambda +stop ; infile = indir + basefile + '*' spawn,'ls -1 '+infile,res diff --git a/examples/DiscPatch/HydroStatic/makeIC.py b/examples/DiscPatch/HydroStatic/makeIC.py index 48cc578658a9520477d40bf504e3eb7c3c8e5164..e2846d82a8cfa8bf08de83632b19ae2e7818f3c1 100644 --- a/examples/DiscPatch/HydroStatic/makeIC.py +++ b/examples/DiscPatch/HydroStatic/makeIC.py @@ -56,9 +56,10 @@ print "UnitVelocity_in_cgs: ", const_unit_velocity_in_cgs # parameters of potential -surface_density = 10. +surface_density = 100. # surface density of all mass, which generates the gravitational potential scale_height = 100. gamma = 5./3. +fgas = 0.1 # gas fraction # derived units const_unit_time_in_cgs = (const_unit_length_in_cgs / const_unit_velocity_in_cgs) @@ -131,7 +132,7 @@ h = glass_h[0:numGas] numGas = numpy.shape(pos)[0] # compute furthe properties of ICs -column_density = surface_density * numpy.tanh(boxSize/2./scale_height) +column_density = fgas * surface_density * numpy.tanh(boxSize/2./scale_height) enclosed_mass = column_density * boxSize * boxSize pmass = enclosed_mass / numGas meanrho = enclosed_mass / boxSize**3 diff --git a/examples/EAGLE_25/README b/examples/EAGLE_25/README index 077fd9cf06ce64be98fa0d8a736125c474fb7a76..88fc1ea3eede1e254907dd5ba1dbf2eaa81fb694 100644 --- a/examples/EAGLE_25/README +++ b/examples/EAGLE_25/README @@ -13,4 +13,4 @@ The particle load of the main EAGLE simulation can be reproduced by running these ICs on 64 cores. MD5 checksum of the ICs: -ada2c728db2bd2d77a20c4eef52dfaf1 EAGLE_ICs_25.hdf5 +02cd1c353b86230af047b5d4ab22afcf EAGLE_ICs_25.hdf5 diff --git a/examples/Feedback/feedback.pro b/examples/Feedback/feedback.pro new file mode 100644 index 0000000000000000000000000000000000000000..02d616fc82f0aeb7011d022d13db9d1d1030e89c --- /dev/null +++ b/examples/Feedback/feedback.pro @@ -0,0 +1,24 @@ +base = 'Feedback' +inf = 'Feedback_005.hdf5' + +blast = [5.650488e-01, 5.004371e-01, 5.010494e-01] ; location of blast +pos = h5rd(inf,'PartType0/Coordinates') +vel = h5rd(inf,'PartType0/Velocities') +rho = h5rd(inf,'PartType0/Density') +utherm = h5rd(inf,'PartType0/InternalEnergy') + +; shift to centre +for ic=0,2 do pos[ic,*] = pos[ic,*] - blast[ic] + +;; distance from centre +dist = fltarr(n_elements(rho)) +for ic=0,2 do dist = dist + pos[ic,*]^2 +dist = sqrt(dist) + +; radial velocity +vr = fltarr(n_elements(rho)) +for ic=0,2 do vr = vr + pos[ic,*]*vel[ic,*] +vr = vr / dist + +; +end diff --git a/examples/Feedback/feedback.yml b/examples/Feedback/feedback.yml new file mode 100644 index 0000000000000000000000000000000000000000..9ff7eea325b43fe3c670d16bdbd4ccc66f33333e --- /dev/null +++ b/examples/Feedback/feedback.yml @@ -0,0 +1,44 @@ +# 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 + +# 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: 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) + +# 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). + delta_neighbours: 0.1 # The tolerance for the targetted number of neighbours. + max_smoothing_length: 0.1 # Maximal smoothing length allowed (in internal units). + CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. + +# Parameters related to the initial conditions +InitialConditions: + file_name: ./Feedback.hdf5 # The file to read + +# Parameters for feedback + +SN: + time: 0.001 # time the SN explodes (internal units) + energy: 1.0 # energy of the explosion (internal units) + x: 0.5 # x-position of explostion (internal units) + y: 0.5 # y-position of explostion (internal units) + z: 0.5 # z-position of explostion (internal units) diff --git a/examples/Feedback/makeIC.py b/examples/Feedback/makeIC.py new file mode 100644 index 0000000000000000000000000000000000000000..bd1081a9c275616038f5fa4e3eb943c36cb4c3eb --- /dev/null +++ b/examples/Feedback/makeIC.py @@ -0,0 +1,109 @@ +############################################################################### + # This file is part of SWIFT. + # Copyright (c) 2013 Pedro Gonnet (pedro.gonnet@durham.ac.uk), + # Matthieu Schaller (matthieu.schaller@durham.ac.uk) + # 2016 Tom Theuns (tom.theuns@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 +import sys +from numpy import * + +# Generates a swift IC file containing a cartesian distribution of particles +# at a constant density and pressure in a cubic box + +# Parameters +periodic= 1 # 1 For periodic box +boxSize = 1. +L = int(sys.argv[1]) # Number of particles along one axis +rho = 1. # Density +P = 1.e-6 # Pressure +gamma = 5./3. # Gas adiabatic index +eta = 1.2349 # 48 ngbs with cubic spline kernel +fileName = "Feedback.hdf5" + +#--------------------------------------------------- +numPart = L**3 +mass = boxSize**3 * rho / numPart +internalEnergy = P / ((gamma - 1.)*rho) + +#-------------------------------------------------- + +#File +file = h5py.File(fileName, 'w') + +# Header +grp = file.create_group("/Header") +grp.attrs["BoxSize"] = boxSize +grp.attrs["NumPart_Total"] = [numPart, 0, 0, 0, 0, 0] +grp.attrs["NumPart_Total_HighWord"] = [0, 0, 0, 0, 0, 0] +grp.attrs["NumPart_ThisFile"] = [numPart, 0, 0, 0, 0, 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 + +#Runtime parameters +grp = file.create_group("/RuntimePars") +grp.attrs["PeriodicBoundariesOn"] = periodic + +#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") + +v = zeros((numPart, 3)) +ds = grp.create_dataset('Velocities', (numPart, 3), 'f') +ds[()] = v +v = zeros(1) + +m = full((numPart, 1), mass) +ds = grp.create_dataset('Masses', (numPart,1), 'f') +ds[()] = m +m = zeros(1) + +h = full((numPart, 1), eta * boxSize / L) +ds = grp.create_dataset('SmoothingLength', (numPart,1), 'f') +ds[()] = h +h = zeros(1) + +u = full((numPart, 1), internalEnergy) +ds = grp.create_dataset('InternalEnergy', (numPart,1), 'f') +ds[()] = u +u = zeros(1) + + +ids = linspace(0, numPart, numPart, endpoint=False).reshape((numPart,1)) +ds = grp.create_dataset('ParticleIDs', (numPart, 1), 'L') +ds[()] = ids + 1 +x = ids % L; +y = ((ids - x) / L) % L; +z = (ids - x - L * y) / L**2; +coords = zeros((numPart, 3)) +coords[:,0] = z[:,0] * boxSize / L + boxSize / (2*L) +coords[:,1] = y[:,0] * boxSize / L + boxSize / (2*L) +coords[:,2] = x[:,0] * boxSize / L + boxSize / (2*L) +ds = grp.create_dataset('Coordinates', (numPart, 3), 'd') +ds[()] = coords + +file.close() diff --git a/examples/SedovBlast_1D/sedov.yml b/examples/SedovBlast_1D/sedov.yml index 6f519835d26ff5aa851ffb8999e650815c522cd3..1ecfeb32452d05f299b98124c4fdfc79126f7504 100644 --- a/examples/SedovBlast_1D/sedov.yml +++ b/examples/SedovBlast_1D/sedov.yml @@ -21,7 +21,7 @@ Snapshots: # Parameters governing the conserved quantities statistics Statistics: - delta_time: 1e-3 # Time between statistics output + delta_time: 1e-5 # Time between statistics output # Parameters for the hydrodynamics scheme SPH: diff --git a/examples/main.c b/examples/main.c index cf252c16f7efc08ee5bc6bf36f58458583631e87..2833ec3169ea93a99eefe8f126356d5848f9fd13 100644 --- a/examples/main.c +++ b/examples/main.c @@ -76,6 +76,7 @@ void print_help_message() { "Overwrite the CPU frequency (Hz) to be used for time measurements"); printf(" %2s %8s %s\n", "-g", "", "Run with an external gravitational potential"); + printf(" %2s %8s %s\n", "-F", "", "Run with feedback "); printf(" %2s %8s %s\n", "-G", "", "Run with self-gravity"); printf(" %2s %8s %s\n", "-n", "{int}", "Execute a fixed number of time steps. When unset use the time_end " @@ -147,6 +148,7 @@ int main(int argc, char *argv[]) { int nsteps = -2; int with_cosmology = 0; int with_external_gravity = 0; + int with_sourceterms = 0; int with_cooling = 0; int with_self_gravity = 0; int with_hydro = 0; @@ -159,7 +161,7 @@ int main(int argc, char *argv[]) { /* Parse the parameters */ int c; - while ((c = getopt(argc, argv, "acCdDef:gGhn:st:v:y:")) != -1) switch (c) { + while ((c = getopt(argc, argv, "acCdDef:FgGhn:st:v:y:")) != -1) switch (c) { case 'a': with_aff = 1; break; @@ -185,6 +187,9 @@ int main(int argc, char *argv[]) { return 1; } break; + case 'F': + with_sourceterms = 1; + break; case 'g': with_external_gravity = 1; break; @@ -444,6 +449,11 @@ int main(int argc, char *argv[]) { if (with_cooling) cooling_init(params, &us, &prog_const, &cooling_func); if (with_cooling && myrank == 0) cooling_print(&cooling_func); + /* Initialise the feedback properties */ + struct sourceterms sourceterms; + if (with_sourceterms) sourceterms_init(params, &us, &sourceterms); + if (with_sourceterms && myrank == 0) sourceterms_print(&sourceterms); + /* Construct the engine policy */ int engine_policies = ENGINE_POLICY | engine_policy_steal; if (with_drift_all) engine_policies |= engine_policy_drift_all; @@ -452,13 +462,14 @@ int main(int argc, char *argv[]) { if (with_external_gravity) engine_policies |= engine_policy_external_gravity; if (with_cosmology) engine_policies |= engine_policy_cosmology; if (with_cooling) engine_policies |= engine_policy_cooling; + if (with_sourceterms) engine_policies |= engine_policy_sourceterms; /* Initialize the engine with the space and policies. */ if (myrank == 0) clocks_gettime(&tic); struct engine e; engine_init(&e, &s, params, nr_nodes, myrank, nr_threads, with_aff, engine_policies, talking, &us, &prog_const, &hydro_properties, - &potential, &cooling_func); + &potential, &cooling_func, &sourceterms); if (myrank == 0) { clocks_gettime(&toc); message("engine_init took %.3f %s.", clocks_diff(&tic, &toc), diff --git a/examples/parameter_example.yml b/examples/parameter_example.yml index be2f512613edffd72eaf03689bccee0dd755726b..fb193a81baf410360172793763875891f654211a 100644 --- a/examples/parameter_example.yml +++ b/examples/parameter_example.yml @@ -12,6 +12,7 @@ Scheduler: cell_max_size: 8000000 # (Optional) Maximal number of interactions per task if we force the split (this is the default value). cell_sub_size: 64000000 # (Optional) Maximal number of interactions per sub-task (this is the default value). cell_split_size: 400 # (Optional) Maximal number of particles per cell (this is the default value). + cell_max_count: 10000 # (Optional) Maximal number of particles per cell allowed before triggering a sanitizing (this is the default value). # Parameters governing the time integration TimeIntegration: diff --git a/examples/plot_scaling_results.py b/examples/plot_scaling_results.py index 5a76e9870bd3ec55807c7b79c475c62b14119e5c..938d204bea8a9da81a93cf2d6d7334ac99d10e3a 100755 --- a/examples/plot_scaling_results.py +++ b/examples/plot_scaling_results.py @@ -131,8 +131,10 @@ def parse_files(): file_list = [ file_list[j] for j in sorted_indices] parse_header(file_list[0]) + + branch[i] = branch[i].replace("_", "\\_") - version.append(branch[i] + " " + revision[i] + "\n" + hydro_scheme[i] + + version.append("$\\textrm{%s}$"%str(branch[i]) + " " + revision[i] + "\n" + hydro_scheme[i] + "\n" + hydro_kernel[i] + r", $N_{ngb}=%d$"%float(hydro_neighbours[i]) + r", $\eta=%.3f$"%float(hydro_eta[i])) times.append([]) @@ -215,14 +217,21 @@ def plot_results(times,totalTime,speedUp,parallelEff): pts = [1, 10**np.floor(np.log10(threadList[i][-1])+1)] totalTimePlot.loglog(pts,totalTime[i][0]/pts, 'k--', lw=1., color='0.2') totalTimePlot.loglog(threadList[i],totalTime[i],linestyle[i],label=version[i]) - + + y_min = 10**np.floor(np.log10(np.min(totalTime[:][-1])*0.6)) + y_max = 1.2*10**np.floor(np.log10(np.max(totalTime[:][0]) * 1.5)+1) totalTimePlot.set_xscale('log') totalTimePlot.set_xlabel("${\\rm Threads}$", labelpad=0.) totalTimePlot.set_ylabel("${\\rm Time~to~solution}~[{\\rm ms}]$", labelpad=0.) totalTimePlot.set_xlim([0.9, 10**(np.floor(np.log10(threadList[i][-1]))+0.5)]) - totalTimePlot.set_ylim([10**np.floor(np.log10(np.min(totalTime)*0.6)), 1.2*10**np.floor(np.log10(np.max(totalTime) * 1.5)+1)]) + totalTimePlot.set_ylim(y_min, y_max) + + ax2 = totalTimePlot.twinx() + ax2.set_yscale('log') + ax2.set_ylabel("${\\rm Time~to~solution}~[{\\rm hr}]$", labelpad=0.) + ax2.set_ylim(y_min / 3.6e6, y_max / 3.6e6) - totalTimePlot.legend(bbox_to_anchor=(1.14, 0.97), loc=2, borderaxespad=0.,prop={'size':12}, frameon=False) + totalTimePlot.legend(bbox_to_anchor=(1.21, 0.97), loc=2, borderaxespad=0.,prop={'size':12}, frameon=False) emptyPlot.axis('off') for i, txt in enumerate(threadList[0]): diff --git a/examples/plot_tasks.py b/examples/plot_tasks.py index fb8b2ce57a47b4d397284bba9960b098c1e3ce62..7b4683422725f206a3f582e00d82712c7e3c3f59 100755 --- a/examples/plot_tasks.py +++ b/examples/plot_tasks.py @@ -56,8 +56,9 @@ pl.rcParams.update(PLOT_PARAMS) # Tasks and subtypes. Indexed as in tasks.h. TASKTYPES = ["none", "sort", "self", "pair", "sub_self", "sub_pair", "init", "ghost", - "kick", "kick_fixdt", "send", "recv", "grav_gather_m", "grav_fft", - "grav_mm", "grav_up", "grav_external", "count"] + "extra_ghost", "kick", "kick_fixdt", "send", "recv", + "grav_gather_m", "grav_fft", "grav_mm", "grav_up", + "grav_external", "cooling", "count"] TASKCOLOURS = {"none": "black", "sort": "lightblue", @@ -67,6 +68,7 @@ TASKCOLOURS = {"none": "black", "sub_pair": "navy", "init": "indigo", "ghost": "cyan", + "extra_ghost": "cyan", "kick": "green", "kick_fixdt": "green", "send": "yellow", @@ -76,6 +78,7 @@ TASKCOLOURS = {"none": "black", "grav_mm": "mediumturquoise", "grav_up": "mediumvioletred", "grav_external": "darkred", + "cooling": "darkblue", "count": "powerblue"} SUBTYPES = ["none", "density", "gradient", "force", "grav", "tend", "count"] @@ -118,7 +121,7 @@ toc_step = int(full_step[5]) CPU_CLOCK = float(full_step[-1]) data = data[1:,:] -print "CPU frequency:", CPU_CLOCK / 1.e9 +print "CPU frequency:", CPU_CLOCK # Avoid start and end times of zero. data = data[data[:,4] != 0] @@ -130,6 +133,7 @@ if delta_t == 0: dt = max(data[:,5]) - min(data[:,4]) if dt > delta_t: delta_t = dt + print "Data range: ", delta_t / CPU_CLOCK * 1000, "ms" # Once more doing the real gather and plots this time. start_t = tic_step diff --git a/examples/plot_tasks_MPI.py b/examples/plot_tasks_MPI.py index 398324cdc773d1dc4b7f26c58866c9df6469cc0b..02bc4a03510afce396429316fb4489926bc41b12 100755 --- a/examples/plot_tasks_MPI.py +++ b/examples/plot_tasks_MPI.py @@ -2,7 +2,7 @@ """ Usage: plot_tasks_MPI.py input.dat png-output-prefix [time-range-ms] - + where input.dat is a thread info file for a step of an MPI run. Use the '-y interval' flag of the swift MPI commands to create these. The output plots will be called 'png-output-prefix<mpi-rank>.png', i.e. one each for all the @@ -40,6 +40,8 @@ matplotlib.use("Agg") import pylab as pl import numpy as np import sys +#import warnings +#warnings.simplefilter("error") # Basic plot configuration. PLOT_PARAMS = {"axes.labelsize": 10, @@ -61,9 +63,10 @@ PLOT_PARAMS = {"axes.labelsize": 10, pl.rcParams.update(PLOT_PARAMS) # Tasks and subtypes. Indexed as in tasks.h. -TASKTYPES = ["none", "sort", "self", "pair", "sub_self", "sub_pair", "init", "ghost", - "kick", "kick_fixdt", "send", "recv", "grav_gather_m", "grav_fft", - "grav_mm", "grav_up", "grav_external", "count"] +TASKTYPES = ["none", "sort", "self", "pair", "sub_self", "sub_pair", "init", + "ghost", "extra_ghost", "kick", "kick_fixdt", "send", "recv", + "grav_gather_m", "grav_fft", "grav_mm", "grav_up", + "grav_external", "cooling", "count"] TASKCOLOURS = {"none": "black", "sort": "lightblue", @@ -73,6 +76,7 @@ TASKCOLOURS = {"none": "black", "sub_pair": "navy", "init": "indigo", "ghost": "cyan", + "extra_ghost": "cyan", "kick": "green", "kick_fixdt": "green", "send": "yellow", @@ -82,6 +86,7 @@ TASKCOLOURS = {"none": "black", "grav_mm": "mediumturquoise", "grav_up": "mediumvioletred", "grav_external": "darkred", + "cooling": "darkblue", "count": "powerblue"} SUBTYPES = ["none", "density", "gradient", "force", "grav", "tend", "count"] @@ -111,7 +116,7 @@ outbase = sys.argv[2] delta_t = 0 if len( sys.argv ) == 4: delta_t = int(sys.argv[3]) - + # Read input. data = pl.loadtxt( infile ) @@ -121,7 +126,7 @@ tic_step = int(full_step[5]) toc_step = int(full_step[6]) CPU_CLOCK = float(full_step[-1]) -print "CPU frequency:", CPU_CLOCK / 1.e9 +print "CPU frequency:", CPU_CLOCK nranks = int(max(data[:,0])) + 1 @@ -143,6 +148,8 @@ if delta_t == 0: dt = max(data[:,6]) - min(data[:,5]) if dt > delta_t: delta_t = dt + print "Data range: ", delta_t / CPU_CLOCK * 1000, "ms" + # Once more doing the real gather and plots this time. for rank in range(nranks): @@ -152,93 +159,107 @@ for rank in range(nranks): tic_step = int(full_step[5]) toc_step = int(full_step[6]) data = data[1:,:] + typesseen = [] - start_t = tic_step - data[:,5] -= start_t - data[:,6] -= start_t - end_t = (toc_step - start_t) / CPU_CLOCK * 1000 - - tasks = {} - tasks[-1] = [] - for i in range(nthread): - tasks[i] = [] - - num_lines = pl.shape(data)[0] - for line in range(num_lines): - thread = int(data[line,1]) - tasks[thread].append({}) - tasks[thread][-1]["type"] = TASKTYPES[int(data[line,2])] - tasks[thread][-1]["subtype"] = SUBTYPES[int(data[line,3])] - tic = int(data[line,5]) / CPU_CLOCK * 1000 - toc = int(data[line,6]) / CPU_CLOCK * 1000 - tasks[thread][-1]["tic"] = tic - tasks[thread][-1]["toc"] = toc - tasks[thread][-1]["t"] = (toc + tic)/ 2 - - combtasks = {} - combtasks[-1] = [] - for i in range(nthread): - combtasks[i] = [] - - for thread in range(nthread): - tasks[thread] = sorted(tasks[thread], key=lambda l: l["t"]) - lasttype = "" - types = [] - for task in tasks[thread]: - if task["type"] not in types: - types.append(task["type"]) - if lasttype == "" or not lasttype == task["type"]: - combtasks[thread].append({}) - combtasks[thread][-1]["type"] = task["type"] - combtasks[thread][-1]["subtype"] = task["subtype"] - combtasks[thread][-1]["tic"] = task["tic"] - combtasks[thread][-1]["toc"] = task["toc"] - if task["type"] == "self" or task["type"] == "pair" or task["type"] == "sub": - combtasks[thread][-1]["colour"] = SUBCOLOURS[task["subtype"]] + # Dummy image for ranks that have no tasks. + if data.size == 0: + print "rank ", rank, " has no tasks" + fig = pl.figure() + ax = fig.add_subplot(1,1,1) + ax.set_xlim(-delta_t * 0.03 * 1000 / CPU_CLOCK, delta_t * 1.03 * 1000 / CPU_CLOCK) + ax.set_ylim(0, nthread) + start_t = tic_step + end_t = (toc_step - start_t) / CPU_CLOCK * 1000 + else: + + start_t = tic_step + data[:,5] -= start_t + data[:,6] -= start_t + end_t = (toc_step - start_t) / CPU_CLOCK * 1000 + + tasks = {} + tasks[-1] = [] + for i in range(nthread): + tasks[i] = [] + + num_lines = pl.shape(data)[0] + for line in range(num_lines): + thread = int(data[line,1]) + tasks[thread].append({}) + tasks[thread][-1]["type"] = TASKTYPES[int(data[line,2])] + tasks[thread][-1]["subtype"] = SUBTYPES[int(data[line,3])] + tic = int(data[line,5]) / CPU_CLOCK * 1000 + toc = int(data[line,6]) / CPU_CLOCK * 1000 + tasks[thread][-1]["tic"] = tic + tasks[thread][-1]["toc"] = toc + tasks[thread][-1]["t"] = (toc + tic)/ 2 + + combtasks = {} + combtasks[-1] = [] + for i in range(nthread): + combtasks[i] = [] + + for thread in range(nthread): + tasks[thread] = sorted(tasks[thread], key=lambda l: l["t"]) + lasttype = "" + types = [] + for task in tasks[thread]: + if task["type"] not in types: + types.append(task["type"]) + if lasttype == "" or not lasttype == task["type"]: + combtasks[thread].append({}) + combtasks[thread][-1]["type"] = task["type"] + combtasks[thread][-1]["subtype"] = task["subtype"] + combtasks[thread][-1]["tic"] = task["tic"] + combtasks[thread][-1]["toc"] = task["toc"] + if task["type"] == "self" or task["type"] == "pair" or task["type"] == "sub": + combtasks[thread][-1]["colour"] = SUBCOLOURS[task["subtype"]] + else: + combtasks[thread][-1]["colour"] = TASKCOLOURS[task["type"]] + lasttype = task["type"] else: - combtasks[thread][-1]["colour"] = TASKCOLOURS[task["type"]] - lasttype = task["type"] - else: - combtasks[thread][-1]["toc"] = task["toc"] + combtasks[thread][-1]["toc"] = task["toc"] + + fig = pl.figure() + ax = fig.add_subplot(1,1,1) + ax.set_xlim(-delta_t * 0.03 * 1000 / CPU_CLOCK, delta_t * 1.03 * 1000 / CPU_CLOCK) + ax.set_ylim(0, nthread) + tictoc = np.zeros(2) + for i in range(nthread): + + # Collect ranges and colours into arrays. + tictocs = np.zeros(len(combtasks[i])*2) + colours = np.empty(len(combtasks[i])*2, dtype='object') + coloursseen = [] + j = 0 + for task in combtasks[i]: + tictocs[j] = task["tic"] + tictocs[j+1] = task["toc"] + colours[j] = task["colour"] + colours[j+1] = task["colour"] + j = j + 2 + if task["colour"] not in coloursseen: + coloursseen.append(task["colour"]) + + # Legend support, collections don't add to this. + if task["subtype"] != "none": + qtask = task["type"] + "/" + task["subtype"] + else: + qtask = task["type"] - typesseen = [] - fig = pl.figure() - ax = fig.add_subplot(1,1,1) - ax.set_xlim(-delta_t * 0.03 * 1000 / CPU_CLOCK, delta_t * 1.03 * 1000 / CPU_CLOCK) - ax.set_ylim(0, nthread) - tictoc = np.zeros(2) - for i in range(nthread): - - # Collect ranges and colours into arrays. - tictocs = np.zeros(len(combtasks[i])*2) - colours = np.empty(len(combtasks[i])*2, dtype='object') - coloursseen = [] - j = 0 - for task in combtasks[i]: - tictocs[j] = task["tic"] - tictocs[j+1] = task["toc"] - colours[j] = task["colour"] - colours[j+1] = task["colour"] - j = j + 2 - if task["colour"] not in coloursseen: - coloursseen.append(task["colour"]) - - # Legend support, collections don't add to this. - if task["subtype"] != "none": - qtask = task["type"] + "/" + task["subtype"] - else: - qtask = task["type"] - if qtask not in typesseen: - pl.plot([], [], color=task["colour"], label=qtask) - typesseen.append(qtask) - - # Now plot each colour, faster to use a mask to select colour ranges. - for colour in coloursseen: - collection = collections.BrokenBarHCollection.span_where(tictocs, ymin=i+0.05, ymax=i+0.95, - where=colours == colour, - facecolor=colour, - linewidths=0) - ax.add_collection(collection) + if qtask not in typesseen: + pl.plot([], [], color=task["colour"], label=qtask) + typesseen.append(qtask) + + # Now plot each colour, faster to use a mask to select colour ranges. + for colour in coloursseen: + collection = collections.BrokenBarHCollection.span_where(tictocs, + ymin=i+0.05, + ymax=i+0.95, + where=colours == colour, + facecolor=colour, + linewidths=0) + ax.add_collection(collection) # Legend and room for it. @@ -247,7 +268,8 @@ for rank in range(nranks): nrow = nrow + 1 ax.fill_between([0, 0], nthread+0.5, nthread + nrow + 0.5, facecolor="white") ax.set_ylim(0, nthread + nrow + 1) - ax.legend(loc=1, shadow=True, mode="expand", ncol=5) + if data.size > 0: + ax.legend(loc=1, shadow=True, mode="expand", ncol=5) # Start and end of time-step ax.plot([0, 0], [0, nthread + nrow + 1], 'k--', linewidth=1) diff --git a/src/Makefile.am b/src/Makefile.am index f7cb52ba40a34269173ab8c5019d3011b0c34b61..5859c59d9ddbc7e701145c40f8f0a35b92a075c3 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -43,7 +43,8 @@ include_HEADERS = space.h runner.h queue.h task.h lock.h cell.h part.h const.h \ engine.h swift.h serial_io.h timers.h debug.h scheduler.h proxy.h parallel_io.h \ common_io.h single_io.h multipole.h map.h tools.h partition.h clocks.h parser.h \ physical_constants.h physical_constants_cgs.h potential.h version.h \ - hydro_properties.h riemann.h threadpool.h cooling.h cooling_struct.h + hydro_properties.h riemann.h threadpool.h cooling.h cooling_struct.h sourceterms.h \ + sourceterms_struct.h # Common source files @@ -52,7 +53,7 @@ AM_SOURCES = space.c runner.c queue.c task.c cell.c engine.c \ units.c common_io.c single_io.c multipole.c version.c map.c \ kernel_hydro.c tools.c part.c partition.c clocks.c parser.c \ physical_constants.c potential.c hydro_properties.c \ - runner_doiact_fft.c threadpool.c cooling.c + runner_doiact_fft.c threadpool.c cooling.c sourceterms.c # Include files for distribution, not installation. nobase_noinst_HEADERS = align.h approx_math.h atomic.h cycle.h error.h inline.h kernel_hydro.h kernel_gravity.h \ @@ -62,6 +63,7 @@ nobase_noinst_HEADERS = align.h approx_math.h atomic.h cycle.h error.h inline.h gravity.h gravity_io.h \ gravity/Default/gravity.h gravity/Default/gravity_iact.h gravity/Default/gravity_io.h \ gravity/Default/gravity_debug.h gravity/Default/gravity_part.h \ + sourceterms.h \ hydro.h hydro_io.h \ hydro/Minimal/hydro.h hydro/Minimal/hydro_iact.h hydro/Minimal/hydro_io.h \ hydro/Minimal/hydro_debug.h hydro/Minimal/hydro_part.h \ @@ -69,6 +71,8 @@ nobase_noinst_HEADERS = align.h approx_math.h atomic.h cycle.h error.h inline.h hydro/Default/hydro_debug.h hydro/Default/hydro_part.h \ hydro/Gadget2/hydro.h hydro/Gadget2/hydro_iact.h hydro/Gadget2/hydro_io.h \ hydro/Gadget2/hydro_debug.h hydro/Gadget2/hydro_part.h \ + hydro/PressureEntropy/hydro.h hydro/PressureEntropy/hydro_iact.h hydro/PressureEntropy/hydro_io.h \ + hydro/PressureEntropy/hydro_debug.h hydro/PressureEntropy/hydro_part.h \ hydro/Gizmo/hydro.h hydro/Gizmo/hydro_iact.h hydro/Gizmo/hydro_io.h \ hydro/Gizmo/hydro_debug.h hydro/Gizmo/hydro_part.h \ riemann/riemann_hllc.h riemann/riemann_trrs.h \ diff --git a/src/adiabatic_index.h b/src/adiabatic_index.h index a0c9ce09e3e004af07e8b208ef9f1af5f46c9e81..59937db3c8d09fc7e6e969de0aee60f01a2e5a2c 100644 --- a/src/adiabatic_index.h +++ b/src/adiabatic_index.h @@ -410,7 +410,7 @@ __attribute__((always_inline)) INLINE static float pow_one_over_gamma(float x) { #if defined(HYDRO_GAMMA_5_3) - return powf(x, 0.6f); /* x^(3/5) */ + return powf(x, hydro_one_over_gamma); /* x^(3/5) */ #elif defined(HYDRO_GAMMA_7_5) @@ -418,7 +418,7 @@ __attribute__((always_inline)) INLINE static float pow_one_over_gamma(float x) { #elif defined(HYDRO_GAMMA_4_3) - return powf(x, 0.75f); /* x^(3/4) */ + return powf(x, hydro_one_over_gamma); /* x^(3/4) */ #elif defined(HYDRO_GAMMA_2_1) diff --git a/src/cell.c b/src/cell.c index 7ce6fb81a8fa6875884d3f5c840c36e5177cdf6b..8728f32cb04f2f1de389386b632371a27d523bb9 100644 --- a/src/cell.c +++ b/src/cell.c @@ -679,6 +679,59 @@ void cell_split(struct cell *c, ptrdiff_t parts_offset) { part_relink_parts(gparts, gcount, parts - parts_offset); } +/** + * @brief Sanitizes the smoothing length values of cells by setting large + * outliers to more sensible values. + * + * We compute the mean and standard deviation of the smoothing lengths in + * logarithmic space and limit values to mean + 4 sigma. + * + * @param c The cell. + */ +void cell_sanitize(struct cell *c) { + + const int count = c->count; + struct part *parts = c->parts; + + /* First collect some statistics */ + float h_mean = 0.f, h_mean2 = 0.f; + float h_min = FLT_MAX, h_max = 0.f; + for (int i = 0; i < count; ++i) { + + const float h = logf(parts[i].h); + h_mean += h; + h_mean2 += h * h; + h_max = max(h_max, h); + h_min = min(h_min, h); + } + h_mean /= count; + h_mean2 /= count; + const float h_var = h_mean2 - h_mean * h_mean; + const float h_std = (h_var > 0.f) ? sqrtf(h_var) : 0.1f * h_mean; + + /* Choose a cut */ + const float h_limit = expf(h_mean + 4.f * h_std); + + /* Be verbose this is not innocuous */ + message("Cell properties: h_min= %f h_max= %f geometric mean= %f.", + expf(h_min), expf(h_max), expf(h_mean)); + + if (c->h_max > h_limit) { + + message("Smoothing lengths will be limited to (mean + 4sigma)= %f.", + h_limit); + + /* Apply the cut */ + for (int i = 0; i < count; ++i) parts->h = min(parts[i].h, h_limit); + + c->h_max = h_limit; + + } else { + + message("Smoothing lengths will not be limited."); + } +} + /** * @brief Converts hydro quantities to a valid state after the initial density * calculation diff --git a/src/cell.h b/src/cell.h index b78cc0a8f842770f60777e3986616a175d2f33ca..f87420c86c5765f42fe04e8dee95558edff90cf9 100644 --- a/src/cell.h +++ b/src/cell.h @@ -168,6 +168,9 @@ struct cell { /* Task for cooling */ struct task *cooling; + /* Task for source terms */ + struct task *sourceterms; + /* Number of tasks that are associated with this cell. */ int nr_tasks; @@ -218,6 +221,7 @@ struct cell { /* Function prototypes. */ void cell_split(struct cell *c, ptrdiff_t parts_offset); +void cell_sanitize(struct cell *c); int cell_locktree(struct cell *c); void cell_unlocktree(struct cell *c); int cell_glocktree(struct cell *c); diff --git a/src/const.h b/src/const.h index 316a13c8ebba4785e8e1a66b8307f8681697c938..ef26525b7865bac4c0164f7678398ca307fc1d67 100644 --- a/src/const.h +++ b/src/const.h @@ -65,6 +65,7 @@ /* SPH variant to use */ //#define MINIMAL_SPH #define GADGET2_SPH +//#define HOPKINS_PE_SPH //#define DEFAULT_SPH //#define GIZMO_SPH @@ -95,6 +96,10 @@ //#define EXTERNAL_POTENTIAL_ISOTHERMALPOTENTIAL //#define EXTERNAL_POTENTIAL_DISC_PATCH +/* Source terms */ +#define SOURCETERMS_NONE +//#define SOURCETERMS_SN_FEEDBACK + /* Cooling properties */ #define COOLING_NONE //#define COOLING_CONST_DU diff --git a/src/debug.c b/src/debug.c index d73bc86a92cf5ca28c202e7b567cf7c40ba6eccb..be42485a38ea8d560797e8f1ccc5936456febcd8 100644 --- a/src/debug.c +++ b/src/debug.c @@ -44,6 +44,8 @@ #include "./hydro/Minimal/hydro_debug.h" #elif defined(GADGET2_SPH) #include "./hydro/Gadget2/hydro_debug.h" +#elif defined(HOPKINS_PE_SPH) +#include "./hydro/PressureEntropy/hydro_debug.h" #elif defined(DEFAULT_SPH) #include "./hydro/Default/hydro_debug.h" #elif defined(GIZMO_SPH) @@ -65,7 +67,7 @@ * * (Should be used for debugging only as it runs in O(N).) */ -void printParticle(const struct part *parts, struct xpart *xparts, +void printParticle(const struct part *parts, const struct xpart *xparts, long long int id, size_t N) { int found = 0; @@ -125,7 +127,7 @@ void printgParticle(const struct gpart *gparts, const struct part *parts, */ void printParticle_single(const struct part *p, const struct xpart *xp) { - printf("## Particle: id=%lld", p->id); + printf("## Particle: id=%lld ", p->id); hydro_debug_particle(p, xp); printf("\n"); } diff --git a/src/debug.h b/src/debug.h index 22b63820745ca7282b7699f0be09e493238d83c2..2142a22eca91338580d8f50197a57de0cf248bee 100644 --- a/src/debug.h +++ b/src/debug.h @@ -24,7 +24,7 @@ #include "part.h" #include "space.h" -void printParticle(const struct part *parts, struct xpart *xparts, +void printParticle(const struct part *parts, const struct xpart *xparts, long long int id, size_t N); void printgParticle(const struct gpart *gparts, const struct part *parts, long long int id, size_t N); diff --git a/src/engine.c b/src/engine.c index 507155e3edb468d268d3a0fd7170414b9cebfb84..0319dfd81fa5c3ebf68b00a04946499326eda261 100644 --- a/src/engine.c +++ b/src/engine.c @@ -68,7 +68,7 @@ #include "units.h" #include "version.h" -const char *engine_policy_names[15] = {"none", +const char *engine_policy_names[16] = {"none", "rand", "steal", "keep", @@ -82,7 +82,8 @@ const char *engine_policy_names[15] = {"none", "external_gravity", "cosmology_integration", "drift_all", - "cooling"}; + "cooling", + "sourceterms"}; /** The rank of the engine as a global variable (for messages). */ int engine_rank; @@ -187,6 +188,8 @@ void engine_make_hydro_hierarchical_tasks(struct engine *e, struct cell *c, const int is_fixdt = (e->policy & engine_policy_fixdt) == engine_policy_fixdt; const int is_with_cooling = (e->policy & engine_policy_cooling) == engine_policy_cooling; + const int is_with_sourceterms = + (e->policy & engine_policy_sourceterms) == engine_policy_sourceterms; /* Is this the super-cell? */ if (super == NULL && (c->density != NULL || (c->count > 0 && !c->split))) { @@ -216,16 +219,18 @@ void engine_make_hydro_hierarchical_tasks(struct engine *e, struct cell *c, /* Generate the ghost task. */ c->ghost = scheduler_addtask(s, task_type_ghost, task_subtype_none, 0, 0, c, NULL, 0); - #ifdef EXTRA_HYDRO_LOOP /* Generate the extra ghost task. */ c->extra_ghost = scheduler_addtask(s, task_type_extra_ghost, task_subtype_none, 0, 0, c, NULL, 0); #endif - if (is_with_cooling) c->cooling = scheduler_addtask(s, task_type_cooling, task_subtype_none, 0, 0, c, NULL, 0); + /* add source terms */ + if (is_with_sourceterms) + c->sourceterms = scheduler_addtask(s, task_type_sourceterms, + task_subtype_none, 0, 0, c, NULL, 0); } } @@ -1782,12 +1787,18 @@ void engine_make_extra_hydroloop_tasks(struct engine *e) { scheduler_addunlock(sched, t->ci->init, t); scheduler_addunlock(sched, t, t->ci->kick); } - - /* Cooling tasks should depend on kick and does not unlock anything since - it is the last task*/ + /* Cooling tasks should depend on kick and unlock sourceterms */ else if (t->type == task_type_cooling) { scheduler_addunlock(sched, t->ci->kick, t); } + /* source terms depend on cooling if performed, else on kick. It is the last + task */ + else if (t->type == task_type_sourceterms) { + if (e->policy == engine_policy_cooling) + scheduler_addunlock(sched, t->ci->cooling, t); + else + scheduler_addunlock(sched, t->ci->kick, t); + } } } @@ -2196,6 +2207,8 @@ void engine_rebuild(struct engine *e) { /* Re-build the space. */ space_rebuild(e->s, 0.0, e->verbose); + if (e->ti_current == 0) space_sanitize(e->s); + /* If in parallel, exchange the cell structure. */ #ifdef WITH_MPI engine_exchange_cells(e); @@ -2611,7 +2624,15 @@ void engine_init_particles(struct engine *e, int flag_entropy_ICs) { TIMER_TOC(timer_runners); /* Apply some conversions (e.g. internal energy -> entropy) */ - if (!flag_entropy_ICs) space_map_cells_pre(s, 0, cell_convert_hydro, NULL); + if (!flag_entropy_ICs) { + + /* Apply the conversion */ + space_map_cells_pre(s, 0, cell_convert_hydro, NULL); + + /* Correct what we did (e.g. in PE-SPH, need to recompute rho_bar) */ + if (hydro_need_extra_init_loop) + engine_launch(e, e->nr_threads, mask, submask); + } clocks_gettime(&time2); @@ -2763,6 +2784,11 @@ void engine_step(struct engine *e) { mask |= 1 << task_type_cooling; } + /* Add the tasks corresponding to sourceterms to the masks */ + if (e->policy & engine_policy_sourceterms) { + mask |= 1 << task_type_sourceterms; + } + /* Add MPI tasks if need be */ if (e->policy & engine_policy_mpi) { @@ -2773,6 +2799,8 @@ void engine_step(struct engine *e) { engine_print_task_counts(e); + if (e->verbose) engine_print_task_counts(e); + /* Send off the runners. */ TIMER_TIC; engine_launch(e, e->nr_threads, mask, submask); @@ -3106,6 +3134,7 @@ void engine_unpin() { * @param hydro The #hydro_props used for this run. * @param potential The properties of the external potential. * @param cooling_func The properties of the cooling function. + * @param sourceterms The properties of the source terms function. */ void engine_init(struct engine *e, struct space *s, const struct swift_params *params, int nr_nodes, int nodeID, @@ -3114,7 +3143,8 @@ void engine_init(struct engine *e, struct space *s, const struct phys_const *physical_constants, const struct hydro_props *hydro, const struct external_potential *potential, - const struct cooling_function_data *cooling_func) { + const struct cooling_function_data *cooling_func, + struct sourceterms *sourceterms) { /* Clean-up everything */ bzero(e, sizeof(struct engine)); @@ -3167,6 +3197,7 @@ void engine_init(struct engine *e, struct space *s, e->hydro_properties = hydro; e->external_potential = potential; e->cooling_func = cooling_func; + e->sourceterms = sourceterms; e->parameter_file = params; engine_rank = nodeID; diff --git a/src/engine.h b/src/engine.h index d36914af611723bc4d496d5c2dc68050eea6ffe6..ec9e16553c3172f22e9bfe60971163488947a47e 100644 --- a/src/engine.h +++ b/src/engine.h @@ -44,6 +44,7 @@ #include "potential.h" #include "runner.h" #include "scheduler.h" +#include "sourceterms_struct.h" #include "space.h" #include "task.h" #include "units.h" @@ -65,6 +66,7 @@ enum engine_policy { engine_policy_cosmology = (1 << 11), engine_policy_drift_all = (1 << 12), engine_policy_cooling = (1 << 13), + engine_policy_sourceterms = (1 << 14) }; extern const char *engine_policy_names[]; @@ -207,6 +209,9 @@ struct engine { /* Properties of the cooling scheme */ const struct cooling_function_data *cooling_func; + /* Properties of source terms */ + struct sourceterms *sourceterms; + /* The (parsed) parameter file */ const struct swift_params *parameter_file; }; @@ -223,7 +228,8 @@ void engine_init(struct engine *e, struct space *s, const struct phys_const *physical_constants, const struct hydro_props *hydro, const struct external_potential *potential, - const struct cooling_function_data *cooling); + const struct cooling_function_data *cooling, + struct sourceterms *sourceterms); void engine_launch(struct engine *e, int nr_runners, unsigned int mask, unsigned int submask); void engine_prepare(struct engine *e, int nodrift); diff --git a/src/equation_of_state.h b/src/equation_of_state.h index af59d8a2cad1632c67b6d377b5ed9dfe9484b4aa..5b19f99ab85d8a2b4e5e6f0b4eeb542925b4ee50 100644 --- a/src/equation_of_state.h +++ b/src/equation_of_state.h @@ -126,6 +126,21 @@ gas_soundspeed_from_internal_energy(float density, float u) { return sqrtf(u * hydro_gamma * hydro_gamma_minus_one); } +/** + * @brief Returns the sound speed given density and pressure + * + * Computes \f$c = \sqrt{\frac{\gamma P}{\rho} }\f$. + * + * @param density The density \f$\rho\f$ + * @param P The pressure \f$P\f$ + */ +__attribute__((always_inline)) INLINE static float gas_soundspeed_from_pressure( + float density, float P) { + + const float density_inv = 1.f / density; + return sqrtf(hydro_gamma * P * density_inv); +} + /* ------------------------------------------------------------------------- */ #elif defined(EOS_ISOTHERMAL_GAS) @@ -221,6 +236,22 @@ gas_soundspeed_from_internal_energy(float density, float u) { hydro_gamma_minus_one); } +/** + * @brief Returns the sound speed given density and pressure + * + * Since we are using an isothermal EoS, the pressure value is ignored + * Computes \f$c = \sqrt{u_{cst} \gamma \rho^{\gamma-1}}\f$. + * + * @param density The density \f$\rho\f$ + * @param P The pressure \f$P\f$ + */ +__attribute__((always_inline)) INLINE static float gas_soundspeed_from_pressure( + float density, float P) { + + return sqrtf(const_isothermal_internal_energy * hydro_gamma * + hydro_gamma_minus_one); +} + /* ------------------------------------------------------------------------- */ #else diff --git a/src/hydro.h b/src/hydro.h index 4a2b0bd029d494e2091b9081d22b7949cec5648c..9e02c2009e307f0623ffb535ff9068603c2d4147 100644 --- a/src/hydro.h +++ b/src/hydro.h @@ -34,6 +34,10 @@ #include "./hydro/Gadget2/hydro.h" #include "./hydro/Gadget2/hydro_iact.h" #define SPH_IMPLEMENTATION "Gadget-2 version of SPH (Springel 2005)" +#elif defined(HOPKINS_PE_SPH) +#include "./hydro/PressureEntropy/hydro.h" +#include "./hydro/PressureEntropy/hydro_iact.h" +#define SPH_IMPLEMENTATION "Pressure-Entropy SPH (Hopkins 2013)" #elif defined(DEFAULT_SPH) #include "./hydro/Default/hydro.h" #include "./hydro/Default/hydro_iact.h" diff --git a/src/hydro/Gadget2/hydro.h b/src/hydro/Gadget2/hydro.h index 2b17505c36f345779b269c90758931a13f9b4e0d..0bdf56fe844309998db1fad4cf9581edb6b88305 100644 --- a/src/hydro/Gadget2/hydro.h +++ b/src/hydro/Gadget2/hydro.h @@ -37,6 +37,7 @@ #include "equation_of_state.h" #include "hydro_properties.h" #include "kernel_hydro.h" +#include "minmax.h" /** * @brief Returns the internal energy of a particle @@ -116,8 +117,9 @@ __attribute__((always_inline)) INLINE static float hydro_get_mass( * @brief Modifies the thermal state of a particle to the imposed internal * energy * - * This overrides the current state of the particle but does *not* change its - * time-derivatives + * This overwrites the current state of the particle but does *not* change its + * time-derivatives. Entropy, pressure, sound-speed and signal velocity will be + * updated. * * @param p The particle * @param u The new internal energy @@ -126,13 +128,31 @@ __attribute__((always_inline)) INLINE static void hydro_set_internal_energy( struct part *restrict p, float u) { p->entropy = gas_entropy_from_internal_energy(p->rho, u); + + /* Compute the new pressure */ + const float pressure = gas_pressure_from_internal_energy(p->rho, u); + + /* Compute the new sound speed */ + const float soundspeed = gas_soundspeed_from_pressure(p->rho, pressure); + + /* Update the signal velocity */ + const float v_sig_old = p->force.v_sig; + const float v_sig_new = p->force.v_sig - p->force.soundspeed + soundspeed; + const float v_sig = max(v_sig_old, v_sig_new); + + const float rho_inv = 1.f / p->rho; + + p->force.soundspeed = soundspeed; + p->force.P_over_rho2 = pressure * rho_inv * rho_inv; + p->force.v_sig = v_sig; } /** * @brief Modifies the thermal state of a particle to the imposed entropy * - * This overrides the current state of the particle but does *not* change its - * time-derivatives + * This overwrites the current state of the particle but does *not* change its + * time-derivatives. Entropy, pressure, sound-speed and signal velocity will be + * updated. * * @param p The particle * @param S The new entropy @@ -141,6 +161,23 @@ __attribute__((always_inline)) INLINE static void hydro_set_entropy( struct part *restrict p, float S) { p->entropy = S; + + /* Compute the pressure */ + const float pressure = gas_pressure_from_entropy(p->rho, p->entropy); + + /* Compute the new sound speed */ + const float soundspeed = gas_soundspeed_from_pressure(p->rho, pressure); + + /* Update the signal velocity */ + const float v_sig_old = p->force.v_sig; + const float v_sig_new = p->force.v_sig - p->force.soundspeed + soundspeed; + const float v_sig = max(v_sig_old, v_sig_new); + + const float rho_inv = 1.f / p->rho; + + p->force.soundspeed = soundspeed; + p->force.P_over_rho2 = pressure * rho_inv * rho_inv; + p->force.v_sig = v_sig; } /** @@ -195,7 +232,7 @@ __attribute__((always_inline)) INLINE static void hydro_init_part( p->density.wcount = 0.f; p->density.wcount_dh = 0.f; p->rho = 0.f; - p->rho_dh = 0.f; + p->density.rho_dh = 0.f; p->density.div_v = 0.f; p->density.rot_v[0] = 0.f; p->density.rot_v[1] = 0.f; @@ -222,27 +259,24 @@ __attribute__((always_inline)) INLINE static void hydro_end_density( /* Final operation on the density (add self-contribution). */ p->rho += p->mass * kernel_root; - p->rho_dh -= hydro_dimension * p->mass * kernel_root; + p->density.rho_dh -= hydro_dimension * p->mass * kernel_root; p->density.wcount += kernel_root; /* Finish the calculation by inserting the missing h-factors */ p->rho *= h_inv_dim; - p->rho_dh *= h_inv_dim_plus_one; + p->density.rho_dh *= h_inv_dim_plus_one; p->density.wcount *= kernel_norm; p->density.wcount_dh *= h_inv * kernel_gamma * kernel_norm; - const float irho = 1.f / p->rho; - - /* Compute the derivative term */ - p->rho_dh = 1.f / (1.f + hydro_dimension_inv * p->h * p->rho_dh * irho); + const float rho_inv = 1.f / p->rho; /* Finish calculation of the velocity curl components */ - p->density.rot_v[0] *= h_inv_dim_plus_one * irho; - p->density.rot_v[1] *= h_inv_dim_plus_one * irho; - p->density.rot_v[2] *= h_inv_dim_plus_one * irho; + p->density.rot_v[0] *= h_inv_dim_plus_one * rho_inv; + p->density.rot_v[1] *= h_inv_dim_plus_one * rho_inv; + p->density.rot_v[2] *= h_inv_dim_plus_one * rho_inv; /* Finish calculation of the velocity divergence */ - p->density.div_v *= h_inv_dim_plus_one * irho; + p->density.div_v *= h_inv_dim_plus_one * rho_inv; } /** @@ -273,19 +307,23 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_force( const float half_dt = (ti_current - (p->ti_begin + p->ti_end) / 2) * timeBase; const float pressure = hydro_get_pressure(p, half_dt); - const float irho = 1.f / p->rho; - - /* Divide the pressure by the density and density gradient */ - const float P_over_rho2 = pressure * irho * irho * p->rho_dh; - /* Compute the sound speed */ - const float soundspeed = sqrtf(hydro_gamma * pressure * irho); + const float soundspeed = gas_soundspeed_from_pressure(p->rho, pressure); + + /* Divide the pressure by the density squared to get the SPH term */ + const float rho_inv = 1.f / p->rho; + const float P_over_rho2 = pressure * rho_inv * rho_inv; /* Compute the Balsara switch */ const float balsara = abs_div_v / (abs_div_v + curl_v + 0.0001f * soundspeed / fac_mu / p->h); + /* Compute the "grad h" term */ + const float grad_h_term = + 1.f / (1.f + hydro_dimension_inv * p->h * p->density.rho_dh * rho_inv); + /* Update variables. */ + p->force.f = grad_h_term; p->force.P_over_rho2 = P_over_rho2; p->force.soundspeed = soundspeed; p->force.balsara = balsara; @@ -349,17 +387,16 @@ __attribute__((always_inline)) INLINE static void hydro_predict_extra( const float dt_entr = (t1 - (p->ti_begin + p->ti_end) / 2) * timeBase; const float pressure = hydro_get_pressure(p, dt_entr); - const float irho = 1.f / p->rho; - - /* Divide the pressure by the density and density gradient */ - const float P_over_rho2 = pressure * irho * irho * p->rho_dh; - /* Compute the new sound speed */ - const float soundspeed = sqrtf(hydro_gamma * pressure * irho); + const float soundspeed = gas_soundspeed_from_pressure(p->rho, pressure); + + /* Divide the pressure by the density squared to get the SPH term */ + const float rho_inv = 1.f / p->rho; + const float P_over_rho2 = pressure * rho_inv * rho_inv; /* Update variables */ - p->force.P_over_rho2 = P_over_rho2; p->force.soundspeed = soundspeed; + p->force.P_over_rho2 = P_over_rho2; } /** @@ -400,6 +437,19 @@ __attribute__((always_inline)) INLINE static void hydro_kick_extra( /* Do not 'overcool' when timestep increases */ if (p->entropy + p->entropy_dt * half_dt < 0.5f * p->entropy) p->entropy_dt = -0.5f * p->entropy / half_dt; + + /* Compute the pressure */ + const float pressure = gas_pressure_from_entropy(p->rho, p->entropy); + + /* Compute the new sound speed */ + const float soundspeed = gas_soundspeed_from_pressure(p->rho, pressure); + + /* Divide the pressure by the density squared to get the SPH term */ + const float rho_inv = 1.f / p->rho; + const float P_over_rho2 = pressure * rho_inv * rho_inv; + + p->force.soundspeed = soundspeed; + p->force.P_over_rho2 = P_over_rho2; } /** @@ -414,6 +464,19 @@ __attribute__((always_inline)) INLINE static void hydro_convert_quantities( /* We read u in the entropy field. We now get S from u */ p->entropy = gas_entropy_from_internal_energy(p->rho, p->entropy); + + /* Compute the pressure */ + const float pressure = gas_pressure_from_entropy(p->rho, p->entropy); + + /* Compute the sound speed */ + const float soundspeed = gas_soundspeed_from_pressure(p->rho, pressure); + + /* Divide the pressure by the density squared to get the SPH term */ + const float rho_inv = 1.f / p->rho; + const float P_over_rho2 = pressure * rho_inv * rho_inv; + + p->force.soundspeed = soundspeed; + p->force.P_over_rho2 = P_over_rho2; } #endif /* SWIFT_GADGET2_HYDRO_H */ diff --git a/src/hydro/Gadget2/hydro_debug.h b/src/hydro/Gadget2/hydro_debug.h index 7c8a6eaba96929b01f1901393d7d0498d58badf4..656299b38374f68824ec20d85ece169d5f1fd599 100644 --- a/src/hydro/Gadget2/hydro_debug.h +++ b/src/hydro/Gadget2/hydro_debug.h @@ -30,8 +30,8 @@ __attribute__((always_inline)) INLINE static void hydro_debug_particle( "v_sig=%e dh/dt=%.3e t_begin=%d, t_end=%d\n", p->x[0], p->x[1], p->x[2], p->v[0], p->v[1], p->v[2], xp->v_full[0], xp->v_full[1], xp->v_full[2], p->a_hydro[0], p->a_hydro[1], p->a_hydro[2], - p->h, p->density.wcount, p->density.wcount_dh, p->mass, p->rho_dh, p->rho, - hydro_get_pressure(p, 0.), p->force.P_over_rho2, p->entropy, + p->h, p->density.wcount, p->density.wcount_dh, p->mass, p->density.rho_dh, + p->rho, hydro_get_pressure(p, 0.), p->force.P_over_rho2, p->entropy, p->entropy_dt, p->force.soundspeed, p->density.div_v, p->density.rot_v[0], p->density.rot_v[1], p->density.rot_v[2], p->force.balsara, p->force.v_sig, p->force.h_dt, p->ti_begin, p->ti_end); diff --git a/src/hydro/Gadget2/hydro_iact.h b/src/hydro/Gadget2/hydro_iact.h index 5ee8cd0370a970aa83cef3d1c8909d923c12ba24..fca1bcff91a71fb2a26adc117382630a576f9090 100644 --- a/src/hydro/Gadget2/hydro_iact.h +++ b/src/hydro/Gadget2/hydro_iact.h @@ -59,7 +59,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_density( /* Compute contribution to the density */ pi->rho += mj * wi; - pi->rho_dh -= mj * (hydro_dimension * wi + ui * wi_dx); + pi->density.rho_dh -= mj * (hydro_dimension * wi + ui * wi_dx); /* Compute contribution to the number of neighbours */ pi->density.wcount += wi; @@ -72,7 +72,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_density( /* Compute contribution to the density */ pj->rho += mi * wj; - pj->rho_dh -= mi * (hydro_dimension * wj + uj * wj_dx); + pj->density.rho_dh -= mi * (hydro_dimension * wj + uj * wj_dx); /* Compute contribution to the number of neighbours */ pj->density.wcount += wj; @@ -209,13 +209,13 @@ __attribute__((always_inline)) INLINE static void runner_iact_vec_density( /* Update particles. */ for (k = 0; k < VEC_SIZE; k++) { pi[k]->rho += rhoi.f[k]; - pi[k]->rho_dh -= rhoi_dh.f[k]; + pi[k]->density.rho_dh -= rhoi_dh.f[k]; pi[k]->density.wcount += wcounti.f[k]; pi[k]->density.wcount_dh -= wcounti_dh.f[k]; pi[k]->density.div_v -= div_vi.f[k]; for (j = 0; j < 3; j++) pi[k]->density.rot_v[j] += curl_vi[j].f[k]; pj[k]->rho += rhoj.f[k]; - pj[k]->rho_dh -= rhoj_dh.f[k]; + pj[k]->density.rho_dh -= rhoj_dh.f[k]; pj[k]->density.wcount += wcountj.f[k]; pj[k]->density.wcount_dh -= wcountj_dh.f[k]; pj[k]->density.div_v -= div_vj.f[k]; @@ -248,17 +248,17 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_density( const float ri = 1.0f / r; /* Compute the kernel function */ - const float h_inv = 1.0f / hi; - const float u = r * h_inv; - kernel_deval(u, &wi, &wi_dx); + const float hi_inv = 1.0f / hi; + const float ui = r * hi_inv; + kernel_deval(ui, &wi, &wi_dx); /* Compute contribution to the density */ pi->rho += mj * wi; - pi->rho_dh -= mj * (hydro_dimension * wi + u * wi_dx); + pi->density.rho_dh -= mj * (hydro_dimension * wi + ui * wi_dx); /* Compute contribution to the number of neighbours */ pi->density.wcount += wi; - pi->density.wcount_dh -= u * wi_dx; + pi->density.wcount_dh -= ui * wi_dx; const float fac = mj * wi_dx * ri; @@ -366,7 +366,7 @@ runner_iact_nonsym_vec_density(float *R2, float *Dx, float *Hi, float *Hj, /* Update particles. */ for (k = 0; k < VEC_SIZE; k++) { pi[k]->rho += rhoi.f[k]; - pi[k]->rho_dh -= rhoi_dh.f[k]; + pi[k]->density.rho_dh -= rhoi_dh.f[k]; pi[k]->density.wcount += wcounti.f[k]; pi[k]->density.wcount_dh -= wcounti_dh.f[k]; pi[k]->density.div_v -= div_vi.f[k]; @@ -415,7 +415,11 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( kernel_deval(xj, &wj, &wj_dx); const float wj_dr = hjd_inv * wj_dx; - /* Compute gradient terms */ + /* Compute h-gradient terms */ + const float f_i = pi->force.f; + const float f_j = pj->force.f; + + /* Compute pressure terms */ const float P_over_rho2_i = pi->force.P_over_rho2; const float P_over_rho2_j = pj->force.P_over_rho2; @@ -447,7 +451,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( /* Now, convolve with the kernel */ const float visc_term = 0.5f * visc * (wi_dr + wj_dr) * r_inv; const float sph_term = - (P_over_rho2_i * wi_dr + P_over_rho2_j * wj_dr) * r_inv; + (f_i * P_over_rho2_i * wi_dr + f_j * P_over_rho2_j * wj_dr) * r_inv; /* Eventually got the acceleration */ const float acc = visc_term + sph_term; @@ -690,7 +694,11 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( kernel_deval(xj, &wj, &wj_dx); const float wj_dr = hjd_inv * wj_dx; - /* Compute gradient terms */ + /* Compute h-gradient terms */ + const float f_i = pi->force.f; + const float f_j = pj->force.f; + + /* Compute pressure terms */ const float P_over_rho2_i = pi->force.P_over_rho2; const float P_over_rho2_j = pj->force.P_over_rho2; @@ -722,7 +730,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( /* Now, convolve with the kernel */ const float visc_term = 0.5f * visc * (wi_dr + wj_dr) * r_inv; const float sph_term = - (P_over_rho2_i * wi_dr + P_over_rho2_j * wj_dr) * r_inv; + (f_i * P_over_rho2_i * wi_dr + f_j * P_over_rho2_j * wj_dr) * r_inv; /* Eventually got the acceleration */ const float acc = visc_term + sph_term; diff --git a/src/hydro/Gadget2/hydro_part.h b/src/hydro/Gadget2/hydro_part.h index 2c12a71ad6256d7372256b6e590790ee0ff4e22e..0c14da957463720dcee5349e88be91986cbb3f54 100644 --- a/src/hydro/Gadget2/hydro_part.h +++ b/src/hydro/Gadget2/hydro_part.h @@ -80,9 +80,6 @@ struct part { /* Entropy time derivative */ float entropy_dt; - /* Derivative of the density with respect to smoothing length. */ - float rho_dh; - union { struct { @@ -93,6 +90,9 @@ struct part { /* Number of neighbours spatial derivative. */ float wcount_dh; + /* Derivative of the density with respect to h. */ + float rho_dh; + /* Particle velocity curl. */ float rot_v[3]; @@ -106,6 +106,9 @@ struct part { /* Balsara switch */ float balsara; + /*! "Grad h" term */ + float f; + /* Pressure over density squared (including drho/dh term) */ float P_over_rho2; diff --git a/src/hydro/Gizmo/hydro.h b/src/hydro/Gizmo/hydro.h index 37ce34e5910a033fab82dbc69839888a28c5ab12..a383a2fdce9e64a5673d0136184368220dc08458 100644 --- a/src/hydro/Gizmo/hydro.h +++ b/src/hydro/Gizmo/hydro.h @@ -150,18 +150,16 @@ __attribute__((always_inline)) INLINE static void hydro_end_density( /* compute primitive variables */ /* eqns (3)-(5) */ const float m = p->conserved.mass; - if (m > 0.f) { - float momentum[3]; - momentum[0] = p->conserved.momentum[0]; - momentum[1] = p->conserved.momentum[1]; - momentum[2] = p->conserved.momentum[2]; - p->primitives.rho = m / volume; - p->primitives.v[0] = momentum[0] / m; - p->primitives.v[1] = momentum[1] / m; - p->primitives.v[2] = momentum[2] / m; - const float energy = p->conserved.energy; - p->primitives.P = hydro_gamma_minus_one * energy / volume; - } + float momentum[3]; + momentum[0] = p->conserved.momentum[0]; + momentum[1] = p->conserved.momentum[1]; + momentum[2] = p->conserved.momentum[2]; + p->primitives.rho = m / volume; + p->primitives.v[0] = momentum[0] / m; + p->primitives.v[1] = momentum[1] / m; + p->primitives.v[2] = momentum[2] / m; + const float energy = p->conserved.energy; + p->primitives.P = hydro_gamma_minus_one * energy / volume; } /** @@ -259,6 +257,12 @@ __attribute__((always_inline)) INLINE static void hydro_convert_quantities( const float m = p->conserved.mass; p->primitives.rho = m / volume; + /* first get the initial velocities, as they were overwritten in end_density + */ + p->primitives.v[0] = p->v[0]; + p->primitives.v[1] = p->v[1]; + p->primitives.v[2] = p->v[2]; + p->conserved.momentum[0] = m * p->primitives.v[0]; p->conserved.momentum[1] = m * p->primitives.v[1]; p->conserved.momentum[2] = m * p->primitives.v[2]; diff --git a/src/hydro/Minimal/hydro.h b/src/hydro/Minimal/hydro.h index eeb389537c56876126c60b2d29a728029c72844b..3015f26c6bad7f006e8cda16695c750ff40d74df 100644 --- a/src/hydro/Minimal/hydro.h +++ b/src/hydro/Minimal/hydro.h @@ -39,6 +39,7 @@ #include "equation_of_state.h" #include "hydro_properties.h" #include "kernel_hydro.h" +#include "minmax.h" /** * @brief Returns the internal energy of a particle @@ -65,9 +66,7 @@ __attribute__((always_inline)) INLINE static float hydro_get_internal_energy( __attribute__((always_inline)) INLINE static float hydro_get_pressure( const struct part *restrict p, float dt) { - const float u = p->u + p->u_dt * dt; - - return gas_pressure_from_internal_energy(p->rho, u); + return p->force.pressure; } /** @@ -97,9 +96,7 @@ __attribute__((always_inline)) INLINE static float hydro_get_entropy( __attribute__((always_inline)) INLINE static float hydro_get_soundspeed( const struct part *restrict p, float dt) { - const float u = p->u + p->u_dt * dt; - - return gas_soundspeed_from_internal_energy(p->rho, u); + return p->force.soundspeed; } /** @@ -128,8 +125,9 @@ __attribute__((always_inline)) INLINE static float hydro_get_mass( * @brief Modifies the thermal state of a particle to the imposed internal * energy * - * This overrides the current state of the particle but does *not* change its - * time-derivatives + * This overwrites the current state of the particle but does *not* change its + * time-derivatives. Internal energy, pressure, sound-speed and signal velocity + * will be updated. * * @param p The particle * @param u The new internal energy @@ -138,13 +136,29 @@ __attribute__((always_inline)) INLINE static void hydro_set_internal_energy( struct part *restrict p, float u) { p->u = u; + + /* Compute the new pressure */ + const float pressure = gas_pressure_from_internal_energy(p->rho, p->u); + + /* Compute the new sound speed */ + const float soundspeed = gas_soundspeed_from_internal_energy(p->rho, p->u); + + /* Update the signal velocity */ + const float v_sig_old = p->force.v_sig; + const float v_sig_new = p->force.v_sig - p->force.soundspeed + soundspeed; + const float v_sig = max(v_sig_old, v_sig_new); + + p->force.soundspeed = soundspeed; + p->force.pressure = pressure; + p->force.v_sig = v_sig; } /** * @brief Modifies the thermal state of a particle to the imposed entropy * - * This overrides the current state of the particle but does *not* change its - * time-derivatives + * This overwrites the current state of the particle but does *not* change its + * time-derivatives. Internal energy, pressure, sound-speed and signal velocity + * will be updated. * * @param p The particle * @param S The new entropy @@ -153,6 +167,21 @@ __attribute__((always_inline)) INLINE static void hydro_set_entropy( struct part *restrict p, float S) { p->u = gas_internal_energy_from_entropy(p->rho, S); + + /* Compute the pressure */ + const float pressure = gas_pressure_from_internal_energy(p->rho, p->u); + + /* Compute the new sound speed */ + const float soundspeed = gas_soundspeed_from_internal_energy(p->rho, p->u); + + /* Update the signal velocity */ + const float v_sig_old = p->force.v_sig; + const float v_sig_new = p->force.v_sig - p->force.soundspeed + soundspeed; + const float v_sig = max(v_sig_old, v_sig_new); + + p->force.soundspeed = soundspeed; + p->force.pressure = pressure; + p->force.v_sig = v_sig; } /** @@ -214,7 +243,7 @@ __attribute__((always_inline)) INLINE static void hydro_init_part( p->density.wcount = 0.f; p->density.wcount_dh = 0.f; p->rho = 0.f; - p->rho_dh = 0.f; + p->density.rho_dh = 0.f; } /** @@ -240,19 +269,14 @@ __attribute__((always_inline)) INLINE static void hydro_end_density( /* Final operation on the density (add self-contribution). */ p->rho += p->mass * kernel_root; - p->rho_dh -= hydro_dimension * p->mass * kernel_root; + p->density.rho_dh -= hydro_dimension * p->mass * kernel_root; p->density.wcount += kernel_root; /* Finish the calculation by inserting the missing h-factors */ p->rho *= h_inv_dim; - p->rho_dh *= h_inv_dim_plus_one; + p->density.rho_dh *= h_inv_dim_plus_one; p->density.wcount *= kernel_norm; p->density.wcount_dh *= h_inv * kernel_gamma * kernel_norm; - - const float irho = 1.f / p->rho; - - /* Compute the derivative term */ - p->rho_dh = 1.f / (1.f + hydro_dimension_inv * p->h * p->rho_dh * irho); } /** @@ -274,10 +298,22 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_force( struct part *restrict p, struct xpart *restrict xp, int ti_current, double timeBase) { + /* Compute the pressure */ const float half_dt = (ti_current - (p->ti_begin + p->ti_end) / 2) * timeBase; const float pressure = hydro_get_pressure(p, half_dt); + /* Compute the sound speed */ + const float soundspeed = gas_soundspeed_from_pressure(p->rho, pressure); + + /* Compute the "grad h" term */ + const float rho_inv = 1.f / p->rho; + const float grad_h_term = + 1.f / (1.f + hydro_dimension_inv * p->h * p->density.rho_dh * rho_inv); + + /* Update variables. */ + p->force.f = grad_h_term; p->force.pressure = pressure; + p->force.soundspeed = soundspeed; } /** @@ -337,7 +373,13 @@ __attribute__((always_inline)) INLINE static void hydro_predict_extra( /* Drift the pressure */ const float dt_entr = (t1 - (p->ti_begin + p->ti_end) / 2) * timeBase; - p->force.pressure = hydro_get_pressure(p, dt_entr); + const float pressure = hydro_get_pressure(p, dt_entr); + + /* Compute the new sound speed */ + const float soundspeed = gas_soundspeed_from_pressure(p->rho, pressure); + + p->force.pressure = pressure; + p->force.soundspeed = soundspeed; } /** @@ -379,6 +421,15 @@ __attribute__((always_inline)) INLINE static void hydro_kick_extra( /* Do not 'overcool' when timestep increases */ if (p->u + p->u_dt * half_dt < 0.5f * p->u) p->u_dt = -0.5f * p->u / half_dt; + + /* Compute the pressure */ + const float pressure = gas_pressure_from_internal_energy(p->rho, p->u); + + /* Compute the sound speed */ + const float soundspeed = gas_soundspeed_from_internal_energy(p->rho, p->u); + + p->force.pressure = pressure; + p->force.soundspeed = soundspeed; } /** @@ -392,6 +443,16 @@ __attribute__((always_inline)) INLINE static void hydro_kick_extra( * @param p The particle to act upon */ __attribute__((always_inline)) INLINE static void hydro_convert_quantities( - struct part *restrict p) {} + struct part *restrict p) { + + /* Compute the pressure */ + const float pressure = gas_pressure_from_internal_energy(p->rho, p->u); + + /* Compute the sound speed */ + const float soundspeed = gas_soundspeed_from_internal_energy(p->rho, p->u); + + p->force.pressure = pressure; + p->force.soundspeed = soundspeed; +} #endif /* SWIFT_MINIMAL_HYDRO_H */ diff --git a/src/hydro/Minimal/hydro_debug.h b/src/hydro/Minimal/hydro_debug.h index 0a6e50da6051fd961f4bf35f32caee311888a13e..16ae62413a0d76b7bf871e615fe5684219752fee 100644 --- a/src/hydro/Minimal/hydro_debug.h +++ b/src/hydro/Minimal/hydro_debug.h @@ -44,7 +44,7 @@ __attribute__((always_inline)) INLINE static void hydro_debug_particle( p->x[0], p->x[1], p->x[2], p->v[0], p->v[1], p->v[2], xp->v_full[0], xp->v_full[1], xp->v_full[2], p->a_hydro[0], p->a_hydro[1], p->a_hydro[2], p->u, p->u_dt, p->force.v_sig, p->force.pressure, p->h, p->force.h_dt, - (int)p->density.wcount, p->mass, p->rho_dh, p->rho, p->ti_begin, + (int)p->density.wcount, p->mass, p->density.rho_dh, p->rho, p->ti_begin, p->ti_end); } diff --git a/src/hydro/Minimal/hydro_iact.h b/src/hydro/Minimal/hydro_iact.h index edb060e4fd71fcc136e1bedf6e8a752d1d50d54f..169947b99e92d9bd1b0870d502a49e311820ff81 100644 --- a/src/hydro/Minimal/hydro_iact.h +++ b/src/hydro/Minimal/hydro_iact.h @@ -55,7 +55,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_density( kernel_deval(xi, &wi, &wi_dx); pi->rho += mj * wi; - pi->rho_dh -= mj * (hydro_dimension * wi + xi * wi_dx); + pi->density.rho_dh -= mj * (hydro_dimension * wi + xi * wi_dx); pi->density.wcount += wi; pi->density.wcount_dh -= xi * wi_dx; @@ -65,7 +65,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_density( kernel_deval(xj, &wj, &wj_dx); pj->rho += mi * wj; - pj->rho_dh -= mi * (hydro_dimension * wj + xj * wj_dx); + pj->density.rho_dh -= mi * (hydro_dimension * wj + xj * wj_dx); pj->density.wcount += wj; pj->density.wcount_dh -= xj * wj_dx; } @@ -100,7 +100,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_density( kernel_deval(xi, &wi, &wi_dx); pi->rho += mj * wi; - pi->rho_dh -= mj * (hydro_dimension * wi + xi * wi_dx); + pi->density.rho_dh -= mj * (hydro_dimension * wi + xi * wi_dx); pi->density.wcount += wi; pi->density.wcount_dh -= xi * wi_dx; } @@ -152,8 +152,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( const float wj_dr = hjd_inv * wj_dx; /* Compute gradient terms */ - const float P_over_rho2_i = pressurei / (rhoi * rhoi) * pi->rho_dh; - const float P_over_rho2_j = pressurej / (rhoj * rhoj) * pj->rho_dh; + const float P_over_rho2_i = pressurei / (rhoi * rhoi) * pi->force.f; + const float P_over_rho2_j = pressurej / (rhoj * rhoj) * pj->force.f; /* Compute dv dot r. */ const float dvdr = (pi->v[0] - pj->v[0]) * dx[0] + @@ -165,8 +165,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( const float mu_ij = fac_mu * r_inv * omega_ij; /* This is 0 or negative */ /* Compute sound speeds and signal velocity */ - const float ci = sqrtf(hydro_gamma * pressurei / rhoi); - const float cj = sqrtf(hydro_gamma * pressurej / rhoj); + const float ci = pi->force.soundspeed; + const float cj = pj->force.soundspeed; const float v_sig = ci + cj - 3.f * mu_ij; /* Construct the full viscosity term */ @@ -263,8 +263,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( const float wj_dr = hjd_inv * wj_dx; /* Compute gradient terms */ - const float P_over_rho2_i = pressurei / (rhoi * rhoi) * pi->rho_dh; - const float P_over_rho2_j = pressurej / (rhoj * rhoj) * pj->rho_dh; + const float P_over_rho2_i = pressurei / (rhoi * rhoi) * pi->force.f; + const float P_over_rho2_j = pressurej / (rhoj * rhoj) * pj->force.f; /* Compute dv dot r. */ const float dvdr = (pi->v[0] - pj->v[0]) * dx[0] + @@ -276,8 +276,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( const float mu_ij = fac_mu * r_inv * omega_ij; /* This is 0 or negative */ /* Compute sound speeds and signal velocity */ - const float ci = sqrtf(hydro_gamma * pressurei / rhoi); - const float cj = sqrtf(hydro_gamma * pressurej / rhoj); + const float ci = pi->force.soundspeed; + const float cj = pj->force.soundspeed; const float v_sig = ci + cj - 3.f * mu_ij; /* Construct the full viscosity term */ diff --git a/src/hydro/Minimal/hydro_part.h b/src/hydro/Minimal/hydro_part.h index 8e23bddf5153043421319810683266b27d297f93..8542177278998d5e0b830dc164988611549ef24d 100644 --- a/src/hydro/Minimal/hydro_part.h +++ b/src/hydro/Minimal/hydro_part.h @@ -93,9 +93,6 @@ struct part { /*! Particle density. */ float rho; - /*! Derivative of density with respect to h */ - float rho_dh; - /* Store density/force specific stuff. */ union { @@ -114,6 +111,9 @@ struct part { /*! Derivative of the neighbour number with respect to h. */ float wcount_dh; + /*! Derivative of density with respect to h */ + float rho_dh; + } density; /** @@ -125,9 +125,15 @@ struct part { */ struct { + /*! "Grad h" term */ + float f; + /*! Particle pressure. */ float pressure; + /*! Particle soundspeed. */ + float soundspeed; + /*! Particle signal velocity */ float v_sig; diff --git a/src/hydro/PressureEntropy/hydro.h b/src/hydro/PressureEntropy/hydro.h new file mode 100644 index 0000000000000000000000000000000000000000..ede16fe5181a546921db7acf8ffcbc130c5e91c5 --- /dev/null +++ b/src/hydro/PressureEntropy/hydro.h @@ -0,0 +1,515 @@ +/******************************************************************************* + * 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/>. + * + ******************************************************************************/ +#ifndef SWIFT_PRESSURE_ENTROPY_HYDRO_H +#define SWIFT_PRESSURE_ENTROPY_HYDRO_H + +/** + * @file PressureEntropy/hydro.h + * @brief Pressure-Entropy implementation of SPH (Non-neighbour loop + * equations) + * + * The thermal variable is the entropy (S) and the entropy is smoothed over + * contact discontinuities to prevent spurious surface tension. + * + * Follows eqautions (19), (21) and (22) of Hopkins, P., MNRAS, 2013, + * Volume 428, Issue 4, pp. 2840-2856 with a simple Balsara viscosity term. + */ + +#include "adiabatic_index.h" +#include "approx_math.h" +#include "dimension.h" +#include "equation_of_state.h" +#include "hydro_properties.h" +#include "kernel_hydro.h" +#include "minmax.h" + +/** + * @brief Returns the internal energy of a particle + * + * @param p The particle of interest + * @param dt Time since the last kick + */ +__attribute__((always_inline)) INLINE static float hydro_get_internal_energy( + const struct part *restrict p, float dt) { + + const float entropy = p->entropy + p->entropy_dt * dt; + + return gas_internal_energy_from_entropy(p->rho_bar, entropy); +} + +/** + * @brief Returns the pressure of a particle + * + * @param p The particle of interest + * @param dt Time since the last kick + */ +__attribute__((always_inline)) INLINE static float hydro_get_pressure( + const struct part *restrict p, float dt) { + + const float entropy = p->entropy + p->entropy_dt * dt; + + return gas_pressure_from_entropy(p->rho_bar, entropy); +} + +/** + * @brief Returns the entropy of a particle + * + * @param p The particle of interest + * @param dt Time since the last kick + */ +__attribute__((always_inline)) INLINE static float hydro_get_entropy( + const struct part *restrict p, float dt) { + + return p->entropy + p->entropy_dt * dt; +} + +/** + * @brief Returns the sound speed of a particle + * + * @param p The particle of interest + * @param dt Time since the last kick + */ +__attribute__((always_inline)) INLINE static float hydro_get_soundspeed( + const struct part *restrict p, float dt) { + + return p->force.soundspeed; +} + +/** + * @brief Returns the physical density of a particle + * + * @param p The particle of interest + */ +__attribute__((always_inline)) INLINE static float hydro_get_density( + const struct part *restrict p) { + + return p->rho; +} + +/** + * @brief Returns the mass of a particle + * + * @param p The particle of interest + */ +__attribute__((always_inline)) INLINE static float hydro_get_mass( + const struct part *restrict p) { + + return p->mass; +} + +/** + * @brief Modifies the thermal state of a particle to the imposed internal + * energy + * + * This overwrites the current state of the particle but does *not* change its + * time-derivatives. Entropy, pressure, sound-speed and signal velocity will be + * updated. + * + * @param p The particle + * @param u The new internal energy + */ +__attribute__((always_inline)) INLINE static void hydro_set_internal_energy( + struct part *restrict p, float u) { + + p->entropy = gas_entropy_from_internal_energy(p->rho_bar, u); + p->entropy_one_over_gamma = pow_one_over_gamma(p->entropy); + + /* Compute the pressure */ + const float pressure = gas_pressure_from_entropy(p->rho_bar, p->entropy); + + /* Compute the sound speed from the pressure*/ + const float soundspeed = gas_soundspeed_from_pressure(p->rho_bar, pressure); + + /* Update the signal velocity */ + const float v_sig_old = p->force.v_sig; + const float v_sig_new = p->force.v_sig - p->force.soundspeed + soundspeed; + const float v_sig = max(v_sig_old, v_sig_new); + + const float rho_bar_inv = 1.f / p->rho_bar; + + p->force.soundspeed = soundspeed; + p->force.P_over_rho2 = pressure * rho_bar_inv * rho_bar_inv; + p->force.v_sig = v_sig; +} + +/** + * @brief Modifies the thermal state of a particle to the imposed entropy + * + * This overwrites the current state of the particle but does *not* change its + * time-derivatives. Entropy, pressure, sound-speed and signal velocity will be + * updated. + * + * @param p The particle + * @param S The new entropy + */ +__attribute__((always_inline)) INLINE static void hydro_set_entropy( + struct part *restrict p, float S) { + + p->entropy = S; + p->entropy_one_over_gamma = pow_one_over_gamma(p->entropy); + + /* Compute the pressure */ + const float pressure = gas_pressure_from_entropy(p->rho_bar, p->entropy); + + /* Compute the sound speed from the pressure*/ + const float soundspeed = gas_soundspeed_from_pressure(p->rho_bar, pressure); + + /* Update the signal velocity */ + const float v_sig_old = p->force.v_sig; + const float v_sig_new = p->force.v_sig - p->force.soundspeed + soundspeed; + const float v_sig = max(v_sig_old, v_sig_new); + + const float rho_bar_inv = 1.f / p->rho_bar; + + p->force.soundspeed = soundspeed; + p->force.P_over_rho2 = pressure * rho_bar_inv * rho_bar_inv; + p->force.v_sig = v_sig; +} + +/** + * @brief Computes the hydro time-step of a given particle + * + * @param p Pointer to the particle data + * @param xp Pointer to the extended particle data + * + */ +__attribute__((always_inline)) INLINE static float hydro_compute_timestep( + const struct part *restrict p, const struct xpart *restrict xp, + const struct hydro_props *restrict hydro_properties) { + + const float CFL_condition = hydro_properties->CFL_condition; + + /* CFL condition */ + const float dt_cfl = + 2.f * kernel_gamma * CFL_condition * p->h / p->force.v_sig; + + return dt_cfl; +} + +/** + * @brief Initialises the particles for the first time + * + * This function is called only once just after the ICs have been + * read in to do some conversions. + * + * @param p The particle to act upon + * @param xp The extended particle data to act upon + */ +__attribute__((always_inline)) INLINE static void hydro_first_init_part( + struct part *restrict p, struct xpart *restrict xp) { + + p->ti_begin = 0; + p->ti_end = 0; + p->rho_bar = 0.f; + p->entropy_one_over_gamma = pow_one_over_gamma(p->entropy); + xp->v_full[0] = p->v[0]; + xp->v_full[1] = p->v[1]; + xp->v_full[2] = p->v[2]; +} + +/** + * @brief Prepares a particle for the density calculation. + * + * Zeroes all the relevant arrays in preparation for the sums taking place in + * the variaous density tasks + * + * @param p The particle to act upon + */ +__attribute__((always_inline)) INLINE static void hydro_init_part( + struct part *restrict p) { + + p->rho = 0.f; + p->rho_bar = 0.f; + p->density.wcount = 0.f; + p->density.wcount_dh = 0.f; + p->density.rho_dh = 0.f; + p->density.pressure_dh = 0.f; + + p->density.div_v = 0.f; + p->density.rot_v[0] = 0.f; + p->density.rot_v[1] = 0.f; + p->density.rot_v[2] = 0.f; +} + +/** + * @brief Finishes the density calculation. + * + * Multiplies the density and number of neighbours by the appropiate constants + * and add the self-contribution term. + * + * @param p The particle to act upon + * @param ti_current The current time (on the integer timeline) + */ +__attribute__((always_inline)) INLINE static void hydro_end_density( + struct part *restrict p, int ti_current) { + + /* Some smoothing length multiples. */ + const float h = p->h; + const float h_inv = 1.0f / h; /* 1/h */ + const float h_inv_dim = pow_dimension(h_inv); /* 1/h^d */ + const float h_inv_dim_plus_one = h_inv_dim * h_inv; /* 1/h^(d+1) */ + + /* Final operation on the density (add self-contribution). */ + p->rho += p->mass * kernel_root; + p->rho_bar += p->mass * p->entropy_one_over_gamma * kernel_root; + p->density.rho_dh -= hydro_dimension * p->mass * kernel_root; + p->density.pressure_dh -= + hydro_dimension * p->mass * p->entropy_one_over_gamma * kernel_root; + p->density.wcount += kernel_root; + + /* Finish the calculation by inserting the missing h-factors */ + p->rho *= h_inv_dim; + p->rho_bar *= h_inv_dim; + p->density.rho_dh *= h_inv_dim_plus_one; + p->density.pressure_dh *= h_inv_dim_plus_one; + p->density.wcount *= kernel_norm; + p->density.wcount_dh *= h_inv * kernel_gamma * kernel_norm; + + const float rho_inv = 1.f / p->rho; + const float entropy_minus_one_over_gamma = 1.f / p->entropy_one_over_gamma; + + /* Final operation on the weighted density */ + p->rho_bar *= entropy_minus_one_over_gamma; + + /* Finish calculation of the velocity curl components */ + p->density.rot_v[0] *= h_inv_dim_plus_one * rho_inv; + p->density.rot_v[1] *= h_inv_dim_plus_one * rho_inv; + p->density.rot_v[2] *= h_inv_dim_plus_one * rho_inv; + + /* Finish calculation of the velocity divergence */ + p->density.div_v *= h_inv_dim_plus_one * rho_inv; +} + +/** + * @brief Prepare a particle for the force calculation. + * + * Computes viscosity term, conduction term and smoothing length gradient terms. + * + * @param p The particle to act upon + * @param xp The extended particle data to act upon + * @param ti_current The current time (on the timeline) + * @param timeBase The minimal time-step size + */ +__attribute__((always_inline)) INLINE static void hydro_prepare_force( + struct part *restrict p, struct xpart *restrict xp, int ti_current, + double timeBase) { + + const float fac_mu = 1.f; /* Will change with cosmological integration */ + + /* Compute the norm of the curl */ + const float curl_v = sqrtf(p->density.rot_v[0] * p->density.rot_v[0] + + p->density.rot_v[1] * p->density.rot_v[1] + + p->density.rot_v[2] * p->density.rot_v[2]); + + /* Compute the norm of div v */ + const float abs_div_v = fabsf(p->density.div_v); + + /* Compute the pressure */ + const float half_dt = (ti_current - (p->ti_begin + p->ti_end) / 2) * timeBase; + const float entropy = hydro_get_entropy(p, half_dt); + const float pressure = gas_pressure_from_entropy(p->rho_bar, entropy); + + /* Compute the sound speed from the pressure*/ + const float soundspeed = gas_soundspeed_from_pressure(p->rho_bar, pressure); + + /* Compute the Balsara switch */ + const float balsara = + abs_div_v / (abs_div_v + curl_v + 0.0001f * soundspeed / fac_mu / p->h); + + /* Divide the pressure by the density squared to get the SPH term */ + const float rho_bar_inv = 1.f / p->rho_bar; + const float P_over_rho2 = pressure * rho_bar_inv * rho_bar_inv; + + /* Compute "grad h" term (note we use rho here and not rho_bar !)*/ + const float rho_inv = 1.f / p->rho; + const float entropy_minus_one_over_gamma = 1.f / p->entropy_one_over_gamma; + const float rho_dh = + 1.f / (1.f + hydro_dimension_inv * p->h * p->density.rho_dh * rho_inv); + const float pressure_dh = p->density.pressure_dh * rho_inv * p->h * + hydro_dimension_inv * entropy_minus_one_over_gamma; + + const float grad_h_term = rho_dh * pressure_dh; + + /* Update variables. */ + p->force.soundspeed = soundspeed; + p->force.P_over_rho2 = P_over_rho2; + p->force.balsara = balsara; + p->force.f = grad_h_term; +} + +/** + * @brief Reset acceleration fields of a particle + * + * Resets all hydro acceleration and time derivative fields in preparation + * for the sums taking place in the variaous force tasks + * + * @param p The particle to act upon + */ +__attribute__((always_inline)) INLINE static void hydro_reset_acceleration( + struct part *restrict p) { + + /* Reset the acceleration. */ + p->a_hydro[0] = 0.0f; + p->a_hydro[1] = 0.0f; + p->a_hydro[2] = 0.0f; + + /* Reset the time derivatives. */ + p->entropy_dt = 0.0f; + p->force.h_dt = 0.0f; + + /* Reset maximal signal velocity */ + p->force.v_sig = 0.0f; +} + +/** + * @brief Predict additional particle fields forward in time when drifting + * + * @param p The particle + * @param xp The extended data of the particle + * @param dt The drift time-step. + * @param t0 The time at the start of the drift (on the timeline). + * @param t1 The time at the end of the drift (on the timeline). + * @param timeBase The minimal time-step size + */ +__attribute__((always_inline)) INLINE static void hydro_predict_extra( + struct part *restrict p, const struct xpart *restrict xp, float dt, int t0, + int t1, double timeBase) { + + const float h_inv = 1.f / p->h; + + /* Predict smoothing length */ + const float w1 = p->force.h_dt * h_inv * dt; + if (fabsf(w1) < 0.2f) + p->h *= approx_expf(w1); /* 4th order expansion of exp(w) */ + else + p->h *= expf(w1); + + /* Predict density */ + const float w2 = -hydro_dimension * w1; + if (fabsf(w2) < 0.2f) { + p->rho *= approx_expf(w2); /* 4th order expansion of exp(w) */ + p->rho_bar *= approx_expf(w2); + } else { + p->rho *= expf(w2); + p->rho_bar *= expf(w2); + } + + /* Drift the entropy */ + const float dt_entr = (t1 - (p->ti_begin + p->ti_end) / 2) * timeBase; + const float entropy = hydro_get_entropy(p, dt_entr); + + /* Compute the pressure */ + const float pressure = gas_pressure_from_entropy(p->rho_bar, entropy); + + /* Compute the new sound speed */ + const float soundspeed = gas_soundspeed_from_pressure(p->rho_bar, pressure); + + /* Divide the pressure by the density squared to get the SPH term */ + const float rho_bar_inv = 1.f / p->rho_bar; + const float P_over_rho2 = pressure * rho_bar_inv * rho_bar_inv; + + /* Update the variables */ + p->entropy_one_over_gamma = pow_one_over_gamma(entropy); + p->force.soundspeed = soundspeed; + p->force.P_over_rho2 = P_over_rho2; +} + +/** + * @brief Finishes the force calculation. + * + * Multiplies the forces and accelerationsby the appropiate constants + * + * @param p The particle to act upon + */ +__attribute__((always_inline)) INLINE static void hydro_end_force( + struct part *restrict p) { + + p->force.h_dt *= p->h * hydro_dimension_inv; + + p->entropy_dt *= + 0.5f * hydro_gamma_minus_one * pow_minus_gamma_minus_one(p->rho_bar); +} + +/** + * @brief Kick the additional variables + * + * @param p The particle to act upon + * @param xp The particle extended data to act upon + * @param dt The time-step for this kick + * @param half_dt The half time-step for this kick + */ +__attribute__((always_inline)) INLINE static void hydro_kick_extra( + struct part *restrict p, struct xpart *restrict xp, float dt, + float half_dt) { + + /* Do not decrease the entropy (temperature) by more than a factor of 2*/ + const float entropy_change = p->entropy_dt * dt; + if (entropy_change > -0.5f * p->entropy) + p->entropy += entropy_change; + else + p->entropy *= 0.5f; + + /* Do not 'overcool' when timestep increases */ + if (p->entropy + p->entropy_dt * half_dt < 0.5f * p->entropy) + p->entropy_dt = -0.5f * p->entropy / half_dt; + + /* Compute the pressure */ + const float pressure = gas_pressure_from_entropy(p->rho_bar, p->entropy); + + /* Compute the new sound speed */ + const float soundspeed = gas_soundspeed_from_pressure(p->rho_bar, pressure); + + /* Divide the pressure by the density squared to get the SPH term */ + const float rho_bar_inv = 1.f / p->rho_bar; + const float P_over_rho2 = pressure * rho_bar_inv * rho_bar_inv; + + p->entropy_one_over_gamma = pow_one_over_gamma(p->entropy); + p->force.soundspeed = soundspeed; + p->force.P_over_rho2 = P_over_rho2; +} + +/** + * @brief Converts hydro quantity of a particle at the start of a run + * + * Requires the density to be known + * + * @param p The particle to act upon + */ +__attribute__((always_inline)) INLINE static void hydro_convert_quantities( + struct part *restrict p) { + + /* We read u in the entropy field. We now get S from u */ + p->entropy = gas_entropy_from_internal_energy(p->rho_bar, p->entropy); + p->entropy_one_over_gamma = pow_one_over_gamma(p->entropy); + + /* Compute the pressure */ + const float pressure = gas_pressure_from_entropy(p->rho_bar, p->entropy); + + /* Compute the sound speed */ + const float soundspeed = gas_soundspeed_from_pressure(p->rho_bar, pressure); + + /* Divide the pressure by the density squared to get the SPH term */ + const float rho_bar_inv = 1.f / p->rho_bar; + const float P_over_rho2 = pressure * rho_bar_inv * rho_bar_inv; + + p->force.soundspeed = soundspeed; + p->force.P_over_rho2 = P_over_rho2; +} + +#endif /* SWIFT_PRESSURE_ENTROPY_HYDRO_H */ diff --git a/src/hydro/PressureEntropy/hydro_debug.h b/src/hydro/PressureEntropy/hydro_debug.h new file mode 100644 index 0000000000000000000000000000000000000000..486543793515795092e7cc97fe7b567b8230be3b --- /dev/null +++ b/src/hydro/PressureEntropy/hydro_debug.h @@ -0,0 +1,50 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Coypright (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/>. + * + ******************************************************************************/ +#ifndef SWIFT_PRESSURE_ENTROPY_HYDRO_DEBUG_H +#define SWIFT_PRESSURE_ENTROPY_HYDRO_DEBUG_H + +/** + * @file PressureEntropy/hydro_debug.h + * @brief Pressure-Entropy implementation of SPH (Debugging routines) + * + * The thermal variable is the entropy (S) and the entropy is smoothed over + * contact discontinuities to prevent spurious surface tension. + * + * Follows eqautions (19), (21) and (22) of Hopkins, P., MNRAS, 2013, + * Volume 428, Issue 4, pp. 2840-2856 with a simple Balsara viscosity term. + */ + +__attribute__((always_inline)) INLINE static void hydro_debug_particle( + const struct part* p, const struct xpart* xp) { + printf( + "x=[%.3e,%.3e,%.3e], " + "v=[%.3e,%.3e,%.3e],v_full=[%.3e,%.3e,%.3e] \n a=[%.3e,%.3e,%.3e],\n " + "h=%.3e, wcount=%.3f, wcount_dh=%.3e, m=%.3e, dh_drho=%.3e, rho=%.3e, " + "rho_bar=%.3e, P=%.3e, dP_dh=%.3e, P_over_rho2=%.3e, S=%.3e, S^1/g=%.3e, " + "dS/dt=%.3e,\nc=%.3e v_sig=%e dh/dt=%.3e t_begin=%d, t_end=%d\n", + p->x[0], p->x[1], p->x[2], p->v[0], p->v[1], p->v[2], xp->v_full[0], + xp->v_full[1], xp->v_full[2], p->a_hydro[0], p->a_hydro[1], p->a_hydro[2], + p->h, p->density.wcount, p->density.wcount_dh, p->mass, p->density.rho_dh, + p->rho, p->rho_bar, hydro_get_pressure(p, 0.), p->density.pressure_dh, + p->force.P_over_rho2, p->entropy, p->entropy_one_over_gamma, + p->entropy_dt, p->force.soundspeed, p->force.v_sig, p->force.h_dt, + p->ti_begin, p->ti_end); +} + +#endif /* SWIFT_PRESSURE_ENTROPY_HYDRO_DEBUG_H */ diff --git a/src/hydro/PressureEntropy/hydro_iact.h b/src/hydro/PressureEntropy/hydro_iact.h new file mode 100644 index 0000000000000000000000000000000000000000..18e22233f6f53e488119a58a988ba4c9faf3db36 --- /dev/null +++ b/src/hydro/PressureEntropy/hydro_iact.h @@ -0,0 +1,399 @@ +/******************************************************************************* + * 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/>. + * + ******************************************************************************/ +#ifndef SWIFT_PRESSURE_ENTROPY_HYDRO_IACT_H +#define SWIFT_PRESSURE_ENTROPY_HYDRO_IACT_H + +/** + * @file PressureEntropy/hydro_iact.h + * @brief Pressure-Entropy implementation of SPH (Neighbour loop equations) + * + * The thermal variable is the entropy (S) and the entropy is smoothed over + * contact discontinuities to prevent spurious surface tension. + * + * Follows eqautions (19), (21) and (22) of Hopkins, P., MNRAS, 2013, + * Volume 428, Issue 4, pp. 2840-2856 with a simple Balsara viscosity term. + */ + +/** + * @brief Density loop + */ +__attribute__((always_inline)) INLINE static void runner_iact_density( + float r2, float *dx, float hi, float hj, struct part *pi, struct part *pj) { + + float wi, wi_dx; + float wj, wj_dx; + float dv[3], curlvr[3]; + + /* Get the masses. */ + const float mi = pi->mass; + const float mj = pj->mass; + + /* Get r and r inverse. */ + const float r = sqrtf(r2); + const float r_inv = 1.0f / r; + + /* Compute the kernel function for pi */ + const float hi_inv = 1.f / hi; + const float ui = r * hi_inv; + kernel_deval(ui, &wi, &wi_dx); + + /* Compute contribution to the density */ + pi->rho += mj * wi; + pi->density.rho_dh -= mj * (hydro_dimension * wi + ui * wi_dx); + + /* Compute contribution to the number of neighbours */ + pi->density.wcount += wi; + pi->density.wcount_dh -= ui * wi_dx; + + /* Compute contribution to the weighted density */ + pi->rho_bar += mj * pj->entropy_one_over_gamma * wi; + pi->density.pressure_dh -= + mj * pj->entropy_one_over_gamma * (hydro_dimension * wi + ui * wi_dx); + + /* Compute the kernel function for pj */ + const float hj_inv = 1.f / hj; + const float uj = r * hj_inv; + kernel_deval(uj, &wj, &wj_dx); + + /* Compute contribution to the density */ + pj->rho += mi * wj; + pj->density.rho_dh -= mi * (hydro_dimension * wj + uj * wj_dx); + + /* Compute contribution to the number of neighbours */ + pj->density.wcount += wj; + pj->density.wcount_dh -= uj * wj_dx; + + /* Compute contribution to the weighted density */ + pj->rho_bar += mi * pi->entropy_one_over_gamma * wj; + pj->density.pressure_dh -= + mi * pi->entropy_one_over_gamma * (hydro_dimension * wj + uj * wj_dx); + + const float faci = mj * wi_dx * r_inv; + const float facj = mi * wj_dx * r_inv; + + /* Compute dv dot r */ + dv[0] = pi->v[0] - pj->v[0]; + dv[1] = pi->v[1] - pj->v[1]; + dv[2] = pi->v[2] - pj->v[2]; + const float dvdr = dv[0] * dx[0] + dv[1] * dx[1] + dv[2] * dx[2]; + + pi->density.div_v -= faci * dvdr; + pj->density.div_v -= facj * dvdr; + + /* Compute dv cross r */ + curlvr[0] = dv[1] * dx[2] - dv[2] * dx[1]; + curlvr[1] = dv[2] * dx[0] - dv[0] * dx[2]; + curlvr[2] = dv[0] * dx[1] - dv[1] * dx[0]; + + pi->density.rot_v[0] += faci * curlvr[0]; + pi->density.rot_v[1] += faci * curlvr[1]; + pi->density.rot_v[2] += faci * curlvr[2]; + + pj->density.rot_v[0] += facj * curlvr[0]; + pj->density.rot_v[1] += facj * curlvr[1]; + pj->density.rot_v[2] += facj * curlvr[2]; +} + +/** + * @brief Density loop (Vectorized version) + */ +__attribute__((always_inline)) INLINE static void runner_iact_vec_density( + float *R2, float *Dx, float *Hi, float *Hj, struct part **pi, + struct part **pj) { + + error("Vectorized version of Pressure-Entropy SPH routine not existant yet."); +} + +/** + * @brief Density loop (non-symmetric version) + */ +__attribute__((always_inline)) INLINE static void runner_iact_nonsym_density( + float r2, float *dx, float hi, float hj, struct part *pi, struct part *pj) { + + float wi, wi_dx; + float dv[3], curlvr[3]; + + /* Get the masses. */ + const float mj = pj->mass; + + /* Get r and r inverse. */ + const float r = sqrtf(r2); + const float ri = 1.0f / r; + + /* Compute the kernel function */ + const float h_inv = 1.0f / hi; + const float ui = r * h_inv; + kernel_deval(ui, &wi, &wi_dx); + + /* Compute contribution to the density */ + pi->rho += mj * wi; + pi->density.rho_dh -= mj * (hydro_dimension * wi + ui * wi_dx); + + /* Compute contribution to the number of neighbours */ + pi->density.wcount += wi; + pi->density.wcount_dh -= ui * wi_dx; + + /* Compute contribution to the weighted density */ + pi->rho_bar += mj * pj->entropy_one_over_gamma * wi; + pi->density.pressure_dh -= + mj * pj->entropy_one_over_gamma * (hydro_dimension * wi + ui * wi_dx); + + const float fac = mj * wi_dx * ri; + + /* Compute dv dot r */ + dv[0] = pi->v[0] - pj->v[0]; + dv[1] = pi->v[1] - pj->v[1]; + dv[2] = pi->v[2] - pj->v[2]; + const float dvdr = dv[0] * dx[0] + dv[1] * dx[1] + dv[2] * dx[2]; + pi->density.div_v -= fac * dvdr; + + /* Compute dv cross r */ + curlvr[0] = dv[1] * dx[2] - dv[2] * dx[1]; + curlvr[1] = dv[2] * dx[0] - dv[0] * dx[2]; + curlvr[2] = dv[0] * dx[1] - dv[1] * dx[0]; + + pi->density.rot_v[0] += fac * curlvr[0]; + pi->density.rot_v[1] += fac * curlvr[1]; + pi->density.rot_v[2] += fac * curlvr[2]; +} + +/** + * @brief Density loop (non-symmetric vectorized version) + */ +__attribute__((always_inline)) INLINE static void +runner_iact_nonsym_vec_density(float *R2, float *Dx, float *Hi, float *Hj, + struct part **pi, struct part **pj) { + + error("Vectorized version of Pressure-Entropy SPH routine not existant yet."); +} + +/** + * @brief Force loop + */ +__attribute__((always_inline)) INLINE static void runner_iact_force( + float r2, float *dx, float hi, float hj, struct part *pi, struct part *pj) { + + float wi, wj, wi_dx, wj_dx; + + const float fac_mu = 1.f; /* Will change with cosmological integration */ + + const float r = sqrtf(r2); + const float r_inv = 1.0f / r; + + /* Get some values in local variables. */ + const float mi = pi->mass; + const float mj = pj->mass; + const float rhoi = pi->rho; + const float rhoj = pj->rho; + + /* Get the kernel for hi. */ + const float hi_inv = 1.0f / hi; + const float hid_inv = pow_dimension_plus_one(hi_inv); /* 1/h^(d+1) */ + const float ui = r * hi_inv; + kernel_deval(ui, &wi, &wi_dx); + const float wi_dr = hid_inv * wi_dx; + + /* Get the kernel for hj. */ + const float hj_inv = 1.0f / hj; + const float hjd_inv = pow_dimension_plus_one(hj_inv); /* 1/h^(d+1) */ + const float xj = r * hj_inv; + kernel_deval(xj, &wj, &wj_dx); + const float wj_dr = hjd_inv * wj_dx; + + /* Compute gradient terms */ + const float f_i = pi->force.f; + const float f_j = pj->force.f; + + /* Compute Pressure terms */ + const float P_over_rho2_i = pi->force.P_over_rho2; + const float P_over_rho2_j = pj->force.P_over_rho2; + + /* Compute entropy terms */ + const float S_gamma_i = pi->entropy_one_over_gamma; + const float S_gamma_j = pj->entropy_one_over_gamma; + + /* Compute sound speeds */ + const float ci = pi->force.soundspeed; + const float cj = pj->force.soundspeed; + + /* Compute dv dot r. */ + const float dvdr = (pi->v[0] - pj->v[0]) * dx[0] + + (pi->v[1] - pj->v[1]) * dx[1] + + (pi->v[2] - pj->v[2]) * dx[2]; + + /* Balsara term */ + const float balsara_i = pi->force.balsara; + const float balsara_j = pj->force.balsara; + + /* Are the particles moving towards each others ? */ + const float omega_ij = fminf(dvdr, 0.f); + const float mu_ij = fac_mu * r_inv * omega_ij; /* This is 0 or negative */ + + /* Signal velocity */ + const float v_sig = ci + cj - 3.f * mu_ij; + + /* Now construct the full viscosity term */ + const float rho_ij = 0.5f * (rhoi + rhoj); + const float visc = -0.25f * const_viscosity_alpha * v_sig * mu_ij * + (balsara_i + balsara_j) / rho_ij; + + /* Now, convolve with the kernel */ + const float visc_term = 0.5f * visc * (wi_dr + wj_dr); + const float sph_term = (S_gamma_j / S_gamma_i - f_i) * P_over_rho2_i * wi_dr + + (S_gamma_i / S_gamma_j - f_j) * P_over_rho2_j * wj_dr; + + /* Eventually got the acceleration */ + const float acc = (visc_term + sph_term) * r_inv; + + /* Use the force Luke ! */ + pi->a_hydro[0] -= mj * acc * dx[0]; + pi->a_hydro[1] -= mj * acc * dx[1]; + pi->a_hydro[2] -= mj * acc * dx[2]; + + pj->a_hydro[0] += mi * acc * dx[0]; + pj->a_hydro[1] += mi * acc * dx[1]; + pj->a_hydro[2] += mi * acc * dx[2]; + + /* Get the time derivative for h. */ + pi->force.h_dt -= mj * dvdr * r_inv / rhoj * wi_dr; + pj->force.h_dt -= mi * dvdr * r_inv / rhoi * wj_dr; + + /* Update the signal velocity. */ + pi->force.v_sig = fmaxf(pi->force.v_sig, v_sig); + pj->force.v_sig = fmaxf(pj->force.v_sig, v_sig); + + /* Change in entropy */ + pi->entropy_dt += mj * visc_term * r_inv * dvdr; + pj->entropy_dt += mi * visc_term * r_inv * dvdr; +} + +/** + * @brief Force loop (Vectorized version) + */ +__attribute__((always_inline)) INLINE static void runner_iact_vec_force( + float *R2, float *Dx, float *Hi, float *Hj, struct part **pi, + struct part **pj) { + + error("Vectorized version of Pressure-Entropy SPH routine not existant yet."); +} + +/** + * @brief Force loop (non-symmetric version) + */ +__attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( + float r2, float *dx, float hi, float hj, struct part *pi, struct part *pj) { + + float wi, wj, wi_dx, wj_dx; + + const float fac_mu = 1.f; /* Will change with cosmological integration */ + + const float r = sqrtf(r2); + const float r_inv = 1.0f / r; + + /* Get some values in local variables. */ + // const float mi = pi->mass; + const float mj = pj->mass; + const float rhoi = pi->rho; + const float rhoj = pj->rho; + + /* Get the kernel for hi. */ + const float hi_inv = 1.0f / hi; + const float hid_inv = pow_dimension_plus_one(hi_inv); /* 1/h^(d+1) */ + const float ui = r * hi_inv; + kernel_deval(ui, &wi, &wi_dx); + const float wi_dr = hid_inv * wi_dx; + + /* Get the kernel for hj. */ + const float hj_inv = 1.0f / hj; + const float hjd_inv = pow_dimension_plus_one(hj_inv); /* 1/h^(d+1) */ + const float xj = r * hj_inv; + kernel_deval(xj, &wj, &wj_dx); + const float wj_dr = hjd_inv * wj_dx; + + /* Compute gradient terms */ + const float f_i = pi->force.f; + const float f_j = pj->force.f; + + /* Compute Pressure terms */ + const float P_over_rho2_i = pi->force.P_over_rho2; + const float P_over_rho2_j = pj->force.P_over_rho2; + + /* Compute entropy terms */ + const float S_gamma_i = pi->entropy_one_over_gamma; + const float S_gamma_j = pj->entropy_one_over_gamma; + + /* Compute sound speeds */ + const float ci = pi->force.soundspeed; + const float cj = pj->force.soundspeed; + + /* Compute dv dot r. */ + const float dvdr = (pi->v[0] - pj->v[0]) * dx[0] + + (pi->v[1] - pj->v[1]) * dx[1] + + (pi->v[2] - pj->v[2]) * dx[2]; + + /* Balsara term */ + const float balsara_i = pi->force.balsara; + const float balsara_j = pj->force.balsara; + + /* Are the particles moving towards each others ? */ + const float omega_ij = fminf(dvdr, 0.f); + const float mu_ij = fac_mu * r_inv * omega_ij; /* This is 0 or negative */ + + /* Signal velocity */ + const float v_sig = ci + cj - 3.f * mu_ij; + + /* Now construct the full viscosity term */ + const float rho_ij = 0.5f * (rhoi + rhoj); + const float visc = -0.25f * const_viscosity_alpha * v_sig * mu_ij * + (balsara_i + balsara_j) / rho_ij; + + /* Now, convolve with the kernel */ + const float visc_term = 0.5f * visc * (wi_dr + wj_dr); + const float sph_term = (S_gamma_j / S_gamma_i - f_i) * P_over_rho2_i * wi_dr + + (S_gamma_i / S_gamma_j - f_j) * P_over_rho2_j * wj_dr; + + /* Eventually got the acceleration */ + const float acc = (visc_term + sph_term) * r_inv; + + /* Use the force Luke ! */ + pi->a_hydro[0] -= mj * acc * dx[0]; + pi->a_hydro[1] -= mj * acc * dx[1]; + pi->a_hydro[2] -= mj * acc * dx[2]; + + /* Get the time derivative for h. */ + pi->force.h_dt -= mj * dvdr * r_inv / rhoj * wi_dr; + + /* Update the signal velocity. */ + pi->force.v_sig = fmaxf(pi->force.v_sig, v_sig); + + /* Change in entropy */ + pi->entropy_dt += mj * visc_term * r_inv * dvdr; +} + +/** + * @brief Force loop (Vectorized non-symmetric version) + */ +__attribute__((always_inline)) INLINE static void runner_iact_nonsym_vec_force( + float *R2, float *Dx, float *Hi, float *Hj, struct part **pi, + struct part **pj) { + + error("Vectorized version of Pressure-Entropy SPH routine not existant yet."); +} + +#endif /* SWIFT_PRESSURE_ENTROPY_HYDRO_IACT_H */ diff --git a/src/hydro/PressureEntropy/hydro_io.h b/src/hydro/PressureEntropy/hydro_io.h new file mode 100644 index 0000000000000000000000000000000000000000..9914a656466f3f0d0a5eeb79b511706d7068ffc6 --- /dev/null +++ b/src/hydro/PressureEntropy/hydro_io.h @@ -0,0 +1,145 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Coypright (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/>. + * + ******************************************************************************/ +#ifndef SWIFT_PRESSURE_ENTROPY_HYDRO_IO_H +#define SWIFT_PRESSURE_ENTROPY_HYDRO_IO_H + +/** + * @file PressureEntropy/hydro_io.h + * @brief Pressure-Entropy implementation of SPH (i/o routines) + * + * The thermal variable is the entropy (S) and the entropy is smoothed over + * contact discontinuities to prevent spurious surface tension. + * + * Follows eqautions (19), (21) and (22) of Hopkins, P., MNRAS, 2013, + * Volume 428, Issue 4, pp. 2840-2856 with a simple Balsara viscosity term. + */ + +#include "adiabatic_index.h" +#include "hydro.h" +#include "io_properties.h" +#include "kernel_hydro.h" + +/** + * @brief Specifies which particle fields to read from a dataset + * + * @param parts The particle array. + * @param list The list of i/o properties to read. + * @param num_fields The number of i/o fields to read. + */ +void hydro_read_particles(struct part* parts, struct io_props* list, + int* num_fields) { + + *num_fields = 8; + + /* List what we want to read */ + list[0] = io_make_input_field("Coordinates", DOUBLE, 3, COMPULSORY, + UNIT_CONV_LENGTH, parts, x); + list[1] = io_make_input_field("Velocities", FLOAT, 3, COMPULSORY, + UNIT_CONV_SPEED, parts, v); + list[2] = io_make_input_field("Masses", FLOAT, 1, COMPULSORY, UNIT_CONV_MASS, + parts, mass); + list[3] = io_make_input_field("SmoothingLength", FLOAT, 1, COMPULSORY, + UNIT_CONV_LENGTH, parts, h); + list[4] = + io_make_input_field("InternalEnergy", FLOAT, 1, COMPULSORY, + UNIT_CONV_ENTROPY_PER_UNIT_MASS, parts, entropy); + list[5] = io_make_input_field("ParticleIDs", ULONGLONG, 1, COMPULSORY, + UNIT_CONV_NO_UNITS, parts, id); + list[6] = io_make_input_field("Accelerations", FLOAT, 3, OPTIONAL, + UNIT_CONV_ACCELERATION, parts, a_hydro); + list[7] = io_make_input_field("Density", FLOAT, 1, OPTIONAL, + UNIT_CONV_DENSITY, parts, rho); +} + +float convert_u(struct engine* e, struct part* p) { + + return hydro_get_internal_energy(p, 0); +} + +float convert_P(struct engine* e, struct part* p) { + + return hydro_get_pressure(p, 0); +} + +/** + * @brief Specifies which particle fields to write to a dataset + * + * @param parts The particle array. + * @param list The list of i/o properties to write. + * @param num_fields The number of i/o fields to write. + */ +void hydro_write_particles(struct part* parts, struct io_props* list, + int* num_fields) { + + *num_fields = 11; + + /* List what we want to write */ + list[0] = io_make_output_field("Coordinates", DOUBLE, 3, UNIT_CONV_LENGTH, + parts, x); + list[1] = + io_make_output_field("Velocities", FLOAT, 3, UNIT_CONV_SPEED, parts, v); + list[2] = + io_make_output_field("Masses", FLOAT, 1, UNIT_CONV_MASS, parts, mass); + list[3] = io_make_output_field("SmoothingLength", FLOAT, 1, UNIT_CONV_LENGTH, + parts, h); + list[4] = io_make_output_field_convert_part("InternalEnergy", FLOAT, 1, + UNIT_CONV_ENERGY_PER_UNIT_MASS, + parts, entropy, convert_u); + list[5] = io_make_output_field("ParticleIDs", ULONGLONG, 1, + UNIT_CONV_NO_UNITS, parts, id); + list[6] = io_make_output_field("Acceleration", FLOAT, 3, + UNIT_CONV_ACCELERATION, parts, a_hydro); + list[7] = + io_make_output_field("Density", FLOAT, 1, UNIT_CONV_DENSITY, parts, rho); + list[8] = io_make_output_field( + "Entropy", FLOAT, 1, UNIT_CONV_ENTROPY_PER_UNIT_MASS, parts, entropy); + list[9] = io_make_output_field_convert_part( + "Pressure", FLOAT, 1, UNIT_CONV_PRESSURE, parts, rho, convert_P); + list[10] = io_make_output_field("WeightedDensity", FLOAT, 1, + UNIT_CONV_DENSITY, parts, rho_bar); +} + +/** + * @brief Writes the current model of SPH to the file + * @param h_grpsph The HDF5 group in which to write + */ +void writeSPHflavour(hid_t h_grpsph) { + + /* Viscosity and thermal conduction */ + /* Nothing in this minimal model... */ + writeAttribute_s(h_grpsph, "Thermal Conductivity Model", "No treatment"); + writeAttribute_s( + h_grpsph, "Viscosity Model", + "as in Springel (2005), i.e. Monaghan (1992) with Balsara (1995) switch"); + writeAttribute_f(h_grpsph, "Viscosity alpha", const_viscosity_alpha); + writeAttribute_f(h_grpsph, "Viscosity beta", 3.f); + + /* Time integration properties */ + writeAttribute_f(h_grpsph, "Maximal Delta u change over dt", + const_max_u_change); +} + +/** + * @brief Are we writing entropy in the internal energy field ? + * + * @return 1 if entropy is in 'internal energy', 0 otherwise. + */ +int writeEntropyFlag() { return 0; } + +#endif /* SWIFT_PRESSURE_ENTROPY_HYDRO_IO_H */ diff --git a/src/hydro/PressureEntropy/hydro_part.h b/src/hydro/PressureEntropy/hydro_part.h new file mode 100644 index 0000000000000000000000000000000000000000..cac585ff79bae737f0e5c09860a38536cbf3a38c --- /dev/null +++ b/src/hydro/PressureEntropy/hydro_part.h @@ -0,0 +1,143 @@ +/******************************************************************************* + * 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/>. + * + ******************************************************************************/ +#ifndef SWIFT_PRESSURE_ENTROPY_HYDRO_PART_H +#define SWIFT_PRESSURE_ENTROPY_HYDRO_PART_H + +/** + * @file PressureEntropy/hydro_part.h + * @brief Pressure-Entropy implementation of SPH (Particle definition) + * + * The thermal variable is the entropy (S) and the entropy is smoothed over + * contact discontinuities to prevent spurious surface tension. + * + * Follows eqautions (19), (21) and (22) of Hopkins, P., MNRAS, 2013, + * Volume 428, Issue 4, pp. 2840-2856 with a simple Balsara viscosity term. + */ + +#include "cooling_struct.h" + +/* Extra particle data not needed during the SPH loops over neighbours. */ +struct xpart { + + /*! Offset between current position and position at last tree rebuild. */ + float x_diff[3]; + + /*! Velocity at the last full step. */ + float v_full[3]; + + /*! Additional data used to record cooling information */ + struct cooling_xpart_data cooling_data; + +} SWIFT_STRUCT_ALIGN; + +/* Data of a single particle. */ +struct part { + + /*! Particle position. */ + double x[3]; + + /*! Particle predicted velocity. */ + float v[3]; + + /*! Particle acceleration. */ + float a_hydro[3]; + + /*! Particle cutoff radius. */ + float h; + + /*! Particle mass. */ + float mass; + + /*! Particle time of beginning of time-step. */ + int ti_begin; + + /*! Particle time of end of time-step. */ + int ti_end; + + /*! Particle density. */ + float rho; + + /*! Particle weighted density */ + float rho_bar; + + /*! Particle entropy. */ + float entropy; + + /*! Entropy time derivative */ + float entropy_dt; + + /*! Particle entropy to the power 1/gamma. */ + float entropy_one_over_gamma; + + union { + + struct { + + /*! Number of neighbours. */ + float wcount; + + /*! Number of neighbours spatial derivative. */ + float wcount_dh; + + /*! Derivative of density with respect to h */ + float rho_dh; + + /*! Derivative of pressure with respect to h */ + float pressure_dh; + + /*! Particle velocity curl. */ + float rot_v[3]; + + /*! Particle velocity divergence. */ + float div_v; + + } density; + + struct { + + /*! Balsara switch */ + float balsara; + + /*! "Grad h" term */ + float f; + + /*! Pressure term */ + float P_over_rho2; + + /*! Particle sound speed. */ + float soundspeed; + + /*! Signal velocity. */ + float v_sig; + + /*! Time derivative of the smoothing length */ + float h_dt; + + } force; + }; + + /*! Particle ID. */ + long long id; + + /*! Pointer to corresponding gravity part. */ + struct gpart* gpart; + +} SWIFT_STRUCT_ALIGN; + +#endif /* SWIFT_PRESSURE_ENTROPY_HYDRO_PART_H */ diff --git a/src/hydro_io.h b/src/hydro_io.h index f0a619b90b774574c434007b1c01a0e55e75e464..05ae94ade7b103ff1b584dc2447cbab40479d1fc 100644 --- a/src/hydro_io.h +++ b/src/hydro_io.h @@ -26,6 +26,8 @@ #include "./hydro/Minimal/hydro_io.h" #elif defined(GADGET2_SPH) #include "./hydro/Gadget2/hydro_io.h" +#elif defined(HOPKINS_PE_SPH) +#include "./hydro/PressureEntropy/hydro_io.h" #elif defined(DEFAULT_SPH) #include "./hydro/Default/hydro_io.h" #elif defined(GIZMO_SPH) diff --git a/src/parallel_io.c b/src/parallel_io.c index 0da34d4dad114db0920c8e8f3bb617295ff3da96..66c9203e39e56d520eeace8858b0c618b45e6a22 100644 --- a/src/parallel_io.c +++ b/src/parallel_io.c @@ -486,6 +486,11 @@ void read_ic_parallel(char* fileName, const struct UnitSystem* internal_units, } } + /* Convert the dimensions of the box */ + for (int j = 0; j < 3; j++) + dim[j] *= + units_conversion_factor(ic_units, internal_units, UNIT_CONV_LENGTH); + /* Allocate memory to store SPH particles */ *Ngas = N[0]; if (posix_memalign((void*)parts, part_align, (*Ngas) * sizeof(struct part)) != diff --git a/src/part.h b/src/part.h index af39d10fafc11d4435ddbcc087fbf08178b18959..6832e0c6c2e0f2c324d90629447cf6a5e809d6fb 100644 --- a/src/part.h +++ b/src/part.h @@ -42,12 +42,19 @@ /* Import the right hydro particle definition */ #if defined(MINIMAL_SPH) #include "./hydro/Minimal/hydro_part.h" +#define hydro_need_extra_init_loop 0 #elif defined(GADGET2_SPH) #include "./hydro/Gadget2/hydro_part.h" +#define hydro_need_extra_init_loop 0 +#elif defined(HOPKINS_PE_SPH) +#include "./hydro/PressureEntropy/hydro_part.h" +#define hydro_need_extra_init_loop 1 #elif defined(DEFAULT_SPH) #include "./hydro/Default/hydro_part.h" +#define hydro_need_extra_init_loop 0 #elif defined(GIZMO_SPH) #include "./hydro/Gizmo/hydro_part.h" +#define hydro_need_extra_init_loop 0 #define EXTRA_HYDRO_LOOP #else #error "Invalid choice of SPH variant" diff --git a/src/riemann/riemann_hllc.h b/src/riemann/riemann_hllc.h index b8b1239d7799221c98522c06631aba5cabe69183..1a4c3f5f8338f6504a8c5f1d9eab8451eb20246c 100644 --- a/src/riemann/riemann_hllc.h +++ b/src/riemann/riemann_hllc.h @@ -145,11 +145,13 @@ __attribute__((always_inline)) INLINE static void riemann_solve_for_flux( we add the extra velocity flux due to the absolute motion of the fluid similarly, we need to add the energy fluxes due to the absolute motion */ v2 = vij[0] * vij[0] + vij[1] * vij[1] + vij[2] * vij[2]; + // order is important: we first use the momentum fluxes to update the energy + // flux and then de-boost the momentum fluxes! + totflux[4] += vij[0] * totflux[1] + vij[1] * totflux[2] + + vij[2] * totflux[3] + 0.5 * v2 * totflux[0]; totflux[1] += vij[0] * totflux[0]; totflux[2] += vij[1] * totflux[0]; totflux[3] += vij[2] * totflux[0]; - totflux[4] += vij[0] * totflux[1] + vij[1] * totflux[2] + - vij[2] * totflux[3] + 0.5 * v2 * totflux[0]; } #endif /* SWIFT_RIEMANN_HLLC_H */ diff --git a/src/runner.c b/src/runner.c index 274976077fe285c749816800fda166795da094b3..a554d059607aad10fea8010906ad4d2617759729 100644 --- a/src/runner.c +++ b/src/runner.c @@ -53,6 +53,7 @@ #include "kick.h" #include "minmax.h" #include "scheduler.h" +#include "sourceterms.h" #include "space.h" #include "task.h" #include "timers.h" @@ -111,6 +112,42 @@ const char runner_flip[27] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, #include "runner_doiact_fft.h" #include "runner_doiact_grav.h" +/** + * @brief Perform source terms + * + * @param r runner task + * @param c cell + * @param timer 1 if the time is to be recorded. + */ +void runner_do_sourceterms(struct runner *r, struct cell *c, int timer) { + const int count = c->count; + const double cell_min[3] = {c->loc[0], c->loc[1], c->loc[2]}; + const double cell_width[3] = {c->width[0], c->width[1], c->width[2]}; + struct sourceterms *sourceterms = r->e->sourceterms; + const int dimen = 3; + + TIMER_TIC; + + /* Recurse? */ + if (c->split) { + for (int k = 0; k < 8; k++) + if (c->progeny[k] != NULL) runner_do_sourceterms(r, c->progeny[k], 0); + return; + } + + if (count > 0) { + + /* do sourceterms in this cell? */ + const int incell = + sourceterms_test_cell(cell_min, cell_width, sourceterms, dimen); + if (incell == 1) { + sourceterms_apply(r, sourceterms, c); + } + } + + if (timer) TIMER_TOC(timer_dosource); +} + /** * @brief Calculate gravity acceleration from external potential * @@ -1397,6 +1434,9 @@ void *runner_main(void *data) { case task_type_cooling: runner_do_cooling(r, t->ci, 1); break; + case task_type_sourceterms: + runner_do_sourceterms(r, t->ci, 1); + break; default: error("Unknown task type."); } diff --git a/src/scheduler.c b/src/scheduler.c index a45aa6570d647d0aa0ad95e102d717b9895d5340..29cdbe86ddc9f6eff31766433148b36bf64d38f6 100644 --- a/src/scheduler.c +++ b/src/scheduler.c @@ -141,6 +141,7 @@ static void scheduler_splittask(struct task *t, struct scheduler *s) { /* Get a handle on the cell involved. */ struct cell *ci = t->ci; + const double hi = ci->dmin; /* Foreign task? */ if (ci->nodeID != s->nodeID) { @@ -149,7 +150,7 @@ static void scheduler_splittask(struct task *t, struct scheduler *s) { } /* Is this cell even split? */ - if (ci->split) { + if (ci->split && ci->h_max * kernel_gamma * space_stretch < hi / 2) { /* Make a sub? */ if (scheduler_dosub && diff --git a/src/serial_io.c b/src/serial_io.c index 6e26be1a33fbc2c74ae1b8f7af2b83db285c962e..b9ad0fbaa856a889d3f84bb42013282f3640fd5e 100644 --- a/src/serial_io.c +++ b/src/serial_io.c @@ -528,6 +528,11 @@ void read_ic_serial(char* fileName, const struct UnitSystem* internal_units, H5Fclose(h_file); } + /* Convert the dimensions of the box */ + for (int j = 0; j < 3; j++) + dim[j] *= + units_conversion_factor(ic_units, internal_units, UNIT_CONV_LENGTH); + /* Now need to broadcast that information to all ranks. */ MPI_Bcast(flag_entropy, 1, MPI_INT, 0, comm); MPI_Bcast(periodic, 1, MPI_INT, 0, comm); diff --git a/src/single_io.c b/src/single_io.c index 6cb7e830209b0d58919fe6f529f675b4c611a51d..ceeba4eb80a47c3feed7e898deb5e1fe7e427c0c 100644 --- a/src/single_io.c +++ b/src/single_io.c @@ -433,6 +433,11 @@ void read_ic_single(char* fileName, const struct UnitSystem* internal_units, internal_units->UnitTemperature_in_cgs); } + /* Convert the dimensions of the box */ + for (int j = 0; j < 3; j++) + dim[j] *= + units_conversion_factor(ic_units, internal_units, UNIT_CONV_LENGTH); + /* Allocate memory to store SPH particles */ *Ngas = N[0]; if (posix_memalign((void*)parts, part_align, *Ngas * sizeof(struct part)) != diff --git a/src/sourceterms.c b/src/sourceterms.c new file mode 100644 index 0000000000000000000000000000000000000000..2a5f326688b52b0e4abfde8dca823d765e0e8a5e --- /dev/null +++ b/src/sourceterms.c @@ -0,0 +1,60 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 Tom Theuns (tom.theuns@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/>. + * + ******************************************************************************/ + +/* Config parameters. */ +#include "../config.h" + +/* Local includes. */ +#include "const.h" +#include "hydro.h" +#include "parser.h" +#include "units.h" + +/* This object's header. */ +#include "sourceterms.h" + +/** + * @brief Initialises the sourceterms + * + * @param parameter_file The parsed parameter file + * @param us The current internal system of units + * @param source the structure that has all the source term properties + */ +void sourceterms_init(const struct swift_params* parameter_file, + struct UnitSystem* us, struct sourceterms* source) { +#ifdef SOURCETERMS_SN_FEEDBACK + supernova_init(parameter_file, us, source); +#endif /* SOURCETERMS_SN_FEEDBACK */ +}; + +/** + * @brief Prints the properties of the source terms to stdout + * @param source the structure that has all the source term properties + */ +void sourceterms_print(struct sourceterms* source) { +#ifdef SOURCETERMS_NONE + error(" no sourceterms defined yet you ran with -F"); +#ifdef SOURCETERMS_SN_FEEDBACK +#error "can't have sourceterms when defined SOURCETERMS_NONE" +#endif +#endif +#ifdef SOURCETERMS_SN_FEEDBACK + supernova_print(source); +#endif /* SOURCETERMS_SN_FEEDBACK */ +}; diff --git a/src/sourceterms.h b/src/sourceterms.h new file mode 100644 index 0000000000000000000000000000000000000000..95c37e9bba9881702a6cf6caea1004b8cfb52636 --- /dev/null +++ b/src/sourceterms.h @@ -0,0 +1,74 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Coypright (c) 2015 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/>. + * + ******************************************************************************/ +#ifndef SWIFT_SOURCETERMS_H +#define SWIFT_SOURCETERMS_H + +/** + * @file src/sourceterms.h + * @brief Branches between the different sourceterms functions. + */ + +#include "./const.h" +#include "runner.h" + +#ifdef SOURCETERMS_SN_FEEDBACK +#include "sourceterms/sn_feedback/sn_feedback_struct.h" +#endif + +/* So far only one model here */ +struct sourceterms { +#ifdef SOURCETERMS_SN_FEEDBACK + struct supernova_struct supernova; +#endif +}; +#ifdef SOURCETERMS_SN_FEEDBACK +#include "sourceterms/sn_feedback/sn_feedback.h" +#endif + +void sourceterms_init(const struct swift_params* parameter_file, + struct UnitSystem* us, struct sourceterms* source); +void sourceterms_print(struct sourceterms* source); + +/** + * @brief Routines related to source terms + * @param cell_min: corner of cell to test + * @param cell_width: width of cell to test + * @param sourceterms: properties of source terms to test + * @param dimen: dimensionality of the problem + * + * This routine tests whether a source term should be applied to this cell + * return: 1 if yes, return: 0 if no + */ + +__attribute__((always_inline)) INLINE static int sourceterms_test_cell( + const double cell_min[], const double cell_width[], + struct sourceterms* sourceterms, const int dimen) { +#ifdef SOURCETERMS_SN_FEEDBACK + return supernova_feedback_test_cell(cell_min, cell_width, sourceterms, dimen); +#endif + return 0; +}; + +__attribute__((always_inline)) INLINE static void sourceterms_apply( + struct runner* r, struct sourceterms* sourceterms, struct cell* c) { +#ifdef SOURCETERMS_SN_FEEDBACK + supernova_feedback_apply(r, sourceterms, c); +#endif +}; +#endif /* SWIFT_SOURCETERMS_H */ diff --git a/src/sourceterms/sn_feedback/sn_feedback.h b/src/sourceterms/sn_feedback/sn_feedback.h new file mode 100644 index 0000000000000000000000000000000000000000..667c79cb1eef42f7fde79c43059d1baa5c61268e --- /dev/null +++ b/src/sourceterms/sn_feedback/sn_feedback.h @@ -0,0 +1,192 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 Tom Theuns (tom.theuns@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/>. + * + ******************************************************************************/ +#ifndef SWIFT_SN_FEEDBACK_H +#define SWIFT_SN_FEEDBACK_H +#include <float.h> +/* Config parameters. */ +#include "../config.h" + +#include "engine.h" +#include "equation_of_state.h" +#include "hydro.h" +#include "runner.h" +#include "timestep.h" + +/** + * @file src/sourceterms/sn_feedback.h + * + * @brief Routines related to sourceterms (supernova feedback): determine if + * feedback occurs in this cell + * + * @param cell_min: corner of cell to test + * @param cell_width: width of cell to test + * @param sourceterms: properties of source terms to test + * @param dimen: dimensionality of the problem + * + * This routine tests whether a source term should be applied to this cell + * return: 1 if yes, return: 0 if no + */ +__attribute__((always_inline)) INLINE static int supernova_feedback_test_cell( + const double cell_min[], const double cell_width[], + struct sourceterms* sourceterms, const int dimen) { + if (sourceterms->supernova.status == supernova_is_done) return 0; + + const double location[3] = {sourceterms->supernova.x, + sourceterms->supernova.y, + sourceterms->supernova.z}; + for (int i = 0; i < dimen; i++) { + if (cell_min[i] > location[i]) return 0; + if ((cell_min[i] + cell_width[i]) <= location[i]) return 0; + }; + return 1; +}; + +/** + * @file src/sourceterms/sn_feedback.h + * + * @brief Routines related to source terms (supernova feedback): perform + * feedback in this cell + * @param r: the runner + * @param sourceterms the structure describing the source terms properties + * @param c the cell to apply feedback to + * + * This routine heats an individual particle (p), increasing its thermal energy + * per unit mass + * by supernova energy / particle mass. + */ +__attribute__((always_inline)) INLINE static void supernova_feedback_apply( + struct runner* restrict r, struct sourceterms* restrict sourceterms, + struct cell* restrict c) { + + const int count = c->count; + struct part* restrict parts = c->parts; + struct xpart* restrict xparts = c->xparts; + const double timeBase = r->e->timeBase; + const int ti_current = r->e->ti_current; + + /* inject SN energy into the particle with highest id in this cell if it is + * active */ + int imax = 0; + struct part* restrict p_sn = NULL; + struct xpart* restrict xp_sn = NULL; + + for (int i = 0; i < count; i++) { + + /* Get a direct pointer on the part. */ + struct part* restrict p = &parts[i]; + if (p->id > imax) { + imax = p->id; + p_sn = p; + xp_sn = &xparts[i]; + } + } + + /* Is this part within the time step? */ + if (p_sn->ti_begin == ti_current) { + + /* Does this time step straddle the feedback injection time? */ + const float t_begin = p_sn->ti_begin * timeBase; + const float t_end = p_sn->ti_end * timeBase; + if (t_begin <= sourceterms->supernova.time && + t_end > sourceterms->supernova.time) { + + /* store old time step */ + const int dti_old = p_sn->ti_end - p_sn->ti_begin; + + /* add supernova feedback */ + const float u_old = hydro_get_internal_energy(p_sn, 0); + const float ent_old = hydro_get_entropy(p_sn, 0.0); + const float u_new = + u_old + sourceterms->supernova.energy / hydro_get_mass(p_sn); + hydro_set_internal_energy(p_sn, u_new); + const float u_set = hydro_get_internal_energy(p_sn, 0.0); + const float ent_set = hydro_get_entropy(p_sn, 0.0); + message( + " applied super nova, time = %e, location= %e %e %e velocity= %e %e " + "%e", + ti_current * timeBase, p_sn->x[0], p_sn->x[1], p_sn->x[2], p_sn->v[0], + p_sn->v[1], p_sn->v[2]); + message( + " injected SN energy in particle = %lld, increased energy from %e to " + "%e and is notw %e, entropy from %e to %e", + p_sn->id, u_old, u_new, u_set, ent_old, ent_set); + + /* label supernova as done */ + sourceterms->supernova.status = supernova_is_done; + + /* update timestep if new time step shorter than old time step */ + const int dti = get_part_timestep(p_sn, xp_sn, r->e); + if (dti < dti_old) { + p_sn->ti_end = p_sn->ti_begin + dti; + message(" changed timestep from %d to %d", dti_old, dti); + + /* apply simple time-step limiter on all particles in same cell: + */ + int i_limit = 0; + for (int i = 0; i < count; i++) { + struct part* restrict p = &parts[i]; + const int dti_old = p->ti_end - p->ti_begin; + if (dti_old > 2 * dti) { + i_limit++; + const int dti_new = 2 * dti; + p->ti_end = p->ti_begin + dti_new; + message(" old step = %d new step = %d", dti_old, dti_new); + } else + message(" old step = %d", dti_old); + } + message(" count= %d limited timestep of %d particles ", count, i_limit); + } /* end of limiter */ + error("end"); + } + } +}; + +/** + * @file src/sourceterms/sn_feedback.h + * + * @brief Routine to initialise supernova feedback + * @param parameterfile: the parse parmeter file + * @param us: the unit system in use + * @param sourceterms the structure describing the source terms properties + * + * This routine heats an individual particle (p), increasing its thermal energy + * per unit mass + * by supernova energy / particle mass. + */ + +__attribute__((always_inline)) INLINE static void supernova_init( + const struct swift_params* parameter_file, struct UnitSystem* us, + struct sourceterms* source) { + source->supernova.time = parser_get_param_double(parameter_file, "SN:time"); + source->supernova.energy = + parser_get_param_double(parameter_file, "SN:energy"); + source->supernova.x = parser_get_param_double(parameter_file, "SN:x"); + source->supernova.y = parser_get_param_double(parameter_file, "SN:y"); + source->supernova.z = parser_get_param_double(parameter_file, "SN:z"); + source->supernova.status = supernova_is_not_done; +} +__attribute__((always_inline)) INLINE static void supernova_print( + struct sourceterms* source) { + message( + " Single SNe of energy= %e will explode at time= %e at location " + "(%e,%e,%e)", + source->supernova.energy, source->supernova.time, source->supernova.x, + source->supernova.y, source->supernova.z); +} +#endif /* SWIFT_SN_FEEDBACK_H */ diff --git a/src/sourceterms/sn_feedback/sn_feedback_struct.h b/src/sourceterms/sn_feedback/sn_feedback_struct.h new file mode 100644 index 0000000000000000000000000000000000000000..2b4dbe97b0a687cc48d0ba7e12b105f306cb7e96 --- /dev/null +++ b/src/sourceterms/sn_feedback/sn_feedback_struct.h @@ -0,0 +1,45 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 Tom Theuns (tom.theuns@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/sourceterms/sn_feedback_struct.h + * @brief Routines related to source terms (feedback) + * + * enumeration type that sets if supernova explosion is done (is_done) or still + * needs doing (is_not_done) + */ +#ifndef SWIFT_SN_FEEDBACK_STRUCT_H +#define SWIFT_SN_FEEDBACK_STRUCT_H +enum supernova_status { supernova_is_done, supernova_is_not_done }; + +/** + * @file src/sourceterms/sn_feedback_struct.h + * @brief Routines related to source terms (feedback) + * + * The structure that describes the source term (supernova feedback) + * It specifies the time, energy and location of the desired supernova + * explosion, and a status (supernova_is_done/supernova_is_not_done) + * that records the status of the supernova + */ +struct supernova_struct { + double time; + double energy; + double x, y, z; + enum supernova_status status; +}; +#endif SWIFT_SN_FEEDBACK_STRUCT_H diff --git a/src/sourceterms_struct.h b/src/sourceterms_struct.h new file mode 100644 index 0000000000000000000000000000000000000000..b3c38986db52d72df825fda97b36c985dff922b6 --- /dev/null +++ b/src/sourceterms_struct.h @@ -0,0 +1,26 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Coypright (c) 2015 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/>. + * + ******************************************************************************/ +#ifndef SWIFT_SOURCETERMS_STRUCT_H +#define SWIFT_SOURCETERMS_STRUCT_H +#include "./const.h" +#ifdef SOURCETERMS_SN_FEEDBACK +#include "sourceterms/sn_feedback/sn_feedback_struct.h" +#endif + +#endif /* SWIFT_SOURCETERMS_STRUCT_H */ diff --git a/src/space.c b/src/space.c index a9958f6fbd7d85060db99a9682b0de10f507085d..1d2733ab2d4356c19cc0e65a0b9441edcf0acb80 100644 --- a/src/space.c +++ b/src/space.c @@ -58,6 +58,7 @@ int space_splitsize = space_splitsize_default; int space_subsize = space_subsize_default; int space_maxsize = space_maxsize_default; +int space_maxcount = space_maxcount_default; /* Map shift vector to sortlist. */ const int sortlistID[27] = { @@ -228,7 +229,14 @@ void space_regrid(struct space *s, double cell_max, int verbose) { if (s->periodic && (cdim[0] < 3 || cdim[1] < 3 || cdim[2] < 3)) error( "Must have at least 3 cells in each spatial dimension when periodicity " - "is switched on."); + "is switched on.\nThis error is often caused by any of the " + "followings:\n" + " - too few particles to generate a sensible grid,\n" + " - the initial value of 'SPH:max_smoothing_length' is too large,\n" + " - the (minimal) time-step is too large leading to particles with " + "predicted smoothing lengths too large for the box size,\n" + " - particle with velocities so large that they move by more than two " + "box sizes per time-step.\n"); /* Check if we have enough cells for gravity. */ if (s->gravity && (cdim[0] < 8 || cdim[1] < 8 || cdim[2] < 8)) @@ -691,7 +699,7 @@ void space_rebuild(struct space *s, double cell_max, int verbose) { /* At this point, we have the upper-level cells, old or new. Now make sure that the parts in each cell are ok. */ - space_split(s, cells_top, verbose); + space_split(s, cells_top, s->nr_cells, verbose); if (verbose) message("took %.3f %s.", clocks_from_ticks(getticks() - tic), @@ -704,14 +712,16 @@ void space_rebuild(struct space *s, double cell_max, int verbose) { * This is done in parallel using threads in the #threadpool. * * @param s The #space. - * @param cells The cell hierarchy + * @param cells The cell hierarchy. + * @param nr_cells The number of cells. * @param verbose Are we talkative ? */ -void space_split(struct space *s, struct cell *cells, int verbose) { +void space_split(struct space *s, struct cell *cells, int nr_cells, + int verbose) { const ticks tic = getticks(); - threadpool_map(&s->e->threadpool, space_split_mapper, cells, s->nr_cells, + threadpool_map(&s->e->threadpool, space_split_mapper, cells, nr_cells, sizeof(struct cell), 1, s); if (verbose) @@ -719,6 +729,29 @@ void space_split(struct space *s, struct cell *cells, int verbose) { clocks_getunit()); } +/** + * @brief Runs through the top-level cells and checks whether tasks associated + * with them can be split. If not, try to sanitize the cells. + * + * @param s The #space to act upon. + */ +void space_sanitize(struct space *s) { + + for (int k = 0; k < s->nr_cells; k++) { + + struct cell *c = &s->cells_top[k]; + const double min_width = c->dmin; + + /* Do we have a problem ? */ + if (c->h_max * kernel_gamma * space_stretch > min_width * 0.5 && + c->count > space_maxcount) { + + /* Ok, clean-up the mess */ + cell_sanitize(c); + } + } +} + /** * @brief Sort the particles and condensed particles according to the given * indices. @@ -1240,17 +1273,17 @@ void space_map_cells_pre(struct space *s, int full, * too many particles. * * @param map_data Pointer towards the top-cells. - * @param num_elements The number of cells to treat. + * @param num_cells The number of cells to treat. * @param extra_data Pointers to the #space. */ -void space_split_mapper(void *map_data, int num_elements, void *extra_data) { +void space_split_mapper(void *map_data, int num_cells, void *extra_data) { /* Unpack the inputs. */ struct space *s = (struct space *)extra_data; struct cell *restrict cells_top = (struct cell *)map_data; struct engine *e = s->e; - for (int ind = 0; ind < num_elements; ind++) { + for (int ind = 0; ind < num_cells; ind++) { struct cell *c = &cells_top[ind]; @@ -1269,6 +1302,12 @@ void space_split_mapper(void *map_data, int num_elements, void *extra_data) { atomic_cas(&s->maxdepth, maxdepth, c->depth); } + /* If the depth is too large, we have a problem and should stop. */ + if (maxdepth > space_cell_maxdepth) { + error("Exceeded maximum depth (%d) when splitting cells, aborting", + space_cell_maxdepth); + } + /* Split or let it be? */ if (count > space_splitsize || gcount > space_splitsize) { @@ -1565,6 +1604,8 @@ void space_init(struct space *s, const struct swift_params *params, space_subsize_default); space_splitsize = parser_get_opt_param_int( params, "Scheduler:cell_split_size", space_splitsize_default); + space_maxcount = parser_get_opt_param_int(params, "Scheduler:cell_max_count", + space_maxcount_default); if (verbose) message("max_size set to %d, sub_size set to %d, split_size set to %d", space_maxsize, space_subsize, space_splitsize); diff --git a/src/space.h b/src/space.h index 72b17405f13766ad2ccc9d53712068f28172067b..85f32bd7580945de6d9713316b557c92667987c0 100644 --- a/src/space.h +++ b/src/space.h @@ -41,13 +41,18 @@ #define space_splitsize_default 400 #define space_maxsize_default 8000000 #define space_subsize_default 64000000 +#define space_maxcount_default 10000 #define space_stretch 1.10f #define space_maxreldx 0.25f +/* Maximum allowed depth of cell splits. */ +#define space_cell_maxdepth 52 + /* Split size. */ extern int space_splitsize; extern int space_maxsize; extern int space_subsize; +extern int space_maxcount; /* Map shift vector to sortlist. */ extern const int sortlistID[27]; @@ -145,6 +150,7 @@ void space_init(struct space *s, const struct swift_params *params, double dim[3], struct part *parts, struct gpart *gparts, size_t Npart, size_t Ngpart, int periodic, int gravity, int verbose, int dry_run); +void space_sanitize(struct space *s); void space_map_cells_pre(struct space *s, int full, void (*fun)(struct cell *c, void *data), void *data); void space_map_parts(struct space *s, @@ -161,7 +167,8 @@ void space_gparts_sort_mapper(void *map_data, int num_elements, void *extra_data); void space_rebuild(struct space *s, double h_max, int verbose); void space_recycle(struct space *s, struct cell *c); -void space_split(struct space *s, struct cell *cells, int verbose); +void space_split(struct space *s, struct cell *cells, int nr_cells, + int verbose); void space_split_mapper(void *map_data, int num_elements, void *extra_data); void space_do_parts_sort(); void space_do_gparts_sort(); diff --git a/src/swift.h b/src/swift.h index 3119adfd4b95639a84137e243c6e971d80ef3825..58745f3111fc3f29490d4591ee549907392cb87e 100644 --- a/src/swift.h +++ b/src/swift.h @@ -49,6 +49,7 @@ #include "scheduler.h" #include "serial_io.h" #include "single_io.h" +#include "sourceterms.h" #include "space.h" #include "task.h" #include "timers.h" diff --git a/src/task.c b/src/task.c index 8da526f57886fa68121061c8eae2316912e73a30..00068f45769b4ca606cc729bd5e89c13ae729eef 100644 --- a/src/task.c +++ b/src/task.c @@ -51,7 +51,7 @@ const char *taskID_names[task_type_count] = { "none", "sort", "self", "pair", "sub_self", "sub_pair", "init", "ghost", "extra_ghost", "kick", "kick_fixdt", "send", "recv", "grav_gather_m", "grav_fft", - "grav_mm", "grav_up", "grav_external", "cooling"}; + "grav_mm", "grav_up", "grav_external", "cooling", "sourceterms"}; const char *subtaskID_names[task_subtype_count] = { "none", "density", "gradient", "force", "grav", "tend"}; @@ -118,6 +118,7 @@ __attribute__((always_inline)) INLINE static enum task_actions task_acts_on( case task_type_ghost: case task_type_extra_ghost: case task_type_cooling: + case task_type_sourceterms: return task_action_part; break; diff --git a/src/task.h b/src/task.h index 4928dc00bcd7958efdd6987b5aec90ab4b3e92fa..bc4df3dc2a4cfee3c382e9f2059cba84f29299f7 100644 --- a/src/task.h +++ b/src/task.h @@ -55,6 +55,7 @@ enum task_types { task_type_grav_up, task_type_grav_external, task_type_cooling, + task_type_sourceterms, task_type_count } __attribute__((packed)); diff --git a/src/timers.h b/src/timers.h index b93a34df4d90251d4686afaef0be51b36dc9a25e..d75508afd88cf2fd57d06f9530bff8607d79d127 100644 --- a/src/timers.h +++ b/src/timers.h @@ -45,6 +45,7 @@ enum { timer_dopair_grav_pm, timer_dopair_grav_pp, timer_dograv_external, + timer_dosource, timer_dosub_self_density, timer_dosub_self_gradient, timer_dosub_self_force, diff --git a/src/timestep.h b/src/timestep.h index 599fb4762e11b08fc942fb02acbbf1970f477de4..db52911ec1e8fbf31f35e8877e0a7ae7ba5ee478 100644 --- a/src/timestep.h +++ b/src/timestep.h @@ -24,8 +24,8 @@ /* Local headers. */ #include "const.h" +#include "cooling.h" #include "debug.h" - /** * @brief Compute a valid integer time-step form a given time-step * diff --git a/tests/test125cells.c b/tests/test125cells.c index e666658f43de135e3e72521b52f2a688c596a6f6..d423efba3fda764310721586e14192afdee18a85 100644 --- a/tests/test125cells.c +++ b/tests/test125cells.c @@ -95,6 +95,8 @@ void set_energy_state(struct part *part, enum pressure_field press, float size, #if defined(GADGET2_SPH) part->entropy = pressure / pow_gamma(density); +#elif defined(HOPKINS_PE_SPH) + part->entropy = pressure / pow_gamma(density); #elif defined(DEFAULT_SPH) part->u = pressure / (hydro_gamma_minus_one * density); #elif defined(MINIMAL_SPH) diff --git a/tests/test27cells.c b/tests/test27cells.c index 1a1ab88748d922b3e7fbb30a73a10809dca10863..22619af53c39218da34d771fab6ed2d10993689c 100644 --- a/tests/test27cells.c +++ b/tests/test27cells.c @@ -104,11 +104,18 @@ struct cell *make_cell(size_t n, double *offset, double size, double h, } part->h = size * h / (float)n; part->id = ++(*partId); + #ifdef GIZMO_SPH part->conserved.mass = density * volume / count; #else part->mass = density * volume / count; #endif + +#if defined(HOPKINS_PE_SPH) + part->entropy = 1.f; + part->entropy_one_over_gamma = 1.f; +#endif + part->ti_begin = 0; part->ti_end = 1; ++part; @@ -193,16 +200,11 @@ void dump_particle_fields(char *fileName, struct cell *main_cell, #if defined(GIZMO_SPH) 0.f, #else - main_cell->parts[pid].rho_dh, + main_cell->parts[pid].density.rho_dh, #endif main_cell->parts[pid].density.wcount, main_cell->parts[pid].density.wcount_dh, -#if defined(GADGET2_SPH) - main_cell->parts[pid].density.div_v, - main_cell->parts[pid].density.rot_v[0], - main_cell->parts[pid].density.rot_v[1], - main_cell->parts[pid].density.rot_v[2] -#elif defined(DEFAULT_SPH) +#if defined(GADGET2_SPH) || defined(DEFAULT_SPH) || defined(HOPKINS_PE_SPH) main_cell->parts[pid].density.div_v, main_cell->parts[pid].density.rot_v[0], main_cell->parts[pid].density.rot_v[1], @@ -235,13 +237,10 @@ void dump_particle_fields(char *fileName, struct cell *main_cell, #if defined(GIZMO_SPH) 0.f, #else - main_cell->parts[pjd].rho_dh, + main_cell->parts[pjd].density.rho_dh, #endif cj->parts[pjd].density.wcount, cj->parts[pjd].density.wcount_dh, -#if defined(GADGET2_SPH) - cj->parts[pjd].density.div_v, cj->parts[pjd].density.rot_v[0], - cj->parts[pjd].density.rot_v[1], cj->parts[pjd].density.rot_v[2] -#elif defined(DEFAULT_SPH) +#if defined(GADGET2_SPH) || defined(DEFAULT_SPH) || defined(HOPKINS_PE_SPH) cj->parts[pjd].density.div_v, cj->parts[pjd].density.rot_v[0], cj->parts[pjd].density.rot_v[1], cj->parts[pjd].density.rot_v[2] #else diff --git a/tests/testInteractions.c b/tests/testInteractions.c index 52ad0c54258848883a9025bbcd9d68133eddc4b9..d14c840ec77819bbef5750b897c72139f4d7b2b4 100644 --- a/tests/testInteractions.c +++ b/tests/testInteractions.c @@ -110,9 +110,9 @@ void dump_indv_particle_fields(char *fileName, struct part *p) { "%13e %13e %13e %13e " "%13e %13e %13e %10f\n", p->id, p->x[0], p->x[1], p->x[2], p->v[0], p->v[1], p->v[2], - p->a_hydro[0], p->a_hydro[1], p->a_hydro[2], p->rho, p->rho_dh, - p->density.wcount, p->density.wcount_dh, p->force.h_dt, - p->force.v_sig, + p->a_hydro[0], p->a_hydro[1], p->a_hydro[2], p->rho, + p->density.rho_dh, p->density.wcount, p->density.wcount_dh, + p->force.h_dt, p->force.v_sig, #if defined(GADGET2_SPH) p->density.div_v, p->density.rot_v[0], p->density.rot_v[1], p->density.rot_v[2], p->entropy_dt diff --git a/tests/testPair.c b/tests/testPair.c index efa1e628c2d57bf7922be8affdd5436ebca2f9cf..8b272b866431db3bfe36239222cd87d669961ae7 100644 --- a/tests/testPair.c +++ b/tests/testPair.c @@ -139,10 +139,10 @@ void dump_particle_fields(char *fileName, struct cell *ci, struct cell *cj) { #if defined(GIZMO_SPH) 0.f, #else - cj->parts[pid].rho_dh, + ci->parts[pid].density.rho_dh, #endif ci->parts[pid].density.wcount, ci->parts[pid].density.wcount_dh, -#if defined(GADGET2_SPH) || defined(DEFAULT_SPH) +#if defined(GADGET2_SPH) || defined(DEFAULT_SPH) || defined(HOPKINS_PE_SPH) ci->parts[pid].density.div_v, ci->parts[pid].density.rot_v[0], ci->parts[pid].density.rot_v[1], ci->parts[pid].density.rot_v[2] #else @@ -163,10 +163,10 @@ void dump_particle_fields(char *fileName, struct cell *ci, struct cell *cj) { #if defined(GIZMO_SPH) 0.f, #else - cj->parts[pjd].rho_dh, + cj->parts[pjd].density.rho_dh, #endif cj->parts[pjd].density.wcount, cj->parts[pjd].density.wcount_dh, -#if defined(GADGET2_SPH) || defined(DEFAULT_SPH) +#if defined(GADGET2_SPH) || defined(DEFAULT_SPH) || defined(HOPKINS_PE_SPH) cj->parts[pjd].density.div_v, cj->parts[pjd].density.rot_v[0], cj->parts[pjd].density.rot_v[1], cj->parts[pjd].density.rot_v[2] #else diff --git a/tests/tolerance_27_normal.dat b/tests/tolerance_27_normal.dat index 141ed3baeedd5dad7a2fda0730c2e9c828ae4b2e..71acaa89be231d02fc33e47c96a7bacf623bbf48 100644 --- a/tests/tolerance_27_normal.dat +++ b/tests/tolerance_27_normal.dat @@ -1,3 +1,3 @@ # ID pos_x pos_y pos_z v_x v_y v_z rho rho_dh wcount wcount_dh div_v curl_vx curl_vy curl_vz 0 1e-6 1e-6 1e-6 1e-6 1e-6 1e-6 1e-6 2e-6 2e-5 2e-3 2e-6 2e-6 2e-6 2e-6 - 0 1e-6 1e-6 1e-6 1e-6 1e-6 1e-6 1e-6 1e-6 1e-5 1e-4 2e-5 2e-5 2e-5 2e-5 + 0 1e-6 1e-6 1e-6 1e-6 1e-6 1e-6 1e-6 2e-6 1e-5 1e-4 2e-5 2e-5 2e-5 2e-5 diff --git a/tests/tolerance_27_perturbed.dat b/tests/tolerance_27_perturbed.dat index f0417d845f83d171dacb6b66024cf9a5dc41c6f1..45293cbaa223b5887f3b0ce05cd9430d0db7440b 100644 --- a/tests/tolerance_27_perturbed.dat +++ b/tests/tolerance_27_perturbed.dat @@ -1,3 +1,3 @@ # ID pos_x pos_y pos_z v_x v_y v_z rho rho_dh wcount wcount_dh div_v curl_vx curl_vy curl_vz - 0 1e-6 1e-6 1e-6 1e-6 1e-6 1e-6 1.2e-6 2e-6 2.1e-5 2e-3 2.1e-6 2e-6 2e-6 2e-6 - 0 1e-6 1e-6 1e-6 1e-6 1e-6 1e-6 1e-6 1e-6 1e-5 1e-4 2e-5 4e-4 4e-4 4e-4 + 0 1e-6 1e-6 1e-6 1e-6 1e-6 1e-6 1.2e-6 1e-5 2.1e-5 2e-3 2.1e-6 2e-6 2e-6 2e-6 + 0 1e-6 1e-6 1e-6 1e-6 1e-6 1e-6 1e-6 3e-3 1e-5 1e-4 2e-5 4e-4 4e-4 4e-4