---
# System prepended metadata

title: Orbyts - UCL | Stellar International School

---

# Orbyts - UCL | Stellar International School

## Histogram
```python=
plt.hist(theta_Bnhat_list, bins=5, 
         facecolor='#313132', 
         edgecolor='#e0e0e0', 
         linewidth=0.5, 
         alpha=0.7)
```

## Coplanarity Normal
```python=
def coplanarity_normal(Bu, Bd):
    """ Coplanarity normal to the shock surface
        derived from coplanarity analysis formula.

    Args:
        Bu (list, array): Upstream magnetic field vector.
        Bd (list, array): Downstream magnetic field vector.

    Returns:
        n_cp (array): Coplanrity normal vector. 

    Example:
    >>> Bu = [-0.2, -0.3, 0.2]
    >>> Bd = [-0.5, -1, 0.4]
    >>> n_cp = coplanarity_normal(Bu, Bd)
    >>> print(n_cp, np.linalg.norm(n_cp))
    """
    
    Bu = np.array(Bu)
    Bd = np.array(Bd)
    dB = Bd-Bu
    BdXBu = np.cross(Bd, Bu)
    dBXBdXBu = np.cross(dB, BdXBu)
    n_cp = dBXBdXBu / np.linalg.norm(dBXBdXBu)
    return n_cp

```

## Calculate coplanarity normal
```python=
n_cp_samples = []
for i in range(N_CHUNKS):
    for j in range(N_CHUNKS):
        Bu = B_up_mean[i][0:3] # starrting from start of B_u
        Bd = B_down_mean[j][0:3] # starting from the end of B_d
        n_cp = coplanarity_normal(Bu, Bd)
        n_cp_samples.append(n_cp)
        #print(n_cp, np.linalg.norm(n_cp))
n_cp_samples = np.array(n_cp_samples)
print(n_cp_samples.shape)
```

## Calculate shock angle relative to the coplanarity shock normal
```python=
B_up_mean_averaged = B_up_mean[:,0:3].mean(axis=0)
theta_Bncp_samples = []

# loop through each coplanarity normal and do dot product with B_u
for i in range(len(n_cp_samples)):
    theta_Bncp = np.arccos(np.dot(B_up_mean_averaged, n_cp_samples[i])/np.linalg.norm(B_up_mean_averaged))*180/np.pi
    if theta_Bncp > 90:
        theta_Bncp = 180 - theta_Bncp
    theta_Bncp_samples.append(theta_Bncp)

theta_Bncp_mean = np.mean(theta_Bncp_samples)
theta_Bncp_std = np.std(theta_Bncp_samples)
theta_Bncp_mean, theta_Bncp_std
```

## Calculate shock angle relative to coplanarity normal in a loop:
The first part is the same as the code you have before when calculating the model normal.

```python=
theta_Bncp_samples_list = []
theta_Bncp_mean_list = []
theta_Bncp_std_list = []
ncp_samples_list = []

for i in range(len(df_BS_crossings)):
    print(f'BS crossing {i}:')

    # Get crossing time
    crossing_time = df_BS_crossings.iloc[i].TIME

    # Define interval
    time_start = crossing_time - pd.Timedelta(7, unit='m')
    time_end = crossing_time + pd.Timedelta(7, unit='m')
    df_crossing = df_1s.query("TIME > @time_start & TIME < @time_end")
    
    # Define B_up and B_down
    if df_BS_crossings.iloc[i].DIRECTION == 'I':
        B_up = df_crossing.query("TIME < @crossing_time")
        B_down = df_crossing.query("TIME > @crossing_time")
    elif df_BS_crossings.iloc[i].DIRECTION == 'O':
        B_up = df_crossing.query("TIME > @crossing_time")
        B_down = df_crossing.query("TIME < @crossing_time")

    # Split into 8 chunks
    N_CHUNKS = 8

    # Split B_up and B_down into 8 subintervals 
    B_up_chunks = np.array_split(B_up, N_CHUNKS)
    B_down_chunks = np.array_split(B_down, N_CHUNKS)
    
    # Calculate mean of each chunk giving 8 mean values for B_up and B_down
    B_up_mean = np.zeros((N_CHUNKS, 4))
    for j in range(N_CHUNKS):
        B_up_mean[j] = B_up_chunks[j].mean().values

    B_down_mean = np.zeros((N_CHUNKS, 4))
    for j in range(N_CHUNKS):
        B_down_mean[j] = B_down_chunks[j].mean().values
```

