#!/usr/bin/env python # coding: utf-8 # # # # # 1D Alfven Wave `GiRaFFEfood` Initial Data for `GiRaFFE` # # ## This module provides another initial data option for `GiRaFFE`, drawn from [Paschalidis and Shapiro](https://arxiv.org/abs/1310.3274) . # # **Notebook Status:** Validated # # **Validation Notes:** This tutorial notebook has been confirmed to be self-consistent with its corresponding NRPy+ module, as documented [below](#code_validation). The initial data has validated against the original `GiRaFFE`, as documented [here](Tutorial-Start_to_Finish_UnitTest-GiRaFFEfood_NRPy.ipynb). # # ### NRPy+ Source Code for this module: [GiRaFFEfood_NRPy/GiRaFFEfood_NRPy_Alfven_Wave.py](../../edit/in_progress/GiRaFFEfood_NRPy/GiRaFFEfood_NRPy_Alfven_Wave.py) # # ## Introduction: # # ### Alfvén Wave: # # This is a flat-spacetime test with initial data # \begin{align} # A_x &= 0 \\ # A_y &= \left \{ \begin{array}{lll}\gamma_\mu x - 0.015 & \mbox{if} & x \leq -0.1/\gamma_\mu \\ # 1.15 \gamma_\mu x - 0.03g(x) & \mbox{if} & -0.1/\gamma_\mu \leq x \leq 0.1/\gamma_\mu \\ # 1.3 \gamma_\mu x - 0.015 & \mbox{if} & x \geq 0.1/\gamma_\mu \end{array} \right. , \\ # A_z &= \ y - \gamma_\mu (1-\mu)x , # \end{align} # which generates the magnetic field in the wave frame, # \begin{align} # B'^{x'}(x') = &\ 1.0,\ B'^y(x') = 1.0, \\ # B'^z(x') = &\ \left \{ \begin{array}{lll} 1.0 & \mbox{if} & x' \leq -0.1/\gamma_\mu \\ # 1.0+0.15 f(x') & \mbox{if} & -0.1/\gamma_\mu \leq x' \leq 0.1/\gamma_\mu \\ # 1.3 & \mbox{if} & x' \geq 0.1/\gamma_\mu \end{array} \right. . # \end{align} # The electric field in the wave frame is then given by # $$E'^{x'}(x') = -B'^z(0,x') \ \ , \ \ E'^y(x') = 0.0 \ \ , \ \ E'^z(x') = 1.0 .$$ # # These are converted to the grid frame by # \begin{align} # B^x(0,x) = &\ B'^{x'}(\gamma_\mu x) , \\ # B^y(0,x) = &\ \gamma_\mu [ B'^y(\gamma_\mu x) - \mu E'^z(\gamma_\mu x) ] , \\ # B^z(0,x) = &\ \gamma_\mu [ B'^z(\gamma_\mu x) + \mu E'^y(\gamma_\mu x) ] , # \end{align} # and # \begin{align} # E^x(0,x) = &\ E'^{x'}(\gamma_\mu x) , \\ # E^y(0,x) = &\ \gamma_\mu [ E'^y(\gamma_\mu x) + \mu B'^z(\gamma_\mu x) ] ,\\ # E^z(0,x) = &\ \gamma_\mu [ E'^z(\gamma_\mu x) - \mu B'^y(\gamma_\mu x) ], # \end{align} # and the velocity is given by $$\mathbf{v} = \frac{\mathbf{E} \times \mathbf{B}}{B^2}$$ in flat spacetime. Additionally, $f(x)=1+\sin (5\pi x)$, $-1<\mu<1$ is the wave speed relative to the grid frame and $\gamma_\mu = (1-\mu^2)^{-1/2}$, and $g(x) = \cos (5\pi \gamma_\mu x)/\pi$. # # For the eventual purpose of testing convergence, any quantity $Q$ evolves as $Q(t,x) = Q(0,x-\mu t)$ # # See the [Tutorial-GiRaFFEfood_NRPy](Tutorial-GiRaFFEfood_NRPy.ipynb) tutorial notebook for more general detail on how this is used. # # # # # Table of Contents: # $$\label{toc}$$ # # This notebook is organized as follows # # 1. [Step 1](#initializenrpy): Import core NRPy+ modules and set NRPy+ parameters # 1. [Step 2](#set_a_i): Set the vector $A_i$ # 1. [Step 3](#set_vi): Calculate $v^i$ from $B^i$ and $E_i$ # 1. [Step 4](#code_validation): Code Validation against `GiRaFFEfood_NRPy.GiRaFFEfood_NRPy` NRPy+ Module # 1. [Step 5](#latex_pdf_output): Output this notebook to $\LaTeX$-formatted PDF file # # # # Step 1: Import core NRPy+ modules and set NRPy+ parameters \[Back to [top](#toc)\] # $$\label{initializenrpy}$$ # # Here, we will import the NRPy+ core modules and set the reference metric to Cartesian, set commonly used NRPy+ parameters, and set C parameters that will be set from outside the code eventually generated from these expressions. We will also set up a parameter to determine what initial data is set up, although it won't do much yet. # In[1]: # Step 0: Add NRPy's directory to the path # https://stackoverflow.com/questions/16780014/import-file-from-parent-directory import os,sys nrpy_dir_path = os.path.join("..") if nrpy_dir_path not in sys.path: sys.path.append(nrpy_dir_path) # Step 0.a: Import the NRPy+ core modules and set the reference metric to Cartesian import sympy as sp # SymPy: The Python computer algebra package upon which NRPy+ depends import NRPy_param_funcs as par # NRPy+: Parameter interface import indexedexp as ixp # NRPy+: Symbolic indexed expression (e.g., tensors, vectors, etc.) support import GiRaFFEfood_NRPy.GiRaFFEfood_NRPy_Common_Functions as gfcf # Some useful functions for GiRaFFE initial data. import reference_metric as rfm # NRPy+: Reference metric support par.set_parval_from_str("reference_metric::CoordSystem","Cartesian") rfm.reference_metric() # Step 1a: Set commonly used parameters. thismodule = "GiRaFFEfood_NRPy_1D" # ##### # # # Step 2: Set the vector $A_i$ \[Back to [top](#toc)\] # $$\label{set_a_i}$$ # # The vector potential is given as # \begin{align} # A_x &= 0 \\ # A_y &= \left \{ \begin{array}{lll}\gamma_\mu x - 0.015 & \mbox{if} & x \leq -0.1/\gamma_\mu \\ # 1.15 \gamma_\mu x - 0.03g(x) & \mbox{if} & -0.1/\gamma_\mu \leq x \leq 0.1/\gamma_\mu \\ # 1.3 \gamma_\mu x - 0.015 & \mbox{if} & x \geq 0.1/\gamma_\mu \end{array} \right. , \\ # A_z &= y - \gamma_\mu (1-\mu)x . # \end{align} # # However, to take full advantage of NRPy+'s automated function generation capabilities, we want to write this without the `if` statements, replacing them with calls to `fabs()`. To do so, we will use the NRPy+ module `Min_Max_and_Piecewise_Expressions`. # In[2]: mu_AW = par.Cparameters("REAL",thismodule,["mu_AW"], -0.5) # The wave speed M_PI = par.Cparameters("#define",thismodule,["M_PI"], "") gammamu = sp.sympify(1)/sp.sqrt(sp.sympify(1)-mu_AW**2) bound = sp.Rational(1,10)/gammamu def g_AW(x): return sp.cos(sp.sympify(5)*M_PI*gammamu*x)/M_PI # Now, we can define the vector potential. We will rewrite $A_y$ to make use of the functions provided by `Min_Max_and_Piecewise_Expressions`. As shown below, we make sure that at each boundary, each $\leq$ is paired with a $>$. (This choice is arbitrary, we could just as easily choose $<$ and $\geq$.) This does not change the data since the function is continuous. However, it is necessary for the functions in `Min_Max_and_Piecewise_Expressions` to output the correct results. # # \begin{align} # A_x &= 0 \\ # A_y &= \left \{ \begin{array}{lll}\gamma_\mu x - 0.015 & \mbox{if} & x \leq -0.1/\gamma_\mu \\ # 1.15 \gamma_\mu x - 0.03g(x) & \mbox{if} & -0.1/\gamma_\mu < x \leq 0.1/\gamma_\mu \\ # 1.3 \gamma_\mu x - 0.015 & \mbox{if} & x > 0.1/\gamma_\mu \end{array} \right. , \\ # A_z &= y - \gamma_\mu (1-\mu)x . # \end{align} # In[3]: import Min_Max_and_Piecewise_Expressions as noif def Ax_AW(x,y,z, **params): return sp.sympify(0) def Ay_AW(x,y,z, **params): # \gamma_\mu x - 0.015 if x <= -0.1/\gamma_\mu # 1.15 \gamma_\mu x - 0.03g(x) if -0.1/\gamma_\mu < x <= 0.1/\gamma_\mu # 1.3 \gamma_\mu x - 0.015 if x > 0.1/\gamma_\mu Ayleft = gammamu*x - sp.Rational(15,1000) Aycenter = sp.Rational(115,100)*gammamu*x - sp.Rational(3,100)*g_AW(x) Ayright = sp.Rational(13,10)*gammamu*x - sp.Rational(15,1000) out = noif.coord_leq_bound(x,-bound)*Ayleft\ +noif.coord_greater_bound(x,-bound)*noif.coord_leq_bound(x,bound)*Aycenter\ +noif.coord_greater_bound(x,bound)*Ayright return out def Az_AW(x,y,z, **params): # y - \gamma_\mu (1-\mu)x return y-gammamu*(sp.sympify(1)-mu_AW)*x # # # # Step 3: Calculate $v^i$ from $B^i$ and $E_i$ \[Back to [top](#toc)\] # $$\label{set_vi}$$ # # Now, we will set the magnetic and electric fields that we will need to define the initial velocities. First, we need to define $$f(x)=1+\sin (5\pi x);$$ note that in the definition of $B^i$, we need $f(x')$ where $x'=\gamma_\mu x$. # In[4]: def f_AW(x): xprime = gammamu*x return 1 + sp.sin(5*M_PI*xprime) # We will first set the magnetic field in the wave frame, once again rewriting $B'^z(x')$ to be compatible with `Min_Max_and_Piecewise_Expressions`: # \begin{align} # B'^{x'}(x') = &\ 1.0,\ B'^y(x') = 1.0, \\ # B'^z(x') = &\ \left \{ \begin{array}{lll} 1.0 & \mbox{if} & x' \leq -0.1 \\ # 1.0+0.15 f(x') & \mbox{if} & -0.1 < x' \leq 0.1 \\ # 1.3 & \mbox{if} & x' > 0.1 \end{array} \right. . # \end{align} # # Then, we will set the electric field in the wave frame: # \begin{align} # E'^{x'}(x') &= -B'^z(0,x'), \\ # E'^y(x') &= 0.0, \\ # E'^z(x') &= 1.0 . # \end{align} # # Next, we must transform the fields into the grid frame. We'll do the magnetic fields first. # \begin{align} # B^x(0,x) = &\ B'^{x'}(\gamma_\mu x) , \\ # B^y(0,x) = &\ \gamma_\mu [ B'^y(\gamma_\mu x) - \mu E'^z(\gamma_\mu x) ] , \\ # B^z(0,x) = &\ \gamma_\mu [ B'^z(\gamma_\mu x) + \mu E'^y(\gamma_\mu x) ] , # \end{align} # # And finally the electric fields: # \begin{align} # E^x(0,x) = &\ E'^{x'}(\gamma_\mu x) , \\ # E^y(0,x) = &\ \gamma_\mu [ E'^y(\gamma_\mu x) + \mu B'^z(\gamma_\mu x) ] ,\\ # E^z(0,x) = &\ \gamma_\mu [ E'^z(\gamma_\mu x) - \mu B'^y(\gamma_\mu x) ], # \end{align} # # In[5]: #Step 3: Compute v^i from B^i and E_i def ValenciavU_func_AW(**params): x = rfm.xx_to_Cart[0] Bzleft = sp.sympify(1) Bzcenter = sp.sympify(1) + sp.Rational(15,100)*f_AW(x) Bzright = sp.Rational(13,10) BpU = ixp.zerorank1() BpU[0] = sp.sympify(1) BpU[1] = sp.sympify(1) BpU[2] = noif.coord_leq_bound(x,-bound)*Bzleft\ +noif.coord_greater_bound(x,-bound)*noif.coord_leq_bound(x,bound)*Bzcenter\ +noif.coord_greater_bound(x,bound)*Bzright EpU = ixp.zerorank1() EpU[0] = -BpU[2] EpU[1] = sp.sympify(0) EpU[2] = sp.sympify(1) BU = ixp.zerorank1() BU[0] = BpU[0] BU[1] = gammamu*(BpU[1]-mu_AW*EpU[2]) BU[2] = gammamu*(BpU[2]+mu_AW*EpU[1]) EU = ixp.zerorank1() EU[0] = EpU[0] EU[1] = gammamu*(EpU[1]+mu_AW*BpU[2]) EU[2] = gammamu*(EpU[2]-mu_AW*BpU[1]) # In flat space, ED and EU are identical, so we can still use this function. return gfcf.compute_ValenciavU_from_ED_and_BU(EU, BU) # # # # Step 4: Code Validation against `GiRaFFEfood_NRPy.GiRaFFEfood_NRPy` NRPy+ Module \[Back to [top](#toc)\] # $$\label{code_validation}$$ # # Here, as a code validation check, we verify agreement in the SymPy expressions for the `GiRaFFE` Aligned Rotator initial data equations we intend to use between # 1. this tutorial and # 2. the NRPy+ [`GiRaFFEfood_NRPy/GiRaFFEfood_NRPy_1D_tests.py`](../edit/GiRaFFEfood_NRPy/GiRaFFEfood_NRPy_1D_tests.py) module. # # # In[6]: import GiRaFFEfood_NRPy.GiRaFFEfood_NRPy as gf A_awD = gfcf.Axyz_func_Cartesian(Ax_AW,Ay_AW,Az_AW,stagger_enable = True,) Valenciav_awD = ValenciavU_func_AW() gf.GiRaFFEfood_NRPy_generate_initial_data(ID_type = "AlfvenWave", stagger_enable = True) def consistency_check(quantity1,quantity2,string): if quantity1-quantity2==0: print(string+" is in agreement!") else: print(string+" does not agree!") sys.exit(1) print("Consistency check between GiRaFFEfood_NRPy tutorial and NRPy+ module:") for i in range(3): consistency_check(Valenciav_awD[i],gf.ValenciavU[i],"ValenciavU"+str(i)) consistency_check(A_awD[i],gf.AD[i],"AD"+str(i)) # # # # Step 5: Output this notebook to $\LaTeX$-formatted PDF file \[Back to [top](#toc)\] # $$\label{latex_pdf_output}$$ # # The following code cell converts this Jupyter notebook into a proper, clickable $\LaTeX$-formatted PDF file. After the cell is successfully run, the generated PDF may be found in the root NRPy+ tutorial directory, with filename # [Tutorial-GiRaFFEfood_NRPy_1D_tests.pdf](Tutorial-GiRaFFEfood_NRPy_1D_tests.pdf) (Note that clicking on this link may not work; you may need to open the PDF file through another means.) # In[7]: import cmdline_helper as cmd # NRPy+: Multi-platform Python command-line interface cmd.output_Jupyter_notebook_to_LaTeXed_PDF("Tutorial-GiRaFFEfood_NRPy-Alfven_Wave")