##### Last modified: March 5th 2021 (Opal)

# Cross-Correlation

### Background
Cross-correlation is a mathematical operation that is commonly used in signal processing and pattern recognition to measure similarity of two functions as they are displaced from one another. For two finite discrete signals $f, g \in \mathbb{C}^{n}$, the uni-variate discrete *circular cross-correlation* ($\star$) is defined as

\begin{equation}\label{cc-dis-circular-definition}
    (f \star g)[\tau] \equiv \sum_{j=0}^{n-1} \overline{f[j]} g[(j + \tau)_{\text{mod} n}]
\end{equation}

where $\overline{f}$ denotes the complex conjugate of $f$, the bracket $[j]$ denotes the $j^{th}$ element of the signal, and $\tau \in \mathbb{Z}$ is the discrete displacement. In general, discrete circular cross-correlation can be extended to the multi-variate case for snapshots with $k \in \mathbb{Z}$ variables:

\begin{equation}\label{cc-dis-definition-multi-variate}
(f \star \overset {k}{\cdots } \star g)[\overline{\tau}] \equiv \sum_{j_{1}=-\infty}^{\infty} ... \sum_{j_{k}=-\infty}^{\infty}
\overline{f[j_{1}, ..., j_{k}]} g[(j_{1}+ \tau_{1})_{\text{mod} n_{1}}, (j_{2}+ \tau_{2})_{\text{mod} n_{2}}, ..., (j_{k} + \tau_{k})_{\text{mod} n_{k}}]
\end{equation}

where $\overline{\tau}= [\tau_{1}, \tau_{2},... ,\tau_{k}] \in \mathbb{Z}^{k}$ is the multi-variate discrete displacement. The optimal shift between the two discrete signals, $\bar{\tau}^{*}$, is found by the maximum of the cross-correlation, such that

\begin{equation}\label{cc-max}
    \bar{\tau}^{*} = {\text{argmax}_{\bar{\tau} \in \mathbb{Z}^{k}}} {(f \star \overset {k}{\cdots } \star g)[\overline{\tau}]}
\end{equation}

Once the optimal cross-correlation is found for all training snapshots by applying circular discrete cross-correlation between each snapshot and the initial condition, the shift function $\mathbf{c}(t)$ is found via polynomial curve fitting between the time increments $\left[t_{0}, t_{1}, ..., t_{f} \right]$ and corresponding optimal shift $\left[\tau^{\star}_{0}, \tau^{\star}_{1}, ..., \tau^{\star}_{f} \right]$.  The wave speed at the testing regime is approximated by extrapolating $\mathbf{c}(t)$ beyond the training regime. 

### Motivation to use to find spatial shifting in convective-dominated problems
When the functions correlate, the value of $(f \ast g)$ is maximized. This is because when peaks and troughs align, they make a positive contribution to the finite sum.

### Circular convolution theorem and cross-correlation theorem
Given sequences $x$ and $y_{N}$, where $y_{N}$ is N-periodic, then the cross-correlation of $x$ and $y_{N}$ is given by 
$$
x \ast y_{N} \equiv \mathcal{F}^{-1}\{ \overline{\mathcal{F}\{x\}} \cdot \mathcal{F}{\{y\}\}}
$$

where $\mathcal{F}$ denotes the Fourier transform, and $\overline{\mathcal{F}\{x\}}$ indicates the complex conjugate of $\mathcal{F}\{x\}$. This is analogous to the convolution theorem.

### In this notebook, we apply the circular discrete cross-correlation to: 
1. MHD 1D SLICES. $\checkmark$
2. MHD 2D SLICES. $\checkmark$
3. HUX 1D SLICES. (A comparison of technqiues) $\checkmark$


```python
import os
curr_dir = os.getcwd()
os.chdir("../")
print("current directory path = ", os.getcwd())
```

    current directory path =  /Users/oissan/PycharmProjects/Space-Weather-ROM2



```python
import numpy as np
import scipy.linalg as la
import scipy.sparse as sparse
from scipy import signal
import scipy
from scipy.ndimage import correlate
from numpy.fft import fft, ifft
from scipy.interpolate import interp1d
from scipy.integrate import solve_ivp
from tools.MASweb import get_mas_path
from psipy.model import MASOutput
from psipy.data import sample_data
from psipy.model.variable import Variable
import rom_operator_inference as roi
from scipy import stats
import numpy as np 
import time
import astropy.units as u
```


