# Example 2: -  Analyzing dim6 operators in WBF

Johann Brehmer, Felix Kling, Kyle Cranmer 2018

In this tutorial we will demonstrate further functionalities of MadMiner. In particular we show how
- to include smearing at parton level
- to include backgrounds

## Preparations

Let us first load all the python libraries again

```python
import sys
import os
madminer_src_path = "/Users/felixkling/Documents/GitHub/madminer"
sys.path.append(madminer_src_path)

from __future__ import absolute_import, division, print_function, unicode_literals
import numpy as np
import matplotlib
from matplotlib import pyplot as plt
from scipy.optimize import curve_fit
% matplotlib inline

from madminer.core import MadMiner
from madminer.plotting import plot_2d_morphing_basis
from madminer.sampling import combine_and_shuffle
from madminer.lhe import LHEProcessor
from madminer.sampling import SampleAugmenter
```

Please enter here the path to your MG5 root directory. This notebook assumes that you installed Delphes and Pythia through MG5. **This needs to be updated by the user**

```python
mg_dir = '/Users/felixkling/work/MG5_aMC_v2_6_2'
```

## 1. Setup

### 1a) Define parameter space

Let us first define the theory parameters. Note the additional option `param_card_transform`, which can transform the theory parameter in MadMiner (`theta`) to the input parameter in the param_card. In this case we re-scale from GeV$^{-2}$ to TeV$^{-2}$.

```python
miner = MadMiner()

miner.add_parameter(
    lha_block='anoinputs',
    lha_id=3,
    parameter_name='FW',
    morphing_max_power=2,
    param_card_transform="0.000001*theta",
    parameter_range=(-10,10)
)
miner.add_parameter(
    lha_block='anoinputs',
    lha_id=7,
    parameter_name='FWW',
    morphing_max_power=2,
    param_card_transform="0.000001*theta",
    parameter_range=(-10,10)
)
```

    06:41
    06:41  ------------------------------------------------------------
    06:41  |                                                          |
    06:41  |  MadMiner v0.1.0                                         |
    06:41  |                                                          |
    06:41  |           Johann Brehmer, Kyle Cranmer, and Felix Kling  |
    06:41  |                                                          |
    06:41  ------------------------------------------------------------
    06:41
    06:41  Added parameter FW (LHA: anoinputs 3, maximal power in squared ME: (2,), range: (-10, 10))
    06:41  Added parameter FWW (LHA: anoinputs 7, maximal power in squared ME: (2,), range: (-10, 10))

### 1b) Define benchmark points (evaluation points for |M|^2)

Define morphing benchmarks by hand / per scan.

```python
miner.add_benchmark(
    {'FW':0., 'FWW':0.},
    'sm'
)
miner.add_benchmark(
    {'FW':2, 'FWW':2},
    'w'
)
```

    06:41  Added benchmark sm: FWW = 0.00e+00, FW = 0.00e+00)
    06:41  Added benchmark w: FWW = 2.00, FW = 2.00)

```python
miner.set_benchmarks_from_morphing(
    keep_existing_benchmarks=True,
    n_trials=1000,
    max_overall_power=2
)
```

    06:41  Optimizing basis for morphing
    06:41  Added benchmark sm: FW = 0.00e+00, FWW = 0.00e+00)
    06:41  Added benchmark w: FW = 2.00, FWW = 2.00)
    06:41  Added benchmark morphing_basis_vector_2: FW = 2.14, FWW = -8.58e+00)
    06:41  Added benchmark morphing_basis_vector_3: FW = -5.61e+00, FWW = -6.12e+00)
    06:41  Added benchmark morphing_basis_vector_4: FW = -6.05e+00, FWW = 9.47)
    06:41  Added benchmark morphing_basis_vector_5: FW = -7.78e+00, FWW = 0.21)

```python
fig = plot_2d_morphing_basis(
    miner.morpher,
    xlabel=r'$c_{W} / \Lambda^2$ [TeV$^{-2}$]',
    ylabel=r'$c_{WW}} / \Lambda^2$ [TeV$^{-2}$]',
    xrange=(-10,10),
    yrange=(-10,10)
)
```

### 1c) Save setup

```python
miner.save('data/madminer_example.h5')
```

    06:41  Saving setup (including morphing) to data/madminer_example.h5

### 1d) Comment on change UFO model to include detector smearing

To simulate the resolution of the invariant masses due to detector smearing, we use a simple trick: we change the Higgs propagator to reproduce the wanted (smeared) invariant mass distribution. The following example illustrates how to change the UFO model `dim6_for_information_parton` to reproduce the invariant mass distribution for $H\to\gamma\gamma$.