This next part is different, because we need to calculate coplanarity normal:

```python=
    # Calculate the coplanarity normal (64 samples)
    n_cp_samples = []
    # ENTER YOUR CODE HERE
    
    
    
    n_cp_samples = np.array(n_cp_samples)
    ncp_samples_list.append(n_cp_samples)

    # Calculate shock angle relative to the coplanarity shock normal
    # ENTER YOUR CODE HERE
    
    
    # Calculate mean
    theta_Bncp_mean = np.mean(theta_Bncp_samples)
    theta_Bncp_mean_list.append(theta_Bncp_mean)
    theta_Bncp_samples_list.append(theta_Bncp_samples)

    # Calculate errors: 1 standard deviation
    theta_Bncp_std = np.std(theta_Bncp_samples)
    theta_Bncp_std_list.append(theta_Bncp_std)
```

### The solution to Analysis 2
```python=
theta_Bncp_samples_list = []
theta_Bncp_mean_list = []
theta_Bncp_std_list = []
ncp_samples_list = []

for i in range(len(df_BS_crossings)):
    print(f'BS crossing {i}:')

    # Get crossing time
    crossing_time = df_BS_crossings.iloc[i].TIME

    # Define interval
    time_start = crossing_time - pd.Timedelta(7, unit='m')
    time_end = crossing_time + pd.Timedelta(7, unit='m')
    df_crossing = df_1s.query("TIME > @time_start & TIME < @time_end")
    
    # Define B_up and B_down
    if df_BS_crossings.iloc[i].DIRECTION == 'I':
        B_up = df_crossing.query("TIME < @crossing_time")
        B_down = df_crossing.query("TIME > @crossing_time")
    elif df_BS_crossings.iloc[i].DIRECTION == 'O':
        B_up = df_crossing.query("TIME > @crossing_time")
        B_down = df_crossing.query("TIME < @crossing_time")

    # Split into 8 chunks
    N_CHUNKS = 8

    # Split B_up and B_down into 8 subintervals 
    B_up_chunks = np.array_split(B_up, N_CHUNKS)
    B_down_chunks = np.array_split(B_down, N_CHUNKS)
    
    # Calculate mean of each chunk giving 8 mean values for B_up and B_down
    B_up_mean = np.zeros((N_CHUNKS, 4))
    for j in range(N_CHUNKS):
        B_up_mean[j] = B_up_chunks[j].mean().values

    B_down_mean = np.zeros((N_CHUNKS, 4))
    for j in range(N_CHUNKS):
        B_down_mean[j] = B_down_chunks[j].mean().values

    # Calculate the coplanarity shock normal (64 samples)
    n_cp_samples = []
    for j in range(N_CHUNKS):
        for k in range(N_CHUNKS):
            Bu = B_up_mean[j][0:3] # starrting from start of B_u
            Bd = B_down_mean[k][0:3] # starting from the end of B_d
            n_cp = coplanarity_normal(Bu, Bd)
            n_cp_samples.append(n_cp)
            #print(n_cp, np.linalg.norm(n_cp))
    n_cp_samples = np.array(n_cp_samples)
    ncp_samples_list.append(n_cp_samples)

    # Calculate shock angle relative to the coplanarity shock normal
    B_up_mean_averaged = B_up_mean[:,0:3].mean(axis=0)
    theta_Bncp_samples = []
    for j in range(len(n_cp_samples)):
        theta_Bncp = np.arccos(np.dot(B_up_mean_averaged, n_cp_samples[j])/np.linalg.norm(B_up_mean_averaged))*180/np.pi
        if theta_Bncp > 90:
            theta_Bncp = 180 - theta_Bncp
        theta_Bncp_samples.append(theta_Bncp)
    theta_Bncp_mean = np.mean(theta_Bncp_samples)
    theta_Bncp_mean_list.append(theta_Bncp_mean)
    theta_Bncp_samples_list.append(theta_Bncp_samples)

    # errors correspond to 1 standard deviation
    theta_Bncp_std = np.std(theta_Bncp_samples)
    theta_Bncp_std_list.append(theta_Bncp_std)

```

