<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 Goal
Step1:  Load the data
According to the method I laid out at the end of singles_correction.ipynb, I need to load the following data
Step2:  emin, emax will be input parameters. I will vary emin.
Step3:  Calculate $A_{sym}$ in a given energy range
Use the all-in-one function for calculating singles_df, det_df, and by_angles_df in one step
Step4:  The code will produce the sum across energy ranges that are rounded down from these values to find the bin edges. Where should I get them back from?
Add optional flag to return energies_real. This way it won't break all my old notebooks. Note that energies_real is [emax_real,emin_real].
Step5:  Store information at $180^\circ$ and $90^\circ$
Find the bins in by_angle_df that contain $180^\circ$ and $90^\circ$.
Step6:  Functionalize this
Step7:  Calculate for variable emin
Create Asym_df for storing results
Create a dataframe for storing these values, because I like dataframes now.
Step8:  Fill Asym_df
Step9:  If instead I want to propagate errors from W_err (statistical error)
Step10:  Plot, save figure
Step11:  Functionalize this
Step12:  I'm updating this to return emin_real and emax_real, which are slightly different than emin and emax due to discrete binning.
Step13:  Calculate for variable ebin
Instead of varying energy threshold, only select events within a given energy bin, bound by emin, emax.
The only thing I really need to change is how I initialize Asym_df.
Step14:  I updated this method in a new function called bicorr_sums.calc_Asym_vs_ebin. The rest of the function is the same as calc_Asym_vs_emin.
<ASSISTANT_TASK:>
Python Code:
import os
import sys
import matplotlib.pyplot as plt
import numpy as np
import imageio
import pandas as pd
import seaborn as sns
sns.set(style='ticks')
sys.path.append('../scripts/')
import bicorr as bicorr
import bicorr_plot as bicorr_plot
import bicorr_sums as bicorr_sums
import bicorr_math as bicorr_math
%load_ext autoreload
%autoreload 2
Explanation: Goal: Calculate $A_{sym}$ for different energy thresholds or energy bins
This is a very simple and straightforward way of demonstrating that higher energy neutrons experience stronger anisotropy. 
We will calculate the magnitude of anisotropy as a one-dimensional parameter as:
$$A_{sym} = \frac{W(180^\circ)}{W(90^\circ)}$$
This is the same method as Tony Shin is using in his multiplicity counter work, and the same method that several previous papers have used.
End of explanation
det_df = bicorr.load_det_df('../meas_info/det_df_pairs_angles.csv', remove_fc_neighbors = True)
singles_hist, dt_bin_edges_sh, dict_det_to_index, dict_index_to_det = bicorr.load_singles_hist(filepath='../analysis/Cf072115_to_Cf072215b/datap')
npzfile = np.load('../analysis_no_Ethresh_with_CT\Cf072115_to_Cf072215b\datap/bhp_nn_by_pair_1ns.npz')
bhp_nn_pos = npzfile['bhp_nn_pos']
bhp_nn_neg = npzfile['bhp_nn_neg']
dt_bin_edges = npzfile['dt_bin_edges']
Explanation: Load the data
According to the method I laid out at the end of singles_correction.ipynb, I need to load the following data:
det_df
dict_index_to_det
singles_hist
dt_bin_edges_sh
bhp_nn_pos
bhp_nn_neg
dt_bin_edges
emin
emax
num_fissions
angle_bin_edges
I will work with the data from the combined data sets in analysis\Cf072115_to_Cf072215b.
End of explanation
num_fissions = 2194651200.00
angle_bin_edges = np.arange(8,190,10)
Explanation: emin, emax will be input parameters. I will vary emin.
End of explanation
emin = .62
emax = 12
Explanation: Calculate $A_{sym}$ in a given energy range
Use the all-in-one function for calculating singles_df, det_df, and by_angles_df in one step: bicorr_sums.perform_W_calcs.
Look at what has been our default energy range.
Produce all of the relevant dataframes
End of explanation
singles_df, det_df, by_angle_df, energies_real = bicorr_sums.perform_W_calcs(det_df,
                    dict_index_to_det, singles_hist, dt_bin_edges_sh,
                    bhp_nn_pos, bhp_nn_neg, dt_bin_edges,
                    num_fissions, emin, emax, angle_bin_edges, return_real_energies_flag = True)