1. We base our simulation of detector effects on the CMS simulation in Fig. 6 (right) of [CMS-PAS-HIG-15-005](https://cds.cern.ch/record/2140979). In particular, we describe the peak region of the $H\to\gamma\gamma$ diphoton mass distribution by a Gaussian

    \begin{equation}
    Gaus\left(m~|~N,m_H,\sigma\right)=\frac{N}{\sqrt{2\pi}\sigma}\text{Exp}\left[-\frac{1}{2} \left(\frac{m-m_H}{\sigma}\right)^2\right].
    \end{equation}

    From a fit to the normalized distribution we obtain: $N=0.92$, $m_H=124.7$ and $\sigma=1.67$. The normalization factor $N$ accounts for the fact, that a gaussian does not describe the tails of the dsitribition well. However, these tails will hardly contribute to the information and we account for the loss of signal rate in these tails through the normalization factor $N$.

2. We now replace the usual Breit-Wigner propagator with the (square-root) of the Gaussian distribution:

    \begin{equation}
    \frac{1}{p^2-m_H^2+i \Gamma m_H} \rightarrow \big[\frac{N}{N_{BW}}\frac{1}{\sqrt{2\pi}\sigma}\big]^{1/2} \text{Exp}\left[-\frac{1}{4} \left(\frac{m-m_H}{\sigma}\right)^2\right].
    \end{equation}

    The normalization of the Breit-Wigner is given by  $N_{BW}\approx 2 m_H^2 \Gamma_H / \pi $.  (Here I used $\Gamma_H \ll m_H$. For full formula see [Wikipedia](https://en.wikipedia.org/wiki/Relativistic_Breit%E2%80%93Wigner_distribution)).

    This can simply be implemented in the file `propagators.py` of the UFO model. First define the new propagator. Here I use the values obtained from the fit above and choose the prefactor $(\sqrt{2\pi}\sigma N_{BW} / N)^{1/2} = 12.438$

    `denominator_Higgs="12.438*cmath.exp(0.25*(cmath.sqrt(P('mu',id)*P('mu',id))-124.7)**2/1.5245**2)" `

    Assign the new propagator to to the scalar particles

    `S = Propagator(name = "S",numerator = "complex(0,1)",denominator = denominator_Higgs)`

3. By defaukt, MadGraph doesn't use the content of `propagators.py`. So we have to make sure it's used. This can simply be done by changing the particle information in the `particles.py` file of the UFO model

    `H = Particle(pdg_code = 25, name = 'H', ... , propagator = Prop.S)`

4. Finally, we will change the `bwcutoff` in `run_card.dat` to ensure that MadGraph integrated over the desired range. The choice of `bwcutoff` depends on the choosen value of the Higgs width in the `param_card.dat`. For $\Gamma_H=4.07$ MeV I would suggest to choose `bwcutoff=2000`, which then covers an integration range of $m_H \pm 2000\times \Gamma_H = 116.9\dots 133.1$ GeV.

    Note: if you get an error message such as `IEEE_UNDERFLOW_FLAG` or `IEEE_DENORMAL`, it probably means you have choosen a too large value for `bwcutoff`

## 2. Event Generation

Load MadMiner again

```python
miner = MadMiner(debug=False)
miner.load('data/madminer_example.h5')
```

    06:41  Found 2 parameters:
    06:41     FW (LHA: anoinputs 3, maximal power in squared ME: (2,), range: (-10.0, 10.0))
    06:41     FWW (LHA: anoinputs 7, maximal power in squared ME: (2,), range: (-10.0, 10.0))
    06:41  Found 6 benchmarks:
    06:41     sm: FW = 0.00e+00, FWW = 0.00e+00
    06:41     w: FW = 2.00, FWW = 2.00
    06:41     morphing_basis_vector_2: FW = 2.14, FWW = -8.58e+00
    06:41     morphing_basis_vector_3: FW = -5.61e+00, FWW = -6.12e+00
    06:41     morphing_basis_vector_4: FW = -6.05e+00, FWW = 9.47
    06:41     morphing_basis_vector_5: FW = -7.78e+00, FWW = 0.21
    06:41  Found morphing setup with 6 components

We now run MadMiner for the signal. Note that we call a new model file from the `ufo_model_directory`.

Here we introduce a new option  `MadMiner.run_multiple()`, which allows to start multiple runs with different run cards or different choices of `sample_benchmark`. In the following example, we will run MadMiner `nrun` times with the same run_card and the same sampling benchmark.

```python
nrun=5

miner.run_multiple(
    mg_directory=mg_dir,
    log_directory='logs/wbf_signal',
    mg_process_directory='./mg_processes/wbf_signal',
    proc_card_file='cards/proc_card_wbf_signal.dat',
    param_card_template_file='cards/param_card_template.dat',
    run_card_files=['cards/run_card_wbf_signal.dat' for i in range(nrun)],
    pythia8_card_file=None,
    ufo_model_directory='cards/dim6_for_information_parton',
    sample_benchmarks=['sm'],
    initial_command='source ~/.bashrc'
)
```

    06:41  Generating MadGraph process folder from cards/proc_card_wbf_signal.dat at ./mg_processes/wbf_signal
    06:41  Run 0
    06:41    Sampling from benchmark: sm
    06:41    Original run card:       cards/run_card_wbf_signal.dat
    06:41    Original Pythia8 card:   None
    06:41    Copied run card:         /madminer/cards/run_card_0.dat
    06:41    Copied Pythia8 card:     None
    06:41    Param card:              /madminer/cards/param_card_0.dat
    06:41    Reweight card:           /madminer/cards/reweight_card_0.dat
    06:41    Log file:                run_0.log
    06:41  Creating param and reweight cards in ./mg_processes/wbf_signal//madminer/cards/param_card_0.dat, ./mg_processes/wbf_signal//madminer/cards/reweight_card_0.dat
    06:41  Starting MadGraph and Pythia in ./mg_processes/wbf_signal
    06:45  Run 1
    06:45    Sampling from benchmark: sm
    06:45    Original run card:       cards/run_card_wbf_signal.dat
    06:45    Original Pythia8 card:   None
    06:45    Copied run card:         /madminer/cards/run_card_1.dat
    06:45    Copied Pythia8 card:     None
    06:45    Param card:              /madminer/cards/param_card_1.dat
    06:45    Reweight card:           /madminer/cards/reweight_card_1.dat
    06:45    Log file:                run_1.log
    06:45  Creating param and reweight cards in ./mg_processes/wbf_signal//madminer/cards/param_card_1.dat, ./mg_processes/wbf_signal//madminer/cards/reweight_card_1.dat
    06:45  Starting MadGraph and Pythia in ./mg_processes/wbf_signal
    06:51  Run 2
    06:51    Sampling from benchmark: sm
    06:51    Original run card:       cards/run_card_wbf_signal.dat
    06:51    Original Pythia8 card:   None
    06:51    Copied run card:         /madminer/cards/run_card_2.dat
    06:51    Copied Pythia8 card:     None
    06:51    Param card:              /madminer/cards/param_card_2.dat
    06:51    Reweight card:           /madminer/cards/reweight_card_2.dat
    06:51    Log file:                run_2.log
    06:51  Creating param and reweight cards in ./mg_processes/wbf_signal//madminer/cards/param_card_2.dat, ./mg_processes/wbf_signal//madminer/cards/reweight_card_2.dat
    06:51  Starting MadGraph and Pythia in ./mg_processes/wbf_signal
    06:56  Run 3
    06:56    Sampling from benchmark: sm
    06:56    Original run card:       cards/run_card_wbf_signal.dat
    06:56    Original Pythia8 card:   None
    06:56    Copied run card:         /madminer/cards/run_card_3.dat
    06:56    Copied Pythia8 card:     None
    06:56    Param card:              /madminer/cards/param_card_3.dat
    06:56    Reweight card:           /madminer/cards/reweight_card_3.dat
    06:56    Log file:                run_3.log
    06:56  Creating param and reweight cards in ./mg_processes/wbf_signal//madminer/cards/param_card_3.dat, ./mg_processes/wbf_signal//madminer/cards/reweight_card_3.dat
    06:56  Starting MadGraph and Pythia in ./mg_processes/wbf_signal
    07:00  Run 4
    07:00    Sampling from benchmark: sm
    07:00    Original run card:       cards/run_card_wbf_signal.dat
    07:00    Original Pythia8 card:   None
    07:00    Copied run card:         /madminer/cards/run_card_4.dat
    07:00    Copied Pythia8 card:     None
    07:00    Param card:              /madminer/cards/param_card_4.dat
    07:00    Reweight card:           /madminer/cards/reweight_card_4.dat
    07:00    Log file:                run_4.log
    07:00  Creating param and reweight cards in ./mg_processes/wbf_signal//madminer/cards/param_card_4.dat, ./mg_processes/wbf_signal//madminer/cards/reweight_card_4.dat
    07:00  Starting MadGraph and Pythia in ./mg_processes/wbf_signal

It is possible to start multiple processes based on the same `MadMiner` instance. This can be used to combine samples sampled according to different benchmarks, and to add reducible backgrounds.

For the latter, a useful option is the `is_background` switch, which should be used for processes that do *not* depend on the parameters theta. `is_background=True` will disable the reweighting and re-use the same weights for all cross sections.

```python
miner.run_multiple(
    mg_directory=mg_dir,
    log_directory='logs/ggF_background',
    mg_process_directory='./mg_processes/ggF_background',
    proc_card_file='cards/proc_card_ggF_background.dat',
    param_card_template_file='cards/param_card_template.dat',
    run_card_files=['cards/run_card_ggF_background.dat' for i in range(nrun)],
    pythia8_card_file=None,
    ufo_model_directory='cards/dim6_for_information_parton',
    sample_benchmarks=['sm'],
    is_background=True,
    initial_command='source ~/.bashrc'
)
```

    07:04  Generating MadGraph process folder from cards/proc_card_ggF_background.dat at ./mg_processes/ggF_background
    07:04  Run 0
    07:04    Sampling from benchmark: sm
    07:04    Original run card:       cards/run_card_ggF_background.dat
    07:04    Original Pythia8 card:   None
    07:04    Copied run card:         /madminer/cards/run_card_0.dat
    07:04    Copied Pythia8 card:     None
    07:04    Param card:              /madminer/cards/param_card_0.dat
    07:04    Reweight card:           /madminer/cards/reweight_card_0.dat
    07:04    Log file:                run_0.log
    07:04  Creating param and reweight cards in ./mg_processes/ggF_background//madminer/cards/param_card_0.dat, ./mg_processes/ggF_background//madminer/cards/reweight_card_0.dat
    07:04  Starting MadGraph and Pythia in ./mg_processes/ggF_background
    07:17  Run 1
    07:17    Sampling from benchmark: sm
    07:17    Original run card:       cards/run_card_ggF_background.dat
    07:17    Original Pythia8 card:   None
    07:17    Copied run card:         /madminer/cards/run_card_1.dat
    07:17    Copied Pythia8 card:     None
    07:17    Param card:              /madminer/cards/param_card_1.dat
    07:17    Reweight card:           /madminer/cards/reweight_card_1.dat
    07:17    Log file:                run_1.log
    07:17  Creating param and reweight cards in ./mg_processes/ggF_background//madminer/cards/param_card_1.dat, ./mg_processes/ggF_background//madminer/cards/reweight_card_1.dat
    07:17  Starting MadGraph and Pythia in ./mg_processes/ggF_background
    07:31  Run 2
    07:31    Sampling from benchmark: sm
    07:31    Original run card:       cards/run_card_ggF_background.dat
    07:31    Original Pythia8 card:   None
    07:31    Copied run card:         /madminer/cards/run_card_2.dat
    07:31    Copied Pythia8 card:     None
    07:31    Param card:              /madminer/cards/param_card_2.dat
    07:31    Reweight card:           /madminer/cards/reweight_card_2.dat
    07:31    Log file:                run_2.log
    07:31  Creating param and reweight cards in ./mg_processes/ggF_background//madminer/cards/param_card_2.dat, ./mg_processes/ggF_background//madminer/cards/reweight_card_2.dat
    07:31  Starting MadGraph and Pythia in ./mg_processes/ggF_background
    07:48  Run 3
    07:48    Sampling from benchmark: sm
    07:48    Original run card:       cards/run_card_ggF_background.dat
    07:48    Original Pythia8 card:   None
    07:48    Copied run card:         /madminer/cards/run_card_3.dat
    07:48    Copied Pythia8 card:     None
    07:48    Param card:              /madminer/cards/param_card_3.dat
    07:48    Reweight card:           /madminer/cards/reweight_card_3.dat
    07:48    Log file:                run_3.log
    07:48  Creating param and reweight cards in ./mg_processes/ggF_background//madminer/cards/param_card_3.dat, ./mg_processes/ggF_background//madminer/cards/reweight_card_3.dat
    07:48  Starting MadGraph and Pythia in ./mg_processes/ggF_background
    08:03  Run 4
    08:03    Sampling from benchmark: sm
    08:03    Original run card:       cards/run_card_ggF_background.dat
    08:03    Original Pythia8 card:   None
    08:03    Copied run card:         /madminer/cards/run_card_4.dat
    08:03    Copied Pythia8 card:     None
    08:03    Param card:              /madminer/cards/param_card_4.dat
    08:03    Reweight card:           /madminer/cards/reweight_card_4.dat
    08:03    Log file:                run_4.log
    08:03  Creating param and reweight cards in ./mg_processes/ggF_background//madminer/cards/param_card_4.dat, ./mg_processes/ggF_background//madminer/cards/reweight_card_4.dat
    08:03  Starting MadGraph and Pythia in ./mg_processes/ggF_background

## 3. Extract Parton Level Observables / Weights

### 3a) Defining observables

Let us first define observables. We will do that as a function, which we then can use for both signal and backgrounds.

```python
def setup_observables(lhep):
    lhep.add_observable('pt_jhard', '(p[0].pt>p[1].pt)*p[0].pt+(p[0].pt<p[1].pt)*p[1].pt')
    lhep.add_observable('pt_jsoft', '(p[0].pt<p[1].pt)*p[0].pt+(p[0].pt>p[1].pt)*p[1].pt')
    lhep.add_observable('dphijj' , 'abs(p[0].deltaphi(p[1]))')
    lhep.add_observable('m_aa' , '(p[2] + p[3]).m')
    lhep.add_observable('m_jj' , '(p[0] + p[1]).m')
    lhep.add_observable('m_jjaa', '(p[0] + p[1] + p[2] + p[3]).m')
    lhep.add_observable('deta_jj', 'abs(p[0].eta - p[1].eta)')

    lhep.add_observable('px_j1', 'p[0].px')
    lhep.add_observable('px_j2', 'p[1].px')
    lhep.add_observable('px_a1', 'p[2].px')
    lhep.add_observable('px_a2', 'p[3].px')
    lhep.add_observable('px_h' , '(p[2]+p[3]).px')

    lhep.add_observable('py_j1', 'p[0].py')
    lhep.add_observable('py_j2', 'p[1].py')
    lhep.add_observable('py_a1', 'p[2].py')
    lhep.add_observable('py_a2', 'p[3].py')
    lhep.add_observable('py_h' , '(p[2]+p[3]).py')

    lhep.add_observable('pz_j1', 'p[0].pz')
    lhep.add_observable('pz_j2', 'p[1].pz')
    lhep.add_observable('pz_a1', 'p[2].pz')
    lhep.add_observable('pz_a2', 'p[3].pz')
    lhep.add_observable('pz_h' , '(p[2]+p[3]).pz')

    lhep.add_observable('en_j1', 'p[0].e')
    lhep.add_observable('en_j2', 'p[1].e')
    lhep.add_observable('en_a1', 'p[2].e')
    lhep.add_observable('en_a2', 'p[3].e')
    lhep.add_observable('en_h' , '(p[2]+p[3]).e')

    lhep.add_observable('pt_j1', 'p[0].pt')
    lhep.add_observable('pt_j2', 'p[1].pt')
    lhep.add_observable('pt_a1', 'p[2].pt')
    lhep.add_observable('pt_a2', 'p[3].pt')
    lhep.add_observable('pt_h' , '(p[2]+p[3]).pt')

    lhep.add_observable('eta_j1', 'p[0].eta')
    lhep.add_observable('eta_j2', 'p[1].eta')
    lhep.add_observable('eta_a1', 'p[2].eta')
    lhep.add_observable('eta_a2', 'p[3].eta')
    lhep.add_observable('eta_h' , '(p[2]+p[3]).eta')

    lhep.add_observable('dphi_j1j2', 'p[0].deltaphi(p[1])')
    lhep.add_observable('dphi_j1j2', 'p[0].deltaphi(p[1])')
    lhep.add_observable('dphi_a1a2', 'p[2].deltaphi(p[3])')
    lhep.add_observable('dphi_j1h' , 'p[0].deltaphi(p[2]+p[3])')
    lhep.add_observable('dphi_j2h' , 'p[1].deltaphi(p[2]+p[3])')
```

### 3b) Setting up and run LHEProcessor for Signal

Set up the processor. Here we include multiple LHE files

```python
lhep = LHEProcessor()
for run in range (nrun):
    run_str = str(run+1)
    if len(run_str) < 2:
        run_str = '0' + run_str
    lhep.add_lhe_sample(
        'mg_processes/wbf_signal/Events/run_{}/unweighted_events.lhe.gz'.format(run_str),
        sampling_benchmark="sm",
        rescale_factor=1./nrun)
lhep.read_benchmark_names('data/madminer_example.h5')
```

    08:20  Adding LHE sample at mg_processes/wbf_signal/Events/run_01/unweighted_events.lhe.gz
    08:20  Adding LHE sample at mg_processes/wbf_signal/Events/run_02/unweighted_events.lhe.gz
    08:20  Adding LHE sample at mg_processes/wbf_signal/Events/run_03/unweighted_events.lhe.gz
    08:20  Adding LHE sample at mg_processes/wbf_signal/Events/run_04/unweighted_events.lhe.gz
    08:20  Adding LHE sample at mg_processes/wbf_signal/Events/run_05/unweighted_events.lhe.gz

Add observbales, analyse samples and save

```python
setup_observables(lhep)
```

    08:20  Adding (not required) observable pt_jhard = (p[0].pt>p[1].pt)*p[0].pt+(p[0].pt<p[1].pt)*p[1].pt
    08:20  Adding (not required) observable pt_jsoft = (p[0].pt<p[1].pt)*p[0].pt+(p[0].pt>p[1].pt)*p[1].pt
    08:20  Adding (not required) observable dphijj = abs(p[0].deltaphi(p[1]))
    08:20  Adding (not required) observable m_aa = (p[2] + p[3]).m
    08:20  Adding (not required) observable m_jj = (p[0] + p[1]).m
    08:20  Adding (not required) observable m_jjaa = (p[0] + p[1] + p[2] + p[3]).m
    08:20  Adding (not required) observable deta_jj = abs(p[0].eta - p[1].eta)
    08:20  Adding (not required) observable px_j1 = p[0].px
    08:20  Adding (not required) observable px_j2 = p[1].px
    08:20  Adding (not required) observable px_a1 = p[2].px
    08:20  Adding (not required) observable px_a2 = p[3].px
    08:20  Adding (not required) observable px_h = (p[2]+p[3]).px
    08:20  Adding (not required) observable py_j1 = p[0].py
    08:20  Adding (not required) observable py_j2 = p[1].py
    08:20  Adding (not required) observable py_a1 = p[2].py
    08:20  Adding (not required) observable py_a2 = p[3].py
    08:20  Adding (not required) observable py_h = (p[2]+p[3]).py
    08:20  Adding (not required) observable pz_j1 = p[0].pz
    08:20  Adding (not required) observable pz_j2 = p[1].pz
    08:20  Adding (not required) observable pz_a1 = p[2].pz
    08:20  Adding (not required) observable pz_a2 = p[3].pz
    08:20  Adding (not required) observable pz_h = (p[2]+p[3]).pz
    08:20  Adding (not required) observable en_j1 = p[0].e
    08:20  Adding (not required) observable en_j2 = p[1].e
    08:20  Adding (not required) observable en_a1 = p[2].e
    08:20  Adding (not required) observable en_a2 = p[3].e
    08:20  Adding (not required) observable en_h = (p[2]+p[3]).e
    08:20  Adding (not required) observable pt_j1 = p[0].pt
    08:20  Adding (not required) observable pt_j2 = p[1].pt
    08:20  Adding (not required) observable pt_a1 = p[2].pt
    08:20  Adding (not required) observable pt_a2 = p[3].pt
    08:20  Adding (not required) observable pt_h = (p[2]+p[3]).pt
    08:20  Adding (not required) observable eta_j1 = p[0].eta
    08:20  Adding (not required) observable eta_j2 = p[1].eta
    08:20  Adding (not required) observable eta_a1 = p[2].eta
    08:20  Adding (not required) observable eta_a2 = p[3].eta
    08:20  Adding (not required) observable eta_h = (p[2]+p[3]).eta
    08:20  Adding (not required) observable dphi_j1j2 = p[0].deltaphi(p[1])
    08:20  Adding (not required) observable dphi_j1j2 = p[0].deltaphi(p[1])
    08:20  Adding (not required) observable dphi_a1a2 = p[2].deltaphi(p[3])
    08:20  Adding (not required) observable dphi_j1h = p[0].deltaphi(p[2]+p[3])
    08:20  Adding (not required) observable dphi_j2h = p[1].deltaphi(p[2]+p[3])

```python
lhep.analyse_lhe_samples()
lhep.save('data/madminer_lhedata_wbf_signal.h5', 'data/madminer_example.h5')
```

    08:20  Analysing LHE sample mg_processes/wbf_signal/Events/run_01/unweighted_events.lhe.gz
    08:20  Analysing LHE sample mg_processes/wbf_signal/Events/run_02/unweighted_events.lhe.gz
    08:21  Analysing LHE sample mg_processes/wbf_signal/Events/run_03/unweighted_events.lhe.gz
    08:21  Analysing LHE sample mg_processes/wbf_signal/Events/run_04/unweighted_events.lhe.gz
    08:21  Analysing LHE sample mg_processes/wbf_signal/Events/run_05/unweighted_events.lhe.gz
    08:22  Loading HDF5 data from data/madminer_example.h5 and saving file to data/madminer_lhedata_wbf_signal.h5

Let's make a quick cross check by plotting a distributions (Warning: plot's don't look very pretty)

```python
mycolors=["black","red","orange","yellow","green","blue","purple"]

fig = plt.figure(figsize=(5,5))
for i, weights in enumerate(lhep.weights):
    plt.hist(lhep.observations['m_jj'], range=(0.,5000.), bins=20, histtype='step', weights=weights,color=mycolors[i])
plt.show()

fig = plt.figure(figsize=(5,5))
for i, weights in enumerate(lhep.weights):
    plt.hist(lhep.observations['dphi_j1j2'], range=(-3.2,3.2), bins=20, histtype='step', weights=weights,color=mycolors[i])
plt.show()
```

### 3c) Cross Check: Does the smearing work?

Here I explicitly check the smearing of the mass peak and compare it to the wanted distribution obtained from the experimental collaboration.

```python
#Define my fitting function
def myfunction(x, mean, amplitude, standard_deviation):
    return amplitude/np.sqrt(2.0*3.1415*standard_deviation**2) * np.exp( - 0.5*((x - mean) / standard_deviation) ** 2)

#Function to do the plotting
def smearing_validation_plot(filename,lheprocessor,inputrange,label):

    #Get Data from Experiment / MadMiner
    exp_data , exp_weights = np.loadtxt(filename)[:,0] , np.loadtxt(filename)[:,1]
    madminer_data , madminer_weights = lheprocessor.observations['m_aa'] , lheprocessor.weights[0]

    #Plot Exp. Data
    fig = plt.figure(figsize=(5,5))
    bin_heights, bin_borders, _ = plt.hist(exp_data, weights=exp_weights,normed=True,
        range=(inputrange[0],inputrange[1]), bins=inputrange[2],
        histtype='step',color='Red',label=label)

    #Fit to Experiment
    bin_centers = bin_borders[:-1] + np.diff(bin_borders) / 2
    bestfit, _ = curve_fit(myfunction, bin_centers, bin_heights, p0=[125, 1, 1])
    fitplotrange=np.arange(inputrange[0], inputrange[1], 0.01)
    plt.plot(fitplotrange,myfunction(fitplotrange,bestfit[0],bestfit[1],bestfit[2]),
        color='Black',linestyle='dashed', label='Fit to Experiment')

    #Plot MadMiner
    norm = sum(madminer_weights)
    print ('The total cross section is: %.6f pb'%(norm),)
    bin_heights_mm, _ , _ = plt.hist(madminer_data,
        weights = madminer_weights/norm*inputrange[2]/(inputrange[1]-inputrange[0])*bestfit[1],
        range=(inputrange[0],inputrange[1]), bins=inputrange[2],
         histtype='step',color='Blue',label='MadMiner')

    #Finish Plot
    plt.legend(loc='upper left')
    upperbound=max(max(bin_heights),max(bin_heights_mm))
    plt.text(inputrange[0],0.75*upperbound,
         r'$\frac{N}{\sqrt{2\pi}\sigma}\exp\left[-\frac{1}{2}\left(\frac{m-m_H}{\sigma}\right)^2\right]$',
         fontsize=14)
    plt.text(inputrange[0],0.65*upperbound,r'$m_H$=%.3f'%(bestfit[0]),fontsize=14)
    plt.text(inputrange[0],0.60*upperbound,r'$N$=%.3f'%(bestfit[1]),fontsize=14)
    plt.text(inputrange[0],0.55*upperbound,r'$\sigma$=%.3f'%(bestfit[2]),fontsize=14)

    plt.show()

#Call Function
smearing_validation_plot('cards/smearing_data/h_2_aa.txt',lhep,[115,130,30],'CMS: PAS HIG-15-005')
```

### 3d) Setting up and run LHEProcessor for Background

Repeat procedure for ggF background

```python
lhep = LHEProcessor()

for run in range (nrun):
    run_str = str(run+1)
    if len(run_str) < 2:
        run_str = '0' + run_str
    lhep.add_lhe_sample(
        'mg_processes/ggF_background/Events/run_{}/unweighted_events.lhe.gz'.format(run_str),
        sampling_benchmark="sm",
        is_background=True,
        rescale_factor=1./nrun)

lhep.read_benchmark_names('data/madminer_example.h5')
setup_observables(lhep)
lhep.analyse_lhe_samples()
lhep.save('data/madminer_lhedata_ggF_background.h5', 'data/madminer_example.h5')
```

    08:22  Adding LHE sample at mg_processes/ggF_background/Events/run_01/unweighted_events.lhe.gz
    08:22  Adding LHE sample at mg_processes/ggF_background/Events/run_02/unweighted_events.lhe.gz
    08:22  Adding LHE sample at mg_processes/ggF_background/Events/run_03/unweighted_events.lhe.gz
    08:22  Adding LHE sample at mg_processes/ggF_background/Events/run_04/unweighted_events.lhe.gz
    08:22  Adding LHE sample at mg_processes/ggF_background/Events/run_05/unweighted_events.lhe.gz
    08:22  Adding (not required) observable pt_jhard = (p[0].pt>p[1].pt)*p[0].pt+(p[0].pt<p[1].pt)*p[1].pt
    08:22  Adding (not required) observable pt_jsoft = (p[0].pt<p[1].pt)*p[0].pt+(p[0].pt>p[1].pt)*p[1].pt
    08:22  Adding (not required) observable dphijj = abs(p[0].deltaphi(p[1]))
    08:22  Adding (not required) observable m_aa = (p[2] + p[3]).m
    08:22  Adding (not required) observable m_jj = (p[0] + p[1]).m
    08:22  Adding (not required) observable m_jjaa = (p[0] + p[1] + p[2] + p[3]).m
    08:22  Adding (not required) observable deta_jj = abs(p[0].eta - p[1].eta)
    08:22  Adding (not required) observable px_j1 = p[0].px
    08:22  Adding (not required) observable px_j2 = p[1].px
    08:22  Adding (not required) observable px_a1 = p[2].px
    08:22  Adding (not required) observable px_a2 = p[3].px
    08:22  Adding (not required) observable px_h = (p[2]+p[3]).px
    08:22  Adding (not required) observable py_j1 = p[0].py
    08:22  Adding (not required) observable py_j2 = p[1].py
    08:22  Adding (not required) observable py_a1 = p[2].py
    08:22  Adding (not required) observable py_a2 = p[3].py
    08:22  Adding (not required) observable py_h = (p[2]+p[3]).py
    08:22  Adding (not required) observable pz_j1 = p[0].pz
    08:22  Adding (not required) observable pz_j2 = p[1].pz
    08:22  Adding (not required) observable pz_a1 = p[2].pz
    08:22  Adding (not required) observable pz_a2 = p[3].pz
    08:22  Adding (not required) observable pz_h = (p[2]+p[3]).pz
    08:22  Adding (not required) observable en_j1 = p[0].e
    08:22  Adding (not required) observable en_j2 = p[1].e
    08:22  Adding (not required) observable en_a1 = p[2].e
    08:22  Adding (not required) observable en_a2 = p[3].e
    08:22  Adding (not required) observable en_h = (p[2]+p[3]).e
    08:22  Adding (not required) observable pt_j1 = p[0].pt
    08:22  Adding (not required) observable pt_j2 = p[1].pt
    08:22  Adding (not required) observable pt_a1 = p[2].pt
    08:22  Adding (not required) observable pt_a2 = p[3].pt
    08:22  Adding (not required) observable pt_h = (p[2]+p[3]).pt
    08:22  Adding (not required) observable eta_j1 = p[0].eta
    08:22  Adding (not required) observable eta_j2 = p[1].eta
    08:22  Adding (not required) observable eta_a1 = p[2].eta
    08:22  Adding (not required) observable eta_a2 = p[3].eta
    08:22  Adding (not required) observable eta_h = (p[2]+p[3]).eta
    08:22  Adding (not required) observable dphi_j1j2 = p[0].deltaphi(p[1])
    08:22  Adding (not required) observable dphi_j1j2 = p[0].deltaphi(p[1])
    08:22  Adding (not required) observable dphi_a1a2 = p[2].deltaphi(p[3])
    08:22  Adding (not required) observable dphi_j1h = p[0].deltaphi(p[2]+p[3])
    08:22  Adding (not required) observable dphi_j2h = p[1].deltaphi(p[2]+p[3])
    08:22  Analysing LHE sample mg_processes/ggF_background/Events/run_01/unweighted_events.lhe.gz
    08:22  Analysing LHE sample mg_processes/ggF_background/Events/run_02/unweighted_events.lhe.gz
    08:22  Analysing LHE sample mg_processes/ggF_background/Events/run_03/unweighted_events.lhe.gz
    08:22  Analysing LHE sample mg_processes/ggF_background/Events/run_04/unweighted_events.lhe.gz
    08:23  Analysing LHE sample mg_processes/ggF_background/Events/run_05/unweighted_events.lhe.gz
    08:23  Loading HDF5 data from data/madminer_example.h5 and saving file to data/madminer_lhedata_ggF_background.h5

Cross check plots

```python
mycolors=["black","red","orange","yellow","green","blue","purple"]

fig = plt.figure(figsize=(5,5))
for i, weights in enumerate(lhep.weights):
    plt.hist(lhep.observations['m_jj'], range=(0.,5000.), bins=20, histtype='step', weights=weights,color=mycolors[i])
plt.show()

fig = plt.figure(figsize=(5,5))
for i, weights in enumerate(lhep.weights):
    plt.hist(lhep.observations['dphi_j1j2'], range=(-3.2,3.2), bins=20, histtype='step', weights=weights,color=mycolors[i])
plt.show()
```

Smearing Plot

```python
smearing_validation_plot('cards/smearing_data/h_2_aa.txt',lhep,[115,130,30],'CMS: PAS HIG-15-005')
```

### 3e) Plot Distributions

```python
sa_wbf = SampleAugmenter('data/madminer_lhedata_wbf_signal.h5', debug=False)
sa_ggF = SampleAugmenter('data/madminer_lhedata_ggF_background.h5', debug=False)
x_weighted_wbf, weights_wbf = sa_wbf.extract_raw_data(theta=[0.,0.])
x_weighted_ggF, weights_ggF = sa_ggF.extract_raw_data(theta=[0.,0.])

bins = 25
n_observables = x_weighted_wbf.shape[1]
n_cols = 3
n_rows = (n_observables + n_cols - 1) // n_cols
labels = sa_wbf.observables.keys()

plt.figure(figsize=(4. * n_cols, 4. * n_rows))

for i, label in enumerate(labels):
    xmin = np.percentile(x_weighted_wbf[:,i], 5.)
    xmax = np.percentile(x_weighted_wbf[:,i], 95.)
    xwidth = xmax - xmin
    xmin -= xwidth * 0.1
    xmax += xwidth * 0.1
    x_range = (xmin, xmax)

    ax = plt.subplot(n_rows, n_cols, i+1)

    plt.hist(x_weighted_wbf[:,i], weights=weights_wbf, histtype='step', range=x_range, bins=bins,
             lw=1.5, label=r'WBF signal', normed=False)
    plt.hist(x_weighted_ggF[:,i], weights=weights_ggF, histtype='step', range=x_range, bins=bins,
             lw=1.5, label=r'ggF background', normed=False)

    if i == 0:
        plt.legend()

    plt.xlabel(label)

plt.tight_layout()
plt.show()
```

### 3f) Combining Samples (if necessary)

Combine signal and background

```python
combine_and_shuffle(
    ['data/madminer_lhedata_wbf_signal.h5','data/madminer_lhedata_ggF_background.h5'],
    'data/madminer_lhedata.h5'
)
```

    08:23  Careful: this tool assumes that all samples are generated with the same setup, including identical benchmarks (and thus morphing setup). If it is used with samples with different settings, there will be wrong results! There are no explicit cross checks in place yet.
    08:23  Copying setup from data/madminer_lhedata_wbf_signal.h5 to data/madminer_lhedata.h5
    08:23  Loading samples from file 1 / 2 at data/madminer_lhedata_wbf_signal.h5
    08:23  Loading samples from file 2 / 2 at data/madminer_lhedata_ggF_background.h5


# Tutorial-IllinoisGRMHD: IllinoisGRMHD_headers.h

## Authors: Leo Werneck & Zach Etienne

**This module is currently under development**

## In this tutorial module we explain the main `IllinoisGRMHD` header file. This module will likely be absorbed by another one by the time we finish documenting the code

### Required and recommended citations:

* **(Required)** Etienne, Z. B., Paschalidis, V., Haas R., Mösta P., and Shapiro, S. L. IllinoisGRMHD: an open-source, user-friendly GRMHD code for dynamical spacetimes. Class. Quantum Grav. 32 (2015) 175009. ([arxiv:1501.07276](http://arxiv.org/abs/1501.07276)).
* **(Required)** Noble, S. C., Gammie, C. F., McKinney, J. C., Del Zanna, L. Primitive Variable Solvers for Conservative General Relativistic Magnetohydrodynamics. Astrophysical Journal, 641, 626 (2006) ([astro-ph/0512420](https://arxiv.org/abs/astro-ph/0512420)).
* **(Recommended)** Del Zanna, L., Bucciantini N., Londrillo, P. An efficient shock-capturing central-type scheme for multidimensional relativistic flows - II. Magnetohydrodynamics. A&A 400 (2) 397-413 (2003). DOI: 10.1051/0004-6361:20021641 ([astro-ph/0210618](https://arxiv.org/abs/astro-ph/0210618)).

If using the version of `IllinoisGRMHD` with piecewise polytropic *or* tabulated (coming soon!) EOS support, then the following citation is also required:

* **(Required)** Etienne, Z. B., Werneck, L., Paschalidis, V., Haas R., Mösta P., and Shapiro, S. L., *IllinoisGRMHD github repository* (2019). Source Code URL: https://github.com/zachetienne/nrpytutorial/tree/master/IllinoisGRMHD/.

### Dependencies

The files generated in this tutorial notebook depend on the following files:

* `apply_tau_floor__enforce_limits_on_primitives_and_recompute_conservs.C` \[[**tutorial**](Tutorial-IllinoisGRMHD__apply_tau_floor__enforce_limits_on_primitives_and_recompute_conservs.ipynb)\]
* `IllinoisGRMHD_convert_ADM_to_BSSN__enforce_detgtij_eq_1__and_compute_gtupij.C` \[[**tutorial**](Tutorial-IllinoisGRMHD__convert_ADM_to_BSSN__enforce_detgtij_eq_1__and_compute_gtupij.ipynb)\]
* `symmetry__set_gzs_staggered_gfs.C` \[[**tutorial**](Tutorial-IllinoisGRMHD__symmetry__set_gzs_staggered_gfs.ipynb)\]
* `IllinoisGRMHD_EoS_lowlevel_functs.C` \[[**tutorial**](Tutorial-IllinoisGRMHD__EoS_lowlevel_functs.ipynb)\]

# Table of Contents
$$\label{toc}$$

This module is organized as follows

0. [Step 0](#src_dir): **Source directory creation**
1. [Step 1](#vars_headers): **Generating `VARS` headers with NRPy+**
    1. [Step 1.a](#load_python_nrpy_modules): *Load necessary Python/NRPy+ modules*
    1. [Step 1.b](#adm_3metric_vars): *The `ADM_3METRIC_VARS.h` file*
    1. [Step 1.c](#conf_metric_facevals_vars): *The `CONF_METRIC_FACEVALS_VARS.h` file*
    1. [Step 1.d](#grmhd_vars): *The `GRMHD_VARS.h` file*
    1. [Step 1.e](#interp_vars): *The `INTERP_VARS.h` file*
    1. [Step 1.f](#smallb_and_conservs_vars): *The `SMALLB_VARS.h` and  `CONSERV_VARS.h` files*
    1. [Step 1.g](#tmunu_vars): *The `TMUNU_VARS.h` files*
1. [Step 2](#igm_headers__h): **`IllinoisGRMHD_headers.h`**
1. [Step 3](#code_validation): **Code validation**
1. [Step 4](#latex_pdf_output): **Output this notebook to $\LaTeX$-formatted PDF file**

# Step 0: Source directory creation \[Back to [top](#toc)\]
$$\label{src_dir}$$

We will now use the [cmdline_helper.py NRPy+ module](Tutorial-Tutorial-cmdline_helper.ipynb) to create the source directory within the `IllinoisGRMHD` NRPy+ directory, if it does not exist yet.

```python
# Step 0: Creation of the IllinoisGRMHD source directory
# Step 0a: Add NRPy's directory to the path
# https://stackoverflow.com/questions/16780014/import-file-from-parent-directory
import os,sys
nrpy_dir_path = os.path.join("..","..")
if nrpy_dir_path not in sys.path:
    sys.path.append(nrpy_dir_path)

# Step 0b: Load up cmdline_helper and create the directory
import cmdline_helper as cmd
outdir = os.path.join("..","src")
cmd.mkdir(outdir)

# Step 0c: Set up header file output path
NRPy_headers_dir_path = os.path.join(outdir,"NRPy_generated_headers")
cmd.mkdir(NRPy_headers_dir_path)
```

# Step 1: Generating `VARS` headers with NRPy+ \[Back to [top](#toc)\]
$$\label{vars_headers}$$

We will now use NRPy+ to generate all the `VARS` header files we need in the `IllinoisGRMHD_headers.h` file.

## Step 1.a: Load necessary Python/NRPy+ modules \[Back to [top](#toc)\]
$$\label{load_python_nrpy_modules}$$

We now load all necessary Python/NRPy+ modules needed to generate the `VARS` header files.

```python
# Imported needed Python modules
import sympy as sp # Python module: symbolic expressions capabilities

# Imported needed NRPy+ modules
import IllinoisGRMHD_output_functions as IGMout # NRPy+ module: IllinoisGRMHD output file functions
```

## Step 1.b: The `ADM_3METRIC_VARS.h` file \[Back to [top](#toc)\]
$$\label{adm_3metric_vars}$$

We will now generate the `ADM_3METRIC_VARS.h` file, which sets integers so that we can locate specific variables inside the `ADM_3METRIC` array. For example, the lapse variable $\alpha$ is contained in the array element `ADM_3METRIC[0]`, so we define a variable
```c
const static int ALPHA = 0;
```
which allow us to access the same array element via `ADM_3METRIC[ALPHA]`. We remind the reader that the `ADM_3METRIC` array contains the following quantities (in order):

$$
\left(
\alpha,
\beta^{x},\beta^{y},\beta^{z},
\gamma_{xx},\gamma_{xy},\gamma_{xz},\gamma_{yy},\gamma_{yz},\gamma_{zz},
\gamma^{xx},\gamma^{xy},\gamma^{xz},\gamma^{yy},\gamma^{yz},\gamma^{zz},
\sqrt{\gamma}
\right)\ .
$$

```python
# Step 1.b: Declare basic ADM variables to be used by IllinoisGRMHD
# Step 1.b.i: Set spatial dimension to 3
DIM = 3

# Step 1.b.ii: Set up alpha
gfslist = [["ALPHA"]]
# Step 1.b.iii: Set up beta^{i}
for i in range(DIM):
    gfslist.append(["BETA"+chr(ord('X')+i)])

# Step 1.b.iv: Set up gamma_{ij}
for i in range(DIM):
    for j in range(i,DIM):
        gfslist.append(["GAMMA"+chr(ord('X')+i)+chr(ord('X')+j)])

# Step 1.b.v: Set up gamma^{ij}
for i in range(3):
    for j in range(i,3):
        gfslist.append(["GAMMAUP"+chr(ord('X')+i)+chr(ord('X')+j)])

# Step 1.b.vi: Set up \sqrt{\gamma}
gfslist.append(["SQRTGAMMA"])

# Step 1.b.vii: Set up NUMVARS_FOR_ADM_3METRIC, which is the
#               number of variables in the gfslist array
gfslist.append(["NUMVARS_FOR_ADM_3METRIC"])

# Step 1.b.viii: Output to file
comment = "/* ADM_3METRIC variables */\n"
filename = "ADM_3METRIC_VARS.h"
IGMout.generate_variable_definition_file(gfslist,filename,comment=comment)
```

    Just generated the file: ../src/NRPy_generated_headers/ADM_3METRIC_VARS.h

## Step 1.c: The `CONF_METRIC_FACEVALS_VARS.h` file \[Back to [top](#toc)\]
$$\label{conf_metric_facevals_vars}$$

We will now generate the `CONF_METRIC_FACEVALS_VARS.h` file, which sets integers so that we can locate specific variables inside the `CONF_METRIC` and `FACEVALS` arrays. This header file replaces the following piece of code

```c
// The order here MATTERS, as we assume that GAMMAUPXX+1=GAMMAUPYY, etc.
static const int PHI=0,PSI=1,GAMMATILDEXX=2,GAMMATILDEXY=3,GAMMATILDEXZ=4,GAMMATILDEYY=5,GAMMATILDEYZ=6,GAMMATILDEZZ=7,
  LAPM1=8,SHIFTX=9,SHIFTY=10,SHIFTZ=11,GAMMATILDEUPXX=12,GAMMATILDEUPYY=13,GAMMATILDEUPZZ=14,
  NUMVARS_FOR_METRIC_FACEVALS=15; //<-- Be _sure_ to set this correctly, or you'll have memory access bugs!

// These are not used for facevals in the reconstruction step, but boy are they useful anyway.
static const int GAMMAUPXY=15,GAMMAUPXZ=16,GAMMAUPYZ=17,
  NUMVARS_FOR_METRIC=18; //<-- Be _sure_ to set this correctly, or you'll have memory access bugs!
```

with a new addition: we rename *all* conformal metric array quantities by adding a `CM_` string before their names.

```python
# Start setting up the gridfunction indices with phi and psi
gfslist = [["CM_PHI"],["CM_PSI"]]

# Add the indices for \tilde{\gamma}_{ij}
for i in range(DIM):
    for j in range(i,DIM):
        gfslist.append(["CM_GAMMATILDE"+chr(ord('X')+i)+chr(ord('X')+j)])

# Add alpha, \beta^{i}, and \tilde{\gamma}^{ii}
gfslist.append(["CM_LAPM1"])
for i in range(DIM):
    gfslist.append(["CM_SHIFT"+chr(ord('X')+i)])

for i in range(DIM):
    gfslist.append(["CM_GAMMATILDEUP"+chr(ord('X')+i)+chr(ord('X')+i)])

gfslist.append(["NUMVARS_FOR_CONF_METRIC_FACEVALS"])
othervars =     [["CM_GAMMATILDEUPXY"]]
othervars.append(["CM_GAMMATILDEUPXZ"])
othervars.append(["CM_GAMMATILDEUPYZ"])

# Set up extra code, not supported by the file generating function
extra = "/* Other useful variables */\n"
for j in range(len(othervars)):
    extra += "static const int "+othervars[j][0]
    for k in range(len("NUMVARS_FOR_CONF_METRIC_FACEVALS") - len(othervars[j][0])):
        extra += " "
    extra += " = "+str(j+len(gfslist)-1)+";\n"
extra += "static const int NUMVARS_FOR_CONF_METRIC          = "+str(len(gfslist)+len(othervars)-1)+";\n\n"

# Set up comments
comment = "/* Variables used for face value reconstructions */\n"
# Set up output file name
filename = "CONF_METRIC_FACEVALS_VARS.h"
# Generate variable definition file
IGMout.generate_variable_definition_file(gfslist,filename,comment=comment,extra=extra)
```

    Just generated the file: ../src/NRPy_generated_headers/CONF_METRIC_FACEVALS_VARS.h

## Step 1.d: The `GRMHD_VARS.h` file \[Back to [top](#toc)\]
$$\label{grmhd_vars}$$

We will now generate the `GRMHD_VARS.h` file, which sets integers so that we can locate specific variables inside the many variations of the primitive arrays (e.g. `prims`, `PRIMS`, `IN_PRIMS`, `OUT_PRIMS_R`, etc). This header file replaces the following piece of code

```c
// The order here MATTERS, and must be consistent with the order in the IN_PRIMS[] array in driver_evaluate_MHD_rhs.C.
static const int RHOB=0,PRESSURE=1,VX=2,VY=3,VZ=4,
  BX_CENTER=5,BY_CENTER=6,BZ_CENTER=7,BX_STAGGER=8,BY_STAGGER=9,BZ_STAGGER=10,
  VXR=11,VYR=12,VZR=13,VXL=14,VYL=15,VZL=16,MAXNUMVARS=17;  //<-- Be _sure_ to define MAXNUMVARS appropriately!
static const int UT=0,UX=1,UY=2,UZ=3;
```

```python
# Add primitives
gfslist =  [["RHOB"],["PRESSURE"],
            ["VX"],["VY"],["VZ"],
            ["BX_CENTER"],["BY_CENTER"],["BZ_CENTER"],
            ["BX_STAGGER"],["BY_STAGGER"],["BZ_STAGGER"],
            ["VXR"],["VYR"],["VZR"],
            ["VXL"],["VYL"],["VZL"],
            ["MAXNUMVARS"]]

# Finally, the 4-velocity
u4list = ["UT","UX","UY","UZ"]

extra = "/* 4-velocity */\n"
for mu in range(4):
    extra += "static const int "+u4list[mu]
    for k in range(len("MAXNUMVARS")-len(u4list[mu])):
        extra += " "
    extra += " = "+str(mu)+";\n"

# Set up comments
comment = "/* GRMHD variables */\n"
# Set up output file name
filename = "GRMHD_VARS.h"
# Generate variable definition file
IGMout.generate_variable_definition_file(gfslist,filename,comment=comment,extra=extra)
```

    Just generated the file: ../src/NRPy_generated_headers/GRMHD_VARS.h

## Step 1.e: The `INTERP_VARS.h` file \[Back to [top](#toc)\]
$$\label{interp_vars}$$

We will now generate the `GRMHD_VARS.h` file, which sets integers so that we can locate specific variables inside the many variations of the interpolation array `INTERP_VARS`. This header file replaces the following piece of code

```c
// The "I" suffix denotes interpolation. In other words, these
//    definitions are used for interpolation ONLY. The order here
//    matters as well!
static const int SHIFTXI=0,SHIFTYI=1,SHIFTZI=2,GAMMAUPXXI=3,GAMMAUPXYI=4,GAMMAUPXZI=5,GAMMAUPYYI=6,GAMMAUPYZI=7,GAMMAUPZZI=8,
  PSII=9,LAPM1I=10,A_XI=11,A_YI=12,A_ZI=13,LAPSE_PSI2I=14,LAPSE_OVER_PSI6I=15,MAXNUMINTERP=16;
```

```python
# Set interpolation variables names
gfslist = [["INTERP_SHIFTX"],["INTERP_SHIFTY"],["INTERP_SHIFTZ"],
           ["INTERP_GAMMATILDEUPXX"],["INTERP_GAMMATILDEUPXY"],["INTERP_GAMMATILDEUPXZ"],
           ["INTERP_GAMMATILDEUPYY"],["INTERP_GAMMATILDEUPYZ"],["INTERP_GAMMATILDEUPZZ"],
           ["INTERP_PSI"],["INTERP_LAPM1"],
           ["INTERP_AX"],["INTERP_AY"],["INTERP_AZ"],
           ["INTERP_LAPSE_PSI2"],["INTERP_LAPSE_OVER_PSI6"],
           ["MAXNUMINTERP"]]

# Define the indices in the GRMHD_VARS.h header file
# Start with the variables used in the face value reconstructions
comment = "/* Interpolation variables */\n"
filename = "INTERP_VARS.h"
IGMout.generate_variable_definition_file(gfslist,filename,comment=comment)
```

    Just generated the file: ../src/NRPy_generated_headers/INTERP_VARS.h

## Step 1.f: The `SMALLB_VARS.h` and  `CONSERV_VARS.h` files \[Back to [top](#toc)\]
$$\label{smallb_and_conservs_vars}$$

We now set up the `SMALLB_VARS.h` and `CONSERV_VARS.h` files to substitute the following lines in the old version of the `IllinoisGRMHD_headers.h` file:

```c
// Again, the order here MATTERS, since we assume in the code that, e.g., smallb[0]=b^t, smallb[3]=b^z, etc.
static const int SMALLBT=0,SMALLBX=1,SMALLBY=2,SMALLBZ=3,SMALLB2=4,NUMVARS_SMALLB=5;

// Again, the order here MATTERS, since we assume in the code that, CONSERV[STILDEX+1] = \tilde{S}_y
static const int RHOSTAR=0,STILDEX=1,STILDEY=2,STILDEZ=3,TAUENERGY=4,NUM_CONSERVS=5;
```

```python
# b^{\mu} quantities
gfslist = [["SMALLBT"],["SMALLBX"],["SMALLBY"],["SMALLBZ"],["SMALLB2"],["NUMVARS_SMALLB"]]

# Set up the b^{\mu} string
comment = "/* smallb (b^{\mu}) variables */\n"
filename = "SMALLB_VARS.h"
IGMout.generate_variable_definition_file(gfslist,filename,comment=comment)

# Conservative quantities
gfslist = [["RHOSTAR"],["STILDEX"],["STILDEY"],["STILDEZ"],["TAUENERGY"],["NUM_CONSERVS"]]

# Set up the b^{\mu} string
comment = "/* Interpolation variables */\n"
filename = "CONSERV_VARS.h"
IGMout.generate_variable_definition_file(gfslist,filename,comment=comment)
```

    Just generated the file: ../src/NRPy_generated_headers/SMALLB_VARS.h
    Just generated the file: ../src/NRPy_generated_headers/CONSERV_VARS.h

## Step 1.g: The `TMUNU_VARS.h` files \[Back to [top](#toc)\]
$$\label{tmunu_vars}$$

The `TMUNU_VARS.h` files sets the position of $T^{\mu\nu}$ components to be access within all $T^{\mu\nu}$ arrays, regardless if it is contravariant, covariant, or "mixed" (i.e. $T^{\mu}_{\ \ \nu}$)

```python
# Then, the right/left values of the 3-velocity
gfslist = [["TMUNU_TT"]]
for i in range(DIM):
    gfslist.append(["TMUNU_T"+chr(ord('X')+i)])

for i in range(DIM):
    for j in range(i,DIM):
        gfslist.append(["TMUNU_"+chr(ord('X')+i)+chr(ord('X')+j)])

# Set up comments
comment = "/* Define TMUNU variables (valid for all variants) */\n"
# Set up output file name
filename = "TMUNU_VARS.h"
# Generate variable definition file
IGMout.generate_variable_definition_file(gfslist,filename,comment=comment)
```

    Just generated the file: ../src/NRPy_generated_headers/TMUNU_VARS.h

# Step 2: The `IllinoisGRMHD_headers.h` file \[Back to [top](#toc)\]
$$\label{igm_headers__h}$$

We will now document the `IllinoisGRMHD_headers.h` file, even though this is probably one of the most straightforward files in `IllinoisGRMHD`. We start by going over the following three macros:

```c
#define MIN(a,b) ( ((a) < (b)) ? (a) : (b) )
#define MAX(a,b) ( ((a) > (b)) ? (a) : (b) )
#define SQR(x) ((x) * (x))
```

which are basically used for our convenience. Using `SQR(x)`, for example, is faster than using `pow(x,2)` and completely equivalent to doing `x*x`, but is very convenient when dealing with large expressions. The `MIN` and `MAX` return the minimum and maximum value of two input numbers, respectively.

We have also a few more definitions

```c
#define ONE_OVER_SQRT_4PI 0.282094791773878143474039725780

#define VERR_DEF_PARAMS __LINE__, __FILE__, CCTK_THORNSTRING

#define TINYDOUBLE 1e-100
```

which are there for convenience. The `ONE_OVER_SQRT_4PI` macro precomputes $\frac{1}{\sqrt{4\pi}}$ to many significant digits. The `TINYDOUBLE` macro is used to set a nonzero, yet extremely small value to avoid division by zero in a few computations.

Next we include all files we have generated in [Step 1](#vars_headers).

Then we set the ghostzones struct,

```c
struct gf_and_gz_struct {
  CCTK_REAL *gf;
  int gz_lo[4],gz_hi[4];
};
```

which is extremely useful to set the ghostzones of each gridfunction and keep track of it in different functions. Note that so that we keep things consistent, we update the values of `gz_lo` and `gz_hi` appropriately within each function.

Then comes the equation of state (EOS) struct,

```c
struct eos_struct {
  int neos;
  CCTK_REAL rho_ppoly_tab[MAX_EOS_PARAMS-1];
  CCTK_REAL eps_integ_const[MAX_EOS_PARAMS],K_ppoly_tab[MAX_EOS_PARAMS],Gamma_ppoly_tab[MAX_EOS_PARAMS];
};
```

which sets all EOS parameters. This struct is currently specialized to simple and piecewise polytropic EOSs.

Next comes the stats struct,

```c
struct output_stats {
  int font_fixed,vel_limited,failure_checker,rho_star_fix_applied;
  long n_iter;
};
```

which is used to identify when particularly fixes are applied in the conservative-to-primitive routine.

Then we define the [Kronecker delta](https://en.wikipedia.org/wiki/Kronecker_delta)

```c
const int kronecker_delta[4][3] = { { 0,0,0 },
                                    { 1,0,0 },
                                    { 0,1,0 },
                                    { 0,0,1 } };
```

Finally we set the prototypes of the `IllinoisGRMHD_enforce_limits_on_primitives_and_recompute_conservs()` ([**tutorial**](Tutorial-IllinoisGRMHD__apply_tau_floor__enforce_limits_on_primitives_and_recompute_conservs.ipynb)), `IllinoisGRMHD_convert_ADM_to_BSSN__enforce_detgtij_eq_1__and_compute_gtupij()` ([**tutorial**](Tutorial-IllinoisGRMHD__convert_ADM_to_BSSN__enforce_detgtij_eq_1__and_compute_gtupij.ipynb)), and `IllinoisGRMHD_set_symmetry_gzs_staggered()` ([**tutorial**](Tutorial-IllinoisGRMHD__symmetry__set_gzs_staggered_gfs.ipynb)) functions.

```python
%%writefile $outdir/IllinoisGRMHD_headers.h
// To safeguard against double-including this header file:
#ifndef ILLINOISGRMHD_HEADERS_H_
#define ILLINOISGRMHD_HEADERS_H_

#define MIN(a,b) ( ((a) < (b)) ? (a) : (b) )
#define MAX(a,b) ( ((a) > (b)) ? (a) : (b) )
#define SQR(x) ((x) * (x))
#define ONE_OVER_SQRT_4PI 0.282094791773878143474039725780

#define VERR_DEF_PARAMS __LINE__, __FILE__, CCTK_THORNSTRING

#define TINYDOUBLE 1e-100

#include "NRPy_generated_headers/ADM_3METRIC_VARS.h"

#include "NRPy_generated_headers/CONF_METRIC_FACEVALS_VARS.h"

#include "NRPy_generated_headers/GRMHD_VARS.h"

#include "NRPy_generated_headers/INTERP_VARS.h"

#include "NRPy_generated_headers/SMALLB_VARS.h"

#include "NRPy_generated_headers/CONSERV_VARS.h"

#include "NRPy_generated_headers/TMUNU_VARS.h"

// Keeping track of ghostzones between routines is a nightmare, so
//   we instead attach ghostzone info to each gridfunction and set
//   the ghostzone information correctly within each routine.
struct gf_and_gz_struct {
  CCTK_REAL *gf;
  int gz_lo[4],gz_hi[4];
};

#define MAX_EOS_PARAMS 10
struct eos_struct {
  int neos;
  CCTK_REAL rho_ppoly_tab[MAX_EOS_PARAMS-1];
  CCTK_REAL eps_integ_const[MAX_EOS_PARAMS],K_ppoly_tab[MAX_EOS_PARAMS],Gamma_ppoly_tab[MAX_EOS_PARAMS];
};

struct output_stats {
  int font_fixed,vel_limited,failure_checker,rho_star_fix_applied;
  long n_iter;
};

// FIXME: For cosmetic purposes, we might want to make everything either zero-offset or one-offset, instead of a mixture.
const int kronecker_delta[4][3] = { { 0,0,0 },
                                    { 1,0,0 },
                                    { 0,1,0 },
                                    { 0,0,1 } };

/* PUBLIC FUNCTIONS, USED OUTSIDE IllinoisGRMHD AS WELL */
void IllinoisGRMHD_enforce_limits_on_primitives_and_recompute_conservs(const int already_computed_physical_metric_and_inverse,CCTK_REAL *U,struct output_stats &stats,eos_struct &eos,
                                                                       CCTK_REAL *METRIC,CCTK_REAL g4dn[4][4],CCTK_REAL g4up[4][4], CCTK_REAL *TUPMUNU,CCTK_REAL *TDNMUNU,CCTK_REAL *CONSERVS);

void IllinoisGRMHD_convert_ADM_to_BSSN__enforce_detgtij_eq_1__and_compute_gtupij
(const cGH *cctkGH,const int *cctk_lsh,
 CCTK_REAL *gxx,CCTK_REAL *gxy,CCTK_REAL *gxz,CCTK_REAL *gyy,CCTK_REAL *gyz,CCTK_REAL *gzz,CCTK_REAL *alp,
 CCTK_REAL *gtxx,CCTK_REAL *gtxy,CCTK_REAL *gtxz,CCTK_REAL *gtyy,CCTK_REAL *gtyz,CCTK_REAL *gtzz,
 CCTK_REAL *gtupxx,CCTK_REAL *gtupxy,CCTK_REAL *gtupxz,CCTK_REAL *gtupyy,CCTK_REAL *gtupyz,CCTK_REAL *gtupzz,
 CCTK_REAL *phi,CCTK_REAL *psi,CCTK_REAL *lapm1);

void IllinoisGRMHD_set_symmetry_gzs_staggered(const cGH *cctkGH, const int *cctk_lsh,CCTK_REAL *X,CCTK_REAL *Y,CCTK_REAL *Z,  CCTK_REAL *gridfunc,
                                              CCTK_REAL *gridfunc_syms,int stagger_x,int stagger_y,int stagger_z);

#include "IllinoisGRMHD_EoS_lowlevel_functs.C"
#endif // ILLINOISGRMHD_HEADERS_H

```

    Overwriting ../src/IllinoisGRMHD_headers.h

# Step 3: Code validation \[Back to [top](#toc)\]
$$\label{code_validation}$$

First we download the original `IllinoisGRMHD` source code and then compare it to the source code generated by this tutorial notebook.

```python
# # Verify if the code generated by this tutorial module
# # matches the original IllinoisGRMHD source code

# # First download the original IllinoisGRMHD source code
# import urllib
# from os import path

# original_IGM_file_url  = "https://bitbucket.org/zach_etienne/wvuthorns/raw/5611b2f0b17135538c9d9d17c7da062abe0401b6/IllinoisGRMHD/src/IllinoisGRMHD_headers.h"
# original_IGM_file_name = "IllinoisGRMHD_headers-original.h"
# original_IGM_file_path = os.path.join(IGM_src_dir_path,original_IGM_file_name)

# # Then download the original IllinoisGRMHD source code
# # We try it here in a couple of ways in an attempt to keep
# # the code more portable
# try:
#     original_IGM_file_code = urllib.request.urlopen(original_IGM_file_url).read().decode("utf-8")
#     # Write down the file the original IllinoisGRMHD source code
#     with open(original_IGM_file_path,"w") as file:
#         file.write(original_IGM_file_code)
# except:
#     try:
#         original_IGM_file_code = urllib.urlopen(original_IGM_file_url).read().decode("utf-8")
#         # Write down the file the original IllinoisGRMHD source code
#         with open(original_IGM_file_path,"w") as file:
#             file.write(original_IGM_file_code)
#     except:
#         # If all else fails, hope wget does the job
#         !wget -O $original_IGM_file_path $original_IGM_file_url

# # Perform validation
# Validation__IllinoisGRMHD_headers__h  = !diff $original_IGM_file_path $outfile_path__IllinoisGRMHD_headers__h

# if Validation__IllinoisGRMHD_headers__h == []:
#     # If the validation passes, we do not need to store the original IGM source code file
#     !rm $original_IGM_file_path
#     print("Validation test for IllinoisGRMHD_headers.h: PASSED!")
# else:
#     # If the validation fails, we keep the original IGM source code file
#     print("Validation test for IllinoisGRMHD_headers.h: FAILED!")
#     # We also print out the difference between the code generated
#     # in this tutorial module and the original IGM source code
#     print("Diff:")
#     for diff_line in Validation__IllinoisGRMHD_headers__h:
#         print(diff_line)
```

# Step 4: Output this notebook to $\LaTeX$-formatted PDF file \[Back to [top](#toc)\]
$$\label{latex_pdf_output}$$

The following code cell converts this Jupyter notebook into a proper, clickable $\LaTeX$-formatted PDF file. After the cell is successfully run, the generated PDF may be found in the root NRPy+ tutorial directory, with filename
[Tutorial-IllinoisGRMHD__IllinoisGRMHD_headers.pdf](Tutorial-IllinoisGRMHD__IllinoisGRMHD_headers.pdf) (Note that clicking on this link may not work; you may need to open the PDF file through another means).

```python
latex_nrpy_style_path = os.path.join(nrpy_dir_path,"latex_nrpy_style.tplx")
#!jupyter nbconvert --to latex --template $latex_nrpy_style_path --log-level='WARN' Tutorial-IllinoisGRMHD__IllinoisGRMHD_headers.ipynb
#!pdflatex -interaction=batchmode Tutorial-IllinoisGRMHD__IllinoisGRMHD_headers.tex
#!pdflatex -interaction=batchmode Tutorial-IllinoisGRMHD__IllinoisGRMHD_headers.tex
#!pdflatex -interaction=batchmode Tutorial-IllinoisGRMHD__IllinoisGRMHD_headers.tex
!rm -f Tut*.out Tut*.aux Tut*.log
```


```python

```

```python
import pandas as pd
%matplotlib inline
import glob, os
from kinetics.ala_kinetics import *
from kinetics.waiting_time import *
from kinetics.sorted_lifetimes import *
import pyprind
from kinetics.check_expon import *
```

```python
cl = sns.color_palette()
```

```python
! ls ../dt*/md_dt*/ | tail
```

    md_st2md_dt5000__27jan16_blNone_pop.pickle
    md_st2md_dt5000__27jan16_blNone_rates.pickle
    md_st2md_dt5000__27jan16_blNone_tba.pickle
    md_st2md_dt5000__27jan16_blNone_tp.pickle

    ../dt5000/md_dt5000_st3/:
    md_st3md_dt5000__27jan16_blNone_pop.pickle
    md_st3md_dt5000__27jan16_blNone_rates.pickle
    md_st3md_dt5000__27jan16_blNone_tba.pickle
    md_st3md_dt5000__27jan16_blNone_tp.pickle

```python
dt_key_l = glob.glob("../dt*")
dt_key_l = [ k[5:] for k in dt_key_l]
dt_key_l
```

    ['1',
     '10',
     '100',
     '1000',
     '2',
     '20',
     '200',
     '25',
     '2500',
     '5',
     '50',
     '500',
     '5000']

# Lag-time dependency of lifetimes from MD

* load MD transition paths and transition-based state assignments

## Calculate lifetimes MD

```python
md_dt_tp_d = {}
md_dt_state_d = {}

#for key in md_r_dt_d.keys():
for key in dt_key_l:
    #print "../dt{}/md*/*blNone_tp.pickle".format(dt)
    #dt_tp_l  = [glob.glob("../dt{}/md*/md_st{}*blNone_tp.pickle".format(dt,st))[0] for st in range(1,4)]
    _dt_tp_l = []
    _dt_st_l = []
    for st in range(1,4):
        _dt_tp_l.extend(glob.glob("../dt{}/md*_st{}/md_st{}md*_27jan16_blNone_tp.pickle".format(key,st,st)))
        _dt_st_l.extend(glob.glob("../dt{}/md*_st{}/md_st{}md*_27jan16_blNone_tba.pickle".format(key,st,st)))
        # print _dt_st_l
    md_dt_tp_d[key] = _dt_tp_l
    md_dt_state_d[key] = _dt_st_l
```

```python
trans_from_h = [t for t in possible_transitions(4) if t[:2] == (0,0,)]
trans_from_c = [t for t in possible_transitions(4) if t[:2] == (0,1,)]

trans_from_11 =  [t for t in possible_transitions(4) if t[:2] == (1,1,)]
trans_from_10 =  [t for t in possible_transitions(4) if t[:2] == (1,0,)]
```

```python
md_dt_hdw_d = {}
md_dt_cdw_d = {}
md_dt_11dw_d = {}
md_dt_10dw_d = {}
bar = pyprind.ProgBar(len(dt_key_l)*3)

for k, v in md_dt_tp_d.items():
    _tba = md_dt_state_d[k]
    print k
    md_h_l = []
    md_c_l = []
    md_11_l = []
    md_10_l = []

    for i, _tp_fn in enumerate(v):
        _st_fn = _tba[i]

        _tp_df = pd.read_pickle(_tp_fn)
        _st_df = pd.read_pickle(_st_fn)

        _tp_df0 = _tp_df[_tp_df.temperature==0]
        _st_df0 = _st_df[_st_df.temperature==0]

        _dw_h = loop_dwell_trans_temp(_tp_df0,
                                      _st_df0, trans_from_h, tu=float(k))
        md_h_l.append(_dw_h)
        _dw_c = loop_dwell_trans_temp(_tp_df0,
                                      _st_df0, trans_from_c, tu=float(k))
        md_c_l.append(_dw_c)

        # lifetimes in the minor states
        _dw_10 = loop_dwell_trans_temp(_tp_df0,
                                      _st_df0, trans_from_10, tu=float(k))
        md_10_l.append(_dw_10)

        _dw_11 = loop_dwell_trans_temp(_tp_df0,
                                      _st_df0, trans_from_11, tu=float(k))
        md_11_l.append(_dw_11)

    md_dt_hdw_d[k] = pd.concat(md_h_l)
    md_dt_cdw_d[k] = pd.concat(md_c_l)
    md_dt_10dw_d[k] = pd.concat(md_10_l)
    md_dt_11dw_d[k] = pd.concat(md_11_l)

    bar.update()
```

    0%                          100%
    [                              ]

    10
    5000

    [#                             ] | ETA: 00:05:04

    20

    [##                            ] | ETA: 00:05:58

    200

    [###                           ] | ETA: 00:05:04

    50
    1

    [####                          ] | ETA: 00:06:51

    2

    [#####                         ] | ETA: 00:07:21

    5

    [######                        ] | ETA: 00:07:22

    25
    100

    [#######                       ] | ETA: 00:06:35

    2500

    [########                      ] | ETA: 00:05:48

    500

    [#########                     ] | ETA: 00:05:13

    1000

## Quick comparison of MD lifetime distributions

```python
_blog = np.logspace(-3,1, 5000)
```

```python
fig, ax = plt.subplots(2,2, figsize=(8,4))

tf = 1.0 / 1000

_ = fit_plot_cdf(ax[0,0], md_dt_hdw_d['1'].wait_T * tf, bins=_blog,
                dist_label="$h$, dt=1 ps", )
_ = fit_plot_cdf(ax[0,1], md_dt_hdw_d['5'].wait_T * tf, bins=_blog, plot_fit=False,
                dist_label="$h$, dt=5 ps")

_ = fit_plot_cdf(ax[1,0], md_dt_hdw_d['10'].wait_T * tf, bins=_blog, plot_fit=True,
                dist_label="$h$, dt=10 ps")

_ = fit_plot_cdf(ax[1,1], md_dt_hdw_d['25'].wait_T * tf, bins=_blog, plot_fit=True,
                dist_label="$h$, dt=25 ps")
#_ = fit_plot_cdf(ax[2,1], md_dt_hdw_d['50'].wait_T * tf, bins=_blog, plot_fit=True)

_ = fit_plot_cdf(ax[0,0], md_dt_cdw_d['1'].wait_T * tf, bins=_blog,
                dist_label="$c$, dt=1 ps")
_ = fit_plot_cdf(ax[0,1], md_dt_cdw_d['5'].wait_T * tf, bins=_blog, plot_fit=True,
                dist_label="$c$, dt=5 ps")
_ = fit_plot_cdf(ax[1,0], md_dt_cdw_d['10'].wait_T * tf, bins=_blog, plot_fit=True,
                dist_label="$c$, dt=10 ps")

_ = fit_plot_cdf(ax[1,1], md_dt_cdw_d['25'].wait_T * tf, bins=_blog, plot_fit=True,
                dist_label="$c$, dt=25 ps")
#_ = fit_plot_cdf(ax[2,1], md_dt_cdw_d['50'].wait_T * tf, bins=_blog, plot_fit=True)

for a in ax.flat:
    a.semilogx()
    a.legend(loc=2)
```

## Uncertainty in $\tau$ REMD from counts

```python
md_r_dt_d = {}
for dt_fn_name in glob.glob("../dt*/c_md_dt*/rates_md_st1-3_sym_ln__27jan16.pickle"):
    dt = dt_fn_name.split("/")[1][2:]
    md_r_dt_d[dt] = pd.read_pickle(dt_fn_name)
```

# Lag-time dependency of lifetimes from REMD

## Calculate

```python
remd_dt_tp_l = glob.glob("../dt*/remd_dt*/*_27jan16_tp.pickle")
```

```python
remd_dt_st_l = glob.glob("../dt*/remd_dt*/*_27jan16_tba.pickle")
```

```python
remd_dt_tp_d = {}
remd_dt_st_d = {}

for fn in remd_dt_tp_l:
    dt = fn.split("/")[1][2:]
    #print dt
    remd_dt_tp_d[dt] = pd.read_pickle(fn)

for fn in remd_dt_st_l:
    dt = fn.split("/")[1][2:]
    remd_dt_st_d[dt] = pd.read_pickle(fn)
```

```python
remd_dt_hdw_d = {}
remd_dt_cdw_d = {}
remd_dt_10w_d = {}
remd_dt_11dw_d = {}

bar = pyprind.ProgBar(len(remd_dt_tp_l)*4)

for k, _tp in remd_dt_tp_d.items():
    _tba = remd_dt_st_d[k]
    _tp0 = _tp[_tp.temperature==0]
    _tba0 = _tba[_tba.temperature==0]

    _dw_h = loop_dwell_trans_temp(_tp0,
                                  _tba0, trans_from_h, tu=float(k))

    _dw_c = loop_dwell_trans_temp(_tp0,
                                  _tba0, trans_from_c, tu=float(k))
    remd_dt_hdw_d[k] = _dw_h
    remd_dt_cdw_d[k] = _dw_c

    remd_dt_10w_d[k] = loop_dwell_trans_temp(_tp0, _tba0,
                                            trans_from_10,
                                            tu=float(k))
    remd_dt_11dw_d[k] = loop_dwell_trans_temp(_tp0, _tba0,
                                             trans_from_11,
                                             tu=float(k))

    bar.update()

```

    0%                          100%
    [#######                       ] | ETA: 00:03:32

## Uncertainty in $\tau$ REMD from counts

```python
remd_r_dt_d = {}
for dt_fn_name in glob.glob("../dt*/remd_dt*/ala_remd_st1_dt*ps__27jan16_rates_sym_ln__27jan16.pickle"):
    dt = dt_fn_name.split("/")[1][2:]
    #print dt
    remd_r_dt_d[dt] = pd.read_pickle(dt_fn_name)
```

# Comparing lag-time dependences from MD and REMD

## Lagtime independent rate coefficients

assume that Ala2 is a quasi two-state system.

First fit $k$

\begin{equation}
\frac{1}{<\tau_F>_{\text{app}}} + \frac{1}{<\tau_U>_{\text{app}}} =  \frac{1 - \exp(-k t) }{ t}
\end{equation}

Then fit the relative populations.

\begin{equation}
\frac{1}{<\tau_F>_{\text{app}}} = \frac{pU' (1 - \exp(-kt)}{t} = k_{U \leftarrow F}
\end{equation}

### rate arrays

```python
md_dt_cdw_d['1'].head() # wait in the coil state
```

      temperature
      type
      traj
      start
      stop
      weight
      wait_T
      wait
      prev_state

      0
      0.0
      (0.0, 1.0, 0.0, 0.0)
      0.0
      0.0
      145.0
      1.0
      80.0
      145.0
      NaN

      1
      0.0
      (0.0, 1.0, 0.0, 0.0)
      0.0
      148.0
      699.0
      1.0
      401.0
      551.0
      NaN

      2
      0.0
      (0.0, 1.0, 0.0, 0.0)
      0.0
      706.0
      929.0
      1.0
      111.0
      223.0
      NaN

      3
      0.0
      (0.0, 1.0, 0.0, 0.0)
      0.0
      939.0
      1068.0
      1.0
      39.0
      129.0
      NaN

      4
      0.0
      (0.0, 1.0, 0.0, 0.0)
      0.0
      1073.0
      1355.0
      1.0
      89.0
      282.0
      NaN

```python
md_dt_hdw_d['1'].head() # wait in the helix state
```

      temperature
      type
      traj
      start
      stop
      weight
      wait_T
      wait
      prev_state

      0
      0.0
      (0.0, 0.0, 0.0, 1.0)
      0.0
      0.0
      63.0
      1.0
      63.0
      63.0
      NaN

      1
      0.0
      (0.0, 0.0, 0.0, 1.0)
      0.0
      68.0
      295.0
      1.0
      149.0
      227.0
      NaN

      2
      0.0
      (0.0, 0.0, 0.0, 1.0)
      0.0
      301.0
      816.0
      1.0
      114.0
      515.0
      NaN

      3
      0.0
      (0.0, 0.0, 0.0, 1.0)
      0.0
      821.0
      1027.0
      1.0
      93.0
      206.0
      NaN

      4
      0.0
      (0.0, 0.0, 0.0, 1.0)
      0.0
      1032.0
      1264.0
      1.0
      194.0
      232.0
      NaN

```python
tf = 1.0 / 1000.0
```

```python
md_lagtime_l = []
md_kc_l, md_kh_l = [], []

for dt in sorted([int(k) for k in md_dt_cdw_d.keys()]):
    md_lagtime_l.append(dt)
    _c = md_dt_cdw_d[str(dt)]
    _h = md_dt_hdw_d[str(dt)]
    md_kh_l.extend([_c[_c.temperature==0].wait_T.mean()])
    md_kc_l.extend([_h[_h.temperature==0].wait_T.mean()])
md_kc_ar = 1.0 / (np.array(md_kc_l)*tf)
md_kh_ar = 1.0/  (np.array(md_kh_l)*tf)
md_kc_kh_ar = md_kc_ar + md_kh_ar

md_lagtime_ar = np.array(md_lagtime_l)
```

```python
md_kc_kh_ar
```

    array([ 9.38203042,  9.25406793,  9.04817276,  8.72595441,  8.14774794,
            7.87115053,  6.76873198,  5.07903558,  3.21880651,  1.44015157,
            0.67970888,  0.33750258,  0.16809117])

```python
 1/ (md_dt_cdw_d['1'][md_dt_cdw_d['1'].temperature==0].wait_T.mean() / 1000.0)
```

    3.7332389118955613

```python
 1/ (md_dt_hdw_d['1'][md_dt_hdw_d['1'].temperature==0].wait_T.mean() / 1000.0)
```

    5.648791509837093

```python
md_r_dt_d['1'][(md_r_dt_d['1'].temperature==0) & (md_r_dt_d['1'].type==(0,1,0,0))]
```

      temperature
      type
      rate
      events
      sum_weight
      rev_events
      rev_sum_weight
      sym_weight
      std_p
      std_m
      err_m
      err_p

      36
      0.0
      (0, 1, 0, 0)
      3.676874
      964.0
      964.0
      965.0
      965.0
      1929.0
      3.761551
      3.594103
      0.082771
      0.084677

```python
remd_lagtime_l = []
remd_kc_l, remd_kh_l = [], []

for dt in sorted([int(k) for k in remd_dt_hdw_d.keys()]):
    print dt
    remd_lagtime_l.append(dt)
    _c = remd_dt_cdw_d[str(dt)]
    _h = remd_dt_hdw_d[str(dt)]
    # tau_av = np.average(v.wait_T/ v.weight, weights=v.weight)
    _c0 = _c[_c.temperature==0]
    _h0 = _h[_h.temperature==0]
    remd_kh_l.extend([np.average(_c0.wait_T / _c0.weight, weights=_c0.weight)])
    remd_kc_l.extend([np.average(_h0.wait_T / _h0.weight, weights=_h0.weight)])
remd_kc_ar = 1.0 / (np.array(remd_kc_l)*tf)
remd_kh_ar = 1.0/  (np.array(remd_kh_l)*tf)
remd_kc_kh_ar = remd_kc_ar + remd_kh_ar

remd_lagtime_ar = np.array(remd_lagtime_l)
```

    1
    2
    5
    10
    20
    25
    50
    100
    200
    500
    1000
    2500
    5000

```python
1/ (remd_dt_hdw_d['1'][remd_dt_hdw_d['1'].temperature==0].wait_T.mean() / 1000.0)
```

    6.813287676068768

```python
1/ (remd_dt_cdw_d['1'][remd_dt_cdw_d['1'].temperature==0].wait_T.mean() / 1000.0)
```

    4.470001829156759

```python
md_kc_kh_ar
```

    array([ 9.38203042,  9.25406793,  9.04817276,  8.72595441,  8.14774794,
            7.87115053,  6.76873198,  5.07903558,  3.21880651,  1.44015157,
            0.67970888,  0.33750258,  0.16809117])

```python
remd_kc_kh_ar
```

    array([ 9.93467867,  9.80515758,  9.4242392 ,  9.11657163,  8.39944827,
            7.99680929,  7.05139893,  5.20324584,  3.51125646,  1.32326228,
            0.57495685,  0.28231217,  0.12527778])

```python
remd_lagtime_ar
```

    array([   1,    2,    5,   10,   20,   25,   50,  100,  200,  500, 1000,
           2500, 5000])

```python
popt_kex_remd = curve_fit(sum_inv_lifetimes_two_state_lagtime,
                         remd_lagtime_ar[:]*tf, remd_kc_kh_ar[:], p0=10)
kex_fit_remd = popt_kex_remd[0][0]
print popt_kex_remd
```

    (array([ 9.44258411]), array([[ 0.04464302]]))

```python
popt = curve_fit(sum_inv_lifetimes_two_state_lagtime, md_lagtime_ar[:]*tf, md_kc_kh_ar[:], p0=[10.0])
print popt
k_ex_fit = popt[0][0]
```

    (array([ 9.00931758]), array([[ 0.03963532]]))

```python
fig, ax = plt.subplots(figsize=(3,3))
plt.plot(md_lagtime_ar / 1000.0, md_kc_kh_ar, ".")
plt.plot(md_lagtime_ar/ 1000.0, sum_inv_lifetimes_two_state_lagtime(md_lagtime_ar*tf, k_ex_fit))

plt.plot(remd_lagtime_ar / 1000.0, remd_kc_kh_ar, ".")
plt.plot(remd_lagtime_ar/ 1000.0, sum_inv_lifetimes_two_state_lagtime(remd_lagtime_ar*tf, kex_fit_remd))

plt.loglog()
```

### $p'$ fit to determine lag time independent rate coefficients and populations
#### MD

```python
f = lambda  t, p : inv_lifetime_two_state_lagtime(t, p, k_ex_fit)
```

```python
popt_pc = curve_fit(f, md_lagtime_ar[:] / 1000.0, md_kc_ar[:])
print popt_pc

popt_ph = curve_fit(f, md_lagtime_ar[:]/1000.0, md_kh_ar[:])
print popt_ph
```

    (array([ 0.59058842]), array([[ 0.00018217]]))
    (array([ 0.39729415]), array([[  4.00681632e-05]]))

```python
pc = popt_pc[0][0] / (popt_pc[0][0] + popt_ph[0][0])
ph = popt_ph[0][0] / (popt_pc[0][0] + popt_ph[0][0])
```

```python
k_ex_fit * pc, k_ex_fit * ph
```

    (5.3860638692643912, 3.6232537105041218)

```python
md_r_dt_d['1'][(md_r_dt_d['1'].temperature==0) & (md_r_dt_d['1'].type.isin([(0,0,0,1), (0,1,0,0)]))]
```

      temperature
      type
      rate
      events
      sum_weight
      rev_events
      rev_sum_weight
      sym_weight
      std_p
      std_m
      err_m
      err_p

      0
      0.0
      (0, 0, 0, 1)
      5.565691
      965.0
      965.0
      964.0
      964.0
      1929.0
      5.693867
      5.440401
      0.125291
      0.128176

      36
      0.0
      (0, 1, 0, 0)
      3.676874
      964.0
      964.0
      965.0
      965.0
      1929.0
      3.761551
      3.594103
      0.082771
      0.084677

#### REMD

```python
f_remd = lambda t, p: inv_lifetime_two_state_lagtime(t, p, kex_fit_remd)
```

```python
popt_pc_remd = curve_fit(f, remd_lagtime_ar[:]*tf, remd_kc_ar[:])
print popt_pc_remd
popt_ph_remd = curve_fit(f, remd_lagtime_ar[:]*tf, remd_kh_ar[:])
print popt_ph_remd
pc_remd = popt_pc_remd[0][0] / (popt_pc_remd[0][0] + popt_ph_remd[0][0])
ph_remd = popt_ph_remd[0][0] / (popt_pc_remd[0][0] + popt_ph_remd[0][0])
print pc_remd, ph_remd
```

    (array([ 0.61844082]), array([[ 0.00022834]]))
    (array([ 0.41136535]), array([[  6.47087531e-05]]))
    0.600540993681 0.399459006319

```python
kex_fit_remd * pc, kex_fit_remd * ph
```

    (5.6450847289329289, 3.7974993774089545)

## Load minor state lag-time dependences expected for ideal rate kinetics

```python
syn_ar = np.genfromtxt("../from-syn-trj/syn_dt_ala_st3_st4_c.txt")
```

## Comparison plot

```python
! ls ../dt10/c_md_dt10/
! mkdir -p plot
```

    pt_md_st1-3__27jan16.txt              rates_md_st1-3_sym_ln__27jan16.pickle
    rates_md_st1-3__27jan16.txt           rep_ar_md_st1-3__27jan16.txt

* error estimates from the count statistics

```python
remd_r_dt_d['1'][(remd_r_dt_d['1'].type.isin(trans_from_10))].head()
```

      temperature
      type
      rate
      events
      sum_weight
      rev_events
      rev_sum_weight
      sym_weight
      std_p
      std_m
      err_m
      err_p

      72
      0.0
      (1, 0, 0, 0)
      0.148457
      1.0
      1.0
      0.0
      0.0
      1.0
      0.403548
      0.054614
      0.093843
      0.255091

      73
      1.0
      (1, 0, 0, 0)
      0.000000
      NaN
      NaN
      0.0
      0.0
      0.0
      NaN
      0.000000
      0.000000
      NaN

      74
      2.0
      (1, 0, 0, 0)
      0.270858
      2.0
      2.0
      0.0
      0.0
      2.0
      0.549331
      0.133551
      0.137306
      0.278473

      75
      3.0
      (1, 0, 0, 0)
      0.000000
      NaN
      NaN
      0.0
      0.0
      0.0
      NaN
      0.000000
      0.000000
      NaN

      76
      4.0
      (1, 0, 0, 0)
      0.000000
      NaN
      NaN
      0.0
      0.0
      0.0
      NaN
      0.000000
      0.000000
      NaN

```python
def _count_err_from_rate_calc(tau, r_df, trans):
    md_r = r_df[(r_df.type.isin(trans))]
    _sum = md_r.rate.sum()
    if _sum > 0:
        _N = md_r.sym_weight.sum()
    return tau*np.exp(1/_N**0.5), tau*np.exp(-1/_N**0.5)

def _count_err_at_temp(r_df, trans, temp):
    p, m = _count_err_from_rate_calc(r_df[r_df.temperature==temp], trans)
    return m, p
```

```python
fig, ax = plt.subplots(2,2, figsize=(6.5,6))
sns.set_style("ticks")

for k, v in remd_dt_hdw_d.items():
    dt_r = remd_r_dt_d[str(k)]
    md_r = dt_r[(dt_r.temperature==0) & (dt_r.type==(0,0,0,1))]
    tau_av = np.average(v.wait_T/ v.weight, weights=v.weight)
    lg1_remd = ax[0,1].errorbar(int(k)*tf,  1.0/tau_av*1000.0, yerr=[md_r.err_m.values ,
                                 md_r.err_p.values ],c=cl[1], fmt=".",
                                 label=r"$h \rightarrow c$")

for k, v in remd_dt_cdw_d.items():
    dt_r = remd_r_dt_d[str(k)]
    md_r = dt_r[(dt_r.temperature==0) & (dt_r.type==(0,1,0,0))]
    tau_av = np.average(v.wait_T/ v.weight, weights=v.weight)
    #lg2_remd, = ax[0,1].plot(int(k)*tf, 1.0 / tau_av*1000.0 , "s", c=cl[3])
    lg2_remd = ax[0,1].errorbar(int(k)*tf,  1.0/tau_av*1000.0, yerr=[md_r.err_m.values ,
                                 md_r.err_p.values ],c=cl[3], fmt=".",
                                 label=r"$c \rightarrow h$")

for k, v in remd_dt_10w_d.items():
    dt_r = remd_r_dt_d[str(k)]
    md_r = dt_r[(dt_r.temperature==0) & (dt_r.type.isin(trans_from_10))]

    if np.any(v.wait_T > 0) and md_r.rate.sum() > 0 :
        tau_av = np.average(v.wait_T/ v.weight, weights=v.weight)

        _m, _p = _count_err_from_rate_calc( 1.0/ tau_av * 1000.0,
                                           md_r, trans_from_10)
        ax[1,1].plot([int(k)*tf]*2, [ _m,  _p], "-", c=cl[4] )
        lg3_remd, = ax[1,1].plot(int(k)*tf, 1.0/ tau_av * 1000.0 , ".", c=cl[4], mec=cl[4]) #  mew=1.0, mfc="None"

for k, v in remd_dt_11dw_d.items():
    dt_r = remd_r_dt_d[str(k)]
    md_r = dt_r[(dt_r.temperature==0) & (dt_r.type.isin(trans_from_11))]
    if np.any(v.wait_T.values > 0) and md_r.rate.sum() > 0 :

        tau_av = np.average(v.wait_T/ v.weight, weights=v.weight)
        _m, _p = _count_err_from_rate_calc( 1.0/ tau_av * 1000.0,
                                           md_r, trans_from_11)
        ax[1,1].plot([int(k)*tf]*2, [ _m,  _p], "-", c=cl[5] )
        lg4_remd, = ax[1,1].plot(int(k)*tf, 1.0/ tau_av * 1000.0 , ".", c=cl[5], mec=cl[5]) #  mew=1.0, mfc="None"

# mfpt h
ax[0,1].plot(remd_lagtime_ar*tf, inv_lifetime_two_state_lagtime(
    remd_lagtime_ar*tf, popt_ph[0][0], kex_fit_remd),
          c=cl[3])
# mfpt c
ax[0,1].plot(remd_lagtime_ar*tf, inv_lifetime_two_state_lagtime(
    remd_lagtime_ar*tf, popt_pc[0][0], kex_fit_remd),
          c=cl[1])

for k, v in md_dt_hdw_d.items():
    dt_r = md_r_dt_d[str(k)]
    md_r = dt_r[(dt_r.temperature==0) & (dt_r.type==(0,0,0,1))]

    _temp =v.wait_T / v.weight
    if np.any(_temp.values > 0):
        tau_av, var_tau = weights_first_second_moment(v.weight, v.wait_T)
        lg1_md = ax[0,0].errorbar(int(k)*tf,  1.0/tau_av*1000.0, yerr=[md_r.err_m.values ,
                                 md_r.err_p.values ],c=cl[0], fmt=".",
                                 label=r"$h \rightarrow c$")

for k, v in md_dt_cdw_d.items():
    dt_r = md_r_dt_d[str(k)]
    md_r = dt_r[(dt_r.temperature==0) & (dt_r.type==(0,1,0,0))]
    _temp =v.wait_T / v.weight
    if np.any(_temp.values > 0):
        tau_av, var_tau = weights_first_second_moment(v.weight, v.wait_T)
        #lg2_md, = ax[0,0].plot(int(k)*tf, 1.0/tau_av*1000.0, "o", c=cl[2])
        lg2_md = ax[0,0].errorbar(int(k)*tf,  1.0/tau_av*1000.0, yerr=[md_r.err_m.values ,
                                 md_r.err_p.values ],c=cl[2], fmt=".",
                                 label=r"$c \rightarrow h$")

ax[0,0].legend([lg1_md, lg2_md], [r"MD $h \rightarrow c$",
                               r"MD $c \rightarrow h$"],
            loc=3)

ax[0,1].legend([lg1_remd, lg2_remd], [r"REMD $h \rightarrow c$",
                               r"REMD $c \rightarrow h$"], loc=3)

for k, v in md_dt_10dw_d.items():
    dt_r = md_r_dt_d[str(k)]
    md_r = dt_r[(dt_r.temperature==0) & (dt_r.type.isin(trans_from_10))]
    if np.any(v.wait_T > 0) and md_r.rate.sum() > 0 :
        tau_av = np.average(v.wait_T/ v.weight, weights=v.weight)

        _m, _p = _count_err_from_rate_calc( 1.0/ tau_av * 1000.0,
                                           md_r, trans_from_10)
        ax[1,0].plot([int(k)*tf]*2, [ _m,  _p], "-", c=cl[4] )
        #tau_av, var_tau = weights_first_second_moment(v.weight, v.wait_T)
        lg3_md, = ax[1,0].plot(int(k)*tf, 1.0/tau_av*1000.0, ".", c=cl[4],  mfc=cl[4], mec=cl[4])

for k, v in md_dt_11dw_d.items():

    dt_r = md_r_dt_d[str(k)]
    md_r = dt_r[(dt_r.temperature==0) & (dt_r.type.isin(trans_from_11))]

    if np.any(v.wait_T > 0) and md_r.rate.sum() > 0 :
        tau_av = np.average(v.wait_T/ v.weight, weights=v.weight)

        _m, _p = _count_err_from_rate_calc( 1.0/ tau_av * 1000.0,
                                           md_r, trans_from_11)
        ax[1,0].plot([int(k)*tf]*2, [ _m,  _p], "-", c=cl[5] )
        #tau_av, var_tau = weights_first_second_moment(v.weight, v.wait_T)
        lg4_md, = ax[1,0].plot(int(k)*tf,  1.0/tau_av*1000.0, ".", c=cl[5],  mfc=cl[5], mec=cl[5])

ax[1,0].legend([lg3_md, lg4_md], [r'MD $\sum_{l(\neq)3}k_{l3}$',
                           r'MD $\sum_{l(\neq)4}k_{l4}$'],
               loc=3, handletextpad=-0.5,handleheight=0.5,
              borderaxespad=0.01)

#mfpt h
ax[0,0].plot(md_lagtime_ar*tf, inv_lifetime_two_state_lagtime(
        md_lagtime_ar*tf, popt_ph[0][0], k_ex_fit), "-",
          c=cl[2])
#mfpt c
ax[0,0].plot(md_lagtime_ar*tf, inv_lifetime_two_state_lagtime(
        md_lagtime_ar*tf, popt_pc[0][0], k_ex_fit),
          c=cl[0])

ax[1,1].legend([lg3_remd, lg4_remd], [r'REMD $\sum_{l(\neq)3}k_{l3}$',
                           r'REMD $\sum_{l(\neq)4}k_{l4}$'],
               loc=3, handletextpad=-0.5,handleheight=0.5,
              borderaxespad=0.01)

for a in ax.flat:
    a.loglog()
    a.set_xlabel("$\mathregular{\Delta t \, [ns]}$", fontsize=12)
    a.set_xlim(10**-3.2, 10**1)
    a.tick_params(axis='both', which='major', labelsize=12)
    a.tick_params(axis='both', which='both', top=True , right=True)

for i, a in enumerate(["A", "B"]):
    ax.flat[i].text(10**-5, 10**0.7, a, fontsize=20)
    ax.flat[i].set_ylabel(r"$\mathregular{k \, [ns^{-1}]}$", fontsize=12)

for i, a in enumerate(["C", "D"]):
    ax.flat[i+2].text(10**-5, 10**1.7, a, fontsize=20)
    ax.flat[i+2].set_ylabel(r"$\mathregular{k \, [ns^{-1}]}$", fontsize=12)

ax[1,0].plot(syn_ar[:,0], syn_ar[:,1], c='grey')
ax[1,0].plot(syn_ar[:,0], syn_ar[:,2], c='black')
ax[1,1].plot(syn_ar[:,0], syn_ar[:,1], c='grey')
ax[1,1].plot(syn_ar[:,0], syn_ar[:,2], c='black')

for a in ax[0,:]:
    a.set_yticks([10**-2, 10**-1, 10**0, 10**1])
    a.set_xticks([10**-3, 10**-2, 10**-1, 10**0, 10**1])

for a in ax[1,:]:
    a.set_yticks([10**-2, 10**-1, 10**0, 10**1, 10**2])
    a.set_xticks([10**-3, 10**-2, 10**-1, 10**0, 10**1])

fig.tight_layout()

fig.savefig("plot/ala_dt-rate.png")
fig.savefig("plot/ala_dt-rate.pdf")
```

# Lag-time dependence of $\mathrm{var}(\tau)$

```python
var_mcmc_h = np.genfromtxt("../from-syn-trj/var_dt/var_lifetimes_h_ns.txt")
var_mcmc_c = np.genfromtxt("../from-syn-trj/var_dt/var_lifetimes_c_ns.txt")
var_mcmc_st3 = np.genfromtxt("../from-syn-trj/var_dt/var_lifetimes_st3_ns.txt")
var_mcmc_st4 = np.genfromtxt("../from-syn-trj/var_dt/var_lifetimes_st4_ns.txt")
```

```python
fig_l = ["A", "B", "C", "D"]
```

```python
fig, ax = plt.subplots(2,2, figsize=(6.5,6))

for k, v in md_dt_hdw_d.items():
    _exp = check_moments_w(v.wait_T*tf, v.weight)
    lg_md, = ax[0,0].plot(int(k)*tf,  _exp[1] , "o", c=cl[0])

for k, v in md_dt_cdw_d.items():
    _exp = check_moments_w(v.wait_T*tf, v.weight)
    lg2_md, = ax[0,0].plot(int(k)*tf,  _exp[1] , "o", c=cl[2], label="$c \rightarrow h$")

for k, v in md_dt_10dw_d.items():
    if np.any(v.wait_T > 0) :
        _exp = check_moments_w(v.wait_T*tf , v.weight)
        lg3_md, = ax[1,0].plot(int(k)*tf, _exp[1], "o", c=cl[4])

for k, v in md_dt_11dw_d.items():
    if np.any(v.wait_T > 0) :
        _exp = check_moments_w(v.wait_T*tf , v.weight)
        lg4_md, = ax[1,0].plot(int(k)*tf, _exp[1], "o", c=cl[5])

# exclude empty arrays

for k, v in remd_dt_hdw_d.items():
    _exp = check_moments_w( v.wait_T*tf, v.weight)
    lg1_remd, = ax[0,1].plot(int(k)*tf,  _exp[1] , "s", c=cl[1])

for k, v in remd_dt_cdw_d.items():
    _exp = check_moments_w(v.wait_T*tf , v.weight)
    lg2_remd, = ax[0,1].plot(int(k)*tf, _exp[1] , "s", c=cl[3], label="$c \rightarrow h$")

for k, v in remd_dt_10w_d.items():
    dt_r = remd_r_dt_d[str(k)]
    md_r = dt_r[(dt_r.temperature==0) & (dt_r.type.isin(trans_from_10))]

    if np.any(v.wait_T > 0) and md_r.rate.sum() > 0 :
        _exp = check_moments_w(v.wait_T*tf , v.weight)
        lg3_remd, = ax[1,1].plot(int(k)*tf, _exp[1], "s", c=cl[4])

for k, v in remd_dt_11dw_d.items():
    dt_r = remd_r_dt_d[str(k)]
    md_r = dt_r[(dt_r.temperature==0) & (dt_r.type.isin(trans_from_11))]

    if np.any(v.wait_T > 0) and md_r.rate.sum() > 0 :
        _exp = check_moments_w(v.wait_T*tf , v.weight)
        lg4_remd, = ax[1,1].plot(int(k)*tf, _exp[1], "s", c=cl[5])

for a in ax[0,:]:
    a.plot(var_mcmc_h[:,0]/1000.0, var_mcmc_h[:,2], c="k")
    a.plot(var_mcmc_c[:,0]/1000.0, var_mcmc_c[:,2], c="grey")
    a.set_ylim(10**-2, 10**3)

for a in ax[1,:]:
    a.plot(var_mcmc_st3[:,0]/1000.0, var_mcmc_st3[:,2], "-D", c='k',
           mfc="None", mec='k', mew=1.0, zorder=1)
    a.plot(var_mcmc_st4[:,0]/1000.0, var_mcmc_st4[:,2], "-D", c='grey',
           mfc="None", mec='grey', mew=1.0, zorder=1)
    a.set_ylim(10**-4, 10**3)

for a in ax.flat:
    a.loglog()
    a.set_xlabel("$\mathregular{\Delta t \, [ns]}$", fontsize=12)
    a.set_xlim(10**-3.2, 10**1)
    a.tick_params(axis='both', which='major', labelsize=12)
    a.tick_params(axis='both', which='both', top=True , right=True)

for i, a in enumerate(fig_l):
    ax.flat[i].text(10**-5, 10**3, a, fontsize=20)
    ax.flat[i].set_ylabel(r"$\mathregular{ var(\tau) \, [ns^2]}$", fontsize=12)

    ax.flat[i].set_xticks([10**-3, 10**-2, 10**-1, 10**0, 10**1])

ax[0,1].legend([lg1_remd, lg2_remd], [r"REMD $h \rightarrow c$",
                               r"REMD $c \rightarrow h$"], loc=2,  borderaxespad=-0.1)
ax[0,0].legend([lg1_md, lg2_md], [r"MD $h \rightarrow c$",
                               r"MD $c \rightarrow h$"], loc=2,  borderaxespad=-0.1)

ax[1,1].legend([lg3_remd, lg4_remd], [r"REMD state 3",
                               r"REMD state 4"], loc=2,  borderaxespad=-0.1)
ax[1,0].legend([lg3_md, lg4_md], [r"MD state 3",
                               r"MD state 4"], loc=2,  borderaxespad=-0.1)

fig.tight_layout()

fig.savefig("plot/ala_dt-var_tau.png")
fig.savefig("plot/ala_dt-var_tau.pdf")
```

```python

```

```python

```