{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# 3D Phantom" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We create a Magritte model from a snapshot of 3D Phantom hydrodynamics simulation.\n", "The hydro model was kindly provided by Jolien Malfait.\n", "The Phantom binary files are used directly, and are loaded using the [plons](https://plons.readthedocs.io) package." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Setup" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Import the required functionalty." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "hwloc/linux: Ignoring PCI device with non-16bit domain.\n", "Pass --enable-32bits-pci-domain to configure to support such devices\n", "(warning: it would break the library ABI, don't enable unless really needed).\n" ] } ], "source": [ "import magritte.setup as setup # Model setup\n", "import magritte.core as magritte # Core functionality\n", "import plons # Loading phantom data\n", "import numpy as np # Data structures\n", "import warnings # Hide warnings\n", "warnings.filterwarnings('ignore') # especially for yt\n", "import yt # 3D plotting\n", "import os\n", "\n", "from tqdm import tqdm # Progress bars\n", "from astropy import constants # Unit conversions\n", "from scipy.spatial import Delaunay # Finding neighbors\n", "from yt.funcs import mylog # To avoid yt output \n", "mylog.setLevel(40) # as error messages" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Define a working directory (you will have to change this; it must be an **absolute path**)." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "wdir = \"/lhome/thomasc/Magritte-examples/Phantom_3D/\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Create the working directory." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "!mkdir -p $wdir" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Define file names." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "dump_file = os.path.join(wdir, 'model_Phantom_3D' ) # Phantom full dump (snapshot)\n", "setup_file = os.path.join(wdir, 'wind.setup' ) # Phantom setup file\n", "input_file = os.path.join(wdir, 'wind.in' ) # Phantom input file\n", "model_file = os.path.join(wdir, 'model_Phantom_3D.hdf5') # Resulting Magritte model\n", "lamda_file = os.path.join(wdir, 'co.txt' ) # Line data file" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We use a snapshot and data file that can be downloaded with the following links." ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "dump_link = \"https://github.com/Ensor-code/phantom-models/raw/main/Malfait+2021/v05e50/wind_v05e50\"\n", "setup_link = \"https://raw.githubusercontent.com/Ensor-code/phantom-models/main/Malfait%2B2021/v05e50/wind.setup\"\n", "input_link = \"https://raw.githubusercontent.com/Ensor-code/phantom-models/main/Malfait%2B2021/v05e50/wind.in\"\n", "lamda_link = \"https://home.strw.leidenuniv.nl/~moldata/datafiles/co.dat\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Dowload the snapshot and the linedata (``%%capture`` is just used to suppress the output)." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "%%capture\n", "print(setup_file)\n", "!wget $dump_link --output-document $dump_file\n", "!wget $setup_link --output-document $setup_file\n", "!wget $input_link --output-document $input_file\n", "!wget $lamda_link --output-document $lamda_file" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Extract data" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The script below extracts the required data from the snapshot `phantom dump` file." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Loading the data\n", "setupData = plons.LoadSetup(wdir, \"wind\")\n", "dumpData = plons.LoadFullDump(dump_file, setupData)\n", "\n", "pos_x = dumpData[\"x\"]*1e-2 # x position vectors [cm -> m]\n", "pos_y = dumpData[\"y\"]*1e-2 # y position vectors [cm -> m]\n", "pos_z = dumpData[\"z\"]*1e-2 # z position vectors [cm -> m]\n", "position = np.array((pos_x, pos_y, pos_z)).T\n", "vel_x = dumpData[\"vx\"]*1e3 # x velocity vectors [km/s -> m/s]\n", "vel_y = dumpData[\"vy\"]*1e3 # y velocity vectors [km/s -> m/s]\n", "vel_z = dumpData[\"vz\"]*1e3 # z velocity vectors [km/s -> m/s]\n", "velocity = np.array((vel_x, vel_y, vel_z)).T\n", "velocity = velocity/constants.c.si.value # velocity vectors [m/s -> 1/c]\n", "rho = dumpData[\"rho\"] # density [g/cm^3]\n", "u = dumpData[\"u\"] # internal energy density [erg/g]\n", "tmp = dumpData[\"temp\"] # temperature [K]\n", "tmp[tmp<2.725] = 2.725 # Cut-off temperatures below 2.725 K\n", "\n", "# Extract the number of points\n", "npoints = len(rho)\n", "\n", "# Convenience arrays\n", "zeros = np.zeros(npoints)\n", "ones = np.ones (npoints)\n", "\n", "# Convert rho (total density) to abundances\n", "nH2 = rho * 1.0e+6 * constants.N_A.si.value / 2.02\n", "nCO = nH2 * 1.0e-4\n", "\n", "# Define turbulence at 150 m/s\n", "trb = (150.0/constants.c.si.value)**2 * ones" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "100%|██████████| 6960643/6960643 [00:38<00:00, 178604.41it/s]\n" ] } ], "source": [ "# Extract Delaunay vertices (= Voronoi neighbors)\n", "delaunay = Delaunay(position)\n", "(indptr, indices) = delaunay.vertex_neighbor_vertices\n", "neighbors = [indices[indptr[k]:indptr[k+1]] for k in range(npoints)]\n", "nbs = [n for sublist in neighbors for n in sublist]\n", "n_nbs = [len(sublist) for sublist in neighbors]\n", "\n", "# Compute the indices of the boundary particles of the mesh, extracted from the Delaunay vertices\n", "boundary = set([])\n", "for i in tqdm(range(delaunay.neighbors.shape[0])):\n", " for k in range(4):\n", " if (delaunay.neighbors[i][k] == -1):\n", " nk1,nk2,nk3 = (k+1)%4, (k+2)%4, (k+3)%4 \n", " boundary.add(delaunay.simplices[i][nk1])\n", " boundary.add(delaunay.simplices[i][nk2])\n", " boundary.add(delaunay.simplices[i][nk3])\n", " \n", "boundary = list(boundary)\n", "boundary = np.array(boundary)\n", "\n", "# The above calculation turned out to be unsatisfactory.\n", "# Since the outer boundary is assumed to be a sphere,\n", "# we add all points which fall inside the boundary defined above.\n", "b_nms = np.linalg.norm(position[boundary], axis=1)\n", "p_nms = np.linalg.norm(position, axis=1)\n", "boundary = np.array([i[0] for i in np.argwhere(p_nms >= np.min(b_nms))])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Create model" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now all data is read, we can use it to construct a Magritte model.\n", "\n", "