diff --git a/.gitignore b/.gitignore
index c54ae8b623..a5e5ebb9a2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,6 +11,8 @@ gamer
GAMER_CompareData
GAMER_ExtractUniform
GAMER_ExtractProfile
+Project
.vscode
src/Makefile
src/Makefile.log
+
diff --git a/doc/wiki/Installation-related/Installation:-Option-List.md b/doc/wiki/Installation-related/Installation:-Option-List.md
index ac4741ff2f..f06c85ae41 100644
--- a/doc/wiki/Installation-related/Installation:-Option-List.md
+++ b/doc/wiki/Installation-related/Installation:-Option-List.md
@@ -49,7 +49,7 @@ disabled). See the "Restriction" of each option carefully.
| `--dual` | `OFF`, `DE_ENPY`, `DE_EINT` | `OFF` | Enable dual energy formalism | Not supported for `--flu_scheme=RTVD`. `DE_EINT` is not supported yet. | `DUAL_ENERGY` |
| `--mhd` | `true`, `false` | `false` | Magnetohydrodynamics | - | `MHD` |
| `--srhd` | `true`, `false` | `false` | Special relativistic hydrodynamics | Must adopt `--eos=TAUBMATHEWS` | `SRHD` |
-| `--cosmic_ray` | `true`, `false` | `false` | Cosmic rays | Must adopt `--eos=COSMIC_RAY` | `COSMIC_RAY` |
+| `--cosmic_ray` | `true`, `false` | `false` | Cosmic rays | Must adopt `--eos=COSMIC_RAY` for `--model=HYDRO` and `--mhd`. Must adopt `--eos=EOS_TAUBMATHEWS` for `--srhd`. | `COSMIC_RAY` |
| `--eos` | `GAMMA`, `ISOTHERMAL`, `COSMIC_RAY`, `TAUBMATHEWS`, `USER` | Depend | [[Equation of state \| equation-of-state]] | The following options only support `GAMMA`: `--flu_scheme=RTVD/CTU`, `--flux=EXACT/ROE`, `--comoving`, `--dual`; see also `--barotropic` | `EOS` |
| `--barotropic` | `true`, `false` | Depend | Is `--eos` barotropic? | Must be disabled for `--eos=GAMMA/COSMIC_RAY/TAUBMATHEWS` and enabled for `--eos=ISOTHERMAL` | `BAROTROPIC_EOS` |
diff --git a/doc/wiki/Runtime-Parameters-related/Runtime-Parameters:-All.md b/doc/wiki/Runtime-Parameters-related/Runtime-Parameters:-All.md
index af754c96b2..ec8e2b9113 100644
--- a/doc/wiki/Runtime-Parameters-related/Runtime-Parameters:-All.md
+++ b/doc/wiki/Runtime-Parameters-related/Runtime-Parameters:-All.md
@@ -401,7 +401,7 @@ For variables with `Default/Min/Max` labeled as `Depend`, click the parameter na
# T
| Name | Default | Min | Max | Short description |
| :--- | :--- | :--- | :--- | :--- |
-| [[ TESTPROB_ID \| Runtime-Parameters:-General#TESTPROB_ID ]] | 0 | 0 | None | test problem ID [0]
0: none
1: HYDRO blast wave [+MHD]
2: HYDRO acoustic wave
3: HYDRO Bondi accretion (+GRAVITY)
4: HYDRO cluster merger vs. Flash (+GRAVITY & PARTICLE)
5: HYDRO AGORA isolated galaxy (+GRAVITY & PARTICLE & STAR_FORMATION & GRACKLE)
6: HYDRO caustic wave
7: HYDRO spherical collapse (+GRAVITY & COMOVING)
8: HYDRO Kelvin Helmholtz instability
9: HYDRO Riemann problems [+MHD]
10: HYDRO jet(s)
11: HYDRO Plummer cloud(s) (+GRAVITY & PARTICLE)
12: HYDRO gravity (+GRAVITY)
13: HYDRO MHD Arnold-Beltrami-Childress (ABC) flow (+MHD)
14: HYDRO MHD Orszag-Tang vortex (+MHD)
15: HYDRO MHD linear wave (+MHD)
16: HYDRO Jeans instability (+GRAVITY) [+MHD]
17: HYDRO particle in equilibrium (+GRAVITY & PARTICLE)
19: HYDRO energy power spectrum
20: HYDRO MHD Cosmic Ray Soundwave
21: HYDRO MHD Cosmic Ray Shocktube
23: HYDRO MHD Cosmic Ray Diffusion
100: HYDRO CDM cosmological simulation (+GRAVITY & COMOVING & PARTICLE)
101: HYDRO Zeldovich pancake collapse (+GRAVITY & COMOVING & PARTICLE)
1000: ELBDM external potential (+GRAVITY)
1001: ELBDM Jeans instability in the comoving frame (+GRAVITY, +COMOVING)
1002: ELBDM Jeans instability in the physical frame (+GRAVITY)
1003: ELBDM soliton merger (+GRAVITY)
1004: ELBDM self-similar halo (+GRAVITY, +COMOVING)
1005: ELBDM rotating vortex pair
1006: ELBDM vortex pair in linear motion
1007: ELBDM halo extracted from a large-scale structure simulation (+GRAVITY)
1008: ELBDM 1D Gaussian wave packet
1009: ELBDM large-scale structure simulation (+GRAVITY, +COMOVING)
1010: ELBDM plane wave
1011: ELBDM small wave perturbations on homogeneous background |
+| [[ TESTPROB_ID \| Runtime-Parameters:-General#TESTPROB_ID ]] | 0 | 0 | None | test problem ID [0]
0: none
1: HYDRO blast wave [+MHD]
2: HYDRO acoustic wave
3: HYDRO Bondi accretion (+GRAVITY)
4: HYDRO cluster merger vs. Flash (+GRAVITY & PARTICLE)
5: HYDRO AGORA isolated galaxy (+GRAVITY & PARTICLE & STAR_FORMATION & GRACKLE)
6: HYDRO caustic wave
7: HYDRO spherical collapse (+GRAVITY & COMOVING)
8: HYDRO Kelvin Helmholtz instability
9: HYDRO Riemann problems [+MHD]
10: HYDRO jet(s)
11: HYDRO Plummer cloud(s) (+GRAVITY & PARTICLE)
12: HYDRO gravity (+GRAVITY)
13: HYDRO MHD Arnold-Beltrami-Childress (ABC) flow (+MHD)
14: HYDRO MHD Orszag-Tang vortex (+MHD)
15: HYDRO MHD linear wave (+MHD)
16: HYDRO Jeans instability (+GRAVITY) [+MHD]
17: HYDRO particle in equilibrium (+GRAVITY & PARTICLE)
19: HYDRO energy power spectrum
20: HYDRO MHD Cosmic Ray Soundwave
21: HYDRO MHD Cosmic Ray Shocktube
23: HYDRO MHD Cosmic Ray Diffusion
24: HYDRO SRHD Fermi Bubble
100: HYDRO CDM cosmological simulation (+GRAVITY & COMOVING & PARTICLE)
101: HYDRO Zeldovich pancake collapse (+GRAVITY & COMOVING & PARTICLE)
1000: ELBDM external potential (+GRAVITY)
1001: ELBDM Jeans instability in the comoving frame (+GRAVITY, +COMOVING)
1002: ELBDM Jeans instability in the physical frame (+GRAVITY)
1003: ELBDM soliton merger (+GRAVITY)
1004: ELBDM self-similar halo (+GRAVITY, +COMOVING)
1005: ELBDM rotating vortex pair
1006: ELBDM vortex pair in linear motion
1007: ELBDM halo extracted from a large-scale structure simulation (+GRAVITY)
1008: ELBDM 1D Gaussian wave packet
1009: ELBDM large-scale structure simulation (+GRAVITY, +COMOVING)
1010: ELBDM plane wave
1011: ELBDM small wave perturbations on homogeneous background |
# U
| Name | Default | Min | Max | Short description |
diff --git a/example/test_problem/Hydro/FermiBubble/Input__Flag_PresGradient b/example/test_problem/Hydro/FermiBubble/Input__Flag_PresGradient
new file mode 100644
index 0000000000..e410856aac
--- /dev/null
+++ b/example/test_problem/Hydro/FermiBubble/Input__Flag_PresGradient
@@ -0,0 +1,13 @@
+# Level Threshold
+ 0 0.3
+ 1 0.3
+ 2 0.3
+ 3 0.3
+ 4 0.3
+ 5 0.3
+ 6 1e99
+ 7 1e99
+ 8 1e99
+ 9 1e99
+ 10 1e99
+ 11 1e99
diff --git a/example/test_problem/Hydro/FermiBubble/Input__Flag_RhoGradient b/example/test_problem/Hydro/FermiBubble/Input__Flag_RhoGradient
new file mode 100644
index 0000000000..6faade9335
--- /dev/null
+++ b/example/test_problem/Hydro/FermiBubble/Input__Flag_RhoGradient
@@ -0,0 +1,16 @@
+# Level Threshold
+ 0 0.3
+ 1 0.3
+ 2 0.3
+ 3 0.3
+ 4 0.3
+ 5 0.3
+ 6 1e99
+ 7 1e99
+ 8 1e99
+ 9 1e99
+ 10 1e99
+ 11 1e99
+ 12 1e99
+ 13 1e99
+ 14 1e99
diff --git a/example/test_problem/Hydro/FermiBubble/Input__Flag_User b/example/test_problem/Hydro/FermiBubble/Input__Flag_User
new file mode 100644
index 0000000000..7c5122dfb2
--- /dev/null
+++ b/example/test_problem/Hydro/FermiBubble/Input__Flag_User
@@ -0,0 +1,16 @@
+# Level Threshold
+ 0 0.01
+ 1 0.01
+ 2 0.01
+ 3 0.01
+ 4 0.01
+ 5 1e99
+ 6 1e99
+ 7 1e99
+ 8 1e99
+ 9 1e99
+ 10 1e99
+ 11 1e99
+ 11 1e99
+ 11 1e99
+ 11 1e99
diff --git a/example/test_problem/Hydro/FermiBubble/Input__Parameter b/example/test_problem/Hydro/FermiBubble/Input__Parameter
new file mode 100644
index 0000000000..3df9d1ce84
--- /dev/null
+++ b/example/test_problem/Hydro/FermiBubble/Input__Parameter
@@ -0,0 +1,297 @@
+
+
+# =================================================================================================================
+# NOTE:
+# 1. Comment symbol: #
+# 2. [*]: defaults
+# 3. Parameters set to "auto" (usually by setting to a negative value) do not have deterministic default values
+# and will be set according to the adopted compilation options and/or other runtime parameters
+# 4. To add new parameters, please edit "Init/Init_Load_Parameter.cpp"
+# 5. All dimensional variables should be set consistently with the code units (set by UNIT_L/M/T/V/D) unless
+# otherwise specified (e.g., SF_CREATE_STAR_MIN_GAS_DENS & SF_CREATE_STAR_MIN_STAR_MASS)
+# 6. For boolean options: 0/1 -> off/on
+# =================================================================================================================
+
+
+# simulation scale
+BOX_SIZE 28.0 # box size along the longest side
+NX0_TOT_X 16 # number of base-level cells along x
+NX0_TOT_Y 16 # number of base-level cells along y
+NX0_TOT_Z 32 # number of base-level cells along z
+OMP_NTHREAD -1 # number of OpenMP threads (<=0=auto) [-1] ##OPENMP ONLY##
+END_T 3.8e3 # end physical time (<0=auto -> must be set by test problems or restart) [-1.0]
+# END_T 3.14872516940871e+01
+END_STEP -1 # end step (<0=auto -> must be set by test problems or restart) [-1]
+
+
+# test problems
+TESTPROB_ID 24 # test problem ID [0]
+ # 0: none
+ # 1: HYDRO blast wave [+MHD]
+ # 2: HYDRO acoustic wave
+ # 3: HYDRO Bondi accretion (+GRAVITY)
+ # 4: HYDRO cluster merger vs. Flash (+GRAVITY & PARTICLE)
+ # 5: HYDRO AGORA isolated galaxy (+GRAVITY & PARTICLE & STAR_FORMATION & GRACKLE)
+ # 6: HYDRO caustic wave
+ # 7: HYDRO spherical collapse (+GRAVITY & COMOVING)
+ # 8: HYDRO Kelvin Helmholtz instability
+ # 9: HYDRO Riemann problems [+MHD]
+ # 10: HYDRO jet(s)
+ # 11: HYDRO Plummer cloud(s) (+GRAVITY & PARTICLE)
+ # 12: HYDRO gravity (+GRAVITY)
+ # 13: HYDRO MHD Arnold-Beltrami-Childress (ABC) flow (+MHD)
+ # 14: HYDRO MHD Orszag-Tang vortex (+MHD)
+ # 15: HYDRO MHD linear wave (+MHD)
+ # 16: HYDRO Jeans instability (+GRAVITY) [+MHD]
+ # 17: HYDRO particle in equilibrium (+GRAVITY & PARTICLE)
+ # 19: HYDRO energy power spectrum
+ # 20: HYDRO MHD Cosmic Ray Soundwave
+ # 21: HYDRO MHD Cosmic Ray Shocktube
+ # 23: HYDRO MHD Cosmic Ray Diffusion
+ # 24: HYDRO SRHD Fermi Bubble
+ # 100: HYDRO CDM cosmological simulation (+GRAVITY & COMOVING & PARTICLE)
+ # 101: HYDRO Zeldovich pancake collapse (+GRAVITY & COMOVING & PARTICLE)
+ # 1000: ELBDM external potential (+GRAVITY)
+
+
+# code units (in cgs)
+OPT__UNIT 1 # specify code units -> must set exactly 3 basic units below [0] ##USELESS FOR COMOVING##
+UNIT_L 3.08567758149e21 # length unit (<=0 -> set to UNIT_V*UNIT_T or (UNIT_M/UNIT_D)^(1/3)) [-1.0]
+UNIT_M 0 # mass unit (<=0 -> set to UNIT_D*UNIT_L^3) [-1.0]
+UNIT_T 0 # time unit (<=0 -> set to UNIT_L/UNIT_V) [-1.0]
+UNIT_V 0 # velocity unit (<=0 -> set to UNIT_L/UNIT_T) [-1.0]
+UNIT_D 1e-24 # mass density unit (<=0 -> set to UNIT_M/UNIT_L^3) [-1.0]
+
+
+# boundary conditions
+OPT__BC_FLU_XM 2 # fluid boundary condition at the -x face: (1=periodic, 2=outflow, 3=reflecting, 4=user, 5=diode) ##2/3/5 for HYDRO ONLY##
+OPT__BC_FLU_XP 2 # fluid boundary condition at the +x face: (1=periodic, 2=outflow, 3=reflecting, 4=user, 5=diode) ##2/3/5 for HYDRO ONLY##
+OPT__BC_FLU_YM 2 # fluid boundary condition at the -y face: (1=periodic, 2=outflow, 3=reflecting, 4=user, 5=diode) ##2/3/5 for HYDRO ONLY##
+OPT__BC_FLU_YP 2 # fluid boundary condition at the +y face: (1=periodic, 2=outflow, 3=reflecting, 4=user, 5=diode) ##2/3/5 for HYDRO ONLY##
+OPT__BC_FLU_ZM 2 # fluid boundary condition at the -z face: (1=periodic, 2=outflow, 3=reflecting, 4=user, 5=diode) ##2/3/5 for HYDRO ONLY##
+OPT__BC_FLU_ZP 2 # fluid boundary condition at the +z face: (1=periodic, 2=outflow, 3=reflecting, 4=user, 5=diode) ##2/3/5 for HYDRO ONLY##
+OPT__BC_POT 1 # gravity boundary condition: (1=periodic, 2=isolated)
+
+
+# time-step
+DT__MAX -1.0 # dt criterion: maximum allowed dt (<0=off) [-1.0]
+DT__FLUID 0.4 # dt criterion: fluid solver CFL factor (<0=auto) [-1.0]
+DT__FLUID_INIT 0.4 # dt criterion: DT__FLUID at the first step (<0=auto) [-1.0]
+DT__SPEED_OF_LIGHT 0 # dt criterion: speed of light [0] ##SRHD ONLY##
+DT__GRAVITY 0.1 # dt criterion: gravity solver safety factor (<0=auto) [-1.0]
+DT__SYNC_PARENT_LV 0.1 # dt criterion: allow dt to adjust by (1.0+DT__SYNC_PARENT) in order to synchronize
+ # with the parent level (for OPT__DT_LEVEL==3 only) [0.1]
+DT__SYNC_CHILDREN_LV 0.1 # dt criterion: allow dt to adjust by (1.0-DT__SYNC_CHILDREN) in order to synchronize
+ # with the children level (for OPT__DT_LEVEL==3 only; 0=off) [0.1]
+OPT__DT_USER 0 # dt criterion: user-defined -> edit "Mis_GetTimeStep_UserCriteria.cpp" [0]
+OPT__DT_LEVEL 3 # dt at different AMR levels (1=shared, 2=differ by two, 3=flexible) [3]
+OPT__RECORD_DT 1 # record info of the dt determination [1]
+AUTO_REDUCE_DT 1 # reduce dt automatically when the program fails (for OPT__DT_LEVEL==3 only) [1]
+AUTO_REDUCE_DT_FACTOR 0.8 # reduce dt by a factor of AUTO_REDUCE_DT_FACTOR when the program fails [0.8]
+AUTO_REDUCE_DT_FACTOR_MIN 0.1 # minimum allowed AUTO_REDUCE_DT_FACTOR after consecutive failures [0.1]
+AUTO_REDUCE_MINMOD_FACTOR 0.8 # reduce MINMOD_COEFF by this factor together with AUTO_REDUCE_DT (1.0=off) [0.8] ##HYDRO ONLY##
+AUTO_REDUCE_MINMOD_MIN 1.0e-2 # minimum allowed MINMOD_COEFF after consecutive failures [1.0e-2] ##HYDRO ONLY##
+AUTO_REDUCE_INT_MONO_FACTOR 0.8 # reduce INT_MONO_COEFF(_B) by this factor together with AUTO_REDUCE_DT (1.0=off) [0.8]
+AUTO_REDUCE_INT_MONO_MIN 1.0e-2 # minimum allowed INT_MONO_COEFF(_B) after consecutive failures [1.0e-2]
+
+
+# grid refinement (examples of Input__Flag_XXX tables are put at "example/input/")
+REGRID_COUNT 4 # refine every REGRID_COUNT sub-step [4]
+REFINE_NLEVEL 1 # number of new AMR levels to be created at once during refinement [1]
+FLAG_BUFFER_SIZE 8 # number of buffer cells for the flag operation (0~PATCH_SIZE) [PATCH_SIZE]
+FLAG_BUFFER_SIZE_MAXM1_LV -1 # FLAG_BUFFER_SIZE at the level MAX_LEVEL-1 (<0=auto -> FLAG_BUFFER_SIZE) [-1]
+FLAG_BUFFER_SIZE_MAXM2_LV -1 # FLAG_BUFFER_SIZE at the level MAX_LEVEL-2 (<0=auto) [-1]
+MAX_LEVEL 10 # maximum refinement level (0~NLEVEL-1) [NLEVEL-1]
+OPT__FLAG_RHO 0 # flag: density (Input__Flag_Rho) [0]
+OPT__FLAG_RHO_GRADIENT 1 # flag: density gradient (Input__Flag_RhoGradient) [0]
+OPT__FLAG_PRES_GRADIENT 1 # flag: pressure gradient (Input__Flag_PresGradient) [0] ##HYDRO/SRHD##
+OPT__FLAG_LRTZ_GRADIENT 0 # flag: Lorentz factor gradient (Input__Flag_LrtzGradient) [0] ##SRHD ONLY##
+OPT__FLAG_VORTICITY 0 # flag: vorticity (Input__Flag_Vorticity) [0] ##HYDRO ONLY##
+OPT__FLAG_JEANS 0 # flag: Jeans length (Input__Flag_Jeans) [0] ##HYDRO ONLY##
+OPT__FLAG_CURRENT 0 # flag: current density in MHD (Input__Flag_Current) [0] ##MHD ONLY##
+OPT__FLAG_CRAY 0 # flag: cosmic-ray energy (Input__Flag_CRay) [0] ##COSMIC_RAY ONLY##
+OPT__FLAG_ENGY_DENSITY 0 # flag: energy density (Input_Flag_EngyDensity) [0] ##ELBDM ONLY##
+OPT__FLAG_LOHNER_DENS 0 # flag: Lohner for mass density (Input__Flag_Lohner) [0] ##HYDRO/SRHD/ELBDM##
+OPT__FLAG_LOHNER_ENGY 0 # flag: Lohner for energy density (Input__Flag_Lohner) [0] ##HYDRO/SRHD##
+OPT__FLAG_LOHNER_PRES 0 # flag: Lohner for pressure (Input__Flag_Lohner) [0] ##HYDRO/SRHD##
+OPT__FLAG_LOHNER_TEMP 0 # flag: Lohner for temperature (Input__Flag_Lohner) [0] ##HYDRO/SRHD##
+OPT__FLAG_LOHNER_ENTR 0 # flag: Lohner for entropy (Input__Flag_Lohner) [0] ##HYDRO ONLY##
+OPT__FLAG_LOHNER_CRAY 0 # flag: Lohner for cosmic-ray energy (Input__Flag_Lohner) [0] ##COSMIC_RAY ONLY##
+OPT__FLAG_LOHNER_FORM 2 # form of Lohner: (1=FLASH-1, 2=FLASH-2, 3=form-invariant-1, 4=form-invariant-2) [2]
+OPT__FLAG_USER 1 # flag: user-defined (Input__Flag_User) -> edit "Flag_User.cpp" [0]
+OPT__FLAG_USER_NUM 1 # number of threshold values in user-defined table (Input__Flag_User) [1]
+OPT__FLAG_REGION 1 # flag: specify the regions **allowed** to be refined -> edit "Flag_Region.cpp" [0]
+OPT__NO_FLAG_NEAR_BOUNDARY 0 # flag: disallow refinement near the boundaries [0]
+OPT__PATCH_COUNT 1 # record the # of patches at each level: (0=off, 1=every step, 2=every sub-step) [1]
+OPT__REUSE_MEMORY 2 # reuse patch memory to reduce memory fragmentation: (0=off, 1=on, 2=aggressive) [2]
+OPT__MEMORY_POOL 0 # preallocate patches for OPT__REUSE_MEMORY=1/2 (Input__MemoryPool) [0]
+
+
+# load balance (LOAD_BALANCE only)
+LB_INPUT__WLI_MAX 0.1 # weighted-load-imbalance (WLI) threshold for redistributing all patches [0.1]
+OPT__RECORD_LOAD_BALANCE 1 # record the load-balance info [1]
+OPT__MINIMIZE_MPI_BARRIER 1 # minimize MPI barriers to improve load balance, especially with particles [1]
+ # (STORE_POT_GHOST, PAR_IMPROVE_ACC=1, OPT__TIMING_BARRIER=0 only; recommend AUTO_REDUCE_DT=0)
+
+
+# fluid solver in HYDRO/SRHD (MODEL==HYDRO only)
+GAMMA 1.333333333333333333 # ratio of specific heats (i.e., adiabatic index)
+MOLECULAR_WEIGHT 1.0 # mean molecular weight -> currently only for post-processing [0.6]
+MU_NORM -1.0 # normalization of MOLECULAR_WEIGHT (<0=m_H, 0=amu, >0=input manually) [-1.0]
+ISO_TEMP 1.0e4 # isothermal temperature in kelvin ##EOS_ISOTHERMAL ONLY##
+MINMOD_COEFF 1.5 # coefficient of the generalized MinMod limiter (1.0~2.0) [1.5]
+MINMOD_MAX_ITER 0 # maximum number of iterations to reduce MINMOD_COEFF when data reconstruction fails (0=off) [0]
+OPT__LR_LIMITER 4 # slope limiter of data reconstruction in the MHM/MHM_RP/CTU schemes:
+ # (-1=auto, 0=none, 1=vanLeer, 2=generalized MinMod, 3=vanAlbada, 4=vanLeer+generalized MinMod, 6=central, 7=Athena) [-1]
+OPT__1ST_FLUX_CORR -1 # correct unphysical results (defined by MIN_DENS/PRES) by the 1st-order fluxes:
+ # (<0=auto, 0=off, 1=3D, 2=3D+1D) [-1] ##MHM/MHM_RP/CTU ONLY##
+OPT__1ST_FLUX_CORR_SCHEME -1 # Riemann solver for OPT__1ST_FLUX_CORR (-1=auto, 0=none, 1=Roe, 2=HLLC, 3=HLLE, 4=HLLD) [-1]
+DUAL_ENERGY_SWITCH 2.0e-2 # apply dual-energy if E_int/E_kin < DUAL_ENERGY_SWITCH [2.0e-2] ##DUAL_ENERGY ONLY##
+
+
+# fluid solvers in all models
+FLU_GPU_NPGROUP -1 # number of patch groups sent into the CPU/GPU fluid solver (<=0=auto) [-1]
+GPU_NSTREAM -1 # number of CUDA streams for the asynchronous memory copy in GPU (<=0=auto) [-1]
+OPT__FIXUP_FLUX 1 # correct coarse grids by the fine-grid boundary fluxes [1] ##HYDRO and ELBDM ONLY##
+OPT__FIXUP_ELECTRIC 1 # correct coarse grids by the fine-grid boundary electric field [1] ##MHD ONLY##
+OPT__FIXUP_RESTRICT 1 # correct coarse grids by averaging the fine-grid data [1]
+OPT__CORR_AFTER_ALL_SYNC -1 # apply various corrections after all levels are synchronized (see "Flu_CorrAfterAllSync"):
+ # (-1=auto, 0=off, 1=every step, 2=before dump) [-1]
+OPT__NORMALIZE_PASSIVE 0 # ensure "sum(passive_scalar_density) == gas_density" [1]
+OPT__OVERLAP_MPI 0 # overlap MPI communication with CPU/GPU computations [0] ##NOT SUPPORTED YET##
+OPT__RESET_FLUID 1 # reset fluid variables after each update -> edit "Flu_ResetByUser.cpp" [0]
+MIN_DENS 0.0 # minimum mass density (must >= 0.0) [0.0] ##HYDRO/SRHD/MHD/ELBDM ONLY##
+MIN_PRES 0.0 # minimum pressure (must >= 0.0) [0.0] ##HYDRO and MHD ONLY##
+MIN_EINT 0.0 # minimum internal energy (must >= 0.0) [0.0] ##HYDRO and MHD ONLY##
+
+
+# gravity solvers in all models
+NEWTON_G 1.0 # gravitational constant (will be overwritten if OPT__UNIT or COMOVING is on)
+SOR_OMEGA -1.0 # over-relaxation parameter in SOR: (<0=auto) [-1.0]
+SOR_MAX_ITER -1 # maximum number of iterations in SOR: (<0=auto) [-1]
+SOR_MIN_ITER -1 # minimum number of iterations in SOR: (<0=auto) [-1]
+MG_MAX_ITER -1 # maximum number of iterations in multigrid: (<0=auto) [-1]
+MG_NPRE_SMOOTH -1 # number of pre-smoothing steps in multigrid: (<0=auto) [-1]
+MG_NPOST_SMOOTH -1 # number of post-smoothing steps in multigrid: (<0=auto) [-1]
+MG_TOLERATED_ERROR -1.0 # maximum tolerated error in multigrid (<0=auto) [-1.0]
+POT_GPU_NPGROUP -1 # number of patch groups sent into the CPU/GPU Poisson solver (<=0=auto) [-1]
+OPT__GRA_P5_GRADIENT 0 # 5-points gradient in the Gravity solver (must have GRA/USG_GHOST_SIZE_G>=2) [0]
+OPT__SELF_GRAVITY 0 # add self-gravity [1]
+OPT__EXT_ACC 0 # add external acceleration (0=off, 1=function, 2=table) [0] ##HYDRO ONLY##
+ # --> 2 (table) is not supported yet
+OPT__EXT_POT 1 # add external potential (0=off, 1=function, 2=table) [0]
+EXT_POT_TABLE_NAME ExtPotTable # external potential table: filename
+EXT_POT_TABLE_NPOINT_X 129 # ... : table size (i.e., number of data points) along x/y/z
+EXT_POT_TABLE_NPOINT_Y 129 #
+EXT_POT_TABLE_NPOINT_Z 129 #
+EXT_POT_TABLE_DH_X 0.0078125 # ... : spatial interval between adjacent data points
+EXT_POT_TABLE_DH_Y 0.0078125 #
+EXT_POT_TABLE_DH_Z 0.0078125 #
+EXT_POT_TABLE_EDGEL_X 0.0 # ... : starting x/y/z coordinates
+EXT_POT_TABLE_EDGEL_Y 0.0 #
+EXT_POT_TABLE_EDGEL_Z 0.0 #
+EXT_POT_TABLE_FLOAT8 -1 # ... : double precision (<0=auto -> FLOAT8, 0=off, 1=on) [-1]
+ # --> not supported yet; use -1 for now
+OPT__GRAVITY_EXTRA_MASS 0 # add extra mass source when computing gravity [0]
+
+
+# initialization
+OPT__INIT 2 # initialization option: (1=FUNCTION, 2=RESTART, 3=FILE->"UM_IC")
+RESTART_LOAD_NRANK 1 # number of parallel I/O (i.e., number of MPI ranks) for restart [1]
+OPT__RESTART_RESET 0 # reset some simulation status parameters (e.g., current step and time) during restart [0]
+OPT__INIT_RESTRICT 1 # restrict all data during the initialization [1]
+OPT__INIT_GRID_WITH_OMP 1 # enable OpenMP when assigning the initial condition of each grid patch [1]
+OPT__GPUID_SELECT -1 # GPU ID selection mode: (-3=Laohu, -2=CUDA, -1=MPI rank, >=0=input) [-1]
+INIT_SUBSAMPLING_NCELL 0 # perform sub-sampling during initialization: (0=off, >0=# of sub-sampling cells) [0]
+OPT__FFTW_STARTUP -1 # initialise fftw plans: (-1=auto, 0=ESTIMATE, 1=MEASURE, 2=PATIENT (only FFTW3)) [-1]
+
+# interpolation schemes: (-1=auto, 1=MinMod-3D, 2=MinMod-1D, 3=vanLeer, 4=CQuad, 5=Quad, 6=CQuar, 7=Quar)
+OPT__INT_TIME 1 # perform "temporal" interpolation for OPT__DT_LEVEL == 2/3 [1]
+OPT__INT_PRIM 1 # switch to primitive variables when the interpolation on conserved variables fails [1] ##HYDRO ONLY##
+OPT__INT_PHASE 1 # interpolation on phase (does not support MinMod-1D) [1] ##ELBDM ONLY##
+OPT__FLU_INT_SCHEME -1 # ghost-zone fluid variables for the fluid solver [-1]
+OPT__REF_FLU_INT_SCHEME -1 # newly allocated fluid variables during grid refinement [-1]
+OPT__MAG_INT_SCHEME 4 # ghost-zone magnetic field for the MHD solver (2,3,4,6 only) [4]
+OPT__REF_MAG_INT_SCHEME 4 # newly allocated magnetic field during grid refinement (2,3,4,6 only) [4]
+OPT__POT_INT_SCHEME 4 # ghost-zone potential for the Poisson solver (only supports 4 & 5) [4]
+OPT__RHO_INT_SCHEME 4 # ghost-zone mass density for the Poisson solver [4]
+OPT__GRA_INT_SCHEME 4 # ghost-zone potential for the gravity solver (for UNSPLIT_GRAVITY as well) [4]
+OPT__REF_POT_INT_SCHEME 4 # newly allocated potential during grid refinement [4]
+INT_MONO_COEFF 2.0 # coefficient for ensuring the interpolation monotonicity (1.0~4.0) [2.0]
+INT_MONO_COEFF_B 2.0 # coefficient for ensuring the interpolation monotonicity of B field (1.0~4.0) [2.0] ##MHD ONLY##
+MONO_MAX_ITER 10 # maximum number of iterations to reduce INT_MONO_COEFF when interpolation fails (0=off) [10]
+INT_OPP_SIGN_0TH_ORDER 1 # switch to 0th-order interpolation if adjacent values change signs [HYDRO:1; ELBDM:0]
+
+
+# data dump
+OPT__OUTPUT_TOTAL 1 # output the simulation snapshot: (0=off, 1=HDF5, 2=C-binary) [1]
+OPT__OUTPUT_PART 0 # output a single line or slice: (0=off, 1=xy, 2=yz, 3=xz, 4=x, 5=y, 6=z, 7=diag) [0]
+OPT__OUTPUT_TEXT_FORMAT_FLT %24.16e # string format of output text files [%24.16e]
+OPT__OUTPUT_USER 0 # output the user-specified data -> edit "Output_User.cpp" [0]
+OPT__OUTPUT_PAR_MODE 0 # output the particle data: (0=off, 1=text-file, 2=C-binary) [0] ##PARTICLE ONLY##
+OPT__OUTPUT_BASEPS 0 # output the base-level power spectrum [0]
+OPT__OUTPUT_BASE 0 # only output the base-level data [0] ##OPT__OUTPUT_PART ONLY##
+OPT__OUTPUT_POT 1 # output gravitational potential [1] ##OPT__OUTPUT_TOTAL ONLY##
+OPT__OUTPUT_CC_MAG 1 # output **cell-centered** magnetic field (necessary for yt analysis) [1] ##MHD ONLY##
+OPT__OUTPUT_MODE 2 # (1=const step, 2=const dt, 3=dump table) -> edit "Input__DumpTable" for 3
+OPT__OUTPUT_RESTART 0 # output data immediately after restart [0]
+OUTPUT_STEP 5 # output data every OUTPUT_STEP step ##OPT__OUTPUT_MODE==1 ONLY##
+OUTPUT_DT 200
+# OUTPUT_DT 2
+OUTPUT_WALLTIME -1.0 # output data every OUTPUT_WALLTIME walltime (<=0.0=off) [-1.0]
+OUTPUT_WALLTIME_UNIT 0 # unit of OUTPUT_WALLTIME (0=second, 1=minute, 2=hour, 3=day) [0]
+OUTPUT_PART_X -1.0 # x coordinate for OPT__OUTPUT_PART [-1.0]
+OUTPUT_PART_Y -1.0 # y coordinate for OPT__OUTPUT_PART [-1.0]
+OUTPUT_PART_Z -1.0 # z coordinate for OPT__OUTPUT_PART [-1.0]
+INIT_DUMPID -1 # set the first dump ID (<0=auto) [-1]
+
+
+# yt inline analysis (SUPPORT_LIBYT only)
+YT_SCRIPT yt_inline # yt inline analysis script (do not include the ".py" file extension)
+YT_VERBOSE 1 # verbose level of yt (0=off, 1=info, 2=warning, 3=debug) [1]
+YT_FIG_BASENAME Fig # figure basename [Fig]
+YT_JUPYTER_USE_CONNECTION_FILE 0 # use user-provided connection file when using libyt Jupyter UI [0]
+
+
+# miscellaneous
+OPT__VERBOSE 0 # output the simulation progress in detail [0]
+OPT__TIMING_BARRIER -1 # synchronize before timing -> more accurate, but may slow down the run (<0=auto) [-1]
+OPT__TIMING_BALANCE 0 # record the max/min elapsed time in various code sections for checking load balance [0]
+OPT__TIMING_MPI 0 # record the MPI bandwidth achieved in various code sections [0] ##LOAD_BALANCE ONLY##
+OPT__RECORD_NOTE 1 # take notes for the general simulation info [1]
+OPT__RECORD_UNPHY 1 # record the number of cells with unphysical results being corrected [1]
+OPT__RECORD_MEMORY 1 # record the memory consumption [1]
+OPT__RECORD_PERFORMANCE 1 # record the code performance [1]
+OPT__MANUAL_CONTROL 1 # support manually dump data, stop run, or pause run during the runtime
+ # (by generating the file DUMP_GAMER_DUMP, STOP_GAMER_STOP, PAUSE_GAMER_PAUSE, respectively) [1]
+OPT__RECORD_CENTER 0 # record the position of maximum density, minimum potential, and center of mass [0]
+COM_CEN_X -1.0 # x coordinate as an initial guess for determining center of mass (if one of COM_CEN_X/Y/Z < 0 -> peak density position x) [-1.0]
+COM_CEN_Y -1.0 # y coordinate as an initial guess for determining center of mass (if one of COM_CEN_X/Y/Z < 0 -> peak density position y) [-1.0]
+COM_CEN_Z -1.0 # z coordinate as an initial guess for determining center of mass (if one of COM_CEN_X/Y/Z < 0 -> peak density position z) [-1.0]
+COM_MAX_R -1.0 # maximum radius for determining center of mass (<0=auto -> __FLT_MAX__) [-1.0]
+COM_MIN_RHO 0.0 # minimum density for determining center of mass (must >= 0.0) [0.0]
+COM_TOLERR_R -1.0 # maximum tolerated error of deviation in radius during the iterations of determining the center of mass (<0=auto -> amr->dh[MAX_LEVEL]) [-1.0]
+COM_MAX_ITER 10 # maximum number of iterations for determining the center of mass (must >= 1) [10]
+OPT__RECORD_USER 0 # record the user-specified info -> edit "Aux_Record_User.cpp" [0]
+OPT__OPTIMIZE_AGGRESSIVE 0 # apply aggressive optimizations (experimental) [0]
+OPT__SORT_PATCH_BY_LBIDX 1 # sort patches to improve bitwise reproducibility [SERIAL:0, LOAD_BALACNE:1]
+
+
+# checks
+OPT__CK_REFINE 0 # check the grid refinement [0]
+OPT__CK_PROPER_NESTING 0 # check the proper-nesting condition [0]
+OPT__CK_CONSERVATION 0 # check the conservation law [0]
+ANGMOM_ORIGIN_X -1.0 # x coordinate of the origin for angular momentum (<0=auto -> BoxCenter) [-1.0]
+ANGMOM_ORIGIN_Y -1.0 # y coordinate of the origin for angular momentum (<0=auto -> BoxCenter) [-1.0]
+ANGMOM_ORIGIN_Z -1.0 # z coordinate of the origin for angular momentum (<0=auto -> BoxCenter) [-1.0]
+OPT__CK_NORMALIZE_PASSIVE 0 # check the normalization of passive scalars [0] ##OPT__NORMALIZE_PASSIVE ONLY##
+OPT__CK_RESTRICT 0 # check the data restriction [0]
+OPT__CK_FINITE 0 # check if all variables are finite [0]
+OPT__CK_PATCH_ALLOCATE 0 # check if all patches are properly allocated [0]
+OPT__CK_FLUX_ALLOCATE 0 # check if all flux arrays are properly allocated [0] ##HYDRO and ELBDM ONLY##
+OPT__CK_NEGATIVE 0 # check the negative values: (0=off, 1=density, 2=pressure and entropy, 3=both) [0] ##HYDRO ONLY##
+OPT__CK_MEMFREE 1.0 # check the free memory in GB (0=off, >0=threshold) [1.0]
+OPT__CK_PARTICLE 0 # check the particle allocation [0]
+OPT__CK_INTERFACE_B 0 # check the consistency of patch interface B field [0] ##MHD ONLY##
+OPT__CK_DIVERGENCE_B 0 # check the divergence-free constraint on B field (0=off, 1=on, 2=on+verbose) [0] ##MHD ONLY##
+OPT__CK_INPUT_FLUID 0 # check the input data of the fluid solver [0]
diff --git a/example/test_problem/Hydro/FermiBubble/Input__TestProb b/example/test_problem/Hydro/FermiBubble/Input__TestProb
new file mode 100644
index 0000000000..58df8328de
--- /dev/null
+++ b/example/test_problem/Hydro/FermiBubble/Input__TestProb
@@ -0,0 +1,68 @@
+# proton mass = 1.672621898e-24 [g]
+# electron mass = 9.109383560e-28 [g]
+
+# problem-specific runtime parameters
+Jet_Ambient 2 # [1]
+ # 0 : uniform
+ # 1 : Milky-Way
+ # 2 : cold disk in stratifed ambient
+ # 3 : cold disk in uniform ambient
+ # 4 :
+ # 9 : load from UM_IC
+Jet_Fire 0 # [3]
+ # 0 : off
+ # 1 : upper jet
+ # 2 : lower jet
+ # 3 : bipoler jet
+
+
+# jet fluid parameters
+Jet_SrcVel 0.6 # jet: jet 4-velocity ( in unit of speed of light )
+Jet_SmoothVel 0 # smooth radial componennt of 4-velocity on cross section (0/1) [0]
+Jet_SrcDens 1e-26 # jet: jet density (in cgs units)
+Jet_SrcTemp 2e10 # jet: jet temperature (in K)
+#Jet_Duration 0.6 # duration time of jet injection from the start of te simulation
+gasDisk_highResRadius 0.5
+gasDisk_lowRes_LEVEL 3
+jetSrc_lowRes_LEVEL 6
+Jet_Src_CR_Engy 3e-10
+Amb_CR_Engy 0.0
+criticalTemp 1e99 # K
+
+
+# source geometry parameters
+Jet_SphericalSrc 0 # (0/1) [0]
+Jet_Radius 0.002 # jet: radius of the cylinder-shape jet source (in kpc)
+Jet_HalfHeight 0.002 # jet: half height of the cylinder-shape jet source (in kpc)
+Jet_HalfOpeningAngle 0.0 # jet: half-opening angle
+Jet_CenOffset_x +0.0 # jet: jet central coordinates offset x (in pc)
+Jet_CenOffset_y +0.0 # jet: jet central coordinates offset y (in pc)
+Jet_CenOffset_z +0.0 # jet: jet central coordinates offset z (in pc)
+
+
+# precission parameters
+Jet_AngularVelocity +0.0 # precession angular velocity at Schwarzschild radius (degree per code_time)
+Jet_PrecessionAngle 0.0 # precession angle
+Jet_PrecessionAxis_x 0.0 # x-corrdinate of precession vector
+Jet_PrecessionAxis_y 1.0 # y-corrdinate of precession vector
+Jet_PrecessionAxis_z 1.0 # z-corrdinate of precession vector
+
+
+# Jet_Ambient == 0
+Amb_UniformDens 5.0e-29 # ambient density (in cgs units)
+Amb_UniformTemp 1e10 # ambient temperature (in K)
+Amb_UniformVel_x +0.0 # ambient velocity x [0.0]
+Amb_UniformVel_y +0.0 # ambient velocity y [0.0]
+Amb_UniformVel_z +0.0 # ambient velocity z [0.0]
+
+# estimate default evolution time (i.e. END_T)
+CharacteristicSpeed 5e-6 # the characteristic speed of the simulation problem
+ # the default end-time (END_T) will be estimated from
+ # `CharacteristicSpeed` and `BOX_SIZE`
+ # The unit of `CharacteristicSpeed` is UNIT_V
+
+
+# Milky Way
+IsothermalSlab_Center_x -1 # [box center]
+IsothermalSlab_Center_y -1 # [box center]
+IsothermalSlab_Center_z -1 # [box center]
diff --git a/example/test_problem/Hydro/FermiBubble/README.md b/example/test_problem/Hydro/FermiBubble/README.md
new file mode 100644
index 0000000000..8ee9bad385
--- /dev/null
+++ b/example/test_problem/Hydro/FermiBubble/README.md
@@ -0,0 +1,214 @@
+# Simulation
+1. Generate makefile (double precision)
+ ```shell
+ python3 configure.py --flu_scheme=MHM_RP --gravity=true --pot_scheme=SOR --store_pot_ghost=true --unsplit_gravity=false --slope=PLM --flux=HLLC --srhd=true --cosmic_ray=true --double=true --hdf5=true --nlevel=14 --fftw=FFTW2 --passive=3 --mpi=true --gpu=true
+ ```
+
+2. Compile GAMER
+ ```shell
+ make clean
+ make -j
+ mv ../bin/gamer ../bin/gamer_double
+ ```
+
+3. Generate makefile (single precision)
+ ```shell
+ python3 configure.py --flu_scheme=MHM_RP --gravity=true --pot_scheme=SOR --store_pot_ghost=true --unsplit_gravity=false --slope=PLM --flux=HLLC --srhd=true --cosmic_ray=true --double=false --hdf5=true --nlevel=14 --fftw=FFTW2 --passive=3 --mpi=true --gpu=true
+ ```
+
+4. Compile GAMER
+ ```shell
+ make clean
+ make -j
+ mv ../bin/gamer ../bin/gamer_single
+ ```
+
+5. Copy simulation files
+ ```shell
+ cd ../bin
+ cp -r ../example/test_problem/Hydro/FermiBubble ./
+ cd FermiBubble
+ cp ../gamer_* ./
+ sh download_ic.sh
+ ```
+
+6. Run the simulation (stage 1)
+ 1. Change the parameters to below as follows:
+ - `Input__Parameter`:
+ - `END_T`: 3.14872516940871e+01
+ - `OUTPUT_DT`: 2
+ - `OPT__INIT`: 1
+
+ - `Input__TestProb`:
+ - `Jet_Fire`: 3
+
+ 2. Execute gamer
+ ```shell
+ mpirun -map-by ppr:2:socket:pe=8 --report-bindings ./gamer_double 1>>log 2>&1
+ ```
+
+7. Run the simulation (stage 2)
+ 1. Change the parameters to below as follows:
+ - `Input__Parameter`:
+ - `END_T`: 3.8e3
+ - `OUTPUT_DT`: 200
+ - `OPT__INIT`: 2
+
+ - `Input__TestProb`:
+ - `Jet_Fire`: 0
+
+ 2. Link the lastest file from stage 1
+ ```shell
+ ln -fs Data_000016 RESTART
+ ```
+
+ 3. Execute gamer
+ ```shell
+ mpirun -map-by ppr:2:socket:pe=8 --report-bindings ./gamer_single 1>>log 2>&1
+ ```
+
+
+
+# Analysis
+> [!CAUTION]
+> Please execute the following commands before the analysis
+```shell
+cd plot_scripts
+ln -s ../R12
+ln -s ../../../tool/analysis/PerspectiveProjection
+```
+
+
+## Slice plot
+```shell
+python plot_slice.py -s 0 -e 35
+```
+
+## Profile plot along z axis of the lower jet
+```shell
+python plot_profile.py -s 0 -e 35
+```
+
+## Generate the fixed resolution data (`Data_000035`)
+```shell
+python AMR2FR.py
+```
+
+
+## Generate x-ray perspective projection map (`Data_000035`)
+1. If you do not have `FRB_Data_000035.h5`, please execute the following command
+ ```shell
+ python AMR2FR.py
+ ```
+
+2. Compile the projection tool
+ ```shell
+ cd PerspectiveProjection
+ make clean && make CFLAGS+=-DNUM_THREADS=32 CFLAGS+=-DXRAY_EROSITA
+ cd ../
+ cp PerspectiveProjection/bin/Project ./
+ ```
+
+3. Execute the projection tool
+ ```shell
+ ./Project FRB_Data_000035.h5
+ ```
+
+4. Plot the map
+ ```shell
+ python plot_map.py -t x_ray
+ ```
+
+
+## Generate x-ray profile (`Data_000035`)
+1. If you do not have `FRB_Data_000035.h5`, please execute the following command
+ ```shell
+ python AMR2FR.py
+ ```
+
+2. Plot the x-ray profile
+ ```shell
+ python plot_xray_profile.py
+ ```
+
+## Generate gamma ray perspective projection map (`Data_000035`)
+1. If you do not have `FRB_Data_000035.h5`, please execute the following command
+ ```shell
+ python AMR2FRB.py
+ ```
+
+2. Compile the projection tool
+ ```shell
+ cd PerspectiveProjection
+ make clean && make CFLAGS+=-DNUM_THREADS=32 CFLAGS+=-DLEPTONIC_GAMMARAY
+ cd ../
+ cp PerspectiveProjection/bin/Project ./
+ ```
+
+3. Execute the projection tool
+ ```shell
+ ./Project FRB_Data_000035.h5 100e9 1e6 2.4 R12/robitaille_DL07_PAHISMMix.dat
+ ```
+> [!NOTE]
+> `arg1`: name of fixed resolution data in Step1
+>
+> `arg2`: observed photon energy (eV)
+>
+> `arg3`: the cut-off Lorentz factor of CR
+>
+> `arg4`: spectral index of CR
+>
+> `arg5`: the path of ISRF data
+
+# 4. Plot the map
+ ```shell
+ python plot_map.py -t gamma_ray
+ ```
+
+
+## Generate spectrum
+1. If you do not have `FRB_Data_000035.h5`, please execute the following command
+ ```shell
+ python AMR2FR.py
+ ```
+
+2. Compile the projection tool
+ 1. synchrotron emissivities
+ ```shell
+ cd PerspectiveProjection
+ make clean && make CFLAGS+=-DNUM_THREADS=32 CFLAGS+=-DSYNCHROTRON
+ cd ../
+ cp PerspectiveProjection/bin/Project synchrotron_spectrum/
+ ```
+
+ 2. gamma-ray emissivities
+ ```shell
+ cd PerspectiveProjection
+ make clean && make CFLAGS+=-DNUM_THREADS=32 CFLAGS+=-DLEPTONIC_GAMMARAY
+ cd ../
+ cp PerspectiveProjection/bin/Project gamma_ray_spectrum/
+ ```
+
+3. Calculate spectrums
+ 1. synchrotron emissivities
+ ```shell
+ cd synchrotron_spectrum
+ ln -s ../FRB_Data_000035.h5
+ ln -s ../../R12
+ sh get_spectrum.sh
+ cd ../
+ ```
+
+ 2. gamma ray emissivities
+ ```shell
+ cd gamma_ray_spectrum
+ ln -s ../FRB_Data_000035.h5
+ ln -s ../../R12
+ sh get_spectrum.sh
+ cd ../
+ ```
+
+4. Plot the spectrum
+ ```shell
+ python plot_spectrum.py
+ ```
diff --git a/example/test_problem/Hydro/FermiBubble/download_ic.sh b/example/test_problem/Hydro/FermiBubble/download_ic.sh
new file mode 100644
index 0000000000..89fa8ed450
--- /dev/null
+++ b/example/test_problem/Hydro/FermiBubble/download_ic.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+
+LOCAL_FILENAME="FermiBubble"
+FILE_ID="677d3e9e999605c485c8de8c"
+
+# 1. download
+curl https://hub.yt/api/v1/item/${FILE_ID}/download -o "${LOCAL_FILENAME}.tar.gz"
+
+# 2. unzip
+tar zxvf ${LOCAL_FILENAME}.tar.gz
+rm ${LOCAL_FILENAME}.tar.gz
+mv IC/FermiBubble_IC ./
+mv IC/R12 ./
+rm -rf IC
diff --git a/example/test_problem/Hydro/FermiBubble/generate_make.sh b/example/test_problem/Hydro/FermiBubble/generate_make.sh
new file mode 100644
index 0000000000..45672b42ae
--- /dev/null
+++ b/example/test_problem/Hydro/FermiBubble/generate_make.sh
@@ -0,0 +1,7 @@
+# This script should run in the same directory as configure.py
+
+PYTHON=python3
+
+${PYTHON} configure.py --flu_scheme=MHM_RP --gravity=true --pot_scheme=SOR --store_pot_ghost=true \
+ --unsplit_gravity=false --slope=PLM --flux=HLLC --srhd=true --cosmic_ray=true \
+ --double=true --hdf5=true --nlevel=14 --fftw=FFTW2 --passive=3 --mpi=true --gpu=true "$@"
diff --git a/example/test_problem/Hydro/FermiBubble/plot_scripts/AMR2FR.py b/example/test_problem/Hydro/FermiBubble/plot_scripts/AMR2FR.py
new file mode 100644
index 0000000000..00d7d3459a
--- /dev/null
+++ b/example/test_problem/Hydro/FermiBubble/plot_scripts/AMR2FR.py
@@ -0,0 +1,106 @@
+#====================================================================================================
+# Imports
+#====================================================================================================
+import numpy as np
+import h5py
+import yt
+import time
+
+
+
+#====================================================================================================
+# Constants
+#====================================================================================================
+PARTICLE_MASS = 1.6726231e-24 # proton mass in gram
+
+
+
+#====================================================================================================
+# Derived fields
+#====================================================================================================
+def pressure_sr( field, data ):
+ from yt.units import speed_of_light_cgs, boltzmann_constant_cgs
+ unit_v = speed_of_light_cgs
+
+ pres = data["frame_density"] * data["Temp"] * yt.YTQuantity(1.0, "K")
+ pres /= PARTICLE_MASS * yt.YTQuantity(1.0, "g")
+ pres /= speed_of_light_cgs**2
+ pres *= unit_v**2
+ pres *= boltzmann_constant_cgs
+ return pres
+
+
+
+#====================================================================================================
+# Main
+#====================================================================================================
+Data = 'Data_000035'
+Path = '../'+Data
+level = 4
+Field = [ "Dens", "Temp", "Pres", "CRay", "Passive_0001" ]
+
+ds = yt.load(Path)
+dims = ds.domain_dimensions * ds.refine_by**level
+
+ds.add_field( name=("gamer", "Pres"), function=_pressure, sampling_type="local", units="dyne/cm**2" )
+ds.add_field( name=("gamer", "CRay_new"), function=_cray, sampling_type="local", units="dyne/cm**2" )
+
+
+# Note that we open with 'w' (write), which will overwrite existing files!
+f = h5py.File( "FRB_%s.h5"%Data, mode="w" )
+
+for i in range(len(Field)):
+ cube = ds.covering_grid( level, left_edge=ds.domain_left_edge, dims=dims, fields=Field[i] )
+ if Field[i] == "CRay":
+ f.create_dataset( "Fields/"+Field[i], shape=cube[("gamer", "CRay_new")].shape, data=cube[("gamer", "CRay_new")].astype(' "$temp_file"
+
+for CRindex in "${CRindexs[@]}"
+do
+ while IFS=$' \t' read -ra line; do
+ Emin=${line[0]}
+ ObservedEngyEv=$(printf "%9.4e" ${line[1]})
+ Emax=${line[2]}
+ FileName="Projected_FRB_Data_000035_${ObservedEngyEv}_1.1e6_${CRindex}.h5"
+
+ if [ -f "$FileName" ]; then
+ echo "${FileName} exist !!"
+ else
+ echo "Running with (ObservedEngyEv=${ObservedEngyEv}, CRindex=${CRindex})"
+ ./Project FRB_Data_000035.h5 $ObservedEngyEv 1.1e6 R12/$CRindex robitaille_DL07_PAHISMMix.dat >& log-$CRindex-$ObservedEngyEv
+ fi
+ done < "${temp_file}"
+done
+
+rm "${temp_file}"
diff --git a/example/test_problem/Hydro/FermiBubble/plot_scripts/observedEnergySpectrum-Lower b/example/test_problem/Hydro/FermiBubble/plot_scripts/observedEnergySpectrum-Lower
new file mode 100644
index 0000000000..a05541edff
--- /dev/null
+++ b/example/test_problem/Hydro/FermiBubble/plot_scripts/observedEnergySpectrum-Lower
@@ -0,0 +1,25 @@
+# The observational Gamma-ray data (lower bound) of Ackermann et al. (2014)
+
+# Photon energy (GeV) E^2dN/dE (GeV/cm**2/s/sr)
+0.11941154224126872, 8.397814043397254e-8
+0.16547580826826042, 1.884326764643684e-7
+0.33980178840778125, 2.708393179140353e-7
+0.6484625035311103, 3.43145631862909e-7
+0.9178256992489282, 3.3991087582589945e-7
+1.5167071028286105, 4.03174274518117e-7
+2.6066930188579227, 4.626344808850217e-7
+3.63425698710994, 4.2382890843792e-7
+5.14304320579965, 4.300521448683542e-7
+7.103101042939144, 5.010922517966781e-7
+10.411983136995458, 5.038622616988608e-7
+14.861855585284927, 4.5469864497476903e-7
+20.554719885372215, 4.3056735898035103e-7
+28.55861196023107, 3.692113237293533e-7
+57.45453698566577, 3.4526937432093356e-7
+82.69419821184174, 2.890201326431414e-7
+115.67959096492962, 2.8716189751802696e-7
+161.08856661505408, 1.7638060376541146e-7
+204.0904248554562, 1.2293023525340635e-7
+239.14173998792367, 8.646241035054578e-8
+349.43711720019356, 8.047383975197925e-8
+369.51618904619505, 2.053807535521798e-8
diff --git a/example/test_problem/Hydro/FermiBubble/plot_scripts/observedEnergySpectrum-Upper b/example/test_problem/Hydro/FermiBubble/plot_scripts/observedEnergySpectrum-Upper
new file mode 100644
index 0000000000..9479d65dda
--- /dev/null
+++ b/example/test_problem/Hydro/FermiBubble/plot_scripts/observedEnergySpectrum-Upper
@@ -0,0 +1,25 @@
+# The observational Gamma-ray data (uppber bound) of Ackermann et al. (2014)
+
+# Photon energy (GeV) E^2dN/dE (GeV/cm**2/s/sr)
+0.11824912023155316, 3.5547057247463444e-7
+0.16199226009435475, 4.346028418011872e-7
+0.3201248126068375, 5.67406565477987e-7
+0.6408165165232559, 6.24131816563235e-7
+0.9285686706988457, 6.108402601946626e-7
+1.3337116835193075, 6.947973273676947e-7
+2.6487575075206027, 7.758397076388866e-7
+5.185298631056162, 7.233775775147767e-7
+7.478186462622065, 8.008365105236716e-7
+10.543088543699023, 7.956794791778269e-7
+15.587609186640089, 7.137055637495445e-7
+21.38911279180427, 6.840107532495574e-7
+29.374394011368665, 5.795374403014595e-7
+42.393689125419016, 5.775213042909137e-7
+59.785612958731384, 5.501553979309034e-7
+86.79431716728308, 4.083487288017067e-7
+121.38796631535577, 4.1936324753506673e-7
+172.92984964977677, 2.835808681412845e-7
+242.62965789133756, 1.8166590928466074e-7
+348.3988832264666, 2.1486978647897313e-7
+487.38452310945524, 1.1951318730760447e-7
+487.49876337663784, 2.053064724576593e-8
diff --git a/example/test_problem/Hydro/FermiBubble/plot_scripts/observed_data/xray_profile/m40.dat b/example/test_problem/Hydro/FermiBubble/plot_scripts/observed_data/xray_profile/m40.dat
new file mode 100644
index 0000000000..221879d863
--- /dev/null
+++ b/example/test_problem/Hydro/FermiBubble/plot_scripts/observed_data/xray_profile/m40.dat
@@ -0,0 +1,677 @@
+118.98045 5.22510
+118.30030 5.32002
+118.14880 5.42129
+117.92494 5.50746
+118.28333 5.22327
+117.80125 5.59593
+117.56757 5.68569
+117.32144 5.77185
+117.11209 5.85844
+116.68434 5.93353
+116.45858 6.01389
+115.73377 5.89424
+115.50801 5.81513
+115.23947 5.72479
+114.94691 5.63333
+114.75830 5.55580
+114.51453 5.47608
+114.37222 5.40044
+114.21880 5.32708
+114.05769 5.25262
+113.89693 5.18128
+113.75115 5.10824
+113.58241 5.04195
+113.47485 4.94836
+113.41466 4.87985
+113.28240 4.79848
+113.13456 4.71290
+113.05237 4.63947
+112.95830 4.56208
+112.79988 4.47444
+112.71571 4.39833
+112.57426 4.32871
+112.43429 4.26270
+112.29093 4.19953
+112.10270 4.13320
+111.91664 4.06688
+111.76844 4.00806
+111.60671 3.95340
+111.30862 3.90129
+110.94825 3.84742
+110.32868 3.80091
+109.31292 3.87589
+109.43765 3.80084
+109.68577 3.77211
+109.08318 3.95383
+108.93720 4.01435
+108.77031 4.08034
+108.55385 4.14622
+108.43815 4.20750
+108.34904 4.26046
+108.23022 4.31569
+108.11139 4.37130
+107.99257 4.42730
+107.74305 4.48788
+107.53462 4.53508
+107.51135 4.59350
+106.64646 4.49329
+106.44790 4.42614
+106.17105 4.36012
+105.88350 4.29542
+105.61615 4.24038
+105.37851 4.18557
+105.24130 4.12935
+105.01423 4.07388
+104.77120 4.02144
+104.51989 3.96769
+104.22350 3.91726
+103.93945 3.86733
+103.64210 3.81764
+102.85176 3.78513
+102.11605 3.85362
+101.90002 3.92228
+102.20995 3.77872
+101.69506 3.98163
+101.45411 4.03736
+101.10802 4.09739
+100.80930 4.15790
+100.51958 4.22046
+100.29297 4.28298
+100.06127 4.34382
+99.72371 4.40600
+99.42842 4.44827
+98.38292 4.41478
+97.62814 4.47667
+97.46701 4.54638
+97.33464 4.61937
+97.23927 4.68545
+97.10064 4.74740
+96.98182 4.81488
+96.86960 4.88462
+96.74826 4.95324
+96.60555 5.01979
+96.51554 5.09969
+96.39672 5.18707
+96.26674 5.28154
+96.18176 5.37200
+96.08389 5.45884
+95.87919 5.56036
+95.58638 5.65031
+95.57673 5.75072
+95.42725 5.80990
+94.67801 5.60636
+94.62149 5.51827
+94.57239 5.44315
+94.83314 5.70741
+94.43887 5.35683
+94.39225 5.27126
+94.34465 5.19189
+94.26874 5.12076
+94.23243 5.05142
+94.14992 4.98465
+94.08680 4.92013
+94.03817 4.84501
+93.93118 4.76428
+93.82613 4.68625
+93.79346 4.61786
+93.75676 4.55568
+93.64493 4.49698
+93.63794 4.43810
+93.51912 4.37867
+93.40030 4.31989
+93.28148 4.26205
+93.16266 4.20498
+92.85833 4.15053
+92.72635 4.09225
+92.62069 4.03431
+92.57554 3.97699
+91.63716 4.21596
+91.59824 4.14192
+91.37923 4.09332
+91.59527 4.29092
+91.17783 4.02548
+90.62023 3.97242
+90.50437 3.90607
+90.10010 4.19720
+90.59123 3.86766
+90.07680 4.12816
+90.16472 4.07047
+90.07573 4.02961
+89.50600 4.28159
+89.88226 3.97779
+89.33226 4.36743
+89.52580 4.19720
+89.17216 4.44068
+89.51590 4.14652
+89.60501 4.10506
+88.93664 4.52617
+88.56731 4.59819
+88.47621 4.66459
+87.59920 4.55789
+87.22199 4.52509
+86.05696 4.57950
+85.48871 4.53257
+85.58490 4.65682
+84.89061 4.54612
+84.83819 4.47339
+84.64423 4.41901
+85.00070 4.62835
+84.50196 4.35804
+84.40659 4.29905
+84.30456 4.23331
+84.14490 4.16085
+84.05013 4.10160
+83.94293 4.03958
+83.75732 3.97024
+83.71638 3.91360
+83.50733 3.86146
+83.02827 3.80763
+82.52741 3.76783
+81.28754 3.78366
+80.01445 3.79833
+79.06272 3.73890
+79.20817 3.81470
+78.76259 3.67642
+78.52495 3.62976
+78.32031 3.58118
+78.15528 3.53623
+77.76330 3.49376
+77.48712 3.45271
+76.57034 3.53932
+76.83175 3.47623
+75.30346 3.53170
+74.63739 3.50112
+74.18798 3.60313
+74.25333 3.54205
+73.97014 3.67766
+73.82727 3.73955
+73.65470 3.80311
+73.43941 3.86453
+73.26712 3.92106
+73.08888 3.97640
+72.91065 4.03092
+72.73242 4.08469
+72.43537 4.14164
+71.15933 4.18119
+70.28779 4.23321
+69.89635 4.29981
+69.60868 4.35714
+69.26582 4.41975
+69.04897 4.47782
+68.80507 4.53806
+68.17266 4.59349
+67.42948 4.64995
+67.32919 4.73657
+67.22374 4.80710
+67.10492 4.87608
+67.50429 4.58620
+66.99149 4.94689
+66.87058 5.01970
+66.75836 5.09284
+66.61314 5.16471
+66.31311 5.23402
+65.83000 5.28299
+65.51404 5.33725
+65.09817 5.20194
+64.74856 5.12693
+64.41495 5.24136
+64.52717 5.05824
+64.54291 5.01508
+63.52379 5.19068
+63.28615 5.09353
+62.67604 5.01178
+62.47816 4.95661
+61.48898 5.05675
+61.76127 4.97979
+61.04976 5.15170
+60.79970 5.22044
+60.58609 5.31991
+60.10132 5.05867
+59.96246 4.96562
+59.90305 4.89456
+60.03554 5.20004
+60.01857 5.14437
+59.75904 4.82426
+59.64144 4.74558
+59.52520 4.65912
+59.44525 4.57830
+59.31855 4.49991
+59.28866 4.42056
+59.13011 4.34569
+59.06283 4.26707
+58.98482 4.19393
+58.86501 4.11997
+58.81847 4.04911
+58.70011 3.97481
+58.54518 3.89832
+57.97484 3.83685
+57.47932 3.79649
+56.89089 3.89186
+56.58839 3.96732
+56.42423 4.02293
+56.15418 4.08029
+55.90439 4.14633
+55.74101 4.20483
+55.47210 4.25995
+54.79786 4.31453
+54.33213 4.37420
+53.93323 4.43428
+53.50887 4.49685
+53.27547 4.56069
+53.04080 4.62724
+52.81669 4.69211
+52.53284 4.75671
+51.88965 4.81547
+51.36542 4.87720
+51.06634 4.94392
+50.72789 5.01377
+50.44753 5.08836
+50.27524 5.16196
+50.09701 5.23359
+49.91877 5.30449
+49.74054 5.37354
+49.41649 5.44044
+48.34890 5.47644
+48.01104 5.57599
+47.67994 5.65266
+47.36082 5.72084
+46.68569 5.77884
+46.51409 5.82681
+45.63726 5.68429
+45.33921 5.78468
+45.58638 5.61446
+44.81205 5.85058
+44.60153 5.95368
+44.85700 5.73235
+44.36389 6.04681
+43.96953 6.15031
+43.67218 6.24162
+43.48353 6.32410
+42.73175 6.09578
+42.46583 5.99858
+42.32611 6.19692
+42.43305 5.96261
+41.66368 6.13533
+41.23493 6.26302
+40.57706 6.35283
+39.41453 6.36201
+38.84046 6.27869
+38.46205 6.18852
+38.03354 6.09851
+37.42159 6.02533
+36.34521 6.10338
+35.44637 6.14982
+34.63046 6.05998
+34.30318 6.17115
+34.13448 6.27103
+33.99972 6.37518
+33.69178 6.49254
+33.38437 6.58529
+32.50692 6.52186
+32.29831 6.44026
+31.93546 6.36715
+31.41664 6.32497
+30.60216 6.43354
+29.64493 6.46457
+29.30002 6.34816
+29.21420 6.25985
+29.09538 6.18230
+28.77896 6.10364
+28.66014 6.02676
+28.59251 5.94367
+28.52558 5.88227
+27.72894 6.18590
+27.82346 6.05874
+27.42892 6.30350
+27.90717 5.98052
+26.99441 6.38814
+26.49617 6.48275
+25.26340 6.48141
+24.55048 6.39942
+24.37537 6.30477
+24.19401 6.20888
+23.94223 6.11436
+23.65932 6.03103
+23.56335 5.94558
+23.58670 5.89435
+22.77226 6.16929
+22.84985 6.05021
+22.48191 6.28833
+22.91669 5.98321
+21.91519 6.36526
+21.51140 6.44170
+20.99814 6.25449
+20.80761 6.14878
+20.94624 6.35695
+20.49166 6.06237
+19.96236 5.98273
+19.24313 5.95642
+18.44010 6.00677
+18.17791 5.92036
+17.84840 5.83960
+17.52872 5.75892
+17.26971 5.67861
+16.83744 5.60128
+16.46524 5.53363
+15.65119 5.65691
+15.84683 5.56012
+14.49524 5.68358
+13.57351 5.63682
+13.18574 5.74951
+13.04133 5.84077
+12.84035 5.92550
+12.68487 6.01160
+12.31191 6.09596
+11.06428 6.13764
+9.92360 6.08901
+9.12602 6.07249
+8.35516 6.15844
+8.15629 6.27647
+8.42052 6.06953
+7.98432 6.36729
+7.80609 6.45820
+7.65525 6.55575
+6.80653 6.62553
+5.78925 6.59128
+5.42240 6.53663
+4.34693 6.56999
+3.71388 6.47983
+3.47954 6.39370
+3.32309 6.30047
+3.23992 6.20324
+3.01545 6.09942
+2.67140 6.00812
+2.51765 5.90806
+2.53887 5.84410
+1.65488 6.11339
+1.81321 6.00556
+1.88536 5.94030
+1.29549 6.22789
+1.03641 6.31593
+0.72119 6.40708
+0.27047 6.49874
+-0.49849 6.57816
+-1.05547 6.66137
+-1.57353 6.70928
+-2.26859 6.57604
+-3.00413 6.47907
+-4.04400 6.49862
+-4.63560 6.61326
+-4.86906 6.71890
+-5.05786 6.81959
+-5.36003 6.92428
+-5.53222 7.02531
+-6.10932 7.12642
+-6.50143 7.21587
+-7.13600 7.05333
+-7.41498 6.94028
+-7.08563 7.17375
+-7.98583 6.84643
+-8.68584 6.75367
+-9.37036 6.67417
+-10.02646 6.58682
+-10.57541 6.53371
+-11.26913 6.63438
+-11.66181 6.73507
+-11.88799 6.82519
+-12.12563 6.91379
+-12.34347 7.00316
+-12.57841 7.10512
+-12.74237 7.22176
+-12.89301 7.33597
+-13.12293 7.46401
+-13.28466 7.59266
+-13.66238 7.69895
+-14.60503 7.56346
+-14.57136 7.71345
+-14.74299 7.41921
+-14.79328 7.31666
+-14.94433 7.21571
+-15.01694 7.11184
+-15.10977 7.01574
+-15.20216 6.92330
+-15.42984 6.82397
+-15.56803 6.74508
+-16.64145 6.85017
+-17.20800 6.96226
+-17.55674 7.03699
+-18.26032 6.87843
+-18.82180 6.76698
+-19.47273 6.69024
+-19.86220 6.58275
+-20.45863 6.47470
+-21.51126 6.58603
+-21.21261 6.48794
+-21.64122 6.71631
+-21.78974 6.81773
+-21.88546 6.92197
+-22.02113 7.02192
+-22.13683 7.12690
+-22.26173 7.23304
+-22.39948 7.33927
+-22.56208 7.44383
+-23.11962 7.52243
+-23.52179 7.60357
+-24.09135 7.41628
+-24.37410 7.29704
+-24.10675 7.54224
+-24.59689 7.19325
+-24.74377 7.09563
+-24.98141 7.00272
+-25.38738 6.90822
+-25.58745 6.82554
+-26.43196 6.98882
+-26.70299 7.11802
+-26.30494 6.88493
+-26.92406 7.22580
+-27.16640 7.33751
+-27.40404 7.45676
+-27.64168 7.57966
+-27.93873 7.69858
+-28.08027 7.80323
+-28.26549 7.90857
+-28.54904 8.02144
+-28.82989 8.13281
+-29.21606 8.24027
+-29.60451 8.31116
+-30.73244 8.28127
+-31.51199 8.43909
+-32.71080 8.42855
+-33.75766 8.56127
+-33.46391 8.44395
+-34.11743 8.73448
+-34.47389 8.85805
+-34.89967 8.98364
+-35.23633 9.10962
+-35.55551 9.23600
+-35.82383 9.35864
+-36.05817 9.47618
+-36.28591 9.60326
+-36.64238 9.73451
+-37.04934 9.86600
+-37.53894 9.98226
+-38.68016 10.00568
+-39.16263 10.16103
+-39.82313 10.25850
+-40.65047 10.07176
+-40.76481 9.88687
+-40.65259 10.27494
+-40.84402 9.74684
+-40.96537 9.61683
+-41.03876 9.49307
+-41.13381 9.35811
+-41.25041 9.22898
+-41.37076 9.10912
+-41.47245 8.97381
+-41.58760 8.82517
+-41.70359 8.67420
+-41.84080 8.52821
+-41.87710 8.40129
+-42.01363 8.27403
+-42.12435 8.13488
+-42.26092 8.00032
+-42.36469 7.86646
+-42.49701 7.73627
+-42.81365 7.60756
+-43.14783 7.47346
+-43.40669 7.35227
+-43.52740 7.24052
+-43.67262 7.13043
+-43.79747 7.00667
+-43.93736 6.88971
+-44.01627 6.79094
+-44.14543 6.68440
+-44.27393 6.56446
+-44.39545 6.46802
+-44.54398 6.37977
+-44.69250 6.29374
+-44.87074 6.21612
+-45.01926 6.13816
+-45.16779 6.06111
+-45.52029 5.97963
+-46.23946 5.95725
+-46.99565 5.85978
+-46.86099 5.98629
+-47.40711 5.75666
+-47.79670 5.72384
+-48.57498 5.78677
+-48.85906 5.70105
+-49.09670 5.62497
+-49.39485 5.55038
+-50.03113 5.47673
+-50.79269 5.44874
+-51.91088 5.51425
+-52.83670 5.52756
+-52.99159 5.44907
+-53.59778 5.38806
+-54.62269 5.45887
+-55.11905 5.54649
+-55.59434 5.61459
+-56.71757 5.66316
+-57.34520 5.76049
+-57.62618 5.78347
+-57.94105 5.64873
+-58.05831 5.55499
+-58.22238 5.48156
+-58.43416 5.40919
+-58.90647 5.33507
+-59.54610 5.28814
+-60.33418 5.38304
+-61.54467 5.41274
+-61.80743 5.50958
+-62.01662 5.59089
+-62.37350 5.67810
+-62.55725 5.75364
+-63.44714 5.62350
+-63.19889 5.71998
+-63.90191 5.53641
+-64.62474 5.46311
+-65.42876 5.41685
+-66.43874 5.48070
+-67.23880 5.57007
+-68.02434 5.63352
+-68.70179 5.71120
+-68.94920 5.79835
+-69.37511 5.87775
+-69.72216 5.92411
+-70.61086 5.86945
+-70.99581 5.97135
+-71.29357 6.05661
+-71.63002 6.14493
+-71.90253 6.23119
+-72.28870 6.31108
+-72.65704 6.36397
+-73.57876 6.26661
+-73.83337 6.16105
+-74.10496 6.07831
+-74.39777 5.99287
+-74.51190 5.91597
+-74.76774 5.82830
+-74.97615 5.73980
+-75.10080 5.65774
+-75.35428 5.57340
+-75.60381 5.49670
+-76.13688 5.42437
+-76.62338 5.38960
+-77.71334 5.46144
+-78.66629 5.54654
+-79.29631 5.63218
+-79.95266 5.70535
+-80.53026 5.77457
+-80.90917 5.85249
+-81.28158 5.93214
+-81.79438 6.00959
+-83.14102 6.02838
+-84.12673 6.10255
+-83.81434 6.00695
+-84.70808 6.21878
+-85.45940 6.29713
+-86.39021 6.36785
+-86.73837 6.47281
+-86.92349 6.57150
+-87.12153 6.66280
+-87.45740 6.73735
+-87.57881 6.82325
+-88.43648 6.65045
+-88.58501 6.52913
+-88.27013 6.75537
+-88.73461 6.42088
+-88.84548 6.31733
+-88.94741 6.22122
+-89.07217 6.11951
+-89.18675 6.01922
+-89.50377 5.90154
+-89.59813 5.83599
+-90.69111 5.92600
+-92.10440 5.95416
+-92.93980 5.91540
+-93.34973 5.84301
+-93.79743 5.75138
+-94.23307 5.66865
+-94.66667 5.59761
+-95.15928 5.52714
+-96.06641 5.50453
+-96.99300 5.57097
+-97.58273 5.64293
+-98.46621 5.53886
+-98.16199 5.62639
+-98.78580 5.44849
+-99.15287 5.36252
+-99.56474 5.29967
+-100.57405 5.38822
+-101.64560 5.39781
+-102.05055 5.32253
+-102.41297 5.25555
+-102.85272 5.19067
+-103.41091 5.12589
+-104.01392 5.05858
+-104.65969 5.00362
+-105.85565 4.95887
+-106.57600 4.93525
+-107.39184 5.00504
+-107.76669 5.09004
+-108.01093 5.15774
+-108.26334 5.22775
+-108.55883 5.29966
+-109.30889 5.35973
+-109.75892 5.42967
+-110.10350 5.50934
+-110.54333 5.57076
+-111.38083 5.46807
+-111.14319 5.55985
+-111.58877 5.36617
+-111.77769 5.26697
+-111.92873 5.17962
+-112.12016 5.10558
+-112.22446 5.03129
+-112.37101 4.95678
+-112.66586 4.88413
+-113.49801 4.82742
+-114.55119 4.86380
+-115.13501 4.79640
+-115.64547 4.74397
+-116.87726 4.75090
+-117.76476 4.80158
+-118.81329 4.82560
+-119.22727 4.90493
+-119.52007 4.94111
diff --git a/example/test_problem/Hydro/FermiBubble/plot_scripts/observed_data/xray_profile/m50.dat b/example/test_problem/Hydro/FermiBubble/plot_scripts/observed_data/xray_profile/m50.dat
new file mode 100644
index 0000000000..ac475f192e
--- /dev/null
+++ b/example/test_problem/Hydro/FermiBubble/plot_scripts/observed_data/xray_profile/m50.dat
@@ -0,0 +1,563 @@
+119.11982 3.95830
+119.52124 3.90814
+118.44049 4.04137
+118.07912 4.09977
+117.77105 4.15873
+117.44289 4.21636
+116.83423 4.26936
+115.85384 4.31643
+115.07423 4.37108
+114.71447 4.43636
+114.17961 4.49938
+113.47774 4.53336
+112.75175 4.45682
+112.92990 4.52889
+112.42515 4.38641
+112.27670 4.33321
+112.06887 4.27835
+111.92041 4.22656
+111.71258 4.17311
+111.59225 4.12035
+111.16394 4.06597
+110.64965 4.00626
+109.75044 3.97226
+108.94074 4.04372
+109.11761 3.96760
+108.60022 4.11671
+108.33208 4.17610
+107.94725 4.23900
+107.47868 4.30003
+106.55827 4.27910
+106.19884 4.22834
+105.71403 4.16733
+105.02368 4.13345
+103.81256 4.11402
+102.74004 4.15661
+101.40990 4.16771
+100.26779 4.18203
+99.78231 4.24443
+99.39093 4.30276
+98.84236 4.35650
+98.45645 4.41278
+98.29237 4.47688
+98.11423 4.54022
+97.86601 4.61348
+97.62645 4.69128
+97.35369 4.74353
+96.43458 4.63866
+96.65938 4.71612
+96.35438 4.54446
+96.22957 4.46786
+96.17734 4.40172
+96.07807 4.33942
+95.99920 4.27767
+95.88445 4.21528
+95.79835 4.15533
+95.76167 4.10071
+95.63293 4.03950
+95.57270 3.97439
+95.36536 3.90775
+95.12896 3.84060
+94.93732 3.77837
+94.56915 3.72346
+94.37050 3.68634
+93.50504 3.75177
+93.21526 3.81295
+92.93398 3.86249
+92.74719 3.90979
+92.50268 3.96112
+92.09511 4.01239
+91.70572 4.06722
+91.04688 4.12064
+90.16371 4.12285
+89.63804 4.06770
+89.31305 4.04679
+88.43175 4.11561
+87.84124 4.16164
+87.41963 4.20900
+87.03045 4.08723
+86.85832 4.01553
+86.73608 3.95750
+86.92082 4.14950
+86.49526 3.89815
+86.14590 3.84248
+85.62262 3.78538
+84.59381 3.76737
+83.49203 3.77409
+83.24907 3.83653
+83.09072 3.89405
+82.88586 3.95372
+82.75281 4.01783
+82.52660 4.08519
+82.27918 4.15026
+82.03505 4.20589
+81.77964 4.26191
+81.43966 4.32005
+80.44825 4.29826
+80.18572 4.24106
+79.93031 4.18585
+79.67035 4.13262
+79.32283 4.07690
+78.96965 4.02375
+78.55794 3.96772
+78.28676 3.91072
+78.01361 3.85861
+77.61031 3.80720
+77.13908 3.79087
+76.30639 3.80804
+75.98333 3.75551
+75.60866 3.70780
+75.35286 3.65125
+75.05773 3.59538
+74.80388 3.54423
+74.46392 3.49811
+73.38995 3.48799
+73.05526 3.43890
+72.69303 3.39060
+72.37237 3.34704
+71.77196 3.31124
+71.42610 3.26944
+70.92962 3.37569
+70.69320 3.43719
+70.85221 3.32093
+70.53155 3.48464
+70.25726 3.53430
+70.02212 3.58509
+69.78459 3.63376
+69.35620 3.67565
+68.36682 3.67201
+68.13979 3.74011
+67.96966 3.79467
+67.80938 3.85227
+67.61889 3.90913
+67.34474 3.96673
+66.59574 4.01438
+66.26174 4.08074
+66.02138 4.14594
+65.79517 4.21099
+65.54890 4.27850
+65.02198 4.34347
+64.45682 4.39844
+64.20148 4.46082
+63.99365 4.52666
+63.73378 4.59548
+63.39768 4.66302
+62.34723 4.63369
+61.67099 4.57855
+61.38055 4.54578
+60.61670 4.65443
+60.76330 4.58144
+59.47175 4.68661
+58.50148 4.63520
+57.84676 4.71796
+57.50563 4.77648
+57.36668 4.83112
+57.17072 4.65733
+56.57691 4.70420
+56.69567 4.77127
+56.11599 4.62119
+55.55520 4.57285
+54.66442 4.65244
+54.89443 4.57710
+54.39855 4.73455
+54.21784 4.81003
+54.05461 4.88932
+53.86085 4.97256
+53.67496 5.06000
+53.51452 5.14879
+53.36041 5.22506
+53.27655 5.30257
+53.10849 5.38796
+53.03590 5.47369
+52.91835 5.55440
+52.79959 5.63242
+52.70314 5.71198
+52.39200 5.81426
+51.28206 5.77024
+50.69358 5.72692
+49.72165 5.70704
+49.37038 5.66007
+48.53072 5.78014
+48.79794 5.69388
+48.24701 5.87372
+47.95175 5.95358
+47.65485 6.03810
+47.35794 6.12337
+47.08525 6.20666
+46.75635 6.29732
+46.42268 6.38962
+45.67231 6.46526
+44.86049 6.45077
+44.52247 6.37802
+44.26515 6.29283
+44.04742 6.20333
+43.73732 6.12239
+43.58368 6.03828
+43.15670 5.95599
+42.63865 5.88449
+42.15093 5.86092
+41.61278 5.98437
+40.89546 6.07858
+40.13300 6.15659
+39.41825 6.22705
+38.11776 6.20426
+37.32601 6.18592
+37.04041 6.27613
+36.74351 6.35102
+36.32784 6.42483
+35.29290 6.38718
+34.74501 6.31710
+34.41003 6.25566
+33.82788 6.44206
+33.37064 6.55140
+33.24000 6.36907
+32.58141 6.50601
+32.31435 6.42556
+32.08206 6.34431
+31.76536 6.26049
+31.53443 6.18446
+31.31010 6.10605
+31.13691 6.01323
+30.89618 5.92067
+30.76860 5.83768
+30.52735 5.74788
+30.03598 5.67411
+29.28053 5.64010
+28.58084 5.73185
+27.89805 5.62436
+27.65116 5.53644
+27.92536 5.73670
+27.44761 5.45715
+27.28788 5.37585
+27.12937 5.29851
+26.99098 5.22167
+26.69265 5.15108
+26.45389 5.08211
+26.38373 5.01696
+25.58329 5.23951
+25.71835 5.12923
+24.45946 5.30887
+23.82557 5.38558
+23.32072 5.47451
+22.60597 5.54100
+21.35831 5.52070
+20.51618 5.45681
+19.47538 5.42703
+18.26973 5.46551
+17.60854 5.54635
+17.23201 5.61736
+16.72047 5.68338
+16.46246 5.76453
+15.84454 5.62905
+15.73237 5.54313
+15.51464 5.46612
+15.87093 5.72468
+15.27711 5.38863
+15.03959 5.31312
+14.78651 5.23852
+14.51788 5.16512
+14.11918 5.09409
+13.72538 5.02876
+13.37361 4.96013
+13.22845 4.89607
+12.83708 4.83564
+12.73791 4.77867
+12.43237 4.70997
+12.57526 4.66402
+11.74583 4.91597
+11.85728 4.81960
+11.57827 5.00850
+11.43711 5.08333
+11.80330 4.77127
+11.14740 5.16219
+10.84803 5.24900
+10.49462 5.31165
+10.31876 5.37833
+9.60619 5.26557
+9.44992 5.18850
+9.24660 5.11908
+9.08825 5.04884
+8.92330 4.98621
+8.76087 4.92264
+8.35219 4.86018
+7.30830 4.92305
+6.93402 5.00805
+6.60899 5.07529
+6.36660 5.14368
+6.01091 5.22094
+5.71811 5.29881
+5.45542 5.37621
+5.20065 5.45764
+4.96849 5.54140
+4.74285 5.62462
+4.48751 5.70789
+3.75537 5.78651
+2.93719 5.78023
+2.63668 5.70476
+2.00819 5.63633
+1.32517 5.59053
+0.27216 5.64140
+0.49113 5.57988
+0.02928 5.75142
+-0.12539 5.82462
+-0.31052 5.90275
+-0.48167 5.97592
+-0.83647 6.06150
+-1.16061 6.14656
+-1.64789 6.22121
+-2.68053 6.25802
+-2.92660 6.38063
+-2.50763 6.18477
+-3.31093 6.47716
+-3.46515 6.58298
+-3.59809 6.66330
+-4.18351 6.35220
+-4.12577 6.46811
+-4.30304 6.24182
+-4.54781 6.14420
+-4.22969 6.52749
+-5.35254 6.08296
+-5.81567 6.20232
+-5.87049 6.30899
+-5.98926 6.40432
+-6.09864 6.50058
+-6.21897 6.59309
+-6.30210 6.69156
+-5.93691 6.08049
+-6.63014 6.81245
+-7.61443 6.81341
+-7.65732 6.72010
+-7.81237 6.62745
+-7.91833 6.53705
+-8.03709 6.45029
+-8.15585 6.36593
+-8.27461 6.28310
+-8.39687 6.20377
+-8.82905 6.15418
+-9.87885 6.20042
+-10.43505 6.30787
+-11.01267 6.38809
+-11.63618 6.47111
+-11.79093 6.56312
+-11.96907 6.65132
+-12.12044 6.73849
+-12.31546 6.82687
+-12.62055 6.91924
+-13.91760 6.92945
+-14.93434 6.93924
+-15.54186 6.85489
+-16.03340 6.76736
+-16.55421 6.70073
+-17.55313 6.73499
+-17.96462 6.64222
+-18.22183 6.55193
+-18.52577 6.46453
+-18.82268 6.37413
+-19.11959 6.28189
+-19.59329 6.18718
+-20.57740 6.17526
+-21.73493 6.17538
+-21.51485 6.08432
+-22.64907 6.28942
+-22.40041 6.17958
+-23.23175 6.38854
+-23.71490 6.43358
+-24.62481 6.36268
+-25.02733 6.48812
+-25.27900 6.58803
+-25.61511 6.68448
+-26.87291 6.67111
+-27.75323 6.59793
+-27.98852 6.47694
+-28.17089 6.39156
+-28.38144 6.30955
+-28.62296 6.22253
+-28.90692 6.13765
+-29.26321 6.06185
+-29.65423 5.98756
+-30.13100 6.17469
+-30.28068 6.28100
+-30.49278 6.36758
+-30.20907 6.07094
+-30.77320 6.45079
+-31.02062 6.53119
+-31.30763 6.61948
+-31.63720 6.70842
+-32.04848 6.79859
+-32.56889 6.90077
+-33.68077 6.95930
+-34.16604 7.06768
+-34.48454 7.16606
+-35.14763 7.25742
+-36.25828 7.31491
+-37.21443 7.40581
+-37.79640 7.30113
+-37.60444 7.51297
+-38.41497 7.19734
+-38.27307 7.37098
+-38.19588 7.30152
+-38.63655 7.07706
+-38.95299 6.98377
+-39.23505 6.88675
+-39.61190 6.79905
+-40.66021 6.77214
+-41.00165 6.67904
+-41.33649 6.59411
+-41.97649 6.53777
+-42.94215 6.63318
+-42.71546 6.52271
+-43.35432 6.76443
+-43.61373 6.86438
+-43.83381 6.96195
+-44.21147 7.05174
+-44.51169 7.12756
+-44.61845 7.21272
+-45.31452 6.96951
+-45.46542 6.85636
+-45.23852 7.08600
+-45.71060 6.75805
+-45.86307 6.65927
+-45.99360 6.56962
+-46.19588 6.47490
+-46.31551 6.38395
+-46.60471 6.29157
+-47.67118 6.22505
+-47.95093 6.11190
+-48.21056 6.03105
+-48.52250 5.95361
+-48.62542 5.90539
+-49.41692 6.07539
+-49.27052 5.98066
+-50.04841 6.17766
+-50.96289 6.22967
+-52.20990 6.19024
+-52.73245 6.10669
+-53.07687 6.01950
+-53.38268 5.92700
+-53.81320 5.84235
+-54.29365 5.75650
+-54.73091 5.67932
+-55.17659 5.60492
+-55.84963 5.55971
+-56.78821 5.59383
+-57.22763 5.69141
+-57.67299 5.76451
+-58.74702 5.72975
+-59.33567 5.65630
+-59.74086 5.58269
+-60.09214 5.51553
+-60.64476 5.46154
+-61.88907 5.47051
+-63.21684 5.48326
+-64.38458 5.51564
+-65.43711 5.41918
+-65.12536 5.51258
+-65.89025 5.33035
+-66.39432 5.25263
+-66.84544 5.22701
+-67.87947 5.21477
+-68.53697 5.14453
+-69.29128 5.09138
+-69.75555 5.05181
+-70.13885 5.18589
+-69.84619 5.10555
+-70.39249 5.28414
+-70.70722 5.35949
+-71.42841 5.42054
+-72.22993 5.40036
+-72.74813 5.34791
+-73.65759 5.28495
+-73.82474 5.19853
+-74.00289 5.13197
+-74.25823 5.05789
+-74.37897 4.98079
+-74.87471 4.92497
+-75.85070 4.95410
+-76.20000 5.05102
+-76.47190 5.12386
+-76.84132 5.20066
+-77.05113 5.26899
+-77.28536 5.33634
+-77.53608 5.41193
+-77.79764 5.49750
+-78.04352 5.58406
+-78.25163 5.67285
+-78.52153 5.76089
+-79.09936 5.83073
+-79.64412 5.87502
+-80.13725 5.74822
+-80.40289 5.66090
+-80.68330 5.58256
+-81.00990 5.50796
+-81.52377 5.43168
+-82.19753 5.41304
+-83.29151 5.44910
+-83.92206 5.52016
+-84.17361 5.60483
+-84.58569 5.68136
+-85.81979 5.66859
+-86.95794 5.59425
+-88.16626 5.60204
+-88.80866 5.68836
+-89.37793 5.74860
+-89.70928 5.80569
+-90.28924 5.70607
+-90.54843 5.63369
+-90.91670 5.56078
+-91.26726 5.48656
+-91.81732 5.43852
+-92.93982 5.47668
+-93.58689 5.56102
+-93.92395 5.63835
+-94.37814 5.70826
+-94.66021 5.77017
+-95.41108 5.63928
+-95.65822 5.53673
+-95.26144 5.72811
+-95.81775 5.44948
+-96.01031 5.37306
+-96.13966 5.29739
+-96.28655 5.22035
+-96.47452 5.14321
+-96.73113 5.06859
+-97.01476 4.99864
+-97.30713 4.92960
+-97.72082 4.85904
+-98.42529 4.79633
+-99.54723 4.75508
+-100.07134 4.69514
+-100.77852 4.64743
+-101.94928 4.66547
+-102.76330 4.73157
+-103.61196 4.78708
+-104.90823 4.81711
+-106.00757 4.84535
+-106.81443 4.87927
+-107.59582 4.81298
+-107.97145 4.73527
+-108.44412 4.66012
+-109.06763 4.62575
+-110.17212 4.64205
+-111.57946 4.65245
+-112.52957 4.71038
+-112.78462 4.79810
+-112.95399 4.87355
+-113.11546 4.94405
+-113.28371 5.03303
+-113.47279 5.09477
+-113.68532 5.17045
+-113.86659 5.24279
+-114.02598 5.31754
+-114.20412 5.38894
+-114.50103 5.46162
+-114.74598 5.51265
+-115.61655 5.46232
+-115.98557 5.39851
+-116.52000 5.33122
+-117.08256 5.26466
+-117.61856 5.19817
+-118.12330 5.13217
+-118.74293 5.06099
+-119.16247 5.01183
+-119.31093 4.95336
+-119.57814 4.90462
diff --git a/example/test_problem/Hydro/FermiBubble/plot_scripts/observed_data/xray_profile/m60.dat b/example/test_problem/Hydro/FermiBubble/plot_scripts/observed_data/xray_profile/m60.dat
new file mode 100644
index 0000000000..4dc1610de6
--- /dev/null
+++ b/example/test_problem/Hydro/FermiBubble/plot_scripts/observed_data/xray_profile/m60.dat
@@ -0,0 +1,476 @@
+119.09394 4.56194
+118.59737 4.52354
+117.38204 4.59096
+117.87226 4.51663
+116.99140 4.67825
+116.53458 4.73822
+115.86543 4.79838
+114.85632 4.79812
+114.74402 4.72603
+114.17514 4.66445
+113.50189 4.60658
+113.07114 4.53669
+112.81446 4.47287
+112.28956 4.41142
+111.92058 4.34278
+111.46089 4.29105
+110.25415 4.36326
+110.46991 4.28822
+109.96114 4.45320
+109.68407 4.52729
+109.50077 4.60464
+109.17072 4.67943
+108.97251 4.75739
+108.69609 4.83597
+108.36784 4.91279
+107.98895 4.98370
+107.53035 5.04137
+106.52944 4.94942
+106.80311 5.03812
+106.06873 4.86220
+105.84932 4.79497
+105.31981 4.73350
+104.79248 4.66369
+103.55455 4.66591
+103.05032 4.74981
+102.78760 4.82367
+102.44731 4.89845
+102.02992 4.96617
+101.62680 5.02880
+100.74788 4.92704
+100.86747 5.02856
+100.36399 4.84415
+99.84077 4.78083
+99.46742 4.73126
+98.85558 4.86890
+98.62155 4.96042
+98.83545 4.78300
+98.35418 5.03864
+98.15703 5.11518
+97.96452 5.18645
+97.77201 5.25877
+97.55474 5.32612
+96.36783 5.21526
+96.53606 5.31104
+96.04492 5.11932
+95.72539 5.03508
+95.43634 4.94897
+94.92520 4.87167
+94.61337 4.79027
+94.07967 4.72141
+93.71262 4.64907
+93.32248 4.58273
+92.87843 4.51741
+92.42924 4.45165
+91.88113 4.39528
+91.26274 4.33708
+90.64286 4.27370
+89.44699 4.25254
+88.17434 4.29573
+87.01709 4.36111
+87.26364 4.29885
+85.98667 4.41700
+84.84767 4.40547
+84.56395 4.35529
+83.36336 4.36266
+82.56326 4.30553
+82.36951 4.23414
+82.25262 4.17203
+82.09803 4.11691
+81.94344 4.06257
+81.74391 4.00183
+81.56405 3.93371
+81.34597 3.86997
+81.19966 3.81305
+81.01429 3.75385
+80.77925 3.69240
+80.58621 3.64422
+79.59873 3.76589
+79.44377 3.83798
+79.79436 3.69143
+79.23716 3.89209
+79.88420 3.64519
+79.05119 3.95371
+78.85108 4.01847
+78.68044 4.08520
+78.32008 4.14614
+77.74781 4.19660
+76.60746 4.12294
+76.90034 4.20251
+75.85151 4.05780
+75.27778 4.00213
+74.63609 3.94922
+74.10899 3.89304
+73.76207 3.82927
+73.26492 3.77248
+72.81491 3.71950
+72.61775 3.67533
+71.62287 3.77821
+71.79598 3.70664
+71.34478 3.85403
+71.95932 3.66830
+71.12988 3.91701
+70.94213 3.98247
+70.73755 4.04759
+70.55748 4.11162
+69.91875 4.17227
+68.80055 4.16760
+68.00491 4.15042
+66.64512 4.16551
+65.84722 4.23655
+65.99160 4.16381
+64.64406 4.26442
+63.58787 4.34916
+63.43600 4.43028
+63.36067 4.49861
+63.82412 4.26240
+63.17292 4.57017
+63.08090 4.64698
+62.94229 4.71988
+62.80112 4.79518
+62.69677 4.87395
+62.56440 4.94851
+62.31344 5.01883
+62.07729 5.09275
+61.75398 5.15498
+61.49977 5.22146
+60.47800 5.08836
+60.19072 5.18756
+59.65551 5.03441
+59.08274 5.12568
+58.83594 5.24884
+58.60415 5.35172
+58.39924 5.42697
+58.96510 5.02923
+58.06920 5.50158
+57.65565 5.59345
+56.41790 5.62395
+55.14028 5.64822
+53.91981 5.60038
+53.10724 5.69773
+52.83474 5.80631
+52.59015 5.90163
+53.10165 5.58122
+52.36735 5.98581
+52.04718 6.06962
+51.72944 6.16484
+50.95614 6.24226
+50.19281 6.32353
+49.91308 6.43744
+49.52978 6.53048
+49.25537 6.63183
+48.92758 6.73397
+48.56725 6.82148
+47.61543 6.69073
+47.83177 6.81975
+46.92195 6.57844
+46.35588 6.47531
+45.71225 6.37744
+44.52953 6.33866
+44.18697 6.24827
+43.82762 6.15949
+43.44421 6.07536
+42.99343 5.98524
+42.78519 5.89202
+41.66152 5.87097
+40.42239 5.87939
+39.14700 5.97003
+38.21135 6.07512
+37.99081 6.19585
+37.67094 6.26987
+38.33475 5.97400
+36.82678 6.28091
+36.60219 6.19256
+36.52686 6.10753
+36.37177 6.02286
+36.21718 5.94247
+36.06259 5.86536
+35.86178 5.77992
+35.68632 5.69418
+35.56712 5.61705
+35.24127 5.54105
+35.05655 5.46229
+34.76696 5.37638
+34.22994 5.29461
+33.50181 5.25357
+33.02223 5.39062
+33.10141 5.29939
+32.74464 5.49740
+32.15725 5.57189
+31.29816 5.64607
+31.06226 5.75105
+30.76034 5.82330
+31.36172 5.55514
+30.50613 5.90169
+29.61297 5.70909
+29.37084 5.59562
+29.70110 5.83469
+29.26067 5.51083
+29.09247 5.41574
+28.86885 5.30797
+28.65264 5.22263
+28.48670 5.17651
+27.65222 5.40751
+27.30432 5.29256
+26.99517 5.21771
+26.65599 5.41308
+26.36081 5.34234
+25.96494 5.14790
+26.08204 5.25929
+25.62740 5.03593
+25.13257 4.95201
+24.62397 4.89493
+23.77405 4.97761
+23.44014 5.06531
+23.96089 4.89352
+23.31207 5.15103
+23.10958 5.24259
+22.91427 5.33919
+22.65184 5.43700
+22.52876 5.52676
+21.34971 5.54296
+20.08113 5.55080
+19.32575 5.63600
+19.06095 5.72022
+18.80427 5.79305
+18.54759 5.87030
+17.80444 5.63906
+17.65559 5.54120
+17.86484 5.74870
+17.45065 5.46492
+17.83275 5.84381
+17.22828 5.38731
+16.93011 5.30514
+16.66762 5.21230
+15.74379 5.40087
+15.86276 5.28975
+15.90768 5.20288
+15.11884 5.48836
+14.09669 5.48300
+13.87031 5.40173
+13.31151 5.33181
+12.62781 5.27420
+11.38376 5.29938
+10.11519 5.21510
+10.29289 5.31846
+9.67687 5.12274
+8.41157 5.18375
+8.73144 5.10830
+8.21886 5.29772
+7.98968 5.39274
+7.88013 5.48348
+7.66196 5.58052
+7.43056 5.69169
+7.22194 5.80303
+7.09560 5.89640
+6.96726 5.98074
+6.84839 6.07826
+6.55420 6.20236
+5.38396 6.24519
+5.35188 6.15304
+5.08237 6.06254
+4.88595 5.97628
+4.56996 5.90366
+3.39661 5.96871
+2.41760 6.05349
+1.76792 6.14829
+1.35118 6.23749
+1.19763 6.33121
+0.98036 6.41250
+0.78785 6.50072
+0.59535 6.59033
+-0.16666 6.28414
+-0.35917 6.16511
+-0.20677 6.42949
+-0.57221 6.07568
+-0.13458 6.56901
+-0.03832 6.51390
+-0.70626 5.98731
+-0.85455 5.89921
+-0.97794 5.80471
+-1.18148 5.70692
+-1.38825 5.60987
+-1.92645 5.52315
+-3.07031 5.48400
+-3.83346 5.44416
+-5.03338 5.42886
+-5.75338 5.40929
+-6.40049 5.50960
+-6.90212 5.60313
+-7.39342 5.68796
+-7.65134 5.77745
+-7.90801 5.86467
+-8.21000 5.95549
+-8.39425 6.04640
+-9.06753 6.12251
+-9.63753 6.20358
+-10.47454 6.29463
+-11.66896 6.36543
+-12.34550 6.46835
+-13.30527 6.44866
+-13.75569 6.38226
+-14.76078 6.31963
+-15.09933 6.20434
+-15.46563 6.12296
+-15.96888 6.03953
+-16.44832 5.98578
+-17.54989 6.09003
+-17.75700 6.20828
+-17.17145 5.98304
+-17.94950 6.29956
+-18.14201 6.39270
+-18.33452 6.48599
+-19.38528 6.46732
+-19.52164 6.38157
+-19.95540 6.30156
+-20.75871 6.24310
+-21.88599 6.22908
+-22.84488 6.20173
+-23.91440 6.29042
+-25.00007 6.31274
+-25.09522 6.21276
+-25.83427 6.13738
+-26.88551 6.06973
+-27.47914 5.96962
+-28.17858 5.89369
+-28.79318 5.81107
+-29.36168 5.73751
+-30.13003 5.86663
+-30.47278 5.98168
+-30.09795 5.74306
+-30.88223 6.07421
+-31.26725 6.16617
+-32.39555 6.15236
+-33.04972 6.09166
+-34.11883 6.02419
+-34.76981 5.92567
+-35.98863 5.86421
+-37.06813 5.83573
+-37.32823 5.75555
+-38.00585 5.69219
+-39.13415 5.70130
+-39.96215 5.79490
+-40.41051 5.88466
+-39.88729 5.70164
+-40.67603 5.96820
+-41.01254 6.06183
+-41.35842 6.16158
+-41.99203 6.25484
+-43.25447 6.29787
+-44.50746 6.38491
+-44.05827 6.28987
+-44.97718 6.51212
+-45.28665 6.60644
+-45.60071 6.71383
+-45.85501 6.81713
+-46.11168 6.91559
+-46.39146 7.01166
+-47.49132 6.98617
+-47.56447 6.88967
+-47.92883 6.79717
+-48.20694 6.71007
+-48.60650 6.59914
+-48.95384 6.49326
+-49.26547 6.39530
+-48.87095 6.70232
+-49.80942 6.32009
+-50.78074 6.29512
+-51.18844 6.20632
+-51.67835 6.12742
+-52.21777 6.03860
+-52.45858 5.99428
+-53.54854 6.08039
+-54.67825 6.09450
+-54.73544 6.00311
+-54.94391 5.91698
+-55.23356 5.82795
+-55.53286 5.74845
+-55.99601 5.66960
+-56.56207 5.59374
+-57.62742 5.52488
+-58.00605 5.42290
+-58.34007 5.34912
+-58.64527 5.27039
+-58.96420 5.19445
+-59.37450 5.12236
+-60.57167 5.21852
+-60.20320 5.12125
+-61.03345 5.31813
+-61.29184 5.39058
+-61.81705 5.46562
+-62.30795 5.53927
+-63.23898 5.61155
+-64.41466 5.64785
+-65.58698 5.54908
+-65.26614 5.64466
+-66.29778 5.45937
+-66.60681 5.37044
+-66.89218 5.28495
+-67.10565 5.20723
+-67.35364 5.12315
+-68.11275 5.05242
+-69.35652 5.12535
+-68.99329 5.03277
+-70.57457 5.11837
+-71.44396 5.07692
+-72.54932 5.05382
+-72.98567 4.99181
+-73.49458 4.93095
+-73.98819 4.86048
+-74.26264 4.78615
+-75.31088 4.76519
+-76.04757 4.82584
+-76.66499 4.90340
+-77.28500 4.97168
+-78.27546 4.96118
+-78.59725 4.89366
+-79.11061 4.82989
+-80.28943 4.81280
+-80.98858 4.74764
+-81.49021 4.67996
+-82.11280 4.61485
+-82.80032 4.55632
+-83.97141 4.50209
+-85.18111 4.48094
+-85.70654 4.53481
+-86.31468 4.59050
+-86.92318 4.65554
+-87.55418 4.71880
+-87.84027 4.79509
+-88.16111 4.86401
+-88.56196 4.92045
+-89.65037 4.90928
+-89.72352 4.83524
+-90.01100 4.76475
+-90.30275 4.69692
+-90.58640 4.63629
+-90.98247 4.57198
+-91.37090 4.50270
+-92.67536 4.49794
+-94.00432 4.53299
+-95.23390 4.60408
+-94.88014 4.52521
+-95.86674 4.67869
+-96.53570 4.73197
+-97.66961 4.71397
+-98.70009 4.67950
+-99.94950 4.70203
+-101.19891 4.69353
+-102.28782 4.68283
+-103.38094 4.73234
+-104.66752 4.75405
+-105.94770 4.71062
+-107.14970 4.68435
+-107.81020 4.62699
+-108.28041 4.56549
+-109.13087 4.50695
+-110.40905 4.49424
+-111.80102 4.46651
+-113.08440 4.48234
+-113.99862 4.54581
+-114.59608 4.62485
+-115.74241 4.65740
+-116.92488 4.64608
+-117.44358 4.59189
+-118.78150 4.60023
+-119.45688 4.61576
diff --git a/example/test_problem/Hydro/FermiBubble/plot_scripts/observed_data/xray_profile/p40.dat b/example/test_problem/Hydro/FermiBubble/plot_scripts/observed_data/xray_profile/p40.dat
new file mode 100644
index 0000000000..58ab061fd6
--- /dev/null
+++ b/example/test_problem/Hydro/FermiBubble/plot_scripts/observed_data/xray_profile/p40.dat
@@ -0,0 +1,676 @@
+119.44646 4.94669
+119.66181 4.86218
+119.10683 5.04779
+118.55215 5.08468
+117.95108 4.95476
+117.53970 4.88780
+117.94299 5.04422
+116.45138 4.83701
+115.76544 4.78191
+114.94989 4.72077
+114.00899 4.70857
+112.76877 4.69885
+111.80663 4.76291
+112.13459 4.69758
+111.36409 4.84916
+110.99577 4.91261
+110.55856 4.98124
+109.49171 4.96717
+108.80450 4.90872
+108.46318 4.88825
+107.70799 4.99898
+107.26372 4.91805
+106.99001 4.84052
+106.72837 4.76912
+106.50400 4.69899
+106.26692 4.63214
+106.02984 4.56726
+105.77724 4.50241
+105.50421 4.44223
+105.12302 4.37928
+104.87409 4.31904
+104.60270 4.25788
+104.45073 4.19270
+104.33369 4.13072
+104.20818 4.07635
+104.03246 4.01797
+103.91880 3.96068
+103.70253 3.89893
+102.97747 3.88372
+102.40094 3.93764
+101.98682 3.99346
+101.61427 4.05054
+101.41953 4.11572
+101.25865 4.18104
+101.14011 4.24891
+100.92674 4.31446
+100.71234 4.38536
+100.52766 4.45044
+100.39925 4.50668
+100.15749 4.56683
+100.04363 4.62620
+99.81972 4.68604
+99.65838 4.74744
+99.32910 4.80832
+98.72340 4.87004
+97.90664 4.78223
+98.08774 4.87802
+97.65968 4.70563
+97.45356 4.63713
+97.29384 4.56985
+97.15512 4.50562
+96.95694 4.44391
+96.66104 4.37991
+96.50599 4.34802
+95.59573 4.43666
+95.30987 4.52432
+95.18354 4.58281
+95.00573 4.63806
+94.82792 4.69460
+94.32413 4.74256
+93.09923 4.70935
+92.20689 4.70821
+91.54505 4.76304
+90.76796 4.82361
+89.55294 4.86594
+88.86146 4.94481
+87.80449 4.99289
+86.67382 5.03257
+86.38203 5.11036
+86.15119 5.18587
+85.93283 5.25913
+85.59918 5.34215
+85.03365 5.39351
+84.61078 5.45521
+84.29164 5.29457
+84.21869 5.20439
+84.10674 5.13429
+84.00428 5.06619
+83.88959 4.99577
+83.62600 4.91582
+84.07052 5.38091
+83.46301 4.88203
+82.21513 4.93834
+81.09519 4.92253
+80.38100 4.90032
+79.61790 4.96983
+79.35860 5.04535
+79.12152 5.11840
+78.89762 5.19178
+78.55038 5.26103
+77.48964 5.28445
+77.05697 5.38118
+76.59359 5.44455
+75.75456 5.34045
+75.98024 5.43348
+75.27324 5.25546
+74.79909 5.19950
+74.55185 5.14919
+74.06311 5.30664
+73.90425 5.39610
+73.63102 5.47211
+74.11326 5.22233
+72.72701 5.45519
+72.36481 5.39004
+71.87795 5.31180
+71.18170 5.28994
+70.07755 5.29130
+69.41809 5.22213
+69.20637 5.14018
+68.91626 5.07018
+68.59738 5.01249
+67.51280 5.07803
+66.70458 5.15064
+65.44140 5.15843
+64.67346 5.23575
+64.27171 5.31153
+63.88505 5.38528
+62.91403 5.40151
+62.47867 5.32463
+62.31864 5.24606
+62.14083 5.17079
+61.94656 5.09740
+61.81485 5.03103
+61.65971 4.96418
+61.55083 4.89130
+61.33800 4.81005
+61.15019 4.72732
+61.03368 4.64237
+60.90605 4.56778
+60.59983 4.48470
+59.32458 4.48881
+58.46613 4.51715
+57.59957 4.45271
+56.54465 4.49154
+56.27645 4.56961
+56.03608 4.62963
+55.82708 4.69248
+55.38412 4.75162
+54.13946 4.74479
+52.78853 4.73455
+51.67830 4.77872
+51.20266 4.69351
+51.05745 4.62687
+50.87964 4.56773
+50.60609 4.50927
+49.48681 4.53754
+49.01375 4.61617
+48.78242 4.68059
+48.56814 4.73981
+48.05357 4.57877
+47.98532 4.50281
+48.03471 4.65190
+47.84296 4.44256
+47.58470 4.37370
+46.89988 4.31623
+46.37754 4.40929
+46.28626 4.49396
+46.14303 4.57679
+46.05342 4.65820
+45.91905 4.73999
+46.26980 4.31010
+45.81803 4.82173
+45.69486 4.90627
+45.52564 4.98381
+45.26465 5.05161
+45.07124 5.11672
+44.84114 5.18105
+44.60754 5.24738
+44.49740 5.32631
+44.37887 5.41961
+44.26033 5.51326
+44.14833 5.60649
+44.00954 5.70361
+43.92087 5.80459
+43.79102 5.90403
+43.69996 6.00399
+43.36654 6.09909
+42.98693 6.17923
+42.52265 6.25830
+42.26911 6.33762
+41.95960 6.42021
+41.70334 6.49901
+41.49012 6.58976
+41.20179 6.68964
+41.04092 6.78556
+40.82459 6.87947
+40.49021 6.99026
+40.00499 7.09188
+39.58289 7.18658
+39.39183 7.28243
+39.18710 7.38105
+39.02576 7.47642
+38.83149 7.57315
+38.67014 7.67409
+38.31749 7.76854
+37.84295 7.87548
+37.43579 7.99832
+37.30695 8.11760
+37.07923 8.23136
+36.90252 8.34440
+36.66576 8.46943
+36.48666 8.62549
+36.40661 8.78816
+36.23713 8.93299
+36.15389 9.08019
+35.98685 9.23132
+35.90944 9.38475
+35.78852 9.53515
+35.66626 9.69655
+35.51015 9.84764
+35.40045 9.98646
+35.29876 10.12326
+35.18022 10.26009
+35.06168 10.40024
+34.94314 10.53970
+34.80775 10.68211
+34.72873 10.82355
+34.51536 10.97073
+33.75078 11.10877
+33.37645 11.26166
+33.08010 11.41985
+32.76098 11.57418
+32.51860 11.73665
+32.27794 11.91227
+32.01850 12.09486
+31.81984 12.28205
+31.53626 12.46501
+31.38889 12.66280
+31.25159 12.84738
+30.97358 13.03229
+30.67813 13.24777
+30.52868 13.46513
+29.86017 12.97958
+29.63156 12.75270
+29.88345 13.22651
+29.39448 12.57748
+29.08409 12.41288
+28.82813 12.24126
+28.33356 12.09044
+27.43118 12.06301
+27.16002 12.22179
+26.77674 12.39724
+26.54531 12.59105
+26.43101 12.79348
+26.31247 12.98217
+26.19393 13.17321
+26.04279 13.37312
+25.94368 13.57593
+25.82739 13.77277
+25.69638 13.97570
+25.56536 14.17983
+25.42811 14.38332
+25.33123 14.59298
+25.22296 14.79782
+24.95197 14.99412
+24.79399 15.18743
+24.69287 15.39171
+24.57433 15.59807
+24.62329 15.80889
+23.92962 14.84764
+23.94910 15.13464
+23.81945 14.58844
+23.70461 14.40279
+23.95947 15.35995
+23.58607 14.21765
+23.46754 14.03699
+23.30542 13.85703
+23.06817 13.70885
+22.93950 13.53526
+22.79624 13.38007
+22.72409 13.20271
+22.60239 13.00547
+22.03178 14.05147
+22.06359 13.77184
+21.95101 14.33846
+21.78064 14.62419
+22.01543 13.57554
+22.07470 13.41740
+21.72755 14.90231
+22.04507 13.24037
+21.59208 15.19523
+21.45238 15.54965
+21.33384 15.92880
+21.21530 16.31997
+21.09369 16.71025
+20.98946 17.09891
+20.85968 17.50555
+20.73603 17.92703
+20.61954 18.34892
+20.43162 18.69636
+20.05717 18.94214
+19.76159 19.28037
+19.54673 19.58786
+19.02389 18.91590
+19.00916 18.60338
+18.86718 18.35587
+19.05196 19.20176
+18.76303 18.11654
+18.58320 17.85775
+18.40000 17.55957
+18.18832 17.27578
+18.03592 16.99557
+17.84772 16.71786
+17.53664 16.43920
+16.17512 16.41191
+15.35398 16.13587
+15.25872 15.85514
+15.37726 16.45417
+15.14018 15.65140
+15.02164 15.45000
+14.90310 15.25120
+14.81420 15.06012
+14.66915 14.86144
+14.59978 14.66278
+14.47475 14.43733
+14.34784 14.20262
+14.31700 14.00143
+14.16647 13.78503
+14.08031 13.57418
+13.99431 13.37248
+13.87057 13.15911
+13.81255 12.95882
+13.70078 12.75242
+13.56825 12.53275
+13.40726 12.32153
+13.22663 12.12686
+13.04882 11.93594
+12.87986 11.74112
+12.48952 11.55579
+12.16486 11.52029
+11.44177 11.62293
+11.13950 11.45929
+10.82339 11.30796
+10.59290 11.17346
+10.33936 11.03271
+10.15458 10.88788
+10.00021 10.75514
+9.59256 10.61878
+9.37993 10.54609
+8.38127 10.69472
+7.40129 10.68465
+7.31662 10.53945
+7.08942 10.39853
+6.89127 10.26334
+6.66465 10.13663
+6.52307 9.99626
+6.45721 9.86074
+6.35349 9.70182
+6.22843 9.51210
+6.11147 9.32631
+5.99610 9.14491
+5.86946 8.96690
+5.75092 8.79697
+5.63013 8.63701
+5.42564 8.50478
+4.81714 8.38640
+4.39847 8.31714
+3.68101 8.55699
+3.27822 8.40336
+2.61458 8.24397
+2.98995 8.56191
+2.27872 8.42447
+1.92310 8.31787
+1.46034 8.25314
+1.11377 8.50203
+1.28299 8.37680
+0.93810 8.66349
+0.76212 8.79627
+0.53733 8.92529
+0.30871 9.06044
+0.07587 9.19049
+-0.17955 9.32161
+-0.51503 9.46082
+-1.78430 9.42985
+-2.54680 9.39187
+-2.84809 9.56935
+-3.14288 9.72729
+-3.36966 9.85981
+-3.69564 10.00675
+-4.15695 10.13430
+-4.53964 10.23627
+-5.25508 10.02954
+-5.63704 9.85981
+-5.85531 9.72361
+-6.23992 9.59725
+-6.58593 9.52261
+-7.66031 9.68060
+-8.60146 9.73338
+-8.71420 9.59889
+-8.90137 9.46800
+-9.14468 9.33641
+-9.37958 9.20645
+-9.91656 9.08297
+-10.74786 9.02807
+-11.77911 9.03676
+-12.47196 8.95875
+-13.21112 9.13021
+-13.07206 8.97719
+-13.55699 9.28874
+-13.79219 9.43507
+-14.03519 9.57009
+-14.24185 9.70725
+-14.46193 9.85583
+-14.64270 10.00823
+-14.83767 10.15626
+-15.05166 10.30768
+-15.21465 10.45691
+-15.36797 10.61233
+-15.53174 10.76120
+-15.80654 10.91496
+-16.02142 11.08518
+-16.28390 11.26281
+-16.49721 11.42471
+-16.88224 11.59160
+-17.19597 11.74996
+-17.98915 11.82720
+-18.79551 11.60457
+-18.73229 11.81415
+-19.06763 11.39632
+-19.31511 11.23509
+-19.51597 11.07989
+-19.71320 10.92243
+-19.98163 10.75598
+-20.14488 10.57828
+-20.47439 10.39405
+-21.13540 10.25763
+-21.81007 10.13183
+-22.49116 10.00295
+-23.51743 10.00250
+-23.67141 9.86785
+-23.84922 9.73251
+-24.02703 9.59870
+-24.21142 9.46579
+-24.36289 9.34540
+-24.53602 9.22666
+-24.79124 9.09440
+-25.17714 8.95203
+-25.48308 8.84612
+-25.70139 8.79226
+-26.44720 9.01946
+-26.17061 8.89433
+-27.07494 9.19036
+-27.45478 9.31324
+-28.05583 9.08797
+-28.25898 8.94313
+-28.03759 9.26641
+-28.51767 8.83017
+-28.87922 8.70455
+-29.28940 8.58044
+-30.01400 8.55931
+-30.79362 8.59238
+-30.88252 8.46724
+-31.37785 8.34903
+-31.61916 8.29267
+-32.52090 8.43132
+-32.97851 8.56544
+-33.47556 8.68422
+-34.70540 8.65720
+-35.95932 8.66025
+-36.96537 8.64625
+-37.57997 8.53823
+-38.27297 8.42953
+-39.32584 8.38892
+-39.98038 8.36778
+-40.12855 8.25157
+-40.61338 8.14394
+-41.49768 8.07080
+-42.16820 8.19545
+-42.74801 8.30317
+-43.63752 8.38997
+-43.77362 8.53974
+-43.59582 8.28261
+-43.86253 8.64674
+-44.00373 8.76012
+-44.14570 8.88015
+-44.21814 8.99617
+-44.36632 9.11295
+-44.51449 9.23274
+-45.29981 9.32868
+-46.07373 9.16009
+-45.96659 9.34267
+-46.30904 9.01217
+-46.60539 8.88738
+-46.76286 8.75089
+-47.03344 8.61943
+-47.22113 8.49755
+-47.49143 8.36824
+-47.94915 8.25380
+-48.36108 8.14879
+-48.60079 8.03592
+-48.75226 7.93178
+-48.89620 7.81776
+-49.13751 7.67812
+-49.48160 7.53336
+-50.05618 7.51123
+-50.75424 7.56384
+-51.56527 7.46909
+-51.79683 7.34401
+-51.95280 7.24370
+-52.13061 7.15264
+-52.33934 7.04619
+-52.54550 6.94734
+-52.79652 6.85782
+-53.05198 6.75915
+-53.39305 6.64955
+-54.26431 6.64413
+-54.68908 6.73152
+-54.88960 6.84570
+-55.15335 6.95165
+-55.37696 7.06387
+-55.86189 7.16974
+-56.53086 7.22086
+-57.21508 7.08933
+-57.61633 6.96721
+-57.83459 6.87258
+-58.13242 6.78303
+-58.41317 6.69672
+-58.58769 6.60593
+-58.75990 6.51436
+-58.98495 6.42058
+-59.10070 6.33013
+-59.31469 6.23626
+-59.51837 6.14728
+-59.67759 6.06256
+-59.85539 5.97742
+-60.03320 5.89426
+-60.22869 5.81082
+-60.38035 5.72643
+-61.04925 5.66566
+-62.00151 5.60673
+-63.17628 5.62490
+-63.90152 5.70507
+-64.71004 5.73741
+-65.59112 5.65915
+-65.77304 5.56004
+-66.01744 5.48525
+-66.17747 5.41006
+-66.41701 5.32805
+-66.78794 5.24848
+-67.16858 5.18230
+-67.53078 5.11845
+-67.88640 5.05471
+-68.23630 4.98964
+-68.76708 4.92364
+-69.57644 4.88018
+-70.62267 4.93383
+-70.35596 4.86927
+-70.85663 5.03375
+-70.99921 5.10216
+-71.17868 5.17877
+-71.52949 5.26878
+-72.53058 5.25023
+-73.02437 5.18322
+-73.50202 5.13295
+-74.23606 5.22696
+-74.53445 5.31357
+-74.08993 5.13865
+-74.81520 5.38362
+-75.14493 5.45775
+-75.58225 5.51506
+-76.43563 5.47269
+-76.59110 5.39961
+-76.80316 5.32389
+-77.02377 5.25505
+-77.19269 5.18505
+-77.48176 5.10584
+-77.97208 5.03166
+-78.52403 4.98166
+-79.26503 5.06820
+-79.57235 5.15504
+-79.89834 5.23071
+-80.17987 5.30920
+-80.59282 5.38021
+-81.81037 5.34376
+-82.55311 5.32327
+-83.44735 5.39892
+-83.90749 5.47945
+-84.47833 5.54661
+-85.59314 5.51598
+-85.79252 5.47983
+-86.76690 5.41309
+-86.68469 5.52084
+-86.94481 5.31329
+-87.12921 5.24085
+-87.29220 5.16431
+-87.43543 5.09155
+-87.59913 5.01913
+-87.77694 4.94202
+-87.95474 4.86608
+-88.13255 4.79159
+-88.40350 4.71279
+-88.68856 4.67062
+-89.59074 4.73798
+-89.83189 4.83343
+-90.02748 4.90648
+-90.19094 4.97811
+-90.35939 5.04688
+-90.70470 5.10328
+-91.63792 5.08578
+-91.72259 5.01605
+-91.90039 4.94914
+-92.14933 4.87868
+-92.47423 4.80946
+-92.82985 4.77869
+-93.74583 4.79275
+-94.16745 4.72839
+-94.67195 4.68682
+-95.71699 4.73211
+-95.56522 4.67049
+-95.96191 4.82585
+-96.07889 4.88989
+-96.29292 4.95285
+-96.52059 5.02400
+-97.26983 5.07563
+-98.06441 5.06943
+-98.20073 4.99990
+-98.74898 4.96428
+-99.75195 5.02164
+-99.51652 4.95449
+-100.02031 5.12580
+-100.36561 5.20362
+-100.69361 5.25492
+-101.58003 5.20345
+-102.19488 5.14708
+-103.20200 5.10230
+-103.72464 5.02997
+-103.86294 4.95350
+-104.06109 4.89040
+-104.28770 4.82849
+-104.49515 4.76140
+-104.69836 4.69979
+-104.85076 4.64310
+-105.03450 4.58522
+-105.25380 4.52071
+-105.61411 4.44633
+-106.89556 4.41458
+-108.20404 4.41994
+-108.94584 4.47829
+-109.46694 4.55704
+-110.50111 4.54922
+-110.69926 4.49114
+-110.90612 4.43272
+-111.10369 4.37607
+-111.26832 4.31978
+-111.70077 4.26139
+-112.67103 4.24746
+-112.78101 4.31040
+-112.97067 4.37018
+-113.22751 4.42588
+-113.56218 4.47049
+-113.63153 4.52517
+-114.45241 4.42888
+-114.70924 4.36293
+-115.07989 4.29985
+-115.42612 4.24778
+-115.68324 4.22076
+-116.41702 4.32821
+-116.70621 4.40393
+-116.26013 4.26493
+-117.07444 4.46879
+-117.60554 4.53249
+-118.51236 4.50678
+-118.80871 4.45039
+-119.07542 4.39809
+-119.31520 4.34205
+-119.49031 4.29437
diff --git a/example/test_problem/Hydro/FermiBubble/plot_scripts/observed_data/xray_profile/p50.dat b/example/test_problem/Hydro/FermiBubble/plot_scripts/observed_data/xray_profile/p50.dat
new file mode 100644
index 0000000000..98172a9879
--- /dev/null
+++ b/example/test_problem/Hydro/FermiBubble/plot_scripts/observed_data/xray_profile/p50.dat
@@ -0,0 +1,584 @@
+119.20780 4.16795
+119.47347 4.08816
+119.53890 4.06028
+118.83648 4.24762
+118.50260 4.29131
+117.60160 4.25673
+117.46706 4.20056
+117.21924 4.14700
+117.01106 4.09612
+116.57984 4.04139
+115.61992 4.03814
+114.88998 4.08995
+113.65052 4.09613
+112.29925 4.08059
+111.26940 4.14238
+110.68849 4.20716
+110.30981 4.25910
+109.86472 4.31043
+109.38790 4.34109
+108.20452 4.32979
+107.33192 4.39372
+107.02650 4.47142
+106.69451 4.53301
+105.99453 4.57596
+105.19771 4.49976
+105.26405 4.58704
+104.97327 4.42324
+104.80144 4.36624
+104.49083 4.30422
+104.17758 4.24759
+103.92579 4.19587
+103.42022 4.14568
+102.24366 4.14377
+101.54240 4.20672
+101.16317 4.27141
+100.75450 4.33205
+100.38398 4.38887
+99.92991 4.45035
+98.95695 4.46760
+97.84665 4.41906
+96.79431 4.48602
+97.13532 4.40720
+96.37984 4.56722
+96.12941 4.62786
+95.89149 4.69145
+95.48727 4.76155
+94.32882 4.76189
+93.63601 4.70073
+92.87931 4.64644
+92.40270 4.61586
+91.82191 4.72202
+91.28660 4.80953
+90.64424 4.85773
+89.85371 4.83418
+89.44547 4.78987
+88.20033 4.76094
+87.25772 4.73800
+86.32865 4.79986
+86.50189 4.73518
+86.14171 4.88171
+85.96328 4.94169
+85.78484 5.00306
+85.45771 5.06425
+84.40445 5.02976
+83.66996 4.97632
+83.16779 4.95373
+82.81687 5.07522
+83.13805 4.99303
+82.39228 5.13816
+81.50064 5.10066
+81.21987 5.03117
+80.85595 4.96911
+80.50534 4.89835
+80.29158 4.82757
+80.02951 4.75457
+79.87417 4.69650
+79.66153 4.63606
+79.12077 4.57471
+78.59466 4.53246
+77.77224 4.62109
+78.02290 4.54670
+77.45316 4.69279
+77.19020 4.75271
+76.70694 4.80191
+76.46023 4.85374
+75.67042 4.74589
+75.40981 4.66624
+75.17643 4.60596
+74.93993 4.53867
+74.70697 4.47422
+73.95924 4.42098
+72.93279 4.46644
+71.93281 4.48343
+71.51661 4.42251
+71.28304 4.36285
+71.12669 4.30300
+70.72191 4.24479
+70.52266 4.18257
+69.64410 4.31051
+69.88921 4.24184
+69.13627 4.38622
+68.57771 4.43630
+67.38082 4.40957
+67.44233 4.37607
+66.40873 4.41501
+66.24615 4.35555
+66.03015 4.29638
+65.78911 4.23580
+65.37466 4.18399
+64.16440 4.17461
+63.09379 4.15521
+62.31207 4.12208
+61.30403 4.16599
+61.45218 4.10932
+60.99952 4.24739
+60.75761 4.30479
+60.40915 4.36455
+59.21618 4.40228
+58.51097 4.46308
+58.39499 4.52868
+58.30577 4.58593
+58.16407 4.64586
+58.04512 4.70903
+57.90264 4.77379
+57.82994 4.83623
+57.69524 4.89873
+57.44333 4.96798
+56.68684 5.00457
+56.31324 4.91473
+56.19428 4.84694
+56.07533 4.78454
+55.95637 4.72335
+55.79280 4.65496
+56.10507 5.00804
+55.49857 4.58687
+55.13854 4.57236
+54.38019 4.65542
+53.87462 4.73000
+53.31552 4.78723
+52.49472 4.80728
+52.08340 4.72057
+51.83357 4.64492
+51.32739 4.59753
+50.28212 4.63576
+49.94904 4.72264
+49.65165 4.79333
+49.27991 4.85720
+48.68512 4.93119
+47.43565 4.95155
+47.63479 4.87138
+47.16545 5.05874
+47.04159 5.14162
+46.91894 5.21243
+46.80368 5.28436
+46.70921 5.35621
+46.57276 5.42933
+46.46294 5.50654
+46.32398 5.58634
+46.21842 5.66675
+46.12142 5.74743
+46.00247 5.82630
+45.88184 5.91600
+45.67650 6.01436
+45.38406 6.10384
+44.50501 6.06411
+44.11474 5.98377
+43.67592 5.91359
+43.29736 5.88770
+42.80441 6.04195
+42.44285 6.12512
+42.02467 5.95054
+41.57822 6.00444
+41.85996 6.09051
+41.27707 5.88264
+41.01593 5.80005
+40.85917 5.73245
+40.61320 5.66546
+40.57324 5.58276
+39.98639 5.91374
+39.86568 6.02654
+39.97152 5.79827
+39.70882 6.10851
+39.63127 6.19450
+39.95665 5.71391
+39.51481 6.29125
+39.41285 6.40191
+39.27911 6.51966
+39.16451 6.64065
+39.02827 6.76343
+38.92118 6.88719
+38.79682 7.01565
+38.65847 7.14688
+38.54809 7.27525
+38.42913 7.40017
+38.31018 7.53115
+38.20628 7.66245
+38.09016 7.79255
+37.96142 7.92527
+37.83976 8.06532
+37.70187 8.20840
+37.61806 8.35254
+37.01357 8.44425
+36.47716 8.51649
+36.13516 8.26332
+35.93590 8.11004
+35.76672 7.99181
+36.00133 8.40077
+35.66370 7.88714
+35.45521 7.77364
+35.30246 7.65579
+35.07002 7.54689
+34.89048 7.43918
+34.70376 7.33417
+34.33822 7.23188
+33.54487 7.19303
+33.23817 7.32696
+33.07367 7.44408
+32.87276 7.55127
+32.72340 7.66028
+32.53600 7.77542
+32.43262 7.90718
+32.31366 8.03794
+32.19471 8.17099
+32.07575 8.30712
+31.95679 8.44551
+31.83783 8.58634
+31.71888 8.73017
+31.58583 8.87319
+31.46157 9.01779
+31.32288 9.16349
+31.22624 9.31282
+31.10851 9.47362
+30.99973 9.63499
+30.85957 9.79345
+30.76864 9.95188
+30.66667 10.12302
+30.53356 10.29843
+30.42116 10.48126
+30.28493 10.66883
+30.16597 10.86127
+30.03365 11.06342
+29.94099 11.27472
+29.81556 11.48318
+29.69931 11.68993
+29.55567 11.90415
+29.29348 12.08220
+28.99695 12.24313
+28.74320 12.41045
+28.36375 12.58448
+28.19040 12.76061
+27.91069 12.92765
+27.71457 13.09658
+27.45767 13.28192
+27.32216 13.47859
+27.08788 13.70796
+26.93086 13.93827
+26.74251 14.13804
+26.34838 14.30468
+25.48751 14.22250
+25.27438 14.02651
+24.94615 13.83211
+24.55352 13.66916
+23.50688 13.88813
+23.86771 13.67217
+23.18371 14.12055
+22.74033 14.28392
+22.39697 14.42255
+21.64686 14.12305
+21.23083 13.89823
+20.86405 13.72144
+20.52283 13.53748
+19.91431 13.38563
+19.44608 13.26374
+19.03084 13.62200
+18.87152 13.86874
+18.64352 14.04757
+18.45814 14.24350
+18.90126 13.41910
+18.32134 14.45604
+18.11671 14.67943
+17.97152 14.90826
+17.83065 15.13193
+17.60264 15.38803
+16.65429 15.57535
+15.96248 15.77839
+15.45150 15.98486
+14.41063 16.12422
+14.23045 16.41059
+14.05201 16.62933
+13.87532 16.84111
+13.53392 17.08173
+13.27897 17.22058
+12.43183 16.92685
+12.35162 16.63461
+12.23267 16.41030
+12.11371 16.19130
+11.99845 15.97017
+11.87949 15.74831
+11.75758 15.52496
+11.60703 15.28891
+11.33333 15.05745
+10.97980 14.83827
+10.30571 14.67968
+9.38470 14.76092
+9.33513 14.55229
+9.19227 14.35887
+9.03774 14.16612
+8.89896 13.97165
+8.70813 13.79066
+8.58032 13.60060
+8.26877 13.39687
+7.97704 13.21615
+7.67604 13.03407
+7.16700 12.87172
+6.55947 12.71932
+6.41777 12.54771
+6.30834 12.37911
+6.17583 12.20142
+6.03580 11.99710
+5.86886 11.80500
+5.76351 11.64540
+5.66357 11.49298
+5.50372 11.33607
+5.34866 11.15623
+5.09658 10.98158
+4.88760 10.79810
+4.67697 10.62369
+4.42419 10.47158
+4.01747 10.32776
+3.40339 10.22930
+2.46735 10.30014
+2.24728 10.16405
+1.93907 10.02013
+1.62961 9.90532
+1.42688 9.78203
+0.58559 10.10344
+0.12092 9.93110
+-0.40784 10.18245
+-0.80340 9.75136
+-0.75936 9.99785
+-0.89256 9.55643
+-1.00655 9.42438
+-1.03804 9.29762
+-1.17646 9.17728
+-1.23593 9.05994
+-1.35117 8.94326
+-1.37794 8.81746
+-1.57827 8.67860
+-1.79726 8.54238
+-1.95692 8.41597
+-2.14521 8.29168
+-2.32067 8.16744
+-2.71918 8.07468
+-3.87134 8.08404
+-4.62108 8.14906
+-5.03884 8.02409
+-5.41554 7.92295
+-5.78232 7.81550
+-6.09021 7.70723
+-6.35818 7.60974
+-7.01472 7.50919
+-7.62616 7.41256
+-8.03149 7.30506
+-8.56047 7.19963
+-9.44743 7.13989
+-10.42590 7.17277
+-10.77623 7.04541
+-10.93946 6.92620
+-11.02693 6.83401
+-11.19487 6.73694
+-11.50217 6.62512
+-12.38161 6.58105
+-13.17109 6.47356
+-12.91974 6.59430
+-13.70590 6.36698
+-14.55991 6.31587
+-15.58928 6.41379
+-16.13158 6.52541
+-16.65202 6.57451
+-17.78177 6.56022
+-18.45125 6.69477
+-19.13168 6.77063
+-19.78681 6.85990
+-20.16047 6.96242
+-20.60058 7.05426
+-20.84691 7.15052
+-21.17239 7.24740
+-21.46681 7.35124
+-22.58212 7.38388
+-23.63947 7.32370
+-24.60370 7.40979
+-25.81270 7.46683
+-26.46854 7.55481
+-27.10894 7.40952
+-27.38867 7.29512
+-27.04589 7.54380
+-27.65789 7.20670
+-28.23794 7.11460
+-28.62207 7.05340
+-29.58902 7.14608
+-30.72612 7.13533
+-31.45635 7.26735
+-31.72817 7.37367
+-31.98436 7.46971
+-32.41835 7.55296
+-32.66661 7.63283
+-33.43745 7.48695
+-34.28046 7.40953
+-35.36477 7.48196
+-36.54758 7.49944
+-36.85515 7.39922
+-37.12750 7.30680
+-37.37136 7.21571
+-37.82637 7.12244
+-38.24272 7.02320
+-38.61446 6.92797
+-38.92235 6.83208
+-39.14166 6.74143
+-39.62301 6.66660
+-40.94156 6.69617
+-41.98293 6.76395
+-42.54004 6.86831
+-42.62926 6.95933
+-42.77096 7.05044
+-42.88992 7.14618
+-43.00887 7.24399
+-43.12783 7.34327
+-43.27031 7.44359
+-43.38926 7.54606
+-43.48470 7.64826
+-44.57421 7.67235
+-45.66343 7.60009
+-46.84034 7.69364
+-47.98130 7.79717
+-48.65260 7.86408
+-49.44496 7.72066
+-50.57162 7.72611
+-50.71834 7.86222
+-50.83729 7.96315
+-50.99260 8.06948
+-51.12477 8.17906
+-51.19417 8.28511
+-51.31312 8.39446
+-51.41886 8.50974
+-51.65318 8.64560
+-51.76516 8.78735
+-52.02805 8.93993
+-52.16617 9.08810
+-52.46002 9.22465
+-52.65139 9.33559
+-53.56425 9.16645
+-53.97726 9.01941
+-54.48571 8.89860
+-55.31365 8.79780
+-55.85384 8.67815
+-56.12619 8.56822
+-56.52688 8.44647
+-56.64834 8.31785
+-56.77523 8.19804
+-56.94871 8.06255
+-57.12850 7.92062
+-57.21471 7.80026
+-57.37993 7.69775
+-57.47964 7.59632
+-57.67732 7.48363
+-57.88863 7.37029
+-58.15018 7.26286
+-58.36699 7.15074
+-58.64220 7.04637
+-58.93959 6.96007
+-59.26289 6.87249
+-59.63137 6.78316
+-59.99698 6.69251
+-60.35386 6.60064
+-60.73734 6.50983
+-61.08742 6.42387
+-61.45421 6.34448
+-61.64586 6.25966
+-61.88708 6.17713
+-62.07378 6.07924
+-62.31665 5.98787
+-62.70584 5.92881
+-63.96374 5.93024
+-64.66605 5.84844
+-64.81474 5.75464
+-64.99318 5.67734
+-65.18153 5.60118
+-65.37129 5.51816
+-65.78362 5.46407
+-66.98571 5.45925
+-68.18123 5.47676
+-69.58294 5.51680
+-70.69981 5.48598
+-71.71850 5.48153
+-71.83164 5.40484
+-72.00173 5.33287
+-72.27304 5.26005
+-72.50156 5.18735
+-72.66591 5.11773
+-72.82160 5.04995
+-72.97520 4.98076
+-73.14174 4.91267
+-73.29743 4.84806
+-73.55501 4.77333
+-74.45770 4.74387
+-75.28494 4.78586
+-76.08051 4.86388
+-76.68071 4.89635
+-77.63236 4.83798
+-78.94089 4.87054
+-80.02752 4.84317
+-80.66577 4.78471
+-81.07928 4.71951
+-81.47413 4.66005
+-82.00899 4.60167
+-82.78496 4.57499
+-83.43692 4.64444
+-83.74116 4.71119
+-84.00978 4.77594
+-84.25100 4.83878
+-84.53187 4.90135
+-84.79170 4.96621
+-85.11179 5.03449
+-85.56680 5.10572
+-86.56813 5.08331
+-86.72266 5.01473
+-86.90110 4.94646
+-87.07953 4.87962
+-87.26788 4.81199
+-87.49228 4.73962
+-87.75770 4.67334
+-88.02128 4.60780
+-88.31300 4.53969
+-88.67837 4.47546
+-89.42398 4.43251
+-90.33966 4.50813
+-90.06337 4.42872
+-90.81677 4.58737
+-91.30161 4.64602
+-91.76594 4.67932
+-92.72801 4.64310
+-93.21573 4.71285
+-93.69156 4.77330
+-94.94061 4.78847
+-96.18966 4.78054
+-97.46947 4.79939
+-97.99186 4.86763
+-98.30115 4.93518
+-98.75467 4.98255
+-99.67340 4.95805
+-99.75837 4.89037
+-99.98141 4.82154
+-100.16220 4.75322
+-100.38289 4.69095
+-100.70485 4.62805
+-101.39522 4.57254
+-102.62478 4.54251
+-103.43460 4.48695
+-104.07213 4.42518
+-104.74916 4.38718
+-105.89609 4.37974
+-106.53892 4.32566
+-107.28469 4.28334
+-107.71814 4.24282
+-108.23406 4.36538
+-108.14484 4.29812
+-108.50484 4.44866
+-108.92617 4.50939
+-109.51104 4.56830
+-110.25633 4.55797
+-110.43320 4.49869
+-111.02955 4.44477
+-111.62433 4.39008
+-112.17686 4.32969
+-112.64710 4.27974
+-113.73384 4.27753
+-114.12712 4.34930
+-114.42139 4.40461
+-114.86591 4.46549
+-115.56622 4.50590
+-116.35828 4.48008
+-116.76922 4.43013
+-118.01827 4.42711
+-119.14836 4.45210
diff --git a/example/test_problem/Hydro/FermiBubble/plot_scripts/observed_data/xray_profile/p60.dat b/example/test_problem/Hydro/FermiBubble/plot_scripts/observed_data/xray_profile/p60.dat
new file mode 100644
index 0000000000..b127e9a24d
--- /dev/null
+++ b/example/test_problem/Hydro/FermiBubble/plot_scripts/observed_data/xray_profile/p60.dat
@@ -0,0 +1,555 @@
+118.92935 4.17051
+119.25200 4.09619
+118.54597 4.24753
+118.25968 4.31179
+118.11310 4.37998
+117.92184 4.44913
+117.72505 4.51607
+117.49624 4.58629
+117.37549 4.65604
+117.11387 4.71585
+116.87041 4.77950
+116.62575 4.84709
+115.79906 4.87658
+114.73102 4.82093
+114.16540 4.73864
+113.59431 4.67380
+113.29303 4.60268
+112.83805 4.54084
+112.33254 4.47721
+111.16373 4.50132
+111.54709 4.44432
+110.67557 4.59186
+110.24680 4.65374
+109.88356 4.72449
+109.38319 4.78728
+108.67060 4.85483
+107.55428 4.85582
+107.18065 4.78945
+106.80996 4.72576
+106.47817 4.66484
+106.23060 4.59777
+105.89242 4.53242
+105.65467 4.46354
+105.43407 4.40141
+104.78637 4.34154
+103.89541 4.31523
+103.00651 4.37007
+103.21059 4.30801
+102.51525 4.45002
+102.07483 4.52017
+101.62193 4.58682
+100.61701 4.57669
+100.36045 4.51598
+99.88228 4.46036
+98.99122 4.45183
+98.57046 4.50937
+98.16936 4.57591
+97.72632 4.63635
+97.36047 4.70071
+97.06360 4.76788
+96.74901 4.83423
+96.42039 4.90210
+96.10097 4.97292
+95.78228 5.03853
+95.40646 5.09382
+94.35502 4.99529
+94.62242 5.08416
+94.07248 4.90283
+93.78420 4.82159
+93.49516 4.73240
+92.81812 4.65830
+92.18801 4.60016
+91.03615 4.60735
+89.82500 4.61775
+89.29545 4.55305
+89.02281 4.48845
+88.73444 4.42998
+88.42401 4.36806
+87.75070 4.30591
+87.19821 4.25282
+86.14435 4.26701
+85.04331 4.31902
+83.80070 4.32137
+82.47372 4.37810
+82.91986 4.30508
+81.78525 4.45819
+80.69439 4.49004
+79.48993 4.51804
+78.92840 4.58506
+78.40556 4.63366
+77.36569 4.55311
+77.66629 4.63035
+76.91478 4.47419
+76.68834 4.41128
+75.98163 4.36203
+74.81143 4.38151
+73.94561 4.44732
+73.44359 4.50929
+72.56816 4.55555
+71.52818 4.54381
+71.32803 4.48240
+71.01057 4.42593
+70.68026 4.36844
+70.13647 4.31163
+69.34748 4.26208
+68.21301 4.25437
+67.16898 4.20498
+66.06473 4.17410
+64.79977 4.21289
+63.58665 4.22961
+62.77594 4.17335
+62.14781 4.14388
+61.68013 4.24788
+61.37805 4.32426
+61.68537 4.18518
+61.05435 4.40087
+60.79970 4.47534
+60.56168 4.54253
+59.51474 4.53031
+59.42036 4.47332
+58.83663 4.42502
+57.94007 4.52530
+57.72044 4.60881
+58.00024 4.42760
+57.43174 4.68507
+57.13700 4.75388
+56.77543 4.81821
+56.52880 4.89081
+55.94299 4.95386
+55.31188 5.02715
+54.77376 5.09011
+53.77199 5.00242
+53.91513 5.11532
+53.41682 4.90818
+53.13653 4.83933
+52.83044 4.77109
+52.44774 4.70048
+51.25798 4.73429
+51.58720 4.67323
+50.85736 4.83165
+50.47333 4.89516
+50.18205 4.97148
+49.85156 5.05027
+49.63677 5.12375
+49.35015 5.20083
+49.03906 5.28063
+48.74195 5.36129
+48.20541 5.41960
+46.98325 5.40718
+46.29073 5.36745
+45.98929 5.51569
+45.83440 5.62337
+45.60222 5.70346
+45.89321 5.41935
+45.48151 5.78153
+45.19570 5.87202
+44.93822 5.96876
+44.68264 6.06558
+44.42724 6.15780
+43.50866 6.19416
+43.03498 6.07913
+42.80902 5.98399
+42.57842 5.89341
+42.84174 6.21046
+42.26542 5.81153
+41.86789 5.73894
+41.46880 5.66429
+41.01040 5.58467
+40.53944 5.50753
+40.20174 5.42645
+40.06129 5.34343
+39.76887 5.26597
+39.51163 5.19956
+38.35066 5.28308
+38.68921 5.20096
+38.04795 5.38474
+37.80139 5.46568
+37.55923 5.54700
+37.42885 5.64677
+37.27987 5.76422
+37.15252 5.88683
+37.02747 6.00870
+36.89608 6.13425
+36.78073 6.26363
+36.64918 6.39100
+36.53153 6.52480
+36.40188 6.65623
+36.24560 6.78336
+36.92754 5.56070
+36.14108 6.91025
+36.00934 7.04162
+35.88102 7.17475
+35.77334 7.30919
+35.66514 7.44350
+36.23545 6.16587
+35.48654 7.58589
+35.17373 7.70570
+34.78081 7.80963
+34.43558 7.92381
+34.23110 8.05615
+34.07530 8.18870
+33.85135 8.32569
+33.70013 8.46274
+33.41767 8.60416
+33.17914 8.73543
+32.98839 8.85844
+32.59928 8.98700
+31.69424 8.78733
+31.38635 8.99080
+30.87492 8.74250
+30.29429 8.87971
+30.07443 9.13392
+30.09475 8.99774
+29.83961 9.32221
+30.38417 8.76485
+29.62917 9.48256
+29.45877 9.63518
+28.53296 9.69845
+28.01982 9.52096
+27.68294 9.35145
+27.42258 9.28560
+27.05363 9.68547
+26.75596 9.51210
+26.50298 9.86454
+26.36647 10.04312
+26.84509 9.38554
+26.21429 10.22027
+26.04739 10.39966
+25.83449 10.59084
+26.35749 9.67405
+25.60682 10.78797
+25.40618 10.96072
+24.42863 11.08200
+23.90605 11.26280
+23.43301 11.41377
+22.22031 11.47076
+21.52546 11.65356
+20.88588 11.80923
+20.22308 11.97842
+19.69877 12.13260
+18.89515 11.91576
+18.56364 11.72198
+18.90182 12.16484
+18.34081 11.55963
+17.95323 11.39109
+17.78042 11.19503
+17.27778 11.12273
+16.45401 11.33398
+16.24426 11.53477
+16.68924 11.15578
+16.05551 11.69529
+15.88865 11.86051
+15.70163 12.02617
+15.48926 12.18516
+15.29377 12.36600
+15.10502 12.56150
+14.94773 12.76274
+14.74930 12.95079
+14.54773 13.14586
+14.07899 13.33162
+13.41079 13.50603
+12.84251 13.71227
+11.82366 13.70522
+11.35528 13.47852
+10.59468 13.29529
+10.02004 13.09781
+9.54001 12.92761
+8.52838 13.04098
+8.08235 12.85896
+7.78532 12.65908
+7.46477 12.47597
+7.17042 12.30534
+6.83487 12.14585
+6.55073 11.98130
+6.08656 11.81588
+5.54648 11.64881
+5.35328 11.46796
+5.20917 11.30436
+5.02212 11.13139
+4.75431 10.95543
+4.62539 10.79874
+4.45563 10.64139
+4.16967 10.48679
+4.02195 10.33583
+3.82565 10.18475
+3.63659 10.03875
+3.42550 9.89977
+3.02227 9.76629
+2.65642 9.62362
+2.25021 9.49238
+1.63111 9.37241
+1.25990 9.22147
+1.03582 9.07175
+0.80225 8.94270
+0.60812 8.81323
+0.20163 8.68658
+-0.00473 8.56186
+-0.26647 8.44686
+-0.58409 8.33928
+-1.31131 8.24296
+-1.87772 8.12464
+-2.28323 7.99974
+-2.61055 7.88670
+-2.90447 7.75526
+-3.25377 7.63108
+-3.68654 7.53978
+-4.88080 7.52608
+-6.02071 7.45929
+-6.82134 7.31937
+-7.18401 7.19175
+-6.86269 7.44156
+-7.59599 7.09543
+-8.85667 7.09927
+-10.11902 7.19262
+-11.36550 7.28712
+-12.05238 7.39748
+-12.42400 7.52119
+-12.94802 7.60948
+-13.51887 7.70607
+-14.71657 7.71720
+-15.88123 7.84528
+-16.25829 7.71033
+-16.27926 8.00851
+-17.07995 7.84291
+-17.26444 7.63116
+-17.02378 7.72696
+-17.08670 8.01401
+-17.44242 7.47990
+-17.63790 7.37360
+-17.86384 7.26666
+-18.08550 7.16292
+-18.34178 7.06036
+-19.01825 7.02863
+-19.59412 7.13662
+-20.48606 7.20507
+-20.96911 7.06849
+-21.16581 6.95814
+-21.39651 6.85285
+-21.14484 7.22495
+-21.79236 6.76244
+-22.25799 6.67446
+-22.66384 6.57955
+-23.05413 6.48575
+-23.54355 6.39194
+-24.73650 6.35052
+-25.89857 6.34381
+-26.10444 6.25333
+-26.68485 6.21611
+-27.46530 6.30403
+-28.10840 6.40895
+-28.74521 6.50802
+-29.25065 6.60780
+-30.37099 6.60963
+-30.64531 6.51677
+-30.94731 6.42625
+-31.25938 6.33877
+-31.64848 6.30127
+-32.67167 6.33120
+-33.23141 6.24422
+-33.81381 6.16180
+-34.37510 6.07203
+-35.45982 6.07016
+-35.69869 6.14524
+-36.13210 6.22495
+-36.52479 6.32946
+-37.19630 6.40956
+-38.27741 6.29412
+-38.20986 6.42852
+-39.48846 6.32151
+-39.71322 6.47568
+-39.93539 6.61690
+-40.11939 6.74237
+-40.30205 6.86966
+-40.50375 6.99343
+-40.99515 7.08827
+-41.64320 7.16358
+-42.70649 7.14531
+-42.91160 7.04742
+-43.38463 6.95980
+-43.67416 6.86427
+-43.87740 6.75787
+-44.19424 6.65214
+-44.37053 6.55020
+-44.82583 6.46414
+-45.51259 6.38399
+-46.58847 6.30455
+-47.06916 6.20283
+-47.58745 6.10121
+-47.97265 5.99686
+-48.13120 5.91019
+-47.69581 6.19731
+-48.61073 5.85594
+-49.44453 5.95239
+-49.74481 6.06525
+-50.05615 6.17812
+-49.33165 5.86013
+-50.29172 6.28305
+-50.54973 6.37927
+-51.74737 6.43166
+-52.26516 6.55182
+-52.67835 6.63582
+-53.04609 6.73641
+-53.41901 6.83505
+-53.72011 6.94227
+-54.00149 7.05133
+-54.23918 7.16081
+-54.48581 7.27087
+-54.58522 7.37431
+-54.72516 7.49892
+-54.83689 7.62769
+-54.94820 7.75848
+-55.10743 7.88977
+-55.20229 8.02975
+-55.33914 8.16637
+-55.46955 8.30775
+-55.67054 8.44545
+-55.86104 8.58607
+-56.02263 8.72732
+-56.21757 8.86129
+-56.41223 9.00203
+-56.65004 9.13172
+-57.09222 9.24699
+-57.48989 9.37227
+-58.70463 9.35650
+-59.87287 9.34115
+-60.30656 9.21980
+-60.68575 9.09362
+-60.86834 8.96699
+-61.12533 8.83686
+-61.47221 8.70005
+-61.57687 8.56619
+-61.74989 8.44129
+-61.84710 8.31822
+-62.08928 8.19297
+-62.14575 8.06234
+-62.40580 7.94080
+-62.54422 7.81897
+-62.61028 7.71199
+-62.77791 7.59969
+-62.88616 7.48407
+-62.99408 7.39264
+-63.13621 7.29958
+-63.26205 7.20309
+-63.36488 7.10470
+-63.53516 6.99940
+-63.95501 6.89854
+-64.31938 6.79513
+-64.72990 6.70229
+-65.20022 6.61317
+-65.67639 6.52705
+-66.37086 6.44441
+-67.39053 6.56292
+-67.12572 6.42600
+-67.81666 6.67415
+-68.22853 6.76549
+-68.69436 6.86125
+-69.78299 6.87350
+-69.89292 6.77444
+-70.23771 6.68103
+-70.50846 6.59011
+-70.74631 6.50287
+-70.93388 6.40540
+-71.20488 6.31324
+-71.38406 6.22388
+-71.54135 6.14389
+-71.82573 6.06241
+-72.04019 5.96734
+-72.29635 5.86752
+-72.55252 5.76949
+-72.80418 5.67543
+-73.05353 5.58023
+-73.31651 5.48658
+-73.56679 5.40076
+-73.82649 5.32489
+-74.21774 5.25005
+-74.55382 5.18347
+-75.77776 5.17003
+-76.94495 5.14823
+-77.43093 5.08070
+-77.93068 5.01920
+-78.62710 4.95590
+-79.04195 4.87547
+-79.33781 4.80968
+-79.81009 4.74548
+-80.19244 4.67366
+-80.43129 4.61087
+-81.63204 4.62180
+-82.02113 4.70680
+-82.33572 4.77237
+-82.66601 4.83792
+-82.96078 4.91296
+-83.25789 4.98824
+-83.79443 5.04890
+-84.84829 5.05071
+-84.90879 4.97650
+-85.26424 4.90618
+-85.76059 4.84309
+-86.56332 4.80595
+-87.25981 4.87769
+-87.70017 4.96362
+-87.17622 4.79511
+-88.22044 5.03284
+-89.15811 5.04320
+-89.33899 4.96798
+-89.54655 4.90002
+-89.68241 4.83564
+-89.87593 4.77451
+-90.07390 4.70702
+-90.30634 4.63734
+-90.44219 4.57317
+-90.80847 4.51083
+-91.04804 4.44421
+-91.53547 4.38777
+-92.69220 4.40781
+-93.33883 4.48636
+-93.73007 4.55037
+-93.96884 4.62045
+-94.33977 4.68922
+-94.70878 4.76052
+-95.31139 4.81982
+-96.32402 4.74397
+-96.37640 4.82137
+-97.60893 4.77923
+-97.14856 4.71351
+-98.01004 4.87151
+-98.38425 4.93961
+-99.31291 4.97653
+-100.32063 4.89258
+-100.10566 4.98540
+-100.74607 4.80754
+-100.99945 4.71867
+-101.26721 4.63834
+-101.50494 4.57086
+-101.89064 4.50469
+-102.13998 4.44014
+-102.45719 4.38457
+-103.09422 4.33342
+-103.80770 4.28240
+-104.76337 4.22505
+-105.29856 4.16565
+-106.43511 4.18017
+-107.43448 4.24626
+-108.48102 4.28816
+-109.68325 4.28027
+-110.53568 4.22619
+-111.70557 4.23445
+-112.45258 4.30089
+-112.77528 4.36478
+-112.98176 4.43401
+-113.25531 4.49440
+-113.44954 4.55576
+-113.84853 4.62121
+-114.22004 4.69175
+-114.98553 4.72680
+-115.89273 4.66905
+-116.23758 4.58564
+-116.59243 4.52984
+-117.49592 4.49509
+-117.95545 4.56926
+-118.25092 4.64255
+-118.62348 4.71024
+-118.09992 4.49404
+-118.93415 4.78400
+-118.99926 4.85856
+-119.16951 4.92473
diff --git a/example/test_problem/Hydro/FermiBubble/plot_scripts/plot_map.py b/example/test_problem/Hydro/FermiBubble/plot_scripts/plot_map.py
new file mode 100644
index 0000000000..1d4d9411f8
--- /dev/null
+++ b/example/test_problem/Hydro/FermiBubble/plot_scripts/plot_map.py
@@ -0,0 +1,149 @@
+#====================================================================================================
+# Imports
+#====================================================================================================
+import h5py
+import matplotlib as mpl
+import matplotlib.pylab as plt
+from matplotlib.colors import LogNorm, ListedColormap
+import numpy as np
+import argparse
+import sys
+
+
+
+#====================================================================================================
+# Constants
+#====================================================================================================
+CM2INCH = 1/2.54
+PROJECT_METHOD = "hammer"
+
+
+
+#====================================================================================================
+# Parser
+#====================================================================================================
+parser = argparse.ArgumentParser( description='Plot perspective projection map' )
+
+parser.add_argument( '-t', action='store', required=True, type=str, dest='plot_type',
+ choices=["x_ray", "gamma_ray"], help='Plot map type.' )
+
+args=parser.parse_args()
+
+# take note
+print( '\nCommand-line arguments:' )
+print( '-------------------------------------------------------------------' )
+print( ' '.join(map(str, sys.argv)) )
+print( '-------------------------------------------------------------------\n' )
+
+
+#====================================================================================================
+# Parameters
+#====================================================================================================
+plot_type = args.plot_type
+plot_args = {}
+if plot_type == "x_ray":
+ plot_args["normal_const"] = 26e26
+ plot_args["observed_energy"] = 1
+ plot_args["scale_cr_engy"] = 1
+ plot_args["offset"] = 4.5
+ plot_args["lower_limit"] = 0.0
+ plot_args["filename"] = "./Projected_FRB_Data_000035_XRay.h5"
+ plot_args["b_max_idx"] = 3
+ plot_args["b_min_idx"] = 4
+ plot_args["l_max_idx"] = 5
+ plot_args["l_min_idx"] = 6
+ plot_args["field"] = "ProjectedXray_08_keV"
+ img = plt.imread("cubehelix.png")
+ img = img[0,:,:]
+ plot_args["cmap"] = ListedColormap( img )
+ plot_args["log_scale"] = True
+ plot_args["savename"] = "XRay_map_0.8keV.png"
+ plot_args["title"] = r"Count rate (photons s$^{-1}$deg$^{-2}$)"
+ plot_args["cbar_max"] = None
+ plot_args["cbar_min"] = None
+ plot_args["xtick_label_dark_range"] = [3, 11]
+elif plot_type == "gamma_ray":
+ plot_args["normal_const"] = 1.687e29
+ plot_args["observed_energy"] = 108.61e9
+ plot_args["scale_cr_engy"] = 250
+ plot_args["offset"] = 0.0
+ plot_args["lower_limit"] = 1.e-13
+ plot_args["filename"] = "./Projected_FRB_Data_000035_100e9_1e6_2.4.h5"
+ plot_args["b_max_idx"] = 7
+ plot_args["b_min_idx"] = 8
+ plot_args["l_max_idx"] = 9
+ plot_args["l_min_idx"] = 10
+ plot_args["field"] = "ProjectedLeptonicGammaRay"
+ plot_args["cmap"] = mpl.colormaps["nipy_spectral"]
+ plot_args["log_scale"] = True
+ plot_args["savename"] = "GammaRay_100e9_1e6.png"
+ plot_args["title"] = r"Photon flux (GeV$^{-1}$cm$^{-2}$s$^{-1}$sr$^{-1}$)"
+ plot_args["cbar_max"] = 1.e-11
+ plot_args["cbar_min"] = 1.e-9
+ plot_args["xtick_label_dark_range"] = [5, 9]
+
+fig_width = 20.0 * CM2INCH
+fig_height = 11.0 * CM2INCH
+fig_dpi = 400
+
+
+
+#====================================================================================================
+# Load data
+#====================================================================================================
+hf = h5py.File( plot_args["filename"], 'r')
+group1 = hf.get("Map")
+group2 = hf.get("Info")
+
+b_max = np.array(group2.get('Keys')).tolist()[plot_args["b_max_idx"]]
+b_min = np.array(group2.get('Keys')).tolist()[plot_args["b_min_idx"]]
+l_max = np.array(group2.get('Keys')).tolist()[plot_args["l_max_idx"]]
+l_min = np.array(group2.get('Keys')).tolist()[plot_args["l_min_idx"]]
+
+image = group1.get( plot_args["field"] )
+image = np.array(image[0]) * plot_args["normal_const"] / plot_args["observed_energy"] * plot_args["scale_cr_engy"] + plot_args["offset"]
+plot_arr = image
+plot_arr = np.where( plot_arr > plot_args["lower_limit"], plot_arr, plot_args["lower_limit"] )
+plot_arr = np.roll( plot_arr, int(plot_arr.shape[1]/2), axis=1 )
+
+# meshgrid
+x = np.linspace( l_min, l_max, plot_arr.shape[1] ) * np.pi/180.0
+y = np.linspace( b_min, b_max, plot_arr.shape[0] ) * np.pi/180.0
+X, Y = np.meshgrid( x, y )
+
+
+
+#====================================================================================================
+# Plot
+#====================================================================================================
+norm_func = mpl.colors.LogNorm if plot_args["log_scale"] else mpl.colors.Normalize
+if plot_args["cbar_min"] is None: plot_args["cbar_min"] = plot_arr.min()
+if plot_args["cbar_max"] is None: plot_args["cbar_max"] = plot_arr.max()
+plot_norm = norm_func( vmin=plot_args["cbar_min"], vmax=plot_args["cbar_max"] )
+plot_x_tick = np.array( [ -150, -120, -90, -60, -45, -30, -15, 0, 15, 30, 45, 60, 90, 120, 150 ] )
+plot_x_tick_label = np.array( [ "10h", "8h", "6h", "4h", "3h", "2h", "1h", "0h", "23h", "22h", "21h", "20h", "18h", "16h", "14h" ] )
+plot_y_tick = np.array( [ -70+10*i for i in range(15) ] )
+plot_y_tick_label = np.array( [ r"%d$^{\circ}$"%i for i in plot_y_tick ] )
+
+fig = plt.figure( figsize=(fig_width, fig_height) )
+
+gs = fig.add_gridspec( 1, 2, width_ratios=(20, 1), wspace=0.1 )
+ax = fig.add_subplot( gs[0, 0], projection=PROJECT_METHOD )
+im = ax.pcolormesh( X, Y, plot_arr, norm=plot_norm, cmap=plot_args["cmap"], shading="auto" )
+ax.grid(color='silver', linestyle='-', linewidth=0.5, axis='x')
+ax.grid(color='silver', linestyle='-', linewidth=0.5, axis='y')
+ax.get_xaxis().set_ticks( plot_x_tick*np.pi/180 )
+ax.get_yaxis().set_ticks( plot_y_tick*np.pi/180 )
+ax.set_xticklabels( plot_x_tick_label, rotation=90 )
+ax.set_yticklabels( plot_y_tick_label )
+for i in range(len(plot_x_tick)):
+ color = "black" if i > plot_args["xtick_label_dark_range"][0] and i < plot_args["xtick_label_dark_range"][1] else "white"
+ ax.get_xticklabels()[i].set_color( color )
+
+ax = fig.add_subplot( gs[0, 1] )
+cbar = fig.colorbar( im, cax=ax, use_gridspec=True )
+
+plt.suptitle( plot_args["title"] )
+
+plt.savefig( plot_args["savename"], dpi=fig_dpi )
+plt.show()
diff --git a/example/test_problem/Hydro/FermiBubble/plot_scripts/plot_profile.py b/example/test_problem/Hydro/FermiBubble/plot_scripts/plot_profile.py
new file mode 100644
index 0000000000..a8306fdf4b
--- /dev/null
+++ b/example/test_problem/Hydro/FermiBubble/plot_scripts/plot_profile.py
@@ -0,0 +1,164 @@
+#====================================================================================================
+# Imports
+#====================================================================================================
+import yt
+import matplotlib as mpl
+import matplotlib.pylab as plt
+from matplotlib.colors import LogNorm
+import numpy as np
+import argparse
+import sys
+
+
+
+#====================================================================================================
+# Constants
+#====================================================================================================
+LINE_RES = 8192
+PARTICLE_MASS = 1.6726231e-24 # proton mass in gram
+CM2INCH = 1/2.54
+
+
+
+#====================================================================================================
+# Derived fields
+#====================================================================================================
+def number_density( field, data ):
+ return data["Dens"] / PARTICLE_MASS / yt.YTQuantity(1.0, "g")
+
+def pressure_sr( field, data ):
+ from yt.units import speed_of_light_cgs, boltzmann_constant_cgs
+ unit_v = speed_of_light_cgs
+
+ pres = data["frame_density"] * data["Temp"] * yt.YTQuantity(1.0, "K")
+ pres /= PARTICLE_MASS * yt.YTQuantity(1.0, "g")
+ pres /= speed_of_light_cgs**2
+ pres *= unit_v**2
+ pres *= boltzmann_constant_cgs
+ return pres
+
+def pressure_cr( field, data ):
+ from yt.units import speed_of_light_cgs
+ unit_v = speed_of_light_cgs
+ unit_d = data.ds.mass_unit / data.ds.length_unit**3
+
+ NormalizedConst_CRPres = 7
+
+ p_cr = data["CRay"] / 3 / data["lorentz_factor"]
+ p_cr *= unit_d
+ p_cr *= unit_v**2
+ return p_cr * NormalizedConst_CRPres
+
+def temperature_erg( field, data ):
+ from yt.units import boltzmann_constant_cgs
+ erg2eV = 6.242e+11
+
+ temp = data["Temp"] * yt.YTQuantity(1.0, "K")
+ temp *= boltzmann_constant_cgs
+ return temp
+
+
+
+#====================================================================================================
+# Functions
+#====================================================================================================
+def get_line_data( ds, start, end, plot_var ):
+ field = plot_var["field"]
+ field_unit = plot_var["var_unit"]
+
+ line = yt.LineBuffer( ds, start, end, LINE_RES )
+ plot_arr = np.array( line[field].to( field_unit ) )
+ plot_r = np.sqrt( (line[("index", "x")]-ds.domain_center[0])**2 +
+ (line[("index", "y")]-ds.domain_center[1])**2 +
+ (line[("index", "z")]-ds.domain_center[2])**2 )
+ return plot_arr, plot_r
+
+
+
+#====================================================================================================
+# Main
+#====================================================================================================
+parser = argparse.ArgumentParser( description='Plot gas pressure, number density, temperature, and cosmic rays pressure slice for Fermi bubble' )
+
+parser.add_argument( '-s', action='store', required=True, type=int, dest='idx_start',
+ help='first data index' )
+parser.add_argument( '-e', action='store', required=True, type=int, dest='idx_end',
+ help='last data index' )
+parser.add_argument( '-d', action='store', required=False, type=int, dest='didx',
+ help='delta data index [%(default)d]', default=1 )
+parser.add_argument( '-i', action='store', required=False, type=str, dest='prefix',
+ help='data path prefix [%(default)s]', default='../' )
+
+args=parser.parse_args()
+
+# take note
+print( '\nCommand-line arguments:' )
+print( '-------------------------------------------------------------------' )
+print( ' '.join(map(str, sys.argv)) )
+print( '-------------------------------------------------------------------\n' )
+
+idx_start = args.idx_start
+idx_end = args.idx_end
+didx = args.didx
+prefix = args.prefix
+
+for i in range(idx_start, idx_end+1, didx):
+ ds = yt.load( prefix + "Data_%06d"%i )
+ ds.add_field( name=("gamer", "Pres_SR"), function=pressure_sr, sampling_type="local", units="dyne/cm**2" )
+ ds.add_field( name=("gamer", "P_CR"), function=pressure_cr, sampling_type="local", units="dyne/cm**2" )
+ ds.add_field( name=("gamer", "num_dens"), function=number_density, sampling_type="local", units="1/cm**3" )
+ ds.add_field( name=("gamer", "Temp_erg"), function=temperature_erg, sampling_type="local", units="erg" )
+
+ fig_width = 22.0 * CM2INCH
+ fig_height = 28.0 * CM2INCH
+ fig_dpi = 400
+
+ start = [7, 7, 0]
+ end = [7, 7, 14]
+ start_zoom = [7, 7, 9.833333]
+ end_zoom = [7, 7, 10.66666]
+
+ plot_vars = [ { "field":("gamer", "Pres_SR"), "cmap":"plasma", "log_scale":True, "var_unit":"erg/cm**3", "title":"P" , "var_min":None },
+ { "field":("gamer", "Temp_erg"), "cmap":"afmhot", "log_scale":True, "var_unit":"keV", "title":"k_BT" , "var_min":None },
+ { "field":("gamer", "num_dens"), "cmap":"gist_earth", "log_scale":True, "var_unit":"1/cm**3", "title":"n" , "var_min":None } ]
+ plot_savename = "Fermi_Bubble_Profile_%06d.png"%(i)
+ plot_axis_unit = "kpc"
+ plot_time_unit = "Myr"
+
+ N_vars = len( plot_vars )
+ fig, ax = plt.subplots( N_vars, 2, figsize=(fig_width, fig_height), sharex="col" )
+
+ for v, var in enumerate(plot_vars):
+ plot_log_scale = plot_vars[v]["log_scale"]
+ plot_var_unit = plot_vars[v]["var_unit"]
+ plot_title = plot_vars[v]["title"]
+ plot_arr, plot_r = get_line_data( ds, start, end, var )
+ plot_time = ds.current_time * ds.time_unit.to( plot_time_unit )
+
+ ax[v, 0].scatter( plot_r, plot_arr, s=5 )
+
+ ax[v, 0].tick_params( which="both", direction="in", top=True, right=True )
+
+ ax[v, 0].set( ylabel=r"$%s$ (%s)"%(plot_title, plot_var_unit) )
+ if plot_log_scale: ax[v, 0].set( yscale="log" )
+
+ # zoom-in
+ for v, var in enumerate(plot_vars):
+ plot_log_scale = plot_vars[v]["log_scale"]
+ plot_var_unit = plot_vars[v]["var_unit"]
+ plot_title = plot_vars[v]["title"]
+ plot_arr, plot_r = get_line_data( ds, start_zoom, end_zoom, var )
+ plot_time = ds.current_time * ds.time_unit.to( plot_time_unit )
+
+ ax[v, 1].scatter( plot_r, plot_arr, s=5 )
+
+ ax[v, 1].tick_params( which="both", direction="in", top=True, right=True, labelleft=False, labelright=True )
+
+ if plot_log_scale: ax[v, 1].set( yscale="log" )
+
+ ax[N_vars-1, 0].set( xlabel=r"$%s$ (%s)"%("r", plot_axis_unit) )
+ ax[N_vars-1, 1].set( xlabel=r"$%s$ (%s)"%("r", plot_axis_unit) )
+ plt.suptitle( "%.2f (%s)"%(plot_time, plot_time_unit) )
+ plt.tight_layout()
+ plt.savefig( plot_savename, dpi=fig_dpi )
+ plt.close()
diff --git a/example/test_problem/Hydro/FermiBubble/plot_scripts/plot_slice.py b/example/test_problem/Hydro/FermiBubble/plot_scripts/plot_slice.py
new file mode 100644
index 0000000000..a86a586b32
--- /dev/null
+++ b/example/test_problem/Hydro/FermiBubble/plot_scripts/plot_slice.py
@@ -0,0 +1,172 @@
+"""
+Please edit the section of `Parameters` and `Plot`.
+"""
+#====================================================================================================
+# Imports
+#====================================================================================================
+import yt
+import matplotlib as mpl
+import matplotlib.pylab as plt
+from matplotlib.colors import LogNorm
+import numpy as np
+import argparse
+import sys
+
+
+#====================================================================================================
+# Constants
+#====================================================================================================
+WIDTH_RES = 800
+PARTICLE_MASS = 1.6726231e-24 # proton mass in gram
+AX_LABEL = ["x", "y", "z"]
+AX_IDX = {"x":0, "y":1, "z":2}
+CM2INCH = 1/2.54
+
+
+
+#====================================================================================================
+# Derived fields
+#====================================================================================================
+def number_density( field, data ):
+ return data["Dens"] / PARTICLE_MASS / yt.YTQuantity(1.0, "g")
+
+def pressure_sr( field, data ):
+ from yt.units import speed_of_light_cgs, boltzmann_constant_cgs
+ unit_v = speed_of_light_cgs
+
+ pres = data["frame_density"] * data["Temp"] * yt.YTQuantity(1.0, "K")
+ pres /= PARTICLE_MASS * yt.YTQuantity(1.0, "g")
+ pres /= speed_of_light_cgs**2
+ pres *= unit_v**2
+ pres *= boltzmann_constant_cgs
+ return pres
+
+def pressure_cr( field, data ):
+ from yt.units import speed_of_light_cgs
+ unit_v = speed_of_light_cgs
+ unit_d = data.ds.mass_unit / data.ds.length_unit**3
+
+ NormalizedConst_CRPres = 7
+
+ p_cr = data["CRay"] / 3 / data["lorentz_factor"]
+ p_cr *= unit_d
+ p_cr *= unit_v**2
+ return p_cr * NormalizedConst_CRPres
+
+def temperature_erg( field, data ):
+ from yt.units import boltzmann_constant_cgs
+
+ temp = data["Temp"] * boltzmann_constant_cgs * yt.YTQuantity(1.0, "K")
+ return temp
+
+
+
+#====================================================================================================
+# Functions
+#====================================================================================================
+def get_slice_frb_data( ds, slice_axis, plot_var, center, width, height ):
+ field = plot_var["field"]
+ field_unit = plot_var["var_unit"]
+ field_min = plot_var["var_min"]
+
+ ax_0_idx = (AX_IDX[slice_axis]+0) % 3
+ ax_1_idx = (AX_IDX[slice_axis]+1) % 3
+ ax_2_idx = (AX_IDX[slice_axis]+2) % 3
+
+ sli = yt.SlicePlot( ds, slice_axis, field, center=center )
+ frb_arr = sli.data_source.to_frb( width, (WIDTH_RES, height/width*WIDTH_RES), height=height )
+ plot_arr = np.array( frb_arr[field].to( field_unit ) )
+ if field_min != None: plot_arr = np.clip( plot_arr, field_min, None ) # apply the minimum allowed value
+
+ plot_center = center - ds.domain_width/2
+ plot_extend = np.array( [plot_center[ax_1_idx] - plot_width /2,
+ plot_center[ax_1_idx] + plot_width /2,
+ plot_center[ax_2_idx] - plot_height/2,
+ plot_center[ax_2_idx] + plot_height/2] )
+ return plot_arr, plot_center, plot_extend
+
+
+
+#====================================================================================================
+# Main
+#====================================================================================================
+parser = argparse.ArgumentParser( description='Plot gas pressure, number density, temperature, and cosmic rays pressure slice for Fermi bubble' )
+
+parser.add_argument( '-s', action='store', required=True, type=int, dest='idx_start',
+ help='first data index' )
+parser.add_argument( '-e', action='store', required=True, type=int, dest='idx_end',
+ help='last data index' )
+parser.add_argument( '-d', action='store', required=False, type=int, dest='didx',
+ help='delta data index [%(default)d]', default=1 )
+parser.add_argument( '-i', action='store', required=False, type=str, dest='prefix',
+ help='data path prefix [%(default)s]', default='../' )
+
+args=parser.parse_args()
+
+# take note
+print( '\nCommand-line arguments:' )
+print( '-------------------------------------------------------------------' )
+print( ' '.join(map(str, sys.argv)) )
+print( '-------------------------------------------------------------------\n' )
+
+idx_start = args.idx_start
+idx_end = args.idx_end
+didx = args.didx
+prefix = args.prefix
+
+for i in range(idx_start, idx_end+1, didx):
+ ds = yt.load( prefix + "Data_%06d"%i )
+ ds.add_field( name=("gamer", "Pres_SR"), function=pressure_sr, sampling_type="local", units="dyne/cm**2" )
+ ds.add_field( name=("gamer", "P_CR"), function=pressure_cr, sampling_type="local", units="dyne/cm**2" )
+ ds.add_field( name=("gamer", "num_dens"), function=number_density, sampling_type="local", units="1/cm**3" )
+ ds.add_field( name=("gamer", "Temp_erg"), function=temperature_erg, sampling_type="local", units="erg" )
+
+ slice_axis = "x"
+ fig_width = 28.0 * CM2INCH
+ fig_height = 11.0 * CM2INCH
+ fig_dpi = 400
+
+ ax_0_idx = (AX_IDX[slice_axis]+0) % 3
+ ax_1_idx = (AX_IDX[slice_axis]+1) % 3
+ ax_2_idx = (AX_IDX[slice_axis]+2) % 3
+
+ plot_vars = [ { "field":("gamer", "Pres_SR"), "cmap":"plasma", "log_scale":True, "var_unit":"erg/cm**3", "title":"P" , "var_min":None },
+ { "field":("gamer", "Temp_erg"), "cmap":"afmhot", "log_scale":True, "var_unit":"keV", "title":"k_BT" , "var_min":None },
+ { "field":("gamer", "num_dens"), "cmap":"gist_earth", "log_scale":True, "var_unit":"1/cm**3", "title":"n" , "var_min":None },
+ { "field":("gamer", "P_CR"), "cmap":"nipy_spectral", "log_scale":True, "var_unit":"erg/cm**3", "title":"P_{CR}", "var_min":1.111e-18 } ]
+ plot_savename = "Fermi_Bubble_Slice_%06d.png"%(i)
+ plot_axis_unit = "kpc"
+ plot_time_unit = "Myr"
+ plot_width = ds.domain_width[ax_1_idx] # yt.YTQuantity(2, 'kpc')
+ plot_height = ds.domain_width[ax_2_idx] # yt.YTQuantity(4, 'kpc')
+
+ N_vars = len( plot_vars )
+ fig, ax = plt.subplots( 1, N_vars, figsize=(fig_width, fig_height), sharey=True )
+
+ for v, var in enumerate(plot_vars):
+ plot_cmap = plot_vars[v]["cmap"]
+ plot_log_scale = plot_vars[v]["log_scale"]
+ plot_var_unit = plot_vars[v]["var_unit"]
+ plot_title = plot_vars[v]["title"]
+
+ plot_arr, plot_center, plot_extend = get_slice_frb_data( ds, slice_axis, var, ds.domain_center, plot_width, plot_height )
+
+ norm_func = mpl.colors.LogNorm if plot_log_scale else mpl.colors.Normalize
+ plot_extend = plot_extend * ds.length_unit.to( plot_axis_unit )
+ plot_norm = norm_func( vmin=plot_arr.min(), vmax=plot_arr.max() )
+ plot_time = ds.current_time * ds.time_unit.to( plot_time_unit )
+
+ im1 = ax[v].imshow( plot_arr, extent=plot_extend, norm=plot_norm, cmap=mpl.colormaps[plot_cmap] )
+ cbar = fig.colorbar( im1, ax=ax[v], pad=0.0 )
+
+ ax[v].tick_params( which="both", direction="in", color="w" )
+ cbar.ax.tick_params( which="both", direction="in" )
+
+ ax[v].set( title=r"$%s$ (%s)"%(plot_title, plot_var_unit) )
+ ax[v].set( xlabel=r"$%s$ (%s)"%(AX_LABEL[ax_1_idx], plot_axis_unit) )
+
+ ax[0].set( ylabel=r"$%s$ (%s)"%(AX_LABEL[ax_2_idx], plot_axis_unit) )
+ plt.suptitle( r"%.2f (%s)"%(plot_time, plot_time_unit) )
+ plt.tight_layout()
+ plt.savefig( plot_savename, dpi=fig_dpi )
+ plt.close()
diff --git a/example/test_problem/Hydro/FermiBubble/plot_scripts/plot_spectrum.py b/example/test_problem/Hydro/FermiBubble/plot_scripts/plot_spectrum.py
new file mode 100644
index 0000000000..1ea3ad76f7
--- /dev/null
+++ b/example/test_problem/Hydro/FermiBubble/plot_scripts/plot_spectrum.py
@@ -0,0 +1,202 @@
+#====================================================================================================
+# Imports
+#====================================================================================================
+import matplotlib.pyplot as plt
+from matplotlib.colors import LogNorm
+import numpy as np
+import h5py
+import matplotlib.font_manager as font_manager
+import os.path
+
+
+
+#====================================================================================================
+# Constants
+#====================================================================================================
+Hz2GHz = 1e9
+CGS2JANSKY = 1e-23
+DeltaL = 1.687e21
+eV2GeV = 1e9
+CRIndex = 2.6
+CRIndices = [ 2.2, 2.4, 2.6 ]
+
+TickLabelSize = 35
+LabelSize = 35
+LegendSize = 24
+Pad = 10
+MajorTickLength = 16
+MinorTickLength = 8
+
+linestyle = [ "-", "--", "-.", ":" ]
+linecolor = [ "red", "orange", "green", "blue" ]
+
+font = font_manager.FontProperties( family='monospace', style='normal', size=LegendSize )
+
+path_gamma_ray = "gamma_ray_spectrum/"
+path_synchrotron = "synchrotron_spectrum/"
+
+
+
+#====================================================================================================
+# Functions
+#====================================================================================================
+def trans_b( b, inverse=False ):
+ if inverse:
+ # transform from [0, 360] to [+90, -90]
+ b = ( 180 - b ) * 0.5
+ else:
+ # transrorm from [+90, -90] to [0, 360]
+ b = 180 - 2*b
+ return b
+
+def trans_l( l, inverse=False ):
+ if inverse:
+ # transform from [0, 360] to [-180, +180]
+ l = l - 180
+ else:
+ # transform from [-180, +180] to [0, 360]
+ l = 180 + l
+ return l
+
+def CRIndex2ScaleCREngy( CRIndex ):
+ if CRIndex == 2.8: ScaleCREngy = 8000
+ elif CRIndex == 2.6: ScaleCREngy = 58
+ elif CRIndex == 2.4: ScaleCREngy = 7
+ elif CRIndex == 2.2: ScaleCREngy = 1.18
+ elif CRIndex == 2.0: ScaleCREngy = 1
+ else: raise ValueError("Please assign a ScaleCREngy !!")
+ return ScaleCREngy
+
+
+gammaray_B_max = np.array( [50, 30, -10, -30] )
+gammaray_B_min = np.array( [30, 10, -30, -50] )
+
+synchrotron_B_max = np.array( [+30, -20] )
+synchrotron_B_min = np.array( [+20, -30] )
+
+L_max = +10
+L_min = -10
+
+gammaray_B_max = trans_b( gammaray_B_max )
+gammaray_B_min = trans_b( gammaray_B_min )
+
+synchrotron_B_max = trans_b( synchrotron_B_max )
+synchrotron_B_min = trans_b( synchrotron_B_min )
+
+L_max = trans_l( L_max )
+L_min = trans_l( L_min )
+
+
+fig, ax = plt.subplots( 3, 2 )
+
+fig.set_size_inches( 28, 20 )
+fig.subplots_adjust( wspace=0.3, hspace=0.05 )
+
+Emin, Emid, Emax = np.loadtxt( "energyBinsTable", comments='#', usecols=(0,1,2), unpack=True )
+
+###################################
+########### Gamma-ray #############
+###################################
+EngyUpper, spectrumUpper = np.loadtxt( "observedEnergySpectrum-Upper", comments='#', usecols=(0,1), unpack=True, delimiter=',' )
+EngyLower, spectrumLower = np.loadtxt( "observedEnergySpectrum-Lower", comments='#', usecols=(0,1), unpack=True, delimiter=',' )
+
+Engy = np.concatenate( (EngyLower[::-1], EngyUpper ) )
+ObservedSpectrum = np.concatenate( (spectrumLower[::-1], spectrumUpper) )
+
+for i in range(len(CRIndices)):
+ spectrumMultipleB = []
+ EmidMultiple = []
+
+ for b_min, b_max in zip(gammaray_B_max, gammaray_B_min):
+ spectrumOneB = np.array([])
+ EmidOneB = np.array([])
+
+ for binIdx in range(len(Emid)):
+ binWidth = Emax[binIdx] - Emin[binIdx]
+ filename = path_gamma_ray + "Projected_FRB_Data_000035_%9.4e_1.1e6_"%Emid[binIdx] + str(CRIndices[i]) + ".h5"
+ if not os.path.isfile( filename ): continue
+ fs = h5py.File( filename, "r" )
+ group = fs.get('Map')
+ gmap = np.array( group.get('ProjectedLeptonicGammaRay') )
+ gmap = gmap[0,:,:]
+ gmap = np.roll( gmap, int(gmap.shape[1]/2), axis=1 )
+ spectrumOneB = np.append( spectrumOneB, CRIndex2ScaleCREngy(CRIndices[i]) * DeltaL * np.average( gmap[b_min:b_max,L_min:L_max] * binWidth / eV2GeV ) )
+ EmidOneB = np.append( EmidOneB, Emid[binIdx] )
+
+ spectrumMultipleB.append( spectrumOneB )
+ EmidMultiple.append( EmidOneB )
+
+ for b_idx in range(len(gammaray_B_max)):
+ label = r"$%+d^{\circ} "$temp_file"
+
+for CRindex in "${CRindexs[@]}"
+do
+ while IFS=$' \t' read -ra line; do
+ Fmin=${line[0]}
+ ObservedFreqHz=$(printf "%9.4e" ${line[1]})
+ Fmax=${line[2]}
+
+ FileName=Projected_FRB_Data_000035_Synchrotron_${ObservedFreqHz}_1.1e6_${CRindex}.h5
+
+ if [ -f "$FileName" ]
+ then
+ echo "${FileName} exist !!"
+ else
+ echo "Running with (ObservedFreqHz=${ObservedFreqHz}, CRindex=${CRindex})"
+ ./Project FRB_Data_000035.h5 $ObservedFreqHz 1.1e6 $CRindex >& log-$CRindex-$ObservedFreqHz
+ fi
+ done < "${temp_file}"
+done
+
+rm "${temp_file}"
diff --git a/example/test_problem/Template/Input__Parameter b/example/test_problem/Template/Input__Parameter
index 5f245d1b6f..01a83b5f96 100644
--- a/example/test_problem/Template/Input__Parameter
+++ b/example/test_problem/Template/Input__Parameter
@@ -47,6 +47,7 @@ TESTPROB_ID 0 # test problem ID [0]
# 20: HYDRO MHD Cosmic Ray Soundwave
# 21: HYDRO MHD Cosmic Ray Shocktube
# 23: HYDRO MHD Cosmic Ray Diffusion
+ # 24: HYDRO SRHD Fermi Bubble
# 100: HYDRO CDM cosmological simulation (+GRAVITY & COMOVING & PARTICLE)
# 101: HYDRO Zeldovich pancake collapse (+GRAVITY & COMOVING & PARTICLE)
# 1000: ELBDM external potential (+GRAVITY)
diff --git a/include/CUPOT.h b/include/CUPOT.h
index 8843f5a058..e49fae4dab 100644
--- a/include/CUPOT.h
+++ b/include/CUPOT.h
@@ -16,6 +16,7 @@
#ifdef __CUDACC__
# include "Macro.h"
# include "Typedef.h"
+# include "EoS.h"
#else
# include "GAMER.h"
#endif
diff --git a/include/EoS.h b/include/EoS.h
index 2c0338dd7b..de29bcd019 100644
--- a/include/EoS.h
+++ b/include/EoS.h
@@ -43,10 +43,8 @@ struct EoS_t
EoS_DE2T_t DensEint2Temp_FuncPtr;
EoS_DT2P_t DensTemp2Pres_FuncPtr;
EoS_DE2S_t DensEint2Entr_FuncPtr;
- EoS_GENE_t General_FuncPtr;
-# ifdef COSMIC_RAY
EoS_CRE2CRP_t CREint2CRPres_FuncPtr;
-# endif
+ EoS_GENE_t General_FuncPtr;
// table pointers
real **Table;
diff --git a/include/Global.h b/include/Global.h
index bc08cdaa9e..11e438f72d 100644
--- a/include/Global.h
+++ b/include/Global.h
@@ -305,10 +305,8 @@ extern EoS_DP2C_t EoS_DensPres2CSqr_CPUPtr;
extern EoS_DE2T_t EoS_DensEint2Temp_CPUPtr;
extern EoS_DT2P_t EoS_DensTemp2Pres_CPUPtr;
extern EoS_DE2S_t EoS_DensEint2Entr_CPUPtr;
-extern EoS_GENE_t EoS_General_CPUPtr;
-#ifdef COSMIC_RAY
extern EoS_CRE2CRP_t EoS_CREint2CRPres_CPUPtr;
-#endif
+extern EoS_GENE_t EoS_General_CPUPtr;
#ifdef GPU
extern EoS_GUESS_t EoS_GuessHTilde_GPUPtr;
extern EoS_H2TEM_t EoS_HTilde2Temp_GPUPtr;
@@ -319,10 +317,8 @@ extern EoS_DP2C_t EoS_DensPres2CSqr_GPUPtr;
extern EoS_DE2T_t EoS_DensEint2Temp_GPUPtr;
extern EoS_DT2P_t EoS_DensTemp2Pres_GPUPtr;
extern EoS_DE2S_t EoS_DensEint2Entr_GPUPtr;
-extern EoS_GENE_t EoS_General_GPUPtr;
-#ifdef COSMIC_RAY
extern EoS_CRE2CRP_t EoS_CREint2CRPres_GPUPtr;
-#endif
+extern EoS_GENE_t EoS_General_GPUPtr;
#endif
extern EoS_t EoS;
#endif // HYDRO
diff --git a/include/Typedef.h b/include/Typedef.h
index c222b20ef2..824519759d 100644
--- a/include/Typedef.h
+++ b/include/Typedef.h
@@ -93,6 +93,7 @@ const TestProbID_t
TESTPROB_HYDRO_CR_SOUNDWAVE = 20,
TESTPROB_HYDRO_CR_SHOCKTUBE = 21,
TESTPROB_HYDRO_CR_DIFFUSION = 23,
+ TESTPROB_HYDRO_FERMI_BUBBLE = 24,
TESTPROB_HYDRO_BARRED_POT = 51,
TESTPROB_HYDRO_JET_ICM_WALL = 52,
TESTPROB_HYDRO_CDM_LSS = 100,
@@ -580,14 +581,12 @@ typedef real (*EoS_DT2P_t) ( const real Dens, const real Temp, const real Pa
typedef real (*EoS_DE2S_t) ( const real Dens, const real Eint, const real Passive[],
const double AuxArray_Flt[], const int AuxArray_Int[],
const real *const Table[EOS_NTABLE_MAX] );
-typedef void (*EoS_GENE_t) ( const int Mode, real Out[], const real In_Flt[], const int In_Int[],
+typedef real (*EoS_CRE2CRP_t) ( const real E_CR,
const double AuxArray_Flt[], const int AuxArray_Int[],
const real *const Table[EOS_NTABLE_MAX] );
-#ifdef COSMIC_RAY
-typedef real (*EoS_CRE2CRP_t) ( const real E_CR,
+typedef void (*EoS_GENE_t) ( const int Mode, real Out[], const real In_Flt[], const int In_Int[],
const double AuxArray_Flt[], const int AuxArray_Int[],
const real *const Table[EOS_NTABLE_MAX] );
-#endif
typedef void (*ExtAcc_t) ( real Acc[], const double x, const double y, const double z, const double Time,
const double UserArray[] );
typedef real (*ExtPot_t) ( const double x, const double y, const double z, const double Time,
diff --git a/src/Auxiliary/Aux_Check_Parameter.cpp b/src/Auxiliary/Aux_Check_Parameter.cpp
index 1c41984f59..0757789eb9 100644
--- a/src/Auxiliary/Aux_Check_Parameter.cpp
+++ b/src/Auxiliary/Aux_Check_Parameter.cpp
@@ -748,10 +748,6 @@ void Aux_Check_Parameter()
# error : ERROR : SRHD does not support MHD !!
# endif
-# ifdef GRAVITY
-# error : ERROR : SRHD does not support GRAVITY !!
-# endif
-
# ifdef COMOVING
# error : ERROR : SRHD does not support COMOVING !!
# endif
@@ -764,10 +760,6 @@ void Aux_Check_Parameter()
# error : ERROR : EOS != EOS_TAUBMATHEWS for SRHD !!
# endif
-# ifdef COSMIC_RAY
-# error : ERROR : SRHD does not support COSMIC_RAY !!
-# endif
-
# ifdef DUAL_ENERGY
# error : ERROR : SRHD does not support DUAL_ENERGY !!
# endif
@@ -781,16 +773,19 @@ void Aux_Check_Parameter()
# endif
if ( OPT__OUTPUT_ENTR )
- Aux_Error( ERROR_INFO, "SRHD does not support OPT__OUTPUT_ENTR !!\n" );
+ Aux_Error( ERROR_INFO, "SRHD does not support OPT__OUTPUT_ENTR !!\n" );
if ( OPT__FLAG_LOHNER_ENTR )
- Aux_Error( ERROR_INFO, "SRHD does not support OPT__FLAG_LOHNER_ENTR !!\n" );
+ Aux_Error( ERROR_INFO, "SRHD does not support OPT__FLAG_LOHNER_ENTR !!\n" );
if ( JEANS_MIN_PRES )
- Aux_Error( ERROR_INFO, "SRHD does not support JEANS_MIN_PRES !!\n" );
+ Aux_Error( ERROR_INFO, "SRHD does not support JEANS_MIN_PRES !!\n" );
if ( OPT__FLAG_JEANS )
- Aux_Error( ERROR_INFO, "SRHD does not support OPT__FLAG_JEANS !!\n" );
+ Aux_Error( ERROR_INFO, "SRHD does not support OPT__FLAG_JEANS !!\n" );
+
+ if ( OPT__1ST_FLUX_CORR != FIRST_FLUX_CORR_NONE && ( OPT__1ST_FLUX_CORR_SCHEME != RSOLVER_1ST_HLLC || OPT__1ST_FLUX_CORR_SCHEME != RSOLVER_1ST_HLLE ) )
+ Aux_Error( ERROR_INFO, "SRHD only supports OPT__1ST_FLUX_CORR_SCHEME == RSOLVER_1ST_HLLC/HLLE !!\n" );
# endif // #ifdef SRHD
# ifdef MHD
@@ -809,10 +804,6 @@ void Aux_Check_Parameter()
# error : ERROR : COSMIC_RAY currently only supports the MHM_RP fluid scheme !!
# endif
-# if ( EOS != EOS_COSMIC_RAY )
-# error : ERROR : COSMIC_RAY must use EOS_COSMIC_RAY !!
-# endif
-
# ifdef DUAL_ENERGY
# error : ERROR : DUAL_ENERGY is not supported for COSMIC_RAY !!
# endif
diff --git a/src/EoS/EoS_Init.cpp b/src/EoS/EoS_Init.cpp
index ed2cb47f2e..2e0a0a3a52 100644
--- a/src/EoS/EoS_Init.cpp
+++ b/src/EoS/EoS_Init.cpp
@@ -95,10 +95,8 @@ void EoS_Init()
EoS.GuessHTilde_FuncPtr = EoS_GuessHTilde_GPUPtr;
EoS.HTilde2Temp_FuncPtr = EoS_HTilde2Temp_GPUPtr;
EoS.Temp2HTilde_FuncPtr = EoS_Temp2HTilde_GPUPtr;
- EoS.General_FuncPtr = EoS_General_GPUPtr;
-# ifdef COSMIC_RAY
EoS.CREint2CRPres_FuncPtr = EoS_CREint2CRPres_GPUPtr;
-# endif
+ EoS.General_FuncPtr = EoS_General_GPUPtr;
CUAPI_SetConstMemory_EoS();
@@ -113,10 +111,8 @@ void EoS_Init()
EoS.GuessHTilde_FuncPtr = EoS_GuessHTilde_CPUPtr;
EoS.HTilde2Temp_FuncPtr = EoS_HTilde2Temp_CPUPtr;
EoS.Temp2HTilde_FuncPtr = EoS_Temp2HTilde_CPUPtr;
- EoS.General_FuncPtr = EoS_General_CPUPtr;
-# ifdef COSMIC_RAY
EoS.CREint2CRPres_FuncPtr = EoS_CREint2CRPres_CPUPtr;
-# endif
+ EoS.General_FuncPtr = EoS_General_CPUPtr;
EoS.AuxArrayDevPtr_Flt = EoS_AuxArray_Flt;
EoS.AuxArrayDevPtr_Int = EoS_AuxArray_Int;
diff --git a/src/EoS/TaubMathews/CPU_EoS_TaubMathews.cpp b/src/EoS/TaubMathews/CPU_EoS_TaubMathews.cpp
index dce66c0272..142b955e1a 100644
--- a/src/EoS/TaubMathews/CPU_EoS_TaubMathews.cpp
+++ b/src/EoS/TaubMathews/CPU_EoS_TaubMathews.cpp
@@ -8,6 +8,20 @@
+#ifdef COSMIC_RAY
+#ifdef __CUDACC__
+__device__ static real EoS_CREint2CRPres_TaubMathews( const real E_CR,
+ const double AuxArray_Flt[], const int AuxArray_Int[],
+ const real *const Table[EOS_NTABLE_MAX] );
+#else // #ifdef __CUDACC__
+static real EoS_CREint2CRPres_TaubMathews( const real E_CR,
+ const double AuxArray_Flt[], const int AuxArray_Int[],
+ const real *const Table[EOS_NTABLE_MAX] );
+#endif // #ifdef __CUDACC__ ... else ...
+#endif // #ifdef COSMIC_RAY
+
+
+
/********************************************************
1. Approximately relativistic ideal gas EoS (Taub-Mathews EoS)
@@ -56,6 +70,10 @@ void EoS_SetAuxArray_TaubMathews( double AuxArray_Flt[], int AuxArray_Int[] )
AuxArray_Flt[0] = ( OPT__UNIT ) ? MOLECULAR_WEIGHT * MU_NORM / Const_kB * (UNIT_E/UNIT_M)
: MOLECULAR_WEIGHT;
AuxArray_Flt[1] = 1.0 / AuxArray_Flt[0];
+# ifdef COSMIC_RAY
+ AuxArray_Flt[2] = GAMMA_CR;
+ AuxArray_Flt[3] = GAMMA_CR - 1.0;
+# endif
} // FUNCTION : EoS_SetAuxArray_TaubMathews
#endif // #ifndef __CUDACC__
@@ -249,6 +267,55 @@ static real EoS_DensPres2CSqr_TaubMathews( const real Dens, const real Pres, con
+#ifdef COSMIC_RAY
+//-------------------------------------------------------------------------------------------------------
+// Function : EoS_CREint2CRPres_TaubMathews
+// Description : Convert cosmic-ray energy density to cosmic-ray pressure
+//
+// Note : 1. Internal energy density here is per unit volume instead of per unit mass
+// 2. See EoS_SetAuxArray_GammaCR() for the values stored in AuxArray_Flt/Int[]
+//
+// Parameter : E_CR : Cosmic-ray energy density
+// AuxArray_* : Auxiliary arrays (see the Note above)
+//
+// Return : Cosmic ray pressure
+//-------------------------------------------------------------------------------------------------------
+GPU_DEVICE_NOINLINE
+static real EoS_CREint2CRPres_TaubMathews( const real E_CR,
+ const double AuxArray_Flt[], const int AuxArray_Int[],
+ const real *const Table[EOS_NTABLE_MAX] )
+{
+
+// check
+# ifdef GAMER_DEBUG
+ if ( E_CR < (real)0.0 )
+ printf( "ERROR : invalid input cosmic-ray energy density (%13.7e) in %s() !!\n", E_CR, __FUNCTION__ );
+# endif // GAMER_DEBUG
+
+
+ const real GammaCR_m1 = (real)AuxArray_Flt[3];
+ real Pres_CR;
+
+ Pres_CR = GammaCR_m1*E_CR;
+
+
+// check
+# ifdef GAMER_DEBUG
+ if ( Pres_CR < (real)0.0 )
+ {
+ printf( "ERROR : invalid output cosmic-ray pressure (%13.7e) in %s() !!\n", Pres_CR, __FUNCTION__ );
+ printf( " CRay=%13.7e\n", E_CR );
+ }
+# endif // GAMER_DEBUG
+
+
+ return Pres_CR;
+
+} // FUNCTION : EoS_CREint2CRPres_TaubMathews
+#endif // #ifdef COSMIC_RAY
+
+
+
// =============================================
// III. Set EoS initialization functions
// =============================================
@@ -259,10 +326,15 @@ static real EoS_DensPres2CSqr_TaubMathews( const real Dens, const real Pres, con
# define FUNC_SPACE static
#endif
-FUNC_SPACE EoS_GUESS_t EoS_GuessHTilde_Ptr = EoS_GuessHTilde_TaubMathews;
-FUNC_SPACE EoS_H2TEM_t EoS_HTilde2Temp_Ptr = EoS_HTilde2Temp_TaubMathews;
-FUNC_SPACE EoS_TEM2H_t EoS_Temp2HTilde_Ptr = EoS_Temp2HTilde_TaubMathews;
-FUNC_SPACE EoS_DP2C_t EoS_DensPres2CSqr_Ptr = EoS_DensPres2CSqr_TaubMathews;
+FUNC_SPACE EoS_GUESS_t EoS_GuessHTilde_Ptr = EoS_GuessHTilde_TaubMathews;
+FUNC_SPACE EoS_H2TEM_t EoS_HTilde2Temp_Ptr = EoS_HTilde2Temp_TaubMathews;
+FUNC_SPACE EoS_TEM2H_t EoS_Temp2HTilde_Ptr = EoS_Temp2HTilde_TaubMathews;
+FUNC_SPACE EoS_DP2C_t EoS_DensPres2CSqr_Ptr = EoS_DensPres2CSqr_TaubMathews;
+#ifdef COSMIC_RAY
+FUNC_SPACE EoS_CRE2CRP_t EoS_CREint2CRPres_Ptr = EoS_CREint2CRPres_TaubMathews;
+#else
+FUNC_SPACE EoS_CRE2CRP_t EoS_CREint2CRPres_Ptr = NULL;
+#endif
//-----------------------------------------------------------------------------------------
// Function : EoS_SetCPU/GPUFunc_TaubMathews
@@ -288,12 +360,14 @@ __host__
void EoS_SetGPUFunc_TaubMathews( EoS_GUESS_t &EoS_GuessHTilde_GPUPtr,
EoS_H2TEM_t &EoS_HTilde2Temp_GPUPtr,
EoS_TEM2H_t &EoS_Temp2HTilde_GPUPtr,
- EoS_DP2C_t &EoS_DensPres2CSqr_GPUPtr )
+ EoS_DP2C_t &EoS_DensPres2CSqr_GPUPtr,
+ EoS_CRE2CRP_t &EoS_CREint2CRPres_GPUPtr )
{
- CUDA_CHECK_ERROR( cudaMemcpyFromSymbol( &EoS_GuessHTilde_GPUPtr, EoS_GuessHTilde_Ptr, sizeof(EoS_GUESS_t) ) );
- CUDA_CHECK_ERROR( cudaMemcpyFromSymbol( &EoS_HTilde2Temp_GPUPtr, EoS_HTilde2Temp_Ptr, sizeof(EoS_H2TEM_t) ) );
- CUDA_CHECK_ERROR( cudaMemcpyFromSymbol( &EoS_Temp2HTilde_GPUPtr, EoS_Temp2HTilde_Ptr, sizeof(EoS_TEM2H_t) ) );
- CUDA_CHECK_ERROR( cudaMemcpyFromSymbol( &EoS_DensPres2CSqr_GPUPtr, EoS_DensPres2CSqr_Ptr, sizeof(EoS_DP2C_t ) ) );
+ CUDA_CHECK_ERROR( cudaMemcpyFromSymbol( &EoS_GuessHTilde_GPUPtr, EoS_GuessHTilde_Ptr, sizeof(EoS_GUESS_t ) ) );
+ CUDA_CHECK_ERROR( cudaMemcpyFromSymbol( &EoS_HTilde2Temp_GPUPtr, EoS_HTilde2Temp_Ptr, sizeof(EoS_H2TEM_t ) ) );
+ CUDA_CHECK_ERROR( cudaMemcpyFromSymbol( &EoS_Temp2HTilde_GPUPtr, EoS_Temp2HTilde_Ptr, sizeof(EoS_TEM2H_t ) ) );
+ CUDA_CHECK_ERROR( cudaMemcpyFromSymbol( &EoS_DensPres2CSqr_GPUPtr, EoS_DensPres2CSqr_Ptr, sizeof(EoS_DP2C_t ) ) );
+ CUDA_CHECK_ERROR( cudaMemcpyFromSymbol( &EoS_CREint2CRPres_GPUPtr, EoS_CREint2CRPres_Ptr, sizeof(EoS_CRE2CRP_t) ) );
}
#else // #ifdef __CUDACC__
@@ -301,12 +375,14 @@ void EoS_SetGPUFunc_TaubMathews( EoS_GUESS_t &EoS_GuessHTilde_GPUPtr,
void EoS_SetCPUFunc_TaubMathews( EoS_GUESS_t &EoS_GuessHTilde_CPUPtr,
EoS_H2TEM_t &EoS_HTilde2Temp_CPUPtr,
EoS_TEM2H_t &EoS_Temp2HTilde_CPUPtr,
- EoS_DP2C_t &EoS_DensPres2CSqr_CPUPtr )
+ EoS_DP2C_t &EoS_DensPres2CSqr_CPUPtr,
+ EoS_CRE2CRP_t &EoS_CREint2CRPres_CPUPtr )
{
EoS_GuessHTilde_CPUPtr = EoS_GuessHTilde_Ptr;
EoS_HTilde2Temp_CPUPtr = EoS_HTilde2Temp_Ptr;
EoS_Temp2HTilde_CPUPtr = EoS_Temp2HTilde_Ptr;
EoS_DensPres2CSqr_CPUPtr = EoS_DensPres2CSqr_Ptr;
+ EoS_CREint2CRPres_CPUPtr = EoS_CREint2CRPres_Ptr;
}
#endif // #ifdef __CUDACC__ ... else ...
@@ -317,9 +393,9 @@ void EoS_SetCPUFunc_TaubMathews( EoS_GUESS_t &EoS_GuessHTilde_CPUPtr,
// local function prototypes
void EoS_SetAuxArray_TaubMathews( double [] );
-void EoS_SetCPUFunc_TaubMathews(EoS_GUESS_t &, EoS_H2TEM_t &, EoS_TEM2H_t &, EoS_DP2C_t & );
+void EoS_SetCPUFunc_TaubMathews( EoS_GUESS_t &, EoS_H2TEM_t &, EoS_TEM2H_t &, EoS_DP2C_t &, EoS_CRE2CRP_t & );
#ifdef GPU
-void EoS_SetGPUFunc_TaubMathews(EoS_GUESS_t &, EoS_H2TEM_t &, EoS_TEM2H_t &, EoS_DP2C_t & );
+void EoS_SetGPUFunc_TaubMathews( EoS_GUESS_t &, EoS_H2TEM_t &, EoS_TEM2H_t &, EoS_DP2C_t &, EoS_CRE2CRP_t & );
#endif
//-----------------------------------------------------------------------------------------
@@ -341,9 +417,11 @@ void EoS_Init_TaubMathews()
{
EoS_SetAuxArray_TaubMathews( EoS_AuxArray_Flt, EoS_AuxArray_Int );
- EoS_SetCPUFunc_TaubMathews( EoS_GuessHTilde_CPUPtr, EoS_HTilde2Temp_CPUPtr, EoS_Temp2HTilde_CPUPtr, EoS_DensPres2CSqr_CPUPtr );
+ EoS_SetCPUFunc_TaubMathews( EoS_GuessHTilde_CPUPtr, EoS_HTilde2Temp_CPUPtr, EoS_Temp2HTilde_CPUPtr,
+ EoS_DensPres2CSqr_CPUPtr, EoS_CREint2CRPres_CPUPtr );
# ifdef GPU
- EoS_SetGPUFunc_TaubMathews( EoS_GuessHTilde_GPUPtr, EoS_HTilde2Temp_GPUPtr, EoS_Temp2HTilde_GPUPtr, EoS_DensPres2CSqr_GPUPtr );
+ EoS_SetGPUFunc_TaubMathews( EoS_GuessHTilde_GPUPtr, EoS_HTilde2Temp_GPUPtr, EoS_Temp2HTilde_GPUPtr,
+ EoS_DensPres2CSqr_GPUPtr, EoS_CREint2CRPres_GPUPtr );
# endif
} // FUNCTION : EoS_Init_TaubMathews
diff --git a/src/Fluid/Flu_Close.cpp b/src/Fluid/Flu_Close.cpp
index 89a375d852..285dc0866e 100644
--- a/src/Fluid/Flu_Close.cpp
+++ b/src/Fluid/Flu_Close.cpp
@@ -15,7 +15,6 @@ static void CorrectFlux( const int SonLv, const real Flux_Array[][9][NFLUX_TOTAL
const int NPG, const int *PID0_List, const real dt );
#if ( MODEL == HYDRO )
static bool Unphysical( const real Fluid[], const int CheckMode, const real Emag );
-#ifndef SRHD
static void CorrectUnphysical( const int lv, const int NPG, const int *PID0_List,
const real h_Flu_Array_F_In[][FLU_NIN][ CUBE(FLU_NXT) ],
real h_Flu_Array_F_Out[][FLU_NOUT][ CUBE(PS2) ],
@@ -24,7 +23,6 @@ static void CorrectUnphysical( const int lv, const int NPG, const int *PID0_List
const real h_Mag_Array_F_In[][NCOMP_MAG][ FLU_NXT_P1*SQR(FLU_NXT) ],
const real h_Mag_Array_F_Out[][NCOMP_MAG][ PS2P1*SQR(PS2) ],
const real dt );
-#endif
#ifdef MHD
void StoreElectric( const int lv, const real h_Ele_Array[][9][NCOMP_ELE][ PS2P1*PS2 ],
const int NPG, const int *PID0_List, const real dt );
@@ -32,10 +30,12 @@ void CorrectElectric( const int SonLv, const real h_Ele_Array[][9][NCOMP_ELE][ P
const int NPG, const int *PID0_List, const real dt );
void ResetLongB( real L[], real R[], const real FC_B, const int d );
#endif
+#ifndef SRHD
extern void Hydro_RiemannSolver_Roe ( const int XYZ, real Flux_Out[], const real L_In[], const real R_In[],
const real MinDens, const real MinPres, const EoS_DE2P_t EoS_DensEint2Pres,
const EoS_DP2C_t EoS_DensPres2CSqr, const double EoS_AuxArray_Flt[],
const int EoS_AuxArray_Int[], const real* const EoS_Table[EOS_NTABLE_MAX] );
+#endif
extern void Hydro_RiemannSolver_HLLC( const int XYZ, real Flux_Out[], const real L_In[], const real R_In[],
const real MinDens, const real MinPres, const EoS_DE2P_t EoS_DensEint2Pres,
const EoS_DP2C_t EoS_DensPres2CSqr, const EoS_GUESS_t EoS_GuessHTilde,
@@ -94,7 +94,7 @@ void Flu_Close( const int lv, const int SaveSg_Flu, const int SaveSg_Mag,
// try to correct the unphysical results in h_Flu_Array_F_Out (e.g., negative density)
// --> must be done BEFORE invoking both StoreFlux() and CorrectFlux() since CorrectUnphysical() might modify the flux array
-# if ( MODEL == HYDRO && !defined SRHD )
+# if ( MODEL == HYDRO )
CorrectUnphysical( lv, NPG, PID0_List, h_Flu_Array_F_In, h_Flu_Array_F_Out, h_DE_Array_F_Out, h_Flux_Array,
h_Mag_Array_F_In, h_Mag_Array_F_Out, dt );
# endif
@@ -433,6 +433,18 @@ bool Unphysical( const real Fluid[], const int CheckMode, const real Emag )
return true;
# endif
+# ifdef SRHD
+ const real Msqr = SQR(Fluid[MOMX]) + SQR(Fluid[MOMY]) + SQR(Fluid[MOMZ]);
+ const real Dsqr = SQR(Fluid[DENS]);
+ const real E_D = Fluid[ENGY] / Fluid[DENS];
+ const real M_D = SQRT( Msqr / Dsqr );
+ const real Temp = SQRT( E_D*E_D + (real)2.0*E_D );
+ const real Discriminant = ( Temp + M_D )*( Temp - M_D ); // replace a^2-b^2 with (a+b)*(a-b) to alleviate a catastrophic cancellation
+
+ if ( Discriminant <= (real)0.0 || !Aux_IsFinite(Discriminant) )
+ return true;
+# endif
+
# ifndef BAROTROPIC_EOS
if ( CheckMode == CheckMinEtot && ( Fluid[ENGY] < (real)MIN_EINT || Fluid[ENGY] != Fluid[ENGY] ) )
return true;
@@ -484,7 +496,6 @@ bool Unphysical( const real Fluid[], const int CheckMode, const real Emag )
-#ifndef SRHD
//-------------------------------------------------------------------------------------------------------
// Function : CorrectUnphysical
// Description : Check if any cell in the output array of Fluid solver contains unphysical results
@@ -673,6 +684,7 @@ void CorrectUnphysical( const int lv, const int NPG, const int *PID0_List,
switch ( OPT__1ST_FLUX_CORR_SCHEME )
{
+# ifndef SRHD
case RSOLVER_1ST_ROE:
# ifdef MHD
ResetLongB( VarL[d], VarC, FC_B[0], d ); // reset the longitudinal B field
@@ -694,6 +706,7 @@ void CorrectUnphysical( const int lv, const int NPG, const int *PID0_List,
VarC[ ENGY ] = CC_Engy;
# endif
break;
+# endif
# ifndef MHD
case RSOLVER_1ST_HLLC:
@@ -822,6 +835,7 @@ void CorrectUnphysical( const int lv, const int NPG, const int *PID0_List,
// (note that the recalculated flux does NOT include gravity even for UNSPLIT_GRAVITY --> reduce to 1st-order accuracy)
switch ( OPT__1ST_FLUX_CORR_SCHEME )
{
+# ifndef SRHD
case RSOLVER_1ST_ROE:
Hydro_RiemannSolver_Roe ( d, FluxL_1D, Corr1D_InOut_PtrL, Corr1D_InOut_PtrC, MIN_DENS, MIN_PRES,
EoS_DensEint2Pres_CPUPtr, EoS_DensPres2CSqr_CPUPtr,
@@ -830,6 +844,7 @@ void CorrectUnphysical( const int lv, const int NPG, const int *PID0_List,
EoS_DensEint2Pres_CPUPtr, EoS_DensPres2CSqr_CPUPtr,
EoS_AuxArray_Flt, EoS_AuxArray_Int, h_EoS_Table );
break;
+# endif
# ifndef MHD
case RSOLVER_1ST_HLLC:
@@ -944,10 +959,12 @@ void CorrectUnphysical( const int lv, const int NPG, const int *PID0_List,
// --> apply it only when AutoReduceDt_Continue is false
// --> otherwise AUTO_REDUCE_DT may not be triggered due to this internal energy floor
# else
+# ifndef SRHD
if ( ! AutoReduceDt_Continue && OPT__LAST_RESORT_FLOOR )
Update[ENGY] = Hydro_CheckMinEintInEngy( Update[DENS], Update[MOMX], Update[MOMY], Update[MOMZ], Update[ENGY],
MIN_EINT, Emag_Out );
# endif
+# endif
// check if the newly updated values are still unphysical
@@ -1265,7 +1282,6 @@ void CorrectUnphysical( const int lv, const int NPG, const int *PID0_List,
}
} // FUNCTION : CorrectUnphysical
-#endif // #ifndef SRHD
diff --git a/src/Fluid/Flu_FixUp_Flux.cpp b/src/Fluid/Flu_FixUp_Flux.cpp
index 4f80a7d5ce..aab1e5910d 100644
--- a/src/Fluid/Flu_FixUp_Flux.cpp
+++ b/src/Fluid/Flu_FixUp_Flux.cpp
@@ -245,7 +245,7 @@ void Flu_FixUp_Flux( const int lv, const long TVar )
if ( Hydro_IsUnphysical( UNPHY_MODE_CONS, CorrVal, NULL, NULL_REAL, NULL_REAL, NULL_REAL,
EoS_DensEint2Pres_CPUPtr, EoS_GuessHTilde_CPUPtr, EoS_HTilde2Temp_CPUPtr,
EoS_AuxArray_Flt, EoS_AuxArray_Int, h_EoS_Table,
- ERROR_INFO, UNPHY_VERBOSE ) )
+ ERROR_INFO, UNPHY_SILENCE ) )
# else
if ( CorrVal[DENS] <= MIN_DENS
# ifndef BAROTROPIC_EOS
diff --git a/src/Fluid/Flu_FixUp_Restrict.cpp b/src/Fluid/Flu_FixUp_Restrict.cpp
index 1966ed459c..1b7a61fbfd 100644
--- a/src/Fluid/Flu_FixUp_Restrict.cpp
+++ b/src/Fluid/Flu_FixUp_Restrict.cpp
@@ -432,6 +432,7 @@ void Flu_FixUp_Restrict( const int FaLv, const int SonFluSg, const int FaFluSg,
# else // #ifdef DUAL_ENERGY
// actually it might not be necessary to check the minimum internal energy here
+# ifndef SRHD
amr->patch[FaFluSg][FaLv][FaPID]->fluid[ENGY][k][j][i]
= Hydro_CheckMinEintInEngy( amr->patch[FaFluSg][FaLv][FaPID]->fluid[DENS][k][j][i],
amr->patch[FaFluSg][FaLv][FaPID]->fluid[MOMX][k][j][i],
@@ -439,6 +440,7 @@ void Flu_FixUp_Restrict( const int FaLv, const int SonFluSg, const int FaFluSg,
amr->patch[FaFluSg][FaLv][FaPID]->fluid[MOMZ][k][j][i],
amr->patch[FaFluSg][FaLv][FaPID]->fluid[ENGY][k][j][i],
MIN_EINT, Emag );
+# endif
# endif // #ifdef DUAL_ENERGY ... else ...
} // i,j,k
# endif // #if ( MODEL == HYDRO )
diff --git a/src/GPU_API/CUAPI_Asyn_PoissonGravitySolver.cu b/src/GPU_API/CUAPI_Asyn_PoissonGravitySolver.cu
index 138718bd04..9570187133 100644
--- a/src/GPU_API/CUAPI_Asyn_PoissonGravitySolver.cu
+++ b/src/GPU_API/CUAPI_Asyn_PoissonGravitySolver.cu
@@ -44,7 +44,7 @@ void CUPOT_HydroGravitySolver(
const real g_Emag_Array [][ CUBE(PS1) ],
const real dt, const real dh, const bool P5_Gradient,
const bool UsePot, const OptExtAcc_t ExtAcc, const ExtAcc_t ExtAcc_Func,
- const double TimeNew, const double TimeOld, const real MinEint );
+ const double TimeNew, const double TimeOld, const real MinEint, const EoS_t EoS );
#elif ( MODEL == ELBDM )
__global__
@@ -447,7 +447,7 @@ void CUAPI_Asyn_PoissonGravitySolver( const real h_Rho_Array [][RHO_NXT][RHO_
d_Emag_Array_G + UsedPatch[s],
dt, dh, P5_Gradient,
(SelfGravity || ExtPot), ExtAcc, GPUExtAcc_Ptr,
- TimeNew, TimeOld, MinEint );
+ TimeNew, TimeOld, MinEint, EoS );
# elif ( MODEL == ELBDM )
# if ( ELBDM_SCHEME == ELBDM_HYBRID )
diff --git a/src/GPU_API/CUAPI_SetCache.cu b/src/GPU_API/CUAPI_SetCache.cu
index d36c2bbbcc..ebd21d839e 100644
--- a/src/GPU_API/CUAPI_SetCache.cu
+++ b/src/GPU_API/CUAPI_SetCache.cu
@@ -171,7 +171,8 @@ void CUPOT_HydroGravitySolver(
const real g_Emag_Array [][ CUBE(PS1) ],
const real dt, const real dh, const bool P5_Gradient,
const bool UsePot, const OptExtAcc_t ExtAcc, const ExtAcc_t ExtAcc_Func,
- const double TimeNew, const double TimeOld, const real MinEint );
+ const double TimeNew, const double TimeOld, const real MinEint,
+ const EoS_t EoS );
#elif ( MODEL == ELBDM )
__global__
diff --git a/src/Init/Init_TestProb.cpp b/src/Init/Init_TestProb.cpp
index 819bc5cd4a..77159bf824 100644
--- a/src/Init/Init_TestProb.cpp
+++ b/src/Init/Init_TestProb.cpp
@@ -30,6 +30,7 @@ void Init_TestProb_Hydro_EnergyPowerSpectrum();
void Init_TestProb_Hydro_CR_SoundWave();
void Init_TestProb_Hydro_CR_ShockTube();
void Init_TestProb_Hydro_CR_Diffusion();
+void Init_TestProb_Hydro_FermiBubble();
void Init_TestProb_ELBDM_ExtPot();
void Init_TestProb_ELBDM_JeansInstabilityComoving();
@@ -96,6 +97,7 @@ void Init_TestProb()
case TESTPROB_HYDRO_CR_SOUNDWAVE : Init_TestProb_Hydro_CR_SoundWave(); break;
case TESTPROB_HYDRO_CR_SHOCKTUBE : Init_TestProb_Hydro_CR_ShockTube(); break;
case TESTPROB_HYDRO_CR_DIFFUSION : Init_TestProb_Hydro_CR_Diffusion(); break;
+ case TESTPROB_HYDRO_FERMI_BUBBLE : Init_TestProb_Hydro_FermiBubble(); break;
case TESTPROB_ELBDM_EXTPOT : Init_TestProb_ELBDM_ExtPot(); break;
case TESTPROB_ELBDM_JEANS_INSTABILITY_COMOVING : Init_TestProb_ELBDM_JeansInstabilityComoving(); break;
diff --git a/src/LoadBalance/LB_Refine_AllocateNewPatch.cpp b/src/LoadBalance/LB_Refine_AllocateNewPatch.cpp
index 76a808b6d7..761e19eaa4 100644
--- a/src/LoadBalance/LB_Refine_AllocateNewPatch.cpp
+++ b/src/LoadBalance/LB_Refine_AllocateNewPatch.cpp
@@ -1114,9 +1114,11 @@ int AllocateSonPatch( const int FaLv, const int *Cr, const int PScale, const int
# else // #ifdef DUAL_ENERGY
// apply internal energy floor
+# ifndef SRHD
FData_Flu[ENGY][k][j][i]
= Hydro_CheckMinEintInEngy( FData_Flu[DENS][k][j][i], FData_Flu[MOMX][k][j][i], FData_Flu[MOMY][k][j][i],
FData_Flu[MOMZ][k][j][i], FData_Flu[ENGY][k][j][i], MIN_EINT, Emag );
+# endif
# endif // #ifdef DUAL_ENERGY ... else ...
# endif // #if ( MODEL == HYDRO )
diff --git a/src/Main/Main.cpp b/src/Main/Main.cpp
index a471b2e184..6ec53d2041 100644
--- a/src/Main/Main.cpp
+++ b/src/Main/Main.cpp
@@ -286,10 +286,8 @@ EoS_DP2C_t EoS_DensPres2CSqr_CPUPtr = NULL;
EoS_DE2T_t EoS_DensEint2Temp_CPUPtr = NULL;
EoS_DT2P_t EoS_DensTemp2Pres_CPUPtr = NULL;
EoS_DE2S_t EoS_DensEint2Entr_CPUPtr = NULL;
-EoS_GENE_t EoS_General_CPUPtr = NULL;
-#ifdef COSMIC_RAY
EoS_CRE2CRP_t EoS_CREint2CRPres_CPUPtr = NULL;
-#endif
+EoS_GENE_t EoS_General_CPUPtr = NULL;
#ifdef GPU
EoS_GUESS_t EoS_GuessHTilde_GPUPtr = NULL;
EoS_H2TEM_t EoS_HTilde2Temp_GPUPtr = NULL;
@@ -300,10 +298,8 @@ EoS_DP2C_t EoS_DensPres2CSqr_GPUPtr = NULL;
EoS_DE2T_t EoS_DensEint2Temp_GPUPtr = NULL;
EoS_DT2P_t EoS_DensTemp2Pres_GPUPtr = NULL;
EoS_DE2S_t EoS_DensEint2Entr_GPUPtr = NULL;
-EoS_GENE_t EoS_General_GPUPtr = NULL;
-#ifdef COSMIC_RAY
EoS_CRE2CRP_t EoS_CREint2CRPres_GPUPtr = NULL;
-#endif
+EoS_GENE_t EoS_General_GPUPtr = NULL;
#endif
// c. data structure for the CPU/GPU solvers
diff --git a/src/Makefile_base b/src/Makefile_base
index 14e06f7d11..55822cc80b 100644
--- a/src/Makefile_base
+++ b/src/Makefile_base
@@ -100,7 +100,7 @@ CPU_FILE += Mis_CompareRealValue.cpp Mis_GetTotalPatchNumber.cpp Mis_GetTim
Mis_BinarySearch.cpp Mis_1D3DIdx.cpp Mis_Matching.cpp Mis_GetTimeStep_User.cpp \
Mis_dTime2dt.cpp Mis_CoordinateTransform.cpp Mis_BinarySearch_Real.cpp Mis_InterpolateFromTable.cpp \
CPU_dtSolver.cpp dt_Prepare_Flu.cpp dt_Prepare_Pot.cpp dt_Close.cpp dt_InvokeSolver.cpp \
- Mis_UserWorkBeforeNextLevel.cpp Mis_UserWorkBeforeNextSubstep.cpp \
+ Mis_UserWorkBeforeNextLevel.cpp Mis_UserWorkBeforeNextSubstep.cpp Mis_CoordinateTransform.cpp \
Mis_SortByRows.cpp
CPU_FILE += Output_DumpData_Total.cpp Output_DumpData.cpp Output_DumpManually.cpp Output_PatchMap.cpp \
diff --git a/src/Miscellaneous/Mis_CoordinateTransform.cpp b/src/Miscellaneous/Mis_CoordinateTransform.cpp
index 1dc9132de8..9e8f9934d1 100644
--- a/src/Miscellaneous/Mis_CoordinateTransform.cpp
+++ b/src/Miscellaneous/Mis_CoordinateTransform.cpp
@@ -77,3 +77,174 @@ int Mis_Cell2Scale( const int NCell, const int lv )
return NCell*amr->scale[lv];
} // FUNCTION : Mis_Cell2Scale
+
+
+
+//-------------------------------------------------------------------------------------------------------
+// Function : Mis_Cartesian2Spherical
+// Description : Convert Cartesian coordinates to spherical coordinates
+//
+// Note : Reference: https://en.wikipedia.org/wiki/Del_in_cylindrical_and_spherical_coordinates
+//
+// Parameter : Cartesian : The array stored Cartesian coordinates
+// --> Cartesian[0/1/2] = x/y/z
+// Spherical : The array stored spherical coordinates
+// --> Spherical[0] = r, the length of radial vector
+// --> Spherical[1] = θ, the angle between the z-axis and the radial vector
+// --> Spherical[2] = φ, the angle between the x-axis and the projection of
+// the radial vector onto the xy-plane
+// Return : Spherical[]
+//-------------------------------------------------------------------------------------------------------
+void Mis_Cartesian2Spherical( const double Cartesian[], double Spherical[] )
+{
+
+ if ( SQR(Cartesian[0]) + SQR(Cartesian[1]) == 0.0 && Cartesian[2] == 0.0 )
+ Aux_Error( ERROR_INFO, "Both arguments in atan2 can not be zero !! (%s)\n", __FUNCTION__ );
+ if ( Cartesian[0] == 0.0 && Cartesian[1] == 0.0 )
+ Aux_Error( ERROR_INFO, "Both arguments in atan2 can not be zero !! (%s)\n", __FUNCTION__ );
+
+ Spherical[0] = SQRT( SQR(Cartesian[0]) + SQR(Cartesian[1]) + SQR(Cartesian[2]) );
+ Spherical[1] = ATAN2( SQRT( SQR(Cartesian[0]) + SQR(Cartesian[1]) ), Cartesian[2] );
+ Spherical[2] = ATAN2( Cartesian[1], Cartesian[0] );
+
+} // FUNCTION : Mis_Cartesian2Spherical
+
+
+
+//-------------------------------------------------------------------------------------------------------
+// Function : Mis_Cartesian2Cylindrical
+// Description : Convert Cartesian coordinates to cylindrical coordinates
+//
+// Note : Reference: https://en.wikipedia.org/wiki/Del_in_cylindrical_and_spherical_coordinates
+//
+// Parameter : Cartesian : The array stored Cartesian coordinates
+// --> Cartesian[0/1/2] = x/y/z
+// Cylindrical : The array stored cylindrical coordinates
+// --> Cylindrical[0] = ρ, the length of radial vector
+// --> Cylindrical[1] = φ, the angle between the x-axis and the radial vector
+// --> Cylindrical[2] = z, z coordinates
+// Return : Cylindrical[]
+//-------------------------------------------------------------------------------------------------------
+void Mis_Cartesian2Cylindrical( const double Cartesian[], double Cylindrical[] )
+{
+
+ if ( Cartesian[1] == 0.0 && Cartesian[0] == 0.0 )
+ Aux_Error( ERROR_INFO, "Both arguments in atan2 can not be zero !! (%s)\n", __FUNCTION__ );
+
+ Cylindrical[0] = SQRT( SQR(Cartesian[0]) + SQR(Cartesian[1]) );
+ Cylindrical[1] = ATAN2( Cartesian[1], Cartesian[0] );
+ Cylindrical[2] = Cartesian[2];
+
+} // FUNCTION : Mis_Cartesian2Cylindrical
+
+
+
+//-------------------------------------------------------------------------------------------------------
+// Function : Mis_Spherical2Cartesian
+// Description : Convert Spherical coordinates to Cartesian coordinates
+//
+// Note : Reference: https://en.wikipedia.org/wiki/Del_in_cylindrical_and_spherical_coordinates
+//
+//
+// Parameter : Spherical : The array stored Spherical coordinates
+// --> Spherical[0] = r, the length of radial vector
+// --> Spherical[1] = θ, the angle between the z-axis and the radial vector
+// --> Spherical[2] = φ, the angle between the x-axis and the projection of
+// the radial vector onto the xy-plane
+// Cartesian : The array stored Cartesian coordinates
+// --> Cartesian[0/1/2] = x/y/z
+//
+// Return : Cartesian[]
+//-------------------------------------------------------------------------------------------------------
+void Mis_Spherical2Cartesian( const double Spherical[], double Cartesian[] )
+{
+
+ Cartesian[0] = Spherical[0]*SIN(Spherical[1])*COS(Spherical[2]);
+ Cartesian[1] = Spherical[0]*SIN(Spherical[1])*SIN(Spherical[2]);
+ Cartesian[2] = Spherical[0]*COS(Spherical[1]);
+
+} // FUNCTION : Mis_Spherical2Cartesian
+
+
+
+//-------------------------------------------------------------------------------------------------------
+// Function : Mis_RotateRigidBody
+// Description : 1. Rotate rigid body based on Euler angles.
+// 2. Rotation convention follows the section 4.4 in Classical Mechenial, Goldstein, 3rd edition
+// --> Using Eq.(4.47)
+// 3. Rotating rigid body is equivalent to active rotation
+// 4. However, below process is for passive coordinate transformation
+// 5. First, rotating the initial axes, xyz, by an angle φ counterclockwise about the z-axis.
+// The resultant coordinate system is labeled the ξηζ axes.
+// Second, rotate ξηζ axes about the ξ-axis counterclockwise by an angle θ to produce another
+// intermediate set, ξ'η'ζ' axes.
+// Finally, the ξ'η'ζ' axes are rotated counterclockwise by an angle ψ about the ζ' axis to produce
+// the desired x'y'z' system.
+// 6. The angle set (φ, θ, ψ) thus completely specify the 3D rotation from the xyz to x'y'z' system.
+//
+// Note : 1. This function rotate and change the array `Cartesian` in-place
+// 2. The Euler angles between two given systems can be found by ....
+//
+// Parameter : Cartesian : The array stored Cartesian coordinates
+// --> Cartesian[0/1/2] = x/y/z
+// EulerAngle[0/1/2] : φ/θ/ψ
+//
+//
+// Return : Cartesian[]
+//-------------------------------------------------------------------------------------------------------
+void Mis_RotateRigidBody( double Cartesian[], const double EulerAngle[] )
+{
+
+ double CartesianRot[3];
+
+ const double Phi = EulerAngle[0];
+ const double Theta = EulerAngle[1];
+ const double Psi = EulerAngle[2];
+
+ CartesianRot[0] = ( COS(Phi)*COS(Psi) - COS(Theta)*SIN(Phi)*SIN(Psi) )*Cartesian[0]
+ - ( COS(Phi)*SIN(Psi) + COS(Theta)*SIN(Phi)*COS(Psi) )*Cartesian[1]
+ + ( SIN(Theta)*SIN(Phi) )*Cartesian[2];
+ CartesianRot[1] = - ( SIN(Phi)*COS(Psi) + COS(Theta)*COS(Phi)*SIN(Psi) )*Cartesian[0]
+ - ( SIN(Phi)*SIN(Psi) - COS(Theta)*COS(Phi)*COS(Psi) )*Cartesian[1]
+ - ( SIN(Theta)*COS(Phi) )*Cartesian[2];
+ CartesianRot[2] = SIN(Theta)*SIN(Psi)*Cartesian[0] + SIN(Theta)*COS(Psi)*Cartesian[1] + COS(Theta)*Cartesian[2];
+
+ for (int idx=0; idx<3; idx++) Cartesian[idx] = CartesianRot[idx];
+
+} // FUNCTION : Mis_RotateRigidBody
+
+
+
+//-------------------------------------------------------------------------------------------------------
+// Function : Mis_GetEulerAngles
+// Description : Get Euler angles
+// Note : See https://en.wikipedia.org/wiki/Euler_angles#Proper_Euler_angles_2
+//
+// Parameter : EulerAngle[0/1/2] : φ/θ/ψ, Euler angles, described in the function `RotateRigidBody`
+// : Unit[0] : The z-coordinate of the unit vector along y'-axis in the original xyz system
+// : Unit[1] : The y-coordinate of the unit vector along z'-axis in the original xyz system
+// : Unit[2] : The z-coordinate of the unit vector along z'-axis in the original xyz system
+//
+// Return : EulerAngle[0/1/2] : φ/θ/ψ
+//-------------------------------------------------------------------------------------------------------
+void Mis_GetEulerAngles( double EulerAngle[], const double Unit[] )
+{
+
+ const double Argument0 = -Unit[1] / SQRT( (double)1.0 - Unit[2]*Unit[2] );
+ const double Argument1 = +Unit[2];
+ const double Argument2 = +Unit[0] / SQRT( (double)1.0 - Unit[2]*Unit[2] );
+
+ bool Fail = false;
+
+ Fail |= Argument0 < (double)-1.0 || Argument0 > (double)1.0;
+ Fail |= Argument1 < (double)-1.0 || Argument1 > (double)1.0;
+ Fail |= Argument2 < (double)-1.0 || Argument2 > (double)1.0;
+
+ if ( Fail )
+ Aux_Error( ERROR_INFO, "Argument should be lie between -1 and 1 !! (%s)\n", Argument2, __FUNCTION__ );
+
+ EulerAngle[0] = acos( Argument0 );
+ EulerAngle[1] = acos( Argument1 );
+ EulerAngle[2] = acos( Argument2 );
+
+} // FUNCTION : Mis_GetEulerAngles
diff --git a/src/Miscellaneous/Mis_GetTimeStep.cpp b/src/Miscellaneous/Mis_GetTimeStep.cpp
index 0ba4340add..e292c9584f 100644
--- a/src/Miscellaneous/Mis_GetTimeStep.cpp
+++ b/src/Miscellaneous/Mis_GetTimeStep.cpp
@@ -40,7 +40,12 @@ double Mis_GetTimeStep( const int lv, const double dTime_SyncFaLv, const double
// -1. return immediately if the target level has no patches
// =============================================================================================================
- if ( NPatchTotal[lv] == 0 ) return HUGE_NUMBER;
+ if ( NPatchTotal[lv] == 0 )
+ {
+ delete [] dTime_Name;
+ delete [] dTime;
+ return HUGE_NUMBER;
+ }
diff --git a/src/Model_Hydro/CPU_Hydro/CPU_CosmicRay.cpp b/src/Model_Hydro/CPU_Hydro/CPU_CosmicRay.cpp
index aa2404b441..b29d03ad4e 100644
--- a/src/Model_Hydro/CPU_Hydro/CPU_CosmicRay.cpp
+++ b/src/Model_Hydro/CPU_Hydro/CPU_CosmicRay.cpp
@@ -2,6 +2,22 @@
#ifdef COSMIC_RAY
+// external functions
+#ifdef __CUDACC__
+
+#include "CUFLU_Shared_FluUtility.cu"
+
+#else // #ifdef __CUDACC__
+
+void Hydro_Con2Pri( const real In[], real Out[], const real MinPres,
+ const bool FracPassive, const int NFrac, const int FracIdx[],
+ const bool JeansMinPres, const real JeansMinPres_Coeff,
+ const EoS_DE2P_t EoS_DensEint2Pres, const EoS_DP2E_t EoS_DensPres2Eint,
+ const EoS_GUESS_t EoS_GuessHTilde, const EoS_H2TEM_t EoS_HTilde2Temp,
+ const double EoS_AuxArray_Flt[], const int EoS_AuxArray_Int[],
+ const real *const EoS_Table[EOS_NTABLE_MAX], real* const EintOut, real* LorentzFactorPtr );
+
+#endif // #ifdef __CUDACC__ ... else ...
@@ -38,14 +54,15 @@ void CR_AdiabaticWork_HalfStep_MHM_RP( real OneCell[NCOMP_TOTAL_PLUS_MAG],
const int idx_flux, const int didx_flux[3],
const real dt_dh2, const EoS_t *EoS )
{
+# ifdef SRHD
+// Although SRHD does not support the magnetic field yet, we still declare the size as
+// NCOMP_TOTAL_PLUS_MAG in case the magnetic field is supported someday.
+ real Con_L[NCOMP_TOTAL_PLUS_MAG], Con_C[NCOMP_TOTAL_PLUS_MAG], Con_R[NCOMP_TOTAL_PLUS_MAG];
+ real Pri_L[NCOMP_TOTAL_PLUS_MAG], Pri_C[NCOMP_TOTAL_PLUS_MAG], Pri_R[NCOMP_TOTAL_PLUS_MAG];
+# endif
-// 1. calculate the cosmic-ray pressure
- const real pCR_old = EoS->CREint2CRPres_FuncPtr( g_ConVar_In[CRAY][idx_in], EoS->AuxArrayDevPtr_Flt,
- EoS->AuxArrayDevPtr_Int, EoS->Table );
-
-
-// 2. compute \div V using the upwind data; reference: [2]
- real div_V[3];
+// 1. compute \div V using the upwind data; reference: [2]
+ real div_V[3], LorentzFactor = 1.0;
for (int d=0; d<3; d++)
{
@@ -57,18 +74,47 @@ void CR_AdiabaticWork_HalfStep_MHM_RP( real OneCell[NCOMP_TOTAL_PLUS_MAG],
const real DensFlux_R = g_Flux_Half[d][DENS][ idx_flux + didx_flux[d] ];
# endif
- div_V[d] = ( DensFlux_R > (real)0.0 ) ?
- ( DensFlux_R / g_ConVar_In[DENS][ idx_in ] ) :
- ( DensFlux_R / g_ConVar_In[DENS][ idx_in + didx_in[d] ] );
+# ifdef SRHD
+ for (int v=0; vDensEint2Pres_FuncPtr, EoS->DensPres2Eint_FuncPtr, EoS->GuessHTilde_FuncPtr,
+ EoS->HTilde2Temp_FuncPtr, EoS->AuxArrayDevPtr_Flt, EoS->AuxArrayDevPtr_Int,
+ EoS->Table, NULL, NULL );
+ Hydro_Con2Pri( Con_C, Pri_C, minPres, NULL_BOOL, NULL_INT, NULL, NULL_BOOL, NULL_REAL,
+ EoS->DensEint2Pres_FuncPtr, EoS->DensPres2Eint_FuncPtr, EoS->GuessHTilde_FuncPtr,
+ EoS->HTilde2Temp_FuncPtr, EoS->AuxArrayDevPtr_Flt, EoS->AuxArrayDevPtr_Int,
+ EoS->Table, NULL, &LorentzFactor );
+ Hydro_Con2Pri( Con_R, Pri_R, minPres, NULL_BOOL, NULL_INT, NULL, NULL_BOOL, NULL_REAL,
+ EoS->DensEint2Pres_FuncPtr, EoS->DensPres2Eint_FuncPtr, EoS->GuessHTilde_FuncPtr,
+ EoS->HTilde2Temp_FuncPtr, EoS->AuxArrayDevPtr_Flt, EoS->AuxArrayDevPtr_Int,
+ EoS->Table, NULL, NULL );
+
+ const real Rho_L = Pri_L[DENS];
+ const real Rho_C = Pri_C[DENS];
+ const real Rho_R = Pri_R[DENS];
+# else
+ const real Rho_L = g_ConVar_In[DENS][ idx_in - didx_in[d] ];
+ const real Rho_C = g_ConVar_In[DENS][ idx_in ];
+ const real Rho_R = g_ConVar_In[DENS][ idx_in + didx_in[d] ];
+# endif
- div_V[d] -= ( DensFlux_L > (real)0.0 ) ?
- ( DensFlux_L / g_ConVar_In[DENS][ idx_in - didx_in[d] ] ) :
- ( DensFlux_L / g_ConVar_In[DENS][ idx_in ] );
+ div_V[d] = ( DensFlux_R > (real)0.0 ) ? DensFlux_R / Rho_C : DensFlux_R / Rho_R;
+ div_V[d] -= ( DensFlux_L > (real)0.0 ) ? DensFlux_L / Rho_L : DensFlux_L / Rho_C;
} // for (int d=0; d<3; d++)
+// 2. calculate the cosmic-ray pressure
+ const real pCR_old = EoS->CREint2CRPres_FuncPtr( g_ConVar_In[CRAY][idx_in]/LorentzFactor, EoS->AuxArrayDevPtr_Flt,
+ EoS->AuxArrayDevPtr_Int, EoS->Table );
// 3. update the cosmic-ray energy
- OneCell[CRAY] -= pCR_old*dt_dh2*( div_V[0] + div_V[1] + div_V[2] );
+ OneCell[CRAY] -= pCR_old * dt_dh2 * ( div_V[0] + div_V[1] + div_V[2] );
} // FUMCTION : CR_AdiabaticWork_HalfStep_MHM_RP
@@ -110,11 +156,17 @@ void CR_AdiabaticWork_FullStep( const real g_PriVar_Half[][ CUBE(FLU_NXT) ],
const real dt, const real dh, const EoS_t *EoS )
{
- const int didx_flux[3] = { 1, N_FL_FLUX, SQR(N_FL_FLUX) };
- const int didx_fc[3] = { 1, N_FC_VAR, SQR(N_FC_VAR) };
- const real dt_dh = dt/dh;
+ const int didx_flux[3] = { 1, N_FL_FLUX, SQR(N_FL_FLUX) };
+ const int didx_fc [3] = { 1, N_FC_VAR, SQR(N_FC_VAR) };
+ const real dt_dh = dt/dh;
real div_V[3];
+# ifdef SRHD
+// Although SRHD does not support the magnetic field yet, we still declare the size as
+// NCOMP_TOTAL_PLUS_MAG in case the magnetic field is supported someday.
+ real Con_LR[NCOMP_TOTAL_PLUS_MAG], Con_CL[NCOMP_TOTAL_PLUS_MAG], Con_CR[NCOMP_TOTAL_PLUS_MAG], Con_RL[NCOMP_TOTAL_PLUS_MAG];
+ real Pri_LR[NCOMP_TOTAL_PLUS_MAG], Pri_CL[NCOMP_TOTAL_PLUS_MAG], Pri_CR[NCOMP_TOTAL_PLUS_MAG], Pri_RL[NCOMP_TOTAL_PLUS_MAG];
+# endif
const int size_ij = SQR(PS2);
CGPU_LOOP( idx_out, CUBE(PS2) )
@@ -149,13 +201,7 @@ void CR_AdiabaticWork_FullStep( const real g_PriVar_Half[][ CUBE(FLU_NXT) ],
const int k_fc = k_out + 1;
const int idx_fc = IDX321( i_fc, j_fc, k_fc, N_FC_VAR, N_FC_VAR );
-
-// 1. calculate the cosmic-ray pressure
- const real pCR_half = EoS->CREint2CRPres_FuncPtr( g_PriVar_Half[CRAY][idx_hf], EoS->AuxArrayDevPtr_Flt,
- EoS->AuxArrayDevPtr_Int, EoS->Table );
-
-
-// 2. compute \div V using the upwind data; reference: [2]
+// 1. compute \div V using the upwind data; reference: [2]
for (int d=0; d<3; d++)
{
const int faceL = 2*d;
@@ -169,18 +215,57 @@ void CR_AdiabaticWork_FullStep( const real g_PriVar_Half[][ CUBE(FLU_NXT) ],
const real DensFlux_R = g_Flux[d][DENS][ idx_flux + didx_flux[d] ];
# endif
- div_V[d] = ( DensFlux_R > (real)0.0 ) ?
- ( DensFlux_R / g_FC_Var[faceR][DENS][ idx_fc ] ) :
- ( DensFlux_R / g_FC_Var[faceL][DENS][ idx_fc + didx_fc[d] ] );
+# ifdef SRHD
+ for (int v=0; vDensEint2Pres_FuncPtr, EoS->DensPres2Eint_FuncPtr, EoS->GuessHTilde_FuncPtr,
+ EoS->HTilde2Temp_FuncPtr, EoS->AuxArrayDevPtr_Flt, EoS->AuxArrayDevPtr_Int,
+ EoS->Table, NULL, NULL );
+ Hydro_Con2Pri( Con_CL, Pri_CL, minPres, NULL_BOOL, NULL_INT, NULL, NULL_BOOL, NULL_REAL,
+ EoS->DensEint2Pres_FuncPtr, EoS->DensPres2Eint_FuncPtr, EoS->GuessHTilde_FuncPtr,
+ EoS->HTilde2Temp_FuncPtr, EoS->AuxArrayDevPtr_Flt, EoS->AuxArrayDevPtr_Int,
+ EoS->Table, NULL, NULL );
+ Hydro_Con2Pri( Con_CR, Pri_CR, minPres, NULL_BOOL, NULL_INT, NULL, NULL_BOOL, NULL_REAL,
+ EoS->DensEint2Pres_FuncPtr, EoS->DensPres2Eint_FuncPtr, EoS->GuessHTilde_FuncPtr,
+ EoS->HTilde2Temp_FuncPtr, EoS->AuxArrayDevPtr_Flt, EoS->AuxArrayDevPtr_Int,
+ EoS->Table, NULL, NULL );
+ Hydro_Con2Pri( Con_RL, Pri_RL, minPres, NULL_BOOL, NULL_INT, NULL, NULL_BOOL, NULL_REAL,
+ EoS->DensEint2Pres_FuncPtr, EoS->DensPres2Eint_FuncPtr, EoS->GuessHTilde_FuncPtr,
+ EoS->HTilde2Temp_FuncPtr, EoS->AuxArrayDevPtr_Flt, EoS->AuxArrayDevPtr_Int,
+ EoS->Table, NULL, NULL );
+
+ const real Rho_LR = Pri_LR[DENS];
+ const real Rho_CL = Pri_CL[DENS];
+ const real Rho_CR = Pri_CR[DENS];
+ const real Rho_RL = Pri_RL[DENS];
+# else
+ const real Rho_LR = g_FC_Var[faceR][DENS][ idx_fc - didx_fc[d] ];
+ const real Rho_CL = g_FC_Var[faceL][DENS][ idx_fc ];
+ const real Rho_CR = g_FC_Var[faceR][DENS][ idx_fc ];
+ const real Rho_RL = g_FC_Var[faceL][DENS][ idx_fc + didx_fc[d] ];
+# endif
- div_V[d] -= ( DensFlux_L > (real)0.0 ) ?
- ( DensFlux_L / g_FC_Var[faceR][DENS][ idx_fc - didx_fc[d] ] ) :
- ( DensFlux_L / g_FC_Var[faceL][DENS][ idx_fc ] );
+ div_V[d] = ( DensFlux_R > (real)0.0 ) ? DensFlux_R / Rho_CR : DensFlux_R / Rho_RL;
+ div_V[d] -= ( DensFlux_L > (real)0.0 ) ? DensFlux_L / Rho_LR : DensFlux_L / Rho_CL;
} // for (int d=0; d<3; d++)
+// 2. calculate the cosmic-ray pressure
+ const real pCR_half = EoS->CREint2CRPres_FuncPtr( g_PriVar_Half[CRAY][idx_hf], EoS->AuxArrayDevPtr_Flt,
+ EoS->AuxArrayDevPtr_Int, EoS->Table );
// 3. update the cosmic-ray energy
- g_Output[CRAY][idx_out] -= pCR_half*dt_dh*( div_V[0] + div_V[1] + div_V[2] );
+ g_Output[CRAY][idx_out] -= pCR_half * dt_dh * ( div_V[0] + div_V[1] + div_V[2] );
+
+// 4. apply floor
+ g_Output[CRAY][idx_out] = FMAX( g_Output[CRAY][idx_out], TINY_NUMBER );
} // CGPU_LOOP( idx_out, CUBE(PS2) )
diff --git a/src/Model_Hydro/CPU_Hydro/CPU_Shared_FluUtility.cpp b/src/Model_Hydro/CPU_Hydro/CPU_Shared_FluUtility.cpp
index 30652c3a76..f0f52f97f1 100644
--- a/src/Model_Hydro/CPU_Hydro/CPU_Shared_FluUtility.cpp
+++ b/src/Model_Hydro/CPU_Hydro/CPU_Shared_FluUtility.cpp
@@ -238,7 +238,7 @@ void Hydro_Con2Pri( const real In[], real Out[], const real MinPres,
const real _LorentzFactor = real(1.0) / LorentzFactor;
Out[0] = In[0]*_LorentzFactor;
- EoS_HTilde2Temp( HTilde, &Temp, NULL, NULL, EoS_AuxArray_Flt, EoS_AuxArray_Int, EoS_Table );
+ EoS_HTilde2Temp( HTilde, &Temp, NULL, In+NCOMP_FLUID, EoS_AuxArray_Flt, EoS_AuxArray_Int, EoS_Table );
Out[4] = Out[0]*Temp;
Out[4] = Hydro_CheckMinPres( Out[4], MinPres );
@@ -1146,6 +1146,9 @@ real Hydro_Con2Pres( const real Dens, const real MomX, const real MomY, const re
Cons[2] = MomY;
Cons[3] = MomZ;
Cons[4] = Engy;
+# if ( NCOMP_PASSIVE > 0 )
+ for (int v=0; v 0 )
+ for (int v=0; v Hence SRHD with the Newtonian gravity can only simulate the low-mass relativistic component
+// embedded in giant gas sphere.
+// e.g., relativistic AGN jet in gas sphere.
//
// Note : 1. Currently this function does NOT ensure the consistency between internal energy and
// dual-energy variable (e.g., entropy)
@@ -46,6 +54,7 @@
// TimeNew : Physical time at the current step (for the external gravity solver)
// TimeOld : Physical time at the previous step (for the external gravity solver in UNSPLIT_GRAVITY)
// MinEint : Internal energy floor
+// EoS : EoS object
//
// Return : g_Flu_Array_New, g_DE_Array
//-----------------------------------------------------------------------------------------
@@ -61,7 +70,8 @@ void CUPOT_HydroGravitySolver(
const real g_Emag_Array [][ CUBE(PS1) ],
const real dt, const real dh, const bool P5_Gradient,
const bool UsePot, const OptExtAcc_t ExtAcc, const ExtAcc_t ExtAcc_Func,
- const double TimeNew, const double TimeOld, const real MinEint )
+ const double TimeNew, const double TimeOld, const real MinEint,
+ const EoS_t EoS )
#else
void CPU_HydroGravitySolver(
real g_Flu_Array_New[][GRA_NIN][ CUBE(PS1) ],
@@ -75,7 +85,8 @@ void CPU_HydroGravitySolver(
const real dt, const real dh, const bool P5_Gradient,
const bool UsePot, const OptExtAcc_t ExtAcc, const ExtAcc_t ExtAcc_Func,
const double c_ExtAcc_AuxArray[],
- const double TimeNew, const double TimeOld, const real MinEint )
+ const double TimeNew, const double TimeOld, const real MinEint,
+ const EoS_t EoS )
#endif
{
@@ -106,6 +117,9 @@ void CPU_HydroGravitySolver(
# endif
# endif // #ifdef GAMER_DEBUG
+#if ( defined UNSPLIT_GRAVITY && defined SRHD )
+# error: SRHD does not support UNSPLIT_GRAVITY !!
+#endif
const real Gra_Const = ( P5_Gradient ) ? -dt/(12.0*dh) : -dt/(2.0*dh);
const int PS1_sqr = SQR(PS1);
@@ -165,8 +179,14 @@ void CPU_HydroGravitySolver(
// _g0: indices for the arrays without any ghost zone
CGPU_LOOP( idx_g0, CUBE(PS1) )
{
+# if ( defined SRHD && defined UNSPLIT_GRAVITY )
+ real Etot_in;
+# elif ( !defined SRHD )
// Enki = non-kinetic energy (i.e. Etot - Ekin)
- real acc_new[3]={0.0, 0.0, 0.0}, px_new, py_new, pz_new, rho_new, Enki_in, Ekin_out, Etot_in, Etot_out, _rho2;
+ real Enki_in, Ekin_out, Etot_in, _rho2;
+# endif
+
+ real acc_new[3]={0.0, 0.0, 0.0}, px_new, py_new, pz_new, rho_new, Etot_out;
# ifdef UNSPLIT_GRAVITY
real acc_old[3]={0.0, 0.0, 0.0}, px_old, py_old, pz_old, rho_old, Emag_in=0.0;
# endif
@@ -271,7 +291,6 @@ void CPU_HydroGravitySolver(
} // if ( P5_Gradient ) ... else ...
} // if ( UsePot )
-
// advance fluid
# ifdef UNSPLIT_GRAVITY
@@ -356,7 +375,29 @@ void CPU_HydroGravitySolver(
# else // #ifdef UNSPLIT_GRAVITY
+# ifdef SRHD
+ real Cons_new[NCOMP_TOTAL]={0.0}, Prim_new[NCOMP_TOTAL]={0.0}, LorentzFactor_new, Cons_old2[NCOMP_TOTAL]={0.0};
+ // NOTE: g_Flu_Array_New has NCOMP_FLUID only instead of NCOMP_TOTAL
+ for (int v=0; vBoxSize[2];
+ AuxArray_Flt[ 7] = IsothermalSlab_Truncation;
+ AuxArray_Flt[ 8] = distance_h;
+ AuxArray_Flt[ 9] = v_halo;
+ AuxArray_Flt[10] = interfaceHeight;
+
+ AuxArray_Int[ 0] = Jet_Ambient;
+
+} // FUNCTION : SetExtPotAuxArray_IsothermalSlab
+#endif // #ifndef __CUDACC__
+
+
+
+// =================================
+// II. Specify external potential
+// =================================
+
+//-----------------------------------------------------------------------------------------
+// Function : ExtPot_IsothermalSlab
+// Description : Calculate the external potential at the given coordinates and time
+//
+// Note : 1. This function is shared by CPU and GPU
+// 2. Auxiliary arrays UserArray_Flt/Int[] are set by SetExtPotAuxArray_IsothermalSlab()
+//
+// Parameter : x/y/z : Target spatial coordinates
+// Time : Target physical time
+// UserArray_Flt/Int : User-provided floating-point/integer auxiliary arrays
+// Usage : Different usages of external potential when computing total potential on level Lv
+// --> EXT_POT_USAGE_ADD : add external potential on Lv
+// EXT_POT_USAGE_SUB : subtract external potential for preparing self-gravity potential on Lv-1
+// EXT_POT_USAGE_SUB_TINT: like SUB but for temporal interpolation
+// --> This parameter is useless in most cases
+// PotTable : 3D potential table used by EXT_POT_TABLE
+// GenePtr : Array of pointers for general potential tables
+//
+// Return : External potential at (x,y,z,Time)
+//-----------------------------------------------------------------------------------------
+GPU_DEVICE_NOINLINE
+static real ExtPot_IsothermalSlab( const double x, const double y, const double z, const double Time,
+ const double UserArray_Flt[], const int UserArray_Int[],
+ const ExtPotUsage_t Usage, const real PotTable[], void **GenePtr )
+{
+
+// halo potential
+ const double cz = UserArray_Flt[ 2]; // z ...
+ const real IsothermalSlab_VelocityDispersion = (real)UserArray_Flt[ 3]; //
+ const real IsothermalSlab_PeakDens = (real)UserArray_Flt[ 4]; //
+ const real NewtonG = UserArray_Flt[ 5];
+ const real BoxSize_Z = UserArray_Flt[ 6];
+ const real IsothermalSlab_Truncation = (real)UserArray_Flt[ 7]; //
+ const real distance_h = (real)UserArray_Flt[ 8];
+ const real v_halo = (real)UserArray_Flt[ 9];
+ const real interfaceHeight = (real)UserArray_Flt[10];
+ const int Jet_Ambient = UserArray_Int[ 0];
+
+ const double IsothermalSlab_VelocityDispersion_Sqr = SQR(IsothermalSlab_VelocityDispersion);
+ const double dz = z - cz;
+
+ real stellarDiskPot = sqrt( ( 2.0*M_PI*NewtonG*IsothermalSlab_PeakDens ) / IsothermalSlab_VelocityDispersion_Sqr );
+
+# ifndef __CUDACC__
+ if ( cz != 0.5*BoxSize_Z ) Aux_Error( ERROR_INFO, "We expect the z-position of stellar disk is at box-center!\n" );
+# endif
+
+ real LogPot;
+
+ if ( Jet_Ambient == 2 )
+ {
+ if ( fabs(dz) > IsothermalSlab_Truncation )
+ {
+ stellarDiskPot = 2.0*IsothermalSlab_VelocityDispersion_Sqr*log(cosh(IsothermalSlab_Truncation*stellarDiskPot));
+ LogPot = SQR(v_halo) * log(SQR(IsothermalSlab_Truncation) + SQR(distance_h));
+ } else
+ {
+ stellarDiskPot = 2.0*IsothermalSlab_VelocityDispersion_Sqr*log(cosh(dz*stellarDiskPot));
+ LogPot = SQR(v_halo) * log(dz*dz + SQR(distance_h));
+ }
+ }
+ else if ( Jet_Ambient == 3 )
+ {
+ if ( fabs(dz) > interfaceHeight )
+ {
+ stellarDiskPot = 2.0*IsothermalSlab_VelocityDispersion_Sqr*log(cosh(interfaceHeight*stellarDiskPot));
+ LogPot = SQR(v_halo) * log(SQR(interfaceHeight) + SQR(distance_h));
+ } else
+ {
+ stellarDiskPot = 2.0*IsothermalSlab_VelocityDispersion_Sqr*log(cosh(dz*stellarDiskPot));
+ LogPot = SQR(v_halo) * log(dz*dz + SQR(distance_h));
+ }
+ } // if ( Jet_Ambient == 2 ) ... else if ... else ...
+
+ real TotPot = stellarDiskPot + LogPot;
+
+ return TotPot;
+
+} // FUNCTION : ExtPot_IsothermalSlab
+
+
+
+// =================================
+// III. Set initialization functions
+// =================================
+
+#ifdef __CUDACC__
+# define FUNC_SPACE __device__ static
+#else
+# define FUNC_SPACE static
+#endif
+
+FUNC_SPACE ExtPot_t ExtPot_Ptr = ExtPot_IsothermalSlab;
+
+//-----------------------------------------------------------------------------------------
+// Function : SetCPU/GPUExtPot_IsothermalSlab
+// Description : Return the function pointers of the CPU/GPU external potential routines
+//
+// Note : 1. Invoked by Init_ExtPot_IsothermalSlab()
+//
+// Parameter : CPU/GPUExtPot_Ptr (call-by-reference)
+//
+// Return : CPU/GPUExtPot_Ptr
+//-----------------------------------------------------------------------------------------
+#ifdef __CUDACC__
+__host__
+void SetGPUExtPot_IsothermalSlab( ExtPot_t &GPUExtPot_Ptr )
+{
+ CUDA_CHECK_ERROR( cudaMemcpyFromSymbol( &GPUExtPot_Ptr, ExtPot_Ptr, sizeof(ExtPot_t) ) );
+}
+
+#else // #ifdef __CUDACC__
+
+void SetCPUExtPot_IsothermalSlab( ExtPot_t &CPUExtPot_Ptr )
+{
+ CPUExtPot_Ptr = ExtPot_Ptr;
+}
+
+#endif // #ifdef __CUDACC__ ... else ...
+
+
+
+#ifndef __CUDACC__
+
+// local function prototypes
+void SetExtPotAuxArray_IsothermalSlab( double [], int [] );
+void SetCPUExtPot_IsothermalSlab( ExtPot_t & );
+#ifdef GPU
+void SetGPUExtPot_IsothermalSlab( ExtPot_t & );
+#endif
+
+//-----------------------------------------------------------------------------------------
+// Function : Init_ExtPot_IsothermalSlab
+// Description : Initialize external potential
+//
+// Note : 1. Set auxiliary arrays by invoking SetExtPotAuxArray_*()
+// --> They will be copied to GPU automatically in CUAPI_SetConstMemory()
+// 2. Set the CPU/GPU external potential major routines by invoking SetCPU/GPUExtPot_*()
+// 3. Invoked by Init_ExtAccPot()
+// --> Enable it by linking to the function pointer "Init_ExtPot_Ptr"
+// 4. Add "#ifndef __CUDACC__" since this routine is only useful on CPU
+//
+// Parameter : None
+//
+// Return : None
+//-----------------------------------------------------------------------------------------
+void Init_ExtPot_IsothermalSlab()
+{
+
+ SetExtPotAuxArray_IsothermalSlab( ExtPot_AuxArray_Flt, ExtPot_AuxArray_Int );
+ SetCPUExtPot_IsothermalSlab( CPUExtPot_Ptr );
+# ifdef GPU
+ SetGPUExtPot_IsothermalSlab( GPUExtPot_Ptr );
+# endif
+
+} // FUNCTION : Init_ExtPot_IsothermalSlab
+
+#endif // #ifndef __CUDACC__
+
+
+
+#endif // #ifdef GRAVITY
diff --git a/src/TestProblem/Hydro/FermiBubble/ExtPot_IsothermalSlab.cu b/src/TestProblem/Hydro/FermiBubble/ExtPot_IsothermalSlab.cu
new file mode 120000
index 0000000000..eb0d9c78f0
--- /dev/null
+++ b/src/TestProblem/Hydro/FermiBubble/ExtPot_IsothermalSlab.cu
@@ -0,0 +1 @@
+ExtPot_IsothermalSlab.cpp
\ No newline at end of file
diff --git a/src/TestProblem/Hydro/FermiBubble/Init_TestProb_FermiBubble.cpp b/src/TestProblem/Hydro/FermiBubble/Init_TestProb_FermiBubble.cpp
new file mode 100644
index 0000000000..1773667dd2
--- /dev/null
+++ b/src/TestProblem/Hydro/FermiBubble/Init_TestProb_FermiBubble.cpp
@@ -0,0 +1,1500 @@
+#include
+#include
+#include
+#include "GAMER.h"
+#include "TestProb.h"
+
+
+
+void ***calloc_3d_array( size_t nt, size_t nr, size_t nc, size_t size );
+void free_3d_array( void ***array );
+void Mis_Cartesian2Spherical( const double Cartesian[], double Spherical[] );
+void CartesianRotate( double x[], double theta, double phi, bool inverse );
+void Interpolation_UM_IC( real x, real y, real z, real ****Pri_input, real **XYZ, real *Pri_output, bool disk );
+real TrilinearInterpolation( real *FieldAtVertices, real *xyz000, real *dxyz, real *xyz );
+static void SetArrayDisk();
+static void SetArrayHVC();
+static real *randXYZ;
+static int numClouds = 0;
+static real radiusCloud = 1.0;
+void randCloud( real **randXYZ, int numClouds );
+
+
+// problem-specific global variables
+// =======================================================================================
+
+// options
+ int Jet_Ambient; // [0/1/9]: uniform/Milky-Way/load-from-file
+static int Jet_Fire; // [0/1/2/3]: no jet/upper jet/lower jet/bipolar jet
+static double Jet_Duration; // a duration of jet injection from the start of simulation
+
+// general parameters
+static double ParticleMass; // atomic mass unit in jet source
+
+// uniform background parameters
+static double Amb_UniformDens; // uniform ambient density
+static double Amb_UniformVel[3]; // uniform ambient 4-velocity
+static double Amb_UniformTemp; // uniform ambient temperature
+
+// Milky Way parameters
+ double IsothermalSlab_Center[3];
+
+// jet fluid parameters
+static double Jet_SrcVel; // jet 4-velocity
+static double Jet_SrcGamma; // jet lorentz factor
+static double Jet_SrcDens; // jet density
+static double Jet_SrcTemp; // jet temperature
+static bool Jet_SmoothVel; // smooth radial component of 4-velocity on cross section
+
+static bool Jet_SphericalSrc;
+
+#ifdef COSMIC_RAY
+static double Jet_Src_CR_Engy;
+static double Amb_CR_Engy;
+#endif
+
+// sound speed
+static double CharacteristicSpeed; // the characteristic speed of the simulation problem
+ // the default end-time (END_T) will be estimated from
+ // `CharacteristicSpeed` and `BOX_SIZE`
+static real *buffer;
+static real *Header_disk;
+static real ***Rhoo_disk;
+static real ***VelX_disk;
+static real ***VelY_disk;
+static real ***VelZ_disk;
+static real ***Pres_disk;
+static real *Header_hvc;
+static real ***Rhoo_hvc;
+static real ***VelX_hvc;
+static real ***VelY_hvc;
+static real ***VelZ_hvc;
+static real ***Pres_hvc;
+static real *X_disk;
+static real *Y_disk;
+static real *Z_disk;
+static real *X_hvc;
+static real *Y_hvc;
+static real *Z_hvc;
+
+// fermi bubbles
+ real IsothermalSlab_VelocityDispersion;
+ real IsothermalSlab_PeakDens;
+ real IsothermalSlab_Truncation;
+static real ambientTemperature;
+static real gasDiskTemperature;
+static real gasDiskPeakDens;
+ real interfaceHeight;
+static double criticalTemp;
+static double gasDisk_highResRadius;
+static int gasDisk_lowRes_LEVEL;
+static int jetSrc_lowRes_LEVEL;
+
+// Dark logarithmic halo potential
+ real v_halo;
+ real distance_h;
+
+void Init_ExtPot_IsothermalSlab();
+
+// =======================================================================================
+/* G A C */
+/* ____________ */
+/* \ | / */
+/* \ E| / z */
+/* \ /|\ / ^ */
+/* \/_|_\/ | */
+/* /\O| /\B | */
+/* / \|/ \ */
+/* / D| \ */
+/* /_____|_____\ */
+/* F */
+// =======================================================================================
+// jet geometry parameters
+static double Jet_Radius; // length of OB
+static double Jet_HalfHeight; // length of OA
+static double Jet_HalfOpeningAngle; // half-opening angle (i.e. ∠ADC)
+static double Jet_CenOffset[3]; // jet central coordinates offset
+static double Jet_Center[3]; // jet central coordinates
+static double Jet_MaxDis; // maximum distance between the jet source and the jet center
+
+
+// precession parameters
+static double Jet_AngularVelocity; // precession angular velocity (degree per code_time)
+static double Jet_PrecessionAngle; // precession angle in degree
+static double Jet_PrecessionAxis[3]; // cone orientation vector (x,y,z). i.e. vector OA
+
+
+#if ( NCOMP_PASSIVE_USER > 0 )
+static FieldIdx_t Passive_0000 = 5; // disk
+static FieldIdx_t Passive_0001 = 6; // src, the ejected material is Jet_Dens
+static FieldIdx_t Passive_0002 = 7; // src, the ejected material is CRay
+#endif
+// =======================================================================================
+
+
+
+//-------------------------------------------------------------------------------------------------------
+// Function : Validate
+// Description : Validate the compilation flags and runtime parameters for this test problem
+//
+// Note : None
+//
+// Parameter : None
+//
+// Return : None
+//-------------------------------------------------------------------------------------------------------
+void Validate()
+{
+
+ if ( MPI_Rank == 0 ) Aux_Message( stdout, " Validating test problem %d ...\n", TESTPROB_ID );
+
+
+// errors
+# if ( MODEL != HYDRO )
+ Aux_Error( ERROR_INFO, "MODEL != HYDRO !!\n" );
+# endif
+
+# ifndef SRHD
+ Aux_Error( ERROR_INFO, "SRHD must be enabled !!\n" );
+# endif
+
+# ifdef PARTICLE
+ Aux_Error( ERROR_INFO, "PARTICLE must be disabled !!\n" );
+# endif
+
+# ifndef GRAVITY
+ if ( Jet_Ambient == 1 || Jet_Ambient == 2 )
+ Aux_Error( ERROR_INFO, "GRAVITY must be enabled !!\n" );
+# endif
+
+
+// warnings
+ if ( MPI_Rank == 0 )
+ {
+ for (int s=0; s<6; s++)
+ if ( OPT__BC_FLU[s] != BC_FLU_OUTFLOW )
+ Aux_Message( stderr, "WARNING : it's recommended to use the outflow BC (currently OPT__BC_FLU[%d] = %d != 2)\n",
+ s, OPT__BC_FLU[s] );
+ }
+
+
+ if ( MPI_Rank == 0 ) Aux_Message( stdout, " Validating test problem %d ... done\n", TESTPROB_ID );
+
+} // FUNCTION : Validate
+
+
+
+#if ( MODEL == HYDRO )
+//-------------------------------------------------------------------------------------------------------
+// Function : SetParameter
+// Description : Load and set the problem-specific runtime parameters
+//
+// Note : 1. Filename is set to "Input__TestProb" by default
+// 2. Major tasks in this function:
+// (1) load the problem-specific runtime parameters
+// (2) set the problem-specific derived parameters
+// (3) reset other general-purpose parameters if necessary
+// (4) make a note of the problem-specific parameters
+//
+// Parameter : None
+//
+// Return : None
+//-------------------------------------------------------------------------------------------------------
+void SetParameter()
+{
+
+ if ( MPI_Rank == 0 ) Aux_Message( stdout, " Setting runtime parameters ...\n" );
+
+
+// (1) load the problem-specific runtime parameters
+ const char FileName[] = "Input__TestProb";
+ ReadPara_t *ReadPara = new ReadPara_t;
+
+// (1-1) add parameters in the following format:
+// --> note that VARIABLE, DEFAULT, MIN, and MAX must have the same data type
+// --> some handy constants (e.g., Useless_bool, Eps_double, NoMin_int, ...) are defined in "include/ReadPara.h"
+// ************************************************************************************************************************
+// ReadPara->Add( "KEY_IN_THE_FILE", &VARIABLE, DEFAULT, MIN, MAX );
+// ************************************************************************************************************************
+
+// load options
+ ReadPara->Add( "Jet_Ambient", &Jet_Ambient, 1, 0, 9 );
+ ReadPara->Add( "Jet_Fire", &Jet_Fire, 3, 0, 3 );
+ ReadPara->Add( "Jet_SphericalSrc", &Jet_SphericalSrc, false, Useless_bool, Useless_bool );
+ ReadPara->Add( "Jet_Duration", &Jet_Duration, NoMax_double, 0.0, NoMax_double );
+
+// load jet fluid parameters
+ ReadPara->Add( "Jet_SrcVel", &Jet_SrcVel, -1.0, NoMin_double, NoMax_double );
+ ReadPara->Add( "Jet_SmoothVel", &Jet_SmoothVel, false, Useless_bool, Useless_bool );
+ ReadPara->Add( "Jet_SrcDens", &Jet_SrcDens, -1.0, Eps_double, NoMax_double );
+ ReadPara->Add( "Jet_SrcTemp", &Jet_SrcTemp, -1.0, Eps_double, NoMax_double );
+ ReadPara->Add( "gasDisk_highResRadius", &gasDisk_highResRadius, -1.0, NoMin_double, NoMax_double );
+ ReadPara->Add( "gasDisk_lowRes_LEVEL", &gasDisk_lowRes_LEVEL, -1, 0, NoMax_int );
+ ReadPara->Add( "jetSrc_lowRes_LEVEL", &jetSrc_lowRes_LEVEL, -1, 0, NoMax_int );
+# ifdef COSMIC_RAY
+ ReadPara->Add( "Jet_Src_CR_Engy", &Jet_Src_CR_Engy, -1.0, 0.0, NoMax_double );
+ ReadPara->Add( "Amb_CR_Engy", &Amb_CR_Engy, -1.0, 0.0, NoMax_double );
+# endif
+
+// load source geometry parameters
+ ReadPara->Add( "Jet_Radius", &Jet_Radius, -1.0, Eps_double, NoMax_double );
+ ReadPara->Add( "Jet_HalfHeight", &Jet_HalfHeight, -1.0, Eps_double, NoMax_double );
+ ReadPara->Add( "Jet_HalfOpeningAngle", &Jet_HalfOpeningAngle, -1.0, 0.0, 90.0 );
+ ReadPara->Add( "Jet_CenOffset_x", &Jet_CenOffset[0], NoDef_double, NoMin_double, NoMax_double );
+ ReadPara->Add( "Jet_CenOffset_y", &Jet_CenOffset[1], NoDef_double, NoMin_double, NoMax_double );
+ ReadPara->Add( "Jet_CenOffset_z", &Jet_CenOffset[2], NoDef_double, NoMin_double, NoMax_double );
+
+// load precession parameters
+ ReadPara->Add( "Jet_AngularVelocity", &Jet_AngularVelocity, NoDef_double, 0.0, NoMax_double );
+ ReadPara->Add( "Jet_PrecessionAngle", &Jet_PrecessionAngle, NoDef_double, NoMin_double, 90.0 );
+ ReadPara->Add( "Jet_PrecessionAxis_x", &Jet_PrecessionAxis[0], NoDef_double, NoMin_double, NoMax_double );
+ ReadPara->Add( "Jet_PrecessionAxis_y", &Jet_PrecessionAxis[1], NoDef_double, NoMin_double, NoMax_double );
+ ReadPara->Add( "Jet_PrecessionAxis_z", &Jet_PrecessionAxis[2], NoDef_double, NoMin_double, NoMax_double );
+
+// load uniform background parameters
+ ReadPara->Add( "Amb_UniformDens", &Amb_UniformDens, -1.0, Eps_double, NoMax_double );
+ ReadPara->Add( "Amb_UniformVel_x", &Amb_UniformVel[0], 0.0, NoMin_double, NoMax_double );
+ ReadPara->Add( "Amb_UniformVel_y", &Amb_UniformVel[1], 0.0, NoMin_double, NoMax_double );
+ ReadPara->Add( "Amb_UniformVel_z", &Amb_UniformVel[2], 0.0, NoMin_double, NoMax_double );
+ ReadPara->Add( "Amb_UniformTemp", &Amb_UniformTemp, -1.0, Eps_double, NoMax_double );
+
+
+ ReadPara->Add( "CharacteristicSpeed", &CharacteristicSpeed, -1.0, NoMin_double, NoMax_double );
+ ReadPara->Add( "criticalTemp", &criticalTemp, -1.0, NoMin_double, NoMax_double );
+
+// load Milky Way parameters
+ ReadPara->Add( "IsothermalSlab_Center_x", &IsothermalSlab_Center[0], -1.0, NoMin_double, NoMax_double );
+ ReadPara->Add( "IsothermalSlab_Center_y", &IsothermalSlab_Center[1], -1.0, NoMin_double, NoMax_double );
+ ReadPara->Add( "IsothermalSlab_Center_z", &IsothermalSlab_Center[2], -1.0, NoMin_double, NoMax_double );
+
+ ReadPara->Read( FileName );
+
+ delete ReadPara;
+
+// Read header for the Fermi bubbles
+ SetArrayDisk();
+// SetArrayHVC();
+ randCloud( &randXYZ, numClouds );
+
+// replace useless parameters with NaN
+ if ( Jet_Ambient != 0 )
+ {
+ Amb_UniformDens = NAN;
+ Amb_UniformVel[0] = NAN;
+ Amb_UniformVel[1] = NAN;
+ Amb_UniformVel[2] = NAN;
+ }
+
+// (1-2) check runtime parameters
+
+// check time-dependent source
+ if ( IsothermalSlab_Center[0] == -1.0 )
+ IsothermalSlab_Center[0] = 0.5*amr->BoxSize[0];
+
+ if ( IsothermalSlab_Center[1] == -1.0 )
+ IsothermalSlab_Center[1] = 0.5*amr->BoxSize[1];
+
+ if ( IsothermalSlab_Center[2] == -1.0 )
+ IsothermalSlab_Center[2] = 0.5*amr->BoxSize[2];
+
+ if ( Jet_Ambient == 9 && OPT__INIT != 3 )
+ Aux_Error( ERROR_INFO, "OPT__INIT must be 3 !!\n" );
+
+
+// check UNIT_L is in reasonable range
+ if ( ( UNIT_L <= 0.5*Const_kpc || 2.0*Const_kpc <= UNIT_L ) && OPT__UNIT )
+ Aux_Error( ERROR_INFO, "UNIT_L=%e is far from %e !!\n", UNIT_L, Const_kpc );
+
+ const double Const_Erg2eV = 6.2415e11;
+
+// (1-2) convert to code unit
+ Jet_SrcVel *= Const_c / UNIT_V;
+ Jet_SrcTemp *= Const_kB / (ParticleMass*Const_c*Const_c);
+ Jet_SrcDens *= 1.0 / UNIT_D;
+
+# ifdef COSMIC_RAY
+ Jet_Src_CR_Engy *= 1.0 / UNIT_P;
+ Amb_CR_Engy *= 1.0 / UNIT_P;
+# endif
+
+ Jet_Radius *= Const_kpc / UNIT_L;
+ Jet_HalfHeight *= Const_kpc / UNIT_L;
+ Jet_HalfOpeningAngle *= M_PI / 180.0;
+ Jet_PrecessionAngle *= M_PI / 180.0;
+
+ Jet_CenOffset[0] *= Const_kpc / UNIT_L;
+ Jet_CenOffset[1] *= Const_kpc / UNIT_L;
+ Jet_CenOffset[2] *= Const_kpc / UNIT_L;
+
+ Jet_Duration *= Const_Myr / UNIT_T;
+
+ gasDisk_highResRadius *= Const_kpc / UNIT_L;
+
+ if ( Jet_Ambient == 0 )
+ {
+ Amb_UniformDens *= 1.0 / UNIT_D;
+ Amb_UniformVel[0] *= Const_c / UNIT_V;
+ Amb_UniformVel[1] *= Const_c / UNIT_V;
+ Amb_UniformVel[2] *= Const_c / UNIT_V;
+ }
+ else if ( Jet_Ambient == 2 || Jet_Ambient == 3 || Jet_Ambient == 4 )
+ {
+ IsothermalSlab_VelocityDispersion = Header_disk[15];
+ IsothermalSlab_PeakDens = Header_disk[16];
+ //IsothermalSlab_Truncation = Header_disk[21];
+ IsothermalSlab_Truncation = 0.95*0.5*amr->BoxSize[2];
+ gasDiskPeakDens = Header_disk[19];
+
+ IsothermalSlab_VelocityDispersion *= 1e5 / UNIT_V; // km/s --> 1/c
+ IsothermalSlab_PeakDens *= 1.0 / UNIT_D;
+ IsothermalSlab_Truncation *= Const_kpc / UNIT_L;
+ IsothermalSlab_Center[0] *= Const_kpc / UNIT_L;
+ IsothermalSlab_Center[1] *= Const_kpc / UNIT_L;
+ IsothermalSlab_Center[2] *= Const_kpc / UNIT_L;
+
+ distance_h = Header_disk[29];
+ distance_h *= Const_kpc / UNIT_L;
+
+ v_halo = Header_disk[30];
+ v_halo *= 1.0 / UNIT_V;
+
+ gasDiskPeakDens /= UNIT_D;
+
+ ambientTemperature = Header_disk[20];
+ ambientTemperature *= Const_kB / (ParticleMass*UNIT_V*UNIT_V);
+
+ gasDiskTemperature = Header_disk[18];
+ gasDiskTemperature *= Const_kB / (ParticleMass*UNIT_V*UNIT_V);
+
+ criticalTemp *= Const_kB / (ParticleMass*Const_c*Const_c);
+ } // if ( Jet_Ambient == 0 ) ... else if ...
+
+ Amb_UniformTemp *= Const_kB / (ParticleMass*Const_c*Const_c);
+ Jet_AngularVelocity *= 1.0; // the unit of Jet_AngularVelocity is UNIT_T
+
+
+// (2) set the problem-specific derived parameters
+ const double SecAngle = 1.0 / cos( 0.5*Jet_HalfOpeningAngle );
+ const double TanAngle = sin( 0.5*Jet_HalfOpeningAngle ) * SecAngle;
+
+ Jet_MaxDis = sqrt( SQR( Jet_Radius ) + SQR( Jet_HalfHeight * SecAngle ) + 2.0 * Jet_Radius * Jet_HalfHeight * TanAngle );
+ Jet_SrcGamma = sqrt(1.0 + SQR(Jet_SrcVel));
+
+ for (int d=0; d<3; d++) Jet_Center[d] = 0.5*amr->BoxSize[d] + Jet_CenOffset[d];
+
+
+// (4) reset other general-purpose parameters
+// --> a helper macro PRINT_RESET_PARA is defined in Macro.h
+ const long End_Step_Default = __INT_MAX__;
+ const double End_T_Default = 0.5 * BOX_SIZE * UNIT_L / CharacteristicSpeed / UNIT_V / UNIT_T;
+
+ if ( END_STEP < 0 )
+ {
+ END_STEP = End_Step_Default;
+ PRINT_RESET_PARA( END_STEP, FORMAT_LONG, "" );
+ }
+
+ if ( END_T < 0.0 )
+ {
+ if ( CharacteristicSpeed == -1.0 ) Aux_Error( ERROR_INFO, "CharacteristicSpeed must be provided !!\n" );
+ else END_T = End_T_Default;
+
+ PRINT_RESET_PARA( END_T, FORMAT_REAL, "" );
+ }
+
+ if ( OUTPUT_DT < 0.0 )
+ {
+ OUTPUT_DT = END_T / 30.0;
+ PRINT_RESET_PARA( OUTPUT_DT, FORMAT_REAL, "" );
+ }
+
+
+// (4) make a note
+ if ( MPI_Rank == 0 )
+ {
+ Aux_Message( stdout, "=============================================================================\n" );
+ Aux_Message( stdout, " test problem ID = %d\n", TESTPROB_ID );
+ Aux_Message( stdout, " Jet_Ambient = %d\n", Jet_Ambient );
+ Aux_Message( stdout, " Jet_Fire = %d\n", Jet_Fire );
+ Aux_Message( stdout, " Jet_SmoothVel = %d\n", Jet_SmoothVel );
+ Aux_Message( stdout, " Jet_SphericalSrc = %d\n", Jet_SphericalSrc );
+ Aux_Message( stdout, " Jet_Duration = %14.7e Myr \n", Jet_Duration*UNIT_T/Const_Myr );
+ Aux_Message( stdout, " ParticleMass = %14.7e g\n", ParticleMass );
+ Aux_Message( stdout, " Jet_SrcVel = %14.7e c\n", Jet_SrcVel );
+ Aux_Message( stdout, " Jet_SrcDens = %14.7e g/cm^3\n", Jet_SrcDens*UNIT_D );
+ Aux_Message( stdout, " Jet_SrcTemp = %14.7e kT/mc**2\n", Jet_SrcTemp );
+# ifdef COSMIC_RAY
+ Aux_Message( stdout, " Jet_Src_CR_Engy = %14.7e \n", Jet_Src_CR_Engy*UNIT_P );
+ Aux_Message( stdout, " Amb_CR_Engy = %14.7e \n", Amb_CR_Engy*UNIT_P );
+# endif
+ Aux_Message( stdout, " Jet_NumDensSrc = %14.7e per cc\n", Jet_SrcDens*UNIT_D/ParticleMass );
+ Aux_Message( stdout, " Jet_CenOffset[x] = %14.7e kpc\n", Jet_CenOffset[0]*UNIT_L/Const_kpc );
+ Aux_Message( stdout, " Jet_CenOffset[y] = %14.7e kpc\n", Jet_CenOffset[1]*UNIT_L/Const_kpc );
+ Aux_Message( stdout, " Jet_CenOffset[z] = %14.7e kpc\n", Jet_CenOffset[2]*UNIT_L/Const_kpc );
+ Aux_Message( stdout, " Jet_AngularVelocity = %14.7e degree/kyr\n", Jet_AngularVelocity );
+ Aux_Message( stdout, " Jet_PrecessionAngle = %14.7e degree\n", Jet_PrecessionAngle*180.0/M_PI );
+ Aux_Message( stdout, " Jet_HalfOpeningAngle = %14.7e degree\n", Jet_HalfOpeningAngle*180.0/M_PI );
+ Aux_Message( stdout, " Jet_Radius = %14.7e kpc\n", Jet_Radius*UNIT_L/Const_kpc );
+ Aux_Message( stdout, " Jet_HalfHeight = %14.7e kpc\n", Jet_HalfHeight*UNIT_L/Const_kpc );
+ Aux_Message( stdout, " Jet_MaxDis = %14.7e kpc\n", Jet_MaxDis*UNIT_L/Const_kpc );
+ Aux_Message( stdout, " gasDisk_highResRadius = %14.7e kpc\n", gasDisk_highResRadius*UNIT_L/Const_kpc );
+ Aux_Message( stdout, " gasDisk_lowRes_LEVEL = %d\n", gasDisk_lowRes_LEVEL );
+ Aux_Message( stdout, " jetSrc_lowRes_LEVEL = %d\n", jetSrc_lowRes_LEVEL );
+
+ if ( Jet_Ambient == 0 )
+ {
+ Aux_Message( stdout, " Amb_UniformDens = %14.7e g/cm^3\n", Amb_UniformDens*UNIT_D );
+ Aux_Message( stdout, " Amb_UniformTemp = %14.7e kT/mc**2\n", Amb_UniformTemp );
+ Aux_Message( stdout, " Amb_UniformVel[x] = %14.7e c\n", Amb_UniformVel[0] );
+ Aux_Message( stdout, " Amb_UniformVel[y] = %14.7e c\n", Amb_UniformVel[1] );
+ Aux_Message( stdout, " Amb_UniformVel[z] = %14.7e c\n", Amb_UniformVel[2] );
+ Aux_Message( stdout, " Jet_UniformNumDens = %14.7e per cc\n", Amb_UniformDens*UNIT_D/ParticleMass );
+ } else if ( Jet_Ambient == 2 )
+ {
+ Aux_Message( stdout, " IsothermalSlab_Center[0] = %14.7e kpc\n", IsothermalSlab_Center[0]*UNIT_L/Const_kpc );
+ Aux_Message( stdout, " IsothermalSlab_Center[1] = %14.7e kpc\n", IsothermalSlab_Center[1]*UNIT_L/Const_kpc );
+ Aux_Message( stdout, " IsothermalSlab_Center[2] = %14.7e kpc\n", IsothermalSlab_Center[2]*UNIT_L/Const_kpc );
+ Aux_Message( stdout, " criticalTemp = %14.7e K\n", criticalTemp / ( Const_kB / (ParticleMass*Const_c*Const_c) ) );
+ } // if ( Jet_Ambient == 0 ) ... else if ...
+
+ Aux_Message( stdout, " CharacteristicSpeed = %14.7e c\n", CharacteristicSpeed / UNIT_V );
+ Aux_Message( stdout, " Jet_PrecessionAxis[x] = %14.7e\n", Jet_PrecessionAxis[0] );
+ Aux_Message( stdout, " Jet_PrecessionAxis[y] = %14.7e\n", Jet_PrecessionAxis[1] );
+ Aux_Message( stdout, " Jet_PrecessionAxis[z] = %14.7e\n", Jet_PrecessionAxis[2] );
+
+ Aux_Message( stdout, "=============================================================================\n" );
+ } // if ( MPI_Rank == 0 )
+
+ if ( MPI_Rank == 0 ) Aux_Message( stdout, " Setting runtime parameters ... done\n" );
+
+} // FUNCTION : SetParameter
+
+
+
+void ReadBinFile( char *FileName, real **buffer )
+{
+
+ FILE *pFile;
+ long lSize;
+ size_t result;
+
+ pFile = fopen( FileName, "rb" );
+ if ( pFile == NULL ) Aux_Error( ERROR_INFO, "File error !!\n" );
+
+ // obtain file size
+ fseek( pFile, 0, SEEK_END );
+ lSize = ftell( pFile );
+ rewind( pFile );
+
+ // allocate memory to contain the whole file
+ *buffer = (real*)calloc( lSize, sizeof(double) );
+ if ( *buffer == NULL ) Aux_Error( ERROR_INFO, "Memory error !!\n" );
+
+ // copy the file into the *buffer
+ result = fread( *buffer, 1, lSize, pFile );
+ if ( result != lSize ) Aux_Error( ERROR_INFO, "Reading error !!\n" );
+
+ fclose( pFile );
+
+} // FUNCTION : ReadBinFile
+
+
+
+void randCloud( real **randXYZ, int numClouds )
+{
+
+ *randXYZ = (real*)malloc( 3*numClouds*sizeof(real) );
+
+ for (int i=0; i<3*numClouds; i+=3)
+ {
+ (*randXYZ)[i+0] = (real)rand() / (real)RAND_MAX * amr->BoxSize[0];
+ (*randXYZ)[i+1] = (real)rand() / (real)RAND_MAX * amr->BoxSize[1];
+ (*randXYZ)[i+2] = (real)rand() / (real)RAND_MAX * amr->BoxSize[2];
+
+ if ( (*randXYZ)[i+2] < amr->BoxSize[2]*0.5+3.0 ) i-=3;
+ } // for (int i=0; i<3*numClouds; i+=3)
+
+} // FUNCTION : randCloud
+
+
+
+bool checkInsideClouds( real *randXYZ, int numClouds, real x, real y, real z, real *cloudCenter )
+{
+
+ for (int i=0; i<3*numClouds; i+=3)
+ {
+ const real distance = SQRT( SQR( x-randXYZ[i+0] ) + SQR( y-randXYZ[i+1] ) + SQR( z-randXYZ[i+2] ) );
+
+ if ( distance < radiusCloud )
+ {
+ cloudCenter[0] = randXYZ[i+0];
+ cloudCenter[1] = randXYZ[i+1];
+ cloudCenter[2] = randXYZ[i+2];
+ return true;
+ } // if ( distance < radiusCloud )
+ } // for (int i=0; i<3*numClouds; i+=3)
+
+ return false;
+
+} // FUNCTION : checkInsideClouds
+
+
+
+// Reading table for interpolations in SetGridIC()
+void SetArrayDisk()
+{
+
+ char TableFileName[] = "FermiBubble_IC";
+ ReadBinFile( TableFileName, &buffer );
+
+ int headerSize = (int)buffer[0];
+
+ Header_disk = (real*)malloc( (size_t)headerSize * sizeof(real) );
+
+ memcpy( Header_disk, buffer, (size_t)headerSize * sizeof(real) );
+
+ ParticleMass = Header_disk[8] * Header_disk[9];
+
+ interfaceHeight = Header_disk[17];
+ interfaceHeight *= Const_kpc / UNIT_L;
+
+ const int Nx = (int)Header_disk[22];
+ const int Ny = (int)Header_disk[23];
+ const int Nz = (int)Header_disk[24];
+
+ if ( 5*Nx*Ny*Nz > INT_MAX ) Aux_Error( ERROR_INFO, "integer overflow !!\n" );
+
+ real Lx = Header_disk[12];
+ real Ly = Header_disk[13];
+ real Lz = Header_disk[14];
+ const int numGhost = (int)Header_disk[25];
+
+ const int NX = Nx + 2*numGhost;
+ const int NY = Ny + 2*numGhost;
+ const int NZ = Nz + 2*numGhost;
+
+ if ( Step == 0 )
+ {
+ Rhoo_disk = (real***)calloc_3d_array( (size_t)NX, (size_t)NY, (size_t)NZ, sizeof(real) );
+ VelX_disk = (real***)calloc_3d_array( (size_t)NX, (size_t)NY, (size_t)NZ, sizeof(real) );
+ VelY_disk = (real***)calloc_3d_array( (size_t)NX, (size_t)NY, (size_t)NZ, sizeof(real) );
+ VelZ_disk = (real***)calloc_3d_array( (size_t)NX, (size_t)NY, (size_t)NZ, sizeof(real) );
+ Pres_disk = (real***)calloc_3d_array( (size_t)NX, (size_t)NY, (size_t)NZ, sizeof(real) );
+
+ X_disk = (real*)calloc( (size_t)NX, sizeof(real) );
+ Y_disk = (real*)calloc( (size_t)NY, sizeof(real) );
+ Z_disk = (real*)calloc( (size_t)NZ, sizeof(real) );
+
+ if ( X_disk == NULL ) Aux_Error( ERROR_INFO, "X_disk is NULL at %d, %s !!\n", __LINE__, __FUNCTION__ );
+ if ( Y_disk == NULL ) Aux_Error( ERROR_INFO, "Y_disk is NULL at %d, %s !!\n", __LINE__, __FUNCTION__ );
+ if ( Z_disk == NULL ) Aux_Error( ERROR_INFO, "Z_disk is NULL at %d, %s !!\n", __LINE__, __FUNCTION__ );
+
+ real *Ptr;
+ Ptr = buffer + headerSize;
+
+ for (int c=0; c<5*NX*NY*NZ; c++)
+ {
+ const int cc = c%(NX*NY*NZ);
+ const int i = (cc - cc%(NY*NZ)) / (NY*NZ);
+ const int j = ((cc - cc%NZ) / NZ) % NY;
+ const int k = cc%NZ;
+
+ if ( 0 <= c && c < NX*NY*NZ ) Rhoo_disk[i][j][k] = Ptr[c];
+ if ( NX*NY*NZ <= c && c < 2*NX*NY*NZ ) VelX_disk[i][j][k] = Ptr[c];
+ if ( 2*NX*NY*NZ <= c && c < 3*NX*NY*NZ ) VelY_disk[i][j][k] = Ptr[c];
+ if ( 3*NX*NY*NZ <= c && c < 4*NX*NY*NZ ) VelZ_disk[i][j][k] = Ptr[c];
+ if ( 4*NX*NY*NZ <= c && c < 5*NX*NY*NZ ) Pres_disk[i][j][k] = Ptr[c];
+ } // for (int c=0; c<5*NX*NY*NZ; c++)
+
+ Ptr += 5*NX*NY*NZ;
+ for (int c=0; c INT_MAX ) Aux_Error( ERROR_INFO, "integer overflow !!\n" );
+
+ real Lx = Header_hvc[12];
+ real Ly = Header_hvc[13];
+ real Lz = Header_hvc[14];
+ int numGhost = (int)Header_hvc[25];
+
+ const int NX = Nx+2*numGhost;
+ const int NY = Ny+2*numGhost;
+ const int NZ = Nz+2*numGhost;
+
+ if ( Step == 0 )
+ {
+ Rhoo_hvc = (real***)calloc_3d_array( (size_t)NX, (size_t)NY, (size_t)NZ, sizeof(real) );
+ VelX_hvc = (real***)calloc_3d_array( (size_t)NX, (size_t)NY, (size_t)NZ, sizeof(real) );
+ VelY_hvc = (real***)calloc_3d_array( (size_t)NX, (size_t)NY, (size_t)NZ, sizeof(real) );
+ VelZ_hvc = (real***)calloc_3d_array( (size_t)NX, (size_t)NY, (size_t)NZ, sizeof(real) );
+ Pres_hvc = (real***)calloc_3d_array( (size_t)NX, (size_t)NY, (size_t)NZ, sizeof(real) );
+
+ X_hvc = (real*)calloc( (size_t)NX, sizeof(real) );
+ Y_hvc = (real*)calloc( (size_t)NY, sizeof(real) );
+ Z_hvc = (real*)calloc( (size_t)NZ, sizeof(real) );
+
+ if ( X_hvc == NULL ) Aux_Error( ERROR_INFO, "X_hvc is NULL at %d, %s !!\n", __LINE__, __FUNCTION__ );
+ if ( Y_hvc == NULL ) Aux_Error( ERROR_INFO, "Y_hvc is NULL at %d, %s !!\n", __LINE__, __FUNCTION__ );
+ if ( Z_hvc == NULL ) Aux_Error( ERROR_INFO, "Z_hvc is NULL at %d, %s !!\n", __LINE__, __FUNCTION__ );
+
+ real *Ptr;
+ Ptr = buffer + headerSize;
+
+ for (int c=0; c<5*NX*NY*NZ; c++)
+ {
+ const int cc = c%(NX*NY*NZ);
+ const int i = (cc - cc%(NY*NZ)) / (NY*NZ);
+ const int j = ((cc - cc%NZ) / NZ) % NY;
+ const int k = cc%NZ;
+
+ if ( 0 <= c && c < NX*NY*NZ ) Rhoo_hvc[i][j][k] = Ptr[c];
+ if ( NX*NY*NZ <= c && c < 2*NX*NY*NZ ) VelX_hvc[i][j][k] = Ptr[c];
+ if ( 2*NX*NY*NZ <= c && c < 3*NX*NY*NZ ) VelY_hvc[i][j][k] = Ptr[c];
+ if ( 3*NX*NY*NZ <= c && c < 4*NX*NY*NZ ) VelZ_hvc[i][j][k] = Ptr[c];
+ if ( 4*NX*NY*NZ <= c && c < 5*NX*NY*NZ ) Pres_hvc[i][j][k] = Ptr[c];
+ } // for (int c=0; c<5*NX*NY*NZ; c++)
+
+ Ptr += 5*NX*NY*NZ;
+ for (int c=0; c NX-2 ) Aux_Error( ERROR_INFO, "x=%e is out of range! XYZ[0][0]=%e, XYZ[0][%d]=%e\n", x, XYZ[0][0], NX-1, XYZ[0][NX-1] );
+ if ( Jdx < 0 || Jdx > NY-2 ) Aux_Error( ERROR_INFO, "y=%e is out of range! XYZ[1][1]=%e, XYZ[1][%d]=%e\n", y, XYZ[1][0], NY-1, XYZ[1][NY-1] );
+ if ( Kdx < 0 || Kdx > NZ-2 ) Aux_Error( ERROR_INFO, "z=%e is out of range! XYZ[2][2]=%e, XYZ[2][%d]=%e\n", z, XYZ[2][0], NZ-1, XYZ[2][NZ-1] );
+
+ // TODO: NCOMP_TOTAL to NCOMP_TOTAL_PLUS_MAG
+ real Vertex000[NCOMP_TOTAL]={0.0}, Vertex001[NCOMP_TOTAL]={0.0}, Vertex010[NCOMP_TOTAL]={0.0}, Vertex100[NCOMP_TOTAL]={0.0},
+ Vertex011[NCOMP_TOTAL]={0.0}, Vertex101[NCOMP_TOTAL]={0.0}, Vertex110[NCOMP_TOTAL]={0.0}, Vertex111[NCOMP_TOTAL]={0.0};
+
+ for (int v=0; v<5; v++)
+ {
+ Vertex000[v] = Pri_input[v][Idx ][Jdx ][Kdx ];
+ Vertex001[v] = Pri_input[v][Idx ][Jdx ][Kdx+1];
+ Vertex010[v] = Pri_input[v][Idx ][Jdx+1][Kdx ];
+ Vertex100[v] = Pri_input[v][Idx+1][Jdx ][Kdx ];
+ Vertex011[v] = Pri_input[v][Idx ][Jdx+1][Kdx+1];
+ Vertex101[v] = Pri_input[v][Idx+1][Jdx ][Kdx+1];
+ Vertex110[v] = Pri_input[v][Idx+1][Jdx+1][Kdx ];
+ Vertex111[v] = Pri_input[v][Idx+1][Jdx+1][Kdx+1];
+ }
+
+ bool Unphy = false;
+ Unphy |= Hydro_IsUnphysical( UNPHY_MODE_PRIM, Vertex000, NULL, NULL_REAL, NULL_REAL, NULL_REAL, EoS_DensEint2Pres_CPUPtr, EoS_GuessHTilde_CPUPtr, EoS_HTilde2Temp_CPUPtr, EoS_AuxArray_Flt, EoS_AuxArray_Int, h_EoS_Table, __FILE__, __LINE__, __FUNCTION__, UNPHY_VERBOSE );
+ Unphy |= Hydro_IsUnphysical( UNPHY_MODE_PRIM, Vertex001, NULL, NULL_REAL, NULL_REAL, NULL_REAL, EoS_DensEint2Pres_CPUPtr, EoS_GuessHTilde_CPUPtr, EoS_HTilde2Temp_CPUPtr, EoS_AuxArray_Flt, EoS_AuxArray_Int, h_EoS_Table, __FILE__, __LINE__, __FUNCTION__, UNPHY_VERBOSE );
+ Unphy |= Hydro_IsUnphysical( UNPHY_MODE_PRIM, Vertex010, NULL, NULL_REAL, NULL_REAL, NULL_REAL, EoS_DensEint2Pres_CPUPtr, EoS_GuessHTilde_CPUPtr, EoS_HTilde2Temp_CPUPtr, EoS_AuxArray_Flt, EoS_AuxArray_Int, h_EoS_Table, __FILE__, __LINE__, __FUNCTION__, UNPHY_VERBOSE );
+ Unphy |= Hydro_IsUnphysical( UNPHY_MODE_PRIM, Vertex100, NULL, NULL_REAL, NULL_REAL, NULL_REAL, EoS_DensEint2Pres_CPUPtr, EoS_GuessHTilde_CPUPtr, EoS_HTilde2Temp_CPUPtr, EoS_AuxArray_Flt, EoS_AuxArray_Int, h_EoS_Table, __FILE__, __LINE__, __FUNCTION__, UNPHY_VERBOSE );
+ Unphy |= Hydro_IsUnphysical( UNPHY_MODE_PRIM, Vertex011, NULL, NULL_REAL, NULL_REAL, NULL_REAL, EoS_DensEint2Pres_CPUPtr, EoS_GuessHTilde_CPUPtr, EoS_HTilde2Temp_CPUPtr, EoS_AuxArray_Flt, EoS_AuxArray_Int, h_EoS_Table, __FILE__, __LINE__, __FUNCTION__, UNPHY_VERBOSE );
+ Unphy |= Hydro_IsUnphysical( UNPHY_MODE_PRIM, Vertex110, NULL, NULL_REAL, NULL_REAL, NULL_REAL, EoS_DensEint2Pres_CPUPtr, EoS_GuessHTilde_CPUPtr, EoS_HTilde2Temp_CPUPtr, EoS_AuxArray_Flt, EoS_AuxArray_Int, h_EoS_Table, __FILE__, __LINE__, __FUNCTION__, UNPHY_VERBOSE );
+ Unphy |= Hydro_IsUnphysical( UNPHY_MODE_PRIM, Vertex101, NULL, NULL_REAL, NULL_REAL, NULL_REAL, EoS_DensEint2Pres_CPUPtr, EoS_GuessHTilde_CPUPtr, EoS_HTilde2Temp_CPUPtr, EoS_AuxArray_Flt, EoS_AuxArray_Int, h_EoS_Table, __FILE__, __LINE__, __FUNCTION__, UNPHY_VERBOSE );
+ Unphy |= Hydro_IsUnphysical( UNPHY_MODE_PRIM, Vertex111, NULL, NULL_REAL, NULL_REAL, NULL_REAL, EoS_DensEint2Pres_CPUPtr, EoS_GuessHTilde_CPUPtr, EoS_HTilde2Temp_CPUPtr, EoS_AuxArray_Flt, EoS_AuxArray_Int, h_EoS_Table, __FILE__, __LINE__, __FUNCTION__, UNPHY_VERBOSE );
+
+ if ( Unphy ) Aux_Error( ERROR_INFO, "Idx=%d, Jdx=%d, Kdx=%d, x=%e, y=%e, z=%e is unphysical !!\n", Idx, Jdx, Kdx, x, y, z );
+
+ real xyz000[3] = { XYZ[0][Idx], XYZ[1][Jdx], XYZ[2][Kdx] };
+
+ for (int v=0; v<5; v++)
+ {
+ real FieldAtVertices[8] = { Vertex000[v], Vertex001[v], Vertex010[v], Vertex100[v], Vertex011[v], Vertex101[v], Vertex110[v], Vertex111[v] };
+
+ Pri_output[v] = TrilinearInterpolation( FieldAtVertices, xyz000, dxyz, xyz );
+ }
+
+} // FUNCTION : Interpolation_UM_IC
+
+
+
+#ifdef GRAVITY
+//-------------------------------------------------------------------------------------------------------
+// Function : IsothermalSlab_Pot
+// Description :
+//
+// Note :
+//
+// Parameter :
+//
+// Return :
+//-------------------------------------------------------------------------------------------------------
+real IsothermalSlab_Pot( const real z )
+{
+
+ real Pot, Log;
+
+// 1. isothermal slab
+ Pot = 2.0 * M_PI * NEWTON_G * IsothermalSlab_PeakDens;
+ Pot /= SQR( IsothermalSlab_VelocityDispersion );
+ Pot = log( cosh( z*sqrt(Pot) ) );
+ Pot *= 2.0 * SQR( IsothermalSlab_VelocityDispersion );
+
+// 2. log potential
+ Log = SQR(v_halo) * log( z*z + SQR(distance_h) );
+ Log -= SQR(v_halo) * log( SQR(distance_h) );
+
+ return Pot + Log;
+
+} // FUNCTION : IsothermalSlab_Pot
+#endif
+
+
+
+//-------------------------------------------------------------------------------------------------------
+// Function : SetGridIC
+// Description : Set the problem-specific initial condition on grids
+//
+// Note : 1. This function may also be used to estimate the numerical errors when OPT__OUTPUT_USER is enabled
+// --> In this case, it should provide the analytical solution at the given "Time"
+// 2. This function will be invoked by multiple OpenMP threads when OPENMP is enabled
+// --> Please ensure that everything here is thread-safe
+//
+// Parameter : fluid : Fluid field to be initialized
+// x/y/z : Physical coordinates
+// Time : Physical time
+// lv : Target refinement level
+// AuxArray : Auxiliary array
+//
+// Return : fluid
+//-------------------------------------------------------------------------------------------------------
+void SetGridIC( real fluid[], const double x, const double y, const double z, const double Time,
+ const int lv, double AuxArray[] )
+{
+
+// variables for jet
+ const real xc = x - IsothermalSlab_Center[0];
+ const real yc = y - IsothermalSlab_Center[1];
+ const real zc = z - IsothermalSlab_Center[2];
+ real Pri[NCOMP_TOTAL] = {0.0};
+
+ real ***Pri_disk_input[5] = { Rhoo_disk, VelX_disk, VelY_disk, VelZ_disk, Pres_disk };
+ real *** Pri_hvc_input[5] = { Rhoo_hvc, VelX_hvc, VelY_hvc, VelZ_hvc, Pres_hvc };
+
+ if ( Jet_Ambient == 0 ) // uniform ambient
+ {
+ Pri[0] = (real)Amb_UniformDens;
+ Pri[1] = (real)Amb_UniformVel[0];
+ Pri[2] = (real)Amb_UniformVel[1];
+ Pri[3] = (real)Amb_UniformVel[2];
+ Pri[4] = (real)Amb_UniformTemp * Amb_UniformDens;
+
+ Hydro_Pri2Con( Pri, fluid, false, PassiveNorm_NVar, PassiveNorm_VarIdx, EoS_DensPres2Eint_CPUPtr,
+ EoS_Temp2HTilde_CPUPtr, EoS_HTilde2Temp_CPUPtr, EoS_AuxArray_Flt, EoS_AuxArray_Int,
+ h_EoS_Table, NULL );
+
+ if ( Hydro_IsUnphysical( UNPHY_MODE_PRIM, Pri, NULL, NULL_REAL, NULL_REAL, NULL_REAL, EoS_DensEint2Pres_CPUPtr,
+ EoS_GuessHTilde_CPUPtr, EoS_HTilde2Temp_CPUPtr, EoS_AuxArray_Flt,
+ EoS_AuxArray_Int, h_EoS_Table, __FILE__, __LINE__, __FUNCTION__, UNPHY_VERBOSE ) )
+ Aux_Error( ERROR_INFO, "Unphysical cell at (%e, %e, %e)\n", x, y, z );
+
+# if ( NCOMP_PASSIVE_USER > 0 )
+ fluid[Passive_0000] = fluid[DENS];
+ fluid[Passive_0001] = 0.0;
+ fluid[Passive_0002] = 0.0;
+# endif
+
+# ifdef COSMIC_RAY
+ fluid[CRAY] = Amb_CR_Engy * Jet_SrcGamma;
+# endif
+ }
+ else if ( Jet_Ambient == 2 ) // cold disk in stratified ambient
+ {
+# ifdef GRAVITY
+ if ( fabs(zc) < interfaceHeight )
+ {
+ real *XYZ[3] = { X_disk, Y_disk, Z_disk };
+
+ Interpolation_UM_IC( xc, yc, zc, Pri_disk_input, XYZ, Pri, true );
+
+ if ( Hydro_IsUnphysical( UNPHY_MODE_PRIM, Pri, NULL, NULL_REAL, NULL_REAL, NULL_REAL, EoS_DensEint2Pres_CPUPtr,
+ EoS_GuessHTilde_CPUPtr, EoS_HTilde2Temp_CPUPtr, EoS_AuxArray_Flt,
+ EoS_AuxArray_Int, h_EoS_Table, __FILE__, __LINE__, __FUNCTION__, UNPHY_VERBOSE ) )
+ Aux_Error( ERROR_INFO, "Unphysical cell at (%e, %e %e)\n", x, y, z );
+
+ if ( Pri[4]/Pri[0] > criticalTemp ) Pri[0] = Pri[4] / ambientTemperature;
+
+ Hydro_Pri2Con( Pri, fluid, false, PassiveNorm_NVar, PassiveNorm_VarIdx, EoS_DensPres2Eint_CPUPtr,
+ EoS_Temp2HTilde_CPUPtr, EoS_HTilde2Temp_CPUPtr, EoS_AuxArray_Flt, EoS_AuxArray_Int,
+ h_EoS_Table, NULL );
+
+# if ( NCOMP_PASSIVE_USER > 0 )
+ fluid[Passive_0000] = fluid[DENS];
+ fluid[Passive_0001] = 0.0;
+ fluid[Passive_0002] = 0.0;
+# endif
+
+# ifdef COSMIC_RAY
+ fluid[CRAY] = Amb_CR_Engy * Jet_SrcGamma;
+# endif
+ }
+ else // if ( fabs(zc) < interfaceHeight )
+ {
+ real Dens_gDisk_ambient, PotAtZ0, ambientDens;
+
+ PotAtZ0 = IsothermalSlab_Pot( interfaceHeight );
+
+ Dens_gDisk_ambient = ambientTemperature / gasDiskTemperature;
+ Dens_gDisk_ambient *= exp( PotAtZ0 * (ambientTemperature-gasDiskTemperature) / (ambientTemperature*gasDiskTemperature) );
+
+ if ( Dens_gDisk_ambient > HUGE_NUMBER || Dens_gDisk_ambient < -HUGE_NUMBER )
+ Aux_Error( ERROR_INFO, "(Dens_gDisk_ambient = %e) not in [-HUGE_NUMBER, HUGE_NUMBER] !! %s: %d\n", Dens_gDisk_ambient, __FUNCTION__, __LINE__ );
+
+ real ambientPeakDens = gasDiskPeakDens / Dens_gDisk_ambient;
+
+ if ( fabs(zc) > IsothermalSlab_Truncation )
+ ambientDens = -IsothermalSlab_Pot(IsothermalSlab_Truncation) / ambientTemperature;
+ else
+ ambientDens = -IsothermalSlab_Pot(zc) / ambientTemperature;
+
+ ambientDens = exp(ambientDens);
+ ambientDens *= ambientPeakDens;
+
+ Pri[0] = ambientDens;
+ Pri[1] = 0.0;
+ Pri[2] = 0.0;
+ Pri[3] = 0.0;
+ Pri[4] = ambientDens * ambientTemperature;
+
+ real cloudCenter[3];
+
+ if ( checkInsideClouds( randXYZ, numClouds, x, y, z, cloudCenter ) )
+ {
+ real Pri_hvc_output[5];
+
+ real *XYZ[3] = { X_hvc, Y_hvc, Z_hvc };
+
+ Interpolation_UM_IC( x-cloudCenter[0], y-cloudCenter[1], z-cloudCenter[2],
+ Pri_hvc_input, XYZ, Pri_hvc_output, false );
+
+ if ( Hydro_IsUnphysical( UNPHY_MODE_PRIM, Pri_hvc_output, NULL, NULL_REAL, NULL_REAL, NULL_REAL, EoS_DensEint2Pres_CPUPtr,
+ EoS_GuessHTilde_CPUPtr, EoS_HTilde2Temp_CPUPtr, EoS_AuxArray_Flt,
+ EoS_AuxArray_Int, h_EoS_Table, __FILE__, __LINE__, __FUNCTION__, UNPHY_VERBOSE ) )
+ Aux_Error( ERROR_INFO, "Unphysical cell at (%e, %e %e)\n", x, y, z );
+
+ Pri[0] = Pri_hvc_output[0]*0.05*Const_mp/UNIT_D;
+ Pri[1] = 0.0;
+ Pri[2] = 0.0;
+ Pri[3] = 0.0;
+ Pri[4] = Pri[0];
+ }
+
+ Hydro_Pri2Con( Pri, fluid, false, PassiveNorm_NVar, PassiveNorm_VarIdx, EoS_DensPres2Eint_CPUPtr,
+ EoS_Temp2HTilde_CPUPtr, EoS_HTilde2Temp_CPUPtr, EoS_AuxArray_Flt, EoS_AuxArray_Int,
+ h_EoS_Table, NULL );
+
+ if ( Hydro_IsUnphysical( UNPHY_MODE_PRIM, Pri, NULL, NULL_REAL, NULL_REAL, NULL_REAL, EoS_DensEint2Pres_CPUPtr,
+ EoS_GuessHTilde_CPUPtr, EoS_HTilde2Temp_CPUPtr, EoS_AuxArray_Flt,
+ EoS_AuxArray_Int, h_EoS_Table, __FILE__, __LINE__, __FUNCTION__, UNPHY_VERBOSE ) )
+ Aux_Error( ERROR_INFO, "Unphysical cell at (%e, %e %e)\n", x, y, z );
+
+# if ( NCOMP_PASSIVE_USER > 0 )
+ fluid[Passive_0000] = 0.0;
+ fluid[Passive_0001] = 0.0;
+ fluid[Passive_0002] = 0.0;
+# endif
+
+# ifdef COSMIC_RAY
+ fluid[CRAY] = Amb_CR_Engy * Jet_SrcGamma;
+# endif
+ } // if ( fabs(zc) < interfaceHeight ) ... else ...
+# endif
+ }
+ else if ( Jet_Ambient == 3 ) // cold disk in uniform ambient
+ {
+# ifdef GRAVITY
+ if ( fabs(zc) < interfaceHeight )
+ {
+ real *XYZ[3] = { X_disk, Y_disk, Z_disk };
+
+ Interpolation_UM_IC( xc, yc, zc, Pri_disk_input, XYZ, Pri, true );
+
+ if ( Hydro_IsUnphysical( UNPHY_MODE_PRIM, Pri, NULL, NULL_REAL, NULL_REAL, NULL_REAL, EoS_DensEint2Pres_CPUPtr,
+ EoS_GuessHTilde_CPUPtr, EoS_HTilde2Temp_CPUPtr, EoS_AuxArray_Flt,
+ EoS_AuxArray_Int, h_EoS_Table, __FILE__, __LINE__, __FUNCTION__, UNPHY_VERBOSE ) )
+ Aux_Error( ERROR_INFO, "Unphysical cell at (%e, %e, %e)\n", x, y, z );
+
+ Hydro_Pri2Con( Pri, fluid, false, PassiveNorm_NVar, PassiveNorm_VarIdx, EoS_DensPres2Eint_CPUPtr,
+ EoS_Temp2HTilde_CPUPtr, EoS_HTilde2Temp_CPUPtr, EoS_AuxArray_Flt, EoS_AuxArray_Int,
+ h_EoS_Table, NULL );
+
+# if ( NCOMP_PASSIVE_USER > 0 )
+ fluid[Passive_0000] = 0.0;
+ fluid[Passive_0001] = 0.0;
+ fluid[Passive_0002] = 0.0;
+# endif
+
+# ifdef COSMIC_RAY
+ fluid[CRAY] = Amb_CR_Engy * Jet_SrcGamma;
+# endif
+ }
+ else // if ( fabs(zc) < interfaceHeight )
+ {
+ real Dens_gDisk_ambient, PotAtZ0, ambientDens;
+
+ PotAtZ0 = IsothermalSlab_Pot(interfaceHeight);
+
+ Dens_gDisk_ambient = ambientTemperature / gasDiskTemperature;
+ Dens_gDisk_ambient *= exp( PotAtZ0*(ambientTemperature-gasDiskTemperature)/(ambientTemperature*gasDiskTemperature) );
+
+ if ( Dens_gDisk_ambient > HUGE_NUMBER || Dens_gDisk_ambient < -HUGE_NUMBER )
+ Aux_Error( ERROR_INFO, "(Dens_gDisk_ambient = %e) not in [-HUGE_NUMBER, HUGE_NUMBER] !! %s: %d\n", Dens_gDisk_ambient, __FUNCTION__, __LINE__ );
+
+ real ambientPeakDens = gasDiskPeakDens / Dens_gDisk_ambient;
+
+ ambientDens = -IsothermalSlab_Pot(interfaceHeight) / ambientTemperature;
+ ambientDens = exp(ambientDens);
+ ambientDens *= ambientPeakDens;
+
+ Pri[0] = ambientDens;
+ Pri[1] = 0.0;
+ Pri[2] = 0.0;
+ Pri[3] = 0.0;
+ Pri[4] = ambientDens * ambientTemperature;
+
+ if ( Hydro_IsUnphysical( UNPHY_MODE_PRIM, Pri, NULL, NULL_REAL, NULL_REAL, NULL_REAL, EoS_DensEint2Pres_CPUPtr,
+ EoS_GuessHTilde_CPUPtr, EoS_HTilde2Temp_CPUPtr, EoS_AuxArray_Flt,
+ EoS_AuxArray_Int, h_EoS_Table, __FILE__, __LINE__, __FUNCTION__, UNPHY_VERBOSE ) )
+ Aux_Error( ERROR_INFO, "Unphysical cell at (%e, %e, %e)\n", x, y, z );
+
+ Hydro_Pri2Con( Pri, fluid, false, PassiveNorm_NVar, PassiveNorm_VarIdx, EoS_DensPres2Eint_CPUPtr,
+ EoS_Temp2HTilde_CPUPtr, EoS_HTilde2Temp_CPUPtr, EoS_AuxArray_Flt, EoS_AuxArray_Int,
+ h_EoS_Table, NULL );
+
+# if ( NCOMP_PASSIVE_USER > 0 )
+ fluid[Passive_0000] = 0.0;
+ fluid[Passive_0001] = 0.0;
+ fluid[Passive_0002] = 0.0;
+# endif
+
+# ifdef COSMIC_RAY
+ fluid[CRAY] = Amb_CR_Engy * Jet_SrcGamma;
+# endif
+
+ } // if ( fabs(zc) < interfaceHeight ) ... else ...
+ } // else if ( Jet_Ambient == 3 )
+ else if ( Jet_Ambient == 4 )
+ {
+ real ambientPeakDens = (real)2.842783e-27 / UNIT_D;
+ real ambientDens;
+
+ if ( fabs(zc) > IsothermalSlab_Truncation )
+ ambientDens = -IsothermalSlab_Pot(IsothermalSlab_Truncation)/ambientTemperature;
+ else
+ ambientDens = -IsothermalSlab_Pot(zc)/ambientTemperature;
+
+ ambientDens = ambientPeakDens * exp(ambientDens);
+
+ Pri[0] = ambientDens;
+ Pri[1] = 0.0;
+ Pri[2] = 0.0;
+ Pri[3] = 0.0;
+ Pri[4] = ambientDens*ambientTemperature;
+
+ Hydro_Pri2Con( Pri, fluid, false, PassiveNorm_NVar, PassiveNorm_VarIdx, EoS_DensPres2Eint_CPUPtr,
+ EoS_Temp2HTilde_CPUPtr, EoS_HTilde2Temp_CPUPtr, EoS_AuxArray_Flt, EoS_AuxArray_Int,
+ h_EoS_Table, NULL );
+
+ if ( Hydro_IsUnphysical( UNPHY_MODE_PRIM, Pri, NULL, NULL_REAL, NULL_REAL, NULL_REAL, EoS_DensEint2Pres_CPUPtr,
+ EoS_GuessHTilde_CPUPtr, EoS_HTilde2Temp_CPUPtr, EoS_AuxArray_Flt,
+ EoS_AuxArray_Int, h_EoS_Table, __FILE__, __LINE__, __FUNCTION__, UNPHY_VERBOSE ) )
+ Aux_Error( ERROR_INFO, "Unphysical cell at (%e, %e, %e)\n", x, y, z );
+
+# if ( NCOMP_PASSIVE_USER > 0 )
+ fluid[Passive_0000] = 0.0;
+ fluid[Passive_0001] = 0.0;
+ fluid[Passive_0002] = 0.0;
+# endif
+
+# ifdef COSMIC_RAY
+ fluid[CRAY] = Amb_CR_Engy * Jet_SrcGamma;
+# endif
+
+# endif // #ifdef GRAVITY
+ } // if ( Jet_Ambient == 0 ) ... else if
+
+} // FUNCTION : SetGridIC
+
+
+
+//-------------------------------------------------------------------------------------------------------
+// Function : Flu_ResetByUser_FermiBubble
+// Description : Function to reset the fluid field
+//
+// Note : 1. Invoked by "Flu_ResetByUser_API()" and "Model_Init_ByFunction_AssignData()" using the
+// function pointer "Flu_ResetByUser_Func_Ptr"
+// 2. This function will be invoked when constructing the initial condition
+// (by calling "Model_Init_ByFunction_AssignData()") and after each update
+// (by calling "Flu_ResetByUser_API()")
+// 3. Input "fluid" array stores the original values
+// 4. Even when DUAL_ENERGY is adopted, one does NOT need to set the dual-energy variable here
+// --> It will be set automatically in "Flu_ResetByUser_API()" and "Model_Init_ByFunction_AssignData()"
+// 5. Enabled by the runtime option "OPT__RESET_FLUID"
+//
+// Parameter : fluid : Fluid array storing both the input (origial) and reset values
+// --> Including both active and passive variables
+// x/y/z : Target physical coordinates
+// Time : Target physical time
+// lv : Target refinement level
+// AuxArray : Auxiliary array
+//
+// Return : true : This cell has been reset
+// false : This cell has not been reset
+//
+// =======================================================================================
+/* G A C */
+/* ____________ */
+/* \ | / */
+/* \ E| / z */
+/* \ /|\ / ^ */
+/* \/_|_\/ | */
+/* /\O| /\B | */
+/* / \|/ \ */
+/* / D| \ */
+/* /_____|_____\ */
+/* F */
+// =======================================================================================
+int Flu_ResetByUser_FermiBubble( real fluid[], const double Emag, const double x, const double y, const double z,
+ const double Time, const double dt, const int lv, double AuxArray[] )
+{
+
+ if ( Jet_Fire == 0 ) return false;
+
+ if ( Jet_Duration < Time ) return false;
+
+ if ( !Jet_SphericalSrc )
+ {
+ double xp[3], rp[3];
+ double Prim[NCOMP_TOTAL] = {0.0}, Cons[NCOMP_TOTAL] = {0.0}, Vel[3];
+ real PriReal[NCOMP_TOTAL] = {0.0};
+ double PrecessionAxis_Spherical[3], Omega_t;
+ bool InsideUpperCone, InsideLowerCone;
+ double Jet_SrcVelSmooth;
+
+ Omega_t = Jet_AngularVelocity * Time * M_PI / 180.0;
+
+// shift the coordinate origin to the source center (the point O)
+ xp[0] = x - Jet_Center[0];
+ xp[1] = y - Jet_Center[1];
+ xp[2] = z - Jet_Center[2];
+
+ if ( Jet_PrecessionAxis[0] != 0.0 || Jet_PrecessionAxis[1] != 0.0 || Jet_PrecessionAxis[2] == 0.0 )
+ {
+// get theta, phi for the first rotation
+ Mis_Cartesian2Spherical( Jet_PrecessionAxis, PrecessionAxis_Spherical );
+// rotate coordinate to align z-axis with fixed precession axis
+ CartesianRotate( xp, PrecessionAxis_Spherical[1], PrecessionAxis_Spherical[2], false );
+ }
+
+// rotate coordinate to align z-axis with rotating symmetric axis
+ CartesianRotate( xp, Jet_PrecessionAngle, Omega_t, false );
+
+// determine whether or not the point is inside of source
+ InsideUpperCone = SQR(xp[0]) + SQR(xp[1]) <= SQR( +tan(Jet_HalfOpeningAngle)*xp[2] + Jet_Radius );
+ InsideUpperCone &= 0.0 <= xp[2] && xp[2] <= Jet_HalfHeight;
+
+ InsideLowerCone = SQR(xp[0]) + SQR(xp[1]) <= SQR( -tan(Jet_HalfOpeningAngle)*xp[2] + Jet_Radius );
+ InsideLowerCone &= -Jet_HalfHeight <= xp[2] && xp[2] <= 0.0;
+
+ if ( Jet_HalfOpeningAngle != 0.0 )
+ {
+ InsideUpperCone &= SQR(xp[0]) + SQR(xp[1]) + SQR(xp[2] + Jet_Radius/tan(Jet_HalfOpeningAngle))
+ <= SQR(Jet_HalfHeight+Jet_Radius/tan(Jet_HalfOpeningAngle));
+
+ InsideUpperCone &= 0.0 <= xp[2];
+
+ InsideLowerCone &= SQR(xp[0]) + SQR(xp[1]) + SQR(xp[2] - Jet_Radius/tan(Jet_HalfOpeningAngle))
+ <= SQR(Jet_HalfHeight+Jet_Radius/tan(Jet_HalfOpeningAngle));
+
+ InsideLowerCone &= xp[2] <= 0.0;
+ }
+ else
+ {
+ InsideUpperCone &= 0.0 <= xp[2] && xp[2] <= Jet_HalfHeight;
+ InsideLowerCone &= -Jet_HalfHeight <= xp[2] && xp[2] <= 0.0;
+ } // if ( Jet_HalfOpeningAngle != 0.0 ) ... else ...
+
+// set fluid variable inside source
+ if ( ( InsideUpperCone && ( Jet_Fire == 1 || Jet_Fire == 3 ) ) ||
+ ( InsideLowerCone && ( Jet_Fire == 2 || Jet_Fire == 3 ) ) )
+ {
+ if ( Jet_HalfOpeningAngle == 0.0 )
+ {
+ Vel[0] = 0.0;
+ Vel[1] = 0.0;
+ if ( InsideUpperCone == true ) Vel[2] = +Jet_SrcVel;
+ else Vel[2] = -Jet_SrcVel;
+
+ CartesianRotate( Vel, Jet_PrecessionAngle, Omega_t, true );
+
+ if ( Jet_PrecessionAxis[0] != 0.0 || Jet_PrecessionAxis[1] != 0.0 || Jet_PrecessionAxis[2] == 0.0 )
+ CartesianRotate( Vel, PrecessionAxis_Spherical[1], PrecessionAxis_Spherical[2], true );
+
+ Prim[0] = Jet_SrcDens;
+ Prim[1] = Vel[0];
+ Prim[2] = Vel[1];
+ Prim[3] = Vel[2];
+ Prim[4] = Jet_SrcTemp*Jet_SrcDens;
+ }
+ else // if ( Jet_HalfOpeningAngle == 0.0 )
+ {
+// shift origin to the point D/E
+ if ( InsideUpperCone == true ) xp[2] += Jet_Radius/tan(Jet_HalfOpeningAngle);
+ else xp[2] -= Jet_Radius/tan(Jet_HalfOpeningAngle);
+
+ CartesianRotate( xp, Jet_PrecessionAngle, Omega_t, true );
+
+ Mis_Cartesian2Spherical( xp, rp );
+
+ if ( InsideLowerCone == true ) rp[1] -= M_PI;
+
+// smooth velocity on cross section
+ if ( Jet_SmoothVel ) Jet_SrcVelSmooth = Jet_SrcVel*SQR(cos( 0.5 * M_PI * rp[1] / Jet_HalfOpeningAngle ));
+ else Jet_SrcVelSmooth = Jet_SrcVel;
+
+ if ( Jet_PrecessionAxis[0] != 0.0 || Jet_PrecessionAxis[1] != 0.0 || Jet_PrecessionAxis[2] == 0.0 )
+ CartesianRotate( xp, PrecessionAxis_Spherical[1], PrecessionAxis_Spherical[2], true );
+
+ Mis_Cartesian2Spherical( xp, rp );
+
+ Prim[0] = Jet_SrcDens;
+ Prim[1] = Jet_SrcVelSmooth*sin(rp[1])*cos(rp[2]);
+ Prim[2] = Jet_SrcVelSmooth*sin(rp[1])*sin(rp[2]);
+ Prim[3] = Jet_SrcVelSmooth*cos(rp[1]);
+ Prim[4] = Jet_SrcTemp*Jet_SrcDens;
+ } // if ( Jet_HalfOpeningAngle == 0.0 ) ... else ...
+
+ PriReal[0] = (real)Prim[0];
+ PriReal[1] = (real)Prim[1];
+ PriReal[2] = (real)Prim[2];
+ PriReal[3] = (real)Prim[3];
+ PriReal[4] = (real)Prim[4];
+
+ Hydro_Pri2Con( PriReal, fluid, NULL_BOOL, NULL_INT, NULL, EoS_DensPres2Eint_CPUPtr,
+ EoS_Temp2HTilde_CPUPtr, EoS_HTilde2Temp_CPUPtr,
+ EoS_AuxArray_Flt, EoS_AuxArray_Int, h_EoS_Table, NULL );
+
+# if ( NCOMP_PASSIVE_USER > 0 )
+ fluid[Passive_0000] = 0.0;
+ fluid[Passive_0001] = fluid[DENS];
+ fluid[Passive_0002] = Jet_Src_CR_Engy * Jet_SrcGamma;
+# endif
+
+# ifdef COSMIC_RAY
+ fluid[CRAY] = Jet_Src_CR_Engy * Jet_SrcGamma;
+# endif
+
+ return true;
+ } // if ( ( InsideUpperCone && ( Jet_Fire == 1 || Jet_Fire == 3 ) ) ||
+// ( InsideLowerCone && ( Jet_Fire == 2 || Jet_Fire == 3 ) ) )
+ }
+ else // if ( !Jet_SphericalSrc )
+ {
+ double xp[3], rp[3];
+ double Prim[NCOMP_TOTAL] = {0.0}, Cons[NCOMP_TOTAL] = {0.0}, Vel[3];
+ real PriReal[NCOMP_TOTAL] = {0.0};
+
+// shift the coordinate origin to the source center (the point O)
+ xp[0] = x - Jet_Center[0];
+ xp[1] = y - Jet_Center[1];
+ xp[2] = z - Jet_Center[2];
+
+ //Mis_Cartesian2Spherical(xp, rp);
+
+ const double R = SQRT( SQR(xp[0]) + SQR(xp[1]) + SQR(xp[2]) );
+
+ if ( R < Jet_HalfHeight )
+ {
+ Prim[0] = Jet_SrcDens;
+ Prim[1] = Jet_SrcVel*xp[0]/R;
+ Prim[2] = Jet_SrcVel*xp[1]/R;
+ Prim[3] = Jet_SrcVel*xp[2]/R;
+ Prim[4] = Jet_SrcTemp*Jet_SrcDens;
+
+ PriReal[0] = (real)Prim[0];
+ PriReal[1] = (real)Prim[1];
+ PriReal[2] = (real)Prim[2];
+ PriReal[3] = (real)Prim[3];
+ PriReal[4] = (real)Prim[4];
+
+ Hydro_Pri2Con( PriReal, fluid, NULL_BOOL, NULL_INT, NULL, EoS_DensPres2Eint_CPUPtr,
+ EoS_Temp2HTilde_CPUPtr, EoS_HTilde2Temp_CPUPtr, EoS_AuxArray_Flt,
+ EoS_AuxArray_Int, h_EoS_Table, NULL );
+
+# if ( NCOMP_PASSIVE_USER > 0 )
+ fluid[Passive_0000] = 0.0;
+ fluid[Passive_0001] = fluid[DENS];
+ fluid[Passive_0002] = Jet_Src_CR_Engy * Jet_SrcGamma;
+# endif
+
+# ifdef COSMIC_RAY
+ fluid[CRAY] = Jet_Src_CR_Engy * Jet_SrcGamma;
+# endif
+
+ return true;
+ } // if ( R < Jet_HalfHeight )
+ } // if ( !Jet_SphericalSrc ) ... else ...
+
+ return false;
+
+} // FUNCTION : Flu_ResetByUser_FermiBubble
+
+
+
+//-------------------------------------------------------------------------------------------------------
+// Function : Flag_Region_FermiBubble
+// Description :
+//
+// Note : 1. Invoked by Flag_Check() using the function pointer "Flag_Region_Ptr",
+// which must be set by a test problem initializer
+// 2. Enabled by the runtime option "OPT__FLAG_REGION"
+//
+// Parameter : i,j,k : Indices of the target element in the patch ptr[0][lv][PID]
+// lv : Refinement level of the target patch
+// PID : ID of the target patch
+//
+// Return : "true/false" if the input cell "is/is not" within the region allowed for refinement
+//-------------------------------------------------------------------------------------------------------
+static bool Flag_Region_FermiBubble( const int i, const int j, const int k, const int lv, const int PID )
+{
+
+ if ( Step <= 0 ) return true;
+ const double dh = amr->dh[lv]; // grid size
+ const double Pos[3] = { amr->patch[0][lv][PID]->EdgeL[0] + (i+0.5)*dh, // x,y,z position
+ amr->patch[0][lv][PID]->EdgeL[1] + (j+0.5)*dh,
+ amr->patch[0][lv][PID]->EdgeL[2] + (k+0.5)*dh };
+ const double Center[3] = { 0.5*amr->BoxSize[0],
+ 0.5*amr->BoxSize[1],
+ 0.5*amr->BoxSize[2] };
+ const double dr[3] = { Pos[0]-Center[0]-Jet_CenOffset[0],
+ Pos[1]-Center[1]-Jet_CenOffset[1],
+ Pos[2]-Center[2]-Jet_CenOffset[2] };
+ const double R = sqrt( SQR(dr[0]) + SQR(dr[1]) );
+
+ const bool Flag = R > gasDisk_highResRadius && lv > gasDisk_lowRes_LEVEL && fabs(dr[2]) < 2.0*interfaceHeight;
+
+ if ( Flag ) return false;
+
+ return true;
+
+} // FUNCTION : Flag_Region_FermiBubble
+
+
+
+//-------------------------------------------------------------------------------------------------------
+// Function : Flag_FermiBubble
+// Description : Refine the area near the jet source or the disk
+//
+// Note : 1. Invoked by Flag_Check() using the function pointer "Flag_User_Ptr",
+// which must be set by a test problem initializer
+// 2. Enabled by the runtime option "OPT__FLAG_USER"
+//
+// Parameter : i,j,k : Indices of the target element in the patch ptr[ amr->FluSg[lv] ][lv][PID]
+// lv : Refinement level of the target patch
+// PID : ID of the target patch
+// Threshold : User-provided threshold for the flag operation, which is loaded from the
+// file "Input__Flag_User"
+//
+// Return : "true" if the flag criteria are satisfied
+// "false" if the flag criteria are not satisfied
+//-------------------------------------------------------------------------------------------------------
+bool Flag_FermiBubble( const int i, const int j, const int k, const int lv, const int PID, const double *Threshold )
+{
+
+ const double dh = amr->dh[lv]; // grid size
+ const double Pos[3] = { amr->patch[0][lv][PID]->EdgeL[0] + (i+0.5)*dh, // x,y,z position
+ amr->patch[0][lv][PID]->EdgeL[1] + (j+0.5)*dh,
+ amr->patch[0][lv][PID]->EdgeL[2] + (k+0.5)*dh };
+
+ const double Center[3] = { Jet_Center[0], Jet_Center[1], Jet_Center[2] };
+
+ const double dR[3] = { Pos[0]-Center[0], Pos[1]-Center[1], Pos[2]-Center[2] };
+ const double R = sqrt( SQR(dR[0]) + SQR(dR[1]) + SQR(dR[2]) );
+
+ bool Flag, Src = R <= dh*1.8;
+ if ( Jet_Ambient != 4 )
+ {
+ bool Disk = fabs(dR[2]) <= dh*1.8;
+ if ( lv >= jetSrc_lowRes_LEVEL ) Disk = false;
+ Flag = Src || Disk;
+ }
+ else
+ {
+ Flag = Src;
+ }
+ return Flag;
+
+} // FUNCTION : Flag_FermiBubble
+
+
+
+//-------------------------------------------------------------------------------------------------------
+// Function : CartesianRotate
+// Description :
+//
+// Note :
+//
+// Parameter :
+//
+// Return :
+//-------------------------------------------------------------------------------------------------------
+void CartesianRotate( double x[], double theta, double phi, bool inverse )
+{
+
+ double xp[3];
+
+ if ( inverse )
+ {
+ xp[0] = - sin(phi)*x[0] - cos(theta)*cos(phi)*x[1] + sin(theta)*cos(phi)*x[2];
+ xp[1] = + cos(phi)*x[0] - cos(theta)*sin(phi)*x[1] + sin(theta)*sin(phi)*x[2];
+ xp[2] = + sin(theta)* x[1] + cos(theta)* x[2];
+ } else
+ {
+ xp[0] = - sin(phi)*x[0] + cos(phi)*x[1];
+ xp[1] = - cos(theta)*cos(phi)*x[0] - cos(theta)*sin(phi)*x[1] + sin(theta)* x[2];
+ xp[2] = + sin(theta)*cos(phi)*x[0] + sin(theta)*sin(phi)*x[1] + cos(theta)* x[2];
+ }
+
+ for (int i=0; i<3; i++) x[i] = xp[i];
+
+} // FUNCTION : CartesianRotate
+
+
+
+//-------------------------------------------------------------------------------------------------------
+// Function : AddNewField_FermiBubble
+// Description : Add the problem-specific fields
+//
+// Note : 1. Ref: https://github.com/gamer-project/gamer/wiki/Adding-New-Simulations#v-add-problem-specific-grid-fields-and-particle-attributes
+// 2. Invoke AddField() for each of the problem-specific field:
+// --> Field label sent to AddField() will be used as the output name of the field
+// --> Field index returned by AddField() can be used to access the field data
+// 3. Pre-declared field indices are put in Field.h
+//
+// Parameter : None
+//
+// Return : None
+//-------------------------------------------------------------------------------------------------------
+void AddNewField_FermiBubble()
+{
+
+# if ( NCOMP_PASSIVE_USER > 0 )
+ if ( Passive_0000 == 5 ) Passive_0000 = AddField( "Passive_0000", FIXUP_FLUX_YES, FIXUP_REST_YES, NORMALIZE_NO, INTERP_FRAC_NO );
+ if ( Passive_0001 == 6 ) Passive_0001 = AddField( "Passive_0001", FIXUP_FLUX_YES, FIXUP_REST_YES, NORMALIZE_NO, INTERP_FRAC_NO );
+ if ( Passive_0002 == 7 ) Passive_0002 = AddField( "Passive_0002", FIXUP_FLUX_YES, FIXUP_REST_YES, NORMALIZE_NO, INTERP_FRAC_NO );
+# endif
+
+} // FUNCTION : AddNewField_Jet
+#endif // #if ( MODEL == HYDRO )
+
+
+
+//-------------------------------------------------------------------------------------------------------
+// Function : Init_TestProb_Hydro_FermiBubble
+// Description : Test problem initializer
+//
+// Note : None
+//
+// Parameter : None
+//
+// Return : None
+//-------------------------------------------------------------------------------------------------------
+void Init_TestProb_Hydro_FermiBubble()
+{
+
+ if ( MPI_Rank == 0 ) Aux_Message( stdout, "%s ...\n", __FUNCTION__ );
+
+
+// validate the compilation flags and runtime parameters
+ Validate();
+
+
+# if ( MODEL == HYDRO )
+// set the problem-specific runtime parameters
+ SetParameter();
+
+
+ Init_Function_User_Ptr = SetGridIC;
+ Flag_User_Ptr = Flag_FermiBubble;
+ Flag_Region_Ptr = Flag_Region_FermiBubble;
+ Flu_ResetByUser_Func_Ptr = Flu_ResetByUser_FermiBubble;
+# ifdef GRAVITY
+ Init_ExtPot_Ptr = Init_ExtPot_IsothermalSlab;
+# endif
+
+# if ( NCOMP_PASSIVE_USER > 0 )
+ Init_Field_User_Ptr = AddNewField_FermiBubble;
+# endif
+# endif // #if ( MODEL == HYDRO )
+
+
+ if ( MPI_Rank == 0 ) Aux_Message( stdout, "%s ... done\n", __FUNCTION__ );
+
+} // FUNCTION : Init_TestProb_Hydro_FermiBubble
diff --git a/src/TestProblem/Hydro/FermiBubble/Interpolation_UM_IC.cpp b/src/TestProblem/Hydro/FermiBubble/Interpolation_UM_IC.cpp
new file mode 100644
index 0000000000..4027cb17ba
--- /dev/null
+++ b/src/TestProblem/Hydro/FermiBubble/Interpolation_UM_IC.cpp
@@ -0,0 +1,127 @@
+#include
+#include
+#include
+#include "Typedef.h"
+
+
+
+//-------------------------------------------------------------------------------------------------------
+// Function : calloc_3d_array
+// Description : Allocate 3D array with size nt*nr*nc
+//
+// Parameter : nt/r/c : size of array
+// size : size of allocated type
+//
+// Return : c
+//-------------------------------------------------------------------------------------------------------
+void ***calloc_3d_array( size_t nt, size_t nr, size_t nc, size_t size )
+{
+
+ void ***array;
+ size_t i, j;
+
+ if ( (array = (void ***)calloc( nt, sizeof(void **) )) == NULL )
+ {
+ printf( "[calloc_3d] failed to allocate memory for %d 1st-pointers\n", (int)nt );
+ return NULL;
+ }
+
+ if ( (array[0] = (void **)calloc( nt*nr, sizeof(void *) )) == NULL )
+ {
+ printf( "[calloc_3d] failed to allocate memory for %d 2nd-pointers\n", (int)(nt*nr) );
+ free( (void *) array );
+ return NULL;
+ }
+
+ for (i=1; i Coordinate order: [ (0,0,0), (0,0,1), (0,1,0), (1,0,0), (0,1,1), (1,0,1), (1,1,0), (1,1,1) ]
+// xyz000 : the left coordinates of x/y/z (0,0,0)
+// dxyz : the width/distance of x/y/z (dx,dy,dz)
+// xyz : the coordinates of x/y/z need to be interpolation
+//
+// Return : c
+//-------------------------------------------------------------------------------------------------------
+real TrilinearInterpolation( real *FieldAtVertices, real *xyz000, real *dxyz, real *xyz )
+{
+
+ real c = 0.0, weight[8];
+
+// weight of the left / right
+ const real w_xR = (xyz[0]-xyz000[0]) / dxyz[0];
+ const real w_yR = (xyz[1]-xyz000[1]) / dxyz[1];
+ const real w_zR = (xyz[2]-xyz000[2]) / dxyz[2];
+
+ const real w_xL = 1.0 - w_xR;
+ const real w_yL = 1.0 - w_yR;
+ const real w_zL = 1.0 - w_zR;
+
+// total weight
+ weight[0] = w_xL * w_yL * w_zL;
+ weight[1] = w_xL * w_yL * w_zR;
+ weight[2] = w_xL * w_yR * w_zL;
+ weight[3] = w_xR * w_yL * w_zL;
+ weight[4] = w_xL * w_yR * w_zR;
+ weight[5] = w_xR * w_yL * w_zR;
+ weight[6] = w_xR * w_yR * w_zL;
+ weight[7] = w_xR * w_yR * w_zR;
+
+ for (int i=0; i<8; i++) c += FieldAtVertices[i] * weight[i];
+
+ return c;
+
+} // FUNCTION : TrilinearInterpolation
diff --git a/src/configure.py b/src/configure.py
index 673bfd6604..57f1523ccc 100755
--- a/src/configure.py
+++ b/src/configure.py
@@ -422,22 +422,22 @@ def load_arguments():
default=False,
depend={"model":"HYDRO"},
constraint={ True:{"flu_scheme":["MHM", "MHM_RP", "CTU"], "flux":["ROE", "HLLE", "HLLD"]},
- False:{"flux":["EXACT", "ROE", "HLLE", "HLLC"]} },
+ False:{"flux":["EXACT", "ROE", "HLLE", "HLLC"]} },
help="Magnetohydrodynamics.\n"
)
parser.add_argument( "--srhd", type=str2bool, metavar="BOOLEAN", gamer_name="SRHD",
default=False,
depend={"model":"HYDRO"},
- constraint={ True:{"flu_scheme":["MHM", "MHM_RP"], "flux":["HLLE", "HLLC"], "eos":["TAUBMATHEWS"], "dual":[NONE_STR], "mhd":False, "gravity":False} },
+ constraint={ True:{"flu_scheme":["MHM", "MHM_RP"], "flux":["HLLE", "HLLC"], "eos":["TAUBMATHEWS"], "dual":[NONE_STR], "mhd":False} },
help="Special Relativistic Hydrodynamics.\n"
)
parser.add_argument( "--cosmic_ray", type=str2bool, metavar="BOOLEAN", gamer_name="COSMIC_RAY",
default=False,
depend={"model":"HYDRO"},
- constraint={ True:{"dual":[NONE_STR], "eos":"COSMIC_RAY", "comoving":False} },
- help="Enable cosmic rays. Must use <--eos=COSMIC_RAY>.\n"
+ constraint={ True:{"dual":[NONE_STR], "eos":["COSMIC_RAY", "TAUBMATHEWS"], "comoving":False} },
+ help="Enable cosmic rays. Hydro/MHD: <--eos=COSMIC_RAY> or SRHD: <--eos=TABMATHEWS>.\n"
)
parser.add_argument( "--eos", type=str, metavar="TYPE", gamer_name="EOS",
@@ -790,8 +790,9 @@ def set_conditional_defaults( args ):
args["flux"] = "HLLD" if args["mhd"] else "HLLC"
if args["eos"] is None:
- if args["cosmic_ray"]: args["eos"] = "COSMIC_RAY"
- elif args["srhd"] : args["eos"] = "TAUBMATHEWS"
+ # The order does matter in this if
+ if args["srhd"] : args["eos"] = "TAUBMATHEWS"
+ elif args["cosmic_ray"]: args["eos"] = "COSMIC_RAY"
else : args["eos"] = "GAMMA"
if args["barotropic"] is None:
diff --git a/tool/analysis/PerspectiveProjection/Makefile b/tool/analysis/PerspectiveProjection/Makefile
new file mode 100644
index 0000000000..3fe9bc4115
--- /dev/null
+++ b/tool/analysis/PerspectiveProjection/Makefile
@@ -0,0 +1,100 @@
+#MACRO += -DXRAY_ROSAT
+#MACRO += -DXRAY_EROSITA
+#MACRO += -DHADRONIC_GAMMARAY
+#MACRO += -DLEPTONIC_GAMMARAY
+#MACRO += -DSYNCHROTRON
+
+
+#MACRO += -DNUM_THREADS=16
+
+#MACRO += -DDEBUG
+
+#MACRO += -DFLOAT8
+
+
+CC := mpic++
+
+CFLAGS = -Wall -O3 -std=c++11
+DEBUG_FLAG = -g
+OPENMPFLAG = -fopenmp
+
+
+##########
+# TW3
+##########
+#INC := -I/work/tseng0929/software/hdf5/hdf5-gnu-7.3/include \
+# -I/work/tseng0929/software/cfitsio/include \
+# -I/work/tseng0929/software/CCfits/include \
+# -I/work/tseng0929/software/gsl/2.7.1/include
+#
+#LIB := -L/work/tseng0929/software/hdf5/hdf5-gnu-7.3/lib -lhdf5 \
+# -L/work/tseng0929/software/cfitsio/lib -lcfitsio \
+# -L/work/tseng0929/software/CCfits/lib -lCCfits \
+# -L/work/tseng0929/software/gsl/2.7.1/lib -lgsl -lgslcblas
+
+###########
+# Eureka
+###########
+#export PATH=/software/hdf5/default/bin:$PATH
+#export PATH=/software/gsl/default/bin:$PATH
+#export PATH=/software/gcc/9.3.0/bin:$PATH
+#export PATH=/software/openmpi/4.1.1-ucx_mt-gnu-9.3.0/bin:$PATH
+#
+#export LD_LIBRARY_PATH=/software/hdf5/default/lib:$LD_LIBRARY_PATH
+#export LD_LIBRARY_PATH=/projectW/tseng/opt/cfitsio/cfitsio-4.0.0/lib:$LD_LIBRARY_PATH
+#export LD_LIBRARY_PATH=/projectW/tseng/opt/CCfits/CCfits-2.6/lib:$LD_LIBRARY_PATH
+#export LD_LIBRARY_PATH=/projectW/tseng/opt/HEALPix/HEALPix-3.80/lib:$LD_LIBRARY_PATH
+#export LD_LIBRARY_PATH=/software/intel/oneapi/compiler/2021.1.1/linux/compiler/lib/intel64_lin:$LD_LIBRARY_PATH
+#export LD_LIBRARY_PATH=/projectW/tseng/opt/openmpi-gcc-9.3.0/lib:$LD_LIBRARY_PATH
+#export LD_LIBRARY_PATH=/software/gsl/default/lib:$LD_LIBRARY_PATH
+
+INC := -I /software/hdf5/default/include \
+ -I /projectW/tseng/opt/cfitsio/cfitsio-4.0.0/include \
+ -I /projectW/tseng/opt/CCfits/CCfits-2.6/include \
+ -I /software/gsl/default/include
+
+LIB := -L /software/hdf5/default/lib -lhdf5 \
+ -L /projectW/tseng/opt/cfitsio/cfitsio-4.0.0/lib -lcfitsio \
+ -L /projectW/tseng/opt/CCfits/CCfits-2.6/lib -lCCfits \
+ -L /software/gsl/default/lib -lgsl -lgslcblas
+
+EXECUTABLE := Project
+
+
+
+## source files
+CC_FILE = Main.c Array.c BinarySearch.c Cooling.c Interpolation.c ReadTable.c \
+ Make_Projection.c Make_Slice.c XRayMap.c HaloDensFun.c Leptonic.c Hadronic.c \
+ Synchrotron.c Utilities.c
+SRC_PATH = src
+
+
+## object files
+OBJ_FILE = $(CC_FILE:.c=.o)
+OBJ_PATH = objective
+OBJ = $(patsubst %,$(OBJ_PATH)/%,$(OBJ_FILE))
+
+
+# header files
+HEADER_FILE = Macro.h Prototypes.h General.h
+INC_PATH = include
+HEADER = $(patsubst %,$(INC_PATH)/%,$(HEADER_FILE))
+
+ALL_FLAG = $(DEBUG_FLAG) $(OPENMPFLAG) $(LIB) $(INC) $(CFLAGS) $(MACRO)
+
+# Compiling
+$(OBJ_PATH)/%.o : $(SRC_PATH)/%.c $(HEADER)
+ $(CC) $(ALL_FLAG) -o $@ -c $<
+
+# Linking
+$(EXECUTABLE): $(OBJ)
+ @echo "Linking ..."
+ $(CC) -o $@ $^ $(LIB) $(OPENMPFLAG)
+ @echo "Done!"
+ @mv $(EXECUTABLE) bin/
+
+.PHONY:
+clean:
+ @rm -rf ${OBJ_PATH}/*.o
+ @rm -rf ${EXECUTABLE}
+ @rm -rf *.o rm ${EXECUTABLE}
diff --git a/tool/analysis/PerspectiveProjection/README b/tool/analysis/PerspectiveProjection/README
new file mode 100644
index 0000000000..32790534c1
--- /dev/null
+++ b/tool/analysis/PerspectiveProjection/README
@@ -0,0 +1,32 @@
+# Packages
+eureka:
+export PATH=/software/hdf5/default/bin:$PATH
+export PATH=/software/gsl/default/bin:$PATH
+export PATH=/software/gcc/9.3.0/bin:$PATH
+export PATH=/software/openmpi/4.1.1-ucx_mt-gnu-9.3.0/bin:$PATH
+
+export LD_LIBRARY_PATH=/software/hdf5/default/lib:$LD_LIBRARY_PATH
+export LD_LIBRARY_PATH=/projectW/tseng/opt/cfitsio/cfitsio-4.0.0/lib:$LD_LIBRARY_PATH
+export LD_LIBRARY_PATH=/projectW/tseng/opt/CCfits/CCfits-2.6/lib:$LD_LIBRARY_PATH
+export LD_LIBRARY_PATH=/projectW/tseng/opt/HEALPix/HEALPix-3.80/lib:$LD_LIBRARY_PATH
+export LD_LIBRARY_PATH=/software/intel/oneapi/compiler/2021.1.1/linux/compiler/lib/intel64_lin:$LD_LIBRARY_PATH
+export LD_LIBRARY_PATH=/projectW/tseng/opt/openmpi-gcc-9.3.0/lib:$LD_LIBRARY_PATH
+export LD_LIBRARY_PATH=/software/gsl/default/lib:$LD_LIBRARY_PATH
+
+
+Tw3:
+module load compiler/intel/2020u4 OpenMPI/4.0.5
+
+# Compile
+make clean
+make
+
+
+# Example
+sh compile.sh
+ # usage
+ ./Project_XRay FRB_Data_000035.h5
+
+ ./Project_Synchrotron FRB_Data_000035.h5 $OBSERVED_FREQ_HZ 1.1e6 $CRIndex >& log
+
+ ./Project FRB_Data_000035.h5 $OBSERVED_ENGY_EV 1.1e6 $CRIndex R12/robitaille_DL07_PAHISMMix.dat >& log
diff --git a/tool/analysis/PerspectiveProjection/bin/.empty b/tool/analysis/PerspectiveProjection/bin/.empty
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/tool/analysis/PerspectiveProjection/compile.sh b/tool/analysis/PerspectiveProjection/compile.sh
new file mode 100644
index 0000000000..d7dc8b497e
--- /dev/null
+++ b/tool/analysis/PerspectiveProjection/compile.sh
@@ -0,0 +1,41 @@
+set -e
+NUM_THREADS=32 # number of threads of openMP
+MY_HOST=`hostname`
+MY_CLUSTER=${MY_HOST::(-2)}
+
+
+
+#====================================================================================================
+# Paths
+#====================================================================================================
+# eureka
+if [[ ${MY_CLUSTER} != "eureka" ]]; then echo "ERROR : These paths are for eureka only!!"; exit 1; fi
+export PATH=/software/hdf5/default/bin:$PATH
+export PATH=/software/gsl/default/bin:$PATH
+export PATH=/software/gcc/9.3.0/bin:$PATH
+export PATH=/software/openmpi/4.1.1-ucx_mt-gnu-9.3.0/bin:$PATH
+
+export LD_LIBRARY_PATH=/software/hdf5/default/lib:$LD_LIBRARY_PATH
+export LD_LIBRARY_PATH=/projectW/tseng/opt/cfitsio/cfitsio-4.0.0/lib:$LD_LIBRARY_PATH
+export LD_LIBRARY_PATH=/projectW/tseng/opt/CCfits/CCfits-2.6/lib:$LD_LIBRARY_PATH
+export LD_LIBRARY_PATH=/projectW/tseng/opt/HEALPix/HEALPix-3.80/lib:$LD_LIBRARY_PATH
+export LD_LIBRARY_PATH=/software/intel/oneapi/compiler/2021.1.1/linux/compiler/lib/intel64_lin:$LD_LIBRARY_PATH
+export LD_LIBRARY_PATH=/projectW/tseng/opt/openmpi-gcc-9.3.0/lib:$LD_LIBRARY_PATH
+export LD_LIBRARY_PATH=/software/gsl/default/lib:$LD_LIBRARY_PATH
+
+# TW3
+# module load compiler/intel/2020u4 OpenMPI/4.0.5
+
+
+
+#====================================================================================================
+# Compile
+#====================================================================================================
+make clean && make CFLAGS+=-DNUM_THREADS=${NUM_THREADS} CFLAGS+=-DXRAY_EROSITA
+mv bin/Project bin/Project_XRay
+
+make clean && make CFLAGS+=-DNUM_THREADS=${NUM_THREADS} CFLAGS+=-DSYNCHROTRON
+mv bin/Project bin/Project_Synchrotron
+
+make clean && make CFLAGS+=-DNUM_THREADS=${NUM_THREADS} CFLAGS+=-DLEPTONIC_GAMMARAY
+mv bin/Project bin/Project_GammaRay
diff --git a/tool/analysis/PerspectiveProjection/include/General.h b/tool/analysis/PerspectiveProjection/include/General.h
new file mode 100644
index 0000000000..710fe5a38e
--- /dev/null
+++ b/tool/analysis/PerspectiveProjection/include/General.h
@@ -0,0 +1,14 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+// parallel header
+#include
+#include
+
+#include "Macro.h"
+#include "Prototypes.h"
diff --git a/tool/analysis/PerspectiveProjection/include/Macro.h b/tool/analysis/PerspectiveProjection/include/Macro.h
new file mode 100644
index 0000000000..ba38bf5d2b
--- /dev/null
+++ b/tool/analysis/PerspectiveProjection/include/Macro.h
@@ -0,0 +1,135 @@
+#ifndef __MACRO__
+#define __MACRO__
+
+#include
+
+#ifndef NUM_THREADS
+# define NUM_THREADS 1
+#endif
+
+// single/double precision
+#ifdef FLOAT8
+typedef double real;
+#else
+typedef float real;
+#endif
+
+//#ifdef FLOAT8
+//# error: This code does not support double precision
+//#endif
+
+# define ELECTRON_MASS ( 9.10938356e-28 ) // g
+# define ELETRON_CHARGE ( 4.80320425e-10 ) // statcoulombs (cm**1.5 g**0.5 s**-1)
+# define SPEED_OF_LIGHT ( 29979245800.0 ) // cm/s
+# define MASS_PROTON_GRAM ( 1.6726219e-24 ) // g
+# define PROTON_MASS_ENERGY ( 9.3827208816e8 ) // eV
+# define BOLTZMANN_CONST_ERG ( 1.38064852e-16 ) // erg/K
+# define BOLTZMANN_CONST_EV ( 8.617333262145e-5 ) // eV/K
+# define REDUCED_PLANCK_EV ( 6.582119569e-16 ) // eV s
+# define PLANCK_EV ( 4.135667696e-15 ) // eV s
+# define ELECTRON_MASS_ENERGY ( 510998.95 ) // eV
+# define THOMSON_CROSS_SECTION ( 6.652e-25 ) // cm**2
+# define ERG2EV ( 6.2415e11 ) // 1 erg = 6.2415e11 eV
+# define EV2ERG ( 1.6021789633902107e-12 ) // 1 / ERG2EV
+# define MILLIBARN_2_CM2 ( 1e-27 ) // 1 mb = 1e-27 cm**-2
+
+#ifdef FLOAT8
+# define FABS( a ) fabs( a )
+# define SQRT( a ) sqrt( a )
+# define SIN( a ) sin( a )
+# define COS( a ) cos( a )
+# define TAN( a ) tan( a )
+# define POW( a , b ) pow( a , b )
+# define LOG( a ) log( a )
+# define EXP( a ) expf( a )
+# define MPI_MYREAL MPI_DOUBLE
+# define EPSILON DBL_EPSILON
+#else
+# define FABS( a ) fabsf( a )
+# define SQRT( a ) sqrtf( a )
+# define SIN( a ) sinf( a )
+# define COS( a ) cosf( a )
+# define TAN( a ) tanf( a )
+# define POW( a , b ) powf( a , b )
+# define LOG( a ) logf( a )
+# define EXP( a ) exp( a )
+# define MPI_MYREAL MPI_FLOAT
+# define EPSILON FLT_EPSILON
+#endif // #ifdef FLOAT8
+
+#define PERSPECTIVE_PROJECTION
+//#define SLICE
+
+
+// distance between the sun and GC
+#define R_SUN (-8.0) // kpc
+
+// beta model
+#define PEAK_DENS ( 0.11 ) // cm**-3 // 0.46-0.35
+#define CORE_RADIUS ( 0.08 ) // kpc // 0.35-0.27
+#define BETA ( 0.71 )
+
+// Galacic halo
+#define HALO_RADIUS ( 250.0 ) // kpc
+#define HALO_TEMP ( 1.0e6 ) // Kelvien
+#define MU_ELECTRON ( 1.67 ) // molecular weight per electron in the Galactic halo
+
+// Handy macro
+#define STRING_LENGTH 50
+#define MAX(a, b) ( ( (a) > (b) ) ? (a) : (b) )
+#define SQR( x ) ( ( x ) * ( x ) )
+#define CUBE( x ) ( ( x ) * ( x ) * ( x ) )
+#define SIGN( a ) ( ( (a) < (real)0.0 ) ? (real)-1.0 : (real)+1.0 )
+
+typedef struct Keys
+{
+ int UpperBound_ll;
+ int UpperBound_bb;
+# ifdef PERSPECTIVE_PROJECTION
+ real b_max;
+ real b_min;
+ real l_max;
+ real l_min;
+ real dt;
+# endif
+ char AMRDataName[STRING_LENGTH];
+ char FRBDataName[STRING_LENGTH];
+ uint64_t EpochTimeStampInFRBData;
+ int numAzimuthalAngle;
+# if ( defined HADRONIC_GAMMARAY || defined LEPTONIC_GAMMARAY )
+ real scatteredEnergy;
+# elif ( defined SYNCHROTRON )
+ real observedFreq;
+# endif
+# if ( defined HADRONIC_GAMMARAY || defined LEPTONIC_GAMMARAY || defined SYNCHROTRON )
+ real gamma_max;
+ real gamma_min;
+ real spectral_index;
+# endif
+# ifdef SLICE
+ char CuttingPlane[STRING_LENGTH];
+# endif
+} Keys_t;
+
+
+#define STR( x ) #x
+#define SHOW_MACRO( x ) STR( x )
+
+// print function
+#define MASTER_PRINT( fmt, ... ) \
+ { \
+ if ( MPI_Rank == 0 ) \
+ { \
+ printf( fmt, ##__VA_ARGS__ ); \
+ fflush( stdout ); \
+ } \
+ }
+
+#define ERROR_EXIT( code, fmt, ... ) \
+ { \
+ printf( fmt, ##__VA_ARGS__ ); \
+ exit( code ); \
+ }
+
+
+#endif // #ifndef __MACRO__
diff --git a/tool/analysis/PerspectiveProjection/include/Prototypes.h b/tool/analysis/PerspectiveProjection/include/Prototypes.h
new file mode 100644
index 0000000000..d57065f36a
--- /dev/null
+++ b/tool/analysis/PerspectiveProjection/include/Prototypes.h
@@ -0,0 +1,69 @@
+#ifndef __PROTOTYPES__
+#define __PROTOTYPES__
+
+#include "Macro.h"
+
+
+// global variables
+extern int MPI_Rank, NRank, RootRank;
+
+
+
+// utilities
+void checkInt32Overflow( int32_t a, int32_t b, int operation, int line );
+bool checkNAN( real input, const char FunctionName[], const int line );
+bool checkMinus ( real input, const char FunctionName[], const int line );
+bool checkmemoryContiguous( real *Ptr, int sizeOf, int Length );
+void OutputBinary( void *Ptr, int size, int count, char Name [] );
+
+// x_ray_map.c
+void X_ray_3D( real ***Density, real ***Temperature, real ***Emissivity,
+ int numCellXYZ[], int numRow, real *tempTable, real *lambdaTable );
+real Xray_emissivity( real dens, real lambda );
+
+void Synchrotron_3D( real ***CREngy, real spectral_index, real ***Synchrotron, real observedFreq,
+ int numCellXYZ[], real BoxSize[], real gamma_max, real gamma_min );
+void Leptonic_3D( real scatteredEnergy, real ***CREngy, real gamma_max, real gamma_min, real spectral_index,
+ real ***Emissivity, int numCellXYZ[], real BoxSize[], char **argv );
+void Hadronic_3D( real scatteredEnergy, real ***Density, real ***CREngy,
+ real gamma_max, real gamma_min, real spectral_index,
+ real ***Emissivity, int numCellXYZ[], real BoxSize[] );
+real GammaRay_Hadronic_Emissivity( real number_density, real CREngy, real scatteredEnergy,
+ real gamma_max, real gamma_min, real spectral_index );
+void Index2Position( const int Index[], double Pos[], const int numCellXYZ[], const real BoxSize[] );
+
+// make_projection.c
+real PerspectiveProject( real b, real l, real dt, real *XYZ[], int numCellXYZ[], real dxyz[], real azimuthalAngle,
+ real BoxSize[], real ***TargetQuantity, int numRow, real *tempTable, real *lambdaTable );
+void rotationMatrix( real x, real y, real z, real *xp, real *yp, real *zp, real angle );
+
+// make_slice.c
+real Slice( int Idx1, int Idx2, int numCellXYZ[], real ***TargetQuantity, char CuttingPlane[] );
+
+// Interpolation.c
+real TrilinearInterpolation( real *FieldAtVertices, real *xyz000, real *dxyz, real *xyz );
+real LinearInterpolation( real x1, real y1, real x2, real y2, real x );
+
+// Array.c
+void ***calloc_3d_array( size_t nt, size_t nr, size_t nc, size_t size );
+void free_3d_array( void ***array );
+void **calloc_2d_array( size_t nr, size_t nc, size_t size );
+void free_2d_array( void *array );
+
+// BinarySearch.c
+int BinarySearch( const real Array[], int Min, int Max, const real Key );
+int double_BinarySearch( const double Array[], int Min, int Max, const real Key );
+
+// cooling.c
+real Lambda( real Temp, int numRow, real *tempTable, real *lambdaTable );
+
+// read_table.c
+int CountNumLine( FILE *table );
+void ReadTable( int TargetColumn, int numRow, FILE *table, float *columnArray );
+
+// haloFun.c
+real haloDensFun( const real x, const real y, const real z );
+real xRayHalo( const real x, const real y, const real z, int numRow, real *tempTable, real *lambdaTable );
+
+
+#endif // #ifndef __PROTOTYPES__
diff --git a/tool/analysis/PerspectiveProjection/src/Array.c b/tool/analysis/PerspectiveProjection/src/Array.c
new file mode 100644
index 0000000000..f0c2bd760c
--- /dev/null
+++ b/tool/analysis/PerspectiveProjection/src/Array.c
@@ -0,0 +1,130 @@
+#include "../include/General.h"
+
+
+
+//-------------------------------------------------------------------------------------------------------
+// Function : calloc_3d_array
+// Description :
+// Note :
+// Parameter :
+// Return :
+//-------------------------------------------------------------------------------------------------------
+void ***calloc_3d_array( size_t nt, size_t nr, size_t nc, size_t size )
+{
+ void ***array;
+ size_t i, j;
+
+ if ( (array = (void ***)calloc( nt, sizeof(void **) )) == NULL )
+ {
+ printf( "[calloc_3d] failed to allocate memory for %d 1st-pointers\n", (int)nt );
+ return NULL;
+ }
+
+ if ( (array[0] = (void **)calloc( nt * nr, sizeof(void *) )) == NULL )
+ {
+ printf( "[calloc_3d] failed to allocate memory for %d 2nd-pointers\n", (int)(nt*nr) );
+ free ( (void *)array );
+ return NULL;
+ }
+
+ for (i=1; i return Min-1
+// if Key >= Array[Max] (maximum value) --> return Max
+// 4. We must have "Max > Min" in the input parameters
+// 5. Overloaded with different types
+// 6. Explicit template instantiation is put in the end of this file
+//
+// Parameter : Array : Sorted look-up array (in ascending numerical order)
+// Min : Minimum array index for searching
+// Max : Maximum array index for searching
+// Key : Target value to search for
+//
+// Return : Idx : if target is found
+// Min-1 : if Key < Array[Min]
+// Max : if Key >= Array[Max]
+//-------------------------------------------------------------------------------------------------------
+int BinarySearch( const real Array[], int Min, int Max, const real Key )
+{
+
+// check whether the input key lies outside the target range
+ if ( Key < Array[Min] ) return Min-1;
+ if ( Key >= Array[Max] ) return Max;
+
+
+// binary search
+ int Idx = -2;
+
+ while ( ( Idx=(Min+Max)/2 ) != Min )
+ {
+ if ( Array[Idx] > Key ) Max = Idx;
+ else Min = Idx;
+ }
+
+ return Idx;
+
+} // FUNCTION : Mis_BinarySearch_Real
+
+
+
+int double_BinarySearch( const double Array[], int Min, int Max, const real Key )
+{
+
+// check whether the input key lies outside the target range
+ if ( Key < Array[Min] ) return Min-1;
+ if ( Key >= Array[Max] ) return Max;
+
+
+// binary search
+ int Idx = -2;
+
+ while ( ( Idx=(Min+Max)/2 ) != Min )
+ {
+ if ( Array[Idx] > Key ) Max = Idx;
+ else Min = Idx;
+ }
+
+ return Idx;
+
+} // FUNCTION : Mis_BinarySearch_Real
diff --git a/tool/analysis/PerspectiveProjection/src/Cooling.c b/tool/analysis/PerspectiveProjection/src/Cooling.c
new file mode 100644
index 0000000000..26d770c21d
--- /dev/null
+++ b/tool/analysis/PerspectiveProjection/src/Cooling.c
@@ -0,0 +1,36 @@
+#include
+#include
+
+#include "../include/General.h"
+
+
+
+//-------------------------------------------------------------------------------------------------------
+// Function : Lambda
+// Description :
+// Note :
+// Parameter :
+// Return : lambdaAtTemp
+//-------------------------------------------------------------------------------------------------------
+real Lambda( real Temp, int numRow, real *tempTable, real *lambdaTable )
+{
+ real lambdaAtTemp;
+ const int Idx = BinarySearch( tempTable, 0, numRow-1, Temp );
+
+// The target value is not between the sorted array
+ if ( Idx < 0 || Idx >= numRow-2 ) lambdaAtTemp = 0.0;
+ else lambdaAtTemp = LinearInterpolation( tempTable[Idx ], lambdaTable[Idx ],
+ tempTable[Idx+1], lambdaTable[Idx+1], Temp );
+
+// gsl_interp_accel *acc = gsl_interp_accel_alloc ();
+// gsl_spline *spline = gsl_spline_alloc (gsl_interp_cspline, numRow);
+//
+// gsl_spline_init (spline, tempTable, lambdaTable, numRow);
+//
+// lambdaAtTemp = gsl_spline_eval (spline, Temp, acc);
+//
+// gsl_spline_free (spline);
+// gsl_interp_accel_free (acc);
+
+ return lambdaAtTemp;
+} // FUNCTION : Lambda
diff --git a/tool/analysis/PerspectiveProjection/src/Hadronic.c b/tool/analysis/PerspectiveProjection/src/Hadronic.c
new file mode 100644
index 0000000000..0ea413682c
--- /dev/null
+++ b/tool/analysis/PerspectiveProjection/src/Hadronic.c
@@ -0,0 +1,272 @@
+#include
+#include
+
+#include "../include/General.h"
+
+
+
+struct Parameters_t
+{
+ double spectral_index;
+ double scatteredEnergy;
+};
+
+real crossSection( real Ep );
+real F_gamma ( real x, real Ep );
+double Integral( double lb, double ub, struct Parameters_t *parameters );
+
+
+//-------------------------------------------------------------------------------------------------------
+// Function : Hadronic_3D
+// Description :
+// Note :
+// Parameter : scatteredEnergy : scattered photon energy (eV)
+// Density : gas density (g/cm**3)
+// CREngy : CR energy density (eV/cm**3)
+// gamma_max : the maximum Lorentz factor of CR
+// gamma_min : the minimum Lorentz factor of CR
+// spectral_index : the spectral index of CR
+// numCellXYZ : number of cells on each side of box
+// BoxSize : simulation box size
+//
+// Return : Emissivity : gamma-ray emissivity (eV/cm**3/s)
+//-------------------------------------------------------------------------------------------------------
+void Hadronic_3D( real scatteredEnergy, real ***Density, real ***CREngy,
+ real gamma_max, real gamma_min, real spectral_index,
+ real ***Emissivity, int numCellXYZ[], real BoxSize[] )
+{
+// 1. define the counts_i
+ int counts_i[NRank];
+ for (int c=0; cspectral_index;
+ double scatteredEnergy = ((struct Parameters_t*)params)->scatteredEnergy;
+ double Ep = scatteredEnergy / x;
+
+ return crossSection(Ep) * F_gamma(x, Ep) * pow(x, spectral_index);
+} // FUNCTION : gsl_integrand
+
+
+
+//-------------------------------------------------------------------------------------------------------
+// Function : Integral
+// Description :
+// Note :
+// Parameter :
+// Return :
+//-------------------------------------------------------------------------------------------------------
+double Integral( double lb, double ub, struct Parameters_t *parameters )
+{
+ gsl_integration_workspace *w = gsl_integration_workspace_alloc(1000);
+ gsl_function F;
+
+ F.function = &gsl_integrand;
+ F.params = (void*)parameters;
+
+ double result, error;
+ int status, key;
+ key = 1;
+
+ gsl_set_error_handler_off();
+
+ do
+ {
+ //if ( key>1 ) printf("Unstable! status: %d key: %d (%s: %d)\n", status, key, __FUNCTION__, __LINE__);
+
+ status = gsl_integration_qag( &F, lb, ub, 0.0, 1e-7, 1000, key, w, &result, &error );
+
+ key++;
+
+ } while( status && key<=6 );
+
+ if ( status ) ERROR_EXIT( 0, "ERROR : status: %d (%s: %d) !!\n", status, __FUNCTION__, __LINE__ );
+
+ gsl_integration_workspace_free( w );
+
+ return result;
+} // FUNCTION : Integral
+
+
+
+//-------------------------------------------------------------------------------------------------------
+// Function : F_gamma
+// Description :
+// Note :
+// Parameter : x : the ratio of scattered photon energy to proton energy, scatteredEnergy/Ep.
+// Ep : the energy of proton (eV)
+//
+// Return : distribution function : Eq. (58) in Kelner (2006) (dimensionless)
+//-------------------------------------------------------------------------------------------------------
+real F_gamma( real x, real Ep )
+{
+ real L, F_gamma;
+ real B, beta, k;
+ real factor;
+
+ L = log( Ep / 1e12 ); // L = ln( Ep/1 TeV )
+ B = 1.3 + 0.14*L + 0.011*L*L;
+ beta = 1.0 / ( 1.79 + 0.11*L + 0.008*L*L );
+ k = 1.0 / ( 0.801 + 0.049*L + 0.014*L*L );
+
+ real pow_x_beta = pow( x, beta );
+ real one_minus_pow_x_beta = 1.0 - pow_x_beta;
+ real k_times_pow_x_beta = k*pow_x_beta;
+ real ln_x = log(x);
+ real beta_times_pow_x_beta = beta * pow_x_beta;
+
+ factor = one_minus_pow_x_beta;
+ factor /= 1.0 + k_times_pow_x_beta * one_minus_pow_x_beta;
+ factor *= factor;
+ factor *= factor;
+
+ F_gamma = 1.0/ln_x;
+ F_gamma -= 4.0*beta_times_pow_x_beta/one_minus_pow_x_beta;
+ F_gamma -= 4.0*k*beta_times_pow_x_beta*(1.0-2.0*pow_x_beta)/(1.0+k_times_pow_x_beta*one_minus_pow_x_beta);
+ F_gamma *= B*ln_x*factor/x;
+
+ return F_gamma;
+} // FUNCTION : F_gamma
+
+
+
+//-------------------------------------------------------------------------------------------------------
+// Function : F_gamma
+// Description :
+// Note :
+// Parameter : Ep: the energy of incident proton (eV)
+//
+// Return : total cross section of pp-collision (cm^-2), Eq.(73) in Kelner (2006)
+//-------------------------------------------------------------------------------------------------------
+real crossSection( real Ep )
+{
+ real Eth, factor, L;
+
+ Eth = 1.22e9; // Eq(79), the threshold energy (eV) of production of neutral \pi meson
+
+ factor = Eth / Ep;
+ factor *= factor;
+ factor *= factor;
+ factor = 1.0 - factor;
+ factor *= factor;
+
+ L = log( Ep / 1e12 ); // L = ln( Ep/1 TeV )
+
+ return ( 34.3 + 1.88*L + 0.25*L*L ) * factor * MILLIBARN_2_CM2;
+} // FUNCTION : crossSection
diff --git a/tool/analysis/PerspectiveProjection/src/HaloDensFun.c b/tool/analysis/PerspectiveProjection/src/HaloDensFun.c
new file mode 100644
index 0000000000..71d1d953e6
--- /dev/null
+++ b/tool/analysis/PerspectiveProjection/src/HaloDensFun.c
@@ -0,0 +1,35 @@
+#include "../include/General.h"
+
+
+
+//-------------------------------------------------------------------------------------------------------
+// Function : haloDensFun
+// Description :
+// Note :
+// Parameter :
+// Return :
+//-------------------------------------------------------------------------------------------------------
+real haloDensFun( const real x, const real y, const real z )
+{
+ const real r = SQRT( x*x + y*y + z*z );
+
+ return PEAK_DENS * POW( (real)1.0 + SQR( r / CORE_RADIUS ), -(real)1.5 * BETA );
+} // FUNCTION : haloDensFun
+
+
+
+//-------------------------------------------------------------------------------------------------------
+// Function : xRayHalo
+// Description :
+// Note :
+// Parameter :
+// Return :
+//-------------------------------------------------------------------------------------------------------
+real xRayHalo( const real x, const real y, const real z, int numRow, real *tempTable, real *lambdaTable )
+{
+ real dens = haloDensFun(x, y, z) * MASS_PROTON_GRAM * MU_ELECTRON;
+
+ real lambda = Lambda( HALO_TEMP, numRow, tempTable, lambdaTable );
+
+ return Xray_emissivity( dens, lambda );
+} // FUNCTION : xRayHalo
diff --git a/tool/analysis/PerspectiveProjection/src/Interpolation.c b/tool/analysis/PerspectiveProjection/src/Interpolation.c
new file mode 100644
index 0000000000..59ee02e1fb
--- /dev/null
+++ b/tool/analysis/PerspectiveProjection/src/Interpolation.c
@@ -0,0 +1,68 @@
+#include "../include/General.h"
+
+
+
+//-------------------------------------------------------------------------------------------------------
+// Function : TrilinearInterpolation
+// Description :
+// Note :
+// Parameter :
+// Return :
+//-------------------------------------------------------------------------------------------------------
+real TrilinearInterpolation( real *FieldAtVertices, real *xyz000, real *dxyz, real *xyz )
+{
+ real x1, y1, z1, x0, y0, z0, xd, yd, zd, x, y, z;
+ real c000, c001, c010, c100, c011, c101, c110, c111, c00, c01, c10, c11, c0, c1, c;
+
+ x0 = xyz000[0];
+ y0 = xyz000[1];
+ z0 = xyz000[2];
+
+ x1 = xyz000[0] + dxyz[0];
+ y1 = xyz000[1] + dxyz[1];
+ z1 = xyz000[2] + dxyz[2];
+
+ x = xyz[0];
+ y = xyz[1];
+ z = xyz[2];
+
+ c000 = FieldAtVertices[0];
+ c001 = FieldAtVertices[1];
+ c010 = FieldAtVertices[2];
+ c100 = FieldAtVertices[3];
+ c011 = FieldAtVertices[4];
+ c101 = FieldAtVertices[5];
+ c110 = FieldAtVertices[6];
+ c111 = FieldAtVertices[7];
+
+ xd = (x-x0)/(x1-x0);
+ yd = (y-y0)/(y1-y0);
+ zd = (z-z0)/(z1-z0);
+
+ c00 = c000*(1.0-xd) + c100*xd;
+ c01 = c001*(1.0-xd) + c101*xd;
+ c10 = c010*(1.0-xd) + c110*xd;
+ c11 = c011*(1.0-xd) + c111*xd;
+
+ c0 = c00*(1.0-yd) + c10*yd;
+ c1 = c01*(1.0-yd) + c11*yd;
+
+ c = c0*(1.0-zd) + c1*zd;
+
+ return c;
+} // FUNCTION : TrilinearInterpolation
+
+
+
+//-------------------------------------------------------------------------------------------------------
+// Function : LinearInterpolation
+// Description :
+// Note :
+// Parameter :
+// Return :
+//-------------------------------------------------------------------------------------------------------
+real LinearInterpolation( real x1, real y1, real x2, real y2, real x )
+{
+ real dx = x2 - x1;
+ return ( y2 * ( x - x1 ) + y1 * ( x2 - x ) ) / dx;
+} //FUNCTION : LinearInterpolation
diff --git a/tool/analysis/PerspectiveProjection/src/Leptonic.c b/tool/analysis/PerspectiveProjection/src/Leptonic.c
new file mode 100644
index 0000000000..23150250d9
--- /dev/null
+++ b/tool/analysis/PerspectiveProjection/src/Leptonic.c
@@ -0,0 +1,856 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "../include/General.h"
+
+
+#define kTwoPi (2.0*M_PI)
+#define kSpeedOfLight_SI 299792458.0 // m/s
+#define kConvertRadiansToDegrees (180.0/M_PI)
+
+
+
+struct Parameters_t
+{
+ double *energyBins;
+ double *numDensPerEnergyArray;
+ int energyBinsSize;
+
+ double p_plus_2;
+ double scatteredEnergy;
+ double photonEnergy;
+ double gamma_max;
+ double (*fun_ptr)( double, double, double );
+};
+
+real GammaRay_Leptonic_Emissivity( double energyBins[], double numDensPerEnergyArray[],
+ int energyBinsSize, real CREngy, real scatteredEnergy,
+ real gamma_max, real gamma_min, real spectral_index );
+
+void ReadInModel( std::string, std::vector&, std::vector&, std::vector&,
+ std::vector&, std::vector>>& );
+void Interpolate3DRadiationField( std::array, std::valarray&, const std::vector&, const std::vector&,
+ const std::vector&, const std::vector>>& );
+
+double gsl_integrandInner( double x, void *params );
+double gsl_integrandOuter( double epsilon, void *params );
+double integralInner( double lb, double ub, void *params );
+double integralOuter( double lb, double ub, struct Parameters_t *parameters );
+double cmb_differetial_number_density( double frequency );
+double distributionFun( const double gamma, const double photonEnergy, const double scatteredEnergy );
+double Bisection( double xmin, double xmax, double (*fun_ptr)(double,double,double), double tolerence, void *params );
+void FindLbUb( double xmin, double xmax, double (*fun)(double,double,double), void *params, double *lb, double *ub );
+
+// gsl arrays (reuse the memory for better performance)
+static gsl_integration_workspace *workspaces_inner[NUM_THREADS];
+static gsl_integration_workspace *workspaces_outer[NUM_THREADS];
+static gsl_interp_accel *accels [NUM_THREADS];
+static gsl_spline *splines [NUM_THREADS];
+
+
+
+//-------------------------------------------------------------------------------------------------------
+// Function : initialize_gsl_arrays
+// Description : Allocate gsl arrays
+//
+// Note : Each MPI_Rank and each openMP thread should have their own array.
+//
+// Parameter : energyBinSize :
+//
+// Return : none
+//-------------------------------------------------------------------------------------------------------
+static void initialize_gsl_arrays( const int energyBinsSize )
+{
+ for (int w=0; w r, phi, z, frequency;
+
+ std::vector>> numdenlist;
+
+ MASTER_PRINT( "Reading files ...\n" );
+
+ ReadInModel( indexfile, r, phi, z, frequency, numdenlist );
+
+ MPI_Barrier( MPI_COMM_WORLD );
+
+ MASTER_PRINT( "Reading files ... done\n" );
+
+ int energyBinsSize = frequency.size();
+
+ double *energyBins = (double*)malloc( energyBinsSize*sizeof(double) );
+
+ for (int i=0; i nd(0., frequency.size());
+ std::array Pos_arr = { Pos[0], Pos[1], Pos[2]};
+ Interpolate3DRadiationField( Pos_arr, nd, r, phi, z, numdenlist );
+
+ //// compute energy difference
+ //double diff_nd[energyBinsSize];
+
+ //for (int ndi=2; ndi& rarray,
+ std::vector& phiarray,
+ std::vector& zarray,
+ std::vector& frequency,
+ std::vector>>& numden)
+{
+//const double kPlanck_SI = 6.62606876e-34; // J s
+//const double e_SI = 1.602176462e-19; // positron charge in coulomb
+ const std::string prefix = name.substr( 0, name.find_last_of("/")+1 );
+
+ std::ifstream rf( name.c_str() );
+
+ std::string line;
+ std::getline( rf, line );
+ const auto pos = line.find_first_of( "!!3D" );
+
+ if ( pos > line.size() )
+ {
+ rf.close();
+ ERROR_EXIT( -1, "ERROR : Reading 3D header from file !!\n" );
+ }
+
+ line.clear();
+ std::getline( rf, line );
+ std::vector totals;
+ { std::stringstream ss( line ); for (;;) { uint64_t val; ss >> val; if ( ss.fail() ) break; totals.push_back( val ); } }
+ auto volumeelements( totals[0] ), wlbins( totals[1] ), numfilters( totals[2] ), numrvals( totals[3] ), numphivals( totals[4] ), numzvals( totals[5] );
+
+ line.clear();
+ std::getline(rf, line);
+ std::vector rdata;
+ { std::stringstream ss( line ); for (;;) { double val; ss >> val; if ( ss.fail() ) break; rdata.push_back( val ); } }
+ rarray.resize( rdata.size() );
+ std::copy( rdata.begin(), rdata.end(), &rarray[0] );
+
+ line.clear();
+ std::getline(rf, line);
+ std::vector phidata;
+ { std::stringstream ss( line ); for (;;) { double val; ss >> val; if ( ss.fail() ) break; phidata.push_back( val ); } }
+ phiarray.resize(phidata.size());
+ std::copy( phidata.begin(), phidata.end(), &phiarray[0] );
+
+ line.clear();
+ std::getline( rf, line );
+ std::vector zdata;
+ { std::stringstream ss( line ); for (;;) { double val; ss >> val; if ( ss.fail() ) break; zdata.push_back( val ); } }
+ zarray.resize( zdata.size() );
+ std::copy( zdata.begin(), zdata.end(), &zarray[0] );
+
+ double luminosity, dustmass;
+ rf >> luminosity >> dustmass;
+
+ uint64_t stellarcomponents(0);
+ rf >> stellarcomponents;
+
+ for (auto ic(0); ic> cname >> lum;
+ }
+
+ uint64_t geometry;
+ std::array regiondata = {{ 0. }};
+ rf >> geometry;
+ rf >> regiondata[0] >> regiondata[1] >> regiondata[2] >> regiondata[3] >> regiondata[4] >> regiondata[5];
+
+ for (auto iv(0); iv> idx;
+ rf >> x >> y >> z;
+ rf >> dx >> dy >> dz;
+
+ std::string filterfilename, filtercountfilename, directfilename, scatteredfilename, transientfilename, thermalfilename, totalfilename, opticalfilename, infraredfilename, filterfluxfilename, fluxfilename;
+ rf >> filterfilename;
+ rf >> filtercountfilename;
+ rf >> directfilename;
+ rf >> scatteredfilename;
+ rf >> transientfilename;
+ rf >> thermalfilename;
+ rf >> totalfilename;
+ rf >> opticalfilename;
+ rf >> infraredfilename;
+ rf >> filterfluxfilename;
+ rf >> fluxfilename;
+
+ const std::string fullfluxfilename = prefix + fluxfilename;
+
+ CCfits::FITS fluxfile( fullfluxfilename, CCfits::Read );
+ CCfits::ExtHDU& table = fluxfile.currentExtension();
+ std::vector wl, total, direct, scattered, transient, thermal;
+ table.column(1).read( wl, 1, wlbins );
+ table.column(2).read( total, 1, wlbins );
+ table.column(3).read( direct, 1, wlbins );
+ table.column(4).read( scattered, 1, wlbins );
+ table.column(5).read( transient, 1, wlbins );
+ table.column(6).read( thermal, 1, wlbins );
+
+ if ( frequency.size() == 0 )
+ {
+ frequency.resize( wlbins, 0. );
+ for (size_t iwl(0); iwl( 0., wlbins ) );
+ std::copy( total.rbegin(), total.rend(), &(*numden.back())[0] ); // Copy is reverse order to make ascending with frequency
+ //for (auto i(0); i < frequency.size(); ++i)
+ // (*numden.back())[i] *= std::pow(kPlanck_SI/e_SI*frequency[i], -2.); // Convert to eV^-1 cm^-3
+ } // for (auto iv(0); iv coord,
+ std::valarray& numberdensity,
+ const std::vector& rarr,
+ const std::vector& phiarr,
+ const std::vector& zarr,
+ const std::vector>>& numdenlist )
+{
+
+ const auto r2( coord[0]*coord[0] + coord[1]*coord[1] ), z( coord[2] );
+ auto phi = std::atan2( coord[1], coord[0] );
+ phi = ( phi < 0. ? phi + kTwoPi : (phi >= kTwoPi ? phi - kTwoPi : phi) ) * kConvertRadiansToDegrees;
+
+ const auto r = ( r2 > rarr[0]*rarr[0] ? std::sqrt(r2) : rarr[0] );
+
+ if ( r >= rarr[rarr.size()-1] || z < zarr[0] || z >= zarr[zarr.size()-1] ) return;
+
+ const auto ridx = std::upper_bound( rarr.begin(), rarr.end(), r ) - rarr.begin();
+ const auto dr = (rarr[ridx] - rarr[ridx-1]);
+ assert( dr > 0. );
+
+ const auto rcoeff = ( rarr[ridx] - r ) / dr;
+ const auto phiidx = std::upper_bound( phiarr.begin(), phiarr.end(), phi ) - phiarr.begin();
+ const auto dphi = ( phi < phiarr[phiarr.size()-1] ) ? ( phiarr[phiidx] - phiarr[phiidx-1] ) : ( 360. - phiarr[phiarr.size()-1] );
+ assert( dphi > 0. );
+
+ const auto phicoeff = ( phi < phiarr[phiarr.size()-1] && dphi > 0. ) ? ( phiarr[phiidx] - phi ) / dphi : ( 360. - phi ) / dphi;
+ const auto zidx = std::upper_bound( zarr.begin(), zarr.end(), z ) - zarr.begin();
+ const auto dz = ( zarr[zidx] - zarr[zidx-1] );
+ assert( dz > 0. );
+
+ const auto zcoeff = ( zarr[zidx] - z ) / dz;
+
+ size_t idx1, idx2, idx3, idx4, idx5, idx6, idx7, idx8;
+
+ // This deals with the wrap around in phi coordinate between the last bin in the grid and 360./0. deg
+ if ( phi > phiarr[phiarr.size()-1] )
+ {
+ idx1 = (zidx-1)*rarr.size()*phiarr.size() + (ridx-1)*phiarr.size() + (phiidx-1);
+ idx2 = (zidx-1)*rarr.size()*phiarr.size() + (ridx-1)*phiarr.size() + (0 );
+ idx3 = (zidx-1)*rarr.size()*phiarr.size() + (ridx )*phiarr.size() + (phiidx-1);
+ idx4 = (zidx-1)*rarr.size()*phiarr.size() + (ridx )*phiarr.size() + (0 );
+ idx5 = (zidx )*rarr.size()*phiarr.size() + (ridx-1)*phiarr.size() + (phiidx-1);
+ idx6 = (zidx )*rarr.size()*phiarr.size() + (ridx-1)*phiarr.size() + (0 );
+ idx7 = (zidx )*rarr.size()*phiarr.size() + (ridx )*phiarr.size() + (phiidx-1);
+ idx8 = (zidx )*rarr.size()*phiarr.size() + (ridx )*phiarr.size() + (0 );
+ }
+ else
+ {
+ idx1 = (zidx-1)*rarr.size()*phiarr.size() + (ridx-1)*phiarr.size() + (phiidx-1);
+ idx2 = (zidx-1)*rarr.size()*phiarr.size() + (ridx-1)*phiarr.size() + (phiidx );
+ idx3 = (zidx-1)*rarr.size()*phiarr.size() + (ridx )*phiarr.size() + (phiidx-1);
+ idx4 = (zidx-1)*rarr.size()*phiarr.size() + (ridx )*phiarr.size() + (phiidx );
+ idx5 = (zidx )*rarr.size()*phiarr.size() + (ridx-1)*phiarr.size() + (phiidx-1);
+ idx6 = (zidx )*rarr.size()*phiarr.size() + (ridx-1)*phiarr.size() + (phiidx );
+ idx7 = (zidx )*rarr.size()*phiarr.size() + (ridx )*phiarr.size() + (phiidx-1);
+ idx8 = (zidx )*rarr.size()*phiarr.size() + (ridx )*phiarr.size() + (phiidx );
+ } // if ( phi > phiarr[phiarr.size()-1] ) ... else ...
+
+ const auto& numberdensity1 = *numdenlist[idx1];
+ const auto& numberdensity2 = *numdenlist[idx2];
+ const auto& numberdensity3 = *numdenlist[idx3];
+ const auto& numberdensity4 = *numdenlist[idx4];
+ const auto& numberdensity5 = *numdenlist[idx5];
+ const auto& numberdensity6 = *numdenlist[idx6];
+ const auto& numberdensity7 = *numdenlist[idx7];
+ const auto& numberdensity8 = *numdenlist[idx8];
+
+ numberdensity = numberdensity1 * zcoeff * rcoeff * phicoeff +
+ numberdensity2 * zcoeff * rcoeff * ( 1. - phicoeff ) +
+ numberdensity3 * zcoeff * ( 1. - rcoeff ) * phicoeff +
+ numberdensity4 * zcoeff * ( 1. - rcoeff ) * ( 1. - phicoeff ) +
+ numberdensity5 * ( 1. - zcoeff )* rcoeff * phicoeff +
+ numberdensity6 * ( 1. - zcoeff )* rcoeff * ( 1. - phicoeff ) +
+ numberdensity7 * ( 1. - zcoeff )* ( 1. - rcoeff ) * phicoeff +
+ numberdensity8 * ( 1. - zcoeff )* ( 1. - rcoeff ) * ( 1. - phicoeff );
+} // FUNCTION : Interpolate3DRadiationField
+
+
+
+//-------------------------------------------------------------------------------------------------------
+// Function : numDensAtEnergy
+// Description :
+// Note :
+// Parameter : photonEnergy : incident photon energy (eV)
+// energyBins : 2.734140e+10*PLANCK_EV - 3.287159e+15*PLANCK_EV (eV)
+// numDensPerEnergyArray : differential number density ( 1 eV**-1 cm**-3 )
+// energyBinsSize : 128
+//
+// Return : numDensAtEnergy : number density of ISRF at a specific energy ( 1 eV**-1 cm**-3 )
+//-------------------------------------------------------------------------------------------------------
+double numDensAtEnergy( double photonEnergy, double energyBins[], double numDensPerEnergyArray[], int energyBinsSize )
+{
+ const int tid = omp_get_thread_num();
+ double numDensAtEnergy;
+
+ gsl_interp_accel *acc = accels [tid];
+ gsl_spline *spline = splines[tid];
+
+ gsl_spline_init( spline, energyBins, numDensPerEnergyArray, energyBinsSize );
+
+ numDensAtEnergy = gsl_spline_eval( spline, photonEnergy, acc );
+
+ return numDensAtEnergy;
+} // FUNCTION : numDensAtEnergy
+
+
+
+//-------------------------------------------------------------------------------------------------------
+// Function : calloc_3d_array
+// Description :
+// Note :
+// Parameter : gamma : the Lorentz factor of CR
+// photonEnergy : the energy of incident photon (eV)
+// scatteredEnergy : the energy of scattered photon (eV)
+//
+// Return : f : distribution function (dimensionless)
+//-------------------------------------------------------------------------------------------------------
+double distributionFun( const double gamma, const double photonEnergy, const double scatteredEnergy )
+{
+ const double cr_engy = gamma * ELECTRON_MASS_ENERGY;
+ const double Gamma = 4.0 * photonEnergy * gamma / ELECTRON_MASS_ENERGY;
+ const double q = 1.0 / ( cr_engy/scatteredEnergy - 1.0 ) / Gamma;
+ const double Gamma_q = Gamma * q;
+ const double one_minus_q = 1.0 - q;
+
+ double f;
+
+ f = 2.0*q*log(q);
+ f += ( 1.0+2.0*q )*one_minus_q;
+ f += 0.5 * Gamma_q * Gamma_q * one_minus_q / ( 1.0 + Gamma_q );
+
+ return f;
+} // FUNCTION : distributionFun
+
+
+
+//-------------------------------------------------------------------------------------------------------
+// Function : gsl_integrandInner
+// Description :
+// Note :
+// Parameter : gamma : the Lorentz factor of CRe
+// Return :
+//-------------------------------------------------------------------------------------------------------
+double gsl_integrandInner( double gamma, void *params )
+{
+ const double p_plus_2 = ((struct Parameters_t*)params)->p_plus_2;
+ const double scatteredEnergy = ((struct Parameters_t*)params)->scatteredEnergy;
+ const double photonEnergy = ((struct Parameters_t*)params)->photonEnergy;
+
+ return pow( gamma, -p_plus_2 ) * distributionFun( gamma, photonEnergy, scatteredEnergy );
+} // FUNCTION : gsl_integrandInner
+
+
+
+//-------------------------------------------------------------------------------------------------------
+// Function : integralInner
+// Description :
+// Note :
+// Parameter : ub: gamma_max
+// lb: gamma_min
+// Return :
+//-------------------------------------------------------------------------------------------------------
+double integralInner( double lb, double ub, void *params )
+{
+ const int tid = omp_get_thread_num();
+ gsl_integration_workspace *w = workspaces_inner[tid];
+ gsl_function F;
+
+ F.function = &gsl_integrandInner;
+ F.params = params;
+
+ double result, error;
+ int status, key;
+
+ key = 1;
+ gsl_set_error_handler_off();
+
+ do
+ {
+ //if ( key>1 ) printf( "Unstable! status: %d key: %d (%s: %d)\n", status, key, __FUNCTION__, __LINE__ );
+ status = gsl_integration_qag( &F, lb, ub, 0.0, 1e-7, 1000, key, w, &result, &error );
+ key++;
+ } while ( status && key <= 6 );
+
+ if ( status )
+ {
+# ifdef DEBUG
+ printf( "scatteredEnergy = %20.16e\n", ((struct Parameters_t*)params)->scatteredEnergy );
+ printf( "gamma_max = %20.16e\n", ((struct Parameters_t*)params)->gamma_max );
+ printf( "photonEnergy = %20.16e\n", ((struct Parameters_t*)params)->photonEnergy );
+ printf( "lb=%20.16e, ub=%20.16e\n", lb, ub );
+# endif
+ ERROR_EXIT( 0, "ERROR : status: %d (%s: %d) !!\n", status, __FUNCTION__, __LINE__ );
+ } // if ( status )
+ return result;
+} // FUNCTION : integralInner
+
+
+
+//-------------------------------------------------------------------------------------------------------
+// Function : gsl_integrandOuter
+// Description :
+// Note :
+// Parameter :
+// Return :
+//-------------------------------------------------------------------------------------------------------
+double gsl_integrandOuter( double photonEnergy, void *params )
+{
+ double *numDensPerEnergyArray = ((struct Parameters_t*)params)->numDensPerEnergyArray;
+ double *energyBins = ((struct Parameters_t*)params)->energyBins;
+ double scatteredEnergy = ((struct Parameters_t*)params)->scatteredEnergy;
+ int energyBinsSize = ((struct Parameters_t*)params)->energyBinsSize;
+ double gamma_max = ((struct Parameters_t*)params)->gamma_max;
+ double (*fun_ptr)(double,double,double) = ((struct Parameters_t*)params)->fun_ptr;
+
+ ((struct Parameters_t*)params)->photonEnergy = photonEnergy;
+
+ double lb, ub;
+ double reduced_scatteredEnergy = scatteredEnergy / ELECTRON_MASS_ENERGY;
+
+ lb = reduced_scatteredEnergy;
+ lb += sqrt( SQR( reduced_scatteredEnergy ) + scatteredEnergy/photonEnergy );
+ lb *= 0.5;
+
+ ub = gamma_max;
+
+ if ( ub <= lb ) return 0.0;
+
+ return numDensAtEnergy( photonEnergy, energyBins, numDensPerEnergyArray, energyBinsSize ) *
+ ( scatteredEnergy / photonEnergy ) *
+ integralInner( lb, gamma_max, params );
+} // FUNCTION : gsl_integrandOuter
+
+
+
+//-------------------------------------------------------------------------------------------------------
+// Function : integralOuter
+// Description :
+// Note :
+// Parameter :
+// Return :
+//-------------------------------------------------------------------------------------------------------
+double integralOuter( double lb, double ub, struct Parameters_t *parameters )
+{
+ const int tid = omp_get_thread_num();
+ gsl_integration_workspace *w = workspaces_outer[tid];
+ gsl_function F;
+
+ F.function = &gsl_integrandOuter;
+ F.params = (void*)parameters;
+
+ double result, error;
+ int status, key;
+ int key_ini = 6;
+ key = key_ini;
+
+ gsl_set_error_handler_off();
+
+ do
+ {
+ //if ( key>key_ini ) printf("Unstable! status: %d key: %d (%s: %d)\n", status, key, __FUNCTION__, __LINE__);
+ status = gsl_integration_qag( &F, lb, ub, 0.0, 1e-7, 1000, key, w, &result, &error );
+ key++;
+ } while ( status && key <= 6 );
+
+ if ( status )
+ {
+# ifdef DEBUG
+ printf( "scatteredEnergy = %20.16e\n", ((struct Parameters_t*)parameters)->scatteredEnergy );
+ printf( "gamma_max = %20.16e\n", ((struct Parameters_t*)parameters)->gamma_max );
+ printf( "photonEnergy = %20.16e\n", ((struct Parameters_t*)parameters)->photonEnergy );
+ printf( "lb=%20.16e, ub=%20.16e\n", lb, ub );
+# endif
+ ERROR_EXIT( 0, "ERROR : status: %d (%s: %d) !!\n", status, __FUNCTION__, __LINE__ );
+ } // if ( status )
+ return result;
+} // FUNCTION : integralOuter
+
+
+
+//-------------------------------------------------------------------------------------------------------
+// Function : cmb_differential_number_density
+// Description :
+// Note :
+// Parameter : frequency: the frequency of emitted photon (Hz)
+// Return : n : differential number density ( 1 eV**-1 cm**-3 )
+//-------------------------------------------------------------------------------------------------------
+double cmb_differetial_number_density( double frequency )
+{
+ const double temperature = 2.72548; // in unit K
+ const double energy = REDUCED_PLANCK_EV * 2.0 * M_PI * frequency; // photon energy
+ const double kT = BOLTZMANN_CONST_EV * temperature;
+ double n;
+
+ n = SQR(energy);
+ n /= ( EXP( energy/kT ) - 1.0 );
+ n /= M_PI* M_PI * pow( REDUCED_PLANCK_EV*SPEED_OF_LIGHT, 3.0 );
+
+ return n;
+} // FUNCTION : cmb_differetial_number_density
+
+
+
+// Belows are useless
+//-------------------------------------------------------------------------------------------------------
+
+
+
+//-------------------------------------------------------------------------------------------------------
+// Function : Bisection
+// Description :
+// Note :
+// Parameter :
+// Return :
+//-------------------------------------------------------------------------------------------------------
+double Bisection( double xmin, double xmax, double (*fun)(double,double,double), double tolerance, void *params )
+{
+ double scatteredEnergy = ((struct Parameters_t*)params)->scatteredEnergy;
+ double photonEnergy = ((struct Parameters_t*)params)->photonEnergy;
+
+ double x, fun_x;
+
+ const int itrMax = 100;
+ int itr = 1;
+
+ while ( itr <= itrMax )
+ {
+ x = 0.5*( xmin + xmax );
+
+ fun_x = fun( x, photonEnergy, scatteredEnergy );
+
+ if ( fun_x == 0.0 || ( 1.0-xmin/xmax < tolerance && fun_x > 0.0 ) ) return x;
+
+ itr++;
+
+ if ( SIGN(fun_x) == SIGN(fun(xmin, photonEnergy, scatteredEnergy)) )
+ xmin = x;
+ else
+ xmax = x;
+ } // while ( itr <= itrMax )
+
+ return x;
+} // FUNCTION : Bisection
+
+
+
+//-------------------------------------------------------------------------------------------------------
+// Function : FindLbUb
+// Description :
+// Note :
+// Parameter :
+// Return : none
+//-------------------------------------------------------------------------------------------------------
+void FindLbUb( double xmin, double xmax, double (*fun)(double,double,double), void *params, double *lb, double *ub )
+{
+ double scatteredEnergy = ((struct Parameters_t*)params)->scatteredEnergy;
+ double photonEnergy = ((struct Parameters_t*)params)->photonEnergy;
+
+ *lb = xmax;
+
+ do
+ {
+ *ub = *lb;
+ *lb = 0.5*( *lb+xmin );
+ } while ( fun( *lb, photonEnergy, scatteredEnergy ) >= 0.0 );
+} // FUNCTION : FindLbUb
diff --git a/tool/analysis/PerspectiveProjection/src/Main.c b/tool/analysis/PerspectiveProjection/src/Main.c
new file mode 100644
index 0000000000..6ee7424e32
--- /dev/null
+++ b/tool/analysis/PerspectiveProjection/src/Main.c
@@ -0,0 +1,735 @@
+#include
+
+#include "../include/General.h"
+#include "hdf5.h"
+
+#define GCC_VERSION ( __GNUC__ * 10000 \
+ + __GNUC_MINOR__ * 100 \
+ + __GNUC_PATCHLEVEL__ )
+
+#ifdef XRAY_ROSAT
+# warning: XRAY_ROSAT defined!
+#endif
+
+#ifdef XRAY_EROSITA
+# warning: XRAY_EROSITA defined!
+#endif
+
+#ifdef HADRONIC_GAMMARAY
+# warning: HADRONIC_GAMMARAY defined!
+#endif
+
+#ifdef LEPTONIC_GAMMARAY
+# warning: LEPTONIC_GAMMARAY defined!
+#endif
+
+#ifdef SYNCHROTRON
+# warning: SYNCHROTRON defined!
+#endif
+
+
+herr_t LoadCompoundData( const char *FieldName, void *FieldPtr, const hid_t H5_SetID_Target, const hid_t H5_TypeID_Target );
+herr_t checkH5Status( const herr_t status1, const herr_t status2 );
+
+int MPI_Rank, NRank, RootRank;
+
+
+
+//-------------------------------------------------------------------------------------------------------
+// Function : main
+// Description :
+// Note :
+// Parameter :
+// Return :
+//-------------------------------------------------------------------------------------------------------
+int main( int argc, char **argv )
+{
+// 1. initialize
+ MPI_Init( &argc, &argv );
+ MPI_Comm_rank( MPI_COMM_WORLD, &MPI_Rank );
+ MPI_Comm_size( MPI_COMM_WORLD, &NRank );
+ RootRank = 0;
+
+ MASTER_PRINT( "NRank = %d, NTHREAD = %d\n", NRank, NUM_THREADS );
+
+// there is run-time error on tw3 with compiler/gcc/7.5.0
+# if ( GCC_VERSION == 70500 )
+# error : We recommand use gcc 9.4.0
+# endif
+
+// 2. parameters
+# if ( defined XRAY_ROSAT || defined XRAY_EROSITA )
+ FILE *table_08_keV = fopen( "cf_0.8keV.dat", "r" );
+ FILE *table_15_keV = fopen( "cf_1.5keV.dat", "r" );
+
+ if ( MPI_Rank == RootRank )
+ {
+ if ( table_15_keV == NULL ) ERROR_EXIT( -1, "ERROR : Could not open file table_15_keV !!\n" );
+ if ( table_08_keV == NULL ) ERROR_EXIT( -1, "ERROR : Could not open file table_08_keV !!\n" );
+ } // if ( MPI_Rank == RootRank )
+# endif // #if ( defined XRAY_ROSAT || defined XRAY_EROSITA )
+
+
+# ifdef PERSPECTIVE_PROJECTION
+// numAngle should be a factor of 360
+ const int numAzimuthalAngle = 1;
+ const int numCellB = 360, numCellL = 360;
+
+ if ( numCellB != numCellL ) ERROR_EXIT( 0, "ERROR : numCellB != numCellL !!\n" );
+
+ const real b_max = +90.0, b_min = -90.0;
+ const real l_max = +180.0, l_min = -180.0;
+# endif // #ifdef PERSPECTIVE_PROJECTION
+
+# ifdef SLICE
+ const char CuttingPlane[1] = "x";
+# endif
+
+// 3. Load data
+ hid_t FRB_file;
+ hid_t FRB_group1, FRB_group2;
+ hid_t FRB_dset1G1, FRB_dset2G1, FRB_dset3G1, FRB_dset4G1, FRB_dset5G1;
+ hid_t H5_SetID_KeyInfo, H5_TypeID_KeyInfo;
+ herr_t H5_Status;
+
+// 3-1. Open FRB_file using the default properties.
+ FRB_file = H5Fopen( argv[1], H5F_ACC_RDONLY, H5P_DEFAULT );
+ FRB_group2 = H5Gopen( FRB_file, "Info", H5P_DEFAULT );
+ H5_SetID_KeyInfo = H5Dopen( FRB_group2, "Keys", H5P_DEFAULT );
+ H5_TypeID_KeyInfo = H5Dget_type( H5_SetID_KeyInfo );
+
+ int numCellX, numCellY, numCellZ;
+ real BoxSizeX, BoxSizeY, BoxSizeZ;
+ real Unit_L, Unit_M, Unit_T, Unit_V, Unit_D, Unit_E, Unit_P;
+
+ LoadCompoundData( "numCellX", &numCellX, H5_SetID_KeyInfo, H5_TypeID_KeyInfo );
+ LoadCompoundData( "numCellY", &numCellY, H5_SetID_KeyInfo, H5_TypeID_KeyInfo );
+ LoadCompoundData( "numCellZ", &numCellZ, H5_SetID_KeyInfo, H5_TypeID_KeyInfo );
+ LoadCompoundData( "BoxSizeX", &BoxSizeX, H5_SetID_KeyInfo, H5_TypeID_KeyInfo );
+ LoadCompoundData( "BoxSizeY", &BoxSizeY, H5_SetID_KeyInfo, H5_TypeID_KeyInfo );
+ LoadCompoundData( "BoxSizeZ", &BoxSizeZ, H5_SetID_KeyInfo, H5_TypeID_KeyInfo );
+ LoadCompoundData( "Unit_L", &Unit_L, H5_SetID_KeyInfo, H5_TypeID_KeyInfo );
+ LoadCompoundData( "Unit_M", &Unit_M, H5_SetID_KeyInfo, H5_TypeID_KeyInfo );
+ LoadCompoundData( "Unit_T", &Unit_T, H5_SetID_KeyInfo, H5_TypeID_KeyInfo );
+ LoadCompoundData( "Unit_V", &Unit_V, H5_SetID_KeyInfo, H5_TypeID_KeyInfo );
+ LoadCompoundData( "Unit_D", &Unit_D, H5_SetID_KeyInfo, H5_TypeID_KeyInfo );
+ LoadCompoundData( "Unit_E", &Unit_E, H5_SetID_KeyInfo, H5_TypeID_KeyInfo );
+ LoadCompoundData( "Unit_P", &Unit_P, H5_SetID_KeyInfo, H5_TypeID_KeyInfo );
+
+ MASTER_PRINT( "numCellX=%d\nnumCellY=%d\nnumCellZ=%d\n", numCellX, numCellY, numCellZ );
+ MASTER_PRINT( "BoxSizeX=%e\nBoxSizeY=%e\nBoxSizeZ=%e\n", BoxSizeX, BoxSizeY, BoxSizeZ );
+ MASTER_PRINT( "Unit_L=%e\nUnit_M=%e\nUnit_T=%e\nUnit_V=%e\nUnit_D=%e\nUnit_E=%e\nUnit_P=%e\n",
+ Unit_L, Unit_M, Unit_T, Unit_V, Unit_D, Unit_E, Unit_P );
+
+ if ( numCellX%NRank != 0 ) ERROR_EXIT( 0, "numCellX(%d) %% NRank(%d) != 0 !!\n", numCellX, NRank );
+
+// 3D array to store the field to be projected
+ real*** Density = (real***)calloc_3d_array( (size_t)numCellX, (size_t)numCellY, (size_t)numCellZ, sizeof(real) );
+ real*** Temperature = (real***)calloc_3d_array( (size_t)numCellX, (size_t)numCellY, (size_t)numCellZ, sizeof(real) );
+ real*** Pressure = (real***)calloc_3d_array( (size_t)numCellX, (size_t)numCellY, (size_t)numCellZ, sizeof(real) );
+ real*** Xray_08_keV = (real***)calloc_3d_array( (size_t)numCellX, (size_t)numCellY, (size_t)numCellZ, sizeof(real) );
+ real*** Xray_15_keV = (real***)calloc_3d_array( (size_t)numCellX, (size_t)numCellY, (size_t)numCellZ, sizeof(real) );
+ real*** CREngy = (real***)calloc_3d_array( (size_t)numCellX, (size_t)numCellY, (size_t)numCellZ, sizeof(real) );
+ real*** Passive_0001 = (real***)calloc_3d_array( (size_t)numCellX, (size_t)numCellY, (size_t)numCellZ, sizeof(real) );
+ real*** HadronicGammaRay = (real***)calloc_3d_array( (size_t)numCellX, (size_t)numCellY, (size_t)numCellZ, sizeof(real) );
+ real*** LeptonicGammaRay = (real***)calloc_3d_array( (size_t)numCellX, (size_t)numCellY, (size_t)numCellZ, sizeof(real) );
+ real*** Synchrotron = (real***)calloc_3d_array( (size_t)numCellX, (size_t)numCellY, (size_t)numCellZ, sizeof(real) );
+
+ real deltaX = (real)BoxSizeX / (real)numCellX;
+ real deltaY = (real)BoxSizeY / (real)numCellY;
+ real deltaZ = (real)BoxSizeZ / (real)numCellZ;
+
+
+ real *X = (real*)calloc( (size_t)numCellX, sizeof(real) );
+ real *Y = (real*)calloc( (size_t)numCellY, sizeof(real) );
+ real *Z = (real*)calloc( (size_t)numCellZ, sizeof(real) );
+
+# ifdef PERSPECTIVE_PROJECTION
+ real dxyz[3] = { deltaX, deltaY, deltaZ };
+ real *XYZ[3] = { X, Y, Z };
+ int numCellXYZ[3] = { numCellX, numCellY, numCellZ };
+# endif
+
+ for ( int x=0; x Dens * UNIT_D
+// Pres --> Pres * UNIT_P
+// ===========================================================
+ for (int i=0; iFRBDataName, argv[1] );
+
+ Keys->UpperBound_ll = UpperBound_ll;
+ Keys->UpperBound_bb = UpperBound_bb;
+ Keys->numAzimuthalAngle = numAzimuthalAngle;
+# if ( defined HADRONIC_GAMMARAY || defined LEPTONIC_GAMMARAY )
+ Keys->scatteredEnergy = scatteredEnergy;
+# elif ( defined SYNCHROTRON )
+ Keys->observedFreq = observedFreq;
+# endif
+# if ( defined HADRONIC_GAMMARAY || defined LEPTONIC_GAMMARAY || defined SYNCHROTRON )
+ Keys->gamma_max = gamma_max;
+ Keys->gamma_min = gamma_min;
+ Keys->spectral_index = spectral_index;
+# endif
+# ifdef PERSPECTIVE_PROJECTION
+ Keys->b_max = b_max;
+ Keys->b_min = b_min;
+ Keys->l_max = l_max;
+ Keys->l_min = l_min;
+ Keys->dt = dt;
+# endif
+
+ LoadCompoundData( "AMRDataName", &(Keys->AMRDataName), H5_SetID_KeyInfo, H5_TypeID_KeyInfo );
+ LoadCompoundData( "EpochTimeStamp", &(Keys->EpochTimeStampInFRBData), H5_SetID_KeyInfo, H5_TypeID_KeyInfo );
+
+// Create a new file using the default properties.
+# ifdef PERSPECTIVE_PROJECTION
+ char MapName[STRING_LENGTH] = "Projected_FRB_";
+# endif
+# ifdef SLICE
+ char MapName[STRING_LENGTH] = "Slice_FRB_";
+# endif
+
+ strcat( MapName, Keys->AMRDataName );
+
+# if ( defined SYNCHROTRON )
+ strcat( MapName, "_Synchrotron" );
+# endif
+
+# if ( defined LEPTONIC_GAMMARAY || defined HADRONIC_GAMMARAY || SYNCHROTRON )
+ strcat( MapName, "_" );
+ strcat( MapName, argv[2] );
+ strcat( MapName, "_" );
+ strcat( MapName, argv[3] );
+ strcat( MapName, "_" );
+ strcat( MapName, argv[4] );
+# endif
+
+# if ( defined XRAY_ROSAT || defined XRAY_EROSITA )
+ strcat( MapName, "_XRay" );
+# endif
+
+ pp_file = H5Fcreate( strcat(MapName, ".h5"), H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT );
+
+// Create groups in the file.
+ pp_group1 = H5Gcreate( pp_file, "/Map", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT );
+ pp_group2 = H5Gcreate( pp_file, "/Info", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT );
+
+ const hsize_t dims[3] = { numAzimuthalAngle, UpperBound_ll, UpperBound_bb };
+
+// Create dataspace. Setting maximum size to NULL sets the maximum size to be the current size.
+ pp_spaceDS1G1 = H5Screate_simple( 3, dims, NULL );
+ pp_spaceDS2G1 = H5Screate_simple( 3, dims, NULL );
+ pp_spaceDS3G1 = H5Screate_simple( 3, dims, NULL );
+ pp_spaceDS4G1 = H5Screate_simple( 3, dims, NULL );
+ pp_spaceDS5G1 = H5Screate_simple( 3, dims, NULL );
+ pp_spaceDS6G1 = H5Screate_simple( 3, dims, NULL );
+ pp_spaceDS7G1 = H5Screate_simple( 3, dims, NULL );
+ pp_spaceDS8G1 = H5Screate_simple( 3, dims, NULL );
+ pp_spaceDS9G1 = H5Screate_simple( 3, dims, NULL );
+ pp_spaceDS10G1 = H5Screate_simple( 3, dims, NULL );
+ pp_spaceKey = H5Screate( H5S_SCALAR );
+
+ typeID = H5Tcreate( H5T_COMPOUND, sizeof(Keys_t) );
+
+ H5Tinsert( typeID, "UpperBound_bb", HOFFSET(Keys_t, UpperBound_bb ), H5T_NATIVE_INT );
+ H5Tinsert( typeID, "UpperBound_ll", HOFFSET(Keys_t, UpperBound_ll ), H5T_NATIVE_INT );
+ H5Tinsert( typeID, "numAzimuthalAngle", HOFFSET(Keys_t, numAzimuthalAngle), H5T_NATIVE_INT );
+# if ( defined HADRONIC_GAMMARAY || defined LEPTONIC_GAMMARAY )
+ H5Tinsert( typeID, "scatteredEnergy", HOFFSET(Keys_t, scatteredEnergy), H5T_IEEE_F32LE );
+# elif ( defined SYNCHROTRON )
+ H5Tinsert( typeID, "observedFreq", HOFFSET(Keys_t, observedFreq), H5T_IEEE_F32LE );
+# endif
+# if ( defined HADRONIC_GAMMARAY || defined LEPTONIC_GAMMARAY || defined SYNCHROTRON )
+ H5Tinsert( typeID, "gamma_max", HOFFSET(Keys_t, gamma_max), H5T_IEEE_F32LE );
+ H5Tinsert( typeID, "gamma_min", HOFFSET(Keys_t, gamma_min), H5T_IEEE_F32LE );
+ H5Tinsert( typeID, "spectral_index", HOFFSET(Keys_t, spectral_index), H5T_IEEE_F32LE );
+# endif
+# ifdef PERSPECTIVE_PROJECTION
+ H5Tinsert( typeID, "b_max", HOFFSET(Keys_t, b_max ), H5T_IEEE_F32LE );
+ H5Tinsert( typeID, "b_min", HOFFSET(Keys_t, b_min ), H5T_IEEE_F32LE );
+ H5Tinsert( typeID, "l_max", HOFFSET(Keys_t, l_max ), H5T_IEEE_F32LE );
+ H5Tinsert( typeID, "l_min", HOFFSET(Keys_t, l_min ), H5T_IEEE_F32LE );
+ H5Tinsert( typeID, "dt", HOFFSET(Keys_t, dt ), H5T_IEEE_F32LE );
+# endif
+ H5Tinsert( typeID, "EpochTimeStampInFRBData", HOFFSET(Keys_t, EpochTimeStampInFRBData ), H5T_NATIVE_UINT64 );
+
+// create the "variable-length string" datatype
+ hid_t H5_TypeID_VarStr;
+
+ H5_TypeID_VarStr = H5Tcopy( H5T_C_S1 );
+ H5_Status = H5Tset_size( H5_TypeID_VarStr, STRING_LENGTH );
+ if ( H5_Status < 0 ) ERROR_EXIT( 0, "ERROR : H5Tset_size() fails: %d !!\n", __LINE__ );
+
+ H5Tinsert( typeID, "AMRDataName", HOFFSET(Keys_t, AMRDataName ), H5_TypeID_VarStr );
+ H5Tinsert( typeID, "FRBDataName", HOFFSET(Keys_t, FRBDataName ), H5_TypeID_VarStr );
+# ifdef SLICE
+ H5Tinsert( typeID, "CuttingPlane", HOFFSET(Keys_t, CuttingPlane ), H5_TypeID_VarStr );
+# endif
+ H5Tclose( H5_TypeID_VarStr );
+
+
+// Create the dataset. We will use all default properties for this example.
+# ifdef PERSPECTIVE_PROJECTION
+ pp_DS1G1 = H5Dcreate( pp_group1, "ProjectedDensity" , H5T_IEEE_F32LE, pp_spaceDS1G1, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT );
+ pp_DS2G1 = H5Dcreate( pp_group1, "ProjectedPressure", H5T_IEEE_F32LE, pp_spaceDS2G1, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT );
+ pp_DS3G1 = H5Dcreate( pp_group1, "ProjectedTemperature", H5T_IEEE_F32LE, pp_spaceDS3G1, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT );
+ pp_DS4G1 = H5Dcreate( pp_group1, "ProjectedXray_08_keV", H5T_IEEE_F32LE, pp_spaceDS4G1, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT );
+ pp_DS5G1 = H5Dcreate( pp_group1, "ProjectedXray_15_keV", H5T_IEEE_F32LE, pp_spaceDS5G1, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT );
+ pp_DS6G1 = H5Dcreate( pp_group1, "ProjectedCRay", H5T_IEEE_F32LE, pp_spaceDS6G1, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT );
+ pp_DS7G1 = H5Dcreate( pp_group1, "ProjectedPassive_0001", H5T_IEEE_F32LE, pp_spaceDS7G1, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT );
+ pp_DS8G1 = H5Dcreate( pp_group1, "ProjectedHadronicGammaRay", H5T_IEEE_F32LE, pp_spaceDS8G1, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT );
+ pp_DS9G1 = H5Dcreate( pp_group1, "ProjectedLeptonicGammaRay", H5T_IEEE_F32LE, pp_spaceDS9G1, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT );
+ pp_DS10G1 = H5Dcreate( pp_group1, "ProjectedSynchrotron", H5T_IEEE_F32LE, pp_spaceDS10G1, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT );
+# endif
+# ifdef SLICE
+ pp_DS1G1 = H5Dcreate( pp_group1, "SliceDensity" , H5T_IEEE_F32LE, pp_spaceDS1G1, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT );
+ pp_DS2G1 = H5Dcreate( pp_group1, "SlicePressure", H5T_IEEE_F32LE, pp_spaceDS2G1, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT );
+ pp_DS3G1 = H5Dcreate( pp_group1, "SliceTemperature", H5T_IEEE_F32LE, pp_spaceDS3G1, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT );
+ pp_DS4G1 = H5Dcreate( pp_group1, "SliceXray_08_keV", H5T_IEEE_F32LE, pp_spaceDS4G1, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT );
+ pp_DS5G1 = H5Dcreate( pp_group1, "SliceXray_15_keV", H5T_IEEE_F32LE, pp_spaceDS5G1, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT );
+ pp_DS6G1 = H5Dcreate( pp_group1, "SliceGammaRay", H5T_IEEE_F32LE, pp_spaceDS5G1, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT );
+# endif
+ pp_dsetKey = H5Dcreate( pp_group2, "Keys", typeID, pp_spaceKey, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT );
+
+// Write the data to the dataset.
+ H5_Status = H5Dwrite( pp_DS1G1, H5T_IEEE_F32LE, H5S_ALL, pp_spaceDS1G1, H5P_DEFAULT, ProjectedSliceDensity [0][0] );
+ H5_Status = checkH5Status( H5Dwrite( pp_DS2G1, H5T_IEEE_F32LE, H5S_ALL, pp_spaceDS2G1, H5P_DEFAULT, ProjectedSlicePressure [0][0] ), H5_Status );
+ H5_Status = checkH5Status( H5Dwrite( pp_DS3G1, H5T_IEEE_F32LE, H5S_ALL, pp_spaceDS3G1, H5P_DEFAULT, ProjectedSliceTemperature [0][0] ), H5_Status );
+ H5_Status = checkH5Status( H5Dwrite( pp_DS4G1, H5T_IEEE_F32LE, H5S_ALL, pp_spaceDS4G1, H5P_DEFAULT, ProjectedSliceXray_08_keV [0][0] ), H5_Status );
+ H5_Status = checkH5Status( H5Dwrite( pp_DS5G1, H5T_IEEE_F32LE, H5S_ALL, pp_spaceDS5G1, H5P_DEFAULT, ProjectedSliceXray_15_keV [0][0] ), H5_Status );
+ H5_Status = checkH5Status( H5Dwrite( pp_DS6G1, H5T_IEEE_F32LE, H5S_ALL, pp_spaceDS6G1, H5P_DEFAULT, ProjectedSliceCRay [0][0] ), H5_Status );
+ H5_Status = checkH5Status( H5Dwrite( pp_DS7G1, H5T_IEEE_F32LE, H5S_ALL, pp_spaceDS7G1, H5P_DEFAULT, ProjectedSlicePassive_0001 [0][0] ), H5_Status );
+ H5_Status = checkH5Status( H5Dwrite( pp_DS8G1, H5T_IEEE_F32LE, H5S_ALL, pp_spaceDS8G1, H5P_DEFAULT, ProjectedSliceHadronicGammaRay[0][0] ), H5_Status );
+ H5_Status = checkH5Status( H5Dwrite( pp_DS9G1, H5T_IEEE_F32LE, H5S_ALL, pp_spaceDS9G1, H5P_DEFAULT, ProjectedSliceLeptonicGammaRay[0][0] ), H5_Status );
+ H5_Status = checkH5Status( H5Dwrite( pp_DS10G1, H5T_IEEE_F32LE, H5S_ALL, pp_spaceDS10G1, H5P_DEFAULT, ProjectedSliceSynchrotron [0][0] ), H5_Status );
+ H5_Status = checkH5Status( H5Dwrite( pp_dsetKey, typeID, H5S_ALL, H5S_ALL, H5P_DEFAULT, Keys ), H5_Status );
+
+ if ( H5_Status < 0 ) ERROR_EXIT( 0, "ERROR : One of the H5Dwrite() fails: %d !!\n", __LINE__ );
+
+// Close dataset
+ H5_Status = H5Dclose( pp_dsetKey );
+ H5_Status = checkH5Status( H5Dclose( pp_DS1G1 ), H5_Status );
+ H5_Status = checkH5Status( H5Dclose( pp_DS2G1 ), H5_Status );
+ H5_Status = checkH5Status( H5Dclose( pp_DS3G1 ), H5_Status );
+ H5_Status = checkH5Status( H5Dclose( pp_DS4G1 ), H5_Status );
+ H5_Status = checkH5Status( H5Dclose( pp_DS5G1 ), H5_Status );
+ H5_Status = checkH5Status( H5Dclose( pp_DS6G1 ), H5_Status );
+ H5_Status = checkH5Status( H5Dclose( pp_DS7G1 ), H5_Status );
+ H5_Status = checkH5Status( H5Dclose( pp_DS8G1 ), H5_Status );
+ H5_Status = checkH5Status( H5Dclose( pp_DS9G1 ), H5_Status );
+ H5_Status = checkH5Status( H5Dclose( pp_DS10G1 ), H5_Status );
+
+ if ( H5_Status < 0 ) ERROR_EXIT( 0, "ERROR : One of the H5Dclose() fails: %d !!\n", __LINE__ );
+
+// Close space
+ H5_Status = H5Sclose( pp_spaceKey );
+ H5_Status = checkH5Status( H5Sclose( pp_spaceDS1G1 ), H5_Status );
+ H5_Status = checkH5Status( H5Sclose( pp_spaceDS2G1 ), H5_Status );
+ H5_Status = checkH5Status( H5Sclose( pp_spaceDS3G1 ), H5_Status );
+ H5_Status = checkH5Status( H5Sclose( pp_spaceDS4G1 ), H5_Status );
+ H5_Status = checkH5Status( H5Sclose( pp_spaceDS5G1 ), H5_Status );
+ H5_Status = checkH5Status( H5Sclose( pp_spaceDS6G1 ), H5_Status );
+ H5_Status = checkH5Status( H5Sclose( pp_spaceDS7G1 ), H5_Status );
+ H5_Status = checkH5Status( H5Sclose( pp_spaceDS8G1 ), H5_Status );
+ H5_Status = checkH5Status( H5Sclose( pp_spaceDS9G1 ), H5_Status );
+ H5_Status = checkH5Status( H5Sclose( pp_spaceDS10G1 ), H5_Status );
+
+ if ( H5_Status < 0 ) ERROR_EXIT( 0, "ERROR : One of the H5Sclose() fails: %d !!\n", __LINE__ );
+
+// Close group
+ H5_Status = H5Gclose( pp_group1 );
+ H5_Status = checkH5Status( H5Gclose( pp_group2 ), H5_Status );
+ if ( H5_Status < 0 ) ERROR_EXIT( 0, "ERROR : One of the H5Gclose() fails: %d !!\n", __LINE__ );
+
+// Close file
+ H5_Status = H5Fclose( pp_file );
+ if ( H5_Status < 0 ) ERROR_EXIT( 0, "ERROR : H5Fclose() fails: %d !!\n", __LINE__ );
+
+ free(Keys);
+ } // if ( MPI_Rank == RootRank )
+
+ MPI_Barrier( MPI_COMM_WORLD );
+
+ free_3d_array( (void***)ProjectedSliceDensity );
+ free_3d_array( (void***)ProjectedSlicePressure );
+ free_3d_array( (void***)ProjectedSliceTemperature );
+ free_3d_array( (void***)ProjectedSliceXray_08_keV );
+ free_3d_array( (void***)ProjectedSliceXray_15_keV );
+ free_3d_array( (void***)ProjectedSliceCRay );
+ free_3d_array( (void***)ProjectedSlicePassive_0001 );
+ free_3d_array( (void***)ProjectedSliceHadronicGammaRay );
+ free_3d_array( (void***)ProjectedSliceLeptonicGammaRay );
+ free_3d_array( (void***)ProjectedSliceSynchrotron );
+
+# if ( defined XRAY_ROSAT || defined XRAY_EROSITA )
+ free( tempTable_08_keV );
+ free( tempTable_15_keV );
+ free( lambdaTable_08_keV );
+ free( lambdaTable_15_keV );
+ fclose( table_08_keV );
+ fclose( table_15_keV );
+# endif
+
+ free( X );
+ free( Y );
+ free( Z );
+
+ MASTER_PRINT( "Done!!\n" );
+
+ MPI_Finalize();
+
+ return 0;
+} // FUNCTION : main
+
+
+
+//-------------------------------------------------------------------------------------------------------
+// Function : LoadCompoundData
+// Description :
+// Note :
+// Parameter :
+// Return :
+//-------------------------------------------------------------------------------------------------------
+herr_t LoadCompoundData( const char *FieldName, void *FieldPtr, const hid_t H5_SetID_Target, const hid_t H5_TypeID_Target )
+{
+ int H5_FieldIdx;
+ size_t H5_FieldSize;
+ hid_t H5_TypeID_Field; // datatype ID of the target field in the compound variable
+ hid_t H5_TypeID_Load; // datatype ID for loading the target field
+ herr_t H5_Status;
+
+// load
+ H5_FieldIdx = H5Tget_member_index( H5_TypeID_Target, FieldName );
+
+ if ( H5_FieldIdx < 0 ) return -1;
+
+ H5_TypeID_Field = H5Tget_member_type( H5_TypeID_Target, H5_FieldIdx );
+ H5_FieldSize = H5Tget_size( H5_TypeID_Field );
+ H5_TypeID_Load = H5Tcreate( H5T_COMPOUND, H5_FieldSize );
+ H5_Status = H5Tinsert( H5_TypeID_Load, FieldName, 0, H5_TypeID_Field );
+
+ H5_Status = H5Dread( H5_SetID_Target, H5_TypeID_Load, H5S_ALL, H5S_ALL, H5P_DEFAULT, FieldPtr );
+ if ( H5_Status < 0 ) printf( "failed to load the field!!\n" );
+
+ H5_Status = H5Tclose( H5_TypeID_Field );
+ H5_Status = H5Tclose( H5_TypeID_Load );
+
+ return 0;
+} // FUNCTION : LoadCompoundData
+
+
+
+//-------------------------------------------------------------------------------------------------------
+// Function : checkH5Status
+// Description : check if any of the H5_Status is fail
+// Parameter : status1/2 : HDF5 function returned status
+//
+// Return : status1
+//-------------------------------------------------------------------------------------------------------
+herr_t checkH5Status( const herr_t status1, const herr_t status2 )
+{
+ if ( status1 < 0 ) return status1;
+ if ( status2 < 0 ) return status2;
+ return status1;
+} // FUNCTION : checkH5Status
diff --git a/tool/analysis/PerspectiveProjection/src/Make_Projection.c b/tool/analysis/PerspectiveProjection/src/Make_Projection.c
new file mode 100644
index 0000000000..a4dd67843d
--- /dev/null
+++ b/tool/analysis/PerspectiveProjection/src/Make_Projection.c
@@ -0,0 +1,89 @@
+#include "../include/General.h"
+
+
+
+//-------------------------------------------------------------------------------------------------------
+// Function : PerspectiveProject
+// Description :
+// Note :
+// Parameter :
+// Return :
+//-------------------------------------------------------------------------------------------------------
+real PerspectiveProject( real b, real l, real dt, real *XYZ[], int numCellXYZ[], real dxyz[], real azimuthalAngle,
+ real BoxSize[], real ***TargetQuantity, int numRow, real *tempTable, real *lambdaTable )
+{
+ if ( FABS(COS(l)) < EPSILON ) ERROR_EXIT( 0, "ERROR : FABS(COS(l)) < EPSILON !! l=%e, COS(l)=%e\n", l, COS(l) );
+
+ real x, y, z, r2, xp, yp, zp;
+ real t = 0.0;
+ double ProjectedValue = 0.0;
+
+ b *= M_PI/180.0;
+ l *= M_PI/180.0;
+
+ do
+ {
+ if ( -0.5*M_PI < l && l < +0.5*M_PI ) t += dt;
+ else t -= dt;
+
+ x = (real)R_SUN - t;
+ y = TAN(l) * t;
+ z = TAN(b) / COS(l) * t;
+
+ r2 = x*x + y*y + z*z;
+
+ if( r2 > (real)SQR(HALO_RADIUS) ) break;
+
+ rotationMatrix( x, y, z, &xp, &yp, &zp, azimuthalAngle );
+
+ real xyz[3] = { xp, yp, zp };
+
+# if ( defined XRAY_ROSAT || defined XRAY_EROSITA )
+// project the X-ray from the Galactic halo
+ if ( numRow > 0 ) ProjectedValue += xRayHalo( x, y, z, numRow, tempTable, lambdaTable );
+# endif
+
+ const int Idx = BinarySearch( XYZ[0], 0, numCellXYZ[0]-1, xp );
+ const int Jdx = BinarySearch( XYZ[1], 0, numCellXYZ[1]-1, yp );
+ const int Kdx = BinarySearch( XYZ[2], 0, numCellXYZ[2]-1, zp );
+
+ if ( ( Idx < 0 || Idx > numCellXYZ[0]-2 ) ||
+ ( Jdx < 0 || Jdx > numCellXYZ[1]-2 ) ||
+ ( Kdx < 0 || Kdx > numCellXYZ[2]-2 ) )
+ continue;
+
+ real xyz000[3] = { XYZ[0][Idx], XYZ[1][Jdx], XYZ[2][Kdx] };
+
+ real Vertex000[1] = { TargetQuantity[Idx ][Jdx ][Kdx ] };
+ real Vertex001[1] = { TargetQuantity[Idx ][Jdx ][Kdx+1] };
+ real Vertex010[1] = { TargetQuantity[Idx ][Jdx+1][Kdx ] };
+ real Vertex100[1] = { TargetQuantity[Idx+1][Jdx ][Kdx ] };
+ real Vertex011[1] = { TargetQuantity[Idx ][Jdx+1][Kdx+1] };
+ real Vertex101[1] = { TargetQuantity[Idx+1][Jdx ][Kdx+1] };
+ real Vertex110[1] = { TargetQuantity[Idx+1][Jdx+1][Kdx ] };
+ real Vertex111[1] = { TargetQuantity[Idx+1][Jdx+1][Kdx+1] };
+
+ real FieldAtVertices[8] = { Vertex000[0], Vertex001[0], Vertex010[0], Vertex100[0],
+ Vertex011[0], Vertex101[0], Vertex110[0], Vertex111[0] };
+
+ ProjectedValue += TrilinearInterpolation( FieldAtVertices, xyz000, dxyz, xyz );
+ } while( 1 );
+
+ return (real)ProjectedValue;
+} // FUNCTION : PerspectiveProject
+
+
+
+//-------------------------------------------------------------------------------------------------------
+// Function : rotationMatrix
+// Description :
+// Note :
+// Parameter :
+// Return :
+//-------------------------------------------------------------------------------------------------------
+void rotationMatrix( real x, real y, real z, real *xp, real *yp, real *zp, real angle )
+{
+ *xp = x * COS(angle) - y * SIN(angle);
+ *yp = x * SIN(angle) + y * COS(angle);
+ *zp = z;
+} // FUNCTION : rotationMatrix
diff --git a/tool/analysis/PerspectiveProjection/src/Make_Slice.c b/tool/analysis/PerspectiveProjection/src/Make_Slice.c
new file mode 100644
index 0000000000..22495d9c32
--- /dev/null
+++ b/tool/analysis/PerspectiveProjection/src/Make_Slice.c
@@ -0,0 +1,34 @@
+#include "../include/General.h"
+
+
+
+//-------------------------------------------------------------------------------------------------------
+// Function : Slice
+// Description :
+// Note :
+// Parameter :
+// Return :
+//-------------------------------------------------------------------------------------------------------
+real Slice( int Idx1, int Idx2, int numCellXYZ[], real ***TargetQuantity, char CuttingPlane[] )
+{
+ if ( atoi(CuttingPlane) == atoi("x") )
+ {
+ int CenterIdx = 0.5 * numCellXYZ[0];
+ return TargetQuantity[CenterIdx][Idx1][Idx2];
+ }
+ else if ( atoi(CuttingPlane) == atoi("y") )
+ {
+ int CenterIdy = 0.5 * numCellXYZ[1];
+ return TargetQuantity[Idx1][CenterIdy][Idx2];
+ }
+ else if ( atoi(CuttingPlane) == atoi("z") )
+ {
+ int CenterIdz = 0.5 * numCellXYZ[2];
+ return TargetQuantity[Idx1][Idx2][CenterIdz];
+ }
+ else
+ {
+ ERROR_EXIT( 0, "ERROR : Something wrong !!\n" );
+ return 0.0;
+ }
+} // FUNCTION : Slice
diff --git a/tool/analysis/PerspectiveProjection/src/ReadTable.c b/tool/analysis/PerspectiveProjection/src/ReadTable.c
new file mode 100644
index 0000000000..25dcbd5b08
--- /dev/null
+++ b/tool/analysis/PerspectiveProjection/src/ReadTable.c
@@ -0,0 +1,117 @@
+#include "../include/General.h"
+
+
+#define COMMENT_SYMBOL '#'
+#define MAX_STRING 100
+#define DELIMITER " "
+
+
+
+//-------------------------------------------------------------------------------------------------------
+// Function : ConntNumLine
+// Description : Count the number of rows in file
+// --> excluding the line beginning with comment symbol (#)
+// --> excluding the line composed with comment symbols and white spaces
+// Note :
+// Parameter : table : File stream
+// Return : The number of rows in table
+//-------------------------------------------------------------------------------------------------------
+int CountNumLine( FILE *table )
+{
+ uint64_t numLine = 0;
+ char line[MAX_STRING];
+
+ rewind(table);
+
+// Get the number of charactors in each line
+ while( fgets(line, MAX_STRING, table) )
+ {
+// Skip the line beginning with comment symbol
+ if ( line[0] == COMMENT_SYMBOL || line[0] == '\n' ) continue;
+
+// Check the line beginning with white space
+ if ( line[0] == ' ' )
+ {
+ bool skip = true;
+ for (int i=0; line[i] != '\0'; i++)
+ {
+// Keep the line when it has any meaningful characters
+ if ( !(line[i] == COMMENT_SYMBOL || line[i] == ' ' || line[i] == '\n') )
+ {
+ skip &= false;
+ break;
+ }
+
+// Break the loop immediately when encounter comment symbol
+ if ( line[i] == COMMENT_SYMBOL ) break;
+ } // for (int i=0; line[i] != '\0'; i++)
+
+ if (skip) continue;
+ } // if ( line[0] == ' ' )
+
+ numLine++;
+ } // while( fgets(line, MAX_STRING, table) )
+
+ return numLine;
+} // FUNCTION : CountNumLine
+
+
+
+//-------------------------------------------------------------------------------------------------------
+// Function : ReadTable
+// Description :
+// Note :
+// Parameter :
+// Return :
+//-------------------------------------------------------------------------------------------------------
+void ReadTable( int TargetColumn, int numRow, FILE *table, float *columnArray )
+{
+ rewind(table);
+
+ char line[MAX_STRING];
+
+ int r = 0;
+
+ while( fgets(line, MAX_STRING, table) )
+ {
+
+// Step 1: Skip the line beginning with comment symbol or newline
+ if ( line[0] == COMMENT_SYMBOL || line[0] == '\n' ) continue;
+
+// Step 2: Check the line beginning with white space
+ if ( line[0] == ' ' )
+ {
+ bool skip = true;
+
+ for (int i=0; line[i] != '\0'; i++)
+ {
+
+// Step 2-1: Kepp the line when it has any meaningful characters
+ if ( !(line[i] == COMMENT_SYMBOL || line[i] == ' ' || line[i] == '\n') ){
+ skip &= false;
+ break;
+ }
+
+// Step 2-2: Break the loop immediately when encounter comment symbol
+ if ( line[i] == COMMENT_SYMBOL ) break;
+
+ } // for (int i=0; line[i] != '\0'; i++)
+
+ if (skip) continue;
+ } // if ( line[0] == ' ' )
+
+ // Step 3: parse a string into tokens
+ char *token = strtok( line, DELIMITER );
+ int c = 1;
+
+ while( token != NULL )
+ {
+ if ( c++ == TargetColumn ) columnArray[r] = atof(token);
+ token = strtok( NULL, DELIMITER );
+ }
+
+ if ( r == numRow-1 ) break;
+
+ r++;
+ } // while( fgets(line, MAX_STRING, table) )
+} // FUNCTION : ReadTable
diff --git a/tool/analysis/PerspectiveProjection/src/Synchrotron.c b/tool/analysis/PerspectiveProjection/src/Synchrotron.c
new file mode 100644
index 0000000000..da09f6b77c
--- /dev/null
+++ b/tool/analysis/PerspectiveProjection/src/Synchrotron.c
@@ -0,0 +1,129 @@
+#include "../include/General.h"
+
+
+
+void ijk2xyz( int i, int j, int k, real *x, real *y, real *z, int numCellXYZ[], real BoxSize[] );
+double Synchrotron_emissivity( real CREngy, real B, real spectral_index, real observedFreq, real gamma_max, real gamma_min );
+double BField( real x, real y, real z);
+
+
+
+//-------------------------------------------------------------------------------------------------------
+// Function : Synchrotron_3D
+// Description :
+// Note :
+// Parameter : CREngy : CR energy density (eV/cm**3)
+// numCellXYZ : number of cells on each side of box
+// BoxSize : simulation box size (kpc)
+//
+// Return : Synchrotron : synchrotron emissivity (erg/cm**3/s)
+//-------------------------------------------------------------------------------------------------------
+void Synchrotron_3D( real ***CREngy, real spectral_index, real ***Synchrotron, real observedFreq,
+ int numCellXYZ[], real BoxSize[], real gamma_max, real gamma_min )
+{
+ for (int i=0; i __DBL_MAX__ || input < __DBL_MIN__ )
+ {
+ printf( "input is -inf/inf %s:%d\n", FunctionName, line );
+ return true;
+ }
+
+ return false;
+} // FUNCTION : checkNAN
+
+
+
+//-------------------------------------------------------------------------------------------------------
+// Function : checkMinus
+// Description :
+// Note :
+// Parameter :
+// Return :
+//-------------------------------------------------------------------------------------------------------
+bool checkMinus( real input, const char FunctionName[], const int line )
+{
+ if ( input >= 0.0 ) return false;
+
+ printf( "minus is at %s:%d\n", FunctionName, line );
+ return true;
+} // FUNCTION : checkMinus
+
+
+
+//-------------------------------------------------------------------------------------------------------
+// Function : checkInt32Overflow
+// Description :
+// Note :
+// Parameter :
+// Return : none
+//-------------------------------------------------------------------------------------------------------
+void checkInt32Overflow( int32_t a, int32_t b, int operation, int line )
+{
+ bool overflow = false;
+
+ if ( operation == '+' )
+ {
+ if ( ( b > 0 ) && ( a > INT32_MAX-b ) ) overflow = true;
+ if ( ( b < 0 ) && ( a < INT32_MIN-b ) ) overflow = true;
+ }
+ else if ( operation == '*' )
+ {
+ if ( b != 0 && a > INT32_MAX / b ) overflow = true;
+ if ( b != 0 && a < INT32_MIN / b ) overflow = true;
+ }
+ else
+ {
+ ERROR_EXIT( 0, "ERROR : something wrong !!\n" );
+ }
+
+ if ( overflow ) ERROR_EXIT( 0, "ERROR : Integer overflow !! a=%d, b=%d, line=%d\n", a, b, line );
+} // FUNCTION : checkInt32Overflow
+
+
+
+//-------------------------------------------------------------------------------------------------------
+// Function : checkmemoryContiguous
+// Description :
+// Note :
+// Parameter :
+// Return :
+//-------------------------------------------------------------------------------------------------------
+bool checkmemoryContiguous( real *Ptr, int sizeOf, int Length )
+{
+ long d = (Ptr+1)-(Ptr);
+
+ for (int i=2; i