```python
os.chdir(os.getcwd() + "/code/")
from numerical_methods import apply_numerical_method
from hux_propagation import apply_hux_f_model
os.chdir("../")
```


```python
import matplotlib.pyplot as plt
import matplotlib
font = {'family' : 'serif',
        'size'   : 15}

matplotlib.rc('font', **font)
matplotlib.rc('xtick', labelsize=15) 
matplotlib.rc('ytick', labelsize=15) 
```

# Import MHD dataset


```python
# mas output - br, rho, vr
mas_path = get_mas_path(cr="2210")
model = MASOutput(mas_path)
print(model.variables)
```

    ['br', 'rho', 'vr']



```python
# read in MHD velocity results. 
vr_model = model['vr']
```


```python
# save MHD mesh coordinates Heliographic (rotating) Coordinate System (HG)
# phi - (0, 2pi)
p = vr_model.phi_coords
# sin(theta) - (-pi/2, pi/2)
t = vr_model.theta_coords
# 30 solar radii to approximately 1 AU
r = (vr_model.r_coords * u.solRad).to(u.AU)
# velocity profile 
f = vr_model.data
print(f.shape)
```

    (128, 111, 140)



```python
# force periodicity on f
# since the last phi index is less than 2*pi, then we will append 2*pi to phi scale.
p = np.append(p, 2*np.pi) 

# append the first slice p=0 to velocity matrix. 
f = np.append(f,[f[0,:, :]],axis=0)
```


```python
r1, r2 = 0, 125
v1 = f[:, 55, r1]
v2 = f[:, 55, r2]
```


```python
fig, ax = plt.subplots(figsize=(15, 5))
_ = ax.plot(180*p/np.pi, v1, ls= "-", label="$v_{1}(\phi, $ r = "+ str(round(r[r1].value, 3)) + "AU)")
_ = ax.plot(180*p/np.pi, v2, ls="-.", label="$v_{2}(\phi$, r = " + str(round(r[r2].value, 3)) + "AU)")
_ = ax.set_xticks([0, 90, 180, 270, 360])
_ = ax.set_xlabel("$\phi$ (Deg.)")
_ = ax.set_ylabel("$v_{r}$(km/s)")
_ = plt.legend()
```

# Circular discrete cross-correlation


```python
def normalize(x):
    return (x - np.mean(x))/np.std(x)
```


```python
def periodic_corr_fft(x, y):
    """Periodic correlation, implemented using the FFT.
    x and y must be real sequences with the same length.
    """
    return ifft(fft(x).conj() * fft(y)).real

def periodic_corr_np(x, y):
    """Periodic correlation, implemented using np.correlate.

    x and y must be real sequences with the same length.
    """
    return np.correlate(x, np.hstack((y[1:], y)), mode='valid')
```


```python
res2 = periodic_corr_np(normalize(v1), normalize(v2))
res2 /= np.max(res2)
```


```python
fig, (ax_v1, ax_v2, ax_corr, ax_lap) = plt.subplots(4, 1, figsize=(10, 10))
ax_v1.plot(180*p/np.pi, v1)
ax_v1.set_title('$v_{1}$')
ax_v1.set_xlabel('Carrington Longitude (Deg.)')
ax_v1.set_ylabel('$v_{r}$ (km/s)')
ax_v1.set_xticks([0, 90, 180, 270, 360])

ax_v2.plot(180*p/np.pi, v2)
ax_v2.set_title('$v_{2}$')
ax_v2.set_xlabel('Carrington Longitude (Deg.)')
ax_v2.set_ylabel('$v_{r}$ (km/s)')
ax_v2.set_xticks([0, 90, 180, 270, 360])


ax_corr.scatter(180*p/np.pi, res2)
ax_corr.scatter(p[np.argmax(res2)]*180/np.pi, np.max(res2), c="r")
ax_corr.axvline(x=p[np.argmax(res2)]*180/np.pi, linestyle="--", c="r")

ax_corr.set_xticks([360, 270, 180, 90, 0, round(p[np.argmax(res2)]*180/np.pi)])
ax_corr.set_title('Cross-correlated signal')
ax_corr.set_xlabel('Lag Carrington Longitude (Deg.)')
ax_corr.set_ylabel("ZNCC")



if np.argmax(res2) < len(p):
    index_roll = len(p) - np.argmax(res2)
else:
    index_roll = np.argmax(res2) - len(p)
ax_lap.plot(180/np.pi*p, v1, c="r", linestyle="--", label="$v_{1}$")
ax_lap.plot(180/np.pi*p, v2, c="g", linestyle="-.", label="$v_{2}$")
ax_lap.scatter(180/np.pi*np.roll(p, shift=index_roll), v2, c="b", s=2, label="shifted $v_{2}$")

ax_lap.set_xticks([0, 90, 180, 270, 360])
ax_lap.set_title('shifted $v_{2}$')
ax_lap.set_xlabel('Carrington Longitude (Deg.)')
ax_lap.set_ylabel("$v_{r}$ (km/s)")

plt.legend()
fig.tight_layout()
```

