import numpy as np
import matplotlib.pyplot as plt
from matplotlib import rcParams
from scipy import io
import os
import matplotlib.cm as cm

rcParams.update({'font.size': 18})
plt.rcParams['figure.figsize'] = [8, 16]
## Building on Reference from: Data-driven Science and Engineering - Brunton and Kutz
results = io.loadmat(os.path.join('cyl_flow_data.mat'))
vortmean = np.mean(results['VORTALL'], axis=1)
X = results['VORTALL'] - vortmean.reshape((-1,1))

# VORTALL contains flow fields reshaped into column vectors
def DMD(X,Xprime,r):
    # Step 1: SVD of X
    U,Sigma,VT = np.linalg.svd(X,full_matrices=0)
    Ur = U[:,:r]
    Sigmar = np.diag(Sigma[:r])
    VTr = VT[:r,:]

    # Step 2: Compute Atilde
    Atilde = np.linalg.solve(Sigmar.T,(Ur.T @ Xprime @ VTr.T).T).T

    # Step 3: Compute eigenvalues and eigenvectors
    Lambda, W = np.linalg.eig(Atilde)
    
    # Step 4: Compute Phi
    Phi = Xprime @ np.linalg.solve(Sigmar.T,VTr).T @ W
    alpha1 = Sigmar @ VTr[:,0]
    b = np.linalg.solve(W @ np.diag(Lambda),alpha1)
    return Phi, Lambda, b
Phi, Lambda, b = DMD(X[:,:-1],X[:,1:],21)
# Reconstruct system dynamics (inference)
timesteps = X.shape[1]
time_dynamics = np.zeros((21, timesteps), dtype='complex')

for i in range(timesteps):
    time_dynamics[:, i] = b * (Lambda ** i)  # Element-wise exponential growth

X_dmd = Phi @ time_dynamics  # Reconstructed data

# Compare original and reconstructed at selected frame
frame_idx = 150  # Choose any time index < timesteps

original_field = np.real(np.reshape(X[:, frame_idx], (449,199))).T
reconstructed_field = np.real(np.reshape(X_dmd[:, frame_idx], (449,199))).T

vmin = min(original_field.min(), reconstructed_field.min())
vmax = max(original_field.max(), reconstructed_field.max())

fig, axes = plt.subplots(1, 2, figsize=(14, 6))
axes[0].imshow(original_field, cmap=cm.RdBu, vmin=vmin, vmax=vmax)
axes[0].set_title(f'Original Field at t={frame_idx}')

axes[1].imshow(reconstructed_field, cmap=cm.RdBu, vmin=vmin, vmax=vmax)
axes[1].set_title(f'DMD Reconstruction at t={frame_idx}')

plt.tight_layout()
plt.show()

png

## Plot Mode 2
vortmin = -5
vortmax = 5
V2 = np.copy(np.real(np.reshape(Phi[:,9],(449,199))))
V2 = V2.T

# normalize values... not symmetric
minval = np.min(V2)
maxval = np.max(V2)

if np.abs(minval) < 5 and np.abs(maxval) < 5:
    if np.abs(minval) > np.abs(maxval):
        vortmax = maxval
        vortmin = -maxval
    else:
        vortmin = minval
        vortmax = -minval

V2[V2 > vortmax] = vortmax
V2[V2 < vortmin] = vortmin

plt.imshow(V2,cmap='jet',vmin=vortmin,vmax=vortmax)

cvals = np.array([-4,-2,-1,-0.5,-0.25,-0.155])
plt.contour(V2,cvals*vortmax/5,colors='k',linestyles='dashed',linewidths=1)
plt.contour(V2,np.flip(-cvals)*vortmax/5,colors='k',linestyles='solid',linewidths=0.4)

plt.scatter(49,99,5000,color='k') # draw cylinder


plt.show()

png