### Plot the results
```python=
plt.hist(theta_Bncp_mean_list, bins=5, facecolor='#002045', edgecolor='#e0e0e0', linewidth=0.5, alpha=0.7)
plt.xlim([0,90])
plt.xlabel(r'Shock angle $\theta_{Bncp}$(degree)')
plt.ylabel('Frequency')
#plt.savefig("./output/fig_shock_angle_coplanarity_distribution.jpg", dpi=300)
```
##  :busts_in_silhouette: Group 1 (Yvonne, Dayana, Sachiin, Diviyesh)
You will be responsible for plotting skewness vs kurtosis of coplanarity shock angles. 

The skewness tells whether the distribution of the shock angle for a particular crossing is 'normal' or not. 

A skewness of 0 means normally distributed (bell curve). 
Skewness > 0 means more weight in the left tail. 
Skewness < 0 means more weight in the right tail. 

A kurtosis of 3 means normally distributed (bell curve).
Kurtosis > 3 means heavier tail (less peaked). 
Kurtosis < 3 means lighter tail (more peaked). 

[Click here](https://scontent-hkg4-2.xx.fbcdn.net/v/t1.6435-9/96665492_125491382456362_2119748312570527744_n.jpg?_nc_cat=110&ccb=1-7&_nc_sid=8bfeb9&_nc_ohc=BHo_-DU9KowAX_5_YEq&_nc_ht=scontent-hkg4-2.xx&oh=00_AT-GOh1aHnR_RtiROkuztdiIrBpYW6UlBgugtyNBZeJ4Ig&oe=62E25E37) to see example distributions for different skewness and kurtosis.
```python=
# Calculate skewness and kurtosis 
from scipy.stats import skew
from scipy.stats import kurtosis

theta_Bncp_skewness = [skew(ENTER CODE HERE) for i in range(len(theta_Bncp_samples_list))]

theta_Bncp_kurtosis = [kurtosis(ENTER CODE HERE, fisher=False) for i in range(len(theta_Bncp_samples_list))]
```

### Plot the results
Make the plot and save it in your colab session.
```python=
plt.scatter(theta_Bncp_kurtosis, theta_Bncp_skewness, marker='+', color='k')
plt.axvline(x=3, color='k', linestyle='--', alpha=0.5)
plt.axhline(y=0, color='k', linestyle='--', alpha=0.5)
plt.xlabel(r'Kurtosis of $\theta_{BN}$')
plt.ylabel(r'Skewness of $\theta_{BN}$')
#plt.savefig("fig_shock_angle_skewness_kurtosis.jpg", dpi=300)

```


##  :busts_in_silhouette: Group 2 (Belle, Junhao, Rui, Tingyu) 
You will be responsible for plotting the angle between coplanarity normal and model normal ($\theta_{CPM}$) vs coplanarity shock angle:

You will need to store the $\hat{n}$ vectors from Analysis 1 where we calculated the shock angle using the BS model. 
```python=
nhat_list = []
...

nhat_list.append(nhat)
```

Now to calculate the angle between the model normal and the coplanarity normal we do the following:
```python=
# loop through the nhat and ncp, and compute the dot product
theta_cpm_samples_list = []
theta_cpm_mean_list = []
theta_cpm_std_list = []
for i in range(len(ncp_samples_list)):
    nhat = nhat_list[i]
    ncp_samples = ncp_samples_list[i]
    theta_cpm_samples = []
    for j in range(len(ncp_samples)):
        theta_cpm = # ENTER CODE HERE
        if theta_cpm > 90:
            theta_cpm = 180 - theta_cpm
        theta_cpm_samples.append(theta_cpm)
    
    theta_cpm_samples_list.append(theta_cpm_samples)
    theta_cpm_mean_list.append(np.mean(theta_cpm_samples))
    theta_cpm_std_list.append(np.std(theta_cpm_samples))
```

### Plot results
Make the plot and save it in your colab session.
```python=
plt.errorbar(x=theta_Bncp_mean_list, y=theta_cpm_mean_list, 
            xerr=theta_Bncp_std_list, yerr=theta_cpm_std_list, 
            fmt='+', color='k', linewidth=1, alpha=0.5)
plt.axvline(x=45, color='k', linestyle='--', alpha=0.5)
plt.xlabel(r'Shock Angle $\theta_{BN}$ (degrees)')
plt.ylabel(r'Angle between model and coplanarity normals $\theta_{CPM}$ (degrees)')
#plt.savefig("fig_thetacpm_vs_shock_angle.jpg", dpi=300)
```


##  :busts_in_silhouette: Group 3 (Angelina, Boston, Boon, Ian)
You will be responsible for plotting Shock angle deflection $\alpha$ vs field strength ratio $Bd/Bu$

Deflection angle is the angle between $B_d$ and $B_u$. 

```python=
# shock deflection angle
# Do dot product of Bu vs Bd to get 64 deflection angles, calculate 1 standard deviation
# Calculate Bd/Bu to get 64 ratios, calculate 1 standard deviation
deflection_angle_samples_list = []
deflection_angle_mean_list = []
deflection_angle_std_list = []
BdToBu_samples_list = []
BdToBu_mean_list = []
BdToBu_std_list = []

for i in range(len(df_BS_crossings)):
    print(f'BS crossing {i}:')

    # Get crossing time
    crossing_time = df_BS_crossings.iloc[i].TIME

    # Define interval
    time_start = crossing_time - pd.Timedelta(7, unit='m')
    time_end = crossing_time + pd.Timedelta(7, unit='m')
    df_crossing = df_1s.query("TIME > @time_start & TIME < @time_end")
    
    # Define B_up and B_down
    if df_BS_crossings.iloc[i].DIRECTION == 'I':
        B_up = df_crossing.query("TIME < @crossing_time")
        B_down = df_crossing.query("TIME > @crossing_time")
    elif df_BS_crossings.iloc[i].DIRECTION == 'O':
        B_up = df_crossing.query("TIME > @crossing_time")
        B_down = df_crossing.query("TIME < @crossing_time")

    # Split B_up and B_down into 8 subintervals
    N_CHUNKS = 8
    B_up_chunks = np.array_split(B_up, N_CHUNKS)
    B_down_chunks = np.array_split(B_down, N_CHUNKS)
    
    # Calculate mean of each chunk giving 8 mean values for B_up and B_down
    B_up_mean = np.zeros((N_CHUNKS, 4))
    for j in range(N_CHUNKS):
        B_up_mean[j] = B_up_chunks[j].mean().values

    B_down_mean = np.zeros((N_CHUNKS, 4))
    for j in range(N_CHUNKS):
        B_down_mean[j] = B_down_chunks[j].mean().values
        
    # Deflection angle analysis STARTS HERE
    BdToBu_samples = []
    deflection_angle_samples = []
    for j in range(N_CHUNKS):
        for k in range(N_CHUNKS):
            Bu = B_up_mean[j][0:3] # starting from start of B_u
            Bd = B_down_mean[k][0:3] # starting from the end of B_d
            # Calculate deflection angle $\alpha$
            deflection_angle = #ENTER CODE HERE
            if deflection_angle > 90:
                deflection_angle = 180 - deflection_angle
            deflection_angle_samples.append(deflection_angle)

            # Calculate Bd/Bu ratio
            BdToBu_samples.append(np.linalg.norm(Bd)/np.linalg.norm(Bu))


    # store results in a list
    deflection_angle_samples_list.append(deflection_angle_samples)
    deflection_angle_mean_list.append(np.nanmean(deflection_angle_samples))
    deflection_angle_std_list.append(np.nanstd(deflection_angle_samples))

    BdToBu_samples_list.append(BdToBu_samples)
    BdToBu_mean_list.append(np.nanmean(BdToBu_samples))
    BdToBu_std_list.append(np.nanstd(BdToBu_samples))

```

### Plot results
Make the plot and save it in your colab session.
```python=
plt.errorbar(x=BdToBu_mean_list, y=deflection_angle_mean_list,
            xerr=BdToBu_std_list, yerr=deflection_angle_std_list, 
            fmt='+', color='k', linewidth=1, alpha=0.5)
plt.xlabel(r'Downstream / Upstream Field Ratio $B_d/B_u$')
plt.ylabel(r'Shock deflection angle $\alpha$ (degrees)')
#plt.savefig("fig_shock_deflection_angle_vs_BdToBu.jpg", dpi=300)
```

# Presentation
Intro and method sections:
* Slides 4-9: Angelina, Dayana, Belle, Boston
* Slides 10-13: Divyesh, Junhao, Boon, Rui, Ian, Tingyu

Results sections:
* Results 1: Rui
* Results 2: Boon
* Results 3: Group 1
* Results 4: Group 2
* Results 5: Group 3

Conclusion: 
* Slide : 