# Apply cross-correlation to all Equator velocity profiles


```python
v1 = f[:, 55, 0]
shift_vec = [0]
for ii in range(1, len(r)):
    v2 = f[:, 55, ii]
    corr = periodic_corr_np(normalize(v1), normalize(v2))
    corr = corr/np.max(corr)
    shift_vec.append(p[np.argmax(corr)]*180/np.pi)
```


```python
fig, ax = plt.subplots(figsize=(7, 3))
# plot the shifted data.
_ = ax.plot(r, shift_vec, ls="-.")

# given only 50%. of the training data- can we extrapolate?
n = int(len(r)*0.5)
m, b = np.polyfit(r[:n].value, shift_vec[:n], 1)
_ = ax.plot(r, m*r.value + b, label="linear-fit")

_ = ax.set_xlabel("Heliocentric Distance (AU)")
_ = ax.set_ylabel("Longitude Shift (Deg.)")
_ = ax.set_title("Cross-Correlation Shifting \n Linear-fit: y = " + str(round(m, 2)) + "x" + str(round(b, 2)))
_ = plt.legend()
_ = plt.tight_layout()

plt.savefig("CrossCorrelation/figs/cross-correlation-shift-128np.png")
```


```python
V_new = np.zeros((len(p), len(r)))
V_new[:, 0] = f[:, 55, 0]

for jj in range(1, len(r)):
    f_interp = interp1d(p*180/np.pi, f[:,55, jj], fill_value="extrapolate", bounds_error=False)
    x_new = (p*180/np.pi - shift_vec[jj]) % (360)
    V_new[:, jj] = f_interp(x_new)
```


```python
def plot_heat_data(Z, title, sample_columns=np.arange(0, len(r), 15), variable="v", xlabel="$\phi$ (Deg.)", ylabel="$v_{r}$ ( km/s)",
                  save_fig=False, file_name=None):
    
    fig, ax = plt.subplots(figsize=(14, 4))
    color = iter(plt.cm.viridis_r(np.linspace(0, 1, len(sample_columns))))

    for j in sample_columns:
        x_all = np.concatenate([[0], Z[:,j], [0]])  # Pad results with boundary conditions.
        ax.plot(p*(180/np.pi), Z[:,j], color=next(color), label=str(variable) + rf"$(\phi,r$" + "=" + 
                str(round(r[j].to(u.AU).value,2)) + " AU)")

    ax.set_xlabel(xlabel, fontsize=15)
    ax.set_xticks([0, 90, 180, 270, 360])
    ax.set_ylabel(ylabel, fontsize=15)
    ax.legend(loc=(1.05, .05))
    ax.spines["right"].set_visible(False)
    ax.spines["top"].set_visible(False)
    ax.tick_params(axis='both', which='major', labelsize=12)
    fig.suptitle(title, fontsize=15)
    
    if save_fig:
        fig.savefig(file_name, dpi=300, bbox_inches='tight')
```


```python
plot_heat_data(V_new, title="Shifted MHD at the Equator")
```

# Apply circular Cross-Correlation to 2D MHD Data


```python
v1 = f[:, :, r1]
v2 = f[:, :, r2]
```


```python
def periodic_corr_2d_longitude_only(x, y):
    """Periodic correlation, implemented using np.correlate.

    x and y must be real sequences with the same length.
    """
    return scipy.signal.correlate2d(x, np.concatenate((y[1:, :], y)), mode='valid')
```


