diff --git a/.gitignore b/.gitignore index c70f1ab3aec9206e6c4465e736b529285daab5da..4a8a0b583aec90cffe3e450155fa54a4885778f0 100644 --- a/.gitignore +++ b/.gitignore @@ -33,7 +33,12 @@ examples/*/*/*.xmf examples/*/*/*.png examples/*/*/*.txt examples/*/*/*.dot +examples/*/*/*.rst +examples/*/*/*.hdf5 +examples/*/snapshots* +examples/*/restart examples/*/*/used_parameters.yml +examples/*/*.mpg examples/*/gravity_checks_*.dat tests/testActivePair @@ -59,7 +64,7 @@ tests/swift_dopair_125_standard.dat tests/brute_force_125_perturbed.dat tests/swift_dopair_125_perturbed.dat tests/brute_force_pair_active.dat -tests/brute_force_dopair2_active.dat +tests/brute_force_dopair2_active.dat tests/swift_dopair2_force_active.dat tests/brute_force_periodic_BC_perturbed.dat tests/swift_dopair_active.dat @@ -108,6 +113,9 @@ tests/benchmarkInteractions tests/testGravityDerivatives tests/testPotentialSelf tests/testPotentialPair +tests/testEOS +tests/testEOS*.txt +tests/testEOS*.png theory/latex/swift.pdf theory/SPH/Kernels/kernels.pdf diff --git a/configure.ac b/configure.ac index dc0f3cb96526adb93a0291e5470113e5740b5df3..e3a796c14251e6a0309a04eab571a9e026c503e5 100644 --- a/configure.ac +++ b/configure.ac @@ -75,7 +75,7 @@ case "$with_subgrid" in with_subgrid_chemistry=EAGLE with_subgrid_hydro=gadget2 ;; - *) + *) AC_MSG_ERROR([Unknown subgrid choice: $with_subgrid]) ;; esac @@ -618,7 +618,7 @@ if test "x$with_fftw" != "xno"; then AC_DEFINE([HAVE_FFTW],1,[The FFTW library appears to be present.]), AC_MSG_ERROR(something is wrong with the FFTW library!), $FFTW_LIBS) have_fftw="yes" - fi + fi if test "$have_fftw" = "no"; then FFTW_LIBS="" FFTW_INCS="" @@ -855,7 +855,7 @@ fi # Hydro scheme. AC_ARG_WITH([hydro], [AS_HELP_STRING([--with-hydro=<scheme>], - [Hydro dynamics to use @<:@gadget2, minimal, hopkins, default, gizmo, shadowfax debug default: gadget2@:>@] + [Hydro dynamics to use @<:@gadget2, minimal, hopkins, default, gizmo, shadowfax, minimal-multi-mat, debug default: gadget2@:>@] )], [with_hydro="$withval"], [with_hydro="gadget2"] @@ -888,6 +888,9 @@ case "$with_hydro" in shadowfax) AC_DEFINE([SHADOWFAX_SPH], [1], [Shadowfax SPH]) ;; + minimal-multi-mat) + AC_DEFINE([MINIMAL_MULTI_MAT_SPH], [1], [Minimal Multiple Material SPH]) + ;; *) AC_MSG_ERROR([Unknown hydrodynamics scheme: $with_hydro]) @@ -975,7 +978,7 @@ esac # Equation of state AC_ARG_WITH([equation-of-state], [AS_HELP_STRING([--with-equation-of-state=<EoS>], - [equation of state @<:@ideal-gas, isothermal-gas default: ideal-gas@:>@] + [equation of state @<:@ideal-gas, isothermal-gas, planetary default: ideal-gas@:>@] )], [with_eos="$withval"], [with_eos="ideal-gas"] @@ -987,6 +990,9 @@ case "$with_eos" in isothermal-gas) AC_DEFINE([EOS_ISOTHERMAL_GAS], [1], [Isothermal gas equation of state]) ;; + planetary) + AC_DEFINE([EOS_PLANETARY], [1], [All planetary equations of state]) + ;; *) AC_MSG_ERROR([Unknown equation of state: $with_eos]) ;; @@ -1074,7 +1080,7 @@ case "$with_cooling" in grackle) AC_DEFINE([COOLING_GRACKLE], [1], [Cooling via the grackle library]) AC_DEFINE([COOLING_GRACKLE_MODE], [0], [Grackle chemistry network, mode 0]) - ;; + ;; grackle1) AC_DEFINE([COOLING_GRACKLE], [1], [Cooling via the grackle library]) AC_DEFINE([COOLING_GRACKLE_MODE], [1], [Grackle chemistry network, mode 1]) @@ -1082,11 +1088,11 @@ case "$with_cooling" in grackle2) AC_DEFINE([COOLING_GRACKLE], [1], [Cooling via the grackle library]) AC_DEFINE([COOLING_GRACKLE_MODE], [2], [Grackle chemistry network, mode 2]) - ;; + ;; grackle3) AC_DEFINE([COOLING_GRACKLE], [1], [Cooling via the grackle library]) AC_DEFINE([COOLING_GRACKLE_MODE], [3], [Grackle chemistry network, mode 3]) - ;; + ;; EAGLE) AC_DEFINE([COOLING_EAGLE], [1], [Cooling following the EAGLE model]) ;; diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in index 844a6fa68029af3c119d7c03e09e6dec957c5da4..a4a28480a09baf4ef0ef58a1bff3a537326e5f16 100644 --- a/doc/Doxyfile.in +++ b/doc/Doxyfile.in @@ -765,6 +765,7 @@ INPUT += @top_srcdir@/src/gravity/Default INPUT += @top_srcdir@/src/stars/Default INPUT += @top_srcdir@/src/riemann INPUT += @top_srcdir@/src/potential/point_mass +INPUT += @top_srcdir@/src/equation_of_state/ideal_gas INPUT += @top_srcdir@/src/cooling/none INPUT += @top_srcdir@/src/chemistry/none diff --git a/examples/MoonFormingImpact/README.md b/examples/MoonFormingImpact/README.md new file mode 100644 index 0000000000000000000000000000000000000000..97a84f67c6aeeff4176a1385381f1cfe9e340c91 --- /dev/null +++ b/examples/MoonFormingImpact/README.md @@ -0,0 +1,34 @@ +Canonical Moon-Forming Giant Impact +=================================== + +NOTE: This doesn't really work because the EOS are different to Canup (2004) so +the impactor just glances then flies away! + +A version of the canonical moon-forming giant impact of Theia onto the early +Earth (Canup 2004; Barr 2016). Both bodies are here made of a (Tillotson) iron +core and granite mantle. Only ~10,000 particles are used for a quick and crude +simulation. + +Setup +----- + +In `swiftsim/`: + +`$ ./configure --with-hydro=minimal-multi-mat --with-equation-of-state=planetary` +`$ make` + +In `swiftsim/examples/MoonFormingImpact/`: + +`$ ./get_init_cond.sh` + +Run +--- + +`$ ./run.sh` + +Output +------ + +`$ python plot.py` +`$ mplayer anim.mpg` + diff --git a/examples/MoonFormingImpact/get_init_cond.sh b/examples/MoonFormingImpact/get_init_cond.sh new file mode 100755 index 0000000000000000000000000000000000000000..7d63943c2c5dc3bd4ab88e63a2abba62cc3f04a5 --- /dev/null +++ b/examples/MoonFormingImpact/get_init_cond.sh @@ -0,0 +1,2 @@ +#!/bin/bash +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/moon_forming_impact.hdf5 diff --git a/examples/MoonFormingImpact/moon_forming_impact.yml b/examples/MoonFormingImpact/moon_forming_impact.yml new file mode 100644 index 0000000000000000000000000000000000000000..7d726bc02ba4fb6c8dd02f74907ffc48c3ed9431 --- /dev/null +++ b/examples/MoonFormingImpact/moon_forming_impact.yml @@ -0,0 +1,44 @@ +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 5.9724e27 # Grams + UnitLength_in_cgs: 6.371e8 # Centimeters + UnitVelocity_in_cgs: 6.371e8 # 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: 100000 # The end time of the simulation (in internal units). + dt_min: 0.001 # The minimal time-step size of the simulation (in internal units). + dt_max: 100 # The maximal time-step size of the simulation (in internal units). + +# Parameters governing the snapshots +Snapshots: + # Common part of the name of output files + basename: snapshots/moon_forming_impact + time_first: 0 # Time of the first output (in internal units) + delta_time: 100 # Time difference between consecutive outputs (in internal units) + label_delta: 100 # Integer increment between snapshot output labels + +# Parameters governing the conserved quantities statistics +Statistics: + delta_time: 500 # 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. + CFL_condition: 0.2 # Courant-Friedrich-Levy condition for time integration. + +# Parameters for the self-gravity scheme +Gravity: + eta: 0.025 # Constant dimensionless multiplier for time integration. + theta: 0.7 # Opening angle (Multipole acceptance criterion) + comoving_softening: 0.005 # Comoving softening length (in internal units). + max_physical_softening: 0.005 # Physical softening length (in internal units). + +# Parameters related to the initial conditions +InitialConditions: + # The initial conditions file to read + file_name: moon_forming_impact.hdf5 diff --git a/examples/MoonFormingImpact/plot.py b/examples/MoonFormingImpact/plot.py new file mode 100644 index 0000000000000000000000000000000000000000..aa0d64a5d0d06709d51b1db231c507e22861f36c --- /dev/null +++ b/examples/MoonFormingImpact/plot.py @@ -0,0 +1,285 @@ +""" +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2018 Jacob Kegerreis (jacob.kegerreis@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/>. +# +############################################################################### + +Plotting script for the Canonical Moon-Forming Giant Impact example. + +Save a figure for each snapshot in `./plots/` then make them into a simple +animation with ffmpeg in `./`. + +Usage: + `$ python plot.py time_end delta_time` + + Sys args: + + `time_end` | (opt) int | The time of the last snapshot to plot. + Default = 100000 + + `delta_time` | (opt) int | The time between successive snapshots. + Default = 100 +""" + +from __future__ import division +import numpy as np +import matplotlib +import matplotlib.pyplot as plt +import h5py +import sys +import subprocess + +# Particle array fields +dtype_picle = [ + ('m', float), ('x', float), ('y', float), ('z', float), ('v_x', float), + ('v_y', float), ('v_z', float), ('ID', int), ('rho', float), ('u', float), + ('phi', float), ('P', float), ('h', float), ('mat_ID', int), ('r', float) + ] + +s_to_hour = 1 / 60**2 + +# Snapshot info +file_snap = "./snapshots/moon_forming_impact_" +file_plot = "./plots/moon_forming_impact_" +# Number of particles in the target body +num_target = 9496 + +# Material types (copied from src/equation_of_state/planetary/equation_of_state.h) +type_factor = 100 +Di_type = { + 'Til' : 1, + 'HM80' : 2, + 'ANEOS' : 3, + 'SESAME' : 4, +} +Di_material = { + # Tillotson + 'Til_iron' : Di_type['Til']*type_factor, + 'Til_granite' : Di_type['Til']*type_factor + 1, + 'Til_water' : Di_type['Til']*type_factor + 2, + # Hubbard & MacFarlane (1980) Uranus/Neptune + 'HM80_HHe' : Di_type['HM80']*type_factor, # Hydrogen-helium atmosphere + 'HM80_ice' : Di_type['HM80']*type_factor + 1, # H20-CH4-NH3 ice mix + 'HM80_rock' : Di_type['HM80']*type_factor + 2, # SiO2-MgO-FeS-FeO rock mix + # ANEOS + 'ANEOS_iron' : Di_type['ANEOS']*type_factor, + 'MANEOS_forsterite' : Di_type['ANEOS']*type_factor + 1, + # SESAME + 'SESAME_iron' : Di_type['SESAME']*type_factor, +} + +# Material offset for impactor particles +ID_imp = 10000 +# Material colours +Di_mat_colour = { + # Target + Di_material['Til_iron'] : 'orange', + Di_material['Til_granite'] : '#FFF0E0', + # Impactor + Di_material['Til_iron'] + ID_imp : 'dodgerblue', + Di_material['Til_granite'] + ID_imp : '#A080D0', + } + + +def load_snapshot(filename): + """ Load the hdf5 snapshot file and return the structured particle array. + """ + # Add extension if needed + if (filename[-5:] != ".hdf5"): + filename += ".hdf5" + + # Load the hdf5 file + with h5py.File(filename, 'r') as f: + header = f['Header'].attrs + A2_pos = f['PartType0/Coordinates'].value + A2_vel = f['PartType0/Velocities'].value + + # Structured array of all particle data + A2_picle = np.empty(header['NumPart_Total'][0], + dtype=dtype_picle) + + A2_picle['x'] = A2_pos[:, 0] + A2_picle['y'] = A2_pos[:, 1] + A2_picle['z'] = A2_pos[:, 2] + A2_picle['v_x'] = A2_vel[:, 0] + A2_picle['v_y'] = A2_vel[:, 1] + A2_picle['v_z'] = A2_vel[:, 2] + A2_picle['m'] = f['PartType0/Masses'].value + A2_picle['ID'] = f['PartType0/ParticleIDs'].value + A2_picle['rho'] = f['PartType0/Density'].value + A2_picle['u'] = f['PartType0/InternalEnergy'].value + A2_picle['phi'] = f['PartType0/Potential'].value + A2_picle['P'] = f['PartType0/Pressure'].value + A2_picle['h'] = f['PartType0/SmoothingLength'].value + A2_picle['mat_ID'] = f['PartType0/MaterialID'].value + + return A2_picle + + +def process_particles(A2_picle, num_target): + """ Modify things like particle units, material IDs, and coordinate origins. + """ + # Offset material IDs for impactor particles + A2_picle['mat_ID'][A2_picle['ID'] >= num_target] += ID_imp + + # Shift coordinates to the centre of the target's core's mass and momentum + sel_tar = np.where(A2_picle['mat_ID'] == Di_material['Til_iron'])[0] + + # Centre of mass + m_tot = np.sum(A2_picle[sel_tar]['m']) + x_com = np.sum(A2_picle[sel_tar]['m'] * A2_picle[sel_tar]['x']) / m_tot + y_com = np.sum(A2_picle[sel_tar]['m'] * A2_picle[sel_tar]['y']) / m_tot + z_com = np.sum(A2_picle[sel_tar]['m'] * A2_picle[sel_tar]['z']) / m_tot + + # Change origin to the centre-of-mass + A2_picle['x'] -= x_com + A2_picle['y'] -= y_com + A2_picle['z'] -= z_com + A2_picle['r'] = np.sqrt( + A2_picle['x']**2 + A2_picle['y']**2 + A2_picle['z']**2 + ) + + # Centre of momentum + v_x_com = np.sum(A2_picle[sel_tar]['m'] * A2_picle[sel_tar]['v_x']) / m_tot + v_y_com = np.sum(A2_picle[sel_tar]['m'] * A2_picle[sel_tar]['v_y']) / m_tot + v_z_com = np.sum(A2_picle[sel_tar]['m'] * A2_picle[sel_tar]['v_z']) / m_tot + + # Change to the centre-of-momentum frame of reference + A2_picle['v_x'] -= v_x_com + A2_picle['v_y'] -= v_y_com + A2_picle['v_z'] -= v_z_com + + return A2_picle + + +def plot_snapshot(A2_picle, filename, time, ax_lim=100, dz=0.1): + """ Plot the snapshot particles and save the figure. + """ + # Add extension if needed + if (filename[-5:] != ".png"): + filename += ".png" + + fig = plt.figure(figsize=(9, 9)) + ax = fig.add_subplot(111, aspect='equal') + + # Plot slices in z below zero + for z in np.arange(-ax_lim, 0, dz): + sel_z = np.where((z < A2_picle['z']) & (A2_picle['z'] < z+dz))[0] + A2_picle_z = A2_picle[sel_z] + + # Plot each material + for mat_ID, colour in Di_mat_colour.iteritems(): + sel_col = np.where(A2_picle_z['mat_ID'] == mat_ID)[0] + + ax.scatter( + A2_picle_z[sel_col]['x'], A2_picle_z[sel_col]['y'], + c=colour, edgecolors='none', marker='.', s=50, alpha=0.7 + ) + + # Axes etc. + ax.set_axis_bgcolor('k') + + ax.set_xlabel("x Position ($R_\oplus$)") + ax.set_ylabel("y Position ($R_\oplus$)") + + ax.set_xlim(-ax_lim, ax_lim) + ax.set_ylim(-ax_lim, ax_lim) + + plt.text( + -0.92*ax_lim, 0.85*ax_lim, "%.1f h" % (time*s_to_hour), fontsize=20, + color='w' + ) + + # Font sizes + for item in ( + [ax.title, ax.xaxis.label, ax.yaxis.label] + ax.get_xticklabels() + + ax.get_yticklabels() + ): + item.set_fontsize(20) + + plt.tight_layout() + + plt.savefig(filename) + plt.close() + + +if __name__ == '__main__': + # Sys args + try: + time_end = int(sys.argv[1]) + + try: + delta_time = int(sys.argv[2]) + except IndexError: + delta_time = 100 + except IndexError: + time_end = 100000 + delta_time = 100 + + # Load and plot each snapshot + for i_snap in range(int(time_end/delta_time) + 1): + snap_time = i_snap * delta_time + print "\rPlotting snapshot %06d (%d of %d)" % ( + snap_time, i_snap+1, int(time_end/delta_time) + ), + sys.stdout.flush() + + # Load particle data + filename = "%s%06d" % (file_snap, snap_time) + A2_picle = load_snapshot(filename) + + # Process particle data + A2_picle = process_particles(A2_picle, num_target) + + # Plot particles + filename = "%s%06d" % (file_plot, snap_time) + plot_snapshot(A2_picle, filename, snap_time) + + # Animation + command = ( + "ffmpeg -framerate 12 -i plots/moon_forming_impact_%*.png -r 25 " + "anim.mpg -y" + ) + print "\n%s\n" % command + subprocess.check_output(command, shell=True) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/MoonFormingImpact/run.sh b/examples/MoonFormingImpact/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..165dae3a24a9c30960959fbb37aa6e1da2eb851f --- /dev/null +++ b/examples/MoonFormingImpact/run.sh @@ -0,0 +1,2 @@ +#!/bin/bash +../swift -G -s -t 8 moon_forming_impact.yml diff --git a/examples/UranusImpact/README.md b/examples/UranusImpact/README.md new file mode 100644 index 0000000000000000000000000000000000000000..178a3937ecbe527df8e8e82a0d8fd8bcbf9dbef7 --- /dev/null +++ b/examples/UranusImpact/README.md @@ -0,0 +1,40 @@ +Uranus Giant Impact +=================== + +A simple version of the low angular momentum impact onto the early Uranus shown +in Kegerreis et al. (2018), Fig. 2; with only ~10,000 particles for a quick and +crude simulation. + +The collision of a 2 Earth mass impactor onto a proto-Uranus that can explain +the spin of the present-day planet, with an angular momentum of 2e36 kg m^2 s^-1 +and velocity at infinity of 5 km s^-1 for a relatively head-on impact. + +Both bodies have a rocky core and icy mantle, with a hydrogen-helium atmosphere +on the target as well. Although with this low number of particles it cannot be +modelled in any detail. + +Setup +----- + +In `swiftsim/`: + +`$ ./configure --with-hydro=minimal-multi-mat --with-equation-of-state=planetary` + +`$ make` + +In `swiftsim/examples/UranusImpact/`: + +`$ ./get_init_cond.sh` + +Run +--- + +`$ ./run.sh` + +Analysis +-------- + +`$ python plot.py` + +`$ mplayer anim.mpg` + diff --git a/examples/UranusImpact/get_init_cond.sh b/examples/UranusImpact/get_init_cond.sh new file mode 100755 index 0000000000000000000000000000000000000000..e12e009adfbd727cb2452ac21c477b3ecd77b9c9 --- /dev/null +++ b/examples/UranusImpact/get_init_cond.sh @@ -0,0 +1,2 @@ +#!/bin/bash +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/uranus_impact.hdf5 diff --git a/examples/UranusImpact/plot.py b/examples/UranusImpact/plot.py new file mode 100644 index 0000000000000000000000000000000000000000..3db3bf21bb15862ec524a069c38e47564b48df1d --- /dev/null +++ b/examples/UranusImpact/plot.py @@ -0,0 +1,291 @@ +""" +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2018 Jacob Kegerreis (jacob.kegerreis@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/>. +# +############################################################################### + +Plotting script for the Uranus Giant Impact example. + +Save a figure for each snapshot in `./plots/` then make them into a simple +animation with ffmpeg in `./`. + +The snapshot plots show all particles with z < 0, coloured by their material. + +Usage: + `$ python plot.py time_end delta_time` + + Sys args: + + `time_end` | (opt) int | The time of the last snapshot to plot. + Default = 100000 + + `delta_time` | (opt) int | The time between successive snapshots. + Default = 500 +""" + +from __future__ import division +import numpy as np +import matplotlib +import matplotlib.pyplot as plt +import h5py +import sys +import subprocess + +# Particle array fields +dtype_picle = [ + ('m', float), ('x', float), ('y', float), ('z', float), ('v_x', float), + ('v_y', float), ('v_z', float), ('ID', int), ('rho', float), ('u', float), + ('phi', float), ('P', float), ('h', float), ('mat_ID', int), ('r', float) + ] + +s_to_hour = 1 / 60**2 +R_Ea = 6.371e6 + +# Default sys args +time_end_default = 100000 +delta_time_default = 500 + +# Snapshot info +file_snap = "./snapshots/uranus_impact_" +file_plot = "./plots/uranus_impact_" + +# Number of particles in the target body +num_target = 8992 + +# Material types (copied from src/equation_of_state/planetary/equation_of_state.h) +type_factor = 100 +Di_type = { + 'Til' : 1, + 'HM80' : 2, + 'ANEOS' : 3, + 'SESAME' : 4, +} +Di_material = { + # Tillotson + 'Til_iron' : Di_type['Til']*type_factor, + 'Til_granite' : Di_type['Til']*type_factor + 1, + 'Til_water' : Di_type['Til']*type_factor + 2, + # Hubbard & MacFarlane (1980) Uranus/Neptune + 'HM80_HHe' : Di_type['HM80']*type_factor, # Hydrogen-helium atmosphere + 'HM80_ice' : Di_type['HM80']*type_factor + 1, # H20-CH4-NH3 ice mix + 'HM80_rock' : Di_type['HM80']*type_factor + 2, # SiO2-MgO-FeS-FeO rock mix + # ANEOS + 'ANEOS_iron' : Di_type['ANEOS']*type_factor, + 'MANEOS_forsterite' : Di_type['ANEOS']*type_factor + 1, + # SESAME + 'SESAME_iron' : Di_type['SESAME']*type_factor, +} + +# Material offset for impactor particles +ID_imp = 10000 +# Material colours +Di_mat_colour = { + # Target + Di_material['HM80_HHe'] : '#33DDFF', + Di_material['HM80_ice'] : 'lightsteelblue', + Di_material['HM80_rock'] : 'slategrey', + # Impactor + Di_material['HM80_ice'] + ID_imp : '#A080D0', + Di_material['HM80_rock'] + ID_imp : '#706050', + } + + +def load_snapshot(filename): + """ Load the hdf5 snapshot file and return the structured particle array. + """ + # Add extension if needed + if (filename[-5:] != ".hdf5"): + filename += ".hdf5" + + # Load the hdf5 file + with h5py.File(filename, 'r') as f: + header = f['Header'].attrs + A2_pos = f['PartType0/Coordinates'].value + A2_vel = f['PartType0/Velocities'].value + + # Structured array of all particle data + A2_picle = np.empty(header['NumPart_Total'][0], + dtype=dtype_picle) + + A2_picle['x'] = A2_pos[:, 0] + A2_picle['y'] = A2_pos[:, 1] + A2_picle['z'] = A2_pos[:, 2] + A2_picle['v_x'] = A2_vel[:, 0] + A2_picle['v_y'] = A2_vel[:, 1] + A2_picle['v_z'] = A2_vel[:, 2] + A2_picle['m'] = f['PartType0/Masses'].value + A2_picle['ID'] = f['PartType0/ParticleIDs'].value + A2_picle['rho'] = f['PartType0/Density'].value + A2_picle['u'] = f['PartType0/InternalEnergy'].value + A2_picle['phi'] = f['PartType0/Potential'].value + A2_picle['P'] = f['PartType0/Pressure'].value + A2_picle['h'] = f['PartType0/SmoothingLength'].value + A2_picle['mat_ID'] = f['PartType0/MaterialID'].value + + return A2_picle + + +def process_particles(A2_picle, num_target): + """ Modify things like particle units, material IDs, and coordinate origins. + """ + # Offset material IDs for impactor particles + A2_picle['mat_ID'][A2_picle['ID'] >= num_target] += ID_imp + + # Shift coordinates to the centre of the target's core's mass and momentum + sel_tar = np.where(A2_picle['mat_ID'] == Di_material['HM80_rock'])[0] + + # Centre of mass + m_tot = np.sum(A2_picle[sel_tar]['m']) + x_com = np.sum(A2_picle[sel_tar]['m'] * A2_picle[sel_tar]['x']) / m_tot + y_com = np.sum(A2_picle[sel_tar]['m'] * A2_picle[sel_tar]['y']) / m_tot + z_com = np.sum(A2_picle[sel_tar]['m'] * A2_picle[sel_tar]['z']) / m_tot + + # Change origin to the centre-of-mass + A2_picle['x'] -= x_com + A2_picle['y'] -= y_com + A2_picle['z'] -= z_com + A2_picle['r'] = np.sqrt( + A2_picle['x']**2 + A2_picle['y']**2 + A2_picle['z']**2 + ) + + # Centre of momentum + v_x_com = np.sum(A2_picle[sel_tar]['m'] * A2_picle[sel_tar]['v_x']) / m_tot + v_y_com = np.sum(A2_picle[sel_tar]['m'] * A2_picle[sel_tar]['v_y']) / m_tot + v_z_com = np.sum(A2_picle[sel_tar]['m'] * A2_picle[sel_tar]['v_z']) / m_tot + + # Change to the centre-of-momentum frame of reference + A2_picle['v_x'] -= v_x_com + A2_picle['v_y'] -= v_y_com + A2_picle['v_z'] -= v_z_com + + return A2_picle + + +def plot_snapshot(A2_picle, filename, time, ax_lim=13, dz=0.1): + """ Plot the snapshot particles and save the figure. + """ + # Add extension if needed + if (filename[-5:] != ".png"): + filename += ".png" + + fig = plt.figure(figsize=(9, 9)) + ax = fig.add_subplot(111, aspect='equal') + + # Plot slices in z below zero + for z in np.arange(-ax_lim, 0, dz): + sel_z = np.where((z < A2_picle['z']) & (A2_picle['z'] < z+dz))[0] + A2_picle_z = A2_picle[sel_z] + + # Plot each material + for mat_ID, colour in Di_mat_colour.iteritems(): + sel_col = np.where(A2_picle_z['mat_ID'] == mat_ID)[0] + + ax.scatter( + A2_picle_z[sel_col]['x'], A2_picle_z[sel_col]['y'], + c=colour, edgecolors='none', marker='.', s=50, alpha=0.7 + ) + + # Axes etc. + ax.set_axis_bgcolor('k') + + ax.set_xlabel("x Position ($R_\oplus$)") + ax.set_ylabel("y Position ($R_\oplus$)") + + ax.set_xlim(-ax_lim, ax_lim) + ax.set_ylim(-ax_lim, ax_lim) + + plt.text( + -0.92*ax_lim, 0.85*ax_lim, "%.1f h" % (time*s_to_hour), fontsize=20, + color='w' + ) + + # Font sizes + for item in ( + [ax.title, ax.xaxis.label, ax.yaxis.label] + ax.get_xticklabels() + + ax.get_yticklabels() + ): + item.set_fontsize(20) + + plt.tight_layout() + + plt.savefig(filename) + plt.close() + + +if __name__ == '__main__': + # Sys args + try: + time_end = int(sys.argv[1]) + try: + delta_time = int(sys.argv[2]) + except IndexError: + delta_time = delta_time_default + except IndexError: + time_end = time_end_default + delta_time = delta_time_default + + # Load and plot each snapshot + for i_snap in range(int(time_end/delta_time) + 1): + snap_time = i_snap * delta_time + print "\rPlotting snapshot %06d (%d of %d)" % ( + snap_time, i_snap+1, int(time_end/delta_time) + ), + sys.stdout.flush() + + # Load particle data + filename = "%s%06d" % (file_snap, snap_time) + A2_picle = load_snapshot(filename) + + # Process particle data + A2_picle = process_particles(A2_picle, num_target) + + # Plot particles + filename = "%s%06d" % (file_plot, snap_time) + plot_snapshot(A2_picle, filename, snap_time) + + # Animation + command = ( + "ffmpeg -framerate 10 -i plots/uranus_impact_%*.png -r 25 anim.mpg -y" + ) + print "\n$ %s\n" % command + subprocess.call(command, shell=True) + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/UranusImpact/run.sh b/examples/UranusImpact/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..c6773b7e40fff3fa312dfcb5ba4ada9d9e4b1b8d --- /dev/null +++ b/examples/UranusImpact/run.sh @@ -0,0 +1,2 @@ +#!/bin/bash +../swift -G -s -t 8 uranus_impact.yml diff --git a/examples/UranusImpact/uranus_impact.yml b/examples/UranusImpact/uranus_impact.yml new file mode 100644 index 0000000000000000000000000000000000000000..aae9f66847a9f9b55984ea5d2ea1c79099c01e95 --- /dev/null +++ b/examples/UranusImpact/uranus_impact.yml @@ -0,0 +1,43 @@ +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 5.9724e27 # Grams + UnitLength_in_cgs: 6.371e8 # Centimeters + UnitVelocity_in_cgs: 6.371e8 # 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: 100000 # The end time of the simulation (in internal units). + dt_min: 0.001 # The minimal time-step size of the simulation (in internal units). + dt_max: 100 # The maximal time-step size of the simulation (in internal units). + +# Parameters governing the snapshots +Snapshots: + # Common part of the name of output files + basename: snapshots/uranus_impact + time_first: 0 # Time of the first output (in internal units) + delta_time: 500 # Time difference between consecutive outputs (in internal units) + label_delta: 500 # Integer increment between snapshot output labels + +# Parameters governing the conserved quantities statistics +Statistics: + delta_time: 1000 # 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. + CFL_condition: 0.2 # Courant-Friedrich-Levy condition for time integration. + +# Parameters for the self-gravity scheme +Gravity: + eta: 0.025 # Constant dimensionless multiplier for time integration. + theta: 0.7 # Opening angle (Multipole acceptance criterion) + comoving_softening: 0.01 # Comoving softening length (in internal units). + max_physical_softening: 0.01 # Physical softening length (in internal units). + +# Parameters related to the initial conditions +InitialConditions: + file_name: uranus_impact.hdf5 # The initial conditions file to read diff --git a/src/debug.c b/src/debug.c index 4000625a5e7d1bc83600a3f20a0db160057f07a6..bf167e9a2954ed7b81588bc2aacf8e3b2b8cbe09 100644 --- a/src/debug.c +++ b/src/debug.c @@ -52,6 +52,8 @@ #include "./hydro/Gizmo/hydro_debug.h" #elif defined(SHADOWFAX_SPH) #include "./hydro/Shadowswift/hydro_debug.h" +#elif defined(MINIMAL_MULTI_MAT_SPH) +#include "./hydro/MinimalMultiMat/hydro_debug.h" #else #error "Invalid choice of SPH variant" #endif diff --git a/src/equation_of_state.h b/src/equation_of_state.h index 195b52514f2acc0c40959e09c088a06f0a411869..1fed652f354e01a89f5e0b0540d9d326a7750b02 100644 --- a/src/equation_of_state.h +++ b/src/equation_of_state.h @@ -34,6 +34,8 @@ #include "./equation_of_state/ideal_gas/equation_of_state.h" #elif defined(EOS_ISOTHERMAL_GAS) #include "./equation_of_state/isothermal/equation_of_state.h" +#elif defined(EOS_PLANETARY) +#include "./equation_of_state/planetary/equation_of_state.h" #else #error "Invalid choice of equation of state" #endif diff --git a/src/equation_of_state/planetary/aneos.h b/src/equation_of_state/planetary/aneos.h new file mode 100644 index 0000000000000000000000000000000000000000..904288b2fdf3ba825cdc7d114ebb61cd42de198d --- /dev/null +++ b/src/equation_of_state/planetary/aneos.h @@ -0,0 +1,144 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * 2018 Jacob Kegerreis (jacob.kegerreis@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_ANEOS_EQUATION_OF_STATE_H +#define SWIFT_ANEOS_EQUATION_OF_STATE_H + +/** + * @file equation_of_state/planetary/aneos.h + * + * Contains the (M)ANEOS EOS functions for + * equation_of_state/planetary/equation_of_state.h + * + * Adapted from the implementation in Gadget 2 of Cuk & Stewart (2012) + * + */ + +/* Some standard headers. */ +#include <math.h> + +/* Local headers. */ +#include "adiabatic_index.h" +#include "common_io.h" +#include "equation_of_state.h" +#include "inline.h" +#include "physical_constants.h" +#include "units.h" + +// ANEOS parameters +struct ANEOS_params { + enum eos_planetary_material_id mat_id; +}; + +// Parameter values for each material (cgs units) +INLINE static void set_ANEOS_iron(struct ANEOS_params *mat, + enum eos_planetary_material_id mat_id) { + mat->mat_id = mat_id; +} +INLINE static void set_MANEOS_forsterite( + struct ANEOS_params *mat, enum eos_planetary_material_id mat_id) { + mat->mat_id = mat_id; +} + +// Convert from cgs to internal units +INLINE static void convert_units_ANEOS(struct ANEOS_params *mat, + const struct unit_system *us) {} + +// gas_internal_energy_from_entropy +INLINE static float ANEOS_internal_energy_from_entropy( + float density, float entropy, const struct ANEOS_params *mat) { + + error("This EOS function is not yet implemented!"); + + return 0; +} + +// gas_pressure_from_entropy +INLINE static float ANEOS_pressure_from_entropy( + float density, float entropy, const struct ANEOS_params *mat) { + + error("This EOS function is not yet implemented!"); + + return 0; +} + +// gas_entropy_from_pressure +INLINE static float ANEOS_entropy_from_pressure( + float density, float pressure, const struct ANEOS_params *mat) { + + error("This EOS function is not yet implemented!"); + + return 0; +} + +// gas_soundspeed_from_entropy +INLINE static float ANEOS_soundspeed_from_entropy( + float density, float entropy, const struct ANEOS_params *mat) { + + error("This EOS function is not yet implemented!"); + + return 0; +} + +// gas_entropy_from_internal_energy +INLINE static float ANEOS_entropy_from_internal_energy( + float density, float u, const struct ANEOS_params *mat) { + + error("This EOS function is not yet implemented!"); + + return 0; +} + +// gas_pressure_from_internal_energy +INLINE static float ANEOS_pressure_from_internal_energy( + float density, float u, const struct ANEOS_params *mat) { + + error("This EOS function is not yet implemented!"); + + return 0; +} + +// gas_internal_energy_from_pressure +INLINE static float ANEOS_internal_energy_from_pressure( + float density, float P, const struct ANEOS_params *mat) { + + error("This EOS function is not yet implemented!"); + + return 0; +} + +// gas_soundspeed_from_internal_energy +INLINE static float ANEOS_soundspeed_from_internal_energy( + float density, float u, const struct ANEOS_params *mat) { + + error("This EOS function is not yet implemented!"); + + return 0; +} + +// gas_soundspeed_from_pressure +INLINE static float ANEOS_soundspeed_from_pressure( + float density, float P, const struct ANEOS_params *mat) { + + error("This EOS function is not yet implemented!"); + + return 0; +} + +#endif /* SWIFT_ANEOS_EQUATION_OF_STATE_H */ diff --git a/src/equation_of_state/planetary/equation_of_state.h b/src/equation_of_state/planetary/equation_of_state.h new file mode 100644 index 0000000000000000000000000000000000000000..fcc1c959152801253d465915d8b28b1552c913b8 --- /dev/null +++ b/src/equation_of_state/planetary/equation_of_state.h @@ -0,0 +1,1147 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * 2018 Jacob Kegerreis (jacob.kegerreis@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_PLANETARY_EQUATION_OF_STATE_H +#define SWIFT_PLANETARY_EQUATION_OF_STATE_H + +/** + * @file equation_of_state/planetary/equation_of_state.h + * + * For any/all of the planetary EOS. Each EOS type's functions are set in its + * own header file: `equation_of_state/planetary/<eos_type>.h`. + * See `material_id` for the available choices. + * + * Not all functions are implemented for all EOS types, so not all can be used + * with all hydro formulations yet. + */ + +/* Some standard headers. */ +#include <math.h> + +/* Local headers. */ +#include "adiabatic_index.h" +#include "common_io.h" +#include "inline.h" +#include "physical_constants.h" +#include "units.h" + +extern struct eos_parameters eos; + +/*! Material identifier flags (material_ID = type_ID * type_factor + unit_ID) */ +#define eos_planetary_type_factor 100 + +/** + * @brief Master type for the planetary equation of state. + */ +enum eos_planetary_type_id { + eos_planetary_type_Til = 1, + eos_planetary_type_HM80 = 2, + eos_planetary_type_ANEOS = 3, + eos_planetary_type_SESAME = 4, +} __attribute__((packed)); + +/** + * @brief Minor type for the planetary equation of state. + */ +enum eos_planetary_material_id { + + /* Tillotson */ + + /*! Tillotson iron */ + eos_planetary_id_Til_iron = + eos_planetary_type_Til * eos_planetary_type_factor, + + /*! Tillotson granite */ + eos_planetary_id_Til_granite = + eos_planetary_type_Til * eos_planetary_type_factor + 1, + + /*! Tillotson water */ + eos_planetary_id_Til_water = + eos_planetary_type_Til * eos_planetary_type_factor + 2, + + /* Hubbard & MacFarlane (1980) Uranus/Neptune */ + + /*! Hydrogen-helium atmosphere */ + eos_planetary_id_HM80_HHe = + eos_planetary_type_HM80 * eos_planetary_type_factor, + + /*! H20-CH4-NH3 ice mix */ + eos_planetary_id_HM80_ice = + eos_planetary_type_HM80 * eos_planetary_type_factor + 1, + + /*! SiO2-MgO-FeS-FeO rock mix */ + eos_planetary_id_HM80_rock = + eos_planetary_type_HM80 * eos_planetary_type_factor + 2, + + /* ANEOS */ + + /*! ANEOS iron */ + eos_planetary_id_ANEOS_iron = + eos_planetary_type_ANEOS * eos_planetary_type_factor, + + /*! ANEOS forsterite */ + eos_planetary_id_MANEOS_forsterite = + eos_planetary_type_ANEOS * eos_planetary_type_factor + 1, + + /* SESAME */ + + /*! SESAME iron */ + eos_planetary_id_SESAME_iron = + eos_planetary_type_SESAME * eos_planetary_type_factor, +} __attribute__((packed)); + +/* Individual EOS function headers. */ +#include "aneos.h" +#include "hm80.h" +#include "sesame.h" +#include "tillotson.h" + +/** + * @brief The parameters of the equation of state. + */ +struct eos_parameters { + struct Til_params Til_iron, Til_granite, Til_water; + struct HM80_params HM80_HHe, HM80_ice, HM80_rock; + struct ANEOS_params ANEOS_iron, MANEOS_forsterite; + struct SESAME_params SESAME_iron; +}; + +/** + * @brief Returns the internal energy given density and entropy + * + * @param density The density \f$\rho\f$. + * @param entropy The entropy \f$S\f$. + */ +__attribute__((always_inline)) INLINE static float +gas_internal_energy_from_entropy(float density, float entropy, + enum eos_planetary_material_id mat_id) { + + const enum eos_planetary_type_id type = mat_id / eos_planetary_type_factor; + + /* Select the material base type */ + switch (type) { + + /* Tillotson EoS */ + case eos_planetary_type_Til: + + /* Select the material */ + switch (mat_id) { + case eos_planetary_id_Til_iron: + return Til_internal_energy_from_entropy(density, entropy, + &eos.Til_iron); + break; + + case eos_planetary_id_Til_granite: + return Til_internal_energy_from_entropy(density, entropy, + &eos.Til_granite); + break; + + case eos_planetary_id_Til_water: + return Til_internal_energy_from_entropy(density, entropy, + &eos.Til_water); + break; + + default: + error("Unknown material ID! mat_id = %d", mat_id); + return 0.f; + }; + break; + + /* Hubbard & MacFarlane (1980) EoS */ + case eos_planetary_type_HM80: + + /* Select the material */ + switch (mat_id) { + case eos_planetary_id_HM80_HHe: + return HM80_internal_energy_from_entropy(density, entropy, + &eos.HM80_HHe); + break; + + case eos_planetary_id_HM80_ice: + return HM80_internal_energy_from_entropy(density, entropy, + &eos.HM80_ice); + break; + + case eos_planetary_id_HM80_rock: + return HM80_internal_energy_from_entropy(density, entropy, + &eos.HM80_rock); + break; + + default: + error("Unknown material ID! mat_id = %d", mat_id); + return 0.f; + }; + break; + + /* ANEOS EoS */ + case eos_planetary_type_ANEOS: + + /* Select the material */ + switch (mat_id) { + case eos_planetary_id_ANEOS_iron: + return ANEOS_internal_energy_from_entropy(density, entropy, + &eos.ANEOS_iron); + break; + + case eos_planetary_id_MANEOS_forsterite: + return ANEOS_internal_energy_from_entropy(density, entropy, + &eos.MANEOS_forsterite); + break; + + default: + error("Unknown material ID! mat_id = %d", mat_id); + return 0.f; + }; + break; + + /* SESAME EoS */ + case eos_planetary_type_SESAME:; + + /* Select the material */ + switch (mat_id) { + case eos_planetary_id_SESAME_iron: + return SESAME_internal_energy_from_entropy(density, entropy, + &eos.SESAME_iron); + break; + + default: + error("Unknown material ID! mat_id = %d", mat_id); + return 0.f; + }; + break; + + default: + error("Unknown material type! mat_id = %d", mat_id); + return 0.f; + } +} + +/** + * @brief Returns the pressure given density and entropy + * + * @param density The density \f$\rho\f$. + * @param entropy The entropy \f$S\f$. + */ +__attribute__((always_inline)) INLINE static float gas_pressure_from_entropy( + float density, float entropy, enum eos_planetary_material_id mat_id) { + + const enum eos_planetary_type_id type = mat_id / eos_planetary_type_factor; + + /* Select the material base type */ + switch (type) { + + /* Tillotson EoS */ + case eos_planetary_type_Til: + + /* Select the material */ + switch (mat_id) { + case eos_planetary_id_Til_iron: + return Til_pressure_from_entropy(density, entropy, &eos.Til_iron); + break; + + case eos_planetary_id_Til_granite: + return Til_pressure_from_entropy(density, entropy, &eos.Til_granite); + break; + + case eos_planetary_id_Til_water: + return Til_pressure_from_entropy(density, entropy, &eos.Til_water); + break; + + default: + error("Unknown material ID! mat_id = %d", mat_id); + return 0.f; + }; + break; + + /* Hubbard & MacFarlane (1980) EoS */ + case eos_planetary_type_HM80: + + /* Select the material */ + switch (mat_id) { + case eos_planetary_id_HM80_HHe: + return HM80_pressure_from_entropy(density, entropy, &eos.HM80_HHe); + break; + + case eos_planetary_id_HM80_ice: + return HM80_pressure_from_entropy(density, entropy, &eos.HM80_ice); + break; + + case eos_planetary_id_HM80_rock: + return HM80_pressure_from_entropy(density, entropy, &eos.HM80_rock); + break; + + default: + error("Unknown material ID! mat_id = %d", mat_id); + return 0.f; + }; + break; + + /* ANEOS EoS */ + case eos_planetary_type_ANEOS: + + /* Select the material */ + switch (mat_id) { + case eos_planetary_id_ANEOS_iron: + return ANEOS_pressure_from_entropy(density, entropy, &eos.ANEOS_iron); + break; + + case eos_planetary_id_MANEOS_forsterite: + return ANEOS_pressure_from_entropy(density, entropy, + &eos.MANEOS_forsterite); + break; + + default: + error("Unknown material ID! mat_id = %d", mat_id); + return 0.f; + }; + break; + + /* SESAME EoS */ + case eos_planetary_type_SESAME:; + + /* Select the material */ + switch (mat_id) { + case eos_planetary_id_SESAME_iron: + return SESAME_pressure_from_entropy(density, entropy, + &eos.SESAME_iron); + break; + + default: + error("Unknown material ID! mat_id = %d", mat_id); + return 0.f; + }; + break; + + default: + error("Unknown material type! mat_id = %d", mat_id); + return 0.f; + } +} + +/** + * @brief Returns the entropy given density and pressure. + * + * @param density The density \f$\rho\f$. + * @param pressure The pressure \f$P\f$. + * @return The entropy \f$A\f$. + */ +__attribute__((always_inline)) INLINE static float gas_entropy_from_pressure( + float density, float P, enum eos_planetary_material_id mat_id) { + + const enum eos_planetary_type_id type = mat_id / eos_planetary_type_factor; + + /* Select the material base type */ + switch (type) { + + /* Tillotson EoS */ + case eos_planetary_type_Til: + + /* Select the material */ + switch (mat_id) { + case eos_planetary_id_Til_iron: + return Til_entropy_from_pressure(density, P, &eos.Til_iron); + break; + + case eos_planetary_id_Til_granite: + return Til_entropy_from_pressure(density, P, &eos.Til_granite); + break; + + case eos_planetary_id_Til_water: + return Til_entropy_from_pressure(density, P, &eos.Til_water); + break; + + default: + error("Unknown material ID! mat_id = %d", mat_id); + return 0.f; + }; + break; + + /* Hubbard & MacFarlane (1980) EoS */ + case eos_planetary_type_HM80: + + /* Select the material */ + switch (mat_id) { + case eos_planetary_id_HM80_HHe: + return HM80_entropy_from_pressure(density, P, &eos.HM80_HHe); + break; + + case eos_planetary_id_HM80_ice: + return HM80_entropy_from_pressure(density, P, &eos.HM80_ice); + break; + + case eos_planetary_id_HM80_rock: + return HM80_entropy_from_pressure(density, P, &eos.HM80_rock); + break; + + default: + error("Unknown material ID! mat_id = %d", mat_id); + return 0.f; + }; + break; + + /* ANEOS EoS */ + case eos_planetary_type_ANEOS: + + /* Select the material */ + switch (mat_id) { + case eos_planetary_id_ANEOS_iron: + return ANEOS_entropy_from_pressure(density, P, &eos.ANEOS_iron); + break; + + case eos_planetary_id_MANEOS_forsterite: + return ANEOS_entropy_from_pressure(density, P, + &eos.MANEOS_forsterite); + break; + + default: + error("Unknown material ID! mat_id = %d", mat_id); + return 0.f; + }; + break; + + /* SESAME EoS */ + case eos_planetary_type_SESAME:; + + /* Select the material */ + switch (mat_id) { + case eos_planetary_id_SESAME_iron: + return SESAME_entropy_from_pressure(density, P, &eos.SESAME_iron); + break; + + default: + error("Unknown material ID! mat_id = %d", mat_id); + return 0.f; + }; + break; + + default: + error("Unknown material type! mat_id = %d", mat_id); + return 0.f; + } +} + +/** + * @brief Returns the sound speed given density and entropy + * + * @param density The density \f$\rho\f$. + * @param entropy The entropy \f$S\f$. + */ +__attribute__((always_inline)) INLINE static float gas_soundspeed_from_entropy( + float density, float entropy, enum eos_planetary_material_id mat_id) { + + const enum eos_planetary_type_id type = mat_id / eos_planetary_type_factor; + + /* Select the material base type */ + switch (type) { + + /* Tillotson EoS */ + case eos_planetary_type_Til: + + /* Select the material */ + switch (mat_id) { + case eos_planetary_id_Til_iron: + return Til_soundspeed_from_entropy(density, entropy, &eos.Til_iron); + break; + + case eos_planetary_id_Til_granite: + return Til_soundspeed_from_entropy(density, entropy, + &eos.Til_granite); + break; + + case eos_planetary_id_Til_water: + return Til_soundspeed_from_entropy(density, entropy, &eos.Til_water); + break; + + default: + error("Unknown material ID! mat_id = %d", mat_id); + return 0.f; + }; + break; + + /* Hubbard & MacFarlane (1980) EoS */ + case eos_planetary_type_HM80: + + /* Select the material */ + switch (mat_id) { + case eos_planetary_id_HM80_HHe: + return HM80_soundspeed_from_entropy(density, entropy, &eos.HM80_HHe); + break; + + case eos_planetary_id_HM80_ice: + return HM80_soundspeed_from_entropy(density, entropy, &eos.HM80_ice); + break; + + case eos_planetary_id_HM80_rock: + return HM80_soundspeed_from_entropy(density, entropy, &eos.HM80_rock); + break; + + default: + error("Unknown material ID! mat_id = %d", mat_id); + return 0.f; + }; + break; + + /* ANEOS EoS */ + case eos_planetary_type_ANEOS: + + /* Select the material */ + switch (mat_id) { + case eos_planetary_id_ANEOS_iron: + return ANEOS_soundspeed_from_entropy(density, entropy, + &eos.ANEOS_iron); + break; + + case eos_planetary_id_MANEOS_forsterite: + return ANEOS_soundspeed_from_entropy(density, entropy, + &eos.MANEOS_forsterite); + break; + + default: + error("Unknown material ID! mat_id = %d", mat_id); + return 0.f; + }; + break; + + /* SESAME EoS */ + case eos_planetary_type_SESAME:; + + /* Select the material */ + switch (mat_id) { + case eos_planetary_id_SESAME_iron: + return SESAME_soundspeed_from_entropy(density, entropy, + &eos.SESAME_iron); + break; + + default: + error("Unknown material ID! mat_id = %d", mat_id); + return 0.f; + }; + break; + + default: + error("Unknown material type! mat_id = %d", mat_id); + return 0.f; + } +} + +/** + * @brief Returns the entropy given density and internal energy + * + * @param density The density \f$\rho\f$ + * @param u The internal energy \f$u\f$ + */ +__attribute__((always_inline)) INLINE static float +gas_entropy_from_internal_energy(float density, float u, + enum eos_planetary_material_id mat_id) { + + const enum eos_planetary_type_id type = mat_id / eos_planetary_type_factor; + + /* Select the material base type */ + switch (type) { + + /* Tillotson EoS */ + case eos_planetary_type_Til: + + /* Select the material */ + switch (mat_id) { + case eos_planetary_id_Til_iron: + return Til_entropy_from_internal_energy(density, u, &eos.Til_iron); + break; + + case eos_planetary_id_Til_granite: + return Til_entropy_from_internal_energy(density, u, &eos.Til_granite); + break; + + case eos_planetary_id_Til_water: + return Til_entropy_from_internal_energy(density, u, &eos.Til_water); + break; + + default: + error("Unknown material ID! mat_id = %d", mat_id); + return 0.f; + }; + break; + + /* Hubbard & MacFarlane (1980) EoS */ + case eos_planetary_type_HM80: + + /* Select the material */ + switch (mat_id) { + case eos_planetary_id_HM80_HHe: + return HM80_entropy_from_internal_energy(density, u, &eos.HM80_HHe); + break; + + case eos_planetary_id_HM80_ice: + return HM80_entropy_from_internal_energy(density, u, &eos.HM80_ice); + break; + + case eos_planetary_id_HM80_rock: + return HM80_entropy_from_internal_energy(density, u, &eos.HM80_rock); + break; + + default: + error("Unknown material ID! mat_id = %d", mat_id); + return 0.f; + }; + break; + + /* ANEOS EoS */ + case eos_planetary_type_ANEOS: + + /* Select the material */ + switch (mat_id) { + case eos_planetary_id_ANEOS_iron: + return ANEOS_entropy_from_internal_energy(density, u, + &eos.ANEOS_iron); + break; + + case eos_planetary_id_MANEOS_forsterite: + return ANEOS_entropy_from_internal_energy(density, u, + &eos.MANEOS_forsterite); + break; + + default: + error("Unknown material ID! mat_id = %d", mat_id); + return 0.f; + }; + break; + + /* SESAME EoS */ + case eos_planetary_type_SESAME:; + + /* Select the material */ + switch (mat_id) { + case eos_planetary_id_SESAME_iron: + return SESAME_entropy_from_internal_energy(density, u, + &eos.SESAME_iron); + break; + + default: + error("Unknown material ID! mat_id = %d", mat_id); + return 0.f; + }; + break; + + default: + error("Unknown material type! mat_id = %d", mat_id); + return 0.f; + } +} + +/** + * @brief Returns the pressure given density and internal energy + * + * @param density The density \f$\rho\f$ + * @param u The internal energy \f$u\f$ + */ +__attribute__((always_inline)) INLINE static float +gas_pressure_from_internal_energy(float density, float u, + enum eos_planetary_material_id mat_id) { + + const enum eos_planetary_type_id type = mat_id / eos_planetary_type_factor; + + /* Select the material base type */ + switch (type) { + + /* Tillotson EoS */ + case eos_planetary_type_Til: + + /* Select the material */ + switch (mat_id) { + case eos_planetary_id_Til_iron: + return Til_pressure_from_internal_energy(density, u, &eos.Til_iron); + break; + + case eos_planetary_id_Til_granite: + return Til_pressure_from_internal_energy(density, u, + &eos.Til_granite); + break; + + case eos_planetary_id_Til_water: + return Til_pressure_from_internal_energy(density, u, &eos.Til_water); + break; + + default: + error("Unknown material ID! mat_id = %d", mat_id); + return 0.f; + }; + break; + + /* Hubbard & MacFarlane (1980) EoS */ + case eos_planetary_type_HM80: + + /* Select the material */ + switch (mat_id) { + case eos_planetary_id_HM80_HHe: + return HM80_pressure_from_internal_energy(density, u, &eos.HM80_HHe); + break; + + case eos_planetary_id_HM80_ice: + return HM80_pressure_from_internal_energy(density, u, &eos.HM80_ice); + break; + + case eos_planetary_id_HM80_rock: + return HM80_pressure_from_internal_energy(density, u, &eos.HM80_rock); + break; + + default: + error("Unknown material ID! mat_id = %d", mat_id); + return 0.f; + }; + break; + + /* ANEOS EoS */ + case eos_planetary_type_ANEOS: + + /* Select the material */ + switch (mat_id) { + case eos_planetary_id_ANEOS_iron: + return ANEOS_pressure_from_internal_energy(density, u, + &eos.ANEOS_iron); + break; + + case eos_planetary_id_MANEOS_forsterite: + return ANEOS_pressure_from_internal_energy(density, u, + &eos.MANEOS_forsterite); + break; + + default: + error("Unknown material ID! mat_id = %d", mat_id); + return 0.f; + }; + break; + + /* SESAME EoS */ + case eos_planetary_type_SESAME:; + + /* Select the material */ + switch (mat_id) { + case eos_planetary_id_SESAME_iron: + return SESAME_pressure_from_internal_energy(density, u, + &eos.SESAME_iron); + break; + + default: + error("Unknown material ID! mat_id = %d", mat_id); + return 0.f; + }; + break; + + default: + error("Unknown material type! mat_id = %d", mat_id); + return 0.f; + } +} + +/** + * @brief Returns the internal energy given density and pressure. + * + * NOT IMPLEMENTED! + * + * @param density The density \f$\rho\f$. + * @param pressure The pressure \f$P\f$. + * @return The internal energy \f$u\f$. + */ +__attribute__((always_inline)) INLINE static float +gas_internal_energy_from_pressure(float density, float P, + enum eos_planetary_material_id mat_id) { + + const enum eos_planetary_type_id type = mat_id / eos_planetary_type_factor; + + /* Select the material base type */ + switch (type) { + + /* Tillotson EoS */ + case eos_planetary_type_Til: + + /* Select the material */ + switch (mat_id) { + case eos_planetary_id_Til_iron: + return Til_internal_energy_from_pressure(density, P, &eos.Til_iron); + break; + + case eos_planetary_id_Til_granite: + return Til_internal_energy_from_pressure(density, P, + &eos.Til_granite); + break; + + case eos_planetary_id_Til_water: + return Til_internal_energy_from_pressure(density, P, &eos.Til_water); + break; + + default: + error("Unknown material ID! mat_id = %d", mat_id); + return 0.f; + }; + break; + + /* Hubbard & MacFarlane (1980) EoS */ + case eos_planetary_type_HM80: + + /* Select the material */ + switch (mat_id) { + case eos_planetary_id_HM80_HHe: + return HM80_internal_energy_from_pressure(density, P, &eos.HM80_HHe); + break; + + case eos_planetary_id_HM80_ice: + return HM80_internal_energy_from_pressure(density, P, &eos.HM80_ice); + break; + + case eos_planetary_id_HM80_rock: + return HM80_internal_energy_from_pressure(density, P, &eos.HM80_rock); + break; + + default: + error("Unknown material ID! mat_id = %d", mat_id); + return 0.f; + }; + break; + + /* ANEOS EoS */ + case eos_planetary_type_ANEOS: + + /* Select the material */ + switch (mat_id) { + case eos_planetary_id_ANEOS_iron: + return ANEOS_internal_energy_from_pressure(density, P, + &eos.ANEOS_iron); + break; + + case eos_planetary_id_MANEOS_forsterite: + return ANEOS_internal_energy_from_pressure(density, P, + &eos.MANEOS_forsterite); + break; + + default: + error("Unknown material ID! mat_id = %d", mat_id); + return 0.f; + }; + break; + + /* SESAME EoS */ + case eos_planetary_type_SESAME:; + + /* Select the material */ + switch (mat_id) { + case eos_planetary_id_SESAME_iron: + return SESAME_internal_energy_from_pressure(density, P, + &eos.SESAME_iron); + break; + + default: + error("Unknown material ID! mat_id = %d", mat_id); + return 0.f; + }; + break; + + default: + error("Unknown material type! mat_id = %d", mat_id); + return 0.f; + } +} + +/** + * @brief Returns the sound speed given density and internal energy + * + * @param density The density \f$\rho\f$ + * @param u The internal energy \f$u\f$ + */ +__attribute__((always_inline)) INLINE static float +gas_soundspeed_from_internal_energy(float density, float u, + enum eos_planetary_material_id mat_id) { + + const enum eos_planetary_type_id type = mat_id / eos_planetary_type_factor; + + /* Select the material base type */ + switch (type) { + + /* Tillotson EoS */ + case eos_planetary_type_Til: + + /* Select the material */ + switch (mat_id) { + case eos_planetary_id_Til_iron: + return Til_soundspeed_from_internal_energy(density, u, &eos.Til_iron); + break; + + case eos_planetary_id_Til_granite: + return Til_soundspeed_from_internal_energy(density, u, + &eos.Til_granite); + break; + + case eos_planetary_id_Til_water: + return Til_soundspeed_from_internal_energy(density, u, + &eos.Til_water); + break; + + default: + error("Unknown material ID! mat_id = %d", mat_id); + return 0.f; + }; + break; + + /* Hubbard & MacFarlane (1980) EoS */ + case eos_planetary_type_HM80: + + /* Select the material */ + switch (mat_id) { + case eos_planetary_id_HM80_HHe: + return HM80_soundspeed_from_internal_energy(density, u, + &eos.HM80_HHe); + break; + + case eos_planetary_id_HM80_ice: + return HM80_soundspeed_from_internal_energy(density, u, + &eos.HM80_ice); + break; + + case eos_planetary_id_HM80_rock: + return HM80_soundspeed_from_internal_energy(density, u, + &eos.HM80_rock); + break; + + default: + error("Unknown material ID! mat_id = %d", mat_id); + return 0.f; + }; + break; + + /* ANEOS EoS */ + case eos_planetary_type_ANEOS: + + /* Select the material */ + switch (mat_id) { + case eos_planetary_id_ANEOS_iron: + return ANEOS_soundspeed_from_internal_energy(density, u, + &eos.ANEOS_iron); + break; + + case eos_planetary_id_MANEOS_forsterite: + return ANEOS_soundspeed_from_internal_energy(density, u, + &eos.MANEOS_forsterite); + break; + + default: + error("Unknown material ID! mat_id = %d", mat_id); + return 0.f; + }; + break; + + /* SESAME EoS */ + case eos_planetary_type_SESAME:; + + /* Select the material */ + switch (mat_id) { + case eos_planetary_id_SESAME_iron: + return SESAME_soundspeed_from_internal_energy(density, u, + &eos.SESAME_iron); + break; + + default: + error("Unknown material ID! mat_id = %d", mat_id); + return 0.f; + }; + break; + + default: + error("Unknown material type! mat_id = %d", mat_id); + return 0.f; + } +} + +/** + * @brief Returns the sound speed given density and pressure + * + * @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, enum eos_planetary_material_id mat_id) { + + const enum eos_planetary_type_id type = mat_id / eos_planetary_type_factor; + + /* Select the material base type */ + switch (type) { + + /* Tillotson EoS */ + case eos_planetary_type_Til: + + /* Select the material */ + switch (mat_id) { + case eos_planetary_id_Til_iron: + return Til_soundspeed_from_pressure(density, P, &eos.Til_iron); + break; + + case eos_planetary_id_Til_granite: + return Til_soundspeed_from_pressure(density, P, &eos.Til_granite); + break; + + case eos_planetary_id_Til_water: + return Til_soundspeed_from_pressure(density, P, &eos.Til_water); + break; + + default: + error("Unknown material ID! mat_id = %d", mat_id); + return 0.f; + }; + break; + + /* Hubbard & MacFarlane (1980) EoS */ + case eos_planetary_type_HM80: + + /* Select the material */ + switch (mat_id) { + case eos_planetary_id_HM80_HHe: + return HM80_soundspeed_from_pressure(density, P, &eos.HM80_HHe); + break; + + case eos_planetary_id_HM80_ice: + return HM80_soundspeed_from_pressure(density, P, &eos.HM80_ice); + break; + + case eos_planetary_id_HM80_rock: + return HM80_soundspeed_from_pressure(density, P, &eos.HM80_rock); + break; + + default: + error("Unknown material ID! mat_id = %d", mat_id); + return 0.f; + }; + break; + + /* ANEOS EoS */ + case eos_planetary_type_ANEOS: + + /* Select the material */ + switch (mat_id) { + case eos_planetary_id_ANEOS_iron: + return ANEOS_soundspeed_from_pressure(density, P, &eos.ANEOS_iron); + break; + + case eos_planetary_id_MANEOS_forsterite: + return ANEOS_soundspeed_from_pressure(density, P, + &eos.MANEOS_forsterite); + break; + + default: + error("Unknown material ID! mat_id = %d", mat_id); + return 0.f; + }; + break; + + /* SESAME EoS */ + case eos_planetary_type_SESAME:; + + /* Select the material */ + switch (mat_id) { + case eos_planetary_id_SESAME_iron: + return SESAME_soundspeed_from_pressure(density, P, &eos.SESAME_iron); + break; + + default: + error("Unknown material ID! mat_id = %d", mat_id); + return 0.f; + }; + break; + + default: + error("Unknown material type! mat_id = %d", mat_id); + return 0.f; + } +} + +/** + * @brief Initialize the eos parameters + * + * @param e The #eos_parameters + * @param params The parsed parameters + */ +__attribute__((always_inline)) INLINE static void eos_init( + struct eos_parameters *e, const struct phys_const *phys_const, + const struct unit_system *us, const struct swift_params *params) { + + // Set the parameters and material IDs, load tables, etc. for each material + // Tillotson + set_Til_iron(&e->Til_iron, eos_planetary_id_Til_iron); + set_Til_granite(&e->Til_granite, eos_planetary_id_Til_granite); + set_Til_water(&e->Til_water, eos_planetary_id_Til_water); + + // Hubbard & MacFarlane (1980) + set_HM80_HHe(&e->HM80_HHe, eos_planetary_id_HM80_HHe); + set_HM80_ice(&e->HM80_ice, eos_planetary_id_HM80_ice); + set_HM80_rock(&e->HM80_rock, eos_planetary_id_HM80_rock); + + load_HM80_table(&e->HM80_HHe, HM80_HHe_table_file); + load_HM80_table(&e->HM80_ice, HM80_ice_table_file); + load_HM80_table(&e->HM80_rock, HM80_rock_table_file); + + // ANEOS + set_ANEOS_iron(&e->ANEOS_iron, eos_planetary_id_ANEOS_iron); + set_MANEOS_forsterite(&e->MANEOS_forsterite, + eos_planetary_id_MANEOS_forsterite); + + // SESAME + set_SESAME_iron(&e->SESAME_iron, eos_planetary_id_SESAME_iron); + + // Convert to internal units + // Tillotson + convert_units_Til(&e->Til_iron, us); + convert_units_Til(&e->Til_granite, us); + convert_units_Til(&e->Til_water, us); + + // Hubbard & MacFarlane (1980) + convert_units_HM80(&e->HM80_HHe, us); + convert_units_HM80(&e->HM80_ice, us); + convert_units_HM80(&e->HM80_rock, us); + + // ANEOS + convert_units_ANEOS(&e->ANEOS_iron, us); + convert_units_ANEOS(&e->MANEOS_forsterite, us); + + // SESAME + convert_units_SESAME(&e->SESAME_iron, us); +} + +/** + * @brief Print the equation of state + * + * @param e The #eos_parameters + */ +__attribute__((always_inline)) INLINE static void eos_print( + const struct eos_parameters *e) { + + message("Equation of state: Planetary."); +} + +#if defined(HAVE_HDF5) +/** + * @brief Write equation of state information to the snapshot + * + * @param h_grpsph The HDF5 group in which to write + * @param e The #eos_parameters + */ +__attribute__((always_inline)) INLINE static void eos_print_snapshot( + hid_t h_grpsph, const struct eos_parameters *e) { + + io_write_attribute_s(h_grpsph, "Equation of state", "Planetary"); +} +#endif + +#endif /* SWIFT_PLANETARY_EQUATION_OF_STATE_H */ diff --git a/src/equation_of_state/planetary/hm80.h b/src/equation_of_state/planetary/hm80.h new file mode 100644 index 0000000000000000000000000000000000000000..b0669c129b2b4d642ae27e58f2f2bec221e3df9b --- /dev/null +++ b/src/equation_of_state/planetary/hm80.h @@ -0,0 +1,306 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * 2018 Jacob Kegerreis (jacob.kegerreis@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_HUBBARD_MACFARLANE_EQUATION_OF_STATE_H +#define SWIFT_HUBBARD_MACFARLANE_EQUATION_OF_STATE_H + +/** + * @file equation_of_state/planetary/hm80.h + * + * Contains the Hubbard & MacFarlane (1980) Uranus/Neptune EOS functions for + * equation_of_state/planetary/equation_of_state.h + * + */ + +/* Some standard headers. */ +#include <math.h> + +/* Local headers. */ +#include "adiabatic_index.h" +#include "common_io.h" +#include "equation_of_state.h" +#include "inline.h" +#include "physical_constants.h" +#include "units.h" + +// Hubbard & MacFarlane (1980) parameters +struct HM80_params { + float **table_P_rho_u; + int num_rho, num_u; + float log_rho_min, log_rho_max, log_rho_step, inv_log_rho_step, log_u_min, + log_u_max, log_u_step, inv_log_u_step, bulk_mod; + enum eos_planetary_material_id mat_id; +}; + +// Table file names +/// to be read in from the parameter file instead once finished testing... +#define HM80_HHe_table_file "/gpfs/data/dc-kege1/gihr_data/P_rho_u_HHe.txt" +#define HM80_ice_table_file "/gpfs/data/dc-kege1/gihr_data/P_rho_u_ice.txt" +#define HM80_rock_table_file "/gpfs/data/dc-kege1/gihr_data/P_rho_u_roc.txt" + +// Parameter values for each material (cgs units) +INLINE static void set_HM80_HHe(struct HM80_params *mat, + enum eos_planetary_material_id mat_id) { + mat->mat_id = mat_id; + mat->num_rho = 100; + mat->num_u = 100; + mat->log_rho_min = -9.2103404f; + mat->log_rho_max = 1.6094379f; + mat->log_rho_step = 0.1092907f; + mat->log_u_min = 9.2103404f; + mat->log_u_max = 22.3327037f; + mat->log_u_step = 0.1325491f; + mat->bulk_mod = 0; + + mat->inv_log_rho_step = 1.f / mat->log_rho_step; + mat->inv_log_u_step = 1.f / mat->log_u_step; +} +INLINE static void set_HM80_ice(struct HM80_params *mat, + enum eos_planetary_material_id mat_id) { + mat->mat_id = mat_id; + mat->num_rho = 200; + mat->num_u = 200; + mat->log_rho_min = -6.9077553f; + mat->log_rho_max = 2.7080502f; + mat->log_rho_step = 0.0483206f; + mat->log_u_min = 6.9077553f; + mat->log_u_max = 22.3327037f; + mat->log_u_step = 0.0775123f; + mat->bulk_mod = 2.0e10f; + + mat->inv_log_rho_step = 1.f / mat->log_rho_step; + mat->inv_log_u_step = 1.f / mat->log_u_step; +} +INLINE static void set_HM80_rock(struct HM80_params *mat, + enum eos_planetary_material_id mat_id) { + mat->mat_id = mat_id; + mat->num_rho = 100; + mat->num_u = 100; + mat->log_rho_min = -6.9077553f; + mat->log_rho_max = 2.9957323f; + mat->log_rho_step = 0.1000352f; + mat->log_u_min = 9.2103404f; + mat->log_u_max = 20.7232658f; + mat->log_u_step = 0.1162922f; + mat->bulk_mod = 3.49e11f; + + mat->inv_log_rho_step = 1.f / mat->log_rho_step; + mat->inv_log_u_step = 1.f / mat->log_u_step; +} + +// Read the table from file +INLINE static void load_HM80_table(struct HM80_params *mat, char *table_file) { + // Allocate table memory + mat->table_P_rho_u = (float **)malloc(mat->num_rho * sizeof(float *)); + for (int i = 0; i < mat->num_rho; i++) { + mat->table_P_rho_u[i] = (float *)malloc(mat->num_u * sizeof(float)); + } + + // Load table contents from file + FILE *f = fopen(table_file, "r"); + int c; + for (int i = 0; i < mat->num_rho; i++) { + for (int j = 0; j < mat->num_u; j++) { + c = fscanf(f, "%f", &mat->table_P_rho_u[i][j]); + if (c != 1) { + error("Failed to read EOS table"); + } + } + } + fclose(f); +} + +// Convert from cgs to internal units +INLINE static void convert_units_HM80(struct HM80_params *mat, + const struct unit_system *us) { + const float Mbar_to_Ba = 1e12f; // Convert Megabar to Barye + const float J_kg_to_erg_g = 1e4f; // Convert J/kg to erg/g + + // Table densities in cgs + mat->log_rho_min -= logf(units_cgs_conversion_factor(us, UNIT_CONV_DENSITY)); + mat->log_rho_max -= logf(units_cgs_conversion_factor(us, UNIT_CONV_DENSITY)); + + // Table energies in SI + mat->log_u_min += + logf(J_kg_to_erg_g / + units_cgs_conversion_factor(us, UNIT_CONV_ENERGY_PER_UNIT_MASS)); + mat->log_u_max += + logf(J_kg_to_erg_g / + units_cgs_conversion_factor(us, UNIT_CONV_ENERGY_PER_UNIT_MASS)); + + // Table Pressures in Mbar + for (int i = 0; i < mat->num_rho; i++) { + for (int j = 0; j < mat->num_u; j++) { + mat->table_P_rho_u[i][j] *= + Mbar_to_Ba / units_cgs_conversion_factor(us, UNIT_CONV_PRESSURE); + } + } + + mat->bulk_mod /= units_cgs_conversion_factor(us, UNIT_CONV_PRESSURE); +} + +// gas_internal_energy_from_entropy +INLINE static float HM80_internal_energy_from_entropy( + float density, float entropy, const struct HM80_params *mat) { + + error("This EOS function is not yet implemented!"); + + return 0; +} + +// gas_pressure_from_entropy +INLINE static float HM80_pressure_from_entropy(float density, float entropy, + const struct HM80_params *mat) { + + error("This EOS function is not yet implemented!"); + + return 0; +} + +// gas_entropy_from_pressure +INLINE static float HM80_entropy_from_pressure(float density, float pressure, + const struct HM80_params *mat) { + + error("This EOS function is not yet implemented!"); + + return 0; +} + +// gas_soundspeed_from_entropy +INLINE static float HM80_soundspeed_from_entropy( + float density, float entropy, const struct HM80_params *mat) { + + error("This EOS function is not yet implemented!"); + + return 0; +} + +// gas_entropy_from_internal_energy +INLINE static float HM80_entropy_from_internal_energy( + float density, float u, const struct HM80_params *mat) { + error("This EOS function is not yet implemented!"); + + return 0; +} + +// gas_pressure_from_internal_energy +INLINE static float HM80_pressure_from_internal_energy( + float density, float u, const struct HM80_params *mat) { + + float P; + + if (u <= 0) { + return 0.f; + } + + int rho_idx, u_idx; + float intp_rho, intp_u; + const float log_rho = logf(density); + const float log_u = logf(u); + + // 2D interpolation (linear in log(rho), log(u)) to find P(rho, u) + rho_idx = floorf((log_rho - mat->log_rho_min) * mat->inv_log_rho_step); + u_idx = floorf((log_u - mat->log_u_min) * mat->inv_log_u_step); + + intp_rho = (log_rho - mat->log_rho_min - rho_idx * mat->log_rho_step) * + mat->inv_log_rho_step; + intp_u = + (log_u - mat->log_u_min - u_idx * mat->log_u_step) * mat->inv_log_u_step; + + // Return zero pressure if below the table minimum/a + // Extrapolate the pressure for low densities + if (rho_idx < 0) { // Too-low rho + P = expf(logf((1 - intp_u) * mat->table_P_rho_u[0][u_idx] + + intp_u * mat->table_P_rho_u[0][u_idx + 1]) + + log_rho - mat->log_rho_min); + if (u_idx < 0) { // and too-low u + P = 0; + } + } else if (u_idx < 0) { // Too-low u + P = 0; + } + // Return an edge value if above the table maximum/a + else if (rho_idx >= mat->num_rho - 1) { // Too-high rho + if (u_idx >= mat->num_u - 1) { // and too-high u + P = mat->table_P_rho_u[mat->num_rho - 1][mat->num_u - 1]; + } else { + P = mat->table_P_rho_u[mat->num_rho - 1][u_idx]; + } + } else if (u_idx >= mat->num_u - 1) { // Too-high u + P = mat->table_P_rho_u[rho_idx][mat->num_u - 1]; + } + // Normal interpolation within the table + else { + P = (1.f - intp_rho) * + ((1.f - intp_u) * mat->table_P_rho_u[rho_idx][u_idx] + + intp_u * mat->table_P_rho_u[rho_idx][u_idx + 1]) + + intp_rho * ((1 - intp_u) * mat->table_P_rho_u[rho_idx + 1][u_idx] + + intp_u * mat->table_P_rho_u[rho_idx + 1][u_idx + 1]); + } + + return P; +} + +// gas_internal_energy_from_pressure +INLINE static float HM80_internal_energy_from_pressure( + float density, float P, const struct HM80_params *mat) { + + error("This EOS function is not yet implemented!"); + + return 0; +} + +// gas_soundspeed_from_internal_energy +INLINE static float HM80_soundspeed_from_internal_energy( + float density, float u, const struct HM80_params *mat) { + + float c, P; + + // Bulk modulus + if (mat->bulk_mod != 0) { + c = sqrtf(mat->bulk_mod / density); + } + // Ideal gas + else { + P = HM80_pressure_from_internal_energy(density, u, mat); + c = sqrtf(hydro_gamma * P / density); + } + + return c; +} + +// gas_soundspeed_from_pressure +INLINE static float HM80_soundspeed_from_pressure( + float density, float P, const struct HM80_params *mat) { + + float c; + + // Bulk modulus + if (mat->bulk_mod != 0) { + c = sqrtf(mat->bulk_mod / density); + } + // Ideal gas + else { + c = sqrtf(hydro_gamma * P / density); + } + + return c; +} + +#endif /* SWIFT_HUBBARD_MACFARLANE_EQUATION_OF_STATE_H */ diff --git a/src/equation_of_state/planetary/sesame.h b/src/equation_of_state/planetary/sesame.h new file mode 100644 index 0000000000000000000000000000000000000000..76574c2ad00282a82649705cd8a2b5a1b428d867 --- /dev/null +++ b/src/equation_of_state/planetary/sesame.h @@ -0,0 +1,140 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * 2018 Jacob Kegerreis (jacob.kegerreis@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_SESAME_EQUATION_OF_STATE_H +#define SWIFT_SESAME_EQUATION_OF_STATE_H + +/** + * @file equation_of_state/planetary/sesame.h + * + * Contains the SESAME EOS functions for + * equation_of_state/planetary/equation_of_state.h + * + * WORK IN PROGRESS! + * + */ + +/* Some standard headers. */ +#include <math.h> + +/* Local headers. */ +#include "adiabatic_index.h" +#include "common_io.h" +#include "equation_of_state.h" +#include "inline.h" +#include "physical_constants.h" +#include "units.h" + +// SESAME parameters +struct SESAME_params { + enum eos_planetary_material_id mat_id; +}; + +// Parameter values for each material (cgs units) +INLINE static void set_SESAME_iron(struct SESAME_params *mat, + enum eos_planetary_material_id mat_id) { + mat->mat_id = mat_id; +} + +// Convert from cgs to internal units +INLINE static void convert_units_SESAME(struct SESAME_params *mat, + const struct unit_system *us) {} + +// gas_internal_energy_from_entropy +INLINE static float SESAME_internal_energy_from_entropy( + float density, float entropy, const struct SESAME_params *mat) { + + error("This EOS function is not yet implemented!"); + + return 0; +} + +// gas_pressure_from_entropy +INLINE static float SESAME_pressure_from_entropy( + float density, float entropy, const struct SESAME_params *mat) { + + error("This EOS function is not yet implemented!"); + + return 0; +} + +// gas_entropy_from_pressure +INLINE static float SESAME_entropy_from_pressure( + float density, float pressure, const struct SESAME_params *mat) { + + error("This EOS function is not yet implemented!"); + + return 0; +} + +// gas_soundspeed_from_entropy +INLINE static float SESAME_soundspeed_from_entropy( + float density, float entropy, const struct SESAME_params *mat) { + + error("This EOS function is not yet implemented!"); + + return 0; +} + +// gas_entropy_from_internal_energy +INLINE static float SESAME_entropy_from_internal_energy( + float density, float u, const struct SESAME_params *mat) { + + error("This EOS function is not yet implemented!"); + + return 0; +} + +// gas_pressure_from_internal_energy +INLINE static float SESAME_pressure_from_internal_energy( + float density, float u, const struct SESAME_params *mat) { + + error("This EOS function is not yet implemented!"); + + return 0; +} + +// gas_internal_energy_from_pressure +INLINE static float SESAME_internal_energy_from_pressure( + float density, float P, const struct SESAME_params *mat) { + + error("This EOS function is not yet implemented!"); + + return 0; +} + +// gas_soundspeed_from_internal_energy +INLINE static float SESAME_soundspeed_from_internal_energy( + float density, float u, const struct SESAME_params *mat) { + + error("This EOS function is not yet implemented!"); + + return 0; +} + +// gas_soundspeed_from_pressure +INLINE static float SESAME_soundspeed_from_pressure( + float density, float P, const struct SESAME_params *mat) { + + error("This EOS function is not yet implemented!"); + + return 0; +} + +#endif /* SWIFT_SESAME_EQUATION_OF_STATE_H */ diff --git a/src/equation_of_state/planetary/tillotson.h b/src/equation_of_state/planetary/tillotson.h new file mode 100644 index 0000000000000000000000000000000000000000..ed280079b040776dfc20480c50fbc9a3b6ff4470 --- /dev/null +++ b/src/equation_of_state/planetary/tillotson.h @@ -0,0 +1,282 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * 2018 Jacob Kegerreis (jacob.kegerreis@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_TILLOTSON_EQUATION_OF_STATE_H +#define SWIFT_TILLOTSON_EQUATION_OF_STATE_H + +/** + * @file equation_of_state/planetary/tillotson.h + * + * Contains the Tillotson EOS functions for + * equation_of_state/planetary/equation_of_state.h + * + */ + +/* Some standard headers. */ +#include <math.h> + +/* Local headers. */ +#include "adiabatic_index.h" +#include "common_io.h" +#include "equation_of_state.h" +#include "inline.h" +#include "physical_constants.h" +#include "units.h" + +// Tillotson parameters +struct Til_params { + float rho_0, a, b, A, B, E_0, E_iv, E_cv, alpha, beta, eta_min, P_min; + enum eos_planetary_material_id mat_id; +}; + +// Parameter values for each material (cgs units) +INLINE static void set_Til_iron(struct Til_params *mat, + enum eos_planetary_material_id mat_id) { + mat->mat_id = mat_id; + mat->rho_0 = 7.800f; + mat->a = 0.5f; + mat->b = 1.5f; + mat->A = 1.28e12f; + mat->B = 1.05e12f; + mat->E_0 = 9.5e10f; + mat->E_iv = 2.4e10f; + mat->E_cv = 8.67e10f; + mat->alpha = 5.0f; + mat->beta = 5.0f; + mat->eta_min = 0.0f; + mat->P_min = 0.0f; +} +INLINE static void set_Til_granite(struct Til_params *mat, + enum eos_planetary_material_id mat_id) { + mat->mat_id = mat_id; + mat->rho_0 = 2.680f; + mat->a = 0.5f; + mat->b = 1.3f; + mat->A = 1.8e11f; + mat->B = 1.8e11f; + mat->E_0 = 1.6e11f; + mat->E_iv = 3.5e10f; + mat->E_cv = 1.8e11f; + mat->alpha = 5.0f; + mat->beta = 5.0f; + mat->eta_min = 0.0f; + mat->P_min = 0.0f; +} +INLINE static void set_Til_water(struct Til_params *mat, + enum eos_planetary_material_id mat_id) { + mat->mat_id = mat_id; + mat->rho_0 = 0.998f; + mat->a = 0.7f; + mat->b = 0.15f; + mat->A = 2.18e10f; + mat->B = 1.325e11f; + mat->E_0 = 7.0e10f; + mat->E_iv = 4.19e9f; + mat->E_cv = 2.69e10f; + mat->alpha = 10.0f; + mat->beta = 5.0f; + mat->eta_min = 0.915f; + mat->P_min = 0.0f; +} + +// Convert from cgs to internal units +INLINE static void convert_units_Til(struct Til_params *mat, + const struct unit_system *us) { + + mat->rho_0 /= units_cgs_conversion_factor(us, UNIT_CONV_DENSITY); + mat->A /= units_cgs_conversion_factor(us, UNIT_CONV_PRESSURE); + mat->B /= units_cgs_conversion_factor(us, UNIT_CONV_PRESSURE); + mat->E_0 /= units_cgs_conversion_factor(us, UNIT_CONV_ENERGY_PER_UNIT_MASS); + mat->E_iv /= units_cgs_conversion_factor(us, UNIT_CONV_ENERGY_PER_UNIT_MASS); + mat->E_cv /= units_cgs_conversion_factor(us, UNIT_CONV_ENERGY_PER_UNIT_MASS); + mat->P_min /= units_cgs_conversion_factor(us, UNIT_CONV_PRESSURE); +} + +// gas_internal_energy_from_entropy +INLINE static float Til_internal_energy_from_entropy( + float density, float entropy, const struct Til_params *mat) { + + error("This EOS function is not yet implemented!"); + + return 0; +} + +// gas_pressure_from_entropy +INLINE static float Til_pressure_from_entropy(float density, float entropy, + const struct Til_params *mat) { + + error("This EOS function is not yet implemented!"); + + return 0; +} + +// gas_entropy_from_pressure +INLINE static float Til_entropy_from_pressure(float density, float pressure, + const struct Til_params *mat) { + + error("This EOS function is not yet implemented!"); + + return 0; +} + +// gas_soundspeed_from_entropy +INLINE static float Til_soundspeed_from_entropy(float density, float entropy, + const struct Til_params *mat) { + + error("This EOS function is not yet implemented!"); + + return 0; +} + +// gas_entropy_from_internal_energy +INLINE static float Til_entropy_from_internal_energy( + float density, float u, const struct Til_params *mat) { + error("This EOS function is not yet implemented!"); + + return 0; +} + +// gas_pressure_from_internal_energy +INLINE static float Til_pressure_from_internal_energy( + float density, float u, const struct Til_params *mat) { + + const float eta = density / mat->rho_0; + const float mu = eta - 1.f; + const float nu = 1.f / eta - 1.f; + float P_c, P_e, P; + + // Condensed or cold + if (eta < mat->eta_min) { + P_c = 0.f; + } else { + P_c = (mat->a + mat->b / (u / (mat->E_0 * eta * eta) + 1.f)) * density * u + + mat->A * mu + mat->B * mu * mu; + } + // Expanded and hot + P_e = mat->a * density * u + + (mat->b * density * u / (u / (mat->E_0 * eta * eta) + 1.f) + + mat->A * mu * expf(-mat->beta * nu)) * + expf(-mat->alpha * nu * nu); + + // Condensed or cold state + if ((1.f < eta) || (u < mat->E_iv)) { + P = P_c; + } + // Expanded and hot state + else if ((eta < 1.f) && (mat->E_cv < u)) { + P = P_e; + } + // Hybrid state + else { + P = ((u - mat->E_iv) * P_e + (mat->E_cv - u) * P_c) / + (mat->E_cv - mat->E_iv); + } + + // Minimum pressure + if (P < mat->P_min) { + P = mat->P_min; + } + + return P; +} + +// gas_internal_energy_from_pressure +INLINE static float Til_internal_energy_from_pressure( + float density, float P, const struct Til_params *mat) { + + error("This EOS function is not yet implemented!"); + + return 0; +} + +// gas_soundspeed_from_internal_energy +INLINE static float Til_soundspeed_from_internal_energy( + float density, float u, const struct Til_params *mat) { + + // const float eta = density / mat->rho_0; + // const float mu = eta - 1.f; + // const float nu = 1.f/eta - 1.f; + // float P_c, P_e, P, c_c, c_e, c; + // + // // Condensed or cold + // if (eta < mat->eta_min) { + // P_c = 0.f; + // } + // else { + // P_c = (mat->a + mat->b / (u / (mat->E_0 * eta*eta) + 1.f)) * density + // * u + // + mat->A * mu + mat->B * mu*mu; + // } + // c_c = mat->a*u + mat->b*u / ((u / (mat->E_0*eta*eta)+1.f) * + // (u / (mat->E_0*eta*eta)+1.f)) * + // (3.f*(u / (mat->E_0*eta*eta)+1.f) - 2.f) + + // (mat->A + 2.f*mat->B*mu) / mat->rho_0 + P_c / (rho*rho) * + // (mat->a*rho + mat->b*rho / ((u / (mat->E_0*eta*eta)+1.f) * + // (u / (mat->E_0*eta*eta)+1.f))); + // + // c_c = max(c_c, mat->A / mat->rho_0); + // + // // Expanded and hot + // P_e = mat->a*density*u + ( + // mat->b * density * u / (u / (mat->E_0 * eta*eta) + 1.f) + // + mat->A*mu * expf(-mat->beta * nu) + // ) * expf(-mat->alpha * nu*nu); + // + // c_e = (mat->a + mat->b / (u / (mat->E_0*eta*eta)+1.f) * + // expf(-mat->beta*((1.f - eta)/eta)*((1.f - eta)/eta)) + // + 1.f)*P_e/rho + mat->A/mat->rho_0 + // *expf(-(mat->alpha*((1.f - eta)/eta)+mat->beta * + // ((1.f - eta)/eta)*((1.f - eta)/eta)))*(1.f+mu/(eta*eta) + // *(mat->alpha+2.f*mat->beta*((1.f - eta)/eta)-eta)) + + // mat->b*rho*u/((u / (mat->E_0*eta*eta)+1.f)* + // (u / (mat->E_0*eta*eta)+1.f)*eta*eta)* + // expf(-mat->beta*((1.f - eta)/eta)*((1.f - eta)/eta))* + // (2.f*mat->beta*((1.f - eta)/eta)*(u / (mat->E_0*eta*eta)+1.f) / + // mat->rho_0 + 1.f/(mat->E_0*rho)*(2.f*u-P_e/rho)); + // + // // Condensed or cold state + // if ((1.f < eta) || (u < mat->E_iv)) { + // c = c_c; + // } + // // Expanded and hot state + // else if ((eta < 1.f) && (mat->E_cv < u)) { + // c = c_e; + // } + // // Hybrid state + // else { + // c = ((u - mat->E_iv)*c_e + (mat->E_cv - u)*c_c) / + // (mat->E_cv - mat->E_iv); + // + // c = max(c_c, mat->A / mat->rho0); + // } + float c = sqrtf(mat->A / mat->rho_0); + + return c; +} + +// gas_soundspeed_from_pressure +INLINE static float Til_soundspeed_from_pressure(float density, float P, + const struct Til_params *mat) { + + float c = sqrtf(mat->A / mat->rho_0); + + return c; +} + +#endif /* SWIFT_TILLOTSON_EQUATION_OF_STATE_H */ diff --git a/src/hydro.h b/src/hydro.h index 78ae7d178ff93b01fa2eeebe09f34b718ce9a826..0b6438471ec799f9b44e01b48dd528de9f57e379 100644 --- a/src/hydro.h +++ b/src/hydro.h @@ -54,6 +54,10 @@ #include "./hydro/Shadowswift/hydro_iact.h" #define SPH_IMPLEMENTATION \ "Shadowfax moving mesh (Vandenbroucke and De Rijcke 2016)" +#elif defined(MINIMAL_MULTI_MAT_SPH) +#include "./hydro/MinimalMultiMat/hydro.h" +#include "./hydro/MinimalMultiMat/hydro_iact.h" +#define SPH_IMPLEMENTATION "Minimal version of SPH with multiple materials" #else #error "Invalid choice of SPH variant" #endif diff --git a/src/hydro/MinimalMultiMat/hydro.h b/src/hydro/MinimalMultiMat/hydro.h new file mode 100644 index 0000000000000000000000000000000000000000..5383ffda8fe67a591691766e4150e75a9dbd4cb0 --- /dev/null +++ b/src/hydro/MinimalMultiMat/hydro.h @@ -0,0 +1,634 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * 2018 Jacob Kegerreis (jacob.kegerreis@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_MINIMAL_MULTI_MAT_HYDRO_H +#define SWIFT_MINIMAL_MULTI_MAT_HYDRO_H + +/** + * @file MinimalMultiMat/hydro.h + * @brief Minimal conservative implementation of SPH (Non-neighbour loop + * equations) with multiple materials. + * + * The thermal variable is the internal energy (u). Simple constant + * viscosity term without switches is implemented. No thermal conduction + * term is implemented. + * + * This corresponds to equations (43), (44), (45), (101), (103) and (104) with + * \f$\beta=3\f$ and \f$\alpha_u=0\f$ of Price, D., Journal of Computational + * Physics, 2012, Volume 231, Issue 3, pp. 759-794. + */ + +#include "adiabatic_index.h" +#include "approx_math.h" +#include "cosmology.h" +#include "dimension.h" +#include "equation_of_state.h" +#include "hydro_properties.h" +#include "hydro_space.h" +#include "kernel_hydro.h" +#include "minmax.h" + +/** + * @brief Returns the comoving internal energy of a particle + * + * For implementations where the main thermodynamic variable + * is not internal energy, this function computes the internal + * energy from the thermodynamic variable. + * + * @param p The particle of interest + */ +__attribute__((always_inline)) INLINE static float +hydro_get_comoving_internal_energy(const struct part *restrict p) { + + return p->u; +} + +/** + * @brief Returns the physical internal energy of a particle + * + * For implementations where the main thermodynamic variable + * is not internal energy, this function computes the internal + * energy from the thermodynamic variable and converts it to + * physical coordinates. + * + * @param p The particle of interest. + * @param cosmo The cosmological model. + */ +__attribute__((always_inline)) INLINE static float +hydro_get_physical_internal_energy(const struct part *restrict p, + const struct cosmology *cosmo) { + + return p->u * cosmo->a_factor_internal_energy; +} + +/** + * @brief Returns the comoving pressure of a particle + * + * Computes the pressure based on the particle's properties. + * + * @param p The particle of interest + */ +__attribute__((always_inline)) INLINE static float hydro_get_comoving_pressure( + const struct part *restrict p) { + + return gas_pressure_from_internal_energy(p->rho, p->u, p->mat_id); +} + +/** + * @brief Returns the physical pressure of a particle + * + * Computes the pressure based on the particle's properties and + * convert it to physical coordinates. + * + * @param p The particle of interest + * @param cosmo The cosmological model. + */ +__attribute__((always_inline)) INLINE static float hydro_get_physical_pressure( + const struct part *restrict p, const struct cosmology *cosmo) { + + return cosmo->a_factor_pressure * + gas_pressure_from_internal_energy(p->rho, p->u, p->mat_id); +} + +/** + * @brief Returns the comoving entropy of a particle + * + * For implementations where the main thermodynamic variable + * is not entropy, this function computes the entropy from + * the thermodynamic variable. + * + * @param p The particle of interest + */ +__attribute__((always_inline)) INLINE static float hydro_get_comoving_entropy( + const struct part *restrict p) { + + return gas_entropy_from_internal_energy(p->rho, p->u, p->mat_id); +} + +/** + * @brief Returns the physical entropy of a particle + * + * For implementations where the main thermodynamic variable + * is not entropy, this function computes the entropy from + * the thermodynamic variable and converts it to + * physical coordinates. + * + * @param p The particle of interest + * @param cosmo The cosmological model. + */ +__attribute__((always_inline)) INLINE static float hydro_get_physical_entropy( + const struct part *restrict p, const struct cosmology *cosmo) { + + /* Note: no cosmological conversion required here with our choice of + * coordinates. */ + return gas_entropy_from_internal_energy(p->rho, p->u, p->mat_id); +} + +/** + * @brief Returns the comoving sound speed of a particle + * + * @param p The particle of interest + */ +__attribute__((always_inline)) INLINE static float +hydro_get_comoving_soundspeed(const struct part *restrict p) { + + return p->force.soundspeed; +} + +/** + * @brief Returns the physical sound speed of a particle + * + * @param p The particle of interest + * @param cosmo The cosmological model. + */ +__attribute__((always_inline)) INLINE static float +hydro_get_physical_soundspeed(const struct part *restrict p, + const struct cosmology *cosmo) { + + return cosmo->a_factor_sound_speed * p->force.soundspeed; +} + +/** + * @brief Returns the comoving density of a particle + * + * @param p The particle of interest + */ +__attribute__((always_inline)) INLINE static float hydro_get_comoving_density( + const struct part *restrict p) { + + return p->rho; +} + +/** + * @brief Returns the comoving density of a particle. + * + * @param p The particle of interest + * @param cosmo The cosmological model. + */ +__attribute__((always_inline)) INLINE static float hydro_get_physical_density( + const struct part *restrict p, const struct cosmology *cosmo) { + + return cosmo->a3_inv * 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 Sets the mass of a particle + * + * @param p The particle of interest + * @param m The mass to set. + */ +__attribute__((always_inline)) INLINE static void hydro_set_mass( + struct part *restrict p, float m) { + + p->mass = m; +} + +/** + * @brief Returns the velocities drifted to the current time of a particle. + * + * @param p The particle of interest + * @param xp The extended data of the particle. + * @param dt_kick_hydro The time (for hydro accelerations) since the last kick. + * @param dt_kick_grav The time (for gravity accelerations) since the last kick. + * @param v (return) The velocities at the current time. + */ +__attribute__((always_inline)) INLINE static void hydro_get_drifted_velocities( + const struct part *restrict p, const struct xpart *xp, float dt_kick_hydro, + float dt_kick_grav, float v[3]) { + + v[0] = xp->v_full[0] + p->a_hydro[0] * dt_kick_hydro + + xp->a_grav[0] * dt_kick_grav; + v[1] = xp->v_full[1] + p->a_hydro[1] * dt_kick_hydro + + xp->a_grav[1] * dt_kick_grav; + v[2] = xp->v_full[2] + p->a_hydro[2] * dt_kick_hydro + + xp->a_grav[2] * dt_kick_grav; +} + +/** + * @brief Returns the time derivative of internal energy of a particle + * + * We assume a constant density. + * + * @param p The particle of interest + */ +__attribute__((always_inline)) INLINE static float hydro_get_internal_energy_dt( + const struct part *restrict p) { + + return p->u_dt; +} + +/** + * @brief Returns the time derivative of internal energy of a particle + * + * We assume a constant density. + * + * @param p The particle of interest. + * @param du_dt The new time derivative of the internal energy. + */ +__attribute__((always_inline)) INLINE static void hydro_set_internal_energy_dt( + struct part *restrict p, float du_dt) { + + p->u_dt = du_dt; +} +/** + * @brief Computes the hydro time-step of a given particle + * + * This function returns the time-step of a particle given its hydro-dynamical + * state. A typical time-step calculation would be the use of the CFL condition. + * + * @param p Pointer to the particle data + * @param xp Pointer to the extended particle data + * @param hydro_properties The SPH parameters + * @param cosmo The cosmological model. + */ +__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 struct cosmology *restrict cosmo) { + + const float CFL_condition = hydro_properties->CFL_condition; + + /* CFL condition */ + const float dt_cfl = 2.f * kernel_gamma * CFL_condition * cosmo->a * p->h / + (cosmo->a_factor_sound_speed * p->force.v_sig); + + return dt_cfl; +} + +/** + * @brief Does some extra hydro operations once the actual physical time step + * for the particle is known. + * + * @param p The particle to act upon. + * @param dt Physical time step of the particle during the next step. + */ +__attribute__((always_inline)) INLINE static void hydro_timestep_extra( + struct part *p, float dt) {} + +/** + * @brief Prepares a particle for the density calculation. + * + * Zeroes all the relevant arrays in preparation for the sums taking place in + * the various density loop over neighbours. Typically, all fields of the + * density sub-structure of a particle get zeroed in here. + * + * @param p The particle to act upon + * @param hs #hydro_space containing hydro specific space information. + */ +__attribute__((always_inline)) INLINE static void hydro_init_part( + struct part *restrict p, const struct hydro_space *hs) { + + p->density.wcount = 0.f; + p->density.wcount_dh = 0.f; + p->rho = 0.f; + p->density.rho_dh = 0.f; +} + +/** + * @brief Finishes the density calculation. + * + * Multiplies the density and number of neighbours by the appropiate constants + * and add the self-contribution term. + * Additional quantities such as velocity gradients will also get the final + * terms added to them here. + * + * Also adds/multiplies the cosmological terms if need be. + * + * @param p The particle to act upon + * @param cosmo The cosmological model. + */ +__attribute__((always_inline)) INLINE static void hydro_end_density( + struct part *restrict p, const struct cosmology *cosmo) { + + /* 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->density.rho_dh -= hydro_dimension * p->mass * kernel_root; + p->density.wcount += kernel_root; + p->density.wcount_dh -= hydro_dimension * kernel_root; + + /* Finish the calculation by inserting the missing h-factors */ + p->rho *= h_inv_dim; + p->density.rho_dh *= h_inv_dim_plus_one; + p->density.wcount *= h_inv_dim; + p->density.wcount_dh *= h_inv_dim_plus_one; +} + +/** + * @brief Sets all particle fields to sensible values when the #part has 0 ngbs. + * + * In the desperate case where a particle has no neighbours (likely because + * of the h_max ceiling), set the particle fields to something sensible to avoid + * NaNs in the next calculations. + * + * @param p The particle to act upon + * @param xp The extended particle data to act upon + * @param cosmo The cosmological model. + */ +__attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours( + struct part *restrict p, struct xpart *restrict xp, + const struct cosmology *cosmo) { + + /* 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 */ + + /* Re-set problematic values */ + p->rho = p->mass * kernel_root * h_inv_dim; + p->density.wcount = kernel_root * kernel_norm * h_inv_dim; + p->density.rho_dh = 0.f; + p->density.wcount_dh = 0.f; +} + +/** + * @brief Prepare a particle for the force calculation. + * + * This function is called in the ghost task to convert some quantities coming + * from the density loop over neighbours into quantities ready to be used in the + * force loop over neighbours. Quantities are typically read from the density + * sub-structure and written to the force sub-structure. + * Examples of calculations done here include the calculation of viscosity term + * constants, thermal conduction terms, hydro conversions, etc. + * + * @param p The particle to act upon + * @param xp The extended particle data to act upon + * @param cosmo The current cosmological model. + */ +__attribute__((always_inline)) INLINE static void hydro_prepare_force( + struct part *restrict p, struct xpart *restrict xp, + const struct cosmology *cosmo) { + + /* Compute the pressure */ + const float pressure = + gas_pressure_from_internal_energy(p->rho, p->u, p->mat_id); + + /* Compute the sound speed */ + const float soundspeed = + gas_soundspeed_from_pressure(p->rho, pressure, p->mat_id); + + /* 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; +} + +/** + * @brief Reset acceleration fields of a particle + * + * Resets all hydro acceleration and time derivative fields in preparation + * for the sums taking place in the various 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->u_dt = 0.0f; + p->force.h_dt = 0.0f; + p->force.v_sig = 0.0f; +} + +/** + * @brief Sets the values to be predicted in the drifts to their values at a + * kick time + * + * @param p The particle. + * @param xp The extended data of this particle. + */ +__attribute__((always_inline)) INLINE static void hydro_reset_predicted_values( + struct part *restrict p, const struct xpart *restrict xp) { + + /* Re-set the predicted velocities */ + p->v[0] = xp->v_full[0]; + p->v[1] = xp->v_full[1]; + p->v[2] = xp->v_full[2]; + + /* Re-set the entropy */ + p->u = xp->u_full; +} + +/** + * @brief Predict additional particle fields forward in time when drifting + * + * Additional hydrodynamic quantites are drifted forward in time here. These + * include thermal quantities (thermal energy or total energy or entropy, ...). + * + * Note the different time-step sizes used for the different quantities as they + * include cosmological factors. + * + * @param p The particle. + * @param xp The extended data of the particle. + * @param dt_drift The drift time-step for positions. + * @param dt_therm The drift time-step for thermal quantities. + */ +__attribute__((always_inline)) INLINE static void hydro_predict_extra( + struct part *restrict p, const struct xpart *restrict xp, float dt_drift, + float dt_therm) { + + const float h_inv = 1.f / p->h; + + /* Predict smoothing length */ + const float w1 = p->force.h_dt * h_inv * dt_drift; + 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) */ + else + p->rho *= expf(w2); + + /* Predict the internal energy */ + p->u += p->u_dt * dt_therm; + + /* Compute the new pressure */ + const float pressure = + gas_pressure_from_internal_energy(p->rho, p->u, p->mat_id); + + /* Compute the new sound speed */ + const float soundspeed = + gas_soundspeed_from_pressure(p->rho, pressure, p->mat_id); + + p->force.pressure = pressure; + p->force.soundspeed = soundspeed; +} + +/** + * @brief Finishes the force calculation. + * + * Multiplies the force and accelerations by the appropiate constants + * and add the self-contribution term. In most cases, there is little + * to do here. + * + * Cosmological terms are also added/multiplied here. + * + * @param p The particle to act upon + * @param cosmo The current cosmological model. + */ +__attribute__((always_inline)) INLINE static void hydro_end_force( + struct part *restrict p, const struct cosmology *cosmo) { + + p->force.h_dt *= p->h * hydro_dimension_inv; +} + +/** + * @brief Kick the additional variables + * + * Additional hydrodynamic quantites are kicked forward in time here. These + * include thermal quantities (thermal energy or total energy or entropy, ...). + * + * @param p The particle to act upon. + * @param xp The particle extended data to act upon. + * @param dt_therm The time-step for this kick (for thermodynamic quantities). + * @param cosmo The cosmological model. + * @param hydro_props The constants used in the scheme + */ +__attribute__((always_inline)) INLINE static void hydro_kick_extra( + struct part *restrict p, struct xpart *restrict xp, float dt_therm, + const struct cosmology *cosmo, const struct hydro_props *hydro_props) { + + /* Do not decrease the energy by more than a factor of 2*/ + if (dt_therm > 0. && p->u_dt * dt_therm < -0.5f * xp->u_full) { + p->u_dt = -0.5f * xp->u_full / dt_therm; + } + xp->u_full += p->u_dt * dt_therm; + + /* Apply the minimal energy limit */ + const float min_energy = + hydro_props->minimal_internal_energy * cosmo->a_factor_internal_energy; + if (xp->u_full < min_energy) { + xp->u_full = min_energy; + p->u_dt = 0.f; + } + + /* Compute the pressure */ + const float pressure = + gas_pressure_from_internal_energy(p->rho, xp->u_full, p->mat_id); + + /* Compute the sound speed */ + const float soundspeed = + gas_soundspeed_from_internal_energy(p->rho, p->u, p->mat_id); + + p->force.pressure = pressure; + p->force.soundspeed = soundspeed; +} + +/** + * @brief Converts hydro quantity of a particle at the start of a run + * + * This function is called once at the end of the engine_init_particle() + * routine (at the start of a calculation) after the densities of + * particles have been computed. + * This can be used to convert internal energy into entropy for instance. + * + * @param p The particle to act upon + * @param xp The extended particle to act upon + * @param cosmo The cosmological model. + */ +__attribute__((always_inline)) INLINE static void hydro_convert_quantities( + struct part *restrict p, struct xpart *restrict xp, + const struct cosmology *cosmo) { + + /* Compute the pressure */ + const float pressure = + gas_pressure_from_internal_energy(p->rho, p->u, p->mat_id); + + /* Compute the sound speed */ + const float soundspeed = + gas_soundspeed_from_internal_energy(p->rho, p->u, p->mat_id); + + p->force.pressure = pressure; + p->force.soundspeed = soundspeed; +} + +/** + * @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 or assignments between the particle + * and extended particle fields. + * + * @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->time_bin = 0; + xp->v_full[0] = p->v[0]; + xp->v_full[1] = p->v[1]; + xp->v_full[2] = p->v[2]; + xp->a_grav[0] = 0.f; + xp->a_grav[1] = 0.f; + xp->a_grav[2] = 0.f; + xp->u_full = p->u; + + hydro_reset_acceleration(p); + hydro_init_part(p, NULL); +} + +/** + * @brief Overwrite the initial internal energy of a particle. + * + * Note that in the cases where the thermodynamic variable is not + * internal energy but gets converted later, we must overwrite that + * field. The conversion to the actual variable happens later after + * the initial fake time-step. + * + * @param p The #part to write to. + * @param u_init The new initial internal energy. + */ +__attribute__((always_inline)) INLINE static void +hydro_set_init_internal_energy(struct part *p, float u_init) { + + p->u = u_init; +} + +#endif /* SWIFT_MINIMAL_MULTI_MAT_HYDRO_H */ diff --git a/src/hydro/MinimalMultiMat/hydro_debug.h b/src/hydro/MinimalMultiMat/hydro_debug.h new file mode 100644 index 0000000000000000000000000000000000000000..d8fe73313fbb175faa9547970c6680346dad0a1b --- /dev/null +++ b/src/hydro/MinimalMultiMat/hydro_debug.h @@ -0,0 +1,53 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Coypright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * 2018 Jacob Kegerreis (jacob.kegerreis@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_MINIMAL_MULTI_MAT_HYDRO_DEBUG_H +#define SWIFT_MINIMAL_MULTI_MAT_HYDRO_DEBUG_H + +/** + * @file MinimalMultiMat/hydro_debug.h + * @brief MinimalMultiMat conservative implementation of SPH (Debugging + * routines) + * + * The thermal variable is the internal energy (u). Simple constant + * viscosity term without switches is implemented. No thermal conduction + * term is implemented. + * + * This corresponds to equations (43), (44), (45), (101), (103) and (104) with + * \f$\beta=3\f$ and \f$\alpha_u=0\f$ of + * Price, D., Journal of Computational Physics, 2012, Volume 231, Issue 3, + * pp. 759-794. + */ + +__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], " + "u=%.3e, du/dt=%.3e v_sig=%.3e, P=%.3e\n" + "h=%.3e, dh/dt=%.3e wcount=%d, m=%.3e, dh_drho=%.3e, rho=%.3e, " + "time_bin=%d, mat_id=%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->u, p->u_dt, p->force.v_sig, hydro_get_comoving_pressure(p), p->h, + p->force.h_dt, (int)p->density.wcount, p->mass, p->density.rho_dh, p->rho, + p->time_bin, p->mat_id); +} + +#endif /* SWIFT_MINIMAL_MULTI_MAT_HYDRO_DEBUG_H */ diff --git a/src/hydro/MinimalMultiMat/hydro_iact.h b/src/hydro/MinimalMultiMat/hydro_iact.h new file mode 100644 index 0000000000000000000000000000000000000000..5984c1c483546d87800792ced0ffcc41e0aaa408 --- /dev/null +++ b/src/hydro/MinimalMultiMat/hydro_iact.h @@ -0,0 +1,340 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * 2018 Jacob Kegerreis (jacob.kegerreis@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_MINIMAL_MULTI_MAT_HYDRO_IACT_H +#define SWIFT_MINIMAL_MULTI_MAT_HYDRO_IACT_H + +/** + * @file MinimalMultiMat/hydro_iact.h + * @brief MinimalMultiMat conservative implementation of SPH (Neighbour loop + * equations) + * + * The thermal variable is the internal energy (u). Simple constant + * viscosity term without switches is implemented. No thermal conduction + * term is implemented. + * + * This corresponds to equations (43), (44), (45), (101), (103) and (104) with + * \f$\beta=3\f$ and \f$\alpha_u=0\f$ of Price, D., Journal of Computational + * Physics, 2012, Volume 231, Issue 3, pp. 759-794. + */ + +#include "adiabatic_index.h" +#include "const.h" +#include "minmax.h" + +/** + * @brief Density interaction between two particles. + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi First particle. + * @param pj Second particle. + * @param a Current scale factor. + * @param H Current Hubble parameter. + */ +__attribute__((always_inline)) INLINE static void runner_iact_density( + float r2, const float *dx, float hi, float hj, struct part *restrict pi, + struct part *restrict pj, float a, float H) { + + float wi, wj, wi_dx, wj_dx; + + /* Get r. */ + const float r_inv = 1.0f / sqrtf(r2); + const float r = r2 * r_inv; + + /* Get the masses. */ + const float mi = pi->mass; + const float mj = pj->mass; + + /* Compute density of pi. */ + const float hi_inv = 1.f / hi; + const float ui = r * hi_inv; + kernel_deval(ui, &wi, &wi_dx); + + pi->rho += mj * wi; + pi->density.rho_dh -= mj * (hydro_dimension * wi + ui * wi_dx); + pi->density.wcount += wi; + pi->density.wcount_dh -= (hydro_dimension * wi + ui * wi_dx); + + /* Compute density of pj. */ + const float hj_inv = 1.f / hj; + const float uj = r * hj_inv; + kernel_deval(uj, &wj, &wj_dx); + + pj->rho += mi * wj; + pj->density.rho_dh -= mi * (hydro_dimension * wj + uj * wj_dx); + pj->density.wcount += wj; + pj->density.wcount_dh -= (hydro_dimension * wj + uj * wj_dx); +} + +/** + * @brief Density interaction between two particles (non-symmetric). + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi First particle. + * @param pj Second particle (not updated). + * @param a Current scale factor. + * @param H Current Hubble parameter. + */ +__attribute__((always_inline)) INLINE static void runner_iact_nonsym_density( + float r2, const float *dx, float hi, float hj, struct part *restrict pi, + const struct part *restrict pj, float a, float H) { + + float wi, wi_dx; + + /* Get the masses. */ + const float mj = pj->mass; + + /* Get r. */ + const float r_inv = 1.0f / sqrtf(r2); + const float r = r2 * r_inv; + + const float h_inv = 1.f / hi; + const float ui = r * h_inv; + kernel_deval(ui, &wi, &wi_dx); + + pi->rho += mj * wi; + pi->density.rho_dh -= mj * (hydro_dimension * wi + ui * wi_dx); + pi->density.wcount += wi; + pi->density.wcount_dh -= (hydro_dimension * wi + ui * wi_dx); +} + +/** + * @brief Force interaction between two particles. + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi First particle. + * @param pj Second particle. + * @param a Current scale factor. + * @param H Current Hubble parameter. + */ +__attribute__((always_inline)) INLINE static void runner_iact_force( + float r2, const float *dx, float hi, float hj, struct part *restrict pi, + struct part *restrict pj, float a, float H) { + + /* Cosmological factors entering the EoMs */ + const float fac_mu = pow_three_gamma_minus_five_over_two(a); + const float a2_Hubble = a * a * H; + + /* Get r and r inverse. */ + const float r_inv = 1.0f / sqrtf(r2); + const float r = r2 * r_inv; + + /* Recover some data */ + const float mi = pi->mass; + const float mj = pj->mass; + const float rhoi = pi->rho; + const float rhoj = pj->rho; + const float pressurei = pi->force.pressure; + const float pressurej = pj->force.pressure; + + /* 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 xi = r * hi_inv; + float wi, wi_dx; + kernel_deval(xi, &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; + float wj, wj_dx; + kernel_deval(xj, &wj, &wj_dx); + const float wj_dr = hjd_inv * wj_dx; + + /* Compute gradient terms */ + 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] + + (pi->v[1] - pj->v[1]) * dx[1] + + (pi->v[2] - pj->v[2]) * dx[2] + a2_Hubble * r2; + + /* Are the particles moving towards each others ? */ + const float omega_ij = min(dvdr, 0.f); + const float mu_ij = fac_mu * r_inv * omega_ij; /* This is 0 or negative */ + + /* Compute sound speeds and signal velocity */ + 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 */ + const float rho_ij = 0.5f * (rhoi + rhoj); + const float visc = -0.5f * const_viscosity_alpha * v_sig * mu_ij / rho_ij; + + /* Convolve with the kernel */ + const float visc_acc_term = 0.5f * visc * (wi_dr + wj_dr) * r_inv; + + /* SPH acceleration term */ + const float sph_acc_term = + (P_over_rho2_i * wi_dr + P_over_rho2_j * wj_dr) * r_inv; + + /* Assemble the acceleration */ + const float acc = sph_acc_term + visc_acc_term; + + /* 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 u. */ + const float sph_du_term_i = P_over_rho2_i * dvdr * r_inv * wi_dr; + const float sph_du_term_j = P_over_rho2_j * dvdr * r_inv * wj_dr; + + /* Viscosity term */ + const float visc_du_term = 0.5f * visc_acc_term * dvdr; + + /* Assemble the energy equation term */ + const float du_dt_i = sph_du_term_i + visc_du_term; + const float du_dt_j = sph_du_term_j + visc_du_term; + + /* Internal energy time derivatibe */ + pi->u_dt += du_dt_i * mj; + pj->u_dt += du_dt_j * mi; + + /* 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 = max(pi->force.v_sig, v_sig); + pj->force.v_sig = max(pj->force.v_sig, v_sig); +} + +/** + * @brief Force interaction between two particles (non-symmetric). + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi First particle. + * @param pj Second particle (not updated). + * @param a Current scale factor. + * @param H Current Hubble parameter. + */ +__attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( + float r2, const float *dx, float hi, float hj, struct part *restrict pi, + const struct part *restrict pj, float a, float H) { + + /* Cosmological factors entering the EoMs */ + const float fac_mu = pow_three_gamma_minus_five_over_two(a); + const float a2_Hubble = a * a * H; + + /* Get r and r inverse. */ + const float r_inv = 1.0f / sqrtf(r2); + const float r = r2 * r_inv; + + /* Recover some data */ + // const float mi = pi->mass; + const float mj = pj->mass; + const float rhoi = pi->rho; + const float rhoj = pj->rho; + const float pressurei = pi->force.pressure; + const float pressurej = pj->force.pressure; + + /* 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 xi = r * hi_inv; + float wi, wi_dx; + kernel_deval(xi, &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; + float wj, wj_dx; + kernel_deval(xj, &wj, &wj_dx); + const float wj_dr = hjd_inv * wj_dx; + + /* Compute gradient terms */ + 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] + + (pi->v[1] - pj->v[1]) * dx[1] + + (pi->v[2] - pj->v[2]) * dx[2] + a2_Hubble * r2; + + /* Are the particles moving towards each others ? */ + const float omega_ij = min(dvdr, 0.f); + const float mu_ij = fac_mu * r_inv * omega_ij; /* This is 0 or negative */ + + /* Compute sound speeds and signal velocity */ + 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 */ + const float rho_ij = 0.5f * (rhoi + rhoj); + const float visc = -0.5f * const_viscosity_alpha * v_sig * mu_ij / rho_ij; + + /* Convolve with the kernel */ + const float visc_acc_term = 0.5f * visc * (wi_dr + wj_dr) * r_inv; + + /* SPH acceleration term */ + const float sph_acc_term = + (P_over_rho2_i * wi_dr + P_over_rho2_j * wj_dr) * r_inv; + + /* Assemble the acceleration */ + const float acc = sph_acc_term + visc_acc_term; + + /* 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 u. */ + const float sph_du_term_i = P_over_rho2_i * dvdr * r_inv * wi_dr; + + /* Viscosity term */ + const float visc_du_term = 0.5f * visc_acc_term * dvdr; + + /* Assemble the energy equation term */ + const float du_dt_i = sph_du_term_i + visc_du_term; + + /* Internal energy time derivatibe */ + pi->u_dt += du_dt_i * mj; + + /* 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 = max(pi->force.v_sig, v_sig); +} + +#endif /* SWIFT_MINIMAL_MULTI_MAT_HYDRO_IACT_H */ diff --git a/src/hydro/MinimalMultiMat/hydro_io.h b/src/hydro/MinimalMultiMat/hydro_io.h new file mode 100644 index 0000000000000000000000000000000000000000..6e55497dc42111de291b8984a7dca048bb774722 --- /dev/null +++ b/src/hydro/MinimalMultiMat/hydro_io.h @@ -0,0 +1,200 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Coypright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * 2018 Jacob Kegerreis (jacob.kegerreis@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_MINIMAL_MULTI_MAT_HYDRO_IO_H +#define SWIFT_MINIMAL_MULTI_MAT_HYDRO_IO_H + +/** + * @file MinimalMultiMat/hydro_io.h + * @brief MinimalMultiMat conservative implementation of SPH (i/o routines) + * + * The thermal variable is the internal energy (u). Simple constant + * viscosity term without switches is implemented. No thermal conduction + * term is implemented. + * + * This corresponds to equations (43), (44), (45), (101), (103) and (104) with + * \f$\beta=3\f$ and \f$\alpha_u=0\f$ of + * Price, D., Journal of Computational Physics, 2012, Volume 231, Issue 3, + * pp. 759-794. + */ + +#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 = 9; + + /* 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_ENERGY_PER_UNIT_MASS, parts, u); + 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); + list[8] = + io_make_input_field("MaterialID", INT, 1, OPTIONAL, 1, parts, mat_id); +} + +void convert_P(const struct engine* e, const struct part* p, + const struct xpart* xp, float* ret) { + + ret[0] = hydro_get_comoving_pressure(p); +} + +void convert_part_pos(const struct engine* e, const struct part* p, + const struct xpart* xp, double* ret) { + + if (e->s->periodic) { + ret[0] = box_wrap(p->x[0], 0.0, e->s->dim[0]); + ret[1] = box_wrap(p->x[1], 0.0, e->s->dim[1]); + ret[2] = box_wrap(p->x[2], 0.0, e->s->dim[2]); + } else { + ret[0] = p->x[0]; + ret[1] = p->x[1]; + ret[2] = p->x[2]; + } +} + +void convert_part_vel(const struct engine* e, const struct part* p, + const struct xpart* xp, float* ret) { + + const int with_cosmology = (e->policy & engine_policy_cosmology); + const struct cosmology* cosmo = e->cosmology; + const integertime_t ti_current = e->ti_current; + const double time_base = e->time_base; + + const integertime_t ti_beg = get_integer_time_begin(ti_current, p->time_bin); + const integertime_t ti_end = get_integer_time_end(ti_current, p->time_bin); + + /* Get time-step since the last kick */ + float dt_kick_grav, dt_kick_hydro; + if (with_cosmology) { + dt_kick_grav = cosmology_get_grav_kick_factor(cosmo, ti_beg, ti_current); + dt_kick_grav -= + cosmology_get_grav_kick_factor(cosmo, ti_beg, (ti_beg + ti_end) / 2); + dt_kick_hydro = cosmology_get_hydro_kick_factor(cosmo, ti_beg, ti_current); + dt_kick_hydro -= + cosmology_get_hydro_kick_factor(cosmo, ti_beg, (ti_beg + ti_end) / 2); + } else { + dt_kick_grav = (ti_current - ((ti_beg + ti_end) / 2)) * time_base; + dt_kick_hydro = (ti_current - ((ti_beg + ti_end) / 2)) * time_base; + } + + /* Extrapolate the velocites to the current time */ + hydro_get_drifted_velocities(p, xp, dt_kick_hydro, dt_kick_grav, ret); + + /* Conversion from internal units to peculiar velocities */ + ret[0] *= cosmo->a2_inv; + ret[1] *= cosmo->a2_inv; + ret[2] *= cosmo->a2_inv; +} + +void convert_part_potential(const struct engine* e, const struct part* p, + const struct xpart* xp, float* ret) { + + if (p->gpart != NULL) + ret[0] = gravity_get_comoving_potential(p->gpart); + else + ret[0] = 0.f; +} + +/** + * @brief Specifies which particle fields to write to a dataset + * + * @param parts The particle array. + * @param xparts The extended 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(const struct part* parts, const struct xpart* xparts, + struct io_props* list, int* num_fields) { + + *num_fields = 10; + + /* List what we want to write */ + list[0] = io_make_output_field_convert_part("Coordinates", DOUBLE, 3, + UNIT_CONV_LENGTH, parts, xparts, + convert_part_pos); + list[1] = io_make_output_field_convert_part( + "Velocities", FLOAT, 3, UNIT_CONV_SPEED, parts, xparts, convert_part_vel); + 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("InternalEnergy", FLOAT, 1, + UNIT_CONV_ENERGY_PER_UNIT_MASS, parts, u); + list[5] = io_make_output_field("ParticleIDs", ULONGLONG, 1, + UNIT_CONV_NO_UNITS, parts, id); + list[6] = + io_make_output_field("Density", FLOAT, 1, UNIT_CONV_DENSITY, parts, rho); + list[7] = io_make_output_field("MaterialID", INT, 1, UNIT_CONV_NO_UNITS, + parts, mat_id); + list[8] = io_make_output_field_convert_part( + "Pressure", FLOAT, 1, UNIT_CONV_PRESSURE, parts, xparts, convert_P); + list[9] = io_make_output_field_convert_part("Potential", FLOAT, 1, + UNIT_CONV_POTENTIAL, parts, + xparts, convert_part_potential); +} + +/** + * @brief Writes the current model of SPH to the file + * @param h_grpsph The HDF5 group in which to write + */ +void hydro_write_flavour(hid_t h_grpsph) { + + /* Viscosity and thermal conduction */ + /* Nothing in this minimal model... */ + io_write_attribute_s(h_grpsph, "Thermal Conductivity Model", "No treatment"); + io_write_attribute_s(h_grpsph, "Viscosity Model", + "Minimal treatment as in Monaghan (1992)"); + + /* Time integration properties */ + io_write_attribute_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_MINIMAL_MULTI_MAT_HYDRO_IO_H */ diff --git a/src/hydro/MinimalMultiMat/hydro_part.h b/src/hydro/MinimalMultiMat/hydro_part.h new file mode 100644 index 0000000000000000000000000000000000000000..dad13e889aa531636e34846825109086177b3dae --- /dev/null +++ b/src/hydro/MinimalMultiMat/hydro_part.h @@ -0,0 +1,180 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * 2018 Jacob Kegerreis (jacob.kegerreis@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_MINIMAL_MULTI_MAT_HYDRO_PART_H +#define SWIFT_MINIMAL_MULTI_MAT_HYDRO_PART_H + +/** + * @file MinimalMultiMat/hydro_part.h + * @brief MinimalMultiMat conservative implementation of SPH (Particle + * definition) + * + * The thermal variable is the internal energy (u). Simple constant + * viscosity term without switches is implemented. No thermal conduction + * term is implemented. + * + * This corresponds to equations (43), (44), (45), (101), (103) and (104) with + * \f$\beta=3\f$ and \f$\alpha_u=0\f$ of Price, D., Journal of Computational + * Physics, 2012, Volume 231, Issue 3, pp. 759-794. + */ + +#include "chemistry_struct.h" +#include "cooling_struct.h" +#include "equation_of_state.h" // For enum material_id + +/** + * @brief Particle fields not needed during the SPH loops over neighbours. + * + * This structure contains the particle fields that are not used in the + * density or force loops. Quantities should be used in the kick, drift and + * potentially ghost tasks only. + */ +struct xpart { + + /*! Offset between current position and position at last tree rebuild. */ + float x_diff[3]; + + /*! Offset between the current position and position at the last sort. */ + float x_diff_sort[3]; + + /*! Velocity at the last full step. */ + float v_full[3]; + + /*! Gravitational acceleration at the last full step. */ + float a_grav[3]; + + /*! Internal energy at the last full step. */ + float u_full; + + /*! Additional data used to record cooling information */ + struct cooling_xpart_data cooling_data; + +} SWIFT_STRUCT_ALIGN; + +/** + * @brief Particle fields for the SPH particles + * + * The density and force substructures are used to contain variables only used + * within the density and force loops over neighbours. All more permanent + * variables should be declared in the main part of the part structure, + */ +struct part { + + /*! Particle unique ID. */ + long long id; + + /*! Pointer to corresponding gravity part. */ + struct gpart* gpart; + + /*! Particle position. */ + double x[3]; + + /*! Particle predicted velocity. */ + float v[3]; + + /*! Particle acceleration. */ + float a_hydro[3]; + + /*! Particle mass. */ + float mass; + + /*! Particle smoothing length. */ + float h; + + /*! Particle internal energy. */ + float u; + + /*! Time derivative of the internal energy. */ + float u_dt; + + /*! Particle density. */ + float rho; + + /* Store density/force specific stuff. */ + union { + + /** + * @brief Structure for the variables only used in the density loop over + * neighbours. + * + * Quantities in this sub-structure should only be accessed in the density + * loop over neighbours and the ghost task. + */ + struct { + + /*! Neighbour number count. */ + float wcount; + + /*! Derivative of the neighbour number with respect to h. */ + float wcount_dh; + + /*! Derivative of density with respect to h */ + float rho_dh; + + } density; + + /** + * @brief Structure for the variables only used in the force loop over + * neighbours. + * + * Quantities in this sub-structure should only be accessed in the force + * loop over neighbours and the ghost, drift and kick tasks. + */ + struct { + + /*! "Grad h" term */ + float f; + + /*! Particle pressure. */ + float pressure; + + /*! Particle soundspeed. */ + float soundspeed; + + /*! Particle signal velocity */ + float v_sig; + + /*! Time derivative of smoothing length */ + float h_dt; + + } force; + }; + + /* Chemistry information */ + struct chemistry_part_data chemistry_data; + + /*! Material identifier flag */ + enum eos_planetary_material_id mat_id; + + /*! Time-step length */ + timebin_t time_bin; + +#ifdef SWIFT_DEBUG_CHECKS + + /* Time of the last drift */ + integertime_t ti_drift; + + /* Time of the last kick */ + integertime_t ti_kick; + +#endif + +} SWIFT_STRUCT_ALIGN; + +#endif /* SWIFT_MINIMAL_MULTI_MAT_HYDRO_PART_H */ diff --git a/src/hydro_io.h b/src/hydro_io.h index 639c2f3ae640d7b74e6a2507bd4e3d5ad5625171..c5f9aae9f7bb5f581b09dcd3f309c5fa95f33e51 100644 --- a/src/hydro_io.h +++ b/src/hydro_io.h @@ -35,6 +35,8 @@ #include "./hydro/Gizmo/hydro_io.h" #elif defined(SHADOWFAX_SPH) #include "./hydro/Shadowswift/hydro_io.h" +#elif defined(MINIMAL_MULTI_MAT_SPH) +#include "./hydro/MinimalMultiMat/hydro_io.h" #else #error "Invalid choice of SPH variant" #endif diff --git a/src/part.h b/src/part.h index 3e9ab5180e7cbfb4622b4a05d396a7f785f08b0b..0cfe731e52167825b89c3e732620bf47ba75bdad 100644 --- a/src/part.h +++ b/src/part.h @@ -62,6 +62,9 @@ #include "./hydro/Shadowswift/hydro_part.h" #define hydro_need_extra_init_loop 0 #define EXTRA_HYDRO_LOOP +#elif defined(MINIMAL_MULTI_MAT_SPH) +#include "./hydro/MinimalMultiMat/hydro_part.h" +#define hydro_need_extra_init_loop 0 #else #error "Invalid choice of SPH variant" #endif diff --git a/src/runner.c b/src/runner.c index c275225e3a4bb881545571110795a5beb17bf6e8..5a382a916abda9dddd45dcb5e703578457f0548a 100644 --- a/src/runner.c +++ b/src/runner.c @@ -847,8 +847,9 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) { error("Smoothing length failed to converge on %i particles.", count); } #else - if (count) + if (count) { error("Smoothing length failed to converge on %i particles.", count); + } #endif /* Be clean */ diff --git a/tests/Makefile.am b/tests/Makefile.am index ca80b30254ac82b7fa26220d706713974bcc1a20..fb3384fa56936b3c7c1fe1b415a2201e419ba566 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,16 +1,16 @@ # This file is part of SWIFT. # Copyright (c) 2015 matthieu.schaller@durham.ac.uk. -# +# # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU 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 General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. @@ -27,7 +27,7 @@ TESTS = testGreetings testMaths testReading.sh testSingle testKernel testSymmetr testMatrixInversion testThreadpool testDump testLogger testInteractions.sh \ testVoronoi1D testVoronoi2D testVoronoi3D testGravityDerivatives \ testPeriodicBC.sh testPeriodicBCPerturbed.sh testPotentialSelf \ - testPotentialPair + testPotentialPair testEOS # List of test programs to compile check_PROGRAMS = testGreetings testReading testSingle testTimeIntegration \ @@ -37,7 +37,7 @@ check_PROGRAMS = testGreetings testReading testSingle testTimeIntegration \ testAdiabaticIndex testRiemannExact testRiemannTRRS \ testRiemannHLLC testMatrixInversion testDump testLogger \ testVoronoi1D testVoronoi2D testVoronoi3D testPeriodicBC \ - testGravityDerivatives testPotentialSelf testPotentialPair + testGravityDerivatives testPotentialSelf testPotentialPair testEOS # Rebuild tests when SWIFT is updated. $(check_PROGRAMS): ../src/.libs/libswiftsim.a @@ -105,6 +105,8 @@ testPotentialSelf_SOURCES = testPotentialSelf.c testPotentialPair_SOURCES = testPotentialPair.c +testEOS_SOURCES = testEOS.c + # Files necessary for distribution EXTRA_DIST = testReading.sh makeInput.py testActivePair.sh \ test27cells.sh test27cellsPerturbed.sh testParser.sh testPeriodicBC.sh \ @@ -112,4 +114,5 @@ EXTRA_DIST = testReading.sh makeInput.py testActivePair.sh \ difffloat.py tolerance_125_normal.dat tolerance_125_perturbed.dat \ tolerance_27_normal.dat tolerance_27_perturbed.dat tolerance_27_perturbed_h.dat tolerance_27_perturbed_h2.dat \ tolerance_testInteractions.dat tolerance_pair_active.dat tolerance_pair_force_active.dat \ - fft_params.yml tolerance_periodic_BC_normal.dat tolerance_periodic_BC_perturbed.dat + fft_params.yml tolerance_periodic_BC_normal.dat tolerance_periodic_BC_perturbed.dat \ + testEOS.sh testEOS_plot.sh diff --git a/tests/test125cells.c b/tests/test125cells.c index ddebce8cabda227e96e04996214104354703c185..1dfd5ea73a1a40c1106999addf7f7ef6352b79d9 100644 --- a/tests/test125cells.c +++ b/tests/test125cells.c @@ -120,6 +120,8 @@ void set_energy_state(struct part *part, enum pressure_field press, float size, part->u = pressure / (hydro_gamma_minus_one * density); #elif defined(MINIMAL_SPH) part->u = pressure / (hydro_gamma_minus_one * density); +#elif defined(MINIMAL_MULTI_MAT_SPH) + part->u = pressure / (hydro_gamma_minus_one * density); #elif defined(GIZMO_SPH) || defined(SHADOWFAX_SPH) part->primitives.P = pressure; #else @@ -403,7 +405,8 @@ void dump_particle_fields(char *fileName, struct cell *main_cell, main_cell->parts[pid].v[0], main_cell->parts[pid].v[1], main_cell->parts[pid].v[2], main_cell->parts[pid].h, hydro_get_comoving_density(&main_cell->parts[pid]), -#if defined(MINIMAL_SPH) || defined(SHADOWFAX_SPH) +#if defined(MINIMAL_SPH) || defined(MINIMAL_MULTI_MAT_SPH) || \ + defined(SHADOWFAX_SPH) 0.f, #else main_cell->parts[pid].density.div_v, diff --git a/tests/testEOS.c b/tests/testEOS.c new file mode 100644 index 0000000000000000000000000000000000000000..2e72d3d1768f3a6ea4ab1665a099efeb28f8f3f9 --- /dev/null +++ b/tests/testEOS.c @@ -0,0 +1,278 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk), + * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * 2018 Jacob Kegerreis (jacob.kegerreis@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" + +/* Some standard headers. */ +#include <fenv.h> +#include <float.h> +#include <limits.h> +#include <math.h> +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +/* Conditional headers. */ +#ifdef HAVE_LIBZ +#include <zlib.h> +#endif + +/* Local headers. */ +#include "equation_of_state.h" +#include "swift.h" + +/* Engine policy flags. */ +#ifndef ENGINE_POLICY +#define ENGINE_POLICY engine_policy_none +#endif + +/** + * @brief Write a list of densities, energies, and resulting pressures to file + * from an equation of state. + * + * WORK IN PROGRESS + * + * So far only does the Hubbard & MacFarlane (1980) equations of state. + * + * Usage: + * $ ./testEOS (mat_id) (do_output) + * + * Sys args (optional): + * mat_id | int | Material ID, see equation_of_state.h for the options. + * Default: 201 (= id_HM80_ice). + * + * do_output | int | Set 1 to write the output file of rho, u, P values, + * set 0 for no output. Default: 0. + * + * Output text file contains: + * header + * num_rho num_u mat_id # Header values + * rho_0 rho_1 rho_2 ... rho_num_rho # Array of densities, rho + * u_0 u_1 u_2 ... u_num_u # Array of energies, u + * P_0_0 P_0_1 ... P_0_num_u # Array of pressures, P(rho, u) + * P_1_0 ... ... P_1_num_u + * ... ... ... ... + * P_num_rho_0 ... P_num_rho_num_u + * + * Note that the values tested extend beyond the range that most EOS are + * designed for (e.g. outside table limits), to help test the EOS in case of + * unexpected particle behaviour. + * + */ + +#ifdef EOS_PLANETARY +int main(int argc, char *argv[]) { + float rho, log_rho, log_u, P; + struct unit_system us; + const struct phys_const *phys_const = 0; // Unused placeholder + const struct swift_params *params = 0; // Unused placeholder + const float J_kg_to_erg_g = 1e4; // Convert J/kg to erg/g + char filename[64]; + // Output table params + const int num_rho = 100, num_u = 100; + float log_rho_min = logf(1e-4), log_rho_max = logf(30.f), + log_u_min = logf(1e4), log_u_max = logf(1e10), + log_rho_step = (log_rho_max - log_rho_min) / (num_rho - 1.f), + log_u_step = (log_u_max - log_u_min) / (num_u - 1.f); + float A1_rho[num_rho], A1_u[num_u]; + // Sys args + int mat_id, do_output; + // Default sys args + const int mat_id_def = eos_planetary_id_HM80_ice; + const int do_output_def = 0; + + // Check the number of system arguments and use defaults if not provided + switch (argc) { + case 1: + // Default both + mat_id = mat_id_def; + do_output = do_output_def; + break; + + case 2: + // Read mat_id, default do_output + mat_id = atoi(argv[1]); + do_output = do_output_def; + break; + + case 3: + // Read both + mat_id = atoi(argv[1]); + do_output = atoi(argv[2]); + break; + + default: + error("Invalid number of system arguments!\n"); + mat_id = mat_id_def; // Ignored, just here to keep the compiler happy + do_output = do_output_def; + }; + + /* Greeting message */ + printf("This is %s\n", package_description()); + + // Check material ID + // Material base type + switch ((int)(mat_id / eos_planetary_type_factor)) { + // Tillotson + case eos_planetary_type_Til: + switch (mat_id) { + case eos_planetary_id_Til_iron: + printf(" Tillotson iron \n"); + break; + + case eos_planetary_id_Til_granite: + printf(" Tillotson granite \n"); + break; + + case eos_planetary_id_Til_water: + printf(" Tillotson water \n"); + break; + + default: + error("Unknown material ID! mat_id = %d \n", mat_id); + }; + break; + + // Hubbard & MacFarlane (1980) + case eos_planetary_type_HM80: + switch (mat_id) { + case eos_planetary_id_HM80_HHe: + printf(" Hubbard & MacFarlane (1980) hydrogen-helium atmosphere \n"); + break; + + case eos_planetary_id_HM80_ice: + printf(" Hubbard & MacFarlane (1980) ice mix \n"); + break; + + case eos_planetary_id_HM80_rock: + printf(" Hubbard & MacFarlane (1980) rock mix \n"); + break; + + default: + error("Unknown material ID! mat_id = %d \n", mat_id); + }; + break; + + // ANEOS + case eos_planetary_type_ANEOS: + switch (mat_id) { + case eos_planetary_id_ANEOS_iron: + printf(" ANEOS iron \n"); + break; + + case eos_planetary_id_MANEOS_forsterite: + printf(" MANEOS forsterite \n"); + break; + + default: + error("Unknown material ID! mat_id = %d \n", mat_id); + }; + break; + + // SESAME + case eos_planetary_type_SESAME: + switch (mat_id) { + case eos_planetary_id_SESAME_iron: + printf(" SESAME iron \n"); + break; + + default: + error("Unknown material ID! mat_id = %d \n", mat_id); + }; + break; + + default: + error("Unknown material type! mat_id = %d \n", mat_id); + } + + // Convert to internal units (Earth masses and radii) + units_init(&us, 5.9724e27, 6.3710e8, 1.f, 1.f, 1.f); + log_rho_min -= logf(units_cgs_conversion_factor(&us, UNIT_CONV_DENSITY)); + log_rho_max -= logf(units_cgs_conversion_factor(&us, UNIT_CONV_DENSITY)); + log_u_min += logf(J_kg_to_erg_g / units_cgs_conversion_factor( + &us, UNIT_CONV_ENERGY_PER_UNIT_MASS)); + log_u_max += logf(J_kg_to_erg_g / units_cgs_conversion_factor( + &us, UNIT_CONV_ENERGY_PER_UNIT_MASS)); + + // Initialise the EOS materials + eos_init(&eos, phys_const, &us, params); + + // Output file + sprintf(filename, "testEOS_rho_u_P_%d.txt", mat_id); + FILE *f = fopen(filename, "w"); + if (f == NULL) { + printf("Could not open output file!\n"); + exit(EXIT_FAILURE); + } + + if (do_output == 1) { + fprintf(f, "Density Sp.Int.Energy mat_id \n"); + fprintf(f, "%d %d %d \n", num_rho, num_u, mat_id); + } + + // Densities + log_rho = log_rho_min; + for (int i = 0; i < num_rho; i++) { + A1_rho[i] = exp(log_rho); + log_rho += log_rho_step; + + if (do_output == 1) + fprintf(f, "%.6e ", + A1_rho[i] * units_cgs_conversion_factor(&us, UNIT_CONV_DENSITY)); + } + if (do_output == 1) fprintf(f, "\n"); + + // Sp. int. energies + log_u = log_u_min; + for (int i = 0; i < num_u; i++) { + A1_u[i] = exp(log_u); + log_u += log_u_step; + + if (do_output == 1) + fprintf(f, "%.6e ", A1_u[i] * units_cgs_conversion_factor( + &us, UNIT_CONV_ENERGY_PER_UNIT_MASS)); + } + if (do_output == 1) fprintf(f, "\n"); + + // Pressures + for (int i = 0; i < num_rho; i++) { + rho = A1_rho[i]; + + for (int j = 0; j < num_u; j++) { + P = gas_pressure_from_internal_energy(rho, A1_u[j], mat_id); + + if (do_output == 1) + fprintf(f, "%.6e ", + P * units_cgs_conversion_factor(&us, UNIT_CONV_PRESSURE)); + } + + if (do_output == 1) fprintf(f, "\n"); + } + fclose(f); + + return 0; +} +#else +int main() { return 0; } +#endif diff --git a/tests/testEOS.py b/tests/testEOS.py new file mode 100644 index 0000000000000000000000000000000000000000..363bab200b58c65fa24cc033af4b8d3c04b7b503 --- /dev/null +++ b/tests/testEOS.py @@ -0,0 +1,176 @@ +############################################################################### + # This file is part of SWIFT. + # Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + # 2018 Jacob Kegerreis (jacob.kegerreis@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/>. + # + ############################################################################## +""" +Plot the output of testEOS to show how the equation of state pressure varies +with density and specific internal energy. + +Usage: + python testEOS.py (mat_id) + + Sys args (optional): + mat_id | int | Material ID, see equation_of_state.h for the options. + Default: 201 (= HM80_ice). + +Text file contains: + header + num_rho num_u mat_id # Header info + rho_0 rho_1 rho_2 ... rho_num_rho # Array of densities, rho + u_0 u_1 u_2 ... u_num_u # Array of energies, u + P_0_0 P_0_1 ... P_0_num_u # Array of pressures, P(rho, u) + P_1_0 ... ... P_1_num_u + ... ... ... ... + P_num_rho_0 ... P_num_rho_num_u + +Note that the values tested extend beyond the range that most EOS are +designed for (e.g. outside table limits), to help test the EOS in case of +unexpected particle behaviour. +""" + +# ======== +# Modules and constants +# ======== +from __future__ import division +import numpy as np +import matplotlib +matplotlib.use("Agg") +import matplotlib.pyplot as plt +import sys + +# Material types (copied from src/equation_of_state/planetary/equation_of_state.h) +type_factor = 100 +Di_type = { + 'Til' : 1, + 'HM80' : 2, + 'ANEOS' : 3, + 'SESAME' : 4, +} +Di_material = { + # Tillotson + 'Til_iron' : Di_type['Til']*type_factor, + 'Til_granite' : Di_type['Til']*type_factor + 1, + 'Til_water' : Di_type['Til']*type_factor + 2, + # Hubbard & MacFarlane (1980) Uranus/Neptune + 'HM80_HHe' : Di_type['HM80']*type_factor, # Hydrogen-helium atmosphere + 'HM80_ice' : Di_type['HM80']*type_factor + 1, # H20-CH4-NH3 ice mix + 'HM80_rock' : Di_type['HM80']*type_factor + 2, # SiO2-MgO-FeS-FeO rock mix + # ANEOS + 'ANEOS_iron' : Di_type['ANEOS']*type_factor, + 'MANEOS_forsterite' : Di_type['ANEOS']*type_factor + 1, + # SESAME + 'SESAME_iron' : Di_type['SESAME']*type_factor, +} +# Invert so the mat_id are the keys +Di_mat_id = {mat_id : mat for mat, mat_id in Di_material.iteritems()} + +# Unit conversion +Ba_to_Mbar = 1e-12 +erg_g_to_J_kg = 1e-4 + +if __name__ == '__main__': + # Sys args + try: + mat_id = int(sys.argv[1]) + except IndexError: + mat_id = Di_material['HM80_ice'] + + # Check the material + try: + mat = Di_mat_id[mat_id] + print mat + sys.stdout.flush() + except KeyError: + print "Error: unknown material ID! mat_id = %d" % mat_id + print "Materials:" + for mat_id, mat in sorted(Di_mat_id.iteritems()): + print " %s%s%d" % (mat, (20 - len("%s" % mat))*" ", mat_id) + + filename = "testEOS_rho_u_P_%d.txt" % mat_id + + # Load the header info and density and energy arrays + with open(filename) as f: + f.readline() + num_rho, num_u, mat_id = np.array(f.readline().split(), dtype=int) + A1_rho = np.array(f.readline().split(), dtype=float) + A1_u = np.array(f.readline().split(), dtype=float) + + # Load pressure array + A2_P = np.loadtxt(filename, skiprows=4) + + # Convert pressures from cgs Barye to Mbar + A2_P *= Ba_to_Mbar + # Convert energies from cgs to SI + A1_u *= erg_g_to_J_kg + + # Check that the numbers are right + assert A1_rho.shape == (num_rho,) + assert A1_u.shape == (num_u,) + assert A2_P.shape == (num_rho, num_u) + + # Plot + plt.figure(figsize=(7, 7)) + ax = plt.gca() + + # P(rho) at fixed u + num_u_fix = 9 + A1_idx = np.floor(np.linspace(0, num_u - 1, num_u_fix)).astype(int) + A1_colour = matplotlib.cm.rainbow(np.linspace(0, 1, num_u_fix)) + + for i, idx in enumerate(A1_idx): + plt.plot(A1_rho, A2_P[:, idx], c=A1_colour[i], + label=r"%.2e" % A1_u[idx]) + + plt.legend(title="Sp. Int. Energy (J kg$^{-1}$)") + plt.xscale('log') + plt.yscale('log') + plt.xlabel(r"Density (g cm$^{-3}$)") + plt.ylabel(r"Pressure (Mbar)") + plt.title(mat) + plt.tight_layout() + + plt.savefig("testEOS_%d.png" % mat_id) + plt.close() + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/testEOS.sh b/tests/testEOS.sh new file mode 100755 index 0000000000000000000000000000000000000000..411ac746be186bfe5758e03c2a852e081daefd10 --- /dev/null +++ b/tests/testEOS.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +echo "" + +rm -f testEOS_rho_u_P_*.txt + +echo "Running testEOS for each planetary material" + +A1_mat_id=( + 100 + 101 + 102 + 200 + 201 + 202 +) + +for mat_id in "${A1_mat_id[@]}" +do + ./testEOS "$mat_id" 1 +done + +exit $? diff --git a/tests/testEOS_plot.sh b/tests/testEOS_plot.sh new file mode 100755 index 0000000000000000000000000000000000000000..39108c5e19d8f4474de508e205951a1fd0aebcc9 --- /dev/null +++ b/tests/testEOS_plot.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +echo "" + +echo "Plotting testEOS output for each planetary material" + +A1_mat_id=( + 100 + 101 + 102 + 200 + 201 + 202 +) + +for mat_id in "${A1_mat_id[@]}" +do + python ./testEOS.py "$mat_id" +done + +exit $? diff --git a/tests/testInteractions.c b/tests/testInteractions.c index 9c5dd36415970ff2a53220aa56cecba6fc5fe193..e1581c52863072b9460e764eb65597fff5817a49 100644 --- a/tests/testInteractions.c +++ b/tests/testInteractions.c @@ -104,7 +104,8 @@ struct part *make_particles(size_t count, double *offset, double spacing, */ void prepare_force(struct part *parts, size_t count) { -#if !defined(GIZMO_SPH) && !defined(SHADOWFAX_SPH) && !defined(MINIMAL_SPH) +#if !defined(GIZMO_SPH) && !defined(SHADOWFAX_SPH) && !defined(MINIMAL_SPH) && \ + !defined(MINIMAL_MULTI_MAT_SPH) struct part *p; for (size_t i = 0; i < count; ++i) { p = &parts[i]; @@ -131,7 +132,8 @@ void dump_indv_particle_fields(char *fileName, struct part *p) { "%8.5f %8.5f %13e %13e %13e %13e %13e %8.5f %8.5f\n", p->id, p->x[0], p->x[1], p->x[2], p->v[0], p->v[1], p->v[2], p->h, hydro_get_comoving_density(p), -#if defined(MINIMAL_SPH) || defined(SHADOWFAX_SPH) +#if defined(MINIMAL_SPH) || defined(MINIMAL_MULTI_MAT_SPH) || \ + defined(SHADOWFAX_SPH) 0.f, #else p->density.div_v,