diff --git a/.gitignore b/.gitignore index 1fd274009591c7ac98d71e05d536c98d4c17c485..9772f54a40e1663bf5441cb5fc8cddf03d22f127 100644 --- a/.gitignore +++ b/.gitignore @@ -30,6 +30,7 @@ examples/fof_mpi examples/*/*/*.xmf examples/*/*/*.dat examples/*/*/*.png +examples/*/*/*.pdf examples/*/*/*.mp4 examples/*/*/*.txt examples/*/*/*.rst diff --git a/examples/Cooling/FeedbackEvent_3D/.gitignore b/examples/Cooling/FeedbackEvent_3D/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..3a8485fdcf1c2638f90686598a442bfef6f50f64 --- /dev/null +++ b/examples/Cooling/FeedbackEvent_3D/.gitignore @@ -0,0 +1,2 @@ +nocool_*/* +default_*/* diff --git a/examples/Cooling/FeedbackEvent_3D/README.md b/examples/Cooling/FeedbackEvent_3D/README.md index 236d03484c0a0afe846b464aef64573788fc639e..565990dd302ccb334c67ffa234294c031b8e6d9f 100644 --- a/examples/Cooling/FeedbackEvent_3D/README.md +++ b/examples/Cooling/FeedbackEvent_3D/README.md @@ -16,4 +16,12 @@ This test emulates what the EAGLE model does to particles for feedback, i.e. + Heats a single particle to 10^7.5 K + Does _not_ switch off cooling -+ Runs to completion. \ No newline at end of file ++ Runs to completion. + + +Running Multiple Tests +---------------------- + +If you would like to run a suite of tests, try the runs.sh script. You'll +need to set the directories in the parameter file to be one higher, i.e. +../coolingtables rather than ./coolingtables. diff --git a/examples/Cooling/FeedbackEvent_3D/feedback.yml b/examples/Cooling/FeedbackEvent_3D/feedback.yml index da624834dcd3347593f5d48c849a8c434cd70392..5e4ddfc618ca64e6633772f155abbdc021cedc0e 100644 --- a/examples/Cooling/FeedbackEvent_3D/feedback.yml +++ b/examples/Cooling/FeedbackEvent_3D/feedback.yml @@ -9,20 +9,20 @@ InternalUnitSystem: # Parameters governing the time integration TimeIntegration: time_begin: 0. # The starting time of the simulation (in internal units). - time_end: 1e-3 # The end time of the simulation (in internal units). + time_end: 1e-4 # The end time of the simulation (in internal units). dt_min: 1e-9 # The minimal time-step size of the simulation (in internal units). - dt_max: 1e-3 # The maximal time-step size of the simulation (in internal units). + dt_max: 1e-4 # The maximal time-step size of the simulation (in internal units). # Parameters governing the snapshots Snapshots: basename: feedback # Common part of the name of output files time_first: 0. # Time of the first output (in internal units) - delta_time: 1e-4 # Time difference between consecutive outputs (in internal units) + delta_time: 1e-5 # Time difference between consecutive outputs (in internal units) compression: 1 # Parameters governing the conserved quantities statistics Statistics: - delta_time: 1e-3 # Time between statistics output + delta_time: 1e-6 # Time between statistics output # Parameters for the hydrodynamics scheme SPH: @@ -75,4 +75,16 @@ EAGLECooling: H_reion_eV_p_H: 2.0 He_reion_z_centre: 3.5 He_reion_z_sigma: 0.5 - He_reion_eV_p_H: 2.0 \ No newline at end of file + He_reion_eV_p_H: 2.0 + +# Parameters for the EAGLE "equation of state" +EAGLEEntropyFloor: + Jeans_density_threshold_H_p_cm3: 0.1 # Physical density above which the EAGLE Jeans limiter entropy floor kicks in expressed in Hydrogen atoms per cm^3. + Jeans_over_density_threshold: 10. # Overdensity above which the EAGLE Jeans limiter entropy floor can kick in. + Jeans_temperature_norm_K: 8000 # Temperature of the EAGLE Jeans limiter entropy floor at the density threshold expressed in Kelvin. + Jeans_gamma_effective: 1.3333333 # Slope the of the EAGLE Jeans limiter entropy floor + Cool_density_threshold_H_p_cm3: 1e-5 # Physical density above which the EAGLE Cool limiter entropy floor kicks in expressed in Hydrogen atoms per cm^3. + Cool_over_density_threshold: 10. # Overdensity above which the EAGLE Cool limiter entropy floor can kick in. + Cool_temperature_norm_K: 8000 # Temperature of the EAGLE Cool limiter entropy floor at the density threshold expressed in Kelvin. + Cool_gamma_effective: 1. # Slope the of the EAGLE Cool limiter entropy floor + diff --git a/examples/Cooling/FeedbackEvent_3D/makeIC.py b/examples/Cooling/FeedbackEvent_3D/makeIC.py index 0002dc459730c53500ab75e88d8765701e4937a2..24052cee995675742808df6cc16ae4b523389351 100644 --- a/examples/Cooling/FeedbackEvent_3D/makeIC.py +++ b/examples/Cooling/FeedbackEvent_3D/makeIC.py @@ -30,43 +30,44 @@ import numpy as np # Parameters gamma = 5.0 / 3.0 initial_density = 0.1 * mh / (cm ** 3) -initial_temperature = 1e4 * K +initial_temperature = 2550 * (5/4) * K # Equilibrium temperature at solar abundance inject_temperature = 10 ** (7.5) * K mu = 0.5 particle_mass = 1e6 * msun -unit_system = cosmo_units -file_name = "feedback.hdf5" +if __name__ == "__main__": + unit_system = cosmo_units + file_name = "feedback.hdf5" -# Read in glass file -with h5py.File("glassCube_32.hdf5", "r") as handle: - positions = handle["/PartType0/Coordinates"][:] - h = handle["PartType0/SmoothingLength"][:] * 0.3 + # Read in glass file + with h5py.File("glassCube_32.hdf5", "r") as handle: + positions = handle["/PartType0/Coordinates"][:] + h = handle["PartType0/SmoothingLength"][:] * 0.3 -number_of_particles = len(h) -side_length = (number_of_particles * particle_mass / initial_density) ** (1 / 3) -side_length.convert_to_base(unit_system) + number_of_particles = len(h) + side_length = (number_of_particles * particle_mass / initial_density) ** (1 / 3) + side_length.convert_to_base(unit_system) -print(f"Your box has a side length of {side_length}") + print(f"Your box has a side length of {side_length}") -# Find the central particle -central_particle = np.sum((positions - 0.5) ** 2, axis=1).argmin() + # Find the central particle + central_particle = np.sum((positions - 0.5) ** 2, axis=1).argmin() -# Inject the feedback into that central particle -background_internal_energy = ( - (1.0 / (mu * mh)) * (kb / (gamma - 1.0)) * initial_temperature -) -heated_internal_energy = (1.0 / (mu * mh)) * (kb / (gamma - 1.0)) * inject_temperature -internal_energy = np.ones_like(h) * background_internal_energy -internal_energy[central_particle] = heated_internal_energy + # Inject the feedback into that central particle + background_internal_energy = ( + (1.0 / (mu * mh)) * (kb / (gamma - 1.0)) * initial_temperature + ) + heated_internal_energy = (1.0 / (mu * mh)) * (kb / (gamma - 1.0)) * inject_temperature + internal_energy = np.ones_like(h) * background_internal_energy + internal_energy[central_particle] = heated_internal_energy -# Now we have all the information we need to set up the initial conditions! -output = Writer(unit_system=unit_system, box_size=side_length) + # Now we have all the information we need to set up the initial conditions! + output = Writer(unit_system=unit_system, box_size=side_length) -output.gas.coordinates = positions * side_length -output.gas.velocities = np.zeros_like(positions) * cm / s -output.gas.smoothing_length = h * side_length -output.gas.internal_energy = internal_energy -output.gas.masses = np.ones_like(h) * particle_mass + output.gas.coordinates = positions * side_length + output.gas.velocities = np.zeros_like(positions) * cm / s + output.gas.smoothing_length = h * side_length + output.gas.internal_energy = internal_energy + output.gas.masses = np.ones_like(h) * particle_mass -output.write(file_name) + output.write(file_name) diff --git a/examples/Cooling/FeedbackEvent_3D/plotEnergy.py b/examples/Cooling/FeedbackEvent_3D/plotEnergy.py new file mode 100644 index 0000000000000000000000000000000000000000..6a199928b34d3d5f6ac163bfec70f9610ccafddf --- /dev/null +++ b/examples/Cooling/FeedbackEvent_3D/plotEnergy.py @@ -0,0 +1,79 @@ +""" +Plots the energy from the energy.txt file for this simulation. +""" + +import matplotlib.pyplot as plt +import numpy as np + +from swiftsimio import load + +from unyt import Gyr, erg, mh, kb + +from makeIC import gamma, initial_density, initial_temperature, inject_temperature, mu, particle_mass + +try: + plt.style.use("mnras_durham") +except: + pass + + +# Snapshot for grabbing the units. +snapshot = load("feedback_0000.hdf5") +units = snapshot.metadata.units +energy_units = units.mass * units.length ** 2 / (units.time ** 2) + +data = np.loadtxt("energy.txt").T + +# Assign correct units to each + +time = data[0] * units.time +mass = data[1] * units.mass +total_energy = data[2] * energy_units +kinetic_energy = data[3] * energy_units +thermal_energy = data[4] * energy_units +radiative_cool = data[8] * energy_units + +# Now we have to figure out how much energy we actually 'injected' +background_internal_energy = ( + (1.0 / (mu * mh)) * (kb / (gamma - 1.0)) * initial_temperature +) + +heated_internal_energy = (1.0 / (mu * mh)) * (kb / (gamma - 1.0)) * inject_temperature + +injected_energy = (heated_internal_energy - background_internal_energy) * particle_mass + +# Also want to remove the 'background' energy +n_parts = snapshot.metadata.n_gas +total_background_energy = background_internal_energy * n_parts * particle_mass + +# Now we can plot + +fig, ax = plt.subplots() + +ax.plot( + time.to(Gyr), + (kinetic_energy).to(erg), + label="Kinetic" +) + +ax.plot( + time.to(Gyr), + (thermal_energy - total_background_energy).to(erg), + label="Thermal" +) + +ax.plot( + time.to(Gyr), + (radiative_cool ).to(erg), + label="Lost to cooling" +) + +ax.set_xlim(0, 0.05 * Gyr) + +ax.set_xlabel("Time [Gyr]") +ax.set_ylabel("Energy [erg]") + +ax.legend() + +fig.tight_layout() +fig.savefig("Energy.pdf") \ No newline at end of file diff --git a/examples/Cooling/FeedbackEvent_3D/plotEnergyAll.py b/examples/Cooling/FeedbackEvent_3D/plotEnergyAll.py new file mode 100644 index 0000000000000000000000000000000000000000..be1ec94d93a8aad59c481a6a662a1462073baec6 --- /dev/null +++ b/examples/Cooling/FeedbackEvent_3D/plotEnergyAll.py @@ -0,0 +1,111 @@ +""" +Plots the energy from the energy.txt file for this simulation. +""" + +import matplotlib.pyplot as plt +import numpy as np + +from swiftsimio import load + +from unyt import Gyr, erg, mh, kb, Myr +from scipy.interpolate import interp1d + +from makeIC import gamma, initial_density, initial_temperature, inject_temperature, mu, particle_mass + +try: + plt.style.use("mnras_durham") +except: + pass + +time_to_plot = 25 * Myr +diffusion_parameters = [0.1 * x for x in range(11)] +plot_directory_name = "default_diffmax" + +kinetic_energy_at_time = [] +thermal_energy_at_time = [] +radiative_energy_at_time = [] + +for diffusion in diffusion_parameters: + directory_name = f"{plot_directory_name}_{diffusion:1.1f}" + +# Snapshot for grabbing the units. + snapshot = load(f"{directory_name}/feedback_0000.hdf5") + units = snapshot.metadata.units + energy_units = units.mass * units.length ** 2 / (units.time ** 2) + + data = np.loadtxt(f"{directory_name}/energy.txt").T + +# Assign correct units to each + + time = data[0] * units.time + mass = data[1] * units.mass + total_energy = data[2] * energy_units + kinetic_energy = data[3] * energy_units + thermal_energy = data[4] * energy_units + radiative_cool = data[8] * energy_units + +# Now we have to figure out how much energy we actually 'injected' + background_internal_energy = ( + (1.0 / (mu * mh)) * (kb / (gamma - 1.0)) * initial_temperature + ) + + heated_internal_energy = (1.0 / (mu * mh)) * (kb / (gamma - 1.0)) * inject_temperature + + injected_energy = (heated_internal_energy - background_internal_energy) * particle_mass + +# Also want to remove the 'background' energy + n_parts = snapshot.metadata.n_gas + total_background_energy = background_internal_energy * n_parts * particle_mass + + kinetic_energy_interpolated = interp1d( + time.to(Myr), + kinetic_energy.to(erg) + ) + + thermal_energy_interpolated = interp1d( + time.to(Myr), + (thermal_energy - total_background_energy).to(erg) + ) + + radiative_cool_interpolated = interp1d( + time.to(Myr), + radiative_cool.to(erg) + ) + + kinetic_energy_at_time.append(kinetic_energy_interpolated(time_to_plot.to(Myr))) + thermal_energy_at_time.append(thermal_energy_interpolated(time_to_plot.to(Myr))) + radiative_energy_at_time.append(radiative_cool_interpolated(time_to_plot.to(Myr))) + + +# Now we can plot + + +fig, ax = plt.subplots() + +ax.plot( + diffusion_parameters, + kinetic_energy_at_time, + label="Kinetic" +) + +ax.plot( + diffusion_parameters, + thermal_energy_at_time, + label="Thermal" +) + +ax.plot( + diffusion_parameters, + radiative_energy_at_time, + label="Lost to cooling" +) + +ax.set_xlim(0, 1.0) + +ax.set_xlabel(r"Diffusion $\alpha_{\rm max}$") +ax.set_ylabel(f"Energy in component at $t=${time_to_plot} [erg]") + +ax.legend() + +fig.tight_layout() +fig.savefig("EnergyFuncDiff.pdf") diff --git a/examples/Cooling/FeedbackEvent_3D/plotSolution.py b/examples/Cooling/FeedbackEvent_3D/plotSolution.py index b9cce959883dbfd62c57b0f25c0b515b24308ab9..fe6f93996dafee2b6e81a70d4786374d86355f6f 100644 --- a/examples/Cooling/FeedbackEvent_3D/plotSolution.py +++ b/examples/Cooling/FeedbackEvent_3D/plotSolution.py @@ -29,6 +29,7 @@ from scipy import stats from unyt import cm, s, km, kpc, Pa, msun, K, keV, mh kPa = 1000 * Pa +plot_radius = 7 * kpc from swiftsimio import load @@ -155,7 +156,7 @@ log = dict( v_r=False, v_phi=False, u=False, S=False, P=False, rho=False, visc=False, diff=False ) ylim = dict( - v_r=[-8, 5], u=[3500, 5500], rho=[0.02, 0.15], visc=[0, 2.0], diff=[0, 0.25], + v_r=[-4, 25], u=[4750, 6000], rho=[0.09, 0.16], visc=[0, 2.0], diff=[0, 0.25], P=[3e-18, 10e-18], S=[-0.5e60, 4e60] ) @@ -198,7 +199,7 @@ for key, label in plot.items(): axis.set_xlabel("Radius ($r$) [kpc]", labelpad=0) axis.set_ylabel(label, labelpad=0) - axis.set_xlim(0.0, 0.7 * boxSize.to(kpc).value) + axis.set_xlim(0.0, plot_radius.to(kpc)) try: axis.set_ylim(*ylim[key]) diff --git a/examples/Cooling/FeedbackEvent_3D/run.sh b/examples/Cooling/FeedbackEvent_3D/run.sh index b91c515266eb5fcc1ed86c21651050613188fe9a..9111b8e8cf2cb9b0b333a1b97c04dac3975fac5c 100755 --- a/examples/Cooling/FeedbackEvent_3D/run.sh +++ b/examples/Cooling/FeedbackEvent_3D/run.sh @@ -5,3 +5,4 @@ # Plot the solution python plotSolution.py 5 +python plotEnergy.py diff --git a/examples/Cooling/FeedbackEvent_3D/runs.sh b/examples/Cooling/FeedbackEvent_3D/runs.sh new file mode 100644 index 0000000000000000000000000000000000000000..5e3853f1e196a2bedfcb32579209fd2f614cfadc --- /dev/null +++ b/examples/Cooling/FeedbackEvent_3D/runs.sh @@ -0,0 +1,31 @@ +#!/bin/bash -l + +#SBATCH -J SWIFTDiffusionCalibration +#SBATCH -N 1 +#SBATCH -o swift_diffusion.out +#SBATCH -e swift_diffusion.err +#SBATCH -p cosma +#SBATCH -A durham +#SBATCH --exclusive + +#SBATCH -t 1:00:00 + + +for diffusion_alpha_max in 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0; +do + mkdir default_diffmax_$diffusion_alpha_max + + cd default_diffmax_$diffusion_alpha_max + + ../../../swift --hydro --cooling --limiter --threads=16 --param="SPH:diffusion_alpha_max:${diffusion_alpha_max}" ../feedback.yml 2>&1 | tee output.log + + cd .. + + mkdir nocool_diffmax_$diffusion_alpha_max + + cd nocool_diffmax_$diffusion_alpha_max + + ../../../swift --hydro --temperature --limiter --threads=16 --param="SPH:diffusion_alpha_max:${diffusion_alpha_max}" ../feedback.yml 2>&1 | tee output.log + + cd .. +done diff --git a/examples/HydroTests/BlobTest_3D/makeSliceMovie.py b/examples/HydroTests/BlobTest_3D/makeSliceMovie.py new file mode 100644 index 0000000000000000000000000000000000000000..726c4ac01b9ae5b046a8bb3b6c1b9206764907bb --- /dev/null +++ b/examples/HydroTests/BlobTest_3D/makeSliceMovie.py @@ -0,0 +1,190 @@ +""" +Makes a movie of the output of the blob test. + +Josh Borrow (joshua.borrow@durham.ac.uk) 2019 + +LGPLv3 +""" + +from swiftsimio import load +from swiftsimio.visualisation import slice + +from p_tqdm import p_map + +import matplotlib.pyplot as plt +import numpy as np + +from matplotlib.colors import LogNorm +from matplotlib.animation import FuncAnimation + +info_frames = 15 +start_frame = 0 +end_frame = 101 +resolution = 1024 +snapshot_name = "blob" +cmap = "Spectral_r" +text_args = dict(color="black") +# plot = "pressure" +# name = "Pressure $P$" +plot = "density" +name = "Fluid Density $\\rho$" + + +def get_image(n): + """ + Gets the image for snapshot n, and also returns the associated + SWIFT metadata object. + """ + filename = f"{snapshot_name}_{n:04d}.hdf5" + + data = load(filename) + boxsize = data.metadata.boxsize[0].value + + output = np.zeros((resolution, resolution * 4), dtype=float) + + x, y, z = data.gas.coordinates.value.T + + # This is an oblong box but we can only make squares! + for box, box_edges in enumerate([[0.0, 1.1], [0.9, 2.1], [1.9, 3.1], [2.9, 4.0]]): + mask = np.logical_and(x >= box_edges[0], x <= box_edges[1]) + masked_x = x[mask] - np.float64(box) + masked_y = y[mask] + masked_z = z[mask] + + hsml = data.gas.smoothing_length.value[mask] + + if plot == "density": + mass = data.gas.masses.value[mask] + image = slice( + x=masked_y, + y=masked_x, + z=masked_z, + m=mass, + h=hsml, + z_slice=0.5, + res=resolution, + ) + else: + quantity = getattr(data.gas, plot).value[mask] + # Need to divide out the particle density for non-projected density quantities + image = scatter( + x=masked_y, + y=masked_x, + z=masked_z, + m=quantity, + h=hsml, + z_slice=0.5, + res=resolution, + ) / scatter( + x=masked_y, + y=masked_x, + z=masked_z, + m=np.ones_like(quantity), + h=hsml, + z_slice=0.5, + res=resolution, + ) + + output[:, box * resolution : (box + 1) * resolution] = image + + return output, data.metadata + + +def get_data_dump(metadata): + """ + Gets a big data dump from the SWIFT metadata + """ + + try: + viscosity = metadata.viscosity_info + except: + viscosity = "No info" + + try: + diffusion = metadata.diffusion_info + except: + diffusion = "No info" + + output = ( + "$\\bf{Blob}$ $\\bf{Test}$\n\n" + "$\\bf{SWIFT}$\n" + + metadata.code_info + + "\n\n" + + "$\\bf{Compiler}$\n" + + metadata.compiler_info + + "\n\n" + + "$\\bf{Hydrodynamics}$\n" + + metadata.hydro_info + + "\n\n" + + "$\\bf{Viscosity}$\n" + + viscosity + + "\n\n" + + "$\\bf{Diffusion}$\n" + + diffusion + ) + + return output + + +def time_formatter(metadata): + return f"$t = {metadata.t:2.2f}$" + + +# Generate the frames and unpack our variables +images, metadata = zip(*p_map(get_image, list(range(start_frame, end_frame)))) + +# The edges are funny because of the non-periodicity. +central_region = images[0][ + resolution // 10 : resolution - resolution // 10, + resolution // 10 : resolution - resolution // 10, +] +norm = LogNorm(vmin=np.min(central_region), vmax=np.max(central_region), clip="black") + +fig, ax = plt.subplots(figsize=(8 * 4, 8), dpi=resolution // 8) + +fig.subplots_adjust(0, 0, 1, 1) +ax.axis("off") + +# Set up the initial state +image = ax.imshow(np.zeros_like(images[0]), norm=norm, cmap=cmap, origin="lower") + +description_text = ax.text( + 0.5, + 0.5, + get_data_dump(metadata[0]), + va="center", + ha="center", + **text_args, + transform=ax.transAxes, +) + +time_text = ax.text( + (1 - 0.025 * 0.25), + 0.975, + time_formatter(metadata[0]), + **text_args, + va="top", + ha="right", + transform=ax.transAxes, +) + +info_text = ax.text( + 0.025 * 0.25, 0.975, name, **text_args, va="top", ha="left", transform=ax.transAxes +) + + +def animate(n): + # Display just our original frames at t < 0 + if n >= 0: + image.set_array(images[n]) + description_text.set_text("") + time_text.set_text(time_formatter(metadata[n])) + + return (image,) + + +animation = FuncAnimation( + fig, animate, range(start_frame - info_frames, end_frame), interval=40 +) + +animation.save("blob_slice.mp4") diff --git a/examples/HydroTests/KelvinHelmholtz_2D/makeMovie.py b/examples/HydroTests/KelvinHelmholtz_2D/makeMovie.py index a52784891ab4689dcd59dc27945e573e602785f3..e40ba44dedb6e43dd25f0ce7e0b5681e61048888 100644 --- a/examples/HydroTests/KelvinHelmholtz_2D/makeMovie.py +++ b/examples/HydroTests/KelvinHelmholtz_2D/makeMovie.py @@ -71,7 +71,7 @@ if __name__ == "__main__": import matplotlib.pyplot as plt - filename = "kelvinhelmholtz" + filename = "kelvinHelmholtz" dpi = 512 @@ -93,7 +93,7 @@ if __name__ == "__main__": fig, ax = plt.subplots(1, 1, figsize=(1, 1), frameon=False) ax.axis("off") # Remove annoying black frame. - data_x, data_y, density = load_and_extract("kelvinhelmholtz_0000.hdf5") + data_x, data_y, density = load_and_extract("kelvinHelmholtz_0000.hdf5") x = np.linspace(0, 1, dpi) y = np.linspace(0, 1, dpi) diff --git a/examples/HydroTests/KelvinHelmholtz_2D/makeMovieSwiftsimIO.py b/examples/HydroTests/KelvinHelmholtz_2D/makeMovieSwiftsimIO.py new file mode 100644 index 0000000000000000000000000000000000000000..a86445e0a369bb3697793be72a3053d20f597e45 --- /dev/null +++ b/examples/HydroTests/KelvinHelmholtz_2D/makeMovieSwiftsimIO.py @@ -0,0 +1,102 @@ +""" +Makes a movie of the KH 2D data. + +You will need to run your movie with far higher time-resolution than usual to +get a nice movie; around 450 snapshots over 6s is required. + +Edit this file near the bottom with the number of snaps you have. + +Written by Josh Borrow (joshua.borrow@durham.ac.uk) +""" + +import os +import h5py as h5 +import numpy as np +import scipy.interpolate as si + +from swiftsimio import load +from swiftsimio.visualisation import project_gas_pixel_grid + +def load_and_extract(filename): + """ + Load the data and extract relevant info. + """ + + return load(filename) + + +def make_plot(filename, array, nx, ny, dx, dy): + """ + Load the data and plop it on the grid using nearest + neighbour searching for finding the 'correct' value of + the density. + """ + + data = load_and_extract(filename) + + mesh = project_gas_pixel_grid(data, nx) + + array.set_array(mesh) + + return array, + + +def frame(n, *args): + """ + Make a single frame. Requires the global variables plot and dpi. + """ + + global plot, dpi + + fn = "{}_{:04d}.hdf5".format(filename, n) + + return make_plot(fn, plot, dpi, dpi, (0, 1), (0, 1)) + + +if __name__ == "__main__": + import matplotlib + matplotlib.use("Agg") + + from tqdm import tqdm + from matplotlib.animation import FuncAnimation + from scipy.stats import gaussian_kde + + import matplotlib.pyplot as plt + + filename = "kelvinHelmholtz" + dpi = 512 + + + # Look for the number of files in the directory. + i = 0 + while True: + if os.path.isfile("{}_{:04d}.hdf5".format(filename, i)): + i += 1 + else: + break + + if i > 10000: + raise FileNotFoundError( + "Could not find the snapshots in the directory") + + frames = tqdm(np.arange(0, i)) + + # Creation of first frame + fig, ax = plt.subplots(1, 1, figsize=(1, 1), frameon=False) + ax.axis("off") # Remove annoying black frame. + + data = load_and_extract("kelvinHelmholtz_0000.hdf5") + + + mesh = project_gas_pixel_grid(data, dpi) + + # Global variable for set_array + plot = ax.imshow(mesh, extent=[0, 1, 0, 1], animated=True, interpolation="none") + + anim = FuncAnimation(fig, frame, frames, interval=40, blit=False) + + # Remove all whitespace + fig.subplots_adjust(left=0, bottom=0, right=1, top=1, wspace=None, hspace=None) + + # Actually make the movie + anim.save("khmovie.mp4", dpi=dpi, bitrate=4096) diff --git a/examples/HydroTests/KelvinHelmholtz_2D/run.sh b/examples/HydroTests/KelvinHelmholtz_2D/run.sh index 355bf052a7ad124bcb4d88254ad780a7ffa97aba..ee3cdc4f542e3be1f28ba0deff279c612b9c49b0 100755 --- a/examples/HydroTests/KelvinHelmholtz_2D/run.sh +++ b/examples/HydroTests/KelvinHelmholtz_2D/run.sh @@ -10,6 +10,6 @@ fi # Run SWIFT ../../swift --hydro --threads=4 kelvinHelmholtz.yml 2>&1 | tee output.log + # Plot the solution -python plotSolution.py 6 -python makeMovie.py +python3 makeMovieSwiftsimIO.py diff --git a/examples/HydroTests/SodShock_BCC_3D/plotSolution.py b/examples/HydroTests/SodShock_BCC_3D/plotSolution.py index dc1b15c3eac862365d4422f95a14ffe1713f91a6..365b679991e9a3a5bbb9e9d5108066c04e497c2f 100644 --- a/examples/HydroTests/SodShock_BCC_3D/plotSolution.py +++ b/examples/HydroTests/SodShock_BCC_3D/plotSolution.py @@ -35,7 +35,7 @@ from analyticSolution import analytic snap = int(sys.argv[1]) -sim = load(f"sodshock_{snap:04d}.hdf5") +sim = load(f"sodShock_{snap:04d}.hdf5") # Set up plotting stuff try: diff --git a/examples/HydroTests/SodShock_BCC_3D/sodShock.yml b/examples/HydroTests/SodShock_BCC_3D/sodShock.yml index 373a70bf6b6782d7bdb4ed91417a13270f6521e6..816af9c9ad620ce8617340d749b8ab3e61e53ec6 100644 --- a/examples/HydroTests/SodShock_BCC_3D/sodShock.yml +++ b/examples/HydroTests/SodShock_BCC_3D/sodShock.yml @@ -28,9 +28,6 @@ Statistics: SPH: resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. - viscosity_alpha: 1.0 - viscosity_alpha_max: 1.0 - viscosity_alpha_min: 1.0 # Parameters related to the initial conditions InitialConditions: diff --git a/src/hydro/AnarchyDU/hydro.h b/src/hydro/AnarchyDU/hydro.h index 53c66e316136b172f7610dd6f066c6e841421ced..ac4ed6c2f8176669aa7364109f95de08ddbf722c 100644 --- a/src/hydro/AnarchyDU/hydro.h +++ b/src/hydro/AnarchyDU/hydro.h @@ -616,6 +616,7 @@ __attribute__((always_inline)) INLINE static void hydro_reset_gradient( struct part *restrict p) { p->viscosity.v_sig = 2.f * p->force.soundspeed; + p->force.alpha_visc_max_ngb = p->viscosity.alpha; } /** @@ -774,11 +775,23 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_force( new_diffusion_alpha += alpha_diff_dt * dt_alpha; /* Consistency checks to ensure min < alpha < max */ - new_diffusion_alpha = - min(new_diffusion_alpha, hydro_props->diffusion.alpha_max); new_diffusion_alpha = max(new_diffusion_alpha, hydro_props->diffusion.alpha_min); + /* Now we limit in viscous flows; remove diffusion there. If we + * don't do that, then we end up diffusing energy away in supernovae. + * This is an EAGLE-specific fix. We limit based on the maximal + * viscous alpha over our neighbours in an attempt to keep diffusion + * low near to supernovae sites. */ + + /* This also enforces alpha_diff < alpha_diff_max */ + + const float viscous_diffusion_limit = + hydro_props->diffusion.alpha_max * + (1.f - p->force.alpha_visc_max_ngb / hydro_props->viscosity.alpha_max); + + new_diffusion_alpha = min(new_diffusion_alpha, viscous_diffusion_limit); + p->diffusion.alpha = new_diffusion_alpha; } diff --git a/src/hydro/AnarchyDU/hydro_iact.h b/src/hydro/AnarchyDU/hydro_iact.h index cba945ecae8cbf2971b3d0d810cd565cb8ecc8eb..19489f7460753b15961603e68c22c039f6de27d3 100644 --- a/src/hydro/AnarchyDU/hydro_iact.h +++ b/src/hydro/AnarchyDU/hydro_iact.h @@ -227,6 +227,13 @@ __attribute__((always_inline)) INLINE static void runner_iact_gradient( const float delta_u_factor = (pi->u - pj->u) * r_inv; pi->diffusion.laplace_u += pj->mass * delta_u_factor * wi_dx / pj->rho; pj->diffusion.laplace_u -= pi->mass * delta_u_factor * wj_dx / pi->rho; + + /* Set the maximal alpha from the previous step over the neighbours + * (this is used to limit the diffusion in hydro_prepare_force) */ + const float alpha_i = pi->viscosity.alpha; + const float alpha_j = pj->viscosity.alpha; + pi->force.alpha_visc_max_ngb = max(pi->force.alpha_visc_max_ngb, alpha_j); + pj->force.alpha_visc_max_ngb = max(pj->force.alpha_visc_max_ngb, alpha_i); } /** @@ -289,6 +296,11 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_gradient( const float delta_u_factor = (pi->u - pj->u) * r_inv; pi->diffusion.laplace_u += pj->mass * delta_u_factor * wi_dx / pj->rho; + + /* Set the maximal alpha from the previous step over the neighbours + * (this is used to limit the diffusion in hydro_prepare_force) */ + const float alpha_j = pj->viscosity.alpha; + pi->force.alpha_visc_max_ngb = max(pi->force.alpha_visc_max_ngb, alpha_j); } /** @@ -398,9 +410,9 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( /* Diffusion term */ const float alpha_diff = 0.5f * (pi->diffusion.alpha + pj->diffusion.alpha); - const float v_diff = - alpha_diff * sqrtf(0.5f * fabsf(pressurei - pressurej) / rho_ij) + - fabsf(fac_mu * r_inv * dvdr_Hubble); + const float v_diff = alpha_diff * 0.5f * + (sqrtf(2.f * fabsf(pressurei - pressurej) / rho_ij) + + fabsf(fac_mu * r_inv * dvdr_Hubble)); /* wi_dx + wj_dx / 2 is F_ij */ const float diff_du_term = v_diff * (pi->u - pj->u) * (wi_dr / rhoi + wj_dr / rhoj); @@ -520,9 +532,9 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( /* Diffusion term */ const float alpha_diff = 0.5f * (pi->diffusion.alpha + pj->diffusion.alpha); - const float v_diff = - alpha_diff * sqrtf(0.5f * fabsf(pressurei - pressurej) / rho_ij) + - fabsf(fac_mu * r_inv * dvdr_Hubble); + const float v_diff = alpha_diff * 0.5f * + (sqrtf(2.f * fabsf(pressurei - pressurej) / rho_ij) + + fabsf(fac_mu * r_inv * dvdr_Hubble)); /* wi_dx + wj_dx / 2 is F_ij */ const float diff_du_term = v_diff * (pi->u - pj->u) * (wi_dr / rhoi + wj_dr / rhoj); diff --git a/src/hydro/AnarchyDU/hydro_part.h b/src/hydro/AnarchyDU/hydro_part.h index c30f778b80d91d576052082c6e849fa9d1a4f38e..4b4cc187a96f5111389cde8c50f535a545e9e7f5 100644 --- a/src/hydro/AnarchyDU/hydro_part.h +++ b/src/hydro/AnarchyDU/hydro_part.h @@ -185,6 +185,9 @@ struct part { /*! Balsara switch */ float balsara; + /*! Maximal alpha (viscosity) over neighbours */ + float alpha_visc_max_ngb; + } force; };