```python
def periodic_corr_2d(x, y):
    """Periodic correlation, implemented using np.correlate.

    x and y must be real sequences with the same length.
    """
    return scipy.signal.correlate2d(x, np.concatenate((y[1:, :], y)), boundary="wrap", mode='same')
```


```python
res = periodic_corr_2d(normalize(v1), normalize(v2))
res/= np.max(res)
```


```python
res.shape
```




    (129, 111)




```python
indices = np.where(res == res.max())
```


```python
print("latitude shift = ", t[indices[1][0]])
```

    latitude shift =  -4.371139006309477e-08



```python
print("longitude shift = ", p[indices[0][0]]*180/np.pi)
```

    longitude shift =  45.00000125223908



```python
fig, ax = plt.subplots(nrows=3, sharex=True, figsize=(6, 9))

ax[0].imshow(v1.T,
                extent=[p[0]*180/np.pi, p[-1]*180/np.pi, t[0]*180/np.pi, t[-1]*180/np.pi],   
                vmin = 300, vmax = 700, cmap="plasma", aspect="auto", origin="lower")
ax[0].set_title("(a) $V_{30R_{S}}$")
im = ax[1].imshow(v2.T,
                extent=[p[0]*180/np.pi, p[-1]*180/np.pi, t[0]*180/np.pi, t[-1]*180/np.pi],   
                vmin = 300, vmax = 700, cmap="plasma", aspect="auto", origin="lower")

ax[1].set_title("(b) $V_{1 AU}$")

img = ax[2].imshow(res.T, extent=[p[0]*180/np.pi, p[-1]*180/np.pi, t[0]*180/np.pi, t[-1]*180/np.pi], origin="lower")
ax[2].scatter(p[indices[0][0]]*180/np.pi, t[indices[1][0]], c="r")

fig.subplots_adjust(right=0.8)
cbar_ax = fig.add_axes([0.85, 0.13, 0.03, 0.21])
plt.colorbar(img, cax=cbar_ax)
_ = cbar_ax.set_ylabel('ZNCC', rotation=90)

ax[2].set_xticks([360, 270, 180, 90, 0, round(p[indices[0][0]]*180/np.pi)])
ax[2].set_title("(c) $V_{30R_{S}} \star V_{1 AU}$")

ax[2].set_xlabel(r"Longitude (Deg.)", fontsize=14)
ax[0].set_ylabel(r"$\theta$ (Deg.)", fontsize=14)
ax[1].set_ylabel(r"$\theta$ (Deg.)", fontsize=14)
ax[2].set_ylabel(r"$\theta$ (Deg.)", fontsize=14)
ax[2].set_ylabel(r"$\theta$ (Deg.)", fontsize=14)
ax[0].set_yticks([-90, -45, 0, 45, 90])
ax[1].set_yticks([-90, -45, 0, 45, 90])
ax[2].set_yticks([-90, -45, 0, 45, 90])

fig.subplots_adjust(right=0.8)
cbar_ax = fig.add_axes([0.85, 0.45, 0.03, 0.4])
fig.colorbar(im, cax=cbar_ax)

_ = cbar_ax.set_ylabel('km/s', rotation=90)

plt.savefig("CrossCorrelation/figs/2d-cc.png" , bbox_inches="tight", dpi=600)
```


```python
fig, ax = plt.subplots(figsize=(5, 3))
pos = ax.imshow(v1.T,
                extent=[p[0]*180/np.pi, p[-1]*180/np.pi, t[0]*180/np.pi, t[-1]*180/np.pi],   
                vmin = 300, vmax = 700, cmap="plasma", aspect="auto", origin="lower")
#ax.set_title("(a) $V_{30R_{S}}$")

cbar = fig.colorbar(pos, ax=ax)
cbar.ax.set_ylabel('km/s', rotation=90)
ax.set_xticks([360, 270, 180, 90, 0])
ax.set_xlabel(r"Longitude (Deg.)", fontsize=14)
ax.set_ylabel(r"$\theta$ (Deg.)", fontsize=14)
ax.set_yticks([90, 45, 0, -45, -90])

plt.savefig("CrossCorrelation/figs/cc_30rs.png" , bbox_inches="tight", dpi=500)

```


