From 9d80be7dba9c4b0098cecabaa32bae54e9ae16db Mon Sep 17 00:00:00 2001 From: Josh Borrow <joshua.borrow@durham.ac.uk> Date: Thu, 20 Jun 2019 10:14:55 +0100 Subject: [PATCH] Adds the blob test to examples/HydroTests/BlobTest_3D --- examples/HydroTests/BlobTest_3D/README.md | 21 +++ examples/HydroTests/BlobTest_3D/blob.yml | 36 ++++ examples/HydroTests/BlobTest_3D/makeIC.py | 169 +++++++++++++++++++ examples/HydroTests/BlobTest_3D/makeMovie.py | 169 +++++++++++++++++++ examples/HydroTests/BlobTest_3D/run.sh | 13 ++ 5 files changed, 408 insertions(+) create mode 100644 examples/HydroTests/BlobTest_3D/README.md create mode 100644 examples/HydroTests/BlobTest_3D/blob.yml create mode 100644 examples/HydroTests/BlobTest_3D/makeIC.py create mode 100644 examples/HydroTests/BlobTest_3D/makeMovie.py create mode 100755 examples/HydroTests/BlobTest_3D/run.sh diff --git a/examples/HydroTests/BlobTest_3D/README.md b/examples/HydroTests/BlobTest_3D/README.md new file mode 100644 index 0000000000..2bd59abd7a --- /dev/null +++ b/examples/HydroTests/BlobTest_3D/README.md @@ -0,0 +1,21 @@ +Blob Test +========= + +This test is very similar to the one presented in Agertz+ 2007 +(https://ui.adsabs.harvard.edu/abs/2007MNRAS.380..963A/abstract) that tests +un-seeded fluid mixing. + +You can use the makeIC.py script to generate the initial conditions, and +then the makeMovie.py script to look at the results. Note that this is a very +dynamic test, and similarly to the kelvin-helmholtz it does _not_ have an explicit +answer and is best viewed as a movie with more than a grain of salt. + +The expected results are: + ++ For schemes with surface tension terms, the blob stays together ++ For schemes without surface tension terms, the blob breaks up. + +This is at much lower resolution than the original test in Agertz+ 2007. +To change the resolution, you will need to edit the `makeIC.py` file. +A similar resolution to the GAD\_10M presented in that paper can be gained +by using `num_on_side=128`. diff --git a/examples/HydroTests/BlobTest_3D/blob.yml b/examples/HydroTests/BlobTest_3D/blob.yml new file mode 100644 index 0000000000..b319618282 --- /dev/null +++ b/examples/HydroTests/BlobTest_3D/blob.yml @@ -0,0 +1,36 @@ +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 1 # Grams + UnitLength_in_cgs: 1 # Centimeters + UnitVelocity_in_cgs: 1 # Centimeters per second + UnitCurrent_in_cgs: 1 # Amperes + UnitTemp_in_cgs: 1 # Kelvin + +# Parameters governing the time integration +TimeIntegration: + time_begin: 0. # The starting time of the simulation (in internal units). + time_end: 10 # The end time of the simulation (in internal units). + dt_min: 1e-7 # The minimal time-step size of the simulation (in internal units). + dt_max: 1e-2 # The maximal time-step size of the simulation (in internal units). + +# Parameters governing the snapshots +Snapshots: + basename: blob # Common part of the name of output files + time_first: 0. # Time of the first output (in internal units) + delta_time: 0.1 # Time difference between consecutive outputs (in internal units) + compression: 1 + +# Parameters governing the conserved quantities statistics +Statistics: + delta_time: 1e-2 # Time between statistics output + +# Parameters for the hydrodynamics scheme +SPH: + resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). + CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. + +# Parameters related to the initial conditions +InitialConditions: + file_name: ./blob.hdf5 # The file to read + periodic: 1 + cleanup_smoothing_lengths: 1 diff --git a/examples/HydroTests/BlobTest_3D/makeIC.py b/examples/HydroTests/BlobTest_3D/makeIC.py new file mode 100644 index 0000000000..859612f8a6 --- /dev/null +++ b/examples/HydroTests/BlobTest_3D/makeIC.py @@ -0,0 +1,169 @@ +""" +Creates the BCC ICs for the blob test. +""" + +import numpy as np +import h5py + +from unyt import cm, g, s, erg +from unyt.unit_systems import cgs_unit_system + +from swiftsimio import Writer + + +def generate_cube(num_on_side, side_length=1.0): + """ + Generates a cube + """ + + values = np.linspace(0.0, side_length, num_on_side + 1)[:-1] + + positions = np.empty((num_on_side ** 3, 3), dtype=float) + + for x in range(num_on_side): + for y in range(num_on_side): + for z in range(num_on_side): + index = x * num_on_side + y * num_on_side ** 2 + z + + positions[index, 0] = values[x] + positions[index, 1] = values[y] + positions[index, 2] = values[z] + + return positions + + +def generate_bcc_lattice(num_on_side, side_length=1.0): + cube = generate_cube(num_on_side // 2, side_length) + + mips = side_length / num_on_side + + positions = np.concatenate([cube, cube + mips * 0.5]) + + return positions + + +def generate_outside_of_blob( + num_on_side, num_copies_x, side_length, blob_centre_x, blob_radius, initial_velocity +): + """ + Generates the area outside of the blob, including a + cut out for where the blob will fit in. + """ + + bcc_lattice = generate_bcc_lattice(num_on_side) + + # We now need to duplicate. + bcc_lattice_full = np.concatenate( + [bcc_lattice + x * np.array([1.0, 0.0, 0.0]) for x in range(num_copies_x)] + ) + + # Now size it appropriately + bcc_lattice_full *= side_length + + # Now we need to chop out the region that our blob will live in + dx = bcc_lattice_full - np.array( + [blob_centre_x, 0.5 * side_length, 0.5 * side_length] + ) + squared_radius = np.sum(dx * dx, axis=1) + + cut_out_mask = squared_radius > blob_radius ** 2 + + positions = bcc_lattice_full[cut_out_mask] + + # Now we can generate the velocities + velocities = np.zeros_like(positions) + velocities[:, 0] = initial_velocity + + return positions, velocities + + +def generate_blob(num_on_side, side_length, blob_centre_x, blob_radius): + """ + Generate the positions and velocities for the blob. + """ + + bcc_lattice = generate_bcc_lattice(num_on_side) + + # Update to respect side length + bcc_lattice *= side_length + + # Find the radii + squared_radius = np.sum((bcc_lattice - 0.5 * side_length) ** 2, axis=1) + inside_sphere = squared_radius < blob_radius * blob_radius + + # Now select out particles + bcc_lattice = bcc_lattice[inside_sphere] + + # Move to the correct x_position + bcc_lattice[:, 0] = bcc_lattice[:, 0] + blob_centre_x - 0.5 * side_length + positions = bcc_lattice + + # Generate velocities + velocities = np.zeros_like(positions) + + return positions, velocities + + +def write_out_ics( + filename, + num_on_side, + side_length=1.0, + duplications=4, + blob_radius=0.1, + blob_location_x=0.5, + inside_density=10, + velocity_outside=2.7, # Actually the mach number +): + x = Writer( + cgs_unit_system, [side_length * duplications, side_length, side_length] * cm + ) + + positions_outside, velocities_outside = generate_outside_of_blob( + num_on_side, + duplications, + side_length, + blob_location_x, + blob_radius, + initial_velocity=1.0, + ) + positions_inside, velocities_inside = generate_blob( + int(num_on_side * np.cbrt(inside_density)), + side_length, + blob_location_x, + blob_radius, + ) + + coordinates = np.concatenate([positions_inside, positions_outside]) + + velocities = np.concatenate([velocities_inside, velocities_outside]) + + x.gas.coordinates = coordinates * cm + + x.gas.velocities = velocities * cm / s + + x.gas.masses = ( + np.ones(coordinates.shape[0], dtype=float) * (side_length / num_on_side) * g + ) + + # Set to be (1 / n) c_s + x.gas.internal_energy = ( + np.ones(coordinates.shape[0], dtype=float) + * ((1.0 / velocity_outside) * x.gas.velocities[-1][0]) ** 2 + / (5.0 / 3.0 - 1) + ) + + # Need to correct the particles inside the sphere so that we are in pressure equilibrium everywhere + x.gas.internal_energy[: len(positions_inside)] /= inside_density + + x.gas.generate_smoothing_lengths( + boxsize=(duplications / 2) * side_length * cm, dimension=3 + ) + + x.write(filename) + + return + + +if __name__ == "__main__": + write_out_ics(filename="blob.hdf5", num_on_side=64) + diff --git a/examples/HydroTests/BlobTest_3D/makeMovie.py b/examples/HydroTests/BlobTest_3D/makeMovie.py new file mode 100644 index 0000000000..9ae4a538e0 --- /dev/null +++ b/examples/HydroTests/BlobTest_3D/makeMovie.py @@ -0,0 +1,169 @@ +""" +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 scatter + +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, _ = 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] + + hsml = data.gas.smoothing_length.value[mask] + + if plot == "density": + mass = data.gas.masses.value[mask] + image = scatter(x=masked_y, y=masked_x, m=mass, h=hsml, 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, m=quantity, h=hsml, res=resolution + ) / scatter( + x=masked_y, y=masked_x, m=np.ones_like(quantity), h=hsml, 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.mp4") diff --git a/examples/HydroTests/BlobTest_3D/run.sh b/examples/HydroTests/BlobTest_3D/run.sh new file mode 100755 index 0000000000..b59594227c --- /dev/null +++ b/examples/HydroTests/BlobTest_3D/run.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +# Generate the initial conditions if they are not present. +if [ ! -e blob.hdf5 ] +then + echo "Generating initial conditions for the Sod shock example..." + python makeIC.py +fi + +# Run SWIFT +../../swift --hydro --threads=4 blob.yml + +python makeMovie.py -- GitLab