{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# 3D Phantom" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We create a Magrittetorch 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": [], "source": [ "import magrittetorch.tools.radiativetransferutils as rtutils\n", "import magrittetorch.tools.setup as setup\n", "import torch\n", "from magrittetorch.model.model import Model # Model class\n", "from magrittetorch.model.geometry.geometry import GeometryType # GeometryType enum\n", "from magrittetorch.model.geometry.boundary import BoundaryType # BoundaryType enum\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, units # 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/Magrittetorch-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", "!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": 7, "metadata": {}, "outputs": [], "source": [ "# Loading the data\n", "setupData = plons.LoadSetup(wdir, \"wind\")\n", "dumpData = plons.LoadFullDump(dump_file, setupData)\n", "\n", "position = dumpData[\"position\"]*units.cm # position vectors [cm]\n", "velocity = dumpData[\"velocity\"]*units.km/units.s # velocity vectors [km/s]\n", "rho = dumpData[\"rho\"]*units.g/units.cm**3 # density [g/cm^3]\n", "u = dumpData[\"u\"] # internal energy density [erg/g]\n", "tmp = dumpData[\"Tgas\"]*units.K # temperature [K]\n", "tmp[tmp<2.725*units.K] = 2.725*units.K # Cut-off temperatures below 2.725 K\n", "\n", "# Extract the number of points\n", "npoints = len(rho)\n", "\n", "# Convert rho (total density) to abundances\n", "nH2 = rho * constants.N_A / (2.02 * units.g / units.mol)\n", "nCO = nH2 * 1.0e-4\n", "\n", "# Define turbulence at 150 m/s\n", "trb = 150.0*units.m/units.s" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "100%|██████████| 6960643/6960643 [00:32<00:00, 213188.65it/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))])\n", "#convert to torch tensor\n", "boundary_torch = torch.from_numpy(boundary)\n", "nbs_torch = torch.Tensor(nbs).type(torch.int64)\n", "n_nbs_torch = torch.Tensor(n_nbs).type(torch.int64)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Create model" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now all data is read, we can use it to construct a Magrittetorch model.\n", "\n", "