```python
fig, ax = plt.subplots(figsize=(5, 3))

pos = ax.imshow(v2.T,
                extent=[p[0]*180/np.pi, p[-1]*180/np.pi, t[0]*180/np.pi, t[-1]*180/np.pi],   
                vmin = 300, vmax = 700, cmap="plasma", aspect="auto", origin="lower")


cbar = fig.colorbar(pos, ax=ax)
cbar.ax.set_ylabel('km/s', rotation=90)
ax.set_xticks([360, 270, 180, 90, 0])
ax.set_yticks([-90, -45, 0, 45, 90])
ax.set_xlabel(r"Longitude (Deg.)", fontsize=14)
ax.set_ylabel(r"$\theta$ (Deg.)", fontsize=14)

plt.savefig("CrossCorrelation/figs/cc_1AU.png" , bbox_inches="tight", dpi=500)
```


```python
fig, ax = plt.subplots(figsize=(5, 3))

pos = ax.imshow(res.T, extent=[p[0]*180/np.pi, p[-1]*180/np.pi, t[0]*180/np.pi, t[-1]*180/np.pi], aspect="auto", origin="lower")
ax.scatter(p[indices[0][0]]*180/np.pi, t[indices[1][0]], marker="x", c="r", label="maximum")

cbar = fig.colorbar(pos, ax=ax)
cbar.ax.set_ylabel('ZNCC', rotation=90)

ax.set_xticks([360, 270, 180, 90, 0, round(p[indices[0][0]]*180/np.pi)])
ax.set_ylabel(r"$\theta$ (Deg.)", fontsize=14)

ax.set_yticks([-90, -45, 0, 45, 90])
ax.set_xlabel(r"Longitude (Deg.)", fontsize=14)

ax.set_yticks([-90, -45, 0, 45, 90])


plt.savefig("CrossCorrelation/figs/cc_subfigure.png" , bbox_inches="tight", dpi=500)
```

# Apply Cross-Correlation to all 2D slices


```python
v1 = f[:, :, 0]

shift_vec_2d = np.zeros((2, len(r)))

for ii in range(1, len(r)):
    v2 = f[:, :, ii]
    corr = periodic_corr_2d(normalize(v1), normalize(v2))
    corr = corr/np.max(corr)
    indices = np.where(corr == corr.max())
    shift_vec_2d[:, ii] = [p[indices[0][0]]*180/np.pi, t[indices[1][0]]*180/np.pi]
```


```python
fig, ax = plt.subplots(figsize=(10, 3))
ax.plot(r, shift_vec_2d[1, :], label="latitude-lag")
ax.plot(r, shift_vec_2d[0, :],  label="longitude-lag")
ax.set_ylabel("Lag (Deg.)")
ax.set_xlabel("Heliocentric Distance (AU)")
_ = ax.legend()
```


```python
fig, ax = plt.subplots(figsize=(7, 3))
# plot the shifted data.
_ = ax.plot(r, shift_vec_2d[0, :], ls="-.", label="2D")
_ = ax.plot(r, shift_vec, ls="-.", label="1D")


# given only 50%. of the training data- can we extrapolate?
n = int(len(r)*0.5)
m, b = np.polyfit(r[:n].value, shift_vec_2d[0, :n], 1)
_ = ax.plot(r, m*r.value + b, label="linear-fit (2D)")

m, b = np.polyfit(r[:n].value, shift_vec[:n], 1)
_ = ax.plot(r, m*r.value + b, label="linear-fit (1D)")


_ = ax.axvline(r[n].value, c="k")


_ = ax.set_xlabel("Heliocentric Distance (AU)")
_ = ax.set_ylabel("Longitude Shift (Deg.)")
_ = ax.set_title("Cross-Correlation Shifting (MHD)")
_ = plt.legend()
```

# Cross-Correlation to HUX dataset (compare with method of characteristics)


```python
HUX_V = apply_hux_f_model(f[:, 55, 0], (r[1:]-r[:-1]).to(u.km).value, p[1:] -p[:-1]).T
```


```python
plot_heat_data(HUX_V, title="HUX")
```

# Cross-Correlation for all slices in comparison to the initial condition


```python
v1 = HUX_V[:, 0]
shift_vec_hux = [0]
for ii in range(1, len(r)):
    v2 = HUX_V[:, ii]
    corr = periodic_corr_np(normalize(v1), normalize(v2))
    corr = corr/np.max(corr)
    shift_vec_hux.append(p[np.argmax(corr)]*180/np.pi)
```