print(energies_real)
Explanation: The code will produce the sum across energy ranges that are rounded down from these values to find the bin edges. Where should I get them back from?
Add optional flag to return energies_real. This way it won't break all my old notebooks. Note that energies_real is [emax_real,emin_real].
End of explanation
angle_bin_edges
series_180 = by_angle_df.loc[np.int(np.digitize(180,angle_bin_edges))-1]
series_90 = by_angle_df.loc[np.int(np.digitize(90,angle_bin_edges))-1]
series_180['W']/series_90['W']
series_min = by_angle_df.loc[by_angle_df['W'].idxmin()]
num = series_180['W']
num_err = series_180['W_err']
denom = series_90['W']
denom_err = series_90['W_err']
minval = series_min['W']
minval_err = series_min['W_err']
Asym, Asym_err = bicorr_math.prop_err_division(num,num_err,denom,denom_err)
Asym_min, Asym_min_err = bicorr_math.prop_err_division(num,num_err,minval,minval_err)
print(Asym, Asym_err)
print(Asym_min, Asym_min_err)
Explanation: Store information at $180^\circ$ and $90^\circ$
Find the bins in by_angle_df that contain $180^\circ$ and $90^\circ$.
End of explanation
bicorr_sums.calc_Asym(by_angle_df)
bicorr_sums.calc_Asym(by_angle_df, min_flag=True)
Explanation: Functionalize this
End of explanation
emins = np.arange(0.5,5,.2)
print(emins)
emax = 12
Asym_df = pd.DataFrame(data = emins, columns = {'emin'})
Asym_df['emax'] = emax
Asym_df['Asym'] = np.nan
Asym_df['Asym_err'] = np.nan
Asym_df['Asym_min'] = np.nan
Asym_df['Asym_min_err'] = np.nan
Asym_df.head()
Explanation: Calculate for variable emin
Create Asym_df for storing results
Create a dataframe for storing these values, because I like dataframes now.
End of explanation
for index, row in Asym_df.iterrows():    
    singles_df, det_df, by_angle_df = bicorr_sums.perform_W_calcs(det_df,
                    dict_index_to_det, singles_hist, dt_bin_edges_sh,
                    bhp_nn_pos, bhp_nn_neg, dt_bin_edges,
                    num_fissions, row['emin'], row['emax'], angle_bin_edges)
    Asym, Asym_err, Asym_min, Asym_min_err = bicorr_sums.calc_Asym(by_angle_df,min_flag=True)
    
    Asym_df.loc[index,'Asym'] = Asym
    Asym_df.loc[index,'Asym_err'] = Asym_err
    Asym_df.loc[index,'Asym_min'] = Asym_min
    Asym_df.loc[index,'Asym_min_err'] = Asym_min_err
    
plt.figure(figsize=(4,3))
plt.errorbar(Asym_df['emin'],Asym_df['Asym'],yerr=Asym_df['Asym_err'],fmt='.',color='k')
plt.xlabel('$E_{min}$ (MeV)')
plt.ylabel('$A_{sym}$')
plt.title('Errors from std(W)')
sns.despine(right=True)
bicorr_plot.save_fig_to_folder('Asym_vs_angle_std_W')
plt.show()
Explanation: Fill Asym_df
End of explanation
for index, row in Asym_df.iterrows():    
    singles_df, det_df, by_angle_df = bicorr_sums.perform_W_calcs(det_df,
                    dict_index_to_det, singles_hist, dt_bin_edges_sh,
                    bhp_nn_pos, bhp_nn_neg, dt_bin_edges,
                    num_fissions, row['emin'], row['emax'], angle_bin_edges)
    Asym, Asym_err, Asym_min, Asym_min_err = bicorr_sums.calc_Asym(by_angle_df,std_flag=True,min_flag=True)
    
    Asym_df.loc[index,'Asym'] = Asym
    Asym_df.loc[index,'Asym_err'] = Asym_err
    Asym_df.loc[index,'Asym_min'] = Asym_min
    Asym_df.loc[index,'Asym_min_err'] = Asym_min_err
Explanation: If instead I want to propagate errors from W_err (statistical error):
End of explanation
plt.figure(figsize=(4,3))
plt.errorbar(Asym_df['emin'],Asym_df['Asym'],yerr=Asym_df['Asym_err'],fmt='.',color='k')
plt.xlabel('$E_{min}$ (MeV)')
plt.ylabel('$A_{sym}$')
plt.title('Errors from W_err')
sns.despine(right=True)
bicorr_plot.save_fig_to_folder('Asym_vs_angle_W_err')
plt.show()
plt.figure(figsize=(4,3))
plt.errorbar(Asym_df['emin'],Asym_df['Asym_min'],yerr=Asym_df['Asym_min_err'],fmt='.',color='k')
plt.xlabel('$E_{min}$ (MeV)')
plt.ylabel('$A_{sym}$')
plt.title('Errors from W_err')
sns.despine(right=True)
bicorr_plot.save_fig_to_folder('Asym_min_vs_angle_W_err')
plt.show()
Explanation: Plot, save figure
End of explanation
Asym_df = bicorr_sums.calc_Asym_vs_emin(det_df,
                    dict_index_to_det, singles_hist, dt_bin_edges_sh,
                    bhp_nn_pos, bhp_nn_neg, dt_bin_edges,
                    num_fissions, emins, emax, angle_bin_edges,
                    plot_flag=True, save_flag=False)
Asym_df.head()
Explanation: Functionalize this
End of explanation
Asym_df = bicorr_sums.calc_Asym_vs_emin(det_df,
                    dict_index_to_det, singles_hist, dt_bin_edges_sh,
                    bhp_nn_pos, bhp_nn_neg, dt_bin_edges,
                    num_fissions, emins, emax, angle_bin_edges,
                    plot_flag=True, save_flag=False)
Asym_df.head()
Explanation: I'm updating this to return emin_real and emax_real, which are slightly different than emin and emax due to discrete binning.
End of explanation
e_bin_edges = np.arange(0.5,5,.2)
Asym_df = pd.DataFrame(data = {'emin':e_bin_edges[:-1],'emax':e_bin_edges[1:]})
Asym_df.head()
Explanation: Calculate for variable ebin
Instead of varying energy threshold, only select events within a given energy bin, bound by emin, emax.
The only thing I really need to change is how I initialize Asym_df.
End of explanation
Asym_df = bicorr_sums.calc_Asym_vs_ebin(det_df,
                    dict_index_to_det, singles_hist, dt_bin_edges_sh,
                    bhp_nn_pos, bhp_nn_neg, dt_bin_edges,
                    num_fissions, e_bin_edges, angle_bin_edges,
                    plot_flag=True, save_flag=False)
Asym_df.head()
Explanation: I updated this method in a new function called bicorr_sums.calc_Asym_vs_ebin. The rest of the function is the same as calc_Asym_vs_emin.
End of explanation
<END_TASK>