```python
fig, ax = plt.subplots(figsize=(7, 3))
# plot the shifted data.
_ = ax.plot(r, shift_vec_hux, ls="-.", label="HUX")
_ = ax.plot(r, shift_vec, ls="-.", label="MHD-1D")

# given only 50%. of the training data- can we extrapolate?
n = int(len(r)*0.5)
m, b = np.polyfit(r[:n].value, shift_vec_hux[:n], 1)
_ = ax.plot(r, m*r.value + b, label="linear-fit (HUX)")


_ = ax.axvline(r[n].value, c="k")

# given only 50%. of the training data- can we extrapolate?
n = int(len(r)*0.5)
m, b = np.polyfit(r[:n].value, shift_vec[:n], 1)
_ = ax.plot(r, m*r.value + b, label="linear-fit (MHD-1D)")

_ = ax.set_xlabel("Heliocentric Distance (AU)")
_ = ax.set_ylabel("Longitude Shift (Deg.)")
_ = ax.set_title("Cross-Correlation Shifting")
_ = plt.legend()
```

# Method of Characteristics vs. Cross-Correlation


```python
def compute_phi_shift_forward(p, r, v, omega=(2 * np.pi) / (25.38 * 86400), method=None):
    # initialize phi shift matrix. 
    phi_shift_mat = np.zeros((len(r), len(p)))
    
    # phi at index 0 is original phi grid 
    phi_shift_mat[0, :] = np.zeros(len(p))
    
    # delta r.
    dr = np.mean(r[1:] - r[:-1])
    
    # compute the phi shift for each idx in r. 
    for ii in range(len(r)-1):
        if method == "ballistic":
            phi_shift = -(omega/v[:, 0])*dr
        else:
            phi_shift = -(omega/v[:, ii])*dr
        phi_shift_mat[ii+1, :] = phi_shift_mat[ii, :] + phi_shift
        
    return phi_shift_mat
```


```python
phi_shift = compute_phi_shift_forward(p, r.to(u.km).value, HUX_V, method="ballistic")
```


```python
peak_b = 60#85
peak_e = 110#110
```


```python
print(p[peak_b]*180/np.pi)
print(p[peak_e]*180/np.pi)
```

    168.75000042702834
    309.375003059615



```python
avg_shift_vec = np.zeros(len(r))

V_new[:, 0] = HUX_V[:, 0]
for jj in range(1, len(r)):
    avg_shift = np.mean(phi_shift[jj, peak_b: peak_e]) #+np.std(phi_shift[jj, :])
    avg_shift_vec[jj] = avg_shift
```


```python
fig, ax = plt.subplots(figsize =(10, 3))
ax.plot(r.to(u.AU), -avg_shift_vec*180/np.pi, c="g", ls="--", label="Method of Characteristics")
ax.scatter(r.to(u.AU), shift_vec_hux, s=2, c="r", label="Cross-Correlation Extrapolation ")

# given only 70%. of the training data- can we extrapolate?
n = int(len(r)*0.7)
m, b = np.polyfit(r[:n].value, shift_vec_hux[:n], 1)
print(m, b)
_ = ax.plot(r, m*r.value + b,  c="r", label="CC linear-fit")

_ = ax.axvline(r[n].value, c="k")

_ = ax.set_xlabel("Heliocentric Distance (AU)")
_ = ax.set_ylabel("Longitude Shift (Deg.)")
_ = ax.set_title("A Comparison of Techniques")
_ = plt.legend()
_ = plt.tight_layout()

plt.savefig("CrossCorrelation/figs/comparison-of-techniques-cc-np128.png")
```


```python

```


```python

```

8bcacfb39bc305a42c3c49dc4a75dfa999aa0ea0
ipynb
Jupyter Notebook
CrossCorrelation/Cross-Correlation-Tutorial.ipynb
opaliss/Space-Weather-ROM
506410fa3e6ad367fb47610d8802c3803eef6393
MIT
CrossCorrelation/Cross-Correlation-Tutorial.ipynb
opaliss/Space-Weather-ROM
506410fa3e6ad367fb47610d8802c3803eef6393
MIT
CrossCorrelation/Cross-Correlation-Tutorial.ipynb
opaliss/Space-Weather-ROM
506410fa3e6ad367fb47610d8802c3803eef6393
MIT
Qwen/Qwen-72B
1. YES
2. YES